code wiki / (root) / nx_subdiv.nx

nx_subdiv.nx source

↩ module page · 137 lines · 5450 B

1// nx_subdiv.nx -- SUBDIVISION + DISPLACEMENT DETAIL (the Elara detail architecture, rung 1 of "8K/16K down 2// to sensor level"): gland/pore-scale geometry cannot live in a base mesh -- you SUBDIVIDE the detail region 3// (each level x4 triangles, midpoint dedup keeps it watertight) and DISPLACE vertices along their normals 4// from a DETERMINISTIC DETAIL FIELD (position-hashed bump field: density/amplitude/seed = DATA -- the exact 5// mechanical form of "Montgomery glands on an areola" or skin pores). Position-hashing (not index-hashing) 6// makes shared vertices displace identically => NO CRACKS across subdivision levels or patch borders. 7// ALL INTEGER on nx_mesh3. license_tier: ORIGINAL 8import "nx_syscalls.nx" 9import "nx_mesh3.nx" 10const K_MAGIC_374761393: i64 = 374761393 11const K_MAGIC_668265263: i64 = 668265263 12const K_MAGIC_2147483647: i64 = 2147483647 13const K_MAGIC_974634599: i64 = 974634599 14const K_MAGIC_1274126177: i64 = 1274126177 15 16func sd_isqrt(v: i64) -> i64 { 17 if v <= 0 { return 0 } 18 var x: i64 = v 19 var y: i64 = (x + 1) / 2 20 while y < x { x = y; y = (x + v / x) / 2 } 21 return x 22} 23// find-or-add an exact vertex (the mesh3 dedup idiom) 24func sd_getvert(mesh: i64, x: i64, y: i64, z: i64) -> i64 { 25 let h: *i64 = m3_hdr(mesh) 26 var i: i64 = 0 27 while i < h[0] { 28 let v: *i64 = m3_vert(mesh, i) 29 if v[0] == x { if v[1] == y { if v[2] == z { return i } } } 30 i = i + 1 31 } 32 return m3_add_vert(mesh, x, y, z) 33} 34 35// one subdivision level: meshIn -> meshOut (tris x4, midpoints deduplicated => watertight) 36func sd_subdivide(meshIn: i64, meshOut: i64) -> i64 { 37 m3_init(meshOut) 38 let hin: *i64 = m3_hdr(meshIn) 39 var t: i64 = 0 40 while t < hin[1] { 41 let tr: *i64 = m3_tri(meshIn, t) 42 let a: *i64 = m3_vert(meshIn, tr[0]) 43 let b: *i64 = m3_vert(meshIn, tr[1]) 44 let c: *i64 = m3_vert(meshIn, tr[2]) 45 let ia: i64 = sd_getvert(meshOut, a[0], a[1], a[2]) 46 let ib: i64 = sd_getvert(meshOut, b[0], b[1], b[2]) 47 let ic: i64 = sd_getvert(meshOut, c[0], c[1], c[2]) 48 let iab: i64 = sd_getvert(meshOut, (a[0] + b[0]) / 2, (a[1] + b[1]) / 2, (a[2] + b[2]) / 2) 49 let ibc: i64 = sd_getvert(meshOut, (b[0] + c[0]) / 2, (b[1] + c[1]) / 2, (b[2] + c[2]) / 2) 50 let ica: i64 = sd_getvert(meshOut, (c[0] + a[0]) / 2, (c[1] + a[1]) / 2, (c[2] + a[2]) / 2) 51 m3_add_tri(meshOut, ia, iab, ica) 52 m3_add_tri(meshOut, iab, ib, ibc) 53 m3_add_tri(meshOut, ica, ibc, ic) 54 m3_add_tri(meshOut, iab, ibc, ica) 55 t = t + 1 56 } 57 return hin[1] * 4 58} 59 60// integer position hash (the detail field's randomness source; POSITION-keyed => crack-free) 61func sd_hash(x: i64, y: i64, z: i64, seed: i64) -> i64 { 62 var h: i64 = x * K_MAGIC_374761393 + y * K_MAGIC_668265263 + z * K_MAGIC_2147483647 + seed * K_MAGIC_974634599 63 h = h ^ (h >> 13) 64 h = h * K_MAGIC_1274126177 65 h = h ^ (h >> 16) 66 if h < 0 { h = 0 - h } 67 return h 68} 69 70// vertex normals: accumulate face cross products per vertex, normalize fx256 (scratch = caller arena) 71func sd_normals(mesh: i64, nrm: *i64) -> i64 { 72 let h: *i64 = m3_hdr(mesh) 73 var i: i64 = 0 74 while i < h[0] * 3 { nrm[i] = 0; i = i + 1 } 75 var t: i64 = 0 76 while t < h[1] { 77 let tr: *i64 = m3_tri(mesh, t) 78 let a: *i64 = m3_vert(mesh, tr[0]) 79 let b: *i64 = m3_vert(mesh, tr[1]) 80 let c: *i64 = m3_vert(mesh, tr[2]) 81 let ux: i64 = b[0] - a[0] 82 let uy: i64 = b[1] - a[1] 83 let uz: i64 = b[2] - a[2] 84 let vx: i64 = c[0] - a[0] 85 let vy: i64 = c[1] - a[1] 86 let vz: i64 = c[2] - a[2] 87 let nx: i64 = (uy * vz - uz * vy) / 256 88 let ny: i64 = (uz * vx - ux * vz) / 256 89 let nz: i64 = (ux * vy - uy * vx) / 256 90 var k: i64 = 0 91 while k < 3 { 92 nrm[tr[k] * 3] = nrm[tr[k] * 3] + nx 93 nrm[tr[k] * 3 + 1] = nrm[tr[k] * 3 + 1] + ny 94 nrm[tr[k] * 3 + 2] = nrm[tr[k] * 3 + 2] + nz 95 k = k + 1 96 } 97 t = t + 1 98 } 99 i = 0 100 while i < h[0] { 101 let mx: i64 = nrm[i * 3] 102 let my: i64 = nrm[i * 3 + 1] 103 let mz: i64 = nrm[i * 3 + 2] 104 let m: i64 = sd_isqrt(mx * mx + my * my + mz * mz) 105 if m > 0 { 106 nrm[i * 3] = mx * 256 / m 107 nrm[i * 3 + 1] = my * 256 / m 108 nrm[i * 3 + 2] = mz * 256 / m 109 } 110 i = i + 1 111 } 112 return 0 113} 114 115// DISPLACE from a bump DETAIL FIELD: density (0..256 fraction of verts bumped), amp (fx256 height), seed. 116// A vertex bumps iff hash(pos)%256 < density; height = amp scaled by a second hash (organic variation). 117// This IS the mechanical form of gland/pore detail-as-DATA. nrm = caller scratch (>= nverts*3 slots). 118func sd_displace(mesh: i64, density: i64, amp: i64, seed: i64, nrm: *i64) -> i64 { 119 sd_normals(mesh, nrm) 120 let h: *i64 = m3_hdr(mesh) 121 var bumped: i64 = 0 122 var i: i64 = 0 123 while i < h[0] { 124 let v: *i64 = m3_vert(mesh, i) 125 let hh: i64 = sd_hash(v[0], v[1], v[2], seed) 126 if hh % 256 < density { 127 let hv: i64 = 128 + (hh / 256) % 128 // 128..255: bump height variation 128 let d: i64 = amp * hv / 256 129 v[0] = v[0] + nrm[i * 3] * d / 256 130 v[1] = v[1] + nrm[i * 3 + 1] * d / 256 131 v[2] = v[2] + nrm[i * 3 + 2] * d / 256 132 bumped = bumped + 1 133 } 134 i = i + 1 135 } 136 return bumped 137}