@rosalana/sandbox 0.0.3 → 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 +193 -155
- 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;if(this.callback=t,this.running=!0,this.frame===0){const e=performance.now();this.startTime=e,this.lastTime=e}return 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.stop(),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
|
/**
|
|
@@ -103,11 +106,9 @@ class R {
|
|
|
103
106
|
*/
|
|
104
107
|
start(t) {
|
|
105
108
|
if (this.running) return this;
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
return this.rafId = requestAnimationFrame(this.loop), this;
|
|
109
|
+
this.callback = t, this.running = !0;
|
|
110
|
+
const e = performance.now();
|
|
111
|
+
return this.frame === 0 ? this.startTime = e : this.startTime = e - this.time * 1e3, this.lastTime = e, this.rafId = requestAnimationFrame(this.loop), this;
|
|
111
112
|
}
|
|
112
113
|
/**
|
|
113
114
|
* Stop the animation loop.
|
|
@@ -120,7 +121,7 @@ class R {
|
|
|
120
121
|
* Reset clock to initial state.
|
|
121
122
|
*/
|
|
122
123
|
reset() {
|
|
123
|
-
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;
|
|
124
125
|
}
|
|
125
126
|
/**
|
|
126
127
|
* Get current clock state snapshot.
|
|
@@ -129,7 +130,9 @@ class R {
|
|
|
129
130
|
return {
|
|
130
131
|
time: this.time,
|
|
131
132
|
delta: this.delta,
|
|
132
|
-
frame: this.frame
|
|
133
|
+
frame: this.frame,
|
|
134
|
+
running: this.running,
|
|
135
|
+
fps: Math.round(this.fps)
|
|
133
136
|
};
|
|
134
137
|
}
|
|
135
138
|
/**
|
|
@@ -149,27 +152,43 @@ class R {
|
|
|
149
152
|
* Cleanup.
|
|
150
153
|
*/
|
|
151
154
|
destroy() {
|
|
152
|
-
this.
|
|
155
|
+
this.reset(), this.callback = null;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Set maximum frames per second.
|
|
159
|
+
*/
|
|
160
|
+
setMaxFps(t) {
|
|
161
|
+
return this.maxFps = t, this;
|
|
153
162
|
}
|
|
154
163
|
/**
|
|
155
164
|
* Internal animation frame handler.
|
|
156
165
|
*/
|
|
157
166
|
loop(t) {
|
|
158
|
-
|
|
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);
|
|
159
178
|
}
|
|
160
179
|
}
|
|
161
180
|
class p {
|
|
162
181
|
constructor(t) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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);
|
|
170
189
|
// WebGL1 VAO extension (if available)
|
|
171
|
-
|
|
172
|
-
|
|
190
|
+
r(this, "vaoExt", null);
|
|
191
|
+
r(this, "isWebGL2");
|
|
173
192
|
this.gl = t, this.isWebGL2 = t instanceof WebGL2RenderingContext, this.isWebGL2 || (this.vaoExt = t.getExtension("OES_vertex_array_object"));
|
|
174
193
|
}
|
|
175
194
|
/**
|
|
@@ -177,7 +196,7 @@ class p {
|
|
|
177
196
|
* This is the most common use case for shader effects.
|
|
178
197
|
*/
|
|
179
198
|
static fullscreenQuad(t) {
|
|
180
|
-
const e = new p(t),
|
|
199
|
+
const e = new p(t), i = new Float32Array([
|
|
181
200
|
// position texcoord
|
|
182
201
|
-1,
|
|
183
202
|
-1,
|
|
@@ -209,14 +228,14 @@ class p {
|
|
|
209
228
|
3
|
|
210
229
|
// second triangle
|
|
211
230
|
]);
|
|
212
|
-
return e.setup(
|
|
231
|
+
return e.setup(i, s), e;
|
|
213
232
|
}
|
|
214
233
|
/**
|
|
215
234
|
* Setup geometry from vertex and index data.
|
|
216
235
|
*/
|
|
217
236
|
setup(t, e) {
|
|
218
|
-
const
|
|
219
|
-
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;
|
|
220
239
|
}
|
|
221
240
|
/**
|
|
222
241
|
* Link vertex attributes to shader program.
|
|
@@ -225,15 +244,15 @@ class p {
|
|
|
225
244
|
linkAttributes(t) {
|
|
226
245
|
const e = this.gl;
|
|
227
246
|
this.bindVAO(), e.bindBuffer(e.ARRAY_BUFFER, this.vbo);
|
|
228
|
-
const
|
|
229
|
-
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));
|
|
230
249
|
const n = this.getTexcoordLocation(t);
|
|
231
250
|
return n >= 0 && (e.enableVertexAttribArray(n), e.vertexAttribPointer(
|
|
232
251
|
n,
|
|
233
252
|
2,
|
|
234
253
|
e.FLOAT,
|
|
235
254
|
!1,
|
|
236
|
-
|
|
255
|
+
i,
|
|
237
256
|
2 * Float32Array.BYTES_PER_ELEMENT
|
|
238
257
|
)), this.useIndices && e.bindBuffer(e.ELEMENT_ARRAY_BUFFER, this.ibo), this.unbindVAO(), this;
|
|
239
258
|
}
|
|
@@ -295,13 +314,13 @@ class p {
|
|
|
295
314
|
this.vao && (this.isWebGL2 ? this.gl.deleteVertexArray(this.vao) : this.vaoExt && this.vaoExt.deleteVertexArrayOES(this.vao), this.vao = null);
|
|
296
315
|
}
|
|
297
316
|
}
|
|
298
|
-
class
|
|
317
|
+
class a {
|
|
299
318
|
constructor(t) {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
319
|
+
r(this, "gl");
|
|
320
|
+
r(this, "program", null);
|
|
321
|
+
r(this, "vertexShader", null);
|
|
322
|
+
r(this, "fragmentShader", null);
|
|
323
|
+
r(this, "version", 1);
|
|
305
324
|
this.gl = t;
|
|
306
325
|
}
|
|
307
326
|
/**
|
|
@@ -318,10 +337,10 @@ class h {
|
|
|
318
337
|
*/
|
|
319
338
|
compile(t, e) {
|
|
320
339
|
this.destroy();
|
|
321
|
-
const
|
|
322
|
-
if (
|
|
323
|
-
throw new k(
|
|
324
|
-
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;
|
|
325
344
|
}
|
|
326
345
|
/**
|
|
327
346
|
* Bind this program for rendering.
|
|
@@ -365,16 +384,16 @@ class h {
|
|
|
365
384
|
* @throws ShaderCompilationError if compilation fails
|
|
366
385
|
*/
|
|
367
386
|
compileShader(t, e) {
|
|
368
|
-
const
|
|
387
|
+
const i = this.gl, s = t === "vertex" ? i.VERTEX_SHADER : i.FRAGMENT_SHADER, n = i.createShader(s);
|
|
369
388
|
if (!n)
|
|
370
389
|
throw new c(
|
|
371
390
|
t,
|
|
372
391
|
e,
|
|
373
392
|
"Failed to create shader object"
|
|
374
393
|
);
|
|
375
|
-
if (
|
|
376
|
-
const f =
|
|
377
|
-
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);
|
|
378
397
|
}
|
|
379
398
|
return n;
|
|
380
399
|
}
|
|
@@ -398,16 +417,16 @@ class h {
|
|
|
398
417
|
}
|
|
399
418
|
class x {
|
|
400
419
|
constructor(t, e) {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
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");
|
|
408
427
|
this.name = t, this.value = e;
|
|
409
|
-
const
|
|
410
|
-
this.method =
|
|
428
|
+
const i = x.inferMethodInfo(e);
|
|
429
|
+
this.method = i.method, this.isArray = i.isArray, this.isMatrix = i.isMatrix;
|
|
411
430
|
}
|
|
412
431
|
/**
|
|
413
432
|
* Infer WebGL method and metadata from value type.
|
|
@@ -419,9 +438,9 @@ class x {
|
|
|
419
438
|
return { method: "uniform1f", isArray: !1, isMatrix: !1 };
|
|
420
439
|
if (!Array.isArray(t))
|
|
421
440
|
return { method: "uniform1f", isArray: !1, isMatrix: !1 };
|
|
422
|
-
const e = t.length,
|
|
423
|
-
if (Array.isArray(
|
|
424
|
-
switch (
|
|
441
|
+
const e = t.length, i = t[0];
|
|
442
|
+
if (Array.isArray(i))
|
|
443
|
+
switch (i.length) {
|
|
425
444
|
case 2:
|
|
426
445
|
return { method: "uniform2fv", isArray: !0, isMatrix: !1 };
|
|
427
446
|
case 3:
|
|
@@ -477,8 +496,8 @@ class x {
|
|
|
477
496
|
* @param program - Current WebGL program (for location resolution)
|
|
478
497
|
*/
|
|
479
498
|
upload(t, e) {
|
|
480
|
-
const
|
|
481
|
-
if (
|
|
499
|
+
const i = this.resolveLocation(t, e);
|
|
500
|
+
if (i === null)
|
|
482
501
|
return;
|
|
483
502
|
const s = this.value;
|
|
484
503
|
let n;
|
|
@@ -486,40 +505,40 @@ class x {
|
|
|
486
505
|
s.flat()
|
|
487
506
|
) : n = new Float32Array(s), this.method) {
|
|
488
507
|
case "uniform1f":
|
|
489
|
-
t.uniform1f(
|
|
508
|
+
t.uniform1f(i, n);
|
|
490
509
|
break;
|
|
491
510
|
case "uniform1i":
|
|
492
|
-
t.uniform1i(
|
|
511
|
+
t.uniform1i(i, n);
|
|
493
512
|
break;
|
|
494
513
|
case "uniform1fv":
|
|
495
|
-
t.uniform1fv(
|
|
514
|
+
t.uniform1fv(i, n);
|
|
496
515
|
break;
|
|
497
516
|
case "uniform2fv":
|
|
498
|
-
t.uniform2fv(
|
|
517
|
+
t.uniform2fv(i, n);
|
|
499
518
|
break;
|
|
500
519
|
case "uniform3fv":
|
|
501
|
-
t.uniform3fv(
|
|
520
|
+
t.uniform3fv(i, n);
|
|
502
521
|
break;
|
|
503
522
|
case "uniform4fv":
|
|
504
|
-
t.uniform4fv(
|
|
523
|
+
t.uniform4fv(i, n);
|
|
505
524
|
break;
|
|
506
525
|
case "uniformMatrix2fv":
|
|
507
|
-
t.uniformMatrix2fv(
|
|
526
|
+
t.uniformMatrix2fv(i, !1, n);
|
|
508
527
|
break;
|
|
509
528
|
case "uniformMatrix3fv":
|
|
510
|
-
t.uniformMatrix3fv(
|
|
529
|
+
t.uniformMatrix3fv(i, !1, n);
|
|
511
530
|
break;
|
|
512
531
|
case "uniformMatrix4fv":
|
|
513
|
-
t.uniformMatrix4fv(
|
|
532
|
+
t.uniformMatrix4fv(i, !1, n);
|
|
514
533
|
break;
|
|
515
534
|
}
|
|
516
535
|
}
|
|
517
536
|
}
|
|
518
537
|
const d = class d {
|
|
519
538
|
constructor(t) {
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
539
|
+
r(this, "gl");
|
|
540
|
+
r(this, "program", null);
|
|
541
|
+
r(this, "uniforms", /* @__PURE__ */ new Map());
|
|
523
542
|
this.gl = t;
|
|
524
543
|
}
|
|
525
544
|
/**
|
|
@@ -537,15 +556,15 @@ const d = class d {
|
|
|
537
556
|
* Creates the uniform if it doesn't exist, updates if it does.
|
|
538
557
|
*/
|
|
539
558
|
set(t, e) {
|
|
540
|
-
const
|
|
541
|
-
return
|
|
559
|
+
const i = this.uniforms.get(t);
|
|
560
|
+
return i ? i.setValue(e) : this.uniforms.set(t, new x(t, e)), this;
|
|
542
561
|
}
|
|
543
562
|
/**
|
|
544
563
|
* Set multiple uniforms at once.
|
|
545
564
|
*/
|
|
546
565
|
setMany(t) {
|
|
547
|
-
for (const [e,
|
|
548
|
-
this.set(e,
|
|
566
|
+
for (const [e, i] of Object.entries(t))
|
|
567
|
+
this.set(e, i);
|
|
549
568
|
return this;
|
|
550
569
|
}
|
|
551
570
|
/**
|
|
@@ -582,8 +601,8 @@ const d = class d {
|
|
|
582
601
|
* Upload only built-in uniforms (u_resolution, u_time, u_delta, u_mouse, u_frame).
|
|
583
602
|
* Call this every frame with current values.
|
|
584
603
|
*/
|
|
585
|
-
uploadBuiltIns(t, e,
|
|
586
|
-
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)
|
|
587
606
|
return this;
|
|
588
607
|
for (const s of d.BUILT_INS) {
|
|
589
608
|
const n = this.uniforms.get(s);
|
|
@@ -617,7 +636,7 @@ const d = class d {
|
|
|
617
636
|
}
|
|
618
637
|
};
|
|
619
638
|
/** Built-in uniform names that are handled automatically */
|
|
620
|
-
|
|
639
|
+
r(d, "BUILT_INS", /* @__PURE__ */ new Set([
|
|
621
640
|
"u_resolution",
|
|
622
641
|
"u_time",
|
|
623
642
|
"u_delta",
|
|
@@ -627,7 +646,7 @@ i(d, "BUILT_INS", /* @__PURE__ */ new Set([
|
|
|
627
646
|
let v = d;
|
|
628
647
|
class b {
|
|
629
648
|
constructor() {
|
|
630
|
-
|
|
649
|
+
r(this, "hooks", /* @__PURE__ */ new Map());
|
|
631
650
|
}
|
|
632
651
|
id() {
|
|
633
652
|
return Math.random().toString(36).substring(2, 10);
|
|
@@ -643,8 +662,8 @@ class b {
|
|
|
643
662
|
}
|
|
644
663
|
/** Run all hooks with the given state */
|
|
645
664
|
run(t) {
|
|
646
|
-
for (const [e,
|
|
647
|
-
|
|
665
|
+
for (const [e, i] of this.hooks)
|
|
666
|
+
i(t) === !1 && this.remove(e);
|
|
648
667
|
}
|
|
649
668
|
destroy() {
|
|
650
669
|
this.hooks.clear();
|
|
@@ -652,27 +671,27 @@ class b {
|
|
|
652
671
|
}
|
|
653
672
|
class _ {
|
|
654
673
|
constructor(t, e) {
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
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);
|
|
669
688
|
}
|
|
670
689
|
/**
|
|
671
690
|
* Factory method to create and setup WebGL instance.
|
|
672
691
|
*/
|
|
673
692
|
static setup(t, e) {
|
|
674
|
-
const
|
|
675
|
-
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;
|
|
676
695
|
}
|
|
677
696
|
/**
|
|
678
697
|
* Initialize WebGL context.
|
|
@@ -689,9 +708,9 @@ class _ {
|
|
|
689
708
|
}, e = this.canvas.getContext("webgl2", t);
|
|
690
709
|
if (e)
|
|
691
710
|
return this._version = 2, e;
|
|
692
|
-
const
|
|
693
|
-
if (
|
|
694
|
-
return this._version = 1,
|
|
711
|
+
const i = this.canvas.getContext("webgl", t);
|
|
712
|
+
if (i)
|
|
713
|
+
return this._version = 1, i;
|
|
695
714
|
const s = new w("not_supported");
|
|
696
715
|
throw this.options.onError(s), s;
|
|
697
716
|
}
|
|
@@ -704,8 +723,8 @@ class _ {
|
|
|
704
723
|
/**
|
|
705
724
|
* Set viewport dimensions.
|
|
706
725
|
*/
|
|
707
|
-
viewport(t, e,
|
|
708
|
-
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;
|
|
709
728
|
}
|
|
710
729
|
/**
|
|
711
730
|
* Set the clock time
|
|
@@ -744,10 +763,10 @@ class _ {
|
|
|
744
763
|
shader(t, e) {
|
|
745
764
|
try {
|
|
746
765
|
this._program.compile(t, e), this._version = this._program.getVersion(), this._geometry.linkAttributes(this._program);
|
|
747
|
-
const
|
|
748
|
-
|
|
749
|
-
} catch (
|
|
750
|
-
|
|
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);
|
|
751
770
|
}
|
|
752
771
|
return this;
|
|
753
772
|
}
|
|
@@ -783,6 +802,12 @@ class _ {
|
|
|
783
802
|
getVersion() {
|
|
784
803
|
return this._version;
|
|
785
804
|
}
|
|
805
|
+
/**
|
|
806
|
+
* Get the clock instance
|
|
807
|
+
*/
|
|
808
|
+
getClock() {
|
|
809
|
+
return this._clock;
|
|
810
|
+
}
|
|
786
811
|
/**
|
|
787
812
|
* Cleanup all resources.
|
|
788
813
|
*/
|
|
@@ -824,7 +849,7 @@ void main() {
|
|
|
824
849
|
vec3 color = vec3(uv.x, uv.y, 0.5 + 0.5 * sin(u_time));
|
|
825
850
|
gl_FragColor = vec4(color, 1.0);
|
|
826
851
|
}
|
|
827
|
-
`,
|
|
852
|
+
`, E = `#version 300 es
|
|
828
853
|
|
|
829
854
|
in vec2 a_position;
|
|
830
855
|
in vec2 a_texcoord;
|
|
@@ -834,7 +859,7 @@ out vec2 v_texcoord;
|
|
|
834
859
|
void main() {
|
|
835
860
|
v_texcoord = a_texcoord;
|
|
836
861
|
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
837
|
-
}`,
|
|
862
|
+
}`, V = `#version 300 es
|
|
838
863
|
precision highp float;
|
|
839
864
|
|
|
840
865
|
uniform vec2 u_resolution;
|
|
@@ -849,17 +874,19 @@ void main() {
|
|
|
849
874
|
vec3 color = vec3(uv.x, uv.y, 0.5 + 0.5 * sin(u_time));
|
|
850
875
|
fragColor = vec4(color, 1.0);
|
|
851
876
|
}`;
|
|
852
|
-
class
|
|
877
|
+
class y {
|
|
853
878
|
constructor(t, e) {
|
|
854
879
|
/** Active event listeners */
|
|
855
|
-
|
|
880
|
+
r(this, "listeners", []);
|
|
856
881
|
/** HTML canvas element */
|
|
857
|
-
|
|
882
|
+
r(this, "canvasEl");
|
|
858
883
|
/** Resolved options */
|
|
859
|
-
|
|
884
|
+
r(this, "options");
|
|
860
885
|
/** WebGL engine */
|
|
861
|
-
|
|
862
|
-
|
|
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();
|
|
863
890
|
}
|
|
864
891
|
/**
|
|
865
892
|
* Sandbox - A lightweight WebGL wrapper for shader effects.
|
|
@@ -880,7 +907,7 @@ class E {
|
|
|
880
907
|
* });
|
|
881
908
|
*/
|
|
882
909
|
static create(t, e) {
|
|
883
|
-
return new
|
|
910
|
+
return new y(t, e);
|
|
884
911
|
}
|
|
885
912
|
resolveOptions(t) {
|
|
886
913
|
const e = {
|
|
@@ -889,12 +916,13 @@ class E {
|
|
|
889
916
|
autoplay: !0,
|
|
890
917
|
pauseWhenHidden: !0,
|
|
891
918
|
dpr: "auto",
|
|
919
|
+
fps: 0,
|
|
892
920
|
preserveDrawingBuffer: !1,
|
|
893
921
|
antialias: !0,
|
|
894
|
-
onError: (
|
|
922
|
+
onError: (i) => {
|
|
895
923
|
console.error(
|
|
896
924
|
"Oops!",
|
|
897
|
-
|
|
925
|
+
i,
|
|
898
926
|
`
|
|
899
927
|
You can handle errors programmatically by providing an onError callback to suppress this log and implement custom fallback behavior.`
|
|
900
928
|
);
|
|
@@ -905,13 +933,13 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
905
933
|
onAfterRender: null,
|
|
906
934
|
uniforms: {}
|
|
907
935
|
};
|
|
908
|
-
if (t != null && t.vertex && !(t != null && t.fragment)) {
|
|
909
|
-
const
|
|
910
|
-
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;
|
|
911
939
|
}
|
|
912
940
|
if (t != null && t.fragment && !(t != null && t.vertex)) {
|
|
913
|
-
const
|
|
914
|
-
e.fragment = t.fragment, e.vertex =
|
|
941
|
+
const i = a.detectVersion(t.fragment);
|
|
942
|
+
e.fragment = t.fragment, e.vertex = i === 2 ? E : g;
|
|
915
943
|
}
|
|
916
944
|
return { ...e, ...t };
|
|
917
945
|
}
|
|
@@ -922,7 +950,7 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
922
950
|
this.setViewport();
|
|
923
951
|
}),
|
|
924
952
|
// Canvas resize
|
|
925
|
-
l.on(this.
|
|
953
|
+
l.on(this.canvasEl, "resize", () => {
|
|
926
954
|
this.setViewport();
|
|
927
955
|
}),
|
|
928
956
|
// Visibility check on scroll
|
|
@@ -946,21 +974,21 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
946
974
|
this.listeners.forEach((t) => t()), this.listeners = [];
|
|
947
975
|
}
|
|
948
976
|
setViewport() {
|
|
949
|
-
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;
|
|
950
978
|
this.engine.viewport(
|
|
951
979
|
0,
|
|
952
980
|
0,
|
|
953
981
|
Math.max(1, Math.floor(e * t)),
|
|
954
|
-
Math.max(1, Math.floor(
|
|
982
|
+
Math.max(1, Math.floor(i * t))
|
|
955
983
|
);
|
|
956
984
|
}
|
|
957
985
|
isInViewport() {
|
|
958
|
-
const t = this.
|
|
986
|
+
const t = this.canvasEl.getBoundingClientRect();
|
|
959
987
|
return t.bottom >= 0 && t.right >= 0 && t.top <= (window.innerHeight || document.documentElement.clientHeight) && t.left <= (window.innerWidth || document.documentElement.clientWidth);
|
|
960
988
|
}
|
|
961
989
|
setMouse(t, e) {
|
|
962
|
-
const
|
|
963
|
-
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);
|
|
964
992
|
}
|
|
965
993
|
/**
|
|
966
994
|
* Set a single uniform value with type checking.
|
|
@@ -1001,7 +1029,7 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
1001
1029
|
* sandbox.setShader(vertexSource, fragmentSource);
|
|
1002
1030
|
*/
|
|
1003
1031
|
setShader(t, e) {
|
|
1004
|
-
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;
|
|
1005
1033
|
}
|
|
1006
1034
|
/**
|
|
1007
1035
|
* Update only fragment shader (uses default vertex).
|
|
@@ -1009,8 +1037,18 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
1009
1037
|
* sandbox.setFragment(fragmentSource);
|
|
1010
1038
|
*/
|
|
1011
1039
|
setFragment(t) {
|
|
1012
|
-
const
|
|
1013
|
-
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;
|
|
1014
1052
|
}
|
|
1015
1053
|
/**
|
|
1016
1054
|
* Add a runtime render hook.
|
|
@@ -1040,8 +1078,8 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
1040
1078
|
* Pause animation loop at specific time (in seconds).
|
|
1041
1079
|
*/
|
|
1042
1080
|
pauseAt(t) {
|
|
1043
|
-
const e = this.hook((
|
|
1044
|
-
|
|
1081
|
+
const e = this.hook((i) => {
|
|
1082
|
+
i.time >= t && (e(), this.pause());
|
|
1045
1083
|
}, "after");
|
|
1046
1084
|
return this;
|
|
1047
1085
|
}
|
|
@@ -1082,14 +1120,14 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
1082
1120
|
/**
|
|
1083
1121
|
* Get WebGL version using (1 or 2).
|
|
1084
1122
|
*/
|
|
1085
|
-
|
|
1123
|
+
get version() {
|
|
1086
1124
|
return this.engine.getVersion();
|
|
1087
1125
|
}
|
|
1088
1126
|
/**
|
|
1089
1127
|
* Get canvas element.
|
|
1090
1128
|
*/
|
|
1091
|
-
|
|
1092
|
-
return this.
|
|
1129
|
+
get canvas() {
|
|
1130
|
+
return this.canvasEl;
|
|
1093
1131
|
}
|
|
1094
1132
|
/**
|
|
1095
1133
|
* Destroy sandbox and release all resources.
|
|
@@ -1103,7 +1141,7 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
1103
1141
|
}
|
|
1104
1142
|
}
|
|
1105
1143
|
export {
|
|
1106
|
-
|
|
1144
|
+
y as Sandbox,
|
|
1107
1145
|
w as SandboxContextError,
|
|
1108
1146
|
u as SandboxError,
|
|
1109
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 {
|