code:
/*
Minetest
Copyright (C) 2010-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.
*/

#include "voxelalgorithms.h"
#include "nodedef.h"
#include "mapblock.h"
#include "map.h"

namespace voxalgo
{

void setLight(VoxelManipulator &v, VoxelArea a, u8 light,
		INodeDefManager *ndef)
{
	for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
	for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
	for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
	{
		v3s16 p(x,y,z);
		MapNode &n = v.getNodeRefUnsafe(p);
		n.setLight(LIGHTBANK_DAY, light, ndef);
		n.setLight(LIGHTBANK_NIGHT, light, ndef);
	}
}

void clearLightAndCollectSources(VoxelManipulator &v, VoxelArea a,
		enum LightBank bank, INodeDefManager *ndef,
		std::set<v3s16> & light_sources,
		std::map<v3s16, u8> & unlight_from)
{
	// The full area we shall touch
	VoxelArea required_a = a;
	required_a.pad(v3s16(0,0,0));
	// Make sure we have access to it
	v.addArea(a);

	for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
	for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
	for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
	{
		v3s16 p(x,y,z);
		MapNode &n = v.getNodeRefUnsafe(p);
		u8 oldlight = n.getLight(bank, ndef);
		n.setLight(bank, 0, ndef);

		// If node sources light, add to list
		u8 source = ndef->get(n).light_source;
		if(source != 0)
			light_sources.insert(p);

		// Collect borders for unlighting
		if((x==a.MinEdge.X || x == a.MaxEdge.X
		|| y==a.MinEdge.Y || y == a.MaxEdge.Y
		|| z==a.MinEdge.Z || z == a.MaxEdge.Z)
		&& oldlight != 0)
		{
			unlight_from[p] = oldlight;
		}
	}
}

SunlightPropagateResult propagateSunlight(VoxelManipulator &v, VoxelArea a,
		bool inexistent_top_provides_sunlight,
		std::set<v3s16> & light_sources,
		INodeDefManager *ndef)
{
	// Return values
	bool bottom_sunlight_valid = true;

	// The full area we shall touch extends one extra at top and bottom
	VoxelArea required_a = a;
	required_a.pad(v3s16(0,1,0));
	// Make sure we have access to it
	v.addArea(a);

	s16 max_y = a.MaxEdge.Y;
	s16 min_y = a.MinEdge.Y;

	for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
	for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
	{
		v3s16 p_overtop(x, max_y+1, z);
		bool overtop_has_sunlight = false;
		// If overtop node does not exist, trust heuristics
		if(!v.exists(p_overtop))
			overtop_has_sunlight = inexistent_top_provides_sunlight;
		else if(v.getNodeRefUnsafe(p_overtop).getContent() == CONTENT_IGNORE)
			overtop_has_sunlight = inexistent_top_provides_sunlight;
		// Otherwise refer to it's light value
		else
			overtop_has_sunlight = (v.getNodeRefUnsafe(p_overtop).getLight(
					LIGHTBANK_DAY, ndef) == LIGHT_SUN);

		// Copy overtop's sunlight all over the place
		u8 incoming_light = overtop_has_sunlight ? LIGHT_SUN : 0;
		for(s32 y=max_y; y>=min_y; y--)
		{
			v3s16 p(x,y,z);
			MapNode &n = v.getNodeRefUnsafe(p);
			if(incoming_light == 0){
				// Do nothing
			} else if(incoming_light == LIGHT_SUN &&
					ndef->get(n).sunlight_propagates){
				// Do nothing
			} else if(ndef->get(n).sunlight_propagates == false){
				incoming_light = 0;
			} else {
				incoming_light = diminish_light(incoming_light);
			}
			u8 old_light = n.getLight(LIGHTBANK_DAY, ndef);

			if(incoming_light > old_light)
				n.setLight(LIGHTBANK_DAY, incoming_light, ndef);

			if(diminish_light(incoming_light) != 0)
				light_sources.insert(p);
		}

		// Check validity of sunlight at top of block below if it
		// hasn't already been proven invalid
		if(bottom_sunlight_valid)
		{
			bool sunlight_should_continue_down = (incoming_light == LIGHT_SUN);
			v3s16 p_overbottom(x, min_y-1, z);
			if(!v.exists(p_overbottom) ||
					v.getNodeRefUnsafe(p_overbottom
							).getContent() == CONTENT_IGNORE){
				// Is not known, cannot compare
			} else {
				bool overbottom_has_sunlight = (v.getNodeRefUnsafe(p_overbottom
						).getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN);
				if(sunlight_should_continue_down != overbottom_has_sunlight){
					bottom_sunlight_valid = false;
				}
			}
		}
	}

	return SunlightPropagateResult(bottom_sunlight_valid);
}

/*!
 * A direction.
 * 0=X+
 * 1=Y+
 * 2=Z+
 * 3=Z-
 * 4=Y-
 * 5=X-
 * 6=no direction
 * Two directions are opposite only if their sum is 5.
 */
typedef u8 direction;
/*!
 * Relative node position.
 * This represents a node's position in its map block.
 * All coordinates must be between 0 and 15.
 */
typedef v3s16 relative_v3;
/*!
 * Position of a map block (block coordinates).
 * One block_pos unit is as long as 16 node position units.
 */
typedef v3s16 mapblock_v3;

//! Contains information about a node whose light is about to change.
struct ChangingLight {
	//! Relative position of the node in its map block.
	relative_v3 rel_position;
	//! Position of the node's block.
	mapblock_v3 block_position;
	//! Pointer to the node's block.
	MapBlock *block;
	/*!
	 * Direction from the node that caused this node's changing
	 * to this node.
	 */
	direction source_direction;

	ChangingLight() :
		rel_position(),
		block_position(),
		block(NULL),
		source_direction(6)
	{}

	ChangingLight(relative_v3 rel_pos, mapblock_v3 block_pos,
		MapBlock *b, direction source_dir) :
		rel_position(rel_pos),
		block_position(block_pos),
		block(b),
		source_direction(source_dir)
	{}
};

/*!
 * A fast, priority queue-like container to contain ChangingLights.
 * The ChangingLights are ordered by the given light levels.
 * The brightest ChangingLight is returned first.
 */
struct LightQueue {
	//! For each light level there is a vector.
	std::vector<ChangingLight> lights[LIGHT_SUN + 1];
	//! Light of the brightest ChangingLight in the queue.
	u8 max_light;

	/*!
	 * Creates a LightQueue.
	 * \param reserve for each light level that many slots are reserved.
	 */
	LightQueue(size_t reserve)
	{
		max_light = LIGHT_SUN;
		for (u8 i = 0; i <= LIGHT_SUN; i++) {
			lights[i].reserve(reserve);
		}
	}

	/*!
	 * Returns the next brightest ChangingLight and
	 * removes it from the queue.
	 * If there were no elements in the queue, the given parameters
	 * remain unmodified.
	 * \param light light level of the popped ChangingLight
	 * \param data the ChangingLight that was popped
	 * \returns true if there was a ChangingLight in the queue.
	 */
	bool next(u8 &light, ChangingLight &data)
	{
		while (lights[max_light].empty()) {
			if (max_light == 0) {
				return false;
			}
			max_light--;
		}
		light = max_light;
		data = lights[max_light].back();
		lights[max_light].pop_back();
		return true;
	}

	/*!
	 * Adds an element to the queue.
	 * The parameters are the same as in ChangingLight's constructor.
	 * \param light light level of the ChangingLight
	 */
	inline void push(u8 light, const relative_v3 &rel_pos,
		const mapblock_v3 &block_pos, MapBlock *block,
		direction source_dir)
	{
		assert(light <= LIGHT_SUN);
		lights[light].push_back(
			ChangingLight(rel_pos, block_pos, block, source_dir));
	}
};

/*!
 * This type of light queue is for unlighting.
 * A node can be pushed in it only if its raw light is zero.
 * This prevents pushing nodes twice into this queue.
 * The light of the pushed ChangingLight must be the
 * light of the node before unlighting it.
 */
typedef LightQueue UnlightQueue;
/*!
 * This type of light queue is for spreading lights.
 * While spreading lights, all the nodes in it must
 * have the same light as the light level the ChangingLights
 * were pushed into this queue with. This prevents unnecessary
 * re-pushing of the nodes into the queue.
 * If a node doesn't let light trough but emits light, it can be added
 * too.
 */
typedef LightQueue ReLightQueue;

/*!
 * neighbor_dirs[i] points towards
 * the direction i.
 * See the definition of the type "direction"
 */
const static v3s16 neighbor_dirs[6] = {
	v3s16(1, 0, 0), // right
	v3s16(0, 1, 0), // top
	v3s16(0, 0, 1), // back
	v3s16(0, 0, -1), // front
	v3s16(0, -1, 0), // bottom
	v3s16(-1, 0, 0), // left
};

/*!
 * Transforms the given map block offset by one node towards
 * the specified direction.
 * \param dir the direction of the transformation
 * \param rel_pos the node's relative position in its map block
 * \param block_pos position of the node's block
 */
bool step_rel_block_pos(direction dir, relative_v3 &rel_pos,
	mapblock_v3 &block_pos)
{
	switch (dir) {
	case 0:
		if (rel_pos.X < MAP_BLOCKSIZE - 1) {
			rel_pos.X++;
		} else {
			rel_pos.X = 0;
			block_pos.X++;
			return true;
		}
		break;
	case 1:
		if (rel_pos.Y < MAP_BLOCKSIZE - 1) {
			rel_pos.Y++;
		} else {
			rel_pos.Y = 0;
			block_pos.Y++;
			return true;
		}
		break;
	case 2:
		if (rel_pos.Z < MAP_BLOCKSIZE - 1) {
			rel_pos.Z++;
		} else {
			rel_pos.Z = 0;
			block_pos.Z++;
			return true;
		}
		break;
	case 3:
		if (rel_pos.Z > 0) {
			rel_pos.Z--;
		} else {
			rel_pos.Z = MAP_BLOCKSIZE - 1;
			block_pos.Z--;
			return true;
		}
		break;
	case 4:
		if (rel_pos.Y > 0) {
			rel_pos.Y--;
		} else {
			rel_pos.Y = MAP_BLOCKSIZE - 1;
			block_pos.Y--;
			return true;
		}
		break;
	case 5:
		if (rel_pos.X > 0) {
			rel_pos.X--;
		} else {
			rel_pos.X = MAP_BLOCKSIZE - 1;
			block_pos.X--;
			return true;
		}
		break;
	}
	return false;
}

/*
 * Removes all light that is potentially emitted by the specified
 * light sources. These nodes will have zero light.
 * Returns all nodes whose light became zero but should be re-lighted.
 *
 * \param bank the light bank in which the procedure operates
 * \param from_nodes nodes whose light is removed
 * \param light_sources nodes that should be re-lighted
 * \param modified_blocks output, all modified map blocks are added to this
 */
void unspread_light(Map *map, INodeDefManager *nodemgr, LightBank bank,
	UnlightQueue &from_nodes, ReLightQueue &light_sources,
	std::map<v3s16, MapBlock*> &modified_blocks)
{
	// Stores data popped from from_nodes
	u8 current_light;
	ChangingLight current;
	// Data of the current neighbor
	mapblock_v3 neighbor_block_pos;
	relative_v3 neighbor_rel_pos;
	// A dummy boolean
	bool is_valid_position;
	// Direction of the brightest neighbor of the node
	direction source_dir;
	while (from_nodes.next(current_light, current)) {
		// For all nodes that need unlighting

		// There is no brightest neighbor
		source_dir = 6;
		// The current node
		const MapNode &node = current.block->getNodeNoCheck(
			current.rel_position, &is_valid_position);
		const ContentFeatures &f = nodemgr->get(node);
		// If the node emits light, it behaves like it had a
		// brighter neighbor.
		u8 brightest_neighbor_light = f.light_source + 1;
		for (direction i = 0; i < 6; i++) {
			//For each neighbor

			// The node that changed this node has already zero light
			// and it can't give light to this node
			if (current.source_direction + i == 5) {
				continue;
			}
			// Get the neighbor's position and block
			neighbor_rel_pos = current.rel_position;
			neighbor_block_pos = current.block_position;
			MapBlock *neighbor_block;
			if (step_rel_block_pos(i, neighbor_rel_pos, neighbor_block_pos)) {
				neighbor_block = map->getBlockNoCreateNoEx(neighbor_block_pos);
				if (neighbor_block == NULL) {
					current.block->setLightingComplete(bank, i, false);
					continue;
				}
			} else {
				neighbor_block = current.block;
			}
			// Get the neighbor itself
			MapNode neighbor = neighbor_block->getNodeNoCheck(neighbor_rel_pos,
				&is_valid_position);
			const ContentFeatures &neighbor_f = nodemgr->get(
				neighbor.getContent());
			u8 neighbor_light = neighbor.getLightRaw(bank, neighbor_f);
			// If the neighbor has at least as much light as this node, then
			// it won't lose its light, since it should have been added to
			// from_nodes earlier, so its light would be zero.
			if (neighbor_f.light_propagates && neighbor_light < current_light) {
				// Unlight, but only if the node has light.
				if (neighbor_light > 0) {
					neighbor.setLight(bank, 0, neighbor_f);
					neighbor_block->setNodeNoCheck(neighbor_rel_pos, neighbor);
					from_nodes.push(neighbor_light, neighbor_rel_pos,
						neighbor_block_pos, neighbor_block, i);
					// The current node was modified earlier, so its block
					// is in modified_blocks.
					if (current.block != neighbor_block) {
						modified_blocks[neighbor_block_pos] = neighbor_block;
					}
				}
			} else {
				// The neighbor can light up this node.
				if (neighbor_light < neighbor_f.light_source) {
					neighbor_light = neighbor_f.light_source;
				}
				if (brightest_neighbor_light < neighbor_light) {
					brightest_neighbor_light = neighbor_light;
					source_dir = i;
				}
			}
		}
		// If the brightest neighbor is able to light up this node,
		// then add this node to the output nodes.
		if (brightest_neighbor_light > 1 && f.light_propagates) {
			brightest_neighbor_light--;
			light_sources.push(brightest_neighbor_light, current.rel_position,
				current.block_position, current.block,
				(source_dir == 6) ? 6 : 5 - source_dir
				/* with opposite direction*/);
		}
	}
}

/*
 * Spreads light from the specified starting nodes.
 *
 * Before calling this procedure, make sure that all ChangingLights
 * in light_sources have as much light on the map as they have in
 * light_sources (if the queue contains a node multiple times, the brightest
 * occurrence counts).
 *
 * \param bank the light bank in which the procedure operates
 * \param light_sources starting nodes
 * \param modified_blocks output, all modified map blocks are added to this
 */
void spread_light(Map *map, INodeDefManager *nodemgr, LightBank bank,
	LightQueue &light_sources,
	std::map<v3s16, MapBlock*> &modified_blocks)
{
	// The light the current node can provide to its neighbors.
	u8 spreading_light;
	// The ChangingLight for the current node.
	ChangingLight current;
	// Position of the current neighbor.
	mapblock_v3 neighbor_block_pos;
	relative_v3 neighbor_rel_pos;
	// A dummy boolean.
	bool is_valid_position;
	while (light_sources.next(spreading_light, current)) {
		spreading_light--;
		for (direction i = 0; i < 6; i++) {
			// This node can't light up its light source
			if (current.source_direction + i == 5) {
				continue;
			}
			// Get the neighbor's position and block
			neighbor_rel_pos = current.rel_position;
			neighbor_block_pos = current.block_position;
			MapBlock *neighbor_block;
			if (step_rel_block_pos(i, neighbor_rel_pos, neighbor_block_pos)) {
				neighbor_block = map->getBlockNoCreateNoEx(neighbor_block_pos);
				if (neighbor_block == NULL) {
					current.block->setLightingComplete(bank, i, false);
					continue;
				}
			} else {
				neighbor_block = current.block;
			}
			// Get the neighbor itself
			MapNode neighbor = neighbor_block->getNodeNoCheck(neighbor_rel_pos,
				&is_valid_position);
			const ContentFeatures &f = nodemgr->get(neighbor.getContent());
			if (f.light_propagates) {
				// Light up the neighbor, if it has less light than it should.
				u8 neighbor_light = neighbor.getLightRaw(bank, f);
				if (neighbor_light < spreading_light) {
					neighbor.setLight(bank, spreading_light, f);
					neighbor_block->setNodeNoCheck(neighbor_rel_pos, neighbor);
					light_sources.push(spreading_light, neighbor_rel_pos,
						neighbor_block_pos, neighbor_block, i);
					// The current node was modified earlier, so its block
					// is in modified_blocks.
					if (current.block != neighbor_block) {
						modified_blocks[neighbor_block_pos] = neighbor_block;
					}
				}
			}
		}
	}
}

struct SunlightPropagationUnit{
	v2s16 relative_pos;
	bool is_sunlit;

