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.
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.
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.
22 #include "util/pointer.h"
23 #include "util/numeric.h"
24 #include "util/mathconstants.h"
26 #include "environment.h"
33 void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
34 bool is_apple_tree, INodeDefManager *ndef, int seed)
37 NOTE: Tree-placing code is currently duplicated in the engine
38 and in games that have saplings; both are deprecated but not
41 MapNode treenode(ndef->getId("mapgen_tree"));
42 MapNode leavesnode(ndef->getId("mapgen_leaves"));
43 MapNode applenode(ndef->getId("mapgen_apple"));
45 PseudoRandom pr(seed);
46 s16 trunk_h = pr.range(4, 5);
48 for(s16 ii=0; ii<trunk_h; ii++)
50 if(vmanip.m_area.contains(p1))
51 if(ii == 0 || vmanip.getNodeNoExNoEmerge(p1).getContent() == CONTENT_AIR)
52 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
56 // p1 is now the last piece of the trunk
59 VoxelArea leaves_a(v3s16(-2,-1,-2), v3s16(2,2,2));
60 //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
61 Buffer<u8> leaves_d(leaves_a.getVolume());
62 for(s32 i=0; i<leaves_a.getVolume(); i++)
65 // Force leaves at near the end of the trunk
68 for(s16 z=-d; z<=d; z++)
69 for(s16 y=-d; y<=d; y++)
70 for(s16 x=-d; x<=d; x++)
72 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
76 // Add leaves randomly
77 for(u32 iii=0; iii<7; iii++)
82 pr.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
83 pr.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
84 pr.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
87 for(s16 z=0; z<=d; z++)
88 for(s16 y=0; y<=d; y++)
89 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 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
102 if(vmanip.m_area.contains(p) == false)
104 u32 vi = vmanip.m_area.index(p);
105 if(vmanip.m_data[vi].getContent() != CONTENT_AIR
106 && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
108 u32 i = leaves_a.index(x,y,z);
109 if(leaves_d[i] == 1) {
110 bool is_apple = pr.range(0,99) < 10;
111 if(is_apple_tree && is_apple) {
112 vmanip.m_data[vi] = applenode;
114 vmanip.m_data[vi] = leavesnode;
120 // L-System tree LUA spawner
121 treegen::error spawn_ltree(ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef, TreeDef tree_definition)
123 ServerMap *map = &env->getServerMap();
124 std::map<v3s16, MapBlock*> modified_blocks;
125 ManualMapVoxelManipulator vmanip(map);
126 v3s16 tree_blockp = getNodeBlockPos(p0);
129 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,3,1));
130 e = make_ltree (vmanip, p0, ndef, tree_definition);
134 vmanip.blitBackAll(&modified_blocks);
137 std::map<v3s16, MapBlock*> lighting_modified_blocks;
138 lighting_modified_blocks.insert(modified_blocks.begin(), modified_blocks.end());
139 map->updateLighting(lighting_modified_blocks, modified_blocks);
140 // Send a MEET_OTHER event
142 event.type = MEET_OTHER;
143 for(std::map<v3s16, MapBlock*>::iterator
144 i = modified_blocks.begin();
145 i != modified_blocks.end(); ++i)
147 event.modified_blocks.insert(i->first);
149 map->dispatchEvent(&event);
153 //L-System tree generator
154 treegen::error make_ltree(ManualMapVoxelManipulator &vmanip, v3s16 p0, INodeDefManager *ndef,
155 TreeDef tree_definition)
157 MapNode dirtnode(ndef->getId("mapgen_dirt"));
159 if (tree_definition.explicit_seed)
161 seed = tree_definition.seed+14002;
165 seed = p0.X*2 + p0.Y*4 + p0.Z; // use the tree position to seed PRNG
167 PseudoRandom ps(seed);
169 // chance of inserting abcd rules
175 //randomize tree growth level, minimum=2
176 s16 iterations = tree_definition.iterations;
177 if (tree_definition.iterations_random_level>0)
178 iterations -= ps.range(0,tree_definition.iterations_random_level);
182 s16 MAX_ANGLE_OFFSET = 5;
183 double angle_in_radians = (double)tree_definition.angle*M_PI/180;
184 double angleOffset_in_radians = (s16)(ps.range(0,1)%MAX_ANGLE_OFFSET)*M_PI/180;
186 //initialize rotation matrix, position and stacks for branches
187 core::matrix4 rotation;
188 rotation = setRotationAxisRadians(rotation, M_PI/2,v3f(0,0,1));
193 std::stack <core::matrix4> stack_orientation;
194 std::stack <v3f> stack_position;
197 std::string axiom = tree_definition.initial_axiom;
198 for(s16 i=0; i<iterations; i++)
200 std::string temp = "";
201 for(s16 j=0; j<(s16)axiom.size(); j++)
203 char axiom_char = axiom.at(j);
207 temp+=tree_definition.rules_a;
210 temp+=tree_definition.rules_b;
213 temp+=tree_definition.rules_c;
216 temp+=tree_definition.rules_d;
219 if (prop_a >= ps.range(1,10))
220 temp+=tree_definition.rules_a;
223 if (prop_b >= ps.range(1,10))
224 temp+=tree_definition.rules_b;
227 if (prop_c >= ps.range(1,10))
228 temp+=tree_definition.rules_c;
231 if (prop_d >= ps.range(1,10))
232 temp+=tree_definition.rules_d;
242 //make sure tree is not floating in the air
243 if (tree_definition.trunk_type == "double")
245 tree_node_placement(vmanip,v3f(position.X+1,position.Y-1,position.Z),dirtnode);
246 tree_node_placement(vmanip,v3f(position.X,position.Y-1,position.Z+1),dirtnode);
247 tree_node_placement(vmanip,v3f(position.X+1,position.Y-1,position.Z+1),dirtnode);
249 else if (tree_definition.trunk_type == "crossed")
251 tree_node_placement(vmanip,v3f(position.X+1,position.Y-1,position.Z),dirtnode);
252 tree_node_placement(vmanip,v3f(position.X-1,position.Y-1,position.Z),dirtnode);
253 tree_node_placement(vmanip,v3f(position.X,position.Y-1,position.Z+1),dirtnode);
254 tree_node_placement(vmanip,v3f(position.X,position.Y-1,position.Z-1),dirtnode);
257 /* build tree out of generated axiom
259 Key for Special L-System Symbols used in Axioms
261 G - move forward one unit with the pen up
262 F - move forward one unit with the pen down drawing trunks and branches
263 f - move forward one unit with the pen down drawing leaves (100% chance)
264 T - move forward one unit with the pen down drawing trunks only
265 R - move forward one unit with the pen down placing fruit
266 A - replace with rules set A
267 B - replace with rules set B
268 C - replace with rules set C
269 D - replace with rules set D
270 a - replace with rules set A, chance 90%
271 b - replace with rules set B, chance 80%
272 c - replace with rules set C, chance 70%
273 d - replace with rules set D, chance 60%
274 + - yaw the turtle right by angle degrees
275 - - yaw the turtle left by angle degrees
276 & - pitch the turtle down by angle degrees
277 ^ - pitch the turtle up by angle degrees
278 / - roll the turtle to the right by angle degrees
279 * - roll the turtle to the left by angle degrees
280 [ - save in stack current state info
281 ] - recover from stack state info
286 for(s16 i=0; i<(s16)axiom.size(); i++)
288 char axiom_char = axiom.at(i);
289 core::matrix4 temp_rotation;
290 temp_rotation.makeIdentity();
296 dir = transposeMatrix(rotation,dir);
300 tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z),tree_definition);
301 if (tree_definition.trunk_type == "double" && !tree_definition.thin_branches)
303 tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z),tree_definition);
304 tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z+1),tree_definition);
305 tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z+1),tree_definition);
307 else if (tree_definition.trunk_type == "crossed" && !tree_definition.thin_branches)
309 tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z),tree_definition);
310 tree_trunk_placement(vmanip,v3f(position.X-1,position.Y,position.Z),tree_definition);
311 tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z+1),tree_definition);
312 tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z-1),tree_definition);
315 dir = transposeMatrix(rotation,dir);
319 tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z),tree_definition);
320 if ((stack_orientation.empty() && tree_definition.trunk_type == "double") ||
321 (!stack_orientation.empty() && tree_definition.trunk_type == "double" && !tree_definition.thin_branches))
323 tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z),tree_definition);
324 tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z+1),tree_definition);
325 tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z+1),tree_definition);
327 else if ((stack_orientation.empty() && tree_definition.trunk_type == "crossed") ||
328 (!stack_orientation.empty() && tree_definition.trunk_type == "crossed" && !tree_definition.thin_branches))
330 tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z),tree_definition);
331 tree_trunk_placement(vmanip,v3f(position.X-1,position.Y,position.Z),tree_definition);
332 tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z+1),tree_definition);
333 tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z-1),tree_definition);
335 if (stack_orientation.empty() == false)
338 for(x=-size; x<=size; x++)
339 for(y=-size; y<=size; y++)
340 for(z=-size; z<=size; z++)
341 if (abs(x) == size && abs(y) == size && abs(z) == size)
343 tree_leaves_placement(vmanip,v3f(position.X+x+1,position.Y+y,position.Z+z),ps.next(), tree_definition);
344 tree_leaves_placement(vmanip,v3f(position.X+x-1,position.Y+y,position.Z+z),ps.next(), tree_definition);
345 tree_leaves_placement(vmanip,v3f(position.X+x,position.Y+y,position.Z+z+1),ps.next(), tree_definition);
346 tree_leaves_placement(vmanip,v3f(position.X+x,position.Y+y,position.Z+z-1),ps.next(), tree_definition);
350 dir = transposeMatrix(rotation,dir);
354 tree_single_leaves_placement(vmanip,v3f(position.X,position.Y,position.Z),ps.next() ,tree_definition);
356 dir = transposeMatrix(rotation,dir);
360 tree_fruit_placement(vmanip,v3f(position.X,position.Y,position.Z),tree_definition);
362 dir = transposeMatrix(rotation,dir);
366 // turtle orientation commands
368 stack_orientation.push(rotation);
369 stack_position.push(position);
372 if (stack_orientation.empty())
373 return UNBALANCED_BRACKETS;
374 rotation=stack_orientation.top();
375 stack_orientation.pop();
376 position=stack_position.top();
377 stack_position.pop();
380 temp_rotation.makeIdentity();
381 temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians+angleOffset_in_radians,v3f(0,0,1));
382 rotation*=temp_rotation;
385 temp_rotation.makeIdentity();
386 temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians+angleOffset_in_radians,v3f(0,0,-1));
387 rotation*=temp_rotation;
390 temp_rotation.makeIdentity();
391 temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians+angleOffset_in_radians,v3f(0,1,0));
392 rotation*=temp_rotation;
395 temp_rotation.makeIdentity();
396 temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians+angleOffset_in_radians,v3f(0,-1,0));
397 rotation*=temp_rotation;
400 temp_rotation.makeIdentity();
401 temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians,v3f(1,0,0));
402 rotation*=temp_rotation;
405 temp_rotation.makeIdentity();
406 temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians,v3f(-1,0,0));
407 rotation*=temp_rotation;
417 void tree_node_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
420 v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
421 if(vmanip.m_area.contains(p1) == false)
423 u32 vi = vmanip.m_area.index(p1);
424 if(vmanip.m_data[vi].getContent() != CONTENT_AIR
425 && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
427 vmanip.m_data[vmanip.m_area.index(p1)] = node;
430 void tree_trunk_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
431 TreeDef &tree_definition)
433 v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
434 if(vmanip.m_area.contains(p1) == false)
436 u32 vi = vmanip.m_area.index(p1);
437 if(vmanip.m_data[vi].getContent() != CONTENT_AIR
438 && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
440 vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.trunknode;
443 void tree_leaves_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
444 PseudoRandom ps ,TreeDef &tree_definition)
446 MapNode leavesnode=tree_definition.leavesnode;
447 if (ps.range(1,100) > 100-tree_definition.leaves2_chance)
448 leavesnode=tree_definition.leaves2node;
449 v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
450 if(vmanip.m_area.contains(p1) == false)
452 u32 vi = vmanip.m_area.index(p1);
453 if(vmanip.m_data[vi].getContent() != CONTENT_AIR
454 && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
456 if (tree_definition.fruit_chance>0)
458 if (ps.range(1,100) > 100-tree_definition.fruit_chance)
459 vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.fruitnode;
461 vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
463 else if (ps.range(1,100) > 20)
464 vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
467 void tree_single_leaves_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
468 PseudoRandom ps, TreeDef &tree_definition)
470 MapNode leavesnode=tree_definition.leavesnode;
471 if (ps.range(1,100) > 100-tree_definition.leaves2_chance)
472 leavesnode=tree_definition.leaves2node;
473 v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
474 if(vmanip.m_area.contains(p1) == false)
476 u32 vi = vmanip.m_area.index(p1);
477 if(vmanip.m_data[vi].getContent() != CONTENT_AIR
478 && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
480 vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
483 void tree_fruit_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
484 TreeDef &tree_definition)
486 v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
487 if(vmanip.m_area.contains(p1) == false)
489 u32 vi = vmanip.m_area.index(p1);
490 if(vmanip.m_data[vi].getContent() != CONTENT_AIR
491 && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
493 vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.fruitnode;
496 irr::core::matrix4 setRotationAxisRadians(irr::core::matrix4 M, double angle, v3f axis)
498 double c = cos(angle);
499 double s = sin(angle);
502 double tx = t * axis.X;
503 double ty = t * axis.Y;
504 double tz = t * axis.Z;
505 double sx = s * axis.X;
506 double sy = s * axis.Y;
507 double sz = s * axis.Z;
509 M[0] = tx * axis.X + c;
510 M[1] = tx * axis.Y + sz;
511 M[2] = tx * axis.Z - sy;
513 M[4] = ty * axis.X - sz;
514 M[5] = ty * axis.Y + c;
515 M[6] = ty * axis.Z + sx;
517 M[8] = tz * axis.X + sy;
518 M[9] = tz * axis.Y - sx;
519 M[10] = tz * axis.Z + c;
523 v3f transposeMatrix(irr::core::matrix4 M, v3f v)
526 double x = M[0] * v.X + M[4] * v.Y + M[8] * v.Z +M[12];
527 double y = M[1] * v.X + M[5] * v.Y + M[9] * v.Z +M[13];
528 double z = M[2] * v.X + M[6] * v.Y + M[10] * v.Z +M[14];
535 void make_jungletree(VoxelManipulator &vmanip, v3s16 p0,
536 INodeDefManager *ndef, int seed)
539 NOTE: Tree-placing code is currently duplicated in the engine
540 and in games that have saplings; both are deprecated but not
543 content_t c_tree = ndef->getId("mapgen_jungletree");
544 content_t c_leaves = ndef->getId("mapgen_jungleleaves");
545 if (c_tree == CONTENT_IGNORE)
546 c_tree = ndef->getId("mapgen_tree");
547 if (c_leaves == CONTENT_IGNORE)
548 c_leaves = ndef->getId("mapgen_leaves");
550 MapNode treenode(c_tree);
551 MapNode leavesnode(c_leaves);
553 PseudoRandom pr(seed);
554 for(s16 x=-1; x<=1; x++)
555 for(s16 z=-1; z<=1; z++)
557 if(pr.range(0, 2) == 0)
559 v3s16 p1 = p0 + v3s16(x,0,z);
560 v3s16 p2 = p0 + v3s16(x,-1,z);
561 u32 vi1 = vmanip.m_area.index(p1);
562 u32 vi2 = vmanip.m_area.index(p2);
564 if (vmanip.m_area.contains(p2) &&
565 vmanip.m_data[vi2].getContent() == CONTENT_AIR)
566 vmanip.m_data[vi2] = treenode;
567 else if (vmanip.m_area.contains(p1) &&
568 vmanip.m_data[vi1].getContent() == CONTENT_AIR)
569 vmanip.m_data[vi1] = treenode;
571 vmanip.m_data[vmanip.m_area.index(p0)] = treenode;
573 s16 trunk_h = pr.range(8, 12);
575 for (s16 ii=0; ii<trunk_h; ii++)
577 if (vmanip.m_area.contains(p1)) {
578 u32 vi = vmanip.m_area.index(p1);
579 if (vmanip.m_data[vi].getContent() == CONTENT_AIR)
580 vmanip.m_data[vi] = treenode;
585 // p1 is now the last piece of the trunk
588 VoxelArea leaves_a(v3s16(-3,-2,-3), v3s16(3,2,3));
589 //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
590 Buffer<u8> leaves_d(leaves_a.getVolume());
591 for(s32 i=0; i<leaves_a.getVolume(); i++)
594 // Force leaves at near the end of the trunk
597 for(s16 z=-d; z<=d; z++)
598 for(s16 y=-d; y<=d; y++)
599 for(s16 x=-d; x<=d; x++)
601 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
605 // Add leaves randomly
606 for(u32 iii=0; iii<30; iii++)
611 pr.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
612 pr.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
613 pr.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
616 for(s16 z=0; z<=d; z++)
617 for(s16 y=0; y<=d; y++)
618 for(s16 x=0; x<=d; x++)
620 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
624 // Blit leaves to vmanip
625 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
626 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
627 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
631 if(vmanip.m_area.contains(p) == false)
633 u32 vi = vmanip.m_area.index(p);
634 if (vmanip.m_data[vi].getContent() != CONTENT_AIR &&
635 vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
637 u32 i = leaves_a.index(x,y,z);
639 vmanip.m_data[vi] = leavesnode;
643 }; // namespace treegen