Skip to content

Commit 8322992

Browse files
committedJan 29, 2021
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.
1 parent edd8c3c commit 8322992

File tree

8 files changed

+164
-66
lines changed

8 files changed

+164
-66
lines changed
 

Diff for: ‎builtin/game/features.lua

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ core.features = {
1818
pathfinder_works = true,
1919
object_step_has_moveresult = true,
2020
direct_velocity_on_players = true,
21+
use_texture_alpha_string_modes = true,
2122
}
2223

2324
function core.has_feature(arg)

Diff for: ‎doc/lua_api.txt

+14-4
Original file line numberDiff line numberDiff line change
@@ -4386,6 +4386,8 @@ Utilities
43864386
object_step_has_moveresult = true,
43874387
-- Whether get_velocity() and add_velocity() can be used on players (5.4.0)
43884388
direct_velocity_on_players = true,
4389+
-- nodedef's use_texture_alpha accepts new string modes (5.4.0)
4390+
use_texture_alpha_string_modes = true,
43894391
}
43904392

43914393
* `minetest.has_feature(arg)`: returns `boolean, missing_features`
@@ -7340,10 +7342,18 @@ Used by `minetest.register_node`.
73407342
-- If the node has a palette, then this setting only has an effect in
73417343
-- the inventory and on the wield item.
73427344

7343-
use_texture_alpha = false,
7344-
-- Use texture's alpha channel
7345-
-- If this is set to false, the node will be rendered fully opaque
7346-
-- regardless of any texture transparency.
7345+
use_texture_alpha = ...,
7346+
-- Specifies how the texture's alpha channel will be used for rendering.
7347+
-- possible values:
7348+
-- * "opaque": Node is rendered opaque regardless of alpha channel
7349+
-- * "clip": A given pixel is either fully see-through or opaque
7350+
-- depending on the alpha channel being below/above 50% in value
7351+
-- * "blend": The alpha channel specifies how transparent a given pixel
7352+
-- of the rendered node is
7353+
-- The default is "opaque" for drawtypes normal, liquid and flowingliquid;
7354+
-- "clip" otherwise.
7355+
-- If set to a boolean value (deprecated): true either sets it to blend
7356+
-- or clip, false sets it to clip or opaque mode depending on the drawtype.
73477357

73487358
palette = "palette.png",
73497359
-- The node's `param2` is used to select a pixel from the image.

Diff for: ‎src/nodedef.cpp

+73-37
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ void ContentFeatures::reset()
360360
i = TileDef();
361361
for (auto &j : tiledef_special)
362362
j = TileDef();
363-
alpha = 255;
363+
alpha = ALPHAMODE_OPAQUE;
364364
post_effect_color = video::SColor(0, 0, 0, 0);
365365
param_type = CPT_NONE;
366366
param_type_2 = CPT2_NONE;
@@ -405,6 +405,31 @@ void ContentFeatures::reset()
405405
node_dig_prediction = "air";
406406
}
407407

408+
void ContentFeatures::setAlphaFromLegacy(u8 legacy_alpha)
409+
{
410+
// No special handling for nodebox/mesh here as it doesn't make sense to
411+
// throw warnings when the server is too old to support the "correct" way
412+
switch (drawtype) {
413+
case NDT_NORMAL:
414+
alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_CLIP;
415+
break;
416+
case NDT_LIQUID:
417+
case NDT_FLOWINGLIQUID:
418+
alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_BLEND;
419+
break;
420+
default:
421+
alpha = legacy_alpha == 255 ? ALPHAMODE_CLIP : ALPHAMODE_BLEND;
422+
break;
423+
}
424+
}
425+
426+
u8 ContentFeatures::getAlphaForLegacy() const
427+
{
428+
// This is so simple only because 255 and 0 mean wildly different things
429+
// depending on drawtype...
430+
return alpha == ALPHAMODE_OPAQUE ? 255 : 0;
431+
}
432+
408433
void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
409434
{
410435
const u8 version = CONTENTFEATURES_VERSION;
@@ -433,7 +458,7 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
433458
for (const TileDef &td : tiledef_special) {
434459
td.serialize(os, protocol_version);
435460
}
436-
writeU8(os, alpha);
461+
writeU8(os, getAlphaForLegacy());
437462
writeU8(os, color.getRed());
438463
writeU8(os, color.getGreen());
439464
writeU8(os, color.getBlue());
@@ -489,6 +514,7 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
489514

490515
os << serializeString16(node_dig_prediction);
491516
writeU8(os, leveled_max);
517+
writeU8(os, alpha);
492518
}
493519

