# 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.
```lua
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)` clears the batch, calls every registered provider with
an `emit` handle, then rebuilds and uploads the combined vertex/index
buffers (grow-only capacity — a stable geometry count costs one buffer write
per tick, not a recreate).
- `debugViz.state()` returns the latest `{ vtx, idx, anchor, indexCount }` 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.
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
```lua
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()`.
## API
- `debugViz.registerProvider(name, fn)` — register (or replace) a provider.
`fn(emit)` runs once per edit-mode tick.
- `debugViz.anchorId()` — the anchor entity's id, or `nil` before the first
tick.
- `debugViz.update(dt)` — clear the batch, run every provider, rebuild and
upload the combined geometry.
- `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.
# debugDraw
Procedural geometry builders for GPU debug visualization — wireframe boxes,
spheres, capsules, crosses, octahedra, line segments, and camera-facing icon
billboards. Pure geometry: every builder appends packed vertex bytes to a
caller-supplied accumulator and returns how many vertices it added. Line
geometry is a non-indexed **line soup** (each line is two consecutive vertices)
and billboards are a **triangle soup** (each quad is six consecutive vertices),
so both draw through one static, grow-only sequential index buffer (0,1,2,…)
that never needs re-uploading per frame — the topology comes from the material.
Nothing here touches `compute.*` or `renderer.*` — callers own the buffers and
the Draw pass.
```lua
local debugDraw = require("@builtin::modules.debugDraw")
local vtxParts = {}
local verts = 0
verts += debugDraw.appendBox(vtxParts, bounds.min, bounds.max, { 1, 0.8, 0, 1 })
verts += debugDraw.appendLine(vtxParts, a, b, { 0, 1, 1, 1 })
local vtxBytes = debugDraw.buildVertexBytes(vtxParts)
local vtx = substrate.createBuffer({
name = "my.vtx", type = "f32", len = math.ceil(#vtxBytes / 4),
kind = "gpu", usage = { "vertex" },
})
vtx:writeBytes(vtxBytes)
-- One static sequential index buffer, grown only when the vertex count does.
local idx = substrate.createBuffer({
name = "my.idx", type = "f32", len = verts, kind = "gpu", usage = { "index" },
})
idx:writeBytes(debugDraw.buildSequentialIndexBytes(verts))
```
## Vertex layout
Every packed vertex matches the engine's standard `Vertex` layout (position,
normal, uv, joints, weights, node_index, tangent, color — 92 bytes,
little-endian) so the resulting buffer draws through the ordinary vertex
pipeline via a Draw pass. Debug geometry only needs position + color; every
other field is filled with a default.
## API
Each builder appends line-soup vertices to `vtxParts` and returns the vertex
count it added:
- `appendLine(vtxParts, a, b, color?)` — one segment (2 verts).
- `appendBox(vtxParts, min, max, color?)` — axis-aligned box, 12 edges (24 verts).
- `appendOrientedBox(vtxParts, center, rotation, halfExtents, color?)` — a posed box.
- `appendWireSphere(vtxParts, center, radius, color?)` — three orthogonal rings.
- `appendWireCapsule(vtxParts, center, rotation, radius, halfHeight, color?)` — two rings + connectors.
- `appendCross(vtxParts, center, size, color?)` — a 3-axis marker (6 verts).
- `appendOctahedron(vtxParts, center, size, color?)` — a diamond marker (12 edges).
- `appendBillboard(vtxParts, center, color?)` — one camera-facing quad (6
triangle-soup verts) at `center`; every corner stores `center` and its uv
corner, and the `debugBillboard` surface shader expands it toward the camera.
Helpers:
- `packVertex(x, y, z, color)` — pack one vertex; for building cached geometry blobs.
- `packVertexUV(x, y, z, u, v, color)` — pack one vertex carrying an explicit uv
(for billboard quads).
- `quatRotate(q, v)` — rotate a vector by a unit quaternion.
- `buildVertexBytes(vtxParts)` — concatenate the packed vertices into buffer bytes.
- `buildSequentialIndexBytes(count)` — the fixed `0,1,2,…,count-1` line-list index buffer.
- `VERTEX_STRIDE` — 92, the byte stride of one packed vertex.
See `@builtin::modules.debug_bounds` for a caller and
`@builtin::renderFeatures.debugViz` for the Draw pass that renders the batch.