code wiki / _hdl_build / nx_viz3d.nx
nx_viz3d.nx source
↩ module page · 92 lines · 4717 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"
12const VP_MAGIC_1024: i64 = 1024
13
14const VP_DIST: i64 = 400 // camera distance (keeps z+DIST > 0 for objects within +/-150)
15const VP_FOCAL: i64 = 300 // focal length (perspective strength)
16const VP_CX: i64 = 300 // screen center x
17const VP_CY: i64 = 210 // screen center y
18const VP_SCALE: i64 = 1024 // fixed-point fraction (sin/cos are x1024)
19
20// fill a 12-entry sin table (sin(i*30deg) x 1024). cos(a) = sin(a+90deg) = tbl[(i+3)%12].
21func vp_fill_sin(tbl: *i64) -> i64 {
22 tbl[0]=0; tbl[1]=512; tbl[2]=887; tbl[3]=VP_MAGIC_1024; tbl[4]=887; tbl[5]=512
23 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
24 return 0
25}
26func vp_idx(i: i64) -> i64 { return ((i % 12) + 12) % 12 }
27
28// rotate (x,y,z) around the Y axis by angle index `ang` (each step = 30 deg), perspective-project to screen.
29// writes pp[0]=screen_x, pp[1]=screen_y. tbl = the sin table (so it is corruptible / swappable).
30func vp_project(x: i64, y: i64, z: i64, ang: i64, tbl: *i64, pp: *i64) -> i64 {
31 let s: i64 = tbl[vp_idx(ang)]
32 let c: i64 = tbl[vp_idx(ang + 3)]
33 let xr: i64 = (x*c - z*s) / VP_SCALE // rotated x
34 let zr: i64 = (x*s + z*c) / VP_SCALE // rotated z
35 let zd: i64 = zr + VP_DIST // camera-space depth (> 0)
36 pp[0] = VP_CX + (xr * VP_FOCAL) / zd
37 pp[1] = VP_CY - (y * VP_FOCAL) / zd
38 return 0
39}
40
41// rotate (x,y,z) around Y -> the rotated CAMERA-SPACE point (for depth-sort + shading). out[0..2] = xr,yr,zr.
42func vp_rotate(x: i64, y: i64, z: i64, ang: i64, tbl: *i64, out: *i64) -> i64 {
43 let s: i64 = tbl[vp_idx(ang)]; let c: i64 = tbl[vp_idx(ang + 3)]
44 out[0] = (x*c - z*s) / VP_SCALE
45 out[1] = y
46 out[2] = (x*s + z*c) / VP_SCALE
47 return 0
48}
49// rotate (x,y,z) around X by `ax` THEN around Y by `ay` (each step = 30 deg) -> camera-space point. For arbitrary
50// viewing orientation (tilt + turn) so e.g. a board's top face can be angled toward the camera.
51func vp_rotate_xy(x: i64, y: i64, z: i64, ax: i64, ay: i64, tbl: *i64, out: *i64) -> i64 {
52 let sx: i64 = tbl[vp_idx(ax)]; let cx: i64 = tbl[vp_idx(ax + 3)]
53 let sy: i64 = tbl[vp_idx(ay)]; let cy: i64 = tbl[vp_idx(ay + 3)]
54 let y1: i64 = (y*cx - z*sx) / VP_SCALE
55 let z1: i64 = (y*sx + z*cx) / VP_SCALE
56 out[0] = (x*cy - z1*sy) / VP_SCALE
57 out[1] = y1
58 out[2] = (x*sy + z1*cy) / VP_SCALE
59 return 0
60}
61// perspective-project an already-rotated point to screen.
62func vp_project_pt(xr: i64, yr: i64, zr: i64, pp: *i64) -> i64 {
63 let zd: i64 = zr + VP_DIST
64 pp[0] = VP_CX + (xr * VP_FOCAL) / zd
65 pp[1] = VP_CY - (yr * VP_FOCAL) / zd
66 return 0
67}
68// integer square root (Newton). isqrt(144)=12.
69func vp_isqrt(n: i64) -> i64 {
70 if n <= 0 { return 0 }
71 var x: i64 = n; var y: i64 = (x + 1) / 2
72 while y < x { x = y; y = (x + n/x) / 2 }
73 return x
74}
75// cross product out = a x b.
76func vp_cross(ax: i64, ay: i64, az: i64, bx: i64, by: i64, bz: i64, out: *i64) -> i64 {
77 out[0] = ay*bz - az*by
78 out[1] = az*bx - ax*bz
79 out[2] = ax*by - ay*bx
80 return 0
81}
82// flat-shade brightness (60..255) of a triangle from its 3 rotated verts: faces toward the camera are bright,
83// edge-on dark (view-space lighting, winding-independent via |Nz|/|N|).
84func vp_shade(p0: *i64, p1: *i64, p2: *i64) -> i64 {
85 let n: *i64 = sys_mmap(32) as *i64
86 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)
87 var nz: i64 = n[2]; if nz < 0 { nz = 0 - nz }
88 let nlen: i64 = vp_isqrt(n[0]*n[0] + n[1]*n[1] + n[2]*n[2])
89 var nl: i64 = nlen; if nl == 0 { nl = 1 }
90 let align: i64 = nz * 256 / nl // 0..256 (256 = face-on to camera)
91 return 60 + align * 195 / 256 // 60..255
92}