494520
void ContentFeatures::deSerialize(std::istream &is)
@@ -524,7 +550,7 @@ void ContentFeatures::deSerialize(std::istream &is)
524550
throw SerializationError("unsupported CF_SPECIAL_COUNT");
525551
for (TileDef &td : tiledef_special)
526552
td.deSerialize(is, version, drawtype);
527-
alpha = readU8(is);
553+
setAlphaFromLegacy(readU8(is));
528554
color.setRed(readU8(is));
529555
color.setGreen(readU8(is));
530556
color.setBlue(readU8(is));
@@ -582,10 +608,16 @@ void ContentFeatures::deSerialize(std::istream &is)
582608

583609
try {
584610
node_dig_prediction = deSerializeString16(is);
585-
u8 tmp_leveled_max = readU8(is);
611+
612+
u8 tmp = readU8(is);
586613
if (is.eof()) /* readU8 doesn't throw exceptions so we have to do this */
587614
throw SerializationError("");
588-
leveled_max = tmp_leveled_max;
615+
leveled_max = tmp;
616+
617+
tmp = readU8(is);
618+
if (is.eof())
619+
throw SerializationError("");
620+
alpha = static_cast<enum AlphaMode>(tmp);
589621
} catch(SerializationError &e) {};
590622
}
591623

