littlejsengine 1.16.1 → 1.17.1

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 (52) hide show
  1. package/dist/littlejs.d.ts +287 -240
  2. package/dist/littlejs.esm.js +1419 -1259
  3. package/dist/littlejs.esm.min.js +1 -1
  4. package/dist/littlejs.js +1076 -910
  5. package/dist/littlejs.min.js +1 -1
  6. package/dist/littlejs.release.js +977 -815
  7. package/examples/box2d/gameObjects.js +6 -6
  8. package/examples/breakout/game.js +5 -6
  9. package/examples/electron/index.html +2 -2
  10. package/examples/electron/tiles.png +0 -0
  11. package/examples/htmlMenu/tiles.png +0 -0
  12. package/examples/index.html +1 -1
  13. package/examples/logo.png +0 -0
  14. package/examples/logo2.png +0 -0
  15. package/examples/module/tiles.png +0 -0
  16. package/examples/platformer/gameEffects.js +24 -23
  17. package/examples/platformer/gameLevel.js +24 -25
  18. package/examples/shorts/base.html +2 -1
  19. package/examples/shorts/clock.js +3 -3
  20. package/examples/shorts/fontImage.js +4 -3
  21. package/examples/shorts/parallax.js +1 -1
  22. package/examples/shorts/sequencer.js +1 -1
  23. package/examples/shorts/shapes.js +1 -1
  24. package/examples/shorts/texture.js +10 -6
  25. package/examples/shorts/tiles.png +0 -0
  26. package/examples/shorts/tiltedView.js +2 -0
  27. package/examples/starter/index.html +3 -2
  28. package/examples/starter/tiles.png +0 -0
  29. package/examples/typescript/tiles.png +0 -0
  30. package/examples/uiSystem/game.js +1 -1
  31. package/examples/uiSystem/tiles.png +0 -0
  32. package/package.json +1 -1
  33. package/plugins/postProcess.js +47 -28
  34. package/plugins/uiSystem.js +16 -16
  35. package/reference.md +6 -6
  36. package/src/engine.js +186 -198
  37. package/src/engineAudio.js +6 -10
  38. package/src/engineBuild.js +1 -0
  39. package/src/engineDebug.js +100 -98
  40. package/src/engineDraw.js +185 -148
  41. package/src/engineExport.js +327 -333
  42. package/src/engineFont.png +0 -0
  43. package/src/engineInput.js +17 -26
  44. package/src/engineMath.js +1112 -0
  45. package/src/engineMedals.js +4 -4
  46. package/src/engineObject.js +27 -27
  47. package/src/engineParticles.js +2 -2
  48. package/src/engineRelease.js +1 -3
  49. package/src/engineSettings.js +2 -2
  50. package/src/engineTileLayer.js +200 -176
  51. package/src/engineUtilities.js +48 -1097
  52. package/src/engineWebGL.js +127 -79
@@ -29,7 +29,7 @@ let glContext;
29
29
  let glAntialias = true;
30
30
 
31
31
  // WebGL internal variables not exposed to documentation
32
- let glShader, glPolyShader, glPolyMode, glAdditive, glBatchAdditive, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glBatchCount, glTextureInfos, glCanBeEnabled = true;
32
+ let glShader, glPolyShader, glPolyMode, glAdditive, glBatchAdditive, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glBatchCount, glTextureInfos, glInstancedVAO, glPolyVAO, glFramebuffer, glRenderTarget, glCanBeEnabled = true;
33
33
 
34
34
  // WebGL internal constants
35
35
  const gl_ARRAY_BUFFER_SIZE = 5e5;
@@ -105,7 +105,7 @@ function glInit(rootElement)
105
105
  // setup instanced rendering shader program
