Skip to content

API Model Appearances

Piggo41 edited this page Oct 16, 2026 · 9 revisions

Block and item model appearances

Lua API / Model appearances

Appearance fields

Add appearance to a block, item, or entity declaration to configure its model presentation. Gameplay properties, collision, and selection use their existing fields.

local appearance = {
    model = "mymod:item/robot", texture = "mymod:item/robot",
    display = {
        gui = { scale = 0.8, rotation = { x = 0, y = 15, z = 0 } },
        held = { position = { x = 0, y = 2, z = 0 } }
    }
}
Field Meaning
model Required model reference, registered key, or relative geometry path.
texture Texture reference/key/path for the default material slot.
material Settings for the default slot; its texture can come from appearance.texture.
materials Table of explicit material settings indexed by exported material_instance name. Every used slot must be bound.
position Model-unit offset {x,y,z}, default zero.
rotation Degrees {x,y,z}, default zero.
scale Nonnegative uniform number (0–1024) or {x,y,z} factors; default 1.
display Optional transforms for gui, held, ground, block, and entity; each accepts position, rotation, and scale.
animation {asset=animationRefOrKeyOrPath, clip=exactName, speed=1}; speed is 0–100. Entity instances can select another clip at runtime.
onPose Function receiving a pose context; {action=function(ctx) ... end} is also accepted.
mode "auto" (default) or "dynamic"; forces per-instance block presentation when dynamic.
layers Up to seven additional model appearance tables. Layers require their own model/material bindings and can have their own clips/callbacks. They inherit the outer appearance transform. Nested layer lists are unsupported.

Base transforms apply before the context-specific transform. In block context the origin is at the block's bottom center; 16 model units equal one block. Inventory, held, and dropped contexts have default display transforms, adjustable through display.

Metadata variants

Set render.variants[metadata].appearance to a complete appearance table. Omit it to inherit the top-level appearance, or set it to false to use ordinary block textures or an item icon. Without either appearance, ordinary rendering applies. Variant appearances replace the default; nested fields are not merged. Reuse Lua tables for shared definitions.

local idle = { model = "mymod:block/machine", texture = "mymod:block/machine_idle" }
local running = {
    model = "mymod:block/machine", texture = "mymod:block/machine_active",
    animation = { asset = "mymod:block/machine", clip = "animation.machine.running" }
}
-- Fields inside a block or item declaration:
local visuals = {
    appearance = idle,
    render = { variants = {
        [1] = { appearance = running },
        [2] = { appearance = false }
    } }
}

Keys are block metadata 0–15 or item damage/metadata 0–32767. The selected model applies in world, inventory, held, and dropped contexts as appropriate. Texture/icon/color fields outside appearance apply to ordinary rendering; model materials use their own textures and tint.

The Facing Stair Display uses four metadata appearances to rotate one model.

Block rendering

If the default appearance or any variant has a model clip animation, onPose, dynamic layer, or mode="dynamic", every state of that block type renders dynamically, including states using ordinary textures. Otherwise, all states use static chunk rendering. Animated textures change material pixels without making the geometry dynamic. Static model materials require opaque/cutout blending and opacity 1; use dynamic mode for transparent models. Dynamic block appearances currently draw within 64 blocks of the camera.

render.preset, render.bounds, and ordinary texture variants remain available for states without a model. Appearance models use their own geometry and transforms. Set collision, selection, opaque, normalCube, and light blocking through the existing block fields; appearances do not change gameplay properties or add world tile entities.

Material fields

Light blocking is a separate gameplay property. Set the block's lightOpacity=0 for an open model that should let world light pass through; changing its model or material does not automatically change light blocking.

local appearance = {
    model = "mymod:item/robot", texture = "mymod:item/robot",
    layers = {
        {
            model = "mymod:item/robot", texture = "mymod/textures/robot_glow.png",
            material = { blend = "additive", lighting = "unlit", opacity = 0.6 }
        }
    }
}
Field Supported values / default
texture Texture reference/key/path; required unless the default slot uses appearance.texture.
blend "opaque", "cutout" (default), "alpha", or "additive". Cutout uses alpha threshold 0.1.
lighting "world" (default) or "unlit". Unlit appearance does not emit world light.
cull Hide back faces when true; default false. Zero-thickness planes keep their separately exported front/back faces directional.
tint Integer RGB color 0x000000–0xFFFFFF; default white.
opacity 0–1; default 1.
uv {u=0,v=0,scaleU=1,scaleV=1} in normalized texture coordinates; values within ±10,000.

Solid/cutout layers render before transparent layers. Transparent layers follow declaration/material order; overlapping transparent geometry can show ordering artifacts. Prefer cutout for sharp transparent edges and additive for simple glow overlays.

For a block using this layered appearance, add mode="dynamic" to the outer table.

Pose context

onPose(ctx) runs after the automatic clip and receives:

Value Meaning
ctx.pose Pose operations; expires when this callback returns.
ctx.state.ageTicks Interpolated time: holder age for held items, dropped-item age for ground items, world time for blocks/GUI, or zero in a GUI without a world.
ctx.state.display "gui", "held", "ground", "block", or "entity".
ctx.state.metadata Block metadata or item damage/metadata.
ctx.state.x/y/z World position, or zero in GUI context.

State is copied presentation input. Mutating its table cannot change gameplay. Use the provided time; callback frequency depends on rendering and visibility. Every evaluation starts at rest, then applies the automatic clip, then calls Lua. A callback error disables that callback and reports the owning script. A rendering error suspends that appearance until an asset refresh.

The Clockwork Bird model item combines an exported clip with onPose. The Animated Machine adds state variants, material slots, and a glow layer.

Refresh and compatibility

Appearance declarations require the BetaMoon Java agent. Asset declarations and pose inspection are available independently of rendering. Appearance changes support hot reload, including static-to-dynamic and dynamic-to-static changes. Reload rebuilds visual data without replacing placed blocks or changing metadata, Lua tile data, or inventories. Other declaration changes follow the existing reload rules.

betamoon.assets:refresh() updates compatible models, clips, and textures. Block visuals update after refresh. Model replacements must preserve bundled part/locator names and cannot introduce unbound material slots. Animation replacements retain bundled clip names and cannot add new bone requirements to those clips. Geometry and clips used together are validated together; incompatible combinations try their bundled files together. If replacements remain unusable, the previous complete appearance stays active. Warnings explain when a consumer needs grouped fallback; an individual asset's source inspection describes that asset's selection.

Clone this wiki locally