aboutsummaryrefslogtreecommitdiff
path: root/builtin
Commit message (Expand)AuthorAge
...
* Adjust some settings to modern environments and networks. (#7549)lhofhansl2018-07-13
* Android: Add 'aux' button (#7477)Muhammad Rifqi Priyo Susanto2018-07-10
* core.spawn_falling_node: Keep metadata (#7476)SmallJoker2018-07-09
* Builtin/../misc.lua: Replace minetest. with core., improve codestyle (#7540)ClobberXD2018-07-09
* Make the server status message customizable (#7357)SmallJoker2018-07-01
* Log usage of /pulverize (#7503)Anand S2018-06-30
* Mgvalleys: Fix 'mgvalleys_spflags' setting name (#7495)Paramat2018-06-28
* Rename CSM flavours to restrictionsSmallJoker2018-06-26
* Remove misleading documentation about IPv6 server announcingsfan52018-06-22
* Update to new ContentDB APIrubenwardy2018-06-18
* Make os.tempfolder work correctly for MinGW & MSVC (#7443)nOOb31672018-06-13
* Add hasprivs chat command (#7336)Anand S2018-06-12
* Fix the /shutdown command (#7431)SmallJoker2018-06-11
* Fix typo (GH#7421)number Zero2018-06-10
* Mapgen flags: Add 'biomes' global mapgen flag (#7355)Paramat2018-06-08
* Stop autoforward on BACKWARD key-press (#7417)tukkek2018-06-06
* CSM/SSM: Add on_mods_loaded callback (#7411)Loïc Blot2018-06-06
* Fix builtin lua function os.tempfolder (#7368)nOOb31672018-06-05
* Add crossview support (#7361)otdav332018-05-29
* Tidy up dlg_config_world.lua (#5351)you2018-05-29
* Fix missing ignore textures (#7326)you2018-05-20
* Small usage changes for air and ignore items (#7305)Wuzzy2018-05-20
* Add screenshots to online content browserrubenwardy2018-05-20
* Add updating to online content browserrubenwardy2018-05-16
* Pointed_thing_to_face_pos: Avoid crash when player is inside a node (#7342)Paramat2018-05-16
* Fix description wrapping in browse online contentrubenwardy2018-04-26
* Mgvalleys: Update settingtypes.txtParamat2018-04-24
* Add online content repositoryrubenwardy2018-04-19
* Builtin auth handler: Speed up file writing (#7252)SmallJoker2018-04-19
* FOV: Raise lower limit to avoid zoom-loading of distant world (#7234)Paramat2018-04-15
* Android: Replace movement buttons with joystick (#7126)Muhammad Rifqi Priyo Susanto2018-04-10
* Mgcarpathian: Remove insignificant 'base' noise variation (#7209)Paramat2018-04-05
* Add player inventory callbacksSmallJoker2018-04-02
* ObjectRef: Add add_velocity() (#3208)you2018-03-31
* core.rotate_node: Do not trigger after_place_node (#6900)SmallJoker2018-03-29
* Optional alpha channel support for entitiesstujones112018-03-29
* Load dependencies and description from mod.confAndrew Ward2018-03-28
* Add reasons to on_dieplayer and on_hpchangeAndrew Ward2018-03-28
* Mgfractal / chunksize: Improve documentationparamat2018-03-20
* Rename 'subgame' to 'game'paramat2018-03-13
* Fix missing warningstream (or similar problem) (#7034)you2018-03-04
* is_area_protected: Rename from intersects_protection (#7073)SmallJoker2018-02-25
* Settingtypes.txt: Fix valleys dungeon ymax errorparamat2018-02-21
* Dungeons: Add Y limits in all mapgensparamat2018-02-20
* Delete world dialog: Move buttons to avoid double click deletionMuhammad Rifqi Priyo Susanto2018-02-20
* /shutdown can't do countdown when using reconnect and/or shutdown message (#7...dopik2018-02-19
* Add a refresh button to the serverlist (#6957)Tre2018-02-19
* Fix "Ignoring CONTENT_IGNORE redefinition" warning (#4393)you2018-02-18
* Add `on_auth_fail` callback (#7039)red-0012018-02-15
* Item entity: Delete in 'ignore' nodesparamat2018-02-13
#n438'>438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 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 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>

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.
*/

#ifndef VOXEL_HEADER
#define VOXEL_HEADER

#include "irrlichttypes.h"
#include "irr_v3d.h"
#include <irrList.h>
#include <iostream>
#include "debug.h"
#include "mapnode.h"
#include <set>
#include <list>

class INodeDefManager;

// For VC++
#undef min
#undef max

/*
	A fast voxel manipulator class.

	In normal operation, it fetches more map when it is requested.
	It can also be used so that all allowed area is fetched at the
	start, using ManualMapVoxelManipulator.

	Not thread-safe.
*/

/*
	Debug stuff
*/
extern u32 emerge_time;
extern u32 emerge_load_time;

/*
	This class resembles aabbox3d<s16> a lot, but has inclusive
	edges for saner handling of integer sizes
*/
class VoxelArea
{
public:
	// Starts as zero sized
	VoxelArea():
		MinEdge(1,1,1),
		MaxEdge(0,0,0)
	{
	}
	VoxelArea(v3s16 min_edge, v3s16 max_edge):
		MinEdge(min_edge),
		MaxEdge(max_edge)
	{
	}
	VoxelArea(v3s16 p):
		MinEdge(p),
		MaxEdge(p)
	{
	}

	/*
		Modifying methods
	*/

	void addArea(VoxelArea &a)
	{
		if(getExtent() == v3s16(0,0,0))
		{
			*this = a;
			return;
		}
		if(a.MinEdge.X < MinEdge.X) MinEdge.X = a.MinEdge.X;
		if(a.MinEdge.Y < MinEdge.Y) MinEdge.Y = a.MinEdge.Y;
		if(a.MinEdge.Z < MinEdge.Z) MinEdge.Z = a.MinEdge.Z;
		if(a.MaxEdge.X > MaxEdge.X) MaxEdge.X = a.MaxEdge.X;
		if(a.MaxEdge.Y > MaxEdge.Y) MaxEdge.Y = a.MaxEdge.Y;
		if(a.MaxEdge.Z > MaxEdge.Z) MaxEdge.Z = a.MaxEdge.Z;
	}
	void addPoint(v3s16 p)
	{
		if(getExtent() == v3s16(0,0,0))
		{
			MinEdge = p;
			MaxEdge = p;
			return;
		}
		if(p.X < MinEdge.X) MinEdge.X = p.X;
		if(p.Y < MinEdge.Y) MinEdge.Y = p.Y;
		if(p.Z < MinEdge.Z) MinEdge.Z = p.Z;
		if(p.X > MaxEdge.X) MaxEdge.X = p.X;
		if(p.Y > MaxEdge.Y) MaxEdge.Y = p.Y;
		if(p.Z > MaxEdge.Z) MaxEdge.Z = p.Z;
	}

	// Pad with d nodes
	void pad(v3s16 d)
	{
		MinEdge -= d;
		MaxEdge += d;
	}

	/*void operator+=(v3s16 off)
	{
		MinEdge += off;
		MaxEdge += off;
	}

	void operator-=(v3s16 off)
	{
		MinEdge -= off;
		MaxEdge -= off;
	}*/

	/*
		const methods
	*/

	v3s16 getExtent() const
	{
		return MaxEdge - MinEdge + v3s16(1,1,1);
	}
	s32 getVolume() const
	{
		v3s16 e = getExtent();
		return (s32)e.X * (s32)e.Y * (s32)e.Z;
	}
	bool contains(const VoxelArea &a) const
	{
		// No area contains an empty area
		// NOTE: Algorithms depend on this, so do not change.
		if(a.getExtent() == v3s16(0,0,0))
			return false;

		return(
			a.MinEdge.X >= MinEdge.X && a.MaxEdge.X <= MaxEdge.X &&
			a.MinEdge.Y >= MinEdge.Y && a.MaxEdge.Y <= MaxEdge.Y &&
			a.MinEdge.Z >= MinEdge.Z && a.MaxEdge.Z <= MaxEdge.Z
		);
	}
	bool contains(v3s16 p) const
	{
		return(
			p.X >= MinEdge.X && p.X <= MaxEdge.X &&
			p.Y >= MinEdge.Y && p.Y <= MaxEdge.Y &&
			p.Z >= MinEdge.Z && p.Z <= MaxEdge.Z
		);
	}
	bool contains(s32 i) const
	{
		return (i >= 0 && i < getVolume());
	}
	bool operator==(const VoxelArea &other) const
	{
		return (MinEdge == other.MinEdge
				&& MaxEdge == other.MaxEdge);
	}

	VoxelArea operator+(v3s16 off) const
	{
		return VoxelArea(MinEdge+off, MaxEdge+off);
	}

	VoxelArea operator-(v3s16 off) const
	{
		return VoxelArea(MinEdge-off, MaxEdge-off);
	}

	/*
		Returns 0-6 non-overlapping areas that can be added to
		a to make up this area.

		a: area inside *this
	*/
	void diff(const VoxelArea &a, std::list<VoxelArea> &result)
	{
		/*
			This can result in a maximum of 6 areas
		*/

		// If a is an empty area, return the current area as a whole
		if(a.getExtent() == v3s16(0,0,0))
		{
			VoxelArea b = *this;
			if(b.getVolume() != 0)
				result.push_back(b);
			return;
		}

		assert(contains(a));

		// Take back area, XY inclusive
		{
			v3s16 min(MinEdge.X, MinEdge.Y, a.MaxEdge.Z+1);
			v3s16 max(MaxEdge.X, MaxEdge.Y, MaxEdge.Z);
			VoxelArea b(min, max);
			if(b.getVolume() != 0)
				result.push_back(b);
		}

		// Take front area, XY inclusive
		{
			v3s16 min(MinEdge.X, MinEdge.Y, MinEdge.Z);
			v3s16 max(MaxEdge.X, MaxEdge.Y, a.MinEdge.Z-1);
			VoxelArea b(min, max);
			if(b.getVolume() != 0)
				result.push_back(b);
		}

		// Take top area, X inclusive
		{
			v3s16 min(MinEdge.X, a.MaxEdge.Y+1, a.MinEdge.Z);
			v3s16 max(MaxEdge.X, MaxEdge.Y, a.MaxEdge.Z);
			VoxelArea b(min, max);
			if(b.getVolume() != 0)
				result.push_back(b);
		}

		// Take bottom area, X inclusive
		{
			v3s16 min(MinEdge.X, MinEdge.Y, a.MinEdge.Z);
			v3s16 max(MaxEdge.X, a.MinEdge.Y-1, a.MaxEdge.Z);
			VoxelArea b(min, max);
			if(b.getVolume() != 0)
				result.push_back(b);
		}

		// Take left area, non-inclusive
		{
			v3s16 min(MinEdge.X, a.MinEdge.Y, a.MinEdge.Z);
			v3s16 max(a.MinEdge.X-1, a.MaxEdge.Y, a.MaxEdge.Z);
			VoxelArea b(min, max);
			if(b.getVolume() != 0)
				result.push_back(b);
		}

		// Take right area, non-inclusive
		{
			v3s16 min(a.MaxEdge.X+1, a.MinEdge.Y, a.MinEdge.Z);
			v3s16 max(MaxEdge.X, a.MaxEdge.Y, a.MaxEdge.Z);
			VoxelArea b(min, max);
			if(b.getVolume() != 0)
				result.push_back(b);
		}

	}

	/*
		Translates position from virtual coordinates to array index
	*/
	s32 index(s16 x, s16 y, s16 z) const
	{
		v3s16 em = getExtent();
		v3s16 off = MinEdge;
		s32 i = (s32)(z-off.Z)*em.Y*em.X + (y-off.Y)*em.X + (x-off.X);
		//dstream<<" i("<<x<<","<<y<<","<<z<<")="<<i<<" ";
		return i;
	}
	s32 index(v3s16 p) const
	{
		return index(p.X, p.Y, p.Z);
	}

	// Translate index in the X coordinate
	void add_x(const v3s16 &extent, u32 &i, s16 a)
	{
		i += a;
	}
	// Translate index in the Y coordinate
	void add_y(const v3s16 &extent, u32 &i, s16 a)
	{
		i += a * extent.X;
	}
	// Translate index in the Z coordinate
	void add_z(const v3s16 &extent, u32 &i, s16 a)
	{
		i += a * extent.X*extent.Y;
	}
	// Translate index in space
	void add_p(const v3s16 &extent, u32 &i, v3s16 a)
	{
		i += a.Z*extent.X*extent.Y + a.Y*extent.X + a.X;
	}

	/*
		Print method for debugging
	*/
	void print(std::ostream &o) const
	{
		v3s16 e = getExtent();
		o<<"("<<MinEdge.X
		 <<","<<MinEdge.Y
		 <<","<<MinEdge.Z
		 <<")("<<MaxEdge.X
		 <<","<<MaxEdge.Y
		 <<","<<MaxEdge.Z
		 <<")"
		 <<"="<<e.X<<"x"<<e.Y<<"x"<<e.Z<<"="<<getVolume();
	}

	// Edges are inclusive
	v3s16 MinEdge;
	v3s16 MaxEdge;
};

// Hasn't been copied from source (emerged)
#define VOXELFLAG_NOT_LOADED (1<<0)
// Checked as being inexistent in source
#define VOXELFLAG_INEXISTENT (1<<1)
// Algorithm-dependent
#define VOXELFLAG_CHECKED1 (1<<2)
// Algorithm-dependent
#define VOXELFLAG_CHECKED2 (1<<3)
// Algorithm-dependent
#define VOXELFLAG_CHECKED3 (1<<4)
// Algorithm-dependent
#define VOXELFLAG_CHECKED4 (1<<5)

enum VoxelPrintMode
{
	VOXELPRINT_NOTHING,
	VOXELPRINT_MATERIAL,
	VOXELPRINT_WATERPRESSURE,
	VOXELPRINT_LIGHT_DAY,
};

class VoxelManipulator /*: public NodeContainer*/
{
public:
	VoxelManipulator();
	virtual ~VoxelManipulator();

	/*
		Virtuals from NodeContainer
	*/
	/*virtual u16 nodeContainerId() const
	{
		return NODECONTAINER_ID_VOXELMANIPULATOR;
	}
	bool isValidPosition(v3s16 p)
	{
		emerge(p);
		return !(m_flags[m_area.index(p)] & VOXELFLAG_INEXISTENT);
	}*/

	/*
		These are a bit slow and shouldn't be used internally.
		Use m_data[m_area.index(p)] instead.
	*/
	MapNode getNode(v3s16 p)
	{
		emerge(p);

		if(m_flags[m_area.index(p)] & VOXELFLAG_INEXISTENT)
		{
			/*dstream<<"EXCEPT: VoxelManipulator::getNode(): "
					<<"p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"
					<<", index="<<m_area.index(p)
					<<", flags="<<(int)m_flags[m_area.index(p)]
					<<" is inexistent"<<std::endl;*/
			throw InvalidPositionException
			("VoxelManipulator: getNode: inexistent");
		}

		return m_data[m_area.index(p)];
	}
	MapNode getNodeNoEx(v3s16 p)
	{
		emerge(p);

		if(m_flags[m_area.index(p)] & VOXELFLAG_INEXISTENT)
		{
			return MapNode(CONTENT_IGNORE);
		}

		return m_data[m_area.index(p)];
	}
	MapNode getNodeNoExNoEmerge(v3s16 p)
	{
		if(m_area.contains(p) == false)
			return MapNode(CONTENT_IGNORE);
		if(m_flags[m_area.index(p)] & VOXELFLAG_INEXISTENT)
			return MapNode(CONTENT_IGNORE);
		return m_data[m_area.index(p)];
	}
	// Stuff explodes if non-emerged area is touched with this.
	// Emerge first, and check VOXELFLAG_INEXISTENT if appropriate.
	MapNode & getNodeRefUnsafe(v3s16 p)
	{
		return m_data[m_area.index(p)];
	}
	u8 & getFlagsRefUnsafe(v3s16 p)
	{
		return m_flags[m_area.index(p)];
	}
	bool exists(v3s16 p)
	{
		return m_area.contains(p) &&
			!(getFlagsRefUnsafe(p) & VOXELFLAG_INEXISTENT);
	}
	MapNode & getNodeRef(v3s16 p)
	{
		emerge(p);
		if(getFlagsRefUnsafe(p) & VOXELFLAG_INEXISTENT)
		{
			/*dstream<<"EXCEPT: VoxelManipulator::getNode(): "
					<<"p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"
					<<", index="<<m_area.index(p)
					<<", flags="<<(int)getFlagsRefUnsafe(p)
					<<" is inexistent"<<std::endl;*/
			throw InvalidPositionException
			("VoxelManipulator: getNode: inexistent");
		}
		return getNodeRefUnsafe(p);
	}
	void setNode(v3s16 p, const MapNode &n)
	{
		emerge(p);

		m_data[m_area.index(p)] = n;
		m_flags[m_area.index(p)] &= ~VOXELFLAG_INEXISTENT;
		m_flags[m_area.index(p)] &= ~VOXELFLAG_NOT_LOADED;
	}
	// TODO: Should be removed and replaced with setNode
	void setNodeNoRef(v3s16 p, const MapNode &n)
	{
		setNode(p, n);
	}

	/*void setExists(VoxelArea a)
	{
		emerge(a);
		for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
		for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
		for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
		{
			m_flags[m_area.index(x,y,z)] &= ~VOXELFLAG_INEXISTENT;
		}
	}*/

	/*MapNode & operator[](v3s16 p)
	{
		//dstream<<"operator[] p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;
		if(isValidPosition(p) == false)
			emerge(VoxelArea(p));

		return m_data[m_area.index(p)];
	}*/

	/*
		Set stuff if available without an emerge.
		Return false if failed.
		This is convenient but slower than playing around directly
		with the m_data table with indices.
	*/
	bool setNodeNoEmerge(v3s16 p, MapNode n)
	{
		if(m_area.contains(p) == false)
			return false;
		m_data[m_area.index(p)] = n;
		return true;
	}
	bool setNodeNoEmerge(s32 i, MapNode n)
	{
		if(m_area.contains(i) == false)
			return false;
		m_data[i] = n;
		return true;
	}
	/*bool setContentNoEmerge(v3s16 p, u8 c)
	{
		if(isValidPosition(p) == false)
			return false;
		m_data[m_area.index(p)].d = c;
	}*/

	/*
		Control
	*/

	virtual void clear();

	void print(std::ostream &o, INodeDefManager *nodemgr,
			VoxelPrintMode mode=VOXELPRINT_MATERIAL);

	void addArea(VoxelArea area);

	/*
		Copy data and set flags to 0
		dst_area.getExtent() <= src_area.getExtent()
	*/
	void copyFrom(MapNode *src, VoxelArea src_area,
			v3s16 from_pos, v3s16 to_pos, v3s16 size);

	// Copy data
	void copyTo(MapNode *dst, VoxelArea dst_area,
			v3s16 dst_pos, v3s16 from_pos, v3s16 size);

	/*
		Algorithms
	*/

	void clearFlag(u8 flag);

	// TODO: Move to voxelalgorithms.h

	void unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
			std::set<v3s16> & light_sources, INodeDefManager *nodemgr);
	void unspreadLight(enum LightBank bank,
			std::map<v3s16, u8> & from_nodes,
			std::set<v3s16> & light_sources, INodeDefManager *nodemgr);

	void spreadLight(enum LightBank bank, v3s16 p, INodeDefManager *nodemgr);
	void spreadLight(enum LightBank bank,
			std::set<v3s16> & from_nodes, INodeDefManager *nodemgr);

	/*
		Virtual functions
	*/

	/*
		Get the contents of the requested area from somewhere.
		Shall touch only nodes that have VOXELFLAG_NOT_LOADED
		Shall reset VOXELFLAG_NOT_LOADED

		If not found from source, add with VOXELFLAG_INEXISTENT
	*/
	virtual void emerge(VoxelArea a, s32 caller_id=-1)
	{
		//dstream<<"emerge p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;
		addArea(a);
		for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
		for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
		for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
		{
			s32 i = m_area.index(x,y,z);
			// Don't touch nodes that have already been loaded
			if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
				continue;
			m_flags[i] = VOXELFLAG_INEXISTENT;
		}
	}

	/*
		Member variables
	*/

	/*
		The area that is stored in m_data.
		addInternalBox should not be used if getExtent() == v3s16(0,0,0)
		MaxEdge is 1 higher than maximum allowed position
	*/
	VoxelArea m_area;

	/*
		NULL if data size is 0 (extent (0,0,0))
		Data is stored as [z*h*w + y*h + x]
	*/
	MapNode *m_data;

	/*
		Flags of all nodes
	*/
	u8 *m_flags;

	//TODO: Use these or remove them
	//TODO: Would these make any speed improvement?
	//bool m_pressure_route_valid;
	//v3s16 m_pressure_route_surface;

	/*
		Some settings
	*/
	//bool m_disable_water_climb;

private:
};

#endif