littlejsengine 1.25.0 → 1.26.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 (51) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/FAQ.md +49 -11
  3. package/README.md +6 -2
  4. package/REFERENCE.md +120 -18
  5. package/dist/littlejs.d.ts +464 -99
  6. package/dist/littlejs.esm.js +2853 -704
  7. package/dist/littlejs.esm.min.js +3 -3
  8. package/dist/littlejs.js +2844 -701
  9. package/dist/littlejs.min.js +2 -2
  10. package/dist/littlejs.release.js +2658 -512
  11. package/package.json +4 -4
  12. package/plugins/box2d.js +110 -34
  13. package/plugins/gltf.js +74 -22
  14. package/plugins/lightSystem.js +410 -28
  15. package/plugins/math3d.js +4 -2
  16. package/plugins/medalSystem.js +19 -7
  17. package/plugins/newgrounds.js +4 -2
  18. package/plugins/parallax.js +1 -0
  19. package/plugins/particleEffects.js +44 -10
  20. package/plugins/pathFinder.js +9 -11
  21. package/plugins/pluginExport.js +9 -0
  22. package/plugins/postProcess.js +4 -2
  23. package/plugins/render3d.js +485 -2385
  24. package/plugins/render3dDebug.js +5 -8
  25. package/plugins/render3dEditor.js +81 -6
  26. package/plugins/render3dExtras.js +103 -31
  27. package/plugins/render3dLevel.js +53 -5
  28. package/plugins/render3dMesh.js +1158 -0
  29. package/plugins/render3dObject.js +1451 -0
  30. package/plugins/render3dVoxels.js +15 -3
  31. package/plugins/textureSheet.js +9 -8
  32. package/plugins/threejs.js +1 -1
  33. package/plugins/tweenSystem.js +76 -51
  34. package/plugins/uiSystem.js +37 -8
  35. package/plugins/wavedash.js +229 -0
  36. package/src/engine.js +184 -36
  37. package/src/engineAudio.js +37 -21
  38. package/src/engineBuild.mjs +6 -1
  39. package/src/engineDebug.js +22 -162
  40. package/src/engineDraw.js +139 -34
  41. package/src/engineEditor.js +79 -18
  42. package/src/engineExport.js +0 -3
  43. package/src/engineInput.js +27 -18
  44. package/src/engineMath.js +35 -14
  45. package/src/engineObject.js +60 -41
  46. package/src/engineParticles.js +58 -11
  47. package/src/engineRelease.js +1 -5
  48. package/src/engineSettings.js +48 -9
  49. package/src/engineTileLayer.js +11 -4
  50. package/src/engineUtilities.js +41 -9
  51. package/src/engineWebGL.js +37 -13
@@ -458,7 +458,11 @@ let soundPauseWhenHidden = true;
458
458
  /** Set position of camera in world space
459
459
  * @param {Vector2} pos
460
460
  * @memberof Settings */
461
- function setCameraPos(pos) { cameraPos = pos.copy(); }
461
+ function setCameraPos(pos)
462
+ {
463
+ ASSERT(isVector2(pos), 'setCameraPos: pos is a vec2, like vec2(5, 2)', pos);
464
+ cameraPos = pos.copy();
465
+ }
462
466
 
463
467
  /** Set angle of camera in world space
464
468
  * @param {number} angle
@@ -472,6 +476,7 @@ function setCameraScale(scale) { cameraScale = scale; }
472
476
 
473
477
  /** Set scale applied to engine time
474
478
  * - 0 freezes the game like a pause, gameUpdatePost and input still run so the game can set it back
479
+ * - Above 1 the fixed step runs that many updates each frame, so a large scale costs as many updates
475
480
  * @param {number} scale - 0 or more
476
481
  * @memberof Settings */
477
482
  function setTimeScale(scale)
@@ -491,12 +496,20 @@ function setCanvasColorTiles(colorTiles) { canvasColorTiles = colorTiles; }
491
496
  /** Set color to clear the canvas to before render, does not clear if alpha is 0
492
497
  * @param {Color} color
493
498
  * @memberof Settings */
494
- function setCanvasClearColor(color) { canvasClearColor = color.copy(); }
499
+ function setCanvasClearColor(color)
500
+ {
501
+ ASSERT(isColor(color), 'setCanvasClearColor: color is a Color, like hsl(.6, .5, .2)', color);
502
+ canvasClearColor = color.copy();
503
+ }
495
504
 
