Textures¶
Introduction¶
Mods should generally prefix their textures with modname_, e.g. given
the mod name foomod, a texture could be called:
foomod_foothing.png
Textures are referred to by their complete name, or alternatively by stripping out the file extension:
- e.g.
foomod_foothing.png - e.g.
foomod_foothing
Supported texture formats are PNG (.png), JPEG (.jpg) and Targa (.tga).
Luanti generally uses nearest-neighbor upscaling for textures to preserve the crisp look of pixel art (low-res textures). Users can optionally enable bilinear and/or trilinear filtering. However, to avoid everything becoming blurry, textures smaller than 192px will either not be filtered, or will be upscaled to that minimum resolution first without filtering.
This is subject to change to move more control to the Lua API, but you can rely on low-res textures not suddenly becoming filtered.
Loading order¶
Texture names are looked up in the following order. Top has the lowest priority.
- Client:
$path_share/textures/base/pack - Server: mod-provided textures, in their
texturesdirectory - Server: game textures, in
<game path>/textures - Server:
$path_share/textures/server - Server:
override.txtin the path specified by the settingtexture_path - Server:
override.txtin<game path>/textures - Client: path specified by the setting
texture_path - Client:
override.txtin the path specified by the settingtexture_path
For details on texture packs, see texture_packs.md.
Texture modifiers¶
There are various texture modifiers that can be used to let the client generate textures on-the-fly. The modifiers are applied directly in sRGB colorspace, i.e. without gamma-correction.
Notes¶
TEXMOD_UPSCALE: The texture with the lower resolution will be automatically upscaled to the higher resolution texture.
Texture overlaying¶
Textures can be overlaid by putting a ^ between them.
Warning: If the lower and upper pixels are both semi-transparent, this operation does not do alpha blending, and it is not associative. Otherwise it does alpha blending in srgb color space.
Example:
default_dirt.png^default_grass_side.png
default_grass_side.png is overlaid over default_dirt.png.
See notes: TEXMOD_UPSCALE
Texture grouping¶
Textures can be grouped together by enclosing them in ( and ).
Example: cobble.png^(thing1.png^thing2.png)
A texture for thing1.png^thing2.png is created and the resulting
texture is overlaid on top of cobble.png.
Escaping¶
Modifiers that accept texture names (e.g. [combine) accept escaping to allow
passing complex texture names as arguments. Escaping is done with backslash and
is required for ^, : and \.
Example: cobble.png^[lowpart:50:color.png\^[mask\:trans.png
Or as a Lua string: "cobble.png^[lowpart:50:color.png\\^[mask\\:trans.png"
The lower 50 percent of color.png^[mask:trans.png are overlaid
on top of cobble.png.
Advanced texture modifiers¶
Crack¶
[crack:<vertical_frames>:<crack_frame>[cracko:<vertical_frames>:<crack_frame>[crack:<grid>:<vertical_frames>:<crack_frame>[cracko:<grid>:<vertical_frames>:<crack_frame>
This draws a step of the crack animation on the texture.
It overlays a scaled frame of the crack texture, crack_anylength.png,
over a texture, with options for alpha and blitting multiple times for
tile sheets / animated textures.
With [crack, the crack will be overlaid over the whole base
texture, while with [cracko, the crack will only be overlaid
over fully opaque base texture regions.
Parameters:
grid(optional): Creates a square grid of multiple cracks, with side lengthgrid, e.g. the number 3 creates a 3x3 grid of cracks (integer [imagesize], default 1)vertical_frames: Amount of times the grid created bygridwill be repeated vertically (integer [imagesize], often1)crack_frame: Crack animation frame (integer [imageframe], counting starts at 0)
Note: The resulting crack (or cracks) texture will always be scaled to the size of the base texture.
Examples:
default_stone.png^[crack:1:2
- Cracked stone
-
Use the third crack progression (
2because of 0-indexing) and draw it once on a stone texturedefault_lava_source_animated.png^[crack:8:2
-
Cracked lava
- Use the 3rd crack progression and draw it 8 times on top of the base texture
-
The base texture should have 8 vertical frames as well
example.png^[crack:3:1:0
-
Blits a 3x3 grid of crack textures, each at frame 0, on
example.png
[combine:<w>x<h>:<parts>¶
<w>: width of resulting texture (integer [imagesize])<h>: height of resulting texture (integer [imagesize])<parts>: Colon-separated (:) list of locationsx,yand textures to blit; written in the form<x>,<y>=<texture>for each texture. Can be empty.
x and y are integers [imageframe].
A <texture> (in <textures>) can contain texture modifiers, but these must
be escaped according to the rules in Escaping.
Creates a texture of size <w> times <h> and blits the listed files to their
specified coordinates. The background is black and transparent (#00000000).
Example:
[combine:16x32:0,0=default_cobble.png:0,16=default_wood.png
[resize:<w>x<h>¶
<w>: width (integer [imagesize])<h>: height (integer [imagesize])
Resizes the texture to the given dimensions.
Example:
default_sandstone.png^[resize:16x16
[opacity:<ratio>¶
Makes the base image transparent according to the given ratio.
This multiplies the alpha value of each pixel of the base texture
with ratio/255 and rounds the result to the closest integer afterwards.
ratio is an integer in range [0, 255]. 0 = transparent, 255 = opaque.
Example:
default_sandstone.png^[opacity:127
[invert:<mode>¶
modeis a string which may contain the charactersr,g,banda.
The channels corresponding to the occurring characters (red, green, blue
and alpha) will be inverted (set to 255 - value).
Example:
default_apple.png^[invert:rgb
[brighten¶
Interpolates 50:50 between the color of each pixel of the base texture and white.
Example:
tnt_tnt_side.png^[brighten
[noalpha¶
Makes the texture completely opaque by setting the alpha channel of the base
texture to the maximum value (255).
As the red, green and blue channels aren't pre-multiplied with alpha in PNGs, this might reveal hidden colors of otherwise transparent portions of an image.
Example:
default_leaves.png^[noalpha
[makealpha:<r>,<g>,<b>¶
Convert the given color to transparency.
Pixels of the base texture having the exact same RGB color will have their
alpha value set to 0.
As the red, green and blue channels are kept, the original color can be
restored using [noalpha (which will however also make originally
semi-transparent portions of the image opaque).
r: red (integer in range [0, 255])g: green (integer in range [0, 255])b: blue (integer in range [0, 255])
Example:
default_cobble.png^[makealpha:128,128,128
[transform<t>¶
Rotates and/or flips the image.
<t> is the concatenation of either integers or names identifying
transformations from the following table:
0 I identity (no transformation)
1 R90 rotate by 90° counterclockwise
2 R180 rotate by 180° counterclockwise
3 R270 rotate by 270° counterclockwise
4 FX flip X
5 FXR90 flip X then rotate by 90° counterclockwise
6 FY flip Y
7 FYR90 flip Y then rotate by 90° counterclockwise
Transformation names are case-insensitive.
Example:
default_stone.png^[transformFXR90
[inventorycube{<top>{<left>{<right>¶
Renders a cube similar to how it would look in the inventory with the three given textures using simple software rendering.
The resulting image size depends on the size of the source texture.
Escaping does not apply here and ^ is replaced by & in texture names
instead.
Example:
[inventorycube{grass.png{dirt.png&grass_side.png{dirt.png&grass_side.png
Renders a cube with grass.png, dirt.png^grass_side.png and
dirt.png^grass_side.png textures
[fill:<w>x<h>:<x>,<y>:<color>¶
<w>: width (integer [imagesize])<h>: height (integer [imagesize])<x>: x position (integer [u32])<y>: y position (integer [u32])<color>: aColorString.
Creates a texture of the given size and color, optionally with
an <x>,<y> position. An alpha value may be specified in the
ColorString.
The optional <x>,<y> position is only used if the [fill is being overlaid
onto another texture with '^'.
When [fill is overlaid onto another texture it will not upscale or change
the resolution of the texture, the base texture will determine the output
resolution.
Examples:
[fill:16x16:#20F02080
texture.png^[fill:8x8:4,4:red
[lowpart:<percent>:<texture>¶
Blit the lower <percent>% part of <texture> on the base texture.
<percent> is an integer with range [0, 100].
<texture> can contain escaped texture modifiers.
Example:
base.png^[lowpart:25:overlay.png
[verticalframe:<t>:<n>¶
<t>: animation frame count, integer in range [imagesize]<n>: current animation frame (integer [imageframe])
Crops the texture to a frame of a vertical animation.
Example:
default_torch_animated.png^[verticalframe:16:8
[mask:<texture>¶
textureis an escaped texture modifier
Applies a bitwise and to all RGBA values of texture and the base texture.
If a pixel of the base texture is out of bounds on texture, it is preserved.
See notes: TEXMOD_UPSCALE
[sheet:<w>x<h>:<x>,<y>¶
<w>: sheet width in tiles (integer [imagesize])<h>: sheet height in tiles (integer [imagesize])<x>: x position in tiles (integer [imageframe], starts at 0)<y>: y position in tiles (integer [imageframe], starts at 0)
Retrieves the tile of a base image at position x, y,
which is assumed to be a tilesheet with dimensions
w, h (in tiles).
[colorize:<color>:<ratio>¶
coloris a ColorString (should not use alpha)ratiois an optional integer in range [0, 255] or the string"alpha"
Colorizes the textures with the given color.
Interpolates between color and the pixel colors of the base texture as specified
by the ratio:
- Defaults to the alpha of
colorif omitted (NOT RECOMMENDED) - If it's an integer from 0 (only base texture color) to 255 (only
color), the resulting color of a pixel isratio * color + (255 - ratio) * base_tex_color. - If
ratio = "alpha", the texture pixel's alpha value determines the ratio per pixel (NOT RECOMMENDED) - NOTE: Due to bugs, we currently can only recommend the base image to
avoid semitransparent pixels entirely (all pixels should have alpha = 0 or alpha = 255),
ratioto be explicitly specified, and the alpha incolorto equal 255. Anything else leads to undefined behavior.
[colorizehsl:<hue>:<saturation>:<lightness>¶
Colorize the texture to the given hue. The texture will be converted into a greyscale image as seen through a colored glass, like "Colorize" in GIMP. Saturation and lightness can optionally be adjusted.
All arguments are integers.
<hue> should be in range [-180, 180]. The hue at 0° on an HSL color wheel is
red, 60° is yellow, 120° is green, and 180° is cyan, while -60° is magenta
and -120° is blue.
<saturation> and <lightness> are optional adjustments.
<lightness> is in range [-100, 100], with a default of 0
<saturation> is in range [0, 100], with a default of 50
[multiply:<color>¶
Multiplies the RGB values of the base texture per pixel with the RGB
values of color; the alpha value of color is ignored.
<color> is specified as a ColorString.
Result is more like what you'd expect if you put a color on top of another
color, meaning white surfaces get a lot of your new color while black parts
don't change very much.
A Multiply blend can be applied between two textures by using the overlay modifier with a brightness adjustment:
textureA.png^[contrast:0:-64^[overlay:textureB.png
[screen:<color>¶
Apply a Screen blend with the given color. A Screen blend is the inverse of a Multiply blend, lightening images instead of darkening them.
<color> is specified as a ColorString.
A Screen blend can be applied between two textures by using the overlay modifier with a brightness adjustment:
textureA.png^[contrast:0:64^[overlay:textureB.png
[hsl:<hue>:<saturation>:<lightness>¶
Adjust the hue, saturation, and lightness of the texture. Like "Hue-Saturation" in GIMP, but with 0 as the mid-point.
All 3 arguments are integers.
<hue> should be in range [-180, 180]
<saturation> and <lightness> are optional, and both percentages.
<lightness> is in range [-100, 100].
<saturation> is in range [-100, 1000]. -100 is fully desaturated,
0 is no change and positive values increase saturation. Values
above 100 allow for even muted colors to become highly saturated.
[contrast:<contrast>:<brightness>¶
Adjust the brightness and contrast of the texture. Conceptually like GIMP's "Brightness-Contrast" feature but allows brightness to be wound all the way up to white or down to black.
<contrast> is an integer in range [-127, 127].
<brightness> is an optional integer in range [-127, 127].
If only a boost in contrast is required, an alternative technique is to hardlight blend the texture with itself, this increases contrast in the same way as an S-shaped color-curve, which avoids dark colors clipping to black and light colors clipping to white:
texture.png^[hardlight:texture.png
[overlay:<file>¶
Applies an Overlay blend with the two textures, like the Overlay layer mode
in GIMP. Overlay is the same as Hard light but with the role of the two
textures swapped, see the [hardlight modifier description for more detail
about these blend modes.
See notes: TEXMOD_UPSCALE
[hardlight:<file>¶
Applies a Hard light blend with the two textures, like the Hard light layer mode in GIMP.
Hard light combines Multiply and Screen blend modes. Light parts of the
<file> texture will lighten (screen) the base texture, and dark parts of the
<file> texture will darken (multiply) the base texture. This can be useful
for applying embossing or chiselled effects to textures. A Hard light with the
same texture acts like applying an S-shaped color-curve, and can be used to
increase contrast without clipping.
Hard light is the same as Overlay but with the roles of the two textures
swapped, i.e. A.png^[hardlight:B.png is the same as B.png^[overlay:A.png
See notes: TEXMOD_UPSCALE
[png:<data>¶
datais a base64-encoded PNG bytestring
Creates a texture from an embedded base64-encoded PNG image
in the data string.
You can produce a valid string for this by calling
core.encode_base64(core.encode_png(tex)),
where tex is pixel data. Refer to the documentation of these
functions for details.
You can use this to send disposable images such as captchas
to individual clients, or render things that would be too
expensive to compose with [combine:.
IMPORTANT: Avoid sending large images this way.
This is not a replacement for asset files, do not use it to do anything
that you could instead achieve by just using a file.
In particular consider core.dynamic_add_media and test whether
using other texture modifiers could result in a shorter string than
embedding a whole image, this may vary by use case.
See notes: TEXMOD_UPSCALE
Hardware coloring¶
The goal of hardware coloring is to simplify the creation of colorful nodes. If your textures use the same pattern, and they only differ in their color (like colored wool blocks), you can use hardware coloring instead of creating and managing many texture files. All of these methods use color multiplication (so a white-black texture with red coloring will result in red-black color).
Static coloring¶
This method is useful if you wish to create nodes/items with the same texture, in different colors, each in a new node/item definition.
Global color¶
When you register an item or node, set its color field (which accepts a
ColorSpec) to the desired color.
An ItemStack's static color can be overwritten by the color metadata
field. If you set that field to a ColorString, that color will be used.
Tile color¶
Each tile may have an individual static color, which overwrites every
other coloring method. To disable the coloring of a face,
set its color to white (because multiplying with white does nothing).
You can set the color property of the tiles in the node's definition
if the tile is in table format.
Palettes¶
For nodes and items which can have many colors, a palette is more suitable. A palette is a texture, which can contain up to 256 pixels. Each pixel is one possible color for the node/item. You can register one node/item, which can have up to 256 colors.
Palette indexing¶
When using palettes, you always provide a pixel index for the given
node or ItemStack. The palette is read from left to right and from
top to bottom. If the palette has less than 256 pixels, then it is
stretched to contain exactly 256 pixels (after arranging the pixels
to one line). Palette colors are indexed in range [0, 255].
Examples:
- 16x16 palette, index = 0: the top left corner
- 16x16 palette, index = 4: the fifth pixel in the first row
- 16x16 palette, index = 16: the pixel below the top left corner
- 16x16 palette, index = 255: the bottom right corner
- 2 (width) x 4 (height) palette, index = 31: the top left corner. The palette has 8 pixels, so each pixel is stretched to 32 pixels, to ensure the total 256 pixels.
- 2x4 palette, index = 32: the top right corner
- 2x4 palette, index = 63: the top right corner
- 2x4 palette, index = 64: the pixel below the top left corner
Using palettes with items¶
When registering an item, set the item definition's palette field to
a texture. You can also use texture modifiers.
The ItemStack's color depends on the palette_index field of the
stack's metadata. palette_index is an integer, which specifies the
index of the pixel to use.
Linking palettes with nodes¶
When registering a node, set the item definition's palette field to
a texture. You can also use texture modifiers.
The node's color depends on its param2, so you also must set an
appropriate paramtype2:
paramtype2 = "color"for nodes which use their fullparam2for palette indexing. These nodes can have 256 different colors. The palette should contain 256 pixels.paramtype2 = "colorwallmounted"for nodes which use the first five bits (most significant) ofparam2for palette indexing. The remaining three bits are describing rotation, as inwallmountedparamtype2. Division by 8 yields the palette index (without stretching the palette). These nodes can have 32 different colors, and the palette should contain 32 pixels. Examples:param2 = 17is 2 * 8 + 1, so the rotation is 1 and the third (= 2 + 1) pixel will be picked from the palette.param2 = 35is 4 * 8 + 3, so the rotation is 3 and the fifth (= 4 + 1) pixel will be picked from the palette.
paramtype2 = "colorfacedir"for nodes which use the first three bits ofparam2for palette indexing. The remaining five bits are describing rotation, as infacedirparamtype2. Division by 32 yields the palette index (without stretching the palette). These nodes can have 8 different colors, and the palette should contain 8 pixels. Examples:param2 = 17is 0 * 32 + 17, so the rotation is 17 and the first (= 0 + 1) pixel will be picked from the palette.param2 = 35is 1 * 32 + 3, so the rotation is 3 and the second (= 1 + 1) pixel will be picked from the palette.
paramtype2 = "color4dir"for nodes which use the first six bits ofparam2for palette indexing. The remaining two bits are describing rotation, as in4dirparamtype2. Division by 4 yields the palette index (without stretching the palette). These nodes can have 64 different colors, and the palette should contain 64 pixels. Examples:param2 = 17is 4 * 4 + 1, so the rotation is 1 and the fifth (= 4 + 1) pixel will be picked from the palette.param2 = 35is 8 * 4 + 3, so the rotation is 3 and the ninth (= 8 + 1) pixel will be picked from the palette.
To colorize a node on the map, set its param2 value (according
to the node's paramtype2).
Conversion between nodes in the inventory and on the map¶
Static coloring is the same for both cases, there is no need for conversion.
If the ItemStack's metadata contains the color field, it will be
lost on placement, because nodes on the map can only use palettes.
If the ItemStack's metadata contains the palette_index field, it is
automatically transferred between node and item forms by the engine,
when a player digs or places a colored node.
You can disable this feature by setting the drop field of the node
to itself (without metadata).
To transfer the color to a special drop, you need a drop table.
Example:
core.register_node("mod:stone", {
description = "Stone",
tiles = {"default_stone.png"},
paramtype2 = "color",
palette = "palette.png",
drop = {
items = {
-- assume that mod:cobblestone also has the same palette
{items = {"mod:cobblestone"}, inherit_color = true },
}
}
})
Colored items in craft recipes¶
Craft recipes only support item strings, but fortunately item strings can also contain metadata. Example craft recipe registration:
core.register_craft({
output = core.itemstring_with_palette("wool:block", 3),
type = "shapeless",
recipe = {
"wool:block",
"dye:red",
},
})
To set the color field, you can use core.itemstring_with_color.
Metadata field filtering in the recipe field are not supported yet,
so the craft output is independent of the color of the ingredients.
Soft texture overlay¶
Sometimes hardware coloring is not enough, because it affects the whole tile. Soft texture overlays were added to Luanti to allow the dynamic coloring of only specific parts of the node's texture. For example a grass block may have colored grass, while keeping the dirt brown.
These overlays are 'soft', because unlike texture modifiers, the layers are not merged in the memory, but they are simply drawn on top of each other. This allows different hardware coloring, but also means that tiles with overlays are drawn slower. Using too much overlays might cause FPS loss.
For inventory and wield images you can specify overlays which
hardware coloring does not modify. You have to set inventory_overlay
and wield_overlay fields to an image name.
To define a node overlay, simply set the overlay_tiles field of the node
definition. These tiles are defined in the same way as plain tiles:
they can have a texture name, color etc.
To skip one face, set that overlay tile to an empty string.
Example (colored grass block):
core.register_node("default:dirt_with_grass", {
description = "Dirt with Grass",
-- Regular tiles, as usual
-- The dirt tile disables palette coloring
tiles = {{name = "default_grass.png"},
{name = "default_dirt.png", color = "white"}},
-- Overlay tiles: define them in the same style
-- The top and bottom tile does not have overlay
overlay_tiles = {"", "",
{name = "default_grass_side.png"}},
-- Global color, used in inventory
color = "green",
-- Palette in the world
paramtype2 = "color",
palette = "default_foilage.png",
})