	SunlightPropagationUnit(v2s16 relpos, bool sunlit):
		relative_pos(relpos),
		is_sunlit(sunlit)
	{}
};

struct SunlightPropagationData{
	std::vector<SunlightPropagationUnit> data;
	v3s16 target_block;
};

/*!
 * Returns true if the node gets sunlight from the
 * node above it.
 *
 * \param pos position of the node.
 */
bool is_sunlight_above(Map *map, v3s16 pos, INodeDefManager *ndef)
{
	bool sunlight = true;
	mapblock_v3 source_block_pos;
	relative_v3 source_rel_pos;
	getNodeBlockPosWithOffset(pos + v3s16(0, 1, 0), source_block_pos,
		source_rel_pos);
	// If the node above has sunlight, this node also can get it.
	MapBlock *source_block = map->getBlockNoCreateNoEx(source_block_pos);
	if (source_block == NULL) {
		// But if there is no node above, then use heuristics
		MapBlock *node_block = map->getBlockNoCreateNoEx(getNodeBlockPos(pos));
		if (node_block == NULL) {
			sunlight = false;
		} else {
			sunlight = !node_block->getIsUnderground();
		}
	} else {
		bool is_valid_position;
		MapNode above = source_block->getNodeNoCheck(source_rel_pos,
			&is_valid_position);
		if (is_valid_position) {
			if (above.getContent() == CONTENT_IGNORE) {
				// Trust heuristics
				if (source_block->getIsUnderground()) {
					sunlight = false;
				}
			} else if (above.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN) {
				// If the node above doesn't have sunlight, this
				// node is in shadow.
				sunlight = false;
			}
		}
	}
	return sunlight;
}

static const LightBank banks[] = { LIGHTBANK_DAY, LIGHTBANK_NIGHT };

void update_lighting_nodes(Map *map,
	std::vector<std::pair<v3s16, MapNode> > &oldnodes,
	std::map<v3s16, MapBlock*> &modified_blocks)
{
	INodeDefManager *ndef = map->getNodeDefManager();
	// For node getter functions
	bool is_valid_position;

	// Process each light bank separately
	for (s32 i = 0; i < 2; i++) {
		LightBank bank = banks[i];
		UnlightQueue disappearing_lights(256);
		ReLightQueue light_sources(256);
		// Nodes that are brighter than the brightest modified node was
		// won't change, since they didn't get their light from a
		// modified node.
		u8 min_safe_light = 0;
		for (std::vector<std::pair<v3s16, MapNode> >::iterator it =
				oldnodes.begin(); it < oldnodes.end(); ++it) {
			u8 old_light = it->second.getLight(bank, ndef);
			if (old_light > min_safe_light) {
				min_safe_light = old_light;
			}
		}
		// If only one node changed, even nodes with the same brightness
		// didn't get their light from the changed node.
		if (oldnodes.size() > 1) {
			min_safe_light++;
		}
		// For each changed node process sunlight and initialize
		for (std::vector<std::pair<v3s16, MapNode> >::iterator it =
				oldnodes.begin(); it < oldnodes.end(); ++it) {
			// Get position and block of the changed node
			v3s16 p = it->first;
			relative_v3 rel_pos;
			mapblock_v3 block_pos;
			getNodeBlockPosWithOffset(p, block_pos, rel_pos);
			MapBlock *block = map->getBlockNoCreateNoEx(block_pos);
			if (block == NULL || block->isDummy()) {
				continue;
			}
			// Get the new node
			MapNode n = block->getNodeNoCheck(rel_pos, &is_valid_position);
			if (!is_valid_position) {
				break;
			}

			// Light of the old node
			u8 old_light = it->second.getLight(bank, ndef);

			// Add the block of the added node to modified_blocks
			modified_blocks[block_pos] = block;

			// Get new light level of the node
			u8 new_light = 0;
			if (ndef->get(n).light_propagates) {
				if (bank == LIGHTBANK_DAY && ndef->get(n).sunlight_propagates
					&& is_sunlight_above(map, p, ndef)) {
					new_light = LIGHT_SUN;
				} else {
					new_light = ndef->get(n).light_source;
					for (int i = 0; i < 6; i++) {
						v3s16 p2 = p + neighbor_dirs[i];
						bool is_valid;
						MapNode n2 = map->getNodeNoEx(p2, &is_valid);
						if (is_valid) {
							u8 spread = n2.getLight(bank, ndef);
							// If it is sure that the neighbor won't be
							// unlighted, its light can spread to this node.
							if (spread > new_light && spread >= min_safe_light) {
								new_light = spread - 1;
							}
						}
					}
				}
			} else {
				// If this is an opaque node, it still can emit light.
				new_light = ndef->get(n).light_source;
			}

			if (new_light > 0) {
				light_sources.push(new_light, rel_pos, block_pos, block, 6);
			}

			if (new_light < old_light) {
				// The node became opaque or doesn't provide as much
				// light as the previous one, so it must be unlighted.

				// Add to unlight queue
				n.setLight(bank, 0, ndef);
				block->setNodeNoCheck(rel_pos, n);
				disappearing_lights.push(old_light, rel_pos, block_pos, block,
					6);

				// Remove sunlight, if there was any
				if (bank == LIGHTBANK_DAY && old_light == LIGHT_SUN) {
					for (s16 y = p.Y - 1;; y--) {
						v3s16 n2pos(p.X, y, p.Z);

						MapNode n2;

						n2 = map->getNodeNoEx(n2pos, &is_valid_position);
						if (!is_valid_position)
							break;

						// If this node doesn't have sunlight, the nodes below
						// it don't have too.
						if (n2.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN) {
							break;
						}
						// Remove sunlight and add to unlight queue.
						n2.setLight(LIGHTBANK_DAY, 0, ndef);
						map->setNode(n2pos, n2);
						relative_v3 rel_pos2;
						mapblock_v3 block_pos2;
						getNodeBlockPosWithOffset(n2pos, block_pos2, rel_pos2);
						MapBlock *block2 = map->getBlockNoCreateNoEx(
							block_pos2);
						disappearing_lights.push(LIGHT_SUN, rel_pos2,
							block_pos2, block2,
							4 /* The node above caused the change */);
					}
				}
			} else if (new_light > old_light) {
				// It is sure that the node provides more light than the previous
				// one, unlighting is not necessary.
				// Propagate sunlight
				if (bank == LIGHTBANK_DAY && new_light == LIGHT_SUN) {
					for (s16 y = p.Y - 1;; y--) {
						v3s16 n2pos(p.X, y, p.Z);

						MapNode n2;

						n2 = map->getNodeNoEx(n2pos, &is_valid_position);
						if (!is_valid_position)
							break;

						// This should not happen, but if the node has sunlight
						// then the iteration should stop.
						if (n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN) {
							break;
						}
						// If the node terminates sunlight, stop.
						if (!ndef->get(n2).sunlight_propagates) {
							break;
						}
						relative_v3 rel_pos2;
						mapblock_v3 block_pos2;
						getNodeBlockPosWithOffset(n2pos, block_pos2, rel_pos2);
						MapBlock *block2 = map->getBlockNoCreateNoEx(
							block_pos2);
						// Mark node for lighting.
						light_sources.push(LIGHT_SUN, rel_pos2, block_pos2,
							block2, 4);
					}
				}
			}

		}
		// Remove lights
		unspread_light(map, ndef, bank, disappearing_lights, light_sources,
			modified_blocks);
		// Initialize light values for light spreading.
		for (u8 i = 0; i <= LIGHT_SUN; i++) {
			const std::vector<ChangingLight> &lights = light_sources.lights[i];
			for (std::vector<ChangingLight>::const_iterator it = lights.begin();
					it < lights.end(); ++it) {
				MapNode n = it->block->getNodeNoCheck(it->rel_position,
					&is_valid_position);
				n.setLight(bank, i, ndef);
				it->block->setNodeNoCheck(it->rel_position, n);
			}
		}
		// Spread lights.
		spread_light(map, ndef, bank, light_sources, modified_blocks);
	}
}

/*!
 * Borders of a map block in relative node coordinates.
 * Compatible with type 'direction'.
 */
const VoxelArea block_borders[] = {
	VoxelArea(v3s16(15, 0, 0), v3s16(15, 15, 15)), //X+
	VoxelArea(v3s16(0, 15, 0), v3s16(15, 15, 15)), //Y+
	VoxelArea(v3s16(0, 0, 15), v3s16(15, 15, 15)), //Z+
	VoxelArea(v3s16(0, 0, 0), v3s16(15, 15, 0)),   //Z-
	VoxelArea(v3s16(0, 0, 0), v3s16(15, 0, 15)),   //Y-
	VoxelArea(v3s16(0, 0, 0), v3s16(0, 15, 15))    //X-
};

/*!
 * Returns true if:
 * -the node has unloaded neighbors
 * -the node doesn't have light
 * -the node's light is the same as the maximum of
 * its light source and its brightest neighbor minus one.
 * .
 */
bool is_light_locally_correct(Map *map, INodeDefManager *ndef, LightBank bank,
	v3s16 pos)
{
	bool is_valid_position;
	MapNode n = map->getNodeNoEx(pos, &is_valid_position);
	const ContentFeatures &f = ndef->get(n);
	if (f.param_type != CPT_LIGHT) {
		return true;
	}
	u8 light = n.getLightNoChecks(bank, &f);
	assert(f.light_source <= LIGHT_MAX);
	u8 brightest_neighbor = f.light_source + 1;
	for (direction d = 0; d < 6; ++d) {
		MapNode n2 = map->getNodeNoEx(pos + neighbor_dirs[d],
			&is_valid_position);
		u8 light2 = n2.getLight(bank, ndef);
		if (brightest_neighbor < light2) {
			brightest_neighbor = light2;
		}
	}
	assert(light <= LIGHT_SUN);
	return brightest_neighbor == light + 1;
}

void update_block_border_lighting(Map *map, MapBlock *block,
	std::map<v3s16, MapBlock*> &modified_blocks)
{
	INodeDefManager *ndef = map->getNodeDefManager();
	bool is_valid_position;
	for (s32 i = 0; i < 2; i++) {
		LightBank bank = banks[i];
		// Since invalid light is not common, do not allocate
		// memory if not needed.
		UnlightQueue disappearing_lights(0);
		ReLightQueue light_sources(0);
		// Get incorrect lights
		for (direction d = 0; d < 6; d++) {
			// For each direction
			// Get neighbor block
			v3s16 otherpos = block->getPos() + neighbor_dirs[d];
			MapBlock *other = map->getBlockNoCreateNoEx(otherpos);
			if (other == NULL) {
				continue;
			}
			// Only update if lighting was not completed.
			if (block->isLightingComplete(bank, d) &&
					other->isLightingComplete(bank, 5 - d))
				continue;
			// Reset flags
			block->setLightingComplete(bank, d, true);
			other->setLightingComplete(bank, 5 - d, true);
			// The two blocks and their connecting surfaces
			MapBlock *blocks[] = {block, other};
			VoxelArea areas[] = {block_borders[d], block_borders[5 - d]};
			// For both blocks
			for (u8 blocknum = 0; blocknum < 2; blocknum++) {
				MapBlock *b = blocks[blocknum];
				VoxelArea a = areas[blocknum];
				// For all nodes
				for (s32 x = a.MinEdge.X; x <= a.MaxEdge.X; x++)
				for (s32 z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++)
				for (s32 y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
					MapNode n = b->getNodeNoCheck(x, y, z,
						&is_valid_position);
					u8 light = n.getLight(bank, ndef);
					// Sunlight is fixed
					if (light < LIGHT_SUN) {
						// Unlight if not correct
						if (!is_light_locally_correct(map, ndef, bank,
								v3s16(x, y, z) + b->getPosRelative())) {
							// Initialize for unlighting
							n.setLight(bank, 0, ndef);
							b->setNodeNoCheck(x, y, z, n);
							modified_blocks[b->getPos()]=b;
							disappearing_lights.push(light,
								relative_v3(x, y, z), b->getPos(), b,
								6);
						}
					}
				}
			}
		}
		// Remove lights
		unspread_light(map, ndef, bank, disappearing_lights, light_sources,
			modified_blocks);
		// Initialize light values for light spreading.
		for (u8 i = 0; i <= LIGHT_SUN; i++) {
			const std::vector<ChangingLight> &lights = light_sources.lights[i];
			for (std::vector<ChangingLight>::const_iterator it = lights.begin();
					it < lights.end(); ++it) {
				MapNode n = it->block->getNodeNoCheck(it->rel_position,
					&is_valid_position);
				n.setLight(bank, i, ndef);
				it->block->setNodeNoCheck(it->rel_position, n);
			}
		}
		// Spread lights.
		spread_light(map, ndef, bank, light_sources, modified_blocks);
	}
}

/*!
 * Resets the lighting of the given VoxelManipulator to
 * complete darkness and full sunlight.
 * Operates in one map sector.
 *
 * \param offset contains the least x and z node coordinates
 * of the map sector.
 * \param light incoming sunlight, light[x][z] is true if there
 * is sunlight above the voxel manipulator at the given x-z coordinates.
 * The array's indices are relative node coordinates in the sector.
 * After the procedure returns, this contains outgoing light at
 * the bottom of the voxel manipulator.
 */
void fill_with_sunlight(MMVManip *vm, INodeDefManager *ndef, v2s16 offset,
	bool light[MAP_BLOCKSIZE][MAP_BLOCKSIZE])
{
	// Distance in array between two nodes on top of each other.
	s16 ystride = vm->m_area.getExtent().X;
	// Cache the ignore node.
	MapNode ignore = MapNode(CONTENT_IGNORE);
	// For each column of nodes:
	for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
	for (s16 x = 0; x < MAP_BLOCKSIZE; x++) {
		// Position of the column on the map.
		v2s16 realpos = offset + v2s16(x, z);
		// Array indices in the voxel manipulator
		s32 maxindex = vm->m_area.index(realpos.X, vm->m_area.MaxEdge.Y,
			realpos.Y);
		s32 minindex = vm->m_area.index(realpos.X, vm->m_area.MinEdge.Y,
			realpos.Y);
		// True if the current node has sunlight.
		bool lig = light[z][x];
		// For each node, downwards:
		for (s32 i = maxindex; i >= minindex; i -= ystride) {
			MapNode *n;
			if (vm->m_flags[i] & VOXELFLAG_NO_DATA)
				n = &ignore;
			else
				n = &vm->m_data[i];
			// Ignore IGNORE nodes, these are not generated yet.
			if(n->getContent() == CONTENT_IGNORE)
				continue;
			const ContentFeatures &f = ndef->get(n->getContent());
			if (lig && !f.sunlight_propagates)
				// Sunlight is stopped.
				lig = false;
			// Reset light
			n->setLight(LIGHTBANK_DAY, lig ? 15 : 0, f);
			n->setLight(LIGHTBANK_NIGHT, 0, f);
		}
		// Output outgoing light.
		light[z][x] = lig;
	}
}

/*!
 * Returns incoming sunlight for one map block.
 * If block above is not found, it is loaded.
 *
 * \param pos position of the map block that gets the sunlight.
 * \param light incoming sunlight, light[z][x] is true if there
 * is sunlight above the block at the given z-x relative
 * node coordinates.
 */
void is_sunlight_above_block(ServerMap *map, mapblock_v3 pos,
	INodeDefManager *ndef, bool light[MAP_BLOCKSIZE][MAP_BLOCKSIZE])
{
	mapblock_v3 source_block_pos = pos + v3s16(0, 1, 0);
	// Get or load source block.
	// It might take a while to load, but correcting incorrect
	// sunlight may be even slower.
	MapBlock *source_block = map->emergeBlock(source_block_pos, false);
	// Trust only generated blocks.
	if (source_block == NULL || source_block->isDummy()
			|| !source_block->isGenerated()) {
		// But if there is no block above, then use heuristics
		bool sunlight = true;
		MapBlock *node_block = map->getBlockNoCreateNoEx(pos);
		if (node_block == NULL)
			// This should not happen.
			sunlight = false;
		else
			sunlight = !node_block->getIsUnderground();
		for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
		for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
			light[z][x] = sunlight;
	} else {
		// Dummy boolean, the position is valid.
		bool is_valid_position;
		// For each column:
		for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
		for (s16 x = 0; x < MAP_BLOCKSIZE; x++) {
			// Get the bottom block.
			MapNode above = source_block->getNodeNoCheck(x, 0, z,
				&is_valid_position);
			light[z][x] = above.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN;
		}
	}
}

/*!
 * Propagates sunlight down in a given map block.
 *
 * \param data contains incoming sunlight and shadow and
 * the coordinates of the target block.
 * \param unlight propagated shadow is inserted here
 * \param relight propagated sunlight is inserted here
 *
 * \returns true if the block was modified, false otherwise.
 */
bool propagate_block_sunlight(Map *map, INodeDefManager *ndef,
	SunlightPropagationData *data, UnlightQueue *unlight, ReLightQueue *relight)
{
	bool modified = false;
	// Get the block.
	MapBlock *block = map->getBlockNoCreateNoEx(data->target_block);
	if (block == NULL || block->isDummy()) {
		// The work is done if the block does not contain data.
		data->data.clear();
		return false;
	}
	// Dummy boolean
	bool is_valid;
	// For each changing column of nodes:
	size_t index;
	for (index = 0; index < data->data.size(); index++) {
		SunlightPropagationUnit it = data->data[index];
		// Relative position of the currently inspected node.
		relative_v3 current_pos(it.relative_pos.X, MAP_BLOCKSIZE - 1,
			it.relative_pos.Y);
		if (it.is_sunlit) {
			// Propagate sunlight.
			// For each node downwards:
			for (; current_pos.Y >= 0; current_pos.Y--) {
				MapNode n = block->getNodeNoCheck(current_pos, &is_valid);
				const ContentFeatures &f = ndef->get(n);
				if (n.getLightRaw(LIGHTBANK_DAY, f) < LIGHT_SUN
						&& f.sunlight_propagates) {
					// This node gets sunlight.
					n.setLight(LIGHTBANK_DAY, LIGHT_SUN, f);
					block->setNodeNoCheck(current_pos, n);
					modified = true;
					relight->push(LIGHT_SUN, current_pos, data->target_block,
						block, 4);
				} else {
					// Light already valid, propagation stopped.
					break;
				}
			}
		} else {
			// Propagate shadow.
			// For each node downwards:
			for (; current_pos.Y >= 0; current_pos.Y--) {
				MapNode n = block->getNodeNoCheck(current_pos, &is_valid);
				const ContentFeatures &f = ndef->get(n);
				if (n.getLightRaw(LIGHTBANK_DAY, f) == LIGHT_SUN) {
					// The sunlight is no longer valid.
					n.setLight(LIGHTBANK_DAY, 0, f);
					block->setNodeNoCheck(current_pos, n);
					modified = true;
					unlight->push(LIGHT_SUN, current_pos, data->target_block,
						block, 4);
				} else {
					// Reached shadow, propagation stopped.
					break;
				}
			}
		}
		if (current_pos.Y >= 0) {
			// Propagation stopped, remove from data.
			data->data[index] = data->data.back();
			data->data.pop_back();
			index--;
		}
	}
	return modified;
}

/*!
 * Borders of a map block in relative node coordinates.
 * The areas do not overlap.
 * Compatible with type 'direction'.
 */
const VoxelArea block_pad[] = {
	VoxelArea(v3s16(15, 0, 0), v3s16(15, 15, 15)), //X+
	VoxelArea(v3s16(1, 15, 0), v3s16(14, 15, 15)), //Y+
	VoxelArea(v3s16(1, 1, 15), v3s16(14, 14, 15)), //Z+
	VoxelArea(v3s16(1, 1, 0), v3s16(14, 14, 0)),   //Z-
	VoxelArea(v3s16(1, 0, 0), v3s16(14, 0, 15)),   //Y-
	VoxelArea(v3s16(0, 0, 0), v3s16(0, 15, 15))    //X-
};

/*!
 * The common part of bulk light updates - it is always executed.
 * The procedure takes the nodes that should be unlit, and the
 * full modified area.
 *
 * The procedure handles the correction of all lighting except
 * direct sunlight spreading.
 *
 * \param minblock least coordinates of the changed area in block
 * coordinates
 * \param maxblock greatest coordinates of the changed area in block
 * coordinates
 * \param unlight the first queue is for day light, the second is for
 * night light. Contains all nodes on the borders that need to be unlit.
 * \param relight the first queue is for day light, the second is for
 * night light. Contains nodes that were not modified, but got sunlight
 * because the changes.
 * \param modified_blocks the procedure adds all modified blocks to
 * this map
 */
void finish_bulk_light_update(Map *map, mapblock_v3 minblock,
	mapblock_v3 maxblock, UnlightQueue unlight[2], ReLightQueue relight[2],
	std::map<v3s16, MapBlock*> *modified_blocks)
{
	INodeDefManager *ndef = map->getNodeDefManager();
	// dummy boolean
	bool is_valid;

	// --- STEP 1: Do unlighting

	for (size_t bank = 0; bank < 2; bank++) {
		LightBank b = banks[bank];
		unspread_light(map, ndef, b, unlight[bank], relight[bank],
			*modified_blocks);
	}

	// --- STEP 2: Get all newly inserted light sources

	// For each block:
	for (s16 b_x = minblock.X; b_x <= maxblock.X; b_x++)
	for (s16 b_y = minblock.Y; b_y <= maxblock.Y; b_y++)
	for (s16 b_z = minblock.Z; b_z <= maxblock.Z; b_z++) {
		const v3s16 blockpos(b_x, b_y, b_z);
		MapBlock *block = map->getBlockNoCreateNoEx(blockpos);
		if (!block || block->isDummy())
			// Skip not existing blocks
			continue;
		// For each node in the block:
		for (s32 x = 0; x < MAP_BLOCKSIZE; x++)
		for (s32 z = 0; z < MAP_BLOCKSIZE; z++)
		for (s32 y = 0; y < MAP_BLOCKSIZE; y++) {
			v3s16 relpos(x, y, z);
			MapNode node = block->getNodeNoCheck(x, y, z, &is_valid);
			const ContentFeatures &f = ndef->get(node);
			// For each light bank
			for (size_t b = 0; b < 2; b++) {
				LightBank bank = banks[b];
				u8 light = f.param_type == CPT_LIGHT ?
					node.getLightNoChecks(bank, &f):
					f.light_source;
				if (light > 1)
					relight[b].push(light, relpos, blockpos, block, 6);
			} // end of banks
		} // end of nodes
	} // end of blocks

	// --- STEP 3: do light spreading

	// For each light bank:
	for (size_t b = 0; b < 2; b++) {
		LightBank bank = banks[b];
		// Sunlight is already initialized.
		u8 maxlight = (b == 0) ? LIGHT_MAX : LIGHT_SUN;
		// Initialize light values for light spreading.
		for (u8 i = 0; i <= maxlight; i++) {
			const std::vector<ChangingLight> &lights = relight[b].lights[i];
			for (std::vector<ChangingLight>::const_iterator it = lights.begin();
					it < lights.end(); ++it) {
				MapNode n = it->block->getNodeNoCheck(it->rel_position,
					&is_valid);
				n.setLight(bank, i, ndef);
				it->block->setNodeNoCheck(it->rel_position, n);
			}
		}
		// Spread lights.
		spread_light(map, ndef, bank, relight[b], *modified_blocks);
	}
}

void blit_back_with_light(ServerMap *map, MMVManip *vm,
	std::map<v3s16, MapBlock*> *modified_blocks)
{
	INodeDefManager *ndef = map->getNodeDefManager();
	mapblock_v3 minblock = getNodeBlockPos(vm->m_area.MinEdge);
	mapblock_v3 maxblock = getNodeBlockPos(vm->m_area.MaxEdge);
	// First queue is for day light, second is for night light.
	UnlightQueue unlight[] = { UnlightQueue(256), UnlightQueue(256) };
	ReLightQueue relight[] = { ReLightQueue(256), ReLightQueue(256) };
	// Will hold sunlight data.
	bool lights[MAP_BLOCKSIZE][MAP_BLOCKSIZE];
	SunlightPropagationData data;
	// Dummy boolean.
	bool is_valid;

	// --- STEP 1: reset everything to sunlight

	// For each map block:
	for (s16 x = minblock.X; x <= maxblock.X; x++)
	for (s16 z = minblock.Z; z <= maxblock.Z; z++) {
		// Extract sunlight above.
		is_sunlight_above_block(map, v3s16(x, maxblock.Y, z), ndef, lights);
		v2s16 offset(x, z);
		offset *= MAP_BLOCKSIZE;
		// Reset the voxel manipulator.
		fill_with_sunlight(vm, ndef, offset, lights);
		// Copy sunlight data
		data.target_block = v3s16(x, minblock.Y - 1, z);
		for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
		for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
			data.data.push_back(
				SunlightPropagationUnit(v2s16(x, z), lights[z][x]));
		// Propagate sunlight and shadow below the voxel manipulator.
		while (!data.data.empty()) {
			if (propagate_block_sunlight(map, ndef, &data, &unlight[0],
					&relight[0]))
				(*modified_blocks)[data.target_block] =
					map->getBlockNoCreateNoEx(data.target_block);
			// Step downwards.
			data.target_block.Y--;
		}
	}

	// --- STEP 2: Get nodes from borders to unlight

	// In case there are unloaded holes in the voxel manipulator
	// unlight each block.
	// For each block:
	for (s16 b_x = minblock.X; b_x <= maxblock.X; b_x++)
	for (s16 b_y = minblock.Y; b_y <= maxblock.Y; b_y++)
	for (s16 b_z = minblock.Z; b_z <= maxblock.Z; b_z++) {
		v3s16 blockpos(b_x, b_y, b_z);
		MapBlock *block = map->getBlockNoCreateNoEx(blockpos);
		if (!block || block->isDummy())
			// Skip not existing blocks.
			continue;
		v3s16 offset = block->getPosRelative();
		// For each border of the block:
		for (direction d = 0; d < 6; d++) {
			VoxelArea a = block_pad[d];
			// For each node of the border:
			for (s32 x = a.MinEdge.X; x <= a.MaxEdge.X; x++)
			for (s32 z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++)
			for (s32 y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
				v3s16 relpos(x, y, z);
				// Get old and new node
				MapNode oldnode = block->getNodeNoCheck(x, y, z, &is_valid);
				const ContentFeatures &oldf = ndef->get(oldnode);
				MapNode newnode = vm->getNodeNoExNoEmerge(relpos + offset);
				const ContentFeatures &newf = ndef->get(newnode);
				// For each light bank
				for (size_t b = 0; b < 2; b++) {
					LightBank bank = banks[b];
					u8 oldlight = oldf.param_type == CPT_LIGHT ?
						oldnode.getLightNoChecks(bank, &oldf):
						LIGHT_SUN; // no light information, force unlighting
					u8 newlight = newf.param_type == CPT_LIGHT ?
						newnode.getLightNoChecks(bank, &newf):
						newf.light_source;
					// If the new node is dimmer, unlight.
					if (oldlight > newlight) {
						unlight[b].push(
							oldlight, relpos, blockpos, block, 6);
					}
				} // end of banks
			} // end of nodes
		} // end of borders
	} // end of blocks

	// --- STEP 3: All information extracted, overwrite

	vm->blitBackAll(modified_blocks, true);

	// --- STEP 4: Finish light update

	finish_bulk_light_update(map, minblock, maxblock, unlight, relight,
		modified_blocks);
}

/*!
 * Resets the lighting of the given map block to
 * complete darkness and full sunlight.
 *
 * \param light incoming sunlight, light[x][z] is true if there
 * is sunlight above the map block at the given x-z coordinates.
 * The array's indices are relative node coordinates in the block.
 * After the procedure returns, this contains outgoing light at
 * the bottom of the map block.
 */
void fill_with_sunlight(MapBlock *block, INodeDefManager *ndef,
	bool light[MAP_BLOCKSIZE][MAP_BLOCKSIZE])
{
	if (block->isDummy())
		return;
	// dummy boolean
	bool is_valid;
	// For each column of nodes:
	for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
	for (s16 x = 0; x < MAP_BLOCKSIZE; x++) {
		// True if the current node has sunlight.
		bool lig = light[z][x];
		// For each node, downwards:
		for (s16 y = MAP_BLOCKSIZE - 1; y >= 0; y--) {
			MapNode n = block->getNodeNoCheck(x, y, z, &is_valid);
			// Ignore IGNORE nodes, these are not generated yet.
			if (n.getContent() == CONTENT_IGNORE)
				continue;
			const ContentFeatures &f = ndef->get(n.getContent());
			if (lig && !f.sunlight_propagates) {
				// Sunlight is stopped.
				lig = false;
			}
			// Reset light
			n.setLight(LIGHTBANK_DAY, lig ? 15 : 0, f);
			n.setLight(LIGHTBANK_NIGHT, 0, f);
			block->setNodeNoCheck(x, y, z, n);
		}
		// Output outgoing light.
		light[z][x] = lig;
	}
}

void repair_block_light(ServerMap *map, MapBlock *block,
	std::map<v3s16, MapBlock*> *modified_blocks)
{
	if (!block || block->isDummy())
		return;
	INodeDefManager *ndef = map->getNodeDefManager();
	// First queue is for day light, second is for night light.
	UnlightQueue unlight[] = { UnlightQueue(256), UnlightQueue(256) };
	ReLightQueue relight[] = { ReLightQueue(256), ReLightQueue(256) };
	// Will hold sunlight data.
	bool lights[MAP_BLOCKSIZE][MAP_BLOCKSIZE];
	SunlightPropagationData data;
	// Dummy boolean.
	bool is_valid;

	// --- STEP 1: reset everything to sunlight

	mapblock_v3 blockpos = block->getPos();
	(*modified_blocks)[blockpos] = block;
	// For each map block:
	// Extract sunlight above.
	is_sunlight_above_block(map, blockpos, ndef, lights);
	// Reset the voxel manipulator.
	fill_with_sunlight(block, ndef, lights);
	// Copy sunlight data
	data.target_block = v3s16(blockpos.X, blockpos.Y - 1, blockpos.Z);
	for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
	for (s16 x = 0; x < MAP_BLOCKSIZE; x++) {
		data.data.push_back(
			SunlightPropagationUnit(v2s16(x, z), lights[z][x]));
	}
	// Propagate sunlight and shadow below the voxel manipulator.
	while (!data.data.empty()) {
		if (propagate_block_sunlight(map, ndef, &data, &unlight[0],
				&relight[0]))
			(*modified_blocks)[data.target_block] =
				map->getBlockNoCreateNoEx(data.target_block);
		// Step downwards.
		data.target_block.Y--;
	}

	// --- STEP 2: Get nodes from borders to unlight

	// For each border of the block:
	for (direction d = 0; d < 6; d++) {
		VoxelArea a = block_pad[d];
		// For each node of the border:
		for (s32 x = a.MinEdge.X; x <= a.MaxEdge.X; x++)
		for (s32 z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++)
		for (s32 y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
			v3s16 relpos(x, y, z);
			// Get node
			MapNode node = block->getNodeNoCheck(x, y, z, &is_valid);
			const ContentFeatures &f = ndef->get(node);
			// For each light bank
			for (size_t b = 0; b < 2; b++) {
				LightBank bank = banks[b];
				u8 light = f.param_type == CPT_LIGHT ?
					node.getLightNoChecks(bank, &f):
					f.light_source;
				// If the new node is dimmer than sunlight, unlight.
				// (if it has maximal light, it is pointless to remove
				// surrounding light, as it can only become brighter)
				if (LIGHT_SUN > light) {
					unlight[b].push(
						LIGHT_SUN, relpos, blockpos, block, 6);
				}
			} // end of banks
		} // end of nodes
	} // end of borders

	// STEP 3: Remove and spread light

	finish_bulk_light_update(map, blockpos, blockpos, unlight, relight,
		modified_blocks);
}

VoxelLineIterator::VoxelLineIterator(
	const v3f &start_position,
	const v3f &line_vector) :
	m_start_position(start_position),
	m_line_vector(line_vector),
	m_next_intersection_multi(10000.0f, 10000.0f, 10000.0f),
	m_intersection_multi_inc(10000.0f, 10000.0f, 10000.0f),
	m_step_directions(1.0f, 1.0f, 1.0f)
{
	m_current_node_pos = floatToInt(m_start_position, 1);

	if (m_line_vector.X > 0) {
		m_next_intersection_multi.X = (floorf(m_start_position.X - 0.5) + 1.5
			- m_start_position.X) / m_line_vector.X;
		m_intersection_multi_inc.X = 1 / m_line_vector.X;
	} else if (m_line_vector.X < 0) {
		m_next_intersection_multi.X = (floorf(m_start_position.X - 0.5)
			- m_start_position.X + 0.5) / m_line_vector.X;
		m_intersection_multi_inc.X = -1 / m_line_vector.X;
		m_step_directions.X = -1;
	}

	if (m_line_vector.Y > 0) {
		m_next_intersection_multi.Y = (floorf(m_start_position.Y - 0.5) + 1.5
			- m_start_position.Y) / m_line_vector.Y;
		m_intersection_multi_inc.Y = 1 / m_line_vector.Y;
	} else if (m_line_vector.Y < 0) {
		m_next_intersection_multi.Y = (floorf(m_start_position.Y - 0.5)
			- m_start_position.Y + 0.5) / m_line_vector.Y;
		m_intersection_multi_inc.Y = -1 / m_line_vector.Y;
		m_step_directions.Y = -1;
	}

	if (m_line_vector.Z > 0) {
		m_next_intersection_multi.Z = (floorf(m_start_position.Z - 0.5) + 1.5
			- m_start_position.Z) / m_line_vector.Z;
		m_intersection_multi_inc.Z = 1 / m_line_vector.Z;
	} else if (m_line_vector.Z < 0) {
		m_next_intersection_multi.Z = (floorf(m_start_position.Z - 0.5)
			- m_start_position.Z + 0.5) / m_line_vector.Z;
		m_intersection_multi_inc.Z = -1 / m_line_vector.Z;
		m_step_directions.Z = -1;
	}

	m_has_next = (m_next_intersection_multi.X <= 1)
		|| (m_next_intersection_multi.Y <= 1)
		|| (m_next_intersection_multi.Z <= 1);
}

void VoxelLineIterator::next()
{
	if ((m_next_intersection_multi.X < m_next_intersection_multi.Y)
			&& (m_next_intersection_multi.X < m_next_intersection_multi.Z)) {
		m_next_intersection_multi.X += m_intersection_multi_inc.X;
		m_current_node_pos.X += m_step_directions.X;
	} else if ((m_next_intersection_multi.Y < m_next_intersection_multi.Z)) {
		m_next_intersection_multi.Y += m_intersection_multi_inc.Y;
		m_current_node_pos.Y += m_step_directions.Y;
	} else {
		m_next_intersection_multi.Z += m_intersection_multi_inc.Z;
		m_current_node_pos.Z += m_step_directions.Z;
	}

	m_has_next = (m_next_intersection_multi.X <= 1)
			|| (m_next_intersection_multi.Y <= 1)
			|| (m_next_intersection_multi.Z <= 1);
}

} // namespace voxalgo


