littlejsengine 1.16.2 → 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 (47) hide show
  1. package/dist/littlejs.d.ts +200 -166
  2. package/dist/littlejs.esm.js +1231 -1086
  3. package/dist/littlejs.esm.min.js +1 -1
  4. package/dist/littlejs.js +888 -735
  5. package/dist/littlejs.min.js +1 -1
  6. package/dist/littlejs.release.js +884 -731
  7. package/examples/box2d/gameObjects.js +1 -1
  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/module/tiles.png +0 -0
  15. package/examples/platformer/gameEffects.js +23 -22
  16. package/examples/platformer/gameLevel.js +24 -25
  17. package/examples/shorts/base.html +1 -1
  18. package/examples/shorts/clock.js +3 -3
  19. package/examples/shorts/fontImage.js +4 -3
  20. package/examples/shorts/parallax.js +1 -1
  21. package/examples/shorts/sequencer.js +1 -1
  22. package/examples/shorts/shapes.js +1 -1
  23. package/examples/shorts/texture.js +10 -6
  24. package/examples/shorts/tiles.png +0 -0
  25. package/examples/shorts/tiltedView.js +2 -0
  26. package/examples/starter/index.html +2 -2
  27. package/examples/starter/tiles.png +0 -0
  28. package/examples/typescript/tiles.png +0 -0
  29. package/examples/uiSystem/game.js +1 -1
  30. package/examples/uiSystem/tiles.png +0 -0
  31. package/package.json +1 -1
  32. package/plugins/postProcess.js +47 -28
  33. package/plugins/uiSystem.js +15 -15
  34. package/reference.md +1 -1
  35. package/src/engine.js +174 -174
  36. package/src/engineAudio.js +4 -6
  37. package/src/engineDebug.js +4 -4
  38. package/src/engineDraw.js +179 -122
  39. package/src/engineExport.js +326 -334
  40. package/src/engineFont.png +0 -0
  41. package/src/engineInput.js +14 -20
  42. package/src/engineMath.js +15 -104
  43. package/src/engineObject.js +26 -25
  44. package/src/engineParticles.js +2 -2
  45. package/src/engineTileLayer.js +196 -170
  46. package/src/engineUtilities.js +100 -4
  47. package/src/engineWebGL.js +123 -72
@@ -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,45 +158,62 @@ 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) return;
210
+ if (!force && !glPolyMode) return;
172
211
 
173
212
  // setup instanced mode
174
213
  glFlush();
175
214
  glPolyMode = false;
176
215
  glContext.useProgram(glShader);
177
-
178
- // set vertex attributes
179
- let offset = 0;
180
- const initVertexAttribArray = (name, type, typeSize, size)=>
181
- {
182
- const location = glContext.getAttribLocation(glShader, name);
183
- const stride = typeSize && gl_INSTANCE_BYTE_STRIDE; // only if not geometry
184
- const divisor = typeSize && 1; // only if not geometry
185
- const normalize = typeSize === 1; // only if color
186
- glContext.enableVertexAttribArray(location);
187
- glContext.vertexAttribPointer(location, size, type, normalize, stride, offset);
188
- glContext.vertexAttribDivisor(location, divisor);
189
- offset += size*typeSize;
190
- }
191
- glContext.bindBuffer(glContext.ARRAY_BUFFER, glGeometryBuffer);
192
- initVertexAttribArray('g', glContext.FLOAT, 0, 2); // geometry
193
- glContext.bindBuffer(glContext.ARRAY_BUFFER, glArrayBuffer);
194
- glContext.bufferData(glContext.ARRAY_BUFFER, gl_ARRAY_BUFFER_SIZE, glContext.DYNAMIC_DRAW);
195
- initVertexAttribArray('p', glContext.FLOAT, 4, 4); // position & size
196
- initVertexAttribArray('u', glContext.FLOAT, 4, 4); // texture coords
197
- initVertexAttribArray('c', glContext.UNSIGNED_BYTE, 1, 4); // color
198
- initVertexAttribArray('a', glContext.UNSIGNED_BYTE, 1, 4); // additiveColor
199
- initVertexAttribArray('r', glContext.FLOAT, 4, 1); // rotation
216
+ glContext.bindVertexArray(glInstancedVAO);
200
217
  }
201
218
 
202
219
  function glSetPolyMode()
@@ -207,36 +224,30 @@ function glSetPolyMode()
207
224
  glFlush();
208
225
  glPolyMode = true;
209
226
  glContext.useProgram(glPolyShader);
210
-
211
- // set vertex attributes
212
- let offset = 0;
213
- const initVertexAttribArray = (name, type, typeSize, size)=>
214
- {
215
- const location = glContext.getAttribLocation(glPolyShader, name);
216
- const normalize = typeSize === 1; // only normalize if color
217
- const stride = gl_POLY_VERTEX_BYTE_STRIDE;
218
- glContext.enableVertexAttribArray(location);
219
- glContext.vertexAttribPointer(location, size, type, normalize, stride, offset);
220
- glContext.vertexAttribDivisor(location, 0);
221
- offset += size*typeSize;
222
- }
223
- glContext.bindBuffer(glContext.ARRAY_BUFFER, glArrayBuffer);
224
- glContext.bufferData(glContext.ARRAY_BUFFER, gl_ARRAY_BUFFER_SIZE, glContext.DYNAMIC_DRAW);
225
- initVertexAttribArray('p', glContext.FLOAT, 4, 2); // position
226
- initVertexAttribArray('c', glContext.UNSIGNED_BYTE, 1, 4); // color
227
+ glContext.bindVertexArray(glPolyVAO);
227
228
  }
228
229
 
229
230
  // Setup WebGL render each frame, called automatically by engine
230
231
  // Also used by tile layer rendering when redrawing tiles
