aboutsummaryrefslogtreecommitdiff
path: root/src/client
Commit message (Expand)AuthorAge
* tile.cpp: Automatically upscale lower resolution textureSmallJoker2016-04-25
* Re-add and disable blit_with_interpolate_overlaykwolekr2016-04-07
* Fix compiler warnings from "Add an option to colorize to respect the destinat...Samuel Sieb2016-04-06
* Add an option to colorize to respect the destination alphaSamuel Sieb2016-04-03
* Clean up StrfndShadowNinja2016-03-19
* Fix two reconnect bugsest312016-03-15
* v2d & aabbox3d<f32> & sky cleanupsnerzhul2016-02-11
* Fix texture tear issueRealBadAngel2016-01-23
* New 3D Mode: PageflipDalai Felinto2016-01-09
* Print --videomodes response to standard output, tooKahrl2015-11-02
* Refactor thread utility interfacekwolekr2015-10-16
* Refactor loggingShadowNinja2015-10-14
* Change i++ to ++iDavid Jones2015-08-25
* Clean up threadingShadowNinja2015-08-23
* tileable flags are needed also without shaders because of filtersRealBadAngel2015-08-20
* Remove use of engine sent texture tiling flags - theyre no longer neededRealBadAngel2015-08-20
* Fix tiling issues for PLANTLIKE and FIRELIKE with FSAARealBadAngel2015-08-05
* src/client/tile.cpp: Fix reference countingBřetislav Štec2015-08-02
* Optional reconnect functionalityest312015-07-23
* Add wielded (and CAOs) shaderRealBadAngel2015-07-21
* Fix relief mapping issuesRealBadAngel2015-07-16
* Misc. minor fixeskwolekr2015-07-10
* Use UTF-8 instead of narrowest312015-07-08
* Use minetest logging facilities for irrlicht log outputShadowNinja2015-07-05
* Add minimap featureRealBadAngel2015-06-27
* Make early protocol auth mechanism generic, and add SRPest312015-05-11
* Revert the upper-case PROJECT_NAME nonsense that was part of #2402sfan52015-04-27
* Fix fast leaves with texture_clean_transparent enabled.Aaron Suen2015-04-26
* Refactor around translatePasswordest312015-04-17
* Move globals from main.cpp to more sane locationsCraig Robbins2015-04-01
* Clean scaling pre-filter for formspec/HUD.Aaron Suen2015-04-01
* Move texture_min_size even further down the pipe. Now, textures are JIT-upsca...Aaron Suen2015-03-31
* Make sure g_timegetter is initalised before running speedtestsCraig Robbins2015-03-29
* Change error_message from wstring to stringShadowNinja2015-03-27
* Clean up and tweak build systemShadowNinja2015-03-27
* Fix for sun/moon tonemaps: don't upscale 1px images.Aaron Suen2015-03-23
* Fix composite textures with texture_min_size. Moved upscaling of textures to ...Aaron Suen2015-03-22
* Configurable automatic texture scaling and filtering at load time.Aaron Suen2015-03-20
* For usages of assert() that are meant to persist in Release builds (when NDEB...Craig Robbins2015-03-07
* Replace std::list to std::vector into tile.cpp (m_texture_trash) and move til...Loic Blot2015-03-05
* Fix some rendering glitchesBlockMen2015-03-05
* Use proper CMakeLists.txt for network and client directoriesLoic Blot2015-02-21
* Re-apply 4a6582c13c36cab457b78f32338c6bb4ab9ea58b broken since 146f77fdb75083...Loic Blot2015-02-14
* main.cpp rework * Move ClientLauncher class to a dedicated file * ClientLaunc...Loic Blot2015-02-12
87' href='#n387'>387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>,
			  2012-2013 RealBadAngel, Maciej Kasatkin <mk@realbadangel.pl>
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 "irr_v3d.h"
#include <stack>
#include "util/pointer.h"
#include "util/numeric.h"
#include "util/mathconstants.h"
#include "map.h"
#include "environment.h"
#include "nodedef.h"
#include "treegen.h"

namespace treegen
{

void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
		bool is_apple_tree, INodeDefManager *ndef, int seed)
{
	/*
		NOTE: Tree-placing code is currently duplicated in the engine
		and in games that have saplings; both are deprecated but not
		replaced yet
	*/
	MapNode treenode(ndef->getId("mapgen_tree"));
	MapNode leavesnode(ndef->getId("mapgen_leaves"));
	MapNode applenode(ndef->getId("mapgen_apple"));

	PseudoRandom pr(seed);
	s16 trunk_h = pr.range(4, 5);
	v3s16 p1 = p0;
	for(s16 ii=0; ii<trunk_h; ii++)
	{
		if(vmanip.m_area.contains(p1))
			if(ii == 0 || vmanip.getNodeNoExNoEmerge(p1).getContent() == CONTENT_AIR)
				vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
		p1.Y++;
	}

	// p1 is now the last piece of the trunk
	p1.Y -= 1;

	VoxelArea leaves_a(v3s16(-2,-1,-2), v3s16(2,2,2));
	//SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
	Buffer<u8> leaves_d(leaves_a.getVolume());
	for(s32 i=0; i<leaves_a.getVolume(); i++)
		leaves_d[i] = 0;

	// Force leaves at near the end of the trunk
	{
		s16 d = 1;
		for(s16 z=-d; z<=d; z++)
		for(s16 y=-d; y<=d; y++)
		for(s16 x=-d; x<=d; x++)
		{
			leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
		}
	}

	// Add leaves randomly
	for(u32 iii=0; iii<7; iii++)
	{
		s16 d = 1;

		v3s16 p(
			pr.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
			pr.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
			pr.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
		);

		for(s16 z=0; z<=d; z++)
		for(s16 y=0; y<=d; y++)
		for(s16 x=0; x<=d; x++)
		{
			leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
		}
	}

	// Blit leaves to vmanip
	for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
	for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
	for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
	{
		v3s16 p(x,y,z);
		p += p1;
		if(vmanip.m_area.contains(p) == false)
			continue;
		u32 vi = vmanip.m_area.index(p);
		if(vmanip.m_data[vi].getContent() != CONTENT_AIR
				&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
			continue;
		u32 i = leaves_a.index(x,y,z);
		if(leaves_d[i] == 1) {
			bool is_apple = pr.range(0,99) < 10;
			if(is_apple_tree && is_apple) {
				vmanip.m_data[vi] = applenode;
			} else {
				vmanip.m_data[vi] = leavesnode;
			}
		}
	}
}

// L-System tree LUA spawner
treegen::error spawn_ltree(ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef, TreeDef tree_definition)
{
	ServerMap *map = &env->getServerMap();
	std::map<v3s16, MapBlock*> modified_blocks;
	ManualMapVoxelManipulator vmanip(map);
	v3s16 tree_blockp = getNodeBlockPos(p0);
	treegen::error e;

	vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,3,1));
	e = make_ltree (vmanip, p0, ndef, tree_definition);
	if (e != SUCCESS)
		return e;

	vmanip.blitBackAll(&modified_blocks);

	// update lighting
	std::map<v3s16, MapBlock*> lighting_modified_blocks;
	lighting_modified_blocks.insert(modified_blocks.begin(), modified_blocks.end());
	map->updateLighting(lighting_modified_blocks, modified_blocks);
	// Send a MEET_OTHER event
	MapEditEvent event;
	event.type = MEET_OTHER;
	for(std::map<v3s16, MapBlock*>::iterator
		i = modified_blocks.begin();
		i != modified_blocks.end(); ++i)
	{
		event.modified_blocks.insert(i->first);
	}
	map->dispatchEvent(&event);
	return SUCCESS;
}

