]> git.lizzy.rs Git - dragonfireclient.git/blob - src/treegen.cpp
L-system: Fix leaves cutting through stems
[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 "map.h"
25 #include "serverenvironment.h"
26 #include "nodedef.h"
27 #include "treegen.h"
28 #include "voxelalgorithms.h"
29
30 namespace treegen
31 {
32
33 void make_tree(MMVManip &vmanip, v3s16 p0,
34                 bool is_apple_tree, INodeDefManager *ndef, s32 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                 if (vmanip.m_area.contains(p1)) {
50                         u32 vi = vmanip.m_area.index(p1);
51                         vmanip.m_data[vi] = treenode;
52                 }
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         Buffer<u8> leaves_d(leaves_a.getVolume());
61         for (s32 i = 0; i < leaves_a.getVolume(); i++)
62                 leaves_d[i] = 0;
63
64         // Force leaves at near the end of the trunk
65         s16 d = 1;
66         for (s16 z = -d; z <= d; z++)
67         for (s16 y = -d; y <= d; y++)
68         for (s16 x = -d; x <= d; x++) {
69                 leaves_d[leaves_a.index(v3s16(x, y, z))] = 1;
70         }
71
72         // Add leaves randomly
73         for (u32 iii = 0; iii < 7; iii++) {
74                 v3s16 p(
75                         pr.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X - d),
76                         pr.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y - d),
77                         pr.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                         leaves_d[leaves_a.index(p + v3s16(x, y, z))] = 1;
84                 }
85         }
86
87         // Blit leaves to vmanip
88         for (s16 z = leaves_a.MinEdge.Z; z <= leaves_a.MaxEdge.Z; z++)
89         for (s16 y = leaves_a.MinEdge.Y; y <= leaves_a.MaxEdge.Y; y++) {
90                 v3s16 pmin(leaves_a.MinEdge.X, y, z);
91                 u32 i = leaves_a.index(pmin);
92                 u32 vi = vmanip.m_area.index(pmin + p1);
93                 for (s16 x = leaves_a.MinEdge.X; x <= leaves_a.MaxEdge.X; x++) {
94                         v3s16 p(x, y, z);
95                         if (vmanip.m_area.contains(p + p1) == true &&
96                                         (vmanip.m_data[vi].getContent() == CONTENT_AIR ||
97                                         vmanip.m_data[vi].getContent() == CONTENT_IGNORE)) {
98                                 if (leaves_d[i] == 1) {
99                                         bool is_apple = pr.range(0, 99) < 10;
100                                         if (is_apple_tree && is_apple)
101                                                 vmanip.m_data[vi] = applenode;
102                                         else
103                                                 vmanip.m_data[vi] = leavesnode;
104                                 }
105                         }
106                         vi++;
107                         i++;
108                 }
109         }
110 }
111
112
113 // L-System tree LUA spawner
114 treegen::error spawn_ltree(ServerEnvironment *env, v3s16 p0,
115                 INodeDefManager *ndef, const TreeDef &tree_definition)
116 {
117         ServerMap *map = &env->getServerMap();
118         std::map<v3s16, MapBlock*> modified_blocks;
119         MMVManip vmanip(map);
120         v3s16 tree_blockp = getNodeBlockPos(p0);
121         treegen::error e;
122
123         vmanip.initialEmerge(tree_blockp - v3s16(1, 1, 1), tree_blockp + v3s16(1, 3, 1));
124         e = make_ltree(vmanip, p0, ndef, tree_definition);
125         if (e != SUCCESS)
126                 return e;
127
128         voxalgo::blit_back_with_light(map, &vmanip, &modified_blocks);
129
130         // Send a MEET_OTHER event
131         MapEditEvent event;
132         event.type = MEET_OTHER;
133         for (std::map<v3s16, MapBlock*>::iterator
134                         i = modified_blocks.begin();
135                         i != modified_blocks.end(); ++i)
136                 event.modified_blocks.insert(i->first);
137         map->dispatchEvent(&event);
138         return SUCCESS;
139 }
140
141
142 //L-System tree generator
143 treegen::error make_ltree(MMVManip &vmanip, v3s16 p0,
144                 INodeDefManager *ndef, TreeDef tree_definition)
145 {
146         MapNode dirtnode(ndef->getId("mapgen_dirt"));
147         s32 seed;
148         if (tree_definition.explicit_seed)
149                 seed = tree_definition.seed + 14002;
150         else
151                 seed = p0.X * 2 + p0.Y * 4 + p0.Z;  // use the tree position to seed PRNG
152         PseudoRandom ps(seed);
153
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                 std::string temp = "";
185                 for (s16 j = 0; j < (s16)axiom.size(); j++) {
186                         char axiom_char = axiom.at(j);
187                         switch (axiom_char) {
188                         case 'A':
189                                 temp += tree_definition.rules_a;
190                                 break;
191                         case 'B':
192                                 temp += tree_definition.rules_b;
193                                 break;
194                         case 'C':
195                                 temp += tree_definition.rules_c;
196                                 break;
197                         case 'D':
198                                 temp += tree_definition.rules_d;
199                                 break;
200                         case 'a':
201                                 if (prop_a >= ps.range(1, 10))
202                                         temp += tree_definition.rules_a;
203                                 break;
204                         case 'b':
205                                 if (prop_b >= ps.range(1, 10))
206                                         temp += tree_definition.rules_b;
207                                 break;
208                         case 'c':
209                                 if (prop_c >= ps.range(1, 10))
210                                         temp += tree_definition.rules_c;
211                                 break;
212                         case 'd':
213                                 if (prop_d >= ps.range(1, 10))
214                                         temp += tree_definition.rules_d;
215                                 break;
216                         default:
217                                 temp += axiom_char;
218                                 break;
219                         }
220                 }
221                 axiom = temp;
222         }
223
224         //make sure tree is not floating in the air
225         if (tree_definition.trunk_type == "double") {
226                 tree_node_placement(
227                         vmanip,
228                         v3f(position.X + 1, position.Y - 1, position.Z),
229                         dirtnode
230                 );
231                 tree_node_placement(
232                         vmanip,
233                         v3f(position.X, position.Y - 1, position.Z + 1),
234                         dirtnode
235                 );
236                 tree_node_placement(
237                         vmanip,
238                         v3f(position.X + 1, position.Y - 1, position.Z + 1),
239                         dirtnode
240                 );
241         } else if (tree_definition.trunk_type == "crossed") {
242                 tree_node_placement(
243                         vmanip,
244                         v3f(position.X + 1, position.Y - 1, position.Z),
245                         dirtnode
246                 );
247                 tree_node_placement(
248                         vmanip,
249                         v3f(position.X - 1, position.Y - 1, position.Z),
250                         dirtnode
251                 );
252                 tree_node_placement(
253                         vmanip,
254                         v3f(position.X, position.Y - 1, position.Z + 1),
255                         dirtnode
256                 );
257                 tree_node_placement(
258                         vmanip,
259                         v3f(position.X, position.Y - 1, position.Z - 1),
260                         dirtnode
261                 );
262         }
263
264         /* build tree out of generated axiom
265
266         Key for Special L-System Symbols used in Axioms
267
268     G  - move forward one unit with the pen up
269     F  - move forward one unit with the pen down drawing trunks and branches
270     f  - move forward one unit with the pen down drawing leaves (100% chance)
271     T  - move forward one unit with the pen down drawing trunks only
272     R  - move forward one unit with the pen down placing fruit
273     A  - replace with rules set A
274     B  - replace with rules set B
275     C  - replace with rules set C
276     D  - replace with rules set D
277     a  - replace with rules set A, chance 90%
278     b  - replace with rules set B, chance 80%
279     c  - replace with rules set C, chance 70%
280     d  - replace with rules set D, chance 60%
281     +  - yaw the turtle right by angle degrees
282     -  - yaw the turtle left by angle degrees
283     &  - pitch the turtle down by angle degrees
284     ^  - pitch the turtle up by angle degrees
285     /  - roll the turtle to the right by angle degrees
286     *  - roll the turtle to the left by angle degrees
287     [  - save in stack current state info
288     ]  - recover from stack state info
289
290     */
291
292         s16 x,y,z;
293         for (s16 i = 0; i < (s16)axiom.size(); i++) {
294                 char axiom_char = axiom.at(i);
295                 core::matrix4 temp_rotation;
296                 temp_rotation.makeIdentity();
297                 v3f dir;
298                 switch (axiom_char) {
299                 case 'G':
300                         dir = v3f(1, 0, 0);
301                         dir = transposeMatrix(rotation, dir);
302                         position += dir;
303                         break;
304                 case 'T':
305                         tree_trunk_placement(
306                                 vmanip,
307                                 v3f(position.X, position.Y, position.Z),
308                                 tree_definition
309                         );
310                         if (tree_definition.trunk_type == "double" &&
311                                         !tree_definition.thin_branches) {
312                                 tree_trunk_placement(
313                                         vmanip,
314                                         v3f(position.X + 1, position.Y, position.Z),
315                                         tree_definition
316                                 );
317                                 tree_trunk_placement(
318                                         vmanip,
319                                         v3f(position.X, position.Y, position.Z + 1),
320                                         tree_definition
321                                 );
322                                 tree_trunk_placement(
323                                         vmanip,
324                                         v3f(position.X + 1, position.Y, position.Z + 1),
325                                         tree_definition
326                                 );
327                         } else if (tree_definition.trunk_type == "crossed" &&
328                                         !tree_definition.thin_branches) {
329                                 tree_trunk_placement(
330                                         vmanip,
331                                         v3f(position.X + 1, position.Y, position.Z),
332                                         tree_definition
333                                 );
334                                 tree_trunk_placement(
335                                         vmanip,
336                                         v3f(position.X - 1, position.Y, position.Z),
337                                         tree_definition
338                                 );
339                                 tree_trunk_placement(
340                                         vmanip,
341                                         v3f(position.X, position.Y, position.Z + 1),
342                                         tree_definition
343                                 );
344                                 tree_trunk_placement(
345                                         vmanip,
346                                         v3f(position.X, position.Y, position.Z - 1),
347                                         tree_definition
348                                 );
349                         }
350                         dir = v3f(1, 0, 0);
351                         dir = transposeMatrix(rotation, dir);
352                         position += dir;
353                         break;
354                 case 'F':
355                         tree_trunk_placement(
356                                 vmanip,
357                                 v3f(position.X, position.Y, position.Z),
358                                 tree_definition
359                         );
360                         if ((stack_orientation.empty() &&
361                                         tree_definition.trunk_type == "double") ||
362                                         (!stack_orientation.empty() &&
363                                         tree_definition.trunk_type == "double" &&
364                                         !tree_definition.thin_branches)) {
365                                 tree_trunk_placement(
366                                         vmanip,
367                                         v3f(position.X +1 , position.Y, position.Z),
368                                         tree_definition
369                                 );
370                                 tree_trunk_placement(
371                                         vmanip,
372                                         v3f(position.X, position.Y, position.Z + 1),
373                                         tree_definition
374                                 );
375                                 tree_trunk_placement(
376                                         vmanip,
377                                         v3f(position.X + 1, position.Y, position.Z + 1),
378                                         tree_definition
379                                 );
380                         } else if ((stack_orientation.empty() &&
381                                         tree_definition.trunk_type == "crossed") ||
382                                         (!stack_orientation.empty() &&
383                                         tree_definition.trunk_type == "crossed" &&
384                                         !tree_definition.thin_branches)) {
385                                 tree_trunk_placement(
386                                         vmanip,
387                                         v3f(position.X + 1, position.Y, position.Z),
388                                         tree_definition
389                                 );
390                                 tree_trunk_placement(
391                                         vmanip,
392                                         v3f(position.X - 1, position.Y, position.Z),
393                                         tree_definition
394                                 );
395                                 tree_trunk_placement(
396                                         vmanip,
397                                         v3f(position.X, position.Y, position.Z + 1),
398                                         tree_definition
399                                 );
400                                 tree_trunk_placement(
401                                         vmanip,
402                                         v3f(position.X, position.Y, position.Z - 1),
403                                         tree_definition
404                                 );
405                         } if (stack_orientation.empty() == false) {
406                                 s16 size = 1;
407                                 for (x = -size; x <= size; x++)
408                                 for (y = -size; y <= size; y++)
409                                 for (z = -size; z <= size; z++) {
410                                         if (abs(x) == size &&
411                                                         abs(y) == size &&
412                                                         abs(z) == size) {
413                                                 tree_leaves_placement(
414                                                         vmanip,
415                                                         v3f(position.X + x + 1, position.Y + y,
416                                                                         position.Z + z),
417                                                         ps.next(),
418                                                         tree_definition
419                                                 );
420                                                 tree_leaves_placement(
421                                                         vmanip,
422                                                         v3f(position.X + x - 1, position.Y + y,
423                                                                         position.Z + z),
424                                                         ps.next(),
425                                                         tree_definition
426                                                 );
427                                                 tree_leaves_placement(
428                                                         vmanip,v3f(position.X + x, position.Y + y,
429                                                                         position.Z + z + 1),
430                                                         ps.next(),
431                                                         tree_definition
432                                                 );
433                                                 tree_leaves_placement(
434                                                         vmanip,v3f(position.X + x, position.Y + y,
435                                                                         position.Z + z - 1),
436                                                         ps.next(),
437                                                         tree_definition
438                                                 );
439                                         }
440                                 }
441                         }
442                         dir = v3f(1, 0, 0);
443                         dir = transposeMatrix(rotation, dir);
444                         position += dir;
445                         break;
446                 case 'f':
447                         tree_single_leaves_placement(
448                                 vmanip,
449                                 v3f(position.X, position.Y, position.Z),
450                                 ps.next(),
451                                 tree_definition
452                         );
453                         dir = v3f(1, 0, 0);
454                         dir = transposeMatrix(rotation, dir);
455                         position += dir;
456                         break;
457                 case 'R':
458                         tree_fruit_placement(
459                                 vmanip,
460                                 v3f(position.X, position.Y, position.Z),
461                                 tree_definition
462                         );
463                         dir = v3f(1, 0, 0);
464                         dir = transposeMatrix(rotation, dir);
465                         position += dir;
466                         break;
467
468                 // turtle orientation commands
469                 case '[':
470                         stack_orientation.push(rotation);
471                         stack_position.push(position);
472                         break;
473                 case ']':
474                         if (stack_orientation.empty())
475                                 return UNBALANCED_BRACKETS;
476                         rotation = stack_orientation.top();
477                         stack_orientation.pop();
478                         position = stack_position.top();
479                         stack_position.pop();
480                         break;
481                 case '+':
482                         temp_rotation.makeIdentity();
483                         temp_rotation = setRotationAxisRadians(temp_rotation,
484                                         angle_in_radians + angleOffset_in_radians, v3f(0, 0, 1));
485                         rotation *= temp_rotation;
486                         break;
487                 case '-':
488                         temp_rotation.makeIdentity();
489                         temp_rotation = setRotationAxisRadians(temp_rotation,
490                                         angle_in_radians + angleOffset_in_radians, v3f(0, 0, -1));
491                         rotation *= temp_rotation;
492                         break;
493                 case '&':
494                         temp_rotation.makeIdentity();
495                         temp_rotation = setRotationAxisRadians(temp_rotation,
496                                         angle_in_radians + angleOffset_in_radians, v3f(0, 1, 0));
497                         rotation *= temp_rotation;
498                         break;
499                 case '^':
500                         temp_rotation.makeIdentity();
501                         temp_rotation = setRotationAxisRadians(temp_rotation,
502                                         angle_in_radians + angleOffset_in_radians, v3f(0, -1, 0));
503                         rotation *= temp_rotation;
504                         break;
505                 case '*':
506                         temp_rotation.makeIdentity();
507                         temp_rotation = setRotationAxisRadians(temp_rotation,
508                                         angle_in_radians, v3f(1, 0, 0));
509                         rotation *= temp_rotation;
510                         break;
511                 case '/':
512                         temp_rotation.makeIdentity();
513                         temp_rotation = setRotationAxisRadians(temp_rotation,
514                                         angle_in_radians, v3f(-1, 0, 0));
515                         rotation *= temp_rotation;
516                         break;
517                 default:
518                         break;
519                 }
520         }
521
522         return SUCCESS;
523 }
524
525
526 void tree_node_placement(MMVManip &vmanip, v3f p0, MapNode node)
527 {
528         v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
529         if (vmanip.m_area.contains(p1) == false)
530                 return;
531         u32 vi = vmanip.m_area.index(p1);
532         if (vmanip.m_data[vi].getContent() != CONTENT_AIR
533                         && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
534                 return;
535         vmanip.m_data[vmanip.m_area.index(p1)] = node;
536 }
537
538
539 void tree_trunk_placement(MMVManip &vmanip, v3f p0, TreeDef &tree_definition)
540 {
541         v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
542         if (vmanip.m_area.contains(p1) == false)
543                 return;
544         u32 vi = vmanip.m_area.index(p1);
545         content_t current_node = vmanip.m_data[vi].getContent();
546         if (current_node != CONTENT_AIR && current_node != CONTENT_IGNORE
547                         && current_node != tree_definition.leavesnode.getContent()
548                         && current_node != tree_definition.leaves2node.getContent()
549                         && current_node != tree_definition.fruitnode.getContent())
550                 return;
551         vmanip.m_data[vi] = tree_definition.trunknode;
552 }
553
554
555 void tree_leaves_placement(MMVManip &vmanip, v3f p0,
556                 PseudoRandom ps, TreeDef &tree_definition)
557 {
558         MapNode leavesnode = tree_definition.leavesnode;
559         if (ps.range(1, 100) > 100 - tree_definition.leaves2_chance)
560                 leavesnode = tree_definition.leaves2node;
561         v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
562         if (vmanip.m_area.contains(p1) == false)
563                 return;
564         u32 vi = vmanip.m_area.index(p1);
565         if (vmanip.m_data[vi].getContent() != CONTENT_AIR
566                         && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
567                 return;
568         if (tree_definition.fruit_chance > 0) {
569                 if (ps.range(1, 100) > 100 - tree_definition.fruit_chance)
570                         vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.fruitnode;
571                 else
572                         vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
573         } else if (ps.range(1, 100) > 20) {
574                 vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
575         }
576 }
577
578
579 void tree_single_leaves_placement(MMVManip &vmanip, v3f p0,
580                 PseudoRandom ps, TreeDef &tree_definition)
581 {
582         MapNode leavesnode = tree_definition.leavesnode;
583         if (ps.range(1, 100) > 100 - tree_definition.leaves2_chance)
584                 leavesnode = tree_definition.leaves2node;
585         v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
586         if (vmanip.m_area.contains(p1) == false)
587                 return;
588         u32 vi = vmanip.m_area.index(p1);
589         if (vmanip.m_data[vi].getContent() != CONTENT_AIR
590                         && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
591                 return;
592         vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
593 }
594
595
596 void tree_fruit_placement(MMVManip &vmanip, v3f p0, TreeDef &tree_definition)
597 {
598         v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
599         if (vmanip.m_area.contains(p1) == false)
600                 return;
601         u32 vi = vmanip.m_area.index(p1);
602         if (vmanip.m_data[vi].getContent() != CONTENT_AIR
603                         && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
604                 return;
605         vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.fruitnode;
606 }
607
608
609 irr::core::matrix4 setRotationAxisRadians(irr::core::matrix4 M, double angle, v3f axis)
610 {
611         double c = cos(angle);
612         double s = sin(angle);
613         double t = 1.0 - c;
614
615         double tx  = t * axis.X;
616         double ty  = t * axis.Y;
617         double tz  = t * axis.Z;
618         double sx  = s * axis.X;
619         double sy  = s * axis.Y;
620         double sz  = s * axis.Z;
621
622         M[0] = tx * axis.X + c;
623         M[1] = tx * axis.Y + sz;
624         M[2] = tx * axis.Z - sy;
625
626         M[4] = ty * axis.X - sz;
627         M[5] = ty * axis.Y + c;
628         M[6] = ty * axis.Z + sx;
629
630         M[8]  = tz * axis.X + sy;
631         M[9]  = tz * axis.Y - sx;
632         M[10] = tz * axis.Z + c;
633         return M;
634 }
635
636
637 v3f transposeMatrix(irr::core::matrix4 M, v3f v)
638 {
639         v3f translated;
640         double x = M[0] * v.X + M[4] * v.Y + M[8]  * v.Z +M[12];
641         double y = M[1] * v.X + M[5] * v.Y + M[9]  * v.Z +M[13];
642         double z = M[2] * v.X + M[6] * v.Y + M[10] * v.Z +M[14];
643         translated.X = x;
644         translated.Y = y;
645         translated.Z = z;
646         return translated;
647 }
648
649
650 void make_jungletree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef, s32 seed)
651 {
652         /*
653                 NOTE: Tree-placing code is currently duplicated in the engine
654                 and in games that have saplings; both are deprecated but not
655                 replaced yet
656         */
657         content_t c_tree   = ndef->getId("mapgen_jungletree");
658         content_t c_leaves = ndef->getId("mapgen_jungleleaves");
659         if (c_tree == CONTENT_IGNORE)
660                 c_tree = ndef->getId("mapgen_tree");
661         if (c_leaves == CONTENT_IGNORE)
662                 c_leaves = ndef->getId("mapgen_leaves");
663
664         MapNode treenode(c_tree);
665         MapNode leavesnode(c_leaves);
666
667         PseudoRandom pr(seed);
668         for (s16 x= -1; x <= 1; x++)
669         for (s16 z= -1; z <= 1; z++) {
670                 if (pr.range(0, 2) == 0)
671                         continue;
672                 v3s16 p1 = p0 + v3s16(x, 0, z);
673                 v3s16 p2 = p0 + v3s16(x, -1, z);
674                 u32 vi1 = vmanip.m_area.index(p1);
675                 u32 vi2 = vmanip.m_area.index(p2);
676
677                 if (vmanip.m_area.contains(p2) &&
678                                 vmanip.m_data[vi2].getContent() == CONTENT_AIR)
679                         vmanip.m_data[vi2] = treenode;
680                 else if (vmanip.m_area.contains(p1) &&
681                                 vmanip.m_data[vi1].getContent() == CONTENT_AIR)
682                         vmanip.m_data[vi1] = treenode;
683         }
684         vmanip.m_data[vmanip.m_area.index(p0)] = treenode;
685
686         s16 trunk_h = pr.range(8, 12);
687         v3s16 p1 = p0;
688         for (s16 ii = 0; ii < trunk_h; ii++) {
689                 if (vmanip.m_area.contains(p1)) {
690                         u32 vi = vmanip.m_area.index(p1);
691                         vmanip.m_data[vi] = treenode;
692                 }
693                 p1.Y++;
694         }
695
696         // p1 is now the last piece of the trunk
697         p1.Y -= 1;
698
699         VoxelArea leaves_a(v3s16(-3, -2, -3), v3s16(3, 2, 3));
700         //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
701         Buffer<u8> leaves_d(leaves_a.getVolume());
702         for (s32 i = 0; i < leaves_a.getVolume(); i++)
703                 leaves_d[i] = 0;
704
705         // Force leaves at near the end of the trunk
706         s16 d = 1;
707         for (s16 z = -d; z <= d; z++)
708         for (s16 y = -d; y <= d; y++)
709         for (s16 x = -d; x <= d; x++) {
710                 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
711         }
712
713         // Add leaves randomly
714         for (u32 iii = 0; iii < 30; iii++) {
715                 v3s16 p(
716                         pr.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X - d),
717                         pr.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y - d),
718                         pr.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z - d)
719                 );
720
721                 for (s16 z = 0; z <= d; z++)
722                 for (s16 y = 0; y <= d; y++)
723                 for (s16 x = 0; x <= d; x++) {
724                         leaves_d[leaves_a.index(p + v3s16(x, y, z))] = 1;
725                 }
726         }
727
728         // Blit leaves to vmanip
729         for (s16 z = leaves_a.MinEdge.Z; z <= leaves_a.MaxEdge.Z; z++)
730         for (s16 y = leaves_a.MinEdge.Y; y <= leaves_a.MaxEdge.Y; y++) {
731                 v3s16 pmin(leaves_a.MinEdge.X, y, z);
732                 u32 i = leaves_a.index(pmin);
733                 u32 vi = vmanip.m_area.index(pmin + p1);
734                 for (s16 x = leaves_a.MinEdge.X; x <= leaves_a.MaxEdge.X; x++) {
735                         v3s16 p(x, y, z);
736                         if (vmanip.m_area.contains(p + p1) == true &&
737                                         (vmanip.m_data[vi].getContent() == CONTENT_AIR ||
738                                         vmanip.m_data[vi].getContent() == CONTENT_IGNORE)) {
739                                 if (leaves_d[i] == 1)
740                                         vmanip.m_data[vi] = leavesnode;
741                         }
742                         vi++;
743                         i++;
744                 }
745         }
746 }
747
748
749 void make_pine_tree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef, s32 seed)
750 {
751         /*
752                 NOTE: Tree-placing code is currently duplicated in the engine
753                 and in games that have saplings; both are deprecated but not
754                 replaced yet
755         */
756         content_t c_tree   = ndef->getId("mapgen_pine_tree");
757         content_t c_leaves = ndef->getId("mapgen_pine_needles");
758         content_t c_snow = ndef->getId("mapgen_snow");
759         if (c_tree == CONTENT_IGNORE)
760                 c_tree = ndef->getId("mapgen_tree");
761         if (c_leaves == CONTENT_IGNORE)
762                 c_leaves = ndef->getId("mapgen_leaves");
763         if (c_snow == CONTENT_IGNORE)
764                 c_snow = CONTENT_AIR;
765
766         MapNode treenode(c_tree);
767         MapNode leavesnode(c_leaves);
768         MapNode snownode(c_snow);
769
770         PseudoRandom pr(seed);
771         u16 trunk_h = pr.range(9, 13);
772         v3s16 p1 = p0;
773         for (u16 ii = 0; ii < trunk_h; ii++) {
774                 if (vmanip.m_area.contains(p1)) {
775                         u32 vi = vmanip.m_area.index(p1);
776                         vmanip.m_data[vi] = treenode;
777                 }
778                 p1.Y++;
779         }
780
781         // Make p1 the top node of the trunk
782         p1.Y -= 1;
783
784         VoxelArea leaves_a(v3s16(-3, -6, -3), v3s16(3, 3, 3));
785         Buffer<u8> leaves_d(leaves_a.getVolume());
786         for (s32 i = 0; i < leaves_a.getVolume(); i++)
787                 leaves_d[i] = 0;
788
789         // Upper branches
790         u16 dev = 3;
791         for (s16 yy = -1; yy <= 1; yy++) {
792                 for (s16 zz = -dev; zz <= dev; zz++) {
793                         u32 i = leaves_a.index(v3s16(-dev, yy, zz));
794                         u32 ia = leaves_a.index(v3s16(-dev, yy+1, zz));
795                         for (s16 xx = -dev; xx <= dev; xx++) {
796                                 if (pr.range(0, 20) <= 19 - dev) {
797                                         leaves_d[i] = 1;
798                                         leaves_d[ia] = 2;
799                                 }
800                                 i++;
801                                 ia++;
802                         }
803                 }
804                 dev--;
805         }
806
807         // Centre top nodes
808         leaves_d[leaves_a.index(v3s16(0, 1, 0))] = 1;
809         leaves_d[leaves_a.index(v3s16(0, 2, 0))] = 1;
810         leaves_d[leaves_a.index(v3s16(0, 3, 0))] = 2;
811
812         // Lower branches
813         s16 my = -6;
814         for (u32 iii = 0; iii < 20; iii++) {
815                 s16 xi = pr.range(-3, 2);
816                 s16 yy = pr.range(-6, -5);
817                 s16 zi = pr.range(-3, 2);
818                 if (yy > my)
819                         my = yy;
820                 for (s16 zz = zi; zz <= zi + 1; zz++) {
821                         u32 i = leaves_a.index(v3s16(xi, yy, zz));
822                         u32 ia = leaves_a.index(v3s16(xi, yy + 1, zz));
823                         for (s32 xx = xi; xx <= xi + 1; xx++) {
824                                 leaves_d[i] = 1;
825                                 if (leaves_d[ia] == 0)
826                                         leaves_d[ia] = 2;
827                                 i++;
828                                 ia++;
829                         }
830                 }
831         }
832
833         dev = 2;
834         for (s16 yy = my + 1; yy <= my + 2; yy++) {
835                 for (s16 zz = -dev; zz <= dev; zz++) {
836                         u32 i = leaves_a.index(v3s16(-dev, yy, zz));
837                         u32 ia = leaves_a.index(v3s16(-dev, yy + 1, zz));
838                         for (s16 xx = -dev; xx <= dev; xx++) {
839                                 if (pr.range(0, 20) <= 19 - dev) {
840                                         leaves_d[i] = 1;
841                                         leaves_d[ia] = 2;
842                                 }
843                                 i++;
844                                 ia++;
845                         }
846                 }
847                 dev--;
848         }
849
850         // Blit leaves to vmanip
851         for (s16 z = leaves_a.MinEdge.Z; z <= leaves_a.MaxEdge.Z; z++)
852         for (s16 y = leaves_a.MinEdge.Y; y <= leaves_a.MaxEdge.Y; y++) {
853                 v3s16 pmin(leaves_a.MinEdge.X, y, z);
854                 u32 i = leaves_a.index(pmin);
855                 u32 vi = vmanip.m_area.index(pmin + p1);
856                 for (s16 x = leaves_a.MinEdge.X; x <= leaves_a.MaxEdge.X; x++) {
857                         v3s16 p(x, y, z);
858                         if (vmanip.m_area.contains(p + p1) == true &&
859                                         (vmanip.m_data[vi].getContent() == CONTENT_AIR ||
860                                         vmanip.m_data[vi].getContent() == CONTENT_IGNORE ||
861                                         vmanip.m_data[vi] == snownode)) {
862                                 if (leaves_d[i] == 1)
863                                         vmanip.m_data[vi] = leavesnode;
864                                 else if (leaves_d[i] == 2)
865                                         vmanip.m_data[vi] = snownode;
866                         }
867                         vi++;
868                         i++;
869                 }
870         }
871 }
872
873 }; // namespace treegen