aboutsummaryrefslogtreecommitdiff
path: root/src/mapgen/treegen.cpp
diff options
context:
space:
mode:
authorVitaliy <silverunicorn2011@yandex.ru>2017-11-09 01:56:20 +0300
committerLoïc Blot <nerzhul@users.noreply.github.com>2017-11-08 23:56:20 +0100
commit20a85d76d94c9c5c7fbe198c3d0e1fbee97a485f (patch)
tree67378802190117d8271b3b6d489a92bcb16203b7 /src/mapgen/treegen.cpp
parentfc9747eb4b7f75e59a28957bc50f7a78256b3d66 (diff)
downloadminetest-20a85d76d94c9c5c7fbe198c3d0e1fbee97a485f.tar.gz
minetest-20a85d76d94c9c5c7fbe198c3d0e1fbee97a485f.tar.bz2
minetest-20a85d76d94c9c5c7fbe198c3d0e1fbee97a485f.zip
Move files to subdirectories (#6599)
* Move files around
Diffstat (limited to 'src/mapgen/treegen.cpp')
-rw-r--r--src/mapgen/treegen.cpp872
1 files changed, 872 insertions, 0 deletions
diff --git a/src/mapgen/treegen.cpp b/src/mapgen/treegen.cpp
new file mode 100644
index 000000000..9e11b1a06
--- /dev/null
+++ b/src/mapgen/treegen.cpp
@@ -0,0 +1,872 @@
+/*
+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 "map.h"
+#include "mapblock.h"
+#include "serverenvironment.h"
+#include "nodedef.h"
+#include "treegen.h"
+#include "voxelalgorithms.h"
+
+namespace treegen
+{
+
+void make_tree(MMVManip &vmanip, v3s16 p0,
+ bool is_apple_tree, INodeDefManager *ndef, s32 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)) {
+ u32 vi = vmanip.m_area.index(p1);
+ vmanip.m_data[vi] = 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));
+ 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++) {
+ 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++) {
+ v3s16 pmin(leaves_a.MinEdge.X, y, z);
+ u32 i = leaves_a.index(pmin);
+ u32 vi = vmanip.m_area.index(pmin + p1);
+ for (s16 x = leaves_a.MinEdge.X; x <= leaves_a.MaxEdge.X; x++) {
+ v3s16 p(x, y, z);
+ if (vmanip.m_area.contains(p + p1) &&
+ (vmanip.m_data[vi].getContent() == CONTENT_AIR ||
+ vmanip.m_data[vi].getContent() == CONTENT_IGNORE)) {
+ 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;
+ }
+ }
+ vi++;
+ i++;
+ }
+ }
+}
+
+
+// L-System tree LUA spawner
+treegen::error spawn_ltree(ServerEnvironment *env, v3s16 p0,
+ INodeDefManager *ndef, const TreeDef &tree_definition)
+{
+ ServerMap *map = &env->getServerMap();
+ std::map<v3s16, MapBlock*> modified_blocks;
+ MMVManip 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;
+
+ voxalgo::blit_back_with_light(map, &vmanip, &modified_blocks);
+
+ // Send a MEET_OTHER event
+ MapEditEvent event;
+ event.type = MEET_OTHER;
+ for (auto &modified_block : modified_blocks)
+ event.modified_blocks.insert(modified_block.first);
+ map->dispatchEvent(&event);
+ return SUCCESS;
+}
+
+
+//L-System tree generator
+treegen::error make_ltree(MMVManip &vmanip, v3s16 p0,
+ INodeDefManager *ndef, TreeDef tree_definition)
+{
+ MapNode dirtnode(ndef->getId("mapgen_dirt"));
+ s32 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()) {
+ 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(MMVManip &vmanip, v3f p0, MapNode node)
+{
+ v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
+ if (!vmanip.m_area.contains(p1))
+ 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(MMVManip &vmanip, v3f p0, TreeDef &tree_definition)
+{
+ v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
+ if (!vmanip.m_area.contains(p1))
+ return;
+ u32 vi = vmanip.m_area.index(p1);
+ content_t current_node = vmanip.m_data[vi].getContent();
+ if (current_node != CONTENT_AIR && current_node != CONTENT_IGNORE
+ && current_node != tree_definition.leavesnode.getContent()
+ && current_node != tree_definition.leaves2node.getContent()
+ && current_node != tree_definition.fruitnode.getContent())
+ return;
+ vmanip.m_data[vi] = tree_definition.trunknode;
+}
+
+
+void tree_leaves_placement(MMVManip &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))
+ 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(MMVManip &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))
+ 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(MMVManip &vmanip, v3f p0, TreeDef &tree_definition)
+{
+ v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
+ if (!vmanip.m_area.contains(p1))
+ 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(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef, s32 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);
+ 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++) {
+ 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++) {
+ v3s16 pmin(leaves_a.MinEdge.X, y, z);
+ u32 i = leaves_a.index(pmin);
+ u32 vi = vmanip.m_area.index(pmin + p1);
+ for (s16 x = leaves_a.MinEdge.X; x <= leaves_a.MaxEdge.X; x++) {
+ v3s16 p(x, y, z);
+ if (vmanip.m_area.contains(p + p1) &&
+ (vmanip.m_data[vi].getContent() == CONTENT_AIR ||
+ vmanip.m_data[vi].getContent() == CONTENT_IGNORE)) {
+ if (leaves_d[i] == 1)
+ vmanip.m_data[vi] = leavesnode;
+ }
+ vi++;
+ i++;
+ }
+ }
+}
+
+
+void make_pine_tree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef, s32 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_pine_tree");
+ content_t c_leaves = ndef->getId("mapgen_pine_needles");
+ content_t c_snow = ndef->getId("mapgen_snow");
+ if (c_tree == CONTENT_IGNORE)
+ c_tree = ndef->getId("mapgen_tree");
+ if (c_leaves == CONTENT_IGNORE)
+ c_leaves = ndef->getId("mapgen_leaves");
+ if (c_snow == CONTENT_IGNORE)
+ c_snow = CONTENT_AIR;
+
+ MapNode treenode(c_tree);
+ MapNode leavesnode(c_leaves);
+ MapNode snownode(c_snow);
+
+ PseudoRandom pr(seed);
+ u16 trunk_h = pr.range(9, 13);
+ v3s16 p1 = p0;
+ for (u16 ii = 0; ii < trunk_h; ii++) {
+ if (vmanip.m_area.contains(p1)) {
+ u32 vi = vmanip.m_area.index(p1);
+ vmanip.m_data[vi] = treenode;
+ }
+ p1.Y++;
+ }
+
+ // Make p1 the top node of the trunk
+ p1.Y -= 1;
+
+ VoxelArea leaves_a(v3s16(-3, -6, -3), v3s16(3, 3, 3));
+ Buffer<u8> leaves_d(leaves_a.getVolume());
+ for (s32 i = 0; i < leaves_a.getVolume(); i++)
+ leaves_d[i] = 0;
+
+ // Upper branches
+ u16 dev = 3;
+ for (s16 yy = -1; yy <= 1; yy++) {
+ for (s16 zz = -dev; zz <= dev; zz++) {
+ u32 i = leaves_a.index(v3s16(-dev, yy, zz));
+ u32 ia = leaves_a.index(v3s16(-dev, yy+1, zz));
+ for (s16 xx = -dev; xx <= dev; xx++) {
+ if (pr.range(0, 20) <= 19 - dev) {
+ leaves_d[i] = 1;
+ leaves_d[ia] = 2;
+ }
+ i++;
+ ia++;
+ }
+ }
+ dev--;
+ }
+
+ // Centre top nodes
+ leaves_d[leaves_a.index(v3s16(0, 1, 0))] = 1;
+ leaves_d[leaves_a.index(v3s16(0, 2, 0))] = 1;
+ leaves_d[leaves_a.index(v3s16(0, 3, 0))] = 2;
+
+ // Lower branches
+ s16 my = -6;
+ for (u32 iii = 0; iii < 20; iii++) {
+ s16 xi = pr.range(-3, 2);
+ s16 yy = pr.range(-6, -5);
+ s16 zi = pr.range(-3, 2);
+ if (yy > my)
+ my = yy;
+ for (s16 zz = zi; zz <= zi + 1; zz++) {
+ u32 i = leaves_a.index(v3s16(xi, yy, zz));
+ u32 ia = leaves_a.index(v3s16(xi, yy + 1, zz));
+ for (s32 xx = xi; xx <= xi + 1; xx++) {
+ leaves_d[i] = 1;
+ if (leaves_d[ia] == 0)
+ leaves_d[ia] = 2;
+ i++;
+ ia++;
+ }
+ }
+ }
+
+ dev = 2;
+ for (s16 yy = my + 1; yy <= my + 2; yy++) {
+ for (s16 zz = -dev; zz <= dev; zz++) {
+ u32 i = leaves_a.index(v3s16(-dev, yy, zz));
+ u32 ia = leaves_a.index(v3s16(-dev, yy + 1, zz));
+ for (s16 xx = -dev; xx <= dev; xx++) {
+ if (pr.range(0, 20) <= 19 - dev) {
+ leaves_d[i] = 1;
+ leaves_d[ia] = 2;
+ }
+ i++;
+ ia++;
+ }
+ }
+ dev--;
+ }
+
+ // 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++) {
+ v3s16 pmin(leaves_a.MinEdge.X, y, z);
+ u32 i = leaves_a.index(pmin);
+ u32 vi = vmanip.m_area.index(pmin + p1);
+ for (s16 x = leaves_a.MinEdge.X; x <= leaves_a.MaxEdge.X; x++) {
+ v3s16 p(x, y, z);
+ if (vmanip.m_area.contains(p + p1) &&
+ (vmanip.m_data[vi].getContent() == CONTENT_AIR ||
+ vmanip.m_data[vi].getContent() == CONTENT_IGNORE ||
+ vmanip.m_data[vi] == snownode)) {
+ if (leaves_d[i] == 1)
+ vmanip.m_data[vi] = leavesnode;
+ else if (leaves_d[i] == 2)
+ vmanip.m_data[vi] = snownode;
+ }
+ vi++;
+ i++;
+ }
+ }
+}
+
+}; // namespace treegen