treegen.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875
  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
  6. This program is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU Lesser General Public License as published by
  8. the Free Software Foundation; either version 2.1 of the License, or
  9. (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.
  14. You should have received a copy of the GNU Lesser General Public License along
  15. with this program; if not, write to the Free Software Foundation, Inc.,
  16. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  17. */
  18. #include "irr_v3d.h"
  19. #include <stack>
  20. #include "util/pointer.h"
  21. #include "util/numeric.h"
  22. #include "map.h"
  23. #include "mapblock.h"
  24. #include "nodedef.h"
  25. #include "treegen.h"
  26. #include "voxelalgorithms.h"
  27. namespace treegen
  28. {
  29. void make_tree(MMVManip &vmanip, v3s16 p0, bool is_apple_tree,
  30. const NodeDefManager *ndef, s32 seed)
  31. {
  32. /*
  33. NOTE: Tree-placing code is currently duplicated in the engine
  34. and in games that have saplings; both are deprecated but not
  35. replaced yet
  36. */
  37. MapNode treenode(ndef->getId("mapgen_tree"));
  38. MapNode leavesnode(ndef->getId("mapgen_leaves"));
  39. MapNode applenode(ndef->getId("mapgen_apple"));
  40. if (treenode == CONTENT_IGNORE)
  41. errorstream << "Treegen: Mapgen alias 'mapgen_tree' is invalid!" << std::endl;
  42. if (leavesnode == CONTENT_IGNORE)
  43. errorstream << "Treegen: Mapgen alias 'mapgen_leaves' is invalid!" << std::endl;
  44. if (applenode == CONTENT_IGNORE)
  45. errorstream << "Treegen: Mapgen alias 'mapgen_apple' is invalid!" << std::endl;
  46. PseudoRandom pr(seed);
  47. s16 trunk_h = pr.range(4, 5);
  48. v3s16 p1 = p0;
  49. for (s16 ii = 0; ii < trunk_h; ii++) {
  50. if (vmanip.m_area.contains(p1)) {
  51. u32 vi = vmanip.m_area.index(p1);
  52. vmanip.m_data[vi] = treenode;
  53. }
  54. p1.Y++;
  55. }
  56. // p1 is now the last piece of the trunk
  57. p1.Y -= 1;
  58. VoxelArea leaves_a(v3s16(-2, -1, -2), v3s16(2, 2, 2));
  59. Buffer<u8> leaves_d(leaves_a.getVolume());
  60. for (s32 i = 0; i < leaves_a.getVolume(); i++)
  61. leaves_d[i] = 0;
  62. // Force leaves at near the end of the trunk
  63. s16 d = 1;
  64. for (s16 z = -d; z <= d; z++)
  65. for (s16 y = -d; y <= d; y++)
  66. for (s16 x = -d; x <= d; x++) {
  67. leaves_d[leaves_a.index(v3s16(x, y, z))] = 1;
  68. }
  69. // Add leaves randomly
  70. for (u32 iii = 0; iii < 7; iii++) {
  71. v3s16 p(
  72. pr.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X - d),
  73. pr.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y - d),
  74. pr.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z - d)
  75. );
  76. for (s16 z = 0; z <= d; z++)
  77. for (s16 y = 0; y <= d; y++)
  78. for (s16 x = 0; x <= d; x++) {
  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. v3s16 pmin(leaves_a.MinEdge.X, y, z);
  86. u32 i = leaves_a.index(pmin);
  87. u32 vi = vmanip.m_area.index(pmin + p1);
  88. for (s16 x = leaves_a.MinEdge.X; x <= leaves_a.MaxEdge.X; x++) {
  89. v3s16 p(x, y, z);
  90. if (vmanip.m_area.contains(p + p1) &&
  91. (vmanip.m_data[vi].getContent() == CONTENT_AIR ||
  92. vmanip.m_data[vi].getContent() == CONTENT_IGNORE)) {
  93. if (leaves_d[i] == 1) {
  94. bool is_apple = pr.range(0, 99) < 10;
  95. if (is_apple_tree && is_apple)
  96. vmanip.m_data[vi] = applenode;
  97. else
  98. vmanip.m_data[vi] = leavesnode;
  99. }
  100. }
  101. vi++;
  102. i++;
  103. }
  104. }
  105. }
  106. // L-System tree LUA spawner
  107. treegen::error spawn_ltree(ServerMap *map, v3s16 p0,
  108. const NodeDefManager *ndef, const TreeDef &tree_definition)
  109. {
  110. std::map<v3s16, MapBlock*> modified_blocks;
  111. MMVManip 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. voxalgo::blit_back_with_light(map, &vmanip, &modified_blocks);
  119. // Send a MEET_OTHER event
  120. MapEditEvent event;
  121. event.type = MEET_OTHER;
  122. for (auto &modified_block : modified_blocks)
  123. event.modified_blocks.insert(modified_block.first);
  124. map->dispatchEvent(event);
  125. return SUCCESS;
  126. }
  127. //L-System tree generator
  128. treegen::error make_ltree(MMVManip &vmanip, v3s16 p0,
  129. const NodeDefManager *ndef, TreeDef tree_definition)
  130. {
  131. s32 seed;
  132. if (tree_definition.explicit_seed)
  133. seed = tree_definition.seed + 14002;
  134. else
  135. seed = p0.X * 2 + p0.Y * 4 + p0.Z; // use the tree position to seed PRNG
  136. PseudoRandom ps(seed);
  137. // chance of inserting abcd rules
  138. double prop_a = 9;
  139. double prop_b = 8;
  140. double prop_c = 7;
  141. double prop_d = 6;
  142. //randomize tree growth level, minimum=2
  143. s16 iterations = tree_definition.iterations;
  144. if (tree_definition.iterations_random_level > 0)
  145. iterations -= ps.range(0, tree_definition.iterations_random_level);
  146. if (iterations < 2)
  147. iterations = 2;
  148. s16 MAX_ANGLE_OFFSET = 5;
  149. double angle_in_radians = (double)tree_definition.angle * M_PI / 180;
  150. double angleOffset_in_radians = (s16)(ps.range(0, 1) % MAX_ANGLE_OFFSET) * M_PI / 180;
  151. //initialize rotation matrix, position and stacks for branches
  152. core::matrix4 rotation;
  153. rotation = setRotationAxisRadians(rotation, M_PI / 2, v3f(0, 0, 1));
  154. v3f position;
  155. position.X = p0.X;
  156. position.Y = p0.Y;
  157. position.Z = p0.Z;
  158. std::stack <core::matrix4> stack_orientation;
  159. std::stack <v3f> stack_position;
  160. //generate axiom
  161. std::string axiom = tree_definition.initial_axiom;
  162. for (s16 i = 0; i < iterations; i++) {
  163. std::string temp;
  164. for (s16 j = 0; j < (s16)axiom.size(); j++) {
  165. char axiom_char = axiom.at(j);
  166. switch (axiom_char) {
  167. case 'A':
  168. temp += tree_definition.rules_a;
  169. break;
  170. case 'B':
  171. temp += tree_definition.rules_b;
  172. break;
  173. case 'C':
  174. temp += tree_definition.rules_c;
  175. break;
  176. case 'D':
  177. temp += tree_definition.rules_d;
  178. break;
  179. case 'a':
  180. if (prop_a >= ps.range(1, 10))
  181. temp += tree_definition.rules_a;
  182. break;
  183. case 'b':
  184. if (prop_b >= ps.range(1, 10))
  185. temp += tree_definition.rules_b;
  186. break;
  187. case 'c':
  188. if (prop_c >= ps.range(1, 10))
  189. temp += tree_definition.rules_c;
  190. break;
  191. case 'd':
  192. if (prop_d >= ps.range(1, 10))
  193. temp += tree_definition.rules_d;
  194. break;
  195. default:
  196. temp += axiom_char;
  197. break;
  198. }
  199. }
  200. axiom = temp;
  201. }
  202. // Add trunk nodes below a wide trunk to avoid gaps when tree is on sloping ground
  203. if (tree_definition.trunk_type == "double") {
  204. tree_trunk_placement(
  205. vmanip,
  206. v3f(position.X + 1, position.Y - 1, position.Z),
  207. tree_definition
  208. );
  209. tree_trunk_placement(
  210. vmanip,
  211. v3f(position.X, position.Y - 1, position.Z + 1),
  212. tree_definition
  213. );
  214. tree_trunk_placement(
  215. vmanip,
  216. v3f(position.X + 1, position.Y - 1, position.Z + 1),
  217. tree_definition
  218. );
  219. } else if (tree_definition.trunk_type == "crossed") {
  220. tree_trunk_placement(
  221. vmanip,
  222. v3f(position.X + 1, position.Y - 1, position.Z),
  223. tree_definition
  224. );
  225. tree_trunk_placement(
  226. vmanip,
  227. v3f(position.X - 1, position.Y - 1, position.Z),
  228. tree_definition
  229. );
  230. tree_trunk_placement(
  231. vmanip,
  232. v3f(position.X, position.Y - 1, position.Z + 1),
  233. tree_definition
  234. );
  235. tree_trunk_placement(
  236. vmanip,
  237. v3f(position.X, position.Y - 1, position.Z - 1),
  238. tree_definition
  239. );
  240. }
  241. /* build tree out of generated axiom
  242. Key for Special L-System Symbols used in Axioms
  243. G - move forward one unit with the pen up
  244. F - move forward one unit with the pen down drawing trunks and branches
  245. f - move forward one unit with the pen down drawing leaves (100% chance)
  246. T - move forward one unit with the pen down drawing trunks only
  247. R - move forward one unit with the pen down placing fruit
  248. A - replace with rules set A
  249. B - replace with rules set B
  250. C - replace with rules set C
  251. D - replace with rules set D
  252. a - replace with rules set A, chance 90%
  253. b - replace with rules set B, chance 80%
  254. c - replace with rules set C, chance 70%
  255. d - replace with rules set D, chance 60%
  256. + - yaw the turtle right by angle degrees
  257. - - yaw the turtle left by angle degrees
  258. & - pitch the turtle down by angle degrees
  259. ^ - pitch the turtle up by angle degrees
  260. / - roll the turtle to the right by angle degrees
  261. * - roll the turtle to the left by angle degrees
  262. [ - save in stack current state info
  263. ] - recover from stack state info
  264. */
  265. s16 x,y,z;
  266. for (s16 i = 0; i < (s16)axiom.size(); i++) {
  267. char axiom_char = axiom.at(i);
  268. core::matrix4 temp_rotation;
  269. temp_rotation.makeIdentity();
  270. v3f dir;
  271. switch (axiom_char) {
  272. case 'G':
  273. dir = v3f(1, 0, 0);
  274. dir = transposeMatrix(rotation, dir);
  275. position += dir;
  276. break;
  277. case 'T':
  278. tree_trunk_placement(
  279. vmanip,
  280. v3f(position.X, position.Y, position.Z),
  281. tree_definition
  282. );
  283. if (tree_definition.trunk_type == "double" &&
  284. !tree_definition.thin_branches) {
  285. tree_trunk_placement(
  286. vmanip,
  287. v3f(position.X + 1, position.Y, position.Z),
  288. tree_definition
  289. );
  290. tree_trunk_placement(
  291. vmanip,
  292. v3f(position.X, position.Y, position.Z + 1),
  293. tree_definition
  294. );
  295. tree_trunk_placement(
  296. vmanip,
  297. v3f(position.X + 1, position.Y, position.Z + 1),
  298. tree_definition
  299. );
  300. } else if (tree_definition.trunk_type == "crossed" &&
  301. !tree_definition.thin_branches) {
  302. tree_trunk_placement(
  303. vmanip,
  304. v3f(position.X + 1, position.Y, position.Z),
  305. tree_definition
  306. );
  307. tree_trunk_placement(
  308. vmanip,
  309. v3f(position.X - 1, position.Y, position.Z),
  310. tree_definition
  311. );
  312. tree_trunk_placement(
  313. vmanip,
  314. v3f(position.X, position.Y, position.Z + 1),
  315. tree_definition
  316. );
  317. tree_trunk_placement(
  318. vmanip,
  319. v3f(position.X, position.Y, position.Z - 1),
  320. tree_definition
  321. );
  322. }
  323. dir = v3f(1, 0, 0);
  324. dir = transposeMatrix(rotation, dir);
  325. position += dir;
  326. break;
  327. case 'F':
  328. tree_trunk_placement(
  329. vmanip,
  330. v3f(position.X, position.Y, position.Z),
  331. tree_definition
  332. );
  333. if ((stack_orientation.empty() &&
  334. tree_definition.trunk_type == "double") ||
  335. (!stack_orientation.empty() &&
  336. tree_definition.trunk_type == "double" &&
  337. !tree_definition.thin_branches)) {
  338. tree_trunk_placement(
  339. vmanip,
  340. v3f(position.X + 1, position.Y, position.Z),
  341. tree_definition
  342. );
  343. tree_trunk_placement(
  344. vmanip,
  345. v3f(position.X, position.Y, position.Z + 1),
  346. tree_definition
  347. );
  348. tree_trunk_placement(
  349. vmanip,
  350. v3f(position.X + 1, position.Y, position.Z + 1),
  351. tree_definition
  352. );
  353. } else if ((stack_orientation.empty() &&
  354. tree_definition.trunk_type == "crossed") ||
  355. (!stack_orientation.empty() &&
  356. tree_definition.trunk_type == "crossed" &&
  357. !tree_definition.thin_branches)) {
  358. tree_trunk_placement(
  359. vmanip,
  360. v3f(position.X + 1, position.Y, position.Z),
  361. tree_definition
  362. );
  363. tree_trunk_placement(
  364. vmanip,
  365. v3f(position.X - 1, position.Y, position.Z),
  366. tree_definition
  367. );
  368. tree_trunk_placement(
  369. vmanip,
  370. v3f(position.X, position.Y, position.Z + 1),
  371. tree_definition
  372. );
  373. tree_trunk_placement(
  374. vmanip,
  375. v3f(position.X, position.Y, position.Z - 1),
  376. tree_definition
  377. );
  378. }
  379. if (!stack_orientation.empty()) {
  380. s16 size = 1;
  381. for (x = -size; x <= size; x++)
  382. for (y = -size; y <= size; y++)
  383. for (z = -size; z <= size; z++) {
  384. if (abs(x) == size &&
  385. abs(y) == size &&
  386. abs(z) == size) {
  387. tree_leaves_placement(
  388. vmanip,
  389. v3f(position.X + x + 1, position.Y + y,
  390. position.Z + z),
  391. ps.next(),
  392. tree_definition
  393. );
  394. tree_leaves_placement(
  395. vmanip,
  396. v3f(position.X + x - 1, position.Y + y,
  397. position.Z + z),
  398. ps.next(),
  399. tree_definition
  400. );
  401. tree_leaves_placement(
  402. vmanip,v3f(position.X + x, position.Y + y,
  403. position.Z + z + 1),
  404. ps.next(),
  405. tree_definition
  406. );
  407. tree_leaves_placement(
  408. vmanip,v3f(position.X + x, position.Y + y,
  409. position.Z + z - 1),
  410. ps.next(),
  411. tree_definition
  412. );
  413. }
  414. }
  415. }
  416. dir = v3f(1, 0, 0);
  417. dir = transposeMatrix(rotation, dir);
  418. position += dir;
  419. break;
  420. case 'f':
  421. tree_single_leaves_placement(
  422. vmanip,
  423. v3f(position.X, position.Y, position.Z),
  424. ps.next(),
  425. tree_definition
  426. );
  427. dir = v3f(1, 0, 0);
  428. dir = transposeMatrix(rotation, dir);
  429. position += dir;
  430. break;
  431. case 'R':
  432. tree_fruit_placement(
  433. vmanip,
  434. v3f(position.X, position.Y, position.Z),
  435. tree_definition
  436. );
  437. dir = v3f(1, 0, 0);
  438. dir = transposeMatrix(rotation, dir);
  439. position += dir;
  440. break;
  441. // turtle orientation commands
  442. case '[':
  443. stack_orientation.push(rotation);
  444. stack_position.push(position);
  445. break;
  446. case ']':
  447. if (stack_orientation.empty())
  448. return UNBALANCED_BRACKETS;
  449. rotation = stack_orientation.top();
  450. stack_orientation.pop();
  451. position = stack_position.top();
  452. stack_position.pop();
  453. break;
  454. case '+':
  455. temp_rotation.makeIdentity();
  456. temp_rotation = setRotationAxisRadians(temp_rotation,
  457. angle_in_radians + angleOffset_in_radians, v3f(0, 0, 1));
  458. rotation *= temp_rotation;
  459. break;
  460. case '-':
  461. temp_rotation.makeIdentity();
  462. temp_rotation = setRotationAxisRadians(temp_rotation,
  463. angle_in_radians + angleOffset_in_radians, v3f(0, 0, -1));
  464. rotation *= temp_rotation;
  465. break;
  466. case '&':
  467. temp_rotation.makeIdentity();
  468. temp_rotation = setRotationAxisRadians(temp_rotation,
  469. angle_in_radians + angleOffset_in_radians, v3f(0, 1, 0));
  470. rotation *= temp_rotation;
  471. break;
  472. case '^':
  473. temp_rotation.makeIdentity();
  474. temp_rotation = setRotationAxisRadians(temp_rotation,
  475. angle_in_radians + angleOffset_in_radians, v3f(0, -1, 0));
  476. rotation *= temp_rotation;
  477. break;
  478. case '*':
  479. temp_rotation.makeIdentity();
  480. temp_rotation = setRotationAxisRadians(temp_rotation,
  481. angle_in_radians, v3f(1, 0, 0));
  482. rotation *= temp_rotation;
  483. break;
  484. case '/':
  485. temp_rotation.makeIdentity();
  486. temp_rotation = setRotationAxisRadians(temp_rotation,
  487. angle_in_radians, v3f(-1, 0, 0));
  488. rotation *= temp_rotation;
  489. break;
  490. default:
  491. break;
  492. }
  493. }
  494. return SUCCESS;
  495. }
  496. void tree_trunk_placement(MMVManip &vmanip, v3f p0, TreeDef &tree_definition)
  497. {
  498. v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
  499. if (!vmanip.m_area.contains(p1))
  500. return;
  501. u32 vi = vmanip.m_area.index(p1);
  502. content_t current_node = vmanip.m_data[vi].getContent();
  503. if (current_node != CONTENT_AIR && current_node != CONTENT_IGNORE
  504. && current_node != tree_definition.leavesnode.getContent()
  505. && current_node != tree_definition.leaves2node.getContent()
  506. && current_node != tree_definition.fruitnode.getContent())
  507. return;
  508. vmanip.m_data[vi] = tree_definition.trunknode;
  509. }
  510. void tree_leaves_placement(MMVManip &vmanip, v3f p0,
  511. PseudoRandom ps, TreeDef &tree_definition)
  512. {
  513. MapNode leavesnode = tree_definition.leavesnode;
  514. if (ps.range(1, 100) > 100 - tree_definition.leaves2_chance)
  515. leavesnode = tree_definition.leaves2node;
  516. v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
  517. if (!vmanip.m_area.contains(p1))
  518. return;
  519. u32 vi = vmanip.m_area.index(p1);
  520. if (vmanip.m_data[vi].getContent() != CONTENT_AIR
  521. && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
  522. return;
  523. if (tree_definition.fruit_chance > 0) {
  524. if (ps.range(1, 100) > 100 - tree_definition.fruit_chance)
  525. vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.fruitnode;
  526. else
  527. vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
  528. } else if (ps.range(1, 100) > 20) {
  529. vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
  530. }
  531. }
  532. void tree_single_leaves_placement(MMVManip &vmanip, v3f p0,
  533. PseudoRandom ps, TreeDef &tree_definition)
  534. {
  535. MapNode leavesnode = tree_definition.leavesnode;
  536. if (ps.range(1, 100) > 100 - tree_definition.leaves2_chance)
  537. leavesnode = tree_definition.leaves2node;
  538. v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
  539. if (!vmanip.m_area.contains(p1))
  540. return;
  541. u32 vi = vmanip.m_area.index(p1);
  542. if (vmanip.m_data[vi].getContent() != CONTENT_AIR
  543. && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
  544. return;
  545. vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
  546. }
  547. void tree_fruit_placement(MMVManip &vmanip, v3f p0, TreeDef &tree_definition)
  548. {
  549. v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
  550. if (!vmanip.m_area.contains(p1))
  551. return;
  552. u32 vi = vmanip.m_area.index(p1);
  553. if (vmanip.m_data[vi].getContent() != CONTENT_AIR
  554. && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
  555. return;
  556. vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.fruitnode;
  557. }
  558. irr::core::matrix4 setRotationAxisRadians(irr::core::matrix4 M, double angle, v3f axis)
  559. {
  560. double c = cos(angle);
  561. double s = sin(angle);
  562. double t = 1.0 - c;
  563. double tx = t * axis.X;
  564. double ty = t * axis.Y;
  565. double tz = t * axis.Z;
  566. double sx = s * axis.X;
  567. double sy = s * axis.Y;
  568. double sz = s * axis.Z;
  569. M[0] = tx * axis.X + c;
  570. M[1] = tx * axis.Y + sz;
  571. M[2] = tx * axis.Z - sy;
  572. M[4] = ty * axis.X - sz;
  573. M[5] = ty * axis.Y + c;
  574. M[6] = ty * axis.Z + sx;
  575. M[8] = tz * axis.X + sy;
  576. M[9] = tz * axis.Y - sx;
  577. M[10] = tz * axis.Z + c;
  578. return M;
  579. }
  580. v3f transposeMatrix(irr::core::matrix4 M, v3f v)
  581. {
  582. v3f translated;
  583. double x = M[0] * v.X + M[4] * v.Y + M[8] * v.Z +M[12];
  584. double y = M[1] * v.X + M[5] * v.Y + M[9] * v.Z +M[13];
  585. double z = M[2] * v.X + M[6] * v.Y + M[10] * v.Z +M[14];
  586. translated.X = x;
  587. translated.Y = y;
  588. translated.Z = z;
  589. return translated;
  590. }
  591. void make_jungletree(MMVManip &vmanip, v3s16 p0, const NodeDefManager *ndef,
  592. s32 seed)
  593. {
  594. /*
  595. NOTE: Tree-placing code is currently duplicated in the engine
  596. and in games that have saplings; both are deprecated but not
  597. replaced yet
  598. */
  599. content_t c_tree = ndef->getId("mapgen_jungletree");
  600. content_t c_leaves = ndef->getId("mapgen_jungleleaves");
  601. if (c_tree == CONTENT_IGNORE)
  602. c_tree = ndef->getId("mapgen_tree");
  603. if (c_leaves == CONTENT_IGNORE)
  604. c_leaves = ndef->getId("mapgen_leaves");
  605. if (c_tree == CONTENT_IGNORE)
  606. errorstream << "Treegen: Mapgen alias 'mapgen_jungletree' is invalid!" << std::endl;
  607. if (c_leaves == CONTENT_IGNORE)
  608. errorstream << "Treegen: Mapgen alias 'mapgen_jungleleaves' is invalid!" << std::endl;
  609. MapNode treenode(c_tree);
  610. MapNode leavesnode(c_leaves);
  611. PseudoRandom pr(seed);
  612. for (s16 x= -1; x <= 1; x++)
  613. for (s16 z= -1; z <= 1; z++) {
  614. if (pr.range(0, 2) == 0)
  615. continue;
  616. v3s16 p1 = p0 + v3s16(x, 0, z);
  617. v3s16 p2 = p0 + v3s16(x, -1, z);
  618. u32 vi1 = vmanip.m_area.index(p1);
  619. u32 vi2 = vmanip.m_area.index(p2);
  620. if (vmanip.m_area.contains(p2) &&
  621. vmanip.m_data[vi2].getContent() == CONTENT_AIR)
  622. vmanip.m_data[vi2] = treenode;
  623. else if (vmanip.m_area.contains(p1) &&
  624. vmanip.m_data[vi1].getContent() == CONTENT_AIR)
  625. vmanip.m_data[vi1] = treenode;
  626. }
  627. vmanip.m_data[vmanip.m_area.index(p0)] = treenode;
  628. s16 trunk_h = pr.range(8, 12);
  629. v3s16 p1 = p0;
  630. for (s16 ii = 0; ii < trunk_h; ii++) {
  631. if (vmanip.m_area.contains(p1)) {
  632. u32 vi = vmanip.m_area.index(p1);
  633. vmanip.m_data[vi] = treenode;
  634. }
  635. p1.Y++;
  636. }
  637. // p1 is now the last piece of the trunk
  638. p1.Y -= 1;
  639. VoxelArea leaves_a(v3s16(-3, -2, -3), v3s16(3, 2, 3));
  640. //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
  641. Buffer<u8> leaves_d(leaves_a.getVolume());
  642. for (s32 i = 0; i < leaves_a.getVolume(); i++)
  643. leaves_d[i] = 0;
  644. // Force leaves at near the end of the trunk
  645. s16 d = 1;
  646. for (s16 z = -d; z <= d; z++)
  647. for (s16 y = -d; y <= d; y++)
  648. for (s16 x = -d; x <= d; x++) {
  649. leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
  650. }
  651. // Add leaves randomly
  652. for (u32 iii = 0; iii < 30; iii++) {
  653. v3s16 p(
  654. pr.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X - d),
  655. pr.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y - d),
  656. pr.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z - d)
  657. );
  658. for (s16 z = 0; z <= d; z++)
  659. for (s16 y = 0; y <= d; y++)
  660. for (s16 x = 0; x <= d; x++) {
  661. leaves_d[leaves_a.index(p + v3s16(x, y, z))] = 1;
  662. }
  663. }
  664. // Blit leaves to vmanip
  665. for (s16 z = leaves_a.MinEdge.Z; z <= leaves_a.MaxEdge.Z; z++)
  666. for (s16 y = leaves_a.MinEdge.Y; y <= leaves_a.MaxEdge.Y; y++) {
  667. v3s16 pmin(leaves_a.MinEdge.X, y, z);
  668. u32 i = leaves_a.index(pmin);
  669. u32 vi = vmanip.m_area.index(pmin + p1);
  670. for (s16 x = leaves_a.MinEdge.X; x <= leaves_a.MaxEdge.X; x++) {
  671. v3s16 p(x, y, z);
  672. if (vmanip.m_area.contains(p + p1) &&
  673. (vmanip.m_data[vi].getContent() == CONTENT_AIR ||
  674. vmanip.m_data[vi].getContent() == CONTENT_IGNORE)) {
  675. if (leaves_d[i] == 1)
  676. vmanip.m_data[vi] = leavesnode;
  677. }
  678. vi++;
  679. i++;
  680. }
  681. }
  682. }
  683. void make_pine_tree(MMVManip &vmanip, v3s16 p0, const NodeDefManager *ndef,
  684. s32 seed)
  685. {
  686. /*
  687. NOTE: Tree-placing code is currently duplicated in the engine
  688. and in games that have saplings; both are deprecated but not
  689. replaced yet
  690. */
  691. content_t c_tree = ndef->getId("mapgen_pine_tree");
  692. content_t c_leaves = ndef->getId("mapgen_pine_needles");
  693. content_t c_snow = ndef->getId("mapgen_snow");
  694. if (c_tree == CONTENT_IGNORE)
  695. c_tree = ndef->getId("mapgen_tree");
  696. if (c_leaves == CONTENT_IGNORE)
  697. c_leaves = ndef->getId("mapgen_leaves");
  698. if (c_snow == CONTENT_IGNORE)
  699. c_snow = CONTENT_AIR;
  700. if (c_tree == CONTENT_IGNORE)
  701. errorstream << "Treegen: Mapgen alias 'mapgen_pine_tree' is invalid!" << std::endl;
  702. if (c_leaves == CONTENT_IGNORE)
  703. errorstream << "Treegen: Mapgen alias 'mapgen_pine_needles' is invalid!" << std::endl;
  704. MapNode treenode(c_tree);
  705. MapNode leavesnode(c_leaves);
  706. MapNode snownode(c_snow);
  707. PseudoRandom pr(seed);
  708. u16 trunk_h = pr.range(9, 13);
  709. v3s16 p1 = p0;
  710. for (u16 ii = 0; ii < trunk_h; ii++) {
  711. if (vmanip.m_area.contains(p1)) {
  712. u32 vi = vmanip.m_area.index(p1);
  713. vmanip.m_data[vi] = treenode;
  714. }
  715. p1.Y++;
  716. }
  717. // Make p1 the top node of the trunk
  718. p1.Y -= 1;
  719. VoxelArea leaves_a(v3s16(-3, -6, -3), v3s16(3, 3, 3));
  720. Buffer<u8> leaves_d(leaves_a.getVolume());
  721. for (s32 i = 0; i < leaves_a.getVolume(); i++)
  722. leaves_d[i] = 0;
  723. // Upper branches
  724. u16 dev = 3;
  725. for (s16 yy = -1; yy <= 1; yy++) {
  726. for (s16 zz = -dev; zz <= dev; zz++) {
  727. u32 i = leaves_a.index(v3s16(-dev, yy, zz));
  728. u32 ia = leaves_a.index(v3s16(-dev, yy+1, zz));
  729. for (s16 xx = -dev; xx <= dev; xx++) {
  730. if (pr.range(0, 20) <= 19 - dev) {
  731. leaves_d[i] = 1;
  732. leaves_d[ia] = 2;
  733. }
  734. i++;
  735. ia++;
  736. }
  737. }
  738. dev--;
  739. }
  740. // Centre top nodes
  741. leaves_d[leaves_a.index(v3s16(0, 1, 0))] = 1;
  742. leaves_d[leaves_a.index(v3s16(0, 2, 0))] = 1;
  743. leaves_d[leaves_a.index(v3s16(0, 3, 0))] = 2;
  744. // Lower branches
  745. s16 my = -6;
  746. for (u32 iii = 0; iii < 20; iii++) {
  747. s16 xi = pr.range(-3, 2);
  748. s16 yy = pr.range(-6, -5);
  749. s16 zi = pr.range(-3, 2);
  750. if (yy > my)
  751. my = yy;
  752. for (s16 zz = zi; zz <= zi + 1; zz++) {
  753. u32 i = leaves_a.index(v3s16(xi, yy, zz));
  754. u32 ia = leaves_a.index(v3s16(xi, yy + 1, zz));
  755. for (s32 xx = xi; xx <= xi + 1; xx++) {
  756. leaves_d[i] = 1;
  757. if (leaves_d[ia] == 0)
  758. leaves_d[ia] = 2;
  759. i++;
  760. ia++;
  761. }
  762. }
  763. }
  764. dev = 2;
  765. for (s16 yy = my + 1; yy <= my + 2; yy++) {
  766. for (s16 zz = -dev; zz <= dev; zz++) {
  767. u32 i = leaves_a.index(v3s16(-dev, yy, zz));
  768. u32 ia = leaves_a.index(v3s16(-dev, yy + 1, zz));
  769. for (s16 xx = -dev; xx <= dev; xx++) {
  770. if (pr.range(0, 20) <= 19 - dev) {
  771. leaves_d[i] = 1;
  772. leaves_d[ia] = 2;
  773. }
  774. i++;
  775. ia++;
  776. }
  777. }
  778. dev--;
  779. }
  780. // Blit leaves to vmanip
  781. for (s16 z = leaves_a.MinEdge.Z; z <= leaves_a.MaxEdge.Z; z++)
  782. for (s16 y = leaves_a.MinEdge.Y; y <= leaves_a.MaxEdge.Y; y++) {
  783. v3s16 pmin(leaves_a.MinEdge.X, y, z);
  784. u32 i = leaves_a.index(pmin);
  785. u32 vi = vmanip.m_area.index(pmin + p1);
  786. for (s16 x = leaves_a.MinEdge.X; x <= leaves_a.MaxEdge.X; x++) {
  787. v3s16 p(x, y, z);
  788. if (vmanip.m_area.contains(p + p1) &&
  789. (vmanip.m_data[vi].getContent() == CONTENT_AIR ||
  790. vmanip.m_data[vi].getContent() == CONTENT_IGNORE ||
  791. vmanip.m_data[vi] == snownode)) {
  792. if (leaves_d[i] == 1)
  793. vmanip.m_data[vi] = leavesnode;
  794. else if (leaves_d[i] == 2)
  795. vmanip.m_data[vi] = snownode;
  796. }
  797. vi++;
  798. i++;
  799. }
  800. }
  801. }
  802. }; // namespace treegen