@rosalana/sandbox 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -15
- package/dist/index.cjs.js +8 -8
- package/dist/index.d.ts +13 -3
- package/dist/index.es.js +189 -149
- package/dist/tools/clock.d.ts +7 -0
- package/dist/tools/web_gl.d.ts +5 -0
- package/dist/types.d.ts +6 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -146,7 +146,7 @@ Sandbox figures out which WebGL version you're using by looking at your shader c
|
|
|
146
146
|
If WebGL2 isn't available, Sandbox falls back to WebGL1 automatically. You can always check what you're running:
|
|
147
147
|
|
|
148
148
|
```ts
|
|
149
|
-
sandbox.
|
|
149
|
+
sandbox.version; // 1 or 2
|
|
150
150
|
```
|
|
151
151
|
|
|
152
152
|
## Uniforms
|
|
@@ -195,6 +195,8 @@ These uniforms are filled automatically every frame — no setup needed. Just de
|
|
|
195
195
|
|
|
196
196
|
Hooks are one of the most powerful features in Sandbox. They let you run logic every frame — before or after render — which opens up a world of possibilities.
|
|
197
197
|
|
|
198
|
+
The callback receives a `ClockState` object with `time`, `delta`, `frame`, `running`, and `fps` (smoothed).
|
|
199
|
+
|
|
198
200
|
**Pre-compute values on the CPU** before they hit the shader:
|
|
199
201
|
|
|
200
202
|
```ts
|
|
@@ -327,6 +329,7 @@ interface SandboxOptions {
|
|
|
327
329
|
autoplay?: boolean;
|
|
328
330
|
pauseWhenHidden?: boolean;
|
|
329
331
|
dpr?: number | "auto";
|
|
332
|
+
fps?: number;
|
|
330
333
|
preserveDrawingBuffer?: boolean;
|
|
331
334
|
antialias?: boolean;
|
|
332
335
|
onError?: (error: SandboxError) => void;
|
|
@@ -337,20 +340,21 @@ interface SandboxOptions {
|
|
|
337
340
|
}
|
|
338
341
|
```
|
|
339
342
|
|
|
340
|
-
| Option | Default | Description
|
|
341
|
-
| ----------------------- | --------------- |
|
|
342
|
-
| `vertex` | built-in | Custom vertex shader
|
|
343
|
-
| `fragment` | built-in | Fragment shader
|
|
344
|
-
| `autoplay` | `true` | Start rendering immediately
|
|
345
|
-
| `pauseWhenHidden` | `true` | Pause when scrolled out of view
|
|
346
|
-
| `dpr` | `"auto"` | Device pixel ratio
|
|
347
|
-
| `
|
|
348
|
-
| `
|
|
349
|
-
| `
|
|
350
|
-
| `
|
|
351
|
-
| `
|
|
352
|
-
| `
|
|
353
|
-
| `
|
|
343
|
+
| Option | Default | Description |
|
|
344
|
+
| ----------------------- | --------------- | ---------------------------------------------- |
|
|
345
|
+
| `vertex` | built-in | Custom vertex shader |
|
|
346
|
+
| `fragment` | built-in | Fragment shader |
|
|
347
|
+
| `autoplay` | `true` | Start rendering immediately |
|
|
348
|
+
| `pauseWhenHidden` | `true` | Pause when scrolled out of view |
|
|
349
|
+
| `dpr` | `"auto"` | Device pixel ratio |
|
|
350
|
+
| `fps` | `0` (unlimited) | Max frame rate (approximate due to rAF timing) |
|
|
351
|
+
| `preserveDrawingBuffer` | `false` | Keep buffer for screenshots |
|
|
352
|
+
| `antialias` | `true` | Enable antialiasing |
|
|
353
|
+
| `onError` | `console.error` | Error callback |
|
|
354
|
+
| `onLoad` | — | Called on each shader compilation |
|
|
355
|
+
| `onBeforeRender` | — | Hook before each frame |
|
|
356
|
+
| `onAfterRender` | — | Hook after each frame |
|
|
357
|
+
| `uniforms` | — | Initial uniform values |
|
|
354
358
|
|
|
355
359
|
## Limitations (by design)
|
|
356
360
|
|
package/dist/index.cjs.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
"use strict";var w=Object.defineProperty;var k=(o,t,e)=>t in o?w(o,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):o[t]=e;var
|
|
1
|
+
"use strict";var w=Object.defineProperty;var k=(o,t,e)=>t in o?w(o,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):o[t]=e;var r=(o,t,e)=>k(o,typeof t!="symbol"?t+"":t,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class u{constructor(t,e,i,s){this.target=t,this.type=e,this.listener=i,this.options=s,this.target.addEventListener(this.type,this.listener,this.options)}remove(){this.target.removeEventListener(this.type,this.listener,this.options)}static on(t,e,i,s){return t.addEventListener(e,i,s),()=>t.removeEventListener(e,i,s)}}class l extends Error{constructor(t,e){super(t),this.code=e,this.name="SandboxError"}}class S extends l{constructor(t){const e=t==="not_supported"?"WebGL is not supported in this browser.":"Failed to create WebGL context. The GPU may be unavailable.";super(e,t==="not_supported"?"WEBGL_NOT_SUPPORTED":"CONTEXT_CREATION_FAILED"),this.name="SandboxContextError"}}class L extends l{constructor(t,e){super(`Vertex and fragment shader WebGL versions do not match (${t} vs ${e})`,"SHADER_VERSION_MISMATCH"),this.vertexVersion=t,this.fragmentVersion=e,this.name="SandboxShaderVersionMismatchError"}}class f extends l{constructor(e,i,s){const n=f.parseErrorLines(s),h=n.length>0?` at line(s): ${n.join(", ")}`:"";super(`${e} shader compilation failed${h}
|
|
2
2
|
|
|
3
|
-
${s}`,"SHADER_COMPILATION_FAILED");
|
|
3
|
+
${s}`,"SHADER_COMPILATION_FAILED");r(this,"lines");this.shaderType=e,this.source=i,this.infoLog=s,this.name="SandboxShaderCompilationError",this.lines=n}static parseErrorLines(e){const i=[/ERROR:\s*\d*:(\d+)/g,/(\d+):(\d+)\(\d+\):/g,/^(\d+):/gm],s=new Set;for(const n of i){let h;for(;(h=n.exec(e))!==null;){const c=parseInt(h[1],10);c>0&&s.add(c)}}return[...s].sort((n,h)=>n-h)}}class d extends l{constructor(t){super(`Shader program linking failed
|
|
4
4
|
|
|
5
|
-
${t}`,"PROGRAM_LINK_FAILED"),this.infoLog=t,this.name="SandboxProgramError"}}class R{constructor(){i(this,"time",0);i(this,"delta",0);i(this,"frame",0);i(this,"running",!1);i(this,"startTime",0);i(this,"lastTime",0);i(this,"rafId",null);i(this,"callback",null);this.loop=this.loop.bind(this)}start(t){if(this.running)return this;this.callback=t,this.running=!0;const e=performance.now();return this.frame===0?this.startTime=e:this.startTime=e-this.time*1e3,this.lastTime=e,this.rafId=requestAnimationFrame(this.loop),this}stop(){return this.running?(this.running=!1,this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this):this}reset(){return this.stop(),this.time=0,this.delta=0,this.frame=0,this}getState(){return{time:this.time,delta:this.delta,frame:this.frame}}tick(t=0){return this.delta=t,this.time+=t,this.frame++,this.callback&&this.callback(this.getState()),this}setTime(t){return this.time=t,this}destroy(){this.reset(),this.callback=null}loop(t){this.running&&(this.delta=(t-this.lastTime)/1e3,this.lastTime=t,this.time=(t-this.startTime)/1e3,this.frame++,this.callback&&this.callback(this.getState()),this.rafId=requestAnimationFrame(this.loop))}}class p{constructor(t){i(this,"gl");i(this,"vao",null);i(this,"vbo",null);i(this,"ibo",null);i(this,"vertexCount",0);i(this,"indexCount",0);i(this,"useIndices",!1);i(this,"vaoExt",null);i(this,"isWebGL2");this.gl=t,this.isWebGL2=t instanceof WebGL2RenderingContext,this.isWebGL2||(this.vaoExt=t.getExtension("OES_vertex_array_object"))}static fullscreenQuad(t){const e=new p(t),r=new Float32Array([-1,-1,0,0,1,-1,1,0,-1,1,0,1,1,1,1,1]),s=new Uint16Array([0,1,2,2,1,3]);return e.setup(r,s),e}setup(t,e){const r=this.gl;return this.createVAO(),this.bindVAO(),this.vbo=r.createBuffer(),r.bindBuffer(r.ARRAY_BUFFER,this.vbo),r.bufferData(r.ARRAY_BUFFER,t,r.STATIC_DRAW),this.vertexCount=t.length/4,e&&(this.ibo=r.createBuffer(),r.bindBuffer(r.ELEMENT_ARRAY_BUFFER,this.ibo),r.bufferData(r.ELEMENT_ARRAY_BUFFER,e,r.STATIC_DRAW),this.indexCount=e.length,this.useIndices=!0),this.unbindVAO(),this}linkAttributes(t){const e=this.gl;this.bindVAO(),e.bindBuffer(e.ARRAY_BUFFER,this.vbo);const r=4*Float32Array.BYTES_PER_ELEMENT,s=this.getPositionLocation(t);s>=0&&(e.enableVertexAttribArray(s),e.vertexAttribPointer(s,2,e.FLOAT,!1,r,0));const n=this.getTexcoordLocation(t);return n>=0&&(e.enableVertexAttribArray(n),e.vertexAttribPointer(n,2,e.FLOAT,!1,r,2*Float32Array.BYTES_PER_ELEMENT)),this.useIndices&&e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,this.ibo),this.unbindVAO(),this}bind(){return this.bindVAO(),this}unbind(){return this.unbindVAO(),this}draw(){const t=this.gl;return this.bindVAO(),this.useIndices?t.drawElements(t.TRIANGLES,this.indexCount,t.UNSIGNED_SHORT,0):t.drawArrays(t.TRIANGLE_STRIP,0,this.vertexCount),this}destroy(){const t=this.gl;this.deleteVAO(),this.vbo&&(t.deleteBuffer(this.vbo),this.vbo=null),this.ibo&&(t.deleteBuffer(this.ibo),this.ibo=null)}getPositionLocation(t){let e=t.getAttribLocation("a_position");return e>=0||(e=t.getAttribLocation("aPosition"),e>=0)||(e=t.getAttribLocation("position"),e>=0)?e:-1}getTexcoordLocation(t){let e=t.getAttribLocation("a_texcoord");return e>=0||(e=t.getAttribLocation("aTexCoord"),e>=0)||(e=t.getAttribLocation("texcoord"),e>=0)||(e=t.getAttribLocation("a_uv"),e>=0)?e:-1}createVAO(){this.isWebGL2?this.vao=this.gl.createVertexArray():this.vaoExt&&(this.vao=this.vaoExt.createVertexArrayOES())}bindVAO(){this.vao&&(this.isWebGL2?this.gl.bindVertexArray(this.vao):this.vaoExt&&this.vaoExt.bindVertexArrayOES(this.vao))}unbindVAO(){this.isWebGL2?this.gl.bindVertexArray(null):this.vaoExt&&this.vaoExt.bindVertexArrayOES(null)}deleteVAO(){this.vao&&(this.isWebGL2?this.gl.deleteVertexArray(this.vao):this.vaoExt&&this.vaoExt.deleteVertexArrayOES(this.vao),this.vao=null)}}class h{constructor(t){i(this,"gl");i(this,"program",null);i(this,"vertexShader",null);i(this,"fragmentShader",null);i(this,"version",1);this.gl=t}static detectVersion(t){return/^\s*#version\s+300\s+es/m.test(t)?2:1}compile(t,e){this.destroy();const r=h.detectVersion(t),s=h.detectVersion(e);if(r!=s)throw new L(r,s);return this.version=Math.max(r,s),this.vertexShader=this.compileShader("vertex",t),this.fragmentShader=this.compileShader("fragment",e),this.linkProgram(),this}use(){return this.program&&this.gl.useProgram(this.program),this}getProgram(){return this.program}getVersion(){return this.version}getAttribLocation(t){return this.program?this.gl.getAttribLocation(this.program,t):-1}getUniformLocation(t){return this.program?this.gl.getUniformLocation(this.program,t):null}destroy(){const t=this.gl;this.program&&(this.vertexShader&&t.detachShader(this.program,this.vertexShader),this.fragmentShader&&t.detachShader(this.program,this.fragmentShader),t.deleteProgram(this.program),this.program=null),this.vertexShader&&(t.deleteShader(this.vertexShader),this.vertexShader=null),this.fragmentShader&&(t.deleteShader(this.fragmentShader),this.fragmentShader=null)}compileShader(t,e){const r=this.gl,s=t==="vertex"?r.VERTEX_SHADER:r.FRAGMENT_SHADER,n=r.createShader(s);if(!n)throw new f(t,e,"Failed to create shader object");if(r.shaderSource(n,e),r.compileShader(n),!r.getShaderParameter(n,r.COMPILE_STATUS)){const c=r.getShaderInfoLog(n)||"Unknown error";throw r.deleteShader(n),new f(t,e,c)}return n}linkProgram(){const t=this.gl;if(!this.vertexShader||!this.fragmentShader)throw new d("Shaders not compiled");const e=t.createProgram();if(!e)throw new d("Failed to create program object");if(t.attachShader(e,this.vertexShader),t.attachShader(e,this.fragmentShader),t.linkProgram(e),!t.getProgramParameter(e,t.LINK_STATUS)){const s=t.getProgramInfoLog(e)||"Unknown error";throw t.deleteProgram(e),new d(s)}this.program=e}}class x{constructor(t,e){i(this,"name");i(this,"method");i(this,"isArray");i(this,"isMatrix");i(this,"location",null);i(this,"locationResolved",!1);i(this,"value");this.name=t,this.value=e;const r=x.inferMethodInfo(e);this.method=r.method,this.isArray=r.isArray,this.isMatrix=r.isMatrix}static inferMethodInfo(t){if(typeof t=="boolean")return{method:"uniform1i",isArray:!1,isMatrix:!1};if(typeof t=="number")return{method:"uniform1f",isArray:!1,isMatrix:!1};if(!Array.isArray(t))return{method:"uniform1f",isArray:!1,isMatrix:!1};const e=t.length,r=t[0];if(Array.isArray(r))switch(r.length){case 2:return{method:"uniform2fv",isArray:!0,isMatrix:!1};case 3:return{method:"uniform3fv",isArray:!0,isMatrix:!1};case 4:return{method:"uniform4fv",isArray:!0,isMatrix:!1};default:return{method:"uniform1fv",isArray:!0,isMatrix:!1}}switch(e){case 2:return{method:"uniform2fv",isArray:!1,isMatrix:!1};case 3:return{method:"uniform3fv",isArray:!1,isMatrix:!1};case 4:return{method:"uniform4fv",isArray:!1,isMatrix:!1};case 9:return{method:"uniformMatrix3fv",isArray:!1,isMatrix:!0};case 16:return{method:"uniformMatrix4fv",isArray:!1,isMatrix:!0};default:return{method:"uniform1fv",isArray:!0,isMatrix:!1}}}resolveLocation(t,e){return this.locationResolved||(this.location=t.getUniformLocation(e,this.name),this.locationResolved=!0),this.location}invalidateLocation(){this.location=null,this.locationResolved=!1}setValue(t){this.value=t}getValue(){return this.value}upload(t,e){const r=this.resolveLocation(t,e);if(r===null)return;const s=this.value;let n;switch(typeof s=="boolean"?n=s?1:0:typeof s=="number"?n=s:this.isArray&&Array.isArray(s[0])?n=new Float32Array(s.flat()):n=new Float32Array(s),this.method){case"uniform1f":t.uniform1f(r,n);break;case"uniform1i":t.uniform1i(r,n);break;case"uniform1fv":t.uniform1fv(r,n);break;case"uniform2fv":t.uniform2fv(r,n);break;case"uniform3fv":t.uniform3fv(r,n);break;case"uniform4fv":t.uniform4fv(r,n);break;case"uniformMatrix2fv":t.uniformMatrix2fv(r,!1,n);break;case"uniformMatrix3fv":t.uniformMatrix3fv(r,!1,n);break;case"uniformMatrix4fv":t.uniformMatrix4fv(r,!1,n);break}}}const m=class m{constructor(t){i(this,"gl");i(this,"program",null);i(this,"uniforms",new Map);this.gl=t}attachProgram(t){this.program=t;for(const e of this.uniforms.values())e.invalidateLocation();return this}set(t,e){const r=this.uniforms.get(t);return r?r.setValue(e):this.uniforms.set(t,new x(t,e)),this}setMany(t){for(const[e,r]of Object.entries(t))this.set(e,r);return this}get(t){var e;return(e=this.uniforms.get(t))==null?void 0:e.getValue()}has(t){return this.uniforms.has(t)}delete(t){return this.uniforms.delete(t)}uploadAll(){if(!this.program)return this;for(const t of this.uniforms.values())t.upload(this.gl,this.program);return this}uploadBuiltIns(t,e,r){if(this.set("u_resolution",e),this.set("u_time",t.time),this.set("u_delta",t.delta),this.set("u_mouse",r),this.set("u_frame",t.frame),!this.program)return this;for(const s of m.BUILT_INS){const n=this.uniforms.get(s);n&&n.upload(this.gl,this.program)}return this}clear(){this.uniforms.clear()}destroy(){this.uniforms.clear(),this.program=null}keys(){return this.uniforms.keys()}get size(){return this.uniforms.size}};i(m,"BUILT_INS",new Set(["u_resolution","u_time","u_delta","u_mouse","u_frame"]));let v=m;class A{constructor(){i(this,"hooks",new Map)}id(){return Math.random().toString(36).substring(2,10)}add(t){const e=this.id();return this.hooks.set(e,t),()=>this.remove(e)}remove(t){this.hooks.delete(t)}run(t){for(const[e,r]of this.hooks)r(t)===!1&&this.remove(e)}destroy(){this.hooks.clear()}}class b{constructor(t,e){i(this,"canvas");i(this,"gl");i(this,"options");i(this,"onBeforeHooks",new A);i(this,"onAfterHooks",new A);i(this,"_program");i(this,"_geometry");i(this,"_uniforms");i(this,"_clock");i(this,"_resolution",[1,1]);i(this,"_mouse",[0,0]);i(this,"_version",1);i(this,"playing",!1);this.canvas=t,this.options=e,this.gl=this.initContext(),this.enableExtensions(),this._program=new h(this.gl),this._geometry=p.fullscreenQuad(this.gl),this._uniforms=new v(this.gl),this._clock=new R,this.options.onBeforeRender&&this.onBeforeHooks.add(this.options.onBeforeRender),this.options.onAfterRender&&this.onAfterHooks.add(this.options.onAfterRender),this.onRender=this.onRender.bind(this)}static setup(t,e){const r=new b(t,e);return e.vertex&&e.fragment&&r.shader(e.vertex,e.fragment),e.uniforms&&r._uniforms.setMany(e.uniforms),r}initContext(){const t={antialias:this.options.antialias,preserveDrawingBuffer:this.options.preserveDrawingBuffer,alpha:!0,depth:!1,stencil:!1},e=this.canvas.getContext("webgl2",t);if(e)return this._version=2,e;const r=this.canvas.getContext("webgl",t);if(r)return this._version=1,r;const s=new S("not_supported");throw this.options.onError(s),s}enableExtensions(){this.gl.getExtension("OES_standard_derivatives"),this.gl.getExtension("OES_texture_float"),this.gl.getExtension("OES_texture_float_linear"),this._version===1&&this.gl.getExtension("OES_vertex_array_object")}viewport(t,e,r,s){return this.canvas.width=r,this.canvas.height=s,this.gl.viewport(t,e,r,s),this._resolution=[r,s],this}clock(t){return this._clock.setTime(t),this}mouse(t,e){return this._mouse=[t,e],this}uniform(t,e){return this._uniforms.set(t,e),this}uniforms(t){return this._uniforms.setMany(t),this}getUniform(t){return this._uniforms.get(t)}shader(t,e){try{this._program.compile(t,e),this._version=this._program.getVersion(),this._geometry.linkAttributes(this._program);const r=this._program.getProgram();r&&this._uniforms.attachProgram(r)}catch(r){r instanceof l&&this.options.onError(r)}return this}play(){return this.playing?this:(this.playing=!0,this._clock.start(this.onRender),this)}pause(){if(!this.playing)return this;const t=this._clock.getState();return this.onBeforeHooks.run(t),this.playing=!1,this._clock.stop(),this.onAfterHooks.run(t),this}render(){return this.onRender(this._clock.getState()),this}getContext(){return this.gl}getVersion(){return this._version}destroy(){this.pause(),this._clock.destroy(),this._geometry.destroy(),this._program.destroy(),this._uniforms.destroy(),this.onAfterHooks.destroy(),this.onBeforeHooks.destroy()}onRender(t){const e=this.gl;this.onBeforeHooks.run(t),e.clearColor(0,0,0,0),e.clear(e.COLOR_BUFFER_BIT),this._program.use(),this._uniforms.uploadBuiltIns(t,this._resolution,this._mouse),this._uniforms.uploadAll(),this._geometry.bind(),this._geometry.draw(),this.onAfterHooks.run(t)}}const g=`#ifdef GL_ES
|
|
5
|
+
${t}`,"PROGRAM_LINK_FAILED"),this.infoLog=t,this.name="SandboxProgramError"}}class R{constructor(){r(this,"time",0);r(this,"delta",0);r(this,"frame",0);r(this,"running",!1);r(this,"fps",0);r(this,"startTime",0);r(this,"lastTime",0);r(this,"rafId",null);r(this,"callback",null);r(this,"maxFps",0);this.loop=this.loop.bind(this)}start(t){if(this.running)return this;this.callback=t,this.running=!0;const e=performance.now();return this.frame===0?this.startTime=e:this.startTime=e-this.time*1e3,this.lastTime=e,this.rafId=requestAnimationFrame(this.loop),this}stop(){return this.running?(this.running=!1,this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this):this}reset(){return this.stop(),this.time=0,this.delta=0,this.frame=0,this.fps=0,this}getState(){return{time:this.time,delta:this.delta,frame:this.frame,running:this.running,fps:Math.round(this.fps)}}tick(t=0){return this.delta=t,this.time+=t,this.frame++,this.callback&&this.callback(this.getState()),this}setTime(t){return this.time=t,this}destroy(){this.reset(),this.callback=null}setMaxFps(t){return this.maxFps=t,this}loop(t){if(!this.running)return;if(this.maxFps>0){const i=1e3/this.maxFps;if(t-this.lastTime<i){this.rafId=requestAnimationFrame(this.loop);return}}this.delta=(t-this.lastTime)/1e3,this.lastTime=t;const e=this.delta>0?1/this.delta:0;this.fps=this.fps*.95+e*.05,this.time=(t-this.startTime)/1e3,this.frame++,this.callback&&this.callback(this.getState()),this.rafId=requestAnimationFrame(this.loop)}}class p{constructor(t){r(this,"gl");r(this,"vao",null);r(this,"vbo",null);r(this,"ibo",null);r(this,"vertexCount",0);r(this,"indexCount",0);r(this,"useIndices",!1);r(this,"vaoExt",null);r(this,"isWebGL2");this.gl=t,this.isWebGL2=t instanceof WebGL2RenderingContext,this.isWebGL2||(this.vaoExt=t.getExtension("OES_vertex_array_object"))}static fullscreenQuad(t){const e=new p(t),i=new Float32Array([-1,-1,0,0,1,-1,1,0,-1,1,0,1,1,1,1,1]),s=new Uint16Array([0,1,2,2,1,3]);return e.setup(i,s),e}setup(t,e){const i=this.gl;return this.createVAO(),this.bindVAO(),this.vbo=i.createBuffer(),i.bindBuffer(i.ARRAY_BUFFER,this.vbo),i.bufferData(i.ARRAY_BUFFER,t,i.STATIC_DRAW),this.vertexCount=t.length/4,e&&(this.ibo=i.createBuffer(),i.bindBuffer(i.ELEMENT_ARRAY_BUFFER,this.ibo),i.bufferData(i.ELEMENT_ARRAY_BUFFER,e,i.STATIC_DRAW),this.indexCount=e.length,this.useIndices=!0),this.unbindVAO(),this}linkAttributes(t){const e=this.gl;this.bindVAO(),e.bindBuffer(e.ARRAY_BUFFER,this.vbo);const i=4*Float32Array.BYTES_PER_ELEMENT,s=this.getPositionLocation(t);s>=0&&(e.enableVertexAttribArray(s),e.vertexAttribPointer(s,2,e.FLOAT,!1,i,0));const n=this.getTexcoordLocation(t);return n>=0&&(e.enableVertexAttribArray(n),e.vertexAttribPointer(n,2,e.FLOAT,!1,i,2*Float32Array.BYTES_PER_ELEMENT)),this.useIndices&&e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,this.ibo),this.unbindVAO(),this}bind(){return this.bindVAO(),this}unbind(){return this.unbindVAO(),this}draw(){const t=this.gl;return this.bindVAO(),this.useIndices?t.drawElements(t.TRIANGLES,this.indexCount,t.UNSIGNED_SHORT,0):t.drawArrays(t.TRIANGLE_STRIP,0,this.vertexCount),this}destroy(){const t=this.gl;this.deleteVAO(),this.vbo&&(t.deleteBuffer(this.vbo),this.vbo=null),this.ibo&&(t.deleteBuffer(this.ibo),this.ibo=null)}getPositionLocation(t){let e=t.getAttribLocation("a_position");return e>=0||(e=t.getAttribLocation("aPosition"),e>=0)||(e=t.getAttribLocation("position"),e>=0)?e:-1}getTexcoordLocation(t){let e=t.getAttribLocation("a_texcoord");return e>=0||(e=t.getAttribLocation("aTexCoord"),e>=0)||(e=t.getAttribLocation("texcoord"),e>=0)||(e=t.getAttribLocation("a_uv"),e>=0)?e:-1}createVAO(){this.isWebGL2?this.vao=this.gl.createVertexArray():this.vaoExt&&(this.vao=this.vaoExt.createVertexArrayOES())}bindVAO(){this.vao&&(this.isWebGL2?this.gl.bindVertexArray(this.vao):this.vaoExt&&this.vaoExt.bindVertexArrayOES(this.vao))}unbindVAO(){this.isWebGL2?this.gl.bindVertexArray(null):this.vaoExt&&this.vaoExt.bindVertexArrayOES(null)}deleteVAO(){this.vao&&(this.isWebGL2?this.gl.deleteVertexArray(this.vao):this.vaoExt&&this.vaoExt.deleteVertexArrayOES(this.vao),this.vao=null)}}class a{constructor(t){r(this,"gl");r(this,"program",null);r(this,"vertexShader",null);r(this,"fragmentShader",null);r(this,"version",1);this.gl=t}static detectVersion(t){return/^\s*#version\s+300\s+es/m.test(t)?2:1}compile(t,e){this.destroy();const i=a.detectVersion(t),s=a.detectVersion(e);if(i!=s)throw new L(i,s);return this.version=Math.max(i,s),this.vertexShader=this.compileShader("vertex",t),this.fragmentShader=this.compileShader("fragment",e),this.linkProgram(),this}use(){return this.program&&this.gl.useProgram(this.program),this}getProgram(){return this.program}getVersion(){return this.version}getAttribLocation(t){return this.program?this.gl.getAttribLocation(this.program,t):-1}getUniformLocation(t){return this.program?this.gl.getUniformLocation(this.program,t):null}destroy(){const t=this.gl;this.program&&(this.vertexShader&&t.detachShader(this.program,this.vertexShader),this.fragmentShader&&t.detachShader(this.program,this.fragmentShader),t.deleteProgram(this.program),this.program=null),this.vertexShader&&(t.deleteShader(this.vertexShader),this.vertexShader=null),this.fragmentShader&&(t.deleteShader(this.fragmentShader),this.fragmentShader=null)}compileShader(t,e){const i=this.gl,s=t==="vertex"?i.VERTEX_SHADER:i.FRAGMENT_SHADER,n=i.createShader(s);if(!n)throw new f(t,e,"Failed to create shader object");if(i.shaderSource(n,e),i.compileShader(n),!i.getShaderParameter(n,i.COMPILE_STATUS)){const c=i.getShaderInfoLog(n)||"Unknown error";throw i.deleteShader(n),new f(t,e,c)}return n}linkProgram(){const t=this.gl;if(!this.vertexShader||!this.fragmentShader)throw new d("Shaders not compiled");const e=t.createProgram();if(!e)throw new d("Failed to create program object");if(t.attachShader(e,this.vertexShader),t.attachShader(e,this.fragmentShader),t.linkProgram(e),!t.getProgramParameter(e,t.LINK_STATUS)){const s=t.getProgramInfoLog(e)||"Unknown error";throw t.deleteProgram(e),new d(s)}this.program=e}}class x{constructor(t,e){r(this,"name");r(this,"method");r(this,"isArray");r(this,"isMatrix");r(this,"location",null);r(this,"locationResolved",!1);r(this,"value");this.name=t,this.value=e;const i=x.inferMethodInfo(e);this.method=i.method,this.isArray=i.isArray,this.isMatrix=i.isMatrix}static inferMethodInfo(t){if(typeof t=="boolean")return{method:"uniform1i",isArray:!1,isMatrix:!1};if(typeof t=="number")return{method:"uniform1f",isArray:!1,isMatrix:!1};if(!Array.isArray(t))return{method:"uniform1f",isArray:!1,isMatrix:!1};const e=t.length,i=t[0];if(Array.isArray(i))switch(i.length){case 2:return{method:"uniform2fv",isArray:!0,isMatrix:!1};case 3:return{method:"uniform3fv",isArray:!0,isMatrix:!1};case 4:return{method:"uniform4fv",isArray:!0,isMatrix:!1};default:return{method:"uniform1fv",isArray:!0,isMatrix:!1}}switch(e){case 2:return{method:"uniform2fv",isArray:!1,isMatrix:!1};case 3:return{method:"uniform3fv",isArray:!1,isMatrix:!1};case 4:return{method:"uniform4fv",isArray:!1,isMatrix:!1};case 9:return{method:"uniformMatrix3fv",isArray:!1,isMatrix:!0};case 16:return{method:"uniformMatrix4fv",isArray:!1,isMatrix:!0};default:return{method:"uniform1fv",isArray:!0,isMatrix:!1}}}resolveLocation(t,e){return this.locationResolved||(this.location=t.getUniformLocation(e,this.name),this.locationResolved=!0),this.location}invalidateLocation(){this.location=null,this.locationResolved=!1}setValue(t){this.value=t}getValue(){return this.value}upload(t,e){const i=this.resolveLocation(t,e);if(i===null)return;const s=this.value;let n;switch(typeof s=="boolean"?n=s?1:0:typeof s=="number"?n=s:this.isArray&&Array.isArray(s[0])?n=new Float32Array(s.flat()):n=new Float32Array(s),this.method){case"uniform1f":t.uniform1f(i,n);break;case"uniform1i":t.uniform1i(i,n);break;case"uniform1fv":t.uniform1fv(i,n);break;case"uniform2fv":t.uniform2fv(i,n);break;case"uniform3fv":t.uniform3fv(i,n);break;case"uniform4fv":t.uniform4fv(i,n);break;case"uniformMatrix2fv":t.uniformMatrix2fv(i,!1,n);break;case"uniformMatrix3fv":t.uniformMatrix3fv(i,!1,n);break;case"uniformMatrix4fv":t.uniformMatrix4fv(i,!1,n);break}}}const m=class m{constructor(t){r(this,"gl");r(this,"program",null);r(this,"uniforms",new Map);this.gl=t}attachProgram(t){this.program=t;for(const e of this.uniforms.values())e.invalidateLocation();return this}set(t,e){const i=this.uniforms.get(t);return i?i.setValue(e):this.uniforms.set(t,new x(t,e)),this}setMany(t){for(const[e,i]of Object.entries(t))this.set(e,i);return this}get(t){var e;return(e=this.uniforms.get(t))==null?void 0:e.getValue()}has(t){return this.uniforms.has(t)}delete(t){return this.uniforms.delete(t)}uploadAll(){if(!this.program)return this;for(const t of this.uniforms.values())t.upload(this.gl,this.program);return this}uploadBuiltIns(t,e,i){if(this.set("u_resolution",e),this.set("u_time",t.time),this.set("u_delta",t.delta),this.set("u_mouse",i),this.set("u_frame",t.frame),!this.program)return this;for(const s of m.BUILT_INS){const n=this.uniforms.get(s);n&&n.upload(this.gl,this.program)}return this}clear(){this.uniforms.clear()}destroy(){this.uniforms.clear(),this.program=null}keys(){return this.uniforms.keys()}get size(){return this.uniforms.size}};r(m,"BUILT_INS",new Set(["u_resolution","u_time","u_delta","u_mouse","u_frame"]));let v=m;class A{constructor(){r(this,"hooks",new Map)}id(){return Math.random().toString(36).substring(2,10)}add(t){const e=this.id();return this.hooks.set(e,t),()=>this.remove(e)}remove(t){this.hooks.delete(t)}run(t){for(const[e,i]of this.hooks)i(t)===!1&&this.remove(e)}destroy(){this.hooks.clear()}}class _{constructor(t,e){r(this,"canvas");r(this,"gl");r(this,"options");r(this,"onBeforeHooks",new A);r(this,"onAfterHooks",new A);r(this,"_program");r(this,"_geometry");r(this,"_uniforms");r(this,"_clock");r(this,"_resolution",[1,1]);r(this,"_mouse",[0,0]);r(this,"_version",1);r(this,"playing",!1);this.canvas=t,this.options=e,this.gl=this.initContext(),this.enableExtensions(),this._program=new a(this.gl),this._geometry=p.fullscreenQuad(this.gl),this._uniforms=new v(this.gl),this._clock=new R,this.options.fps&&this._clock.setMaxFps(this.options.fps),this.options.onBeforeRender&&this.onBeforeHooks.add(this.options.onBeforeRender),this.options.onAfterRender&&this.onAfterHooks.add(this.options.onAfterRender),this.onRender=this.onRender.bind(this)}static setup(t,e){const i=new _(t,e);return e.vertex&&e.fragment&&i.shader(e.vertex,e.fragment),e.uniforms&&i._uniforms.setMany(e.uniforms),i}initContext(){const t={antialias:this.options.antialias,preserveDrawingBuffer:this.options.preserveDrawingBuffer,alpha:!0,depth:!1,stencil:!1},e=this.canvas.getContext("webgl2",t);if(e)return this._version=2,e;const i=this.canvas.getContext("webgl",t);if(i)return this._version=1,i;const s=new S("not_supported");throw this.options.onError(s),s}enableExtensions(){this.gl.getExtension("OES_standard_derivatives"),this.gl.getExtension("OES_texture_float"),this.gl.getExtension("OES_texture_float_linear"),this._version===1&&this.gl.getExtension("OES_vertex_array_object")}viewport(t,e,i,s){return this.canvas.width=i,this.canvas.height=s,this.gl.viewport(t,e,i,s),this._resolution=[i,s],this}clock(t){return this._clock.setTime(t),this}mouse(t,e){return this._mouse=[t,e],this}uniform(t,e){return this._uniforms.set(t,e),this}uniforms(t){return this._uniforms.setMany(t),this}getUniform(t){return this._uniforms.get(t)}shader(t,e){try{this._program.compile(t,e),this._version=this._program.getVersion(),this._geometry.linkAttributes(this._program);const i=this._program.getProgram();i&&this._uniforms.attachProgram(i),this.options.onLoad()}catch(i){i instanceof l&&this.options.onError(i)}return this}play(){return this.playing?this:(this.playing=!0,this._clock.start(this.onRender),this)}pause(){if(!this.playing)return this;const t=this._clock.getState();return this.onBeforeHooks.run(t),this.playing=!1,this._clock.stop(),this.onAfterHooks.run(t),this}render(){return this.onRender(this._clock.getState()),this}getContext(){return this.gl}getVersion(){return this._version}getClock(){return this._clock}destroy(){this.pause(),this._clock.destroy(),this._geometry.destroy(),this._program.destroy(),this._uniforms.destroy(),this.onAfterHooks.destroy(),this.onBeforeHooks.destroy()}onRender(t){const e=this.gl;this.onBeforeHooks.run(t),e.clearColor(0,0,0,0),e.clear(e.COLOR_BUFFER_BIT),this._program.use(),this._uniforms.uploadBuiltIns(t,this._resolution,this._mouse),this._uniforms.uploadAll(),this._geometry.bind(),this._geometry.draw(),this.onAfterHooks.run(t)}}const g=`#ifdef GL_ES
|
|
6
6
|
precision mediump float;
|
|
7
7
|
#endif
|
|
8
8
|
|
|
@@ -15,7 +15,7 @@ void main() {
|
|
|
15
15
|
v_texcoord = a_texcoord;
|
|
16
16
|
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
17
17
|
}
|
|
18
|
-
`,
|
|
18
|
+
`,E=`#ifdef GL_ES
|
|
19
19
|
precision mediump float;
|
|
20
20
|
#endif
|
|
21
21
|
|
|
@@ -29,7 +29,7 @@ void main() {
|
|
|
29
29
|
vec3 color = vec3(uv.x, uv.y, 0.5 + 0.5 * sin(u_time));
|
|
30
30
|
gl_FragColor = vec4(color, 1.0);
|
|
31
31
|
}
|
|
32
|
-
`,
|
|
32
|
+
`,y=`#version 300 es
|
|
33
33
|
|
|
34
34
|
in vec2 a_position;
|
|
35
35
|
in vec2 a_texcoord;
|
|
@@ -39,7 +39,7 @@ out vec2 v_texcoord;
|
|
|
39
39
|
void main() {
|
|
40
40
|
v_texcoord = a_texcoord;
|
|
41
41
|
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
42
|
-
}`,
|
|
42
|
+
}`,V=`#version 300 es
|
|
43
43
|
precision highp float;
|
|
44
44
|
|
|
45
45
|
uniform vec2 u_resolution;
|
|
@@ -53,5 +53,5 @@ void main() {
|
|
|
53
53
|
vec2 uv = gl_FragCoord.xy / u_resolution;
|
|
54
54
|
vec3 color = vec3(uv.x, uv.y, 0.5 + 0.5 * sin(u_time));
|
|
55
55
|
fragColor = vec4(color, 1.0);
|
|
56
|
-
}`;class
|
|
57
|
-
You can handle errors programmatically by providing an onError callback to suppress this log and implement custom fallback behavior.`)},onLoad:()=>{},onBeforeRender:null,onAfterRender:null,uniforms:{}};if(t!=null&&t.vertex&&!(t!=null&&t.fragment)){const
|
|
56
|
+
}`;class b{constructor(t,e){r(this,"listeners",[]);r(this,"canvasEl");r(this,"options");r(this,"engine");r(this,"usingCustomVertex",!1);this.canvasEl=t,this.options=this.resolveOptions(e),this.engine=_.setup(this.canvasEl,this.options),this.setupListeners(),this.setViewport(),this.options.autoplay&&this.play()}static create(t,e){return new b(t,e)}resolveOptions(t){const e={vertex:g,fragment:E,autoplay:!0,pauseWhenHidden:!0,dpr:"auto",fps:0,preserveDrawingBuffer:!1,antialias:!0,onError:i=>{console.error("Oops!",i,`
|
|
57
|
+
You can handle errors programmatically by providing an onError callback to suppress this log and implement custom fallback behavior.`)},onLoad:()=>{},onBeforeRender:null,onAfterRender:null,uniforms:{}};if(t!=null&&t.vertex&&(this.usingCustomVertex=!0),t!=null&&t.vertex&&!(t!=null&&t.fragment)){const i=a.detectVersion(t.vertex);e.vertex=t.vertex,e.fragment=i===2?V:E}if(t!=null&&t.fragment&&!(t!=null&&t.vertex)){const i=a.detectVersion(t.fragment);e.fragment=t.fragment,e.vertex=i===2?y:g}return{...e,...t}}setupListeners(){this.listeners.push(u.on(window,"resize",()=>{this.setViewport()}),u.on(this.canvasEl,"resize",()=>{this.setViewport()}),(()=>{let t=!1;return u.on(document,"scroll",e=>{this.options.pauseWhenHidden&&(this.isInViewport()?t&&!this.isPlaying()&&(this.play(),t=!1):this.isPlaying()&&(this.pause(),t=!0))})})(),u.on(document,"mousemove",t=>{this.setMouse(t.clientX||t.pageX,t.clientY||t.pageY)}),u.on(document,"touchmove",t=>{t.touches.length>0&&this.setMouse(t.touches[0].clientX,t.touches[0].clientY)}))}destroyListeners(){this.listeners.forEach(t=>t()),this.listeners=[]}setViewport(){const t=this.options.dpr==="auto"?Math.min(2,window.devicePixelRatio||1):this.options.dpr,e=this.canvasEl.clientWidth||this.canvasEl.width||1,i=this.canvasEl.clientHeight||this.canvasEl.height||1;this.engine.viewport(0,0,Math.max(1,Math.floor(e*t)),Math.max(1,Math.floor(i*t)))}isInViewport(){const t=this.canvasEl.getBoundingClientRect();return t.bottom>=0&&t.right>=0&&t.top<=(window.innerHeight||document.documentElement.clientHeight)&&t.left<=(window.innerWidth||document.documentElement.clientWidth)}setMouse(t,e){const i=this.canvasEl.getBoundingClientRect();t>=i.left&&t<=i.right&&e>=i.top&&e<=i.bottom&&this.engine.mouse(t-i.left,e-i.top)}setUniform(t,e){return this.engine.uniform(t,e),this}setUniforms(t){return this.engine.uniforms(t),this}getUniform(t){return this.engine.getUniform(t)}setShader(t,e){return this.options.vertex=t,this.options.fragment=e,this.usingCustomVertex=!0,this.engine.shader(this.options.vertex,this.options.fragment),this}setFragment(t){const e=a.detectVersion(t),i=a.detectVersion(this.options.vertex);return this.options.fragment=t,e!==i&&(this.usingCustomVertex||(this.options.vertex=e===2?y:g)),this.engine.shader(this.options.vertex,this.options.fragment),this}setFps(t){return this.engine.getClock().setMaxFps(t),this}hook(t,e="before"){return e==="before"?this.engine.onBeforeHooks.add(t):this.engine.onAfterHooks.add(t)}play(){return this.engine.play(),this}playAt(t){return this.engine.clock(t),this.engine.play(),this}pause(){return this.engine.pause(),this}pauseAt(t){const e=this.hook(i=>{i.time>=t&&(e(),this.pause())},"after");return this}toggle(){return this.engine.playing?this.pause():this.play(),this}time(t){return this.engine.clock(t),this}render(){return this.engine.render(),this}renderAt(t){return this.engine.clock(t),this.engine.render(),this}isPlaying(){return this.engine.playing}get version(){return this.engine.getVersion()}get canvas(){return this.canvasEl}destroy(){this.destroyListeners(),this.engine.destroy()}}exports.Sandbox=b;exports.SandboxContextError=S;exports.SandboxError=l;exports.SandboxProgramError=d;exports.SandboxShaderCompilationError=f;exports.SandboxShaderVersionMismatchError=L;
|
package/dist/index.d.ts
CHANGED
|
@@ -23,11 +23,13 @@ export declare class Sandbox {
|
|
|
23
23
|
/** Active event listeners */
|
|
24
24
|
private listeners;
|
|
25
25
|
/** HTML canvas element */
|
|
26
|
-
private
|
|
26
|
+
private canvasEl;
|
|
27
27
|
/** Resolved options */
|
|
28
28
|
private options;
|
|
29
29
|
/** WebGL engine */
|
|
30
30
|
private engine;
|
|
31
|
+
/** User sets custom vertex shader */
|
|
32
|
+
private usingCustomVertex;
|
|
31
33
|
constructor(canvas: HTMLCanvasElement, options?: SandboxOptions);
|
|
32
34
|
/**
|
|
33
35
|
* Sandbox - A lightweight WebGL wrapper for shader effects.
|
|
@@ -93,6 +95,14 @@ export declare class Sandbox {
|
|
|
93
95
|
* sandbox.setFragment(fragmentSource);
|
|
94
96
|
*/
|
|
95
97
|
setFragment(fragment: string): this;
|
|
98
|
+
/**
|
|
99
|
+
* Set the max frame rate runtime
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* sandbox.setFps(30); // Limit to 30 FPS
|
|
103
|
+
* sandbox.setFps(0); // Unlimited FPS
|
|
104
|
+
*/
|
|
105
|
+
setFps(fps: number): this;
|
|
96
106
|
/**
|
|
97
107
|
* Add a runtime render hook.
|
|
98
108
|
*/
|
|
@@ -140,11 +150,11 @@ export declare class Sandbox {
|
|
|
140
150
|
/**
|
|
141
151
|
* Get WebGL version using (1 or 2).
|
|
142
152
|
*/
|
|
143
|
-
|
|
153
|
+
get version(): WebGLVersion;
|
|
144
154
|
/**
|
|
145
155
|
* Get canvas element.
|
|
146
156
|
*/
|
|
147
|
-
|
|
157
|
+
get canvas(): HTMLCanvasElement;
|
|
148
158
|
/**
|
|
149
159
|
* Destroy sandbox and release all resources.
|
|
150
160
|
* @example
|
package/dist/index.es.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
var S = Object.defineProperty;
|
|
2
2
|
var L = (o, t, e) => t in o ? S(o, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : o[t] = e;
|
|
3
|
-
var
|
|
3
|
+
var r = (o, t, e) => L(o, typeof t != "symbol" ? t + "" : t, e);
|
|
4
4
|
class l {
|
|
5
|
-
constructor(t, e,
|
|
6
|
-
this.target = t, this.type = e, this.listener =
|
|
5
|
+
constructor(t, e, i, s) {
|
|
6
|
+
this.target = t, this.type = e, this.listener = i, this.options = s, this.target.addEventListener(
|
|
7
7
|
this.type,
|
|
8
8
|
this.listener,
|
|
9
9
|
this.options
|
|
@@ -16,8 +16,8 @@ class l {
|
|
|
16
16
|
this.options
|
|
17
17
|
);
|
|
18
18
|
}
|
|
19
|
-
static on(t, e,
|
|
20
|
-
return t.addEventListener(e,
|
|
19
|
+
static on(t, e, i, s) {
|
|
20
|
+
return t.addEventListener(e, i, s), () => t.removeEventListener(e, i, s);
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
class u extends Error {
|
|
@@ -43,21 +43,21 @@ class k extends u {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
class c extends u {
|
|
46
|
-
constructor(e,
|
|
47
|
-
const n = c.parseErrorLines(s),
|
|
46
|
+
constructor(e, i, s) {
|
|
47
|
+
const n = c.parseErrorLines(s), h = n.length > 0 ? ` at line(s): ${n.join(", ")}` : "";
|
|
48
48
|
super(
|
|
49
|
-
`${e} shader compilation failed${
|
|
49
|
+
`${e} shader compilation failed${h}
|
|
50
50
|
|
|
51
51
|
${s}`,
|
|
52
52
|
"SHADER_COMPILATION_FAILED"
|
|
53
53
|
);
|
|
54
54
|
/** Line numbers where errors occurred */
|
|
55
|
-
|
|
56
|
-
this.shaderType = e, this.source =
|
|
55
|
+
r(this, "lines");
|
|
56
|
+
this.shaderType = e, this.source = i, this.infoLog = s, this.name = "SandboxShaderCompilationError", this.lines = n;
|
|
57
57
|
}
|
|
58
58
|
/** Parse error log to extract line numbers */
|
|
59
59
|
static parseErrorLines(e) {
|
|
60
|
-
const
|
|
60
|
+
const i = [
|
|
61
61
|
/ERROR:\s*\d*:(\d+)/g,
|
|
62
62
|
// Chrome/ANGLE: ERROR: 0:15
|
|
63
63
|
/(\d+):(\d+)\(\d+\):/g,
|
|
@@ -65,14 +65,14 @@ ${s}`,
|
|
|
65
65
|
/^(\d+):/gm
|
|
66
66
|
// Simple: 15:
|
|
67
67
|
], s = /* @__PURE__ */ new Set();
|
|
68
|
-
for (const n of
|
|
69
|
-
let
|
|
70
|
-
for (; (
|
|
71
|
-
const f = parseInt(
|
|
68
|
+
for (const n of i) {
|
|
69
|
+
let h;
|
|
70
|
+
for (; (h = n.exec(e)) !== null; ) {
|
|
71
|
+
const f = parseInt(h[1], 10);
|
|
72
72
|
f > 0 && s.add(f);
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
|
-
return [...s].sort((n,
|
|
75
|
+
return [...s].sort((n, h) => n - h);
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
class m extends u {
|
|
@@ -85,17 +85,20 @@ ${t}`, "PROGRAM_LINK_FAILED"), this.infoLog = t, this.name = "SandboxProgramErro
|
|
|
85
85
|
class R {
|
|
86
86
|
constructor() {
|
|
87
87
|
/** Total elapsed time in seconds */
|
|
88
|
-
|
|
88
|
+
r(this, "time", 0);
|
|
89
89
|
/** Delta time since last frame in seconds */
|
|
90
|
-
|
|
90
|
+
r(this, "delta", 0);
|
|
91
91
|
/** Frame counter */
|
|
92
|
-
|
|
92
|
+
r(this, "frame", 0);
|
|
93
93
|
/** Is clock running */
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
r(this, "running", !1);
|
|
95
|
+
/** Smoothed frames per second */
|
|
96
|
+
r(this, "fps", 0);
|
|
97
|
+
r(this, "startTime", 0);
|
|
98
|
+
r(this, "lastTime", 0);
|
|
99
|
+
r(this, "rafId", null);
|
|
100
|
+
r(this, "callback", null);
|
|
101
|
+
r(this, "maxFps", 0);
|
|
99
102
|
this.loop = this.loop.bind(this);
|
|
100
103
|
}
|
|
101
104
|
/**
|
|
@@ -118,7 +121,7 @@ class R {
|
|
|
118
121
|
* Reset clock to initial state.
|
|
119
122
|
*/
|
|
120
123
|
reset() {
|
|
121
|
-
return this.stop(), this.time = 0, this.delta = 0, this.frame = 0, this;
|
|
124
|
+
return this.stop(), this.time = 0, this.delta = 0, this.frame = 0, this.fps = 0, this;
|
|
122
125
|
}
|
|
123
126
|
/**
|
|
124
127
|
* Get current clock state snapshot.
|
|
@@ -127,7 +130,9 @@ class R {
|
|
|
127
130
|
return {
|
|
128
131
|
time: this.time,
|
|
129
132
|
delta: this.delta,
|
|
130
|
-
frame: this.frame
|
|
133
|
+
frame: this.frame,
|
|
134
|
+
running: this.running,
|
|
135
|
+
fps: Math.round(this.fps)
|
|
131
136
|
};
|
|
132
137
|
}
|
|
133
138
|
/**
|
|
@@ -149,25 +154,41 @@ class R {
|
|
|
149
154
|
destroy() {
|
|
150
155
|
this.reset(), this.callback = null;
|
|
151
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* Set maximum frames per second.
|
|
159
|
+
*/
|
|
160
|
+
setMaxFps(t) {
|
|
161
|
+
return this.maxFps = t, this;
|
|
162
|
+
}
|
|
152
163
|
/**
|
|
153
164
|
* Internal animation frame handler.
|
|
154
165
|
*/
|
|
155
166
|
loop(t) {
|
|
156
|
-
|
|
167
|
+
if (!this.running) return;
|
|
168
|
+
if (this.maxFps > 0) {
|
|
169
|
+
const i = 1e3 / this.maxFps;
|
|
170
|
+
if (t - this.lastTime < i) {
|
|
171
|
+
this.rafId = requestAnimationFrame(this.loop);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
this.delta = (t - this.lastTime) / 1e3, this.lastTime = t;
|
|
176
|
+
const e = this.delta > 0 ? 1 / this.delta : 0;
|
|
177
|
+
this.fps = this.fps * 0.95 + e * 0.05, this.time = (t - this.startTime) / 1e3, this.frame++, this.callback && this.callback(this.getState()), this.rafId = requestAnimationFrame(this.loop);
|
|
157
178
|
}
|
|
158
179
|
}
|
|
159
180
|
class p {
|
|
160
181
|
constructor(t) {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
182
|
+
r(this, "gl");
|
|
183
|
+
r(this, "vao", null);
|
|
184
|
+
r(this, "vbo", null);
|
|
185
|
+
r(this, "ibo", null);
|
|
186
|
+
r(this, "vertexCount", 0);
|
|
187
|
+
r(this, "indexCount", 0);
|
|
188
|
+
r(this, "useIndices", !1);
|
|
168
189
|
// WebGL1 VAO extension (if available)
|
|
169
|
-
|
|
170
|
-
|
|
190
|
+
r(this, "vaoExt", null);
|
|
191
|
+
r(this, "isWebGL2");
|
|
171
192
|
this.gl = t, this.isWebGL2 = t instanceof WebGL2RenderingContext, this.isWebGL2 || (this.vaoExt = t.getExtension("OES_vertex_array_object"));
|
|
172
193
|
}
|
|
173
194
|
/**
|
|
@@ -175,7 +196,7 @@ class p {
|
|
|
175
196
|
* This is the most common use case for shader effects.
|
|
176
197
|
*/
|
|
177
198
|
static fullscreenQuad(t) {
|
|
178
|
-
const e = new p(t),
|
|
199
|
+
const e = new p(t), i = new Float32Array([
|
|
179
200
|
// position texcoord
|
|
180
201
|
-1,
|
|
181
202
|
-1,
|
|
@@ -207,14 +228,14 @@ class p {
|
|
|
207
228
|
3
|
|
208
229
|
// second triangle
|
|
209
230
|
]);
|
|
210
|
-
return e.setup(
|
|
231
|
+
return e.setup(i, s), e;
|
|
211
232
|
}
|
|
212
233
|
/**
|
|
213
234
|
* Setup geometry from vertex and index data.
|
|
214
235
|
*/
|
|
215
236
|
setup(t, e) {
|
|
216
|
-
const
|
|
217
|
-
return this.createVAO(), this.bindVAO(), this.vbo =
|
|
237
|
+
const i = this.gl;
|
|
238
|
+
return this.createVAO(), this.bindVAO(), this.vbo = i.createBuffer(), i.bindBuffer(i.ARRAY_BUFFER, this.vbo), i.bufferData(i.ARRAY_BUFFER, t, i.STATIC_DRAW), this.vertexCount = t.length / 4, e && (this.ibo = i.createBuffer(), i.bindBuffer(i.ELEMENT_ARRAY_BUFFER, this.ibo), i.bufferData(i.ELEMENT_ARRAY_BUFFER, e, i.STATIC_DRAW), this.indexCount = e.length, this.useIndices = !0), this.unbindVAO(), this;
|
|
218
239
|
}
|
|
219
240
|
/**
|
|
220
241
|
* Link vertex attributes to shader program.
|
|
@@ -223,15 +244,15 @@ class p {
|
|
|
223
244
|
linkAttributes(t) {
|
|
224
245
|
const e = this.gl;
|
|
225
246
|
this.bindVAO(), e.bindBuffer(e.ARRAY_BUFFER, this.vbo);
|
|
226
|
-
const
|
|
227
|
-
s >= 0 && (e.enableVertexAttribArray(s), e.vertexAttribPointer(s, 2, e.FLOAT, !1,
|
|
247
|
+
const i = 4 * Float32Array.BYTES_PER_ELEMENT, s = this.getPositionLocation(t);
|
|
248
|
+
s >= 0 && (e.enableVertexAttribArray(s), e.vertexAttribPointer(s, 2, e.FLOAT, !1, i, 0));
|
|
228
249
|
const n = this.getTexcoordLocation(t);
|
|
229
250
|
return n >= 0 && (e.enableVertexAttribArray(n), e.vertexAttribPointer(
|
|
230
251
|
n,
|
|
231
252
|
2,
|
|
232
253
|
e.FLOAT,
|
|
233
254
|
!1,
|
|
234
|
-
|
|
255
|
+
i,
|
|
235
256
|
2 * Float32Array.BYTES_PER_ELEMENT
|
|
236
257
|
)), this.useIndices && e.bindBuffer(e.ELEMENT_ARRAY_BUFFER, this.ibo), this.unbindVAO(), this;
|
|
237
258
|
}
|
|
@@ -293,13 +314,13 @@ class p {
|
|
|
293
314
|
this.vao && (this.isWebGL2 ? this.gl.deleteVertexArray(this.vao) : this.vaoExt && this.vaoExt.deleteVertexArrayOES(this.vao), this.vao = null);
|
|
294
315
|
}
|
|
295
316
|
}
|
|
296
|
-
class
|
|
317
|
+
class a {
|
|
297
318
|
constructor(t) {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
319
|
+
r(this, "gl");
|
|
320
|
+
r(this, "program", null);
|
|
321
|
+
r(this, "vertexShader", null);
|
|
322
|
+
r(this, "fragmentShader", null);
|
|
323
|
+
r(this, "version", 1);
|
|
303
324
|
this.gl = t;
|
|
304
325
|
}
|
|
305
326
|
/**
|
|
@@ -316,10 +337,10 @@ class h {
|
|
|
316
337
|
*/
|
|
317
338
|
compile(t, e) {
|
|
318
339
|
this.destroy();
|
|
319
|
-
const
|
|
320
|
-
if (
|
|
321
|
-
throw new k(
|
|
322
|
-
return this.version = Math.max(
|
|
340
|
+
const i = a.detectVersion(t), s = a.detectVersion(e);
|
|
341
|
+
if (i != s)
|
|
342
|
+
throw new k(i, s);
|
|
343
|
+
return this.version = Math.max(i, s), this.vertexShader = this.compileShader("vertex", t), this.fragmentShader = this.compileShader("fragment", e), this.linkProgram(), this;
|
|
323
344
|
}
|
|
324
345
|
/**
|
|
325
346
|
* Bind this program for rendering.
|
|
@@ -363,16 +384,16 @@ class h {
|
|
|
363
384
|
* @throws ShaderCompilationError if compilation fails
|
|
364
385
|
*/
|
|
365
386
|
compileShader(t, e) {
|
|
366
|
-
const
|
|
387
|
+
const i = this.gl, s = t === "vertex" ? i.VERTEX_SHADER : i.FRAGMENT_SHADER, n = i.createShader(s);
|
|
367
388
|
if (!n)
|
|
368
389
|
throw new c(
|
|
369
390
|
t,
|
|
370
391
|
e,
|
|
371
392
|
"Failed to create shader object"
|
|
372
393
|
);
|
|
373
|
-
if (
|
|
374
|
-
const f =
|
|
375
|
-
throw
|
|
394
|
+
if (i.shaderSource(n, e), i.compileShader(n), !i.getShaderParameter(n, i.COMPILE_STATUS)) {
|
|
395
|
+
const f = i.getShaderInfoLog(n) || "Unknown error";
|
|
396
|
+
throw i.deleteShader(n), new c(t, e, f);
|
|
376
397
|
}
|
|
377
398
|
return n;
|
|
378
399
|
}
|
|
@@ -396,16 +417,16 @@ class h {
|
|
|
396
417
|
}
|
|
397
418
|
class x {
|
|
398
419
|
constructor(t, e) {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
420
|
+
r(this, "name");
|
|
421
|
+
r(this, "method");
|
|
422
|
+
r(this, "isArray");
|
|
423
|
+
r(this, "isMatrix");
|
|
424
|
+
r(this, "location", null);
|
|
425
|
+
r(this, "locationResolved", !1);
|
|
426
|
+
r(this, "value");
|
|
406
427
|
this.name = t, this.value = e;
|
|
407
|
-
const
|
|
408
|
-
this.method =
|
|
428
|
+
const i = x.inferMethodInfo(e);
|
|
429
|
+
this.method = i.method, this.isArray = i.isArray, this.isMatrix = i.isMatrix;
|
|
409
430
|
}
|
|
410
431
|
/**
|
|
411
432
|
* Infer WebGL method and metadata from value type.
|
|
@@ -417,9 +438,9 @@ class x {
|
|
|
417
438
|
return { method: "uniform1f", isArray: !1, isMatrix: !1 };
|
|
418
439
|
if (!Array.isArray(t))
|
|
419
440
|
return { method: "uniform1f", isArray: !1, isMatrix: !1 };
|
|
420
|
-
const e = t.length,
|
|
421
|
-
if (Array.isArray(
|
|
422
|
-
switch (
|
|
441
|
+
const e = t.length, i = t[0];
|
|
442
|
+
if (Array.isArray(i))
|
|
443
|
+
switch (i.length) {
|
|
423
444
|
case 2:
|
|
424
445
|
return { method: "uniform2fv", isArray: !0, isMatrix: !1 };
|
|
425
446
|
case 3:
|
|
@@ -475,8 +496,8 @@ class x {
|
|
|
475
496
|
* @param program - Current WebGL program (for location resolution)
|
|
476
497
|
*/
|
|
477
498
|
upload(t, e) {
|
|
478
|
-
const
|
|
479
|
-
if (
|
|
499
|
+
const i = this.resolveLocation(t, e);
|
|
500
|
+
if (i === null)
|
|
480
501
|
return;
|
|
481
502
|
const s = this.value;
|
|
482
503
|
let n;
|
|
@@ -484,40 +505,40 @@ class x {
|
|
|
484
505
|
s.flat()
|
|
485
506
|
) : n = new Float32Array(s), this.method) {
|
|
486
507
|
case "uniform1f":
|
|
487
|
-
t.uniform1f(
|
|
508
|
+
t.uniform1f(i, n);
|
|
488
509
|
break;
|
|
489
510
|
case "uniform1i":
|
|
490
|
-
t.uniform1i(
|
|
511
|
+
t.uniform1i(i, n);
|
|
491
512
|
break;
|
|
492
513
|
case "uniform1fv":
|
|
493
|
-
t.uniform1fv(
|
|
514
|
+
t.uniform1fv(i, n);
|
|
494
515
|
break;
|
|
495
516
|
case "uniform2fv":
|
|
496
|
-
t.uniform2fv(
|
|
517
|
+
t.uniform2fv(i, n);
|
|
497
518
|
break;
|
|
498
519
|
case "uniform3fv":
|
|
499
|
-
t.uniform3fv(
|
|
520
|
+
t.uniform3fv(i, n);
|
|
500
521
|
break;
|
|
501
522
|
case "uniform4fv":
|
|
502
|
-
t.uniform4fv(
|
|
523
|
+
t.uniform4fv(i, n);
|
|
503
524
|
break;
|
|
504
525
|
case "uniformMatrix2fv":
|
|
505
|
-
t.uniformMatrix2fv(
|
|
526
|
+
t.uniformMatrix2fv(i, !1, n);
|
|
506
527
|
break;
|
|
507
528
|
case "uniformMatrix3fv":
|
|
508
|
-
t.uniformMatrix3fv(
|
|
529
|
+
t.uniformMatrix3fv(i, !1, n);
|
|
509
530
|
break;
|
|
510
531
|
case "uniformMatrix4fv":
|
|
511
|
-
t.uniformMatrix4fv(
|
|
532
|
+
t.uniformMatrix4fv(i, !1, n);
|
|
512
533
|
break;
|
|
513
534
|
}
|
|
514
535
|
}
|
|
515
536
|
}
|
|
516
537
|
const d = class d {
|
|
517
538
|
constructor(t) {
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
539
|
+
r(this, "gl");
|
|
540
|
+
r(this, "program", null);
|
|
541
|
+
r(this, "uniforms", /* @__PURE__ */ new Map());
|
|
521
542
|
this.gl = t;
|
|
522
543
|
}
|
|
523
544
|
/**
|
|
@@ -535,15 +556,15 @@ const d = class d {
|
|
|
535
556
|
* Creates the uniform if it doesn't exist, updates if it does.
|
|
536
557
|
*/
|
|
537
558
|
set(t, e) {
|
|
538
|
-
const
|
|
539
|
-
return
|
|
559
|
+
const i = this.uniforms.get(t);
|
|
560
|
+
return i ? i.setValue(e) : this.uniforms.set(t, new x(t, e)), this;
|
|
540
561
|
}
|
|
541
562
|
/**
|
|
542
563
|
* Set multiple uniforms at once.
|
|
543
564
|
*/
|
|
544
565
|
setMany(t) {
|
|
545
|
-
for (const [e,
|
|
546
|
-
this.set(e,
|
|
566
|
+
for (const [e, i] of Object.entries(t))
|
|
567
|
+
this.set(e, i);
|
|
547
568
|
return this;
|
|
548
569
|
}
|
|
549
570
|
/**
|
|
@@ -580,8 +601,8 @@ const d = class d {
|
|
|
580
601
|
* Upload only built-in uniforms (u_resolution, u_time, u_delta, u_mouse, u_frame).
|
|
581
602
|
* Call this every frame with current values.
|
|
582
603
|
*/
|
|
583
|
-
uploadBuiltIns(t, e,
|
|
584
|
-
if (this.set("u_resolution", e), this.set("u_time", t.time), this.set("u_delta", t.delta), this.set("u_mouse",
|
|
604
|
+
uploadBuiltIns(t, e, i) {
|
|
605
|
+
if (this.set("u_resolution", e), this.set("u_time", t.time), this.set("u_delta", t.delta), this.set("u_mouse", i), this.set("u_frame", t.frame), !this.program)
|
|
585
606
|
return this;
|
|
586
607
|
for (const s of d.BUILT_INS) {
|
|
587
608
|
const n = this.uniforms.get(s);
|
|
@@ -615,7 +636,7 @@ const d = class d {
|
|
|
615
636
|
}
|
|
616
637
|
};
|
|
617
638
|
/** Built-in uniform names that are handled automatically */
|
|
618
|
-
|
|
639
|
+
r(d, "BUILT_INS", /* @__PURE__ */ new Set([
|
|
619
640
|
"u_resolution",
|
|
620
641
|
"u_time",
|
|
621
642
|
"u_delta",
|
|
@@ -625,7 +646,7 @@ i(d, "BUILT_INS", /* @__PURE__ */ new Set([
|
|
|
625
646
|
let v = d;
|
|
626
647
|
class b {
|
|
627
648
|
constructor() {
|
|
628
|
-
|
|
649
|
+
r(this, "hooks", /* @__PURE__ */ new Map());
|
|
629
650
|
}
|
|
630
651
|
id() {
|
|
631
652
|
return Math.random().toString(36).substring(2, 10);
|
|
@@ -641,8 +662,8 @@ class b {
|
|
|
641
662
|
}
|
|
642
663
|
/** Run all hooks with the given state */
|
|
643
664
|
run(t) {
|
|
644
|
-
for (const [e,
|
|
645
|
-
|
|
665
|
+
for (const [e, i] of this.hooks)
|
|
666
|
+
i(t) === !1 && this.remove(e);
|
|
646
667
|
}
|
|
647
668
|
destroy() {
|
|
648
669
|
this.hooks.clear();
|
|
@@ -650,27 +671,27 @@ class b {
|
|
|
650
671
|
}
|
|
651
672
|
class _ {
|
|
652
673
|
constructor(t, e) {
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
this.canvas = t, this.options = e, this.gl = this.initContext(), this.enableExtensions(), this._program = new
|
|
674
|
+
r(this, "canvas");
|
|
675
|
+
r(this, "gl");
|
|
676
|
+
r(this, "options");
|
|
677
|
+
r(this, "onBeforeHooks", new b());
|
|
678
|
+
r(this, "onAfterHooks", new b());
|
|
679
|
+
r(this, "_program");
|
|
680
|
+
r(this, "_geometry");
|
|
681
|
+
r(this, "_uniforms");
|
|
682
|
+
r(this, "_clock");
|
|
683
|
+
r(this, "_resolution", [1, 1]);
|
|
684
|
+
r(this, "_mouse", [0, 0]);
|
|
685
|
+
r(this, "_version", 1);
|
|
686
|
+
r(this, "playing", !1);
|
|
687
|
+
this.canvas = t, this.options = e, this.gl = this.initContext(), this.enableExtensions(), this._program = new a(this.gl), this._geometry = p.fullscreenQuad(this.gl), this._uniforms = new v(this.gl), this._clock = new R(), this.options.fps && this._clock.setMaxFps(this.options.fps), this.options.onBeforeRender && this.onBeforeHooks.add(this.options.onBeforeRender), this.options.onAfterRender && this.onAfterHooks.add(this.options.onAfterRender), this.onRender = this.onRender.bind(this);
|
|
667
688
|
}
|
|
668
689
|
/**
|
|
669
690
|
* Factory method to create and setup WebGL instance.
|
|
670
691
|
*/
|
|
671
692
|
static setup(t, e) {
|
|
672
|
-
const
|
|
673
|
-
return e.vertex && e.fragment &&
|
|
693
|
+
const i = new _(t, e);
|
|
694
|
+
return e.vertex && e.fragment && i.shader(e.vertex, e.fragment), e.uniforms && i._uniforms.setMany(e.uniforms), i;
|
|
674
695
|
}
|
|
675
696
|
/**
|
|
676
697
|
* Initialize WebGL context.
|
|
@@ -687,9 +708,9 @@ class _ {
|
|
|
687
708
|
}, e = this.canvas.getContext("webgl2", t);
|
|
688
709
|
if (e)
|
|
689
710
|
return this._version = 2, e;
|
|
690
|
-
const
|
|
691
|
-
if (
|
|
692
|
-
return this._version = 1,
|
|
711
|
+
const i = this.canvas.getContext("webgl", t);
|
|
712
|
+
if (i)
|
|
713
|
+
return this._version = 1, i;
|
|
693
714
|
const s = new w("not_supported");
|
|
694
715
|
throw this.options.onError(s), s;
|
|
695
716
|
}
|
|
@@ -702,8 +723,8 @@ class _ {
|
|
|
702
723
|
/**
|
|
703
724
|
* Set viewport dimensions.
|
|
704
725
|
*/
|
|
705
|
-
viewport(t, e,
|
|
706
|
-
return this.canvas.width =
|
|
726
|
+
viewport(t, e, i, s) {
|
|
727
|
+
return this.canvas.width = i, this.canvas.height = s, this.gl.viewport(t, e, i, s), this._resolution = [i, s], this;
|
|
707
728
|
}
|
|
708
729
|
/**
|
|
709
730
|
* Set the clock time
|
|
@@ -742,10 +763,10 @@ class _ {
|
|
|
742
763
|
shader(t, e) {
|
|
743
764
|
try {
|
|
744
765
|
this._program.compile(t, e), this._version = this._program.getVersion(), this._geometry.linkAttributes(this._program);
|
|
745
|
-
const
|
|
746
|
-
|
|
747
|
-
} catch (
|
|
748
|
-
|
|
766
|
+
const i = this._program.getProgram();
|
|
767
|
+
i && this._uniforms.attachProgram(i), this.options.onLoad();
|
|
768
|
+
} catch (i) {
|
|
769
|
+
i instanceof u && this.options.onError(i);
|
|
749
770
|
}
|
|
750
771
|
return this;
|
|
751
772
|
}
|
|
@@ -781,6 +802,12 @@ class _ {
|
|
|
781
802
|
getVersion() {
|
|
782
803
|
return this._version;
|
|
783
804
|
}
|
|
805
|
+
/**
|
|
806
|
+
* Get the clock instance
|
|
807
|
+
*/
|
|
808
|
+
getClock() {
|
|
809
|
+
return this._clock;
|
|
810
|
+
}
|
|
784
811
|
/**
|
|
785
812
|
* Cleanup all resources.
|
|
786
813
|
*/
|
|
@@ -822,7 +849,7 @@ void main() {
|
|
|
822
849
|
vec3 color = vec3(uv.x, uv.y, 0.5 + 0.5 * sin(u_time));
|
|
823
850
|
gl_FragColor = vec4(color, 1.0);
|
|
824
851
|
}
|
|
825
|
-
`,
|
|
852
|
+
`, E = `#version 300 es
|
|
826
853
|
|
|
827
854
|
in vec2 a_position;
|
|
828
855
|
in vec2 a_texcoord;
|
|
@@ -832,7 +859,7 @@ out vec2 v_texcoord;
|
|
|
832
859
|
void main() {
|
|
833
860
|
v_texcoord = a_texcoord;
|
|
834
861
|
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
835
|
-
}`,
|
|
862
|
+
}`, V = `#version 300 es
|
|
836
863
|
precision highp float;
|
|
837
864
|
|
|
838
865
|
uniform vec2 u_resolution;
|
|
@@ -847,17 +874,19 @@ void main() {
|
|
|
847
874
|
vec3 color = vec3(uv.x, uv.y, 0.5 + 0.5 * sin(u_time));
|
|
848
875
|
fragColor = vec4(color, 1.0);
|
|
849
876
|
}`;
|
|
850
|
-
class
|
|
877
|
+
class y {
|
|
851
878
|
constructor(t, e) {
|
|
852
879
|
/** Active event listeners */
|
|
853
|
-
|
|
880
|
+
r(this, "listeners", []);
|
|
854
881
|
/** HTML canvas element */
|
|
855
|
-
|
|
882
|
+
r(this, "canvasEl");
|
|
856
883
|
/** Resolved options */
|
|
857
|
-
|
|
884
|
+
r(this, "options");
|
|
858
885
|
/** WebGL engine */
|
|
859
|
-
|
|
860
|
-
|
|
886
|
+
r(this, "engine");
|
|
887
|
+
/** User sets custom vertex shader */
|
|
888
|
+
r(this, "usingCustomVertex", !1);
|
|
889
|
+
this.canvasEl = t, this.options = this.resolveOptions(e), this.engine = _.setup(this.canvasEl, this.options), this.setupListeners(), this.setViewport(), this.options.autoplay && this.play();
|
|
861
890
|
}
|
|
862
891
|
/**
|
|
863
892
|
* Sandbox - A lightweight WebGL wrapper for shader effects.
|
|
@@ -878,7 +907,7 @@ class E {
|
|
|
878
907
|
* });
|
|
879
908
|
*/
|
|
880
909
|
static create(t, e) {
|
|
881
|
-
return new
|
|
910
|
+
return new y(t, e);
|
|
882
911
|
}
|
|
883
912
|
resolveOptions(t) {
|
|
884
913
|
const e = {
|
|
@@ -887,12 +916,13 @@ class E {
|
|
|
887
916
|
autoplay: !0,
|
|
888
917
|
pauseWhenHidden: !0,
|
|
889
918
|
dpr: "auto",
|
|
919
|
+
fps: 0,
|
|
890
920
|
preserveDrawingBuffer: !1,
|
|
891
921
|
antialias: !0,
|
|
892
|
-
onError: (
|
|
922
|
+
onError: (i) => {
|
|
893
923
|
console.error(
|
|
894
924
|
"Oops!",
|
|
895
|
-
|
|
925
|
+
i,
|
|
896
926
|
`
|
|
897
927
|
You can handle errors programmatically by providing an onError callback to suppress this log and implement custom fallback behavior.`
|
|
898
928
|
);
|
|
@@ -903,13 +933,13 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
903
933
|
onAfterRender: null,
|
|
904
934
|
uniforms: {}
|
|
905
935
|
};
|
|
906
|
-
if (t != null && t.vertex && !(t != null && t.fragment)) {
|
|
907
|
-
const
|
|
908
|
-
e.vertex = t.vertex, e.fragment =
|
|
936
|
+
if (t != null && t.vertex && (this.usingCustomVertex = !0), t != null && t.vertex && !(t != null && t.fragment)) {
|
|
937
|
+
const i = a.detectVersion(t.vertex);
|
|
938
|
+
e.vertex = t.vertex, e.fragment = i === 2 ? V : A;
|
|
909
939
|
}
|
|
910
940
|
if (t != null && t.fragment && !(t != null && t.vertex)) {
|
|
911
|
-
const
|
|
912
|
-
e.fragment = t.fragment, e.vertex =
|
|
941
|
+
const i = a.detectVersion(t.fragment);
|
|
942
|
+
e.fragment = t.fragment, e.vertex = i === 2 ? E : g;
|
|
913
943
|
}
|
|
914
944
|
return { ...e, ...t };
|
|
915
945
|
}
|
|
@@ -920,7 +950,7 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
920
950
|
this.setViewport();
|
|
921
951
|
}),
|
|
922
952
|
// Canvas resize
|
|
923
|
-
l.on(this.
|
|
953
|
+
l.on(this.canvasEl, "resize", () => {
|
|
924
954
|
this.setViewport();
|
|
925
955
|
}),
|
|
926
956
|
// Visibility check on scroll
|
|
@@ -944,21 +974,21 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
944
974
|
this.listeners.forEach((t) => t()), this.listeners = [];
|
|
945
975
|
}
|
|
946
976
|
setViewport() {
|
|
947
|
-
const t = this.options.dpr === "auto" ? Math.min(2, window.devicePixelRatio || 1) : this.options.dpr, e = this.
|
|
977
|
+
const t = this.options.dpr === "auto" ? Math.min(2, window.devicePixelRatio || 1) : this.options.dpr, e = this.canvasEl.clientWidth || this.canvasEl.width || 1, i = this.canvasEl.clientHeight || this.canvasEl.height || 1;
|
|
948
978
|
this.engine.viewport(
|
|
949
979
|
0,
|
|
950
980
|
0,
|
|
951
981
|
Math.max(1, Math.floor(e * t)),
|
|
952
|
-
Math.max(1, Math.floor(
|
|
982
|
+
Math.max(1, Math.floor(i * t))
|
|
953
983
|
);
|
|
954
984
|
}
|
|
955
985
|
isInViewport() {
|
|
956
|
-
const t = this.
|
|
986
|
+
const t = this.canvasEl.getBoundingClientRect();
|
|
957
987
|
return t.bottom >= 0 && t.right >= 0 && t.top <= (window.innerHeight || document.documentElement.clientHeight) && t.left <= (window.innerWidth || document.documentElement.clientWidth);
|
|
958
988
|
}
|
|
959
989
|
setMouse(t, e) {
|
|
960
|
-
const
|
|
961
|
-
t >=
|
|
990
|
+
const i = this.canvasEl.getBoundingClientRect();
|
|
991
|
+
t >= i.left && t <= i.right && e >= i.top && e <= i.bottom && this.engine.mouse(t - i.left, e - i.top);
|
|
962
992
|
}
|
|
963
993
|
/**
|
|
964
994
|
* Set a single uniform value with type checking.
|
|
@@ -999,7 +1029,7 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
999
1029
|
* sandbox.setShader(vertexSource, fragmentSource);
|
|
1000
1030
|
*/
|
|
1001
1031
|
setShader(t, e) {
|
|
1002
|
-
return this.engine.shader(
|
|
1032
|
+
return this.options.vertex = t, this.options.fragment = e, this.usingCustomVertex = !0, this.engine.shader(this.options.vertex, this.options.fragment), this;
|
|
1003
1033
|
}
|
|
1004
1034
|
/**
|
|
1005
1035
|
* Update only fragment shader (uses default vertex).
|
|
@@ -1007,8 +1037,18 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
1007
1037
|
* sandbox.setFragment(fragmentSource);
|
|
1008
1038
|
*/
|
|
1009
1039
|
setFragment(t) {
|
|
1010
|
-
const
|
|
1011
|
-
return this.engine.shader(
|
|
1040
|
+
const e = a.detectVersion(t), i = a.detectVersion(this.options.vertex);
|
|
1041
|
+
return this.options.fragment = t, e !== i && (this.usingCustomVertex || (this.options.vertex = e === 2 ? E : g)), this.engine.shader(this.options.vertex, this.options.fragment), this;
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1044
|
+
* Set the max frame rate runtime
|
|
1045
|
+
*
|
|
1046
|
+
* @example
|
|
1047
|
+
* sandbox.setFps(30); // Limit to 30 FPS
|
|
1048
|
+
* sandbox.setFps(0); // Unlimited FPS
|
|
1049
|
+
*/
|
|
1050
|
+
setFps(t) {
|
|
1051
|
+
return this.engine.getClock().setMaxFps(t), this;
|
|
1012
1052
|
}
|
|
1013
1053
|
/**
|
|
1014
1054
|
* Add a runtime render hook.
|
|
@@ -1038,8 +1078,8 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
1038
1078
|
* Pause animation loop at specific time (in seconds).
|
|
1039
1079
|
*/
|
|
1040
1080
|
pauseAt(t) {
|
|
1041
|
-
const e = this.hook((
|
|
1042
|
-
|
|
1081
|
+
const e = this.hook((i) => {
|
|
1082
|
+
i.time >= t && (e(), this.pause());
|
|
1043
1083
|
}, "after");
|
|
1044
1084
|
return this;
|
|
1045
1085
|
}
|
|
@@ -1080,14 +1120,14 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
1080
1120
|
/**
|
|
1081
1121
|
* Get WebGL version using (1 or 2).
|
|
1082
1122
|
*/
|
|
1083
|
-
|
|
1123
|
+
get version() {
|
|
1084
1124
|
return this.engine.getVersion();
|
|
1085
1125
|
}
|
|
1086
1126
|
/**
|
|
1087
1127
|
* Get canvas element.
|
|
1088
1128
|
*/
|
|
1089
|
-
|
|
1090
|
-
return this.
|
|
1129
|
+
get canvas() {
|
|
1130
|
+
return this.canvasEl;
|
|
1091
1131
|
}
|
|
1092
1132
|
/**
|
|
1093
1133
|
* Destroy sandbox and release all resources.
|
|
@@ -1101,7 +1141,7 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
1101
1141
|
}
|
|
1102
1142
|
}
|
|
1103
1143
|
export {
|
|
1104
|
-
|
|
1144
|
+
y as Sandbox,
|
|
1105
1145
|
w as SandboxContextError,
|
|
1106
1146
|
u as SandboxError,
|
|
1107
1147
|
m as SandboxProgramError,
|
package/dist/tools/clock.d.ts
CHANGED
|
@@ -12,10 +12,13 @@ export default class Clock {
|
|
|
12
12
|
frame: number;
|
|
13
13
|
/** Is clock running */
|
|
14
14
|
running: boolean;
|
|
15
|
+
/** Smoothed frames per second */
|
|
16
|
+
fps: number;
|
|
15
17
|
private startTime;
|
|
16
18
|
private lastTime;
|
|
17
19
|
private rafId;
|
|
18
20
|
private callback;
|
|
21
|
+
private maxFps;
|
|
19
22
|
constructor();
|
|
20
23
|
/**
|
|
21
24
|
* Start the animation loop with a render callback.
|
|
@@ -47,6 +50,10 @@ export default class Clock {
|
|
|
47
50
|
* Cleanup.
|
|
48
51
|
*/
|
|
49
52
|
destroy(): void;
|
|
53
|
+
/**
|
|
54
|
+
* Set maximum frames per second.
|
|
55
|
+
*/
|
|
56
|
+
setMaxFps(fps: number): this;
|
|
50
57
|
/**
|
|
51
58
|
* Internal animation frame handler.
|
|
52
59
|
*/
|
package/dist/tools/web_gl.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AnyUniformValue, ResolvedSandboxOptions, UniformSchema, WebGLContext, WebGLVersion } from "../types";
|
|
2
|
+
import Clock from "./clock";
|
|
2
3
|
import Hooks from "./hooks";
|
|
3
4
|
/**
|
|
4
5
|
* Main WebGL orchestrator.
|
|
@@ -82,6 +83,10 @@ export default class WebGL {
|
|
|
82
83
|
* Get detected WebGL version.
|
|
83
84
|
*/
|
|
84
85
|
getVersion(): WebGLVersion;
|
|
86
|
+
/**
|
|
87
|
+
* Get the clock instance
|
|
88
|
+
*/
|
|
89
|
+
getClock(): Clock;
|
|
85
90
|
/**
|
|
86
91
|
* Cleanup all resources.
|
|
87
92
|
*/
|
package/dist/types.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export interface SandboxOptions {
|
|
|
11
11
|
pauseWhenHidden?: boolean;
|
|
12
12
|
/** Device pixel ratio - "auto" uses window.devicePixelRatio (default: "auto") */
|
|
13
13
|
dpr?: number | "auto";
|
|
14
|
+
/** Max Frame rate (default: 0 as unlimited) */
|
|
15
|
+
fps?: number;
|
|
14
16
|
/** Preserve drawing buffer for screenshots (default: false) */
|
|
15
17
|
preserveDrawingBuffer?: boolean;
|
|
16
18
|
/** Enable antialiasing (default: true) */
|
|
@@ -95,6 +97,10 @@ export interface ClockState {
|
|
|
95
97
|
delta: number;
|
|
96
98
|
/** Frame counter */
|
|
97
99
|
frame: number;
|
|
100
|
+
/** Is the clock currently running */
|
|
101
|
+
running: boolean;
|
|
102
|
+
/** Frame rate in frames per second */
|
|
103
|
+
fps: number;
|
|
98
104
|
}
|
|
99
105
|
/** Internal uniform entry for caching */
|
|
100
106
|
export interface UniformEntry {
|