Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit3bb1da2647

README.md

debugViz

The shared edit-mode debug-visualization core. Owns the ONE anchor entity, the ONE vertex-colour line material, the ONE batched vertex/index compute-buffer pair, and the ONE render feature that every debug-visualization gizmo draws through. Individual gizmos (bounds boxes, camera frustums, ...) are providers — small modules that append their own geometry into the shared batch every tick, instead of each owning their own GPU state.

entity.spawn("DebugViz"):component.add("DebugViz")

How it fits together

  • DebugViz.component owns the lifecycle: registers the built-in providers and calls debugViz.update(dt) every edit-mode tick; onDisable/onDestroy call debugViz.teardown().
  • debugViz.registerProvider(name, fn) registers fn(emit) under name. Registering an existing name replaces its function.
  • debugViz.update(dt) calls every registered provider with an emit handle, which records what each call asked to draw, then uploads the combined vertex/index buffers (grow-only capacity). The upload follows what changed: a tick that drew the same thing writes nothing, a tick where some draws changed in place (the same commands, the same vertex counts) writes only their vertices, and a tick where a draw appeared, went or changed its vertex count writes the whole batch.
  • emit.section(changed, draw) marks a part of a provider's drawing that changes apart from the rest: draw(emit) runs when changed is true or the section has not drawn before, and otherwise its last recording is replayed without running draw.
  • debugViz.state() returns the latest { vtx, idx, anchor, indexCount, uploadedBytes, shapes, icons } for the render feature to draw.
  • @builtin::renderFeatures.debugViz reads state() every frame and draws the batch through one Draw pass, using @builtin::shaders.debugGeometry — a solid-colour shader that reads each vertex's baked colour, so every provider can use its own colour in the same draw call. Each instanced shape (below) draws in a Draw pass of its own.

The draw needs an anchor entity with an IDENTITY world transform and a render-object (so the Draw pass has a valid instance slot and a material to override) — this module owns one, spawned lazily, internal so it stays out of the inspector and excluded from scene saves.

Writing a provider

local debugViz = require("@builtin::modules.debugViz")

local function tick(emit)
    emit.box({ x = -1, y = -1, z = -1 }, { x = 1, y = 1, z = 1 }, { 1, 0, 1, 1 })
    emit.line({ x = 0, y = 0, z = 0 }, { x = 0, y = 5, z = 0 }, { 1, 0, 1, 1 })
end

debugViz.registerProvider("my_gizmo", tick)

emit.box(min, max, color?) and emit.line(a, b, color?) append world-space geometry into the shared batch for this tick; color is an {r, g, b, a} array baked per-vertex (defaults to amber if omitted). A provider that needs to exclude the shared anchor entity from its own scene queries reads debugViz.anchorId().

A provider that hands the same packed geometry back every tick (emit.rawLineSoup(bytes, vertexCount)) costs nothing to upload while the geometry stays the same string.

Instanced shapes

emit.shape(entityId, key, bytes, vertexCount) draws a line-list blob in the local space of entityId. The blob is uploaded once under key and drawn once per entity that asks for it, through that entity's own world transform: any number of entities sharing a shape cost one GPU buffer, and moving them costs no per-tick work. A key names immutable bytes (geometry that changes takes a new key), the buffer lives for as long as some entity asks for the key each tick, and the entity needs a render object (a Mesh). debug_wireframe draws every mesh this way.

API

  • debugViz.registerProvider(name, fn, opts?) — register (or replace) a provider. fn(emit) runs once per edit-mode tick. opts.label is the name the Debug menu and the Rendering panel list the overlay under: text as written, or an i18n.text value (i18n.text("hud.my_gizmo")), which reads in the editor's language. Without one the overlay is listed by name. opts.default is whether the overlay starts on, and opts.available() whether it currently has anything to show.
  • debugViz.providers() — every provider that currently has something to draw, in registration order, as { name, label, enabled } with the label in the editor's language.
  • debugViz.anchorId() — the anchor entity's id, or nil before the first tick.
  • debugViz.update(dt) — run every provider and upload what their drawing changed.
  • debugViz.state() — the latest batched draw, or nil before the first tick.
  • debugViz.teardown() — tear down the feature, material, buffers, and anchor entity. Registered providers stay registered.
  • debugViz.MATERIAL — the registry key of the shared vertex-colour line material the render feature draws with.