littlejsengine 1.8.9 → 1.9.0

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.
Files changed (45) hide show
  1. package/README.md +17 -17
  2. package/build/littlejs.d.ts +312 -250
  3. package/build/littlejs.esm.js +633 -599
  4. package/build/littlejs.esm.min.js +1 -1
  5. package/build/littlejs.js +632 -599
  6. package/build/littlejs.min.js +1 -1
  7. package/build/littlejs.release.js +567 -535
  8. package/examples/breakout/game.js +4 -4
  9. package/examples/breakout/index.html +3 -3
  10. package/examples/breakoutTutorial/index.html +2 -2
  11. package/examples/electron/game.js +1 -1
  12. package/examples/electron/index.html +2 -2
  13. package/examples/favicon.png +0 -0
  14. package/examples/js13k/index.html +13 -13
  15. package/examples/module/game.js +1 -1
  16. package/examples/module/index.html +1 -1
  17. package/examples/particles/index.html +1 -1
  18. package/examples/platformer/game.js +6 -6
  19. package/examples/platformer/gameCharacter.js +293 -0
  20. package/examples/platformer/gameEffects.js +8 -5
  21. package/examples/platformer/gameObjects.js +13 -13
  22. package/examples/platformer/gamePlayer.js +9 -293
  23. package/examples/platformer/index.html +7 -6
  24. package/examples/puzzle/game.js +3 -2
  25. package/examples/puzzle/index.html +2 -2
  26. package/examples/starter/game.js +2 -2
  27. package/examples/starter/index.html +13 -13
  28. package/examples/stress/index.html +34 -26
  29. package/examples/typescript/index.html +1 -1
  30. package/package.json +1 -1
  31. package/src/engine.js +28 -28
  32. package/src/engineAudio.js +57 -57
  33. package/src/engineDebug.js +66 -65
  34. package/src/engineDraw.js +47 -56
  35. package/src/engineExport.js +1 -0
  36. package/src/engineInput.js +57 -40
  37. package/src/engineMedals.js +32 -29
  38. package/src/engineObject.js +41 -26
  39. package/src/engineParticles.js +98 -72
  40. package/src/engineRelease.js +1 -1
  41. package/src/engineSettings.js +22 -22
  42. package/src/engineTileLayer.js +34 -33
  43. package/src/engineUtilities.js +44 -44
  44. package/src/engineWebGL.js +105 -126
  45. package/src/jsconfig.json +10 -0
@@ -44,15 +44,15 @@ function sign(value) { return Math.sign(value); }
44
44
 
45
45
  /** Returns first parm modulo the second param, but adjusted so negative numbers work as expected
46
46
  * @param {Number} dividend
47
- * @param {Number} [divisor=1]
47
+ * @param {Number} [divisor]
48
48
  * @return {Number}
49
49
  * @memberof Utilities */
50
50
  function mod(dividend, divisor=1) { return ((dividend % divisor) + divisor) % divisor; }
51
51
 
52
52
  /** Clamps the value beween max and min
53
53
  * @param {Number} value
54
- * @param {Number} [min=0]
55
- * @param {Number} [max=1]
54
+ * @param {Number} [min]
55
+ * @param {Number} [max]
56
56
  * @return {Number}
57
57
  * @memberof Utilities */
58
58
  function clamp(value, min=0, max=1) { return value < min ? min : value > max ? max : value; }
