renderer
The renderer namespace — the engine's Luau API reference for renderer.
The renderer namespace — 40 functions.
globals/renderer/destroy
globals/renderer/destroy(handle: any) -> boolean
Free the GPU resource behind a MeshHandle / TextureHandle (the
GPU-destroy verb). Routes by the handle's category. The on-disk asset, if
any, is untouched. A CPU handle's :unload() frees the CPU copy separately.
globals/renderer/feature/create
globals/renderer/feature.create(ref: any, guid: string?) -> any
Instantiate a render feature so the engine calls its render(ctx) hook
every frame. ref is an AssetRef<renderFeature> whose init.luau returns
{ setup?, render, teardown? }. Returns a live RenderFeatureHandle (its
guid is the stable id, same as mesh/texture handles); tear it down with
renderer:destroy(handle). Pass guid to assign a specific id.
globals/renderer/feature/destroy
globals/renderer/feature.destroy(handleOrGuid: any) -> boolean
Tear down a live render feature by its RenderFeatureHandle OR its guid
string — the by-id path for when the handle was lost (e.g. across execute
calls). Same effect as renderer.destroy(handle). Returns true if a feature
was live under that id.
globals/renderer/feature/list
globals/renderer/feature.list() -> { { guid: string, identity: string } }
List every render feature currently live (running its render(ctx) each
frame). Each entry is { guid, identity } — the guid is the same id a
RenderFeatureHandle carries, so you can tear a feature down by guid even
after losing its handle (e.g. across separate execute calls).
globals/renderer/getRaytrace
globals/renderer/getRaytrace() -> boolean
Whether ray tracing is currently enabled.
globals/renderer/mainCameraView
globals/renderer/mainCameraView() -> { number }?
The main camera's inverse view-projection (column-major, 16 numbers)
followed by its world position (3 numbers) — {m0..m15, px,py,pz} — for
reconstructing world positions from the depth buffer in a ray-tracing pass.
Nil before the first render.
globals/renderer/material/create
globals/renderer/material.create(content: MaterialContent, key: string) -> MaterialHandle
Register (or update) a material's GPU resource under key and return its
MaterialHandle. content = { shader, properties, textures, aliases?, render? }.
The referenced textures must already be GPU-resident (the material assetType
materialises each slot via texRef:handle() before calling this) — this only
files the shader+property+texture-slot record the renderer binds. Mirrors
renderer.mesh.create / renderer.texture.create; idempotent upsert by key.
render sets pipeline render-state (cull / depthWrite / blend) — e.g.
{ cull = "front", depthWrite = false } for an inverted-hull outline program.
globals/renderer/material/describe
globals/renderer/material.describe(key: string) -> any
The recoverable definition ({ shader, properties, textures, name })
this module registered under key via renderer.material.create, or nil
for keys registered elsewhere (e.g. material assets resolved by the
assetType).
globals/renderer/material/destroy
globals/renderer/material.destroy(key: string) -> boolean
Drop a runtime material registered via renderer.material.create: clears
its recoverable definition, unregisters its runtime-resource stamp so it is no
longer swept into the material freeze/save flow, and frees the GPU record. Use
for transient materials (e.g. a preview swatch) that must not outlive their use.
The on-disk asset, if any, is untouched.
globals/renderer/material/setProperty
globals/renderer/material.setProperty(key: string, name: string, value: any) -> ()
Push one changed uniform property to a registered material's GPU record (frame-fast incremental update; no re-register). Keyed by the material's registry key.
globals/renderer/material/setTexture
globals/renderer/material.setTexture(key: string, slot: string, ref: string) -> ()
Push one changed texture slot to a registered material's GPU record. The
texture must already be GPU-resident (materialise it with texRef:handle()
first). Keyed by the material's registry key.
globals/renderer/mesh/buildClusters
globals/renderer/mesh.buildClusters(guid: string) -> string?
Build a cluster-LOD DAG (Nanite-style virtualized geometry) for the
static CPU mesh held under guid and return its serialized data.clusters
bytes. The native offline bake; returns nil when the mesh is degenerate or
the platform has no builder (the cross-platform runtime consumes pre-baked
clusters). Pair with renderer.mesh.uploadClusters.
globals/renderer/mesh/clusterComponents
globals/renderer/mesh.clusterComponents(clusterBytes: string) -> (ClusterComponents?, string?)
Split a cluster blob (from renderer.mesh.buildClusters) into its
GPU-ready component byte pools — the cluster vertex pool, the u32 index
pool, and the per-cluster record array — plus their counts. A pure decode
(no GPU work): upload the pools to compute buffers (compute.createBuffer +
compute.writeBufferBytes) to drive a cluster draw from Luau.
globals/renderer/mesh/create
globals/renderer/mesh.create(src: any, guid: string?) -> MeshHandle
Create (or fetch) a GPU mesh resource and return its MeshHandle. src:
a MeshCpuHandle from meshRef:load() (CPU→GPU upload under the asset's
guid, idempotent — returns the resident handle if already uploaded); raw
geometry {positions, indices, normals?, uvs?, colors?} (a new runtime
mesh); GPU compute buffers {vertexBuffer, indexBuffer, vertexCount, indexCount, aabbMin?, aabbMax?}; or a MeshHandle (returned as-is). NEVER
takes an AssetRef — load the CPU first.
globals/renderer/mesh/decode
globals/renderer/mesh.decode(zmsh: string) -> (MeshGeometry?, string?)
Decode engine-native ZMSH bytes back into geometry
{ positions, indices, normals?, uvs?, colors? }. Inverse of renderer.mesh.encode.
globals/renderer/mesh/encode
globals/renderer/mesh.encode(geom: MeshGeometry) -> string?
Encode raw geometry into engine-native ZMSH bytes (the on-disk mesh
payload). The CPU codec behind the mesh assetType's onCreate.
globals/renderer/mesh/encodeCpu
globals/renderer/mesh.encodeCpu(guid: string) -> string
Encode the CPU mesh resident under guid into ZMSH bytes. Reads the
ONE guid-keyed CPU store — meshRef:load() populates it for assets, and
renderer.mesh.readback(guid) populates it for a runtime mesh. Errors
loudly when no CPU mesh is resident under the guid.
globals/renderer/mesh/getVertices
globals/renderer/mesh.getVertices(guid: string) -> { any }
Read the vertices of the CPU mesh resident under guid — one entry per
vertex, { pos = {x,y,z}, normal = {x,y,z}, uv = {u,v} }. Reads the resident
CPU store directly (no re-decode). Errors when no CPU mesh is resident under
the guid — load it (meshRef:load()) or retain it (keepCpu) first.
globals/renderer/mesh/isCpuResident
globals/renderer/mesh.isCpuResident(guid: string) -> boolean
True if a CPU mesh is resident under guid in the guid-keyed CPU store.
globals/renderer/mesh/isResident
globals/renderer/mesh.isResident(guid: string) -> boolean
True if a GPU mesh is resident under guid.
globals/renderer/mesh/loadCpu
globals/renderer/mesh.loadCpu(ref: any) -> MeshCpuHandle
Load a .mesh asset's geometry into the ONE guid-keyed CPU store (the
Disk→CPU step) and return a CPU handle. The handle holds NO geometry — only
the guid plus counts and the per-handle read/encode/unload ops (which read
the Rust-side store). Called by meshRef:load(). DEFAULT lifecycle: upload
to the GPU then handle:unload(); the store is populated only by this call.
globals/renderer/mesh/readback
globals/renderer/mesh.readback(guid: string) -> MeshCpuHandle
Read a runtime GPU mesh's geometry back to CPU and return a
MeshCpuHandle for it — the GPU→CPU half of the runtime-mesh freeze path. A
mesh made with renderer.mesh.create keeps no CPU copy, so persisting it
(:encode() → asset.create("mesh", …)) reads it back here first. Yields
until the readback completes (a frame or two). After it returns the geometry
is resident in the guid-keyed CPU store: :getTriangles, :getVertices,
:getBounds, :encode, :unload all work. Errors if no GPU mesh is
resident in the vertex pool under guid.
globals/renderer/mesh/setVertices
globals/renderer/mesh.setVertices(guid: string, positions: { number })
Replace the resident CPU mesh's vertex positions (flat { x,y,z, ... })
under guid, IN PLACE — indices, normals/uvs, and skinning are preserved, the
AABB recomputes, and the GPU re-fetches the new geometry so it shows on screen.
The mesh must be CPU-resident (load it, or retain it with keepCpu). Errors with
the reason otherwise, or when the vertex count doesn't match.
globals/renderer/mesh/unloadCpu
globals/renderer/mesh.unloadCpu(guid: string)
Drop the CPU mesh resident under guid from the guid-keyed CPU store.
The explicit release for a runtime geometry mesh's recoverable definition.
globals/renderer/mesh/update
globals/renderer/mesh.update(handle: MeshHandle, src: any) -> MeshHandle
Overwrite the GPU resource behind handle IN PLACE, under the same guid,
from new geometry or compute buffers. Never writes a .mesh file — the
play-mode mutate path. A Model bound to handle.guid reflects the change
with no re-bind. Returns the same handle with refreshed bounds.
globals/renderer/mesh/uploadClusters
globals/renderer/mesh.uploadClusters(guid: string, clusters: string) -> boolean
Attach a cluster-LOD DAG (bytes from renderer.mesh.buildClusters) to
the GPU mesh keyed by guid, enabling the continuous-cut cluster draw path
for that mesh.
globals/renderer/raytraceCapability
globals/renderer/raytraceCapability() -> string
The active ray-tracing backend: "hardware" (GPU ray query) or
"compute" (software traversal — the path on devices without hardware ray
query, e.g. the web). The same ray-tracing features work on both.
globals/renderer/setRaytrace
globals/renderer/setRaytrace(enabled: boolean) -> ()
Enable or disable GPU ray tracing. While enabled the engine builds the scene acceleration structure each frame so ray-tracing render features can trace against it; disabling stops the build (so it costs nothing until a ray-traced effect is active). Required before any ray-traced shadows / AO / reflections render.
globals/renderer/texture/capture
globals/renderer/texture.capture(handle: TextureHandle) -> string
Request a CPU readback of the GPU texture behind handle (e.g. a camera's
rendered output). Returns a result key to pass to a TextureCpuHandle's
:encode() once the readback completes.
globals/renderer/texture/cpuCreate
globals/renderer/texture.cpuCreate(width: number, height: number, fill: any?) -> TextureCpuHandle
Allocate a blank CPU image (RGBA8) filled with a solid colour and return a
TextureCpuHandle. Compose into it with canvas:blit(src, x, y, w, h), then
canvas:encodeJpeg() / :encodePng() for the bytes; :unload() drops it.
globals/renderer/texture/create
globals/renderer/texture.create(src: any, guid: string?) -> TextureHandle
Create (or fetch) a GPU texture resource and return its TextureHandle.
src: a TextureCpuHandle from texRef:load() (CPU→GPU under the asset's
guid, idempotent); raw pixels {rgba, width, height, srgb?} (a flat
widthheight4 byte buffer, 0-255, row-major, top-to-bottom, RGBA); a
TextureHandle (returned as-is); or render-target dimensions
{width, height, name?} with no pixel source — an empty GPU texture a
render pass writes into (camera output, UI surface) and that samples like
any other texture. NEVER takes an AssetRef — load the CPU first.
globals/renderer/texture/decode
globals/renderer/texture.decode(ztex: string) -> (any, any, any)
Decode an engine-native ZTEX payload to RGBA8 pixels.
globals/renderer/texture/destroy
globals/renderer/texture.destroy(handle: TextureHandle)
Release the GPU texture behind handle. For an empty render-into texture
(camera output, UI surface) this also frees its render scratch; for an
uploaded runtime texture it drops the GPU resource (and any CPU shadow).
globals/renderer/texture/encode
globals/renderer/texture.encode(rgba: any, width: number, height: number, opts: any) -> (string?, string?)
Encode raw RGBA8 pixels into an engine-native ZTEX payload (the on-disk
texture content). The CPU codec behind the texture assetType's onCreate.
globals/renderer/texture/encodeFromImage
globals/renderer/texture.encodeFromImage(bytes: string, opts: any) -> (string?, string?)
Encode source image bytes (png/jpg/webp/…) into an engine-native ZTEX
payload. Used by the texture importer / assetType onChange.
globals/renderer/texture/info
globals/renderer/texture.info(ztex: string) -> (any, any)
Read the header of an engine-native ZTEX payload without copying the
pixels. Returns its format, dimensions, mip count, and filter ("nearest"
or "linear" — the sampler baked into the blob from the asset's
settings.filter).
globals/renderer/texture/isResident
globals/renderer/texture.isResident(guid: string) -> boolean
True if a GPU texture is resident under guid.
globals/renderer/texture/loadCpu
globals/renderer/texture.loadCpu(ref: any, encodeOpts: any?) -> TextureCpuHandle
Load a .texture asset's pixels into the ONE guid-keyed CPU store (the
Disk→CPU step) and return a CPU handle for per-pixel access (no GPU
readback). The handle holds NO pixels — only the guid plus dims and the
read/write/encode/unload ops (which read the Rust store). Called by
texRef:load(). DEFAULT: upload to the GPU then handle:unload().
globals/renderer/texture/readback
globals/renderer/texture.readback(guid: string) -> TextureCpuHandle
Read a runtime GPU texture's pixels back to CPU and return a
TextureCpuHandle for them — the GPU→CPU half of the runtime-texture freeze
path. A texture made with renderer.texture.create keeps no CPU copy, so
persisting it (:encode() → asset.create("texture", …)) reads it back
here first. Yields until the readback completes (a frame or two). After it
returns the pixels are resident in the guid-keyed CPU store: :readPixel,
:writePixel, :getInfo, :encode, :unload all work. Errors if no GPU
texture is resident under guid.
globals/renderer/texture/update
globals/renderer/texture.update(handle: TextureHandle, src: any) -> TextureHandle
Overwrite the GPU texture behind handle IN PLACE, under the same guid,
from new raw pixels. Never writes a .texture file — the play-mode mutate
path. Returns the same handle (refreshed dims).