L-System treegen
authorRealBadAngel <mk@realbadangel.pl>
Sat, 29 Dec 2012 15:20:09 +0000 (16:20 +0100)
committerIlya Zhuravlev <zhuravlevilya@ya.ru>
Sun, 30 Dec 2012 09:42:19 +0000 (13:42 +0400)
doc/lua_api.txt
src/CMakeLists.txt
src/content_abm.cpp
src/mapgen.cpp
src/mapgen.h
src/scriptapi.cpp
src/treegen.cpp [new file with mode: 0644]
src/treegen.h [new file with mode: 0644]

index a9944935fe0bda5d58f9371afbf008d578b5a7d4..117d4b24e9624511e603e353ecd1b60e109c69b3 100644 (file)
@@ -1063,6 +1063,60 @@ methods:
   ^ Return world-specific perlin noise (int(worldseed)+seeddiff)
 - clear_objects()
   ^ clear all objects in the environments 
+- spawn_tree (pos, {treedef})
+  ^ spawns L-System tree at given pos with definition in treedef table
+  treedef={
+      axiom,        - string    initial tree axiom
+      rules_a,      - string    rules set A
+      rules_b,      - string    rules set B
+      rules_c,      - string    rules set C
+      rules_d,      - string    rules set D
+      trunk,        - string    trunk node name
+      leaves,       - string    leaves node name
+      angle,        - num       angle in deg
+      iterations,   - num       max # of iterations, usually 2 -5
+      random_level, - num       factor to lower nr of iterations, usually 0 - 3
+      thin_trunks,  - boolean   true -> use thin trunks
+      fruit_tree,   - boolean   true -> is a fruit tree
+      fruit         - string    fruit node name
+  }
+
+  Key for Special L-System Symbols used in Axioms
+    G  - move forward one unit with the pin down
+    F  - move forward one unit with the pin up
+    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 parameter
+    -  - yaw the turtle left by angle parameter
+    &  - pitch the turtle down by angle parameter
+    ^  - pitch the turtle up by angle parameter
+    /  - roll the turtle to the right by angle parameter
+    *  - roll the turtle to the left by angle parameter
+    [  - save in stack current state info
+    ]  - recover from stack state info
+
+  Example usage: spawn small apple tree
+  apple_tree={
+      axiom="FFFFFAFFBF",
+      rules_a="[&&&FFFFF&&FFFF][&&&++++FFFFF&&FFFF][&&&----FFFFF&&FFFF]",
+      rules_b="[&&&++FFFFF&&FFFF][&&&--FFFFF&&FFFF][&&&------FFFFF&&FFFF]",
+      trunk="default:tree",
+      leaves="default:leaves",
+      angle=30,
+      iterations=2,
+      random_level=0,
+      thin_trunks=true,
+      fruit_tree=true,
+      fruit="default:apple"
+  }
+  minetest.env:spawn_tree(pos,apple_tree)
+
 Deprecated:
 - add_rat(pos): Add C++ rat object (no-op)
 - add_firefly(pos): Add C++ firefly object (no-op)