code:
/*
Minetest
Copyright (C) 2010-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 VOXELALGORITHMS_HEADER
#define VOXELALGORITHMS_HEADER

#include "voxel.h"
#include "mapnode.h"
#include "util/container.h"
#include "util/cpp11_container.h"

class Map;
class ServerMap;
class MapBlock;
class MMVManip;

namespace voxalgo
{

// TODO: Move unspreadLight and spreadLight from VoxelManipulator to here

void setLight(VoxelManipulator &v, VoxelArea a, u8 light,
		INodeDefManager *ndef);

void clearLightAndCollectSources(VoxelManipulator &v, VoxelArea a,
		enum LightBank bank, INodeDefManager *ndef,
		std::set<v3s16> & light_sources,
		std::map<v3s16, u8> & unlight_from);

struct SunlightPropagateResult
{
	bool bottom_sunlight_valid;

	SunlightPropagateResult(bool bottom_sunlight_valid_):
		bottom_sunlight_valid(bottom_sunlight_valid_)
	{}
};

SunlightPropagateResult propagateSunlight(VoxelManipulator &v, VoxelArea a,
		bool inexistent_top_provides_sunlight,
		std::set<v3s16> & light_sources,
		INodeDefManager *ndef);

/*!
 * Updates the lighting on the map.
 * The result will be correct only if
 * no nodes were changed except the given ones.
 * Before calling this procedure make sure that all new nodes on
 * the map have zero light level!
 *
 * \param oldnodes contains the MapNodes that were replaced by the new
 * MapNodes and their positions
 * \param modified_blocks output, contains all map blocks that
 * the function modified
 */
