Venus Lua API › Rendering

Material

Shader + textures + constants — colors, UV transforms, render queue.

authored lua/venus_lua/src/lua_material.rs

Material

A material resource: a shader plus its bound textures, up to 16 color/vector slots, per-slot UV
scale/offset, and a render queue. Materials are named manager resources (not scene components) —
created by name, looked up by name, cloned, and destroyed. Colors are stored linear internally;
the Color/MainColor accessors convert to and from sRGB at the boundary, while
SetColorLinear bypasses conversion.

Static calls use the type table (Material.Create("name")); instance methods use colon syntax on a
handle (material:SetShader(...)).

Types

Material — an opaque handle (userdata) to a material resource. A handle may be null or
stale (its resource destroyed); it is safe to keep and pass around, and a method never raises on
one. Null/stale behavior is not uniform: ID() logs a warning and returns nil, but the color,
UV, and render-queue getters return a silent default (see the known issue below), and every setter
silently no-ops. Passing a non-Material value where a method expects self raises a Lua type
error. tostring(material) yields Material{name}, or Material{NULL} if the handle is dead.

Known issue — dead-handle reads fail silently. Only ID() reports a null/stale handle (logs
NotFound, returns nil). The other getters return a manager default with no warning:
MainColor()/Color() return transparent black (0,0,0,0), UVScaleOffset() returns
(1,1,0,0), and RenderQueue() returns 65535 (RENDER_QUEUE_USE_DEFAULT). Out-of-range color
indices and UV slots behave the same way, and every setter no-ops without warning
(material_manager.rs:375, :392, :518, :547). Use Material.Exists(mat) to test liveness —
do not infer it from a getter’s result.

Creation & Destruction

Material.Create( string name ) : Material
Creates a new named material resource. Returns nil and logs InvalidArgument on failure — a
duplicate name (AlreadyExists) or pool exhaustion (OutOfMemory) both surface as the same log.

Material.Get( string name ) : Material
Looks up an existing material by name. Returns nil if none matches (no warning — a miss is a valid result).

Material.Exists( Material material ) : boolean
Returns true if the handle refers to a live material. Safe on null, stale, or foreign (non-material
userdata / nil) arguments — returns false without raising or warning. This is the reliable
liveness check.

Material.Clone( Material material ) : Material
Duplicates material into a new anonymous resource (not registered by name; gets its own GPU
slot) and returns it. Returns nil and logs NotFound if the source is null/stale. Note this is a
static call — Material.Clone(mat), not mat:Clone().

material:Destroy( ) : boolean
Marks the material for deferred destruction. Returns true if the handle was valid and got marked,
false if it was already null/invalid. (Unlike most instance methods, this one returns a value.)

Functions

Read-only queries. See the known issue above — only ID() reports a dead handle; the rest return
silent defaults.

material:ID( ) : string
The material’s unique name. Returns nil and logs NotFound on a null/stale handle, or on an
anonymous clone (which has no registered name).

Properties (get / set)

Setters return void and silently no-op on a null/stale handle or out-of-range index (no
warning). Color indices and UV/texture slots are 0-based: color indices 0..15
(MAX_MATERIAL_COLORS = 16), UV slots 0..7 (MAX_MATERIAL_TEXTURES = 8).

RenderQueue

material:RenderQueue( ) : integer — the material’s render-queue override, or 65535
(use-shader-default) if unset or the handle is dead.
material:SetRenderQueue( integer queue ) : void — stores the raw 16-bit override (recomputes
the effective 8-bit sort key internally).

MainColor

material:MainColor( ) : Vector4 — color slot 0 as sRGB RGBA.
material:SetMainColor( Vector4 srgba ) : void — accepts sRGB RGBA (stored linear).
Alias for Color(0) / SetColor(0, …) — the same slot-0 color (material_manager.rs:365, :370).

Color

material:Color( integer index ) : Vector4 — the color at 0-based index (0..15) as sRGB
RGBA. Out-of-range or dead handle → (0,0,0,0).
material:SetColor( integer index, Vector4 srgba ) : void — sets 0-based index from sRGB
RGBA (stored linear).

UVScaleOffset

material:UVScaleOffset( integer slot ) : Vector4(scaleU, scaleV, offsetU, offsetV) for
0-based texture slot (0..7). Out-of-range or dead handle → (1,1,0,0).
material:SetUVScale( integer slot, number scaleU, number scaleV ) : void — sets scale only,
preserving the existing offset.
material:SetUVOffset( integer slot, number offsetU, number offsetV ) : void — sets offset
only, preserving the existing scale.

Methods

Actions that mutate the material. All silently no-op on a null/stale handle (no warning).

material:SetShader( Shader shader ) : void
Sets the material’s shader, pulling the pipeline handle, texture count, and default render queue from
the ShaderManager. No-ops and logs NotFound if the shader handle does not resolve.

material:SetTexture( integer slot, Texture texture ) : void
Binds texture at the given 0-based texture slot (0..7), copying its GPU image/sampler and
dimensions. No-ops and logs NotFound if the texture handle does not resolve.

material:SetColorLinear( integer index, Vector4 linearRgba ) : void
Sets the color at 0-based index to a linear-space value, verbatim (no sRGB conversion). The
counterpart write path to SetColor; there is no linear getter exposed to Lua (read back through
Color, which re-applies sRGB).

Example

-- Clone a base material and tint it, then swap its albedo texture and tile the UVs.
function Script.OnEnable(self)
    local mat = Material.Clone(Material.Get("crate"))
    mat:SetMainColor(Vector4.new(0.9, 0.2, 0.2, 1.0))   -- sRGB red tint (== SetColor(0, ...))
    mat:SetTexture(0, Texture.Get("crate_red"))         -- a registered texture, by name
    mat:SetUVScale(0, 2.0, 2.0)                          -- slot 0, 2x2 tiling
    self.material = mat
end

function Script.OnDisable(self)
    if Material.Exists(self.material) then
        self.material:Destroy()
    end
end