Skip to content

13. Material Templates

Bram Stout edited this page Mar 10, 2026 · 1 revision

MiEx exports out materials for you, so that you don't have to set them up after importing the export into your animation software. It generates these materials using material templates, which are defined in resource packs. Material templates are located in materials/minecraft/templates in any kind of resource pack.

Typically, these materials are exported out as USD data, but the material templates can also be set up to export the materials out into an accompanying _materials.json file. This is used when wanting to import worlds into animation software, where an import script or addon will read the json file in and import the materials from there.

A material template file has the following structure:

{
    "priority": <priority value>,
    "selection": [ <selection patterns> ],
    "include": [ <material template names> ],
    "shadingGroup": {
        <material terminal>: <input connection>,
        ...
    },
    "network": {
        <condition string>: {
            <node name>: {
                "type": <USD Shading Node type>,
                "attributes": {
                    <attribute name>:{
                        "type": <USD Shading Attribute type>,
                        "value": <value for the attribute>,
                        "connection": <input connection>,
                        "expression": <material template expression>
                    },
                    ...
                }
            },
            ...
        },
        ...
    }
}

When MiEx needs to create a material for a texture, it first checks the material templates in the top most active resource pack for any hits. If no material templates accept that texture, it'll go down through the enabled resource pack list until it finds a template that does accept the texture. Material templates can specify which textures they accept using the selection list. It's a list of selection string where it'll accept any texture whose resource identifier name matches the selection string. Selection strings may use wild cards (*) in the string. Especially with wild cards, a texture may be accepted by multiple material templates. In that case, MiEx will look at the priority value and select the material template with the highest priority. If multiple material templates have the same priority, it will be undefined behaviour.

Each material consists of a Material node which then has a bunch of Shader nodes inside of it. The Material node should be seen as a sort of group and it contains the terminals of the material. Generally, a terminal for a surface material, a volume material, and a displacement material. surface, volume, and displacement are valid terminals. You can also specify different terminals to use in different render contexts. In that case you would use, for example, <render context>:surface. This could be a render context for a specific renderer (ri for Renderman) or mtlx for MaterialX materials.

Network parts

Each material template specifies multiple potential network parts, where each network part is either included in the final material network or not based on a condition string. The condition string has the following syntax:

Syntax Description
@texture@ Checks if the texture exists
<Minecraft Texture resource identifier> Checks if a texture with that resource identifier exists. @texture@ can be included in this and then it will replace it with the texture resource identifier for which it's making the material. @texture@_emission would for example check if another texture of the same name but with _emission at the end of it exists.
<texture identifier>.a Checks if the given texture has an alpha channel. @texture@ can be included.
<texture identifier>.cutout Checks if the given texture has an alpha channel, but all alpha values are either 0 or 255. @texture@ can be included.
<texture identifier>.animated Checks if the given texture is animated, but not interpolated. @texture@ can be included.
<texture identifier>.interpolated Checks if the given texture is animated and interpolation is turned on. @texture@ can be included.
@biomeColor@ Checks if the mesh for which this material is being created has biome colours on it (or more general vertex colours, like the redstone power level).
@color.<vertex color set>@ Checks if the mesh for which this material is being created has a vertex color set of the given name.
@doubleSided@ Checks if the mesh for which this material is being created is double sided or not.
@shadingMode.<name>@ Checks if the face has the given shading mode applied to it.
<condition string>&&<condition string> Checks if both condition strings are true. You can combine as many conditions strings as you want.
!<condition string> Checks if the condition string is false.
An empty condition string will always evaluate to true.

If a condition string evaluates to true, then the shading nodes specified in that network part gets added to the final network. In a network part, you can define new shading nodes, but you can also override existing shading nodes. When constructing the final network, MiEx goes from top to bottom in the material template file.

Shading nodes

When defining a new shading node, you must give it a type that is a valid USD SDR type. It then may specify one or more attributes. Input attributes always start with inputs: and output attributes always start with outputs:. These should match the attributes available for the shading node type. If you want to connect an output into the input of another node, then you must specify that output. Each attribute that gets defined, must specify a type that is a valid USD attribute type. It can then optionally specify a value for it. There are three options to choose from for specifying a value:

The normal value option is specified with value, which then just gives the direct value. For values that have multiple components, use a JSON array.

The connection value option is specified with connection. Here you can specify an output attribute on another node that should be connected into this attribute. You specify another output attribute as follows: <node name>.<attribute name>. To help with composing complex networks when using multiple network parts, you can also use the following syntax: ${<node name>.<input attribute name>}. In this case, it will copy the value (whether a normal value, connection value, or expression value) of the specified input attribute into this attribute. This makes it easy for inserting a new node in the middle of a network defined further up. Keep in mind that MiEx goes in top to bottom order when constructing the final network, so if you have a network part where you first change an attribute on a node and then later on, in the same network part, use this special syntax to get the attribute's value, then it will be the value that you changed it to in that same network part, and not the value from before that network part got evaluated.