//L-System tree generator
treegen::error make_ltree(ManualMapVoxelManipulator &vmanip, v3s16 p0, INodeDefManager *ndef,
		TreeDef tree_definition)
{
	MapNode dirtnode(ndef->getId("mapgen_dirt"));
	int seed;
	if (tree_definition.explicit_seed)
	{
		seed = tree_definition.seed+14002;
	}
	else
	{
		seed = p0.X*2 + p0.Y*4 + p0.Z;      // use the tree position to seed PRNG
	}
	PseudoRandom ps(seed);

	// chance of inserting abcd rules
	double prop_a = 9;
	double prop_b = 8;
	double prop_c = 7;
	double prop_d = 6;

	//randomize tree growth level, minimum=2
	s16 iterations = tree_definition.iterations;
	if (tree_definition.iterations_random_level>0)
		iterations -= ps.range(0,tree_definition.iterations_random_level);
	if (iterations<2)
		iterations=2;

	s16 MAX_ANGLE_OFFSET = 5;
	double angle_in_radians = (double)tree_definition.angle*M_PI/180;
	double angleOffset_in_radians = (s16)(ps.range(0,1)%MAX_ANGLE_OFFSET)*M_PI/180;

	//initialize rotation matrix, position and stacks for branches
	core::matrix4 rotation;
	rotation = setRotationAxisRadians(rotation, M_PI/2,v3f(0,0,1));
	v3f position;
	position.X = p0.X;
	position.Y = p0.Y;
	position.Z = p0.Z;
	std::stack <core::matrix4> stack_orientation;
	std::stack <v3f> stack_position;

	//generate axiom
	std::string axiom = tree_definition.initial_axiom;
	for(s16 i=0; i<iterations; i++)
	{
		std::string temp = "";
		for(s16 j=0; j<(s16)axiom.size(); j++)
		{
			char axiom_char = axiom.at(j);
			switch (axiom_char)
			{
			case 'A':
				temp+=tree_definition.rules_a;
				break;
			case 'B':
				temp+=tree_definition.rules_b;
				break;
			case 'C':
				temp+=tree_definition.rules_c;
				break;
			case 'D':
				temp+=tree_definition.rules_d;
				break;
			case 'a':
				if (prop_a >= ps.range(1,10))
					temp+=tree_definition.rules_a;
				break;
			case 'b':
				if (prop_b >= ps.range(1,10))
					temp+=tree_definition.rules_b;
				break;
			case 'c':
				if (prop_c >= ps.range(1,10))
					temp+=tree_definition.rules_c;
				break;
			case 'd':
				if (prop_d >= ps.range(1,10))
					temp+=tree_definition.rules_d;
				break;
			default:
				temp+=axiom_char;
				break;
			}
		}
		axiom=temp;
	}

	//make sure tree is not floating in the air
	if (tree_definition.trunk_type == "double")
	{
		tree_node_placement(vmanip,v3f(position.X+1,position.Y-1,position.Z),dirtnode);
		tree_node_placement(vmanip,v3f(position.X,position.Y-1,position.Z+1),dirtnode);
		tree_node_placement(vmanip,v3f(position.X+1,position.Y-1,position.Z+1),dirtnode);
	}
	else if (tree_definition.trunk_type == "crossed")
	{
		tree_node_placement(vmanip,v3f(position.X+1,position.Y-1,position.Z),dirtnode);
		tree_node_placement(vmanip,v3f(position.X-1,position.Y-1,position.Z),dirtnode);
		tree_node_placement(vmanip,v3f(position.X,position.Y-1,position.Z+1),dirtnode);
		tree_node_placement(vmanip,v3f(position.X,position.Y-1,position.Z-1),dirtnode);
	}

	/* build tree out of generated axiom

	Key for Special L-System Symbols used in Axioms

    G  - move forward one unit with the pen up
    F  - move forward one unit with the pen down drawing trunks and branches
    f  - move forward one unit with the pen down drawing leaves (100% chance)
    T  - move forward one unit with the pen down drawing trunks only
    R  - move forward one unit with the pen down placing fruit
    A  - replace with rules set A
    B  - replace with rules set B
    C  - replace with rules set C
    D  - replace with rules set D
    a  - replace with rules set A, chance 90%
    b  - replace with rules set B, chance 80%
    c  - replace with rules set C, chance 70%
    d  - replace with rules set D, chance 60%
    +  - yaw the turtle right by angle degrees
    -  - yaw the turtle left by angle degrees
    &  - pitch the turtle down by angle degrees
    ^  - pitch the turtle up by angle degrees
    /  - roll the turtle to the right by angle degrees
    *  - roll the turtle to the left by angle degrees
    [  - save in stack current state info
    ]  - recover from stack state info

    */

	s16 x,y,z;
	for(s16 i=0; i<(s16)axiom.size(); i++)
	{
		char axiom_char = axiom.at(i);
		core::matrix4 temp_rotation;
		temp_rotation.makeIdentity();
		v3f dir;
		switch (axiom_char)
		{
		case 'G':
			dir = v3f(1,0,0);
			dir = transposeMatrix(rotation,dir);
			position+=dir;
			break;
		case 'T':
			tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z),tree_definition);
			if (tree_definition.trunk_type == "double" && !tree_definition.thin_branches)
			{
				tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z),tree_definition);
				tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z+1),tree_definition);
				tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z+1),tree_definition);
			}
			else if (tree_definition.trunk_type == "crossed" && !tree_definition.thin_branches)
			{
				tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z),tree_definition);
				tree_trunk_placement(vmanip,v3f(position.X-1,position.Y,position.Z),tree_definition);
				tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z+1),tree_definition);
				tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z-1),tree_definition);
			}
			dir = v3f(1,0,0);
			dir = transposeMatrix(rotation,dir);
			position+=dir;
			break;
		case 'F':
			tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z),tree_definition);
			if ((stack_orientation.empty() && tree_definition.trunk_type == "double") ||
				(!stack_orientation.empty() && tree_definition.trunk_type == "double" && !tree_definition.thin_branches))
			{
				tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z),tree_definition);
				tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z+1),tree_definition);
				tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z+1),tree_definition);
			}
			else if ((stack_orientation.empty() && tree_definition.trunk_type == "crossed") ||
				(!stack_orientation.empty() && tree_definition.trunk_type == "crossed" && !tree_definition.thin_branches))
			{
				tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z),tree_definition);
				tree_trunk_placement(vmanip,v3f(position.X-1,position.Y,position.Z),tree_definition);
				tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z+1),tree_definition);
				tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z-1),tree_definition);
			}
			if (stack_orientation.empty() == false)
			{
				s16 size = 1;
				for(x=-size; x<=size; x++)
					for(y=-size; y<=size; y++)
						for(z=-size; z<=size; z++)
							if (abs(x) == size && abs(y) == size && abs(z) == size)
							{
								tree_leaves_placement(vmanip,v3f(position.X+x+1,position.Y+y,position.Z+z),ps.next(), tree_definition);
								tree_leaves_placement(vmanip,v3f(position.X+x-1,position.Y+y,position.Z+z),ps.next(), tree_definition);
								tree_leaves_placement(vmanip,v3f(position.X+x,position.Y+y,position.Z+z+1),ps.next(), tree_definition);
								tree_leaves_placement(vmanip,v3f(position.X+x,position.Y+y,position.Z+z-1),ps.next(), tree_definition);
							}
			}
			dir = v3f(1,0,0);
			dir = transposeMatrix(rotation,dir);
			position+=dir;
			break;
		case 'f':
			tree_single_leaves_placement(vmanip,v3f(position.X,position.Y,position.Z),ps.next() ,tree_definition);
			dir = v3f(1,0,0);
			dir = transposeMatrix(rotation,dir);
			position+=dir;
			break;
		case 'R':
			tree_fruit_placement(vmanip,v3f(position.X,position.Y,position.Z),tree_definition);
			dir = v3f(1,0,0);
			dir = transposeMatrix(rotation,dir);
			position+=dir;
			break;

		// turtle orientation commands
		case '[':
			stack_orientation.push(rotation);
			stack_position.push(position);
			break;
		case ']':
			if (stack_orientation.empty())
				return UNBALANCED_BRACKETS;
			rotation=stack_orientation.top();
			stack_orientation.pop();
			position=stack_position.top();
			stack_position.pop();
			break;
		case '+':
			temp_rotation.makeIdentity();
			temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians+angleOffset_in_radians,v3f(0,0,1));
			rotation*=temp_rotation;
			break;
		case '-':
			temp_rotation.makeIdentity();
			temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians+angleOffset_in_radians,v3f(0,0,-1));
			rotation*=temp_rotation;
			break;
		case '&':
			temp_rotation.makeIdentity();
			temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians+angleOffset_in_radians,v3f(0,1,0));
			rotation*=temp_rotation;
			break;
		case '^':
			temp_rotation.makeIdentity();
			temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians+angleOffset_in_radians,v3f(0,-1,0));
			rotation*=temp_rotation;
			break;
		case '*':
			temp_rotation.makeIdentity();
			temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians,v3f(1,0,0));
			rotation*=temp_rotation;
			break;
		case '/':
			temp_rotation.makeIdentity();
			temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians,v3f(-1,0,0));
			rotation*=temp_rotation;
			break;
		default:
			break;
		}
	}

	return SUCCESS;
}

