Rework use_texture_alpha to provide three opaque/clip/blend modes
The change that turns nodeboxes and meshes opaque when possible is kept, as is the compatibility code that warns modders to adjust their nodedefs.master
parent
edd8c3c664
commit
83229921e5
|
@ -18,6 +18,7 @@ core.features = {
|
|||
pathfinder_works = true,
|
||||
object_step_has_moveresult = true,
|
||||
direct_velocity_on_players = true,
|
||||
use_texture_alpha_string_modes = true,
|
||||
}
|
||||
|
||||
function core.has_feature(arg)
|
||||
|
|
|
@ -4386,6 +4386,8 @@ Utilities
|
|||
object_step_has_moveresult = true,
|
||||
-- Whether get_velocity() and add_velocity() can be used on players (5.4.0)
|
||||
direct_velocity_on_players = true,
|
||||
-- nodedef's use_texture_alpha accepts new string modes (5.4.0)
|
||||
use_texture_alpha_string_modes = true,
|
||||
}
|
||||
|
||||
* `minetest.has_feature(arg)`: returns `boolean, missing_features`
|
||||
|
@ -7340,10 +7342,18 @@ Used by `minetest.register_node`.
|
|||
-- If the node has a palette, then this setting only has an effect in
|
||||
-- the inventory and on the wield item.
|
||||
|
||||
use_texture_alpha = false,
|
||||
-- Use texture's alpha channel
|
||||
-- If this is set to false, the node will be rendered fully opaque
|
||||
-- regardless of any texture transparency.
|
||||
use_texture_alpha = ...,
|
||||
-- Specifies how the texture's alpha channel will be used for rendering.
|
||||
-- possible values:
|
||||
-- * "opaque": Node is rendered opaque regardless of alpha channel
|
||||
-- * "clip": A given pixel is either fully see-through or opaque
|
||||
-- depending on the alpha channel being below/above 50% in value
|
||||
-- * "blend": The alpha channel specifies how transparent a given pixel
|
||||
-- of the rendered node is
|
||||
-- The default is "opaque" for drawtypes normal, liquid and flowingliquid;
|
||||
-- "clip" otherwise.
|
||||
-- If set to a boolean value (deprecated): true either sets it to blend
|
||||
-- or clip, false sets it to clip or opaque mode depending on the drawtype.
|
||||
|
||||
palette = "palette.png",
|
||||
-- The node's `param2` is used to select a pixel from the image.
|
||||
|
|
110
src/nodedef.cpp
110
src/nodedef.cpp
|
@ -360,7 +360,7 @@ void ContentFeatures::reset()
|
|||
i = TileDef();
|
||||
for (auto &j : tiledef_special)
|
||||
j = TileDef();
|
||||
alpha = 255;
|
||||
alpha = ALPHAMODE_OPAQUE;
|
||||
post_effect_color = video::SColor(0, 0, 0, 0);
|
||||
param_type = CPT_NONE;
|
||||
param_type_2 = CPT2_NONE;
|
||||
|
@ -405,6 +405,31 @@ void ContentFeatures::reset()
|
|||
node_dig_prediction = "air";
|
||||
}
|
||||
|
||||
void ContentFeatures::setAlphaFromLegacy(u8 legacy_alpha)
|
||||
{
|
||||
// No special handling for nodebox/mesh here as it doesn't make sense to
|
||||
// throw warnings when the server is too old to support the "correct" way
|
||||
switch (drawtype) {
|
||||
case NDT_NORMAL:
|
||||
alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_CLIP;
|
||||
break;
|
||||
case NDT_LIQUID:
|
||||
case NDT_FLOWINGLIQUID:
|
||||
alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_BLEND;
|
||||
break;
|
||||
default:
|
||||
alpha = legacy_alpha == 255 ? ALPHAMODE_CLIP : ALPHAMODE_BLEND;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
u8 ContentFeatures::getAlphaForLegacy() const
|
||||
{
|
||||
// This is so simple only because 255 and 0 mean wildly different things
|
||||
// depending on drawtype...
|
||||
return alpha == ALPHAMODE_OPAQUE ? 255 : 0;
|
||||
}
|
||||
|
||||
void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
|
||||
{
|
||||
const u8 version = CONTENTFEATURES_VERSION;
|
||||
|
@ -433,7 +458,7 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
|
|||
for (const TileDef &td : tiledef_special) {
|
||||
td.serialize(os, protocol_version);
|
||||
}
|
||||
writeU8(os, alpha);
|
||||
writeU8(os, getAlphaForLegacy());
|
||||
writeU8(os, color.getRed());
|
||||
writeU8(os, color.getGreen());
|
||||
writeU8(os, color.getBlue());
|
||||
|
@ -489,6 +514,7 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
|
|||
|
||||
os << serializeString16(node_dig_prediction);
|
||||
writeU8(os, leveled_max);
|
||||
writeU8(os, alpha);
|
||||
}
|
||||
|
||||
void ContentFeatures::deSerialize(std::istream &is)
|
||||
|
@ -524,7 +550,7 @@ void ContentFeatures::deSerialize(std::istream &is)
|
|||
throw SerializationError("unsupported CF_SPECIAL_COUNT");
|
||||
for (TileDef &td : tiledef_special)
|
||||
td.deSerialize(is, version, drawtype);
|
||||
alpha = readU8(is);
|
||||
setAlphaFromLegacy(readU8(is));
|
||||
color.setRed(readU8(is));
|
||||
color.setGreen(readU8(is));
|
||||
color.setBlue(readU8(is));
|
||||
|
@ -582,10 +608,16 @@ void ContentFeatures::deSerialize(std::istream &is)
|
|||
|
||||
try {
|
||||
node_dig_prediction = deSerializeString16(is);
|
||||
u8 tmp_leveled_max = readU8(is);
|
||||
|
||||
u8 tmp = readU8(is);
|
||||
if (is.eof()) /* readU8 doesn't throw exceptions so we have to do this */
|
||||
throw SerializationError("");
|
||||
leveled_max = tmp_leveled_max;
|
||||
leveled_max = tmp;
|
||||
|
||||
tmp = readU8(is);
|
||||
if (is.eof())
|
||||
throw SerializationError("");
|
||||
alpha = static_cast<enum AlphaMode>(tmp);
|
||||
} catch(SerializationError &e) {};
|
||||
}
|
||||
|
||||
|
@ -677,6 +709,7 @@ bool ContentFeatures::textureAlphaCheck(ITextureSource *tsrc, const TileDef *til
|
|||
video::IVideoDriver *driver = RenderingEngine::get_video_driver();
|
||||
static thread_local bool long_warning_printed = false;
|
||||
std::set<std::string> seen;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (seen.find(tiles[i].name) != seen.end())
|
||||
continue;
|
||||
|
@ -701,20 +734,21 @@ bool ContentFeatures::textureAlphaCheck(ITextureSource *tsrc, const TileDef *til
|
|||
|
||||
break_loop:
|
||||
image->drop();
|
||||
if (!ok) {
|
||||
warningstream << "Texture \"" << tiles[i].name << "\" of "
|
||||
<< name << " has transparent pixels, assuming "
|
||||
"use_texture_alpha = true." << std::endl;
|
||||
if (!long_warning_printed) {
|
||||
warningstream << " This warning can be a false-positive if "
|
||||
"unused pixels in the texture are transparent. However if "
|
||||
"it is meant to be transparent, you *MUST* update the "
|
||||
"nodedef and set use_texture_alpha = true! This compatibility "
|
||||
"code will be removed in a few releases." << std::endl;
|
||||
long_warning_printed = true;
|
||||
}
|
||||
return true;
|
||||
if (ok)
|
||||
continue;
|
||||
warningstream << "Texture \"" << tiles[i].name << "\" of "
|
||||
<< name << " has transparency, assuming "
|
||||
"use_texture_alpha = \"clip\"." << std::endl;
|
||||
if (!long_warning_printed) {
|
||||
warningstream << " This warning can be a false-positive if "
|
||||
"unused pixels in the texture are transparent. However if "
|
||||
"it is meant to be transparent, you *MUST* update the "
|
||||
"nodedef and set use_texture_alpha = \"clip\"! This "
|
||||
"compatibility code will be removed in a few releases."
|
||||
<< std::endl;
|
||||
long_warning_printed = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -759,14 +793,18 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
|||
|
||||
bool is_liquid = false;
|
||||
|
||||
MaterialType material_type = (alpha == 255) ?
|
||||
TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
|
||||
if (alpha == ALPHAMODE_LEGACY_COMPAT) {
|
||||
// Before working with the alpha mode, resolve any legacy kludges
|
||||
alpha = textureAlphaCheck(tsrc, tdef, 6) ? ALPHAMODE_CLIP : ALPHAMODE_OPAQUE;
|
||||
}
|
||||
|
||||
MaterialType material_type = alpha == ALPHAMODE_OPAQUE ?
|
||||
TILE_MATERIAL_OPAQUE : (alpha == ALPHAMODE_CLIP ? TILE_MATERIAL_BASIC :
|
||||
TILE_MATERIAL_ALPHA);
|
||||
|
||||
switch (drawtype) {
|
||||
default:
|
||||
case NDT_NORMAL:
|
||||
material_type = (alpha == 255) ?
|
||||
TILE_MATERIAL_OPAQUE : TILE_MATERIAL_ALPHA;
|
||||
solidness = 2;
|
||||
break;
|
||||
case NDT_AIRLIKE:
|
||||
|
@ -774,14 +812,14 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
|||
break;
|
||||
case NDT_LIQUID:
|
||||
if (tsettings.opaque_water)
|
||||
alpha = 255;
|
||||
alpha = ALPHAMODE_OPAQUE;
|
||||
solidness = 1;
|
||||
is_liquid = true;
|
||||
break;
|
||||
case NDT_FLOWINGLIQUID:
|
||||
solidness = 0;
|
||||
if (tsettings.opaque_water)
|
||||
alpha = 255;
|
||||
alpha = ALPHAMODE_OPAQUE;
|
||||
is_liquid = true;
|
||||
break;
|
||||
case NDT_GLASSLIKE:
|
||||
|
@ -833,19 +871,16 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
|||
break;
|
||||
case NDT_MESH:
|
||||
case NDT_NODEBOX:
|
||||
if (alpha == 255 && textureAlphaCheck(tsrc, tdef, 6))
|
||||
alpha = 0;
|
||||
|
||||
solidness = 0;
|
||||
if (waving == 1)
|
||||
if (waving == 1) {
|
||||
material_type = TILE_MATERIAL_WAVING_PLANTS;
|
||||
else if (waving == 2)
|
||||
} else if (waving == 2) {
|
||||
material_type = TILE_MATERIAL_WAVING_LEAVES;
|
||||
else if (waving == 3)
|
||||
material_type = (alpha == 255) ? TILE_MATERIAL_WAVING_LIQUID_OPAQUE :
|
||||
TILE_MATERIAL_WAVING_LIQUID_BASIC;
|
||||
else if (alpha == 255)
|
||||
material_type = TILE_MATERIAL_OPAQUE;
|
||||
} else if (waving == 3) {
|
||||
material_type = alpha == ALPHAMODE_OPAQUE ?
|
||||
TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
|
||||
TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
|
||||
}
|
||||
break;
|
||||
case NDT_TORCHLIKE:
|
||||
case NDT_SIGNLIKE:
|
||||
|
@ -860,10 +895,11 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
|||
|
||||
if (is_liquid) {
|
||||
if (waving == 3) {
|
||||
material_type = (alpha == 255) ? TILE_MATERIAL_WAVING_LIQUID_OPAQUE :
|
||||
TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT;
|
||||
material_type = alpha == ALPHAMODE_OPAQUE ?
|
||||
TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
|
||||
TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
|
||||
} else {
|
||||
material_type = (alpha == 255) ? TILE_MATERIAL_LIQUID_OPAQUE :
|
||||
material_type = alpha == ALPHAMODE_OPAQUE ? TILE_MATERIAL_LIQUID_OPAQUE :
|
||||
TILE_MATERIAL_LIQUID_TRANSPARENT;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -231,6 +231,14 @@ enum AlignStyle : u8 {
|
|||
ALIGN_STYLE_USER_DEFINED,
|
||||
};
|
||||
|
||||
enum AlphaMode : u8 {
|
||||
ALPHAMODE_BLEND,
|
||||
ALPHAMODE_CLIP,
|
||||
ALPHAMODE_OPAQUE,
|
||||
ALPHAMODE_LEGACY_COMPAT, /* means either opaque or clip */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Stand-alone definition of a TileSpec (basically a server-side TileSpec)
|
||||
*/
|
||||
|
@ -315,9 +323,7 @@ struct ContentFeatures
|
|||
// These will be drawn over the base tiles.
|
||||
TileDef tiledef_overlay[6];
|
||||
TileDef tiledef_special[CF_SPECIAL_COUNT]; // eg. flowing liquid
|
||||
// If 255, the node is opaque.
|
||||
// Otherwise it uses texture alpha.
|
||||
u8 alpha;
|
||||
AlphaMode alpha;
|
||||
// The color of the node.
|
||||
video::SColor color;
|
||||
std::string palette_name;
|
||||
|
@ -418,20 +424,27 @@ struct ContentFeatures
|
|||
void serialize(std::ostream &os, u16 protocol_version) const;
|
||||
void deSerialize(std::istream &is);
|
||||
|
||||
#ifndef SERVER
|
||||
/*
|
||||
* Checks if any tile texture has any transparent pixels.
|
||||
* Prints a warning and returns true if that is the case, false otherwise.
|
||||
* This is supposed to be used for use_texture_alpha backwards compatibility.
|
||||
*/
|
||||
bool textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles,
|
||||
int length);
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
Some handy methods
|
||||
*/
|
||||
void setDefaultAlphaMode()
|
||||
{
|
||||
switch (drawtype) {
|
||||
case NDT_NORMAL:
|
||||
case NDT_LIQUID:
|
||||
case NDT_FLOWINGLIQUID:
|
||||
alpha = ALPHAMODE_OPAQUE;
|
||||
break;
|
||||
case NDT_NODEBOX:
|
||||
case NDT_MESH:
|
||||
alpha = ALPHAMODE_LEGACY_COMPAT; // this should eventually be OPAQUE
|
||||
break;
|
||||
default:
|
||||
alpha = ALPHAMODE_CLIP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool needsBackfaceCulling() const
|
||||
{
|
||||
switch (drawtype) {
|
||||
|
@ -465,6 +478,21 @@ struct ContentFeatures
|
|||
void updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
|
||||
scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings);
|
||||
#endif
|
||||
|
||||
private:
|
||||
#ifndef SERVER
|
||||
/*
|
||||
* Checks if any tile texture has any transparent pixels.
|
||||
* Prints a warning and returns true if that is the case, false otherwise.
|
||||
* This is supposed to be used for use_texture_alpha backwards compatibility.
|
||||
*/
|
||||
bool textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles,
|
||||
int length);
|
||||
#endif
|
||||
|
||||
void setAlphaFromLegacy(u8 legacy_alpha);
|
||||
|
||||
u8 getAlphaForLegacy() const;
|
||||
};
|
||||
|
||||
/*!
|
||||
|
|
|
@ -618,25 +618,39 @@ void read_content_features(lua_State *L, ContentFeatures &f, int index)
|
|||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
/* alpha & use_texture_alpha */
|
||||
// This is a bit complicated due to compatibility
|
||||
|
||||
f.setDefaultAlphaMode();
|
||||
|
||||
warn_if_field_exists(L, index, "alpha",
|
||||
"Obsolete, only limited compatibility provided");
|
||||
"Obsolete, only limited compatibility provided; "
|
||||
"replaced by \"use_texture_alpha\"");
|
||||
if (getintfield_default(L, index, "alpha", 255) != 255)
|
||||
f.alpha = 0;
|
||||
f.alpha = ALPHAMODE_BLEND;
|
||||
|
||||
bool usealpha = getboolfield_default(L, index,
|
||||
"use_texture_alpha", false);
|
||||
if (usealpha)
|
||||
f.alpha = 0;
|
||||
lua_getfield(L, index, "use_texture_alpha");
|
||||
if (lua_isboolean(L, -1)) {
|
||||
warn_if_field_exists(L, index, "use_texture_alpha",
|
||||
"Boolean values are deprecated; use the new choices");
|
||||
if (lua_toboolean(L, -1))
|
||||
f.alpha = (f.drawtype == NDT_NORMAL) ? ALPHAMODE_CLIP : ALPHAMODE_BLEND;
|
||||
} else if (check_field_or_nil(L, -1, LUA_TSTRING, "use_texture_alpha")) {
|
||||
int result = f.alpha;
|
||||
string_to_enum(ScriptApiNode::es_TextureAlphaMode, result,
|
||||
std::string(lua_tostring(L, -1)));
|
||||
f.alpha = static_cast<enum AlphaMode>(result);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
/* Other stuff */
|
||||
|
||||
// Read node color.
|
||||
lua_getfield(L, index, "color");
|
||||
read_color(L, -1, &f.color);
|
||||
lua_pop(L, 1);
|
||||
|
||||
getstringfield(L, index, "palette", f.palette_name);
|
||||
|
||||
/* Other stuff */
|
||||
|
||||
lua_getfield(L, index, "post_effect_color");
|
||||
read_color(L, -1, &f.post_effect_color);
|
||||
lua_pop(L, 1);
|
||||
|
|
|
@ -93,6 +93,14 @@ struct EnumString ScriptApiNode::es_NodeBoxType[] =
|
|||
{0, NULL},
|
||||
};
|
||||
|
||||
struct EnumString ScriptApiNode::es_TextureAlphaMode[] =
|
||||
{
|
||||
{ALPHAMODE_OPAQUE, "opaque"},
|
||||
{ALPHAMODE_CLIP, "clip"},
|
||||
{ALPHAMODE_BLEND, "blend"},
|
||||
{0, NULL},
|
||||
};
|
||||
|
||||
bool ScriptApiNode::node_on_punch(v3s16 p, MapNode node,
|
||||
ServerActiveObject *puncher, const PointedThing &pointed)
|
||||
{
|
||||
|
|
|
@ -54,4 +54,5 @@ public:
|
|||
static struct EnumString es_ContentParamType2[];
|
||||
static struct EnumString es_LiquidType[];
|
||||
static struct EnumString es_NodeBoxType[];
|
||||
static struct EnumString es_TextureAlphaMode[];
|
||||
};
|
||||
|
|
|
@ -180,7 +180,7 @@ void TestGameDef::defineSomeNodes()
|
|||
"{default_water.png";
|
||||
f = ContentFeatures();
|
||||
f.name = itemdef.name;
|
||||
f.alpha = 128;
|
||||
f.alpha = ALPHAMODE_BLEND;
|
||||
f.liquid_type = LIQUID_SOURCE;
|
||||
f.liquid_viscosity = 4;
|
||||
f.is_ground_content = true;
|
||||
|
@ -201,7 +201,7 @@ void TestGameDef::defineSomeNodes()
|
|||
"{default_lava.png";
|
||||
f = ContentFeatures();
|
||||
f.name = itemdef.name;
|
||||
f.alpha = 128;
|
||||
f.alpha = ALPHAMODE_OPAQUE;
|
||||
f.liquid_type = LIQUID_SOURCE;
|
||||
f.liquid_viscosity = 7;
|
||||
f.light_source = LIGHT_MAX-1;
|
||||
|
|
Loading…
Reference in New Issue