/* Minetest Copyright (C) 2010-2018 celeron55, Perttu Ahola , Copyright (C) 2012-2018 RealBadAngel, Maciej Kasatkin Copyright (C) 2015-2018 paramat This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "irr_v3d.h" #include #include "util/pointer.h" #include "util/numeric.h" #include "map.h" #include "mapblock.h" #include "nodedef.h" #include "treegen.h" #include "voxelalgorithms.h" namespace treegen { void make_tree(MMVManip &vmanip, v3s16 p0, bool is_apple_tree, const NodeDefManager *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")); if (treenode == CONTENT_IGNORE) errorstream << "Treegen: Mapgen alias 'mapgen_tree' is invalid!" << std::endl; if (leavesnode == CONTENT_IGNORE) errorstream << "Treegen: Mapgen alias 'mapgen_leaves' is invalid!" << std::endl; if (applenode == CONTENT_IGNORE) errorstream << "Treegen: Mapgen alias 'mapgen_apple' is invalid!" << std::endl; 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 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(ServerMap *map, v3s16 p0, const NodeDefManager *ndef, const TreeDef &tree_definition) { std::map 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, const N# - Find vorbis # Find the native vorbis includes and libraries # # VORBIS_INCLUDE_DIR - where to find vorbis.h, etc. # OGG_INCLUDE_DIR - where to find ogg/ogg.h, etc. # VORBIS_LIBRARIES - List of libraries when using vorbis(file). # VORBIS_FOUND - True if vorbis found. if(NOT GP2XWIZ) if(VORBIS_INCLUDE_DIR) # Already in cache, be silent set(VORBIS_FIND_QUIETLY TRUE) endif(VORBIS_INCLUDE_DIR) find_path(OGG_INCLUDE_DIR ogg/ogg.h) find_path(VORBIS_INCLUDE_DIR vorbis/vorbisfile.h) # MSVC built ogg/vorbis may be named ogg_static and vorbis_static find_library(OGG_LIBRARY NAMES ogg ogg_static) find_library(VORBIS_LIBRARY NAMES vorbis vorbis_static) find_library(VORBISFILE_LIBRARY NAMES vorbisfile vorbisfile_static) # Handle the QUIETLY and REQUIRED arguments and set VORBIS_FOUND # to TRUE if all listed variables are TRUE. include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Vorbis DEFAULT_MSG OGG_INCLUDE_DIR VORBIS_INCLUDE_DIR OGG_LIBRARY VORBIS_LIBRARY VORBISFILE_LIBRARY) else(NOT GP2XWIZ) find_path(VORBIS_INCLUDE_DIR tremor/ivorbisfile.h) find_library(VORBIS_LIBRARY NAMES vorbis_dec) find_package_handle_standard_args(Vorbis DEFAULT_MSG VORBIS_INCLUDE_DIR VORBIS_LIBRARY) endif(NOT GP2XWIZ) if(VORBIS_FOUND) if(NOT GP2XWIZ) set(VORBIS_LIBRARIES ${VORBISFILE_LIBRARY} ${VORBIS_LIBRARY} ${OGG_LIBRARY}) else(NOT GP2XWIZ) set(VORBIS_LIBRARIES ${VORBIS_LIBRARY}) endif(NOT GP2XWIZ) else(VORBIS_FOUND) set(VORBIS_LIBRARIES) endif(VORBIS_FOUND) mark_as_advanced(OGG_INCLUDE_DIR VORBIS_INCLUDE_DIR) mark_as_advanced(OGG_LIBRARY VORBIS_LIBRARY VORBISFILE_LIBRARY) 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_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, const NodeDefManager *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"); if (c_tree == CONTENT_IGNORE) errorstream << "Treegen: Mapgen alias 'mapgen_jungletree' is invalid!" << std::endl; if (c_leaves == CONTENT_IGNORE) errorstream << "Treegen: Mapgen alias 'mapgen_jungleleaves' is invalid!" << std::endl; 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 leaves_d(new u8[leaves_a.getVolume()]); Buffer 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, const NodeDefManager *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; if (c_tree == CONTENT_IGNORE) errorstream << "Treegen: Mapgen alias 'mapgen_pine_tree' is invalid!" << std::endl; if (c_leaves == CONTENT_IGNORE) errorstream << "Treegen: Mapgen alias 'mapgen_pine_needles' is invalid!" << std::endl; 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 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