106
106
  glShader = glCreateProgram(
107
107
  '#version 300 es\n' + // specify GLSL ES version
108
- 'precision highp float;'+ // use highp for better accuracy
108
+ 'precision highp float;'+ // use highp for accuracy
109
109
  'uniform mat4 m;'+ // transform matrix
110
110
  'in vec2 g;'+ // in: geometry
111
111
  'in vec4 p,u,c,a;'+ // in: position/size, uvs, color, additiveColor
@@ -120,7 +120,7 @@ function glInit(rootElement)
120
120
  '}' // end of shader
121
121
  ,
122
122
  '#version 300 es\n' + // specify GLSL ES version
123
- 'precision highp float;'+ // use highp for better accuracy
123
+ 'precision highp float;'+ // use highp for accuracy
124
124
  'uniform sampler2D s;'+ // texture
125
125
  'in vec2 v;'+ // in: uv
126
126
  'in vec4 d,e;'+ // in: color, additiveColor
@@ -158,87 +158,96 @@ function glInit(rootElement)
158
158
  glColorData = new Uint32Array(glInstanceData);
159
159
  glArrayBuffer = glContext.createBuffer();
160
160
  glGeometryBuffer = glContext.createBuffer();
161
+ glFramebuffer = glContext.createFramebuffer();
162
+ glBatchCount = 0;
161
163
 
162
164
  // create the geometry buffer, triangle strip square
163
- const geometry = new Float32Array([glBatchCount=0,0,1,0,0,1,1,1]);
165
+ const geometry = new Float32Array([0,0,1,0,0,1,1,1]);
164
166
  glContext.bindBuffer(glContext.ARRAY_BUFFER, glGeometryBuffer);
165
167
  glContext.bufferData(glContext.ARRAY_BUFFER, geometry, glContext.STATIC_DRAW);
168
+
169
+ let offset, shader, stride;
170
+ const initVertexAttrib = (name, type, typeSize, size, divisor=0)=>
171
+ {
172
+ const location = glContext.getAttribLocation(shader, name);
173
+ const normalize = typeSize === 1;
174
+ const fixedStride = typeSize && stride;
175
+ glContext.enableVertexAttribArray(location);
176
+ glContext.vertexAttribPointer(location, size, type, normalize, fixedStride, offset);
177
+ glContext.vertexAttribDivisor(location, divisor);
178
+ offset += size*typeSize;
179
+ }
180
+
181
+ // setup VAO for instanced rendering
182
+ glInstancedVAO = glContext.createVertexArray();
183
+ glContext.bindVertexArray(glInstancedVAO);
184
+
185
+ // configure instanced vertex attributes
186
+ offset = 0, shader = glShader, stride = gl_INSTANCE_BYTE_STRIDE;
187
+ glContext.bindBuffer(glContext.ARRAY_BUFFER, glGeometryBuffer);
188
+ initVertexAttrib('g', glContext.FLOAT, 0, 2); // geometry
189
+ glContext.bindBuffer(glContext.ARRAY_BUFFER, glArrayBuffer);
190
+ glContext.bufferData(glContext.ARRAY_BUFFER, gl_ARRAY_BUFFER_SIZE, glContext.DYNAMIC_DRAW);
191
+ initVertexAttrib('p', glContext.FLOAT, 4, 4, 1); // position & size
192
+ initVertexAttrib('u', glContext.FLOAT, 4, 4, 1); // texture coords
193
+ initVertexAttrib('c', glContext.UNSIGNED_BYTE, 1, 4, 1); // color
194
+ initVertexAttrib('a', glContext.UNSIGNED_BYTE, 1, 4, 1); // additiveColor
195
+ initVertexAttrib('r', glContext.FLOAT, 4, 1, 1); // rotation
196
+
197
+ // setup VAO for poly rendering
198
+ glPolyVAO = glContext.createVertexArray();
199
+ glContext.bindVertexArray(glPolyVAO);
200
+
201
+ // configure poly vertex attributes
202
+ offset = 0, shader = glPolyShader, stride = gl_POLY_VERTEX_BYTE_STRIDE;
203
+ initVertexAttrib('p', glContext.FLOAT, 4, 2); // position
204
+ initVertexAttrib('c', glContext.UNSIGNED_BYTE, 1, 4); // color
166
205
  }
167
206
  }
168
207
 
