nx_camera_q14.nx
buildroot/runtime/nx_camera_q14.nx
about
nx_camera_q14.nx -- camera + projection + 4x4 matrix math in Q14.
/voxels runtime primitive per
nxc2/docs/NISHI_GAME_ENGINE_ROADMAP.md. The math layer that powers
view, projection, and per-vertex transform for the software
rasterizer (queued).
Flat-buffer API: every matrix is a 16-element i64 array (row-major,
Q14 entries). Every vector is a 4-element i64 array. No struct
usage by design -- this primitive stays WAT-clean for browser
delivery today. Once self-host closes OP_GEP-on-WAT, a companion
nx_camera_struct.nx will wrap these flat-buffer helpers behind a
natural CameraState struct. Both APIs ship side-by-side after
that.
Q14 convention (matches nx_matrix.nx + nx_sound.nx): 1.0 = 16384.
Compose freely with existing primitives that use Q14.
Trig: 19-entry sin table at 5-degree resolution covering 0..90.
Symmetry folds extend to 0..360. Linear interpolation between
table entries yields ~0.5 degree max error -- acceptable for
general camera control where the eye cannot distinguish under
1 degree.
FULL CAPABILITY (per feedback-maximum-capability-no-simplification
cardinal, 2026-05-16): higher-resolution variants use Bhaskara I's
sin approximation (closed-form rational, accurate to ~0.0017 peak
absolute error = ~28 Q14 units, no table required). Variants:
nx_camera_sin_q14_centideg(d100): input in centi-degrees,
range [-36000, 36000]
-- 0.01-degree precision
nx_camera_sin_q14_millideg(d1000): input in milli-degrees,
range [-360000, 360000]
-- 0.001-degree precision
Both variants use the same Bhaskara closed-form, scaled to the
integer input unit. The 5-degree table remains the legacy API
for callers that don't need sub-degree precision.
Loss audit:
- Q14 multiplications lose 14 fractional bits per multiply. For
dependencies 3 imports · 5 importers
imports: nx_syscalls.nxnx_hal.nxnx_tier.nx
imported by: nx_mountain_chain.nxnx_pets_voxel3d.nxnx_river_carve.nxnx_silhouette_quality.nxnx_weather_pattern.nx
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 70 | const NX_MAGIC_1428: i64 = 1428 |
| 71 | const NX_MAGIC_2845: i64 = 2845 |
| 72 | const NX_MAGIC_4240: i64 = 4240 |
| 73 | const NX_MAGIC_5604: i64 = 5604 |
| 74 | const NX_MAGIC_6923: i64 = 6923 |
| 75 | const NX_MAGIC_8192: i64 = 8192 |
| 76 | const NX_MAGIC_9397: i64 = 9397 |
| 77 | const NX_MAGIC_10531: i64 = 10531 |
| 78 | const NX_MAGIC_11585: i64 = 11585 |
| 79 | const NX_MAGIC_12551: i64 = 12551 |
| 80 | const NX_MAGIC_13421: i64 = 13421 |
| 81 | const NX_MAGIC_14189: i64 = 14189 |
| 82 | const NX_MAGIC_14849: i64 = 14849 |
| 83 | const NX_MAGIC_15394: i64 = 15394 |
| 84 | const NX_MAGIC_15824: i64 = 15824 |
| 85 | const NX_MAGIC_16134: i64 = 16134 |
| 86 | const NX_MAGIC_16321: i64 = 16321 |
| 87 | const NX_MAGIC_16384: i64 = 16384 |
| 88 | const NX_MAGIC_36000: i64 = 36000 |
| 89 | const NX_MAGIC_27000: i64 = 27000 |
| 90 | const NX_MAGIC_18000: i64 = 18000 |
| 91 | const NX_MAGIC_9000: i64 = 9000 |
| 92 | const NX_MAGIC_405000000: i64 = 405000000 |
| 93 | const NX_MAGIC_360000: i64 = 360000 |
| 94 | const NX_MAGIC_270000: i64 = 270000 |
| 95 | const NX_MAGIC_180000: i64 = 180000 |
| 96 | const NX_MAGIC_90000: i64 = 90000 |
| 97 | const NX_MAGIC_40500000000: i64 = 40500000000 |
| 98 | const NX_MAGIC_14000: i64 = 14000 |
| 99 | const NX_MAGIC_3000: i64 = 3000 |
| 100 | const NX_MAGIC_8162: i64 = 8162 |
| 101 | const NX_MAGIC_8222: i64 = 8222 |
| 102 | const NX_MAGIC_45000: i64 = 45000 |
| 103 | const NX_MAGIC_45001: i64 = 45001 |
| 104 | const NX_MAGIC_11530: i64 = 11530 |
| 105 | const NX_MAGIC_11620: i64 = 11620 |
| 108 | const NX_CAM_Q: nx_int = 16384 |
| 116 | const NX_CAM_M4_ELEMS: nx_int = 16 |
| 117 | const NX_CAM_M4_BYTES: nx_int = 128 // 16 * 8 |
| 120 | const NX_CAM_V4_ELEMS: nx_int = 4 |
| 121 | const NX_CAM_V4_BYTES: nx_int = 32 |
| 124 | const NX_CAM_TRIG_RES_DEG: nx_int = 5 // table entry every 5 degrees |
| 125 | const NX_CAM_TRIG_N: nx_int = 19 // 0, 5, 10, ..., 85, 90 = 19 entries |
functions
| 132 | func _sin_table_alloc() -> *i64 |
| 157 | func _normalise_deg(deg: nx_int) -> nx_int called by 1: nx_camera_sin_q14_deg |
| 172 | func nx_camera_sin_q14_deg(deg: nx_int) -> nx_int |
| 210 | func nx_camera_cos_q14_deg(deg: nx_int) -> nx_int |
| 227 | func _normalise_centideg(d100: nx_int) -> nx_int called by 1: nx_camera_sin_q14_centideg |
| 234 | func nx_camera_sin_q14_centideg(d100: nx_int) -> nx_int |
| 263 | func nx_camera_cos_q14_centideg(d100: nx_int) -> nx_int |
| 275 | func _normalise_millideg(d1000: nx_int) -> nx_int called by 1: nx_camera_sin_q14_millideg |
| 282 | func nx_camera_sin_q14_millideg(d1000: nx_int) -> nx_int |
| 309 | func nx_camera_cos_q14_millideg(d1000: nx_int) -> nx_int calls 1: nx_camera_sin_q14_millideg |
| 324 | func nx_camera_identity_4x4(out: *i64) -> *i64 |
| 338 | func nx_camera_translation_4x4(tx: nx_int, ty: nx_int, tz: nx_int, out: *i64) -> *i64 |
| 352 | func nx_camera_rotation_y_deg(deg: nx_int, out: *i64) -> *i64 called by 2: mainvp_build_mvp calls 3: nx_camera_cos_q14_degnx_camera_sin_q14_degnx_camera_identity_4x4 |
| 368 | func nx_camera_rotation_x_deg(deg: nx_int, out: *i64) -> *i64 |
| 396 | func nx_camera_perspective( called by 3: mainvp_build_mvpmain calls 3: nx_camera_cos_q14_degnx_camera_sin_q14_degnx_camera_identity_4x4 |
| 456 | func nx_camera_mat4_mul(a: *i64, b: *i64, out: *i64) -> *i64 |
| 480 | func nx_camera_mat4_vec4(m: *i64, v: *i64, out: *i64) -> *i64 |
| 496 | func main() -> i64 |