From 60810c2d375a8c63e096669dd6c6cd791f2c5075 Mon Sep 17 00:00:00 2001 From: cx384 Date: Tue, 12 Mar 2024 20:10:28 +0100 Subject: [PATCH] Add L-system trees as decorations (#14355) --- builtin/game/features.lua | 1 + doc/lua_api.md | 16 +++++++++++- src/mapgen/mg_decoration.cpp | 22 ++++++++++++++++ src/mapgen/mg_decoration.h | 14 ++++++---- src/mapgen/treegen.cpp | 10 +++++++ src/mapgen/treegen.h | 4 ++- src/script/common/c_content.cpp | 46 +++++++++++++++++++++++++++++++++ src/script/common/c_content.h | 5 ++++ src/script/lua_api/l_env.cpp | 39 ---------------------------- src/script/lua_api/l_mapgen.cpp | 14 ++++++++++ 10 files changed, 125 insertions(+), 46 deletions(-) diff --git a/builtin/game/features.lua b/builtin/game/features.lua index 68005811f..0b5b40fb5 100644 --- a/builtin/game/features.lua +++ b/builtin/game/features.lua @@ -37,6 +37,7 @@ core.features = { blocking_pointability_type = true, dynamic_add_media_startup = true, dynamic_add_media_filepath = true, + lsystem_decoration_type = true, } function core.has_feature(arg) diff --git a/doc/lua_api.md b/doc/lua_api.md index f01e3cd35..13f4571ff 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -4533,6 +4533,12 @@ Can specify a probability of a node randomly appearing when placed. This decoration type is intended to be used for multi-node sized discrete structures, such as trees, cave spikes, rocks, and so on. +`lsystem` +----------- + +Generates a L-system tree at the position where the decoration is placed. +Uses the same L-system as `minetest.spawn_tree`, but is faster than using it manually. +The `treedef` field in the decoration definition is used for the tree definition. @@ -5376,6 +5382,8 @@ Utilities dynamic_add_media_startup = true, -- dynamic_add_media supports `filename` and `filedata` parameters (5.9.0) dynamic_add_media_filepath = true, + -- L-system decoration type (5.9.0) + lsystem_decoration_type = true, } ``` @@ -10134,7 +10142,7 @@ See [Decoration types]. Used by `minetest.register_decoration`. ```lua { deco_type = "simple", - -- Type. "simple" or "schematic" supported + -- Type. "simple", "schematic" or "lsystem" supported place_on = "default:dirt_with_grass", -- Node (or list of nodes) that the decoration can be placed on @@ -10287,6 +10295,12 @@ See [Decoration types]. Used by `minetest.register_decoration`. -- Effect is inverted for "all_ceilings" decorations. -- Ignored by 'y_min', 'y_max' and 'spawn_by' checks, which always refer -- to the 'place_on' node. + + ----- L-system-type parameters + + treedef = {}, + -- Same as for `minetest.spawn_tree`. + -- See section [L-system trees] for more details. } ``` diff --git a/src/mapgen/mg_decoration.cpp b/src/mapgen/mg_decoration.cpp index e9e249220..da364e4a1 100644 --- a/src/mapgen/mg_decoration.cpp +++ b/src/mapgen/mg_decoration.cpp @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/numeric.h" #include #include +#include "mapgen/treegen.h" FlagDesc flagdesc_deco[] = { @@ -472,3 +473,24 @@ size_t DecoSchematic::generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceilin return 1; } + +/////////////////////////////////////////////////////////////////////////////// +ObjDef *DecoLSystem::clone() const +{ + auto def = new DecoLSystem(); + Decoration::cloneTo(def); + + def->tree_def = tree_def; + return def; +} + + +size_t DecoLSystem::generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling) +{ + if (!canPlaceDecoration(vm, p)) + return 0; + + // Make sure that tree_def can't be modified, since it is shared. + const auto &ref = *tree_def; + return treegen::make_ltree(*vm, p, m_ndef, ref); +} diff --git a/src/mapgen/mg_decoration.h b/src/mapgen/mg_decoration.h index 89d8d355f..41d0dac64 100644 --- a/src/mapgen/mg_decoration.h +++ b/src/mapgen/mg_decoration.h @@ -31,6 +31,7 @@ class Mapgen; class MMVManip; class PcgRandom; class Schematic; +namespace treegen { struct TreeDef; } enum DecorationType { DECO_SIMPLE, @@ -112,12 +113,15 @@ public: }; -/* class DecoLSystem : public Decoration { public: - virtual void generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax); + ObjDef *clone() const; + + virtual size_t generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling); + + // In case it gets cloned it uses the same tree def. + std::shared_ptr tree_def; }; -*/ class DecorationManager : public ObjDefManager { @@ -139,8 +143,8 @@ public: return new DecoSimple; case DECO_SCHEMATIC: return new DecoSchematic; - //case DECO_LSYSTEM: - // return new DecoLSystem; + case DECO_LSYSTEM: + return new DecoLSystem; default: return NULL; } diff --git a/src/mapgen/treegen.cpp b/src/mapgen/treegen.cpp index 2c1266431..adff72950 100644 --- a/src/mapgen/treegen.cpp +++ b/src/mapgen/treegen.cpp @@ -32,6 +32,16 @@ with this program; if not, write to the Free Software Foundation, Inc., namespace treegen { +void TreeDef::resolveNodeNames() +{ + getIdFromNrBacklog(&trunknode.param0, "", CONTENT_IGNORE); + getIdFromNrBacklog(&leavesnode.param0, "", CONTENT_IGNORE); + if (leaves2_chance) + getIdFromNrBacklog(&leaves2node.param0, "", CONTENT_IGNORE); + if (fruit_chance) + getIdFromNrBacklog(&fruitnode.param0, "", CONTENT_IGNORE); +} + void make_tree(MMVManip &vmanip, v3s16 p0, bool is_apple_tree, const NodeDefManager *ndef, s32 seed) { diff --git a/src/mapgen/treegen.h b/src/mapgen/treegen.h index 59a418824..5ba1cc10c 100644 --- a/src/mapgen/treegen.h +++ b/src/mapgen/treegen.h @@ -35,7 +35,9 @@ namespace treegen { UNBALANCED_BRACKETS }; - struct TreeDef { + struct TreeDef : public NodeResolver { + virtual void resolveNodeNames(); + std::string initial_axiom; std::string rules_a; std::string rules_b; diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index d9f179112..02f69b3e1 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -37,6 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "debug.h" // For FATAL_ERROR #include #include +#include "mapgen/treegen.h" struct EnumString es_TileAnimationType[] = { @@ -2006,6 +2007,51 @@ void push_noiseparams(lua_State *L, NoiseParams *np) lua_setfield(L, -2, "spread"); } +bool read_tree_def(lua_State *L, int idx, const NodeDefManager *ndef, + treegen::TreeDef &tree_def) +{ + std::string trunk, leaves, fruit; + if (!lua_istable(L, idx)) + return false; + + getstringfield(L, idx, "axiom", tree_def.initial_axiom); + getstringfield(L, idx, "rules_a", tree_def.rules_a); + getstringfield(L, idx, "rules_b", tree_def.rules_b); + getstringfield(L, idx, "rules_c", tree_def.rules_c); + getstringfield(L, idx, "rules_d", tree_def.rules_d); + getstringfield(L, idx, "trunk", trunk); + tree_def.m_nodenames.push_back(trunk); + getstringfield(L, idx, "leaves", leaves); + tree_def.m_nodenames.push_back(leaves); + tree_def.leaves2_chance = 0; + getstringfield(L, idx, "leaves2", leaves); + if (!leaves.empty()) { + getintfield(L, idx, "leaves2_chance", tree_def.leaves2_chance); + if (tree_def.leaves2_chance) + tree_def.m_nodenames.push_back(leaves); + } + getintfield(L, idx, "angle", tree_def.angle); + getintfield(L, idx, "iterations", tree_def.iterations); + if (!getintfield(L, idx, "random_level", tree_def.iterations_random_level)) + tree_def.iterations_random_level = 0; + getstringfield(L, idx, "trunk_type", tree_def.trunk_type); + getboolfield(L, idx, "thin_branches", tree_def.thin_branches); + tree_def.fruit_chance = 0; + getstringfield(L, idx, "fruit", fruit); + if (!fruit.empty()) { + getintfield(L, idx, "fruit_chance", tree_def.fruit_chance); + if (tree_def.fruit_chance) + tree_def.m_nodenames.push_back(fruit); + } + tree_def.explicit_seed = getintfield(L, idx, "seed", tree_def.seed); + + // Resolves the node IDs for trunk, leaves, leaves2 and fruit at runtime, + // when tree_def.resolveNodeNames will be called. + ndef->pendNodeResolve(&tree_def); + + return true; +} + /******************************************************************************/ // Returns depth of json value tree static int push_json_value_getdepth(const Json::Value &value) diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index c8481d98d..178a86cf4 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -70,6 +70,7 @@ struct NoiseParams; class Schematic; class ServerActiveObject; struct collisionMoveResult; +namespace treegen { struct TreeDef; } extern struct EnumString es_TileAnimationType[]; @@ -189,6 +190,10 @@ bool read_noiseparams (lua_State *L, int index, NoiseParams *np); void push_noiseparams (lua_State *L, NoiseParams *np); +bool read_tree_def (lua_State *L, int idx, + const NodeDefManager *ndef, + treegen::TreeDef &tree_def); + void luaentity_get (lua_State *L,u16 id); bool push_json_value (lua_State *L, diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index ccf2cd653..37aba7500 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -1331,45 +1331,6 @@ int ModApiEnv::l_find_path(lua_State *L) return 0; } -static bool read_tree_def(lua_State *L, int idx, - const NodeDefManager *ndef, treegen::TreeDef &tree_def) -{ - std::string trunk, leaves, fruit; - if (!lua_istable(L, idx)) - return false; - - getstringfield(L, idx, "axiom", tree_def.initial_axiom); - getstringfield(L, idx, "rules_a", tree_def.rules_a); - getstringfield(L, idx, "rules_b", tree_def.rules_b); - getstringfield(L, idx, "rules_c", tree_def.rules_c); - getstringfield(L, idx, "rules_d", tree_def.rules_d); - getstringfield(L, idx, "trunk", trunk); - tree_def.trunknode = ndef->getId(trunk); - getstringfield(L, idx, "leaves", leaves); - tree_def.leavesnode = ndef->getId(leaves); - tree_def.leaves2_chance = 0; - getstringfield(L, idx, "leaves2", leaves); - if (!leaves.empty()) { - tree_def.leaves2node = ndef->getId(leaves); - getintfield(L, idx, "leaves2_chance", tree_def.leaves2_chance); - } - getintfield(L, idx, "angle", tree_def.angle); - getintfield(L, idx, "iterations", tree_def.iterations); - if (!getintfield(L, idx, "random_level", tree_def.iterations_random_level)) - tree_def.iterations_random_level = 0; - getstringfield(L, idx, "trunk_type", tree_def.trunk_type); - getboolfield(L, idx, "thin_branches", tree_def.thin_branches); - tree_def.fruit_chance = 0; - getstringfield(L, idx, "fruit", fruit); - if (!fruit.empty()) { - tree_def.fruitnode = ndef->getId(fruit); - getintfield(L, idx, "fruit_chance", tree_def.fruit_chance); - } - tree_def.explicit_seed = getintfield(L, idx, "seed", tree_def.seed); - - return true; -} - // spawn_tree(pos, treedef) int ModApiEnv::l_spawn_tree(lua_State *L) { diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp index b8c6e7105..0dcf7b68a 100644 --- a/src/script/lua_api/l_mapgen.cpp +++ b/src/script/lua_api/l_mapgen.cpp @@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapgen/mg_schematic.h" #include "mapgen/mapgen_v5.h" #include "mapgen/mapgen_v7.h" +#include "mapgen/treegen.h" #include "filesys.h" #include "settings.h" #include "log.h" @@ -110,6 +111,7 @@ bool read_schematic_def(lua_State *L, int index, bool read_deco_simple(lua_State *L, DecoSimple *deco); bool read_deco_schematic(lua_State *L, SchematicManager *schemmgr, DecoSchematic *deco); +bool read_deco_lsystem(lua_State *L, const NodeDefManager *ndef, DecoLSystem *deco); /////////////////////////////////////////////////////////////////////////////// @@ -1226,6 +1228,7 @@ int ModApiMapgen::l_register_decoration(lua_State *L) success = read_deco_schematic(L, schemmgr, (DecoSchematic *)deco); break; case DECO_LSYSTEM: + success = read_deco_lsystem(L, ndef, (DecoLSystem *)deco); break; } @@ -1308,6 +1311,17 @@ bool read_deco_schematic(lua_State *L, SchematicManager *schemmgr, DecoSchematic return schem != NULL; } +bool read_deco_lsystem(lua_State *L, const NodeDefManager *ndef, DecoLSystem *deco) +{ + deco->tree_def = std::make_shared(); + + lua_getfield(L, 1, "treedef"); + bool has_def = read_tree_def(L, -1, ndef, *(deco->tree_def)); + lua_pop(L, 1); + + return has_def; +} + // register_ore({lots of stuff}) int ModApiMapgen::l_register_ore(lua_State *L)