littlejsengine 1.14.2 → 1.14.4

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.
@@ -33,7 +33,7 @@ const engineName = 'LittleJS';
33
33
  * @type {string}
34
34
  * @default
35
35
  * @memberof Engine */
36
- const engineVersion = '1.14.2';
36
+ const engineVersion = '1.14.4';
37
37
 
38
38
  /** Frames per second to update
39
39
  * @type {number}
@@ -249,8 +249,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
249
249
  overlayContext.fillStyle = '#fff';
250
250
  overlayContext.fillText(text, mainCanvas.width-2, 2);
251
251
  }
252
- if (debug || showWatermark)
253
- drawCount = 0;
252
+ drawCount = 0;
254
253
  }
255
254
  requestAnimationFrame(engineUpdate);
256
255
  }
@@ -284,6 +283,11 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
284
283
 
285
284
  // save canvas size
286
285
  mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
286
+
287
+ // set default line join and cap
288
+ const lineJoin = 'round', lineCap = 'round';
289
+ mainContext.lineJoin = overlayContext.lineJoin = lineJoin;
290
+ mainContext.lineCap = overlayContext.lineCap = lineCap;
287
291
  }
288
292
 
289
293
  // wait for gameInit to load
@@ -772,13 +776,13 @@ function debugPoint(pos, color, time, angle)
772
776
  * @param {Vector2} posA
773
777
  * @param {Vector2} posB
774
778
  * @param {string} [color]
775
- * @param {number} [thickness]
779
+ * @param {number} [width]
776
780
  * @param {number} [time]
777
781
  * @memberof Debug */
778
- function debugLine(posA, posB, color, thickness=.1, time)
782
+ function debugLine(posA, posB, color, width=.1, time)
779
783
  {
780
784
  const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2);
781
- const size = vec2(thickness, halfDelta.length()*2);
785
+ const size = vec2(width, halfDelta.length()*2);
782
786
  debugRect(posA.add(halfDelta), size, color, time, halfDelta.angle(), true);
783
787
  }
784
788
 
@@ -1619,10 +1623,10 @@ function vec2(x=0, y) { return new Vector2(x, y === undefined ? x : y); }
1619
1623
  * @param {any} v
1620
1624
  * @return {boolean}
1621
1625
  * @memberof Utilities */
1622
- function isVector2(v) { return v instanceof Vector2; }
1626
+ function isVector2(v) { return v instanceof Vector2 && v.isValid(); }
1623
1627
 
1624
1628
  // vector2 asserts
1625
- function ASSERT_VECTOR2_VALID(v) { ASSERT(isVector2(v) && v.isValid(), 'Vector2 is invalid.', v); }
1629
+ function ASSERT_VECTOR2_VALID(v) { ASSERT(isVector2(v), 'Vector2 is invalid.', v); }
1626
1630
  function ASSERT_NUMBER_VALID(n) { ASSERT(isNumber(n), 'Number is invalid.', n); }
1627
1631
  function ASSERT_VECTOR2_NORMAL(v)
1628
1632
  {
@@ -1880,10 +1884,10 @@ function hsl(h, s, l, a) { return new Color().setHSLA(h, s, l, a); }
1880
1884
  * @param {any} c
1881
1885
  * @return {boolean}
1882
1886
  * @memberof Utilities */
1883
- function isColor(c) { return c instanceof Color; }
1887
+ function isColor(c) { return c instanceof Color && c.isValid(); }
1884
1888
 
1885
1889
  // color asserts
1886
- function ASSERT_COLOR_VALID(c) { ASSERT(isColor(c) && c.isValid(), 'Color is invalid.', c); }
1890
+ function ASSERT_COLOR_VALID(c) { ASSERT(isColor(c), 'Color is invalid.', c); }
1887
1891
 
1888
1892
  /**
1889
1893
  * Color object (red, green, blue, alpha) with some helpful functions
@@ -2816,11 +2820,11 @@ class EngineObject
2816
2820
  constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color=new Color, renderOrder=0)
2817
2821
  {
2818
2822
  // check passed in params
2819
- ASSERT(isVector2(pos) && pos.isValid(), 'object pos should be a vec2');
2820
- ASSERT(isVector2(size) && size.isValid(), 'object size should be a vec2');
2823
+ ASSERT(isVector2(pos), 'object pos should be a vec2');
2824
+ ASSERT(isVector2(size), 'object size should be a vec2');
2821
2825
  ASSERT(!tileInfo || tileInfo instanceof TileInfo, 'object tileInfo should be a TileInfo or undefined');
2822
2826
  ASSERT(typeof angle === 'number' && isFinite(angle), 'object angle should be a number');
2823
- ASSERT(isColor(color) && color.isValid(), 'object color should be a valid rgba color');
2827
+ ASSERT(isColor(color), 'object color should be a valid rgba color');
2824
2828
  ASSERT(typeof renderOrder === 'number', 'object renderOrder should be a number');
2825
2829
 
2826
2830
  /** @property {Vector2} - World space position of the object */
@@ -3314,7 +3318,9 @@ let mainCanvasSize = vec2();
3314
3318
  * @memberof Draw */
3315
3319
  let textureInfos = [];
3316
3320
 
3317
- // Keep track of how many draw calls there were each frame for debugging
3321
+ /** Keeps track of how many draw calls there were each frame for debugging
3322
+ * @type {number}
3323
+ * @memberof Draw */
3318
3324
  let drawCount;
3319
3325
 
3320
3326
  ///////////////////////////////////////////////////////////////////////////////
@@ -3469,8 +3475,8 @@ class TextureInfo
3469
3475
  function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
3470
3476
  angle=0, mirror, additiveColor, useWebGL=glEnable, screenSpace, context)