@@ -677,6 +709,7 @@ bool ContentFeatures::textureAlphaCheck(ITextureSource *tsrc, const TileDef *til
677709
video::IVideoDriver *driver = RenderingEngine::get_video_driver();
678710
static thread_local bool long_warning_printed = false;
679711
std::set<std::string> seen;
712+
680713
for (int i = 0; i < length; i++) {
681714
if (seen.find(tiles[i].name) != seen.end())
682715
continue;
@@ -701,20 +734,21 @@ bool ContentFeatures::textureAlphaCheck(ITextureSource *tsrc, const TileDef *til
701734

702735
break_loop:
703736
image->drop();
704-
if (!ok) {
705-
warningstream << "Texture \"" << tiles[i].name << "\" of "
706-
<< name << " has transparent pixels, assuming "
707-
"use_texture_alpha = true." << std::endl;
708-
if (!long_warning_printed) {
709-
warningstream << " This warning can be a false-positive if "
710-
"unused pixels in the texture are transparent. However if "
711-
"it is meant to be transparent, you *MUST* update the "
712-
"nodedef and set use_texture_alpha = true! This compatibility "
713-
"code will be removed in a few releases." << std::endl;
714-
long_warning_printed = true;
715-
}
716-
return true;
737+
if (ok)
738+
continue;
739+
warningstream << "Texture \"" << tiles[i].name << "\" of "
740+
<< name << " has transparency, assuming "
741+
"use_texture_alpha = \"clip\"." << std::endl;
742+
if (!long_warning_printed) {
743+
warningstream << " This warning can be a false-positive if "
744+
"unused pixels in the texture are transparent. However if "
745+
"it is meant to be transparent, you *MUST* update the "
746+
"nodedef and set use_texture_alpha = \"clip\"! This "
747+
"compatibility code will be removed in a few releases."
748+
<< std::endl;
749+
long_warning_printed = true;
717750
}
751+
return true;
718752
}
719753
return false;
720754
}
@@ -759,29 +793,33 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
759793

760794
bool is_liquid = false;
761795

762-
MaterialType material_type = (alpha == 255) ?
763-
TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
796+
if (alpha == ALPHAMODE_LEGACY_COMPAT) {
797+
// Before working with the alpha mode, resolve any legacy kludges
798+
alpha = textureAlphaCheck(tsrc, tdef, 6) ? ALPHAMODE_CLIP : ALPHAMODE_OPAQUE;
799+
}
800+
801+
MaterialType material_type = alpha == ALPHAMODE_OPAQUE ?
802+
TILE_MATERIAL_OPAQUE : (alpha == ALPHAMODE_CLIP ? TILE_MATERIAL_BASIC :
803+
TILE_MATERIAL_ALPHA);
764804

765805
switch (drawtype) {
766806
default:
767807
case NDT_NORMAL:
768-
material_type = (alpha == 255) ?
769-
TILE_MATERIAL_OPAQUE : TILE_MATERIAL_ALPHA;
770808
solidness = 2;
771809
break;
772810
case NDT_AIRLIKE:
773811
solidness = 0;
774812
break;
775813
case NDT_LIQUID:
776814
if (tsettings.opaque_water)
777-
alpha = 255;
815+
alpha = ALPHAMODE_OPAQUE;
778816
solidness = 1;
779817
is_liquid = true;
780818
break;
781819
case NDT_FLOWINGLIQUID:
782820
solidness = 0;
783821
if (tsettings.opaque_water)
784-
alpha = 255;
822+
alpha = ALPHAMODE_OPAQUE;
785823
is_liquid = true;
786824
break;
787825
case NDT_GLASSLIKE:
@@ -833,19 +871,16 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
833871
break;
834872
case NDT_MESH:
835873
case NDT_NODEBOX:
836-
if (alpha == 255 && textureAlphaCheck(tsrc, tdef, 6))
837-
alpha = 0;
838-
839874
solidness = 0;
840-
if (waving == 1)
875+
if (waving == 1) {
841876
material_type = TILE_MATERIAL_WAVING_PLANTS;
842-
else if (waving == 2)
877+
} else if (waving == 2) {
843878
material_type = TILE_MATERIAL_WAVING_LEAVES;
844-
else if (waving == 3)
845-
material_type = (alpha == 255) ? TILE_MATERIAL_WAVING_LIQUID_OPAQUE :
846-
TILE_MATERIAL_WAVING_LIQUID_BASIC;
847-
else if (alpha == 255)
848-
material_type = TILE_MATERIAL_OPAQUE;
879+
} else if (waving == 3) {
880+
material_type = alpha == ALPHAMODE_OPAQUE ?
881+
TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
882+
TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
883+
}
849884
break;
850885
case NDT_TORCHLIKE:
851886
case NDT_SIGNLIKE:
@@ -860,10 +895,11 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
860895

861896
if (is_liquid) {
862897
if (waving == 3) {
863-
material_type = (alpha == 255) ? TILE_MATERIAL_WAVING_LIQUID_OPAQUE :
864-
TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT;
898+
material_type = alpha == ALPHAMODE_OPAQUE ?
899+
TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
900+
TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
865901
} else {
866-
material_type = (alpha == 255) ? TILE_MATERIAL_LIQUID_OPAQUE :
902+
material_type = alpha == ALPHAMODE_OPAQUE ? TILE_MATERIAL_LIQUID_OPAQUE :
867903
TILE_MATERIAL_LIQUID_TRANSPARENT;
868904
}
869905
}

Diff for: ‎src/nodedef.h

+42-14
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,14 @@ enum AlignStyle : u8 {
231231
ALIGN_STYLE_USER_DEFINED,
232232
};
233233

234+
enum AlphaMode : u8 {
235+
ALPHAMODE_BLEND,
236+
ALPHAMODE_CLIP,
237+
ALPHAMODE_OPAQUE,
238+
ALPHAMODE_LEGACY_COMPAT, /* means either opaque or clip */
239+
};
240+
241+
234242
/*
235243
Stand-alone definition of a TileSpec (basically a server-side TileSpec)
236244
*/
@@ -315,9 +323,7 @@ struct ContentFeatures
315323
// These will be drawn over the base tiles.
316324
TileDef tiledef_overlay[6];
317325
TileDef tiledef_special[CF_SPECIAL_COUNT]; // eg. flowing liquid
318-
// If 255, the node is opaque.
319-
// Otherwise it uses texture alpha.
320-
u8 alpha;
326+
AlphaMode alpha;
321327
// The color of the node.
322328
video::SColor color;
323329
std::string palette_name;
@@ -418,20 +424,27 @@ struct ContentFeatures
418424
void serialize(std::ostream &os, u16 protocol_version) const;
419425
void deSerialize(std::istream &is);
420426

421-
#ifndef SERVER
422-
/*
423-
* Checks if any tile texture has any transparent pixels.
424-
* Prints a warning and returns true if that is the case, false otherwise.
425-
* This is supposed to be used for use_texture_alpha backwards compatibility.
426-
*/
427-
bool textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles,
428-
int length);
429-
#endif
430-
431-
432427
/*
433428
Some handy methods
434429
*/
430+
void setDefaultAlphaMode()
431+
{
432+
switch (drawtype) {
433+
case NDT_NORMAL:
434+
case NDT_LIQUID:
435+
case NDT_FLOWINGLIQUID:
436+
alpha = ALPHAMODE_OPAQUE;
437+
break;
438+
case NDT_NODEBOX:
439+
case NDT_MESH:
440+
alpha = ALPHAMODE_LEGACY_COMPAT; // this should eventually be OPAQUE
441+
break;
442+
default:
443+
alpha = ALPHAMODE_CLIP;
444+
break;
445+
}
446+
}
447+
435448
bool needsBackfaceCulling() const
436449
{
437450
switch (drawtype) {
@@ -465,6 +478,21 @@ struct ContentFeatures
465478
void updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
466479
scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings);
467480
#endif
481+
482+
private:
483+
#ifndef SERVER
484+
/*
485+
* Checks if any tile texture has any transparent pixels.
486+
* Prints a warning and returns true if that is the case, false otherwise.
487+
* This is supposed to be used for use_texture_alpha backwards compatibility.
488+
*/
489+
bool textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles,
490+
int length);
491+
#endif
492+
493+
void setAlphaFromLegacy(u8 legacy_alpha);
494+
495+
u8 getAlphaForLegacy() const;
468496
};
469497

470498
/*!

Diff for: ‎src/script/common/c_content.cpp

+23-9
Original file line numberDiff line numberDiff line change
@@ -618,25 +618,39 @@ void read_content_features(lua_State *L, ContentFeatures &f, int index)
618618
}
619619
lua_pop(L, 1);
620620

621+
/* alpha & use_texture_alpha */
622+
// This is a bit complicated due to compatibility
623+
624+
f.setDefaultAlphaMode();
625+
621626
warn_if_field_exists(L, index, "alpha",
622-
"Obsolete, only limited compatibility provided");
627+
"Obsolete, only limited compatibility provided; "
628+
"replaced by \"use_texture_alpha\"");
623629
if (getintfield_default(L, index, "alpha", 255) != 255)
624-
f.alpha = 0;
630+
f.alpha = ALPHAMODE_BLEND;
631+
632+
lua_getfield(L, index, "use_texture_alpha");
633+
if (lua_isboolean(L, -1)) {
634+
warn_if_field_exists(L, index, "use_texture_alpha",
635+
"Boolean values are deprecated; use the new choices");
636+
if (lua_toboolean(L, -1))
637+
f.alpha = (f.drawtype == NDT_NORMAL) ? ALPHAMODE_CLIP : ALPHAMODE_BLEND;
638+
} else if (check_field_or_nil(L, -1, LUA_TSTRING, "use_texture_alpha")) {
639+
int result = f.alpha;
640+
string_to_enum(ScriptApiNode::es_TextureAlphaMode, result,
641+
std::string(lua_tostring(L, -1)));
642+
f.alpha = static_cast<enum AlphaMode>(result);
643+
}
644+
lua_pop(L, 1);
625645