169
- function glSetInstancedMode()
208
+ function glSetInstancedMode(force=false)
170
209
  {
171
- if (!glPolyMode)
172
- return;
210
+ if (!force && !glPolyMode) return;
173
211
 
174
212
  // setup instanced mode
175
213
  glFlush();
176
214
  glPolyMode = false;
177
215
  glContext.useProgram(glShader);
178
-
179
- // set vertex attributes
180
- let offset = 0;
181
- const initVertexAttribArray = (name, type, typeSize, size)=>
182
- {
183
- const location = glContext.getAttribLocation(glShader, name);
184
- const stride = typeSize && gl_INSTANCE_BYTE_STRIDE; // only if not geometry
185
- const divisor = typeSize && 1; // only if not geometry
186
- const normalize = typeSize === 1; // only if color
187
- glContext.enableVertexAttribArray(location);
188
- glContext.vertexAttribPointer(location, size, type, normalize, stride, offset);
189
- glContext.vertexAttribDivisor(location, divisor);
190
- offset += size*typeSize;
191
- }
192
- glContext.bindBuffer(glContext.ARRAY_BUFFER, glGeometryBuffer);
193
- initVertexAttribArray('g', glContext.FLOAT, 0, 2); // geometry
194
- glContext.bindBuffer(glContext.ARRAY_BUFFER, glArrayBuffer);
195
- glContext.bufferData(glContext.ARRAY_BUFFER, gl_ARRAY_BUFFER_SIZE, glContext.DYNAMIC_DRAW);
196
- initVertexAttribArray('p', glContext.FLOAT, 4, 4); // position & size
197
- initVertexAttribArray('u', glContext.FLOAT, 4, 4); // texture coords
198
- initVertexAttribArray('c', glContext.UNSIGNED_BYTE, 1, 4); // color
199
- initVertexAttribArray('a', glContext.UNSIGNED_BYTE, 1, 4); // additiveColor
200
- initVertexAttribArray('r', glContext.FLOAT, 4, 1); // rotation
216
+ glContext.bindVertexArray(glInstancedVAO);
201
217
  }
202
218
 
203
219
  function glSetPolyMode()
204
220
  {
205
- if (glPolyMode)
206
- return;
221
+ if (glPolyMode) return;
207
222
 
208
223
  // setup poly mode
209
224
  glFlush();
210
225
  glPolyMode = true;
211
226
  glContext.useProgram(glPolyShader);
212
-
213
- // set vertex attributes
214
- let offset = 0;
215
- const initVertexAttribArray = (name, type, typeSize, size)=>
216
- {
217
- const location = glContext.getAttribLocation(glPolyShader, name);
218
- const normalize = typeSize === 1; // only normalize if color
219
- const stride = gl_POLY_VERTEX_BYTE_STRIDE;
220
- glContext.enableVertexAttribArray(location);
221
- glContext.vertexAttribPointer(location, size, type, normalize, stride, offset);
222
- glContext.vertexAttribDivisor(location, 0);
223
- offset += size*typeSize;
224
- }
225
- glContext.bindBuffer(glContext.ARRAY_BUFFER, glArrayBuffer);
226
- glContext.bufferData(glContext.ARRAY_BUFFER, gl_ARRAY_BUFFER_SIZE, glContext.DYNAMIC_DRAW);
227
- initVertexAttribArray('p', glContext.FLOAT, 4, 2); // position
228
- initVertexAttribArray('c', glContext.UNSIGNED_BYTE, 1, 4); // color
227
+ glContext.bindVertexArray(glPolyVAO);
229
228
  }
230
229
 
231
230
  // Setup WebGL render each frame, called automatically by engine
232
231
  // Also used by tile layer rendering when redrawing tiles
233
- function glPreRender()
232
+ function glPreRender(clear=true)
234
233
  {
235
234
  if (!glEnable || !glContext) return;
236
235
 
237
- // clear the canvas
238
- glClearCanvas();
236
+ ASSERT(!glBatchCount, 'glPreRender called with unflushed batch.');
237
+
238
+ if (!glRenderTarget)
239
+ {
240
+ // set to same size as main canvas
241
+ glCanvas.width = mainCanvasSize.x;
242
+ glCanvas.height = mainCanvasSize.y;
243
+ }
244
+ glContext.viewport(0, 0, mainCanvasSize.x, mainCanvasSize.y);
245
+ clear && glClearCanvas();
239
246
 
240
247
  // build the transform matrix
241
248
  const s = vec2(2*cameraScale).divide(mainCanvasSize);
249
+ if (glRenderTarget)
250
+ s.y = -s.y; // invert y when using render target
242
251
  const rotatedCam = cameraPos.rotate(-cameraAngle);
243
252
  const p = vec2(-1).subtract(rotatedCam.multiply(s));
244
253
  const ca = cos(cameraAngle);
@@ -267,12 +276,14 @@ function glPreRender()
267
276
  glContext.bindTexture(glContext.TEXTURE_2D, glActiveTexture);
268
277
  }