496
505
  /** Set max size of the canvas
497
506
  * @param {Vector2} size
498
507
  * @memberof Settings */
499
- function setCanvasMaxSize(size) { canvasMaxSize = size.copy(); }
508
+ function setCanvasMaxSize(size)
509
+ {
510
+ ASSERT(isVector2(size), 'setCanvasMaxSize: size is a vec2, like vec2(1920, 1080)', size);
511
+ canvasMaxSize = size.copy();
512
+ }
500
513
 
501
514
  /** Set minimum aspect ratio of the canvas (width/height), unused if 0
502
515
  * @param {number} aspect
@@ -511,7 +524,11 @@ function setCanvasMaxAspect(aspect) { canvasMaxAspect = aspect; }
511
524
  /** Set fixed size of the canvas
512
525
  * @param {Vector2} size
513
526
  * @memberof Settings */
514
- function setCanvasFixedSize(size) { canvasFixedSize = size.copy(); }
527
+ function setCanvasFixedSize(size)
528
+ {
529
+ ASSERT(isVector2(size), 'setCanvasFixedSize: size is a vec2, like vec2(1280, 720)', size);
530
+ canvasFixedSize = size.copy();
531
+ }
515
532
 
516
533
  /** Use nearest scaling algorithm for canvas for more pixelated look
517
534
  * @param {boolean} pixelated
@@ -553,6 +570,7 @@ function setCanvasPixelRatio(pixelRatio) { canvasPixelRatio = pixelRatio; }
553
570
  function getCanvasPixelRatio() { return canvasPixelRatio ?? (devicePixelRatio || 1); }
554
571
 
555
572
  /** Set default font used for text rendering
573
+ * - A family name with spaces or digits goes in quotes, as "'Press Start 2P'", or the canvas does not take it
556
574
  * @param {string} font
557
575
  * @memberof Settings */
558
576
  function setFontDefault(font) { fontDefault = font; }
@@ -641,7 +659,11 @@ function setGLCircleSides(sides)
641
659
  /** Set default size of tiles in pixels
642
660
  * @param {Vector2} size
643
661
  * @memberof Settings */
644
- function setTileDefaultSize(size) { tileDefaultSize = size.copy(); }
662
+ function setTileDefaultSize(size)
663
+ {
664
+ ASSERT(isVector2(size), 'setTileDefaultSize: size is a vec2, like vec2(16)', size);
665
+ tileDefaultSize = size.copy();
666
+ }
645
667
 
646
668
  /** Default padding pixels around tiles
647
669
  * @param {number} padding
@@ -666,12 +688,23 @@ function setObjectDefaultMass(mass) { objectDefaultMass = mass; }
666
688
  /** Set the fraction of velocity objects keep each frame, 1 keeps all of it, 0 stops at once
667
689
  * @param {number} damp
668
690
  * @memberof Settings */
669
- function setObjectDefaultDamping(damp) { objectDefaultDamping = damp; }
691
+ function setObjectDefaultDamping(damp)
692
+ {
693
+ ASSERT(damp >= 0 && damp <= 1,
694
+ 'setObjectDefaultDamping: damping is 0 to 1, the fraction of velocity kept each frame', damp);
695
+ objectDefaultDamping = damp;
696
+ }
670
697
 
671
698
  /** Set the fraction of angular velocity objects keep each frame, 1 keeps all of it, 0 stops at once
672
699
  * @param {number} damp
673
700
  * @memberof Settings */
674
- function setObjectDefaultAngleDamping(damp) { objectDefaultAngleDamping = damp; }
701
+ function setObjectDefaultAngleDamping(damp)
702
+ {
703
+ ASSERT(damp >= 0 && damp <= 1,
704
+ 'setObjectDefaultAngleDamping: angle damping is 0 to 1, the fraction of angular velocity kept each frame',
705
+ damp);
706
+ objectDefaultAngleDamping = damp;
707
+ }
675
708
 
676
709
  /** Set how much to bounce when a collision occurs
677
710
  * @param {number} restitution
@@ -691,7 +724,11 @@ function setObjectMaxSpeed(speed) { objectMaxSpeed = speed; }
691
724
  /** Set how much gravity to apply to objects
692
725
  * @param {Vector2} newGravity
693
726
  * @memberof Settings */
