nx_vertex_pipeline.nx
buildroot/runtime/nx_vertex_pipeline.nx
about
nx_vertex_pipeline.nx -- world-space vertex -> screen-space vertex.
The missing glue between mesh-emitter output and rasterizer input.
nx_voxel_mesh (game-engine) emits world-space vertices in Q14.
nx_raster_triangle (this repo) consumes screen-space vertices in
pixel coords + Q14 depth. This primitive bridges them: applies an
MVP matrix, perspective divide, viewport mapping.
Generic substrate -- any 3D pipeline (CAD viewer, scientific
visualisation, voxel game, raymarched fractal-explorer) needs
this transform. Lives in nishi-core per the placement cardinal.
Vertex layout in both input and output (matches nx_voxel_mesh +
nx_raster_triangle exactly so the buffers compose directly):
verts[i*4 + 0] = x in Q14
verts[i*4 + 1] = y in Q14
verts[i*4 + 2] = z in Q14
verts[i*4 + 3] = packed_color (u32 RGBA in low 32 bits)
On output:
x_pixel = (ndc_x + 1) * viewport_w / 2
y_pixel = (1 - ndc_y) * viewport_h / 2 (Y flipped for screen)
z_depth = (ndc_z + 1) * Q / 2 (Q14, in [0, Q])
color unchanged (per-vertex passthrough)
Perspective-divide guard: vertices behind the camera (w <= 0) get
flagged via out-of-screen pixel coords (-1, -1) so the rasterizer's
existing bounding-box clip silently drops them. Per cardinal
`feedback-honest-perf-verdict`, a proper near-plane clip is the v2
follow-up (currently named in the loss audit below).
Loss audit:
- Perspective divide is integer-division of two Q14 values to a
Q14 result. Loses 14 fractional bits per step. Acceptable for
voxel-block-aligned geometry where pixel-edge precision is
chunky by design.
- Viewport map uses integer division at the final stage; sub-pixel
centroid drift up to 0.5 pixel.
- W <= 0 vertices are coarse-dropped (no Sutherland-Hodgman clip).
Triangles spanning the near plane will have wrong rasterization.
dependencies 2 imports · 1 importers
imports: nx_syscalls.nxnx_tier.nx
imported by: nx_render_pass.nx
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 57 | const NX_VP_Q: nx_int = 16384 |
| 60 | const NX_VP_VERT_STRIDE: nx_int = 4 |
| 63 | const NX_VP_OFF_X: nx_int = 0 |
| 64 | const NX_VP_OFF_Y: nx_int = 1 |
| 65 | const NX_VP_OFF_Z: nx_int = 2 |
| 66 | const NX_VP_OFF_COLOR: nx_int = 3 |
| 71 | const NX_VP_OFFSCREEN_X: nx_int = -100000 |
| 72 | const NX_VP_OFFSCREEN_Y: nx_int = -100000 |
functions
| 82 | func _vp_mat4_mul_vec4(m: *i64, x: nx_int, y: nx_int, z: nx_int, w: nx_int, out: *i64) called by 1: nx_vertex_pipeline_transform_one |
| 94 | func nx_vertex_pipeline_transform_one( |
| 146 | func nx_vertex_pipeline_transform( |
| 169 | func main() -> i64 |