u8 version = 6;
writeU8(os, version);
- os << serializeString(name);
+ os << serializeString16(name);
animation.serialize(os, version);
bool has_scale = scale > 0;
u16 flags = 0;
int version = readU8(is);
if (version < 6)
throw SerializationError("unsupported TileDef version");
- name = deSerializeString(is);
+ name = deSerializeString16(is);
animation.deSerialize(is, version);
u16 flags = readU16(is);
backface_culling = flags & TILE_FLAG_BACKFACE_CULLING;
{
connected_glass = g_settings->getBool("connected_glass");
opaque_water = g_settings->getBool("opaque_water");
- bool enable_shaders = g_settings->getBool("enable_shaders");
- bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
- bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
bool smooth_lighting = g_settings->getBool("smooth_lighting");
enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
enable_minimap = g_settings->getBool("enable_minimap");
if (smooth_lighting)
enable_mesh_cache = false;
- use_normal_texture = enable_shaders &&
- (enable_bumpmapping || enable_parallax_occlusion);
if (leaves_style_str == "fancy") {
leaves_style = LEAVES_FANCY;
} else if (leaves_style_str == "simple") {
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;
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;
writeU8(os, version);
// general
- os << serializeString(name);
+ os << serializeString16(name);
writeU16(os, groups.size());
for (const auto &group : groups) {
- os << serializeString(group.first);
+ os << serializeString16(group.first);
writeS16(os, group.second);
}
writeU8(os, param_type);
// visual
writeU8(os, drawtype);
- os << serializeString(mesh);
+ os << serializeString16(mesh);
writeF32(os, visual_scale);
writeU8(os, 6);
for (const TileDef &td : tiledef)
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());
- os << serializeString(palette_name);
+ os << serializeString16(palette_name);
writeU8(os, waving);
writeU8(os, connect_sides);
writeU16(os, connects_to_ids.size());
// liquid
writeU8(os, liquid_type);
- os << serializeString(liquid_alternative_flowing);
- os << serializeString(liquid_alternative_source);
+ os << serializeString16(liquid_alternative_flowing);
+ os << serializeString16(liquid_alternative_source);
writeU8(os, liquid_viscosity);
writeU8(os, liquid_renewable);
writeU8(os, liquid_range);
writeU8(os, legacy_facedir_simple);
writeU8(os, legacy_wallmounted);
- os << serializeString(node_dig_prediction);
+ os << serializeString16(node_dig_prediction);
writeU8(os, leveled_max);
-}
-
-void ContentFeatures::correctAlpha(TileDef *tiles, int length)
-{
- // alpha == 0 means that the node is using texture alpha
- if (alpha == 0 || alpha == 255)
- return;
-
- for (int i = 0; i < length; i++) {
- if (tiles[i].name.empty())
- continue;
- std::stringstream s;
- s << tiles[i].name << "^[noalpha^[opacity:" << ((int)alpha);
- tiles[i].name = s.str();
- }
+ writeU8(os, alpha);
}
void ContentFeatures::deSerialize(std::istream &is)
throw SerializationError("unsupported ContentFeatures version");
// general
- name = deSerializeString(is);
+ name = deSerializeString16(is);
groups.clear();
u32 groups_size = readU16(is);
for (u32 i = 0; i < groups_size; i++) {
- std::string name = deSerializeString(is);
+ std::string name = deSerializeString16(is);
int value = readS16(is);
groups[name] = value;
}
// visual
drawtype = (enum NodeDrawType) readU8(is);
- mesh = deSerializeString(is);
+ mesh = deSerializeString16(is);
visual_scale = readF32(is);
if (readU8(is) != 6)
throw SerializationError("unsupported tile count");
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));
- palette_name = deSerializeString(is);
+ palette_name = deSerializeString16(is);
waving = readU8(is);
connect_sides = readU8(is);
u16 connects_to_size = readU16(is);
// liquid
liquid_type = (enum LiquidType) readU8(is);
- liquid_alternative_flowing = deSerializeString(is);
- liquid_alternative_source = deSerializeString(is);
+ liquid_alternative_flowing = deSerializeString16(is);
+ liquid_alternative_source = deSerializeString16(is);
liquid_viscosity = readU8(is);
liquid_renewable = readU8(is);
liquid_range = readU8(is);
legacy_wallmounted = readU8(is);
try {
- node_dig_prediction = deSerializeString(is);
- u8 tmp_leveled_max = readU8(is);
+ node_dig_prediction = deSerializeString16(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) {};
}
bool has_scale = tiledef.scale > 0;
bool use_autoscale = tsettings.autoscale_mode == AUTOSCALE_FORCE ||
(tsettings.autoscale_mode == AUTOSCALE_ENABLE && !has_scale);
- if (use_autoscale && layer->texture) {
+ if (use_autoscale) {
auto texture_size = layer->texture->getOriginalSize();
float base_size = tsettings.node_texture_size;
float size = std::fmin(texture_size.Width, texture_size.Height);
if (!tile.world_aligned)
layer->scale = 1;
- // Normal texture and shader flags texture
- if (tsettings.use_normal_texture) {
- layer->normal_texture = tsrc->getNormalTexture(tiledef.name);
- }
layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
// Material flags
}
}
}
-#endif
-#ifndef SERVER
+bool ContentFeatures::textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles, int length)
+{
+ 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;
+ seen.insert(tiles[i].name);
+
+ // Load the texture and see if there's any transparent pixels
+ video::ITexture *texture = tsrc->getTexture(tiles[i].name);
+ video::IImage *image = driver->createImage(texture,
+ core::position2d<s32>(0, 0), texture->getOriginalSize());
+ if (!image)
+ continue;
+ core::dimension2d<u32> dim = image->getDimension();
+ bool ok = true;
+ for (u16 x = 0; x < dim.Width; x++) {
+ for (u16 y = 0; y < dim.Height; y++) {
+ if (image->getPixel(x, y).getAlpha() < 255) {
+ ok = false;
+ goto break_loop;
+ }
+ }
+ }
+
+break_loop:
+ image->drop();
+ 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;
+}
+
bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
{
if (style == ALIGN_STYLE_WORLD)
bool is_liquid = false;
- u8 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:
solidness = 0;
break;
case NDT_LIQUID:
- assert(liquid_type == LIQUID_SOURCE);
if (tsettings.opaque_water)
- alpha = 255;
+ alpha = ALPHAMODE_OPAQUE;
solidness = 1;
is_liquid = true;
break;
case NDT_FLOWINGLIQUID:
- assert(liquid_type == LIQUID_FLOWING);
solidness = 0;
if (tsettings.opaque_water)
- alpha = 255;
+ alpha = ALPHAMODE_OPAQUE;
is_liquid = true;
break;
case NDT_GLASSLIKE:
case NDT_MESH:
case NDT_NODEBOX:
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 = TILE_MATERIAL_WAVING_LIQUID_BASIC;
+ } 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:
}
if (is_liquid) {
- // Vertex alpha is no longer supported, correct if necessary.
- correctAlpha(tdef, 6);
- correctAlpha(tdef_overlay, 6);
- correctAlpha(tdef_spec, CF_SPECIAL_COUNT);
-
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;
}
}
u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
- u8 overlay_material = material_type;
+ MaterialType overlay_material = material_type;
if (overlay_material == TILE_MATERIAL_OPAQUE)
overlay_material = TILE_MATERIAL_BASIC;
else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
tdef[j].backface_culling, tsettings);
}
- u8 special_material = material_type;
+ MaterialType special_material = material_type;
if (drawtype == NDT_PLANTLIKE_ROOTED) {
if (waving == 1)
special_material = TILE_MATERIAL_WAVING_PLANTS;
ContentFeatures &nodedef = m_content_features[id];
+ // Override tiles
if (texture_override.hasTarget(OverrideTarget::TOP))
nodedef.tiledef[0].name = texture_override.texture;
if (texture_override.hasTarget(OverrideTarget::FRONT))
nodedef.tiledef[5].name = texture_override.texture;
+
+
+ // Override special tiles, if applicable
+ if (texture_override.hasTarget(OverrideTarget::SPECIAL_1))
+ nodedef.tiledef_special[0].name = texture_override.texture;
+
+ if (texture_override.hasTarget(OverrideTarget::SPECIAL_2))
+ nodedef.tiledef_special[1].name = texture_override.texture;
+
+ if (texture_override.hasTarget(OverrideTarget::SPECIAL_3))
+ nodedef.tiledef_special[2].name = texture_override.texture;
+
+ if (texture_override.hasTarget(OverrideTarget::SPECIAL_4))
+ nodedef.tiledef_special[3].name = texture_override.texture;
+
+ if (texture_override.hasTarget(OverrideTarget::SPECIAL_5))
+ nodedef.tiledef_special[4].name = texture_override.texture;
+
+ if (texture_override.hasTarget(OverrideTarget::SPECIAL_6))
+ nodedef.tiledef_special[5].name = texture_override.texture;
}
}
// strict version incompatibilities
std::ostringstream wrapper_os(std::ios::binary);
f->serialize(wrapper_os, protocol_version);
- os2<<serializeString(wrapper_os.str());
+ os2<<serializeString16(wrapper_os.str());
// must not overflow
u16 next = count + 1;
count++;
}
writeU16(os, count);
- os << serializeLongString(os2.str());
+ os << serializeString32(os2.str());
}
if (version != 1)
throw SerializationError("unsupported NodeDefinitionManager version");
u16 count = readU16(is);
- std::istringstream is2(deSerializeLongString(is), std::ios::binary);
+ std::istringstream is2(deSerializeString32(is), std::ios::binary);
ContentFeatures f;
for (u16 n = 0; n < count; n++) {
u16 i = readU16(is2);
// Read it from the string wrapper
- std::string wrapper = deSerializeString(is2);
+ std::string wrapper = deSerializeString16(is2);
std::istringstream wrapper_is(wrapper, std::ios::binary);
f.deSerialize(wrapper_is);
void NodeDefManager::resolveCrossrefs()
{
for (ContentFeatures &f : m_content_features) {
- if (f.liquid_type != LIQUID_NONE) {
+ if (f.liquid_type != LIQUID_NONE || f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) {
f.liquid_alternative_flowing_id = getId(f.liquid_alternative_flowing);
f.liquid_alternative_source_id = getId(f.liquid_alternative_source);
continue;