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}