clientobject: add null checks
[waspsaliva.git] / src / mapgen / treegen.cpp
blobe633d800a3713001d68289fd7d487574abd0b2ba
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
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.
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"
32 namespace treegen
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
41 replaced yet
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);
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;
61 p1.Y++;
64 // p1 is now the last piece of the trunk
65 p1.Y -= 1;
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;
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;
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)
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++) {
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;
114 vi++;
115 i++;
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);
128 treegen::error e;
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;
135 voxalgo::blit_back_with_light(map, &vmanip, &modified_blocks);
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;
147 //L-System tree generator
148 treegen::error make_ltree(MMVManip &vmanip, v3s16 p0,
149 const NodeDefManager *ndef, TreeDef tree_definition)
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);
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;
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;
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));
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;
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;
225 axiom = temp;
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
235 tree_trunk_placement(
236 vmanip,
237 v3f(position.X, position.Y - 1, position.Z + 1),
238 tree_definition
240 tree_trunk_placement(
241 vmanip,
242 v3f(position.X + 1, position.Y - 1, position.Z + 1),
243 tree_definition
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
251 tree_trunk_placement(
252 vmanip,
253 v3f(position.X - 1, position.Y - 1, position.Z),
254 tree_definition
256 tree_trunk_placement(
257 vmanip,
258 v3f(position.X, position.Y - 1, position.Z + 1),
259 tree_definition
261 tree_trunk_placement(
262 vmanip,
263 v3f(position.X, position.Y - 1, position.Z - 1),
264 tree_definition
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
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
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
321 tree_trunk_placement(
322 vmanip,
323 v3f(position.X, position.Y, position.Z + 1),
324 tree_definition
326 tree_trunk_placement(
327 vmanip,
328 v3f(position.X + 1, position.Y, position.Z + 1),
329 tree_definition
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
338 tree_trunk_placement(
339 vmanip,
340 v3f(position.X - 1, position.Y, position.Z),
341 tree_definition
343 tree_trunk_placement(
344 vmanip,
345 v3f(position.X, position.Y, position.Z + 1),
346 tree_definition
348 tree_trunk_placement(
349 vmanip,
350 v3f(position.X, position.Y, position.Z - 1),
351 tree_definition
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
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
374 tree_trunk_placement(
375 vmanip,
376 v3f(position.X, position.Y, position.Z + 1),
377 tree_definition
379 tree_trunk_placement(
380 vmanip,
381 v3f(position.X + 1, position.Y, position.Z + 1),
382 tree_definition
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
394 tree_trunk_placement(
395 vmanip,
396 v3f(position.X - 1, position.Y, position.Z),
397 tree_definition
399 tree_trunk_placement(
400 vmanip,
401 v3f(position.X, position.Y, position.Z + 1),
402 tree_definition
404 tree_trunk_placement(
405 vmanip,
406 v3f(position.X, position.Y, position.Z - 1),
407 tree_definition
409 } if (!stack_orientation.empty()) {
410 s16 size = 1;
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 &&
415 abs(y) == size &&
416 abs(z) == size) {
417 tree_leaves_placement(
418 vmanip,
419 v3f(position.X + x + 1, position.Y + y,
420 position.Z + z),
421 ps.next(),
422 tree_definition
424 tree_leaves_placement(
425 vmanip,
426 v3f(position.X + x - 1, position.Y + y,
427 position.Z + z),
428 ps.next(),
429 tree_definition
431 tree_leaves_placement(
432 vmanip,v3f(position.X + x, position.Y + y,
433 position.Z + z + 1),
434 ps.next(),
435 tree_definition
437 tree_leaves_placement(
438 vmanip,v3f(position.X + x, position.Y + y,
439 position.Z + z - 1),
440 ps.next(),
441 tree_definition
446 dir = v3f(1, 0, 0);
447 dir = transposeMatrix(rotation, dir);
448 position += dir;
449 break;
450 case 'f':
451 tree_single_leaves_placement(
452 vmanip,
453 v3f(position.X, position.Y, position.Z),
454 ps.next(),
455 tree_definition
457 dir = v3f(1, 0, 0);
458 dir = transposeMatrix(rotation, dir);
459 position += dir;
460 break;
461 case 'R':
462 tree_fruit_placement(
463 vmanip,
464 v3f(position.X, position.Y, position.Z),
465 tree_definition
467 dir = v3f(1, 0, 0);
468 dir = transposeMatrix(rotation, dir);
469 position += dir;
470 break;
472 // turtle orientation commands
473 case '[':
474 stack_orientation.push(rotation);
475 stack_position.push(position);
476 break;
477 case ']':
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();
484 break;
485 case '+':
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;
490 break;
491 case '-':
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;
496 break;
497 case '&':
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;
502 break;
503 case '^':
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;
508 break;
509 case '*':
510 temp_rotation.makeIdentity();
511 temp_rotation = setRotationAxisRadians(temp_rotation,
512 angle_in_radians, v3f(1, 0, 0));
513 rotation *= temp_rotation;
514 break;
515 case '/':
516 temp_rotation.makeIdentity();
517 temp_rotation = setRotationAxisRadians(temp_rotation,
518 angle_in_radians, v3f(-1, 0, 0));
519 rotation *= temp_rotation;
520 break;
521 default:
522 break;
526 return SUCCESS;
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))
534 return;
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())
541 return;
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))
554 return;
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)
558 return;
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;
562 else
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))
578 return;
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)
582 return;
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))
591 return;
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)
595 return;
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);
604 double t = 1.0 - c;
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;
624 return M;
628 v3f transposeMatrix(irr::core::matrix4 M, v3f v)
630 v3f translated;
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];
634 translated.X = x;
635 translated.Y = y;
636 translated.Z = z;
637 return translated;
641 void make_jungletree(MMVManip &vmanip, v3s16 p0, const NodeDefManager *ndef,
642 s32 seed)
645 NOTE: Tree-placing code is currently duplicated in the engine
646 and in games that have saplings; both are deprecated but not
647 replaced yet
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)
667 continue;
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);
683 v3s16 p1 = p0;
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;
689 p1.Y++;
692 // p1 is now the last piece of the trunk
693 p1.Y -= 1;
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++)
699 leaves_d[i] = 0;
701 // Force leaves at near the end of the trunk
702 s16 d = 1;
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++) {
711 v3s16 p(
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++) {
731 v3s16 p(x, y, z);
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;
738 vi++;
739 i++;
745 void make_pine_tree(MMVManip &vmanip, v3s16 p0, const NodeDefManager *ndef,
746 s32 seed)
749 NOTE: Tree-placing code is currently duplicated in the engine
750 and in games that have saplings; both are deprecated but not
751 replaced yet
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);
773 v3s16 p1 = p0;
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;
779 p1.Y++;
782 // Make p1 the top node of the trunk
783 p1.Y -= 1;
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++)
788 leaves_d[i] = 0;
790 // Upper branches
791 u16 dev = 3;
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) {
798 leaves_d[i] = 1;
799 leaves_d[ia] = 2;
801 i++;
802 ia++;
805 dev--;
808 // Centre top nodes
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;
813 // Lower branches
814 s16 my = -6;
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);
819 if (yy > my)
820 my = yy;
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++) {
825 leaves_d[i] = 1;
826 if (leaves_d[ia] == 0)
827 leaves_d[ia] = 2;
828 i++;
829 ia++;
834 dev = 2;
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) {
841 leaves_d[i] = 1;
842 leaves_d[ia] = 2;
844 i++;
845 ia++;
848 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++) {
858 v3s16 p(x, y, z);
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;
868 vi++;
869 i++;
874 }; // namespace treegen