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