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