index 107f580cef7dff96adf1203a2bb04e7306d3187f..5e1bfd65d684c624bb1a6d472fea8f86614451de 100644 (file)
@@ -188,6 +188,7 @@ set(common_SRCS
        log.cpp
        content_sao.cpp
        mapgen.cpp
+       treegen.cpp
        content_nodemeta.cpp
        content_mapnode.cpp
        collision.cpp
index 12dbeea8e99a80a65d20df3f28b3abbf5e2e1f53..0d0f0dedb713e89f5de43812a2c3ce068a2ed8f9 100644 (file)
@@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "content_sao.h"
 #include "settings.h"
 #include "mapblock.h" // For getNodeBlockPos
-#include "mapgen.h" // For mapgen::make_tree
+#include "treegen.h" // For treegen::make_tree
 #include "map.h"
 
 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
@@ -118,7 +118,7 @@ public:
                v3s16 tree_blockp = getNodeBlockPos(tree_p);
                vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
                bool is_apple_tree = myrand()%4 == 0;
-               mapgen::make_tree(vmanip, tree_p, is_apple_tree, ndef);
+               treegen::make_tree(vmanip, tree_p, is_apple_tree, ndef);
                vmanip.blitBackAll(&modified_blocks);
 
                // update lighting
index 782f00b62bb0de7e7e10c0285b0f7d3d2bfe4f10..ae0c551cb5d6d84cdb311034e229fdf209cd69f5 100644 (file)
@@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "voxelalgorithms.h"
 #include "profiler.h"
 #include "main.h" // For g_profiler
+#include "treegen.h"
 
 namespace mapgen
 {
@@ -120,174 +121,7 @@ static s16 find_stone_level(VoxelManipulator &vmanip, v2s16 p2d,
 }
 #endif
 
-void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
-               bool is_apple_tree, INodeDefManager *ndef)
-{
-       MapNode treenode(ndef->getId("mapgen_tree"));
-       MapNode leavesnode(ndef->getId("mapgen_leaves"));
-       MapNode applenode(ndef->getId("mapgen_apple"));
-       
-       s16 trunk_h = myrand_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(
-                       myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
-                       myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
-                       myrand_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 = myrand_range(0,99) < 10;
-                       if(is_apple_tree && is_apple) {
-                               vmanip.m_data[vi] = applenode;
-                       } else {
-                               vmanip.m_data[vi] = leavesnode;
-                       }
-               }
-       }
-}
-
 #if 0
-static void make_jungletree(VoxelManipulator &vmanip, v3s16 p0,
-               INodeDefManager *ndef)
-{
-       MapNode treenode(ndef->getId("mapgen_jungletree"));
-       MapNode leavesnode(ndef->getId("mapgen_leaves"));
-
-       for(s16 x=-1; x<=1; x++)
-       for(s16 z=-1; z<=1; z++)
-       {
-               if(myrand_range(0, 2) == 0)
-                       continue;
-               v3s16 p1 = p0 + v3s16(x,0,z);
-               v3s16 p2 = p0 + v3s16(x,-1,z);
-               if(vmanip.m_area.contains(p2)
-                               && vmanip.m_data[vmanip.m_area.index(p2)] == CONTENT_AIR)
-                       vmanip.m_data[vmanip.m_area.index(p2)] = treenode;
-               else if(vmanip.m_area.contains(p1))
-                       vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
-       }
-
-       s16 trunk_h = myrand_range(8, 12);
-       v3s16 p1 = p0;
-       for(s16 ii=0; ii<trunk_h; ii++)
-       {
-               if(vmanip.m_area.contains(p1))
-                       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(-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(
-                       myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
-                       myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
-                       myrand_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;
-       }
-}
 
 static void make_papyrus(VoxelManipulator &vmanip, v3s16 p0,
                INodeDefManager *ndef)
@@ -2239,7 +2073,7 @@ void make_block(BlockMakeData *data)
                                }
                                p.Y++;
                                // Make a tree
-                               make_tree(vmanip, p, false, ndef);
+                               treegen::make_tree(vmanip, p, false, ndef);
                        }
                }
        }
index e2f20ecafdb92544390b8bd2a3ad66b6a27aa660..8986ddab0cb2898e9e92d0d4fc08ebe83150c449 100644 (file)
@@ -42,10 +42,6 @@ namespace mapgen
        // Main map generation routine
        void make_block(BlockMakeData *data);
        
-       // Add a tree
-       void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
-                       bool is_apple_tree, INodeDefManager *ndef);
-       
        /*
                These are used by FarMesh
        */