@@ -77,7 +77,7 @@ function lerp(percent, valueA, valueB) { return valueA + clamp(percent) * (value
77
77
  /** Returns signed wrapped distance between the two values passed in
78
78
  * @param {Number} valueA
79
79
  * @param {Number} valueB
80
- * @param {Number} [wrapSize=1]
80
+ * @param {Number} [wrapSize]
81
81
  * @returns {Number}
82
82
  * @memberof Utilities */
83
83
  function distanceWrap(valueA, valueB, wrapSize=1)
@@ -87,7 +87,7 @@ function distanceWrap(valueA, valueB, wrapSize=1)
87
87
  * @param {Number} percent
88
88
  * @param {Number} valueA
89
89
  * @param {Number} valueB
90
- * @param {Number} [wrapSize=1]
90
+ * @param {Number} [wrapSize]
91
91
  * @returns {Number}
92
92
  * @memberof Utilities */
93
93
  function lerpWrap(percent, valueA, valueB, wrapSize=1)
@@ -98,7 +98,7 @@ function lerpWrap(percent, valueA, valueB, wrapSize=1)
98
98
  * @param {Number} angleB
99
99
  * @returns {Number}
100
100
  * @memberof Utilities */
101
- function distanceAngle(angleA, angleB) { distanceWrap(angleA, angleB, 2*PI); }
101
+ function distanceAngle(angleA, angleB) { return distanceWrap(angleA, angleB, 2*PI); }
102
102
 
103
103
  /** Linearly interpolates between the angles passed in with wrappping
104
104
  * @param {Number} percent
@@ -134,10 +134,10 @@ function isOverlapping(pointA, sizeA, pointB, sizeB)
134
134
  }
135
135
 
136
136
  /** Returns an oscillating wave between 0 and amplitude with frequency of 1 Hz by default
137
- * @param {Number} [frequency=1] - Frequency of the wave in Hz
138
- * @param {Number} [amplitude=1] - Amplitude (max height) of the wave
139
- * @param {Number} [t=time] - Value to use for time of the wave
140
- * @return {Number} - Value waving between 0 and amplitude
137
+ * @param {Number} [frequency] - Frequency of the wave in Hz
138
+ * @param {Number} [amplitude] - Amplitude (max height) of the wave
139
+ * @param {Number} [t=time] - Value to use for time of the wave
140
+ * @return {Number} - Value waving between 0 and amplitude
141
141
  * @memberof Utilities */
142
142
  function wave(frequency=1, amplitude=1, t=time)
143
143
  { return amplitude/2 * (1 - Math.cos(t*frequency*2*PI)); }
@@ -154,15 +154,15 @@ function formatTime(t) { return (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0); }
154
154
  * @namespace Random */
155
155
 
156
156
  /** Returns a random value between the two values passed in
157
- * @param {Number} [valueA=1]
158
- * @param {Number} [valueB=0]
157
+ * @param {Number} [valueA]
158
+ * @param {Number} [valueB]
159
159
  * @return {Number}
160
160
  * @memberof Random */
161
161
  function rand(valueA=1, valueB=0) { return valueB + Math.random() * (valueA-valueB); }
162
162
 
163
163
  /** Returns a floored random value the two values passed in
164
164
  * @param {Number} valueA
165
- * @param {Number} [valueB=0]
165
+ * @param {Number} [valueB]
166
166
  * @return {Number}
167
167
  * @memberof Random */
168
168
  function randInt(valueA, valueB=0) { return Math.floor(rand(valueA,valueB)); }
@@ -173,14 +173,14 @@ function randInt(valueA, valueB=0) { return Math.floor(rand(valueA,valueB)); }
173
173
  function randSign() { return randInt(2) * 2 - 1; }
174
174
 
175
175
  /** Returns a random Vector2 with the passed in length
176
- * @param {Number} [length=1]
176
+ * @param {Number} [length]
177
177
  * @return {Vector2}
178
178
  * @memberof Random */
179
179
  function randVector(length=1) { return new Vector2().setAngle(rand(2*PI), length); }
180
180
 
181
181
  /** Returns a random Vector2 within a circular shape
182
- * @param {Number} [radius=1]
183
- * @param {Number} [minRadius=0]
182
+ * @param {Number} [radius]
183
+ * @param {Number} [minRadius]
184
184
  * @return {Vector2}
185
185
  * @memberof Random */
186
186
  function randInCircle(radius=1, minRadius=0)
@@ -192,7 +192,7 @@ function randInCircle(radius=1, minRadius=0)
192
192
  * @param {Boolean} [linear]
193
193
  * @return {Color}
194
194
  * @memberof Random */
195
- function randColor(colorA=new Color, colorB=new Color(0,0,0,1), linear)
195
+ function randColor(colorA=new Color, colorB=new Color(0,0,0,1), linear=false)
196
196
  {
197
197
  return linear ? colorA.lerp(colorB, rand()) :
198
198
  new Color(rand(colorA.r,colorB.r), rand(colorA.g,colorB.g), rand(colorA.b,colorB.b), rand(colorA.a,colorB.a));
@@ -221,8 +221,8 @@ class RandomGenerator
221
221
  }
222
222
 
223
223
  /** Returns a seeded random value between the two values passed in
224
- * @param {Number} [valueA=1]
225
- * @param {Number} [valueB=0]
224
+ * @param {Number} [valueA]
225
+ * @param {Number} [valueB]
226
226
  * @return {Number} */
227
227
  float(valueA=1, valueB=0)
228
228
  {
@@ -235,7 +235,7 @@ class RandomGenerator
235
235
 
236
236
  /** Returns a floored seeded random value the two values passed in
237
237
  * @param {Number} valueA
238
- * @param {Number} [valueB=0]
238
+ * @param {Number} [valueB]
239
239
  * @return {Number} */
240
240
  int(valueA, valueB=0) { return Math.floor(this.float(valueA, valueB)); }
241
241
 
@@ -248,8 +248,8 @@ class RandomGenerator
248
248
 
249
249
  /**
250
250
  * Create a 2d vector, can take another Vector2 to copy, 2 scalars, or 1 scalar
251
- * @param {(Number|Vector2)} [x=0]
252
- * @param {Number} [y=0]
251
+ * @param {(Number|Vector2)} [x]
252
+ * @param {Number} [y]
253
253
  * @return {Vector2}
254
254
  * @example
255
255
  * let a = vec2(0, 1); // vector with coordinates (0, 1)
@@ -259,15 +259,15 @@ class RandomGenerator
259
259
  * @memberof Utilities
260
260
  */
261
261
  function vec2(x=0, y)
262
- { return x.x == undefined ? new Vector2(x, y == undefined? x : y) : new Vector2(x.x, x.y); }
262
+ { return typeof x === 'number'? new Vector2(x, y == undefined? x : y) : new Vector2(x.x, x.y); }
263
263
 
264
264
  /**
265
265
  * Check if object is a valid Vector2
266
- * @param {Vector2} v
266
+ * @param {any} v
267
267
  * @return {Boolean}
268
268
  * @memberof Utilities
269
269
  */
270
- function isVector2(v) { return !isNaN(v.x) && !isNaN(v.y); }
270
+ function isVector2(v) { return typeof v === 'object' && typeof v.x === 'number' && typeof v.y === 'number'; }
271
271
 
272
272
  /**
273
273
  * 2D Vector object with vector math library
@@ -281,8 +281,8 @@ function isVector2(v) { return !isNaN(v.x) && !isNaN(v.y); }
281
281
  class Vector2
282
282
  {
283
283
  /** Create a 2D vector with the x and y passed in, can also be created with vec2()
284
- * @param {Number} [x=0] - X axis location
285
- * @param {Number} [y=0] - Y axis location */
284
+ * @param {Number} [x] - X axis location
285
+ * @param {Number} [y] - Y axis location */
286
286
  constructor(x=0, y=0)
287
287
  {
288
288
  /** @property {Number} - X axis location */
@@ -339,12 +339,12 @@ class Vector2
339
339
  distanceSquared(v) { return (this.x - v.x)**2 + (this.y - v.y)**2; }
340
340
 
341
341
  /** Returns a new vector in same direction as this one with the length passed in
342
- * @param {Number} [length=1]
342
+ * @param {Number} [length]
343
343
  * @return {Vector2} */
344
344
  normalize(length=1) { const l = this.length(); return l ? this.scale(length/l) : new Vector2(0, length); }
345
345
 
346
346
  /** Returns a new vector clamped to length passed in
347
- * @param {Number} [length=1]
347
+ * @param {Number} [length]
348
348
  * @return {Vector2} */
349
349
  clampLength(length=1) { const l = this.length(); return l > length ? this.scale(length/l) : this; }
350
350
 
@@ -363,8 +363,8 @@ class Vector2
363
363
  angle() { return Math.atan2(this.x, this.y); }
364
364
 
365
365
  /** Sets this vector with angle and length passed in
366
- * @param {Number} [angle=0]
367
- * @param {Number} [length=1]
366
+ * @param {Number} [angle]
367
+ * @param {Number} [length]
368
368
  * @return {Vector2} */
369
369
  setAngle(angle=0, length=1)
370
370
  { this.x = length*Math.sin(angle); this.y = length*Math.cos(angle); return this; }
@@ -449,10 +449,10 @@ function hsl(h, s, l, a) { return new Color().setHSLA(h, s, l, a); }
449
449
  class Color
450
450
  {
451
451
  /** Create a color with the rgba components passed in, white by default
452
- * @param {Number} [r=1] - red
453
- * @param {Number} [g=1] - green
454
- * @param {Number} [b=1] - blue
455
- * @param {Number} [a=1] - alpha*/
452
+ * @param {Number} [r] - red
453
+ * @param {Number} [g] - green
454
+ * @param {Number} [b] - blue
455
+ * @param {Number} [a] - alpha*/
456
456
  constructor(r=1, g=1, b=1, a=1)
457
457
  {
458
458
  /** @property {Number} - Red */
@@ -507,10 +507,10 @@ class Color
507
507
  lerp(c, percent) { return this.add(c.subtract(this).scale(clamp(percent))); }
508
508
 
509
509
  /** Sets this color given a hue, saturation, lightness, and alpha
510
- * @param {Number} [h=0] - hue
511
- * @param {Number} [s=0] - saturation
512
- * @param {Number} [l=1] - lightness
513
- * @param {Number} [a=1] - alpha
510
+ * @param {Number} [h] - hue
511
+ * @param {Number} [s] - saturation
512
+ * @param {Number} [l] - lightness
513
+ * @param {Number} [a] - alpha
514
514
  * @return {Color} */
515
515
  setHSLA(h=0, s=0, l=1, a=1)
516
516
  {
@@ -556,8 +556,8 @@ class Color
556
556
  }
557
557
 
558
558
  /** Returns a new color that has each component randomly adjusted
559
- * @param {Number} [amount=.05]
560
- * @param {Number} [alphaAmount=0]
559
+ * @param {Number} [amount]
560
+ * @param {Number} [alphaAmount]
561
561
  * @return {Color} */
562
562
  mutate(amount=.05, alphaAmount=0)
563
563
  {
@@ -571,9 +571,9 @@ class Color
571
571
  }
572
572
 
573
573
  /** Returns this color expressed as a hex color code
574
- * @param {Boolean} [useAlpha=1] - if alpha should be included in result
574
+ * @param {Boolean} [useAlpha] - if alpha should be included in result
575
575
  * @return {String} */
576
- toString(useAlpha = 1)
576
+ toString(useAlpha = true)
577
577
  {
578
578
  const toHex = (c)=> ((c=c*255|0)<16 ? '0' : '') + c.toString(16);
579
579
  return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b) + (useAlpha ? toHex(this.a) : '');
@@ -622,7 +622,7 @@ class Timer
622
622
  constructor(timeLeft) { this.time = timeLeft == undefined ? undefined : time + timeLeft; this.setTime = timeLeft; }
623
623
 
624
624
  /** Set the timer with seconds passed in
625
- * @param {Number} [timeLeft=0] - How much time left before the timer is elapsed in seconds */
625
+ * @param {Number} [timeLeft] - How much time left before the timer is elapsed in seconds */
626
626
  set(timeLeft=0) { this.time = time + timeLeft; this.setTime = timeLeft; }
627
627
 
628
628
  /** Unset the timer */
@@ -1,4 +1,4 @@
1
- /**
1
+ /**
2
2
  * LittleJS WebGL Interface
3
3
  * - All webgl used by the engine is wrapped up here
4
4
  * - For normal stuff you won't need to see or call anything in this file
@@ -17,13 +17,13 @@
17
17
  * @memberof WebGL */
18
18
  let glCanvas;
19
19
 
20
- /** 2d context for glCanvas
21
- * @type {WebGLRenderingContext}
20
+ /** 2d context for glCanvas
21
+ * @type {WebGL2RenderingContext}
22
22
  * @memberof WebGL */
23
23
  let glContext;
24
24
 
25
25
  // WebGL internal variables not exposed to documentation
26
- let glActiveTexture, glShader, glArrayBuffer, glVertexData, glPositionData, glColorData, glBatchCount, glBatchAdditive, glAdditive;
26
+ let glShader, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glInstanceCount, glAdditive, glBatchAdditive;
27
27
 
28
28
  ///////////////////////////////////////////////////////////////////////////////
29
29
 
@@ -39,73 +39,89 @@ function glInit()
39
39
 
40
40
  // setup vertex and fragment shaders
41
41
  glShader = glCreateProgram(
42
- '#version 300 es\n' + // specify GLSL ES version
43
- 'precision highp float;'+ // use highp for better accuracy
44
- 'uniform mat4 m;'+ // transform matrix
45
- 'in vec4 p,c,a;'+ // position, uv, color, additiveColor
46
- 'out vec4 v,d,e;'+ // return uv, color, additiveColor
47
- 'void main(){'+ // shader entry point
48
- 'gl_Position=m*vec4(p.xy,1,1);'+ // transform position
49
- 'v=p;d=c;e=a;'+ // pass stuff to fragment shader
50
- '}' // end of shader
42
+ '#version 300 es\n' + // specify GLSL ES version
43
+ 'precision highp float;'+ // use highp for better accuracy
44
+ 'uniform mat4 m;'+ // transform matrix
45
+ 'in vec2 g;'+ // geometry
46
+ 'in vec4 p,u,c,a;'+ // position/size, uvs, color, additiveColor
47
+ 'in float r;'+ // rotation
48
+ 'out vec2 v;'+ // return uv, color, additiveColor
49
+ 'out vec4 d,e;'+ // return uv, color, additiveColor
50
+ 'void main(){'+ // shader entry point
51
+ 'vec2 s=(g-.5)*p.zw;'+ // get size offset
52
+ 'gl_Position=m*vec4(p.xy+s*cos(r)-vec2(-s.y,s)*sin(r),1,1);'+ // transform position
53
+ 'v=mix(u.xw,u.zy,g);'+ // pass uv to fragment shader
54
+ 'd=c;e=a;'+ // pass colors to fragment shader
55
+ '}' // end of shader
51
56
  ,
52
- '#version 300 es\n' + // specify GLSL ES version
53
- 'precision highp float;'+ // use highp for better accuracy
54
- 'in vec4 v,d,e;'+ // position, uv, color, additiveColor
55
- 'uniform sampler2D s;'+ // texture
56
- 'out vec4 c;'+ // out color
57
- 'void main(){'+ // shader entry point
58
- 'c=texture(s,v.zw)*d+e;'+ // modulate texture by color plus additive
59
- '}' // end of shader
57
+ '#version 300 es\n' + // specify GLSL ES version
58
+ 'precision highp float;'+ // use highp for better accuracy
59
+ 'in vec2 v;'+ // uv
60
+ 'in vec4 d,e;'+ // color, additiveColor
61
+ 'uniform sampler2D s;'+ // texture
62
+ 'out vec4 c;'+ // out color
63
+ 'void main(){'+ // shader entry point
64
+ 'c=texture(s,v)*d+e;'+ // modulate texture by color plus additive
65
+ '}' // end of shader
60
66
  );
61
67
 
62
68
  // init buffers
63
- glVertexData = new ArrayBuffer(gl_VERTEX_BUFFER_SIZE);
64
- glPositionData = new Float32Array(glVertexData);
65
- glColorData = new Uint32Array(glVertexData);
69
+ const glInstanceData = new ArrayBuffer(gl_INSTANCE_BUFFER_SIZE);
70
+ glPositionData = new Float32Array(glInstanceData);
71
+ glColorData = new Uint32Array(glInstanceData);
66
72
  glArrayBuffer = glContext.createBuffer();
67
- glBatchCount = 0;
73
+ glGeometryBuffer = glContext.createBuffer();
74
+
75
+ // create the geometry buffer, triangle strip square
76
+ const geometry = new Float32Array([glInstanceCount=0,0,1,0,0,1,1,1]);
77
+ glContext.bindBuffer(gl_ARRAY_BUFFER, glGeometryBuffer);
78
+ glContext.bufferData(gl_ARRAY_BUFFER, geometry, gl_STATIC_DRAW);
68
79
  }
69
80
 
70
81
  // Setup render each frame, called automatically by engine
71
82
  function glPreRender()
72
83
  {
73
84
  // clear and set to same size as main canvas
74
- glContext.viewport(0, 0, glCanvas.width = mainCanvas.width, glCanvas.height = mainCanvas.height);
85
+ glContext.viewport(0, 0, glCanvas.width=mainCanvas.width, glCanvas.height=mainCanvas.height);
75
86
  glContext.clear(gl_COLOR_BUFFER_BIT);
76
87
 
77
88
  // set up the shader
78
89
  glContext.useProgram(glShader);
79
90
  glContext.activeTexture(gl_TEXTURE0);
80
91
  glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = textureInfos[0].glTexture);
81
- glContext.bindBuffer(gl_ARRAY_BUFFER, glArrayBuffer);
82
- glContext.bufferData(gl_ARRAY_BUFFER, gl_VERTEX_BUFFER_SIZE, gl_DYNAMIC_DRAW);
83
- glAdditive = 0;
84
-
92
+
85
93
  // set vertex attributes
86
- let offset = 0;
87
- const initVertexAttribArray = (name, type, typeSize, size, normalize=0)=>
94
+ let offset = glAdditive = glBatchAdditive = 0;
95
+ let initVertexAttribArray = (name, type, typeSize, size)=>
88
96
  {
89
97
  const location = glContext.getAttribLocation(glShader, name);
98
+ const stride = typeSize && gl_INSTANCE_BYTE_STRIDE; // only if not geometry
99
+ const divisor = typeSize && 1; // only if not geometry
100
+ const normalize = typeSize==1; // only if color
90
101
  glContext.enableVertexAttribArray(location);
91
- glContext.vertexAttribPointer(location, size, type, normalize, gl_VERTEX_BYTE_STRIDE, offset);
102
+ glContext.vertexAttribPointer(location, size, type, normalize, stride, offset);
103
+ glContext.vertexAttribDivisor(location, divisor);
92
104
  offset += size*typeSize;
93
105
  }
94
- initVertexAttribArray('p', gl_FLOAT, 4, 4); // position & texture
95
- initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4, 1); // color
96
- initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4, 1); // additiveColor
106
+ glContext.bindBuffer(gl_ARRAY_BUFFER, glGeometryBuffer);
107
+ initVertexAttribArray('g', gl_FLOAT, 0, 2); // geometry
108
+ glContext.bindBuffer(gl_ARRAY_BUFFER, glArrayBuffer);
109
+ glContext.bufferData(gl_ARRAY_BUFFER, gl_INSTANCE_BUFFER_SIZE, gl_DYNAMIC_DRAW);
110
+ initVertexAttribArray('p', gl_FLOAT, 4, 4); // position & size
111
+ initVertexAttribArray('u', gl_FLOAT, 4, 4); // texture coords
112
+ initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4); // color
113
+ initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4); // additiveColor
114
+ initVertexAttribArray('r', gl_FLOAT, 4, 1); // rotation
97
115
 
98
116
  // build the transform matrix
99
- const sx = 2 * cameraScale / mainCanvas.width;
100
- const sy = 2 * cameraScale / mainCanvas.height;
101
- const cx = -1 - sx*cameraPos.x;
102
- const cy = -1 - sy*cameraPos.y;
103
- glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader, 'm'), 0,
117
+ const s = vec2(2*cameraScale).divide(mainCanvasSize);
118
+ const p = vec2(-1).subtract(cameraPos.multiply(s));
119
+ glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader, 'm'), false,
104
120
  new Float32Array([
105
- sx, 0, 0, 0,
106
- 0, sy, 0, 0,
107
- 1, 1, -1, 1,
108
- cx, cy, 0, 0
121
+ s.x, 0, 0, 0,
122
+ 0, s.y, 0, 0,
123
+ 1, 1, 1, 1,
124
+ p.x, p.y, 0, 0
109
125
  ])
