code wiki / (root) / nx_texpaint_lib.nx

nx_texpaint_lib.nx

buildroot/runtime/nx_texpaint_lib.nx

38004 B684 linesdepth 3pulls 5 transitivereach 1 importersview sourcekind library
docsdependenciesstructsconstsfunctions

about

nx_texpaint_lib.nx -- TEXTURE PAINTING: a brush stroke placed on the SURFACE that lands in the correct texel of the baked PBR map set, and that does not TEAR where the UV parameterization is cut. /compare/dcc DC7, and the board says why it matters: texture-painting is the ONE axis on that board where all four rivals are Best -- Blender paints directly onto the mesh, ZBrush PolyPaint needs no UVs assigned in advance, Maya has the 3D Paint Tool, and ArmorPaint exists for precisely this. CHECK-BEFORE-BUILD, MEASURED NOT ASSUMED. nx_capsearch over 6892 organs (1375 tools + 5517 libs, corpus_complete=1) returned a glyph STROKE PEN, four 2D FRAMEBUFFER painters and one mesh SCULPT brush -- nothing that writes a texel of a texture. nx_absent then returned tp_stroke ABSENT-PROVEN at coverage_complete=1 corpus_complete=1 over buildroot/runtime. The estate BAKES maps (ntb_bake) and GENERATES a whole PBR set per genome (ntb_bake_pbr); it has never PAINTED one. THE ATLAS CONTRACT IS READ OUT OF THE INCUMBENTS, NEVER INVENTED. Two files own it and both were read: nx_nxa_texc_lib.nx -- the TEXC section. Payload words [nv, stride=3, grid g, reserved] then per vertex [u q16 0..65535][v q16 0..65535][owning joint]. Joint j owns ONE TILE of a g x g grid, tile width tw = 65536/g in UV units, so charts from different bones are DISJOINT BY CONSTRUCTION. The unwrap is CYLINDRICAL around the bone: u is the angle (a full circle mapped onto the tile) and v is the clamped position along the parent->joint segment. nx_nxa_texbake_lib.nx -- the atlas itself: res*res*3 u8 RGB row-major, the TM_IMG contract, and a map set of albedo, specular, gloss and tangent-space normal. EVERYTHING BELOW FOLLOWS FROM THOSE TWO FACTS. In particular the map COUNT is a parameter of the map set rather than a constant in this file: a second copy of NTB_NMAP here would be a duplicate ruler that drifts the day the PBR set grows a fifth map. WHERE THE SEAM IS, AND WHY THE NAIVE PAINTER TEARS. Because the unwrap is cylindrical, EVERY chart carries its own wrap seam: the texel column at the left edge of a tile and the column at the right edge are the SAME PLACE ON THE BODY, cut apart so the cylinder could be laid flat. A brush disc centred near that cut covers surface on both sides of it, but in the atlas the far side is a whole tile-width away. A painter that clips the disc to the tile rectangle -- which is what a painter must do, or paint leaks onto another bone -- writes one side and drops the other, and the asset renders with a hard-edged half-stroke: THE TEAR. This is the defect that makes naive 3D painting unusable and it is the reason DC7 exists. THE FIX IS A METRIC, NOT A HEURISTIC. tp_stroke_core paints with the PLANAR metric and therefore tears -- it is shipped, callable and named, because a negative control you can still run is worth more than a paragraph saying you once deleted the fix and watched a test fail. tp_seam_bleed then writes exactly the texels the planar pass could not reach: those whose CYLINDRICAL distance to the centre is inside the brush while their planar distance is not. The two sets are disjoint by that definition, so no texel is ever written twice, and their union is the full disc. tp_stroke is the composition of the two. u WRAPS AND v DOES NOT, and that asymmetry is the geometry rather than a preference: u is an angle around

dependencies 3 imports · 1 importers

nx_syscalls.nx nx_vecmath.nx nx_editstack_lib.nx nx_texpaint_lib.nx nx_texpaint_gate.nx

imports: nx_syscalls.nxnx_vecmath.nxnx_editstack_lib.nx

imported by: nx_texpaint_gate.nx

structs

none

consts

