]> git.lizzy.rs Git - dragonfireclient.git/blob - src/treegen.cpp
L-systems treegen code tweaks.
[dragonfireclient.git] / src / treegen.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>,
4                           2012-2013 RealBadAngel, Maciej Kasatkin <mk@realbadangel.pl>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "irr_v3d.h"
21 #include <stack>
22 #include "util/pointer.h"
23 #include "util/numeric.h"
24 #include "util/mathconstants.h"
25 #include "map.h"
26 #include "environment.h"
27 #include "nodedef.h"
28 #include "treegen.h"
29
30 namespace treegen
31 {
32
33 void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
34                 bool is_apple_tree, INodeDefManager *ndef, int seed)
35 {
36         /*
37                 NOTE: Tree-placing code is currently duplicated in the engine
38                 and in games that have saplings; both are deprecated but not
39                 replaced yet
40         */
41         MapNode treenode(ndef->getId("mapgen_tree"));
42         MapNode leavesnode(ndef->getId("mapgen_leaves"));
43         MapNode applenode(ndef->getId("mapgen_apple"));
44
45         PseudoRandom pr(seed);
46         s16 trunk_h = pr.range(4, 5);
47         v3s16 p1 = p0;
48         for(s16 ii=0; ii<trunk_h; ii++)
49         {
50                 if(vmanip.m_area.contains(p1))
51                         if(ii == 0 || vmanip.getNodeNoExNoEmerge(p1).getContent() == CONTENT_AIR)
52                                 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
53                 p1.Y++;
54         }
55
56         // p1 is now the last piece of the trunk
57         p1.Y -= 1;
58
59         VoxelArea leaves_a(v3s16(-2,-1,-2), v3s16(2,2,2));
60         //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
61         Buffer<u8> leaves_d(leaves_a.getVolume());
62         for(s32 i=0; i<leaves_a.getVolume(); i++)
63                 leaves_d[i] = 0;
64
65         // Force leaves at near the end of the trunk
66         {
67                 s16 d = 1;
68                 for(s16 z=-d; z<=d; z++)
69                 for(s16 y=-d; y<=d; y++)
70                 for(s16 x=-d; x<=d; x++)
71                 {
72                         leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
73                 }
74         }
75
76         // Add leaves randomly
77         for(u32 iii=0; iii<7; iii++)
78         {
79                 s16 d = 1;
80
81                 v3s16 p(
82                         pr.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
83                         pr.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
84                         pr.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
85                 );
86
87                 for(s16 z=0; z<=d; z++)
88                 for(s16 y=0; y<=d; y++)
89                 for(s16 x=0; x<=d; x++)
90                 {
91                         leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
92                 }
93         }
94
95         // Blit leaves to vmanip
96         for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
97         for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
98         for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
99         {
100                 v3s16 p(x,y,z);
101                 p += p1;
102                 if(vmanip.m_area.contains(p) == false)
103                         continue;
104                 u32 vi = vmanip.m_area.index(p);
105                 if(vmanip.m_data[vi].getContent() != CONTENT_AIR
106                                 && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
107                         continue;
108                 u32 i = leaves_a.index(x,y,z);
109                 if(leaves_d[i] == 1) {
110                         bool is_apple = pr.range(0,99) < 10;
111                         if(is_apple_tree && is_apple) {
112                                 vmanip.m_data[vi] = applenode;
113                         } else {
114                                 vmanip.m_data[vi] = leavesnode;
115                         }
116                 }
117         }
118 }
119
120 // L-System tree LUA spawner
121 void spawn_ltree(ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef, TreeDef tree_definition)
122 {
123         ServerMap *map = &env->getServerMap();
124         std::map<v3s16, MapBlock*> modified_blocks;
125         ManualMapVoxelManipulator vmanip(map);
126         v3s16 tree_blockp = getNodeBlockPos(p0);
127         vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,3,1));
128         make_ltree (vmanip, p0, ndef, tree_definition);
129         vmanip.blitBackAll(&modified_blocks);
130
131         // update lighting
132         std::map<v3s16, MapBlock*> lighting_modified_blocks;
133         lighting_modified_blocks.insert(modified_blocks.begin(), modified_blocks.end());
134         map->updateLighting(lighting_modified_blocks, modified_blocks);
135         // Send a MEET_OTHER event
136         MapEditEvent event;
137         event.type = MEET_OTHER;
138         for(std::map<v3s16, MapBlock*>::iterator
139                 i = modified_blocks.begin();
140                 i != modified_blocks.end(); ++i)
141         {
142                 event.modified_blocks.insert(i->first);
143         }
144         map->dispatchEvent(&event);
145 }
146
147 //L-System tree generator
148 void make_ltree(ManualMapVoxelManipulator &vmanip, v3s16 p0, INodeDefManager *ndef,
149                 TreeDef tree_definition)
150 {
151         MapNode dirtnode(ndef->getId("mapgen_dirt"));
152
153         PseudoRandom ps(tree_definition.seed+14002);
154         // chance of inserting abcd rules
155         double prop_a = 9;
156         double prop_b = 8;
157         double prop_c = 7;
158         double prop_d = 6;
159
160         //randomize tree growth level, minimum=2
161         s16 iterations = tree_definition.iterations;
162         if (tree_definition.iterations_random_level>0)
163                 iterations -= ps.range(0,tree_definition.iterations_random_level);
164         if (iterations<2)
165                 iterations=2;
166
167         s16 MAX_ANGLE_OFFSET = 5;
168         double angle_in_radians = (double)tree_definition.angle*M_PI/180;
169         double angleOffset_in_radians = (s16)(ps.range(0,1)%MAX_ANGLE_OFFSET)*M_PI/180;
170
171         //initialize rotation matrix, position and stacks for branches
172         core::matrix4 rotation;
173         rotation = setRotationAxisRadians(rotation, M_PI/2,v3f(0,0,1));
174         v3f position;
175         position.X = p0.X;
176         position.Y = p0.Y;
177         position.Z = p0.Z;
178         std::stack <core::matrix4> stack_orientation;
179         std::stack <v3f> stack_position;
180
181         //generate axiom
182         std::string axiom = tree_definition.initial_axiom;
183         for(s16 i=0; i<iterations; i++)
184         {
185                 std::string temp = "";
186                 for(s16 j=0; j<(s16)axiom.size(); j++)
187                 {
188                         char axiom_char = axiom.at(j);
189                         switch (axiom_char)
190                         {
191                         case 'A':
192                                 temp+=tree_definition.rules_a;
193                                 break;
194                         case 'B':
195                                 temp+=tree_definition.rules_b;
196                                 break;
197                         case 'C':
198                                 temp+=tree_definition.rules_c;
199                                 break;
200                         case 'D':
201                                 temp+=tree_definition.rules_d;
202                                 break;
203                         case 'a':
204                                 if (prop_a >= ps.range(1,10))
205                                         temp+=tree_definition.rules_a;
206                                 break;
207                         case 'b':
208                                 if (prop_b >= ps.range(1,10))
209                                         temp+=tree_definition.rules_b;
210                                 break;
211                         case 'c':
212                                 if (prop_c >= ps.range(1,10))
213                                         temp+=tree_definition.rules_c;
214                                 break;
215                         case 'd':
216                                 if (prop_d >= ps.range(1,10))
217                                         temp+=tree_definition.rules_d;
218                                 break;
219                         default:
220                                 temp+=axiom_char;
221                                 break;
222                         }
223                 }
224                 axiom=temp;
225         }
226
227         //make sure tree is not floating in the air
228         if (tree_definition.trunk_type == "double")
229         {
230                 tree_node_placement(vmanip,v3f(position.X+1,position.Y-1,position.Z),dirtnode);
231                 tree_node_placement(vmanip,v3f(position.X,position.Y-1,position.Z+1),dirtnode);
232                 tree_node_placement(vmanip,v3f(position.X+1,position.Y-1,position.Z+1),dirtnode);
233         }
234         else if (tree_definition.trunk_type == "crossed")
235         {
236                 tree_node_placement(vmanip,v3f(position.X+1,position.Y-1,position.Z),dirtnode);
237                 tree_node_placement(vmanip,v3f(position.X-1,position.Y-1,position.Z),dirtnode);
238                 tree_node_placement(vmanip,v3f(position.X,position.Y-1,position.Z+1),dirtnode);
239                 tree_node_placement(vmanip,v3f(position.X,position.Y-1,position.Z-1),dirtnode);
240         }
241
242         /* build tree out of generated axiom
243
244         Key for Special L-System Symbols used in Axioms
245
246     G  - move forward one unit with the pen up
247     F  - move forward one unit with the pen down drawing trunks and branches
248     f  - move forward one unit with the pen down drawing leaves (100% chance)
249     T  - move forward one unit with the pen down drawing trunks only
250     R  - move forward one unit with the pen down placing fruit
251     A  - replace with rules set A
252     B  - replace with rules set B
253     C  - replace with rules set C
254     D  - replace with rules set D
255     a  - replace with rules set A, chance 90%
256     b  - replace with rules set B, chance 80%
257     c  - replace with rules set C, chance 70%
258     d  - replace with rules set D, chance 60%
259     +  - yaw the turtle right by angle degrees
260     -  - yaw the turtle left by angle degrees
261     &  - pitch the turtle down by angle degrees
262     ^  - pitch the turtle up by angle degrees
263     /  - roll the turtle to the right by angle degrees
264     *  - roll the turtle to the left by angle degrees
265     [  - save in stack current state info
266     ]  - recover from stack state info
267
268     */
269
270         s16 x,y,z;
271         for(s16 i=0; i<(s16)axiom.size(); i++)
272         {
273                 char axiom_char = axiom.at(i);
274                 core::matrix4 temp_rotation;
275                 temp_rotation.makeIdentity();
276                 v3f dir;
277                 switch (axiom_char)
278                 {
279                 case 'G':
280                         dir = v3f(1,0,0);
281                         dir = transposeMatrix(rotation,dir);
282                         position+=dir;
283                         break;
284                 case 'T':
285                         tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z),tree_definition);
286                         if (tree_definition.trunk_type == "double" && !tree_definition.thin_branches)
287                         {
288                                 tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z),tree_definition);
289                                 tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z+1),tree_definition);
290                                 tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z+1),tree_definition);
291                         }
292                         else if (tree_definition.trunk_type == "crossed" && !tree_definition.thin_branches)
293                         {
294                                 tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z),tree_definition);
295                                 tree_trunk_placement(vmanip,v3f(position.X-1,position.Y,position.Z),tree_definition);
296                                 tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z+1),tree_definition);
297                                 tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z-1),tree_definition);
298                         }
299                         dir = v3f(1,0,0);
300                         dir = transposeMatrix(rotation,dir);
301                         position+=dir;
302                         break;
303                 case 'F':
304                         tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z),tree_definition);
305                         if ((stack_orientation.empty() && tree_definition.trunk_type == "double") ||
306                                 (!stack_orientation.empty() && tree_definition.trunk_type == "double" && !tree_definition.thin_branches))
307                         {
308                                 tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z),tree_definition);
309                                 tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z+1),tree_definition);
310                                 tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z+1),tree_definition);
311                         }
312                         else if ((stack_orientation.empty() && tree_definition.trunk_type == "crossed") ||
313                                 (!stack_orientation.empty() && tree_definition.trunk_type == "crossed" && !tree_definition.thin_branches))
314                         {
315                                 tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z),tree_definition);
316                                 tree_trunk_placement(vmanip,v3f(position.X-1,position.Y,position.Z),tree_definition);
317                                 tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z+1),tree_definition);
318                                 tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z-1),tree_definition);
319                         }
320                         if (stack_orientation.empty() == false)
321                         {
322                                 s16 size = 1;
323                                 for(x=-size; x<=size; x++)
324                                         for(y=-size; y<=size; y++)
325                                                 for(z=-size; z<=size; z++)
326                                                         if (abs(x) == size && abs(y) == size && abs(z) == size)
327                                                         {
328                                                                 tree_leaves_placement(vmanip,v3f(position.X+x+1,position.Y+y,position.Z+z),ps.next(), tree_definition);
329                                                                 tree_leaves_placement(vmanip,v3f(position.X+x-1,position.Y+y,position.Z+z),ps.next(), tree_definition);
330                                                                 tree_leaves_placement(vmanip,v3f(position.X+x,position.Y+y,position.Z+z+1),ps.next(), tree_definition);
331                                                                 tree_leaves_placement(vmanip,v3f(position.X+x,position.Y+y,position.Z+z-1),ps.next(), tree_definition);
332                                                         }
333                         }
334                         dir = v3f(1,0,0);
335                         dir = transposeMatrix(rotation,dir);
336                         position+=dir;
337                         break;
338                 case 'f':
339                         tree_single_leaves_placement(vmanip,v3f(position.X,position.Y,position.Z),ps.next() ,tree_definition);
340                         dir = v3f(1,0,0);
341                         dir = transposeMatrix(rotation,dir);
342                         position+=dir;
343                         break;
344                 case 'R':
345                         tree_fruit_placement(vmanip,v3f(position.X,position.Y,position.Z),tree_definition);
346                         dir = v3f(1,0,0);
347                         dir = transposeMatrix(rotation,dir);
348                         position+=dir;
349                         break;
350
351                 // turtle orientation commands
352                 case '[':
353                         stack_orientation.push(rotation);
354                         stack_position.push(position);
355                         break;
356                 case ']':
357                         rotation=stack_orientation.top();
358                         stack_orientation.pop();
359                         position=stack_position.top();
360                         stack_position.pop();
361                         break;
362                 case '+':
363                         temp_rotation.makeIdentity();
364                         temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians+angleOffset_in_radians,v3f(0,0,1));
365                         rotation*=temp_rotation;
366                         break;
367                 case '-':
368                         temp_rotation.makeIdentity();
369                         temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians+angleOffset_in_radians,v3f(0,0,-1));
370                         rotation*=temp_rotation;
371                         break;
372                 case '&':
373                         temp_rotation.makeIdentity();
374                         temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians+angleOffset_in_radians,v3f(0,1,0));
375                         rotation*=temp_rotation;
376                         break;
377                 case '^':
378                         temp_rotation.makeIdentity();
379                         temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians+angleOffset_in_radians,v3f(0,-1,0));
380                         rotation*=temp_rotation;
381                         break;
382                 case '*':
383                         temp_rotation.makeIdentity();
384                         temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians,v3f(1,0,0));
385                         rotation*=temp_rotation;
386                         break;
387                 case '/':
388                         temp_rotation.makeIdentity();
389                         temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians,v3f(-1,0,0));
390                         rotation*=temp_rotation;
391                         break;
392                 default:
393                         break;
394                 }
395         }
396 }
397
398 void tree_node_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
399                 MapNode node)
400 {
401         v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
402         if(vmanip.m_area.contains(p1) == false)
403                 return;
404         u32 vi = vmanip.m_area.index(p1);
405         if(vmanip.m_data[vi].getContent() != CONTENT_AIR
406                         && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
407                 return;
408         vmanip.m_data[vmanip.m_area.index(p1)] = node;
409 }
410
411 void tree_trunk_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
412                 TreeDef &tree_definition)
413 {
414         v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
415         if(vmanip.m_area.contains(p1) == false)
416                 return;
417         u32 vi = vmanip.m_area.index(p1);
418         if(vmanip.m_data[vi].getContent() != CONTENT_AIR
419                         && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
420                 return;
421         vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.trunknode;
422 }
423
424 void tree_leaves_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
425                 PseudoRandom ps ,TreeDef &tree_definition)
426 {
427         MapNode leavesnode=tree_definition.leavesnode;
428         if (ps.range(1,100) > 100-tree_definition.leaves2_chance)
429                 leavesnode=tree_definition.leaves2node;
430         v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
431         if(vmanip.m_area.contains(p1) == false)
432                 return;
433         u32 vi = vmanip.m_area.index(p1);
434         if(vmanip.m_data[vi].getContent() != CONTENT_AIR
435                         && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
436                 return; 
437         if (tree_definition.fruit_chance>0)
438         {
439                 if (ps.range(1,100) > 100-tree_definition.fruit_chance)
440                         vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.fruitnode;
441                 else
442                         vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
443         }
444         else if (ps.range(1,100) > 20)
445                 vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
446 }
447
448 void tree_single_leaves_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
449                 PseudoRandom ps, TreeDef &tree_definition)
450 {
451         MapNode leavesnode=tree_definition.leavesnode;
452         if (ps.range(1,100) > 100-tree_definition.leaves2_chance)
453                 leavesnode=tree_definition.leaves2node;
454         v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
455         if(vmanip.m_area.contains(p1) == false)
456                 return;
457         u32 vi = vmanip.m_area.index(p1);
458         if(vmanip.m_data[vi].getContent() != CONTENT_AIR
459                 && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
460                 return;
461         vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
462 }
463
464 void tree_fruit_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
465                 TreeDef &tree_definition)
466 {
467         v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
468         if(vmanip.m_area.contains(p1) == false)
469                 return;
470         u32 vi = vmanip.m_area.index(p1);
471         if(vmanip.m_data[vi].getContent() != CONTENT_AIR
472                 && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
473                 return;
474         vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.fruitnode;
475 }
476
477 irr::core::matrix4 setRotationAxisRadians(irr::core::matrix4 M, double angle, v3f axis)
478 {
479         double c = cos(angle);
480         double s = sin(angle);
481         double t = 1.0 - c;
482
483         double tx  = t * axis.X;
484         double ty  = t * axis.Y;
485         double tz  = t * axis.Z;
486         double sx  = s * axis.X;
487         double sy  = s * axis.Y;
488         double sz  = s * axis.Z;
489
490         M[0] = tx * axis.X + c;
491         M[1] = tx * axis.Y + sz;
492         M[2] = tx * axis.Z - sy;
493
494         M[4] = ty * axis.X - sz;
495         M[5] = ty * axis.Y + c;
496         M[6] = ty * axis.Z + sx;
497
498         M[8]  = tz * axis.X + sy;
499         M[9]  = tz * axis.Y - sx;
500         M[10] = tz * axis.Z + c;
501         return M;
502 }
503
504 v3f transposeMatrix(irr::core::matrix4 M, v3f v)
505 {
506         v3f translated;
507         double x = M[0] * v.X + M[4] * v.Y + M[8]  * v.Z +M[12];
508         double y = M[1] * v.X + M[5] * v.Y + M[9]  * v.Z +M[13];
509         double z = M[2] * v.X + M[6] * v.Y + M[10] * v.Z +M[14];
510         translated.X=x;
511         translated.Y=y;
512         translated.Z=z;
513         return translated;
514 }
515
516 void make_jungletree(VoxelManipulator &vmanip, v3s16 p0,
517                 INodeDefManager *ndef, int seed)
518 {
519         /*
520                 NOTE: Tree-placing code is currently duplicated in the engine
521                 and in games that have saplings; both are deprecated but not
522                 replaced yet
523         */
524         content_t c_tree   = ndef->getId("mapgen_jungletree");
525         content_t c_leaves = ndef->getId("mapgen_jungleleaves");
526         if (c_tree == CONTENT_IGNORE)
527                 c_tree = ndef->getId("mapgen_tree");
528         if (c_leaves == CONTENT_IGNORE)
529                 c_leaves = ndef->getId("mapgen_leaves");
530
531         MapNode treenode(c_tree);
532         MapNode leavesnode(c_leaves);
533
534         PseudoRandom pr(seed);
535         for(s16 x=-1; x<=1; x++)
536         for(s16 z=-1; z<=1; z++)
537         {
538                 if(pr.range(0, 2) == 0)
539                         continue;
540                 v3s16 p1 = p0 + v3s16(x,0,z);
541                 v3s16 p2 = p0 + v3s16(x,-1,z);
542                 u32 vi1 = vmanip.m_area.index(p1);
543                 u32 vi2 = vmanip.m_area.index(p2);
544                 
545                 if (vmanip.m_area.contains(p2) &&
546                         vmanip.m_data[vi2].getContent() == CONTENT_AIR)
547                         vmanip.m_data[vi2] = treenode;
548                 else if (vmanip.m_area.contains(p1) &&
549                                 vmanip.m_data[vi1].getContent() == CONTENT_AIR)
550                         vmanip.m_data[vi1] = treenode;
551         }
552         vmanip.m_data[vmanip.m_area.index(p0)] = treenode;
553
554         s16 trunk_h = pr.range(8, 12);
555         v3s16 p1 = p0;
556         for (s16 ii=0; ii<trunk_h; ii++)
557         {
558                 if (vmanip.m_area.contains(p1)) {
559                         u32 vi = vmanip.m_area.index(p1);
560                         if (vmanip.m_data[vi].getContent() == CONTENT_AIR)
561                                 vmanip.m_data[vi] = treenode;
562                 }
563                 p1.Y++;
564         }
565
566         // p1 is now the last piece of the trunk
567         p1.Y -= 1;
568
569         VoxelArea leaves_a(v3s16(-3,-2,-3), v3s16(3,2,3));
570         //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
571         Buffer<u8> leaves_d(leaves_a.getVolume());
572         for(s32 i=0; i<leaves_a.getVolume(); i++)
573                 leaves_d[i] = 0;
574
575         // Force leaves at near the end of the trunk
576         {
577                 s16 d = 1;
578                 for(s16 z=-d; z<=d; z++)
579                 for(s16 y=-d; y<=d; y++)
580                 for(s16 x=-d; x<=d; x++)
581                 {
582                         leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
583                 }
584         }
585
586         // Add leaves randomly
587         for(u32 iii=0; iii<30; iii++)
588         {
589                 s16 d = 1;
590
591                 v3s16 p(
592                         pr.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
593                         pr.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
594                         pr.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
595                 );
596
597                 for(s16 z=0; z<=d; z++)
598                 for(s16 y=0; y<=d; y++)
599                 for(s16 x=0; x<=d; x++)
600                 {
601                         leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
602                 }
603         }
604
605         // Blit leaves to vmanip
606         for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
607         for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
608         for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
609         {
610                 v3s16 p(x,y,z);
611                 p += p1;
612                 if(vmanip.m_area.contains(p) == false)
613                         continue;
614                 u32 vi = vmanip.m_area.index(p);
615                 if (vmanip.m_data[vi].getContent() != CONTENT_AIR &&
616                         vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
617                         continue;
618                 u32 i = leaves_a.index(x,y,z);
619                 if(leaves_d[i] == 1)
620                         vmanip.m_data[vi] = leavesnode;
621         }
622 }
623
624 }; // namespace treegen