694
- function setGravity(newGravity) { gravity = newGravity.copy(); }
727
+ function setGravity(newGravity)
728
+ {
729
+ ASSERT(isVector2(newGravity), 'setGravity: newGravity is a vec2, like vec2(0, -.01)', newGravity);
730
+ gravity = newGravity.copy();
731
+ }
695
732
 
696
733
  /** Set the scale for the emit rate of particles, 0 disables particle emitters
697
734
  * @param {number} scale
@@ -821,7 +858,8 @@ function setTouchGamepadVibration(ms) { touchGamepadVibration = ms; }
821
858
  * @memberof Settings */
822
859
  function setVibrateEnable(enable) { vibrateEnable = enable; }
823
860
 
824
- /** Set if audio is enabled, false turns all sound off
861
+ /** Set if audio is enabled, false turns all sound off; a setting for before sounds are made, since a sound made
862
+ * while it is off has nothing to play and one already playing goes on; to mute while the game runs, setSoundVolume(0)
825
863
  * @param {boolean} enable
826
864
  * @memberof Settings */
827
865
  function setSoundEnable(enable) { soundEnable = enable; }
@@ -840,6 +878,7 @@ function setSoundIgnoreSilentSwitch(ignore)
840
878
  * @memberof Settings */
841
879
  function setSoundVolume(volume)
842
880
  {
881
+ ASSERT(isNumber(volume) && abs(volume) < Infinity, 'setSoundVolume: volume must be a finite number, 1 is full', volume);
843
882
  soundVolume = volume;
844
883
  audioUpdateVolume(); // update gain immediately, sound off or not
845
884
  }
@@ -844,7 +844,7 @@ class TileLayer extends CanvasLayer
844
844
  redrawStart(clear=false)
