nx_perlin.nx
buildroot/runtime/nx_perlin.nx
about
nx_perlin.nx -- seeded Perlin noise primitive.
Foundational procgen primitive. Every higher-level generative
thing (terrain, clouds, marble, organic textures, agent trajectories,
node-graph composition like Houdini engine) composes noise as a
building block. This file ships the seed -> permutation table ->
gradient -> bilinear-blend chain in pure Q10 integer math, no f64.
Pattern parity with Houdini's noise SOP: stateful (allocated once),
position-keyed (lookup at any 2D point), seamless (continuous across
integer-cell boundaries via gradient interpolation), deterministic
from seed (same seed -> same noise field forever).
1D + 2D + FBM 2D (fractional Brownian motion -- multi-octave sum).
3D Perlin / simplex / worley queued for follow-up files.
genealogy_id: perlin_1985_image_synthesizer + perlin_2002_improved +
mandelbrot_1982_fractal_geometry_nature
lineage_id: perlin_2d_q10_int
nx_safety_envelope:
intended_use: "Perlin 2D noise (Q10 fixed-point) -- ground
truth for substrate procgen. Composes with
biome_classifier / world_event / kind-specific
generators per cardinal feedback-kind-
specific-generators-not-broad-noise."
sil_target: SIL1 (procgen quality; not safety-runtime)
asil_target: QM
dal_target: NONE
evidence: [Perlin_1985_canonical_paper,
Q10_fixed_point_deterministic,
bit_equal_reproducible_across_runs,
seed_deterministic_no_global_state]
hazard_register: [bug-tape-seed-collision-low-entropy-source,
bug-tape-Q10-saturation-at-extreme-coords]
residual_risk: "Q10 saturates at coord magnitude > 2^21;
substrate-acceptable for world coords in
the [-1M, 1M] range. Caller MUST tile
for larger worlds (queued: nx_perlin_tile)."
verdict: NOT_YET_EVALUATED
dependencies 2 imports · 7 importers
imports: nx_syscalls.nxnx_tier.nx
imported by: nx_perlin_test.nxnx_pets_voxel3d.nxnx_procgen_biome.nxnx_procgen_preset.nxnx_procgen_preset_test.nxnx_procgen_signature.nxnx_procgen_signature_test.nx
structs
| 53 | struct PerlinState { |
consts
| 44 | const NX_MAGIC_48271: i64 = 48271 |
| 45 | const NX_MAGIC_2147483647: i64 = 2147483647 |
| 47 | const NX_PERLIN_Q: nx_int = 1024 |
| 48 | const NX_PERLIN_TABLE_SIZE: nx_int = 256 |
| 49 | const NX_PERLIN_TABLE_MASK: nx_int = 255 |
| 51 | const NX_PERLIN_DIAG_Q10: nx_int = 724 |
| 271 | const NX_PERLIN_BAND_VOID: nx_int = 0 |
| 272 | const NX_PERLIN_BAND_QUIET: nx_int = 1 |
| 273 | const NX_PERLIN_BAND_MODERATE: nx_int = 2 |
| 274 | const NX_PERLIN_BAND_STRONG: nx_int = 3 |
| 275 | const NX_PERLIN_BAND_SATURATED: nx_int = 4 |
| 276 | const NX_PERLIN_N_BANDS: nx_int = 5 |
functions
| 60 | func _perlin_lcg_step(state: nx_int) -> nx_int {
called by 1: nx_perlin_alloc |
| 73 | func nx_perlin_alloc(seed: nx_int) -> *PerlinState { |
| 105 | func _perlin_ease(t_q10: nx_int) -> nx_int { |
| 112 | func _perlin_lerp(a: nx_int, b: nx_int, t_q10: nx_int) -> nx_int { |
| 118 | func _perlin_grad_1d(hash: nx_int, x_frac_q10: nx_int) -> nx_int {
called by 1: nx_perlin_1d |
| 128 | func nx_perlin_1d(s: *PerlinState, x_q10: nx_int) -> nx_int { |
| 156 | func _perlin_grad_2d_x(hash: nx_int) -> nx_int {
called by 1: nx_perlin_2d |
| 168 | func _perlin_grad_2d_y(hash: nx_int) -> nx_int {
called by 1: nx_perlin_2d |
| 182 | func _perlin_dot_2d(gx: nx_int, gy: nx_int, dx: nx_int, dy: nx_int) -> nx_int {
called by 1: nx_perlin_2d |
| 187 | func _perlin_hash_2d(s: *PerlinState, xi: nx_int, yi: nx_int) -> nx_int {
called by 1: nx_perlin_2d |
| 199 | func nx_perlin_2d(s: *PerlinState, x_q10: nx_int, y_q10: nx_int) -> nx_int { |
| 236 | func nx_perlin_fbm_2d(s: *PerlinState, x_q10: nx_int, y_q10: nx_int, |
| 278 | func nx_perlin_classify(noise_q10: nx_int) -> nx_int { |
| 288 | func nx_perlin_band_is_valid(band: nx_int) -> nx_int { |