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