code wiki / (root) / nx_mesh_sculpt.nx

nx_mesh_sculpt.nx source

↩ module page · 124 lines · 5019 B

1// nx_mesh_sculpt.nx -- ZBrush-class curved primitive + sculpt brush, sovereign. 2// 3// Closes the "curved geometry + sculpting" gap on the mesh kernel. Builds an 4// icosphere by subdividing an octahedron and projecting every vertex onto the 5// sphere with integer nx_isqrt (NO trig, NO f64 -- both unreliable here), then 6// a radial "inflate/draw" brush that pushes verts within a brush radius outward 7// along their normal with a smooth (linear) falloff. For an origin-centred 8// primitive the outward radial direction IS the surface normal, so this is a 9// correct draw brush on the sphere; general per-vertex normals are a follow-up. 10// Reuses nx_mesh_subdivide (nx_mesh_edit) + nx_isqrt. No dup: grepped, none 11// existed. Output is pipeline-native Q14-mm -> flows to nx_stl_write_mesh. 12// license_tier: ORIGINAL. 13 14import "nx_syscalls.nx" 15import "nx_mesh.nx" 16import "nx_mesh_edit.nx" 17import "nx_isqrt.nx" 18 19// distance of vertex `idx` from the origin (Q14), = |pos|. 20func nx_mesh_vert_radius(m: *NxMesh, idx: i64) -> i64 { 21 let x: i64 = nx_mesh_get_vertex_x(m, idx) 22 let y: i64 = nx_mesh_get_vertex_y(m, idx) 23 let z: i64 = nx_mesh_get_vertex_z(m, idx) 24 return nx_isqrt(x * x + y * y + z * z) 25} 26 27// ===== factory: octahedron ======================================= 28// 6 verts on the axes at +/-radius, 8 outward-wound triangles. 29 30func nx_mesh_make_octahedron(radius_q14: i64, color: i64) -> *NxMesh { 31 let m: *NxMesh = nx_mesh_alloc(6, 8, 0) 32 if (m as i64) == 0 { return m } 33 let r: i64 = radius_q14 34 let nr: i64 = 0 - radius_q14 35 nx_mesh_set_vertex(m, 0, r, 0, 0, color) // +x 36 nx_mesh_set_vertex(m, 1, nr, 0, 0, color) // -x 37 nx_mesh_set_vertex(m, 2, 0, r, 0, color) // +y 38 nx_mesh_set_vertex(m, 3, 0, nr, 0, color) // -y 39 nx_mesh_set_vertex(m, 4, 0, 0, r, color) // +z (north) 40 nx_mesh_set_vertex(m, 5, 0, 0, nr, color) // -z (south) 41 // top hemisphere (apex 4), CCW outward 42 nx_mesh_set_triangle(m, 0, 4, 0, 2) 43 nx_mesh_set_triangle(m, 1, 4, 2, 1) 44 nx_mesh_set_triangle(m, 2, 4, 1, 3) 45 nx_mesh_set_triangle(m, 3, 4, 3, 0) 46 // bottom hemisphere (apex 5) 47 nx_mesh_set_triangle(m, 4, 5, 2, 0) 48 nx_mesh_set_triangle(m, 5, 5, 1, 2) 49 nx_mesh_set_triangle(m, 6, 5, 3, 1) 50 nx_mesh_set_triangle(m, 7, 5, 0, 3) 51 return m 52} 53 54// ===== project every vertex onto the sphere of given radius ====== 55// In-place. v' = v * R / |v|, integer-rounded via nx_isqrt. Axis 56// verts already at radius R are exactly preserved. 57 58func nx_mesh_project_to_sphere(m: *NxMesh, radius_q14: i64) -> i64 { 59 var i: i64 = 0 60 while i < m.n_verts { 61 let base: i64 = i * NX_MESH_VTX_STRIDE 62 let x: i64 = m.verts[base + 0] 63 let y: i64 = m.verts[base + 1] 64 let z: i64 = m.verts[base + 2] 65 let len: i64 = nx_isqrt(x * x + y * y + z * z) 66 if len > 0 { 67 m.verts[base + 0] = x * radius_q14 / len 68 m.verts[base + 1] = y * radius_q14 / len 69 m.verts[base + 2] = z * radius_q14 / len 70 } 71 i = i + 1 72 } 73 return NX_MESH_OK 74} 75 76// ===== factory: icosphere ======================================== 77// Octahedron subdivided `subdiv` times, each level projected to the 78// sphere. subdiv=0 -> octahedron; each level: tris*4, verts+tris*3. 79 80func nx_mesh_make_icosphere(radius_q14: i64, subdiv: i64, color: i64) -> *NxMesh { 81 var m: *NxMesh = nx_mesh_make_octahedron(radius_q14, color) 82 var k: i64 = 0 83 while k < subdiv { 84 m = nx_mesh_subdivide(m) 85 nx_mesh_project_to_sphere(m, radius_q14) 86 k = k + 1 87 } 88 return m 89} 90 91// ===== sculpt: radial inflate/draw brush ========================= 92// 93// Pushes every vertex within `radius_q14` of the brush centre outward along 94// its radial (= normal, for origin-centred meshes) direction by 95// disp = strength_q14 * (radius - dist_to_centre) / radius (linear falloff) 96// New position = v * (|v| + disp) / |v|. strength>0 raises a bump, <0 carves. 97// Topology (vert/tri counts) is preserved. In-place. 98 99func nx_mesh_sculpt_brush(m: *NxMesh, cx: i64, cy: i64, cz: i64, 100 radius_q14: i64, strength_q14: i64) -> i64 { 101 if radius_q14 <= 0 { return NX_MESH_ERR_BAD_DIMS } 102 var i: i64 = 0 103 while i < m.n_verts { 104 let base: i64 = i * NX_MESH_VTX_STRIDE 105 let x: i64 = m.verts[base + 0] 106 let y: i64 = m.verts[base + 1] 107 let z: i64 = m.verts[base + 2] 108 let dx: i64 = x - cx 109 let dy: i64 = y - cy 110 let dz: i64 = z - cz 111 let d: i64 = nx_isqrt(dx * dx + dy * dy + dz * dz) 112 if d < radius_q14 { 113 let disp: i64 = strength_q14 * (radius_q14 - d) / radius_q14 114 let len: i64 = nx_isqrt(x * x + y * y + z * z) 115 if len > 0 { 116 m.verts[base + 0] = x * (len + disp) / len 117 m.verts[base + 1] = y * (len + disp) / len 118 m.verts[base + 2] = z * (len + disp) / len 119 } 120 } 121 i = i + 1 122 } 123 return NX_MESH_OK 124}