Triangle vertex indices [numFaces * 3]
Per-vertex visibility from
mesh_vertex_visibility_mask; its length defines the valid vertex
range, so pass a view sized exactly numVertices rather than a larger
reused scratch buffer
Number of triangles
Output indices [numFaces * 3] worst case
Number of visible faces written. Slice output to 3 × this before
use. Everything past that point is left untouched — deliberately, to avoid a
second pass — so a reused buffer still holds the previous frame's indices and a
fresh one holds zeros. Uploading the whole buffer as an index range draws
stale or degenerate triangles rather than nothing, which is the failure this
return value exists to prevent.
Compact
facesto those whose three vertices are all visible.Writes original (un-remapped) vertex indices: on a slice change only the index buffer is rebuilt, while the vertex attribute buffers stay uploaded in full.
drawElementsnever fetches an unreferenced vertex, so culled vertices cost nothing to draw.The authored per-face index order is preserved exactly, so this is winding-agnostic; restoring front-facing winding under a reflected display permutation is a separate caller-owned post-pass (§5.4).
A face index
>= vertexMask.lengthdrops the whole face. The values come from the store — the viewer loads arbitrary, possibly corrupted datasets — and the two backends fail differently without this guard: Rust would read out of bounds and, beingpanic = "abort", trap with an opaque, uncatchableRuntimeError: unreachable, while here the read would yieldundefinedand silently diverge. The loader range-checks face indices up front and fails the node with aLoaderErrorbefore reaching either backend (§3.5 Stage 2), so on the sanctioned path this is unreachable; it is defense in depth, not a substitute for that gate.