78const TP_I64_BYTES: i64 = 8
79const TP_CHAN: i64 = 3 // RGB. The TM_IMG contract read from nx_nxa_texbake_lib: res*res*3 u8.
80const TP_CH_SPAN: i64 = 8 // 2^TP_CHAN: the number of distinct channel masks. Guarded by a tooth
82const TP_VALUE_MAX: i64 = 255 // the u8 channel range of the atlas element type
83const TP_ONE: i64 = 1000 // permil, the estate's fixed-point unit for a normalised 0..1 quantity
84const TP_STRENGTH_MAX: i64 = 1000 // == TP_ONE, so full strength reaches the target value EXACTLY
85const TP_BARY_ONE: i64 = 1000 // barycentric coordinates in permil; the triple must sum to this
86const TP_UV_SPAN: i64 = 65536 // NT_Q16 from nx_nxa_texc_lib -- the codomain of a TEXC u or v word
87const TP_HALF: i64 = 2 // the divisor in W/2; named so the margin derivation reads as arithmetic
88const TP_BANDS: i64 = 2 // a cylinder is cut ONCE, which leaves TWO edges in the atlas -- so the
92const TP_SMOOTH_A: i64 = 3 // the 3 in S(t) = 3t^2 - 2t^3
93const TP_SMOOTH_B: i64 = 2 // the 2 in S(t) = 3t^2 - 2t^3
94const TP_TEXEL_XY: i64 = 2 // a texel address is a pair
95const TP_TRI_V: i64 = 3 // a triangle names three vertices
101const TP_H_RES: i64 = 0
102const TP_H_NMAPS: i64 = 1
103const TP_H_GRID: i64 = 2
104const TP_H_ERR: i64 = 3
105const TP_HDR: i64 = 4
109const TP_M_PLANAR: i64 = 0
110const TP_M_WRAPONLY: i64 = 1
114const TP_OK: i64 = 0
115const TP_E_ARGS: i64 = 0 - 1
116const TP_E_RES: i64 = 0 - 2
117const TP_E_GRID: i64 = 0 - 3
118const TP_E_MAP: i64 = 0 - 4
119const TP_E_TEXEL: i64 = 0 - 5
120const TP_E_BARY: i64 = 0 - 6
121const TP_E_BARYSUM: i64 = 0 - 7
122const TP_E_TRI: i64 = 0 - 8
123const TP_E_VERT: i64 = 0 - 9
124const TP_E_RADIUS: i64 = 0 - 10
125const TP_E_RADIUS_CHART: i64 = 0 - 11
126const TP_E_STRENGTH: i64 = 0 - 12
127const TP_E_VALUE: i64 = 0 - 13
128const TP_E_CHMASK: i64 = 0 - 14
129const TP_E_CHART: i64 = 0 - 15
130const TP_E_GEOM: i64 = 0 - 16
131const TP_E_FULL: i64 = 0 - 17
132const TP_E_PUSH: i64 = 0 - 18
133const TP_E_MODE: i64 = 0 - 19

functions