Network parts don't need to only define shading nodes. They can also specify overrides on existing nodes. This is done by just specifying a node with a name that already exists. When you do that, you can override pretty much everything. If you specify a new type, it'll override it to that new type. If you specify a new attribute, it will add that new attribute to the existing attributes. If you specify an already existing attribute, it will override that new attribute. When overriding attributes, if you specify a new type, it'll override the type. If you specify a new value, connection, or expression then it'll override those. An attribute can only have a value, connection, or an expression at a given time.

Expressions

The expression value option is specified with expression. Here you can choose from a few different expressions. This is useful for animated materials. Expressions are defined as ${expression_name} and they can optionally have arguments specified ${expression_name(arg_name=arg_value,arg2_name=arg2_value)}. By default the expression will search up info about the current texture, but you can also search up info about other textures using the following syntax ${texture_resource_name.expression_name}. The texture resource name can use @texture@ which will be replaced with the resource name of the current texture.

Expression Description
${frameId} The texture offset for animated textures. Can be directly put into inputs:translation on a UsdTransform2D node. Supports both float and float2 types. It supports the offset argument which takes an integer value providing the frame offset to apply to it. It also supports the powerOf2 argument. If reverse is true, then it will play the texture animation in reverse. If negative is true then it will multiply the value by -1. If normalised is true (default) then the value will be rescaled to be between 0.0 and 1.0.
${frameScale} The value to scale the UVs by for animated textures. Can be directly put into inputs:scale on a UsdTransform2D node. Supports both float and float2 types. It supports the inverse argument which will give the inverse of the frame scale value. It also supports the powerOf2 argument.
${interpFactor} A value going from 0 to 1 in a sawtooth motion, which can be a mix value in order to interpolate between the two frames for interpolated textures. A value of 0 means the frame specified by ${frameId} and a value of 1 means the frame specified by ${nextFrameId}. Supports both float and float2 types.
${biomeColor} The biome colour for a specific biome. This can be used to bake in biome colours for when vertex colours aren't supported. It supports the type argument which can be grass, foliage, or water and the biome argument which is then the name of the biome from which it should get the colours.
${animData} Outputs json data encoded as a string containing the texture animation data. This can be used to still get the texture animation data imported in even if the animation software doesn't support timesampled data.

Some renderers can't support textures whose resolutions aren't a power of two. They will then either upscale the image or add padding. In case it adds padding, the UVs could need to be adjusted to get rid of that padding. If you set the powerOf2 argument equal to true, then it will calculate the right values to cut out the padding. However, this won't work with random animated texture offsets.

Terminals

Once the shading nodes have been specified, they need to be connected to the material's terminals. This is done in the shadingGroup map. It's a JSON object that specifies the terminal name as the key and then the shading node output attribute that you want to connect to that terminal.

Including and overriding material templates

Material templates can also include other material templates, allowing you to more easily create different templates without having to copy and paste large network parts. You can include another template with the include key, which holds a list of material template names. A material template name is simply the file name of the material template JSON file, but without the .json extension. You can specify multiple templates. MiEx will then include them in order, where the next template can override the previous template. When including a material template, it'll load in the shadingGroup and network keys, but it will not include the priority and selection keys.

Resource packs higher up on the enabled resource pack list can override material templates in lower resource packs (as if the file was copy and pasted into that lower resource pack). This makes it easy to override a few attributes on some base template specified in a lower resource pack, and it all will then properly propagate throughout all of the material templates. Overriding an existing material template is as simple as just creating a material template file with the same name as the material template that you want to override. Other material templates that include that overridden material template (no matter in which resource pack they are) will now use your material template. You can then include the original material template by simply specifying its name in the include list. This is useful if you just want to override a couple of attributes.

When making materials, you most likely will need access to the UVs or vertex colours (for the biome colours). UVs are located in the st primvar which is of type float2/vector2. Vertex colours are located in the Cd primvar which is of type color3f.

Export materials to JSON

Some animation software don't properly support USD materials, which MiEx will try to export out to by default. To help with this, MiEx can also export out the materials into a JSON file which can then be read by an import script or addon in the animation software to set up the materials from that after importing. This way, you can still make use of the material templating system while also being able to import in all of the materials. In order to have a node be exported out into a JSON file, prefix the type of a node with JSON:. This will cause MiEx to put that node into the JSON file rather than the USD file. In order to have a terminal be specified in the JSON file, you prefix the terminal name with json:.

A common setup would be to create a material template that writes out the nodes to JSON, but to also specify a UsdPreviewSurface node without the JSON: prefix which is used as a placeholder. This still creates a USD material, which causes the animation software to create the material and assign it to the right meshes on import. A script or addon can then replace the placeholder material with the final material from the JSON file.

Clone this wiki locally