diff --git a/builtin/game/register.lua b/builtin/game/register.lua index d6ada6920..bb76caf7f 100644 --- a/builtin/game/register.lua +++ b/builtin/game/register.lua @@ -150,6 +150,14 @@ local function preprocess_node(nodedef) if nodedef.liquidtype == "flowing" then nodedef.paramtype2 = "flowingliquid" end + + if itemdef.type == "node" and (itemdef.drawtype == "sunken" or itemdef.drawtype == "covered") then + if not core.registered_nodes[itemdef.inner_node] then + error("Unable to register item " .. name .. ": Inner node not registered: " .. itemdef.inner_node) + end + else + itemdef.inner_node = nil + end end local function preprocess_craft(itemdef) diff --git a/games/devtest/mods/testnodes/init.lua b/games/devtest/mods/testnodes/init.lua index 49a45e08c..5597dfe4f 100644 --- a/games/devtest/mods/testnodes/init.lua +++ b/games/devtest/mods/testnodes/init.lua @@ -10,4 +10,5 @@ dofile(path.."/liquids.lua") dofile(path.."/light.lua") dofile(path.."/textures.lua") dofile(path.."/overlays.lua") +dofile(path.."/sunken_covered.lua") dofile(path.."/commands.lua") diff --git a/games/devtest/mods/testnodes/sunken_covered.lua b/games/devtest/mods/testnodes/sunken_covered.lua new file mode 100644 index 000000000..99736f884 --- /dev/null +++ b/games/devtest/mods/testnodes/sunken_covered.lua @@ -0,0 +1,78 @@ +local S = minetest.get_translator("testnodes") + +-- Sunken node example. +minetest.register_node("testnodes:sunken_torchlike", { + description = "Sunken Torchlike Test Node in Liquid Source Range 5", + drawtype = "sunken", + tiles = {"testnodes_liquidsource_r5.png"}, + special_tiles = { + {name = "testnodes_liquidsource_r5.png", backface_culling = false}, + {name = "testnodes_liquidsource_r5.png", backface_culling = true}, + }, + use_texture_alpha = "blend", + paramtype = "light", + walkable = false, + buildable_to = true, + is_ground_content = false, + liquidtype = "source", + --liquid_alternative_flowing = "testnodes:rliquid_flowing_5", + --liquid_alternative_source = "testnodes:rliquid_5", + liquid_alternative_flowing = "testnodes:sunken_torchlike", + liquid_alternative_source = "testnodes:sunken_torchlike", + liquid_range = 0, + + inner_node = "testnodes:torchlike", +}) + +minetest.register_node("testnodes:sunken_nodebox", { + description = "Sunken Nodebox Test Node in Liquid Source Range 5", + drawtype = "sunken", + tiles = {"testnodes_liquidsource_r5.png"}, + special_tiles = { + {name = "testnodes_liquidsource_r5.png", backface_culling = false}, + {name = "testnodes_liquidsource_r5.png", backface_culling = true}, + }, + use_texture_alpha = "blend", + paramtype = "light", + walkable = false, + buildable_to = true, + is_ground_content = false, + liquidtype = "source", + --liquid_alternative_flowing = "testnodes:rliquid_flowing_5", + --liquid_alternative_source = "testnodes:rliquid_5", + liquid_alternative_flowing = "testnodes:sunken_nodebox", + liquid_alternative_source = "testnodes:sunken_nodebox", + liquid_range = 0, + + inner_node = "testnodes:nodebox_fixed", +}) + + +-- Covered node example. +-- An simple example nodebox with one centered box +minetest.register_node("testnodes:covered_torchlike_node", { + description = S("Covered Tochlike Test Node").."\n".. + S("Torchlike node inside"), + --tiles = {"testnodes_nodebox.png"}, + tiles = { "testnodes_glasslike.png" }, + drawtype = "covered", + paramtype = "light", + + inner_node = "testnodes:torchlike", + + groups = {dig_immediate=3}, +}) + +minetest.register_node("testnodes:covered_nodebox_node", { + description = S("Covered Fixed Nodebox Test Node").."\n".. + S("Torchlike node inside"), + --tiles = {"testnodes_nodebox.png"}, + tiles = { "testnodes_glasslike.png" }, + drawtype = "covered", + paramtype = "light", + + inner_node = "testnodes:nodebox_fixed", + + groups = {dig_immediate=3}, +}) + diff --git a/src/client/content_mapblock.cpp b/src/client/content_mapblock.cpp index 6c1cfea33..ec5febe63 100644 --- a/src/client/content_mapblock.cpp +++ b/src/client/content_mapblock.cpp @@ -1763,6 +1763,117 @@ void MapblockMeshGenerator::drawMeshNode() } mesh->drop(); } +void MapblockMeshGenerator::drawSunkenNode() +{ + MapNode store_n; + const ContentFeatures *store_f; + store_n = cur_node.n; + store_f = cur_node.f; + cur_node.n.param0 = cur_node.f->inner_node_id; + cur_node.f = &nodedef->get(cur_node.f->inner_node_id); + drawNode(); + + cur_node.n = store_n; + cur_node.f = store_f; + drawSolidNode(); +} +void MapblockMeshGenerator::drawCoveredNode() +{ + MapNode store_n; + const ContentFeatures *store_f; + store_n = cur_node.n; + store_f = cur_node.f; + cur_node.n.param0 = cur_node.f->inner_node_id; + cur_node.n.param2 &= 0x0F; + cur_node.f = &nodedef->get(cur_node.f->inner_node_id); + drawNode(); + cur_node.n = store_n; + cur_node.f = store_f; + + u8 faces = 0; // k-th bit will be set if k-th face is to be drawn. + static const v3s16 tile_dirs[6] = { + v3s16(0, 1, 0), + v3s16(0, -1, 0), + v3s16(1, 0, 0), + v3s16(-1, 0, 0), + v3s16(0, 0, 1), + v3s16(0, 0, -1) + }; + TileSpec tiles[6]; + u16 lights[6]; + content_t n1 = cur_node.n.getContent(); + for (int face = 0; face < 6; face++) { + v3s16 p2 = blockpos_nodes + cur_node.p + tile_dirs[face]; + MapNode neighbor = data->m_vmanip.getNodeNoEx(p2); + content_t n2 = neighbor.getContent(); + bool backface_culling = cur_node.f->drawtype == NDT_NORMAL; + if (n2 == n1) + continue; + if (n2 == CONTENT_IGNORE) + continue; + if (n2 != CONTENT_AIR) { + const ContentFeatures &f2 = nodedef->get(n2); + if (f2.solidness == 2) + continue; + } + faces |= 1 << face; + getTile(tile_dirs[face], &tiles[face]); + for (auto &layer : tiles[face].layers) { + if (backface_culling) + layer.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING; + layer.material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL; + layer.material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL; + } + if (!data->m_smooth_lighting) { + lights[face] = getFaceLight(cur_node.n, neighbor, nodedef); + } + } + if (!faces) + return; + u8 mask = faces ^ 0b0011'1111; // k-th bit is set if k-th face is to be *omitted*, as expected by cuboid drawing functions. + cur_node.origin = intToFloat(cur_node.p, BS); + auto box = aabb3f(v3f(-0.5 * BS), v3f(0.5 * BS)); + f32 corr_y = ((cur_node.n.param2&0xF0)>>4)/16.0; + box.MaxEdge.Y -= corr_y * BS; + f32 texture_coord_buf[24]; + box.MinEdge += cur_node.origin; + box.MaxEdge += cur_node.origin; + generateCuboidTextureCoords(box, texture_coord_buf); + if (data->m_smooth_lighting) { + LightPair lights[6][4]; + for (int face = 0; face < 6; ++face) { + for (int k = 0; k < 4; k++) { + v3s16 corner = light_dirs[light_indices[face][k]]; + lights[face][k] = LightPair(getSmoothLightSolid( + blockpos_nodes + cur_node.p, tile_dirs[face], corner, data)); + } + } + + drawCuboid(box, tiles, 6, texture_coord_buf, mask, [&] (int face, video::S3DVertex vertices[4]) { + auto final_lights = lights[face]; + for (int j = 0; j < 4; j++) { + video::S3DVertex &vertex = vertices[j]; + vertex.Color = encode_light(final_lights[j], cur_node.f->light_source); + if (!cur_node.f->light_source) + applyFacesShading(vertex.Color, vertex.Normal); + } + if (lightDiff(final_lights[1], final_lights[3]) < lightDiff(final_lights[0], final_lights[2])) + return QuadDiagonal::Diag13; + return QuadDiagonal::Diag02; + }); + } else { + drawCuboid(box, tiles, 6, texture_coord_buf, mask, [&] (int face, video::S3DVertex vertices[4]) { + video::SColor color = encode_light(lights[face], cur_node.f->light_source); + if (!cur_node.f->light_source) + applyFacesShading(color, vertices[0].Normal); + for (int j = 0; j < 4; j++) { + video::S3DVertex &vertex = vertices[j]; + vertex.Color = color; + } + return QuadDiagonal::Diag02; + }); + } +} // also called when the drawtype is known but should have been pre-converted void MapblockMeshGenerator::errorUnknownDrawtype() @@ -1804,6 +1915,8 @@ void MapblockMeshGenerator::drawNode() case NDT_RAILLIKE: drawRaillikeNode(); break; case NDT_NODEBOX: drawNodeboxNode(); break; case NDT_MESH: drawMeshNode(); break; + case NDT_SUNKEN: drawSunkenNode(); break; + case NDT_COVERED: drawCoveredNode(); break; default: errorUnknownDrawtype(); break; } } diff --git a/src/client/content_mapblock.h b/src/client/content_mapblock.h index 9d51ba2bc..b2275f87a 100644 --- a/src/client/content_mapblock.h +++ b/src/client/content_mapblock.h @@ -164,6 +164,8 @@ private: void drawRaillikeNode(); void drawNodeboxNode(); void drawMeshNode(); + void drawSunkenNode(); + void drawCoveredNode(); // common void errorUnknownDrawtype(); diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 9d54c3957..79a2c3037 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -408,6 +408,8 @@ void ContentFeatures::reset() move_resistance = 0; liquid_move_physics = false; post_effect_color_shaded = false; + inner_node.clear(); + inner_node_id = CONTENT_IGNORE; } void ContentFeatures::setAlphaFromLegacy(u8 legacy_alpha) @@ -535,6 +537,7 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const writeU8(os, move_resistance); writeU8(os, liquid_move_physics); writeU8(os, post_effect_color_shaded); + os << serializeString16(inner_node); } void ContentFeatures::deSerialize(std::istream &is, u16 protocol_version) @@ -665,6 +668,9 @@ void ContentFeatures::deSerialize(std::istream &is, u16 protocol_version) if (is.eof()) throw SerializationError(""); post_effect_color_shaded = tmp; + if (is.eof()) + throw SerializationError(""); + inner_node = deSerializeString16(is); } catch (SerializationError &e) {}; } @@ -883,6 +889,10 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc case NDT_PLANTLIKE_ROOTED: solidness = 2; break; + case NDT_SUNKEN: + case NDT_COVERED: + solidness = 0; + break; } if (is_liquid) { @@ -1630,6 +1640,9 @@ void NodeDefManager::resetNodeResolveState() void NodeDefManager::resolveCrossrefs() { for (ContentFeatures &f : m_content_features) { + if (f.drawtype == NDT_SUNKEN || f.drawtype == NDT_COVERED) { + f.inner_node_id = getId(f.inner_node); + } if (f.isLiquid() || f.isLiquidRender()) { f.liquid_alternative_flowing_id = getId(f.liquid_alternative_flowing); f.liquid_alternative_source_id = getId(f.liquid_alternative_source); diff --git a/src/nodedef.h b/src/nodedef.h index 967e3fcd4..5002e02cb 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -227,6 +227,10 @@ enum NodeDrawType : u8 NDT_MESH, // Combined plantlike-on-solid NDT_PLANTLIKE_ROOTED, + // sunken node + NDT_SUNKEN, + // covered node + NDT_COVERED, // Dummy for validity check NodeDrawType_END @@ -368,6 +372,9 @@ struct ContentFeatures u8 leveled; // Maximum value for leveled nodes u8 leveled_max; + // inner node for sunken and covered draw types + std::string inner_node; + content_t inner_node_id; // --- LIGHTING-RELATED --- diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 91aa5b405..a801fddbf 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -674,7 +674,7 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype, bool special) } /******************************************************************************/ -void read_content_features(lua_State *L, ContentFeatures &f, int index) +void read_content_features(lua_State *L, ContentFeatures &f, int index, NodeDefManager *ndef) { if(index < 0) index = lua_gettop(L) + 1 + index; @@ -1013,6 +1013,9 @@ void read_content_features(lua_State *L, ContentFeatures &f, int index) errorstream << "Field \"liquid_move_physics\": Invalid type!" << std::endl; } lua_pop(L, 1); + + // inner_node for sunken and covered drawtype + getstringfield(L, index, "inner_node", f.inner_node); } void push_content_features(lua_State *L, const ContentFeatures &c) diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index f57c15797..62ac4cd77 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -63,7 +63,8 @@ extern struct EnumString es_TouchInteractionMode[]; extern const std::array object_property_keys; -void read_content_features(lua_State *L, ContentFeatures &f, int index); +void read_content_features(lua_State *L, ContentFeatures &f, int index, + NodeDefManager *ndef); void push_content_features(lua_State *L, const ContentFeatures &c); void push_nodebox(lua_State *L, const NodeBox &box); diff --git a/src/script/cpp_api/s_node.cpp b/src/script/cpp_api/s_node.cpp index 98169975a..767849601 100644 --- a/src/script/cpp_api/s_node.cpp +++ b/src/script/cpp_api/s_node.cpp @@ -33,6 +33,8 @@ struct EnumString ScriptApiNode::es_DrawType[] = {NDT_GLASSLIKE_FRAMED_OPTIONAL, "glasslike_framed_optional"}, {NDT_MESH, "mesh"}, {NDT_PLANTLIKE_ROOTED, "plantlike_rooted"}, + {NDT_SUNKEN, "sunken"}, + {NDT_COVERED, "covered"}, {0, NULL}, }; diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index bae8aa2d2..5aaf77d65 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -616,7 +616,7 @@ int ModApiItem::l_register_item_raw(lua_State *L) // Read the node definition (content features) and register it if (def.type == ITEM_NODE) { ContentFeatures f; - read_content_features(L, f, table); + read_content_features(L, f, table, ndef); // when a mod reregisters ignore, only texture changes and such should // be done if (f.name == "ignore")