tecs.gpu.materials
Material identity, numbering, and the component that selects one.
A material decides a fragment's color, coverage, surface normal, and lighting response. It is authored as a WGSL file under a material root, and every material found is folded into one generated fragment shader that dispatches on the id an instance carries. This module owns the names and the numbering; the backend owns the compiled dispatch.
world:spawn(
tecs.ecs.Transform2D(120, 80, 1, 0, 0, 64, 64),
tecs.gfx.Material(tecs.gpu.materials.id("rounded"), 0.25),
tecs.gfx.Tint(0.2, 0.6, 1.0, 1.0),
tecs.gfx.Renderable2D
)An entity carrying no Material draws through textured, which holds id zero for exactly that reason. Every other name takes an id from sorted order, so adding or removing a material renumbers the ones after it. Persisted state therefore stores the name and resolves it again, which is what the component's own snapshot does.
Material.param supplies one scalar from zero to one. The built-ins read it as follows:
ellipseuses the height fraction.ringuses the inner-radius fraction.roundeduses the corner-radius fraction.frameandlineuse a thickness fraction.capsuleuses a height fraction.pieuses a full-turn sweep fraction.staruses valley depth.emissiveuses how much of its own color the surface gives off.glyphuses the distance-field range.textured,circle,glyphalpha, andtriangleignore it.
Authoring a material#
A .wgsl file under a material root defines one material function:
fn material(frag: MaterialInput) -> MaterialOutput {
var result = materialDefaults();
let radius = mix(0.02, 0.20, frag.param);
result.albedo = textureSample(image, imageSampler, frag.uv) * frag.color;
result.coverage = -sdRoundedBox(frag.local, vec2(0.5), radius);
result.lit = 1.0;
return result;
}frag.local runs from -0.5 to 0.5 inside the quad. frag.uv, frag.color, and frag.param carry the image coordinates, tint, and instance parameter. frag.blended says whether the fragment reaches a pass that blends it. Start from materialDefaults, then set albedo, normal, orm, lit, emission, and coverage.
Coverage above zero keeps a fragment; zero or below discards it. The deferred lane does not blend partial coverage: a tint alpha below one routes the instance to the blended lane instead. A material that resolves an edge by discarding puts the edge in alpha where frag.blended is set, which is what textured does.
Adding materials to a build#
addRoot names a directory this module globs for *.wgsl. The same directory is read by the build step that produces the shader pack, so the ids this module assigns and the ids the compiled dispatch answers to come from one rule applied to one set of files. A root added after the pack was built renumbers the set and the backend rejects an id its dispatch does not carry, rather than drawing the wrong material.
Runtime assembly and reload are deliberately absent. A release links no shader assembler and loads a prebuilt pack, so a changed material body is a rebuild.
Module contents
Types
| Type | Kind | Description |
|---|---|---|
Material | struct | Selects one material for a renderable entity. |
Functions
| Function | Kind | Description |
|---|---|---|
addRoot | function | Adds a directory to search before the ones already known. |
count | function | Returns how many materials the build numbers. |
define | function | Registers a material name that no root supplies. |
find | function | Returns the id an instance carries to select a material, or nil. |
id | function | Returns the id an instance carries to select a material. |
install | function | Reads every root and assigns ids. |
name | function | Returns the material name an id selects. |
names | function | Returns every material name in id order. |
reset | function | Forgets every root and definition, so a test starts from the built-ins. |
revision | function | Returns the installed material numbering revision. |
Values
| Value | Kind | Description |
|---|---|---|
DEFAULT | variable | Takes id zero, because an instance with no Material writes a zero. |
MaterialComponent | variable | The process-wide Material component definition. |
Types#
Materialstruct#
Selects one material for a renderable entity.
Fields
id#
id: integerCaller-writable. Selects a material by an id id returned. Zero draws through textured.
param#
param: numberCaller-writable. Passes a value from zero to one to the material. What it means is the material's business.
Functions#
addRootfunction#
function addRoot(path: string): nilAdds a directory to search before the ones already known.
The directory is read by install rather than here, so adding a root costs nothing until the next name is resolved. A game's own material of a given name does not replace an engine one: two files with the same stem are one material, and the build that produces the dispatch decides which body wins.
Arguments
| Name | Type | Description |
|---|---|---|
path | string | a directory holding |
Returns
| Type | Description |
|---|---|
nil |
Raises
when the path is empty
countfunction#
function count(): integerReturns how many materials the build numbers.
The backend rejects an instance whose material id reaches this, so a packet built against a larger set than the pack holds fails rather than drawing the wrong material.
Returns
| Type | Description |
|---|---|
integer | the count, so the valid ids run from zero to one below it |
definefunction#
function define(name: string): nilRegisters a material name that no root supplies.
A generated material a build writes into the pack without writing a file is what this is for. It renumbers the set on the next install, exactly as adding a file does.
Arguments
| Name | Type | Description |
|---|---|---|
name | string | the material's name, matching the name the pack's dispatch uses |
Returns
| Type | Description |
|---|---|
nil |
Raises
when the name is empty
findfunction#
function find(name: string): integer?Returns the id an instance carries to select a material, or nil.
Arguments
| Name | Type | Description |
|---|---|---|
name | string | the material's file name without its extension |
Returns
| Type | Description |
|---|---|
integer? | the id, or nil when nothing carries that name |
idfunction#
function id(name: string): integerReturns the id an instance carries to select a material.
Arguments
| Name | Type | Description |
|---|---|---|
name | string | the material's file name without its extension |
Returns
| Type | Description |
|---|---|
integer | the id, counting from zero |
Raises
when nothing carries the name, listing what the build has, because a misspelled material is a build mistake rather than a runtime state
installfunction#
function install(): nilReads every root and assigns ids. Idempotent.
Resolving a name installs first, so a game rarely calls this. Adding a root puts it back on.
Returns
| Type | Description |
|---|---|
nil |
Raises
when a root cannot be listed
namefunction#
function name(value: integer): string?Returns the material name an id selects.
This is what a snapshot writes in place of the number, since an id holds only for the set of files the build was made from.
Arguments
| Name | Type | Description |
|---|---|---|
value | integer | an id as an instance carries it |
Returns
| Type | Description |
|---|---|
string? | the name, or nil when nothing carries that id |
namesfunction#
function names(): {string}Returns every material name in id order.
Returns
| Type | Description |
|---|---|
{string} | a fresh list the caller owns, indexed from one while the ids it describes count from zero |
resetfunction#
function reset(): nilForgets every root and definition, so a test starts from the built-ins.
Returns
| Type | Description |
|---|---|
nil |
revisionfunction#
function revision(): integerReturns the installed material numbering revision.
Returns
| Type | Description |
|---|---|
integer | A monotonic count that changes whenever the registry is rebuilt. |
Values#
DEFAULTvariable#
const DEFAULT: stringTakes id zero, because an instance with no Material writes a zero.
This name is a compatibility surface: it is what a snapshot with no material resolves to and what the generated dispatch numbers from.
MaterialComponentvariable#
const MaterialComponent: components.FFIComponent<Material>The process-wide Material component definition.