Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit3bb1da2647

README.md

material_schema

A shader's declared property vocabulary, and the routing of an authored key onto it.

A material's properties belong to its shader. The uniform buffer is packed by reflected field name, so a key the shader does not declare is never read by anything: it sits in the material's property table, reads back as though it applied, and contributes nothing to the render. This module is the single place that answers "is this key real, is it a texture slot, or is it neither" — so every path that accepts material properties gives the same answer.

Consumed by material.assetType (the .material asset path) and by renderer.material.create (the runtime GPU path).

API

local schema = require("modules.material_schema")

local s = schema.forShader("pbr")           -- nil when the shader can't resolve

schema.route(s, "base_color", {1,0,0,1})    -- "property", "base_color"
schema.route(s, "color", {1,0,0,1})         -- "property", "base_color"  (alias)
schema.route(s, "base_color_texture", tex)  -- "texture",  "base_color_texture"
schema.route(s, "baseColor", {1,0,0,1})     -- "unknown",  "baseColor"

schema.declaredNamesList(s)                 -- "alpha_cutoff, anisotropy, …"
schema.undeclaredMessage(name, key, "pbr", s)

schema.acceptsSlot(s, "base_color_texture") -- true: the shader declares the slot
schema.acceptsSlot(s, "base_color")         -- false: a property, not a texture slot
schema.declaredSlotsList(s)                 -- "base_color_texture, emissive_texture, …"
schema.undeclaredSlotMessage(name, slot, "pbr", s)

schema.isTextureSlotType("texture")         -- true
schema.isTextureSlotType("texture_array")   -- true
schema.isTextureSlotType("color")           -- false

A properties.yaml entry of type texture or texture_array declares a texture slot; every other type except buffer declares a property.

A texture reaches the bind group only through a slot the shader declares, since the material interface is generated from those slots. renderer.material.create, renderer.material.setTexture and a material asset's setTexture / writeTexture refuse any other slot through acceptsSlot and report it with undeclaredSlotMessage. A schema that holds no names at all (a shader whose vocabulary has not been read yet) accepts every slot.

Routing order

route applies the alias table first, then decides by surface:

  1. Alias — color → base_color, but only when the shader declares the canonical name and not the alias, so a shader that genuinely exposes color keeps its own meaning.
  2. Texture — the schema declares the name as a texture slot, or the value is itself a texture (handle / AssetRef<texture>). A texture stored as a uniform property reads back via getProperty but never reaches the bind group, so the value's own shape decides regardless of the key.
  3. Property — the schema declares the name as a uniform property.
  4. Unknown — neither. Nothing reads it; the caller warns with undeclaredMessage and drops it.

When the shader can't be resolved

forShader returns nil for a shader that is not registered yet (a runtime shader still compiling, a name that resolves later). Callers stay permissive in that case rather than reject every key against an empty vocabulary — the check applies where the vocabulary is actually known.