269
278
 
279
+ // rebind the array buffer
280
+ glContext.bindBuffer(glContext.ARRAY_BUFFER, glArrayBuffer);
281
+
270
282
  // start with additive blending off
271
283
  glAdditive = glBatchAdditive = false;
272
284
 
273
- // force it to set instanced mode by first setting poly mode true
274
- glPolyMode = true;
275
- glSetInstancedMode();
285
+ // force it to set instanced mode
286
+ glSetInstancedMode(true);
276
287
  }
277
288
 
278
289
  /** Clear the canvas and setup the viewport
@@ -281,13 +292,9 @@ function glClearCanvas()
281
292
  {
282
293
  if (!glContext) return;
283
294
 
284
- // clear and set to same size as main canvas
285
- glCanvas.width = drawCanvas.width;
286
- glCanvas.height = drawCanvas.height;
287
- glContext.viewport(0, 0, glCanvas.width, glCanvas.height);
295
+ // clear using the canvasClearColor
288
296
  const color = canvasClearColor;
289
- if (color.a > 0)
290
- glContext.clearColor(color.r, color.g, color.b, color.a);
297
+ glContext.clearColor(color.r, color.g, color.b, color.a);
291
298
  glContext.clear(glContext.COLOR_BUFFER_BIT);
292
299
  }
293
300
 
@@ -299,8 +306,7 @@ function glClearCanvas()
299
306
  function glSetTexture(texture, wrap=false)
300
307
  {
301
308
  // must flush cache with the old texture to set a new one
302
- if (!glContext || texture === glActiveTexture)
303
- return;
309
+ if (!glContext || texture === glActiveTexture) return;
304
310
 
305
311
  glFlush();
306
312
  glActiveTexture = texture;
@@ -365,7 +371,7 @@ function glCreateTexture(image)
365
371
  // build the texture
366
372
  const texture = glContext.createTexture();
367
373
  let mipMap = false;
368
- if (image && image.width)
374
+ if (image?.width)
369
375
  {
370
376
  glSetTextureData(texture, image);
371
377
  glContext.bindTexture(glContext.TEXTURE_2D, texture);
@@ -386,7 +392,9 @@ function glCreateTexture(image)
386
392
  glContext.texParameteri(glContext.TEXTURE_2D, glContext.TEXTURE_MIN_FILTER, minFilter);
387
393
  if (mipMap)
388
394
  glContext.generateMipmap(glContext.TEXTURE_2D);
389
- glContext.bindTexture(glContext.TEXTURE_2D, glActiveTexture); // rebind active texture
395
+
396
+ // rebind active texture
397
+ glContext.bindTexture(glContext.TEXTURE_2D, glActiveTexture);
390
398
  return texture;
391
399
  }
392
400
 
@@ -396,6 +404,7 @@ function glCreateTexture(image)
396
404
  function glDeleteTexture(texture)
397
405
  {
398
406
  if (!glContext) return;
407
+
399
408
  glContext.deleteTexture(texture);
400
409
  }
401
410
 
@@ -408,10 +417,12 @@ function glSetTextureData(texture, image)
408
417
  if (!glContext) return;
409
418
 
410
419
  // build the texture
411
- ASSERT(!!image && image.width > 0, 'Invalid image data.');
420
+ ASSERT(image?.width > 0, 'Invalid image data.');
412
421
  glContext.bindTexture(glContext.TEXTURE_2D, texture);
413
422
  glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, glContext.RGBA, glContext.UNSIGNED_BYTE, image);
414
- glContext.bindTexture(glContext.TEXTURE_2D, glActiveTexture); // rebind active texture
423
+
424
+ // rebind active texture
425
+ glContext.bindTexture(glContext.TEXTURE_2D, glActiveTexture);
415
426
  }
416
427
 
417
428
  /** Tells WebGL to create or update the glTexture and start tracking it
@@ -480,8 +491,7 @@ function glFlush()
480
491
  * @memberof WebGL */