626-
bool usealpha = getboolfield_default(L, index,
627-
"use_texture_alpha", false);
628-
if (usealpha)
629-
f.alpha = 0;
646+
/* Other stuff */
630647

631-
// Read node color.
632648
lua_getfield(L, index, "color");
633649
read_color(L, -1, &f.color);
634650
lua_pop(L, 1);
635651

636652
getstringfield(L, index, "palette", f.palette_name);
637653

638-
/* Other stuff */
639-
640654
lua_getfield(L, index, "post_effect_color");
641655
read_color(L, -1, &f.post_effect_color);
642656
lua_pop(L, 1);

Diff for: ‎src/script/cpp_api/s_node.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,14 @@ struct EnumString ScriptApiNode::es_NodeBoxType[] =
9393
{0, NULL},
9494
};
9595

96+
struct EnumString ScriptApiNode::es_TextureAlphaMode[] =
97+
{
98+
{ALPHAMODE_OPAQUE, "opaque"},
99+
{ALPHAMODE_CLIP, "clip"},
100+
{ALPHAMODE_BLEND, "blend"},
101+
{0, NULL},
102+
};
103+
96104
bool ScriptApiNode::node_on_punch(v3s16 p, MapNode node,
97105
ServerActiveObject *puncher, const PointedThing &pointed)
98106
{

Diff for: ‎src/script/cpp_api/s_node.h

+1
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,5 @@ class ScriptApiNode
5454
static struct EnumString es_ContentParamType2[];
5555
static struct EnumString es_LiquidType[];
5656
static struct EnumString es_NodeBoxType[];
57+
static struct EnumString es_TextureAlphaMode[];
5758
};

Diff for: ‎src/unittest/test.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ void TestGameDef::defineSomeNodes()
180180
"{default_water.png";
181181
f = ContentFeatures();
182182
f.name = itemdef.name;
183-
f.alpha = 128;
183+
f.alpha = ALPHAMODE_BLEND;
184184
f.liquid_type = LIQUID_SOURCE;
185185
f.liquid_viscosity = 4;
186186
f.is_ground_content = true;
@@ -201,7 +201,7 @@ void TestGameDef::defineSomeNodes()
201201
"{default_lava.png";
202202
f = ContentFeatures();
203203
f.name = itemdef.name;
204-
f.alpha = 128;
204+
f.alpha = ALPHAMODE_OPAQUE;
205205
f.liquid_type = LIQUID_SOURCE;
206206
f.liquid_viscosity = 7;
207207
f.light_source = LIGHT_MAX-1;

0 commit comments

Comments
 (0)
Please sign in to comment.