code wiki / _hdl_build / nx_wasm_html_vox3d.nx

nx_wasm_html_vox3d.nx source

↩ module page · 27 lines · 4921 B

1// nx_wasm_html_vox3d.nx -- SOVEREIGN packager for the TRUE 3D voxel world. Emits a self-contained playable .html: 2// pointer-lock mouse-look + WASD + gravity/jump/fly + crosshair; L-click = break_at (dig the voxel you aim at), 3// R-click = place_at (drop a block in the air cell before it) -- both raycast inside wasm. JS = emitted output. 4// usage: nx_wasm_html_vox3d <in.wasm> <out.html>. license_tier: ORIGINAL 5import "nx_syscalls.nx" 6import "nx_base64.nx" 7 8func hw(fd: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(fd, s, n); return 0 } 9 10func main(argc: i64, argv: *i64) -> i64 { 11 if argc < 3 { hw(2, "usage: nx_wasm_html_vox3d <in.wasm> <out.html>\n" as *u8); return 2 } 12 let wlen_p: *i64 = sys_mmap(8) as *i64 13 let wbytes: *u8 = sys_read_file(argv[1] as *u8, wlen_p) 14 if (wbytes as i64) == 0 { hw(2, "read wasm failed\n" as *u8); return 3 } 15 let wlen: i64 = wlen_p[0] 16 let b64: *u8 = sys_mmap(wlen * 2 + 64) as *u8 17 let b64len: i64 = b64_encode(wbytes, wlen, b64) 18 let fd: i64 = sys_openat_wr(argv[2] as *u8, 0x1a4) 19 if fd < 0 { hw(2, "open html failed\n" as *u8); return 4 } 20 hw(fd, "<!doctype html><html><head><meta charset='utf-8'><title>Nishi 3D Voxel World</title><style>html,body{background:#0e1118;margin:0;height:100%;overflow:hidden;font-family:sans-serif;color:#cdd}#wrap{display:flex;align-items:center;justify-content:center;height:100vh}canvas{image-rendering:pixelated;width:768px;height:576px;background:#9ec3e6;border:1px solid #333;cursor:pointer}#hud{position:fixed;top:10px;left:0;right:0;text-align:center;font-size:13px;color:#aef;text-shadow:0 1px 2px #000;pointer-events:none}</style></head><body><div id='wrap'><canvas id='c'></canvas></div><div id='hud'>click to play &middot; WASD walk &middot; mouse look &middot; L-click dig &middot; R-click place &middot; Space jump &middot; F fly</div><script>\n" as *u8) 21 hw(fd, "const B='" as *u8) 22 sys_write(fd, b64, b64len) 23 hw(fd, "';\nconst bytes=Uint8Array.from(atob(B),c=>c.charCodeAt(0));\nWebAssembly.instantiate(bytes).then(r=>{const ex=r.instance.exports;const W=Number(ex.ww()),H=Number(ex.hh()),off=Number(ex.fb_off());const cv=document.getElementById('c');cv.width=W;cv.height=H;const ctx=cv.getContext('2d');const img=ctx.createImageData(W,H);const mem=new Uint8Array(ex.memory.buffer);let cam={x:24.5,y:12,z:4.5},yaw=0,pitch=-0.13,vy=0,fly=false,onground=false;function srf(x,z){let h=0;try{h=Number(ex.terrain_at(BigInt(Math.floor(x)),BigInt(Math.floor(z))));}catch(e){}return h;}function gy(){return srf(cam.x,cam.z)+1.6;}cam.y=gy();const keys={};addEventListener('keydown',e=>{keys[e.code]=1;if(e.code=='Space')e.preventDefault();if(e.code=='KeyF')fly=!fly;});addEventListener('keyup',e=>{keys[e.code]=0;});addEventListener('mousemove',e=>{if(document.pointerLockElement===cv){yaw+=e.movementX*0.0032;pitch-=e.movementY*0.0032;if(pitch>1.4)pitch=1.4;if(pitch<-1.4)pitch=-1.4;}});cv.addEventListener('mousedown',e=>{if(document.pointerLockElement!==cv){cv.requestPointerLock();return;}e.preventDefault();const ddx=Math.sin(yaw)*Math.cos(pitch),ddy=Math.sin(pitch),ddz=Math.cos(yaw)*Math.cos(pitch);const A=BigInt(Math.round(cam.x*256)),Bb=BigInt(Math.round(cam.y*256)),C=BigInt(Math.round(cam.z*256)),Dx=BigInt(Math.round(ddx*1000)),Dy=BigInt(Math.round(ddy*1000)),Dz=BigInt(Math.round(ddz*1000));if(e.button===0)ex.break_at(A,Bb,C,Dx,Dy,Dz);else if(e.button===2)ex.place_at(A,Bb,C,Dx,Dy,Dz);});cv.addEventListener('contextmenu',e=>e.preventDefault());function frame(){const sp=0.26;const fx=Math.sin(yaw),fz=Math.cos(yaw);let dx=0,dz=0;if(keys['KeyW']){dx+=fx;dz+=fz;}if(keys['KeyS']){dx-=fx;dz-=fz;}if(keys['KeyD']){dx+=fz;dz-=fx;}if(keys['KeyA']){dx-=fz;dz+=fx;}const tx=cam.x+dx*sp,tz=cam.z+dz*sp;if(fly){cam.x=tx;cam.z=tz;}else{const feet=cam.y-1.6;if(srf(tx,cam.z)<=feet+1.3)cam.x=tx;if(srf(cam.x,tz)<=feet+1.3)cam.z=tz;}if(fly){if(keys['Space'])cam.y+=sp;if(keys['ShiftLeft']||keys['ShiftRight'])cam.y-=sp;vy=0;}else{const g=gy();vy-=0.045;cam.y+=vy;if(cam.y<=g){cam.y=g;vy=0;onground=true;}else{onground=false;}if(keys['Space']&&onground)vy=0.7;}const yc=Math.round(Math.cos(yaw)*1000),ys=Math.round(Math.sin(yaw)*1000),pc=Math.round(Math.cos(pitch)*1000),ps=Math.round(Math.sin(pitch)*1000);ex.render(BigInt(Math.round(cam.x*256)),BigInt(Math.round(cam.y*256)),BigInt(Math.round(cam.z*256)),BigInt(yc),BigInt(ys),BigInt(pc),BigInt(ps));for(let i=0;i<W*H;i++){const b=off+i*8;img.data[i*4]=mem[b];img.data[i*4+1]=mem[b+1];img.data[i*4+2]=mem[b+2];img.data[i*4+3]=255;}ctx.putImageData(img,0,0);ctx.fillStyle='rgba(255,255,255,0.8)';ctx.fillRect((W>>1)-1,(H>>1)-5,2,11);ctx.fillRect((W>>1)-5,(H>>1)-1,11,2);requestAnimationFrame(frame);}frame();}).catch(e=>{document.body.innerHTML+='<pre style=color:#f99>'+e+'</pre>';});\n</script></body></html>\n" as *u8) 24 sys_close(fd) 25 hw(1, "nx_wasm_html_vox3d: wrote html\n" as *u8) 26 return 0 27}