Real global textures (#6105)
authorVitaliy <silverunicorn2011@yandex.ru>
Sun, 15 Oct 2017 07:34:14 +0000 (10:34 +0300)
committerLoïc Blot <nerzhul@users.noreply.github.com>
Sun, 15 Oct 2017 07:34:14 +0000 (09:34 +0200)
* Real global textures

* Add world-aligned textures
* Update minimal to support world-aligned tiles
* Update minimal

26 files changed:
builtin/settingtypes.txt
doc/lua_api.txt
games/minimal/mods/experimental/depends.txt
games/minimal/mods/experimental/init.lua
games/minimal/mods/experimental/textures/experimental_tiled.png [new file with mode: 0644]
games/minimal/mods/stairs/init.lua
minetest.conf.example
src/client/tile.cpp
src/client/tile.h
src/content_mapblock.cpp
src/content_mapblock.h
src/defaultsettings.cpp
src/mapblock_mesh.cpp
src/mapblock_mesh.h
src/mapnode.cpp
src/mapnode.h
src/network/networkprotocol.h
src/nodedef.cpp
src/nodedef.h
src/particles.cpp
src/script/common/c_content.cpp
src/settings_translation_file.cpp
src/util/directiontables.cpp
src/util/directiontables.h
src/wieldmesh.cpp
src/wieldmesh.h

index 11651c9b245f3a0d8fad60197b1b0a064f80a3a3..4f7a5c769bc4bed3f5927d44a73f723211914e85 100644 (file)
@@ -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
 
index c93f06c8a58a0d7bed6bdd1277b88957ed9c8efe..91ea900d0ce9f366e51e851402cdc9f93d68b62b 100644 (file)
@@ -284,11 +284,19 @@ on top of `cobble.png`.
 
 ### Advanced texture modifiers
 
-#### `[crack:<n>:<p>`
+#### Crack
+* `[crack:<n>:<p>`
+* `[cracko:<n>:<p>`
+* `[crack:<t>:<n>:<p>`
+* `[cracko:<t>:<n>:<p>`
+
+Parameters:
+* `<t>` = tile count (in each direction)
 * `<n>` = animation frame count
 * `<p>` = 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.
index 3a7daa1d73f7d85b9485257ee09e11bfcefe1eaa..3296b1e0a4cb511efeefb6e4b1c3c88cc4927252 100644 (file)
@@ -1,2 +1,2 @@
 default
-
+stairs
index 657148dce263433e0362ede8211a091e835afdec..578c353643d04b81b24e7de4410084894d498b97 100644 (file)
@@ -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 (file)
index 0000000..67397b9
Binary files /dev/null and b/games/minimal/mods/experimental/textures/experimental_tiled.png differ
index c3bbc561e05c3998c0851a5e5c6b509e96e9aab2..74b7890eedaf2a2298e040008c868dd9ceab56b0 100644 (file)
@@ -2,7 +2,7 @@ stairs = {}
 
 -- Node will be called stairs:stair_<subname>
 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_<subname>
 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,
index 6eca94401c4e8f1615d814cc8ccd8485bd25973f..28281fc38b5deeb802bc373213dc6e5a08933612 100644 (file)
 #    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
 
 #    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
index a2284eed39531747ef8506045ddb9cb412bd819f..9321a45863c291fd160c1388eab31e77155f16ee 100644 (file)
@@ -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][:<tiles>]:<frame_count>:<frame>
                        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 <tiles> 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<u32> size, u8 tiles, video::IVideoDriver *driver)
+{
+       core::dimension2d<u32> strip_size = crack->getDimension();
+       core::dimension2d<u32> frame_size(strip_size.Width, strip_size.Width);
+       core::dimension2d<u32> 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<s32> 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<u32> dim_dst = dst->getDimension();
-       // Dimension of original image
-       core::dimension2d<u32> 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<u32> 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<u32> dim_crack_scaled(
+       core::dimension2d<u32> 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<s32>(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)
index d4e25ea6a900bc2e4769dd9fef27ae430c856c82..e69dbe0c7bb3d6d7352dd74f28f907a53662ee13 100644 (file)
@@ -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;
index 4e32e27caceee1802ba7af78fbe0c416b2d5f31d..c2a25037c3b842a97eed1624bbc4ee852590e749 100644 (file)
@@ -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]) {
index aee550d34c358a63705c946cd3e94bd72eb7d6e0..93e3fe53cac5a4064a360fdb90bacb6ea7b15725 100644 (file)
@@ -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);
 