void tree_node_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
		MapNode node)
{
	v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
	if(vmanip.m_area.contains(p1) == false)
		return;
	u32 vi = vmanip.m_area.index(p1);
	if(vmanip.m_data[vi].getContent() != CONTENT_AIR
			&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
		return;
	vmanip.m_data[vmanip.m_area.index(p1)] = node;
}

void tree_trunk_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
		TreeDef &tree_definition)
{
	v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
	if(vmanip.m_area.contains(p1) == false)
		return;
	u32 vi = vmanip.m_area.index(p1);
	if(vmanip.m_data[vi].getContent() != CONTENT_AIR
			&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
		return;
	vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.trunknode;
}

void tree_leaves_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
		PseudoRandom ps ,TreeDef &tree_definition)
{
	MapNode leavesnode=tree_definition.leavesnode;
	if (ps.range(1,100) > 100-tree_definition.leaves2_chance)
		leavesnode=tree_definition.leaves2node;
	v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
	if(vmanip.m_area.contains(p1) == false)
		return;
	u32 vi = vmanip.m_area.index(p1);
	if(vmanip.m_data[vi].getContent() != CONTENT_AIR
			&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
		return;	
	if (tree_definition.fruit_chance>0)
	{
		if (ps.range(1,100) > 100-tree_definition.fruit_chance)
			vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.fruitnode;
		else
			vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
	}
	else if (ps.range(1,100) > 20)
		vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
}

void tree_single_leaves_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
		PseudoRandom ps, TreeDef &tree_definition)
{
	MapNode leavesnode=tree_definition.leavesnode;
	if (ps.range(1,100) > 100-tree_definition.leaves2_chance)
		leavesnode=tree_definition.leaves2node;
	v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
	if(vmanip.m_area.contains(p1) == false)
		return;
	u32 vi = vmanip.m_area.index(p1);
	if(vmanip.m_data[vi].getContent() != CONTENT_AIR
		&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
		return;
	vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
}

void tree_fruit_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
		TreeDef &tree_definition)
{
	v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
	if(vmanip.m_area.contains(p1) == false)
		return;
	u32 vi = vmanip.m_area.index(p1);
	if(vmanip.m_data[vi].getContent() != CONTENT_AIR
		&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
		return;
	vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.fruitnode;
}

irr::core::matrix4 setRotationAxisRadians(irr::core::matrix4 M, double angle, v3f axis)
{
	double c = cos(angle);
	double s = sin(angle);
	double t = 1.0 - c;

	double tx  = t * axis.X;
	double ty  = t * axis.Y;
	double tz  = t * axis.Z;
	double sx  = s * axis.X;
	double sy  = s * axis.Y;
	double sz  = s * axis.Z;

	M[0] = tx * axis.X + c;
	M[1] = tx * axis.Y + sz;
	M[2] = tx * axis.Z - sy;

	M[4] = ty * axis.X - sz;
	M[5] = ty * axis.Y + c;
	M[6] = ty * axis.Z + sx;

	M[8]  = tz * axis.X + sy;
	M[9]  = tz * axis.Y - sx;
	M[10] = tz * axis.Z + c;
	return M;
}

v3f transposeMatrix(irr::core::matrix4 M, v3f v)
{
	v3f translated;
	double x = M[0] * v.X + M[4] * v.Y + M[8]  * v.Z +M[12];
	double y = M[1] * v.X + M[5] * v.Y + M[9]  * v.Z +M[13];
	double z = M[2] * v.X + M[6] * v.Y + M[10] * v.Z +M[14];
	translated.X=x;
	translated.Y=y;
	translated.Z=z;
	return translated;
}

void make_jungletree(VoxelManipulator &vmanip, v3s16 p0,
		INodeDefManager *ndef, int seed)
{
	/*
		NOTE: Tree-placing code is currently duplicated in the engine
		and in games that have saplings; both are deprecated but not
		replaced yet
	*/
	content_t c_tree   = ndef->getId("mapgen_jungletree");
	content_t c_leaves = ndef->getId("mapgen_jungleleaves");
	if (c_tree == CONTENT_IGNORE)
		c_tree = ndef->getId("mapgen_tree");
	if (c_leaves == CONTENT_IGNORE)
		c_leaves = ndef->getId("mapgen_leaves");

	MapNode treenode(c_tree);
	MapNode leavesnode(c_leaves);

	PseudoRandom pr(seed);
	for(s16 x=-1; x<=1; x++)
	for(s16 z=-1; z<=1; z++)
	{
		if(pr.range(0, 2) == 0)
			continue;
		v3s16 p1 = p0 + v3s16(x,0,z);
		v3s16 p2 = p0 + v3s16(x,-1,z);
		u32 vi1 = vmanip.m_area.index(p1);
		u32 vi2 = vmanip.m_area.index(p2);
		
		if (vmanip.m_area.contains(p2) &&
			vmanip.m_data[vi2].getContent() == CONTENT_AIR)
			vmanip.m_data[vi2] = treenode;
		else if (vmanip.m_area.contains(p1) &&
				vmanip.m_data[vi1].getContent() == CONTENT_AIR)
			vmanip.m_data[vi1] = treenode;
	}
	vmanip.m_data[vmanip.m_area.index(p0)] = treenode;

	s16 trunk_h = pr.range(8, 12);
	v3s16 p1 = p0;
	for (s16 ii=0; ii<trunk_h; ii++)
	{
		if (vmanip.m_area.contains(p1)) {
			u32 vi = vmanip.m_area.index(p1);
			if (vmanip.m_data[vi].getContent() == CONTENT_AIR)
				vmanip.m_data[vi] = treenode;
		}
		p1.Y++;
	}

	// p1 is now the last piece of the trunk
	p1.Y -= 1;

	VoxelArea leaves_a(v3s16(-3,-2,-3), v3s16(3,2,3));
	//SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
	Buffer<u8> leaves_d(leaves_a.getVolume());
	for(s32 i=0; i<leaves_a.getVolume(); i++)
		leaves_d[i] = 0;

	// Force leaves at near the end of the trunk
	{
		s16 d = 1;
		for(s16 z=-d; z<=d; z++)
		for(s16 y=-d; y<=d; y++)
		for(s16 x=-d; x<=d; x++)
		{
			leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
		}
	}

	// Add leaves randomly
	for(u32 iii=0; iii<30; iii++)
	{
		s16 d = 1;

		v3s16 p(
			pr.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
			pr.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
			pr.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
		);

		for(s16 z=0; z<=d; z++)
		for(s16 y=0; y<=d; y++)
		for(s16 x=0; x<=d; x++)
		{
			leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
		}
	}

	// Blit leaves to vmanip
	for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
	for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
	for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
	{
		v3s16 p(x,y,z);
		p += p1;
		if(vmanip.m_area.contains(p) == false)
			continue;
		u32 vi = vmanip.m_area.index(p);
		if (vmanip.m_data[vi].getContent() != CONTENT_AIR &&
			vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
			continue;
		u32 i = leaves_a.index(x,y,z);
		if(leaves_d[i] == 1)
			vmanip.m_data[vi] = leavesnode;
	}
}

}; // namespace treegen