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
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.
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.
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.
24 #include "util/pointer.h"
25 #include "util/numeric.h"
30 #include "voxelalgorithms.h"
35 void make_tree(MMVManip
&vmanip
, v3s16 p0
, bool is_apple_tree
,
36 const NodeDefManager
*ndef
, s32 seed
)
39 NOTE: Tree-placing code is currently duplicated in the engine
40 and in games that have saplings; both are deprecated but not
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
;
53 PseudoRandom
pr(seed
);
54 s16 trunk_h
= pr
.range(4, 5);
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
;
64 // p1 is now the last piece of the trunk
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
++)
72 // Force leaves at near the end of the trunk
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;
80 // Add leaves randomly
81 for (u32 iii
= 0; iii
< 7; iii
++) {
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
)
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;
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
++) {
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
;
111 vmanip
.m_data
[vi
] = leavesnode
;
121 // L-System tree LUA spawner
122 treegen::error
spawn_ltree(ServerMap
*map
, v3s16 p0
,
123 const NodeDefManager
*ndef
, const TreeDef
&tree_definition
)
125 std::map
<v3s16
, MapBlock
*> modified_blocks
;
126 MMVManip
vmanip(map
);
127 v3s16 tree_blockp
= getNodeBlockPos(p0
);
130 vmanip
.initialEmerge(tree_blockp
- v3s16(1, 1, 1), tree_blockp
+ v3s16(1, 3, 1));
131 e
= make_ltree(vmanip
, p0
, ndef
, tree_definition
);
135 voxalgo::blit_back_with_light(map
, &vmanip
, &modified_blocks
);
137 // Send a MEET_OTHER 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
);
147 //L-System tree generator
148 treegen::error
make_ltree(MMVManip
&vmanip
, v3s16 p0
,
149 const NodeDefManager
*ndef
, TreeDef tree_definition
)
152 if (tree_definition
.explicit_seed
)
153 seed
= tree_definition
.seed
+ 14002;
155 seed
= p0
.X
* 2 + p0
.Y
* 4 + p0
.Z
; // use the tree position to seed PRNG
156 PseudoRandom
ps(seed
);
158 // chance of inserting abcd rules
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
);
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;
175 //initialize rotation matrix, position and stacks for branches
176 core::matrix4 rotation
;
177 rotation
= setRotationAxisRadians(rotation
, M_PI
/ 2, v3f(0, 0, 1));
182 std::stack
<core::matrix4
> stack_orientation
;
183 std::stack
<v3f
> stack_position
;
186 std::string axiom
= tree_definition
.initial_axiom
;
187 for (s16 i
= 0; i
< iterations
; i
++) {
189 for (s16 j
= 0; j
< (s16
)axiom
.size(); j
++) {
190 char axiom_char
= axiom
.at(j
);
191 switch (axiom_char
) {
193 temp
+= tree_definition
.rules_a
;
196 temp
+= tree_definition
.rules_b
;
199 temp
+= tree_definition
.rules_c
;
202 temp
+= tree_definition
.rules_d
;
205 if (prop_a
>= ps
.range(1, 10))
206 temp
+= tree_definition
.rules_a
;
209 if (prop_b
>= ps
.range(1, 10))
210 temp
+= tree_definition
.rules_b
;
213 if (prop_c
>= ps
.range(1, 10))
214 temp
+= tree_definition
.rules_c
;
217 if (prop_d
>= ps
.range(1, 10))
218 temp
+= tree_definition
.rules_d
;
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(
232 v3f(position
.X
+ 1, position
.Y
- 1, position
.Z
),
235 tree_trunk_placement(
237 v3f(position
.X
, position
.Y
- 1, position
.Z
+ 1),
240 tree_trunk_placement(
242 v3f(position
.X
+ 1, position
.Y
- 1, position
.Z
+ 1),
245 } else if (tree_definition
.trunk_type
== "crossed") {
246 tree_trunk_placement(
248 v3f(position
.X
+ 1, position
.Y
- 1, position
.Z
),
251 tree_trunk_placement(
253 v3f(position
.X
- 1, position
.Y
- 1, position
.Z
),
256 tree_trunk_placement(
258 v3f(position
.X
, position
.Y
- 1, position
.Z
+ 1),
261 tree_trunk_placement(
263 v3f(position
.X
, position
.Y
- 1, position
.Z
- 1),
268 /* build tree out of generated axiom
270 Key for Special L-System Symbols used in Axioms
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
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();
302 switch (axiom_char
) {
305 dir
= transposeMatrix(rotation
, dir
);
309 tree_trunk_placement(
311 v3f(position
.X
, position
.Y
, position
.Z
),
314 if (tree_definition
.trunk_type
== "double" &&
315 !tree_definition
.thin_branches
) {
316 tree_trunk_placement(
318 v3f(position
.X
+ 1, position
.Y
, position
.Z
),
321 tree_trunk_placement(
323 v3f(position
.X
, position
.Y
, position
.Z
+ 1),
326 tree_trunk_placement(
328 v3f(position
.X
+ 1, position
.Y
, position
.Z
+ 1),
331 } else if (tree_definition
.trunk_type
== "crossed" &&
332 !tree_definition
.thin_branches
) {
333 tree_trunk_placement(
335 v3f(position
.X
+ 1, position
.Y
, position
.Z
),
338 tree_trunk_placement(
340 v3f(position
.X
- 1, position
.Y
, position
.Z
),
343 tree_trunk_placement(
345 v3f(position
.X
, position
.Y
, position
.Z
+ 1),
348 tree_trunk_placement(
350 v3f(position
.X
, position
.Y
, position
.Z
- 1),
355 dir
= transposeMatrix(rotation
, dir
);
359 tree_trunk_placement(
361 v3f(position
.X
, position
.Y
, position
.Z
),
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(
371 v3f(position
.X
+ 1, position
.Y
, position
.Z
),
374 tree_trunk_placement(
376 v3f(position
.X
, position
.Y
, position
.Z
+ 1),
379 tree_trunk_placement(
381 v3f(position
.X
+ 1, position
.Y
, position
.Z
+ 1),
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(
391 v3f(position
.X
+ 1, position
.Y
, position
.Z
),
394 tree_trunk_placement(
396 v3f(position
.X
- 1, position
.Y
, position
.Z
),
399 tree_trunk_placement(
401 v3f(position
.X
, position
.Y
, position
.Z
+ 1),
404 tree_trunk_placement(
406 v3f(position
.X
, position
.Y
, position
.Z
- 1),
409 } if (!stack_orientation
.empty()) {
411 for (x
= -size
; x
<= size
; x
++)
412 for (y
= -size
; y
<= size
; y
++)
413 for (z
= -size
; z
<= size
; z
++) {
414 if (abs(x
) == size
&&
417 tree_leaves_placement(
419 v3f(position
.X
+ x
+ 1, position
.Y
+ y
,
424 tree_leaves_placement(
426 v3f(position
.X
+ x
- 1, position
.Y
+ y
,
431 tree_leaves_placement(
432 vmanip
,v3f(position
.X
+ x
, position
.Y
+ y
,
437 tree_leaves_placement(
438 vmanip
,v3f(position
.X
+ x
, position
.Y
+ y
,
447 dir
= transposeMatrix(rotation
, dir
);
451 tree_single_leaves_placement(
453 v3f(position
.X
, position
.Y
, position
.Z
),
458 dir
= transposeMatrix(rotation
, dir
);
462 tree_fruit_placement(
464 v3f(position
.X
, position
.Y
, position
.Z
),
468 dir
= transposeMatrix(rotation
, dir
);
472 // turtle orientation commands
474 stack_orientation
.push(rotation
);
475 stack_position
.push(position
);
478 if (stack_orientation
.empty())
479 return UNBALANCED_BRACKETS
;
480 rotation
= stack_orientation
.top();
481 stack_orientation
.pop();
482 position
= stack_position
.top();
483 stack_position
.pop();
486 temp_rotation
.makeIdentity();
487 temp_rotation
= setRotationAxisRadians(temp_rotation
,
488 angle_in_radians
+ angleOffset_in_radians
, v3f(0, 0, 1));
489 rotation
*= temp_rotation
;
492 temp_rotation
.makeIdentity();
493 temp_rotation
= setRotationAxisRadians(temp_rotation
,
494 angle_in_radians
+ angleOffset_in_radians
, v3f(0, 0, -1));
495 rotation
*= temp_rotation
;
498 temp_rotation
.makeIdentity();
499 temp_rotation
= setRotationAxisRadians(temp_rotation
,
500 angle_in_radians
+ angleOffset_in_radians
, v3f(0, 1, 0));
501 rotation
*= temp_rotation
;
504 temp_rotation
.makeIdentity();
505 temp_rotation
= setRotationAxisRadians(temp_rotation
,
506 angle_in_radians
+ angleOffset_in_radians
, v3f(0, -1, 0));
507 rotation
*= temp_rotation
;
510 temp_rotation
.makeIdentity();
511 temp_rotation
= setRotationAxisRadians(temp_rotation
,
512 angle_in_radians
, v3f(1, 0, 0));
513 rotation
*= temp_rotation
;
516 temp_rotation
.makeIdentity();
517 temp_rotation
= setRotationAxisRadians(temp_rotation
,
518 angle_in_radians
, v3f(-1, 0, 0));
519 rotation
*= temp_rotation
;
530 void tree_trunk_placement(MMVManip
&vmanip
, v3f p0
, TreeDef
&tree_definition
)
532 v3s16 p1
= v3s16(myround(p0
.X
), myround(p0
.Y
), myround(p0
.Z
));
533 if (!vmanip
.m_area
.contains(p1
))
535 u32 vi
= vmanip
.m_area
.index(p1
);
536 content_t current_node
= vmanip
.m_data
[vi
].getContent();
537 if (current_node
!= CONTENT_AIR
&& current_node
!= CONTENT_IGNORE
538 && current_node
!= tree_definition
.leavesnode
.getContent()
539 && current_node
!= tree_definition
.leaves2node
.getContent()
540 && current_node
!= tree_definition
.fruitnode
.getContent())
542 vmanip
.m_data
[vi
] = tree_definition
.trunknode
;
546 void tree_leaves_placement(MMVManip
&vmanip
, v3f p0
,
547 PseudoRandom ps
, TreeDef
&tree_definition
)
549 MapNode leavesnode
= tree_definition
.leavesnode
;
550 if (ps
.range(1, 100) > 100 - tree_definition
.leaves2_chance
)
551 leavesnode
= tree_definition
.leaves2node
;
552 v3s16 p1
= v3s16(myround(p0
.X
), myround(p0
.Y
), myround(p0
.Z
));
553 if (!vmanip
.m_area
.contains(p1
))
555 u32 vi
= vmanip
.m_area
.index(p1
);
556 if (vmanip
.m_data
[vi
].getContent() != CONTENT_AIR
557 && vmanip
.m_data
[vi
].getContent() != CONTENT_IGNORE
)
559 if (tree_definition
.fruit_chance
> 0) {
560 if (ps
.range(1, 100) > 100 - tree_definition
.fruit_chance
)
561 vmanip
.m_data
[vmanip
.m_area
.index(p1
)] = tree_definition
.fruitnode
;
563 vmanip
.m_data
[vmanip
.m_area
.index(p1
)] = leavesnode
;
564 } else if (ps
.range(1, 100) > 20) {
565 vmanip
.m_data
[vmanip
.m_area
.index(p1
)] = leavesnode
;
570 void tree_single_leaves_placement(MMVManip
&vmanip
, v3f p0
,
571 PseudoRandom ps
, TreeDef
&tree_definition
)
573 MapNode leavesnode
= tree_definition
.leavesnode
;
574 if (ps
.range(1, 100) > 100 - tree_definition
.leaves2_chance
)
575 leavesnode
= tree_definition
.leaves2node
;
576 v3s16 p1
= v3s16(myround(p0
.X
), myround(p0
.Y
), myround(p0
.Z
));
577 if (!vmanip
.m_area
.contains(p1
))
579 u32 vi
= vmanip
.m_area
.index(p1
);
580 if (vmanip
.m_data
[vi
].getContent() != CONTENT_AIR
581 && vmanip
.m_data
[vi
].getContent() != CONTENT_IGNORE
)
583 vmanip
.m_data
[vmanip
.m_area
.index(p1
)] = leavesnode
;
587 void tree_fruit_placement(MMVManip
&vmanip
, v3f p0
, TreeDef
&tree_definition
)
589 v3s16 p1
= v3s16(myround(p0
.X
), myround(p0
.Y
), myround(p0
.Z
));
590 if (!vmanip
.m_area
.contains(p1
))
592 u32 vi
= vmanip
.m_area
.index(p1
);
593 if (vmanip
.m_data
[vi
].getContent() != CONTENT_AIR
594 && vmanip
.m_data
[vi
].getContent() != CONTENT_IGNORE
)
596 vmanip
.m_data
[vmanip
.m_area
.index(p1
)] = tree_definition
.fruitnode
;
600 irr::core::matrix4
setRotationAxisRadians(irr::core::matrix4 M
, double angle
, v3f axis
)
602 double c
= cos(angle
);
603 double s
= sin(angle
);
606 double tx
= t
* axis
.X
;
607 double ty
= t
* axis
.Y
;
608 double tz
= t
* axis
.Z
;
609 double sx
= s
* axis
.X
;
610 double sy
= s
* axis
.Y
;
611 double sz
= s
* axis
.Z
;
613 M
[0] = tx
* axis
.X
+ c
;
614 M
[1] = tx
* axis
.Y
+ sz
;
615 M
[2] = tx
* axis
.Z
- sy
;
617 M
[4] = ty
* axis
.X
- sz
;
618 M
[5] = ty
* axis
.Y
+ c
;
619 M
[6] = ty
* axis
.Z
+ sx
;
621 M
[8] = tz
* axis
.X
+ sy
;
622 M
[9] = tz
* axis
.Y
- sx
;
623 M
[10] = tz
* axis
.Z
+ c
;
628 v3f
transposeMatrix(irr::core::matrix4 M
, v3f v
)
631 double x
= M
[0] * v
.X
+ M
[4] * v
.Y
+ M
[8] * v
.Z
+M
[12];
632 double y
= M
[1] * v
.X
+ M
[5] * v
.Y
+ M
[9] * v
.Z
+M
[13];
633 double z
= M
[2] * v
.X
+ M
[6] * v
.Y
+ M
[10] * v
.Z
+M
[14];
641 void make_jungletree(MMVManip
&vmanip
, v3s16 p0
, const NodeDefManager
*ndef
,
645 NOTE: Tree-placing code is currently duplicated in the engine
646 and in games that have saplings; both are deprecated but not
649 content_t c_tree
= ndef
->getId("mapgen_jungletree");
650 content_t c_leaves
= ndef
->getId("mapgen_jungleleaves");
651 if (c_tree
== CONTENT_IGNORE
)
652 c_tree
= ndef
->getId("mapgen_tree");
653 if (c_leaves
== CONTENT_IGNORE
)
654 c_leaves
= ndef
->getId("mapgen_leaves");
655 if (c_tree
== CONTENT_IGNORE
)
656 errorstream
<< "Treegen: Mapgen alias 'mapgen_jungletree' is invalid!" << std::endl
;
657 if (c_leaves
== CONTENT_IGNORE
)
658 errorstream
<< "Treegen: Mapgen alias 'mapgen_jungleleaves' is invalid!" << std::endl
;
660 MapNode
treenode(c_tree
);
661 MapNode
leavesnode(c_leaves
);
663 PseudoRandom
pr(seed
);
664 for (s16 x
= -1; x
<= 1; x
++)
665 for (s16 z
= -1; z
<= 1; z
++) {
666 if (pr
.range(0, 2) == 0)
668 v3s16 p1
= p0
+ v3s16(x
, 0, z
);
669 v3s16 p2
= p0
+ v3s16(x
, -1, z
);
670 u32 vi1
= vmanip
.m_area
.index(p1
);
671 u32 vi2
= vmanip
.m_area
.index(p2
);
673 if (vmanip
.m_area
.contains(p2
) &&
674 vmanip
.m_data
[vi2
].getContent() == CONTENT_AIR
)
675 vmanip
.m_data
[vi2
] = treenode
;
676 else if (vmanip
.m_area
.contains(p1
) &&
677 vmanip
.m_data
[vi1
].getContent() == CONTENT_AIR
)
678 vmanip
.m_data
[vi1
] = treenode
;
680 vmanip
.m_data
[vmanip
.m_area
.index(p0
)] = treenode
;
682 s16 trunk_h
= pr
.range(8, 12);
684 for (s16 ii
= 0; ii
< trunk_h
; ii
++) {
685 if (vmanip
.m_area
.contains(p1
)) {
686 u32 vi
= vmanip
.m_area
.index(p1
);
687 vmanip
.m_data
[vi
] = treenode
;
692 // p1 is now the last piece of the trunk
695 VoxelArea
leaves_a(v3s16(-3, -2, -3), v3s16(3, 2, 3));
696 //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
697 Buffer
<u8
> leaves_d(leaves_a
.getVolume());
698 for (s32 i
= 0; i
< leaves_a
.getVolume(); i
++)
701 // Force leaves at near the end of the trunk
703 for (s16 z
= -d
; z
<= d
; z
++)
704 for (s16 y
= -d
; y
<= d
; y
++)
705 for (s16 x
= -d
; x
<= d
; x
++) {
706 leaves_d
[leaves_a
.index(v3s16(x
,y
,z
))] = 1;
709 // Add leaves randomly
710 for (u32 iii
= 0; iii
< 30; iii
++) {
712 pr
.range(leaves_a
.MinEdge
.X
, leaves_a
.MaxEdge
.X
- d
),
713 pr
.range(leaves_a
.MinEdge
.Y
, leaves_a
.MaxEdge
.Y
- d
),
714 pr
.range(leaves_a
.MinEdge
.Z
, leaves_a
.MaxEdge
.Z
- d
)
717 for (s16 z
= 0; z
<= d
; z
++)
718 for (s16 y
= 0; y
<= d
; y
++)
719 for (s16 x
= 0; x
<= d
; x
++) {
720 leaves_d
[leaves_a
.index(p
+ v3s16(x
, y
, z
))] = 1;
724 // Blit leaves to vmanip
725 for (s16 z
= leaves_a
.MinEdge
.Z
; z
<= leaves_a
.MaxEdge
.Z
; z
++)
726 for (s16 y
= leaves_a
.MinEdge
.Y
; y
<= leaves_a
.MaxEdge
.Y
; y
++) {
727 v3s16
pmin(leaves_a
.MinEdge
.X
, y
, z
);
728 u32 i
= leaves_a
.index(pmin
);
729 u32 vi
= vmanip
.m_area
.index(pmin
+ p1
);
730 for (s16 x
= leaves_a
.MinEdge
.X
; x
<= leaves_a
.MaxEdge
.X
; x
++) {
732 if (vmanip
.m_area
.contains(p
+ p1
) &&
733 (vmanip
.m_data
[vi
].getContent() == CONTENT_AIR
||
734 vmanip
.m_data
[vi
].getContent() == CONTENT_IGNORE
)) {
735 if (leaves_d
[i
] == 1)
736 vmanip
.m_data
[vi
] = leavesnode
;
745 void make_pine_tree(MMVManip
&vmanip
, v3s16 p0
, const NodeDefManager
*ndef
,
749 NOTE: Tree-placing code is currently duplicated in the engine
750 and in games that have saplings; both are deprecated but not
753 content_t c_tree
= ndef
->getId("mapgen_pine_tree");
754 content_t c_leaves
= ndef
->getId("mapgen_pine_needles");
755 content_t c_snow
= ndef
->getId("mapgen_snow");
756 if (c_tree
== CONTENT_IGNORE
)
757 c_tree
= ndef
->getId("mapgen_tree");
758 if (c_leaves
== CONTENT_IGNORE
)
759 c_leaves
= ndef
->getId("mapgen_leaves");
760 if (c_snow
== CONTENT_IGNORE
)
761 c_snow
= CONTENT_AIR
;
762 if (c_tree
== CONTENT_IGNORE
)
763 errorstream
<< "Treegen: Mapgen alias 'mapgen_pine_tree' is invalid!" << std::endl
;
764 if (c_leaves
== CONTENT_IGNORE
)
765 errorstream
<< "Treegen: Mapgen alias 'mapgen_pine_needles' is invalid!" << std::endl
;
767 MapNode
treenode(c_tree
);
768 MapNode
leavesnode(c_leaves
);
769 MapNode
snownode(c_snow
);
771 PseudoRandom
pr(seed
);
772 u16 trunk_h
= pr
.range(9, 13);
774 for (u16 ii
= 0; ii
< trunk_h
; ii
++) {
775 if (vmanip
.m_area
.contains(p1
)) {
776 u32 vi
= vmanip
.m_area
.index(p1
);
777 vmanip
.m_data
[vi
] = treenode
;
782 // Make p1 the top node of the trunk
785 VoxelArea
leaves_a(v3s16(-3, -6, -3), v3s16(3, 3, 3));
786 Buffer
<u8
> leaves_d(leaves_a
.getVolume());
787 for (s32 i
= 0; i
< leaves_a
.getVolume(); i
++)
792 for (s16 yy
= -1; yy
<= 1; yy
++) {
793 for (s16 zz
= -dev
; zz
<= dev
; zz
++) {
794 u32 i
= leaves_a
.index(v3s16(-dev
, yy
, zz
));
795 u32 ia
= leaves_a
.index(v3s16(-dev
, yy
+1, zz
));
796 for (s16 xx
= -dev
; xx
<= dev
; xx
++) {
797 if (pr
.range(0, 20) <= 19 - dev
) {
809 leaves_d
[leaves_a
.index(v3s16(0, 1, 0))] = 1;
810 leaves_d
[leaves_a
.index(v3s16(0, 2, 0))] = 1;
811 leaves_d
[leaves_a
.index(v3s16(0, 3, 0))] = 2;
815 for (u32 iii
= 0; iii
< 20; iii
++) {
816 s16 xi
= pr
.range(-3, 2);
817 s16 yy
= pr
.range(-6, -5);
818 s16 zi
= pr
.range(-3, 2);
821 for (s16 zz
= zi
; zz
<= zi
+ 1; zz
++) {
822 u32 i
= leaves_a
.index(v3s16(xi
, yy
, zz
));
823 u32 ia
= leaves_a
.index(v3s16(xi
, yy
+ 1, zz
));
824 for (s32 xx
= xi
; xx
<= xi
+ 1; xx
++) {
826 if (leaves_d
[ia
] == 0)
835 for (s16 yy
= my
+ 1; yy
<= my
+ 2; yy
++) {
836 for (s16 zz
= -dev
; zz
<= dev
; zz
++) {
837 u32 i
= leaves_a
.index(v3s16(-dev
, yy
, zz
));
838 u32 ia
= leaves_a
.index(v3s16(-dev
, yy
+ 1, zz
));
839 for (s16 xx
= -dev
; xx
<= dev
; xx
++) {
840 if (pr
.range(0, 20) <= 19 - dev
) {
851 // Blit leaves to vmanip
852 for (s16 z
= leaves_a
.MinEdge
.Z
; z
<= leaves_a
.MaxEdge
.Z
; z
++)
853 for (s16 y
= leaves_a
.MinEdge
.Y
; y
<= leaves_a
.MaxEdge
.Y
; y
++) {
854 v3s16
pmin(leaves_a
.MinEdge
.X
, y
, z
);
855 u32 i
= leaves_a
.index(pmin
);
856 u32 vi
= vmanip
.m_area
.index(pmin
+ p1
);
857 for (s16 x
= leaves_a
.MinEdge
.X
; x
<= leaves_a
.MaxEdge
.X
; x
++) {
859 if (vmanip
.m_area
.contains(p
+ p1
) &&
860 (vmanip
.m_data
[vi
].getContent() == CONTENT_AIR
||
861 vmanip
.m_data
[vi
].getContent() == CONTENT_IGNORE
||
862 vmanip
.m_data
[vi
] == snownode
)) {
863 if (leaves_d
[i
] == 1)
864 vmanip
.m_data
[vi
] = leavesnode
;
865 else if (leaves_d
[i
] == 2)
866 vmanip
.m_data
[vi
] = snownode
;
874 }; // namespace treegen