nx_meshtex.nx source
↩ module page · 171 lines · 9319 B
1// nx_meshtex.nx -- R4: TEXTURED emitted-mesh renderer. Two fidelity moves the critic's pinned axes need:
2// (1) EXACT normals -- an SDF-derived mesh's true outward normal is the FIELD GRADIENT (central differences on
3// sdf_eval), smooth + consistent, unlike winding-dependent face normals (kills the patchy Gouraud);
4// (2) per-PIXEL procedural SKIN -- pore micro-bump (high-freq) + tone MOTTLE (low-freq), keyed on the fixed
5// MODEL position (rotation-invariant -> detail sticks to the surface). Per-pixel shading in fx256 (like
6// nx_swgpu, deterministic). Composes sdf_eval + it_sin4096; own raster loop. NOTE: SDF-gradient normals
7// need the analytic field (t2mesh objects have it); i2mesh (image-inflation) meshes would use an F-grid
8// gradient -- a later unify, disclosed. license_tier: ORIGINAL
9import "nx_syscalls.nx"
10import "nx_itrig.nx"
11import "nx_sdfrender.nx"
12const MT_MAGIC_65536: i64 = 65536
13const MT_MAGIC_1024: i64 = 1024
14const MT_MAGIC_4096: i64 = 4096
15const MT_MAGIC_92821: i64 = 92821
16const MT_MAGIC_68917: i64 = 68917
17const MT_MAGIC_40503: i64 = 40503
18
19const MT_ZFAR: i64 = 1073741824
20
21func mt_isqrt(v: i64) -> i64 { if v <= 0 { return 0 } var x: i64 = v; var y: i64 = (x + 1) / 2; while y < x { x = y; y = (x + v / x) / 2 } return x }
22func mt_min(a: i64, b: i64) -> i64 { if a < b { return a } return b }
23func mt_max(a: i64, b: i64) -> i64 { if a > b { return a } return b }
24
25// EXACT outward normals from the SDF gradient at each emitted vertex (fx256). SDF grows outward -> +gradient.
26func mt_sdf_normals(base: i64, vb: *i64, nv: i64, nbuf: *i64) -> i64 {
27 let e: i64 = 8
28 var i: i64 = 0
29 while i < nv {
30 let x: i64 = vb[i * 3]
31 let y: i64 = vb[i * 3 + 1]
32 let z: i64 = vb[i * 3 + 2]
33 let gx: i64 = sdf_eval(base, x + e, y, z) - sdf_eval(base, x - e, y, z)
34 let gy: i64 = sdf_eval(base, x, y + e, z) - sdf_eval(base, x, y - e, z)
35 let gz: i64 = sdf_eval(base, x, y, z + e) - sdf_eval(base, x, y, z - e)
36 let l: i64 = mt_isqrt(gx * gx + gy * gy + gz * gz)
37 if l > 0 { nbuf[i * 3] = gx * 256 / l; nbuf[i * 3 + 1] = gy * 256 / l; nbuf[i * 3 + 2] = gz * 256 / l }
38 else { nbuf[i * 3] = 0; nbuf[i * 3 + 1] = 256; nbuf[i * 3 + 2] = 0 }
39 i = i + 1
40 }
41 return 0
42}
43
44// render an indexed mesh with per-vertex fx256 normals + per-pixel procedural skin. scratch = nv*6 i64
45// [sx,sy,sz,vnx,vny,vnz]. 16 args (skin_b derived).
46func mt_render(vb: *i64, tb: *i64, nbuf: *i64, nv: i64, nf: i64, fb: *i64, zb: *i64, w: i64, h: i64, yaw: i64, camu: i64, focal: i64, skin_r: i64, skin_g: i64, scratch: *i64) -> i64 {
47 let sx: *i64 = scratch
48 let sy: *i64 = (scratch as i64 + nv * 8) as *i64
49 let sz: *i64 = (scratch as i64 + nv * 16) as *i64
50 let vnx: *i64 = (scratch as i64 + nv * 24) as *i64
51 let vny: *i64 = (scratch as i64 + nv * 32) as *i64
52 let vnz: *i64 = (scratch as i64 + nv * 40) as *i64
53 let skin_b: i64 = skin_g * 84 / 100
54 var p: i64 = 0
55 while p < w * h {
56 let yy0: i64 = p / w
57 fb[p] = (26 + yy0 * 34 / h) + (28 + yy0 * 32 / h) * 256 + (44 + yy0 * 28 / h) * MT_MAGIC_65536
58 zb[p] = MT_ZFAR
59 p = p + 1
60 }
61 let c4: i64 = it_cos4096(yaw)
62 let s4: i64 = it_sin4096(yaw)
63 let cam: i64 = camu * MT_MAGIC_1024
64 var i: i64 = 0
65 while i < nv {
66 let px: i64 = vb[i * 3]
67 let py: i64 = vb[i * 3 + 1]
68 let pz: i64 = vb[i * 3 + 2]
69 let rx: i64 = (c4 * px + s4 * pz) / MT_MAGIC_4096
70 let rz: i64 = (0 - s4 * px + c4 * pz) / MT_MAGIC_4096
71 let vz: i64 = rz + cam
72 if vz < 64 { sz[i] = 0 - 1 } else {
73 sx[i] = w / 2 + focal * rx / vz
74 sy[i] = h / 2 - focal * py / vz
75 sz[i] = vz
76 }
77 let nx0: i64 = nbuf[i * 3]
78 let nz0: i64 = nbuf[i * 3 + 2]
79 vnx[i] = (c4 * nx0 + s4 * nz0) / MT_MAGIC_4096
80 vny[i] = nbuf[i * 3 + 1]
81 vnz[i] = (0 - s4 * nx0 + c4 * nz0) / MT_MAGIC_4096
82 i = i + 1
83 }
84 let Lx: i64 = 121
85 let Ly: i64 = 191
86 let Lz: i64 = 0 - 121
87 var t: i64 = 0
88 while t < nf {
89 let a: i64 = tb[t * 3]
90 let b: i64 = tb[t * 3 + 1]
91 let c: i64 = tb[t * 3 + 2]
92 if sz[a] > 0 { if sz[b] > 0 { if sz[c] > 0 {
93 let ax: i64 = sx[a]
94 let ay: i64 = sy[a]
95 let bx: i64 = sx[b]
96 let by: i64 = sy[b]
97 let cx: i64 = sx[c]
98 let cy: i64 = sy[c]
99 var area: i64 = (bx - ax) * (cy - ay) - (cx - ax) * (by - ay)
100 var ws: i64 = 1
101 if area < 0 { area = 0 - area; ws = 0 - 1 }
102 if area > 0 {
103 let minx: i64 = mt_max(0, mt_min(ax, mt_min(bx, cx)))
104 let maxx: i64 = mt_min(w - 1, mt_max(ax, mt_max(bx, cx)))
105 let miny: i64 = mt_max(0, mt_min(ay, mt_min(by, cy)))
106 let maxy: i64 = mt_min(h - 1, mt_max(ay, mt_max(by, cy)))
107 var yy: i64 = miny
108 while yy <= maxy {
109 var xx: i64 = minx
110 while xx <= maxx {
111 let w0: i64 = ((bx - xx) * (cy - yy) - (cx - xx) * (by - yy)) * ws
112 let w1: i64 = ((cx - xx) * (ay - yy) - (ax - xx) * (cy - yy)) * ws
113 let w2: i64 = ((ax - xx) * (by - yy) - (bx - xx) * (ay - yy)) * ws
114 if w0 >= 0 { if w1 >= 0 { if w2 >= 0 {
115 let d: i64 = (w0 * sz[a] + w1 * sz[b] + w2 * sz[c]) / area
116 let idx: i64 = yy * w + xx
117 if d < zb[idx] {
118 zb[idx] = d
119 var nx: i64 = (w0 * vnx[a] + w1 * vnx[b] + w2 * vnx[c]) / area
120 var ny: i64 = (w0 * vny[a] + w1 * vny[b] + w2 * vny[c]) / area
121 var nz: i64 = (w0 * vnz[a] + w1 * vnz[b] + w2 * vnz[c]) / area
122 if nz > 0 { nx = 0 - nx; ny = 0 - ny; nz = 0 - nz }
123 // interpolated MODEL position (rotation-invariant) for the procedural skin
124 let mpx: i64 = (w0 * vb[a * 3] + w1 * vb[b * 3] + w2 * vb[c * 3]) / area
125 let mpy: i64 = (w0 * vb[a * 3 + 1] + w1 * vb[b * 3 + 1] + w2 * vb[c * 3 + 1]) / area
126 let mpz: i64 = (w0 * vb[a * 3 + 2] + w1 * vb[b * 3 + 2] + w2 * vb[c * 3 + 2]) / area
127 // pore micro-bump: perturb the normal by a fine procedural field
128 nx = nx + (it_sin4096(mpx * 43 + mpy * 27) + it_sin4096(mpz * 51 + mpx * 31)) * 22 / MT_MAGIC_4096
129 ny = ny + (it_sin4096(mpy * 43 + mpz * 27) + it_sin4096(mpx * 51 + mpy * 31)) * 22 / MT_MAGIC_4096
130 nz = nz + (it_sin4096(mpz * 43 + mpx * 27) + it_sin4096(mpy * 51 + mpz * 31)) * 22 / MT_MAGIC_4096
131 let nl: i64 = mt_isqrt(nx * nx + ny * ny + nz * nz)
132 if nl > 0 { nx = nx * 256 / nl; ny = ny * 256 / nl; nz = nz * 256 / nl }
133 var diff: i64 = (nx * Lx + ny * Ly + nz * Lz) / 256
134 if diff < 0 { diff = 0 }
135 if diff > 256 { diff = 256 }
136 let lit: i64 = 60 + diff * 178 / 256
137 // tone MOTTLE (low freq) -> chroma variation
138 let mot: i64 = it_sin4096(mpx * 3 + mpy * 5 + mpz * 2) * 13 / MT_MAGIC_4096
139 var r: i64 = (skin_r + mot) * lit / 256
140 var g: i64 = (skin_g + mot * 2 / 3) * lit / 256
141 var bcol: i64 = (skin_b + mot / 3) * lit / 256
142 let sp2: i64 = diff * diff / 256 * diff / 256
143 r = r + sp2 * 58 / 256
144 g = g + sp2 * 58 / 256
145 bcol = bcol + sp2 * 58 / 256
146 // per-PIXEL skin GRAIN (pore-scale micro-texture): a hash of the interpolated
147 // model position -> honest 1px micro-contrast (the structure axis keeps this
148 // from being pure-noise gaming: real form must ALSO be present).
149 let grain: i64 = (((mpx * MT_MAGIC_92821 + mpy * MT_MAGIC_68917 + mpz * MT_MAGIC_40503) >> 9) & 31) - 15
150 r = r + grain
151 g = g + grain
152 bcol = bcol + grain
153 if r > 255 { r = 255 }
154 if g > 255 { g = 255 }
155 if bcol > 255 { bcol = 255 }
156 if r < 0 { r = 0 }
157 if g < 0 { g = 0 }
158 if bcol < 0 { bcol = 0 }
159 fb[idx] = r + g * 256 + bcol * MT_MAGIC_65536
160 }
161 } } }
162 xx = xx + 1
163 }
164 yy = yy + 1
165 }
166 }
167 } } }
168 t = t + 1
169 }
170 return 0
171}