3471
3477
  {
3472
- ASSERT(isVector2(pos) && pos.isValid(), 'drawTile pos should be a vec2');
3473
- ASSERT(isVector2(size) && size.isValid(), 'drawTile size should be a vec2');
3478
+ ASSERT(isVector2(pos), 'drawTile pos should be a vec2');
3479
+ ASSERT(isVector2(size), 'drawTile size should be a vec2');
3474
3480
  ASSERT(isColor(color) && (!additiveColor || isColor(additiveColor)), 'drawTile color is invalid');
3475
3481
  ASSERT(isNumber(angle), 'drawTile angle should be a number');
3476
3482
  ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
@@ -3518,7 +3524,7 @@ function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
3518
3524
  else
3519
3525
  {
3520
3526
  // normal canvas 2D rendering method (slower)
3521
- showWatermark && ++drawCount;
3527
+ ++drawCount;
3522
3528
  size = new Vector2(size.x, -size.y); // fix upside down sprites
3523
3529
  drawCanvas2D(pos, size, angle, mirror, (context)=>
3524
3530
  {
@@ -3554,10 +3560,124 @@ function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
3554
3560
  drawTile(pos, size, undefined, color, angle, false, undefined, useWebGL, screenSpace, context);
3555
3561
  }
3556
3562
 
3563
+ /** Draw a rect centered on pos with a gradient from top to bottom
3564
+ * @param {Vector2} pos
3565
+ * @param {Vector2} [size=(1,1)]
3566
+ * @param {Color} [colorTop=(1,1,1,1)]
3567
+ * @param {Color} [colorBottom=(0,0,0,1)]
3568
+ * @param {number} [angle]
3569
+ * @param {boolean} [useWebGL=glEnable]
3570
+ * @param {boolean} [screenSpace]
3571
+ * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
3572
+ * @memberof Draw */
3573
+ function drawRectGradient(pos, size, colorTop=WHITE, colorBottom=BLACK, angle=0, useWebGL=glEnable, screenSpace=false, context)
3574
+ {
3575
+ ASSERT(isVector2(pos), 'drawRectGradient pos should be a vec2');
3576
+ ASSERT(isVector2(size), 'drawRectGradient size should be a vec2');
3577
+ ASSERT(isColor(colorTop) && isColor(colorBottom), 'drawRectGradient color is invalid');
3578
+ ASSERT(isNumber(angle), 'drawRectGradient angle should be a number');
3579
+ ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
3580
+ if (useWebGL)
3581
+ {
3582
+ if (screenSpace)
3583
+ {
3584
+ // convert to world space
3585
+ pos = screenToWorld(pos);
3586
+ size = size.scale(1/cameraScale);
3587
+ }
3588
+ // build 4 corner points for the rectangle
3589
+ const points = [], colors = [];
3590
+ const halfSizeX = size.x/2, halfSizeY = size.y/2;
3591
+ const colorTopInt = colorTop.rgbaInt();
3592
+ const colorBottomInt = colorBottom.rgbaInt();
3593
+ const c = Math.cos(-angle), s = Math.sin(-angle);
3594
+ for (let i=4; i--;)
3595
+ {
3596
+ const x = i & 1 ? halfSizeX : -halfSizeX;
3597
+ const y = i & 2 ? halfSizeY : -halfSizeY;
3598
+ const rx = x * c - y * s;
3599
+ const ry = x * s + y * c;
3600
+ const color = i & 2 ? colorTopInt : colorBottomInt;
3601
+ points.push(vec2(pos.x + rx, pos.y + ry));
3602
+ colors.push(color);
3603
+ }
3604
+ glDrawColoredPoints(points, colors);
3605
+ }
3606
+ else
3607
+ {
3608
+ // normal canvas 2D rendering method (slower)
3609
+ ++drawCount;
3610
+ size = new Vector2(size.x, -size.y); // fix upside down sprites
3611
+ drawCanvas2D(pos, size, angle, false, (context)=>
3612
+ {
3613
+ // if no tile info, use untextured rect
3614
+ const gradient = context.createLinearGradient(0, -.5, 0, .5);
3615
+ gradient.addColorStop(0, colorTop.toString());
3616
+ gradient.addColorStop(1, colorBottom.toString());
3617
+ context.fillStyle = gradient;
3618
+ context.fillRect(-.5, -.5, 1, 1);
3619
+ }, screenSpace, context);
3620
+ }
3621
+ }
3622
+
3623
+ /** Draw connected lines between a series of points
3624
+ * @param {Array<Vector2>} points
3625
+ * @param {number} [width]
3626
+ * @param {Color} [color=(1,1,1,1)]
3627
+ * @param {boolean} [wrap] - Should the last point connect to the first?
3628
+ * @param {Vector2} [pos=(0,0)] - Offset to apply
3629
+ * @param {number} [angle] - Angle to rotate by
3630
+ * @param {boolean} [useWebGL=glEnable]
3631
+ * @param {boolean} [screenSpace]
3632
+ * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
3633
+ * @memberof Draw */
3634
+ function drawLineList(points, width=.1, color, wrap=false, pos=vec2(), angle=0, useWebGL=glEnable, screenSpace, context)
3635
+ {
3636
+ ASSERT(Array.isArray(points), 'drawLineList points should be an array');
3637
+ ASSERT(isNumber(width), 'drawLineList width should be a number');
3638
+ ASSERT(isColor(color), 'drawLineList color is invalid');
3639
+ ASSERT(isVector2(pos), 'drawLineList pos should be a vec2');
3640
+ ASSERT(isNumber(angle), 'drawLineList angle should be a number');
3641
+ ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
3642
+ if (useWebGL)
3643
+ {
3644
+ let scale = 1;
3645
+ if (screenSpace)
3646
+ {
3647
+ // convert to world space
3648
+ pos = screenToWorld(pos);
3649
+ scale = 1/cameraScale;
3650
+ }
3651
+ glDrawOutlineTransform(points, color.rgbaInt(), width, pos.x, pos.y, scale, scale, angle, wrap);
3652
+ }
3653
+ else
3654
+ {
3655
+ // normal canvas 2D rendering method (slower)
3656
+ ++drawCount;
3657
+ drawCanvas2D(pos, vec2(1), angle, false, (context)=>
3658
+ {
3659
+ context.strokeStyle = color.toString();
3660
+ context.lineWidth = width;
3661
+ context.beginPath();
3662
+ for (let i=0; i<points.length; ++i)
3663
+ {
3664
+ const point = points[i];
3665
+ if (i)
3666
+ context.lineTo(point.x, point.y);
3667
+ else
3668
+ context.moveTo(point.x, point.y);
3669
+ }
3670
+ if (wrap)
3671
+ context.closePath();
3672
+ context.stroke();
3673
+ }, screenSpace, context);
3674
+ }
3675
+ }
3676
+
3557
3677
  /** Draw colored line between two points
3558
3678
  * @param {Vector2} posA
3559
3679
  * @param {Vector2} posB
3560
- * @param {number} [thickness]
3680
+ * @param {number} [width]
3561
3681
  * @param {Color} [color=(1,1,1,1)]
3562
3682
  * @param {Vector2} [pos=(0,0)] - Offset to apply
3563
3683
  * @param {number} [angle] - Angle to rotate by
@@ -3565,10 +3685,10 @@ function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
3565
3685
  * @param {boolean} [screenSpace]
3566
3686
  * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
3567
3687
  * @memberof Draw */
3568
- function drawLine(posA, posB, thickness=.1, color, pos=vec2(), angle=0, useWebGL, screenSpace, context)
3688
+ function drawLine(posA, posB, width=.1, color, pos=vec2(), angle=0, useWebGL, screenSpace, context)
3569
3689
  {
3570
3690
  const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2);
3571
- const size = vec2(thickness, halfDelta.length()*2);
3691
+ const size = vec2(width, halfDelta.length()*2);
3572
3692
  pos = pos.add(posA.add(halfDelta));
3573
3693
  angle += halfDelta.angle();
3574
3694
  drawRect(pos, size, color, angle, useWebGL, screenSpace, context);
@@ -3588,12 +3708,16 @@ function drawLine(posA, posB, thickness=.1, color, pos=vec2(), angle=0, useWebGL
3588
3708
  * @memberof Draw */
3589
3709
  function drawRegularPoly(pos, size=vec2(1), sides=3, color=WHITE, lineWidth=0, lineColor=BLACK, angle=0, useWebGL=glEnable, screenSpace=false, context)
3590
3710
  {
3711
+ ASSERT(isVector2(size), 'drawRegularPoly size should be a vec2');
3712
+ ASSERT(isNumber(sides), 'drawRegularPoly sides should be a number');
3713
+
3591
3714
  // build regular polygon points
3592
3715
  const points = [];
3716
+ const sizeX = size.x/2, sizeY = size.y/2;
3593
3717
  for (let i=sides; i--;)
3594
3718
  {
3595
3719
  const a = (i/sides)*PI*2;
3596
- points.push(vec2(Math.sin(a)*size.x, Math.cos(a)*size.y));
3720
+ points.push(vec2(Math.sin(a)*sizeX, Math.cos(a)*sizeY));
3597
3721
  }
3598
3722
  drawPoly(points, color, lineWidth, lineColor, pos, angle, useWebGL, screenSpace, context);
3599
3723
  }
@@ -3611,7 +3735,7 @@ function drawRegularPoly(pos, size=vec2(1), sides=3, color=WHITE, lineWidth=0, l
3611
3735
  * @memberof Draw */
3612
3736
  function drawPoly(points, color=WHITE, lineWidth=0, lineColor=BLACK, pos=vec2(), angle=0, useWebGL=glEnable, screenSpace=false, context=undefined)
3613
3737
  {
3614
- ASSERT(isVector2(pos) && pos.isValid(), 'drawPoly pos should be a vec2');
3738
+ ASSERT(isVector2(pos), 'drawPoly pos should be a vec2');
3615
3739
  ASSERT(Array.isArray(points), 'drawPoly points should be an array');
3616
3740
  ASSERT(isColor(color) && isColor(lineColor), 'drawPoly color is invalid');
3617
3741
  ASSERT(isNumber(lineWidth), 'drawPoly lineWidth should be a number');
@@ -3652,7 +3776,7 @@ function drawPoly(points, color=WHITE, lineWidth=0, lineColor=BLACK, pos=vec2(),
3652
3776
 
3653
3777
  /** Draw colored ellipse using passed in point
3654
3778
  * @param {Vector2} pos
3655
- * @param {Vector2} [size=(1,1)]
3779
+ * @param {Vector2} [size=(1,1)] - Width and height diameter
3656
3780
  * @param {Color} [color=(1,1,1,1)]
3657
3781
  * @param {number} [angle]
3658
3782
  * @param {number} [lineWidth]
@@ -3663,8 +3787,8 @@ function drawPoly(points, color=WHITE, lineWidth=0, lineColor=BLACK, pos=vec2(),
3663
3787
  * @memberof Draw */
3664
3788
  function drawEllipse(pos, size=vec2(1), color=WHITE, angle=0, lineWidth=0, lineColor=BLACK, useWebGL=glEnable, screenSpace=false, context)
3665
3789
  {
3666
- ASSERT(isVector2(pos) && pos.isValid(), 'drawEllipse pos should be a vec2');
3667
- ASSERT(isVector2(size) && size.isValid(), 'drawEllipse size should be a vec2');
3790
+ ASSERT(isVector2(pos), 'drawEllipse pos should be a vec2');
3791
+ ASSERT(isVector2(size), 'drawEllipse size should be a vec2');
3668
3792
  ASSERT(isColor(color) && isColor(lineColor), 'drawEllipse color is invalid');
3669
3793
  ASSERT(isNumber(angle), 'drawEllipse angle should be a number');
3670
3794
  ASSERT(isNumber(lineWidth), 'drawEllipse lineWidth should be a number');
@@ -3682,7 +3806,7 @@ function drawEllipse(pos, size=vec2(1), color=WHITE, angle=0, lineWidth=0, lineC
3682
3806
  {
3683
3807
  context.fillStyle = color.toString();
3684
3808
  context.beginPath();
3685
- context.ellipse(0, 0, size.x, size.y, 0, 0, 9);
3809
+ context.ellipse(0, 0, size.x/2, size.y/2, 0, 0, 9);
3686
3810
  context.fill();
3687
3811
  if (lineWidth)
3688
3812
  {
@@ -3696,7 +3820,7 @@ function drawEllipse(pos, size=vec2(1), color=WHITE, angle=0, lineWidth=0, lineC
3696
3820
 
3697
3821
  /** Draw colored circle using passed in point
3698
3822
  * @param {Vector2} pos
3699
- * @param {number} [radius=1]
3823
+ * @param {number} [size=1] - Diameter
3700
3824
  * @param {Color} [color=(1,1,1,1)]
3701
3825
  * @param {number} [lineWidth=0]
3702
3826
  * @param {Color} [lineColor=(0,0,0,1)]
@@ -3704,8 +3828,11 @@ function drawEllipse(pos, size=vec2(1), color=WHITE, angle=0, lineWidth=0, lineC
3704
3828
  * @param {boolean} [screenSpace]
3705
3829
  * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
3706
3830
  * @memberof Draw */
3707
- function drawCircle(pos, radius=1, color=WHITE, lineWidth=0, lineColor=BLACK, useWebGL=glEnable, screenSpace=false, context)
3708
- { drawEllipse(pos, vec2(radius), color, 0, lineWidth, lineColor, useWebGL, screenSpace, context); }
3831
+ function drawCircle(pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, useWebGL=glEnable, screenSpace=false, context)
3832
+ {
3833
+ ASSERT(isNumber(size), 'drawCircle size should be a number');
3834
+ drawEllipse(pos, vec2(size), color, 0, lineWidth, lineColor, useWebGL, screenSpace, context);
3835
+ }
3709
3836
 
3710
3837
  /** Draw directly to a 2d canvas context in world space
3711
3838
  * @param {Vector2} pos
@@ -3791,7 +3918,6 @@ function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=B
3791
3918
  context.textAlign = textAlign;
3792
3919
  context.font = size + 'px '+ font;
3793
3920
  context.textBaseline = 'middle';
3794
- context.lineJoin = 'round';
3795
3921
 
3796
3922
  const lines = (text+'').split('\n');
3797
3923
  let posY = pos.y;
@@ -3991,7 +4117,7 @@ let engineFontImage;
3991
4117
  * const font = new FontImage;
3992
4118
  *
3993
4119
  * // draw text
3994
- * font.drawTextScreen("LittleJS\nHello World!", vec2(200, 50));
4120
+ * font.drawTextScreen('LittleJS\nHello World!', vec2(200, 50));
3995
4121
  */
3996
4122
  class FontImage
3997
4123
  {
@@ -5314,7 +5440,6 @@ function zzfxG
5314
5440
  * - Caches arrays of tiles to off screen canvas for fast rendering
5315
5441
  * - Unlimited numbers of layers, allocates canvases as needed
5316
5442
  * - Tile layers can be drawn to using their context with canvas2d
5317
- * - Drawn directly to the main canvas without using WebGL
5318
5443
  * - Tile layers can also have collision with EngineObjects
5319
5444
  * @namespace TileCollision
5320
5445
  */
@@ -6516,7 +6641,7 @@ let glAntialias = true;
6516
6641
  let glShader, glPolyShader, glPolyMode, glAdditive, glBatchAdditive, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glBatchCount;
6517
6642
 
6518
6643
  // WebGL internal constants
6519
- const gl_ARRAY_BUFFER_SIZE = 4e5;
6644
+ const gl_ARRAY_BUFFER_SIZE = 5e5;
6520
6645
  const gl_INDICES_PER_INSTANCE = 11;
6521
6646
  const gl_INSTANCE_BYTE_STRIDE = gl_INDICES_PER_INSTANCE * 4;
6522
6647
  const gl_MAX_INSTANCES = gl_ARRAY_BUFFER_SIZE / gl_INSTANCE_BYTE_STRIDE | 0;
@@ -6610,9 +6735,9 @@ function glInit()
6610
6735
  glContext.bufferData(glContext.ARRAY_BUFFER, geometry, glContext.STATIC_DRAW);
6611
6736
  }
6612
6737
 
6613
- function glSetInstancedMode(force=false)
6738
+ function glSetInstancedMode()
6614
6739
  {
6615
- if (!glPolyMode && !force)
6740
+ if (!glPolyMode)
6616
6741
  return;
6617
6742
 
6618
6743
  // setup instanced mode
@@ -6693,7 +6818,7 @@ function glPreRender()
6693
6818
  1, 1, 1, 0,
6694
6819
  p.x, p.y, 0, 1];
6695
6820
 
6696
- // set the same matrix for both shaders
6821
+ // set the same transform matrix for both shaders
6697
6822
  const initUniform = (program, uniform, value) =>
6698
6823
  {
6699
6824
  glContext.useProgram(program);
@@ -6711,9 +6836,12 @@ function glPreRender()
6711
6836
  glContext.bindTexture(glContext.TEXTURE_2D, glActiveTexture);
6712
6837
  }
6713
6838
 
6714
- // start in instanced rendering mode with additive blending off
6715
- glAdditive = glBatchAdditive = glPolyMode = false;
6716
- glSetInstancedMode(true);
6839
+ // start with additive blending off
6840
+ glAdditive = glBatchAdditive = false;
6841
+
6842
+ // force it to enter instanced mode
6843
+ glPolyMode = true;
6844
+ glSetInstancedMode();
6717
6845
  }
6718
6846
 
6719
6847
  /** Clear the canvas and setup the viewport
@@ -6723,7 +6851,9 @@ function glClearCanvas()
6723
6851
  if (!glContext) return;
6724
6852
 
6725
6853
  // clear and set to same size as main canvas
6726
- glContext.viewport(0, 0, glCanvas.width=drawCanvas.width, glCanvas.height=drawCanvas.height);
6854
+ glCanvas.width = drawCanvas.width;
6855
+ glCanvas.height = drawCanvas.height;
6856
+ glContext.viewport(0, 0, glCanvas.width, glCanvas.height);
6727
6857
  glContext.clear(glContext.COLOR_BUFFER_BIT);
6728
6858
  }
6729
6859
 
@@ -6858,15 +6988,17 @@ function glFlush()
6858
6988
  const destBlend = glBatchAdditive ? glContext.ONE : glContext.ONE_MINUS_SRC_ALPHA;
6859
6989
  glContext.blendFuncSeparate(glContext.SRC_ALPHA, destBlend, glContext.ONE, destBlend);
6860
6990
  glContext.enable(glContext.BLEND);
6861
- glContext.bufferSubData(glContext.ARRAY_BUFFER, 0, glPositionData);
6991
+
6992
+ const byteLength = glBatchCount *
6993
+ (glPolyMode ? gl_INDICES_PER_POLY_VERTEX : gl_INDICES_PER_INSTANCE);
6994
+ glContext.bufferSubData(glContext.ARRAY_BUFFER, 0, glPositionData, 0, byteLength);
6862
6995
 
6863
6996
  // draw the batch
6864
6997
  if (glPolyMode)
6865
6998
  glContext.drawArrays(glContext.TRIANGLE_STRIP, 0, glBatchCount);
6866
6999
  else
6867
7000
  glContext.drawArraysInstanced(glContext.TRIANGLE_STRIP, 0, 4, glBatchCount);
6868
- if (debug || showWatermark)
6869
- drawCount += glBatchCount;
7001
+ drawCount += glBatchCount;
6870
7002
  glBatchCount = 0;
6871
7003
  }
6872
7004
  glBatchAdditive = glAdditive;
@@ -6930,7 +7062,7 @@ function glDraw(x, y, sizeX, sizeY, angle=0, uv0X=0, uv0Y=0, uv1X=1, uv1Y=1, rgb
6930
7062
  }
6931
7063
 
6932
7064
  /** Transform and add a polygon to the gl draw list
6933
- * @param {Array} points - Array of Vector2 points
7065
+ * @param {Array<Vector2>} points - Array of Vector2 points
6934
7066
  * @param {number} rgba - Color of the polygon as a 32-bit integer
6935
7067
  * @param {number} x
6936
7068
  * @param {number} y
@@ -6956,7 +7088,7 @@ function glDrawPointsTransform(points, rgba, x, y, sx, sy, angle, tristrip=true)
6956
7088
  }
6957
7089
 
6958
7090
  /** Transform and add a polygon to the gl draw list
6959
- * @param {Array} points - Array of Vector2 points
7091
+ * @param {Array<Vector2>} points - Array of Vector2 points
6960
7092
  * @param {number} rgba - Color of the polygon as a 32-bit integer
6961
7093
  * @param {number} lineWidth - Width of the outline
6962
7094
  * @param {number} x
@@ -6964,61 +7096,73 @@ function glDrawPointsTransform(points, rgba, x, y, sx, sy, angle, tristrip=true)
6964
7096
  * @param {number} sx
6965
7097
  * @param {number} sy
6966
7098
  * @param {number} angle
7099
+ * @param {boolean} [wrap] - Should the outline connect the first and last points
6967
7100
  * @memberof WebGL */
6968
- function glDrawOutlineTransform(points, rgba, lineWidth, x, y, sx, sy, angle)
7101
+ function glDrawOutlineTransform(points, rgba, lineWidth, x, y, sx, sy, angle, wrap=true)
6969
7102
  {
6970
- const outlinePoints = glMakeOutline(points, lineWidth);
7103
+ const outlinePoints = glMakeOutline(points, lineWidth, wrap);
6971
7104
  glDrawPointsTransform(outlinePoints, rgba, x, y, sx, sy, angle, false);
6972
7105
  }
6973
7106
 
6974
- /** Add a polygon to the gl draw list
6975
- * @param {Array} points - Array of Vector2 points in triangle strip order
6976
- * @param {number} rgba - Color of the polygon as a 32-bit integer
7107
+ /** Add a list of points to the gl draw list
7108
+ * @param {Array<Vector2>} points - Array of Vector2 points in tri strip order
7109
+ * @param {number} rgba - Color as a 32-bit integer
6977
7110
  * @memberof WebGL */
6978
7111
  function glDrawPoints(points, rgba)
6979
7112
  {
6980
7113
  if (!glEnable || points.length < 3)
6981
7114
  return; // needs at least 3 points to have area
6982
7115
 
6983
- // add 2 degenerate verts if batching with existing polys to separate them
6984
- const needsBridge = glPolyMode && glBatchCount > 0;
6985
- const bridgeVerts = needsBridge ? 2 : 0;
6986
- const vertCount = points.length + bridgeVerts;
6987
-
6988
7116
  // flush if there is not enough room or if different blend mode
6989
- if (!glPolyMode || glBatchCount+vertCount >= gl_MAX_POLY_VERTEXES || glBatchAdditive !== glAdditive)
7117
+ const vertCount = points.length + 2;
7118
+ if (glBatchCount+vertCount >= gl_MAX_POLY_VERTEXES || glBatchAdditive !== glAdditive)
6990
7119
  glFlush();
6991
7120
  glSetPolyMode();
6992
7121
 
7122
+ // setup triangle strip with degenerate verts at start and end
6993
7123
  let offset = glBatchCount * gl_INDICES_PER_POLY_VERTEX;
6994
-
6995
- // add degenerate bridge if needed (repeat last vertex of previous poly, then first of new poly)
6996
- if (needsBridge)
6997
- {
6998
- // repeat last vertex from previous batch (it's at offset - 3)
6999
- const prevOffset = offset - 3;
7000
- glPositionData[offset++] = glPositionData[prevOffset];
7001
- glPositionData[offset++] = glPositionData[prevOffset + 1];
7002
- glColorData[offset++] = glColorData[prevOffset + 2];
7003
-
7004
- // repeat first vertex of new poly
7005
- glPositionData[offset++] = points[0].x;
7006
- glPositionData[offset++] = points[0].y;
7124
+ for(let i = vertCount; i--;)
7125
+ {
7126
+ const j = clamp(i-1, 0, vertCount-3);
7127
+ const point = points[j];
7128
+ glPositionData[offset++] = point.x;
7129
+ glPositionData[offset++] = point.y;
7007
7130
  glColorData[offset++] = rgba;
7008
7131
  }
7132
+ glBatchCount += vertCount;
7133
+ }
7134
+
7135
+ /** Add a list of colored points to the gl draw list
7136
+ * @param {Array<Vector2>} points - Array of Vector2 points in tri strip order
7137
+ * @param {Array<number>} pointColors - Array of 32-bit integer colors
7138
+ * @memberof WebGL */
7139
+ function glDrawColoredPoints(points, pointColors)
7140
+ {
7141
+ if (!glEnable || points.length < 3)
7142
+ return; // needs at least 3 points to have area
7009
7143
 
7010
- // write vertices - they're already in triangle strip order
7011
- for (const point of points)
7144
+ // flush if there is not enough room or if different blend mode
7145
+ const vertCount = points.length + 2;
7146
+ if (glBatchCount+vertCount >= gl_MAX_POLY_VERTEXES || glBatchAdditive !== glAdditive)
7147
+ glFlush();
7148
+ glSetPolyMode();
7149
+
7150
+ // setup triangle strip with degenerate verts at start and end
7151
+ let offset = glBatchCount * gl_INDICES_PER_POLY_VERTEX;
7152
+ for(let i = vertCount; i--;)
7012
7153
  {
7154
+ const j = clamp(i-1, 0, vertCount-3);
7155
+ const point = points[j];
7156
+ const color = pointColors[j];
7013
7157
  glPositionData[offset++] = point.x;
7014
7158
  glPositionData[offset++] = point.y;
7015
- glColorData[offset++] = rgba;
7159
+ glColorData[offset++] = color;
7016
7160
  }
7017
7161
  glBatchCount += vertCount;
7018
7162
  }
7019
7163
 
7020
7164
  // WebGL internal function to convert polygon to outline triangle strip
7021
- function glMakeOutline(points, width)
7165
+ function glMakeOutline(points, width, wrap=true)
7022
7166
  {
7023
7167
  if (points.length < 2)
7024
7168
  return [];
@@ -7027,12 +7171,13 @@ function glMakeOutline(points, width)
7027
7171
  const strip = [];
7028
7172
  const n = points.length;
7029
7173
  const e = 1e-6;
7174
+ const miterLimit = width*100;
7030
7175
  for (let i = 0; i < n; i++)
7031
7176
  {
7032
7177
  // for each vertex, calculate normal based on adjacent edges
7033
- const prev = points[(i - 1 + n) % n];
7178
+ const prev = points[wrap ? (i - 1 + n) % n : max(i - 1, 0)];
7034
7179
  const curr = points[i];
7035
- const next = points[(i + 1) % n];
7180
+ const next = points[wrap ? (i + 1) % n : min(i + 1, n - 1)];
7036
7181
 
7037
7182
  // direction from previous to current
7038
7183
  const dx1 = curr.x - prev.x;
@@ -7071,8 +7216,8 @@ function glMakeOutline(points, width)
7071
7216
  const dot = nx1 * nx + ny1 * ny;
7072
7217
  if (dot > e)
7073
7218
  {
7074
- // scale normal by miter length
7075
- const miterLength = 1 / dot;
7219
+ // scale normal by miter length, clamped to miterLimit
7220
+ const miterLength = min(1 / dot, miterLimit);
7076
7221
  nx *= miterLength;
7077
7222
  ny *= miterLength;
7078
7223
  }
@@ -7084,7 +7229,7 @@ function glMakeOutline(points, width)
7084
7229
  strip.push(inner);
7085
7230
  strip.push(outer);
7086
7231
  }
7087
- if (strip.length > 1)
7232
+ if (strip.length > 1 && wrap)
7088
7233
  {
7089
7234
  // close the loop
7090
7235
  strip.push(strip[0]);
@@ -7119,10 +7264,8 @@ function glPolyStrip(points)
7119
7264
  if (signedArea(points) < 0)
7120
7265
  points = points.reverse();
7121
7266
 
7122
- // tolerance constants
7123
- const e = 1e-10;
7124
-
7125
7267
  // check if point is inside triangle
7268
+ const e = 1e-9;
7126
7269
  const pointInTriangle = (p, a, b, c)=>
7127
7270
  {
7128
7271
  const c1 = cross(a, b, p);
@@ -9858,7 +10001,7 @@ class Box2dPlugin
9858
10001
  case box2d.instance.b2Shape.e_circle:
9859
10002
  {
9860
10003
  const radius = shape.get_m_radius();
9861
- drawCircle(pos, radius, color, lineWidth, lineColor);
10004
+ drawCircle(pos, radius*2, color, lineWidth, lineColor);
9862
10005
  break;
9863
10006
  }
9864
10007
  case box2d.instance.b2Shape.e_edge:
@@ -10009,14 +10152,14 @@ async function box2dInit()
10009
10152
  {
10010
10153
  color = getDebugColor(color);
10011
10154
  center = box2d.vec2FromPointer(center);
10012
- drawCircle(center, radius, CLEAR_WHITE, debugLineWidth, color, false, false, overlayContext);
10155
+ drawCircle(center, radius*2, CLEAR_WHITE, debugLineWidth, color, false, false, overlayContext);
10013
10156
  };
10014
10157
  debugDraw.DrawSolidCircle = function(center, radius, axis, color)
10015
10158
  {
10016
10159
  color = getDebugColor(color);
10017
10160
  center = box2d.vec2FromPointer(center);
10018
10161
  axis = box2d.vec2FromPointer(axis).scale(radius);
10019
- drawCircle(center, radius, color, debugLineWidth, color, false, false, overlayContext);
10162
+ drawCircle(center, radius*2, color, debugLineWidth, color, false, false, overlayContext);
10020
10163
  drawLine(vec2(), axis, debugLineWidth, color, center, 0, false, false, overlayContext);
10021
10164
  };
10022
10165
  debugDraw.DrawTransform = function(transform)
@@ -10364,11 +10507,14 @@ export
10364
10507
 
10365
10508
  // Draw
10366
10509
  textureInfos,
10510
+ drawCount,
10367
10511
  tile,
10368
10512
  TileInfo,
10369
10513
  TextureInfo,
10370
10514
  mainCanvas,
10371
10515
  mainContext,
10516
+ drawCanvas,
10517
+ drawContext,
10372
10518
  overlayCanvas,
10373
10519
  overlayContext,
10374
10520
  mainCanvasSize,
@@ -10376,6 +10522,8 @@ export
10376
10522
  worldToScreen,
10377
10523
  drawTile,
10378
10524
  drawRect,
10525
+ drawRectGradient,
10526
+ drawLineList,
10379
10527
  drawLine,
10380
10528
  drawPoly,
10381
10529
  drawEllipse,
@@ -10410,6 +10558,7 @@ export
10410
10558
  glDrawPointsTransform,
10411
10559
  glDrawOutlineTransform,
10412
10560
  glDrawPoints,
10561
+ glDrawColoredPoints,
10413
10562
  glAntialias,
10414
10563
  glShader,
10415
10564
  glPolyShader,