diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 11651c9b2..4f7a5c769 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -431,7 +431,9 @@ texture_clean_transparent (Clean transparent textures) bool false # memory. Powers of 2 are recommended. Setting this higher than 1 may not # have a visible effect unless bilinear/trilinear/anisotropic filtering is # enabled. -texture_min_size (Minimum texture size for filters) int 64 +# This is also used as the base node texture size for world-aligned +# texture autoscaling. +texture_min_size (Minimum texture size) int 64 # Experimental option, might cause visible spaces between blocks # when set to higher number than 0. @@ -687,6 +689,22 @@ fog_start (Fog Start) float 0.4 0.0 0.99 # Makes all liquids opaque opaque_water (Opaque liquids) bool false +# Textures on a node may be aligned either to the node or to the world. +# The former mode suits better things like machines, furniture, etc., while +# the latter makes stairs and microblocks fit surroundings better. +# However, as this possibility is new, thus may not be used by older servers, +# this option allows enforcing it for certain node types. Note though that +# that is considered EXPERIMENTAL and may not work properly. +world_aligned_mode (World-aligned textures mode) enum enable disable,enable,force_solid,force_nodebox + +# World-aligned textures may be scaled to span several nodes. However, +# the server may not send the scale you want, especially if you use +# a specially-designed texture pack; with this option, the client tries +# to determine the scale automatically basing on the texture size. +# See also texture_min_size. +# Warning: this option is EXPERIMENTAL! +autoscale_mode (Autoscaling mode) enum disable disable,enable,force + # Show entity selection boxes show_entity_selectionbox (Show entity selection boxes) bool true diff --git a/doc/lua_api.txt b/doc/lua_api.txt index c93f06c8a..91ea900d0 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -284,11 +284,19 @@ on top of `cobble.png`. ### Advanced texture modifiers -#### `[crack::

