From c1e588f22944e044726a2a3226871513ae98c341 Mon Sep 17 00:00:00 2001 From: Lars Mueller Date: Mon, 5 Feb 2024 19:28:56 +0100 Subject: [PATCH 1/6] Fix particles not supporting the texture property --- doc/lua_api.md | 2 +- src/particles.cpp | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index be11a4c5d..cffb82c04 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -10532,7 +10532,7 @@ Used by `minetest.add_particle`. texture = "image.png", -- The texture of the particle - -- v5.6.0 and later: also supports the table format described in the + -- v5.9.0 and later: also supports the table format described in the -- following section playername = "singleplayer", diff --git a/src/particles.cpp b/src/particles.cpp index c67d72711..7c32f7552 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -254,6 +254,7 @@ void ParticleParameters::serialize(std::ostream &os, u16 protocol_ver) const writeV3F32(os, drag); jitter.serialize(os); bounce.serialize(os); + texture.serialize(os, protocol_ver, true); } template @@ -291,4 +292,7 @@ void ParticleParameters::deSerialize(std::istream &is, u16 protocol_ver) return; jitter.deSerialize(is); bounce.deSerialize(is); + if (is.eof()) + return; + texture.deSerialize(is, protocol_ver, true); } From e5a4423ef529980d7a3992f788b546428fa68116 Mon Sep 17 00:00:00 2001 From: Lars Mueller Date: Mon, 5 Feb 2024 19:36:36 +0100 Subject: [PATCH 2/6] Add particle blend mode "clip" --- builtin/game/features.lua | 1 + doc/lua_api.md | 10 +++ games/devtest/mods/testtools/particles.lua | 28 ++++++-- .../textures/testtools_particle_clip.png | Bin 0 -> 179 bytes src/client/particles.cpp | 60 ++++++++++-------- src/particles.cpp | 6 +- src/particles.h | 3 +- src/script/lua_api/l_particleparams.h | 3 +- 8 files changed, 75 insertions(+), 36 deletions(-) create mode 100644 games/devtest/mods/testtools/textures/testtools_particle_clip.png diff --git a/builtin/game/features.lua b/builtin/game/features.lua index 874d3e885..6e15a2b5f 100644 --- a/builtin/game/features.lua +++ b/builtin/game/features.lua @@ -39,6 +39,7 @@ core.features = { dynamic_add_media_filepath = true, lsystem_decoration_type = true, item_meta_range = true, + particle_blend_clip = true, } function core.has_feature(arg) diff --git a/doc/lua_api.md b/doc/lua_api.md index cffb82c04..07ca70874 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -5401,6 +5401,8 @@ Utilities lsystem_decoration_type = true, -- Overrideable pointing range using the itemstack meta key `"range"` (5.9.0) item_meta_range = true, + -- Particles can specify a "clip" blend mode + particle_blend_clip = true, } ``` @@ -10880,6 +10882,14 @@ texture = { -- (default) blends transparent pixels with those they are drawn atop -- according to the alpha channel of the source texture. useful for -- e.g. material objects like rocks, dirt, smoke, or node chunks + blend = "clip", + -- pixels are either fully opaque or fully transparent, + -- depending on whether alpha is greater than or less than 50% + -- (similar to `use_texture_alpha = "clip"` for nodes). + -- this fixes rendering bugs (invisibility) that occur when particles + -- interact with translucent nodes + -- (see https://github.com/minetest/minetest/issues/3761). + -- in the future, it may be useful for better performance. blend = "add", -- adds the value of pixels to those underneath them, modulo the sources -- alpha channel. useful for e.g. bright light effects like sparks or fire diff --git a/games/devtest/mods/testtools/particles.lua b/games/devtest/mods/testtools/particles.lua index 18efe2572..eea9faac1 100644 --- a/games/devtest/mods/testtools/particles.lua +++ b/games/devtest/mods/testtools/particles.lua @@ -1,14 +1,27 @@ +local function spawn_clip_test_particle(pos) + minetest.add_particle({ + pos = pos, + size = 5, + expirationtime = 10, + texture = { + name = "testtools_particle_clip.png", + blend = "clip", + }, + }) +end + minetest.register_tool("testtools:particle_spawner", { - description = "Particle Spawner".."\n".. + description = table.concat({ + "Particle Spawner", "Punch: Spawn random test particle", + "Place: Spawn clip test particle", + }, "\n"), inventory_image = "testtools_particle_spawner.png", groups = { testtool = 1, disable_repair = 1 }, on_use = function(itemstack, user, pointed_thing) local pos = minetest.get_pointed_thing_position(pointed_thing, true) if pos == nil then - if user then - pos = user:get_pos() - end + pos = assert(user):get_pos() end pos = vector.add(pos, {x=0, y=0.5, z=0}) local tex, anim @@ -32,5 +45,12 @@ minetest.register_tool("testtools:particle_spawner", { glow = math.random(0, 5), }) end, + on_place = function(itemstack, user, pointed_thing) + local pos = assert(minetest.get_pointed_thing_position(pointed_thing, true)) + spawn_clip_test_particle(pos) + end, + on_secondary_use = function(_, user) + spawn_clip_test_particle(assert(user):get_pos()) + end, }) diff --git a/games/devtest/mods/testtools/textures/testtools_particle_clip.png b/games/devtest/mods/testtools/textures/testtools_particle_clip.png new file mode 100644 index 0000000000000000000000000000000000000000..5fb9ad09a6c8596e60004b045007fdecb5039c08 GIT binary patch literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|ay?xfLoEE0 zQx*sqG=166BmR%y*uCay^f6}r*8kQW`&mynHhegJ`v3i|*GBt9=JAHJ3yIgyKE+|q zutabf<9tR1-vsa0^#Agv4QC7-`bxW)1k(g=UbM(!P|_<%Q=1_au*~V4&xSn>LLF0{ aWH5Bde-YerZTTjk#SEUVelF{r5}E*N6h21) literal 0 HcmV?d00001 diff --git a/src/client/particles.cpp b/src/client/particles.cpp index 14384f3b8..c99c24ee3 100644 --- a/src/client/particles.cpp +++ b/src/client/particles.cpp @@ -18,7 +18,10 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "particles.h" +#include +#include #include +#include #include "client.h" #include "collision.h" #include "client/content_cao.h" @@ -75,36 +78,30 @@ Particle::Particle( // Set material { // translate blend modes to GL blend functions - video::E_BLEND_FACTOR bfsrc, bfdst; - video::E_BLEND_OPERATION blendop; + bool blend = true; + video::E_BLEND_FACTOR bfsrc = video::EBF_SRC_ALPHA, bfdst = video::EBF_DST_ALPHA; + video::E_BLEND_OPERATION blendop = video::EBO_ADD; const auto blendmode = texture.tex != nullptr ? texture.tex->blendmode : ParticleParamTypes::BlendMode::alpha; switch (blendmode) { - case ParticleParamTypes::BlendMode::add: - bfsrc = video::EBF_SRC_ALPHA; - bfdst = video::EBF_DST_ALPHA; - blendop = video::EBO_ADD; - break; - + case ParticleParamTypes::BlendMode::clip: + blend = false; + break; + case ParticleParamTypes::BlendMode::alpha: + bfdst = video::EBF_ONE_MINUS_SRC_ALPHA; + break; + case ParticleParamTypes::BlendMode::add: break; case ParticleParamTypes::BlendMode::sub: - bfsrc = video::EBF_SRC_ALPHA; - bfdst = video::EBF_DST_ALPHA; blendop = video::EBO_REVSUBTRACT; - break; - + break; case ParticleParamTypes::BlendMode::screen: bfsrc = video::EBF_ONE; bfdst = video::EBF_ONE_MINUS_SRC_COLOR; - blendop = video::EBO_ADD; - break; - - default: // includes ParticleParamTypes::BlendMode::alpha - bfsrc = video::EBF_SRC_ALPHA; - bfdst = video::EBF_ONE_MINUS_SRC_ALPHA; - blendop = video::EBO_ADD; - break; + break; + case ParticleParamTypes::BlendMode::BlendMode_END: + throw std::logic_error("invalid blend mode"); } // Texture @@ -120,12 +117,16 @@ Particle::Particle( m_material.ZWriteEnable = video::EZW_AUTO; // enable alpha blending and set blend mode - m_material.MaterialType = video::EMT_ONETEXTURE_BLEND; - m_material.MaterialTypeParam = video::pack_textureBlendFunc( - bfsrc, bfdst, - video::EMFN_MODULATE_1X, - video::EAS_TEXTURE | video::EAS_VERTEX_COLOR); - m_material.BlendOperation = blendop; + if (blend) { + m_material.MaterialType = video::EMT_ONETEXTURE_BLEND; + m_material.MaterialTypeParam = video::pack_textureBlendFunc( + bfsrc, bfdst, + video::EMFN_MODULATE_1X, + video::EAS_TEXTURE | video::EAS_VERTEX_COLOR); + m_material.BlendOperation = blendop; + } else { + m_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + } m_material.setTexture(0, m_texture.ref); } @@ -141,8 +142,11 @@ Particle::Particle( void Particle::OnRegisterSceneNode() { - if (IsVisible) - SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT_EFFECT); + if (IsVisible) { + bool solid = m_texture.tex && m_texture.tex->blendmode == ParticleParamTypes::BlendMode::clip; + SceneManager->registerNodeForRendering(this, + solid ? scene::ESNRP_SOLID : scene::ESNRP_TRANSPARENT_EFFECT); + } ISceneNode::OnRegisterSceneNode(); } diff --git a/src/particles.cpp b/src/particles.cpp index 7c32f7552..0c962ad68 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "particles.h" +#include "exceptions.h" #include using namespace ParticleParamTypes; @@ -204,8 +205,7 @@ void ServerParticleTexture::serialize(std::ostream &os, u16 protocol_ver, bool n FlagT flags = 0; if (animated) flags |= FlagT(ParticleTextureFlags::animated); - if (blendmode != BlendMode::alpha) - flags |= FlagT(blendmode) << 1; + flags |= FlagT(blendmode) << 1; serializeParameterValue(os, flags); alpha.serialize(os); @@ -224,6 +224,8 @@ void ServerParticleTexture::deSerialize(std::istream &is, u16 protocol_ver, bool animated = !!(flags & FlagT(ParticleTextureFlags::animated)); blendmode = BlendMode((flags & FlagT(ParticleTextureFlags::blend)) >> 1); + if (blendmode >= BlendMode::BlendMode_END) + throw SerializationError("invalid blend mode"); alpha.deSerialize(is); scale.deSerialize(is); diff --git a/src/particles.h b/src/particles.h index b9fbe38c8..31852fc02 100644 --- a/src/particles.h +++ b/src/particles.h @@ -249,7 +249,8 @@ namespace ParticleParamTypes } enum class AttractorKind : u8 { none, point, line, plane }; - enum class BlendMode : u8 { alpha, add, sub, screen }; + // Note: Allows at most 8 enum members (due to how this is serialized) + enum class BlendMode : u8 { alpha, add, sub, screen, clip, BlendMode_END }; // these are consistently-named convenience aliases to make code more readable without `using ParticleParamTypes` declarations using v3fRange = RangedParameter; diff --git a/src/script/lua_api/l_particleparams.h b/src/script/lua_api/l_particleparams.h index 0ad1541b4..01b0c150c 100644 --- a/src/script/lua_api/l_particleparams.h +++ b/src/script/lua_api/l_particleparams.h @@ -133,13 +133,14 @@ namespace LuaParticleParams {(int)BlendMode::add, "add"}, {(int)BlendMode::sub, "sub"}, {(int)BlendMode::screen, "screen"}, + {(int)BlendMode::clip, "clip"}, {0, nullptr}, }; luaL_checktype(L, -1, LUA_TSTRING); int v = (int)BlendMode::alpha; if (!string_to_enum(opts, v, lua_tostring(L, -1))) { - throw LuaError("blend mode must be one of ('alpha', 'add', 'sub', 'screen')"); + throw LuaError("blend mode must be one of ('alpha', 'clip', 'add', 'sub', 'screen')"); } ret = (BlendMode)v; } From fa9dfb96a53e06093cc577a71be6e309206a0f9f Mon Sep 17 00:00:00 2001 From: Lars Mueller Date: Mon, 5 Feb 2024 19:58:53 +0100 Subject: [PATCH 3/6] Send proper values to older clients --- src/particles.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/particles.cpp b/src/particles.cpp index 0c962ad68..c264f8cb4 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -205,7 +205,9 @@ void ServerParticleTexture::serialize(std::ostream &os, u16 protocol_ver, bool n FlagT flags = 0; if (animated) flags |= FlagT(ParticleTextureFlags::animated); - flags |= FlagT(blendmode) << 1; + // Default to `blend = "alpha"` for older clients that don't support `blend = "clip"` + flags |= FlagT(protocol_ver < 44 && blendmode == BlendMode::clip + ? BlendMode::alpha : blendmode) << 1; serializeParameterValue(os, flags); alpha.serialize(os); From 5017b8a89748d6f2a7597fcc0d6c04632bfbc158 Mon Sep 17 00:00:00 2001 From: Lars Mueller Date: Tue, 6 Feb 2024 13:20:22 +0100 Subject: [PATCH 4/6] Fix backwards compat code (untested) --- src/particles.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/particles.cpp b/src/particles.cpp index c264f8cb4..977fd4e10 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -223,6 +223,9 @@ void ServerParticleTexture::deSerialize(std::istream &is, u16 protocol_ver, bool { FlagT flags = 0; deSerializeParameterValue(is, flags); + // Backwards compatibility: Older clients don't send these, leave them at the defaults + if (is.eof()) + return; animated = !!(flags & FlagT(ParticleTextureFlags::animated)); blendmode = BlendMode((flags & FlagT(ParticleTextureFlags::blend)) >> 1); @@ -296,7 +299,5 @@ void ParticleParameters::deSerialize(std::istream &is, u16 protocol_ver) return; jitter.deSerialize(is); bounce.deSerialize(is); - if (is.eof()) - return; texture.deSerialize(is, protocol_ver, true); } From 96e545e5295962c916635a2669eeab02361f4c87 Mon Sep 17 00:00:00 2001 From: Lars Mueller Date: Fri, 9 Feb 2024 00:44:26 +0100 Subject: [PATCH 5/6] Improve docs --- doc/lua_api.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index 07ca70874..0fb211f85 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -10534,8 +10534,10 @@ Used by `minetest.add_particle`. texture = "image.png", -- The texture of the particle - -- v5.9.0 and later: also supports the table format described in the - -- following section + -- v5.6.0 and later: also supports the table format described in the + -- following section, but due to a bug this did not take effect + -- (beyond the texture name). + -- v5.9.0 and later: fixes the bug. playername = "singleplayer", -- Optional, if specified spawns particle only on the player's client From 69b2a3b9f178717e09784a3f4ede9a01cd2fd5b2 Mon Sep 17 00:00:00 2001 From: Lars Mueller Date: Fri, 9 Feb 2024 00:46:31 +0100 Subject: [PATCH 6/6] Check material type instead --- src/client/particles.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/particles.cpp b/src/client/particles.cpp index c99c24ee3..f3793b008 100644 --- a/src/client/particles.cpp +++ b/src/client/particles.cpp @@ -143,7 +143,7 @@ Particle::Particle( void Particle::OnRegisterSceneNode() { if (IsVisible) { - bool solid = m_texture.tex && m_texture.tex->blendmode == ParticleParamTypes::BlendMode::clip; + bool solid = m_material.MaterialType == video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; SceneManager->registerNodeForRendering(this, solid ? scene::ESNRP_SOLID : scene::ESNRP_TRANSPARENT_EFFECT); }