Skip to content

Commit

Permalink
Real global textures (#6105)
Browse files Browse the repository at this point in the history
* Real global textures

* Add world-aligned textures
* Update minimal to support world-aligned tiles
* Update minimal
  • Loading branch information
numberZero authored and nerzhul committed Oct 15, 2017
1 parent 6bab695 commit 75320e7
Show file tree
Hide file tree
Showing 26 changed files with 469 additions and 160 deletions.
20 changes: 19 additions & 1 deletion builtin/settingtypes.txt
Expand Up @@ -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.
Expand Down Expand Up @@ -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

Expand Down
21 changes: 19 additions & 2 deletions doc/lua_api.txt
Expand Up @@ -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:

Expand Down Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion games/minimal/mods/experimental/depends.txt
@@ -1,2 +1,2 @@
default

stairs
51 changes: 51 additions & 0 deletions games/minimal/mods/experimental/init.lua
Expand Up @@ -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",
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions games/minimal/mods/stairs/init.lua
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
20 changes: 20 additions & 0 deletions minetest.conf.example
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
119 changes: 67 additions & 52 deletions src/client/tile.cpp
Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
/*
Expand All @@ -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();
}
}
Expand Down Expand Up @@ -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)
Expand Down
8 changes: 7 additions & 1 deletion src/client/tile.h
Expand Up @@ -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;
}

/*!
Expand Down Expand Up @@ -298,6 +299,8 @@ struct TileLayer
* a color then the color of the node owning this tile.
*/
video::SColor color;

u8 scale;
};

/*!
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 75320e7

Please sign in to comment.