nx_subdiv.nx source
↩ module page · 132 lines · 5357 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"
10import "nx_vecmath.nx"
11const K_MAGIC_374761393: i64 = 374761393
12const K_MAGIC_668265263: i64 = 668265263
13const K_MAGIC_2147483647: i64 = 2147483647
14const K_MAGIC_974634599: i64 = 974634599
15const K_MAGIC_1274126177: i64 = 1274126177
16
17func sd_isqrt(v: i64) -> i64 { return vm_isqrt(v) }
18// find-or-add an exact vertex (the mesh3 dedup idiom)
19func sd_getvert(mesh: i64, x: i64, y: i64, z: i64) -> i64 {
20 let h: *i64 = m3_hdr(mesh)
21 var i: i64 = 0
22 while i < h[0] {
23 let v: *i64 = m3_vert(mesh, i)
24 if v[0] == x { if v[1] == y { if v[2] == z { return i } } }
25 i = i + 1
26 }
27 return m3_add_vert(mesh, x, y, z)
28}
29
30// one subdivision level: meshIn -> meshOut (tris x4, midpoints deduplicated => watertight)
31func sd_subdivide(meshIn: i64, meshOut: i64) -> i64 {
32 m3_init(meshOut)
33 let hin: *i64 = m3_hdr(meshIn)
34 var t: i64 = 0
35 while t < hin[1] {
36 let tr: *i64 = m3_tri(meshIn, t)
37 let a: *i64 = m3_vert(meshIn, tr[0])
38 let b: *i64 = m3_vert(meshIn, tr[1])
39 let c: *i64 = m3_vert(meshIn, tr[2])
40 let ia: i64 = sd_getvert(meshOut, a[0], a[1], a[2])
41 let ib: i64 = sd_getvert(meshOut, b[0], b[1], b[2])
42 let ic: i64 = sd_getvert(meshOut, c[0], c[1], c[2])
43 let iab: i64 = sd_getvert(meshOut, (a[0] + b[0]) / 2, (a[1] + b[1]) / 2, (a[2] + b[2]) / 2)
44 let ibc: i64 = sd_getvert(meshOut, (b[0] + c[0]) / 2, (b[1] + c[1]) / 2, (b[2] + c[2]) / 2)
45 let ica: i64 = sd_getvert(meshOut, (c[0] + a[0]) / 2, (c[1] + a[1]) / 2, (c[2] + a[2]) / 2)
46 m3_add_tri(meshOut, ia, iab, ica)
47 m3_add_tri(meshOut, iab, ib, ibc)
48 m3_add_tri(meshOut, ica, ibc, ic)
49 m3_add_tri(meshOut, iab, ibc, ica)
50 t = t + 1
51 }
52 return hin[1] * 4
53}
54
55// integer position hash (the detail field's randomness source; POSITION-keyed => crack-free)
56func sd_hash(x: i64, y: i64, z: i64, seed: i64) -> i64 {
57 var h: i64 = x * K_MAGIC_374761393 + y * K_MAGIC_668265263 + z * K_MAGIC_2147483647 + seed * K_MAGIC_974634599
58 h = h ^ (h >> 13)
59 h = h * K_MAGIC_1274126177
60 h = h ^ (h >> 16)
61 if h < 0 { h = 0 - h }
62 return h
63}
64
65// vertex normals: accumulate face cross products per vertex, normalize fx256 (scratch = caller arena)
66func sd_normals(mesh: i64, nrm: *i64) -> i64 {
67 let h: *i64 = m3_hdr(mesh)
68 var i: i64 = 0
69 while i < h[0] * 3 { nrm[i] = 0; i = i + 1 }
70 var t: i64 = 0
71 while t < h[1] {
72 let tr: *i64 = m3_tri(mesh, t)
73 let a: *i64 = m3_vert(mesh, tr[0])
74 let b: *i64 = m3_vert(mesh, tr[1])
75 let c: *i64 = m3_vert(mesh, tr[2])
76 let ux: i64 = b[0] - a[0]
77 let uy: i64 = b[1] - a[1]
78 let uz: i64 = b[2] - a[2]
79 let vx: i64 = c[0] - a[0]
80 let vy: i64 = c[1] - a[1]
81 let vz: i64 = c[2] - a[2]
82 let nx: i64 = (uy * vz - uz * vy) / 256
83 let ny: i64 = (uz * vx - ux * vz) / 256
84 let nz: i64 = (ux * vy - uy * vx) / 256
85 var k: i64 = 0
86 while k < 3 {
87 nrm[tr[k] * 3] = nrm[tr[k] * 3] + nx
88 nrm[tr[k] * 3 + 1] = nrm[tr[k] * 3 + 1] + ny
89 nrm[tr[k] * 3 + 2] = nrm[tr[k] * 3 + 2] + nz
90 k = k + 1
91 }
92 t = t + 1
93 }
94 i = 0
95 while i < h[0] {
96 let mx: i64 = nrm[i * 3]
97 let my: i64 = nrm[i * 3 + 1]
98 let mz: i64 = nrm[i * 3 + 2]
99 let m: i64 = sd_isqrt(mx * mx + my * my + mz * mz)
100 if m > 0 {
101 nrm[i * 3] = mx * 256 / m
102 nrm[i * 3 + 1] = my * 256 / m
103 nrm[i * 3 + 2] = mz * 256 / m
104 }
105 i = i + 1
106 }
107 return 0
108}
109
110// DISPLACE from a bump DETAIL FIELD: density (0..256 fraction of verts bumped), amp (fx256 height), seed.
111// A vertex bumps iff hash(pos)%256 < density; height = amp scaled by a second hash (organic variation).
112// This IS the mechanical form of gland/pore detail-as-DATA. nrm = caller scratch (>= nverts*3 slots).
113func sd_displace(mesh: i64, density: i64, amp: i64, seed: i64, nrm: *i64) -> i64 {
114 sd_normals(mesh, nrm)
115 let h: *i64 = m3_hdr(mesh)
116 var bumped: i64 = 0
117 var i: i64 = 0
118 while i < h[0] {
119 let v: *i64 = m3_vert(mesh, i)
120 let hh: i64 = sd_hash(v[0], v[1], v[2], seed)
121 if hh % 256 < density {
122 let hv: i64 = 128 + (hh / 256) % 128 // 128..255: bump height variation
123 let d: i64 = amp * hv / 256
124 v[0] = v[0] + nrm[i * 3] * d / 256
125 v[1] = v[1] + nrm[i * 3 + 1] * d / 256
126 v[2] = v[2] + nrm[i * 3 + 2] * d / 256
127 bumped = bumped + 1
128 }
129 i = i + 1
130 }
131 return bumped
132}