845
845
  {
846
846
  if (!this.context) return;
847
- ASSERT(drawContext !== this.context);
847
+ ASSERT(drawContext !== this.context, 'redrawStart: already started, call redrawEnd() first');
848
848
  clear || this.redrawIfSwitched(); // a partial redraw goes on top of the whole layer on the side in use
849
849
 
850
850
  // save current render settings
@@ -889,7 +889,7 @@ class TileLayer extends CanvasLayer
889
889
  redrawEnd()
890
890
  {
891
891
  if (!this.context) return;
892
- ASSERT(drawContext === this.context);
892
+ ASSERT(drawContext === this.context, 'redrawEnd: call redrawStart() first');
893
893
 
894
894
  // set stuff back to normal, the camera first, so a target that was drawing before gets its own transform back
895
895
  [drawContext, mainCanvasSize, cameraPos, cameraScale, cameraAngle, canvasClearColor, glCustomShader] = this.savedRenderSettings;
@@ -1085,9 +1085,9 @@ class TileCollisionLayer extends TileLayer
1085
1085
  for (let y = y0; y < y1; ++y)
1086
1086
  for (let x = x0; x < x1; ++x)
1087
1087
  {
1088
- if (!this.collisionData[y*size.x + x]) continue;
1088
+ if (!(this.collisionData[y*size.x + x] > 0)) continue; // solid is positive, a negative is a marker
1089
1089
  let end = x + 1; // a run of solid cells along the row
1090
- while (end < x1 && this.collisionData[y*size.x + end]) ++end;
1090
+ while (end < x1 && this.collisionData[y*size.x + end] > 0) ++end;
1091
1091
  const count = end - x;
1092
1092
  const tileInfo = new TileInfo(vec2(x*cellPixels.x, textureHeight - (y+1)*cellPixels.y),
1093
1093
  vec2(count*cellPixels.x, cellPixels.y), this.textureInfo, 0, 0);
@@ -1217,7 +1217,14 @@ class TileCollisionLayer extends TileLayer
1217
1217
  const hitPos = lineTest(t0 ? a.add(d.scale(t0)) : a, t1 < 1 ? a.add(d.scale(t1)) : posEnd.subtract(offset),
1218
1218
  testFunction, normal);
1219
1219
  if (hitPos)
1220
+ {
1221
+ // into the world, kept inside its tile there too, as adding the layer's place can round it onto the edge
1222
+ const cell = hitPos.floor();
1220
1223
  hitPos.x += offset.x, hitPos.y += offset.y;
1224
+ const inside = (v, low)=> v < low ? low : v < low + 1 ? v : low + 1 - max(1e-9, abs(low) * 1e-15);
1225
+ hitPos.x = inside(hitPos.x, cell.x + offset.x);
1226
+ hitPos.y = inside(hitPos.y, cell.y + offset.y);
1227
+ }
1221
1228
  if (debugRaycast && hitPos)
1222
1229
  {
1223
1230
  const tilePos = hitPos.floor().add(vec2(.5));
@@ -29,7 +29,7 @@ class Timer
29
29
  * @param {boolean} [useRealTime] - Should the timer keep running even when the game is paused? (useful for UI) */
30
30
  constructor(timeLeft, useRealTime=false)
31
31
  {
32
- ASSERT(timeLeft === undefined || isNumber(timeLeft), 'Constructed Timer is invalid.', timeLeft);
32
+ ASSERT(timeLeft === undefined || isNumber(timeLeft), 'Timer: time must be a number of seconds', timeLeft);
33
33
  this.useRealTime = useRealTime;
34
34
  const globalTime = this.getGlobalTime();
35
35
  /** @type {number|undefined} */
@@ -42,7 +42,7 @@ class Timer
42
42
  * @param {number} [timeLeft] - How much time left before the timer is elapsed in seconds */
43
43
  set(timeLeft=0)
44
44
  {
45
- ASSERT(isNumber(timeLeft), 'Timer is invalid.', timeLeft);
45
+ ASSERT(isNumber(timeLeft), 'Timer.set: time must be a number of seconds', timeLeft);
46
46
  const globalTime = this.getGlobalTime();
47
47
  this.time = globalTime + timeLeft;
48
48
  this.setTime = timeLeft;
@@ -121,9 +121,9 @@ function formatTime(t)
121
121
  * @memberof Utilities */
122
122
  async function fetchJSON(url)
123
123
  {
124
- const response = await fetch(url);
124
+ const response = await loadFetch(url, 'fetchJSON');
125
125
  if (!response.ok)
126
- throw new Error(`Failed to fetch JSON from ${url}: ${response.status} ${response.statusText}`);
126
+ throw new Error(`fetchJSON: could not load ${url}, HTTP ${response.status} ${response.statusText}`);
127
127
  const text = await response.text();
128
128
  let json;
129
129
  try { json = JSON.parse(text); }
@@ -140,6 +140,21 @@ async function fetchJSON(url)
140
140
  // is a file's text a web page, as a dev server sends for a path it does not have
141
141
  const loadIsWebPage = (text)=> /^\s*</.test(text);
142
142
 
143
+ // fetch a file for a loader, a failure to reach it named with the file and who asked, and with the usual cause when
144
+ // the page was opened from a file on disk, where the browser lets it load nothing
145
+ async function loadFetch(url, who)
146
+ {
147
+ try { return await fetch(url); }
148
+ catch (error)
149
+ {
150
+ const fromDisk = globalThis.location?.protocol === 'file:';
151
+ const failed = new Error(`${who}: could not load ${url}, ${error.message}` + (fromDisk ?
152
+ ', the page was opened from a file, serve it from a local web server' : ''));
153
+ /** @type {any} */ (failed).cause = error; // what the browser said, as the error option newer libraries know
154
+ throw failed;
155
+ }
156
+ }
157
+
143
158
  ///////////////////////////////////////////////////////////////////////////////
144
159
 
145
160
  /** Save a text file to disk
@@ -249,7 +264,7 @@ function readSaveData(saveName, defaultSaveData)
249
264
  if (data)
250
265
  {
251
266
  try { loadedData = JSON.parse(data); }
252
- catch { LOG('readSaveData: corrupt JSON for', saveName, '— using defaults'); }
267
+ catch { console.warn('readSaveData: the save ' + saveName + ' can not be read, using the defaults'); }
253
268
  if (!loadedData || typeof loadedData !== 'object' || isArray(loadedData))
254
269
  loadedData = {}; // only an object of saved values, never a string spread into its letters
255
270
  }
@@ -268,9 +283,26 @@ function writeSaveData(saveName, saveData)
268
283
  ASSERT(isStringLike(saveName), 'writeSaveData requires saveName string');
269
284
  ASSERT(typeof saveData === 'object' && saveData !== null && !isArray(saveData),
270
285
  'writeSaveData: save data must be an object, readSaveData reads it back into one');
271
- // tolerate localStorage being unavailable or quota exceeded
272
- try { localStorage.setItem(saveName, JSON.stringify(saveData)); return true; }
273
- catch { LOG('writeSaveData: failed to write', saveName); return false; }
286
+ // tolerate localStorage being unavailable or quota exceeded; in release too, a player's save going nowhere is
287
+ // not silent, and the warning says which it was
288
+ let text;
289
+ try { text = JSON.stringify(saveData); }
290
+ catch (error)
291
+ {
292
+ console.warn('writeSaveData: the save ' + saveName + ' can not be written as JSON, ' + error.message);
293
+ return false;
294
+ }
295
+ if (text === undefined) // a toJSON that returns nothing, which would be stored as the text undefined
296
+ {
297
+ console.warn('writeSaveData: the save ' + saveName + ' is nothing as JSON, it is not written');
298
+ return false;
299
+ }
300
+ try { localStorage.setItem(saveName, text); return true; }
301
+ catch
302
+ {
303
+ console.warn('writeSaveData: the save ' + saveName + ' could not be written, storage is full or off');
304
+ return false;
305
+ }
274
306
  }
275
307
 
276
308
  ///////////////////////////////////////////////////////////////////////////////
@@ -298,7 +330,7 @@ function noise1D(x)
298
330
  return lerp(noiseHash(i), noiseHash(i + 1), smoothStep(x - i));
299
331
  }
300
332
 
301
- /** 2D value noise — returns a smooth value in [0, 1] for any real (x, y).
333
+ /** 2D value noise — returns a smooth value in [0, 1] for any real (x, y)
302
334
  * @param {number} x
303
335
  * @param {number} y
304
336
  * @return {number}
@@ -45,7 +45,7 @@ function glClampTextureSize(size) { return glMaxTextureSize ? min(size, glMaxTex
45
45
  let glAntialias = true;
46
46
 
47
47
  // WebGL internal variables not exposed to documentation
48
- let glMipmappedTextures = new WeakSet, glMipmapsUntilTarget = new WeakSet, glMipmapsStale = new Set, glPremultipliedTextures = new WeakSet, glShaderPremultiplied, glEnableBeforeLoss = true, glShader, glPolyShader, glPolyMode, glAdditive, glBatchAdditive, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glBatchCount, glTextureInfos = new Set, glInstancedVAO, glPolyVAO, glFramebuffer, glRenderTarget, glShaderObjects = [], glCustomShader, glBatchShader, glProgramCustom, glTransform, glRenderTargetSaved, glUniformLocations = new WeakMap, glCanBeEnabled = true;
48
+ let glMipmappedTextures = new WeakSet, glMipmapsUntilTarget = new WeakSet, glMipmapsStale = new Set, glPremultipliedTextures = new WeakSet, glShaderPremultiplied, glEnableBeforeLoss = true, glShader, glPolyShader, glPolyMode, glAdditive, glBatchAdditive, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glBatchCount, glTextureInfos = new Set, glInstancedVAO, glPolyVAO, glFramebuffer, glRenderTarget, glShaderObjects = [], glCustomShader, glBatchShader, glProgramCustom, glTransform, glRenderTargetSaved, glUniformLocations = new WeakMap, glCanBeEnabled = true, glDeviceFailed = false;
49
49
  let glFailedPrograms = new WeakSet; // programs that did not build in a release build, which nothing draws with
50
50
  // ANDed onto every packed color as a draw is queued; the light system's shadow pass sets 0xff000000
51
51
  // to draw everything black with its alpha kept (rgbaInt packs alpha in the top byte)
@@ -120,6 +120,7 @@ function glInit(rootElement)
120
120
  glCanvas = glContext = undefined;
121
121
  glEnable = false;
122
122
  glCanBeEnabled = false;
123
+ glDeviceFailed = true; // the device, not the game, turned it off
123
124
  return;
124
125
  }
125
126
 
@@ -224,6 +225,7 @@ function glInit(rootElement)
224
225
  {
225
226
  console.error('LittleJS: WebGL can not draw on this device, using Canvas2D');
226
227
  glEnable = glCanBeEnabled = false;
228
+ glDeviceFailed = true;
227
229
  glCanvas.style.display = 'none';
228
230
  }
229
231
 
@@ -505,6 +507,7 @@ function glUniformLocation(program, name)
505
507
  // color, then the sprite's color and additive color apply as the engine's own fragment shader does
506
508
  function glShaderProgram(shader)
507
509
  {
510
+ shader.program || glShaderTrack(shader);
508
511
  const program = shader.program ||= glCreateProgram(gl_VERTEX_SOURCE,
509
512
  '#version 300 es\n' +
510
513
  'precision highp float;' +
@@ -512,13 +515,33 @@ function glShaderProgram(shader)
512
515
  'uniform vec3 iResolution;' + // canvas size in pixels
513
516
  'uniform float iTime;' + // engine time
514
517
  'uniform bool premultipliedTexture;' + // is the texture premultiplied, a render target or a smooth image
515
- 'in vec2 v,l;in vec4 d,e;out vec4 c;\n' + // a define needs its own line
518
+ 'in vec2 v,l;in vec4 d,e;out vec4 c;' +
519
+ // main first, the snippet's mainImage declared for it, so a define in the snippet can not reach main's names
520
+ 'void mainImage(out vec4,vec2);' +
521
+ 'void main(){vec4 t;mainImage(t,v);' + gl_FRAGMENT_TINT_SOURCE + '}\n' + // a define needs its own line
516
522
  '#define localUV l\n' +
517
- shader.fragmentCode + '\n' +
518
- 'void main(){vec4 t;mainImage(t,v);' + gl_FRAGMENT_TINT_SOURCE + '}');
523
+ shader.fragmentCode + '\n');
519
524
  return glFailedPrograms.has(program) ? glShader : program;
520
525
  }
521
526
 
527
+ // a Shader compiled again after dispose is kept for a lost context again
528
+ function glShaderTrack(shader) { glShaderObjects.includes(shader) || glShaderObjects.push(shader); }
529
+
530
+ // let go of a Shader's programs, 2D and 3D, and of its place in the list; a batch drawing with it is drawn first
531
+ function glShaderDispose(shader)
532
+ {
533
+ const i = glShaderObjects.indexOf(shader);
534
+ i < 0 || glShaderObjects.splice(i, 1);
535
+ if (glContext && !headlessMode)
536
+ {
537
+ if (glBatchShader === shader) // a batch drawing with it is drawn first, the batch holds the Shader
538
+ glFlush();
539
+ for (const program of [shader.program, shader.program3D])
540
+ program && glContext.deleteProgram(program);
541
+ }
542
+ shader.program = shader.program3D = undefined;
543
+ }
544
+
522
545
  /** Create WebGL texture from an image and init the texture settings
523
546
  * Restores the active texture when done
524
547
  * @param {HTMLImageElement|HTMLCanvasElement|OffscreenCanvas|ImageBitmap} [image]
@@ -574,8 +597,16 @@ function glCreateTexture(image, wrap=false, pixelated=tilesPixelated)
574
597
  * @memberof WebGL */
575
598
  function glDeleteTexture(texture)
576
599
  {
577
- if (!glContext) return;
578
-
600
+ if (!glContext || !texture) return;
601
+
602
+ // what is batched with it is drawn first, since deleting unbinds it, and nothing keeps it after
603
+ if (texture === glActiveTexture)
604
+ {
605
+ glFlush();
606
+ glActiveTexture = undefined; // so nothing binds the deleted texture again
607
+ }
608
+ glMipmapsStale.delete(texture);
609
+ glPremultipliedTextures.delete(texture);
579
610
  glContext.deleteTexture(texture);
580
611
  }
581
612
 
@@ -651,13 +682,6 @@ function glUnregisterTextureInfo(textureInfo)
651
682
  // unset and destroy the texture, drawing what is batched with it first, since deleting unbinds it
652
683
  const glTexture = textureInfo.glTexture;
653
684
  textureInfo.glTexture = undefined;
654
- if (glTexture && glTexture === glActiveTexture)
655
- {
656
- glFlush();
657
- glActiveTexture = undefined; // so nothing binds the deleted texture again
658
- }
659
- glMipmapsStale.delete(glTexture);
660
- glPremultipliedTextures.delete(glTexture);
661
685
  glDeleteTexture(glTexture);
662
686
  }
663
687