index e26e880a99095173c5d5e7b544ff1407f1a57ddf..d086b7e510549242b6005045df246ac9b9e31958 100644 (file)
@@ -48,6 +48,7 @@ extern "C" {
 #include "noise.h" // PseudoRandom for LuaPseudoRandom
 #include "util/pointedthing.h"
 #include "rollback.h"
+#include "treegen.h"
 
 static void stackDump(lua_State *L, std::ostream &o)
 {
@@ -4004,6 +4005,45 @@ private:
                return 0;
        }
 
+       static int l_spawn_tree(lua_State *L)
+       {
+               EnvRef *o = checkobject(L, 1);
+               ServerEnvironment *env = o->m_env;
+               if(env == NULL) return 0;
+               v3s16 p0 = read_v3s16(L, 2);
+
+               treegen::TreeDef tree_def;
+               std::string trunk,leaves,fruit;
+               INodeDefManager *ndef = env->getGameDef()->ndef();
+
+               if(lua_istable(L, 3))
+               {
+                       getstringfield(L, 3, "axiom", tree_def.initial_axiom);
+                       getstringfield(L, 3, "rules_a", tree_def.rules_a);
+                       getstringfield(L, 3, "rules_b", tree_def.rules_b);
+                       getstringfield(L, 3, "rules_c", tree_def.rules_a);
+                       getstringfield(L, 3, "rules_d", tree_def.rules_b);
+                       getstringfield(L, 3, "trunk", trunk);
+                       tree_def.trunknode=ndef->getId(trunk);
+                       getstringfield(L, 3, "leaves", leaves);
+                       tree_def.leavesnode=ndef->getId(leaves);
+                       getintfield(L, 3, "angle", tree_def.angle);
+                       getintfield(L, 3, "iterations", tree_def.iterations);
+                       getintfield(L, 3, "random_level", tree_def.iterations_random_level);
+                       getboolfield(L, 3, "thin_trunks", tree_def.thin_trunks);
+                       getboolfield(L, 3, "fruit_tree", tree_def.fruit_tree);
+                       if (tree_def.fruit_tree)
+                       {
+                               getstringfield(L, 3, "fruit", fruit);
+                               tree_def.fruitnode=ndef->getId(fruit);
+                       }
+               }
+               else
+                       return 0;
+               treegen::spawn_ltree (env, p0, ndef, tree_def);
+               return 1;
+       }
+
 public:
        EnvRef(ServerEnvironment *env):
                m_env(env)
@@ -4086,6 +4126,7 @@ const luaL_reg EnvRef::methods[] = {
        method(EnvRef, find_nodes_in_area),
        method(EnvRef, get_perlin),
        method(EnvRef, clear_objects),
+       method(EnvRef, spawn_tree),
        {0,0}
 };
 
diff --git a/src/treegen.cpp b/src/treegen.cpp
new file mode 100644 (file)
index 0000000..3354f97
--- /dev/null
@@ -0,0 +1,518 @@
+/*
+Minetest-c55
+Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>,
+                                  2012 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 "noise.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)
+{
+       MapNode treenode(ndef->getId("mapgen_tree"));
+       MapNode leavesnode(ndef->getId("mapgen_leaves"));
+       MapNode applenode(ndef->getId("mapgen_apple"));
+
+       s16 trunk_h = myrand_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(
+                       myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
+                       myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
+                       myrand_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 = myrand_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
+void spawn_ltree (ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef, TreeDef tree_definition)
+{
+       ServerMap *map = &env->getServerMap();
+       core::map<v3s16, MapBlock*> modified_blocks;
+       ManualMapVoxelManipulator vmanip(map);
+       v3s16 tree_blockp = getNodeBlockPos(p0);
+       vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
+       make_ltree (vmanip, p0, ndef, tree_definition);
+       vmanip.blitBackAll(&modified_blocks);
+
+       // update lighting
+       core::map<v3s16, MapBlock*> lighting_modified_blocks;
+       for(core::map<v3s16, MapBlock*>::Iterator
+               i = modified_blocks.getIterator();
+               i.atEnd() == false; i++)
+       {
+               lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
+       }
+       map->updateLighting(lighting_modified_blocks, modified_blocks);
+       // Send a MEET_OTHER event
+       MapEditEvent event;
+       event.type = MEET_OTHER;
+       for(core::map<v3s16, MapBlock*>::Iterator
+               i = modified_blocks.getIterator();
+               i.atEnd() == false; i++)
+       {
+               v3s16 p = i.getNode()->getKey();
+               event.modified_blocks.insert(p, true);
+       }
+       map->dispatchEvent(&event);
+}
+
+//L-System tree generator
+void make_ltree(ManualMapVoxelManipulator &vmanip, v3s16 p0, INodeDefManager *ndef,
+               TreeDef tree_definition)
+{
+       MapNode dirtnode(ndef->getId("mapgen_dirt"));
+
+       // 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 -= myrand_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)(myrand_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 = v3f(0,0,0);
+       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 >= myrand_range(1,10))
+                                       temp+=tree_definition.rules_a;
+                               break;
+                       case 'b':
+                               if (prop_b >= myrand_range(1,10))
+                                       temp+=tree_definition.rules_b;
+                               break;
+                       case 'c':
+                               if (prop_c >= myrand_range(1,10))
+                                       temp+=tree_definition.rules_c;
+                               break;
+                       case 'd':
+                               if (prop_d >= myrand_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.thin_trunks == false)
+       {
+               make_tree_node_placement(vmanip,v3f(p0.X+position.X+1,p0.Y+position.Y-1,p0.Z+position.Z),dirtnode);
+               make_tree_node_placement(vmanip,v3f(p0.X+position.X-1,p0.Y+position.Y-1,p0.Z+position.Z),dirtnode);
+               make_tree_node_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y-1,p0.Z+position.Z+1),dirtnode);
+               make_tree_node_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y-1,p0.Z+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 pin down
+    F  - move forward one unit with the pin up
+    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 'F':
+                       make_tree_trunk_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y,p0.Z+position.Z),tree_definition);
+                       if (tree_definition.thin_trunks == false)
+                       {
+                               make_tree_trunk_placement(vmanip,v3f(p0.X+position.X+1,p0.Y+position.Y,p0.Z+position.Z),tree_definition);
+                               make_tree_trunk_placement(vmanip,v3f(p0.X+position.X-1,p0.Y+position.Y,p0.Z+position.Z),tree_definition);
+                               make_tree_trunk_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y,p0.Z+position.Z+1),tree_definition);
+                               make_tree_trunk_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y,p0.Z+position.Z-1),tree_definition);
+                       }
+                       if (stack_orientation.empty() == false)
+                       {
+                               s16 size = 1;
+                               for(x=-size; x<size+1; x++)
+                                       for(y=-size; y<size+1; y++)
+                                               for(z=-size; z<size+1; z++)
+                                                       if (abs(x) == size and abs(y) == size and abs(z) == size)
+                                                       {
+                                                               make_tree_leaves_placement(vmanip,v3f(p0.X+position.X+x+1,p0.Y+position.Y+y,p0.Z+position.Z+z),tree_definition);
+                                                               make_tree_leaves_placement(vmanip,v3f(p0.X+position.X+x-1,p0.Y+position.Y+y,p0.Z+position.Z+z),tree_definition);
+                                                               make_tree_leaves_placement(vmanip,v3f(p0.X+position.X+x,p0.Y+position.Y+y,p0.Z+position.Z+z+1),tree_definition);
+                                                               make_tree_leaves_placement(vmanip,v3f(p0.X+position.X+x,p0.Y+position.Y+y,p0.Z+position.Z+z-1),tree_definition);
+                                                       }
+                       }
+                       dir = v3f(1,0,0);
+                       dir = transposeMatrix(rotation,dir);
+                       position+=dir;
+                       break;
+               // turtle commands
+               case '[':
+                       stack_orientation.push(rotation);
+                       stack_position.push(position);
+                       break;
+               case ']':
+                       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;
+               }
+       }
+}
+
+void make_tree_node_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
+               MapNode node)
+{
+       v3s16 p1 = v3s16(round(p0.X),round(p0.Y),round(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 make_tree_trunk_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
+               TreeDef &tree_definition)
+{
+       v3s16 p1 = v3s16(round(p0.X),round(p0.Y),round(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 make_tree_leaves_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
+               TreeDef &tree_definition)
+{
+       v3s16 p1 = v3s16(round(p0.X),round(p0.Y),round(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_tree)
+       {
+               if (myrand_range(1,100) > 90+tree_definition.iterations)
+                       vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.fruitnode;
+               else
+                       vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.leavesnode;
+       }
+       else if (myrand_range(1,100) > 20)
+               vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.leavesnode;
+}
+
+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;
+}
+
+#if 0
+static void make_jungletree(VoxelManipulator &vmanip, v3s16 p0,
+               INodeDefManager *ndef)
+{
+       MapNode treenode(ndef->getId("mapgen_jungletree"));
+       MapNode leavesnode(ndef->getId("mapgen_leaves"));
+
+       for(s16 x=-1; x<=1; x++)
+       for(s16 z=-1; z<=1; z++)
+       {
+               if(myrand_range(0, 2) == 0)
+                       continue;
+               v3s16 p1 = p0 + v3s16(x,0,z);
+               v3s16 p2 = p0 + v3s16(x,-1,z);
+               if(vmanip.m_area.contains(p2)
+                               && vmanip.m_data[vmanip.m_area.index(p2)] == CONTENT_AIR)
+                       vmanip.m_data[vmanip.m_area.index(p2)] = treenode;
+               else if(vmanip.m_area.contains(p1))
+                       vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
+       }
+
+       s16 trunk_h = myrand_range(8, 12);
+       v3s16 p1 = p0;
+       for(s16 ii=0; ii<trunk_h; ii++)
+       {
+               if(vmanip.m_area.contains(p1))
+                       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(-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(
+                       myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
+                       myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
+                       myrand_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;
+       }
+}
+#endif
+
+}; // namespace treegen
diff --git a/src/treegen.h b/src/treegen.h
new file mode 100644 (file)
index 0000000..62a220e
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+Minetest-c55
+Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>,
+                                  2012 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.
+*/
+
+#ifndef TREEGEN_HEADER
+#define TREEGEN_HEADER
+
+#include <matrix4.h>
+
+class ManualMapVoxelManipulator;
+class INodeDefManager;
+
+
+namespace treegen
+{
+
+struct TreeDef
+{
+std::string initial_axiom;
+std::string rules_a;
+std::string rules_b;
+std::string rules_c;
+std::string rules_d;
+MapNode trunknode;
+MapNode leavesnode;
+int angle;
+int iterations;
+int iterations_random_level;
+bool thin_trunks;
+bool fruit_tree;
+MapNode fruitnode;
+};
+
+       // Add default tree
+       void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
+                       bool is_apple_tree, INodeDefManager *ndef);
+       
+       // Add L-Systems tree (used by engine)
+       void make_ltree(ManualMapVoxelManipulator &vmanip, v3s16 p0, INodeDefManager *ndef,
+               TreeDef tree_definition);
+       // Spawn L-systems tree from LUA
+       void spawn_ltree (ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef, TreeDef tree_definition);    
+       
+       // L-System tree gen helper functions
+       void make_tree_node_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
+               MapNode node);
+       void make_tree_trunk_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
+               TreeDef &tree_definition);
+       void make_tree_leaves_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
+               TreeDef &tree_definition);      
+       irr::core::matrix4 setRotationAxisRadians(irr::core::matrix4 M, double angle,v3f axis);
+       
+       v3f transposeMatrix(irr::core::matrix4 M ,v3f v);
+                       
+}; // namespace treegen
+#endif