110
126
  );
111
127
  }
@@ -126,7 +142,7 @@ function glSetTexture(texture)
126
142
 
127
143
  /** Compile WebGL shader of the given type, will throw errors if in debug mode
128
144
  * @param {String} source
129
- * @param type
145
+ * @param {Number} type
130
146
  * @return {WebGLShader}
131
147
  * @memberof WebGL */
132
148
  function glCompileShader(source, type)
@@ -143,8 +159,8 @@ function glCompileShader(source, type)
143
159
  }
144
160
 
145
161
  /** Create WebGL program with given shaders
146
- * @param {WebGLShader} vsSource
147
- * @param {WebGLShader} fsSource
162
+ * @param {String} vsSource
163
+ * @param {String} fsSource
148
164
  * @return {WebGLProgram}
149
165
  * @memberof WebGL */
150
166
  function glCreateProgram(vsSource, fsSource)
@@ -162,7 +178,7 @@ function glCreateProgram(vsSource, fsSource)
162
178
  }
163
179
 
164
180
  /** Create WebGL texture from an image and init the texture settings
165
- * @param {Image} image
181
+ * @param {HTMLImageElement} image
166
182
  * @return {WebGLTexture}
167
183
  * @memberof WebGL */
168
184
  function glCreateTexture(image)
