Textures

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).

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:<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:

default_cobble.png^[crack:10:1

[combine:<w>x<h>:<x1>,<y1>=<file1>:<x2>,<y2>=<file2>:...

  • <w>: width
  • <h>: height
  • <x>: x position, negative numbers allowed
  • <y>: y position, negative numbers allowed
  • <file>: texture to combine

Creates a texture of size <w> times <h> and blits the listed files to their specified coordinates.

Example:

[combine:16x32:0,0=default_cobble.png:0,16=default_wood.png

[resize:<w>x<h>

Resizes the texture to the given dimensions.

Example:

default_sandstone.png^[resize:16x16

[opacity:<r>

Makes the base image transparent according to the given ratio.

r must be between 0 (transparent) and 255 (opaque).

Example:

default_sandstone.png^[opacity:127

[invert:<mode>

Inverts the given channels of the base image. Mode may contain the characters "r", "g", "b", "a". Only the channels that are mentioned in the mode string will be inverted.

Example:

default_apple.png^[invert:rgb

[brighten

Brightens the texture.

Example:

tnt_tnt_side.png^[brighten

[noalpha

Makes the texture completely opaque.

Example:

default_leaves.png^[noalpha

[makealpha:<r>,<g>,<b>

Convert one color to transparency.

Example:

default_cobble.png^[makealpha:128,128,128

[transform<t>

  • <t>: transformation(s) to apply

Rotates and/or flips the image.

<t> can be a number (between 0 and 7) or a transform name. Rotations are counter-clockwise.

0  I      identity
1  R90    rotate by 90 degrees
2  R180   rotate by 180 degrees
3  R270   rotate by 270 degrees
4  FX     flip X
5  FXR90  flip X then rotate by 90 degrees
6  FY     flip Y
7  FYR90  flip Y then rotate by 90 degrees

Example:

default_stone.png^[transformFXR90

[inventorycube{<top>{<left>{<right>

Escaping does not apply here and ^ is replaced by & in texture names instead.

Create an inventory cube texture using the side textures.

Example:

[inventorycube{grass.png{dirt.png&grass_side.png{dirt.png&grass_side.png

Creates an inventorycube with grass.png, dirt.png^grass_side.png and dirt.png^grass_side.png textures

[fill:<w>x<h>:<x>,<y>:<color>

  • <w>: width
  • <h>: height
  • <x>: x position
  • <y>: y position
  • <color>: a ColorString.

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>:<file>

Blit the lower <percent>% part of <file> on the texture.

Example:

base.png^[lowpart:25:overlay.png

[verticalframe:<t>:<n>

  • <t>: animation frame count
  • <n>: current animation frame

Crops the texture to a frame of a vertical animation.

Example:

default_torch_animated.png^[verticalframe:16:8

[mask:<file>

Apply a mask to the base image.

The mask is applied using binary AND.

See notes: TEXMOD_UPSCALE

[sheet:<w>x<h>:<x>,<y>

Retrieves a tile at position x, y (in tiles, 0-indexed) from the base image, which it assumes to be a tilesheet with dimensions w, h (in tiles).

[colorize:<color>:<ratio>

Colorize the textures with the given color. <color> is specified as a ColorString. <ratio> is an int ranging from 0 to 255 or the word "alpha". If it is an int, then it specifies how far to interpolate between the colors where 0 is only the texture color and 255 is only <color>. If omitted, the alpha of <color> will be used as the ratio. If it is the word "alpha", then each texture pixel will contain the RGB of <color> and the alpha of <color> multiplied by the alpha of the texture pixel.

[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.

<hue> should be from -180 to +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 from -100 to +100, with a default of 0

<saturation> is from 0 to 100, with a default of 50

[multiply:<color>

Multiplies texture colors with the given color. <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.

<hue> should be from -180 to +180

<saturation> and <lightness> are optional, and both percentages.

<lightness> is from -100 to +100.

<saturation> goes down to -100 (fully desaturated) but may go above 100, allowing 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 a value from -127 to +127.

<brightness> is an optional value, from -127 to +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:<base64>

Embed a base64 encoded PNG image in the texture 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). The indexing starts from 0.

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 full param2 for 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) of param2 for palette indexing. The remaining three bits are describing rotation, as in wallmounted paramtype2. 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 = 17 is 2 * 8 + 1, so the rotation is 1 and the third (= 2 + 1) pixel will be picked from the palette.
    • param2 = 35 is 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 of param2 for palette indexing. The remaining five bits are describing rotation, as in facedir paramtype2. 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 = 17 is 0 * 32 + 17, so the rotation is 17 and the first (= 0 + 1) pixel will be picked from the palette.
    • param2 = 35 is 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 of param2 for palette indexing. The remaining two bits are describing rotation, as in 4dir paramtype2. 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 = 17 is 4 * 4 + 1, so the rotation is 1 and the fifth (= 4 + 1) pixel will be picked from the palette.
    • param2 = 35 is 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",
})