littlejsengine 1.18.26 → 1.18.27

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.
@@ -514,6 +514,13 @@ declare module "littlejsengine" {
514
514
  * @default
515
515
  * @memberof Settings */
516
516
  export let gamepadDirectionEmulateStick: boolean;
517
+ /** If true, axes that do not rest near center are ignored on gamepads without
518
+ * standard mapping. Steering wheels and flight sticks report pedal and throttle
519
+ * axes that rest at full deflection, which otherwise reads as a stick held down.
520
+ * @type {boolean}
521
+ * @default
522
+ * @memberof Settings */
523
+ export let gamepadAxisFilterEnable: boolean;
517
524
  /** If true the WASD keys are also routed to the direction keys (for better accessibility)
518
525
  * @type {boolean}
519
526
  * @default
@@ -780,6 +787,10 @@ declare module "littlejsengine" {
780
787
  * @param {boolean} enable
781
788
  * @memberof Settings */
782
789
  export function setGamepadDirectionEmulateStick(enable: boolean): void;
790
+ /** Set if axes that do not rest near center are ignored on non-standard gamepads
791
+ * @param {boolean} enable
792
+ * @memberof Settings */
793
+ export function setGamepadAxisFilterEnable(enable: boolean): void;
783
794
  /** Set if true the WASD keys are also routed to the direction keys
784
795
  * @param {boolean} enable
785
796
  * @memberof Settings */
@@ -1817,6 +1828,11 @@ declare module "littlejsengine" {
1817
1828
  * @type {OffscreenCanvasRenderingContext2D}
1818
1829
  * @memberof Draw */
1819
1830
  export let workReadContext: OffscreenCanvasRenderingContext2D;
1831
+ /** Extra canvas to composite behind the engine canvases when combining canvases
1832
+ * Set by plugins that render to their own canvas below the LittleJS canvases
1833
+ * @type {HTMLCanvasElement}
1834
+ * @memberof Draw */
1835
+ export let backgroundCanvas: HTMLCanvasElement;
1820
1836
  /** The size of the main canvas (and other secondary canvases)
1821
1837
  * @type {Vector2}
1822
1838
  * @memberof Draw */
@@ -2050,6 +2066,12 @@ declare module "littlejsengine" {
2050
2066
  * @param {boolean} [additive]
2051
2067
  * @memberof Draw */
2052
2068
  export function setAdditiveBlendMode(additive?: boolean): void;
2069
+ /** Set an extra canvas to composite behind the engine canvases when combining
2070
+ * Plugins that insert their own canvas below the LittleJS canvases should set
2071
+ * this so it appears in screenshots and video capture
2072
+ * @param {HTMLCanvasElement} [canvas]
2073
+ * @memberof Draw */
2074
+ export function setBackgroundCanvas(canvas?: HTMLCanvasElement): void;
2053
2075
  /** Combines LittleJS canvases onto the main canvas
2054
2076
  * This is necessary for things like screenshots and video
2055
2077
  * @memberof Draw */
@@ -35,7 +35,7 @@ const engineName = 'LittleJS';
35
35
  * @type {string}
36
36
  * @default
37
37
  * @memberof Engine */
38
- const engineVersion = '1.18.26';
38
+ const engineVersion = '1.18.27';
39
39
 
40
40
  /** Frames per second to update
41
41
  * @type {number}
@@ -3037,6 +3037,14 @@ let gamepadsEnable = true;
3037
3037
  * @memberof Settings */
3038
3038
  let gamepadDirectionEmulateStick = true;
3039
3039
 
3040
+ /** If true, axes that do not rest near center are ignored on gamepads without
3041
+ * standard mapping. Steering wheels and flight sticks report pedal and throttle
3042
+ * axes that rest at full deflection, which otherwise reads as a stick held down.
3043
+ * @type {boolean}
3044
+ * @default
3045
+ * @memberof Settings */
3046
+ let gamepadAxisFilterEnable = true;
3047
+
3040
3048
  /** If true the WASD keys are also routed to the direction keys (for better accessibility)
3041
3049
  * @type {boolean}
3042
3050
  * @default
@@ -3375,6 +3383,11 @@ function setGamepadsEnable(enable) { gamepadsEnable = enable; }
3375
3383
  * @memberof Settings */
3376
3384
  function setGamepadDirectionEmulateStick(enable) { gamepadDirectionEmulateStick = enable; }
3377
3385
 
3386
+ /** Set if axes that do not rest near center are ignored on non-standard gamepads
3387
+ * @param {boolean} enable
3388
+ * @memberof Settings */
3389
+ function setGamepadAxisFilterEnable(enable) { gamepadAxisFilterEnable = enable; }
3390
+
3378
3391
  /** Set if true the WASD keys are also routed to the direction keys
3379
3392
  * @param {boolean} enable
3380
3393
  * @memberof Settings */
@@ -4119,6 +4132,12 @@ let workReadCanvas;
4119
4132
  * @memberof Draw */
4120
4133
  let workReadContext;
4121
4134
 
4135
+ /** Extra canvas to composite behind the engine canvases when combining canvases
4136
+ * Set by plugins that render to their own canvas below the LittleJS canvases
4137
+ * @type {HTMLCanvasElement}
4138
+ * @memberof Draw */
4139
+ let backgroundCanvas;
4140
+
4122
4141
  /** The size of the main canvas (and other secondary canvases)
4123
4142
  * @type {Vector2}
4124
4143
  * @memberof Draw */
@@ -5270,6 +5289,13 @@ function setAdditiveBlendMode(additive=true)
5270
5289
  drawContext.globalCompositeOperation = additive ? 'lighter' : 'source-over';
5271
5290
  }
5272
5291
 
5292
+ /** Set an extra canvas to composite behind the engine canvases when combining
5293
+ * Plugins that insert their own canvas below the LittleJS canvases should set
5294
+ * this so it appears in screenshots and video capture
5295
+ * @param {HTMLCanvasElement} [canvas]
5296
+ * @memberof Draw */
5297
+ function setBackgroundCanvas(canvas) { backgroundCanvas = canvas; }
5298
+
5273
5299
  /** Combines LittleJS canvases onto the main canvas
5274
5300
  * This is necessary for things like screenshots and video
5275
5301
  * @memberof Draw */
@@ -5282,6 +5308,8 @@ function combineCanvases()
5282
5308
  // leaving workContext.fillStyle transparent can't silently no-op this
5283
5309
  workContext.fillStyle = '#000';
5284
5310
  workContext.fillRect(0,0,w,h);
5311
+ if (backgroundCanvas)
5312
+ workContext.drawImage(backgroundCanvas, 0, 0, w, h);
5285
5313
  glCopyToContext(workContext);
5286
5314
  workContext.drawImage(mainCanvas, 0, 0);
5287
5315
  mainContext.drawImage(workCanvas, 0, 0);
@@ -5680,6 +5708,7 @@ function inputClear()
5680
5708
  touchGamepadStickPointerId.length = 0; // release floating sticks so they re-anchor
5681
5709
  gamepadStickData.length = 0;
5682
5710
  gamepadDpadData.length = 0;
5711
+ gamepadAxisCentered.length = 0;
5683
5712
  }
5684
5713
 
5685
5714
  ///////////////////////////////////////////////////////////////////////////////
@@ -5917,6 +5946,11 @@ const inputData = [[]];
5917
5946
 
5918
5947
  // gamepad internal variables
5919
5948
  const gamepadStickData = [], gamepadDpadData = [], gamepadHadInput = [];
5949
+ // per gamepad, how many consecutive frames each axis has rested inside the
5950
+ // dead zone, used to tell stick axes from axes that rest at full deflection
5951
+ const gamepadAxisCentered = [];
5952
+ // how long an axis must rest inside the dead zone before it counts as a stick
5953
+ const gamepadAxisCenteredFrames = 15;
5920
5954
 
5921
5955
  // touch gamepad internal variables
5922
5956
  const touchGamepadTimer = new Timer, touchGamepadButtons = [], touchGamepadSticks = [];
@@ -6165,7 +6199,7 @@ function inputUpdate()
6165
6199
  // gamepad: any button held or stick moved
6166
6200
  let gamepadActive = false;
6167
6201
  for (let s = gamepadStickCount(); s-- && !gamepadActive;)
6168
- gamepadActive = gamepadStick(s).lengthSquared() > .04;
6202
+ gamepadActive = gamepadStick(s).lengthSquared() > .2;
6169
6203
  for (let b = 17; b-- && !gamepadActive;)
6170
6204
  gamepadActive = gamepadIsDown(b);
6171
6205
 
@@ -6193,12 +6227,12 @@ function inputUpdate()
6193
6227
  // gamepads are updated by engine every frame automatically
6194
6228
  function gamepadsUpdate()
6195
6229
  {
6230
+ const deadZoneMin=.3, deadZoneMax=.8;
6196
6231
  const applyDeadZones = (v)=>
6197
6232
  {
6198
- const min=.3, max=.8;
6199
6233
  const deadZone = (v)=>
6200
- v > min ? percent(v, min, max) :
6201
- v < -min ? -percent(-v, min, max) : 0;
6234
+ v > deadZoneMin ? percent(v, deadZoneMin, deadZoneMax) :
6235
+ v < -deadZoneMin ? -percent(-v, deadZoneMin, deadZoneMax) : 0;
6202
6236
  return vec2(deadZone(v.x), deadZone(-v.y)).clampLength();
6203
6237
  };
6204
6238
 
@@ -6282,6 +6316,7 @@ function inputUpdate()
6282
6316
  gamepadStickData[i] = undefined;
6283
6317
  gamepadDpadData[i] = undefined;
6284
6318
  gamepadHadInput[i] = undefined;
6319
+ gamepadAxisCentered[i] = undefined;
6285
6320
  continue;
6286
6321
  }
6287
6322
 
@@ -6290,8 +6325,30 @@ function inputUpdate()
6290
6325
  const dpad = gamepadDpadData[i] ?? (gamepadDpadData[i] = vec2());
6291
6326
 
6292
6327
  // read analog sticks
6328
+ // gamepads without standard mapping (steering wheels, flight sticks)
6329
+ // can report axes that rest at full deflection instead of center,
6330
+ // which would otherwise read as a stick held down forever, so only
6331
+ // trust an axis once it has rested inside the dead zone for a moment
6332
+ const isStandard = gamepad.mapping === 'standard';
6333
+ const centered = gamepadAxisCentered[i] ?? (gamepadAxisCentered[i] = []);
6334
+ const readAxis = (j)=>
6335
+ {
6336
+ const v = gamepad.axes[j];
6337
+ if (isStandard && j < 4)
6338
+ return v; // spec guarantees axes 0-3 are the two sticks
6339
+ if (!gamepadAxisFilterEnable)
6340
+ return v;
6341
+
6342
+ // once an axis has proven it rests at center it stays trusted,
6343
+ // otherwise moving it would immediately disqualify it again
6344
+ const frames = centered[j] | 0;
6345
+ if (frames > gamepadAxisCenteredFrames)
6346
+ return v;
6347
+ centered[j] = abs(v) < deadZoneMin ? frames + 1 : 0;
6348
+ return 0;
6349
+ };
6293
6350
  for (let j = 0; j < gamepad.axes.length-1; j+=2)
6294
- sticks[j>>1] = applyDeadZones(vec2(gamepad.axes[j],gamepad.axes[j+1]));
6351
+ sticks[j>>1] = applyDeadZones(vec2(readAxis(j), readAxis(j+1)));
6295
6352
 
6296
6353
  // read buttons
6297
6354
  let hadInput = false;
@@ -16808,6 +16865,9 @@ class ThreeJSPlugin
16808
16865
  rootElement.insertBefore(threeCanvas, rootElement.firstChild);
16809
16866
  threeCanvas.style.cssText = mainCanvas.style.cssText;
16810
16867
 
16868
+ // composite the 3D canvas into screenshots and video capture
16869
+ setBackgroundCanvas(threeCanvas);
16870
+
16811
16871
  // render automatically each frame after the engine renders
16812
16872
  engineAddPlugin(undefined, ()=> this.render());
16813
16873
  }
