treegen.cpp 19 KB

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