├── .gitattributes ├── .gitignore ├── README.md ├── argbcolor.nelua ├── camera.nelua ├── config.nelua ├── docs ├── index.html ├── lunray.js └── lunray.wasm ├── lunray.nelua ├── material.nelua ├── mathx.nelua ├── perlin.nelua ├── ray.nelua ├── sdf2d.nelua ├── sdl2.nelua ├── sphere.nelua ├── texture.nelua ├── vec2.nelua ├── vec3.nelua └── window.nelua /.gitattributes: -------------------------------------------------------------------------------- 1 | *.nelua text eol=lf linguist-language=lua 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | nelua_cache 2 | neluacfg.lua 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ray tracing experiment in Nelua 2 | 3 | ![Screenshot](https://i.gyazo.com/e115cf341b496ef022ebd5900d80a099.png) 4 | 5 | This project was developed for fun while [Nelua](https://github.com/edubart/nelua-lang) 6 | was being created. 7 | Ray tracing in realtime on the CPU is hard and tackling this problem was interesting 8 | to check out the efficiency of Nelua language. 9 | 10 | This is highly inspired on the 11 | [Ray Tracing in One Weekend](https://raytracing.github.io/books/RayTracingInOneWeekend.html) 12 | tutorial but with some personal customizations. 13 | 14 | ## Controls 15 | 16 | Use ASWDQE keys to walk and arrows to rotate the camera. 17 | 18 | ## Running on the Web Browser with WASM 19 | 20 | A demo with half resolution compiled with [emscripten](https://emscripten.org/) is available at https://edubart.github.io/lunray/ 21 | Your browser needs WASM support to run, note that this is running on the CPU inside the 22 | browser so slowness is expected, on my browser I get about ~11 FPS. 23 | 24 | ## Running natively 25 | 26 | With Nelua and SDL2 libs installed just do: 27 | 28 | ``` 29 | git clone https://github.com/edubart/lunray && cd lunray 30 | nelua --maximum-performance lunray.nelua 31 | ``` 32 | 33 | For higher FPS uncomment the `OPENMP = true` line on `lunray.nelua` to enable 34 | multi-threading rendering. I get about ~18 FPS on the screenshot generated above. 35 | -------------------------------------------------------------------------------- /argbcolor.nelua: -------------------------------------------------------------------------------- 1 | global argbcolor = @record{ 2 | r: byte, 3 | g: byte, 4 | b: byte, 5 | a: byte, 6 | } 7 | 8 | function argbcolor.fromvec3(col: vec3): argbcolor 9 | return argbcolor{ 10 | (@uint8)(math.clamp(col.x*255, 0, 255)), 11 | (@uint8)(math.clamp(col.y*255, 0, 255)), 12 | (@uint8)(math.clamp(col.z*255, 0, 255)), 13 | 255 14 | } 15 | end 16 | -------------------------------------------------------------------------------- /camera.nelua: -------------------------------------------------------------------------------- 1 | global Camera = @record{ 2 | lower_left_corner: vec3, 3 | lower_left_corner_direction: vec3, 4 | horizontal: vec3, 5 | vertical: vec3, 6 | origin: vec3, 7 | lookat: vec3, 8 | vup: vec3, 9 | u: vec3, 10 | v: vec3, 11 | w: vec3, 12 | lensradius: number, 13 | vfov: number, 14 | aspect: number, 15 | aperture: number, 16 | focusdist: number, 17 | } 18 | 19 | function Camera:update() 20 | local theta = self.vfov*math.pi/180 21 | local half_height = math.tan(theta/2) 22 | local half_width = self.aspect * half_height 23 | self.w = vec3.unit(self.origin - self.lookat) 24 | self.u = vec3.unit(vec3.cross(self.vup, self.w)) 25 | self.v = vec3.cross(self.w, self.u) 26 | self.lower_left_corner = 27 | self.origin - ( 28 | self.u * (half_width*self.focusdist) + 29 | self.v * (half_height*self.focusdist) + 30 | self.w * self.focusdist) 31 | self.lower_left_corner_direction = self.lower_left_corner - self.origin 32 | self.horizontal = self.u * (2*half_width*self.focusdist) 33 | self.vertical = self.v * (2*half_height*self.focusdist) 34 | self.lensradius = self.aperture / 2 35 | end 36 | 37 | function Camera.create(lookfrom: vec3, lookat: vec3, vup: vec3, 38 | vfov: number, aspect: number, aperture: number, focusdist: number): Camera 39 | if aperture == 0 then 40 | focusdist = 1 41 | end 42 | local self = Camera{ 43 | origin = lookfrom, 44 | lookat = lookat, 45 | vup = vup, 46 | vfov = vfov, 47 | aspect = aspect, 48 | aperture = aperture, 49 | focusdist = focusdist, 50 | } 51 | self:update() 52 | return self 53 | end 54 | 55 | function Camera:getray(u: number, v: number) 56 | local r: Ray 57 | if self.aperture ~= 0 then 58 | local rd = vec3.random_in_disk(self.lensradius) 59 | local offset = self.u * rd.x + self.v * rd.y 60 | r.origin = self.origin + offset 61 | r.direction = (self.lower_left_corner + self.horizontal * u + self.vertical * v) - r.origin 62 | else 63 | r.origin = self.origin 64 | r.direction = self.lower_left_corner_direction + self.horizontal * u + self.vertical * v 65 | end 66 | r.direction = r.direction:unit() 67 | return r 68 | end 69 | 70 | function Camera:rotate(x: number, y: number) 71 | local dir = self.lookat - self.origin 72 | dir = vec3.rotvec(dir, self.u, x) 73 | dir = vec3.rotvec(dir, self.v, y) 74 | self.lookat = self.origin + dir 75 | self:update() 76 | end 77 | 78 | function Camera:translate(x: number, y: number, z: number) 79 | local trans = vec3{0,0,0} 80 | trans = trans + self.u * x 81 | trans = trans + self.w * -z 82 | trans = trans + self.v * y 83 | self.origin = self.origin + trans 84 | self.lookat = self.lookat + trans 85 | self:update() 86 | end 87 | -------------------------------------------------------------------------------- /config.nelua: -------------------------------------------------------------------------------- 1 | ##[[ 2 | primtypes.number = primtypes.float32 3 | primtypes.integer = primtypes.int32 4 | primtypes.uinteger = primtypes.uint32 5 | 6 | if EMSCRIPTEN then 7 | OPENMP = false 8 | config.cc = 'emcc' 9 | cflags '--llvm-lto 1 --closure 1 -s USE_SDL=2 -s TOTAL_MEMORY=67108864' 10 | cinclude '' 11 | end 12 | if OPENMP then 13 | cflags '-fopenmp' 14 | end 15 | ]] 16 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | Emscripten-Generated Codeimage/svg+xml
Downloading...
Resize canvasLock/hide mouse pointer    
-------------------------------------------------------------------------------- /docs/lunray.js: -------------------------------------------------------------------------------- 1 | var g;g||(g=typeof Module !== 'undefined' ? Module : {});var aa={},ba;for(ba in g)g.hasOwnProperty(ba)&&(aa[ba]=g[ba]);var ca=[],da="./this.program";function ea(a,b){throw b;}var fa=!1,ha=!1,ia=!1,ja=!1;fa="object"===typeof window;ha="function"===typeof importScripts;ia="object"===typeof process&&"object"===typeof process.versions&&"string"===typeof process.versions.node;ja=!fa&&!ia&&!ha;var k="",ka,la,ma,na,oa; 2 | if(ia)k=ha?require("path").dirname(k)+"/":__dirname+"/",ka=function(a,b){na||(na=require("fs"));oa||(oa=require("path"));a=oa.normalize(a);return na.readFileSync(a,b?null:"utf8")},la=function(a){a=ka(a,!0);a.buffer||(a=new Uint8Array(a));assert(a.buffer);return a},1>0]=b;break;case "i8":r[a>>0]=b;break;case "i16":xa[a>>1]=b;break;case "i32":t[a>>2]=b;break;case "i64":ya=[b>>>0,(v=b,1<=+za(v)?0>>0:~~+Ca((v-+(~~v>>>0))/4294967296)>>>0:0)];t[a>>2]=ya[0];t[a+4>>2]=ya[1];break;case "float":x[a>>2]=b;break;case "double":z[a>>3]=b;break;default:l("invalid type for setValue: "+c)}} 8 | var Da,Ea=new WebAssembly.Table({initial:574,maximum:574,element:"anyfunc"}),Fa=!1;function assert(a,b){a||l("Assertion failed: "+b)}function Ga(a){if("number"===typeof a){var b=!0;var c=a}else b=!1,c=a.length;var d=A(Math.max(c,1));if(b){a=d;assert(0==(d&3));for(b=d+(c&-4);a>2]=0;for(b=d+c;a>0]=0;return d}a.subarray||a.slice?B.set(a,d):B.set(new Uint8Array(a),d);return d}var Ha="undefined"!==typeof TextDecoder?new TextDecoder("utf8"):void 0; 9 | function Ia(a,b,c){var d=b+c;for(c=b;a[c]&&!(c>=d);)++c;if(16e?d+=String.fromCharCode(e):(e-=65536,d+=String.fromCharCode(55296|e>>10,56320|e&1023))}}else d+=String.fromCharCode(e)}return d}function Ja(a,b){return a?Ia(B,a,b):""} 10 | function Ka(a,b,c,d){if(!(0=h){var m=a.charCodeAt(++f);h=65536+((h&1023)<<10)|m&1023}if(127>=h){if(c>=d)break;b[c++]=h}else{if(2047>=h){if(c+1>=d)break;b[c++]=192|h>>6}else{if(65535>=h){if(c+2>=d)break;b[c++]=224|h>>12}else{if(c+3>=d)break;b[c++]=240|h>>18;b[c++]=128|h>>12&63}b[c++]=128|h>>6&63}b[c++]=128|h&63}}b[c]=0;return c-e}function C(a,b,c){return Ka(a,B,b,c)} 11 | function La(a){for(var b=0,c=0;c=d&&(d=65536+((d&1023)<<10)|a.charCodeAt(++c)&1023);127>=d?++b:b=2047>=d?b+2:65535>=d?b+3:b+4}return b}"undefined"!==typeof TextDecoder&&new TextDecoder("utf-16le");function Ma(a){var b=La(a)+1,c=Na(b);Ka(a,r,c,b);return c}var buffer,r,B,xa,Oa,t,Pa,x,z,Qa=g.TOTAL_MEMORY||67108864;g.wasmMemory?Da=g.wasmMemory:Da=new WebAssembly.Memory({initial:Qa/65536,maximum:Qa/65536});Da&&(buffer=Da.buffer);Qa=buffer.byteLength; 12 | var E=buffer;buffer=E;g.HEAP8=r=new Int8Array(E);g.HEAP16=xa=new Int16Array(E);g.HEAP32=t=new Int32Array(E);g.HEAPU8=B=new Uint8Array(E);g.HEAPU16=Oa=new Uint16Array(E);g.HEAPU32=Pa=new Uint32Array(E);g.HEAPF32=x=new Float32Array(E);g.HEAPF64=z=new Float64Array(E);t[685976]=7986944;function Ra(a){for(;0>2;var e=0;if("undefined"!==typeof CanvasPixelArray&&a instanceof CanvasPixelArray)for(c=a.length;e>8&255;a[e+2]=f>>16&255;a[e+3]=255;b++;e+=4}else if(d.Tf!==a&&(d.tf=new Int32Array(a.buffer),d.uf=new Uint8Array(a.buffer)), 17 | a=d.tf,c=a.length,a.set(t.subarray(b,b+c)),a=d.uf,b=3,e=b+4*c,0==c%8)for(;b>=2;var m=0,n;if("undefined"!==typeof CanvasPixelArray&&b instanceof 18 | CanvasPixelArray)for(n=b.length;m>8&255;b[m+2]=q>>16&255;b[m+3]=q>>24&255;e++;m+=4}else b=new Int32Array(b.buffer),n=b.length,b.set(t.subarray(e,e+n));h.putImageData(a,0,0);c=0===c&&0===d?"url("+f.toDataURL()+"), auto":"url("+f.toDataURL()+") "+c+" "+d+", auto";d=A(c.length+1);C(c,d,c.length+1);return d},36016:function(a){g.canvas&&(g.canvas.style.cursor=Ja(a));return 0},36109:function(){g.canvas&&(g.canvas.style.cursor="none")},37334:function(){return screen.width}, 19 | 37361:function(){return screen.height},37434:function(a){"undefined"!==typeof g.setWindowTitle&&g.setWindowTitle(Ja(a));return 0},37588:function(){return"undefined"!==typeof AudioContext||"undefined"!==typeof webkitAudioContext?1:0},37754:function(){return"undefined"!==typeof navigator.mediaDevices&&"undefined"!==typeof navigator.mediaDevices.getUserMedia||"undefined"!==typeof navigator.webkitGetUserMedia?1:0},37980:function(a){"undefined"===typeof g.SDL2&&(g.SDL2={});var b=g.SDL2;a?b.capture={}: 20 | b.audio={};b.me||("undefined"!==typeof AudioContext?b.me=new AudioContext:"undefined"!==typeof webkitAudioContext&&(b.me=new webkitAudioContext));return void 0===b.me?-1:0},38463:function(){return g.SDL2.me.sampleRate},38533:function(a,b,c,d){function e(){}function f(m){void 0!==h.capture.Ge&&(clearTimeout(h.capture.Ge),h.capture.Ge=void 0);h.capture.Ke=h.me.createMediaStreamSource(m);h.capture.oe=h.me.createScriptProcessor(b,a,1);h.capture.oe.onaudioprocess=function(n){void 0!==h&&void 0!==h.capture&& 21 | (n.outputBuffer.getChannelData(0).fill(0),h.capture.Qe=n.inputBuffer,ta(c,[d]))};h.capture.Ke.connect(h.capture.oe);h.capture.oe.connect(h.me.destination);h.capture.stream=m}var h=g.SDL2;h.capture.Oe=h.me.createBuffer(a,b,h.me.sampleRate);h.capture.Oe.getChannelData(0).fill(0);h.capture.Ge=setTimeout(function(){h.capture.Qe=h.capture.Oe;ta(c,[d])},b/h.me.sampleRate*1E3);void 0!==navigator.mediaDevices&&void 0!==navigator.mediaDevices.getUserMedia?navigator.mediaDevices.getUserMedia({audio:!0,video:!1}).then(f).catch(e): 22 | void 0!==navigator.webkitGetUserMedia&&navigator.webkitGetUserMedia({audio:!0,video:!1},f,e)},40185:function(a,b,c,d){var e=g.SDL2;e.audio.oe=e.me.createScriptProcessor(b,0,a);e.audio.oe.onaudioprocess=function(f){void 0!==e&&void 0!==e.audio&&(e.audio.bf=f.outputBuffer,ta(c,[d]))};e.audio.oe.connect(e.me.destination)},40595:function(a,b){for(var c=g.SDL2,d=c.capture.Qe.numberOfChannels,e=0;e>2]}},41680:function(a){var b=g.SDL2;if(a){void 0!==b.capture.Ge&&clearTimeout(b.capture.Ge); 24 | if(void 0!==b.capture.stream){a=b.capture.stream.getAudioTracks();for(var c=0;c>2]=28)}function jb(a,b){for(var c=0,d=a.length-1;0<=d;d--){var e=a[d];"."===e?a.splice(d,1):".."===e?(a.splice(d,1),c++):c&&(a.splice(d,1),c--)}if(b)for(;c;c--)a.unshift("..");return a} 26 | function kb(a){var b="/"===a.charAt(0),c="/"===a.substr(-1);(a=jb(a.split("/").filter(function(d){return!!d}),!b).join("/"))||b||(a=".");a&&c&&(a+="/");return(b?"/":"")+a}function lb(a){var b=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.exec(a).slice(1);a=b[0];b=b[1];if(!a&&!b)return".";b&&(b=b.substr(0,b.length-1));return a+b}function mb(a){if("/"===a)return"/";var b=a.lastIndexOf("/");return-1===b?a:a.substr(b+1)} 27 | function nb(){for(var a="",b=!1,c=arguments.length-1;-1<=c&&!b;c--){b=0<=c?arguments[c]:"/";if("string"!==typeof b)throw new TypeError("Arguments to path.resolve must be strings");if(!b)return"";a=b+"/"+a;b="/"===b.charAt(0)}a=jb(a.split("/").filter(function(d){return!!d}),!b).join("/");return(b?"/":"")+a||"."}var ob=[];function pb(a,b){ob[a]={input:[],output:[],Ce:b};qb(a,rb)} 28 | var rb={open:function(a){var b=ob[a.node.rdev];if(!b)throw new F(43);a.tty=b;a.seekable=!1},close:function(a){a.tty.Ce.flush(a.tty)},flush:function(a){a.tty.Ce.flush(a.tty)},read:function(a,b,c,d){if(!a.tty||!a.tty.Ce.gf)throw new F(60);for(var e=0,f=0;f=b||(b=Math.max(b,c*(1048576>c?2:1.125)|0),0!=c&&(b=Math.max(b,256)),c=a.ie,a.ie=new Uint8Array(b),0b)a.ie.length=b;else for(;a.ie.length=a.node.le)return 0;a=Math.min(a.node.le-e,d);if(8b)throw new F(28);return b},Xe:function(a,b,c){G.cf(a.node,b+c);a.node.le=Math.max(a.node.le,b+c)},kf:function(a,b,c,d,e,f,h){if(32768!==(a.node.mode&61440))throw new F(43);c=a.node.ie;if(h& 38 | 2||c.buffer!==b.buffer){if(0>>0)%Db.length}function Kb(a){var b=Jb(a.parent.id,a.name);a.Gf=Db[b];Db[b]=a}function yb(a,b){var c;if(c=(c=Lb(a,"x"))?c:a.je.lookup?0:2)throw new F(c,a);for(c=Db[Jb(a.id,b)];c;c=c.Gf){var d=c.name;if(c.parent.id===a.id&&d===b)return c}return a.je.lookup(a,b)} 41 | function wb(a,b,c,d){Mb||(Mb=function(e,f,h,m){e||(e=this);this.parent=e;this.we=e.we;this.Me=null;this.id=Cb++;this.name=f;this.mode=h;this.je={};this.ke={};this.rdev=m},Mb.prototype={},Object.defineProperties(Mb.prototype,{read:{get:function(){return 365===(this.mode&365)},set:function(e){e?this.mode|=365:this.mode&=-366}},write:{get:function(){return 146===(this.mode&146)},set:function(e){e?this.mode|=146:this.mode&=-147}}}));a=new Mb(a,b,c,d);Kb(a);return a} 42 | var Nb={r:0,rs:1052672,"r+":2,w:577,wx:705,xw:705,"w+":578,"wx+":706,"xw+":706,a:1089,ax:1217,xa:1217,"a+":1090,"ax+":1218,"xa+":1218};function Ob(a){var b=["r","w","rw"][a&3];a&512&&(b+="w");return b}function Lb(a,b){if(Eb)return 0;if(-1===b.indexOf("r")||a.mode&292){if(-1!==b.indexOf("w")&&!(a.mode&146)||-1!==b.indexOf("x")&&!(a.mode&73))return 2}else return 2;return 0}function Pb(a,b){try{return yb(a,b),20}catch(c){}return Lb(a,"wx")} 43 | function Qb(a){var b=4096;for(a=a||0;a<=b;a++)if(!Bb[a])return a;throw new F(33);}function Rb(a,b){Sb||(Sb=function(){},Sb.prototype={},Object.defineProperties(Sb.prototype,{object:{get:function(){return this.node},set:function(e){this.node=e}}}));var c=new Sb,d;for(d in a)c[d]=a[d];a=c;b=Qb(b);a.fd=b;return Bb[b]=a}var vb={open:function(a){a.ke=Ab[a.node.rdev].ke;a.ke.open&&a.ke.open(a)},Be:function(){throw new F(70);}};function qb(a,b){Ab[a]={ke:b}} 44 | function Tb(a,b){var c="/"===b,d=!b;if(c&&zb)throw new F(10);if(!c&&!d){var e=Gb(b,{ef:!1});b=e.path;e=e.node;if(e.Me)throw new F(10);if(16384!==(e.mode&61440))throw new F(54);}b={type:a,cg:{},lf:b,Ff:[]};a=a.we(b);a.we=b;b.root=a;c?zb=a:e&&(e.Me=b,e.we&&e.we.Ff.push(b))}function Ub(a,b,c){var d=Gb(a,{parent:!0}).node;a=mb(a);if(!a||"."===a||".."===a)throw new F(28);var e=Pb(d,a);if(e)throw new F(e);if(!d.je.Le)throw new F(63);return d.je.Le(d,a,b,c)}function H(a){Ub(a,16895,0)} 45 | function Vb(a,b,c){"undefined"===typeof c&&(c=b,b=438);Ub(a,b|8192,c)}function Wb(a,b){if(!nb(a))throw new F(44);var c=Gb(b,{parent:!0}).node;if(!c)throw new F(44);b=mb(b);var d=Pb(c,b);if(d)throw new F(d);if(!c.je.symlink)throw new F(63);c.je.symlink(c,b,a)}function Hb(a){a=Gb(a).node;if(!a)throw new F(44);if(!a.je.readlink)throw new F(28);return nb(Ib(a.parent),a.je.readlink(a))} 46 | function Xb(a,b,c,d){if(""===a)throw new F(44);if("string"===typeof b){var e=Nb[b];if("undefined"===typeof e)throw Error("Unknown file open mode: "+b);b=e}c=b&64?("undefined"===typeof c?438:c)&4095|32768:0;if("object"===typeof a)var f=a;else{a=kb(a);try{f=Gb(a,{df:!(b&131072)}).node}catch(m){}}e=!1;if(b&64)if(f){if(b&128)throw new F(20);}else f=Ub(a,c,0),e=!0;if(!f)throw new F(44);8192===(f.mode&61440)&&(b&=-513);if(b&65536&&16384!==(f.mode&61440))throw new F(54);if(!e&&(c=f?40960===(f.mode&61440)? 47 | 32:16384===(f.mode&61440)&&("r"!==Ob(b)||b&512)?31:Lb(f,Ob(b)):44))throw new F(c);if(b&512){c=f;var h;"string"===typeof c?h=Gb(c,{df:!0}).node:h=c;if(!h.je.ve)throw new F(63);if(16384===(h.mode&61440))throw new F(31);if(32768!==(h.mode&61440))throw new F(28);if(c=Lb(h,"w"))throw new F(c);h.je.ve(h,{size:0,timestamp:Date.now()})}b&=-641;d=Rb({node:f,path:Ib(f),flags:b,seekable:!0,position:0,ke:f.ke,Mf:[],error:!1},d);d.ke.open&&d.ke.open(d);!g.logReadFiles||b&1||(Yb||(Yb={}),a in Yb||(Yb[a]=1,p("FS.trackingDelegate error on read file: "+ 48 | a)));try{Fb.onOpenFile&&(f=0,1!==(b&2097155)&&(f|=1),0!==(b&2097155)&&(f|=2),Fb.onOpenFile(a,f))}catch(m){p("FS.trackingDelegate['onOpenFile']('"+a+"', flags) threw an exception: "+m.message)}return d}function Zb(a,b,c){if(null===a.fd)throw new F(8);if(!a.seekable||!a.ke.Be)throw new F(70);if(0!=c&&1!=c&&2!=c)throw new F(28);a.position=a.ke.Be(a,b,c);a.Mf=[]} 49 | function $b(){F||(F=function(a,b){this.node=b;this.Kf=function(c){this.Ae=c};this.Kf(a);this.message="FS error"},F.prototype=Error(),F.prototype.constructor=F,[44].forEach(function(a){xb[a]=new F(a);xb[a].stack=""}))}var ac;function bc(a,b){var c=0;a&&(c|=365);b&&(c|=146);return c} 50 | function cc(a,b,c){a=kb("/dev/"+a);var d=bc(!!b,!!c);dc||(dc=64);var e=dc++<<8|0;qb(e,{open:function(f){f.seekable=!1},close:function(){c&&c.buffer&&c.buffer.length&&c(10)},read:function(f,h,m,n){for(var q=0,u=0;u>2]}function hc(a){void 0===a&&(a=I());a=Bb[a];if(!a)throw new F(8);return a}function ic(){l()}var jc=ia||"undefined"!==typeof dateNow||1;function kc(){l("To use dlopen, you need to use Emscripten's linking support, see https://github.com/emscripten-core/emscripten/wiki/Linking")} 52 | function lc(a,b){mc=a;nc=b;if(oc)if(0==a)pc=function(){var d=Math.max(0,qc+b-ic())|0;setTimeout(rc,d)};else if(1==a)pc=function(){sc(rc)};else if(2==a){if("undefined"===typeof setImmediate){var c=[];addEventListener("message",function(d){if("setimmediate"===d.data||"setimmediate"===d.data.target)d.stopPropagation(),c.shift()()},!0);setImmediate=function(d){c.push(d);ha?(void 0===g.setImmediates&&(g.setImmediates=[]),g.setImmediates.push(d),postMessage({target:"setimmediate"})):postMessage("setimmediate", 53 | "*")}}pc=function(){setImmediate(rc)}}} 54 | function tc(a,b,c,d,e){va=!0;assert(!oc,"emscripten_set_main_loop: there can only be one main loop function at once: call emscripten_cancel_main_loop to cancel the previous one before setting a new one with different parameters.");oc=a;uc=d;var f="undefined"!==typeof d?function(){g.dynCall_vi(a,d)}:function(){g.dynCall_v(a)};var h=vc;rc=function(){if(!Fa)if(0>y-6&63;y-=6;w+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[P]}2==y?(w+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(D&3)<<4],w+="=="):4==y&&(w+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(D&15)<<2],w+="=");u.src="data:audio/x-"+d.substr(-3)+";base64,"+w;h(u)}};u.src=q;Jc(function(){h(u)})}else return m()}});var b=g.canvas;b&&(b.requestPointerLock=b.requestPointerLock||b.mozRequestPointerLock|| 62 | b.webkitRequestPointerLock||b.msRequestPointerLock||function(){},b.exitPointerLock=document.exitPointerLock||document.mozExitPointerLock||document.webkitExitPointerLock||document.msExitPointerLock||function(){},b.exitPointerLock=b.exitPointerLock.bind(document),document.addEventListener("pointerlockchange",a,!1),document.addEventListener("mozpointerlockchange",a,!1),document.addEventListener("webkitpointerlockchange",a,!1),document.addEventListener("mspointerlockchange",a,!1),g.elementPointerLock&& 63 | b.addEventListener("click",function(c){!Bc&&g.canvas.requestPointerLock&&(g.canvas.requestPointerLock(),c.preventDefault())},!1))}} 64 | function Kc(a,b,c,d){if(b&&g.se&&a==g.canvas)return g.se;var e;if(b){var f={antialias:!1,alpha:!1,jf:1};if(d)for(var h in d)f[h]=d[h];if("undefined"!==typeof Lc&&(e=Mc(a,f)))var m=J[e].xe}else m=a.getContext("2d");if(!m)return null;c&&(b||assert("undefined"===typeof K,"cannot set in module if GLctx is used, but we are a non-GL context that would replace it"),g.se=m,b&&Nc(e),g.Nf=b,Cc.forEach(function(n){n()}),Dc());return m}var Oc=!1,Pc=void 0,Qc=void 0; 65 | function Rc(a,b,c){function d(){Ac=!1;var h=e.parentNode;(document.fullscreenElement||document.mozFullScreenElement||document.msFullscreenElement||document.webkitFullscreenElement||document.webkitCurrentFullScreenElement)===h?(e.exitFullscreen=Sc,Pc&&e.requestPointerLock(),Ac=!0,Qc?("undefined"!=typeof SDL&&(t[SDL.screen>>2]=Pa[SDL.screen>>2]|8388608),Tc(g.canvas),Uc()):Tc(e)):(h.parentNode.insertBefore(e,h),h.parentNode.removeChild(h),Qc?("undefined"!=typeof SDL&&(t[SDL.screen>>2]=Pa[SDL.screen>> 66 | 2]&-8388609),Tc(g.canvas),Uc()):Tc(e));if(g.onFullScreen)g.onFullScreen(Ac);if(g.onFullscreen)g.onFullscreen(Ac)}Pc=a;Qc=b;Vc=c;"undefined"===typeof Pc&&(Pc=!0);"undefined"===typeof Qc&&(Qc=!1);"undefined"===typeof Vc&&(Vc=null);var e=g.canvas;Oc||(Oc=!0,document.addEventListener("fullscreenchange",d,!1),document.addEventListener("mozfullscreenchange",d,!1),document.addEventListener("webkitfullscreenchange",d,!1),document.addEventListener("MSFullscreenChange",d,!1));var f=document.createElement("div"); 67 | e.parentNode.insertBefore(f,e);f.appendChild(e);f.requestFullscreen=f.requestFullscreen||f.mozRequestFullScreen||f.msRequestFullscreen||(f.webkitRequestFullscreen?function(){f.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT)}:null)||(f.webkitRequestFullScreen?function(){f.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT)}:null);c?f.requestFullscreen({eg:c}):f.requestFullscreen()} 68 | function Sc(){if(!Ac)return!1;(document.exitFullscreen||document.cancelFullScreen||document.mozCancelFullScreen||document.msExitFullscreen||document.webkitCancelFullScreen||function(){}).apply(document,[]);return!0}var Wc=0;function sc(a){if("function"===typeof requestAnimationFrame)requestAnimationFrame(a);else{var b=Date.now();if(0===Wc)Wc=b+1E3/60;else for(;b+2>=Wc;)Wc+=1E3/60;setTimeout(a,Math.max(Wc-b,0))}}function Jc(a){va=!0;setTimeout(function(){Fa||a()},1E4)} 69 | function Ic(a){return{jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",bmp:"image/bmp",ogg:"audio/ogg",wav:"audio/wav",mp3:"audio/mpeg"}[a.substr(a.lastIndexOf(".")+1)]}var Xc=[];function Uc(){var a=g.canvas;Xc.forEach(function(b){b(a.width,a.height)})} 70 | function Tc(a,b,c){b&&c?(a.Pf=b,a.Af=c):(b=a.Pf,c=a.Af);var d=b,e=c;g.forcedAspectRatio&&0a.version&&(dd(b),ed(b),fd(b));b.pe=b.getExtension("EXT_disjoint_timer_query");var c="OES_texture_float OES_texture_half_float OES_standard_derivatives OES_vertex_array_object WEBGL_compressed_texture_s3tc WEBGL_depth_texture OES_element_index_uint EXT_texture_filter_anisotropic EXT_frag_depth WEBGL_draw_buffers ANGLE_instanced_arrays OES_texture_float_linear OES_texture_half_float_linear EXT_blend_minmax EXT_shader_texture_lod EXT_texture_norm16 WEBGL_compressed_texture_pvrtc EXT_color_buffer_half_float WEBGL_color_buffer_float EXT_sRGB WEBGL_compressed_texture_etc1 EXT_disjoint_timer_query WEBGL_compressed_texture_etc WEBGL_compressed_texture_astc EXT_color_buffer_float WEBGL_compressed_texture_s3tc_srgb EXT_disjoint_timer_query_webgl2 WEBKIT_WEBGL_compressed_texture_pvrtc".split(" "); 76 | (b.getSupportedExtensions()||[]).forEach(function(d){-1!=c.indexOf(d)&&b.getExtension(d)})}}var Lc={},vd,wd;function xd(){L=12288;return 1}var yd=0,zd=0,U=0,Ad=0,Bd=0,Cd=0,Dd=0,Ed=0,Fd=0,Gd=null,Hd=null,Id=null,Jd=!1;function Kd(){for(var a=V.length-1;0<=a;--a)Ld(a);V=[];W=[]}var W=[]; 77 | function Md(a,b,c){function d(h,m){if(h.length!=m.length)return!1;for(var n in h)if(h[n]!=m[n])return!1;return!0}for(var e in W){var f=W[e];if(f.We==a&&d(f.Ye,c))return}W.push({We:a,pf:b,Ye:c});W.sort(function(h,m){return h.pf>2]=a.width,t[d>>2]=a.height;c=[t[c>>2],t[d>>2]];ae(b);return c}function be(a,b,c){a=Y(a);if(!a)return-4;a.width=b;a.height=c;return 0} 80 | function ce(a,b,c){if(a.Rf){var d=$d(),e=Na(a.id.length+1);C(a.id,e,a.id.length+1);be(e,b,c);ae(d)}else a.width=b,a.height=c} 81 | function de(a){function b(){document.fullscreenElement||document.webkitFullscreenElement||document.msFullscreenElement||(document.removeEventListener("fullscreenchange",b),document.removeEventListener("webkitfullscreenchange",b),ce(a,d,e),a.style.width=f,a.style.height=h,a.style.backgroundColor=m,n||(document.body.style.backgroundColor="white"),document.body.style.backgroundColor=n,a.style.paddingLeft=q,a.style.paddingRight=u,a.style.paddingTop=w,a.style.paddingBottom=D,a.style.marginLeft=y,a.style.marginRight= 82 | O,a.style.marginTop=P,a.style.marginBottom=hb,document.body.style.margin=Le,document.documentElement.style.overflow=Me,document.body.scroll=Ne,a.style.hf=Oe,a.De&&a.De.xe.viewport(0,0,d,e),Wd.Ie&&Z(Wd.Ie,37,0,Wd.Ze))}var c=Zd(a),d=c[0],e=c[1],f=a.style.width,h=a.style.height,m=a.style.backgroundColor,n=document.body.style.backgroundColor,q=a.style.paddingLeft,u=a.style.paddingRight,w=a.style.paddingTop,D=a.style.paddingBottom,y=a.style.marginLeft,O=a.style.marginRight,P=a.style.marginTop,hb=a.style.marginBottom, 83 | Le=document.body.style.margin,Me=document.documentElement.style.overflow,Ne=document.body.scroll,Oe=a.style.hf;document.addEventListener("fullscreenchange",b);document.addEventListener("webkitfullscreenchange",b)}function ee(a,b,c){a.style.paddingLeft=a.style.paddingRight=c+"px";a.style.paddingTop=a.style.paddingBottom=b+"px"} 84 | function fe(a,b){if(0!=b.Ve||0!=b.Pe){de(a);var c=b.Lf?innerWidth:screen.width,d=b.Lf?innerHeight:screen.height,e=a.getBoundingClientRect(),f=e.width;e=e.height;var h=Zd(a),m=h[0];h=h[1];3==b.Ve?(ee(a,(d-e)/2,(c-f)/2),c=f,d=e):2==b.Ve&&(c*h>3]=b.timestamp;for(var c=0;c>3]=b.axes[c];for(c=0;c>3]="object"===typeof b.buttons[c]?b.buttons[c].value:b.buttons[c];for(c=0;c>2]="object"===typeof b.buttons[c]?b.buttons[c].pressed:1==b.buttons[c];t[a+1296>>2]=b.connected;t[a+1300>>2]=b.index;t[a+8>>2]=b.axes.length;t[a+12>>2]=b.buttons.length;C(b.id,a+1304,64);C(b.mapping,a+1368,64)}var ie=[]; 87 | function je(a,b,c,d){for(var e=0;e>2]=h}}function ke(a,b){Pa[a>>2]=b;Pa[a+4>>2]=(b-Pa[a>>2])/4294967296} 88 | function le(a,b,c){if(b){var d=void 0;switch(a){case 36346:d=1;break;case 36344:0!=c&&1!=c&&T(1280);return;case 36345:d=0;break;case 34466:var e=K.getParameter(34467);d=e?e.length:0}if(void 0===d)switch(e=K.getParameter(a),typeof e){case "number":d=e;break;case "boolean":d=e?1:0;break;case "string":T(1280);return;case "object":if(null===e)switch(a){case 34964:case 35725:case 34965:case 36006:case 36007:case 32873:case 34229:case 34068:d=0;break;default:T(1280);return}else{if(e instanceof Float32Array|| 89 | e instanceof Uint32Array||e instanceof Int32Array||e instanceof Array){for(a=0;a>2]=e[a];break;case 2:x[b+4*a>>2]=e[a];break;case 4:r[b+a>>0]=e[a]?1:0}return}try{d=e.name|0}catch(f){T(1280);p("GL_INVALID_ENUM in glGet"+c+"v: Unknown object returned from WebGL getParameter("+a+")! (error: "+f+")");return}}break;default:T(1280);p("GL_INVALID_ENUM in glGet"+c+"v: Native code calling glGet"+c+"v("+a+") and it returns "+e+" of type "+typeof e+"!");return}switch(c){case 1:ke(b, 90 | d);break;case 0:t[b>>2]=d;break;case 2:x[b>>2]=d;break;case 4:r[b>>0]=d?1:0}}else T(1281)}function me(a){var b=La(a)+1,c=A(b);C(a,c,b);return c} 91 | function ne(a,b,c,d){if(c)if(a=K.getUniform(N[a],Q[b]),"number"==typeof a||"boolean"==typeof a)switch(d){case 0:t[c>>2]=a;break;case 2:x[c>>2]=a;break;default:throw"internal emscriptenWebGLGetUniform() error, bad type: "+d;}else for(b=0;b>2]=a[b];break;case 2:x[c+4*b>>2]=a[b];break;default:throw"internal emscriptenWebGLGetUniform() error, bad type: "+d;}else T(1281)} 92 | function oe(a,b,c,d){if(c)if(a=K.getVertexAttrib(a,b),34975==b)t[c>>2]=a.name;else if("number"==typeof a||"boolean"==typeof a)switch(d){case 0:t[c>>2]=a;break;case 2:x[c>>2]=a;break;case 5:t[c>>2]=Math.fround(a);break;default:throw"internal emscriptenWebGLGetVertexAttrib() error, bad type: "+d;}else for(b=0;b>2]=a[b];break;case 2:x[c+4*b>>2]=a[b];break;case 5:t[c+4*b>>2]=Math.fround(a[b]);break;default:throw"internal emscriptenWebGLGetVertexAttrib() error, bad type: "+ 93 | d;}else T(1281)}function pe(a,b,c,d,e){a-=5120;a=1==a?B:4==a?t:6==a?x:5==a||28922==a?Pa:Oa;var f=31-Math.clz32(a.BYTES_PER_ELEMENT),h=qd;return a.subarray(e>>f,e+d*(c*({5:3,6:4,8:2,29502:3,29504:4}[b-6402]||1)*(1<>f)}function qe(a,b){if(!Sd())return-1;a=Y(a);return a?a.requestFullscreen||a.webkitRequestFullscreen?Pd&&Qd.ze?fe(a,b):b.vf?(Md(fe,1,[a,b]),1):-2:-3:-4} 94 | function re(a,b,c,d,e,f){Bd||(Bd=A(256));a={target:Y(a),ne:f,re:d,te:function(h){h=h||event;var m=h.target.id?h.target.id:"",n=Bd;C(Rd(h.target),n+0,128);C(m,n+128,128);Z(d,e,n,b)&&h.preventDefault()},qe:c};X(a)} 95 | function se(a,b,c,d,e){Cd||(Cd=A(280));X({target:a,ne:e,re:d,te:function(f){f=f||event;var h=Cd,m=document.fullscreenElement||document.mozFullScreenElement||document.webkitFullscreenElement||document.msFullscreenElement,n=!!m;t[h>>2]=n;t[h+4>>2]=Sd();var q=n?m:Gd,u=q&&q.id?q.id:"";C(Rd(q),h+8,128);C(u,h+136,128);t[h+264>>2]=q?q.clientWidth:0;t[h+268>>2]=q?q.clientHeight:0;t[h+272>>2]=screen.width;t[h+276>>2]=screen.height;n&&(Gd=m);Z(d,19,h,b)&&f.preventDefault()},qe:c})} 96 | function te(a,b,c,d,e){Vd||(Vd=A(1432));b={target:Y(2),ze:!0,ne:e,re:c,te:function(f){f=f||event;var h=Vd;he(h,f.gamepad);Z(c,d,h,a)&&f.preventDefault()},qe:b};X(b)} 97 | function ue(a,b,c,d,e,f){yd||(yd=A(164));a={target:Y(a),ze:!0,ne:f,re:d,te:function(h){h=h||event;var m=yd;C(h.key?h.key:"",m+0,32);C(h.code?h.code:"",m+32,32);t[m+64>>2]=h.location;t[m+68>>2]=h.ctrlKey;t[m+72>>2]=h.shiftKey;t[m+76>>2]=h.altKey;t[m+80>>2]=h.metaKey;t[m+84>>2]=h.repeat;C(h.locale?h.locale:"",m+88,32);C(h.char?h.char:"",m+120,32);t[m+152>>2]=h.charCode;t[m+156>>2]=h.keyCode;t[m+160>>2]=h.which;Z(d,e,m,b)&&h.preventDefault()},qe:c};X(a)} 98 | function ve(a,b,c){t[a>>2]=b.screenX;t[a+4>>2]=b.screenY;t[a+8>>2]=b.clientX;t[a+12>>2]=b.clientY;t[a+16>>2]=b.ctrlKey;t[a+20>>2]=b.shiftKey;t[a+24>>2]=b.altKey;t[a+28>>2]=b.metaKey;xa[a+32>>1]=b.button;xa[a+34>>1]=b.buttons;var d=b.movementY||b.screenY-Id;t[a+36>>2]=b.movementX||b.screenX-Hd;t[a+40>>2]=d;c=0>Yd.indexOf(c)?c.getBoundingClientRect():{left:0,top:0};t[a+44>>2]=b.clientX-c.left;t[a+48>>2]=b.clientY-c.top;"wheel"!==b.type&&"mousewheel"!==b.type&&(Hd=b.screenX,Id=b.screenY)} 99 | function we(a,b,c,d,e,f){zd||(zd=A(64));a=Y(a);X({target:a,ze:"mousemove"!=f&&"mouseenter"!=f&&"mouseleave"!=f,ne:f,re:d,te:function(h){h=h||event;ve(zd,h,a);Z(d,e,zd,b)&&h.preventDefault()},qe:c})}function xe(a,b,c,d,e){Dd||(Dd=A(260));X({target:a,ne:e,re:d,te:function(f){f=f||event;var h=Dd,m=document.pointerLockElement||document.Ef||document.Zf||document.Yf;t[h>>2]=!!m;var n=m&&m.id?m.id:"";C(Rd(m),h+4,128);C(n,h+132,128);Z(d,20,h,b)&&f.preventDefault()},qe:c})} 100 | function ye(a,b,c,d){Ad||(Ad=A(36));a=Y(a);X({target:a,ne:"resize",re:d,te:function(e){e=e||event;if(e.target==a){var f=Ad,h=document.body;t[f>>2]=e.detail;t[f+4>>2]=h.clientWidth;t[f+8>>2]=h.clientHeight;t[f+12>>2]=innerWidth;t[f+16>>2]=innerHeight;t[f+20>>2]=outerWidth;t[f+24>>2]=outerHeight;t[f+28>>2]=pageXOffset;t[f+32>>2]=pageYOffset;Z(d,10,f,b)&&e.preventDefault()}},qe:c})} 101 | function ze(a,b,c,d,e,f){Fd||(Fd=A(1684));a=Y(a);X({target:a,ze:"touchstart"==f||"touchend"==f,ne:f,re:d,te:function(h){h=h||event;for(var m={},n=0;n>2]=h.ctrlKey;t[u+8>>2]=h.shiftKey;t[u+12>>2]=h.altKey;t[u+16>>2]=h.metaKey;u+=20;var w=a.getBoundingClientRect(), 102 | D=0;for(n in m){var y=m[n];t[u>>2]=y.identifier;t[u+4>>2]=y.screenX;t[u+8>>2]=y.screenY;t[u+12>>2]=y.clientX;t[u+16>>2]=y.clientY;t[u+20>>2]=y.pageX;t[u+24>>2]=y.pageY;t[u+28>>2]=y.$e;t[u+32>>2]=y.Hf;t[u+36>>2]=y.clientX-w.left;t[u+40>>2]=y.clientY-w.top;u+=52;if(32<=++D)break}t[q>>2]=D;Z(d,e,q,b)&&h.preventDefault()},qe:c})} 103 | function Ae(a,b,c){var d=Yd[1];Ed||(Ed=A(8));X({target:d,ne:"visibilitychange",re:c,te:function(e){e=e||event;var f=Ed,h=["hidden","visible","prerender","unloaded"].indexOf(document.visibilityState);t[f>>2]=document.hidden;t[f+4>>2]=h;Z(c,21,f,a)&&e.preventDefault()},qe:b})} 104 | function Be(a,b,c,d,e){function f(m){m=m||event;ve(U,m,a);z[U+64>>3]=m.wheelDeltaX||0;z[U+72>>3]=-(m.wheelDeltaY||m.wheelDelta);z[U+80>>3]=0;t[U+88>>2]=0;Z(d,9,U,b)&&m.preventDefault()}function h(m){m=m||event;var n=U;ve(n,m,a);z[n+64>>3]=m.deltaX;z[n+72>>3]=m.deltaY;z[n+80>>3]=m.deltaZ;t[n+88>>2]=m.deltaMode;Z(d,9,n,b)&&m.preventDefault()}U||(U=A(96));X({target:a,ze:!0,ne:e,re:d,te:"wheel"==e?h:f,qe:c})}var Ce={}; 105 | function De(){if(!Ee){var a={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:("object"===typeof navigator&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",_:da||"./this.program"},b;for(b in Ce)a[b]=Ce[b];var c=[];for(b in a)c.push(b+"="+a[b]);Ee=c}return Ee}var Ee,Fe;$b();Db=Array(4096);Tb(G,"/");H("/tmp");H("/home");H("/home/web_user"); 106 | (function(){H("/dev");qb(259,{read:function(){return 0},write:function(d,e,f,h){return h}});Vb("/dev/null",259);pb(1280,tb);pb(1536,ub);Vb("/dev/tty",1280);Vb("/dev/tty1",1536);if("object"===typeof crypto&&"function"===typeof crypto.getRandomValues){var a=new Uint8Array(1);var b=function(){crypto.getRandomValues(a);return a[0]}}else if(ia)try{var c=require("crypto");b=function(){return c.randomBytes(1)[0]}}catch(d){}b||(b=function(){l("random_device")});cc("random",b);cc("urandom",b);H("/dev/shm"); 107 | H("/dev/shm/tmp")})();H("/proc");H("/proc/self");H("/proc/self/fd");Tb({we:function(){var a=wb("/proc/self","fd",16895,73);a.je={lookup:function(b,c){var d=Bb[+c];if(!d)throw new F(8);b={parent:null,we:{lf:"fake"},je:{readlink:function(){return d.path}}};return b.parent=b}};return a}},"/proc/self/fd");ia?ic=function(){var a=process.hrtime();return 1E3*a[0]+a[1]/1E6}:"undefined"!==typeof dateNow?ic=dateNow:ic=function(){return performance.now()};g.requestFullscreen=function(a,b,c){Rc(a,b,c)}; 108 | g.requestAnimationFrame=function(a){sc(a)};g.setCanvasSize=function(a,b,c){Tc(g.canvas,a,b);c||Uc()};g.pauseMainLoop=function(){pc=null;vc++};g.resumeMainLoop=function(){vc++;var a=mc,b=nc,c=oc;oc=null;tc(c,0,!1,uc,!0);lc(a,b);pc()};g.getUserMedia=function(){window.getUserMedia||(window.getUserMedia=navigator.getUserMedia||navigator.mozGetUserMedia);window.getUserMedia(void 0)};g.createContext=function(a,b,c,d){return Kc(a,b,c,d)}; 109 | for(var K,Ge=new Float32Array(256),He=0;256>He;He++)sd[He]=Ge.subarray(0,He+1);var Ie=new Int32Array(256);for(He=0;256>He;He++)td[He]=Ie.subarray(0,He+1);for(var Je=0;32>Je;Je++)ie.push(Array(Je));function sb(a,b){var c=Array(La(a)+1);a=Ka(a,c,0,c.length);b&&(c.length=a);return c} 110 | var Qe={ta:function(){},k:function(a,b){fc=b;try{var c=hc();switch(I()){case 0:var d=I();return 0>d?-28:Xb(c.path,c.flags,0,d).fd;case 1:case 2:return 0;case 3:return c.flags;case 4:return d=I(),c.flags|=d,0;case 12:return d=I(),xa[d+0>>1]=2,0;case 13:case 14:return 0;case 16:case 8:return-28;case 9:return ib(),-1;default:return-28}}catch(e){return"undefined"!==typeof ec&&e instanceof F||l(e),-e.Ae}},wa:function(a,b){fc=b;try{var c=Ja(I()),d=I(),e=I();return Xb(c,d,e).fd}catch(f){return"undefined"!== 111 | typeof ec&&f instanceof F||l(f),-f.Ae}},ua:function(a,b){fc=b;try{var c=hc(),d=I();switch(d){case 21509:case 21505:return c.tty?0:-59;case 21510:case 21511:case 21512:case 21506:case 21507:case 21508:return c.tty?0:-59;case 21519:if(!c.tty)return-59;var e=I();return t[e>>2]=0;case 21520:return c.tty?-28:-59;case 21531:a=e=I();if(!c.ke.Df)throw new F(59);return c.ke.Df(c,d,a);case 21523:return c.tty?0:-59;case 21524:return c.tty?0:-59;default:l("bad ioctl syscall "+d)}}catch(f){return"undefined"!== 112 | typeof ec&&f instanceof F||l(f),-f.Ae}},f:function(){},sd:function(){l()},h:function(a,b){if(0===a)a=Date.now();else{if(1!==a&&4!==a||!jc)return ib(),-1;a=ic()}t[b>>2]=a/1E3|0;t[b+4>>2]=a%1E3*1E6|0;return 0},Aa:function(){return kc.apply(null,arguments)},Ca:function(){return kc.apply(null,arguments)},q:function(){return kc.apply(null,arguments)},rd:function(a){if(12448==a)return L=12288,1;L=12300;return 0},Ba:function(a,b,c,d,e){if(62E3!=a)L=12296,c=0;else{if(b)for(;;){a=t[b>>2];if(12321==a)M.alpha= 113 | 0>2];else if(12325==a)M.depth=0>2];else if(12326==a)M.stencil=0>2];else if(12337==a)a=t[b+4>>2],M.antialias=0>2],M.antialias=1==a;else if(12544==a)M.$f=12547!=t[b+4>>2];else if(12344==a)break;b+=8}c&&d||e?(e&&(t[e>>2]=1),c&&0>2]=62002),L=12288,c=1):(L=12300,c=0)}return c},Ab:function(a,b,c,d){if(62E3!=a)return L=12296,0;for(a=1;;){b=t[d>>2];if(12440==b)a=t[d+4>>2];else if(12344==b)break;else return L=12292,0;d+=8}if(2!=a)return L=12293,0;M.jf= 114 | a-1;M.ag=0;cd=Mc(g.canvas,M);if(0!=cd)return L=12288,Nc(cd),g.Nf=!0,Cc.forEach(function(e){e()}),Nc(null),62004;L=12297;return 0},Wb:function(a,b){if(62E3!=a)return L=12296,0;if(62002!=b)return L=12293,0;L=12288;return 62006},Lb:function(a,b){if(62E3!=a)return L=12296,0;if(62004!=b)return L=12294,0;a=cd;nd===J[a]&&(nd=null);if("object"===typeof Td)for(var c=J[a].xe.canvas,d=0;d>2]=M.alpha?32:24,1;case 12321:return t[d>>2]=M.alpha?8:0,1;case 12322:return t[d>>2]=8,1;case 12323:return t[d>>2]=8,1;case 12324:return t[d>>2]=8,1;case 12325:return t[d>>2]=M.depth?24:0,1;case 12326:return t[d>>2]=M.stencil? 116 | 8:0,1;case 12327:return t[d>>2]=12344,1;case 12328:return t[d>>2]=62002,1;case 12329:return t[d>>2]=0,1;case 12330:return t[d>>2]=4096,1;case 12331:return t[d>>2]=16777216,1;case 12332:return t[d>>2]=4096,1;case 12333:return t[d>>2]=0,1;case 12334:return t[d>>2]=0,1;case 12335:return t[d>>2]=12344,1;case 12337:return t[d>>2]=M.antialias?4:0,1;case 12338:return t[d>>2]=M.antialias?1:0,1;case 12339:return t[d>>2]=4,1;case 12340:return t[d>>2]=12344,1;case 12341:case 12342:case 12343:return t[d>>2]= 117 | -1,1;case 12345:case 12346:return t[d>>2]=0,1;case 12347:return t[d>>2]=0,1;case 12348:return t[d>>2]=1;case 12349:case 12350:return t[d>>2]=0,1;case 12351:return t[d>>2]=12430,1;case 12352:return t[d>>2]=4,1;case 12354:return t[d>>2]=0,1;default:return L=12292,0}},j:function(){L=12288;return 62E3},pb:function(){return L},W:function(a){return Pe(a)},pa:function(a,b,c){if(62E3==a)return b&&(t[b>>2]=1),c&&(t[c>>2]=4),Yc=!0,L=12288,1;L=12296;return 0},qc:function(a,b,c,d){if(62E3!=a)return L=12296,0; 118 | if(0!=d&&62004!=d)return L=12294,0;if(0!=c&&62006!=c||0!=b&&62006!=b)return L=12301,0;Nc(d?cd:null);Zc=d;ad=b;$c=c;L=12288;return 1},eb:function(a,b){if(62E3!=a)return L=12296,0;L=12288;if(bd[b])return bd[b];switch(b){case 12371:a=Ga(sb("Emscripten"));break;case 12372:a=Ga(sb("1.4 Emscripten EGL"));break;case 12373:a=Ga(sb(""));break;case 12429:a=Ga(sb("OpenGL_ES"));break;default:return L=12300,0}return bd[b]=a},Bc:function(){if(Yc)if(g.se)if(g.se.isContextLost())L=12302;else return L=12288,1;else L= 119 | 12290;else L=12289;return 0},Mc:function(a,b){if(62E3!=a)return L=12296,0;0==b?lc(0,0):lc(1,b);L=12288;return 1},xa:function(a){if(62E3!=a)return L=12296,0;ad=$c=Zc=0;Yc=!1;L=12288;return 1},gd:function(){return xd.apply(null,arguments)},Xc:function(){L=12288;return 1},a:function(a,b,c){Fe||(Fe=[]);var d=Fe;d.length=0;for(var e;e=B[b++];)100===e||102===e?(c=c+7&-8,d.push(z[c>>3]),c+=8):(c=c+3&-4,d.push(t[c>>2]),c+=4);return fb[a].apply(null,d)},Da:function(){if(!Sd())return-1;Nd(fe);var a=Yd[1];if(a.exitFullscreen)a.fullscreenElement&& 120 | a.exitFullscreen();else if(a.webkitExitFullscreen)a.webkitFullscreenElement&&a.webkitExitFullscreen();else return-1;return 0},La:function(){Nd(ge);if(document.exitPointerLock)document.exitPointerLock();else if(document.Je)document.Je();else return-1;return 0},e:function(){return devicePixelRatio||1},c:function(a,b,c){a=Y(a);if(!a)return-4;a=a.getBoundingClientRect();z[b>>3]=a.width;z[c>>3]=a.height;return 0},o:function(a,b){if(0>a||a>=Ud.length)return-5;if(!Ud[a])return-7;he(b,Ud[a]);return 0},za:function(){return Ud.length}, 121 | U:function(a){K.activeTexture(a)},T:function(a,b){K.attachShader(N[a],R[b])},ja:function(a,b){K.pe.beginQueryEXT(a,S[b])},S:function(a,b,c){K.bindAttribLocation(N[a],b,Ja(c))},R:function(a,b){K.bindBuffer(a,id[b])},Q:function(a,b){K.bindFramebuffer(a,jd[b])},P:function(a,b){K.bindRenderbuffer(a,kd[b])},O:function(a,b){K.bindTexture(a,ld[b])},ba:function(a){K.bindVertexArray(md[a])},N:function(a,b,c,d){K.blendColor(a,b,c,d)},M:function(a){K.blendEquation(a)},qd:function(a,b){K.blendEquationSeparate(a, 122 | b)},pd:function(a,b){K.blendFunc(a,b)},od:function(a,b,c,d){K.blendFuncSeparate(a,b,c,d)},nd:function(a,b,c,d){K.bufferData(a,c?B.subarray(c,c+b):b,d)},md:function(a,b,c,d){K.bufferSubData(a,b,B.subarray(d,d+c))},ld:function(a){return K.checkFramebufferStatus(a)},kd:function(a){K.clear(a)},jd:function(a,b,c,d){K.clearColor(a,b,c,d)},id:function(a){K.clearDepth(a)},hd:function(a){K.clearStencil(a)},fd:function(a,b,c,d){K.colorMask(!!a,!!b,!!c,!!d)},ed:function(a){K.compileShader(R[a])},dd:function(a, 123 | b,c,d,e,f,h,m){K.compressedTexImage2D(a,b,c,d,e,f,m?B.subarray(m,m+h):null)},cd:function(a,b,c,d,e,f,h,m,n){K.compressedTexSubImage2D(a,b,c,d,e,f,h,n?B.subarray(n,n+m):null)},bd:function(a,b,c,d,e,f,h,m){K.copyTexImage2D(a,b,c,d,e,f,h,m)},ad:function(a,b,c,d,e,f,h,m){K.copyTexSubImage2D(a,b,c,d,e,f,h,m)},$c:function(){var a=rd(N),b=K.createProgram();b.name=a;N[a]=b;return a},_c:function(a){var b=rd(R);R[b]=K.createShader(a);return b},Zc:function(a){K.cullFace(a)},Yc:function(a,b){for(var c=0;c>2],e=id[d];e&&(K.deleteBuffer(e),e.name=0,id[d]=null,d==vd&&(vd=0),d==wd&&(wd=0))}},Wc:function(a,b){for(var c=0;c>2],e=jd[d];e&&(K.deleteFramebuffer(e),e.name=0,jd[d]=null)}},Vc:function(a){if(a){var b=N[a];b?(K.deleteProgram(b),b.name=0,N[a]=null,od[a]=null):T(1281)}},la:function(a,b){for(var c=0;c>2],e=S[d];e&&(K.pe.deleteQueryEXT(e),S[d]=null)}},Uc:function(a,b){for(var c=0;c>2],e=kd[d];e&&(K.deleteRenderbuffer(e),e.name= 125 | 0,kd[d]=null)}},Tc:function(a){if(a){var b=R[a];b?(K.deleteShader(b),R[a]=null):T(1281)}},Sc:function(a,b){for(var c=0;c>2],e=ld[d];e&&(K.deleteTexture(e),e.name=0,ld[d]=null)}},aa:function(a,b){for(var c=0;c>2];K.deleteVertexArray(md[d]);md[d]=null}},Rc:function(a){K.depthFunc(a)},Qc:function(a){K.depthMask(!!a)},Pc:function(a,b){K.depthRange(a,b)},Oc:function(a,b){K.detachShader(N[a],R[b])},Nc:function(a){K.disable(a)},Lc:function(a){K.disableVertexAttribArray(a)}, 126 | Kc:function(a,b,c){K.drawArrays(a,b,c)},Y:function(a,b,c,d){K.drawArraysInstanced(a,b,c,d)},Z:function(a,b){for(var c=ie[a],d=0;d>2];K.drawBuffers(c)},Jc:function(a,b,c,d){K.drawElements(a,b,c,d)},X:function(a,b,c,d,e){K.drawElementsInstanced(a,b,c,d,e)},Ic:function(a){K.enable(a)},Hc:function(a){K.enableVertexAttribArray(a)},ia:function(a){K.pe.endQueryEXT(a)},Gc:function(){K.finish()},Fc:function(){K.flush()},Ec:function(a,b,c,d){K.framebufferRenderbuffer(a,b,c,kd[d])},Dc:function(a, 127 | b,c,d,e){K.framebufferTexture2D(a,b,c,ld[d],e)},Cc:function(a){K.frontFace(a)},Ac:function(a,b){je(a,b,"createBuffer",id)},yc:function(a,b){je(a,b,"createFramebuffer",jd)},ma:function(a,b){for(var c=0;c>2]=0;break}var e=rd(S);d.name=e;S[e]=d;t[b+4*c>>2]=e}},xc:function(a,b){je(a,b,"createRenderbuffer",kd)},wc:function(a,b){je(a,b,"createTexture",ld)},$:function(a,b){je(a,b,"createVertexArray",md)},zc:function(a){K.generateMipmap(a)}, 128 | vc:function(a,b,c,d,e,f,h){a=N[a];if(a=K.getActiveAttrib(a,b))c=0>2]=c),e&&(t[e>>2]=a.size),f&&(t[f>>2]=a.type)},uc:function(a,b,c,d,e,f,h){a=N[a];if(a=K.getActiveUniform(a,b))c=0>2]=c),e&&(t[e>>2]=a.size),f&&(t[f>>2]=a.type)},tc:function(a,b,c,d){a=K.getAttachedShaders(N[a]);var e=a.length;e>b&&(e=b);t[c>>2]=e;for(b=0;b>2]=R.indexOf(a[b])},sc:function(a,b){return K.getAttribLocation(N[a],Ja(b))},rc:function(a,b){le(a,b,4)}, 129 | pc:function(a,b,c){c?t[c>>2]=K.getBufferParameter(a,b):T(1281)},oc:function(){var a=K.getError()||hd;hd=0;return a},nc:function(a,b){le(a,b,2)},mc:function(a,b,c,d){a=K.getFramebufferAttachmentParameter(a,b,c);if(a instanceof WebGLRenderbuffer||a instanceof WebGLTexture)a=a.name|0;t[d>>2]=a},lc:function(a,b){le(a,b,0)},jc:function(a,b,c,d){a=K.getProgramInfoLog(N[a]);null===a&&(a="(unknown error)");b=0>2]=b)},kc:function(a,b,c){if(c)if(a>=gd)T(1281);else{var d=od[a];if(d)if(35716== 130 | b)a=K.getProgramInfoLog(N[a]),null===a&&(a="(unknown error)"),t[c>>2]=a.length+1;else if(35719==b)t[c>>2]=d.Se;else if(35722==b){if(-1==d.Ee){a=N[a];var e=K.getProgramParameter(a,35721);for(b=d.Ee=0;b>2]=d.Ee}else if(35381==b){if(-1==d.Fe)for(a=N[a],e=K.getProgramParameter(a,35382),b=d.Fe=0;b>2]=d.Fe}else t[c>>2]=K.getProgramParameter(N[a],b);else T(1282)}else T(1281)}, 131 | da:function(a,b,c){if(c){a=K.pe.getQueryObjectEXT(S[a],b);var d;"boolean"==typeof a?d=a?1:0:d=a;ke(c,d)}else T(1281)},fa:function(a,b,c){if(c){a=K.pe.getQueryObjectEXT(S[a],b);var d;"boolean"==typeof a?d=a?1:0:d=a;t[c>>2]=d}else T(1281)},ca:function(a,b,c){if(c){a=K.pe.getQueryObjectEXT(S[a],b);var d;"boolean"==typeof a?d=a?1:0:d=a;ke(c,d)}else T(1281)},ea:function(a,b,c){if(c){a=K.pe.getQueryObjectEXT(S[a],b);var d;"boolean"==typeof a?d=a?1:0:d=a;t[c>>2]=d}else T(1281)},ga:function(a,b,c){c?t[c>> 132 | 2]=K.pe.getQueryEXT(a,b):T(1281)},ic:function(a,b,c){c?t[c>>2]=K.getRenderbufferParameter(a,b):T(1281)},gc:function(a,b,c,d){a=K.getShaderInfoLog(R[a]);null===a&&(a="(unknown error)");b=0>2]=b)},ec:function(a,b,c,d){a=K.getShaderPrecisionFormat(a,b);t[c>>2]=a.rangeMin;t[c+4>>2]=a.rangeMax;t[d>>2]=a.precision},dc:function(a,b,c,d){if(a=K.getShaderSource(R[a]))b=0>2]=b)},hc:function(a,b,c){c?35716==b?(a=K.getShaderInfoLog(R[a]),null===a&&(a="(unknown error)"), 133 | t[c>>2]=a.length+1):35720==b?(a=K.getShaderSource(R[a]),t[c>>2]=null===a||0==a.length?0:a.length+1):t[c>>2]=K.getShaderParameter(R[a],b):T(1281)},cc:function(a){if(pd[a])return pd[a];switch(a){case 7939:var b=K.getSupportedExtensions()||[];b=b.concat(b.map(function(d){return"GL_"+d}));b=me(b.join(" "));break;case 7936:case 7937:case 37445:case 37446:(b=K.getParameter(a))||T(1280);b=me(b);break;case 7938:b=me("OpenGL ES 2.0 ("+K.getParameter(7938)+")");break;case 35724:b=K.getParameter(35724);var c= 134 | b.match(/^WebGL GLSL ES ([0-9]\.[0-9][0-9]?)(?:$| .*)/);null!==c&&(3==c[1].length&&(c[1]+="0"),b="OpenGL ES GLSL ES "+c[1]+" ("+b+")");b=me(b);break;default:return T(1280),0}return pd[a]=b},bc:function(a,b,c){c?x[c>>2]=K.getTexParameter(a,b):T(1281)},ac:function(a,b,c){c?t[c>>2]=K.getTexParameter(a,b):T(1281)},Zb:function(a,b){b=Ja(b);var c=0;if("]"==b[b.length-1]){var d=b.lastIndexOf("[");c="]"!=b[d+1]?parseInt(b.slice(d+1)):0;b=b.slice(0,d)}return(a=od[a]&&od[a].qf[b])&&0<=c&&c>2]=K.getVertexAttribOffset(a,b):T(1281)},Yb:function(a,b,c){oe(a,b,c,2)},Xb:function(a,b,c){oe(a,b,c,5)},Ub:function(a,b){K.hint(a,b)},Tb:function(a){return(a=id[a])?K.isBuffer(a):0},Sb:function(a){return K.isEnabled(a)},Rb:function(a){return(a=jd[a])?K.isFramebuffer(a):0},Qb:function(a){return(a=N[a])?K.isProgram(a):0},ka:function(a){return(a=S[a])?K.pe.isQueryEXT(a):0},Pb:function(a){return(a=kd[a])?K.isRenderbuffer(a): 136 | 0},Ob:function(a){return(a=R[a])?K.isShader(a):0},Nb:function(a){return(a=ld[a])?K.isTexture(a):0},_:function(a){return(a=md[a])?K.isVertexArray(a):0},Mb:function(a){K.lineWidth(a)},Kb:function(a){K.linkProgram(N[a]);var b=N[a];a=od[a]={qf:{},Se:0,Ee:-1,Fe:-1};for(var c=a.qf,d=K.getProgramParameter(b,35718),e=0;e>2]:-1;e+=Ja(t[c+4*f>>2],0>h?void 0:h)}K.shaderSource(R[a],e)},zb:function(a,b,c){K.stencilFunc(a,b,c)},yb:function(a,b,c,d){K.stencilFuncSeparate(a,b,c,d)},xb:function(a){K.stencilMask(a)},wb:function(a,b){K.stencilMaskSeparate(a,b)},vb:function(a,b,c){K.stencilOp(a,b,c)},ub:function(a,b,c,d){K.stencilOpSeparate(a,b,c,d)},tb:function(a,b,c,d,e,f,h,m,n){K.texImage2D(a,b,c,d,e,f,h,m,n?pe(m,h,d,e,n):null)},sb:function(a,b,c){K.texParameterf(a,b,c)},rb:function(a, 139 | b,c){K.texParameterf(a,b,x[c>>2])},qb:function(a,b,c){K.texParameteri(a,b,c)},ob:function(a,b,c){K.texParameteri(a,b,t[c>>2])},nb:function(a,b,c,d,e,f,h,m,n){var q=null;n&&(q=pe(m,h,e,f,n));K.texSubImage2D(a,b,c,d,e,f,h,m,q)},mb:function(a,b){K.uniform1f(Q[a],b)},lb:function(a,b,c){if(256>=b)for(var d=sd[b-1],e=0;e>2];else d=x.subarray(c>>2,c+4*b>>2);K.uniform1fv(Q[a],d)},kb:function(a,b){K.uniform1i(Q[a],b)},jb:function(a,b,c){if(256>=b)for(var d=td[b-1],e=0;e>2];else d=t.subarray(c>>2,c+4*b>>2);K.uniform1iv(Q[a],d)},ib:function(a,b,c){K.uniform2f(Q[a],b,c)},hb:function(a,b,c){if(256>=2*b)for(var d=sd[2*b-1],e=0;e<2*b;e+=2)d[e]=x[c+4*e>>2],d[e+1]=x[c+(4*e+4)>>2];else d=x.subarray(c>>2,c+8*b>>2);K.uniform2fv(Q[a],d)},gb:function(a,b,c){K.uniform2i(Q[a],b,c)},fb:function(a,b,c){if(256>=2*b)for(var d=td[2*b-1],e=0;e<2*b;e+=2)d[e]=t[c+4*e>>2],d[e+1]=t[c+(4*e+4)>>2];else d=t.subarray(c>>2,c+8*b>>2);K.uniform2iv(Q[a],d)},db:function(a,b,c,d){K.uniform3f(Q[a], 141 | b,c,d)},cb:function(a,b,c){if(256>=3*b)for(var d=sd[3*b-1],e=0;e<3*b;e+=3)d[e]=x[c+4*e>>2],d[e+1]=x[c+(4*e+4)>>2],d[e+2]=x[c+(4*e+8)>>2];else d=x.subarray(c>>2,c+12*b>>2);K.uniform3fv(Q[a],d)},bb:function(a,b,c,d){K.uniform3i(Q[a],b,c,d)},ab:function(a,b,c){if(256>=3*b)for(var d=td[3*b-1],e=0;e<3*b;e+=3)d[e]=t[c+4*e>>2],d[e+1]=t[c+(4*e+4)>>2],d[e+2]=t[c+(4*e+8)>>2];else d=t.subarray(c>>2,c+12*b>>2);K.uniform3iv(Q[a],d)},$a:function(a,b,c,d,e){K.uniform4f(Q[a],b,c,d,e)},_a:function(a,b,c){if(256>= 142 | 4*b)for(var d=sd[4*b-1],e=0;e<4*b;e+=4)d[e]=x[c+4*e>>2],d[e+1]=x[c+(4*e+4)>>2],d[e+2]=x[c+(4*e+8)>>2],d[e+3]=x[c+(4*e+12)>>2];else d=x.subarray(c>>2,c+16*b>>2);K.uniform4fv(Q[a],d)},Za:function(a,b,c,d,e){K.uniform4i(Q[a],b,c,d,e)},Ya:function(a,b,c){if(256>=4*b)for(var d=td[4*b-1],e=0;e<4*b;e+=4)d[e]=t[c+4*e>>2],d[e+1]=t[c+(4*e+4)>>2],d[e+2]=t[c+(4*e+8)>>2],d[e+3]=t[c+(4*e+12)>>2];else d=t.subarray(c>>2,c+16*b>>2);K.uniform4iv(Q[a],d)},Xa:function(a,b,c,d){if(256>=4*b)for(var e=sd[4*b-1],f=0;f<4* 143 | b;f+=4)e[f]=x[d+4*f>>2],e[f+1]=x[d+(4*f+4)>>2],e[f+2]=x[d+(4*f+8)>>2],e[f+3]=x[d+(4*f+12)>>2];else e=x.subarray(d>>2,d+16*b>>2);K.uniformMatrix2fv(Q[a],!!c,e)},Wa:function(a,b,c,d){if(256>=9*b)for(var e=sd[9*b-1],f=0;f<9*b;f+=9)e[f]=x[d+4*f>>2],e[f+1]=x[d+(4*f+4)>>2],e[f+2]=x[d+(4*f+8)>>2],e[f+3]=x[d+(4*f+12)>>2],e[f+4]=x[d+(4*f+16)>>2],e[f+5]=x[d+(4*f+20)>>2],e[f+6]=x[d+(4*f+24)>>2],e[f+7]=x[d+(4*f+28)>>2],e[f+8]=x[d+(4*f+32)>>2];else e=x.subarray(d>>2,d+36*b>>2);K.uniformMatrix3fv(Q[a],!!c,e)}, 144 | Va:function(a,b,c,d){if(256>=16*b)for(var e=sd[16*b-1],f=0;f<16*b;f+=16)e[f]=x[d+4*f>>2],e[f+1]=x[d+(4*f+4)>>2],e[f+2]=x[d+(4*f+8)>>2],e[f+3]=x[d+(4*f+12)>>2],e[f+4]=x[d+(4*f+16)>>2],e[f+5]=x[d+(4*f+20)>>2],e[f+6]=x[d+(4*f+24)>>2],e[f+7]=x[d+(4*f+28)>>2],e[f+8]=x[d+(4*f+32)>>2],e[f+9]=x[d+(4*f+36)>>2],e[f+10]=x[d+(4*f+40)>>2],e[f+11]=x[d+(4*f+44)>>2],e[f+12]=x[d+(4*f+48)>>2],e[f+13]=x[d+(4*f+52)>>2],e[f+14]=x[d+(4*f+56)>>2],e[f+15]=x[d+(4*f+60)>>2];else e=x.subarray(d>>2,d+64*b>>2);K.uniformMatrix4fv(Q[a], 145 | !!c,e)},Ua:function(a){K.useProgram(N[a])},Ta:function(a){K.validateProgram(N[a])},Sa:function(a,b){K.vertexAttrib1f(a,b)},Ra:function(a,b){K.vertexAttrib1f(a,x[b>>2])},Qa:function(a,b,c){K.vertexAttrib2f(a,b,c)},Pa:function(a,b){K.vertexAttrib2f(a,x[b>>2],x[b+4>>2])},Oa:function(a,b,c,d){K.vertexAttrib3f(a,b,c,d)},Na:function(a,b){K.vertexAttrib3f(a,x[b>>2],x[b+4>>2],x[b+8>>2])},Ma:function(a,b,c,d,e){K.vertexAttrib4f(a,b,c,d,e)},Ka:function(a,b){K.vertexAttrib4f(a,x[b>>2],x[b+4>>2],x[b+8>>2],x[b+ 146 | 12>>2])},V:function(a,b){K.vertexAttribDivisor(a,b)},Ja:function(a,b,c,d,e,f){K.vertexAttribPointer(a,b,c,!!d,e,f)},Ia:function(a,b,c,d){K.viewport(a,b,c,d)},na:function(a,b,c){B.set(B.subarray(b,b+c),a)},Ea:function(a,b,c){var d={};d.Ve=t[c>>2];d.Pe=t[c+4>>2];d.yf=t[c+8>>2];d.vf=b;d.Ie=t[c+12>>2];d.Ze=t[c+16>>2];Wd=d;return qe(a,d)},L:function(a,b){a=Y(a);return a?a.requestPointerLock||a.Ne?Pd&&Qd.ze?ge(a):b?(Md(ge,2,[a]),1):-2:-1:-4},oa:function(){l("OOM")},p:function(){return(Ud=navigator.getGamepads? 147 | navigator.getGamepads():navigator.webkitGetGamepads?navigator.webkitGetGamepads():null)?0:-1},C:function(a,b,c,d){re(a,b,c,d,12,"blur");return 0},d:be,i:function(a,b,c){a=Y(a);if(!a)return-4;a.style.width=b+"px";a.style.height=c+"px";return 0},D:function(a,b,c,d){re(a,b,c,d,13,"focus");return 0},t:function(a,b,c,d){if(!Sd())return-1;a=Y(a);if(!a)return-4;se(a,b,c,d,"fullscreenchange");se(a,b,c,d,"webkitfullscreenchange");return 0},n:function(a,b,c){if(!navigator.getGamepads&&!navigator.webkitGetGamepads)return-1; 148 | te(a,b,c,26,"gamepadconnected");return 0},m:function(a,b,c){if(!navigator.getGamepads&&!navigator.webkitGetGamepads)return-1;te(a,b,c,27,"gamepaddisconnected");return 0},w:function(a,b,c,d){ue(a,b,c,d,2,"keydown");return 0},u:function(a,b,c,d){ue(a,b,c,d,1,"keypress");return 0},v:function(a,b,c,d){ue(a,b,c,d,3,"keyup");return 0},td:function(a,b,c,d){tc(a,c,d,b)},I:function(a,b,c,d){we(a,b,c,d,5,"mousedown");return 0},G:function(a,b,c,d){we(a,b,c,d,33,"mouseenter");return 0},F:function(a,b,c,d){we(a, 149 | b,c,d,34,"mouseleave");return 0},J:function(a,b,c,d){we(a,b,c,d,8,"mousemove");return 0},H:function(a,b,c,d){we(a,b,c,d,6,"mouseup");return 0},x:function(a,b,c,d){if(!document||!document.body||!(document.body.requestPointerLock||document.body.Je||document.body.Ef||document.body.Ne))return-1;a=Y(a);if(!a)return-4;xe(a,b,c,d,"pointerlockchange");xe(a,b,c,d,"mozpointerlockchange");xe(a,b,c,d,"webkitpointerlockchange");xe(a,b,c,d,"mspointerlockchange");return 0},s:function(a,b,c,d){ye(a,b,c,d);return 0}, 150 | y:function(a,b,c,d){ze(a,b,c,d,25,"touchcancel");return 0},A:function(a,b,c,d){ze(a,b,c,d,23,"touchend");return 0},z:function(a,b,c,d){ze(a,b,c,d,24,"touchmove");return 0},B:function(a,b,c,d){ze(a,b,c,d,22,"touchstart");return 0},r:function(a,b,c){if(!Yd[1])return-4;Ae(a,b,c);return 0},E:function(a,b,c,d){a=Y(a);return"undefined"!==typeof a.onwheel?(Be(a,b,c,d,"wheel"),0):"undefined"!==typeof a.onmousewheel?(Be(a,b,c,d,"mousewheel"),0):-1},qa:function(a,b){var c=0;De().forEach(function(d,e){var f= 151 | b+c;e=t[a+4*e>>2]=f;for(f=0;f>0]=d.charCodeAt(f);r[e>>0]=0;c+=d.length+1});return 0},ra:function(a,b){var c=De();t[a>>2]=c.length;var d=0;c.forEach(function(e){d+=e.length+1});t[b>>2]=d;return 0},l:function(a){try{var b=hc(a);if(null===b.fd)throw new F(8);b.Re&&(b.Re=null);try{b.ke.close&&b.ke.close(b)}catch(c){throw c;}finally{Bb[b.fd]=null}b.fd=null;return 0}catch(c){return"undefined"!==typeof ec&&c instanceof F||l(c),c.Ae}},sa:function(a,b,c,d){try{a:{for(var e=hc(a),f=a=0;f< 152 | c;f++){var h=t[b+(8*f+4)>>2],m=e,n=t[b+8*f>>2],q=h,u=void 0,w=r;if(0>q||0>u)throw new F(28);if(null===m.fd)throw new F(8);if(1===(m.flags&2097155))throw new F(8);if(16384===(m.node.mode&61440))throw new F(31);if(!m.ke.read)throw new F(28);var D="undefined"!==typeof u;if(!D)u=m.position;else if(!m.seekable)throw new F(70);var y=m.ke.read(m,w,n,q,u);D||(m.position+=y);var O=y;if(0>O){var P=-1;break a}a+=O;if(O>2]=P;return 0}catch(hb){return"undefined"!==typeof ec&&hb instanceof F|| 153 | l(hb),hb.Ae}},Ha:function(a,b,c,d,e){try{var f=hc(a);a=4294967296*c+(b>>>0);if(-9007199254740992>=a||9007199254740992<=a)return-61;Zb(f,a,d);ya=[f.position>>>0,(v=f.position,1<=+za(v)?0>>0:~~+Ca((v-+(~~v>>>0))/4294967296)>>>0:0)];t[e>>2]=ya[0];t[e+4>>2]=ya[1];f.Re&&0===a&&0===d&&(f.Re=null);return 0}catch(h){return"undefined"!==typeof ec&&h instanceof F||l(h),h.Ae}},va:function(a,b,c,d){try{a:{for(var e=hc(a),f=a=0;f>2],n=t[b+(8*f+ 154 | 4)>>2],q=void 0,u=r;if(0>n||0>q)throw new F(28);if(null===h.fd)throw new F(8);if(0===(h.flags&2097155))throw new F(8);if(16384===(h.node.mode&61440))throw new F(31);if(!h.ke.write)throw new F(28);h.flags&1024&&Zb(h,0,2);var w="undefined"!==typeof q;if(!w)q=h.position;else if(!h.seekable)throw new F(70);var D=h.ke.write(h,u,m,n,q,void 0);w||(h.position+=D);try{if(h.path&&Fb.onWriteToFile)Fb.onWriteToFile(h.path)}catch(P){p("FS.trackingDelegate['onWriteToFile']('"+h.path+"') threw an exception: "+P.message)}var y= 155 | D;if(0>y){var O=-1;break a}a+=y}O=a}t[d>>2]=O;return 0}catch(P){return"undefined"!==typeof ec&&P instanceof F||l(P),P.Ae}},g:function(a){var b=Date.now();t[a>>2]=b/1E3|0;t[a+4>>2]=b%1E3*1E3|0;return 0},memory:Da,ya:function(a,b){if(0===a)return ib(),-1;var c=t[a>>2];a=t[a+4>>2];if(0>a||999999999c)return ib(),-1;0!==b&&(t[b>>2]=0,t[b+4>>2]=0);b=1E6*c+a/1E3;for(c=ic();ic()-c>2]=Ma(da);for(var h=1;h>2)+h]=Ma(c[h-1]);t[(f>>2)+e]=0;try{var m=d(e,f);if(!va||0!==m){if(!va&&(Fa=!0,g.onExit))g.onExit(m);ea(m,new pa(m))}}catch(n){n instanceof pa||("unwind"==n?va=!0:((c=n)&&"object"===typeof n&&n.stack&&(c=[n,n.stack]),p("exception thrown: "+c),ea(1,n)))}finally{}}if(g.postRun)for("function"==typeof g.postRun&&(g.postRun=[g.postRun]);g.postRun.length;)c=g.postRun.shift(),Wa.unshift(c);Ra(Wa)}}a=a||ca;if(!(0 = 480 23 | local SCREEN_HEIGHT = 270 24 | local NUMSAMPLES = 1 25 | local MAXDEPTH = 8 26 | local APERTURE = 0.05 27 | local SCREEN_SCALE = 1 28 | 29 | local worldsize 30 | local Hitlist = @array(Sphere,500) 31 | local world: Hitlist 32 | local cam: Camera 33 | local window: Window 34 | local pixels: [SCREEN_HEIGHT][SCREEN_WIDTH]argbcolor 35 | ## if DENOISER then 36 | local tempixels: [SCREEN_HEIGHT][SCREEN_WIDTH]vec3 37 | local ntem = 0 38 | ## end 39 | local lastticks = SDL_GetTicks() 40 | local fps = 0 41 | local quit = false 42 | 43 | local function Hitlist_hit(self: *Hitlist, r: Ray, tmin: number, tmax: number): (boolean, Hit) 44 | local rec: Hit 45 | local tnear = tmax 46 | local hashit = false 47 | for i=0, 64 | local hitted, rec = Hitlist_hit(world, r, 0.001, 1e32) 65 | if hitted then 66 | local emitted = Material.emitted(rec.mat, rec.uv, rec.p) 67 | if depth > MAXDEPTH then 68 | return emitted 69 | end 70 | local forward, attenuation, scattered = Material.scatter(rec.mat, r, rec) 71 | if forward then 72 | return emitted + trace_ray(scattered, depth+1) * attenuation 73 | else 74 | return emitted 75 | end 76 | end 77 | return sky_color(r.direction) 78 | end 79 | 80 | local function draw() 81 | ## if DENOISER then 82 | if ntem == 0 then 83 | for y=0, = 3 151 | for a=-NUMSPHERES, 182 | local event: SDL_Event 183 | while SDL_PollEvent(&event) ~= 0 do 184 | switch event.type 185 | case SDL_QUIT then 186 | quit = true 187 | case SDL_KEYDOWN then 188 | local kevent = (@*SDL_KeyboardEvent)(&event) 189 | switch kevent.keysym.sym 190 | case SDLK_UP then 191 | cam:rotate(0.05, 0) 192 | refresh() 193 | case SDLK_DOWN then 194 | cam:rotate(-0.05, 0) 195 | refresh() 196 | case SDLK_RIGHT then 197 | cam:rotate(0, -0.05) 198 | refresh() 199 | case SDLK_LEFT then 200 | cam:rotate(0, 0.05) 201 | refresh() 202 | case SDLK_ESCAPE then 203 | quit = true 204 | case SDLK_w then 205 | cam:translate(0, 0, 0.05) 206 | refresh() 207 | case SDLK_s then 208 | cam:translate(0, 0, -0.05) 209 | refresh() 210 | case SDLK_a then 211 | cam:translate(-0.05, 0, 0) 212 | refresh() 213 | case SDLK_d then 214 | cam:translate(0.05, 0, 0) 215 | refresh() 216 | case SDLK_e then 217 | cam:translate(0, 0.05, 0) 218 | refresh() 219 | case SDLK_q then 220 | cam:translate(0, -0.05, 0) 221 | refresh() 222 | end 223 | end 224 | end 225 | end 226 | 227 | local function frame() 228 | local ticks = SDL_GetTicks() 229 | if ticks - lastticks >= 1000 then 230 | print('FPS', fps) 231 | lastticks = ticks 232 | fps = 0 233 | end 234 | 235 | poll_events() 236 | draw() 237 | fps = fps + 1 238 | end 239 | 240 | local function emframe(context: pointer) 241 | frame() 242 | end 243 | 244 | local function go() 245 | world_init() 246 | window = Window.create(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_SCALE) 247 | 248 | ## if not EMSCRIPTEN then 249 | repeat 250 | frame() 251 | until quit 252 | 253 | window:destroy() 254 | ## else 255 | ## cemit 'emscripten_set_main_loop_arg(emframe, 0, -1, 1);' 256 | ## end 257 | end 258 | 259 | go() 260 | -------------------------------------------------------------------------------- /material.nelua: -------------------------------------------------------------------------------- 1 | global MATERIALS = @enum { 2 | LAMBERTIAN = 0, 3 | METAL = 1, 4 | DIELECTRIC = 2 5 | } 6 | 7 | global Material = @record{ 8 | kind: MATERIALS, 9 | albedo: Texture, 10 | emitting: Texture, 11 | fuzz: number 12 | } 13 | 14 | global Hit = @record{ 15 | p: vec3, 16 | normal: vec3, 17 | uv: vec2, 18 | mat: *Material, 19 | t: number 20 | } 21 | 22 | local function reflect(v: vec3, n: vec3): vec3 23 | return v - n * (2*vec3.dot(v,n)) 24 | end 25 | 26 | local function refract(v: vec3, n: vec3, ni: number): (boolean, vec3) 27 | local dt = vec3.dot(v, n) 28 | local discriminant = 1 - ni*ni*(1-dt*dt) 29 | if discriminant > 0 then 30 | local refracted = 31 | ni * (v - n * dt) - 32 | n * math.sqrt(discriminant) 33 | return true, refracted 34 | else 35 | return false, vec3{} 36 | end 37 | end 38 | 39 | local function schlick(cosine: number, refidx: number): number 40 | local r0 = (1-refidx) / (1+refidx) 41 | r0 = r0*r0 42 | return r0 + (1-r0)*((1-cosine)^5) 43 | end 44 | 45 | function Material:lambertian_scatter(r: Ray, rec: Hit): (boolean, vec3, Ray) 46 | local scattered = Ray{rec.p} 47 | ## if CARTOONIZED then 48 | scattered.direction = vec3{0,1,0} 49 | ## else 50 | scattered.direction = vec3.random_in_hemisphere(rec.normal) 51 | ## end 52 | return true, self.albedo:color(rec.uv, rec.p), scattered 53 | end 54 | 55 | function Material:metal_scatter(r: Ray, rec: Hit): (boolean, vec3, Ray) 56 | local reflected = reflect(r.direction, rec.normal) 57 | ## if not CARTOONIZED then 58 | if self.fuzz > 0 then 59 | reflected = reflected + vec3.random_in_hemisphere(rec.normal) * self.fuzz 60 | end 61 | ## end 62 | local scattered = Ray{rec.p, reflected} 63 | local forward = vec3.dot(scattered.direction, rec.normal) > 0 64 | return forward, self.albedo:color(rec.uv, rec.p), scattered 65 | end 66 | 67 | function Material:dielectric_scatter(r: Ray, rec: Hit): (boolean, vec3, Ray) 68 | local outward_normal: vec3 69 | local ni: number, reflectprob: number 70 | local cosine = vec3.dot(r.direction, rec.normal) 71 | if vec3.dot(r.direction, rec.normal) > 0 then 72 | cosine = math.sqrt(1 - self.fuzz*self.fuzz*(1-cosine*cosine)) 73 | outward_normal = -rec.normal 74 | ni = self.fuzz 75 | else 76 | cosine = -cosine 77 | outward_normal = rec.normal 78 | ni = 1 / self.fuzz 79 | end 80 | local forward, refracted = refract(r.direction, outward_normal, ni) 81 | local scattered = Ray{rec.p} 82 | ## if not CARTOONIZED then 83 | if forward then 84 | reflectprob = schlick(cosine, self.fuzz) 85 | else 86 | reflectprob = 1 87 | end 88 | if math.fastrand() < reflectprob then 89 | scattered.direction = reflect(r.direction, rec.normal) 90 | else 91 | scattered.direction = refracted 92 | end 93 | ## else 94 | scattered.direction = refracted 95 | ## end 96 | return true, vec3{1,1,1}, scattered 97 | end 98 | 99 | function Material:scatter(r: Ray, rec: Hit): (boolean, vec3, Ray) 100 | switch self.kind 101 | case MATERIALS.LAMBERTIAN then 102 | return self:lambertian_scatter(r, rec) 103 | case MATERIALS.METAL then 104 | return self:metal_scatter(r, rec) 105 | case MATERIALS.DIELECTRIC then 106 | return self:dielectric_scatter(r, rec) 107 | end 108 | return false, vec3{}, Ray{} 109 | end 110 | 111 | function Material:emitted(uv: vec2, p: vec3): vec3 112 | return Texture.color(self.emitting, uv, p) 113 | end 114 | -------------------------------------------------------------------------------- /mathx.nelua: -------------------------------------------------------------------------------- 1 | require 'math' 2 | 3 | global math.tau: number = 6.2831853071795865 4 | 5 | function math.smoothstep(edge0: number, edge1: number, x: number): number 6 | local t = math.clamp((x - edge0) / (edge1 - edge0), 0, 1) 7 | return t * t * (3 - 2 * t) 8 | end 9 | 10 | function math.step(edge: number, x: number): number 11 | if x < edge then 12 | return 0 13 | else 14 | return 1 15 | end 16 | end 17 | 18 | function math.fastexp4(x: number): number 19 | x = 1.0 + x * (1.0 / 17.0) 20 | local t = x 21 | ## for i=1,4 do 22 | t=t*t 23 | ## end 24 | return math.max(t*x,0.0) 25 | end 26 | 27 | function math.fastexp8(x: number): number 28 | x = 1.0 + x * (1.0 / 257.0) 29 | local t = x 30 | ## for i=1,8 do 31 | t=t*t 32 | ## end 33 | return math.max(t*x,0.0) 34 | end 35 | 36 | function math.fastsin(x: number): number 37 | x = x*(1.0/math.pi) 38 | local xi = math.floor(x) 39 | local xs = xi - 2*math.floor(0.5*x) 40 | local xf = x - xi 41 | return (1-2*xs)*(4*xf)*(1-xf) 42 | end 43 | 44 | function math.fastcos(x: number): number 45 | return math.fastsin(x + math.pi/2) 46 | end 47 | 48 | local seed: uint32 = 1337 49 | function math.fastrand(): number 50 | seed = 214013 * seed + 2531011 51 | return (seed >> 8) / (1 << 24) 52 | end 53 | -------------------------------------------------------------------------------- /perlin.nelua: -------------------------------------------------------------------------------- 1 | global PERLIN_SIZE = 256 2 | 3 | global Perlin = @record{} 4 | 5 | local perlin: record{ 6 | rands: [PERLIN_SIZE]vec3, 7 | permx: [PERLIN_SIZE]integer, 8 | permy: [PERLIN_SIZE]integer, 9 | permz: [PERLIN_SIZE]integer 10 | } 11 | 12 | function Perlin.generate() 13 | for i=0,PERLIN_SIZE-1 do 14 | perlin.rands[i] = vec3.unit(vec3{2*math.random()-1, 2*math.random()-1, 2*math.random()-1}) 15 | end 16 | end 17 | 18 | function Perlin.permute(perm: *[PERLIN_SIZE]integer) 19 | for i=0,PERLIN_SIZE-1 do 20 | perm[i] = i 21 | end 22 | for i=PERLIN_SIZE-1,1,-1 do 23 | local target = math.random(0, i) 24 | perm[i], perm[target] = perm[target], perm[i] 25 | end 26 | end 27 | 28 | local function trilinear_interp(c: [2][2][2]vec3, u: number, v: number, w: number) 29 | u, v, w = u*u*(3-2*u), v*v*(3-2*v), w*w*(3-2*w) 30 | local accum: number = 0 31 | for i=0,1 do 32 | for j=0,1 do 33 | for k=0,1 do 34 | local weight = vec3{u-i, v-j, w-k} 35 | accum = accum + 36 | ((i*u) + (1-i)*(1-u))* 37 | ((j*v) + (1-j)*(1-v))* 38 | ((k*w) + (1-k)*(1-w))* 39 | vec3.dot(c[i][j][k], weight) 40 | end 41 | end 42 | end 43 | return accum 44 | end 45 | 46 | function Perlin.noise(p: vec3): number 47 | local u, v, w = p.x - math.floor(p.x), p.y - math.floor(p.y), p.z - math.floor(p.z) 48 | local pi, pj, pk = (@integer)(math.floor(p.x)), (@integer)(math.floor(p.y)), (@integer)(math.floor(p.z)) 49 | local c: [2][2][2]vec3 50 | for i=0,1 do 51 | for j=0,1 do 52 | for k=0,1 do 53 | c[i][j][k] = perlin.rands[ 54 | perlin.permx[(pi+i) & 255] ~ 55 | perlin.permy[(pj+j) & 255] ~ 56 | perlin.permx[(pk+k) & 255]] 57 | end 58 | end 59 | end 60 | return trilinear_interp(c, u, v, w) 61 | end 62 | 63 | function Perlin.turb(p: vec3, depth: integer) 64 | local accum: number = 0 65 | local weight: number = 1 66 | for i=0,depth-1 do 67 | accum = accum + weight * Perlin.noise(p) 68 | weight = weight * 0.5 69 | p = p + p 70 | end 71 | return math.abs(accum) 72 | end 73 | 74 | Perlin.generate() 75 | Perlin.permute(perlin.permx) 76 | Perlin.permute(perlin.permy) 77 | Perlin.permute(perlin.permz) 78 | -------------------------------------------------------------------------------- /ray.nelua: -------------------------------------------------------------------------------- 1 | global Ray = @record{ 2 | origin: vec3, 3 | direction: vec3 4 | } 5 | 6 | function Ray:pointat(t: number): vec3 7 | return self.origin + self.direction * t 8 | end 9 | -------------------------------------------------------------------------------- /sdf2d.nelua: -------------------------------------------------------------------------------- 1 | ##[[ 2 | --OPENMP = true 3 | --EMSCRIPTEN = true 4 | ]] 5 | 6 | require 'config' 7 | require 'mathx' 8 | require 'vec2' 9 | require 'vec3' 10 | require 'argbcolor' 11 | require 'sdl2' 12 | require 'window' 13 | 14 | local SCREEN_WIDTH = 512 15 | local SCREEN_HEIGHT = 512 16 | local SCREEN_SCALE = 1 17 | local pixels: [SCREEN_HEIGHT][SCREEN_WIDTH]argbcolor 18 | local iResolution = vec2{SCREEN_WIDTH, SCREEN_HEIGHT} 19 | local iInvResolution = vec2{1/SCREEN_WIDTH, 1/SCREEN_HEIGHT} 20 | local iTime: number 21 | local window: Window 22 | local lastticks = SDL_GetTicks() 23 | local fps = 0 24 | local quit = false 25 | 26 | local function sd_circle(p: vec2, r: number): number 27 | return #p - r 28 | end 29 | 30 | local function sd_vline(p: vec2, r: number): number 31 | return math.abs(p.x) - r 32 | end 33 | 34 | local function sd_box(p: vec2, r: vec2): number 35 | local d = vec2.abs(p) - r 36 | return # (vec2.max(d, vec2{0,0})) + math.min(math.max(d.x,d.y), 0.0) 37 | end 38 | 39 | local function map(p: vec2): number 40 | local d: number 41 | p = p * 10.0 42 | d = sd_circle(p - vec2{-10.0*math.fastsin(iTime),5.0}, 1.0) 43 | d = math.min(d, sd_vline(p - vec2{-5.0,5.0}, 1.0)) 44 | d = math.min(d, sd_box(p - vec2{0.0,5.0}, vec2{2.0,2.0})) 45 | d = d * (1.0/10.0) 46 | return d 47 | end 48 | 49 | local function get_distance_field_color(d: number) 50 | local outcolor = vec3{0.9,0.6,0.3} 51 | local incolor = vec3{0.3,0.6,0.9} 52 | local boundcolor = vec3{1.0,1.0,1.0} 53 | local linechange = 0.2 54 | local numlines = 120.0 55 | local linesmooth = 0.5 56 | local linestep = math.smoothstep(-linesmooth, linesmooth, math.fastsin(d*numlines))*2.0 - 1.0 57 | local brightness = (1.0-linechange) + linechange*linestep 58 | local decay = math.fastexp4(-math.abs(d)) 59 | local colfield = vec3.lerp(incolor, outcolor, math.step(0.0, d)) * (brightness * decay) 60 | local colbound = boundcolor * math.fastexp4(-(numlines*0.5)*math.abs(d)) 61 | return colfield + colbound 62 | end 63 | 64 | local function main_image(fragcoord: vec2) 65 | local uv = (2.0 * fragcoord - iResolution) *iInvResolution.y 66 | local d = map(uv) * 1.0 67 | local col = get_distance_field_color(d) 68 | return col 69 | --[[ 70 | local uv = vec2.div(fragcoord, iResolution) 71 | local col = vec3{uv.x,uv.y,uv.x} + vec3{0,2,4} 72 | col = col + iTime 73 | col = vec3{math.fastcos(col.x),math.fastcos(col.y),math.fastcos(col.z)} 74 | col = col * 0.5 75 | col = col + 0.5 76 | return col 77 | ]] 78 | end 79 | 80 | local function draw() 81 | ##[[if OPENMP then 82 | cemit '#pragma omp parallel for schedule(dynamic)' 83 | end]] 84 | for y=0,= 1000 then 107 | print('FPS', fps) 108 | lastticks = ticks 109 | fps = 0 110 | end 111 | 112 | poll_events() 113 | draw() 114 | fps = fps + 1 115 | end 116 | 117 | local function emframe(context: pointer) 118 | frame() 119 | end 120 | 121 | local function go() 122 | window = Window.create(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_SCALE) 123 | 124 | ## if not EMSCRIPTEN then 125 | repeat 126 | frame() 127 | until quit 128 | 129 | window:destroy() 130 | ## else 131 | ## cemit 'emscripten_set_main_loop_arg(emframe, 0, -1, 1);' 132 | ## end 133 | end 134 | 135 | go() 136 | -------------------------------------------------------------------------------- /sphere.nelua: -------------------------------------------------------------------------------- 1 | global Sphere = @record{ 2 | center: vec3, 3 | _radius2: number, 4 | _invradius: number, 5 | radius: number, 6 | mat: Material 7 | } 8 | 9 | function Sphere.get_uv(p: vec3): vec2 10 | local phi = math.atan(p.z, p.x) 11 | local theta = math.asin(p.y) 12 | return vec2{1 - ((phi + math.pi) / math.tau), (theta + math.pi/2) / math.pi } 13 | end 14 | 15 | function Sphere:update() 16 | self._invradius = 1 / self.radius 17 | self._radius2 = self.radius*self.radius 18 | end 19 | 20 | function Sphere:hit(r: Ray, tmin: number, tmax: number): (boolean, Hit) 21 | local oc = r.origin - self.center 22 | local b = vec3.dot(oc, r.direction) 23 | local c = vec3.squaredlength(oc) - self._radius2 24 | local discriminant = b*b - c 25 | local rec: Hit 26 | local t: number 27 | if discriminant > 0 then 28 | local droot = math.sqrt(discriminant) 29 | local t1 = -b 30 | local t2 = droot 31 | t = t1 - t2 32 | if t < tmax and t > tmin then 33 | goto hitted 34 | end 35 | t = t1 + t2 36 | if t < tmax and t > tmin then 37 | goto hitted 38 | end 39 | end 40 | do return false, rec end 41 | ::hitted:: 42 | rec.p = r:pointat(t) 43 | rec.normal = (rec.p - self.center) * self._invradius 44 | rec.uv = Sphere.get_uv(rec.normal) 45 | rec.mat = &self.mat 46 | rec.t = t 47 | return true, rec 48 | end 49 | -------------------------------------------------------------------------------- /texture.nelua: -------------------------------------------------------------------------------- 1 | global TEXTURES = @enum { 2 | COLOR = 0, 3 | CHECKER = 1, 4 | NOISE = 2, 5 | } 6 | 7 | global Texture = @record { 8 | kind: TEXTURES, 9 | diffuse_color: vec3, 10 | scale: number 11 | } 12 | 13 | function Texture:color_color(uv: vec2, p: vec3): vec3 14 | return self.diffuse_color 15 | end 16 | 17 | function Texture:checker_color(uv: vec2, p: vec3): vec3 18 | local sines = math.fastsin(10*p.x)*math.fastsin(10*p.y)*math.fastsin(10*p.z) 19 | if sines < 0 then 20 | return vec3{1,1,1} 21 | else 22 | return self.diffuse_color 23 | end 24 | end 25 | 26 | function Texture:noise_color(uv: vec2, p: vec3): vec3 27 | local noise = Perlin.turb(p * self.scale, 2)*0.5+0.5 28 | return self.diffuse_color * noise 29 | end 30 | 31 | function Texture:color(uv: vec2, p: vec3): vec3 32 | switch self.kind 33 | case TEXTURES.COLOR then 34 | return Texture.color_color(self, uv, p) 35 | case TEXTURES.CHECKER then 36 | return Texture.checker_color(self, uv, p) 37 | case TEXTURES.NOISE then 38 | return Texture.noise_color(self, uv, p) 39 | end 40 | return vec3{} 41 | end 42 | -------------------------------------------------------------------------------- /vec2.nelua: -------------------------------------------------------------------------------- 1 | global vec2 = @record { 2 | x: number, 3 | y: number 4 | } 5 | ## vec2.value.is_vec2 = true 6 | 7 | local is_vec2_or_scalar = #[concept(function(b) 8 | return b.type.is_vec2 or b.type.is_scalar 9 | end)]# 10 | 11 | function vec2.__add(a: is_vec2_or_scalar, b: is_vec2_or_scalar): vec2 12 | ## if b.type.is_scalar then 13 | return vec2{a.x + b, a.y + b} 14 | ## elseif a.type.is_scalar then 15 | return vec2{a + b.x, a + b.y} 16 | ## else 17 | return vec2{a.x + b.x, a.y + b.y} 18 | ## end 19 | end 20 | 21 | function vec2.__sub(a: is_vec2_or_scalar, b: is_vec2_or_scalar): vec2 22 | ## if b.type.is_scalar then 23 | return vec2{a.x - b, a.y - b} 24 | ## elseif a.type.is_scalar then 25 | return vec2{a - b.x, a - b.y} 26 | ## else 27 | return vec2{a.x - b.x, a.y - b.y} 28 | ## end 29 | end 30 | 31 | function vec2.__mul(a: is_vec2_or_scalar, b: is_vec2_or_scalar): vec2 32 | ## if b.type.is_scalar then 33 | return vec2{a.x * b, a.y * b} 34 | ## elseif a.type.is_scalar then 35 | return vec2{a * b.x, a * b.y} 36 | ## else 37 | return vec2{a.x * b.x, a.y * b.y} 38 | ## end 39 | end 40 | 41 | function vec2.__div(a: is_vec2_or_scalar, b: is_vec2_or_scalar): vec2 42 | ## if b.type.is_scalar then 43 | local k: number = 1 / b 44 | return vec2{a.x * k, a.y * k} 45 | ## elseif a.type.is_scalar then 46 | local k: number = 1 / a 47 | return vec2{k * b.x, k * b.y} 48 | ## else 49 | return vec2{a.x / b.x, a.y / b.y} 50 | ## end 51 | end 52 | 53 | function vec2.__len(a: vec2): number 54 | return math.sqrt(a.x * a.x + a.y * a.y) 55 | end 56 | 57 | function vec2.abs(a: vec2): vec2 58 | return vec2{math.abs(a.x), math.abs(a.y)} 59 | end 60 | 61 | function vec2.max(a: vec2, b: vec2): vec2 62 | return vec2{math.max(a.x, b.x), math.max(a.y, b.y)} 63 | end 64 | -------------------------------------------------------------------------------- /vec3.nelua: -------------------------------------------------------------------------------- 1 | global vec3 = @record{ 2 | x: number, 3 | y: number, 4 | z: number 5 | } 6 | ## vec3.value.is_vec3 = true 7 | 8 | local is_vec3_or_scalar = #[concept(function(b) 9 | return b.type.is_vec3 or b.type.is_scalar 10 | end)]# 11 | 12 | function vec3.__add(a: is_vec3_or_scalar, b: is_vec3_or_scalar): vec3 13 | ## if b.type.is_scalar then 14 | return vec3{a.x+b, a.y+b, a.z+b} 15 | ## elseif a.type.is_scalar then 16 | return vec3{a+b.x, a+b.y, a+b.z} 17 | ## else 18 | return vec3{a.x+b.x, a.y+b.y, a.z+b.z} 19 | ## end 20 | end 21 | 22 | function vec3.__sub(a: is_vec3_or_scalar, b: is_vec3_or_scalar): vec3 23 | ## if b.type.is_scalar then 24 | return vec3{a.x-b, a.y-b, a.z-b} 25 | ## elseif a.type.is_scalar then 26 | return vec3{a-b.x, a-b.y, a-b.z} 27 | ## else 28 | return vec3{a.x-b.x, a.y-b.y, a.z-b.z} 29 | ## end 30 | end 31 | 32 | function vec3.__mul(a: is_vec3_or_scalar, b: is_vec3_or_scalar): vec3 33 | ## if b.type.is_scalar then 34 | return vec3{a.x*b, a.y*b, a.z*b} 35 | ## elseif a.type.is_scalar then 36 | return vec3{a*b.x, a*b.y, a*b.z} 37 | ## else 38 | return vec3{a.x*b.x, a.y*b.y, a.z*b.z} 39 | ## end 40 | end 41 | 42 | function vec3.__div(a: is_vec3_or_scalar, b: is_vec3_or_scalar): vec3 43 | ## if b.type.is_scalar then 44 | local k: number = 1 / b 45 | return vec3{a.x*k, a.y*k, a.z*k} 46 | ## elseif a.type.is_scalar then 47 | local k: number = 1 / a 48 | return vec3{k*b.x, k*b.y, k*b.z} 49 | ## else 50 | return vec3{a.x/b.x, a.y/b.y, a.z/b.z} 51 | ## end 52 | end 53 | 54 | function vec3.__unm(a: vec3): vec3 55 | return vec3{-a.x, -a.y, -a.z} 56 | end 57 | 58 | function vec3.min(a: vec3, b: vec3): vec3 59 | return vec3{math.min(a.x, b.x), math.min(a.y, b.y), math.min(a.z, b.z)} 60 | end 61 | 62 | function vec3.dot(a: vec3, b: vec3): number 63 | return a.x*b.x + a.y*b.y + a.z*b.z 64 | end 65 | 66 | function vec3.cross(a: vec3, b: vec3): vec3 67 | return vec3{a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x} 68 | end 69 | 70 | function vec3.sqrt(a: vec3): vec3 71 | return vec3{math.sqrt(a.x), math.sqrt(a.y), math.sqrt(a.z)} 72 | end 73 | 74 | function vec3.squaredlength(v: vec3): number 75 | return v.x*v.x + v.y*v.y + v.z*v.z 76 | end 77 | 78 | function vec3.length(v: vec3): number 79 | return math.sqrt(vec3.squaredlength(v)) 80 | end 81 | 82 | function vec3.__len(v: vec3): number 83 | return math.sqrt(vec3.squaredlength(v)) 84 | end 85 | 86 | function vec3.unit(v: vec3): vec3 87 | return v / #v 88 | end 89 | 90 | function vec3.lerp(a: vec3, b: vec3, t: number): vec3 91 | return a * (1-t) + b * t 92 | end 93 | 94 | function vec3.print(v: vec3) 95 | print(v.x, v.y, v.z) 96 | end 97 | 98 | function vec3.rotvec(v: vec3, axis: vec3, theta: number): vec3 99 | local n = vec3.unit(axis) 100 | local w = vec3.cross(n, v) 101 | local sint, cost = math.sin(theta), math.cos(theta) 102 | local ns = vec3.dot(v, n) * (1 - cost) 103 | return n * ns + w * sint + v * cost 104 | end 105 | 106 | function vec3.random_in_disk(length: number): vec3 107 | local theta = math.fastrand()*math.tau 108 | local sint, cost = math.sin(theta), math.cos(theta) 109 | local rlen = math.sqrt(math.fastrand())*length 110 | return vec3{rlen*cost, rlen*sint, 0} 111 | end 112 | 113 | function vec3.random_in_hemisphere(n: vec3) 114 | local theta, len = math.fastrand()*math.tau, math.fastrand() 115 | local w 116 | if math.abs(n.y) > 0.5 then 117 | w = vec3{1,0,0} 118 | else 119 | w = vec3{0,1,0} 120 | end 121 | local u = vec3.unit(vec3.cross(n, w)) 122 | local v = vec3.cross(u, n) 123 | local sint, cost = math.sin(theta), math.cos(theta) 124 | local r = math.sqrt(len) 125 | local rx, ry, rz = r*cost, r*sint, math.sqrt(1 - len) 126 | local rr = u * rx + v * ry + n * rz 127 | return vec3.unit(rr) 128 | end 129 | -------------------------------------------------------------------------------- /window.nelua: -------------------------------------------------------------------------------- 1 | global Window = @record { 2 | window: *SDL_Window, 3 | renderer: *SDL_Renderer, 4 | texture: *SDL_Texture, 5 | width: integer, 6 | height: integer, 7 | pitch: cint 8 | } 9 | 10 | function Window.create(width: integer, height: integer, scale: integer): Window 11 | -- SDL_SetHint('SDL_FRAMEBUFFER_ACCELERATION', '0') 12 | -- SDL_SetHint('SDL_HINT_RENDER_SCALE_QUALITY', 'linear') 13 | -- SDL_SetHint('SDL_HINT_RENDER_DRIVER', "opengl"); 14 | SDL_Init(SDL_INIT_VIDEO) 15 | 16 | local window = SDL_CreateWindow("An SDL2 Window", 17 | SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 18 | width * scale, height * scale, SDL_WINDOW_OPENGL) 19 | assert(window, "Could not create window") 20 | 21 | local renderer = SDL_CreateRenderer(window, -1, 0) 22 | assert(renderer, "Could not create renderer") 23 | -- SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE) 24 | 25 | local texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_XBGR8888, SDL_TEXTUREACCESS_STREAMING, width, height) 26 | -- SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_NONE) 27 | 28 | return Window{window, renderer, texture, width, height, width*4} 29 | end 30 | 31 | function Window.destroy(self: *Window) 32 | SDL_DestroyTexture(self.texture) 33 | SDL_DestroyRenderer(self.renderer) 34 | SDL_DestroyWindow(self.window) 35 | SDL_Quit() 36 | end 37 | 38 | function Window.uploadpixels(self: *Window, pixels: *argbcolor) 39 | SDL_UpdateTexture(self.texture, nilptr, pixels, self.pitch) 40 | SDL_RenderCopy(self.renderer, self.texture, nilptr, nilptr) 41 | SDL_RenderPresent(self.renderer) 42 | end 43 | --------------------------------------------------------------------------------