@@ -16992,6 +17052,7 @@ export
16992
17052
  glCircleSides,
16993
17053
  gamepadsEnable,
16994
17054
  gamepadDirectionEmulateStick,
17055
+ gamepadAxisFilterEnable,
16995
17056
  inputWASDEmulateDirection,
16996
17057
  touchInputEnable,
16997
17058
  touchGamepadEnable,
@@ -17048,6 +17109,7 @@ export
17048
17109
  setTouchInputEnable,
17049
17110
  setGamepadsEnable,
17050
17111
  setGamepadDirectionEmulateStick,
17112
+ setGamepadAxisFilterEnable,
17051
17113
  setInputWASDEmulateDirection,
17052
17114
  setTouchGamepadEnable,
17053
17115
  setTouchGamepadPassthrough,
@@ -17163,6 +17225,7 @@ export
17163
17225
  workContext,
17164
17226
  workReadCanvas,
17165
17227
  workReadContext,
17228
+ backgroundCanvas,
17166
17229
  mainCanvasSize,
17167
17230
  textureInfos,
17168
17231
  drawCount,
@@ -17188,6 +17251,7 @@ export
17188
17251
  drawText,
17189
17252
  drawTextScreen,
17190
17253
  setAdditiveBlendMode,
17254
+ setBackgroundCanvas,
17191
17255
  combineCanvases,
17192
17256
  engineImageFont,
17193
17257
  ImageFont,