void update_lighting_nodes(
	Map *map,
	std::vector<std::pair<v3s16, MapNode> > &oldnodes,
	std::map<v3s16, MapBlock*> &modified_blocks);

/*!
 * Updates borders of the given mapblock.
 * Only updates if the block was marked with incomplete
 * lighting and the neighbor is also loaded.
 *
 * \param block the block to update
 * \param modified_blocks output, contains all map blocks that
 * the function modified
 */
void update_block_border_lighting(Map *map, MapBlock *block,
	std::map<v3s16, MapBlock*> &modified_blocks);

/*!
 * Copies back nodes from a voxel manipulator
 * to the map and updates lighting.
 * For server use only.
 *
 * \param modified_blocks output, contains all map blocks that
 * the function modified
 */
void blit_back_with_light(ServerMap *map, MMVManip *vm,
	std::map<v3s16, MapBlock*> *modified_blocks);

/*!
 * Corrects the light in a map block.
 * For server use only.
 *
 * \param block the block to update
 */
void repair_block_light(ServerMap *map, MapBlock *block,
	std::map<v3s16, MapBlock*> *modified_blocks);

/*!
 * This class iterates trough voxels that intersect with
 * a line. The collision detection does not see nodeboxes,
 * every voxel is a cube and is returned.
 * This iterator steps to all nodes exactly once.
 */