231
- function glPreRender()
232
+ function glPreRender(clear=true)
232
233
  {
233
234
  if (!glEnable || !glContext) return;
234
235
 
235
- // clear the canvas
236
- 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();
237
246
 
238
247
  // build the transform matrix
239
248
  const s = vec2(2*cameraScale).divide(mainCanvasSize);
249
+ if (glRenderTarget)
250
+ s.y = -s.y; // invert y when using render target
240
251
  const rotatedCam = cameraPos.rotate(-cameraAngle);
241
252
  const p = vec2(-1).subtract(rotatedCam.multiply(s));
242
253
  const ca = cos(cameraAngle);
@@ -265,12 +276,14 @@ function glPreRender()
265
276
  glContext.bindTexture(glContext.TEXTURE_2D, glActiveTexture);
266
277
  }
267
278
 
279
+ // rebind the array buffer
280
+ glContext.bindBuffer(glContext.ARRAY_BUFFER, glArrayBuffer);
281
+
268
282
  // start with additive blending off
269
283
  glAdditive = glBatchAdditive = false;
270
284
 
271
- // force it to set instanced mode by first setting poly mode true
272
- glPolyMode = true;
273
- glSetInstancedMode();
285
+ // force it to set instanced mode
286
+ glSetInstancedMode(true);
274
287
  }
275
288
 
276
289
  /** Clear the canvas and setup the viewport
@@ -279,13 +292,9 @@ function glClearCanvas()
279
292
  {
280
293
  if (!glContext) return;
281
294
 
282
- // clear and set to same size as main canvas
283
- glCanvas.width = mainCanvasSize.x;
284
- glCanvas.height = mainCanvasSize.y;
285
- glContext.viewport(0, 0, glCanvas.width, glCanvas.height);
295
+ // clear using the canvasClearColor
286
296
  const color = canvasClearColor;
287
- if (color.a > 0)
288
- glContext.clearColor(color.r, color.g, color.b, color.a);
297
+ glContext.clearColor(color.r, color.g, color.b, color.a);
289
298
  glContext.clear(glContext.COLOR_BUFFER_BIT);
290
299
  }
291
300
 
@@ -362,7 +371,7 @@ function glCreateTexture(image)
362
371
  // build the texture
363
372
  const texture = glContext.createTexture();
364
373
  let mipMap = false;
365
- if (image && image.width)
374
+ if (image?.width)
366
375
  {
367
376
  glSetTextureData(texture, image);
368
377
  glContext.bindTexture(glContext.TEXTURE_2D, texture);
@@ -383,7 +392,9 @@ function glCreateTexture(image)
383
392
  glContext.texParameteri(glContext.TEXTURE_2D, glContext.TEXTURE_MIN_FILTER, minFilter);
384
393
  if (mipMap)
385
394
  glContext.generateMipmap(glContext.TEXTURE_2D);
386
- glContext.bindTexture(glContext.TEXTURE_2D, glActiveTexture); // rebind active texture
395
+
396
+ // rebind active texture
397
+ glContext.bindTexture(glContext.TEXTURE_2D, glActiveTexture);
387
398
  return texture;
388
399
  }
389
400
 
@@ -406,10 +417,12 @@ function glSetTextureData(texture, image)
406
417
  if (!glContext) return;
407
418
 
408
419
  // build the texture
409
- ASSERT(!!image && image.width > 0, 'Invalid image data.');
420
+ ASSERT(image?.width > 0, 'Invalid image data.');
410
421
  glContext.bindTexture(glContext.TEXTURE_2D, texture);
411
422
  glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, glContext.RGBA, glContext.UNSIGNED_BYTE, image);
412
- glContext.bindTexture(glContext.TEXTURE_2D, glActiveTexture); // rebind active texture
423
+
424
+ // rebind active texture
425
+ glContext.bindTexture(glContext.TEXTURE_2D, glActiveTexture);
413
426
  }
414
427
 
415
428
  /** Tells WebGL to create or update the glTexture and start tracking it
@@ -628,6 +641,48 @@ function glDrawColoredPoints(points, pointColors)
628
641
  glBatchCount += vertCount;
629
642
  }
630
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
+
631
686
  // WebGL internal function to convert polygon to outline triangle strip
632
687
  function glMakeOutline(points, width, wrap=true)
633
688
  {
@@ -761,23 +816,20 @@ function glPolyStrip(points)
761
816
  const a = points[i0], b = points[i1], c = points[i2];
762
817
 
763
818
  // check if convex
764
- if (cross(a, b, c) < e)
765
- continue;
819
+ if (cross(a, b, c) < e) continue;
766
820
 
767
821
  // check if any other point is inside
768
822
  let hasInside = false;
769
823
  for (let j = 0; j < indices.length; j++)
770
824
  {
771
825
  const k = indices[j];
772
- if (k === i0 || k === i1 || k === i2)
773
- continue;
826
+ if (k === i0 || k === i1 || k === i2) continue;
827
+
774
828
  const p = points[k];
775
829
  hasInside = pointInTriangle(p, a, b, c);
776
- if (hasInside)
777
- break;
830
+ if (hasInside) break;
778
831
  }
779
- if (hasInside)
780
- continue;
832
+ if (hasInside) continue;
781
833
 
782
834
  // found valid ear
783
835
  triangles.push([i0, i1, i2]);
@@ -802,8 +854,7 @@ function glPolyStrip(points)
802
854
  worstIndex = i;
803
855
  }
804
856
  }
805
- if (worstIndex < 0)
806
- break;
857
+ if (worstIndex < 0) break;
807
858
 
808
859
  const i0 = indices[(worstIndex + indices.length - 1) % indices.length];
809
860
  const i1 = indices[worstIndex];