` +#### Crack +* `[crack::

` +* `[cracko::

` +* `[crack:::

` +* `[cracko:::

` + +Parameters: +* `` = tile count (in each direction) * `` = animation frame count * `

` = current animation frame Draw a step of the crack animation on the texture. +`crack` draws it normally, while `cracko` lays it over, keeping transparent pixels intact. Example: @@ -4420,12 +4428,21 @@ Definition tables * `"image.png"` * `{name="image.png", animation={Tile Animation definition}}` * `{name="image.png", backface_culling=bool, tileable_vertical=bool, - tileable_horizontal=bool}` + tileable_horizontal=bool, align_style="node"/"world"/"user", scale=int}` * backface culling enabled by default for most nodes * tileable flags are info for shaders, how they should treat texture when displacement mapping is used Directions are from the point of view of the tile texture, not the node it's on + * align style determines whether the texture will be rotated with the node + or kept aligned with its surroundings. "user" means that client + setting will be used, similar to `glasslike_framed_optional`. + Note: supported by solid nodes and nodeboxes only. + * scale is used to make texture span several (exactly `scale`) nodes, + instead of just one, in each direction. Works for world-aligned + textures only. + Note that as the effect is applied on per-mapblock basis, `16` should + be equally divisible by `scale` or you may get wrong results. * `{name="image.png", color=ColorSpec}` * the texture's color will be multiplied with this color. * the tile's color overrides the owning node's color in all cases. diff --git a/games/minimal/mods/experimental/depends.txt b/games/minimal/mods/experimental/depends.txt index 3a7daa1d7..3296b1e0a 100644 --- a/games/minimal/mods/experimental/depends.txt +++ b/games/minimal/mods/experimental/depends.txt @@ -1,2 +1,2 @@ default - +stairs diff --git a/games/minimal/mods/experimental/init.lua b/games/minimal/mods/experimental/init.lua index 657148dce..578c35364 100644 --- a/games/minimal/mods/experimental/init.lua +++ b/games/minimal/mods/experimental/init.lua @@ -501,6 +501,57 @@ minetest.register_node("experimental:tester_node_1", { end, }) +minetest.register_node("experimental:tiled", { + description = "Tiled stone", + tiles = {{ + name = "experimental_tiled.png", + align_style = "world", + scale = 8, + }}, + groups = {cracky=2}, +}) + +stairs.register_stair_and_slab("tiled_n", "experimental:tiled", + {cracky=2}, + {{name="experimental_tiled.png", align_style="node", scale=8}}, + "Tiled stair (node-aligned)", + "Tiled slab (node-aligned)") + +stairs.register_stair_and_slab("tiled", "experimantal:tiled", + {cracky=2}, + {{name="experimental_tiled.png", align_style="world", scale=8}}, + "Tiled stair", + "Tiled slab") + +minetest.register_craft({ + output = 'experimental:tiled 4', + recipe = { + {'default:cobble', '', 'default:cobble'}, + {'', '', ''}, + {'default:cobble', '', 'default:cobble'}, + } +}) + +minetest.register_craft({ + output = 'stairs:stair_tiled', + recipe = {{'stairs:stair_tiled_n'}} +}) + +minetest.register_craft({ + output = 'stairs:stair_tiled_n', + recipe = {{'stairs:stair_tiled'}} +}) + +minetest.register_craft({ + output = 'stairs:slab_tiled', + recipe = {{'stairs:slab_tiled_n'}} +}) + +minetest.register_craft({ + output = 'stairs:slab_tiled_n', + recipe = {{'stairs:slab_tiled'}} +}) + minetest.register_craftitem("experimental:tester_tool_1", { description = "Tester Tool 1", inventory_image = "experimental_tester_tool_1.png", diff --git a/games/minimal/mods/experimental/textures/experimental_tiled.png b/games/minimal/mods/experimental/textures/experimental_tiled.png new file mode 100644 index 000000000..67397b902 Binary files /dev/null and b/games/minimal/mods/experimental/textures/experimental_tiled.png differ diff --git a/games/minimal/mods/stairs/init.lua b/games/minimal/mods/stairs/init.lua index c3bbc561e..74b7890ee 100644 --- a/games/minimal/mods/stairs/init.lua +++ b/games/minimal/mods/stairs/init.lua @@ -2,7 +2,7 @@ stairs = {} -- Node will be called stairs:stair_ function stairs.register_stair(subname, recipeitem, groups, images, description) - minetest.register_node("stairs:stair_" .. subname, { + minetest.register_node(":stairs:stair_" .. subname, { description = description, drawtype = "nodebox", tiles = images, @@ -31,7 +31,7 @@ end -- Node will be called stairs:slab_ function stairs.register_slab(subname, recipeitem, groups, images, description) - minetest.register_node("stairs:slab_" .. subname, { + minetest.register_node(":stairs:slab_" .. subname, { description = description, drawtype = "nodebox", tiles = images, diff --git a/minetest.conf.example b/minetest.conf.example index 6eca94401..28281fc38 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -489,6 +489,8 @@ # memory. Powers of 2 are recommended. Setting this higher than 1 may not # have a visible effect unless bilinear/trilinear/anisotropic filtering is # enabled. +# This is also used as the base node texture size for world-aligned +# texture autoscaling. # type: int # texture_min_size = 64 @@ -808,6 +810,24 @@ # type: bool # opaque_water = false +# Textures on a node may be aligned either to the node or to the world. +# The former mode sutis better things like machines, furniture, etc., while +# the latter makes stairs and microblocks fit surroundings better. +# However, as this possibility is new, thus may not be used by older servers, +# this option allows enforcing it for certain node types. Note though that +# that is considered EXPERIMENTAL and may not work properly. +# type: enum values: disable, enable, force_solid, force_nodebox +# world_aligned_mode = enable + +# World-aligned textures may be scaled to span several nodes. However, +# the server may not send the scale you want, especially if you use +# a specially-designed texture pack; with this option, the client tries +# to determine the scale automatically basing on the texture size. +# See also texture_min_size. +# Warning: this option is EXPERIMENTAL! +# type: enum values: disable, enable, force +# autoscale_mode = disable + # Show entity selection boxes # type: bool # show_entity_selectionbox = true diff --git a/src/client/tile.cpp b/src/client/tile.cpp index a2284eed3..9321a4586 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -545,7 +545,7 @@ static void apply_mask(video::IImage *mask, video::IImage *dst, // Draw or overlay a crack static void draw_crack(video::IImage *crack, video::IImage *dst, bool use_overlay, s32 frame_count, s32 progression, - video::IVideoDriver *driver); + video::IVideoDriver *driver, u8 tiles = 1); // Brighten image void brighten(video::IImage *image); @@ -1280,11 +1280,22 @@ bool TextureSource::generateImagePart(std::string part_of_name, } // Crack image number and overlay option + // Format: crack[o][:]:: bool use_overlay = (part_of_name[6] == 'o'); Strfnd sf(part_of_name); sf.next(":"); s32 frame_count = stoi(sf.next(":")); s32 progression = stoi(sf.next(":")); + s32 tiles = 1; + // Check whether there is the argument, that is, + // whether there are 3 arguments. If so, shift values + // as the first and not the last argument is optional. + auto s = sf.next(":"); + if (!s.empty()) { + tiles = frame_count; + frame_count = progression; + progression = stoi(s); + } if (progression >= 0) { /* @@ -1299,7 +1310,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, if (img_crack) { draw_crack(img_crack, baseimg, use_overlay, frame_count, - progression, driver); + progression, driver, tiles); img_crack->drop(); } } @@ -2102,74 +2113,78 @@ static void apply_mask(video::IImage *mask, video::IImage *dst, } } +video::IImage *create_crack_image(video::IImage *crack, s32 frame_index, + core::dimension2d size, u8 tiles, video::IVideoDriver *driver) +{ + core::dimension2d strip_size = crack->getDimension(); + core::dimension2d frame_size(strip_size.Width, strip_size.Width); + core::dimension2d tile_size(size / tiles); + s32 frame_count = strip_size.Height / strip_size.Width; + if (frame_index >= frame_count) + frame_index = frame_count - 1; + core::rect frame(v2s32(0, frame_index * frame_size.Height), frame_size); + video::IImage *result = nullptr; + +// extract crack frame + video::IImage *crack_tile = driver->createImage(video::ECF_A8R8G8B8, tile_size); + if (!crack_tile) + return nullptr; + if (tile_size == frame_size) { + crack->copyTo(crack_tile, v2s32(0, 0), frame); + } else { + video::IImage *crack_frame = driver->createImage(video::ECF_A8R8G8B8, frame_size); + if (!crack_frame) + goto exit__has_tile; + crack->copyTo(crack_frame, v2s32(0, 0), frame); + crack_frame->copyToScaling(crack_tile); + crack_frame->drop(); + } + if (tiles == 1) + return crack_tile; + +// tile it + result = driver->createImage(video::ECF_A8R8G8B8, size); + if (!result) + goto exit__has_tile; + result->fill({}); + for (u8 i = 0; i < tiles; i++) + for (u8 j = 0; j < tiles; j++) + crack_tile->copyTo(result, v2s32(i * tile_size.Width, j * tile_size.Height)); + +exit__has_tile: + crack_tile->drop(); + return result; +} + static void draw_crack(video::IImage *crack, video::IImage *dst, bool use_overlay, s32 frame_count, s32 progression, - video::IVideoDriver *driver) + video::IVideoDriver *driver, u8 tiles) { // Dimension of destination image core::dimension2d dim_dst = dst->getDimension(); - // Dimension of original image - core::dimension2d dim_crack = crack->getDimension(); - // Count of crack stages - s32 crack_count = dim_crack.Height / dim_crack.Width; // Limit frame_count if (frame_count > (s32) dim_dst.Height) frame_count = dim_dst.Height; if (frame_count < 1) frame_count = 1; - // Limit progression - if (progression > crack_count-1) - progression = crack_count-1; - // Dimension of a single crack stage - core::dimension2d dim_crack_cropped( - dim_crack.Width, - dim_crack.Width - ); // Dimension of the scaled crack stage, // which is the same as the dimension of a single destination frame - core::dimension2d dim_crack_scaled( + core::dimension2d frame_size( dim_dst.Width, dim_dst.Height / frame_count ); - // Create cropped and scaled crack images - video::IImage *crack_cropped = driver->createImage( - video::ECF_A8R8G8B8, dim_crack_cropped); - video::IImage *crack_scaled = driver->createImage( - video::ECF_A8R8G8B8, dim_crack_scaled); + video::IImage *crack_scaled = create_crack_image(crack, progression, + frame_size, tiles, driver); + if (!crack_scaled) + return; - if (crack_cropped && crack_scaled) - { - // Crop crack image - v2s32 pos_crack(0, progression*dim_crack.Width); - crack->copyTo(crack_cropped, - v2s32(0,0), - core::rect(pos_crack, dim_crack_cropped)); - // Scale crack image by copying - crack_cropped->copyToScaling(crack_scaled); - // Copy or overlay crack image onto each frame - for (s32 i = 0; i < frame_count; ++i) - { - v2s32 dst_pos(0, dim_crack_scaled.Height * i); - if (use_overlay) - { - blit_with_alpha_overlay(crack_scaled, dst, - v2s32(0,0), dst_pos, - dim_crack_scaled); - } - else - { - blit_with_alpha(crack_scaled, dst, - v2s32(0,0), dst_pos, - dim_crack_scaled); - } - } + auto blit = use_overlay ? blit_with_alpha_overlay : blit_with_alpha; + for (s32 i = 0; i < frame_count; ++i) { + v2s32 dst_pos(0, frame_size.Height * i); + blit(crack_scaled, dst, v2s32(0,0), dst_pos, frame_size); } - if (crack_scaled) - crack_scaled->drop(); - - if (crack_cropped) - crack_cropped->drop(); + crack_scaled->drop(); } void brighten(video::IImage *image) diff --git a/src/client/tile.h b/src/client/tile.h index d4e25ea6a..e69dbe0c7 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -209,7 +209,8 @@ struct TileLayer texture_id == other.texture_id && material_type == other.material_type && material_flags == other.material_flags && - color == other.color; + color == other.color && + scale == other.scale; } /*! @@ -298,6 +299,8 @@ struct TileLayer * a color then the color of the node owning this tile. */ video::SColor color; + + u8 scale; }; /*! @@ -325,6 +328,9 @@ struct TileSpec && emissive_light == other.emissive_light; } + //! If true, the tile rotation is ignored. + bool world_aligned = false; + //! Tile rotation. u8 rotation = 0; //! This much light does the tile emit. u8 emissive_light = 0; diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 4e32e27ca..c2a25037c 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -77,7 +77,7 @@ void MapblockMeshGenerator::useTile(int index, u8 set_flags, u8 reset_flags, boo if (special) getSpecialTile(index, &tile, p == data->m_crack_pos_relative); else - getNodeTileN(n, p, index, data, tile); + getTile(index, &tile); if (!data->m_smooth_lighting) color = encode_light(light, f->light_source); @@ -87,14 +87,19 @@ void MapblockMeshGenerator::useTile(int index, u8 set_flags, u8 reset_flags, boo } } +// Returns a tile, ready for use, non-rotated. +void MapblockMeshGenerator::getTile(int index, TileSpec *tile) +{ + getNodeTileN(n, p, index, data, *tile); +} + +// Returns a tile, ready for use, rotated according to the node facedir. void MapblockMeshGenerator::getTile(v3s16 direction, TileSpec *tile) { getNodeTile(n, p, direction, data, *tile); } -/*! - * Returns the i-th special tile for a map node. - */ +// Returns a special tile, ready for use, non-rotated. void MapblockMeshGenerator::getSpecialTile(int index, TileSpec *tile, bool apply_crack) { *tile = f->special_tiles[index]; @@ -1256,10 +1261,8 @@ void MapblockMeshGenerator::drawMeshNode() // Convert wallmounted to 6dfacedir. // When cache enabled, it is already converted. facedir = n.getWallMounted(nodedef); - if (!enable_mesh_cache) { - static const u8 wm_to_6d[6] = {20, 0, 16 + 1, 12 + 3, 8, 4 + 2}; - facedir = wm_to_6d[facedir]; - } + if (!enable_mesh_cache) + facedir = wallmounted_to_facedir[facedir]; } if (!data->m_smooth_lighting && f->mesh_ptr[facedir]) { diff --git a/src/content_mapblock.h b/src/content_mapblock.h index aee550d34..93e3fe53c 100644 --- a/src/content_mapblock.h +++ b/src/content_mapblock.h @@ -63,6 +63,7 @@ public: void useTile(int index = 0, u8 set_flags = MATERIAL_FLAG_CRACK_OVERLAY, u8 reset_flags = 0, bool special = false); + void getTile(int index, TileSpec *tile); void getTile(v3s16 direction, TileSpec *tile); void getSpecialTile(int index, TileSpec *tile, bool apply_crack = false); diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 58ea0c77a..7612ceb44 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -139,6 +139,8 @@ void set_default_settings(Settings *settings) #endif settings->setDefault("fsaa", "0"); settings->setDefault("undersampling", "0"); + settings->setDefault("world_aligned_mode", "enable"); + settings->setDefault("autoscale_mode", "disable"); settings->setDefault("enable_fog", "true"); settings->setDefault("fog_start", "0.4"); settings->setDefault("3d_mode", "none"); diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index 7708fb438..5d298da3d 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -466,6 +466,31 @@ static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs) } } +static void getNodeTextureCoords(v3f base, const v3f &scale, v3s16 dir, float *u, float *v) +{ + if (dir.X > 0 || dir.Y > 0 || dir.Z < 0) + base -= scale; + if (dir == v3s16(0,0,1)) { + *u = -base.X - 1; + *v = -base.Y - 1; + } else if (dir == v3s16(0,0,-1)) { + *u = base.X + 1; + *v = -base.Y - 2; + } else if (dir == v3s16(1,0,0)) { + *u = base.Z + 1; + *v = -base.Y - 2; + } else if (dir == v3s16(-1,0,0)) { + *u = -base.Z - 1; + *v = -base.Y - 1; + } else if (dir == v3s16(0,1,0)) { + *u = base.X + 1; + *v = -base.Z - 2; + } else if (dir == v3s16(0,-1,0)) { + *u = base.X; + *v = base.Z; + } +} + struct FastFace { TileLayer layer; @@ -477,10 +502,11 @@ struct FastFace */ bool vertex_0_2_connected; u8 layernum; + bool world_aligned; }; static void makeFastFace(const TileSpec &tile, u16 li0, u16 li1, u16 li2, u16 li3, - const v3f &p, v3s16 dir, v3f scale, std::vector &dest) + v3f tp, v3f p, v3s16 dir, v3f scale, std::vector &dest) { // Position is at the center of the cube. v3f pos = p * BS; @@ -493,6 +519,8 @@ static void makeFastFace(const TileSpec &tile, u16 li0, u16 li1, u16 li2, u16 li v3f vertex_pos[4]; v3s16 vertex_dirs[4]; getNodeVertexDirs(dir, vertex_dirs); + if (tile.world_aligned) + getNodeTextureCoords(tp, scale, dir, &x0, &y0); v3s16 t; u16 t1; @@ -671,6 +699,8 @@ static void makeFastFace(const TileSpec &tile, u16 li0, u16 li1, u16 li2, u16 li face.layer = *layer; face.layernum = layernum; + + face.world_aligned = tile.world_aligned; } } @@ -806,7 +836,7 @@ void getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data, TileSpec &t }; u16 tile_index = facedir * 16 + dir_i; getNodeTileN(mn, p, dir_to_tile[tile_index], data, tile); - tile.rotation = dir_to_tile[tile_index + 1]; + tile.rotation = tile.world_aligned ? 0 : dir_to_tile[tile_index + 1]; } static void getTileInfo( @@ -965,7 +995,7 @@ static void updateFastFaceRow( scale.Z = continuous_tiles_count; makeFastFace(tile, lights[0], lights[1], lights[2], lights[3], - sp, face_dir_corrected, scale, dest); + pf, sp, face_dir_corrected, scale, dest); g_profiler->avg("Meshgen: faces drawn by tiling", 0); for (int i = 1; i < continuous_tiles_count; i++) @@ -1092,7 +1122,7 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): f.vertex_0_2_connected ? indices : indices_alternate; collector.append(f.layer, f.vertices, 4, indices_p, 6, - f.layernum); + f.layernum, f.world_aligned); } } @@ -1128,6 +1158,9 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): os << m_tsrc->getTextureName(p.layer.texture_id) << "^[crack"; if (p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY) os << "o"; // use ^[cracko + u8 tiles = p.layer.scale; + if (tiles > 1) + os << ":" << (u32)tiles; os << ":" << (u32)p.layer.animation_frame_count << ":"; m_crack_materials.insert(std::make_pair( std::pair(layer, i), os.str())); @@ -1392,13 +1425,15 @@ void MeshCollector::append(const TileSpec &tile, const TileLayer *layer = &tile.layers[layernum]; if (layer->texture_id == 0) continue; - append(*layer, vertices, numVertices, indices, numIndices, layernum); + append(*layer, vertices, numVertices, indices, numIndices, + layernum, tile.world_aligned); } } void MeshCollector::append(const TileLayer &layer, const video::S3DVertex *vertices, u32 numVertices, - const u16 *indices, u32 numIndices, u8 layernum) + const u16 *indices, u32 numIndices, u8 layernum, + bool use_scale) { if (numIndices > 65535) { dstream << "FIXME: MeshCollector::append() called with numIndices=" @@ -1422,20 +1457,24 @@ void MeshCollector::append(const TileLayer &layer, p = &(*buffers)[buffers->size() - 1]; } + f32 scale = 1.0; + if (use_scale) + scale = 1.0 / layer.scale; + u32 vertex_count; if (m_use_tangent_vertices) { vertex_count = p->tangent_vertices.size(); for (u32 i = 0; i < numVertices; i++) { video::S3DVertexTangents vert(vertices[i].Pos, vertices[i].Normal, - vertices[i].Color, vertices[i].TCoords); + vertices[i].Color, scale * vertices[i].TCoords); p->tangent_vertices.push_back(vert); } } else { vertex_count = p->vertices.size(); for (u32 i = 0; i < numVertices; i++) { video::S3DVertex vert(vertices[i].Pos, vertices[i].Normal, - vertices[i].Color, vertices[i].TCoords); + vertices[i].Color, scale * vertices[i].TCoords); p->vertices.push_back(vert); } @@ -1461,14 +1500,15 @@ void MeshCollector::append(const TileSpec &tile, if (layer->texture_id == 0) continue; append(*layer, vertices, numVertices, indices, numIndices, pos, - c, light_source, layernum); + c, light_source, layernum, tile.world_aligned); } } void MeshCollector::append(const TileLayer &layer, const video::S3DVertex *vertices, u32 numVertices, const u16 *indices, u32 numIndices, - v3f pos, video::SColor c, u8 light_source, u8 layernum) + v3f pos, video::SColor c, u8 light_source, u8 layernum, + bool use_scale) { if (numIndices > 65535) { dstream << "FIXME: MeshCollector::append() called with numIndices=" @@ -1492,6 +1532,10 @@ void MeshCollector::append(const TileLayer &layer, p = &(*buffers)[buffers->size() - 1]; } + f32 scale = 1.0; + if (use_scale) + scale = 1.0 / layer.scale; + video::SColor original_c = c; u32 vertex_count; if (m_use_tangent_vertices) { @@ -1502,7 +1546,7 @@ void MeshCollector::append(const TileLayer &layer, applyFacesShading(c, vertices[i].Normal); } video::S3DVertexTangents vert(vertices[i].Pos + pos, - vertices[i].Normal, c, vertices[i].TCoords); + vertices[i].Normal, c, scale * vertices[i].TCoords); p->tangent_vertices.push_back(vert); } } else { @@ -1513,7 +1557,7 @@ void MeshCollector::append(const TileLayer &layer, applyFacesShading(c, vertices[i].Normal); } video::S3DVertex vert(vertices[i].Pos + pos, vertices[i].Normal, c, - vertices[i].TCoords); + scale * vertices[i].TCoords); p->vertices.push_back(vert); } } diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h index 13cee537c..13e3da9a1 100644 --- a/src/mapblock_mesh.h +++ b/src/mapblock_mesh.h @@ -200,7 +200,8 @@ struct MeshCollector const u16 *indices, u32 numIndices); void append(const TileLayer &material, const video::S3DVertex *vertices, u32 numVertices, - const u16 *indices, u32 numIndices, u8 layernum); + const u16 *indices, u32 numIndices, u8 layernum, + bool use_scale = false); void append(const TileSpec &material, const video::S3DVertex *vertices, u32 numVertices, const u16 *indices, u32 numIndices, v3f pos, @@ -208,7 +209,8 @@ struct MeshCollector void append(const TileLayer &material, const video::S3DVertex *vertices, u32 numVertices, const u16 *indices, u32 numIndices, v3f pos, - video::SColor c, u8 light_source, u8 layernum); + video::SColor c, u8 light_source, u8 layernum, + bool use_scale = false); /*! * Colorizes all vertices in the collector. */ diff --git a/src/mapnode.cpp b/src/mapnode.cpp index c46719a68..9d3459173 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "serialization.h" // For ser_ver_supported #include "util/serialize.h" #include "log.h" +#include "util/directiontables.h" #include "util/numeric.h" #include #include @@ -152,12 +153,15 @@ bool MapNode::getLightBanks(u8 &lightday, u8 &lightnight, INodeDefManager *nodem return f.param_type == CPT_LIGHT || f.light_source != 0; } -u8 MapNode::getFaceDir(INodeDefManager *nodemgr) const +u8 MapNode::getFaceDir(INodeDefManager *nodemgr, bool allow_wallmounted) const { const ContentFeatures &f = nodemgr->get(*this); if (f.param_type_2 == CPT2_FACEDIR || f.param_type_2 == CPT2_COLORED_FACEDIR) return (getParam2() & 0x1F) % 24; + if (allow_wallmounted && (f.param_type_2 == CPT2_WALLMOUNTED || + f.param_type_2 == CPT2_COLORED_WALLMOUNTED)) + return wallmounted_to_facedir[getParam2() & 0x07]; return 0; } @@ -246,7 +250,7 @@ void transformNodeBox(const MapNode &n, const NodeBox &nodebox, if (nodebox.type == NODEBOX_FIXED || nodebox.type == NODEBOX_LEVELED) { const std::vector &fixed = nodebox.fixed; - int facedir = n.getFaceDir(nodemgr); + int facedir = n.getFaceDir(nodemgr, true); u8 axisdir = facedir>>2; facedir&=0x03; for (aabb3f box : fixed) { diff --git a/src/mapnode.h b/src/mapnode.h index 338ae41fe..2abc4b97b 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -240,7 +240,7 @@ struct MapNode return blend_light(daylight_factor, lightday, lightnight); } - u8 getFaceDir(INodeDefManager *nodemgr) const; + u8 getFaceDir(INodeDefManager *nodemgr, bool allow_wallmounted = false) const; u8 getWallMounted(INodeDefManager *nodemgr) const; v3s16 getWallMountedDir(INodeDefManager *nodemgr) const; diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 76e90b560..ba3926492 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -180,6 +180,8 @@ with this program; if not, write to the Free Software Foundation, Inc., Backwards compatibility drop Add 'can_zoom' to player object properties Add glow to object properties + Change TileDef serialization format. + Add world-aligned tiles. Mod channels */ diff --git a/src/nodedef.cpp b/src/nodedef.cpp index d13f93068..209bea86a 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -169,40 +169,72 @@ void NodeBox::deSerialize(std::istream &is) TileDef */ +#define TILE_FLAG_BACKFACE_CULLING (1 << 0) +#define TILE_FLAG_TILEABLE_HORIZONTAL (1 << 1) +#define TILE_FLAG_TILEABLE_VERTICAL (1 << 2) +#define TILE_FLAG_HAS_COLOR (1 << 3) +#define TILE_FLAG_HAS_SCALE (1 << 4) +#define TILE_FLAG_HAS_ALIGN_STYLE (1 << 5) + void TileDef::serialize(std::ostream &os, u16 protocol_version) const { // protocol_version >= 36 - u8 version = 5; + u8 version = 6; writeU8(os, version); os << serializeString(name); animation.serialize(os, version); - writeU8(os, backface_culling); - writeU8(os, tileable_horizontal); - writeU8(os, tileable_vertical); - writeU8(os, has_color); + bool has_scale = scale > 0; + u16 flags = 0; + if (backface_culling) + flags |= TILE_FLAG_BACKFACE_CULLING; + if (tileable_horizontal) + flags |= TILE_FLAG_TILEABLE_HORIZONTAL; + if (tileable_vertical) + flags |= TILE_FLAG_TILEABLE_VERTICAL; + if (has_color) + flags |= TILE_FLAG_HAS_COLOR; + if (has_scale) + flags |= TILE_FLAG_HAS_SCALE; + if (align_style != ALIGN_STYLE_NODE) + flags |= TILE_FLAG_HAS_ALIGN_STYLE; + writeU16(os, flags); if (has_color) { writeU8(os, color.getRed()); writeU8(os, color.getGreen()); writeU8(os, color.getBlue()); } + if (has_scale) + writeU8(os, scale); + if (align_style != ALIGN_STYLE_NODE) + writeU8(os, align_style); } void TileDef::deSerialize(std::istream &is, u8 contentfeatures_version, NodeDrawType drawtype) { int version = readU8(is); + if (version < 6) + throw SerializationError("unsupported TileDef version"); name = deSerializeString(is); animation.deSerialize(is, version); - backface_culling = readU8(is); - tileable_horizontal = readU8(is); - tileable_vertical = readU8(is); - has_color = readU8(is); + u16 flags = readU16(is); + backface_culling = flags & TILE_FLAG_BACKFACE_CULLING; + tileable_horizontal = flags & TILE_FLAG_TILEABLE_HORIZONTAL; + tileable_vertical = flags & TILE_FLAG_TILEABLE_VERTICAL; + has_color = flags & TILE_FLAG_HAS_COLOR; + bool has_scale = flags & TILE_FLAG_HAS_SCALE; + bool has_align_style = flags & TILE_FLAG_HAS_ALIGN_STYLE; if (has_color) { color.setRed(readU8(is)); color.setGreen(readU8(is)); color.setBlue(readU8(is)); } + scale = has_scale ? readU8(is) : 0; + if (has_align_style) + align_style = static_cast(readU8(is)); + else + align_style = ALIGN_STYLE_NODE; } @@ -235,7 +267,10 @@ void TextureSettings::readSettings() bool smooth_lighting = g_settings->getBool("smooth_lighting"); enable_mesh_cache = g_settings->getBool("enable_mesh_cache"); enable_minimap = g_settings->getBool("enable_minimap"); + node_texture_size = g_settings->getU16("texture_min_size"); std::string leaves_style_str = g_settings->get("leaves_style"); + std::string world_aligned_mode_str = g_settings->get("world_aligned_mode"); + std::string autoscale_mode_str = g_settings->get("autoscale_mode"); // Mesh cache is not supported in combination with smooth lighting if (smooth_lighting) @@ -250,6 +285,22 @@ void TextureSettings::readSettings() } else { leaves_style = LEAVES_OPAQUE; } + + if (world_aligned_mode_str == "enable") + world_aligned_mode = WORLDALIGN_ENABLE; + else if (world_aligned_mode_str == "force_solid") + world_aligned_mode = WORLDALIGN_FORCE; + else if (world_aligned_mode_str == "force_nodebox") + world_aligned_mode = WORLDALIGN_FORCE_NODEBOX; + else + world_aligned_mode = WORLDALIGN_DISABLE; + + if (autoscale_mode_str == "enable") + autoscale_mode = AUTOSCALE_ENABLE; + else if (autoscale_mode_str == "force") + autoscale_mode = AUTOSCALE_FORCE; + else + autoscale_mode = AUTOSCALE_DISABLE; } /* @@ -541,77 +592,108 @@ void ContentFeatures::deSerialize(std::istream &is) } #ifndef SERVER -void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileLayer *tile, - TileDef *tiledef, u32 shader_id, bool use_normal_texture, - bool backface_culling, u8 material_type) +static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer, + const TileSpec &tile, const TileDef &tiledef, video::SColor color, + u8 material_type, u32 shader_id, bool backface_culling, + const TextureSettings &tsettings) { - tile->shader_id = shader_id; - tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id); - tile->material_type = material_type; + layer->shader_id = shader_id; + layer->texture = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id); + layer->material_type = material_type; + + bool has_scale = tiledef.scale > 0; + if (((tsettings.autoscale_mode == AUTOSCALE_ENABLE) && !has_scale) || + (tsettings.autoscale_mode == AUTOSCALE_FORCE)) { + auto texture_size = layer->texture->getOriginalSize(); + float base_size = tsettings.node_texture_size; + float size = std::fmin(texture_size.Width, texture_size.Height); + layer->scale = std::fmax(base_size, size) / base_size; + } else if (has_scale) { + layer->scale = tiledef.scale; + } else { + layer->scale = 1; + } + if (!tile.world_aligned) + layer->scale = 1; // Normal texture and shader flags texture - if (use_normal_texture) { - tile->normal_texture = tsrc->getNormalTexture(tiledef->name); + if (tsettings.use_normal_texture) { + layer->normal_texture = tsrc->getNormalTexture(tiledef.name); } - tile->flags_texture = tsrc->getShaderFlagsTexture(tile->normal_texture ? true : false); + layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false); // Material flags - tile->material_flags = 0; + layer->material_flags = 0; if (backface_culling) - tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING; - if (tiledef->animation.type != TAT_NONE) - tile->material_flags |= MATERIAL_FLAG_ANIMATION; - if (tiledef->tileable_horizontal) - tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL; - if (tiledef->tileable_vertical) - tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL; + layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING; + if (tiledef.animation.type != TAT_NONE) + layer->material_flags |= MATERIAL_FLAG_ANIMATION; + if (tiledef.tileable_horizontal) + layer->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL; + if (tiledef.tileable_vertical) + layer->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL; // Color - tile->has_color = tiledef->has_color; - if (tiledef->has_color) - tile->color = tiledef->color; + layer->has_color = tiledef.has_color; + if (tiledef.has_color) + layer->color = tiledef.color; else - tile->color = color; + layer->color = color; // Animation parameters int frame_count = 1; - if (tile->material_flags & MATERIAL_FLAG_ANIMATION) { + if (layer->material_flags & MATERIAL_FLAG_ANIMATION) { int frame_length_ms; - tiledef->animation.determineParams(tile->texture->getOriginalSize(), + tiledef.animation.determineParams(layer->texture->getOriginalSize(), &frame_count, &frame_length_ms, NULL); - tile->animation_frame_count = frame_count; - tile->animation_frame_length_ms = frame_length_ms; + layer->animation_frame_count = frame_count; + layer->animation_frame_length_ms = frame_length_ms; } if (frame_count == 1) { - tile->material_flags &= ~MATERIAL_FLAG_ANIMATION; + layer->material_flags &= ~MATERIAL_FLAG_ANIMATION; } else { std::ostringstream os(std::ios::binary); - if (!tile->frames) { - tile->frames = std::make_shared>(); + if (!layer->frames) { + layer->frames = std::make_shared>(); } - tile->frames->resize(frame_count); + layer->frames->resize(frame_count); for (int i = 0; i < frame_count; i++) { FrameSpec frame; os.str(""); - os << tiledef->name; - tiledef->animation.getTextureModifer(os, - tile->texture->getOriginalSize(), i); + os << tiledef.name; + tiledef.animation.getTextureModifer(os, + layer->texture->getOriginalSize(), i); frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id); - if (tile->normal_texture) + if (layer->normal_texture) frame.normal_texture = tsrc->getNormalTexture(os.str()); - frame.flags_texture = tile->flags_texture; - (*tile->frames)[i] = frame; + frame.flags_texture = layer->flags_texture; + (*layer->frames)[i] = frame; } } } #endif #ifndef SERVER +bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype) +{ + if (style == ALIGN_STYLE_WORLD) + return true; + if (mode == WORLDALIGN_DISABLE) + return false; + if (style == ALIGN_STYLE_USER_DEFINED) + return true; + if (drawtype == NDT_NORMAL) + return mode >= WORLDALIGN_FORCE; + if (drawtype == NDT_NODEBOX) + return mode >= WORLDALIGN_FORCE_NODEBOX; + return false; +} + void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc, scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings) { @@ -751,13 +833,15 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc // Tiles (fill in f->tiles[]) for (u16 j = 0; j < 6; j++) { - fillTileAttribs(tsrc, &tiles[j].layers[0], &tdef[j], tile_shader, - tsettings.use_normal_texture, - tdef[j].backface_culling, material_type); + tiles[j].world_aligned = isWorldAligned(tdef[j].align_style, + tsettings.world_aligned_mode, drawtype); + fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j], + color, material_type, tile_shader, + tdef[j].backface_culling, tsettings); if (!tdef_overlay[j].name.empty()) - fillTileAttribs(tsrc, &tiles[j].layers[1], &tdef_overlay[j], - overlay_shader, tsettings.use_normal_texture, - tdef[j].backface_culling, overlay_material); + fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j], tdef_overlay[j], + color, overlay_material, overlay_shader, + tdef[j].backface_culling, tsettings); } u8 special_material = material_type; @@ -770,11 +854,10 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype); // Special tiles (fill in f->special_tiles[]) - for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) { - fillTileAttribs(tsrc, &special_tiles[j].layers[0], &tdef_spec[j], - special_shader, tsettings.use_normal_texture, - tdef_spec[j].backface_culling, special_material); - } + for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) + fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j], tdef_spec[j], + color, special_material, special_shader, + tdef_spec[j].backface_culling, tsettings); if (param_type_2 == CPT2_COLOR || param_type_2 == CPT2_COLORED_FACEDIR || @@ -791,18 +874,6 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc recalculateBoundingBox(mesh_ptr[0]); meshmanip->recalculateNormals(mesh_ptr[0], true, false); } - } else if ((drawtype == NDT_NODEBOX) && - ((node_box.type == NODEBOX_REGULAR) || - (node_box.type == NODEBOX_FIXED)) && - (!node_box.fixed.empty())) { - //Convert regular nodebox nodes to meshnodes - //Change the drawtype and apply scale - drawtype = NDT_MESH; - mesh_ptr[0] = convertNodeboxesToMesh(node_box.fixed); - v3f scale = v3f(1.0, 1.0, 1.0) * visual_scale; - scaleMesh(mesh_ptr[0], scale); - recalculateBoundingBox(mesh_ptr[0]); - meshmanip->recalculateNormals(mesh_ptr[0], true, false); } //Cache 6dfacedir and wallmounted rotated clones of meshes diff --git a/src/nodedef.h b/src/nodedef.h index 23100a2ed..790c7fd28 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -125,9 +125,25 @@ enum LeavesStyle { LEAVES_OPAQUE, }; +enum AutoScale : u8 { + AUTOSCALE_DISABLE, + AUTOSCALE_ENABLE, + AUTOSCALE_FORCE, +}; + +enum WorldAlignMode : u8 { + WORLDALIGN_DISABLE, + WORLDALIGN_ENABLE, + WORLDALIGN_FORCE, + WORLDALIGN_FORCE_NODEBOX, +}; + class TextureSettings { public: LeavesStyle leaves_style; + WorldAlignMode world_aligned_mode; + AutoScale autoscale_mode; + int node_texture_size; bool opaque_water; bool connected_glass; bool use_normal_texture; @@ -198,6 +214,12 @@ enum PlantlikeStyle { PLANT_STYLE_HASH2, }; +enum AlignStyle : u8 { + ALIGN_STYLE_NODE, + ALIGN_STYLE_WORLD, + ALIGN_STYLE_USER_DEFINED, +}; + /* Stand-alone definition of a TileSpec (basically a server-side TileSpec) */ @@ -212,6 +234,8 @@ struct TileDef bool has_color = false; //! The color of the tile. video::SColor color = video::SColor(0xFFFFFFFF); + AlignStyle align_style = ALIGN_STYLE_NODE; + u8 scale = 0; struct TileAnimationParams animation; @@ -401,9 +425,6 @@ struct ContentFeatures } #ifndef SERVER - void fillTileAttribs(ITextureSource *tsrc, TileLayer *tile, TileDef *tiledef, - u32 shader_id, bool use_normal_texture, bool backface_culling, - u8 material_type); void updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc, scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings); #endif diff --git a/src/particles.cpp b/src/particles.cpp index 053b68fff..0a6651ce4 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -598,6 +598,8 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef, float size = rand() % 64 / 512.; float visual_size = BS * size; + if (tile.scale) + size /= tile.scale; v2f texsize(size * 2, size * 2); v2f texpos; texpos.X = ((rand() % 64) / 64. - texsize.X); diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 8f08de1e5..4d8cdd352 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -431,6 +431,16 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype) L, index, "tileable_horizontal", default_tiling); tiledef.tileable_vertical = getboolfield_default( L, index, "tileable_vertical", default_tiling); + std::string align_style; + if (getstringfield(L, index, "align_style", align_style)) { + if (align_style == "user") + tiledef.align_style = ALIGN_STYLE_USER_DEFINED; + else if (align_style == "world") + tiledef.align_style = ALIGN_STYLE_WORLD; + else + tiledef.align_style = ALIGN_STYLE_NODE; + } + tiledef.scale = getintfield_default(L, index, "scale", 0); // color = ... lua_getfield(L, index, "color"); tiledef.has_color = read_color(L, -1, &tiledef.color); diff --git a/src/settings_translation_file.cpp b/src/settings_translation_file.cpp index ffb960784..cebe6bdf1 100644 --- a/src/settings_translation_file.cpp +++ b/src/settings_translation_file.cpp @@ -172,12 +172,13 @@ fake_function() { gettext("Use trilinear filtering when scaling textures."); gettext("Clean transparent textures"); gettext("Filtered textures can blend RGB values with fully-transparent neighbors,\nwhich PNG optimizers usually discard, sometimes resulting in a dark or\nlight edge to transparent textures. Apply this filter to clean that up\nat texture load time."); - gettext("Minimum texture size for filters"); - gettext("When using bilinear/trilinear/anisotropic filters, low-resolution textures\ncan be blurred, so automatically upscale them with nearest-neighbor\ninterpolation to preserve crisp pixels. This sets the minimum texture size\nfor the upscaled textures; higher values look sharper, but require more\nmemory. Powers of 2 are recommended. Setting this higher than 1 may not\nhave a visible effect unless bilinear/trilinear/anisotropic filtering is\nenabled."); + gettext("Minimum texture size"); + gettext("When using bilinear/trilinear/anisotropic filters, low-resolution textures\ncan be blurred, so automatically upscale them with nearest-neighbor\ninterpolation to preserve crisp pixels. This sets the minimum texture size\nfor the upscaled textures; higher values look sharper, but require more\nmemory. Powers of 2 are recommended. Setting this higher than 1 may not\nhave a visible effect unless bilinear/trilinear/anisotropic filtering is\nenabled.\nThis is also used as the base node texture size for world-aligned\ntexture autoscaling."); gettext("FSAA"); gettext("Experimental option, might cause visible spaces between blocks\nwhen set to higher number than 0."); gettext("Undersampling"); gettext("Undersampling is similar to using lower screen resolution, but it applies\nto the game world only, keeping the GUI intact.\nIt should give significant performance boost at the cost of less detailed image."); + gettext("Autoscaling mode"); gettext("Shaders"); gettext("Shaders"); gettext("Shaders allow advanced visual effects and may increase performance on some video cards.\nThis only works with the OpenGL video backend."); @@ -303,6 +304,10 @@ fake_function() { gettext("Fraction of the visible distance at which fog starts to be rendered"); gettext("Opaque liquids"); gettext("Makes all liquids opaque"); + gettext("World-aligned textures mode"); + gettext("Textures on a node may be aligned either to the node or to the world.\nThe former mode sutis better things like machines, furniture, etc., while\nthe latter makes stairs and microblocks fit surroundings better.\nHowever, as this possibility is new, thus may not be used by older servers,\nthis option allows enforcing it for certain node types. Note though that\nthat is considered EXPERIMENTAL and may not work properly."); + gettext("Autoscaling mode"); + gettext("World-aligned textures may be scaled to span several nodes. However,\nthe server may not send the scale you want, especially if you use\na specially-designed texture pack; with this option, the client tries\nto determine the scale automatically basing on the texture size.\nSee also min_texture_size.\nWarning: this option is EXPERIMENTAL!"); gettext("Menus"); gettext("Clouds in menu"); gettext("Use a cloud animation for the main menu background."); diff --git a/src/util/directiontables.cpp b/src/util/directiontables.cpp index 587b0caf7..d700355bc 100644 --- a/src/util/directiontables.cpp +++ b/src/util/directiontables.cpp @@ -110,3 +110,11 @@ const v3s16 g_27dirs[27] = v3s16(0,0,0), }; +constexpr u8 wallmounted_to_facedir[6] = { + 20, + 0, + 16 + 1, + 12 + 3, + 8, + 4 + 2 +}; diff --git a/src/util/directiontables.h b/src/util/directiontables.h index 8464453bc..97e18343a 100644 --- a/src/util/directiontables.h +++ b/src/util/directiontables.h @@ -31,6 +31,8 @@ extern const v3s16 g_26dirs[26]; // 26th is (0,0,0) extern const v3s16 g_27dirs[27]; +extern const u8 wallmounted_to_facedir[6]; + /// Direction in the 6D format. g_27dirs contains corresponding vectors. /// Here P means Positive, N stands for Negative. enum Direction6D { diff --git a/src/wieldmesh.cpp b/src/wieldmesh.cpp index 0394cc9ea..98e7b5fa1 100644 --- a/src/wieldmesh.cpp +++ b/src/wieldmesh.cpp @@ -233,7 +233,7 @@ void WieldMeshSceneNode::setCube(const ContentFeatures &f, scene::IMesh *cubemesh = g_extrusion_mesh_cache->createCube(); scene::SMesh *copy = cloneMesh(cubemesh); cubemesh->drop(); - postProcessNodeMesh(copy, f, false, true, &m_material_type, &m_colors); + postProcessNodeMesh(copy, f, false, true, &m_material_type, &m_colors, true); changeToMesh(copy); copy->drop(); m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR); @@ -554,7 +554,7 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result) // add overlays (since getMesh() returns // the base layer only) postProcessNodeMesh(mesh, f, false, false, nullptr, - &result->buffer_colors); + &result->buffer_colors, f.drawtype == NDT_NORMAL); } } } @@ -622,7 +622,7 @@ scene::SMesh *getExtrudedMesh(ITextureSource *tsrc, void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f, bool use_shaders, bool set_material, const video::E_MATERIAL_TYPE *mattype, - std::vector *colors) + std::vector *colors, bool apply_scale) { u32 mc = mesh->getMeshBufferCount(); // Allocate colors for existing buffers @@ -670,6 +670,11 @@ void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f, } material.setTexture(2, layer->flags_texture); } + if (apply_scale && tile->world_aligned) { + u32 n = buf->getVertexCount(); + for (u32 k = 0; k != n; ++k) + buf->getTCoords(k) /= layer->scale; + } } } } diff --git a/src/wieldmesh.h b/src/wieldmesh.h index 87b8a8efe..0908d3ac2 100644 --- a/src/wieldmesh.h +++ b/src/wieldmesh.h @@ -137,4 +137,4 @@ scene::SMesh *getExtrudedMesh(ITextureSource *tsrc, const std::string &imagename */ void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f, bool use_shaders, bool set_material, const video::E_MATERIAL_TYPE *mattype, - std::vector *colors); + std::vector *colors, bool apply_scale = false);