@@ -172,7 +188,7 @@ function glCreateTexture(image)
172
188
  glContext.bindTexture(gl_TEXTURE_2D, texture);
173
189
  if (image)
174
190
  glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, image);
175
-
191
+
176
192
  // use point filtering for pixelated rendering
177
193
  const filter = canvasPixelated ? gl_NEAREST : gl_LINEAR;
178
194
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, filter);
@@ -187,29 +203,31 @@ function glCreateTexture(image)
187
203
  * @memberof WebGL */
188
204
  function glFlush()
189
205
  {
190
- if (!glBatchCount) return;
206
+ if (!glInstanceCount) return;
191
207
 
192
208
  const destBlend = glBatchAdditive ? gl_ONE : gl_ONE_MINUS_SRC_ALPHA;
193
209
  glContext.blendFuncSeparate(gl_SRC_ALPHA, destBlend, gl_ONE, destBlend);
194
210
  glContext.enable(gl_BLEND);
195
211
 
196
212
  // draw all the sprites in the batch and reset the buffer
197
- glContext.bufferSubData(gl_ARRAY_BUFFER, 0, glVertexData);
198
- glContext.drawArrays(gl_TRIANGLE_STRIP, 0, glBatchCount);
199
- glBatchCount = 0;
213
+ glContext.bufferSubData(gl_ARRAY_BUFFER, 0, glPositionData);
214
+ glContext.drawArraysInstanced(gl_TRIANGLE_STRIP, 0, 4, glInstanceCount);
215
+ if (showWatermark)
216
+ drawCount += glInstanceCount;
217
+ glInstanceCount = 0;
200
218
  glBatchAdditive = glAdditive;
201
219
  }
