Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit3bb1da2647

README.md

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 writes packed vertices into a caller-supplied Writer 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.

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

-- Keep one writer and reset it for each build: a steady rebuild allocates nothing.
local w = debugDraw.writer()
debugDraw.reset(w)
debugDraw.writeBox(w, -1, 0, -1, 1, 2, 1, 1, 0.8, 0, 1)
debugDraw.writeLine(w, 0, 0, 0, 0, 3, 0, 0, 1, 1, 1)

local vtx = substrate.createBuffer({
    name = "my.vtx", type = "f32", len = buffer.len(w.buf) // 4,
    kind = "gpu", usage = { "vertex" },
})
vtx:writeBytes(w.buf)

-- One static sequential index buffer, grown only when the vertex count does.
local idx = substrate.createBuffer({
    name = "my.idx", type = "f32", len = w.count, kind = "gpu", usage = { "index" },
})
idx:writeBytes(debugDraw.buildSequentialIndexBytes(w.count))

The Draw pass draws w.count vertices; the bytes past them in w.buf are storage the writer keeps for its next build.

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.

Writer builders

A Writer is { buf: buffer, count: number }: buf grows by doubling and is kept across builds, count is how many vertices the current build holds.

  • writer(capacity?): a new, empty writer with room for capacity vertices.
  • reset(w): empty w for the next build, keeping its storage.

Each builder takes its points, rotation and colour as plain numbers (a rotation is a unit quaternion qx, qy, qz, qw; a colour is r, g, b, a), writes at the writer's end and returns the vertex count it added:

  • writeLine(w, ax, ay, az, bx, by, bz, r, g, b, a): one segment (2 verts).
  • writeBox(w, minX, minY, minZ, maxX, maxY, maxZ, r, g, b, a): axis-aligned box, 12 edges (24 verts).
  • writeOrientedBox(w, cx, cy, cz, qx, qy, qz, qw, hx, hy, hz, r, g, b, a): a posed box with half-extents hx, hy, hz.
  • writeWireSphere(w, cx, cy, cz, radius, r, g, b, a): three orthogonal rings.
  • writeWireCapsule(w, cx, cy, cz, qx, qy, qz, qw, radius, halfHeight, r, g, b, a): two rings + connectors.
  • writeCross(w, cx, cy, cz, size, r, g, b, a): a 3-axis marker (6 verts).
  • writeOctahedron(w, cx, cy, cz, size, r, g, b, a): a diamond marker (12 edges).
  • writeBillboard(w, cx, cy, cz, r, g, b, a): one camera-facing quad (6 triangle-soup verts); every corner stores the centre and its uv corner, and the debugBillboard surface shader expands it toward the camera.
  • writeWriter(dst, src): every vertex src holds, after the ones dst holds.
  • writeRaw(w, bytes): pre-packed vertex bytes (whole VERTEX_STRIDE records).

Parts-table builders

The append* builders draw the same shapes through the writer builders and append them to a { string } parts table, one packed string per vertex. They take points as { x, y, z } tables, a rotation as an { x, y, z, w } table, and a colour as an { r, g, b, a? } table (alpha 1 when left out, the default colour when nil), and return the vertex count they added:

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 })
vtx:writeBytes(debugDraw.buildVertexBytes(vtxParts))
  • appendLine(vtxParts, a, b, color?)
  • appendBox(vtxParts, min, max, color?)
  • appendOrientedBox(vtxParts, center, rotation, halfExtents, color?)
  • appendWireSphere(vtxParts, center, radius, color?)
  • appendWireCapsule(vtxParts, center, rotation, radius, halfHeight, color?)
  • appendCross(vtxParts, center, size, color?)
  • appendOctahedron(vtxParts, center, size, color?)
  • appendBillboard(vtxParts, center, color?)
  • buildVertexBytes(vtxParts): the parts joined into buffer-ready bytes.

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 (a billboard corner).
  • quatRotate(q, v): rotate a { x, y, z } vector by a unit quaternion table.
  • rotate(qx, qy, qz, qw, vx, vy, vz): quatRotate on scalars.
  • defaultColor(): the colour builders draw in when a caller states none.
  • buildSequentialIndexBytes(count): the fixed 0,1,2,…,count-1 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.