Builtin Library
README.md
material_utils
High-level wrappers around the __material FFI global. Convenience
helpers for the common material-authoring operations: create a named
material, apply it to an entity, query / set material properties, and
bind textures.
Exposed as the Material Luau global via --!global (auto-injected by
the prelude). Material lives ON the Model / SkinnedModel component — there
is no standalone Material component, because a material only renders where
there is a mesh. To apply a material, use Material.apply() (which errors if
the entity has no model) or set the model's material field directly:
entity(id).component.add("Model", { model = "cube", material = "gold" }).
PascalCase aliases (Material.Create, Material.Apply, etc.) are kept
for back-compat with the wider codebase — 88+ call sites across demos,
scenes, components, guides, and the engine test suite use the legacy
names. The lowercase forms are canonical.
Exports
Canonical (lowercase) surface:
Material.create(name: string, opts: MaterialOpts?) -> MaterialRef?— register a named material; returns the AssetRef envelope.Material.apply(entityId: string, materialRef: MaterialRefOrName) -> boolean— set thematerialfield on the entity's Model / SkinnedModel. Errors if neither is present.Material.exists(name: MaterialRefOrName) -> boolean— registry predicate.Material.getPropertyNames(name: MaterialRefOrName) -> { string }?— list property names exposed by a material.Material.getProperty(name: MaterialRefOrName, prop: string) -> any— read a property value.Material.setProperty(entityId: string, property: string, value: any)— set a property on the material assigned to an entity's Model / SkinnedModel.Material.setTexture(name: MaterialRefOrName, slot: string, textureRef: string) -> boolean— bind a texture slot.
PascalCase back-compat aliases:
Material.Create(name, opts_or_shader, props?) -> MaterialRef?— accepts the legacy(name, shaderString, opts)3-arg form OR the canonical(name, opts)form.Material.Apply/Material.Exists/Material.GetPropertyNames/Material.GetProperty/Material.SetTexture— direct aliases for the lowercase counterparts.Material.SetProperty(materialName, property, value)— writes to the REGISTERED material by name (NOT on a component). Different from lowercasesetProperty.
Types:
Color = { number } | { r: number, g: number, b: number, a?: number }Textures = { [string]: string }MaterialOpts = { shader?, color?, roughness?, metallic?, emissive?, textures?, [string]: any }MaterialRef = { __ref?, type?, name?, guid? }MaterialRefOrName = MaterialRef | string
Usage
local mat = Material.create("gold", {
color = { 1, 0.85, 0.2 },
metallic = 1,
roughness = 0.15,
})
-- entityId must already carry a Model / SkinnedModel (a material only
-- renders on a mesh); apply errors otherwise.
Material.apply(entityId, mat) -- AssetRef envelope
Material.apply(entityId, "gold") -- bare name also works
Material.setProperty(entityId, "roughness", 0.3)
Material.setTexture("gold", "albedo", "@builtin::textures.gold")
Notes
Material.applyaccepts either an AssetRef envelope or a bare name — the model'smaterialfield auto-wraps strings.- Material is a field on the Model / SkinnedModel component (declared as
material, anAssetRef<material>). There is no standalone Material component —apply/setPropertyroute to whichever model is present and error when neither is. Material.setPropertywrites the property on the material assigned to the entity's model;Material.SetPropertywrites on the REGISTERED material by name. They behave differently — keep them straight.- All argument validation raises on bad input rather than silently no-oping, so call sites surface mistakes early.