code wiki / _hdl_build / nx_viz3d.nx

nx_viz3d.nx source

↩ module page · 88 lines · 4629 B

1// nx_viz3d.nx -- LIB: the SOVEREIGN 3D viewport (foundation of Nishi first-party 3D visualization). Operator: 2// "for all these 3d simulations and visualization from games to cad to stls to blender to zbrush ... lets get our 3// nishi browser and 3rd party visualization and management s class exceed so i can see these things." 4// 5// This is the BEDROCK: fixed-point (NO-FLOAT, sovereign) 3D math -- rotate a point around Y, perspective-project it 6// to 2D screen space -- so a mesh (vertices + edges/triangles) can be rendered to SVG and viewed/animated in the 7// Nishi browser with 0 JavaScript. Fixed-point scale = 1024 (10-bit fraction); a 12-entry sin table (30 deg steps) 8// drives a turntable. Integer division throughout; the camera distance keeps z+DIST > 0 so projection never divides 9// by zero. NEVER-BRICK (#26): pure integer, bounded, deterministic, zero hardware-state writes. 10// license_tier: ORIGINAL 11import "nx_syscalls.nx" 12import "nx_vecmath.nx" 13const VP_MAGIC_1024: i64 = 1024 14 15const VP_DIST: i64 = 400 // camera distance (keeps z+DIST > 0 for objects within +/-150) 16const VP_FOCAL: i64 = 300 // focal length (perspective strength) 17const VP_CX: i64 = 300 // screen center x 18const VP_CY: i64 = 210 // screen center y 19const VP_SCALE: i64 = 1024 // fixed-point fraction (sin/cos are x1024) 20 21// fill a 12-entry sin table (sin(i*30deg) x 1024). cos(a) = sin(a+90deg) = tbl[(i+3)%12]. 22func vp_fill_sin(tbl: *i64) -> i64 { 23 tbl[0]=0; tbl[1]=512; tbl[2]=887; tbl[3]=VP_MAGIC_1024; tbl[4]=887; tbl[5]=512 24 tbl[6]=0; tbl[7]=0-512; tbl[8]=0-887; tbl[9]=0-VP_MAGIC_1024; tbl[10]=0-887; tbl[11]=0-512 25 return 0 26} 27func vp_idx(i: i64) -> i64 { return ((i % 12) + 12) % 12 } 28 29// rotate (x,y,z) around the Y axis by angle index `ang` (each step = 30 deg), perspective-project to screen. 30// writes pp[0]=screen_x, pp[1]=screen_y. tbl = the sin table (so it is corruptible / swappable). 31func vp_project(x: i64, y: i64, z: i64, ang: i64, tbl: *i64, pp: *i64) -> i64 { 32 let s: i64 = tbl[vp_idx(ang)] 33 let c: i64 = tbl[vp_idx(ang + 3)] 34 let xr: i64 = (x*c - z*s) / VP_SCALE // rotated x 35 let zr: i64 = (x*s + z*c) / VP_SCALE // rotated z 36 let zd: i64 = zr + VP_DIST // camera-space depth (> 0) 37 pp[0] = VP_CX + (xr * VP_FOCAL) / zd 38 pp[1] = VP_CY - (y * VP_FOCAL) / zd 39 return 0 40} 41 42// rotate (x,y,z) around Y -> the rotated CAMERA-SPACE point (for depth-sort + shading). out[0..2] = xr,yr,zr. 43func vp_rotate(x: i64, y: i64, z: i64, ang: i64, tbl: *i64, out: *i64) -> i64 { 44 let s: i64 = tbl[vp_idx(ang)]; let c: i64 = tbl[vp_idx(ang + 3)] 45 out[0] = (x*c - z*s) / VP_SCALE 46 out[1] = y 47 out[2] = (x*s + z*c) / VP_SCALE 48 return 0 49} 50// rotate (x,y,z) around X by `ax` THEN around Y by `ay` (each step = 30 deg) -> camera-space point. For arbitrary 51// viewing orientation (tilt + turn) so e.g. a board's top face can be angled toward the camera. 52func vp_rotate_xy(x: i64, y: i64, z: i64, ax: i64, ay: i64, tbl: *i64, out: *i64) -> i64 { 53 let sx: i64 = tbl[vp_idx(ax)]; let cx: i64 = tbl[vp_idx(ax + 3)] 54 let sy: i64 = tbl[vp_idx(ay)]; let cy: i64 = tbl[vp_idx(ay + 3)] 55 let y1: i64 = (y*cx - z*sx) / VP_SCALE 56 let z1: i64 = (y*sx + z*cx) / VP_SCALE 57 out[0] = (x*cy - z1*sy) / VP_SCALE 58 out[1] = y1 59 out[2] = (x*sy + z1*cy) / VP_SCALE 60 return 0 61} 62// perspective-project an already-rotated point to screen. 63func vp_project_pt(xr: i64, yr: i64, zr: i64, pp: *i64) -> i64 { 64 let zd: i64 = zr + VP_DIST 65 pp[0] = VP_CX + (xr * VP_FOCAL) / zd 66 pp[1] = VP_CY - (yr * VP_FOCAL) / zd 67 return 0 68} 69// integer square root (Newton). isqrt(144)=12. 70func vp_isqrt(n: i64) -> i64 { return vm_isqrt(n) } 71// cross product out = a x b. 72func vp_cross(ax: i64, ay: i64, az: i64, bx: i64, by: i64, bz: i64, out: *i64) -> i64 { 73 out[0] = ay*bz - az*by 74 out[1] = az*bx - ax*bz 75 out[2] = ax*by - ay*bx 76 return 0 77} 78// flat-shade brightness (60..255) of a triangle from its 3 rotated verts: faces toward the camera are bright, 79// edge-on dark (view-space lighting, winding-independent via |Nz|/|N|). 80func vp_shade(p0: *i64, p1: *i64, p2: *i64) -> i64 { 81 let n: *i64 = sys_mmap(32) as *i64 82 vp_cross(p1[0]-p0[0], p1[1]-p0[1], p1[2]-p0[2], p2[0]-p0[0], p2[1]-p0[1], p2[2]-p0[2], n) 83 var nz: i64 = n[2]; if nz < 0 { nz = 0 - nz } 84 let nlen: i64 = vp_isqrt(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]) 85 var nl: i64 = nlen; if nl == 0 { nl = 1 } 86 let align: i64 = nz * 256 / nl // 0..256 (256 = face-on to camera) 87 return 60 + align * 195 / 256 // 60..255 88}