202
220
 
203
221
  /** Draw any sprites still in the buffer, copy to main canvas and clear
204
222
  * @param {CanvasRenderingContext2D} context
205
- * @param {Boolean} [forceDraw=0]
223
+ * @param {Boolean} [forceDraw]
206
224
  * @memberof WebGL */
207
- function glCopyToContext(context, forceDraw)
225
+ function glCopyToContext(context, forceDraw=false)
208
226
  {
209
- if (!glBatchCount && !forceDraw) return;
210
-
227
+ if (!glInstanceCount && !forceDraw) return;
228
+
211
229
  glFlush();
212
-
230
+
213
231
  // do not draw in overlay mode because the canvas is visible
214
232
  if (!glOverlay || forceDraw)
215
233
  context.drawImage(glCanvas, 0, 0);
@@ -231,60 +249,22 @@ function glCopyToContext(context, forceDraw)
231
249
  function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba, rgbaAdditive=0)
232
250
  {
233
251
  // flush if there is not enough room or if different blend mode
234
- const vertCount = 6;
235
- if (glBatchCount >= gl_MAX_BATCH-vertCount || glBatchAdditive != glAdditive)
252
+ if (glInstanceCount >= gl_MAX_INSTANCES-1 || glBatchAdditive != glAdditive)
236
253
  glFlush();
237
254
 
238
- // prepare to create the verts from size and angle
239
- const c = Math.cos(angle)/2, s = Math.sin(angle)/2;
240
- const cx = c*sizeX, cy = c*sizeY, sx = s*sizeX, sy = s*sizeY;
241
- const positionData =
242
- [
243
- x-cx+sy, y+cy+sx, uv0X, uv0Y,
244
- x-cx-sy, y-cy+sx, uv0X, uv1Y,
245
- x+cx+sy, y+cy-sx, uv1X, uv0Y,
246
- x+cx-sy, y-cy-sx, uv1X, uv1Y,
247
- ];
248
-
249
- // setup 2 triangle strip quad
250
- for(let i = vertCount, offset = glBatchCount * gl_INDICIES_PER_VERT; i--;)
251
- {
252
- const j = clamp(i-1, 0, 3)*4; // degenerate tri at ends
253
- glPositionData[offset++] = positionData[j+0];
254
- glPositionData[offset++] = positionData[j+1];
255
- glPositionData[offset++] = positionData[j+2];
256
- glPositionData[offset++] = positionData[j+3];
257
- glColorData[offset++] = rgba;
258
- glColorData[offset++] = rgbaAdditive;
259
- }
260
- glBatchCount += vertCount;
261
- }
262
-
263
- /** Add a convex polygon to the gl draw list
264
- * @param {Array} points - Array of Vector2 points
265
- * @param {Number} rgba - Color of the polygon
266
- * @memberof WebGL */
267
- function glDrawPoints(points, rgba)
268
- {
269
- // flush if there is not enough room or if different blend mode
270
- const vertCount = points.length + 2;
271
- if (glBatchCount >= gl_MAX_BATCH-vertCount || glBatchAdditive != glAdditive)
272
- glFlush();
273
-
274
- // setup triangle strip from list of points
275
- for(let i = vertCount, offset = glBatchCount * gl_INDICIES_PER_VERT; i--;)
276
- {
277
- const j = clamp(i-1, 0, vertCount-3); // degenerate tri at ends
278
- const h = j>>1;
279
- const point = points[j%2? h : vertCount-3-h];
280
- glPositionData[offset++] = point.x;
281
- glPositionData[offset++] = point.y;
282
- glPositionData[offset++] = 0; // uvx
283
- glPositionData[offset++] = 0; // uvy
284
- glColorData[offset++] = 0; // nothing to tint
285
- glColorData[offset++] = rgba; // apply rgba via additive
286
- }
287
- glBatchCount += vertCount;
255
+ let offset = glInstanceCount * gl_INDICIES_PER_INSTANCE;
256
+ glPositionData[offset++] = x;
257
+ glPositionData[offset++] = y;
258
+ glPositionData[offset++] = sizeX;
259
+ glPositionData[offset++] = sizeY;
260
+ glPositionData[offset++] = uv0X;
261
+ glPositionData[offset++] = uv0Y;
262
+ glPositionData[offset++] = uv1X;
263
+ glPositionData[offset++] = uv1Y;
264
+ glColorData[offset++] = rgba;
265
+ glColorData[offset++] = rgbaAdditive;
266
+ glPositionData[offset++] = angle;
267
+ glInstanceCount++;
288
268
  }
