mirror of
https://github.com/minetest/minetest.git
synced 2024-12-23 06:32:23 +01:00
Add particle blend mode "clip" (#15444)
This lets modders avoid alpha blending rendering bugs as well as potential (future) performance issues. The appropriate blend modes are also used for node dig particles. --------- Co-authored-by: sfan5 <sfan5@live.de>
This commit is contained in:
parent
f493e73aeb
commit
138052adfc
@ -44,6 +44,7 @@ core.features = {
|
|||||||
bulk_lbms = true,
|
bulk_lbms = true,
|
||||||
abm_without_neighbors = true,
|
abm_without_neighbors = true,
|
||||||
biome_weights = true,
|
biome_weights = true,
|
||||||
|
particle_blend_clip = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
function core.has_feature(arg)
|
function core.has_feature(arg)
|
||||||
|
@ -21,3 +21,4 @@ This list is largely advisory and items may be reevaluated once the time comes.
|
|||||||
* merge `sound` and `sounds` table in itemdef
|
* merge `sound` and `sounds` table in itemdef
|
||||||
* remove `DIR_DELIM` from Lua
|
* remove `DIR_DELIM` from Lua
|
||||||
* stop reading initial properties from bare entity def
|
* stop reading initial properties from bare entity def
|
||||||
|
* change particle default blend mode to `clip`
|
||||||
|
@ -5657,6 +5657,8 @@ Utilities
|
|||||||
abm_without_neighbors = true,
|
abm_without_neighbors = true,
|
||||||
-- biomes have a weight parameter (5.11.0)
|
-- biomes have a weight parameter (5.11.0)
|
||||||
biome_weights = true,
|
biome_weights = true,
|
||||||
|
-- Particles can specify a "clip" blend mode (5.11.0)
|
||||||
|
particle_blend_clip = true,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -11483,6 +11485,14 @@ texture = {
|
|||||||
-- (default) blends transparent pixels with those they are drawn atop
|
-- (default) blends transparent pixels with those they are drawn atop
|
||||||
-- according to the alpha channel of the source texture. useful for
|
-- according to the alpha channel of the source texture. useful for
|
||||||
-- e.g. material objects like rocks, dirt, smoke, or node chunks
|
-- e.g. material objects like rocks, dirt, smoke, or node chunks
|
||||||
|
-- note: there will be rendering bugs when particles interact with
|
||||||
|
-- translucent nodes. particles are also not transparency-sorted
|
||||||
|
-- relative to each other.
|
||||||
|
blend = "clip",
|
||||||
|
-- pixels are either fully opaque or fully transparent,
|
||||||
|
-- depending on whether alpha is greater than or less than 50%
|
||||||
|
-- (just like `use_texture_alpha = "clip"` for nodes).
|
||||||
|
-- you should prefer this if you don't need semi-transparency, as it's faster.
|
||||||
blend = "add",
|
blend = "add",
|
||||||
-- adds the value of pixels to those underneath them, modulo the sources
|
-- 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
|
-- alpha channel. useful for e.g. bright light effects like sparks or fire
|
||||||
|
@ -1,14 +1,27 @@
|
|||||||
|
local function spawn_clip_test_particle(pos)
|
||||||
|
core.add_particle({
|
||||||
|
pos = pos,
|
||||||
|
size = 5,
|
||||||
|
expirationtime = 10,
|
||||||
|
texture = {
|
||||||
|
name = "testtools_particle_clip.png",
|
||||||
|
blend = "clip",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
core.register_tool("testtools:particle_spawner", {
|
core.register_tool("testtools:particle_spawner", {
|
||||||
description = "Particle Spawner".."\n"..
|
description = table.concat({
|
||||||
|
"Particle Spawner",
|
||||||
"Punch: Spawn random test particle",
|
"Punch: Spawn random test particle",
|
||||||
|
"Place: Spawn clip test particle",
|
||||||
|
}, "\n"),
|
||||||
inventory_image = "testtools_particle_spawner.png",
|
inventory_image = "testtools_particle_spawner.png",
|
||||||
groups = { testtool = 1, disable_repair = 1 },
|
groups = { testtool = 1, disable_repair = 1 },
|
||||||
on_use = function(itemstack, user, pointed_thing)
|
on_use = function(itemstack, user, pointed_thing)
|
||||||
local pos = core.get_pointed_thing_position(pointed_thing, true)
|
local pos = core.get_pointed_thing_position(pointed_thing, true)
|
||||||
if pos == nil then
|
if pos == nil then
|
||||||
if user then
|
pos = assert(user):get_pos()
|
||||||
pos = user:get_pos()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
pos = vector.add(pos, {x=0, y=0.5, z=0})
|
pos = vector.add(pos, {x=0, y=0.5, z=0})
|
||||||
local tex, anim
|
local tex, anim
|
||||||
@ -32,5 +45,12 @@ core.register_tool("testtools:particle_spawner", {
|
|||||||
glow = math.random(0, 5),
|
glow = math.random(0, 5),
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
|
on_place = function(itemstack, user, pointed_thing)
|
||||||
|
local pos = assert(core.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,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 179 B |
@ -22,6 +22,8 @@
|
|||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "profiler.h"
|
#include "profiler.h"
|
||||||
|
|
||||||
|
using BlendMode = ParticleParamTypes::BlendMode;
|
||||||
|
|
||||||
ClientParticleTexture::ClientParticleTexture(const ServerParticleTexture& p, ITextureSource *tsrc)
|
ClientParticleTexture::ClientParticleTexture(const ServerParticleTexture& p, ITextureSource *tsrc)
|
||||||
{
|
{
|
||||||
tex = p;
|
tex = p;
|
||||||
@ -603,8 +605,11 @@ video::S3DVertex *ParticleBuffer::getVertices(u16 index)
|
|||||||
|
|
||||||
void ParticleBuffer::OnRegisterSceneNode()
|
void ParticleBuffer::OnRegisterSceneNode()
|
||||||
{
|
{
|
||||||
if (IsVisible)
|
if (IsVisible) {
|
||||||
SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT_EFFECT);
|
SceneManager->registerNodeForRendering(this,
|
||||||
|
m_mesh_buffer->getMaterial().MaterialType == video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF
|
||||||
|
? scene::ESNRP_SOLID : scene::ESNRP_TRANSPARENT_EFFECT);
|
||||||
|
}
|
||||||
scene::ISceneNode::OnRegisterSceneNode();
|
scene::ISceneNode::OnRegisterSceneNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -906,6 +911,9 @@ void ParticleManager::addNodeParticle(IGameDef *gamedef,
|
|||||||
if (!getNodeParticleParams(n, f, p, &ref, texpos, texsize, &color))
|
if (!getNodeParticleParams(n, f, p, &ref, texpos, texsize, &color))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
p.texture.blendmode = f.alpha == ALPHAMODE_BLEND
|
||||||
|
? BlendMode::alpha : BlendMode::clip;
|
||||||
|
|
||||||
p.expirationtime = myrand_range(0, 100) / 100.0f;
|
p.expirationtime = myrand_range(0, 100) / 100.0f;
|
||||||
|
|
||||||
// Physics
|
// Physics
|
||||||
@ -940,40 +948,47 @@ void ParticleManager::reserveParticleSpace(size_t max_estimate)
|
|||||||
m_particles.reserve(m_particles.size() + max_estimate);
|
m_particles.reserve(m_particles.size() + max_estimate);
|
||||||
}
|
}
|
||||||
|
|
||||||
video::SMaterial ParticleManager::getMaterialForParticle(const ClientParticleTexRef &texture)
|
static void setBlendMode(video::SMaterial &material, BlendMode blendmode)
|
||||||
{
|
{
|
||||||
// translate blend modes to GL blend functions
|
|
||||||
video::E_BLEND_FACTOR bfsrc, bfdst;
|
video::E_BLEND_FACTOR bfsrc, bfdst;
|
||||||
video::E_BLEND_OPERATION blendop;
|
video::E_BLEND_OPERATION blendop;
|
||||||
const auto blendmode = texture.tex ? texture.tex->blendmode :
|
|
||||||
ParticleParamTypes::BlendMode::alpha;
|
|
||||||
|
|
||||||
switch (blendmode) {
|
switch (blendmode) {
|
||||||
case ParticleParamTypes::BlendMode::add:
|
case BlendMode::add:
|
||||||
bfsrc = video::EBF_SRC_ALPHA;
|
bfsrc = video::EBF_SRC_ALPHA;
|
||||||
bfdst = video::EBF_DST_ALPHA;
|
bfdst = video::EBF_DST_ALPHA;
|
||||||
blendop = video::EBO_ADD;
|
blendop = video::EBO_ADD;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ParticleParamTypes::BlendMode::sub:
|
case BlendMode::sub:
|
||||||
bfsrc = video::EBF_SRC_ALPHA;
|
bfsrc = video::EBF_SRC_ALPHA;
|
||||||
bfdst = video::EBF_DST_ALPHA;
|
bfdst = video::EBF_DST_ALPHA;
|
||||||
blendop = video::EBO_REVSUBTRACT;
|
blendop = video::EBO_REVSUBTRACT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ParticleParamTypes::BlendMode::screen:
|
case BlendMode::screen:
|
||||||
bfsrc = video::EBF_ONE;
|
bfsrc = video::EBF_ONE;
|
||||||
bfdst = video::EBF_ONE_MINUS_SRC_COLOR;
|
bfdst = video::EBF_ONE_MINUS_SRC_COLOR;
|
||||||
blendop = video::EBO_ADD;
|
blendop = video::EBO_ADD;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: // includes ParticleParamTypes::BlendMode::alpha
|
default: // includes BlendMode::alpha
|
||||||
bfsrc = video::EBF_SRC_ALPHA;
|
bfsrc = video::EBF_SRC_ALPHA;
|
||||||
bfdst = video::EBF_ONE_MINUS_SRC_ALPHA;
|
bfdst = video::EBF_ONE_MINUS_SRC_ALPHA;
|
||||||
blendop = video::EBO_ADD;
|
blendop = video::EBO_ADD;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
material.MaterialTypeParam = video::pack_textureBlendFunc(
|
||||||
|
bfsrc, bfdst,
|
||||||
|
video::EMFN_MODULATE_1X,
|
||||||
|
video::EAS_TEXTURE | video::EAS_VERTEX_COLOR);
|
||||||
|
material.BlendOperation = blendop;
|
||||||
|
}
|
||||||
|
|
||||||
|
video::SMaterial ParticleManager::getMaterialForParticle(const Particle *particle)
|
||||||
|
{
|
||||||
|
const ClientParticleTexRef &texture = particle->getTextureRef();
|
||||||
|
|
||||||
video::SMaterial material;
|
video::SMaterial material;
|
||||||
|
|
||||||
// Texture
|
// Texture
|
||||||
@ -984,17 +999,18 @@ video::SMaterial ParticleManager::getMaterialForParticle(const ClientParticleTex
|
|||||||
tex.MagFilter = video::ETMAGF_NEAREST;
|
tex.MagFilter = video::ETMAGF_NEAREST;
|
||||||
});
|
});
|
||||||
|
|
||||||
// We don't have working transparency sorting. Disable Z-Write for
|
const auto blendmode = particle->getBlendMode();
|
||||||
// correct results for clipped-alpha at least.
|
if (blendmode == BlendMode::clip) {
|
||||||
material.ZWriteEnable = video::EZW_OFF;
|
material.ZWriteEnable = video::EZW_ON;
|
||||||
|
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
|
||||||
// enable alpha blending and set blend mode
|
material.MaterialTypeParam = 0.5f;
|
||||||
material.MaterialType = video::EMT_ONETEXTURE_BLEND;
|
} else {
|
||||||
material.MaterialTypeParam = video::pack_textureBlendFunc(
|
// We don't have working transparency sorting. Disable Z-Write for
|
||||||
bfsrc, bfdst,
|
// correct results for clipped-alpha at least.
|
||||||
video::EMFN_MODULATE_1X,
|
material.ZWriteEnable = video::EZW_OFF;
|
||||||
video::EAS_TEXTURE | video::EAS_VERTEX_COLOR);
|
material.MaterialType = video::EMT_ONETEXTURE_BLEND;
|
||||||
material.BlendOperation = blendop;
|
setBlendMode(material, blendmode);
|
||||||
|
}
|
||||||
material.setTexture(0, texture.ref);
|
material.setTexture(0, texture.ref);
|
||||||
|
|
||||||
return material;
|
return material;
|
||||||
@ -1004,7 +1020,7 @@ bool ParticleManager::addParticle(std::unique_ptr<Particle> toadd)
|
|||||||
{
|
{
|
||||||
MutexAutoLock lock(m_particle_list_lock);
|
MutexAutoLock lock(m_particle_list_lock);
|
||||||
|
|
||||||
auto material = getMaterialForParticle(toadd->getTextureRef());
|
auto material = getMaterialForParticle(toadd.get());
|
||||||
|
|
||||||
ParticleBuffer *found = nullptr;
|
ParticleBuffer *found = nullptr;
|
||||||
// simple shortcut when multiple particles of the same type get added
|
// simple shortcut when multiple particles of the same type get added
|
||||||
|
@ -77,6 +77,9 @@ public:
|
|||||||
|
|
||||||
const ClientParticleTexRef &getTextureRef() const { return m_texture; }
|
const ClientParticleTexRef &getTextureRef() const { return m_texture; }
|
||||||
|
|
||||||
|
ParticleParamTypes::BlendMode getBlendMode() const
|
||||||
|
{ return m_texture.tex ? m_texture.tex->blendmode : m_p.texture.blendmode; }
|
||||||
|
|
||||||
ParticleBuffer *getBuffer() const { return m_buffer; }
|
ParticleBuffer *getBuffer() const { return m_buffer; }
|
||||||
bool attachToBuffer(ParticleBuffer *buffer);
|
bool attachToBuffer(ParticleBuffer *buffer);
|
||||||
|
|
||||||
@ -231,7 +234,7 @@ protected:
|
|||||||
ParticleParameters &p, video::ITexture **texture, v2f &texpos,
|
ParticleParameters &p, video::ITexture **texture, v2f &texpos,
|
||||||
v2f &texsize, video::SColor *color, u8 tilenum = 0);
|
v2f &texsize, video::SColor *color, u8 tilenum = 0);
|
||||||
|
|
||||||
static video::SMaterial getMaterialForParticle(const ClientParticleTexRef &texture);
|
static video::SMaterial getMaterialForParticle(const Particle *texture);
|
||||||
|
|
||||||
bool addParticle(std::unique_ptr<Particle> toadd);
|
bool addParticle(std::unique_ptr<Particle> toadd);
|
||||||
|
|
||||||
|
@ -59,9 +59,12 @@
|
|||||||
Rename TOSERVER_RESPAWN to TOSERVER_RESPAWN_LEGACY
|
Rename TOSERVER_RESPAWN to TOSERVER_RESPAWN_LEGACY
|
||||||
Support float animation frame numbers in TOCLIENT_LOCAL_PLAYER_ANIMATIONS
|
Support float animation frame numbers in TOCLIENT_LOCAL_PLAYER_ANIMATIONS
|
||||||
[scheduled bump for 5.10.0]
|
[scheduled bump for 5.10.0]
|
||||||
|
PROTOCOL VERSION 47
|
||||||
|
Add particle blend mode "clip"
|
||||||
|
[scheduled bump for 5.11.0]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const u16 LATEST_PROTOCOL_VERSION = 46;
|
const u16 LATEST_PROTOCOL_VERSION = 47;
|
||||||
|
|
||||||
// See also formspec [Version History] in doc/lua_api.md
|
// See also formspec [Version History] in doc/lua_api.md
|
||||||
const u16 FORMSPEC_API_VERSION = 8;
|
const u16 FORMSPEC_API_VERSION = 8;
|
||||||
|
@ -190,8 +190,10 @@ void ServerParticleTexture::serialize(std::ostream &os, u16 protocol_ver,
|
|||||||
FlagT flags = 0;
|
FlagT flags = 0;
|
||||||
if (animated)
|
if (animated)
|
||||||
flags |= FlagT(ParticleTextureFlags::animated);
|
flags |= FlagT(ParticleTextureFlags::animated);
|
||||||
if (blendmode != BlendMode::alpha)
|
// Default to `blend = "alpha"` for older clients that don't support `blend = "clip"`
|
||||||
flags |= FlagT(blendmode) << 1;
|
auto sent_blendmode = (protocol_ver < 47 && blendmode == BlendMode::clip)
|
||||||
|
? BlendMode::alpha : blendmode;
|
||||||
|
flags |= FlagT(sent_blendmode) << 1;
|
||||||
serializeParameterValue(os, flags);
|
serializeParameterValue(os, flags);
|
||||||
|
|
||||||
alpha.serialize(os);
|
alpha.serialize(os);
|
||||||
@ -215,6 +217,8 @@ void ServerParticleTexture::deSerialize(std::istream &is, u16 protocol_ver,
|
|||||||
|
|
||||||
animated = !!(flags & FlagT(ParticleTextureFlags::animated));
|
animated = !!(flags & FlagT(ParticleTextureFlags::animated));
|
||||||
blendmode = BlendMode((flags & FlagT(ParticleTextureFlags::blend)) >> 1);
|
blendmode = BlendMode((flags & FlagT(ParticleTextureFlags::blend)) >> 1);
|
||||||
|
if (blendmode >= BlendMode::BlendMode_END)
|
||||||
|
throw SerializationError("invalid blend mode");
|
||||||
|
|
||||||
alpha.deSerialize(is);
|
alpha.deSerialize(is);
|
||||||
scale.deSerialize(is);
|
scale.deSerialize(is);
|
||||||
|
@ -233,7 +233,8 @@ namespace ParticleParamTypes
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum class AttractorKind : u8 { none, point, line, plane };
|
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
|
// these are consistently-named convenience aliases to make code more readable without `using ParticleParamTypes` declarations
|
||||||
using v3fRange = RangedParameter<v3fParameter>;
|
using v3fRange = RangedParameter<v3fParameter>;
|
||||||
|
@ -118,13 +118,14 @@ namespace LuaParticleParams
|
|||||||
{(int)BlendMode::add, "add"},
|
{(int)BlendMode::add, "add"},
|
||||||
{(int)BlendMode::sub, "sub"},
|
{(int)BlendMode::sub, "sub"},
|
||||||
{(int)BlendMode::screen, "screen"},
|
{(int)BlendMode::screen, "screen"},
|
||||||
|
{(int)BlendMode::clip, "clip"},
|
||||||
{0, nullptr},
|
{0, nullptr},
|
||||||
};
|
};
|
||||||
|
|
||||||
luaL_checktype(L, -1, LUA_TSTRING);
|
luaL_checktype(L, -1, LUA_TSTRING);
|
||||||
int v = (int)BlendMode::alpha;
|
int v = (int)BlendMode::alpha;
|
||||||
if (!string_to_enum(opts, v, lua_tostring(L, -1))) {
|
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;
|
ret = (BlendMode)v;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user