L-System treegen

This commit is contained in:
RealBadAngel 2012-12-29 16:20:09 +01:00 committed by Ilya Zhuravlev
parent 45029117f5
commit b40e599866
8 changed files with 689 additions and 174 deletions

View File

@ -1063,6 +1063,60 @@ methods:
^ Return world-specific perlin noise (int(worldseed)+seeddiff) ^ Return world-specific perlin noise (int(worldseed)+seeddiff)
- clear_objects() - clear_objects()
^ clear all objects in the environments ^ 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: Deprecated:
- add_rat(pos): Add C++ rat object (no-op) - add_rat(pos): Add C++ rat object (no-op)
- add_firefly(pos): Add C++ firefly object (no-op) - add_firefly(pos): Add C++ firefly object (no-op)

View File

@ -188,6 +188,7 @@ set(common_SRCS
log.cpp log.cpp
content_sao.cpp content_sao.cpp
mapgen.cpp mapgen.cpp
treegen.cpp
content_nodemeta.cpp content_nodemeta.cpp
content_mapnode.cpp content_mapnode.cpp
collision.cpp collision.cpp

View File

@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "content_sao.h" #include "content_sao.h"
#include "settings.h" #include "settings.h"
#include "mapblock.h" // For getNodeBlockPos #include "mapblock.h" // For getNodeBlockPos
#include "mapgen.h" // For mapgen::make_tree #include "treegen.h" // For treegen::make_tree
#include "map.h" #include "map.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
@ -118,7 +118,7 @@ public:
v3s16 tree_blockp = getNodeBlockPos(tree_p); v3s16 tree_blockp = getNodeBlockPos(tree_p);
vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1)); vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
bool is_apple_tree = myrand()%4 == 0; 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); vmanip.blitBackAll(&modified_blocks);
// update lighting // update lighting

View File

@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "voxelalgorithms.h" #include "voxelalgorithms.h"
#include "profiler.h" #include "profiler.h"
#include "main.h" // For g_profiler #include "main.h" // For g_profiler
#include "treegen.h"
namespace mapgen namespace mapgen
{ {
@ -120,174 +121,7 @@ static s16 find_stone_level(VoxelManipulator &vmanip, v2s16 p2d,
} }
#endif #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 #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, static void make_papyrus(VoxelManipulator &vmanip, v3s16 p0,
INodeDefManager *ndef) INodeDefManager *ndef)
@ -2239,7 +2073,7 @@ void make_block(BlockMakeData *data)
} }
p.Y++; p.Y++;
// Make a tree // Make a tree
make_tree(vmanip, p, false, ndef); treegen::make_tree(vmanip, p, false, ndef);
} }
} }
} }

View File

@ -42,10 +42,6 @@ namespace mapgen
// Main map generation routine // Main map generation routine
void make_block(BlockMakeData *data); 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 These are used by FarMesh
*/ */

View File

@ -48,6 +48,7 @@ extern "C" {
#include "noise.h" // PseudoRandom for LuaPseudoRandom #include "noise.h" // PseudoRandom for LuaPseudoRandom
#include "util/pointedthing.h" #include "util/pointedthing.h"
#include "rollback.h" #include "rollback.h"
#include "treegen.h"
static void stackDump(lua_State *L, std::ostream &o) static void stackDump(lua_State *L, std::ostream &o)
{ {
@ -4004,6 +4005,45 @@ private:
return 0; 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: public:
EnvRef(ServerEnvironment *env): EnvRef(ServerEnvironment *env):
m_env(env) m_env(env)
@ -4086,6 +4126,7 @@ const luaL_reg EnvRef::methods[] = {
method(EnvRef, find_nodes_in_area), method(EnvRef, find_nodes_in_area),
method(EnvRef, get_perlin), method(EnvRef, get_perlin),
method(EnvRef, clear_objects), method(EnvRef, clear_objects),
method(EnvRef, spawn_tree),
{0,0} {0,0}
}; };

518
src/treegen.cpp Normal file
View File

@ -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

71
src/treegen.h Normal file
View File

@ -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