135func tp_err_name(e: i64) -> *u8
called by 1: main
160func tp_value_span() -> i64 { return TP_VALUE_MAX + 1 }
161func tp_strength_span() -> i64 { return TP_STRENGTH_MAX + 1 }
164func tp_ch_span_derived() -> i64
called by 1: main
171func tp_ch_on(chmask: i64, c: i64) -> i64
called by 1: tp_put
188func tp_smoothstep(t: i64) -> i64
called by 2: maintp_falloff
195func tp_falloff(d: i64, r: i64) -> i64
called by 2: maintp_put calls 1: tp_smoothstep
203func tp_hdr_bytes() -> i64 { return TP_HDR * TP_I64_BYTES }
204func tp_map_bytes(res: i64) -> i64 { return res * res * TP_CHAN }
205func tp_set_bytes(res: i64, nmaps: i64) -> i64 { return tp_hdr_bytes() + nmaps * tp_map_bytes(res) }
212func tp_geom_ok(res: i64, nmaps: i64, grid: i64) -> i64
223func tp_tile_uv(g: i64) -> i64 { return TP_UV_SPAN / g }
226func tp_chart_x0(res: i64, g: i64, t: i64) -> i64 { return t * tp_tile_uv(g) * res / TP_UV_SPAN }
227func tp_chart_x1(res: i64, g: i64, t: i64) -> i64 { return (t + 1) * tp_tile_uv(g) * res / TP_UV_SPAN }
231func tp_chart_axis(res: i64, g: i64, x: i64) -> i64
241func tp_chart_of_texel(res: i64, g: i64, x: i64, y: i64) -> i64
252func tp_bleed_margin_w(w: i64, r: i64) -> i64
261func tp_maps_new(res: i64, nmaps: i64, grid: i64) -> *i64
271func tp_res(ms: *i64) -> i64 { return ms[TP_H_RES] }
272func tp_nmaps(ms: *i64) -> i64 { return ms[TP_H_NMAPS] }
273func tp_grid(ms: *i64) -> i64 { return ms[TP_H_GRID] }
called by 1: tg_out_of_disc
275func tp_map_ptr(ms: *i64, m: i64) -> *u8
280func tp_texel_off(res: i64, x: i64, y: i64, c: i64) -> i64 { return (y * res + x) * TP_CHAN + c }
called by 3: tp_gettp_settp_put
284func tp_get(ms: *i64, m: i64, x: i64, y: i64, c: i64) -> i64
297func tp_set(ms: *i64, m: i64, x: i64, y: i64, c: i64, v: i64) -> i64
306func tp_geom_same(a: *i64, b: *i64) -> i64
314func tp_maps_copy(dst: *i64, src: *i64) -> i64
326func tp_maps_equal(a: *i64, b: *i64) -> i64
338func tp_maps_digest(ms: *i64) -> i64
called by 1: main calls 2: tp_map_bytestp_map_ptr
360func tp_bary_ok(b0: i64, b1: i64, b2: i64) -> i64
called by 1: tp_surface_to_uv
371func tp_surface_to_uv(uv: *i64, uvstride: i64, tris: *i64, tri: i64, ntri: i64, nvert: i64,
called by 2: maintp_surface_to_texel calls 1: tp_bary_ok
409func tp_uv_to_texel(u: i64, v: i64, res: i64, out2: *i64) -> i64
423func tp_surface_to_texel(uv: *i64, uvstride: i64, tris: *i64, tri: i64, ntri: i64, nvert: i64,
called by 1: main calls 2: tp_surface_to_uvtp_uv_to_texel
433func tp_stroke_valid(ms: *i64, map_id: i64, chmask: i64, cx: i64, cy: i64, r: i64,
462func tp_bleed_margin(ms: *i64, cx: i64, cy: i64, r: i64) -> i64
480func tp_put(m: *u8, res: i64, chmask: i64, x: i64, y: i64, dx: i64, dy: i64, r: i64,
503func tp_stroke_at(ms: *i64, map_id: i64, chmask: i64, cx: i64, cy: i64, r: i64,
570func tp_stroke_core(ms: *i64, map_id: i64, chmask: i64, cx: i64, cy: i64, r: i64,
called by 2: maintp_stroke calls 1: tp_stroke_at
580func tp_seam_bleed(ms: *i64, map_id: i64, chmask: i64, cx: i64, cy: i64, r: i64,
called by 2: maintp_stroke calls 1: tp_stroke_at
586func tp_stroke(ms: *i64, map_id: i64, chmask: i64, cx: i64, cy: i64, r: i64,
606func tp_pack_what(map_id: i64, chmask: i64, value: i64) -> i64
609func tp_what_map(p: i64) -> i64 { return p / tp_value_span() / TP_CH_SPAN }
called by 2: maintp_render calls 1: tp_value_span
610func tp_what_chmask(p: i64) -> i64 { return p / tp_value_span() % TP_CH_SPAN }
called by 2: maintp_render calls 1: tp_value_span
611func tp_what_value(p: i64) -> i64 { return p % tp_value_span() }
called by 2: maintp_render calls 1: tp_value_span
612func tp_pack_centre(res: i64, cx: i64, cy: i64) -> i64 { return cy * res + cx }
613func tp_centre_x(res: i64, p: i64) -> i64 { return p % res }
called by 2: maintp_render
614func tp_centre_y(res: i64, p: i64) -> i64 { return p / res }
called by 2: maintp_render
615func tp_pack_brush(r: i64, strength: i64) -> i64 { return r * tp_strength_span() + strength }
616func tp_brush_r(p: i64) -> i64 { return p / tp_strength_span() }
called by 2: maintp_render calls 1: tp_strength_span
617func tp_brush_strength(p: i64) -> i64 { return p % tp_strength_span() }
called by 2: maintp_render calls 1: tp_strength_span
620func tp_session_new(cap_strokes: i64) -> *i64 { return es_new(cap_strokes, cap_strokes) }
called by 1: main calls 1: es_new
621func tp_strokes(st: *i64) -> i64 { return es_head(st) }
called by 1: main calls 1: es_head
628func tp_record(st: *i64, ms: *i64, map_id: i64, chmask: i64, cx: i64, cy: i64, r: i64,
645func tp_reeval(st: *i64, ms: *i64, idx: i64, map_id: i64, chmask: i64, cx: i64, cy: i64, r: i64,
665func tp_render(st: *i64, base: *i64, out: *i64) -> i64