Skip to content

Item and block models

Status: Implemented (API 1.15). glTF 2.0 models for items (in every place an item is drawn) and for blocks and block entities, level-of-detail meshes, the file’s alpha mode, and normal / roughness maps for shader packs. Per-block animation calls and block entity data in the model are Planned.

Entity Models and Animation puts a glTF model on an entity type. The same models also work for items and blocks: a sword with real geometry, a lamp that sways, a machine block with moving parts, a statue on a pedestal. Everything here is client-side rendering; the server, the network and players without the mod see the vanilla item or block.

Reference: render, model, Rendering.

1. An item model

cl.on("lifecycle.register", function()
  cl.render.item_model("my_mod:crystal_staff", "my_mod:models/item/staff.glb", {
    default_clip = "glow",                        -- looped by every copy of the item (optional)
  })
end)

From then on the item draws the model everywhere: in first and third person, in the inventory, the hotbar and tooltips, in item frames, dropped on the ground, on armor stand heads and on shelves. A later call for the same item replaces its model. Vanilla items work too ("minecraft:stick").

Placement. With fit = true (the default) the model’s bounding box is centred on the item and its largest side is scaled to one block, so the model looks like a full block item. scale then makes it bigger or smaller, rotation = { x, y, z } (degrees) turns a model that faces another way (glTF faces +Z), and offset moves it (blocks). With fit = false the model’s origin is the item’s centre and one glTF metre is one block.

Display contexts. How the item sits in each place comes from the same display block vanilla item models use. Contexts you leave out use the block item defaults (minecraft:block/block):

cl.render.item_model("my_mod:crystal_staff", "my_mod:models/item/staff.glb", {
  display = {
    gui = { rotation = { 20, 30, 0 }, scale = 0.9 },
    firstperson_righthand = { rotation = { 0, 90, -20 }, translation = { 0, 2, 0 }, scale = 0.5 },
    ground = { translation = { 0, 3, 0 }, scale = 0.3 },
  },
})

The names are vanilla’s: thirdperson_righthand, thirdperson_lefthand, firstperson_righthand, firstperson_lefthand, head, gui, ground, fixed (item frames), on_shelf. Translations are in pixels (16 per block) and clamped like vanilla’s. The left hands default to the right ones and are mirrored. In C and C++ display is the same JSON as a string: {"gui": {"rotation": [20, 30, 0], "scale": [0.9, 0.9, 0.9]}}.

The enchantment glint shows on the model’s materials that have no normal or metallic-roughness map. The GUI image of an item is cached like vanilla items; an item with a default_clip is redrawn every frame.

2. A block model

cl.render.block_model("my_mod:statue", "my_mod:models/block/statue.glb", {})
cl.render.block_model("minecraft:chest", "my_mod:models/block/strongbox.glb", { yaw_offset = 0 })

The model’s origin is the bottom centre of the block, one metre is one block, and the model turns with the block: +Z of the model points where a facing property points (a chest’s front, a furnace’s front) or along a rotation property in 22.5 degree steps (signs, banners, skulls). yaw_offset, scale and offset work like for entity models.

  • Block entities (chests, signs, banners, skulls, beds, …) draw the model instead of their vanilla renderer. keep_vanilla = true draws both (an overlay).
  • Any other block, a mod block too, draws the model on top of its block model. To show only the glTF model, give the block an empty block model ({"elements": []} in assets/my_mod/models/block/statue.json).

Like block entities, block models are drawn within 64 blocks of the camera and skipped when hidden behind terrain. Sections without such blocks cost nothing; the engine looks only at the blocks a section’s palette contains.

3. Level of detail

Big models can carry simpler versions of themselves. Two ways work:

  • Names: next to a node (or mesh) "statue" put "statue_lod1", "statue_lod2", … (any case). Blender exports them as they are.
  • MSFT_lod: the node’s MSFT_lod extension lists the simpler nodes; MSFT_screencoverage in the node’s extras says when to switch.

Without MSFT_screencoverage level 1 draws from 16 blocks, level 2 from 32, level 3 from 64. Set your own distances (blocks), and optionally a distance beyond which the model is not drawn:

local m = cl.render.block_model("my_mod:statue", "my_mod:models/block/statue.glb", {})
m:set_lod_distances({ 12, 32 })        -- level 1 from 12 blocks, level 2 from 32
m:set_lod_distances({ 12, 32 }, 96)    -- and nothing from 96 blocks
print(m:lod_levels())                  -- 3

LOD applies to entity, block and item models in the world; the hand and the GUI always draw full detail. Parts of the model without simpler versions draw at every level. Skinned LOD meshes use the same skeleton as the full mesh, so animations stay the same.

4. Transparency

The model’s materials keep their glTF alpha mode: OPAQUE ignores the texture’s alpha, MASK cuts away pixels below the material’s alphaCutoff (0.5 by default), BLEND draws see-through (only fully transparent pixels are dropped). Shader packs follow the same rule. Use MASK for leaves, fences and hair cards, and BLEND for glass and ghosts.

5. Normal and roughness maps in shader packs

Materials with a normal map or a metallic-roughness map get per-pixel lighting and a soft highlight without shader packs. With a shader pack, the same maps reach the pack’s entity programs as the LabPBR normals and specular textures (converted when the model loads: the normal map’s scale and the roughness / metallic factors are baked in), so packs that support LabPBR resource packs light mod models the same way. Vanilla entities and items keep the pack’s defaults.

6. Performance

Models are uploaded once; each drawn copy is one GPU skinning job and ordinary instanced draws, nothing is allocated per frame. Measured on an RTX 4060 Ti at 2560x1440, render distance 16: ten walking mobs with a 36,000-triangle, 66-bone model cost about 0.3 ms of GPU time and fifty about 1.3 ms (1.8 million triangles a frame), while 121 statues of 6,300 triangles with two LOD levels cost under 0.01 ms because only the nearest few draw full detail. Give big models LOD meshes and keep game models to a few thousand triangles where you can.

7. Checking support

if cl.has("item.models") and cl.has("block.models") then ... end

Feature names: item.models, block.models, block_entity.models, model.lod, model.alpha_mode, model.pbr_shaderpacks.