struct VoxelLineIterator
{
public:
	//! Starting position of the line in world coordinates.
	v3f m_start_position;
	//! Direction and length of the line in world coordinates.
	v3f m_line_vector;
	/*!
	 * Each component stores the next smallest positive number, by
	 * which multiplying the line's vector gives a vector that ends
	 * on the intersection of two nodes.
	 */
	v3f m_next_intersection_multi;
	/*!
	 * Each component stores the smallest positive number, by which
	 * m_next_intersection_multi's components can be increased.
	 */
	v3f m_intersection_multi_inc;
	/*!
	 * Direction of the line. Each component can be -1 or 1 (if a
	 * component of the line's vector is 0, then there will be 1).
	 */
	v3s16 m_step_directions;
	//! Position of the current node.
	v3s16 m_current_node_pos;
	//! If true, the next node will intersect the line, too.
	bool m_has_next;

	/*!
	 * Creates a voxel line iterator with the given line.
	 * @param start_position starting point of the line
	 * in voxel coordinates
	 * @param line_vector    length and direction of the
	 * line in voxel coordinates. start_position+line_vector
	 * is the end of the line
	 */
	VoxelLineIterator(const v3f &start_position,const v3f &line_vector);

	/*!
	 * Steps to the next voxel.
	 * Updates m_current_node_pos and
	 * m_previous_node_pos.
	 * Note that it works even if hasNext() is false,
	 * continuing the line as a ray.
	 */
	void next();

	/*!
	 * Returns true if the next voxel intersects the given line.
	 */
	inline bool hasNext() const { return m_has_next; }
};

} // namespace voxalgo

#endif

repo_name: pgimeno/minetest

license: unknown


code:
/*
Minetest
Copyright (C) 2010-2014 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.
*/

#include "settings.h"
#include "wieldmesh.h"
#include "inventory.h"
#include "client.h"
#include "itemdef.h"
#include "nodedef.h"
#include "mesh.h"
#include "mapblock_mesh.h"
#include "client/tile.h"
#include "log.h"
#include "util/numeric.h"
#include <map>
#include <IMeshManipulator.h>

#define WIELD_SCALE_FACTOR 30.0
#define WIELD_SCALE_FACTOR_EXTRUDED 40.0

#define MIN_EXTRUSION_MESH_RESOLUTION 16
#define MAX_EXTRUSION_MESH_RESOLUTION 512

static scene::IMesh *createExtrusionMesh(int resolution_x, int resolution_y)
{
	const f32 r = 0.5;

	scene::IMeshBuffer *buf = new scene::SMeshBuffer();
	video::SColor c(255,255,255,255);
	v3f scale(1.0, 1.0, 0.1);

	// Front and back
	{
		video::S3DVertex vertices[8] = {
			// z-
			video::S3DVertex(-r,+r,-r, 0,0,-1, c, 0,0),
			video::S3DVertex(+r,+r,-r, 0,0,-1, c, 1,0),
			video::S3DVertex(+r,-r,-r, 0,0,-1, c, 1,1),
			video::S3DVertex(-r,-r,-r, 0,0,-1, c, 0,1),
			// z+
			video::S3DVertex(-r,+r,+r, 0,0,+1, c, 0,0),
			video::S3DVertex(-r,-r,+r, 0,0,+1, c, 0,1),
			video::S3DVertex(+r,-r,+r, 0,0,+1, c, 1,1),
			video::S3DVertex(+r,+r,+r, 0,0,+1, c, 1,0),
		};
		u16 indices[12] = {0,1,2,2,3,0,4,5,6,6,7,4};
		buf->append(vertices, 8, indices, 12);
	}

	f32 pixelsize_x = 1 / (f32) resolution_x;
	f32 pixelsize_y = 1 / (f32) resolution_y;

	for (int i = 0; i < resolution_x; ++i) {
		f32 pixelpos_x = i * pixelsize_x - 0.5;
		f32 x0 = pixelpos_x;
		f32 x1 = pixelpos_x + pixelsize_x;
		f32 tex0 = (i + 0.1) * pixelsize_x;
		f32 tex1 = (i + 0.9) * pixelsize_x;
		video::S3DVertex vertices[8] = {
			// x-
			video::S3DVertex(x0,-r,-r, -1,0,0, c, tex0,1),
			video::S3DVertex(x0,-r,+r, -1,0,0, c, tex1,1),
			video::S3DVertex(x0,+r,+r, -1,0,0, c, tex1,0),
			video::S3DVertex(x0,+r,-r, -1,0,0, c, tex0,0),
			// x+
			video::S3DVertex(x1,-r,-r, +1,0,0, c, tex0,1),
			video::S3DVertex(x1,+r,-r, +1,0,0, c, tex0,0),
			video::S3DVertex(x1,+r,+r, +1,0,0, c, tex1,0),
			video::S3DVertex(x1,-r,+r, +1,0,0, c, tex1,1),
		};
		u16 indices[12] = {0,1,2,2,3,0,4,5,6,6,7,4};
		buf->append(vertices, 8, indices, 12);
	}
	for (int i = 0; i < resolution_y; ++i) {
		f32 pixelpos_y = i * pixelsize_y - 0.5;
		f32 y0 = -pixelpos_y - pixelsize_y;
		f32 y1 = -pixelpos_y;
		f32 tex0 = (i + 0.1) * pixelsize_y;
		f32 tex1 = (i + 0.9) * pixelsize_y;
		video::S3DVertex vertices[8] = {
			// y-
			video::S3DVertex(-r,y0,-r, 0,-1,0, c, 0,tex0),
			video::S3DVertex(+r,y0,-r, 0,-1,0, c, 1,tex0),
			video::S3DVertex(+r,y0,+r, 0,-1,0, c, 1,tex1),
			video::S3DVertex(-r,y0,+r, 0,-1,0, c, 0,tex1),
			// y+
			video::S3DVertex(-r,y1,-r, 0,+1,0, c, 0,tex0),
			video::S3DVertex(-r,y1,+r, 0,+1,0, c, 0,tex1),
			video::S3DVertex(+r,y1,+r, 0,+1,0, c, 1,tex1),
			video::S3DVertex(+r,y1,-r, 0,+1,0, c, 1,tex0),
		};
		u16 indices[12] = {0,1,2,2,3,0,4,5,6,6,7,4};
		buf->append(vertices, 8, indices, 12);
	}

	// Create mesh object
	scene::SMesh *mesh = new scene::SMesh();
	mesh->addMeshBuffer(buf);
	buf->drop();
	scaleMesh(mesh, scale);  // also recalculates bounding box
	return mesh;
}

/*
	Caches extrusion meshes so that only one of them per resolution
	is needed. Also caches one cube (for convenience).

	E.g. there is a single extrusion mesh that is used for all
	16x16 px images, another for all 256x256 px images, and so on.

	WARNING: Not thread safe. This should not be a problem since
	rendering related classes (such as WieldMeshSceneNode) will be
	used from the rendering thread only.
*/
class ExtrusionMeshCache: public IReferenceCounted
{
public:
	// Constructor
	ExtrusionMeshCache()
	{
		for (int resolution = MIN_EXTRUSION_MESH_RESOLUTION;
				resolution <= MAX_EXTRUSION_MESH_RESOLUTION;
				resolution *= 2) {
			m_extrusion_meshes[resolution] =
				createExtrusionMesh(resolution, resolution);
		}
		m_cube = createCubeMesh(v3f(1.0, 1.0, 1.0));
	}
	// Destructor
	virtual ~ExtrusionMeshCache()
	{
		for (std::map<int, scene::IMesh*>::iterator
				it = m_extrusion_meshes.begin();
				it != m_extrusion_meshes.end(); ++it) {
			it->second->drop();
		}
		m_cube->drop();
	}
	// Get closest extrusion mesh for given image dimensions
	// Caller must drop the returned pointer
	scene::IMesh* create(core::dimension2d<u32> dim)
	{
		// handle non-power of two textures inefficiently without cache
		if (!is_power_of_two(dim.Width) || !is_power_of_two(dim.Height)) {
			return createExtrusionMesh(dim.Width, dim.Height);
		}

		int maxdim = MYMAX(dim.Width, dim.Height);

		std::map<int, scene::IMesh*>::iterator
			it = m_extrusion_meshes.lower_bound(maxdim);

		if (it == m_extrusion_meshes.end()) {
			// no viable resolution found; use largest one
			it = m_extrusion_meshes.find(MAX_EXTRUSION_MESH_RESOLUTION);
			sanity_check(it != m_extrusion_meshes.end());
		}

		scene::IMesh *mesh = it->second;
		mesh->grab();
		return mesh;
	}
	// Returns a 1x1x1 cube mesh with one meshbuffer (material) per face
	// Caller must drop the returned pointer
	scene::IMesh* createCube()
	{
		m_cube->grab();
		return m_cube;
	}

private:
	std::map<int, scene::IMesh*> m_extrusion_meshes;
	scene::IMesh *m_cube;
};

ExtrusionMeshCache *g_extrusion_mesh_cache = NULL;

WieldMeshSceneNode::WieldMeshSceneNode(
		scene::ISceneNode *parent,
		scene::ISceneManager *mgr,
		s32 id,
		bool lighting
):
	scene::ISceneNode(parent, mgr, id),
	m_meshnode(NULL),
	m_material_type(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF),
	m_lighting(lighting),
	m_bounding_box(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
{
	m_enable_shaders = g_settings->getBool("enable_shaders");
	m_anisotropic_filter = g_settings->getBool("anisotropic_filter");
	m_bilinear_filter = g_settings->getBool("bilinear_filter");
	m_trilinear_filter = g_settings->getBool("trilinear_filter");

	// If this is the first wield mesh scene node, create a cache
	// for extrusion meshes (and a cube mesh), otherwise reuse it
	if (g_extrusion_mesh_cache == NULL)
		g_extrusion_mesh_cache = new ExtrusionMeshCache();
	else
		g_extrusion_mesh_cache->grab();

	// Disable bounding box culling for this scene node
	// since we won't calculate the bounding box.
	setAutomaticCulling(scene::EAC_OFF);

	// Create the child scene node
	scene::IMesh *dummymesh = g_extrusion_mesh_cache->createCube();
	m_meshnode = SceneManager->addMeshSceneNode(dummymesh, this, -1);
	m_meshnode->setReadOnlyMaterials(false);
	m_meshnode->setVisible(false);
	dummymesh->drop(); // m_meshnode grabbed it
}

WieldMeshSceneNode::~WieldMeshSceneNode()
{
	sanity_check(g_extrusion_mesh_cache);
	if (g_extrusion_mesh_cache->drop())
		g_extrusion_mesh_cache = NULL;
}

void WieldMeshSceneNode::setCube(const ContentFeatures &f,
			v3f wield_scale, ITextureSource *tsrc)
{
	scene::IMesh *cubemesh = g_extrusion_mesh_cache->createCube();
	scene::SMesh *copy = cloneMesh(cubemesh);
	cubemesh->drop();
	postProcessNodeMesh(copy, f, false, true, &m_material_type, &m_colors);
	changeToMesh(copy);
	copy->drop();
	m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR);
}

void WieldMeshSceneNode::setExtruded(const std::string &imagename,
		v3f wield_scale, ITextureSource *tsrc, u8 num_frames)
{
	video::ITexture *texture = tsrc->getTexture(imagename);
	if (!texture) {
		changeToMesh(NULL);
		return;
	}

	core::dimension2d<u32> dim = texture->getSize();
	// Detect animation texture and pull off top frame instead of using entire thing
	if (num_frames > 1) {
		u32 frame_height = dim.Height / num_frames;
		dim = core::dimension2d<u32>(dim.Width, frame_height);
	}
	scene::IMesh *mesh = g_extrusion_mesh_cache->create(dim);
	scene::SMesh *copy = cloneMesh(mesh);
	mesh->drop();
	changeToMesh(copy);
	copy->drop();

	m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR_EXTRUDED);

	// Customize material
	video::SMaterial &material = m_meshnode->getMaterial(0);
	material.setTexture(0, tsrc->getTextureForMesh(imagename));
	material.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
	material.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
	material.MaterialType = m_material_type;
	material.setFlag(video::EMF_BACK_FACE_CULLING, true);
	// Enable bi/trilinear filtering only for high resolution textures
	if (dim.Width > 32) {
		material.setFlag(video::EMF_BILINEAR_FILTER, m_bilinear_filter);
		material.setFlag(video::EMF_TRILINEAR_FILTER, m_trilinear_filter);
	} else {
		material.setFlag(video::EMF_BILINEAR_FILTER, false);
		material.setFlag(video::EMF_TRILINEAR_FILTER, false);
	}
	material.setFlag(video::EMF_ANISOTROPIC_FILTER, m_anisotropic_filter);
	// mipmaps cause "thin black line" artifacts
#if (IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR >= 2
	material.setFlag(video::EMF_USE_MIP_MAPS, false);
#endif
	if (m_enable_shaders) {
		material.setTexture(2, tsrc->getShaderFlagsTexture(false));
	}
}