289
269
 
290
270
  ///////////////////////////////////////////////////////////////////////////////
@@ -298,7 +278,7 @@ let glPostShader, glPostArrayBuffer, glPostTexture, glPostIncludeOverlay;
298
278
  * @memberof WebGL */
299
279
  function glInitPostProcess(shaderCode, includeOverlay)
300
280
  {
301
- ASSERT(!glPostShader); // can only have 1 post effects shader
281
+ ASSERT(!glPostShader, 'can only have 1 post effects shader');
302
282
 
303
283
  if (!shaderCode) // default shader pass through
304
284
  shaderCode = 'void mainImage(out vec4 c,vec2 p){c=texture(iChannel0,p/iResolution.xy);}';
@@ -309,14 +289,14 @@ function glInitPostProcess(shaderCode, includeOverlay)
309
289
  'precision highp float;'+ // use highp for better accuracy
310
290
  'in vec2 p;'+ // position
311
291
  'void main(){'+ // shader entry point
312
- 'gl_Position=vec4(p,1,1);'+ // set position
292
+ 'gl_Position=vec4(p+p-1.,1,1);'+ // set position
313
293
  '}' // end of shader
314
294
  ,
315
295
  '#version 300 es\n' + // specify GLSL ES version
316
296
  'precision highp float;'+ // use highp for better accuracy
317
297
  'uniform sampler2D iChannel0;'+ // input texture
318
298
  'uniform vec3 iResolution;'+ // size of output texture
319
- 'uniform float iTime;'+ // time passed
299
+ 'uniform float iTime;'+ // time
320
300
  'out vec4 c;'+ // out color
321
301
  '\n' + shaderCode + '\n'+ // insert custom shader code
322
302
  'void main(){'+ // shader entry point
@@ -327,7 +307,7 @@ function glInitPostProcess(shaderCode, includeOverlay)
327
307
 
328
308
  // create buffer and texture
329
309
  glPostArrayBuffer = glContext.createBuffer();
330
- glPostTexture = glCreateTexture();
310
+ glPostTexture = glCreateTexture(undefined);
331
311
  glPostIncludeOverlay = includeOverlay;
332
312
 
333
313
  // hide the original 2d canvas
@@ -363,10 +343,9 @@ function glRenderPostProcess()
363
343
 
364
344
  // setup shader program to draw one triangle
365
345
  glContext.useProgram(glPostShader);
346
+ glContext.bindBuffer(gl_ARRAY_BUFFER, glGeometryBuffer);
347
+ glContext.pixelStorei(gl_UNPACK_FLIP_Y_WEBGL, 1);
366
348
  glContext.disable(gl_BLEND);
367
- glContext.bindBuffer(gl_ARRAY_BUFFER, glPostArrayBuffer);
368
- glContext.bufferData(gl_ARRAY_BUFFER, new Float32Array([-3,1,1,-3,1,1]), gl_STATIC_DRAW);
369
- glContext.pixelStorei(gl_UNPACK_FLIP_Y_WEBGL, true);
370
349
 
371
350
  // set textures, pass in the 2d canvas and gl canvas in separate texture channels
372
351
  glContext.activeTexture(gl_TEXTURE0);
@@ -377,19 +356,19 @@ function glRenderPostProcess()
377
356
  const vertexByteStride = 8;
378
357
  const pLocation = glContext.getAttribLocation(glPostShader, 'p');
379
358
  glContext.enableVertexAttribArray(pLocation);
380
- glContext.vertexAttribPointer(pLocation, 2, gl_FLOAT, 0, vertexByteStride, 0);
359
+ glContext.vertexAttribPointer(pLocation, 2, gl_FLOAT, false, vertexByteStride, 0);
381
360
 
382
361
  // set uniforms and draw
383
362
  const uniformLocation = (name)=>glContext.getUniformLocation(glPostShader, name);
384
363
  glContext.uniform1i(uniformLocation('iChannel0'), 0);
385
364
  glContext.uniform1f(uniformLocation('iTime'), time);
386
365
  glContext.uniform3f(uniformLocation('iResolution'), mainCanvas.width, mainCanvas.height, 1);
387
- glContext.drawArrays(gl_TRIANGLE_STRIP, 0, 3);
366
+ glContext.drawArrays(gl_TRIANGLE_STRIP, 0, 4);
388
367
  }