index 58ea0c77a6bd7a2b8dd02399eb19faaeff285f46..7612ceb44c0629bd39b7eb4c006f21a75afb9228 100644 (file)
@@ -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");
index 7708fb43878e60779130bd0c187374cea16ddc21..5d298da3dd6d3c1a8ec593443f68349dba4f2cc7 100644 (file)
@@ -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<FastFace> &dest)
+       v3f tp, v3f p, v3s16 dir, v3f scale, std::vector<FastFace> &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<u8, u32>(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);
                }
        }
index 13cee537c5e5cf736ae076a55bd362a10c01aea7..13e3da9a1da0338789f52f232c16c4ae5c88dc6c 100644 (file)
@@ -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.
         */
index c46719a681b8695af0aa577629dd4b064f572f2a..9d3459173d4eb83ac1ea0079f6416938dfab3eaa 100644 (file)
@@ -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 <string>
 #include <sstream>
@@ -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<aabb3f> &fixed = nodebox.fixed;
-               int facedir = n.getFaceDir(nodemgr);
+               int facedir = n.getFaceDir(nodemgr, true);
                u8 axisdir = facedir>>2;
                facedir&=0x03;
                for (aabb3f box : fixed) {
index 338ae41fe340290eb73dda9e98b899d1d3e4452a..2abc4b97b3739941e44d2707a072011d81910312 100644 (file)
@@ -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;
 
index 76e90b560208b95077bd51cff3b89257c5f9b2ae..ba3926492a545dde020fb6844cfa5c92948cb411 100644 (file)
@@ -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
 */
 
index d13f93068a3829f2bcd854d641aaed7b893bfe2d..209bea86a5f182ebe71a1c88596e991e07e05425 100644 (file)
@@ -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<AlignStyle>(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<std::vector<FrameSpec>>();
+               if (!layer->frames) {
+                       layer->frames = std::make_shared<std::vector<FrameSpec>>();
                }
-               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
index 23100a2ed6ceb2a6cbaac981cac8c0ce2fda90e3..790c7fd28ad822e4d191e7504b180cbeb81baf51 100644 (file)
@@ -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
index 053b68fff07959abf7f6ac744265be322b5f3a00..0a6651ce453624a68c1a02b034cfe735480f3bca 100644 (file)
@@ -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);
index 8f08de1e5f838babd84b74bba6f93c59944d892d..4d8cdd352a1bf621f0989d4fbf002168468a6d0a 100644 (file)
@@ -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);
index ffb960784392de47099b36c62d4b9f85ce29bc80..cebe6bdf1d4cba716048c8d16d5d1c96da66443f 100644 (file)
@@ -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.");
index 587b0caf7edae1755428bec664a63d6ce7bd9911..d700355bc0e0c5fa86da702e585c59805a513d02 100644 (file)
@@ -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
+};
index 8464453bcea66757a03fafb24fadb725b1a16373..97e18343afe4c2b09a82d078130c216cfe0d04a6 100644 (file)
@@ -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 {
index 0394cc9eadd1e8761af042049ccb3ef3343342f5..98e7b5fa1b2a6b4314253443bd038533014b9e19 100644 (file)
@@ -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<ItemPartColor> *colors)
+       std::vector<ItemPartColor> *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;
+                       }
                }
        }
 }
index 87b8a8efe047452796f0cebf4cf66572b6ccc224..0908d3ac28f42edac8d57f26d0195ea9ea066132 100644 (file)
@@ -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<ItemPartColor> *colors);
+               std::vector<ItemPartColor> *colors, bool apply_scale = false);