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}