389
368
 
390
369
  ///////////////////////////////////////////////////////////////////////////////
391
370
  // store gl constants as integers so their name doesn't use space in minifed
392
- const
371
+ const
393
372
  gl_ONE = 1,
394
373
  gl_TRIANGLE_STRIP = 5,
395
374
  gl_SRC_ALPHA = 770,
@@ -411,14 +390,14 @@ gl_TEXTURE0 = 33984,
411
390
  gl_ARRAY_BUFFER = 34962,
412
391
  gl_STATIC_DRAW = 35044,
413
392
  gl_DYNAMIC_DRAW = 35048,
414
- gl_FRAGMENT_SHADER = 35632,
393
+ gl_FRAGMENT_SHADER = 35632,
415
394
  gl_VERTEX_SHADER = 35633,
416
395
  gl_COMPILE_STATUS = 35713,
417
396
  gl_LINK_STATUS = 35714,
418
397
  gl_UNPACK_FLIP_Y_WEBGL = 37440,
419
398
 
420
399
  // constants for batch rendering
421
- gl_INDICIES_PER_VERT = 6,
422
- gl_MAX_BATCH = 1e5,
423
- gl_VERTEX_BYTE_STRIDE = (4 * 2) * 2 + (4) * 2, // vec2 * 2 + (char * 4) * 2
424
- gl_VERTEX_BUFFER_SIZE = gl_MAX_BATCH * gl_VERTEX_BYTE_STRIDE;
400
+ gl_INDICIES_PER_INSTANCE = 11,
401
+ gl_MAX_INSTANCES = 1e4,
402
+ gl_INSTANCE_BYTE_STRIDE = gl_INDICIES_PER_INSTANCE * 4, // 11 * 4
403
+ gl_INSTANCE_BUFFER_SIZE = gl_MAX_INSTANCES * gl_INSTANCE_BYTE_STRIDE;
@@ -0,0 +1,10 @@
1
+ {
2
+ "compilerOptions": {
3
+ "checkJs": true,
4
+ "target": "ES6"
5
+ },
6
+ "exclude": [
7
+ "engineRelease.js",
8
+ "engineExport.js"
9
+ ]
10
+ }