void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client)
{
	ITextureSource *tsrc = client->getTextureSource();
	IItemDefManager *idef = client->getItemDefManager();
	IShaderSource *shdrsrc = client->getShaderSource();
	INodeDefManager *ndef = client->getNodeDefManager();
	const ItemDefinition &def = item.getDefinition(idef);
	const ContentFeatures &f = ndef->get(def.name);
	content_t id = ndef->getId(def.name);

	if (m_enable_shaders) {
		u32 shader_id = shdrsrc->getShader("wielded_shader", TILE_MATERIAL_BASIC, NDT_NORMAL);
		m_material_type = shdrsrc->getShaderInfo(shader_id).material;
	}

	// Color-related
	m_colors.clear();
	m_base_color = idef->getItemstackColor(item, client);

	// If wield_image is defined, it overrides everything else
	if (def.wield_image != "") {
		setExtruded(def.wield_image, def.wield_scale, tsrc, 1);
		m_colors.push_back(ItemPartColor());
		return;
	}
	// Handle nodes
	// See also CItemDefManager::createClientCached()
	else if (def.type == ITEM_NODE) {
		if (f.mesh_ptr[0]) {
			// e.g. mesh nodes and nodeboxes
			scene::SMesh *mesh = cloneMesh(f.mesh_ptr[0]);
			postProcessNodeMesh(mesh, f, m_enable_shaders, true,
				&m_material_type, &m_colors);
			changeToMesh(mesh);
			mesh->drop();
			// mesh is pre-scaled by BS * f->visual_scale
			m_meshnode->setScale(
					def.wield_scale * WIELD_SCALE_FACTOR
					/ (BS * f.visual_scale));
		} else if (f.drawtype == NDT_AIRLIKE) {
			changeToMesh(NULL);
		} else if (f.drawtype == NDT_PLANTLIKE) {
			setExtruded(tsrc->getTextureName(f.tiles[0].layers[0].texture_id),
				def.wield_scale, tsrc,
				f.tiles[0].layers[0].animation_frame_count);
		} else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES) {
			setCube(f, def.wield_scale, tsrc);
		} else {
			MeshMakeData mesh_make_data(client, false);
			MapNode mesh_make_node(id, 255, 0);
			mesh_make_data.fillSingleNode(&mesh_make_node);
			MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0));
			scene::SMesh *mesh = cloneMesh(mapblock_mesh.getMesh());
			translateMesh(mesh, v3f(-BS, -BS, -BS));
			postProcessNodeMesh(mesh, f, m_enable_shaders, true,
				&m_material_type, &m_colors);
			changeToMesh(mesh);
			mesh->drop();
			m_meshnode->setScale(
					def.wield_scale * WIELD_SCALE_FACTOR
					/ (BS * f.visual_scale));
		}
		u32 material_count = m_meshnode->getMaterialCount();
		for (u32 i = 0; i < material_count; ++i) {
			video::SMaterial &material = m_meshnode->getMaterial(i);
			material.setFlag(video::EMF_BACK_FACE_CULLING, true);
			material.setFlag(video::EMF_BILINEAR_FILTER, m_bilinear_filter);
			material.setFlag(video::EMF_TRILINEAR_FILTER, m_trilinear_filter);
		}
		return;
	}
	else if (def.inventory_image != "") {
		setExtruded(def.inventory_image, def.wield_scale, tsrc, 1);
		m_colors.push_back(ItemPartColor());
		return;
	}

	// no wield mesh found
	changeToMesh(NULL);
}

void WieldMeshSceneNode::setColor(video::SColor c)
{
	assert(!m_lighting);
	scene::IMesh *mesh=m_meshnode->getMesh();
	if (mesh == NULL)
		return;

	u8 red = c.getRed();
	u8 green = c.getGreen();
	u8 blue = c.getBlue();
	u32 mc = mesh->getMeshBufferCount();
	for (u32 j = 0; j < mc; j++) {
		video::SColor bc(m_base_color);
		if ((m_colors.size() > j) && (m_colors[j].override_base))
			bc = m_colors[j].color;
		video::SColor buffercolor(255,
			bc.getRed() * red / 255,
			bc.getGreen() * green / 255,
			bc.getBlue() * blue / 255);
		scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
		colorizeMeshBuffer(buf, &buffercolor);
	}
}

void WieldMeshSceneNode::render()
{
	// note: if this method is changed to actually do something,
	// you probably should implement OnRegisterSceneNode as well
}

void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh)
{
	if (mesh == NULL) {
		scene::IMesh *dummymesh = g_extrusion_mesh_cache->createCube();
		m_meshnode->setVisible(false);
		m_meshnode->setMesh(dummymesh);
		dummymesh->drop();  // m_meshnode grabbed it
	} else {
		m_meshnode->setMesh(mesh);
	}

	m_meshnode->setMaterialFlag(video::EMF_LIGHTING, m_lighting);
	// need to normalize normals when lighting is enabled (because of setScale())
	m_meshnode->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, m_lighting);
	m_meshnode->setVisible(true);
}

void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
{
	ITextureSource *tsrc = client->getTextureSource();
	IItemDefManager *idef = client->getItemDefManager();
	INodeDefManager *ndef = client->getNodeDefManager();
	const ItemDefinition &def = item.getDefinition(idef);
	const ContentFeatures &f = ndef->get(def.name);
	content_t id = ndef->getId(def.name);

	if (!g_extrusion_mesh_cache) {
		g_extrusion_mesh_cache = new ExtrusionMeshCache();
	} else {
		g_extrusion_mesh_cache->grab();
	}

	scene::SMesh *mesh = NULL;

	// Shading is on by default
	result->needs_shading = true;

	// If inventory_image is defined, it overrides everything else
	if (def.inventory_image != "") {
		mesh = getExtrudedMesh(tsrc, def.inventory_image);
		result->buffer_colors.push_back(ItemPartColor());
		// Items with inventory images do not need shading
		result->needs_shading = false;
	} else if (def.type == ITEM_NODE) {
		if (f.mesh_ptr[0]) {
			mesh = cloneMesh(f.mesh_ptr[0]);
			scaleMesh(mesh, v3f(0.12, 0.12, 0.12));
		} else if (f.drawtype == NDT_PLANTLIKE) {
			mesh = getExtrudedMesh(tsrc,
				tsrc->getTextureName(f.tiles[0].layers[0].texture_id));
		} else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES
			|| f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) {
			scene::IMesh *cube = g_extrusion_mesh_cache->createCube();
			mesh = cloneMesh(cube);
			cube->drop();
			scaleMesh(mesh, v3f(1.2, 1.2, 1.2));
		} else {
			MeshMakeData mesh_make_data(client, false);
			MapNode mesh_make_node(id, 255, 0);
			mesh_make_data.fillSingleNode(&mesh_make_node);
			MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0));
			mesh = cloneMesh(mapblock_mesh.getMesh());
			translateMesh(mesh, v3f(-BS, -BS, -BS));
			scaleMesh(mesh, v3f(0.12, 0.12, 0.12));

			u32 mc = mesh->getMeshBufferCount();
			for (u32 i = 0; i < mc; ++i) {
				video::SMaterial &material1 =
					mesh->getMeshBuffer(i)->getMaterial();
				video::SMaterial &material2 =
					mapblock_mesh.getMesh()->getMeshBuffer(i)->getMaterial();
				material1.setTexture(0, material2.getTexture(0));
				material1.setTexture(1, material2.getTexture(1));
				material1.setTexture(2, material2.getTexture(2));
				material1.setTexture(3, material2.getTexture(3));
				material1.MaterialType = material2.MaterialType;
			}
		}

		u32 mc = mesh->getMeshBufferCount();
		for (u32 i = 0; i < mc; ++i) {
			scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
			video::SMaterial &material = buf->getMaterial();
			material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
			material.setFlag(video::EMF_BILINEAR_FILTER, false);
			material.setFlag(video::EMF_TRILINEAR_FILTER, false);
			material.setFlag(video::EMF_BACK_FACE_CULLING, true);
			material.setFlag(video::EMF_LIGHTING, false);
		}

		rotateMeshXZby(mesh, -45);
		rotateMeshYZby(mesh, -30);

		postProcessNodeMesh(mesh, f, false, false, NULL,
			&result->buffer_colors);
	}
	result->mesh = mesh;
}

scene::SMesh * getExtrudedMesh(ITextureSource *tsrc,
		const std::string &imagename)
{
	video::ITexture *texture = tsrc->getTextureForMesh(imagename);
	if (!texture) {
		return NULL;
	}

	core::dimension2d<u32> dim = texture->getSize();
	scene::IMesh *original = g_extrusion_mesh_cache->create(dim);
	scene::SMesh *mesh = cloneMesh(original);
	original->drop();

	// Customize material
	video::SMaterial &material = mesh->getMeshBuffer(0)->getMaterial();
	material.setTexture(0, tsrc->getTexture(imagename));
	material.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
	material.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
	material.setFlag(video::EMF_BILINEAR_FILTER, false);
	material.setFlag(video::EMF_TRILINEAR_FILTER, false);
	material.setFlag(video::EMF_BACK_FACE_CULLING, true);
	material.setFlag(video::EMF_LIGHTING, false);
	material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
	scaleMesh(mesh, v3f(2.0, 2.0, 2.0));

	return mesh;
}

void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f,
	bool use_shaders, bool set_material, video::E_MATERIAL_TYPE *mattype,
	std::vector<ItemPartColor> *colors)
{
	u32 mc = mesh->getMeshBufferCount();
	// Allocate colors for existing buffers
	colors->clear();
	for (u32 i = 0; i < mc; ++i)
		colors->push_back(ItemPartColor());

	for (u32 i = 0; i < mc; ++i) {
		const TileSpec *tile = &(f.tiles[i]);
		scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
		for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
			const TileLayer *layer = &tile->layers[layernum];
			if (layer->texture_id == 0)
				continue;
			if (layernum != 0) {
				scene::IMeshBuffer *copy = cloneMeshBuffer(buf);
				copy->getMaterial() = buf->getMaterial();
				mesh->addMeshBuffer(copy);
				copy->drop();
				buf = copy;
				colors->push_back(
					ItemPartColor(layer->has_color, layer->color));
			} else {
				(*colors)[i] = ItemPartColor(layer->has_color, layer->color);
			}
			video::SMaterial &material = buf->getMaterial();
			if (set_material)
				layer->applyMaterialOptions(material);
			if (mattype) {
				material.MaterialType = *mattype;
			}
			if (layer->animation_frame_count > 1) {
				FrameSpec animation_frame = layer->frames[0];
				material.setTexture(0, animation_frame.texture);
			} else {
				material.setTexture(0, layer->texture);
			}
			if (use_shaders) {
				if (layer->normal_texture) {
					if (layer->animation_frame_count > 1) {
						FrameSpec animation_frame = layer->frames[0];
						material.setTexture(1, animation_frame.normal_texture);
					} else
						material.setTexture(1, layer->normal_texture);
				}
				material.setTexture(2, layer->flags_texture);
			}
		}
	}
}


code:
/*
Minetest
Copyright (C) 2010-2014 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 WIELDMESH_HEADER
#define WIELDMESH_HEADER

#include <string>
#include "irrlichttypes_extrabloated.h"

struct ItemStack;
class Client;
class ITextureSource;
struct ContentFeatures;

/*!
 * Holds color information of an item mesh's buffer.
 */
struct ItemPartColor
{
	/*!
	 * If this is false, the global base color of the item
	 * will be used instead of the specific color of the
	 * buffer.
	 */
	bool override_base;
	/*!
	 * The color of the buffer.
	 */
	video::SColor color;

	ItemPartColor() : override_base(false), color(0) {}

	ItemPartColor(bool override, video::SColor color)
	    : override_base(override), color(color)
	{
	}
};

struct ItemMesh
{
	scene::IMesh *mesh;
	/*!
	 * Stores the color of each mesh buffer.
	 */
	std::vector<ItemPartColor> buffer_colors;
	/*!
	 * If false, all faces of the item should have the same brightness.
	 * Disables shading based on normal vectors.
	 */
	bool needs_shading;

	ItemMesh() : mesh(NULL), buffer_colors(), needs_shading(true) {}
};

/*
	Wield item scene node, renders the wield mesh of some item
*/
class WieldMeshSceneNode : public scene::ISceneNode
{
public:
	WieldMeshSceneNode(scene::ISceneNode *parent, scene::ISceneManager *mgr,
			s32 id = -1, bool lighting = false);
	virtual ~WieldMeshSceneNode();

	void setCube(const ContentFeatures &f, v3f wield_scale, ITextureSource *tsrc);
	void setExtruded(const std::string &imagename, v3f wield_scale,
			ITextureSource *tsrc, u8 num_frames);
	void setItem(const ItemStack &item, Client *client);

	// Sets the vertex color of the wield mesh.
	// Must only be used if the constructor was called with lighting = false
	void setColor(video::SColor color);

	scene::IMesh *getMesh() { return m_meshnode->getMesh(); }

	virtual void render();

	virtual const aabb3f &getBoundingBox() const { return m_bounding_box; }

private:
	void changeToMesh(scene::IMesh *mesh);

	// Child scene node with the current wield mesh
	scene::IMeshSceneNode *m_meshnode;
	video::E_MATERIAL_TYPE m_material_type;

	// True if EMF_LIGHTING should be enabled.
	bool m_lighting;

	bool m_enable_shaders;
	bool m_anisotropic_filter;
	bool m_bilinear_filter;
	bool m_trilinear_filter;
	/*!
	 * Stores the colors of the mesh's mesh buffers.
	 * This does not include lighting.
	 */
	std::vector<ItemPartColor> m_colors;
	/*!
	 * The base color of this mesh. This is the default
	 * for all mesh buffers.
	 */
	video::SColor m_base_color;

	// Bounding box culling is disabled for this type of scene node,
	// so this variable is just required so we can implement
	// getBoundingBox() and is set to an empty box.
	aabb3f m_bounding_box;
};

void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result);

scene::SMesh *getExtrudedMesh(ITextureSource *tsrc, const std::string &imagename);

/*!
 * Applies overlays, textures and optionally materials to the given mesh and
 * extracts tile colors for colorization.
 * \param mattype overrides the buffer's material type, but can also
 * be NULL to leave the original material.
 * \param colors returns the colors of the mesh buffers in the mesh.
 */
void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f, bool use_shaders,
		bool set_material, video::E_MATERIAL_TYPE *mattype,
		std::vector<ItemPartColor> *colors);
#endif


code: Put your texture pack folders in this folder. Textures in the "server" pack will be used by the server.


code:
#!/bin/bash
set -e

dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
if [ $# -ne 1 ]; then
	echo "Usage: $0 <build directory>"
	exit 1
fi
builddir=$1
mkdir -p $builddir
builddir="$( cd "$builddir" && pwd )"
packagedir=$builddir/packages
libdir=$builddir/libs

toolchain_file=$dir/toolchain_mingw.cmake
irrlicht_version=1.8.4
ogg_version=1.3.2
vorbis_version=1.3.5
curl_version=7.50.3
gettext_version=0.19.8.1
freetype_version=2.7
sqlite3_version=3.14.2
luajit_version=2.1.0-beta2
leveldb_version=1.18
zlib_version=1.2.8

mkdir -p $packagedir
mkdir -p $libdir

cd $builddir

# Get stuff
[ -e $packagedir/irrlicht-$irrlicht_version.zip ] || wget http://minetest.kitsunemimi.pw/irrlicht-$irrlicht_version-win32.zip \
	-c -O $packagedir/irrlicht-$irrlicht_version.zip
[ -e $packagedir/zlib-$zlib_version.zip ] || wget http://minetest.kitsunemimi.pw/zlib-$zlib_version-win32.zip \
	-c -O $packagedir/zlib-$zlib_version.zip
[ -e $packagedir/libogg-$ogg_version.zip ] || wget http://minetest.kitsunemimi.pw/libogg-$ogg_version-win32.zip \
	-c -O $packagedir/libogg-$ogg_version.zip
[ -e $packagedir/libvorbis-$vorbis_version.zip ] || wget http://minetest.kitsunemimi.pw/libvorbis-$vorbis_version-win32.zip \
	-c -O $packagedir/libvorbis-$vorbis_version.zip
[ -e $packagedir/curl-$curl_version.zip ] || wget http://minetest.kitsunemimi.pw/curl-$curl_version-win32.zip \
	-c -O $packagedir/curl-$curl_version.zip
[ -e $packagedir/gettext-$gettext_version.zip ] || wget http://minetest.kitsunemimi.pw/gettext-$gettext_version-win32.zip \
	-c -O $packagedir/gettext-$gettext_version.zip
[ -e $packagedir/freetype2-$freetype_version.zip ] || wget http://minetest.kitsunemimi.pw/freetype2-$freetype_version-win32.zip \
	-c -O $packagedir/freetype2-$freetype_version.zip
[ -e $packagedir/sqlite3-$sqlite3_version.zip ] || wget http://minetest.kitsunemimi.pw/sqlite3-$sqlite3_version-win32.zip \
	-c -O $packagedir/sqlite3-$sqlite3_version.zip
[ -e $packagedir/luajit-$luajit_version.zip ] || wget http://minetest.kitsunemimi.pw/luajit-$luajit_version-win32.zip \
	-c -O $packagedir/luajit-$luajit_version.zip
[ -e $packagedir/libleveldb-$leveldb_version.zip ] || wget http://minetest.kitsunemimi.pw/libleveldb-$leveldb_version-win32.zip \
	-c -O $packagedir/libleveldb-$leveldb_version.zip
[ -e $packagedir/openal_stripped.zip ] || wget http://minetest.kitsunemimi.pw/openal_stripped.zip \
	-c -O $packagedir/openal_stripped.zip

# Extract stuff
cd $libdir
[ -d irrlicht ] || unzip -o $packagedir/irrlicht-$irrlicht_version.zip -d irrlicht
[ -d zlib ] || unzip -o $packagedir/zlib-$zlib_version.zip -d zlib
[ -d libogg ] || unzip -o $packagedir/libogg-$ogg_version.zip -d libogg
[ -d libvorbis ] || unzip -o $packagedir/libvorbis-$vorbis_version.zip -d libvorbis
[ -d libcurl ] || unzip -o $packagedir/curl-$curl_version.zip -d libcurl
[ -d gettext ] || unzip -o $packagedir/gettext-$gettext_version.zip -d gettext
[ -d freetype ] || unzip -o $packagedir/freetype2-$freetype_version.zip -d freetype
[ -d sqlite3 ] || unzip -o $packagedir/sqlite3-$sqlite3_version.zip -d sqlite3
[ -d openal_stripped ] || unzip -o $packagedir/openal_stripped.zip
[ -d luajit ] || unzip -o $packagedir/luajit-$luajit_version.zip -d luajit
[ -d leveldb ] || unzip -o $packagedir/libleveldb-$leveldb_version.zip -d leveldb

# Get minetest
cd $builddir
if [ ! "x$EXISTING_MINETEST_DIR" = "x" ]; then
	ln -s $EXISTING_MINETEST_DIR minetest
else
	[ -d minetest ] && (cd minetest && git pull) || (git clone https://github.com/minetest/minetest)
fi
cd minetest
git_hash=$(git rev-parse --short HEAD)

# Get minetest_game
cd games
if [ "x$NO_MINETEST_GAME" = "x" ]; then
	[ -d minetest_game ] && (cd minetest_game && git pull) || (git clone https://github.com/minetest/minetest_game)
fi
cd ../..

# Build the thing
cd minetest
[ -d _build ] && rm -Rf _build/
mkdir _build
cd _build
cmake .. \
	-DCMAKE_INSTALL_PREFIX=/tmp \
	-DVERSION_EXTRA=$git_hash \
	-DBUILD_CLIENT=1 -DBUILD_SERVER=0 \
	-DCMAKE_TOOLCHAIN_FILE=$toolchain_file \
	\
	-DENABLE_SOUND=1 \
	-DENABLE_CURL=1 \
	-DENABLE_GETTEXT=1 \
	-DENABLE_FREETYPE=1 \
	-DENABLE_LEVELDB=1 \
	\
	-DIRRLICHT_INCLUDE_DIR=$libdir/irrlicht/include \
	-DIRRLICHT_LIBRARY=$libdir/irrlicht/lib/Win32-gcc/libIrrlicht.dll.a \
	-DIRRLICHT_DLL=$libdir/irrlicht/bin/Win32-gcc/Irrlicht.dll \
	\
	-DZLIB_INCLUDE_DIR=$libdir/zlib/include \
	-DZLIB_LIBRARIES=$libdir/zlib/lib/libz.dll.a \
	-DZLIB_DLL=$libdir/zlib/bin/zlib1.dll \
	\
	-DLUA_INCLUDE_DIR=$libdir/luajit/include \
	-DLUA_LIBRARY=$libdir/luajit/libluajit.a \
	\
	-DOGG_INCLUDE_DIR=$libdir/libogg/include \
	-DOGG_LIBRARY=$libdir/libogg/lib/libogg.dll.a \
	-DOGG_DLL=$libdir/libogg/bin/libogg-0.dll \
	\
	-DVORBIS_INCLUDE_DIR=$libdir/libvorbis/include \
	-DVORBIS_LIBRARY=$libdir/libvorbis/lib/libvorbis.dll.a \
	-DVORBIS_DLL=$libdir/libvorbis/bin/libvorbis-0.dll \
	-DVORBISFILE_LIBRARY=$libdir/libvorbis/lib/libvorbisfile.dll.a \
	-DVORBISFILE_DLL=$libdir/libvorbis/bin/libvorbisfile-3.dll \
	\
	-DOPENAL_INCLUDE_DIR=$libdir/openal_stripped/include/AL \
	-DOPENAL_LIBRARY=$libdir/openal_stripped/lib/libOpenAL32.dll.a \
	-DOPENAL_DLL=$libdir/openal_stripped/bin/OpenAL32.dll \
	\
	-DCURL_DLL=$libdir/libcurl/bin/libcurl-4.dll \
	-DCURL_INCLUDE_DIR=$libdir/libcurl/include \
	-DCURL_LIBRARY=$libdir/libcurl/lib/libcurl.dll.a \
	\
	-DGETTEXT_MSGFMT=`which msgfmt` \
	-DGETTEXT_DLL=$libdir/gettext/bin/libintl-8.dll \
	-DGETTEXT_ICONV_DLL=$libdir/gettext/bin/libiconv-2.dll \
	-DGETTEXT_INCLUDE_DIR=$libdir/gettext/include \
	-DGETTEXT_LIBRARY=$libdir/gettext/lib/libintl.dll.a \
	\
	-DFREETYPE_INCLUDE_DIR_freetype2=$libdir/freetype/include/freetype2 \
	-DFREETYPE_INCLUDE_DIR_ft2build=$libdir/freetype/include/freetype2 \
	-DFREETYPE_LIBRARY=$libdir/freetype/lib/libfreetype.dll.a \
	-DFREETYPE_DLL=$libdir/freetype/bin/libfreetype-6.dll \
	\
	-DSQLITE3_INCLUDE_DIR=$libdir/sqlite3/include \
	-DSQLITE3_LIBRARY=$libdir/sqlite3/lib/libsqlite3.dll.a \
	-DSQLITE3_DLL=$libdir/sqlite3/bin/libsqlite3-0.dll \
	\
	-DLEVELDB_INCLUDE_DIR=$libdir/leveldb/include \
	-DLEVELDB_LIBRARY=$libdir/leveldb/lib/libleveldb.dll.a \
	-DLEVELDB_DLL=$libdir/leveldb/bin/libleveldb.dll

make package -j2

# EOF


code:
#!/bin/bash
set -e

dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
if [ $# -ne 1 ]; then
	echo "Usage: $0 <build directory>"
	exit 1
fi
builddir=$1
mkdir -p $builddir
builddir="$( cd "$builddir" && pwd )"
packagedir=$builddir/packages
libdir=$builddir/libs

toolchain_file=$dir/toolchain_mingw64.cmake
irrlicht_version=1.8.4
ogg_version=1.3.2
vorbis_version=1.3.5
curl_version=7.50.3
gettext_version=0.19.8.1
freetype_version=2.7
sqlite3_version=3.14.2
luajit_version=2.1.0-beta2
leveldb_version=1.18
zlib_version=1.2.8

mkdir -p $packagedir
mkdir -p $libdir

cd $builddir

# Get stuff
[ -e $packagedir/irrlicht-$irrlicht_version.zip ] || wget http://minetest.kitsunemimi.pw/irrlicht-$irrlicht_version-win64.zip \
	-c -O $packagedir/irrlicht-$irrlicht_version.zip
[ -e $packagedir/zlib-$zlib_version.zip ] || wget http://minetest.kitsunemimi.pw/zlib-$zlib_version-win64.zip \
	-c -O $packagedir/zlib-$zlib_version.zip
[ -e $packagedir/libogg-$ogg_version.zip ] || wget http://minetest.kitsunemimi.pw/libogg-$ogg_version-win64.zip \
	-c -O $packagedir/libogg-$ogg_version.zip
[ -e $packagedir/libvorbis-$vorbis_version.zip ] || wget http://minetest.kitsunemimi.pw/libvorbis-$vorbis_version-win64.zip \
	-c -O $packagedir/libvorbis-$vorbis_version.zip
[ -e $packagedir/curl-$curl_version.zip ] || wget http://minetest.kitsunemimi.pw/curl-$curl_version-win64.zip \
	-c -O $packagedir/curl-$curl_version.zip
[ -e $packagedir/gettext-$gettext_version.zip ] || wget http://minetest.kitsunemimi.pw/gettext-$gettext_version-win64.zip \
	-c -O $packagedir/gettext-$gettext_version.zip
[ -e $packagedir/freetype2-$freetype_version.zip ] || wget http://minetest.kitsunemimi.pw/freetype2-$freetype_version-win64.zip \
	-c -O $packagedir/freetype2-$freetype_version.zip
[ -e $packagedir/sqlite3-$sqlite3_version.zip ] || wget http://minetest.kitsunemimi.pw/sqlite3-$sqlite3_version-win64.zip \
	-c -O $packagedir/sqlite3-$sqlite3_version.zip
[ -e $packagedir/luajit-$luajit_version.zip ] || wget http://minetest.kitsunemimi.pw/luajit-$luajit_version-win64.zip \
	-c -O $packagedir/luajit-$luajit_version.zip
[ -e $packagedir/libleveldb-$leveldb_version.zip ] || wget http://minetest.kitsunemimi.pw/libleveldb-$leveldb_version-win64.zip \
	-c -O $packagedir/libleveldb-$leveldb_version.zip
[ -e $packagedir/openal_stripped.zip ] || wget http://minetest.kitsunemimi.pw/openal_stripped64.zip \
	-c -O $packagedir/openal_stripped.zip

# Extract stuff
cd $libdir
[ -d irrlicht ] || unzip -o $packagedir/irrlicht-$irrlicht_version.zip -d irrlicht
[ -d zlib ] || unzip -o $packagedir/zlib-$zlib_version.zip -d zlib
[ -d libogg ] || unzip -o $packagedir/libogg-$ogg_version.zip -d libogg
[ -d libvorbis ] || unzip -o $packagedir/libvorbis-$vorbis_version.zip -d libvorbis
[ -d libcurl ] || unzip -o $packagedir/curl-$curl_version.zip -d libcurl
[ -d gettext ] || unzip -o $packagedir/gettext-$gettext_version.zip -d gettext
[ -d freetype ] || unzip -o $packagedir/freetype2-$freetype_version.zip -d freetype
[ -d sqlite3 ] || unzip -o $packagedir/sqlite3-$sqlite3_version.zip -d sqlite3
[ -d openal_stripped ] || unzip -o $packagedir/openal_stripped.zip
[ -d luajit ] || unzip -o $packagedir/luajit-$luajit_version.zip -d luajit
[ -d leveldb ] || unzip -o $packagedir/libleveldb-$leveldb_version.zip -d leveldb

# Get minetest
cd $builddir
if [ ! "x$EXISTING_MINETEST_DIR" = "x" ]; then
	ln -s $EXISTING_MINETEST_DIR minetest
else
	[ -d minetest ] && (cd minetest && git pull) || (git clone https://github.com/minetest/minetest)
fi
cd minetest
git_hash=$(git rev-parse --short HEAD)

# Get minetest_game
cd games
if [ "x$NO_MINETEST_GAME" = "x" ]; then
	[ -d minetest_game ] && (cd minetest_game && git pull) || (git clone https://github.com/minetest/minetest_game)
fi
cd ../..

# Build the thing
cd minetest
[ -d _build ] && rm -Rf _build/
mkdir _build
cd _build
cmake .. \
	-DCMAKE_TOOLCHAIN_FILE=$toolchain_file \
	-DCMAKE_INSTALL_PREFIX=/tmp \
	-DVERSION_EXTRA=$git_hash \
	-DBUILD_CLIENT=1 -DBUILD_SERVER=0 \
	\
	-DENABLE_SOUND=1 \
	-DENABLE_CURL=1 \
	-DENABLE_GETTEXT=1 \
	-DENABLE_FREETYPE=1 \
	-DENABLE_LEVELDB=1 \
	\
	-DIRRLICHT_INCLUDE_DIR=$libdir/irrlicht/include \
	-DIRRLICHT_LIBRARY=$libdir/irrlicht/lib/Win64-gcc/libIrrlicht.dll.a \
	-DIRRLICHT_DLL=$libdir/irrlicht/bin/Win64-gcc/Irrlicht.dll \
	\
	-DZLIB_INCLUDE_DIR=$libdir/zlib/include \
	-DZLIB_LIBRARIES=$libdir/zlib/lib/libz.dll.a \
	-DZLIB_DLL=$libdir/zlib/bin/zlib1.dll \
	\
	-DLUA_INCLUDE_DIR=$libdir/luajit/include \
	-DLUA_LIBRARY=$libdir/luajit/libluajit.a \
	\
	-DOGG_INCLUDE_DIR=$libdir/libogg/include \
	-DOGG_LIBRARY=$libdir/libogg/lib/libogg.dll.a \
	-DOGG_DLL=$libdir/libogg/bin/libogg-0.dll \
	\
	-DVORBIS_INCLUDE_DIR=$libdir/libvorbis/include \
	-DVORBIS_LIBRARY=$libdir/libvorbis/lib/libvorbis.dll.a \
	-DVORBIS_DLL=$libdir/libvorbis/bin/libvorbis-0.dll \
	-DVORBISFILE_LIBRARY=$libdir/libvorbis/lib/libvorbisfile.dll.a \
	-DVORBISFILE_DLL=$libdir/libvorbis/bin/libvorbisfile-3.dll \
	\
	-DOPENAL_INCLUDE_DIR=$libdir/openal_stripped/include/AL \
	-DOPENAL_LIBRARY=$libdir/openal_stripped/lib/libOpenAL32.dll.a \
	-DOPENAL_DLL=$libdir/openal_stripped/bin/OpenAL32.dll \
	\
	-DCURL_DLL=$libdir/libcurl/bin/libcurl-4.dll \
	-DCURL_INCLUDE_DIR=$libdir/libcurl/include \
	-DCURL_LIBRARY=$libdir/libcurl/lib/libcurl.dll.a \
	\
	-DGETTEXT_MSGFMT=`which msgfmt` \
	-DGETTEXT_DLL=$libdir/gettext/bin/libintl-8.dll \
	-DGETTEXT_ICONV_DLL=$libdir/gettext/bin/libiconv-2.dll \
	-DGETTEXT_INCLUDE_DIR=$libdir/gettext/include \
	-DGETTEXT_LIBRARY=$libdir/gettext/lib/libintl.dll.a \
	\
	-DFREETYPE_INCLUDE_DIR_freetype2=$libdir/freetype/include/freetype2 \
	-DFREETYPE_INCLUDE_DIR_ft2build=$libdir/freetype/include/freetype2 \
	-DFREETYPE_LIBRARY=$libdir/freetype/lib/libfreetype.dll.a \
	-DFREETYPE_DLL=$libdir/freetype/bin/libfreetype-6.dll \
	\
	-DSQLITE3_INCLUDE_DIR=$libdir/sqlite3/include \
	-DSQLITE3_LIBRARY=$libdir/sqlite3/lib/libsqlite3.dll.a \
	-DSQLITE3_DLL=$libdir/sqlite3/bin/libsqlite3-0.dll \
	\
	-DLEVELDB_INCLUDE_DIR=$libdir/leveldb/include \
	-DLEVELDB_LIBRARY=$libdir/leveldb/lib/libleveldb.dll.a \
	-DLEVELDB_DLL=$libdir/leveldb/bin/libleveldb.dll

make package -j2

# EOF


code:
# name of the target operating system
SET(CMAKE_SYSTEM_NAME Windows)

# which compilers to use for C and C++
SET(CMAKE_C_COMPILER i586-mingw32msvc-gcc)
SET(CMAKE_CXX_COMPILER i586-mingw32msvc-g++)
SET(CMAKE_RC_COMPILER i586-mingw32msvc-windres)

# here is the target environment located
SET(CMAKE_FIND_ROOT_PATH /usr/i586-mingw32msvc)

# adjust the default behaviour of the FIND_XXX() commands:
# search headers and libraries in the target environment, search
# programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)


code:
# name of the target operating system
SET(CMAKE_SYSTEM_NAME Windows)

# which compilers to use for C and C++
SET(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
SET(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
SET(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)

# here is the target environment located
SET(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32)

# adjust the default behaviour of the FIND_XXX() commands:
# search headers and libraries in the target environment, search
# programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)


code:
#!/bin/bash -e

prompt_for_number() {
	local prompt_text=$1
	local default_value=$2
	local tmp=""
	while true; do
		read -p "$prompt_text [$default_value]: " tmp
		if [ "$tmp" = "" ]; then
			echo "$default_value"; return
		elif echo "$tmp" | grep -q -E '^[0-9]+$'; then
			echo "$tmp"; return
		fi
	done
}

# On a release the following actions are performed
# * DEVELOPMENT_BUILD is set to false
# * android versionCode is bumped
# * Commit the changes
# * Tag with current version
perform_release() {
	sed -i -re "s/^set\(DEVELOPMENT_BUILD TRUE\)$/set(DEVELOPMENT_BUILD FALSE)/" CMakeLists.txt

	sed -i -re "s/versionCode [0-9]+$/versionCode $NEW_ANDROID_VERSION_CODE/" build/android/build.gradle

	git add -f CMakeLists.txt build/android/build.gradle

	git commit -m "Bump version to $RELEASE_VERSION"

	echo "Tagging $RELEASE_VERSION"

	git tag -a "$RELEASE_VERSION" -m "$RELEASE_VERSION"
}

# After release
# * Set DEVELOPMENT_BUILD to true
# * Bump version in CMakeLists and docs
# * Commit the changes
back_to_devel() {
	echo 'Creating "return back to development" commit'

	sed -i -re 's/^set\(DEVELOPMENT_BUILD FALSE\)$/set(DEVELOPMENT_BUILD TRUE)/' CMakeLists.txt

	sed -i -re "s/^set\(VERSION_MAJOR [0-9]+\)$/set(VERSION_MAJOR $NEXT_VERSION_MAJOR)/" CMakeLists.txt

	sed -i -re "s/^set\(VERSION_MINOR [0-9]+\)$/set(VERSION_MINOR $NEXT_VERSION_MINOR)/" CMakeLists.txt

	sed -i -re "s/^set\(VERSION_PATCH [0-9]+\)$/set(VERSION_PATCH $NEXT_VERSION_PATCH)/" CMakeLists.txt

	sed -i -re "1s/[0-9]+\.[0-9]+\.[0-9]+/$NEXT_VERSION/g" doc/lua_api.txt

	sed -i -re "1s/[0-9]+\.[0-9]+\.[0-9]+/$NEXT_VERSION/g" doc/menu_lua_api.txt

	sed -i -re "1s/[0-9]+\.[0-9]+\.[0-9]+/$NEXT_VERSION/g" doc/client_lua_api.md

	git add -f CMakeLists.txt doc/lua_api.txt doc/menu_lua_api.txt doc/client_lua_api.md

	git commit -m "Continue with $NEXT_VERSION-dev"
}
##################################
# Switch to top minetest directory
##################################

cd ${0%/*}/..

#######################
# Determine old version
#######################

# Make sure all the files we need exist
grep -q -E '^set\(VERSION_MAJOR [0-9]+\)$' CMakeLists.txt
grep -q -E '^set\(VERSION_MINOR [0-9]+\)$' CMakeLists.txt
grep -q -E '^set\(VERSION_PATCH [0-9]+\)$' CMakeLists.txt
grep -q -E 'versionCode [0-9]+$' build/android/build.gradle

VERSION_MAJOR=$(grep -E '^set\(VERSION_MAJOR [0-9]+\)$' CMakeLists.txt | tr -dC 0-9)
VERSION_MINOR=$(grep -E '^set\(VERSION_MINOR [0-9]+\)$' CMakeLists.txt | tr -dC 0-9)
VERSION_PATCH=$(grep -E '^set\(VERSION_PATCH [0-9]+\)$' CMakeLists.txt | tr -dC 0-9)
ANDROID_VERSION_CODE=$(grep -E 'versionCode [0-9]+$' build/android/build.gradle | tr -dC 0-9)

RELEASE_VERSION="$VERSION_MAJOR.$VERSION_MINOR.$VERSION_PATCH"

echo "Current Minetest version: $RELEASE_VERSION"
echo "Current Android version code: $ANDROID_VERSION_CODE"

NEW_ANDROID_VERSION_CODE=$(expr $ANDROID_VERSION_CODE + 1)
NEW_ANDROID_VERSION_CODE=$(prompt_for_number "Set android version code" $NEW_ANDROID_VERSION_CODE)

echo
echo "New android version code: $NEW_ANDROID_VERSION_CODE"

########################
# Perform release
########################

perform_release

########################
# Prompt for next version
########################

NEXT_VERSION_MAJOR=$VERSION_MAJOR
NEXT_VERSION_MINOR=$VERSION_MINOR
NEXT_VERSION_PATCH=$(expr $VERSION_PATCH + 1)

NEXT_VERSION_MAJOR=$(prompt_for_number "Set next major" $NEXT_VERSION_MAJOR)

if [ "$NEXT_VERSION_MAJOR" != "$VERSION_MAJOR" ]; then
	NEXT_VERSION_MINOR=0
	NEXT_VERSION_PATCH=0
fi

NEXT_VERSION_MINOR=$(prompt_for_number "Set next minor" $NEXT_VERSION_MINOR)

if [ "$NEXT_VERSION_MINOR" != "$VERSION_MINOR" ]; then
	NEXT_VERSION_PATCH=0
fi

NEXT_VERSION_PATCH=$(prompt_for_number "Set next patch" $NEXT_VERSION_PATCH)

NEXT_VERSION="$NEXT_VERSION_MAJOR.$NEXT_VERSION_MINOR.$NEXT_VERSION_PATCH"

echo
echo "New version: $NEXT_VERSION"

########################
# Return back to devel
########################

back_to_devel


code:
0 128 128 128   # CONTENT_STONE
2 39 66 106     # CONTENT_WATER
3 255 255 0     # CONTENT_TORCH
9 39 66 106     # CONTENT_WATERSOURCE
e 117 86 41     # CONTENT_SIGN_WALL
f 128 79 0      # CONTENT_CHEST
10 118 118 118  # CONTENT_FURNACE
15 103 78 42    # CONTENT_FENCE
1e 162 119 53   # CONTENT_RAIL
1f 154 110 40   # CONTENT_LADDER
20 255 100 0    # CONTENT_LAVA
21 255 100 0    # CONTENT_LAVASOURCE
800 107 134 51  # CONTENT_GRASS
801 86 58 31    # CONTENT_TREE
802 48 95 8     # CONTENT_LEAVES
803 102 129 38  # CONTENT_GRASS_FOOTSTEPS
804 178 178 0   # CONTENT_MESE
805 101 84 36   # CONTENT_MUD
808 104 78 42   # CONTENT_WOOD
809 210 194 156 # CONTENT_SAND
80a 123 123 123 # CONTENT_COBBLE
80b 199 199 199 # CONTENT_STEEL
80c 183 183 222 # CONTENT_GLASS
80d 219 202 178 # CONTENT_MOSSYCOBBLE
80e 70 70 70    # CONTENT_GRAVEL
80f 204 0 0     # CONTENT_SANDSTONE
810 0 215 0     # CONTENT_CACTUS
811 170 50 25   # CONTENT_BRICK
812 104 78 42   # CONTENT_CLAY
813 58 105 18   # CONTENT_PAPYRUS
814 196 160 0   # CONTENT_BOOKSHELF
815 205 190 121 # CONTENT_JUNGLETREE
816 62 101 25   # CONTENT_JUNGLEGRASS
817 255 153 255 # CONTENT_NC
818 102 50 255  # CONTENT_NC_RB
819 200 0 0     # CONTENT_APPLE

default:stone 128 128 128
default:stone_with_coal 50 50 50
default:water_flowing 39 66 106
default:torch 255 255 0
default:water_source 39 66 106
default:sign_wall 117 86 41
default:chest 128 79 0
default:furnace 118 118 118
default:fence_wood 103 78 42
default:rail 162 119 53
default:ladder 154 110 40
default:lava_flowing 255 100 0
default:lava_source 255 100 0
default:dirt_with_grass 107 134 51
default:tree 86 58 31
default:leaves 48 95 8
default:dirt_with_grass_and_footsteps 102 129 38
default:mese 178 178 0
default:dirt 101 84 36
default:wood 104 78 42
default:sand 210 194 156
default:cobble 123 123 123
default:steelblock 199 199 199
default:glass 183 183 222
default:mossycobble 219 202 178
default:gravel 70 70 70
default:sandstone 204 0 0
default:cactus 0 215 0
default:brick 170 50 25
default:clay 104 78 42
default:papyrus 58 105 18
default:bookshelf 196 160 0
default:jungletree 205 190 121
default:junglegrass 62 101 25
default:nyancat 255 153 255
default:nyancat_rainbow 102 50 255
default:apple 200 0 0
default:desert_sand 210 180 50
default:desert_stone 150 100 30
default:dry_shrub 100 80 40