├── logo.jpg
├── .gitignore
├── .travis.yml
├── .eslintrc.json
├── src
├── list.js
└── renderer.js
├── LICENSE
├── example
├── index.html
├── stats.min.js
├── src
│ └── app.js
└── bundle.js
├── package.json
├── dist
├── renderer.js
├── renderer.m.js
└── renderer.umd.js
└── README.md
/logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kutuluk/js13k-2d/HEAD/logo.jpg
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .vscode/
3 | dev/
4 | package-lock.json
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "node"
4 | script:
5 | - npm run build
6 | - npm test
7 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb-base",
3 | "plugins": ["import"],
4 | "env": {
5 | "browser": true,
6 | "es6": true
7 | },
8 | "rules": {
9 | "no-plusplus": "off",
10 | "no-param-reassign": "off",
11 | "no-bitwise": "off",
12 | "no-unused-expressions": "off"
13 | },
14 | "globals": {
15 | "Stats": true
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/list.js:
--------------------------------------------------------------------------------
1 | class Node {
2 | constructor(list, cargo, next) {
3 | this.l = list;
4 | this.c = cargo;
5 | this.n = next;
6 | this.p = null;
7 | }
8 |
9 | r() {
10 | if (this.p) {
11 | this.p.n = this.n;
12 | } else {
13 | this.l.h = this.n;
14 | }
15 | this.n && (this.n.p = this.p);
16 | }
17 | }
18 |
19 | export default class List {
20 | constructor() {
21 | this.h = null;
22 | }
23 |
24 | add(cargo) {
25 | const node = new Node(this, cargo, this.h);
26 | this.h && (this.h.p = node);
27 | this.h = node;
28 | return node;
29 | }
30 |
31 | i(fn) {
32 | let node = this.h;
33 | while (node) {
34 | fn(node.c);
35 | node = node.n;
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Evgeniy Pavlov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | js13k-2d example
7 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "js13k-2d",
3 | "description": "A 2kb webgl 2d sprite renderer, designed for Js13kGames",
4 | "keywords": [
5 | "webgl",
6 | "2d",
7 | "renderer",
8 | "js13k"
9 | ],
10 | "repository": "github:kutuluk/js13k-2d",
11 | "author": "Evgeniy Pavlov ",
12 | "license": "MIT",
13 | "version": "0.8.0",
14 | "source": "src/renderer.js",
15 | "main": "dist/renderer.js",
16 | "module": "dist/renderer.m.js",
17 | "files": [
18 | "src",
19 | "dist"
20 | ],
21 | "scripts": {
22 | "clean": "rimraf dist && mkdirp dist",
23 | "microbundle": "microbundle --compress true --sourcemap false --name Renderer",
24 | "example": "microbundle -i ./example/src/app.js -o ./example/bundle.js -f cjs --compress false --sourcemap false",
25 | "build": "npm-run-all --silent clean microbundle example",
26 | "lint": "eslint src",
27 | "release": "npm install && npm-run-all --silent lint build && npm publish"
28 | },
29 | "devDependencies": {
30 | "eslint": "^5.3.0",
31 | "eslint-config-airbnb-base": "^13.1.0",
32 | "eslint-plugin-import": "^2.8.0",
33 | "microbundle": "^0.6.0",
34 | "mkdirp": "^0.5.1",
35 | "npm-run-all": "^4.1.2",
36 | "rimraf": "^2.6.2"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/example/stats.min.js:
--------------------------------------------------------------------------------
1 | // stats.js - http://github.com/mrdoob/stats.js
2 | (function(f,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"===typeof define&&define.amd?define(e):f.Stats=e()})(this,function(){var f=function(){function e(a){c.appendChild(a.dom);return a}function u(a){for(var d=0;dg+1E3&&(r.update(1E3*a/(c-g),100),g=c,a=0,t)){var d=performance.memory;t.update(d.usedJSHeapSize/
4 | 1048576,d.jsHeapSizeLimit/1048576)}return c},update:function(){k=this.end()},domElement:c,setMode:u}};f.Panel=function(e,f,l){var c=Infinity,k=0,g=Math.round,a=g(window.devicePixelRatio||1),r=80*a,h=48*a,t=3*a,v=2*a,d=3*a,m=15*a,n=74*a,p=30*a,q=document.createElement("canvas");q.width=r;q.height=h;q.style.cssText="width:80px;height:48px";var b=q.getContext("2d");b.font="bold "+9*a+"px Helvetica,Arial,sans-serif";b.textBaseline="top";b.fillStyle=l;b.fillRect(0,0,r,h);b.fillStyle=f;b.fillText(e,t,v);
5 | b.fillRect(d,m,n,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d,m,n,p);return{dom:q,update:function(h,w){c=Math.min(c,h);k=Math.max(k,h);b.fillStyle=l;b.globalAlpha=1;b.fillRect(0,0,r,m);b.fillStyle=f;b.fillText(g(h)+" "+e+" ("+g(c)+"-"+g(k)+")",t,v);b.drawImage(q,d+a,m,n-a,p,d,m,n-a,p);b.fillRect(d+n-a,m,a,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d+n-a,m,a,g((1-h/w)*p))}}};return f});
6 |
--------------------------------------------------------------------------------
/dist/renderer.js:
--------------------------------------------------------------------------------
1 | var t=function(t,n,i){this.l=t,this.c=n,this.n=i,this.p=null};t.prototype.r=function(){this.p?this.p.n=this.n:this.l.h=this.n,this.n&&(this.n.p=this.p)};var n=function(){this.h=null};n.prototype.add=function(n){var i=new t(this,n,this.h);return this.h&&(this.h.p=i),this.h=i,i},n.prototype.i=function(t){for(var n=this.h;n;)t(n.c),n=n.n};var i={p:{t:0}},e=function(t){this.z=t,this.o=new n,this.t=new n};e.prototype.add=function(t){t.remove(),t.l=this,t.n=(1!==t.a||0===t.frame.p.a?this.t:this.o).add(t)};var r=function(t,n){var a=new e(0),o=[a],s=new ArrayBuffer(3407820),u=new Float32Array(s),c=new Uint32Array(s),h=r.Point,f=Object.assign({antialias:!1,alpha:!1},n),l=f.alpha?1:770,p=f.scale||1;delete f.scale;var v=t.getContext("webgl",f),d=v.getExtension("ANGLE_instanced_arrays"),g=function(t,n){var i=v.createShader(n);return v.shaderSource(i,t),v.compileShader(i),i},m=v.createProgram();v.attachShader(m,g("attribute vec2 g;\nattribute vec2 a;\nattribute vec2 t;\nattribute float r;\nattribute vec2 s;\nattribute vec4 u;\nattribute vec4 c;\nattribute float z;\nuniform mat4 m;\nvarying vec2 v;\nvarying vec4 i;\nvoid main(){\nv=u.xy+g*u.zw;\ni=c.abgr;\nvec2 p=(g-a)*s;\nfloat q=cos(r);\nfloat w=sin(r);\np=vec2(p.x*q-p.y*w,p.x*w+p.y*q);\np+=a+t;\ngl_Position=m*vec4(p,z,1);}",35633)),v.attachShader(m,g("precision mediump float;\nuniform sampler2D x;\nuniform float j;\nvarying vec2 v;\nvarying vec4 i;\nvoid main(){\nvec4 c=texture2D(x,v);\ngl_FragColor=c*i;\nif(j>0.0){\nif(c.a>>0,u[e]=t.l.z,E++}},C={gl:v,camera:{at:h(),to:h(),angle:0},background:function(t,n,i,e){v.clearColor(t,n,i,0===e?0:e||1)},layer:function(t){var n=o.find(function(n){return n.z===t});return n||(n=new e(t),o.push(n),o.sort(function(t,n){return n.z-t.z})),n},add:function(t){a.add(t)},texture:function(t,n,i,e){var r=t.width,a=t.height,o=v.createTexture();return v.bindTexture(3553,o),v.texParameteri(3553,10240,9728|+i),v.texParameteri(3553,10241,9728|+i|+e<<8|+e<<1),v.texImage2D(3553,0,6408,6408,5121,t),e&&v.generateMipmap(3553),{size:h(r,a),anchor:h(),uvs:[0,0,1,1],p:{a:0===n?0:n||1,t:o},frame:function(t,n,i){return{size:n,anchor:i||this.anchor,uvs:[t.x/r,t.y/a,n.x/r,n.y/a],p:this.p}}}},resize:F,render:function(){F();var t=C.camera,n=t.at,e=t.to,r=t.angle,a=n.x-b*e.x,s=n.y-w*e.y,u=Math.cos(r),c=Math.sin(r),h=2/b,f=-2/w,l=[u*h,c*f,0,0,-c*h,u*f,0,0,0,0,-1e-5,0,(n.x*(1-u)+n.y*c)*h-2*a/b-1,(n.y*(1-u)-n.x*c)*f+2*s/w+1,0,1];v.useProgram(m),v.enable(3042),v.enable(2929),v.uniformMatrix4fv(j,!1,l),v.viewport(0,0,b,w),v.clear(16640),z=i,P=!0,o.forEach(function(t){return t.o.i(_)}),L(),P=!1;for(var p=o.length-1;p>=0;p--)o[p].t.i(_);L()}};return F(),C};r.Point=function(){function t(t,n){if(!(this instanceof r.Point))return new r.Point(t,n);this.set(t,n)}return t.prototype.set=function(t,n){return this.x=t||0,this.y=n||(0!==n?this.x:0),this},t}(),r.Sprite=function(){function t(n,i){if(!(this instanceof t))return new t(n,i);Object.assign(this,{frame:n,visible:!0,position:r.Point(),rotation:0,scale:r.Point(1),tint:16777215,a:1,l:null,n:null},i)}var n={alpha:{configurable:!0}};return n.alpha.get=function(){return this.a},n.alpha.set=function(t){var n=t<1&&1===this.a||1===t&&this.a<1;this.a=t,n&&this.frame.p.a>0&&this.l&&this.l.add(this)},t.prototype.remove=function(){this.n&&this.n.r(),this.l=null,this.n=null},Object.defineProperties(t.prototype,n),t}(),module.exports=r;
2 |
--------------------------------------------------------------------------------
/dist/renderer.m.js:
--------------------------------------------------------------------------------
1 | var t=function(t,n,i){this.l=t,this.c=n,this.n=i,this.p=null};t.prototype.r=function(){this.p?this.p.n=this.n:this.l.h=this.n,this.n&&(this.n.p=this.p)};var n=function(){this.h=null};n.prototype.add=function(n){var i=new t(this,n,this.h);return this.h&&(this.h.p=i),this.h=i,i},n.prototype.i=function(t){for(var n=this.h;n;)t(n.c),n=n.n};var i={p:{t:0}},e=function(t){this.z=t,this.o=new n,this.t=new n};e.prototype.add=function(t){t.remove(),t.l=this,t.n=(1!==t.a||0===t.frame.p.a?this.t:this.o).add(t)};var r=function(t,n){var a=new e(0),o=[a],s=new ArrayBuffer(3407820),u=new Float32Array(s),c=new Uint32Array(s),h=r.Point,f=Object.assign({antialias:!1,alpha:!1},n),l=f.alpha?1:770,p=f.scale||1;delete f.scale;var v=t.getContext("webgl",f),d=v.getExtension("ANGLE_instanced_arrays"),g=function(t,n){var i=v.createShader(n);return v.shaderSource(i,t),v.compileShader(i),i},m=v.createProgram();v.attachShader(m,g("attribute vec2 g;\nattribute vec2 a;\nattribute vec2 t;\nattribute float r;\nattribute vec2 s;\nattribute vec4 u;\nattribute vec4 c;\nattribute float z;\nuniform mat4 m;\nvarying vec2 v;\nvarying vec4 i;\nvoid main(){\nv=u.xy+g*u.zw;\ni=c.abgr;\nvec2 p=(g-a)*s;\nfloat q=cos(r);\nfloat w=sin(r);\np=vec2(p.x*q-p.y*w,p.x*w+p.y*q);\np+=a+t;\ngl_Position=m*vec4(p,z,1);}",35633)),v.attachShader(m,g("precision mediump float;\nuniform sampler2D x;\nuniform float j;\nvarying vec2 v;\nvarying vec4 i;\nvoid main(){\nvec4 c=texture2D(x,v);\ngl_FragColor=c*i;\nif(j>0.0){\nif(c.a>>0,u[e]=t.l.z,E++}},C={gl:v,camera:{at:h(),to:h(),angle:0},background:function(t,n,i,e){v.clearColor(t,n,i,0===e?0:e||1)},layer:function(t){var n=o.find(function(n){return n.z===t});return n||(n=new e(t),o.push(n),o.sort(function(t,n){return n.z-t.z})),n},add:function(t){a.add(t)},texture:function(t,n,i,e){var r=t.width,a=t.height,o=v.createTexture();return v.bindTexture(3553,o),v.texParameteri(3553,10240,9728|+i),v.texParameteri(3553,10241,9728|+i|+e<<8|+e<<1),v.texImage2D(3553,0,6408,6408,5121,t),e&&v.generateMipmap(3553),{size:h(r,a),anchor:h(),uvs:[0,0,1,1],p:{a:0===n?0:n||1,t:o},frame:function(t,n,i){return{size:n,anchor:i||this.anchor,uvs:[t.x/r,t.y/a,n.x/r,n.y/a],p:this.p}}}},resize:F,render:function(){F();var t=C.camera,n=t.at,e=t.to,r=t.angle,a=n.x-b*e.x,s=n.y-w*e.y,u=Math.cos(r),c=Math.sin(r),h=2/b,f=-2/w,l=[u*h,c*f,0,0,-c*h,u*f,0,0,0,0,-1e-5,0,(n.x*(1-u)+n.y*c)*h-2*a/b-1,(n.y*(1-u)-n.x*c)*f+2*s/w+1,0,1];v.useProgram(m),v.enable(3042),v.enable(2929),v.uniformMatrix4fv(j,!1,l),v.viewport(0,0,b,w),v.clear(16640),z=i,P=!0,o.forEach(function(t){return t.o.i(_)}),L(),P=!1;for(var p=o.length-1;p>=0;p--)o[p].t.i(_);L()}};return F(),C};r.Point=function(){function t(t,n){if(!(this instanceof r.Point))return new r.Point(t,n);this.set(t,n)}return t.prototype.set=function(t,n){return this.x=t||0,this.y=n||(0!==n?this.x:0),this},t}(),r.Sprite=function(){function t(n,i){if(!(this instanceof t))return new t(n,i);Object.assign(this,{frame:n,visible:!0,position:r.Point(),rotation:0,scale:r.Point(1),tint:16777215,a:1,l:null,n:null},i)}var n={alpha:{configurable:!0}};return n.alpha.get=function(){return this.a},n.alpha.set=function(t){var n=t<1&&1===this.a||1===t&&this.a<1;this.a=t,n&&this.frame.p.a>0&&this.l&&this.l.add(this)},t.prototype.remove=function(){this.n&&this.n.r(),this.l=null,this.n=null},Object.defineProperties(t.prototype,n),t}();export default r;
2 |
--------------------------------------------------------------------------------
/dist/renderer.umd.js:
--------------------------------------------------------------------------------
1 | !function(t,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):t.Renderer=n()}(this,function(){var t=function(t,n,e){this.l=t,this.c=n,this.n=e,this.p=null};t.prototype.r=function(){this.p?this.p.n=this.n:this.l.h=this.n,this.n&&(this.n.p=this.p)};var n=function(){this.h=null};n.prototype.add=function(n){var e=new t(this,n,this.h);return this.h&&(this.h.p=e),this.h=e,e},n.prototype.i=function(t){for(var n=this.h;n;)t(n.c),n=n.n};var e={p:{t:0}},i=function(t){this.z=t,this.o=new n,this.t=new n};i.prototype.add=function(t){t.remove(),t.l=this,t.n=(1!==t.a||0===t.frame.p.a?this.t:this.o).add(t)};var r=function(t,n){var a=new i(0),o=[a],u=new ArrayBuffer(3407820),s=new Float32Array(u),c=new Uint32Array(u),h=r.Point,f=Object.assign({antialias:!1,alpha:!1},n),l=f.alpha?1:770,p=f.scale||1;delete f.scale;var v=t.getContext("webgl",f),d=v.getExtension("ANGLE_instanced_arrays"),g=function(t,n){var e=v.createShader(n);return v.shaderSource(e,t),v.compileShader(e),e},m=v.createProgram();v.attachShader(m,g("attribute vec2 g;\nattribute vec2 a;\nattribute vec2 t;\nattribute float r;\nattribute vec2 s;\nattribute vec4 u;\nattribute vec4 c;\nattribute float z;\nuniform mat4 m;\nvarying vec2 v;\nvarying vec4 i;\nvoid main(){\nv=u.xy+g*u.zw;\ni=c.abgr;\nvec2 p=(g-a)*s;\nfloat q=cos(r);\nfloat w=sin(r);\np=vec2(p.x*q-p.y*w,p.x*w+p.y*q);\np+=a+t;\ngl_Position=m*vec4(p,z,1);}",35633)),v.attachShader(m,g("precision mediump float;\nuniform sampler2D x;\nuniform float j;\nvarying vec2 v;\nvarying vec4 i;\nvoid main(){\nvec4 c=texture2D(x,v);\ngl_FragColor=c*i;\nif(j>0.0){\nif(c.a>>0,s[i]=t.l.z,E++}},C={gl:v,camera:{at:h(),to:h(),angle:0},background:function(t,n,e,i){v.clearColor(t,n,e,0===i?0:i||1)},layer:function(t){var n=o.find(function(n){return n.z===t});return n||(n=new i(t),o.push(n),o.sort(function(t,n){return n.z-t.z})),n},add:function(t){a.add(t)},texture:function(t,n,e,i){var r=t.width,a=t.height,o=v.createTexture();return v.bindTexture(3553,o),v.texParameteri(3553,10240,9728|+e),v.texParameteri(3553,10241,9728|+e|+i<<8|+i<<1),v.texImage2D(3553,0,6408,6408,5121,t),i&&v.generateMipmap(3553),{size:h(r,a),anchor:h(),uvs:[0,0,1,1],p:{a:0===n?0:n||1,t:o},frame:function(t,n,e){return{size:n,anchor:e||this.anchor,uvs:[t.x/r,t.y/a,n.x/r,n.y/a],p:this.p}}}},resize:F,render:function(){F();var t=C.camera,n=t.at,i=t.to,r=t.angle,a=n.x-b*i.x,u=n.y-w*i.y,s=Math.cos(r),c=Math.sin(r),h=2/b,f=-2/w,l=[s*h,c*f,0,0,-c*h,s*f,0,0,0,0,-1e-5,0,(n.x*(1-s)+n.y*c)*h-2*a/b-1,(n.y*(1-s)-n.x*c)*f+2*u/w+1,0,1];v.useProgram(m),v.enable(3042),v.enable(2929),v.uniformMatrix4fv(j,!1,l),v.viewport(0,0,b,w),v.clear(16640),z=e,P=!0,o.forEach(function(t){return t.o.i(_)}),L(),P=!1;for(var p=o.length-1;p>=0;p--)o[p].t.i(_);L()}};return F(),C};return r.Point=function(){function t(t,n){if(!(this instanceof r.Point))return new r.Point(t,n);this.set(t,n)}return t.prototype.set=function(t,n){return this.x=t||0,this.y=n||(0!==n?this.x:0),this},t}(),r.Sprite=function(){function t(n,e){if(!(this instanceof t))return new t(n,e);Object.assign(this,{frame:n,visible:!0,position:r.Point(),rotation:0,scale:r.Point(1),tint:16777215,a:1,l:null,n:null},e)}var n={alpha:{configurable:!0}};return n.alpha.get=function(){return this.a},n.alpha.set=function(t){var n=t<1&&1===this.a||1===t&&this.a<1;this.a=t,n&&this.frame.p.a>0&&this.l&&this.l.add(this)},t.prototype.remove=function(){this.n&&this.n.r(),this.l=null,this.n=null},Object.defineProperties(t.prototype,n),t}(),r});
2 |
--------------------------------------------------------------------------------
/example/src/app.js:
--------------------------------------------------------------------------------
1 | import Renderer from '../../dist/renderer.m';
2 |
3 | const { Point, Sprite } = Renderer;
4 |
5 | const stats = new Stats();
6 | document.body.appendChild(stats.dom);
7 |
8 | const view = document.getElementById('view');
9 | // const scene = Renderer(view, { alpha: true });
10 | const scene = Renderer(view);
11 | const { gl } = scene;
12 | console.log(gl);
13 |
14 | scene.background(1, 1, 1, 0);
15 |
16 | scene.camera.at.set(400, 300);
17 | scene.camera.to.set(0.5);
18 |
19 | const atlasImg = () => {
20 | const canvas = document.createElement('canvas');
21 | const size = 32;
22 | const half = size / 2;
23 | canvas.width = 128;
24 | canvas.height = 32;
25 | const ctx = canvas.getContext('2d');
26 |
27 | let offset = 0;
28 |
29 | ctx.lineWidth = size / 16;
30 | ctx.fillStyle = '#cccccc';
31 | ctx.strokeStyle = '#000000';
32 | ctx.beginPath();
33 |
34 | ctx.moveTo(offset + half, half);
35 | for (let angle = 0; angle < Math.PI * 2; angle += (Math.PI * 2) / 5) {
36 | ctx.lineTo(offset + half - Math.sin(angle) * half * 0.9, half - Math.cos(angle) * half * 0.9);
37 | }
38 |
39 | ctx.closePath();
40 | ctx.fill();
41 | ctx.stroke();
42 |
43 | offset += size;
44 |
45 | ctx.beginPath();
46 |
47 | ctx.moveTo(offset + 3, 3);
48 | ctx.lineTo(offset + size - 3, 3);
49 | ctx.lineTo(offset + size - 3, size - 3);
50 | ctx.lineTo(offset + 3, size - 3);
51 |
52 | ctx.closePath();
53 | ctx.fill();
54 | ctx.stroke();
55 |
56 | offset += size;
57 |
58 | ctx.beginPath();
59 |
60 | ctx.moveTo(offset + 3, 3);
61 | ctx.lineTo(offset + 29, 3);
62 | ctx.lineTo(offset + 29, 8);
63 | ctx.lineTo(offset + 8, 8);
64 | ctx.lineTo(offset + 8, 14);
65 | ctx.lineTo(offset + 20, 14);
66 | ctx.lineTo(offset + 20, 18);
67 | ctx.lineTo(offset + 8, 18);
68 | ctx.lineTo(offset + 8, 29);
69 | ctx.lineTo(offset + 3, 29);
70 |
71 | ctx.closePath();
72 | ctx.fill();
73 | ctx.stroke();
74 |
75 | return canvas;
76 | };
77 |
78 | const logoMask = () => {
79 | const canvas = document.createElement('canvas');
80 | canvas.width = 800;
81 | canvas.height = 600;
82 | const ctx = canvas.getContext('2d');
83 |
84 | ctx.fillStyle = '#ffffff';
85 | ctx.beginPath();
86 |
87 | ctx.moveTo(400, 300);
88 | for (let angle = 0; angle < Math.PI * 2; angle += (Math.PI * 2) / 5) {
89 | ctx.lineTo(400 - Math.sin(angle) * 250, 300 - Math.cos(angle) * 250);
90 | }
91 |
92 | ctx.closePath();
93 | ctx.fill();
94 |
95 | const { data } = ctx.getImageData(0, 0, 800, 600);
96 |
97 | return (x, y) => data[(y * 800 + x) * 4] > 0;
98 | };
99 |
100 | const atlas = scene.texture(atlasImg(), 0.5);
101 | atlas.anchor = Point(0.5);
102 |
103 | const bFrame = atlas.frame(Point(), Point(32));
104 | const qFrame = atlas.frame(Point(32, 0), Point(32));
105 | const fFrame = atlas.frame(Point(64, 0), Point(32));
106 |
107 | const frames = [atlas, bFrame, qFrame, fFrame];
108 |
109 | let len = 0;
110 |
111 | const sprs = [];
112 |
113 | const mask = logoMask();
114 |
115 | let cl = 0;
116 |
117 | const addSprite = (a) => {
118 | if (len % 250 === 0) {
119 | cl++;
120 | }
121 |
122 | const layer = scene.layer(cl);
123 |
124 | len += a;
125 | for (let i = 0; i < a; i++) {
126 | const sprite = Sprite(frames[i % 4]);
127 |
128 | let x = 0;
129 | let y = 0;
130 |
131 | while (!mask(x, y)) {
132 | x = ~~(800 * Math.random());
133 | y = ~~(600 * Math.random());
134 | }
135 |
136 | sprite.position.set(x, y);
137 | sprite.tint = Math.random() * 0xffffff;
138 | sprite.rotation = Math.random() * Math.PI * 2;
139 | // sprite.scale.set(0.5);
140 | // sprite.dr = (0.5 - Math.random()) * 0.1;
141 | // sprite.trans = !Math.round(Math.random());
142 | // sprite.alpha = !Math.round(Math.random()) ? 1 : 0.8;
143 | sprs.push(sprite);
144 | layer.add(sprite);
145 | }
146 | };
147 |
148 | const sprites = document.getElementById('info');
149 |
150 | const dbgRenderInfo = gl.getExtension('WEBGL_debug_renderer_info');
151 | const info = gl.getParameter(dbgRenderInfo ? dbgRenderInfo.UNMASKED_RENDERER_WEBGL : gl.VENDOR);
152 |
153 | let add = false;
154 |
155 | view.onmousedown = () => {
156 | add = true;
157 | };
158 | view.ontouchstart = () => {
159 | add = true;
160 | };
161 |
162 | view.onmouseup = () => {
163 | add = false;
164 | };
165 | view.ontouchend = () => {
166 | add = false;
167 | };
168 |
169 | const loop = () => {
170 | stats.begin();
171 |
172 | if (len < 3000 || add) addSprite(25);
173 |
174 | sprites.innerHTML = `Renderer: ${info}Sprites: ${len} (click to add)`;
175 |
176 | /*
177 | sprs.forEach((sprite) => {
178 | sprite.dr && (sprite.rotation += sprite.dr);
179 | if (sprite.trans && sprite.alpha > 0.8) {
180 | sprite.alpha -= 0.001;
181 | }
182 | });
183 | */
184 |
185 | scene.camera.angle += 0.005;
186 |
187 | scene.render();
188 | stats.end();
189 |
190 | requestAnimationFrame(loop);
191 | };
192 |
193 | loop();
194 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | > 
2 | >
3 | > A 2kb webgl 2d sprite renderer, designed for [Js13kGames](http://js13kgames.com).
4 |
5 | [](https://www.npmjs.com/package/js13k-2d)
6 |
7 | - **Tiny:** weighs about 2 kilobyte gzipped
8 | - **Extremely fast:** tens of thousands sprites onscreen at 60 fps
9 |
10 | ## Demo
11 |
12 | [Live examples](https://kutuluk.github.io/js13k-2d)
13 |
14 | ## Install
15 |
16 | ```sh
17 | $ npm install js13k-2d
18 | ```
19 |
20 | Then with a module bundler like [rollup](http://rollupjs.org/) or [webpack](https://webpack.js.org/), use as you would anything else:
21 |
22 | ```javascript
23 | // using ES6 modules
24 | import Renderer from 'js13k-2d';
25 |
26 | // using CommonJS modules
27 | var Renderer = require('js13k-2d');
28 | ```
29 |
30 | The [UMD](https://github.com/umdjs/umd) build is also available on [unpkg](https://unpkg.com):
31 |
32 | ```html
33 |
34 | ```
35 |
36 | You can find the library on `window.Renderer`.
37 |
38 | ## Usage
39 |
40 | ```javascript
41 | // Import the library
42 | import Renderer from 'js13k-2d';
43 |
44 | // Extract classes
45 | const { Point, Sprite } = Renderer;
46 |
47 | // Get canvas element, where the scene will be rendered to.
48 | const view = document.getElementById('view');
49 |
50 | // Create a scene
51 | const scene = Renderer(view);
52 |
53 | // Set background color
54 | scene.background(1, 1, 1);
55 |
56 | // Create a texture
57 | const atlas = scene.texture(image);
58 |
59 | // Create a frame
60 | const frame = atlas.frame(Point(), Point(32));
61 |
62 | // Create a sprite
63 | const sprite = Sprite(frame);
64 |
65 | // Add a sprite to the scene
66 | scene.add(sprite);
67 |
68 | const loop = () => {
69 | // Get the actual canvas size
70 | scene.resize();
71 | const { width, height } = view; // or scene.gl.canvas
72 |
73 | // Change sprite position
74 | sprite.position.set(Math.random() * width, Math.random() * height);
75 |
76 | // Render a scene
77 | scene.render();
78 |
79 | requestAnimationFrame(loop);
80 | };
81 |
82 | loop();
83 | ```
84 |
85 | > For a better understanding of how to use the library, read along or see example folder and have a look at the [live examples](https://kutuluk.github.io/js13k-2d).
86 |
87 | ## API (in progress)
88 |
89 | **This library is under development and should be considered as an unstable. There are no guarantees regarding API stability until the release 1.0.**
90 |
91 | ## Renderer.Point
92 |
93 | The Point object represents a location in a two-dimensional coordinate system, where x represents the horizontal axis and y represents the vertical axis. The class provides the most minimal functionality.
94 |
95 | ### `new Renderer.Point(x, y)`
96 |
97 | Creates a point with a `x` and `y` position. If `y` is omitted, both `x` and `y` will be set to `x` (0 by default). The presence of the keyword `new` is optional, so it is recommended to omit it for size reduction.
98 |
99 | ##### Properties
100 |
101 | #### `x: number`
102 |
103 | Position of the point on the x axis.
104 |
105 | #### `y: number`
106 |
107 | Position of the point on the y axis.
108 |
109 | ##### Methods
110 |
111 | ### `set(x, y): this`
112 |
113 | Sets the point to a new `x` and `y` position. If `y` is omitted, both `x` and `y` will be set to `x` (0 by default).
114 |
115 | ##### Tips
116 |
117 | For a smaller size reduction, you can use this class as the base for your vector class:
118 |
119 | ```javascript
120 | class Vector extends Renderer.Point {
121 | clone() {
122 | return new Vector(this.x, this.y);
123 | }
124 |
125 | copy(vec) {
126 | this.x = vec.x;
127 | this.y = vec.y;
128 | return this;
129 | }
130 |
131 | add(vec) {
132 | this.x += vec.x;
133 | this.y += vec.y;
134 | return this;
135 | }
136 |
137 | cross(vec) {
138 | return this.x * vec.y - this.y * vec.x;
139 | }
140 |
141 | dot(vec) {
142 | return this.x * vec.x + this.y * vec.y;
143 | }
144 |
145 | // etc...
146 | }
147 | ```
148 |
149 | And even override Renderer.Point. **Note**: you need to do this before calling Renderer.
150 |
151 | ```javascript
152 | Renderer.Point = Vector;
153 |
154 | // then
155 | const view = document.getElementById('view');
156 | const scene = Renderer(view);
157 |
158 | console.log(new Renderer.Point() instanceof Vector); // true
159 | console.log(scene.camera.at instanceof Vector); // true
160 | console.log(Renderer.Sprite(frame).position instanceof Vector); // true
161 | // etc
162 | ```
163 |
164 | ## Renderer(canvas, options)
165 |
166 | Returns an Renderer instance.
167 |
168 | ##### Parameters
169 |
170 | - `canvas` - The element where the scene will be rendered to. The provided element has to be `