481
492
  function glCopyToContext(context)
482
493
  {
483
- if (!glEnable || !glContext)
484
- return;
494
+ if (!glEnable || !glContext) return;
485
495
 
486
496
  glFlush();
487
497
  context.drawImage(glCanvas, 0, 0);
@@ -631,6 +641,48 @@ function glDrawColoredPoints(points, pointColors)
631
641
  glBatchCount += vertCount;
632
642
  }
633
643
 
644
+ /** Set the WebGL render target to the given texture or back to the canvas
645
+ * @param {WebGLTexture} [texture] - a texture or undefined to use normal glCanvas
646
+ * @param {boolean} [clear] - should the render target be cleared
647
+ * @memberof WebGL */
648
+ function glSetRenderTarget(texture, clear=false)
649
+ {
650
+ if (texture)
651
+ {
652
+ glRenderTarget = texture;
653
+ glContext.bindFramebuffer(glContext.FRAMEBUFFER, glFramebuffer);
654
+ glContext.framebufferTexture2D(glContext.FRAMEBUFFER,
655
+ glContext.COLOR_ATTACHMENT0, glContext.TEXTURE_2D, texture, 0);
656
+ glPreRender(clear);
657
+ }
658
+ else
659
+ {
660
+ glFlush();
661
+ glRenderTarget = undefined;
662
+ glContext.bindFramebuffer(glContext.FRAMEBUFFER, null);
663
+ }
664
+ }
665
+
666
+ /** Clear out a rectangle area of the WebGL canvas or render target
667
+ * @param {number} x
668
+ * @param {number} y
669
+ * @param {number} width
670
+ * @param {number} height
671
+ * @memberof WebGL */
672
+ function glClearRect(x, y, width, height)
673
+ {
674
+ if (!glEnable) return;
675
+
676
+ // Enable scissor test to clear only the specified area
677
+ glContext.enable(glContext.SCISSOR_TEST);
678
+ glContext.scissor(x, y, width, height);
679
+ glContext.clearColor(0, 0, 0, 0);
680
+ glContext.clear(glContext.COLOR_BUFFER_BIT);
681
+ glContext.disable(glContext.SCISSOR_TEST);
682
+ }
683
+
684
+ ///////////////////////////////////////////////////////////////////////////////
685
+
634
686
  // WebGL internal function to convert polygon to outline triangle strip
635
687
  function glMakeOutline(points, width, wrap=true)
636
688
  {
@@ -764,23 +816,20 @@ function glPolyStrip(points)
764
816
  const a = points[i0], b = points[i1], c = points[i2];
765
817
 
766
818
  // check if convex
767
- if (cross(a, b, c) < e)
768
- continue;
819
+ if (cross(a, b, c) < e) continue;
769
820
 
770
821
  // check if any other point is inside
771
822
  let hasInside = false;
772
823
  for (let j = 0; j < indices.length; j++)
773
824
  {
774
825
  const k = indices[j];
775
- if (k === i0 || k === i1 || k === i2)
776
- continue;
826
+ if (k === i0 || k === i1 || k === i2) continue;
827
+
777
828
  const p = points[k];
778
829
  hasInside = pointInTriangle(p, a, b, c);
779
- if (hasInside)
780
- break;
830
+ if (hasInside) break;
781
831
  }
782
- if (hasInside)
783
- continue;
832
+ if (hasInside) continue;
784
833
 
785
834
  // found valid ear
786
835
  triangles.push([i0, i1, i2]);
@@ -805,8 +854,7 @@ function glPolyStrip(points)
805
854
  worstIndex = i;
806
855
  }
807
856
  }
808
- if (worstIndex < 0)
809
- break;
857
+ if (worstIndex < 0) break;
810
858
 
811
859
  const i0 = indices[(worstIndex + indices.length - 1) % indices.length];
812
860
  const i1 = indices[worstIndex];