]> git.lizzy.rs Git - dragonfireclient.git/blob - src/treegen.cpp
49f0666bc3e9b0f80c938d2673aa7b6fc05ab8da
[dragonfireclient.git] / src / treegen.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>,
4                                    2012 RealBadAngel, Maciej Kasatkin <mk@realbadangel.pl>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "irr_v3d.h"
21 #include <stack>
22 #include "util/numeric.h"
23 #include "util/mathconstants.h"
24 #include "noise.h"
25 #include "map.h"
26 #include "environment.h"
27 #include "nodedef.h"
28 #include "treegen.h"
29
30 namespace treegen
31 {
32
33 void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
34                 bool is_apple_tree, INodeDefManager *ndef)
35 {
36         MapNode treenode(ndef->getId("mapgen_tree"));
37         MapNode leavesnode(ndef->getId("mapgen_leaves"));
38         MapNode applenode(ndef->getId("mapgen_apple"));
39
40         s16 trunk_h = myrand_range(4, 5);
41         v3s16 p1 = p0;
42         for(s16 ii=0; ii<trunk_h; ii++)
43         {
44                 if(vmanip.m_area.contains(p1))
45                         if(ii == 0 || vmanip.getNodeNoExNoEmerge(p1).getContent() == CONTENT_AIR)
46                                 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
47                 p1.Y++;
48         }
49
50         // p1 is now the last piece of the trunk
51         p1.Y -= 1;
52
53         VoxelArea leaves_a(v3s16(-2,-1,-2), v3s16(2,2,2));
54         //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
55         Buffer<u8> leaves_d(leaves_a.getVolume());
56         for(s32 i=0; i<leaves_a.getVolume(); i++)
57                 leaves_d[i] = 0;
58
59         // Force leaves at near the end of the trunk
60         {
61                 s16 d = 1;
62                 for(s16 z=-d; z<=d; z++)
63                 for(s16 y=-d; y<=d; y++)
64                 for(s16 x=-d; x<=d; x++)
65                 {
66                         leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
67                 }
68         }
69
70         // Add leaves randomly
71         for(u32 iii=0; iii<7; iii++)
72         {
73                 s16 d = 1;
74
75                 v3s16 p(
76                         myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
77                         myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
78                         myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
79                 );
80
81                 for(s16 z=0; z<=d; z++)
82                 for(s16 y=0; y<=d; y++)
83                 for(s16 x=0; x<=d; x++)
84                 {
85                         leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
86                 }
87         }
88
89         // Blit leaves to vmanip
90         for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
91         for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
92         for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
93         {
94                 v3s16 p(x,y,z);
95                 p += p1;
96                 if(vmanip.m_area.contains(p) == false)
97                         continue;
98                 u32 vi = vmanip.m_area.index(p);
99                 if(vmanip.m_data[vi].getContent() != CONTENT_AIR
100                                 && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
101                         continue;
102                 u32 i = leaves_a.index(x,y,z);
103                 if(leaves_d[i] == 1) {
104                         bool is_apple = myrand_range(0,99) < 10;
105                         if(is_apple_tree && is_apple) {
106                                 vmanip.m_data[vi] = applenode;
107                         } else {
108                                 vmanip.m_data[vi] = leavesnode;
109                         }
110                 }
111         }
112 }
113
114 // L-System tree LUA spawner
115 void spawn_ltree (ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef, TreeDef tree_definition)
116 {
117         ServerMap *map = &env->getServerMap();
118         core::map<v3s16, MapBlock*> modified_blocks;
119         ManualMapVoxelManipulator vmanip(map);
120         v3s16 tree_blockp = getNodeBlockPos(p0);
121         vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,2,1));
122         make_ltree (vmanip, p0, ndef, tree_definition);
123         vmanip.blitBackAll(&modified_blocks);
124
125         // update lighting
126         core::map<v3s16, MapBlock*> lighting_modified_blocks;
127         for(core::map<v3s16, MapBlock*>::Iterator
128                 i = modified_blocks.getIterator();
129                 i.atEnd() == false; i++)
130         {
131                 lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
132         }
133         map->updateLighting(lighting_modified_blocks, modified_blocks);
134         // Send a MEET_OTHER event
135         MapEditEvent event;
136         event.type = MEET_OTHER;
137         for(core::map<v3s16, MapBlock*>::Iterator
138                 i = modified_blocks.getIterator();
139                 i.atEnd() == false; i++)
140         {
141                 v3s16 p = i.getNode()->getKey();
142                 event.modified_blocks.insert(p, true);
143         }
144         map->dispatchEvent(&event);
145 }
146
147 //L-System tree generator
148 void make_ltree(ManualMapVoxelManipulator &vmanip, v3s16 p0, INodeDefManager *ndef,
149                 TreeDef tree_definition)
150 {
151         MapNode dirtnode(ndef->getId("mapgen_dirt"));
152
153         // chance of inserting abcd rules
154         double prop_a = 9;
155         double prop_b = 8;
156         double prop_c = 7;
157         double prop_d = 6;
158
159         //randomize tree growth level, minimum=2
160         s16 iterations = tree_definition.iterations;
161         if (tree_definition.iterations_random_level>0)
162                 iterations -= myrand_range(0,tree_definition.iterations_random_level);
163         if (iterations<2)
164                 iterations=2;
165
166         s16 MAX_ANGLE_OFFSET = 5;
167         double angle_in_radians = (double)tree_definition.angle*M_PI/180;
168         double angleOffset_in_radians = (s16)(myrand_range(0,1)%MAX_ANGLE_OFFSET)*M_PI/180;
169
170         //initialize rotation matrix, position and stacks for branches
171         core::matrix4 rotation;
172         rotation=setRotationAxisRadians(rotation, M_PI/2,v3f(0,0,1));
173         v3f position = v3f(0,0,0);
174         std::stack <core::matrix4> stack_orientation;
175         std::stack <v3f> stack_position;
176
177         //generate axiom
178         std::string axiom = tree_definition.initial_axiom;
179         for(s16 i=0; i<iterations; i++)
180         {
181                 std::string temp = "";
182                 for(s16 j=0; j<(s16)axiom.size(); j++)
183                 {
184                         char axiom_char = axiom.at(j);
185                         switch (axiom_char)
186                         {
187                         case 'A':
188                                 temp+=tree_definition.rules_a;
189                                 break;
190                         case 'B':
191                                 temp+=tree_definition.rules_b;
192                                 break;
193                         case 'C':
194                                 temp+=tree_definition.rules_c;
195                                 break;
196                         case 'D':
197                                 temp+=tree_definition.rules_d;
198                                 break;
199                         case 'a':
200                                 if (prop_a >= myrand_range(1,10))
201                                         temp+=tree_definition.rules_a;
202                                 break;
203                         case 'b':
204                                 if (prop_b >= myrand_range(1,10))
205                                         temp+=tree_definition.rules_b;
206                                 break;
207                         case 'c':
208                                 if (prop_c >= myrand_range(1,10))
209                                         temp+=tree_definition.rules_c;
210                                 break;
211                         case 'd':
212                                 if (prop_d >= myrand_range(1,10))
213                                         temp+=tree_definition.rules_d;
214                                 break;
215                         default:
216                                 temp+=axiom_char;
217                                 break;
218                         }
219                 }
220                 axiom=temp;
221         }
222
223         //make sure tree is not floating in the air
224         if (tree_definition.trunk_type == "double")
225         {
226                 make_tree_node_placement(vmanip,v3f(p0.X+position.X+1,p0.Y+position.Y-1,p0.Z+position.Z),dirtnode);
227                 make_tree_node_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y-1,p0.Z+position.Z+1),dirtnode);
228                 make_tree_node_placement(vmanip,v3f(p0.X+position.X+1,p0.Y+position.Y-1,p0.Z+position.Z+1),dirtnode);
229         }
230         if (tree_definition.trunk_type == "crossed")
231         {
232                 make_tree_node_placement(vmanip,v3f(p0.X+position.X+1,p0.Y+position.Y-1,p0.Z+position.Z),dirtnode);
233                 make_tree_node_placement(vmanip,v3f(p0.X+position.X-1,p0.Y+position.Y-1,p0.Z+position.Z),dirtnode);
234                 make_tree_node_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y-1,p0.Z+position.Z+1),dirtnode);
235                 make_tree_node_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y-1,p0.Z+position.Z-1),dirtnode);
236         }
237
238         /* build tree out of generated axiom
239
240         Key for Special L-System Symbols used in Axioms
241
242     G  - move forward one unit with the pen up
243     F  - move forward one unit with the pen down drawing trunks and branches
244     f  - move forward one unit with the pen down drawing leaves
245     A  - replace with rules set A
246     B  - replace with rules set B
247     C  - replace with rules set C
248     D  - replace with rules set D
249     a  - replace with rules set A, chance 90%
250     b  - replace with rules set B, chance 80%
251     c  - replace with rules set C, chance 70%
252     d  - replace with rules set D, chance 60%
253     +  - yaw the turtle right by angle degrees
254     -  - yaw the turtle left by angle degrees
255     &  - pitch the turtle down by angle degrees
256     ^  - pitch the turtle up by angle degrees
257     /  - roll the turtle to the right by angle degrees
258     *  - roll the turtle to the left by angle degrees
259     [  - save in stack current state info
260     ]  - recover from stack state info
261
262     */
263
264         s16 x,y,z;
265         for(s16 i=0; i<(s16)axiom.size(); i++)
266         {
267                 char axiom_char = axiom.at(i);
268                 core::matrix4 temp_rotation;
269                 temp_rotation.makeIdentity();
270                 v3f dir;
271                 switch (axiom_char)
272                 {
273                 case 'G':
274                         dir = v3f(1,0,0);
275                         dir = transposeMatrix(rotation,dir);
276                         position+=dir;
277                         break;
278                 case 'F':
279                         make_tree_trunk_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y,p0.Z+position.Z),tree_definition);
280                         if ((stack_orientation.empty() && tree_definition.trunk_type == "double") ||
281                                 (!stack_orientation.empty() && tree_definition.trunk_type == "double" && !tree_definition.thin_branches))
282                         {
283                                 make_tree_trunk_placement(vmanip,v3f(p0.X+position.X+1,p0.Y+position.Y,p0.Z+position.Z),tree_definition);
284                                 make_tree_trunk_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y,p0.Z+position.Z+1),tree_definition);
285                                 make_tree_trunk_placement(vmanip,v3f(p0.X+position.X+1,p0.Y+position.Y,p0.Z+position.Z+1),tree_definition);
286                         }
287                         if ((stack_orientation.empty() && tree_definition.trunk_type == "crossed") ||
288                                 (!stack_orientation.empty() && tree_definition.trunk_type == "crossed" && !tree_definition.thin_branches))
289                         {
290                                 make_tree_trunk_placement(vmanip,v3f(p0.X+position.X+1,p0.Y+position.Y,p0.Z+position.Z),tree_definition);
291                                 make_tree_trunk_placement(vmanip,v3f(p0.X+position.X-1,p0.Y+position.Y,p0.Z+position.Z),tree_definition);
292                                 make_tree_trunk_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y,p0.Z+position.Z+1),tree_definition);
293                                 make_tree_trunk_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y,p0.Z+position.Z-1),tree_definition);
294                         }
295                         if (stack_orientation.empty() == false)
296                         {
297                                 s16 size = 1;
298                                 for(x=-size; x<size+1; x++)
299                                         for(y=-size; y<size+1; y++)
300                                                 for(z=-size; z<size+1; z++)
301                                                         if (abs(x) == size && abs(y) == size && abs(z) == size)
302                                                         {
303                                                                 make_tree_leaves_placement(vmanip,v3f(p0.X+position.X+x+1,p0.Y+position.Y+y,p0.Z+position.Z+z),tree_definition);
304                                                                 make_tree_leaves_placement(vmanip,v3f(p0.X+position.X+x-1,p0.Y+position.Y+y,p0.Z+position.Z+z),tree_definition);
305                                                                 make_tree_leaves_placement(vmanip,v3f(p0.X+position.X+x,p0.Y+position.Y+y,p0.Z+position.Z+z+1),tree_definition);
306                                                                 make_tree_leaves_placement(vmanip,v3f(p0.X+position.X+x,p0.Y+position.Y+y,p0.Z+position.Z+z-1),tree_definition);
307                                                         }
308                         }
309                         dir = v3f(1,0,0);
310                         dir = transposeMatrix(rotation,dir);
311                         position+=dir;
312                         break;
313                 case 'f':
314                         make_tree_leaves_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y,p0.Z+position.Z),tree_definition);
315                         dir = v3f(1,0,0);
316                         dir = transposeMatrix(rotation,dir);
317                         position+=dir;
318                         break;
319                 // turtle commands
320                 case '[':
321                         stack_orientation.push(rotation);
322                         stack_position.push(position);
323                         break;
324                 case ']':
325                         rotation=stack_orientation.top();
326                         stack_orientation.pop();
327                         position=stack_position.top();
328                         stack_position.pop();
329                         break;
330                 case '+':
331                         temp_rotation.makeIdentity();
332                         temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians+angleOffset_in_radians,v3f(0,0,1));
333                         rotation*=temp_rotation;
334                         break;
335                 case '-':
336                         temp_rotation.makeIdentity();
337                         temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians+angleOffset_in_radians,v3f(0,0,-1));
338                         rotation*=temp_rotation;
339                         break;
340                 case '&':
341                         temp_rotation.makeIdentity();
342                         temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians+angleOffset_in_radians,v3f(0,1,0));
343                         rotation*=temp_rotation;
344                         break;
345                 case '^':
346                         temp_rotation.makeIdentity();
347                         temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians+angleOffset_in_radians,v3f(0,-1,0));
348                         rotation*=temp_rotation;
349                         break;
350                 case '*':
351                         temp_rotation.makeIdentity();
352                         temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians,v3f(1,0,0));
353                         rotation*=temp_rotation;
354                         break;
355                 case '/':
356                         temp_rotation.makeIdentity();
357                         temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians,v3f(-1,0,0));
358                         rotation*=temp_rotation;
359                         break;
360                 default:
361                         break;
362                 }
363         }
364 }
365
366 void make_tree_node_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
367                 MapNode node)
368 {
369         v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
370         if(vmanip.m_area.contains(p1) == false)
371                 return;
372         u32 vi = vmanip.m_area.index(p1);
373         if(vmanip.m_data[vi].getContent() != CONTENT_AIR
374                         && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
375                 return;
376         vmanip.m_data[vmanip.m_area.index(p1)] = node;
377 }
378
379 void make_tree_trunk_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
380                 TreeDef &tree_definition)
381 {
382         v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
383         if(vmanip.m_area.contains(p1) == false)
384                 return;
385         u32 vi = vmanip.m_area.index(p1);
386         if(vmanip.m_data[vi].getContent() != CONTENT_AIR
387                         && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
388                 return;
389         vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.trunknode;
390 }
391
392 void make_tree_leaves_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
393                 TreeDef &tree_definition)
394 {
395         MapNode leavesnode=tree_definition.leavesnode;
396         if (myrand_range(1,100) > 100-tree_definition.leaves2_chance)
397                 leavesnode=tree_definition.leaves2node;
398         v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
399         if(vmanip.m_area.contains(p1) == false)
400                 return;
401         u32 vi = vmanip.m_area.index(p1);
402         if(vmanip.m_data[vi].getContent() != CONTENT_AIR
403                         && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
404                 return;
405         if (tree_definition.fruit_chance>0)
406         {
407                 if (myrand_range(1,100) > 100-tree_definition.fruit_chance)
408                         vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.fruitnode;
409                 else
410                         vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
411         }
412         else if (myrand_range(1,100) > 20)
413                 vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
414 }
415
416 irr::core::matrix4 setRotationAxisRadians(irr::core::matrix4 M, double angle, v3f axis)
417 {
418         double c = cos(angle);
419         double s = sin(angle);
420         double t = 1.0 - c;
421
422         double tx  = t * axis.X;
423         double ty  = t * axis.Y;
424         double tz  = t * axis.Z;
425         double sx  = s * axis.X;
426         double sy  = s * axis.Y;
427         double sz  = s * axis.Z;
428
429         M[0] = tx * axis.X + c;
430         M[1] = tx * axis.Y + sz;
431         M[2] = tx * axis.Z - sy;
432
433         M[4] = ty * axis.X - sz;
434         M[5] = ty * axis.Y + c;
435         M[6] = ty * axis.Z + sx;
436
437         M[8]  = tz * axis.X + sy;
438         M[9]  = tz * axis.Y - sx;
439         M[10] = tz * axis.Z + c;
440         return M;
441 }
442
443 v3f transposeMatrix(irr::core::matrix4 M, v3f v)
444 {
445         v3f translated;
446         double x = M[0] * v.X + M[4] * v.Y + M[8]  * v.Z +M[12];
447         double y = M[1] * v.X + M[5] * v.Y + M[9]  * v.Z +M[13];
448         double z = M[2] * v.X + M[6] * v.Y + M[10] * v.Z +M[14];
449         translated.X=x;
450         translated.Y=y;
451         translated.Z=z;
452         return translated;
453 }
454
455 #if 0
456 static void make_jungletree(VoxelManipulator &vmanip, v3s16 p0,
457                 INodeDefManager *ndef)
458 {
459         MapNode treenode(ndef->getId("mapgen_jungletree"));
460         MapNode leavesnode(ndef->getId("mapgen_leaves"));
461
462         for(s16 x=-1; x<=1; x++)
463         for(s16 z=-1; z<=1; z++)
464         {
465                 if(myrand_range(0, 2) == 0)
466                         continue;
467                 v3s16 p1 = p0 + v3s16(x,0,z);
468                 v3s16 p2 = p0 + v3s16(x,-1,z);
469                 if(vmanip.m_area.contains(p2)
470                                 && vmanip.m_data[vmanip.m_area.index(p2)] == CONTENT_AIR)
471                         vmanip.m_data[vmanip.m_area.index(p2)] = treenode;
472                 else if(vmanip.m_area.contains(p1))
473                         vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
474         }
475
476         s16 trunk_h = myrand_range(8, 12);
477         v3s16 p1 = p0;
478         for(s16 ii=0; ii<trunk_h; ii++)
479         {
480                 if(vmanip.m_area.contains(p1))
481                         vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
482                 p1.Y++;
483         }
484
485         // p1 is now the last piece of the trunk
486         p1.Y -= 1;
487
488         VoxelArea leaves_a(v3s16(-3,-2,-3), v3s16(3,2,3));
489         //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
490         Buffer<u8> leaves_d(leaves_a.getVolume());
491         for(s32 i=0; i<leaves_a.getVolume(); i++)
492                 leaves_d[i] = 0;
493
494         // Force leaves at near the end of the trunk
495         {
496                 s16 d = 1;
497                 for(s16 z=-d; z<=d; z++)
498                 for(s16 y=-d; y<=d; y++)
499                 for(s16 x=-d; x<=d; x++)
500                 {
501                         leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
502                 }
503         }
504
505         // Add leaves randomly
506         for(u32 iii=0; iii<30; iii++)
507         {
508                 s16 d = 1;
509
510                 v3s16 p(
511                         myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
512                         myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
513                         myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
514                 );
515
516                 for(s16 z=0; z<=d; z++)
517                 for(s16 y=0; y<=d; y++)
518                 for(s16 x=0; x<=d; x++)
519                 {
520                         leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
521                 }
522         }
523
524         // Blit leaves to vmanip
525         for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
526         for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
527         for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
528         {
529                 v3s16 p(x,y,z);
530                 p += p1;
531                 if(vmanip.m_area.contains(p) == false)
532                         continue;
533                 u32 vi = vmanip.m_area.index(p);
534                 if(vmanip.m_data[vi].getContent() != CONTENT_AIR
535                                 && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
536                         continue;
537                 u32 i = leaves_a.index(x,y,z);
538                 if(leaves_d[i] == 1)
539                         vmanip.m_data[vi] = leavesnode;
540         }
541 }
542 #endif
543
544 }; // namespace treegen