littlejsengine 1.14.16 → 1.14.19
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.
- package/dist/littlejs.d.ts +55 -32
- package/dist/littlejs.esm.js +393 -227
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +393 -227
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +393 -227
- package/examples/box2d/game.js +3 -2
- package/examples/box2d/tiles.png +0 -0
- package/examples/breakout/game.js +5 -5
- package/examples/electron/game.js +2 -2
- package/examples/electron/index.html +2 -2
- package/examples/index.html +57 -49
- package/examples/module/game.js +2 -2
- package/examples/platformer/gameEffects.js +1 -1
- package/examples/shorts/base.html +1 -1
- package/examples/shorts/musicPlayer.js +1 -0
- package/examples/shorts/parallax.js +1 -1
- package/examples/shorts/sequencer.js +2 -2
- package/examples/shorts/shader.js +29 -0
- package/examples/shorts/tileLayer.js +1 -1
- package/examples/shorts/tiltedView.js +14 -6
- package/examples/shorts/topDown.js +1 -1
- package/examples/starter/game.js +2 -2
- package/examples/starter/index.html +2 -2
- package/examples/style.css +1 -0
- package/examples/typescript/game.js +2 -2
- package/examples/typescript/game.ts +1 -1
- package/examples/uiSystem/game.js +1 -0
- package/package.json +1 -1
- package/plugins/postProcess.js +71 -51
- package/plugins/uiSystem.js +76 -23
- package/src/engine.js +28 -14
- package/src/engineAudio.js +20 -10
- package/src/engineDraw.js +35 -29
- package/src/engineParticles.js +1 -1
- package/src/engineTileLayer.js +31 -39
- package/src/engineWebGL.js +134 -63
package/dist/littlejs.release.js
CHANGED
|
@@ -33,7 +33,7 @@ const engineName = 'LittleJS';
|
|
|
33
33
|
* @type {string}
|
|
34
34
|
* @default
|
|
35
35
|
* @memberof Engine */
|
|
36
|
-
const engineVersion = '1.14.
|
|
36
|
+
const engineVersion = '1.14.19';
|
|
37
37
|
|
|
38
38
|
/** Frames per second to update
|
|
39
39
|
* @type {number}
|
|
@@ -94,7 +94,17 @@ let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
|
|
|
94
94
|
///////////////////////////////////////////////////////////////////////////////
|
|
95
95
|
// plugin hooks
|
|
96
96
|
|
|
97
|
-
const
|
|
97
|
+
const pluginList = [];
|
|
98
|
+
class EnginePlugin
|
|
99
|
+
{
|
|
100
|
+
constructor(update, render, glContextLost, glContextRestored)
|
|
101
|
+
{
|
|
102
|
+
this.update = update;
|
|
103
|
+
this.render = render;
|
|
104
|
+
this.glContextLost = glContextLost;
|
|
105
|
+
this.glContextRestored = glContextRestored;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
98
108
|
|
|
99
109
|
/**
|
|
100
110
|
* @callback PluginCallback - Update or render function for a plugin
|
|
@@ -102,15 +112,21 @@ const pluginUpdateList = [], pluginRenderList = [];
|
|
|
102
112
|
*/
|
|
103
113
|
|
|
104
114
|
/** Add a new update function for a plugin
|
|
105
|
-
* @param {PluginCallback} [
|
|
106
|
-
* @param {PluginCallback} [
|
|
115
|
+
* @param {PluginCallback} [update]
|
|
116
|
+
* @param {PluginCallback} [render]
|
|
117
|
+
* @param {PluginCallback} [glContextLost]
|
|
118
|
+
* @param {PluginCallback} [glContextRestored]
|
|
107
119
|
* @memberof Engine */
|
|
108
|
-
function engineAddPlugin(
|
|
120
|
+
function engineAddPlugin(update, render, glContextLost, glContextRestored)
|
|
109
121
|
{
|
|
110
|
-
|
|
111
|
-
ASSERT(!
|
|
112
|
-
|
|
113
|
-
|
|
122
|
+
// make sure plugin functions are unique
|
|
123
|
+
ASSERT(!pluginList.find(p=>
|
|
124
|
+
p.update === update && p.render === render &&
|
|
125
|
+
p.glContextLost === glContextLost &&
|
|
126
|
+
p.glContextRestored === glContextRestored));
|
|
127
|
+
|
|
128
|
+
const plugin = new EnginePlugin(update, render, glContextLost, glContextRestored);
|
|
129
|
+
pluginList.push(plugin);
|
|
114
130
|
}
|
|
115
131
|
|
|
116
132
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -195,7 +211,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
195
211
|
wasUpdated = true;
|
|
196
212
|
updateCanvas();
|
|
197
213
|
inputUpdate();
|
|
198
|
-
|
|
214
|
+
pluginList.forEach(plugin=>plugin.update?.());
|
|
199
215
|
|
|
200
216
|
// update object transforms even when paused
|
|
201
217
|
for (const o of engineObjects)
|
|
@@ -228,7 +244,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
228
244
|
updateCanvas();
|
|
229
245
|
inputUpdate();
|
|
230
246
|
gameUpdate();
|
|
231
|
-
|
|
247
|
+
pluginList.forEach(plugin=>plugin.update?.());
|
|
232
248
|
engineObjectsUpdate();
|
|
233
249
|
|
|
234
250
|
// do post update
|
|
@@ -262,7 +278,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
262
278
|
for (const o of engineObjects)
|
|
263
279
|
o.destroyed || o.render();
|
|
264
280
|
gameRenderPost();
|
|
265
|
-
|
|
281
|
+
pluginList.forEach(plugin=>plugin.render?.());
|
|
266
282
|
touchGamepadRender();
|
|
267
283
|
debugRender();
|
|
268
284
|
glFlush();
|
|
@@ -389,7 +405,6 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
389
405
|
image.onerror = image.onload = ()=>
|
|
390
406
|
{
|
|
391
407
|
const textureInfo = new TextureInfo(image);
|
|
392
|
-
textureInfo.createWebGLTexture();
|
|
393
408
|
textureInfos[textureIndex] = textureInfo;
|
|
394
409
|
resolve();
|
|
395
410
|
}
|
|
@@ -405,7 +420,6 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
405
420
|
{
|
|
406
421
|
const textureInfo = new TextureInfo(new Image);
|
|
407
422
|
textureInfos[0] = textureInfo;
|
|
408
|
-
textureInfo.createWebGLTexture();
|
|
409
423
|
resolve();
|
|
410
424
|
}));
|
|
411
425
|
}
|
|
@@ -2987,16 +3001,15 @@ class TileInfo
|
|
|
2987
3001
|
}
|
|
2988
3002
|
|
|
2989
3003
|
/**
|
|
2990
|
-
* Set this tile to use a full image
|
|
2991
|
-
* @param {
|
|
2992
|
-
* @param {WebGLTexture} [glTexture] - WebGL texture
|
|
3004
|
+
* Set this tile to use a full image in a texture info
|
|
3005
|
+
* @param {TextureInfo} textureInfo
|
|
2993
3006
|
* @return {TileInfo}
|
|
2994
3007
|
*/
|
|
2995
|
-
setFullImage(
|
|
3008
|
+
setFullImage(textureInfo)
|
|
2996
3009
|
{
|
|
2997
3010
|
this.pos = new Vector2;
|
|
2998
|
-
this.size =
|
|
2999
|
-
this.textureInfo =
|
|
3011
|
+
this.size = textureInfo.size.copy();
|
|
3012
|
+
this.textureInfo = textureInfo;
|
|
3000
3013
|
// do not use padding or bleed
|
|
3001
3014
|
this.bleedScale = this.padding = 0;
|
|
3002
3015
|
return this;
|
|
@@ -3012,26 +3025,30 @@ class TextureInfo
|
|
|
3012
3025
|
/**
|
|
3013
3026
|
* Create a TextureInfo, called automatically by the engine
|
|
3014
3027
|
* @param {HTMLImageElement|OffscreenCanvas} image
|
|
3015
|
-
* @param {
|
|
3028
|
+
* @param {boolean} [useWebGL] - Should use WebGL if available?
|
|
3016
3029
|
*/
|
|
3017
|
-
constructor(image,
|
|
3030
|
+
constructor(image, useWebGL=true)
|
|
3018
3031
|
{
|
|
3019
|
-
/** @property {HTMLImageElement} - image source */
|
|
3032
|
+
/** @property {HTMLImageElement|OffscreenCanvas} - image source */
|
|
3020
3033
|
this.image = image;
|
|
3021
3034
|
/** @property {Vector2} - size of the image */
|
|
3022
|
-
this.size = vec2(image.width, image.height);
|
|
3035
|
+
this.size = image ? vec2(image.width, image.height) : vec2();
|
|
3023
3036
|
/** @property {Vector2} - inverse of the size, cached for rendering */
|
|
3024
|
-
this.sizeInverse = vec2(1/image.width, 1/image.height);
|
|
3037
|
+
this.sizeInverse = image ? vec2(1/image.width, 1/image.height) : vec2();
|
|
3025
3038
|
/** @property {WebGLTexture} - WebGL texture */
|
|
3026
|
-
this.glTexture =
|
|
3039
|
+
this.glTexture = undefined;
|
|
3040
|
+
useWebGL && this.createWebGLTexture();
|
|
3027
3041
|
}
|
|
3028
3042
|
|
|
3029
|
-
|
|
3030
|
-
{
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3043
|
+
/** Creates the WebGL texture, updates if already created */
|
|
3044
|
+
createWebGLTexture() { glRegisterTextureInfo(this); }
|
|
3045
|
+
|
|
3046
|
+
/** Destroys the WebGL texture */
|
|
3047
|
+
destroyWebGLTexture() { glUnregisterTextureInfo(this); }
|
|
3048
|
+
|
|
3049
|
+
/** Check if the texture is webgl enabled
|
|
3050
|
+
* @return {boolean} */
|
|
3051
|
+
hasWebGL() { return !!this.glTexture; }
|
|
3035
3052
|
}
|
|
3036
3053
|
|
|
3037
3054
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -3464,12 +3481,13 @@ function drawCanvas2D(pos, size, angle=0, mirror=false, drawFunction, screenSpac
|
|
|
3464
3481
|
* @param {Color} [lineColor=(0,0,0,1)]
|
|
3465
3482
|
* @param {CanvasTextAlign} [textAlign='center']
|
|
3466
3483
|
* @param {string} [font=fontDefault]
|
|
3484
|
+
* @param {string} [fontStyle]
|
|
3467
3485
|
* @param {number} [maxWidth]
|
|
3468
3486
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
3469
3487
|
* @memberof Draw */
|
|
3470
|
-
function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, maxWidth, context=drawContext)
|
|
3488
|
+
function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, fontStyle, maxWidth, context=drawContext)
|
|
3471
3489
|
{
|
|
3472
|
-
drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, maxWidth, context);
|
|
3490
|
+
drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, fontStyle, maxWidth, context);
|
|
3473
3491
|
}
|
|
3474
3492
|
|
|
3475
3493
|
/** Draw text on overlay canvas in world space
|
|
@@ -3482,11 +3500,12 @@ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, f
|
|
|
3482
3500
|
* @param {Color} [lineColor=(0,0,0,1)]
|
|
3483
3501
|
* @param {CanvasTextAlign} [textAlign='center']
|
|
3484
3502
|
* @param {string} [font=fontDefault]
|
|
3503
|
+
* @param {string} [fontStyle]
|
|
3485
3504
|
* @param {number} [maxWidth]
|
|
3486
3505
|
* @memberof Draw */
|
|
3487
|
-
function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, maxWidth)
|
|
3506
|
+
function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, fontStyle, maxWidth)
|
|
3488
3507
|
{
|
|
3489
|
-
drawText(text, pos, size, color, lineWidth, lineColor, textAlign, font, maxWidth, overlayContext);
|
|
3508
|
+
drawText(text, pos, size, color, lineWidth, lineColor, textAlign, font, fontStyle, maxWidth, overlayContext);
|
|
3490
3509
|
}
|
|
3491
3510
|
|
|
3492
3511
|
/** Draw text on overlay canvas in screen space
|
|
@@ -3499,10 +3518,11 @@ function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textA
|
|
|
3499
3518
|
* @param {Color} [lineColor=(0,0,0,1)]
|
|
3500
3519
|
* @param {CanvasTextAlign} [textAlign]
|
|
3501
3520
|
* @param {string} [font=fontDefault]
|
|
3521
|
+
* @param {string} [fontStyle]
|
|
3502
3522
|
* @param {number} [maxWidth]
|
|
3503
3523
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=overlayContext]
|
|
3504
3524
|
* @memberof Draw */
|
|
3505
|
-
function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, textAlign='center', font=fontDefault, maxWidth, context=overlayContext)
|
|
3525
|
+
function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, textAlign='center', font=fontDefault, fontStyle='', maxWidth, context=overlayContext)
|
|
3506
3526
|
{
|
|
3507
3527
|
ASSERT(isString(text), 'text must be a string');
|
|
3508
3528
|
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
@@ -3510,15 +3530,15 @@ function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=B
|
|
|
3510
3530
|
ASSERT(isColor(color), 'color must be a color');
|
|
3511
3531
|
ASSERT(isNumber(lineWidth), 'lineWidth must be a number');
|
|
3512
3532
|
ASSERT(isColor(lineColor), 'lineColor must be a color');
|
|
3513
|
-
ASSERT(isColor(lineColor), 'lineColor must be a color');
|
|
3514
3533
|
ASSERT(['left','center','right'].includes(textAlign), 'align must be left, center, or right');
|
|
3515
3534
|
ASSERT(isString(font), 'font must be a string');
|
|
3535
|
+
ASSERT(isString(fontStyle), 'fontStyle must be a string');
|
|
3516
3536
|
|
|
3517
3537
|
context.fillStyle = color.toString();
|
|
3518
3538
|
context.strokeStyle = lineColor.toString();
|
|
3519
3539
|
context.lineWidth = lineWidth;
|
|
3520
3540
|
context.textAlign = textAlign;
|
|
3521
|
-
context.font = size + 'px '+ font;
|
|
3541
|
+
context.font = fontStyle + ' ' + size + 'px '+ font;
|
|
3522
3542
|
context.textBaseline = 'middle';
|
|
3523
3543
|
|
|
3524
3544
|
const lines = (text+'').split('\n');
|
|
@@ -3726,11 +3746,11 @@ let engineFontImage;
|
|
|
3726
3746
|
class FontImage
|
|
3727
3747
|
{
|
|
3728
3748
|
/** Create an image font
|
|
3729
|
-
* @param {HTMLImageElement} [image]
|
|
3730
|
-
* @param {Vector2} [tileSize=(8,8)]
|
|
3731
|
-
* @param {Vector2} [paddingSize=(0,1)] - How much
|
|
3749
|
+
* @param {HTMLImageElement} [image] - Image for the font, default if undefined
|
|
3750
|
+
* @param {Vector2} [tileSize=(8,8)] - Size of the font source tiles
|
|
3751
|
+
* @param {Vector2} [paddingSize=(0,1)] - How much space between characters
|
|
3732
3752
|
*/
|
|
3733
|
-
constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1)
|
|
3753
|
+
constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1))
|
|
3734
3754
|
{
|
|
3735
3755
|
// load default font image
|
|
3736
3756
|
if (!engineFontImage)
|
|
@@ -4743,8 +4763,16 @@ class SoundInstance
|
|
|
4743
4763
|
this.stop();
|
|
4744
4764
|
this.gainNode = audioContext.createGain();
|
|
4745
4765
|
this.source = playSamples(this.sound.sampleChannels, this.volume, this.rate, this.pan, this.loop, this.sound.sampleRate, this.gainNode, offset, this.onendedCallback);
|
|
4746
|
-
this.
|
|
4747
|
-
|
|
4766
|
+
if (this.source)
|
|
4767
|
+
{
|
|
4768
|
+
this.startTime = audioContext.currentTime - offset;
|
|
4769
|
+
this.pausedTime = undefined;
|
|
4770
|
+
}
|
|
4771
|
+
else
|
|
4772
|
+
{
|
|
4773
|
+
this.startTime = undefined;
|
|
4774
|
+
this.pausedTime = 0;
|
|
4775
|
+
}
|
|
4748
4776
|
}
|
|
4749
4777
|
|
|
4750
4778
|
/** Set the volume of this sound instance
|
|
@@ -4893,12 +4921,19 @@ function getNoteFrequency(semitoneOffset, rootFrequency=220)
|
|
|
4893
4921
|
* @param {GainNode} [gainNode] - Optional gain node for volume control while playing
|
|
4894
4922
|
* @param {number} [offset] - Offset in seconds to start playback from
|
|
4895
4923
|
* @param {AudioEndedCallback} [onended] - Callback for when the sound ends
|
|
4896
|
-
* @return {AudioBufferSourceNode} - The
|
|
4924
|
+
* @return {AudioBufferSourceNode} - The source node of the sound played, may be undefined if play fails
|
|
4897
4925
|
* @memberof Audio */
|
|
4898
4926
|
function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sampleRate=audioDefaultSampleRate, gainNode, offset=0, onended)
|
|
4899
4927
|
{
|
|
4900
4928
|
if (!soundEnable || headlessMode) return;
|
|
4901
4929
|
|
|
4930
|
+
if (!audioIsRunning())
|
|
4931
|
+
{
|
|
4932
|
+
// fix stalled audio, this sound won't be able to play
|
|
4933
|
+
audioContext.resume();
|
|
4934
|
+
return;
|
|
4935
|
+
}
|
|
4936
|
+
|
|
4902
4937
|
// create buffer and source
|
|
4903
4938
|
const channelCount = sampleChannels.length;
|
|
4904
4939
|
const sampleLength = sampleChannels[0].length;
|
|
@@ -4924,14 +4959,9 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
|
|
|
4924
4959
|
if (onended)
|
|
4925
4960
|
source.addEventListener('ended', ()=> onended(source));
|
|
4926
4961
|
|
|
4962
|
+
// play and return sound
|
|
4927
4963
|
const startOffset = offset * rate;
|
|
4928
|
-
|
|
4929
|
-
{
|
|
4930
|
-
// fix stalled audio and start
|
|
4931
|
-
audioContext.resume().then(()=>source.start(0, startOffset));
|
|
4932
|
-
}
|
|
4933
|
-
else
|
|
4934
|
-
source.start(0, startOffset);
|
|
4964
|
+
source.start(0, startOffset);
|
|
4935
4965
|
return source;
|
|
4936
4966
|
}
|
|
4937
4967
|
|
|
@@ -5287,10 +5317,13 @@ class CanvasLayer extends EngineObject
|
|
|
5287
5317
|
/** @property {HTMLCanvasElement} - The canvas used by this layer */
|
|
5288
5318
|
this.canvas = headlessMode ? undefined : new OffscreenCanvas(canvasSize.x, canvasSize.y);
|
|
5289
5319
|
/** @property {OffscreenCanvasRenderingContext2D} - The 2D canvas context used by this layer */
|
|
5290
|
-
this.context =
|
|
5291
|
-
/** @property {
|
|
5292
|
-
|
|
5293
|
-
this.
|
|
5320
|
+
this.context = this.canvas?.getContext('2d');
|
|
5321
|
+
/** @property {TextureInfo} - Texture info to use for this object rendering */
|
|
5322
|
+
const useWebGL = false; // do not use webgl by default
|
|
5323
|
+
this.textureInfo = new TextureInfo(this.canvas, useWebGL);
|
|
5324
|
+
|
|
5325
|
+
// disable physics by default
|
|
5326
|
+
this.mass = this.gravityScale = this.friction = this.restitution = 0;
|
|
5294
5327
|
}
|
|
5295
5328
|
|
|
5296
5329
|
/** Destroy this canvas layer */
|
|
@@ -5299,9 +5332,7 @@ class CanvasLayer extends EngineObject
|
|
|
5299
5332
|
if (this.destroyed)
|
|
5300
5333
|
return;
|
|
5301
5334
|
|
|
5302
|
-
|
|
5303
|
-
if (this.glTexture)
|
|
5304
|
-
glDeleteTexture(this.glTexture);
|
|
5335
|
+
this.textureInfo.destroyWebGLTexture();
|
|
5305
5336
|
super.destroy();
|
|
5306
5337
|
}
|
|
5307
5338
|
|
|
@@ -5324,8 +5355,8 @@ class CanvasLayer extends EngineObject
|
|
|
5324
5355
|
draw(pos, size, angle=0, color=WHITE, mirror=false, additiveColor, screenSpace=false, context)
|
|
5325
5356
|
{
|
|
5326
5357
|
// draw the canvas layer as a single tile that uses the whole texture
|
|
5327
|
-
const useWebGL = glEnable && this.
|
|
5328
|
-
const tileInfo = new TileInfo().setFullImage(this.
|
|
5358
|
+
const useWebGL = glEnable && this.textureInfo.hasWebGL();
|
|
5359
|
+
const tileInfo = new TileInfo().setFullImage(this.textureInfo);
|
|
5329
5360
|
drawTile(pos, size, tileInfo, color, angle, mirror, additiveColor, useWebGL, screenSpace, context);
|
|
5330
5361
|
}
|
|
5331
5362
|
|
|
@@ -5343,6 +5374,8 @@ class CanvasLayer extends EngineObject
|
|
|
5343
5374
|
* @param {Canvas2DDrawCallback} drawFunction */
|
|
5344
5375
|
drawCanvas2D(pos, size, angle, mirror, drawFunction)
|
|
5345
5376
|
{
|
|
5377
|
+
if (!this.context) return;
|
|
5378
|
+
|
|
5346
5379
|
const context = this.context;
|
|
5347
5380
|
context.save();
|
|
5348
5381
|
pos = pos.subtract(this.pos).multiply(this.tileInfo.size);
|
|
@@ -5395,15 +5428,10 @@ class CanvasLayer extends EngineObject
|
|
|
5395
5428
|
* @param {boolean} [enable] - enable WebGL rendering and update the texture */
|
|
5396
5429
|
useWebGL(enable=true)
|
|
5397
5430
|
{
|
|
5398
|
-
if (
|
|
5399
|
-
|
|
5400
|
-
if (this.glTexture)
|
|
5401
|
-
glSetTextureData(this.glTexture, this.canvas);
|
|
5402
|
-
else
|
|
5403
|
-
this.glTexture = glCreateTexture(this.canvas);
|
|
5404
|
-
}
|
|
5431
|
+
if (enable)
|
|
5432
|
+
this.textureInfo.createWebGLTexture();
|
|
5405
5433
|
else
|
|
5406
|
-
this.
|
|
5434
|
+
this.textureInfo.destroyWebGLTexture();
|
|
5407
5435
|
}
|
|
5408
5436
|
}
|
|
5409
5437
|
|
|
@@ -5427,24 +5455,14 @@ class TileLayer extends CanvasLayer
|
|
|
5427
5455
|
* @param {Vector2} size - World space size
|
|
5428
5456
|
* @param {TileInfo} [tileInfo] - Default tile info for layer (used for size and texture)
|
|
5429
5457
|
* @param {number} [renderOrder] - Objects are sorted by renderOrder
|
|
5430
|
-
* @param {boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering
|
|
5431
5458
|
*/
|
|
5432
|
-
constructor(position, size, tileInfo=tile(), renderOrder=0
|
|
5459
|
+
constructor(position, size, tileInfo=tile(), renderOrder=0)
|
|
5433
5460
|
{
|
|
5434
|
-
super(position, size, 0, renderOrder, size);
|
|
5435
|
-
|
|
5436
|
-
this.tileInfo = tileInfo;
|
|
5437
5461
|
const canvasSize = size.multiply(tileInfo.size);
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
this.
|
|
5442
|
-
/** @property {WebGLTexture} - Texture if using WebGL for this layer */
|
|
5443
|
-
this.glTexture = useWebGL ? glCreateTexture(this.canvas) : undefined;
|
|
5444
|
-
// set no friction by default, applied friction is max of both objects
|
|
5445
|
-
this.friction = 0;
|
|
5446
|
-
// set no restitution by default, applied restitution is max of both objects
|
|
5447
|
-
this.restitution = 0;
|
|
5462
|
+
super(position, size, 0, renderOrder, canvasSize);
|
|
5463
|
+
|
|
5464
|
+
// set tile info
|
|
5465
|
+
this.tileInfo = tileInfo;
|
|
5448
5466
|
|
|
5449
5467
|
// init tile data
|
|
5450
5468
|
this.data = [];
|
|
@@ -5494,10 +5512,10 @@ class TileLayer extends CanvasLayer
|
|
|
5494
5512
|
ASSERT(drawContext !== this.context, 'must call redrawEnd() after drawing tiles!');
|
|
5495
5513
|
|
|
5496
5514
|
// draw the tile layer as a single tile
|
|
5497
|
-
const tileInfo = new TileInfo().setFullImage(this.
|
|
5515
|
+
const tileInfo = new TileInfo().setFullImage(this.textureInfo);
|
|
5498
5516
|
const size = this.drawSize || this.size;
|
|
5499
5517
|
const pos = this.pos.add(size.scale(.5));
|
|
5500
|
-
const useWebGL = glEnable && this.
|
|
5518
|
+
const useWebGL = glEnable && this.textureInfo.hasWebGL();
|
|
5501
5519
|
drawTile(pos, size, tileInfo, WHITE, 0, false, CLEAR_BLACK, useWebGL);
|
|
5502
5520
|
}
|
|
5503
5521
|
|
|
@@ -5510,8 +5528,7 @@ class TileLayer extends CanvasLayer
|
|
|
5510
5528
|
for (let y = this.size.y; y--;)
|
|
5511
5529
|
this.drawTileData(vec2(x,y), false);
|
|
5512
5530
|
this.redrawEnd();
|
|
5513
|
-
|
|
5514
|
-
this.useWebGL(); // update WebGL texture
|
|
5531
|
+
this.useWebGL();
|
|
5515
5532
|
}
|
|
5516
5533
|
|
|
5517
5534
|
/** Call to start the redraw process
|
|
@@ -5519,6 +5536,8 @@ class TileLayer extends CanvasLayer
|
|
|
5519
5536
|
* @param {boolean} [clear] - Should it clear the canvas before drawing */
|
|
5520
5537
|
redrawStart(clear=false)
|
|
5521
5538
|
{
|
|
5539
|
+
if (!this.context) return;
|
|
5540
|
+
|
|
5522
5541
|
// save current render settings
|
|
5523
5542
|
/** @type {[HTMLCanvasElement|OffscreenCanvas, CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D, Vector2, Vector2, number]} */
|
|
5524
5543
|
this.savedRenderSettings = [drawCanvas, drawContext, mainCanvasSize, cameraPos, cameraScale];
|
|
@@ -5547,6 +5566,8 @@ class TileLayer extends CanvasLayer
|
|
|
5547
5566
|
/** Call to end the redraw process */
|
|
5548
5567
|
redrawEnd()
|
|
5549
5568
|
{
|
|
5569
|
+
if (!this.context) return;
|
|
5570
|
+
|
|
5550
5571
|
ASSERT(drawContext === this.context, 'must call redrawStart() before drawing tiles');
|
|
5551
5572
|
glCopyToContext(drawContext);
|
|
5552
5573
|
//debugSaveCanvas(this.canvas);
|
|
@@ -5563,6 +5584,8 @@ class TileLayer extends CanvasLayer
|
|
|
5563
5584
|
*/
|
|
5564
5585
|
drawTileData(layerPos, clear=true)
|
|
5565
5586
|
{
|
|
5587
|
+
if (!this.context) return;
|
|
5588
|
+
|
|
5566
5589
|
// clear out where the tile was, for full opaque tiles this can be skipped
|
|
5567
5590
|
const s = this.tileInfo.size;
|
|
5568
5591
|
if (clear)
|
|
@@ -5599,11 +5622,10 @@ class TileCollisionLayer extends TileLayer
|
|
|
5599
5622
|
* @param {Vector2} size - World space size
|
|
5600
5623
|
* @param {TileInfo} [tileInfo] - Tile info for layer
|
|
5601
5624
|
* @param {number} [renderOrder] - Objects are sorted by renderOrder
|
|
5602
|
-
* @param {boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering
|
|
5603
5625
|
*/
|
|
5604
|
-
constructor(position, size, tileInfo=tile(), renderOrder=0
|
|
5626
|
+
constructor(position, size, tileInfo=tile(), renderOrder=0)
|
|
5605
5627
|
{
|
|
5606
|
-
super(position, size.floor(), tileInfo, renderOrder
|
|
5628
|
+
super(position, size.floor(), tileInfo, renderOrder);
|
|
5607
5629
|
|
|
5608
5630
|
/** @property {Array<number>} - The tile collision grid */
|
|
5609
5631
|
this.collisionData = [];
|
|
@@ -5771,7 +5793,7 @@ class TileCollisionLayer extends TileLayer
|
|
|
5771
5793
|
* tile(0, 16), // tileInfo
|
|
5772
5794
|
* rgb(1,1,1,1), rgb(0,0,0,1), // colorStartA, colorStartB
|
|
5773
5795
|
* rgb(1,1,1,0), rgb(0,0,0,0), // colorEndA, colorEndB
|
|
5774
|
-
*
|
|
5796
|
+
* 1, .2, .2, .1, .05, // particleTime, sizeStart, sizeEnd, particleSpeed, particleAngleSpeed
|
|
5775
5797
|
* .99, 1, 1, PI, .05, // damping, angleDamping, gravityScale, particleCone, fadeRate,
|
|
5776
5798
|
* .5, 1 // randomness, collide, additive, randomColorLinear, renderOrder
|
|
5777
5799
|
* );
|
|
@@ -6345,7 +6367,7 @@ let glContext;
|
|
|
6345
6367
|
let glAntialias = true;
|
|
6346
6368
|
|
|
6347
6369
|
// WebGL internal variables not exposed to documentation
|
|
6348
|
-
let glShader, glPolyShader, glPolyMode, glAdditive, glBatchAdditive, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glBatchCount;
|
|
6370
|
+
let glShader, glPolyShader, glPolyMode, glAdditive, glBatchAdditive, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glBatchCount, glTextureInfos;
|
|
6349
6371
|
|
|
6350
6372
|
// WebGL internal constants
|
|
6351
6373
|
const gl_ARRAY_BUFFER_SIZE = 5e5;
|
|
@@ -6361,6 +6383,9 @@ const gl_MAX_POLY_VERTEXES = gl_ARRAY_BUFFER_SIZE / gl_POLY_VERTEX_BYTE_STRIDE |
|
|
|
6361
6383
|
// Initialize WebGL, called automatically by the engine
|
|
6362
6384
|
function glInit()
|
|
6363
6385
|
{
|
|
6386
|
+
// keep set of texture infos so they can be restored if context is lost
|
|
6387
|
+
glTextureInfos = new Set;
|
|
6388
|
+
|
|
6364
6389
|
if (!glEnable || headlessMode) return;
|
|
6365
6390
|
|
|
6366
6391
|
// create the canvas and textures
|
|
@@ -6378,68 +6403,101 @@ function glInit()
|
|
|
6378
6403
|
// create the WebGL canvas
|
|
6379
6404
|
const rootElement = mainCanvas.parentElement;
|
|
6380
6405
|
rootElement.appendChild(glCanvas);
|
|
6406
|
+
|
|
6407
|
+
// startup webgl
|
|
6408
|
+
initWebGL();
|
|
6409
|
+
|
|
6410
|
+
// setup context lost and restore handlers
|
|
6411
|
+
glCanvas.addEventListener('webglcontextlost', (e)=>
|
|
6412
|
+
{
|
|
6413
|
+
glEnable = false; // disable WebGL rendering
|
|
6414
|
+
glCanvas.style.display = 'none'; // hide the gl canvas
|
|
6415
|
+
e.preventDefault(); // prevent default to allow restoration
|
|
6416
|
+
LOG('WebGL context lost! Switching to Canvas2d rendering.');
|
|
6417
|
+
|
|
6418
|
+
// remove WebGL textures
|
|
6419
|
+
for (const info of glTextureInfos)
|
|
6420
|
+
info.glTexture = undefined;
|
|
6421
|
+
glActiveTexture = undefined;
|
|
6422
|
+
pluginList.forEach(plugin=>plugin.glContextLost?.());
|
|
6423
|
+
});
|
|
6424
|
+
glCanvas.addEventListener('webglcontextrestored', ()=>
|
|
6425
|
+
{
|
|
6426
|
+
glEnable = true; // disable WebGL rendering
|
|
6427
|
+
glCanvas.style.display = ''; // show the gl canvas
|
|
6428
|
+
LOG('WebGL context restored, reinitializing...');
|
|
6381
6429
|
|
|
6382
|
-
|
|
6383
|
-
|
|
6384
|
-
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
'in vec4 p,u,c,a;'+ // in: position/size, uvs, color, additiveColor
|
|
6389
|
-
'in float r;'+ // in: rotation
|
|
6390
|
-
'out vec2 v;'+ // out: uv
|
|
6391
|
-
'out vec4 d,e;'+ // out: color, additiveColor
|
|
6392
|
-
'void main(){'+ // shader entry point
|
|
6393
|
-
'vec2 s=(g-.5)*p.zw;'+ // get size offset
|
|
6394
|
-
'gl_Position=m*vec4(p.xy+s*cos(r)-vec2(-s.y,s)*sin(r),1,1);'+ // transform position
|
|
6395
|
-
'v=mix(u.xw,u.zy,g);'+ // pass uv to fragment shader
|
|
6396
|
-
'd=c;e=a;'+ // pass colors to fragment shader
|
|
6397
|
-
'}' // end of shader
|
|
6398
|
-
,
|
|
6399
|
-
'#version 300 es\n' + // specify GLSL ES version
|
|
6400
|
-
'precision highp float;'+ // use highp for better accuracy
|
|
6401
|
-
'uniform sampler2D s;'+ // texture
|
|
6402
|
-
'in vec2 v;'+ // in: uv
|
|
6403
|
-
'in vec4 d,e;'+ // in: color, additiveColor
|
|
6404
|
-
'out vec4 c;'+ // out: color
|
|
6405
|
-
'void main(){'+ // shader entry point
|
|
6406
|
-
'c=texture(s,v)*d+e;'+ // modulate texture by color plus additive
|
|
6407
|
-
'}' // end of shader
|
|
6408
|
-
);
|
|
6430
|
+
// reinit WebGL and restore textures
|
|
6431
|
+
initWebGL();
|
|
6432
|
+
for (const info of glTextureInfos)
|
|
6433
|
+
info.glTexture = glCreateTexture(info.image);
|
|
6434
|
+
pluginList.forEach(plugin=>plugin.glContextRestored?.());
|
|
6435
|
+
});
|
|
6409
6436
|
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
|
|
6421
|
-
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
6437
|
+
function initWebGL()
|
|
6438
|
+
{
|
|
6439
|
+
// setup instanced rendering shader program
|
|
6440
|
+
glShader = glCreateProgram(
|
|
6441
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
6442
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
6443
|
+
'uniform mat4 m;'+ // transform matrix
|
|
6444
|
+
'in vec2 g;'+ // in: geometry
|
|
6445
|
+
'in vec4 p,u,c,a;'+ // in: position/size, uvs, color, additiveColor
|
|
6446
|
+
'in float r;'+ // in: rotation
|
|
6447
|
+
'out vec2 v;'+ // out: uv
|
|
6448
|
+
'out vec4 d,e;'+ // out: color, additiveColor
|
|
6449
|
+
'void main(){'+ // shader entry point
|
|
6450
|
+
'vec2 s=(g-.5)*p.zw;'+ // get size offset
|
|
6451
|
+
'gl_Position=m*vec4(p.xy+s*cos(r)-vec2(-s.y,s)*sin(r),1,1);'+ // transform position
|
|
6452
|
+
'v=mix(u.xw,u.zy,g);'+ // pass uv to fragment shader
|
|
6453
|
+
'd=c;e=a;'+ // pass colors to fragment shader
|
|
6454
|
+
'}' // end of shader
|
|
6455
|
+
,
|
|
6456
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
6457
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
6458
|
+
'uniform sampler2D s;'+ // texture
|
|
6459
|
+
'in vec2 v;'+ // in: uv
|
|
6460
|
+
'in vec4 d,e;'+ // in: color, additiveColor
|
|
6461
|
+
'out vec4 c;'+ // out: color
|
|
6462
|
+
'void main(){'+ // shader entry point
|
|
6463
|
+
'c=texture(s,v)*d+e;'+ // modulate texture by color plus additive
|
|
6464
|
+
'}' // end of shader
|
|
6465
|
+
);
|
|
6466
|
+
|
|
6467
|
+
// setup poly rendering shaders
|
|
6468
|
+
glPolyShader = glCreateProgram(
|
|
6469
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
6470
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
6471
|
+
'uniform mat4 m;'+ // transform matrix
|
|
6472
|
+
'in vec2 p;'+ // in: position
|
|
6473
|
+
'in vec4 c;'+ // in: color
|
|
6474
|
+
'out vec4 d;'+ // out: color
|
|
6475
|
+
'void main(){'+ // shader entry point
|
|
6476
|
+
'gl_Position=m*vec4(p,1,1);'+ // transform position
|
|
6477
|
+
'd=c;'+ // pass color to fragment shader
|
|
6478
|
+
'}' // end of shader
|
|
6479
|
+
,
|
|
6480
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
6481
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
6482
|
+
'in vec4 d;'+ // in: color
|
|
6483
|
+
'out vec4 c;'+ // out: color
|
|
6484
|
+
'void main(){'+ // shader entry point
|
|
6485
|
+
'c=d;'+ // set color
|
|
6486
|
+
'}' // end of shader
|
|
6487
|
+
);
|
|
6431
6488
|
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6489
|
+
// init buffers
|
|
6490
|
+
const glInstanceData = new ArrayBuffer(gl_ARRAY_BUFFER_SIZE);
|
|
6491
|
+
glPositionData = new Float32Array(glInstanceData);
|
|
6492
|
+
glColorData = new Uint32Array(glInstanceData);
|
|
6493
|
+
glArrayBuffer = glContext.createBuffer();
|
|
6494
|
+
glGeometryBuffer = glContext.createBuffer();
|
|
6438
6495
|
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6496
|
+
// create the geometry buffer, triangle strip square
|
|
6497
|
+
const geometry = new Float32Array([glBatchCount=0,0,1,0,0,1,1,1]);
|
|
6498
|
+
glContext.bindBuffer(glContext.ARRAY_BUFFER, glGeometryBuffer);
|
|
6499
|
+
glContext.bufferData(glContext.ARRAY_BUFFER, geometry, glContext.STATIC_DRAW);
|
|
6500
|
+
}
|
|
6443
6501
|
}
|
|
6444
6502
|
|
|
6445
6503
|
function glSetInstancedMode()
|
|
@@ -6546,7 +6604,7 @@ function glPreRender()
|
|
|
6546
6604
|
// start with additive blending off
|
|
6547
6605
|
glAdditive = glBatchAdditive = false;
|
|
6548
6606
|
|
|
6549
|
-
// force it to
|
|
6607
|
+
// force it to set instanced mode by first setting poly mode true
|
|
6550
6608
|
glPolyMode = true;
|
|
6551
6609
|
glSetInstancedMode();
|
|
6552
6610
|
}
|
|
@@ -6687,6 +6745,41 @@ function glSetTextureData(texture, image)
|
|
|
6687
6745
|
glContext.bindTexture(glContext.TEXTURE_2D, glActiveTexture); // rebind active texture
|
|
6688
6746
|
}
|
|
6689
6747
|
|
|
6748
|
+
/** Tells WebGL to create or update the glTexture and start tracking it
|
|
6749
|
+
* @param {TextureInfo} textureInfo
|
|
6750
|
+
* @memberof WebGL */
|
|
6751
|
+
function glRegisterTextureInfo(textureInfo)
|
|
6752
|
+
{
|
|
6753
|
+
if (headlessMode) return;
|
|
6754
|
+
|
|
6755
|
+
// add texture info to tracking list even if gl is not enabled
|
|
6756
|
+
glTextureInfos.add(textureInfo);
|
|
6757
|
+
|
|
6758
|
+
if (!glContext) return;
|
|
6759
|
+
|
|
6760
|
+
// create or set the texture data
|
|
6761
|
+
if (textureInfo.glTexture)
|
|
6762
|
+
glSetTextureData(textureInfo.glTexture, textureInfo.image);
|
|
6763
|
+
else
|
|
6764
|
+
textureInfo.glTexture = glCreateTexture(textureInfo.image);
|
|
6765
|
+
}
|
|
6766
|
+
|
|
6767
|
+
/** Tells WebGL to destroy the glTexture and stop tracking it
|
|
6768
|
+
* @param {TextureInfo} textureInfo
|
|
6769
|
+
* @memberof WebGL */
|
|
6770
|
+
function glUnregisterTextureInfo(textureInfo)
|
|
6771
|
+
{
|
|
6772
|
+
if (headlessMode) return;
|
|
6773
|
+
|
|
6774
|
+
// delete texture info from tracking list even if gl is not enabled
|
|
6775
|
+
glTextureInfos.delete(textureInfo);
|
|
6776
|
+
|
|
6777
|
+
// unset and destroy the texture
|
|
6778
|
+
const glTexture = textureInfo.glTexture;
|
|
6779
|
+
textureInfo.glTexture = undefined;
|
|
6780
|
+
glDeleteTexture(glTexture);
|
|
6781
|
+
}
|
|
6782
|
+
|
|
6690
6783
|
/** Draw all sprites and clear out the buffer, called automatically by the system whenever necessary
|
|
6691
6784
|
* @memberof WebGL */
|
|
6692
6785
|
function glFlush()
|
|
@@ -7277,80 +7370,97 @@ class PostProcessPlugin
|
|
|
7277
7370
|
/** Create global post processing shader
|
|
7278
7371
|
* @param {string} shaderCode
|
|
7279
7372
|
* @param {boolean} [includeOverlay]
|
|
7373
|
+
* @param {boolean} [includeMainCanvas]
|
|
7280
7374
|
* @example
|
|
7281
7375
|
* // create the post process plugin object
|
|
7282
7376
|
* new PostProcessPlugin(shaderCode);
|
|
7283
7377
|
*/
|
|
7284
|
-
constructor(shaderCode, includeOverlay=false)
|
|
7378
|
+
constructor(shaderCode, includeOverlay=false, includeMainCanvas=true)
|
|
7285
7379
|
{
|
|
7286
7380
|
ASSERT(!postProcess, 'Post process already initialized');
|
|
7287
7381
|
postProcess = this;
|
|
7288
7382
|
|
|
7289
|
-
if (headlessMode) return;
|
|
7290
|
-
|
|
7291
|
-
if (!glEnable)
|
|
7292
|
-
{
|
|
7293
|
-
console.warn('PostProcessPlugin: WebGL not enabled!');
|
|
7294
|
-
return;
|
|
7295
|
-
}
|
|
7296
|
-
|
|
7297
7383
|
if (!shaderCode) // default shader pass through
|
|
7298
7384
|
shaderCode = 'void mainImage(out vec4 c,vec2 p){c=texture(iChannel0,p/iResolution.xy);}';
|
|
7299
7385
|
|
|
7300
7386
|
/** @property {WebGLProgram} - Shader for post processing */
|
|
7301
|
-
this.shader =
|
|
7302
|
-
'#version 300 es\n' + // specify GLSL ES version
|
|
7303
|
-
'precision highp float;'+ // use highp for better accuracy
|
|
7304
|
-
'in vec2 p;'+ // position
|
|
7305
|
-
'void main(){'+ // shader entry point
|
|
7306
|
-
'gl_Position=vec4(p+p-1.,1,1);'+ // set position
|
|
7307
|
-
'}' // end of shader
|
|
7308
|
-
,
|
|
7309
|
-
'#version 300 es\n' + // specify GLSL ES version
|
|
7310
|
-
'precision highp float;'+ // use highp for better accuracy
|
|
7311
|
-
'uniform sampler2D iChannel0;'+ // input texture
|
|
7312
|
-
'uniform vec3 iResolution;'+ // size of output texture
|
|
7313
|
-
'uniform float iTime;'+ // time
|
|
7314
|
-
'out vec4 c;'+ // out color
|
|
7315
|
-
'\n' + shaderCode + '\n'+ // insert custom shader code
|
|
7316
|
-
'void main(){'+ // shader entry point
|
|
7317
|
-
'mainImage(c,gl_FragCoord.xy);'+ // call post process function
|
|
7318
|
-
'c.a=1.;'+ // always use full alpha
|
|
7319
|
-
'}' // end of shader
|
|
7320
|
-
);
|
|
7387
|
+
this.shader = undefined;
|
|
7321
7388
|
|
|
7322
7389
|
/** @property {WebGLTexture} - Texture for post processing */
|
|
7323
|
-
this.texture =
|
|
7390
|
+
this.texture = undefined;
|
|
7324
7391
|
|
|
7325
|
-
|
|
7326
|
-
|
|
7392
|
+
// setup the post processing plugin
|
|
7393
|
+
initPostProcess();
|
|
7394
|
+
engineAddPlugin(undefined, postProcessRender, postProcessContextLost, postProcessContextRestored);
|
|
7327
7395
|
|
|
7328
|
-
|
|
7329
|
-
engineAddPlugin(undefined, postProcessRender);
|
|
7330
|
-
function postProcessRender()
|
|
7396
|
+
function initPostProcess()
|
|
7331
7397
|
{
|
|
7332
7398
|
if (headlessMode) return;
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
if (glEnable)
|
|
7336
|
-
{
|
|
7337
|
-
glFlush(); // clear out the buffer
|
|
7338
|
-
mainContext.drawImage(glCanvas, 0, 0); // copy to the main canvas
|
|
7339
|
-
}
|
|
7340
|
-
else
|
|
7399
|
+
|
|
7400
|
+
if (!glEnable)
|
|
7341
7401
|
{
|
|
7342
|
-
|
|
7343
|
-
|
|
7402
|
+
console.warn('PostProcessPlugin: WebGL not enabled!');
|
|
7403
|
+
return;
|
|
7344
7404
|
}
|
|
7345
7405
|
|
|
7346
|
-
|
|
7406
|
+
// create resources
|
|
7407
|
+
postProcess.texture = glCreateTexture();
|
|
7408
|
+
postProcess.shader = glCreateProgram(
|
|
7409
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
7410
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
7411
|
+
'in vec2 p;'+ // position
|
|
7412
|
+
'void main(){'+ // shader entry point
|
|
7413
|
+
'gl_Position=vec4(p+p-1.,1,1);'+ // set position
|
|
7414
|
+
'}' // end of shader
|
|
7415
|
+
,
|
|
7416
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
7417
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
7418
|
+
'uniform sampler2D iChannel0;'+ // input texture
|
|
7419
|
+
'uniform vec3 iResolution;'+ // size of output texture
|
|
7420
|
+
'uniform float iTime;'+ // time
|
|
7421
|
+
'out vec4 c;'+ // out color
|
|
7422
|
+
'\n' + shaderCode + '\n'+ // insert custom shader code
|
|
7423
|
+
'void main(){'+ // shader entry point
|
|
7424
|
+
'mainImage(c,gl_FragCoord.xy);'+ // call post process function
|
|
7425
|
+
'c.a=1.;'+ // always use full alpha
|
|
7426
|
+
'}' // end of shader
|
|
7427
|
+
);
|
|
7428
|
+
}
|
|
7429
|
+
function postProcessContextLost()
|
|
7430
|
+
{
|
|
7431
|
+
postProcess.shader = undefined;
|
|
7432
|
+
postProcess.texture = undefined;
|
|
7433
|
+
LOG('PostProcessPlugin: WebGL context lost');
|
|
7434
|
+
}
|
|
7435
|
+
function postProcessContextRestored()
|
|
7436
|
+
{
|
|
7437
|
+
initPostProcess();
|
|
7438
|
+
LOG('PostProcessPlugin: WebGL context restored');
|
|
7439
|
+
}
|
|
7440
|
+
function postProcessRender()
|
|
7441
|
+
{
|
|
7442
|
+
if (headlessMode) return;
|
|
7443
|
+
|
|
7444
|
+
if (!glEnable)
|
|
7445
|
+
return;
|
|
7446
|
+
|
|
7447
|
+
// clear out the buffer
|
|
7448
|
+
glFlush();
|
|
7449
|
+
|
|
7450
|
+
if (includeMainCanvas || includeOverlay)
|
|
7347
7451
|
{
|
|
7348
|
-
// copy
|
|
7349
|
-
mainContext.drawImage(
|
|
7350
|
-
|
|
7452
|
+
// copy WebGL to the main canvas
|
|
7453
|
+
mainContext.drawImage(glCanvas, 0, 0);
|
|
7454
|
+
|
|
7455
|
+
if (includeOverlay)
|
|
7456
|
+
{
|
|
7457
|
+
// copy overlay canvas so it will be included in post processing
|
|
7458
|
+
mainContext.drawImage(overlayCanvas, 0, 0);
|
|
7459
|
+
overlayCanvas.width |= 0; // clear overlay canvas
|
|
7460
|
+
}
|
|
7351
7461
|
}
|
|
7352
7462
|
|
|
7353
|
-
// setup shader program to draw
|
|
7463
|
+
// setup shader program to draw a quad
|
|
7354
7464
|
glContext.useProgram(postProcess.shader);
|
|
7355
7465
|
glContext.bindBuffer(glContext.ARRAY_BUFFER, glGeometryBuffer);
|
|
7356
7466
|
glContext.pixelStorei(glContext.UNPACK_FLIP_Y_WEBGL, 1);
|
|
@@ -7359,7 +7469,10 @@ class PostProcessPlugin
|
|
|
7359
7469
|
// set textures, pass in the 2d canvas and gl canvas in separate texture channels
|
|
7360
7470
|
glContext.activeTexture(glContext.TEXTURE0);
|
|
7361
7471
|
glContext.bindTexture(glContext.TEXTURE_2D, postProcess.texture);
|
|
7362
|
-
|
|
7472
|
+
if (includeMainCanvas || includeOverlay)
|
|
7473
|
+
{
|
|
7474
|
+
glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, glContext.RGBA, glContext.UNSIGNED_BYTE, mainCanvas);
|
|
7475
|
+
}
|
|
7363
7476
|
|
|
7364
7477
|
// set vertex position attribute
|
|
7365
7478
|
const vertexByteStride = 8;
|
|
@@ -7589,6 +7702,8 @@ class UISystemPlugin
|
|
|
7589
7702
|
this.defaultHoverColor = hsl(0,0,.9);
|
|
7590
7703
|
/** @property {Color} - Default color for disabled UI elements */
|
|
7591
7704
|
this.defaultDisabledColor = hsl(0,0,.3);
|
|
7705
|
+
/** @property {Color} - Uses a gradient fill combined with color */
|
|
7706
|
+
this.defaultGradientColor = undefined;
|
|
7592
7707
|
/** @property {number} - Default line width for UI elements */
|
|
7593
7708
|
this.defaultLineWidth = 4;
|
|
7594
7709
|
/** @property {number} - Default rounded rect corner radius for UI elements */
|
|
@@ -7596,7 +7711,7 @@ class UISystemPlugin
|
|
|
7596
7711
|
/** @property {number} - Default scale to use for fitting text to object */
|
|
7597
7712
|
this.defaultTextScale = .8;
|
|
7598
7713
|
/** @property {string} - Default font for UI elements */
|
|
7599
|
-
this.defaultFont =
|
|
7714
|
+
this.defaultFont = fontDefault;
|
|
7600
7715
|
/** @property {Sound} - Default sound when interactive UI element is pressed */
|
|
7601
7716
|
this.defaultSoundPress = undefined;
|
|
7602
7717
|
/** @property {Sound} - Default sound when interactive UI element is released */
|
|
@@ -7613,7 +7728,9 @@ class UISystemPlugin
|
|
|
7613
7728
|
this.hoverObject = undefined;
|
|
7614
7729
|
/** @property {UIObject} - Hover object at start of update */
|
|
7615
7730
|
this.lastHoverObject = undefined;
|
|
7616
|
-
|
|
7731
|
+
/** @property {number} - If set ui coords will be renormalized to this canvas height */
|
|
7732
|
+
this.nativeHeight = 0;
|
|
7733
|
+
|
|
7617
7734
|
engineAddPlugin(uiUpdate, uiRender);
|
|
7618
7735
|
|
|
7619
7736
|
// setup recursive update and render
|
|
@@ -7653,6 +7770,17 @@ class UISystemPlugin
|
|
|
7653
7770
|
}
|
|
7654
7771
|
function uiRender()
|
|
7655
7772
|
{
|
|
7773
|
+
const context = uiSystem.uiContext;
|
|
7774
|
+
context.save();
|
|
7775
|
+
if (uiSystem.nativeHeight)
|
|
7776
|
+
{
|
|
7777
|
+
// convert to native height
|
|
7778
|
+
const s = mainCanvasSize.y / uiSystem.nativeHeight;
|
|
7779
|
+
context.translate(-s*mainCanvasSize.x/2,0);
|
|
7780
|
+
context.scale(s,s);
|
|
7781
|
+
context.translate(mainCanvasSize.x/2/s,0);
|
|
7782
|
+
}
|
|
7783
|
+
|
|
7656
7784
|
function renderObject(o)
|
|
7657
7785
|
{
|
|
7658
7786
|
if (!o.visible)
|
|
@@ -7664,6 +7792,7 @@ class UISystemPlugin
|
|
|
7664
7792
|
renderObject(c);
|
|
7665
7793
|
}
|
|
7666
7794
|
uiSystem.uiObjects.forEach(o=> o.parent || renderObject(o));
|
|
7795
|
+
context.restore();
|
|
7667
7796
|
}
|
|
7668
7797
|
}
|
|
7669
7798
|
|
|
@@ -7673,8 +7802,9 @@ class UISystemPlugin
|
|
|
7673
7802
|
* @param {Color} [color=uiSystem.defaultColor]
|
|
7674
7803
|
* @param {number} [lineWidth=uiSystem.defaultLineWidth]
|
|
7675
7804
|
* @param {Color} [lineColor=uiSystem.defaultLineColor]
|
|
7676
|
-
* @param {number} [cornerRadius=uiSystem.defaultCornerRadius]
|
|
7677
|
-
|
|
7805
|
+
* @param {number} [cornerRadius=uiSystem.defaultCornerRadius]
|
|
7806
|
+
* @param {Color} [gradientColor=uiSystem.defaultGradientColor] */
|
|
7807
|
+
drawRect(pos, size, color=uiSystem.defaultColor, lineWidth=uiSystem.defaultLineWidth, lineColor=uiSystem.defaultLineColor, cornerRadius=uiSystem.defaultCornerRadius, gradientColor=uiSystem.defaultGradientColor)
|
|
7678
7808
|
{
|
|
7679
7809
|
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
7680
7810
|
ASSERT(isVector2(size), 'size must be a vec2');
|
|
@@ -7684,7 +7814,18 @@ class UISystemPlugin
|
|
|
7684
7814
|
ASSERT(isNumber(cornerRadius), 'cornerRadius must be a number');
|
|
7685
7815
|
|
|
7686
7816
|
const context = uiSystem.uiContext;
|
|
7687
|
-
|
|
7817
|
+
if (gradientColor)
|
|
7818
|
+
{
|
|
7819
|
+
const g = context.createLinearGradient(
|
|
7820
|
+
pos.x, pos.y-size.y/2, pos.x, pos.y+size.y/2);
|
|
7821
|
+
const c = color.toString();
|
|
7822
|
+
g.addColorStop(0, c);
|
|
7823
|
+
g.addColorStop(.5, gradientColor.toString());
|
|
7824
|
+
g.addColorStop(1, c);
|
|
7825
|
+
context.fillStyle = g;
|
|
7826
|
+
}
|
|
7827
|
+
else
|
|
7828
|
+
context.fillStyle = color.toString();
|
|
7688
7829
|
context.beginPath();
|
|
7689
7830
|
if (cornerRadius && context['roundRect'])
|
|
7690
7831
|
context['roundRect'](pos.x-size.x/2, pos.y-size.y/2, size.x, size.y, cornerRadius);
|
|
@@ -7741,10 +7882,11 @@ class UISystemPlugin
|
|
|
7741
7882
|
* @param {Color} [lineColor=uiSystem.defaultLineColor]
|
|
7742
7883
|
* @param {string} [align]
|
|
7743
7884
|
* @param {string} [font=uiSystem.defaultFont]
|
|
7885
|
+
* @param {string} [fontStyle]
|
|
7744
7886
|
* @param {boolean} [applyMaxWidth=true] */
|
|
7745
|
-
drawText(text, pos, size, color=uiSystem.defaultColor, lineWidth=uiSystem.defaultLineWidth, lineColor=uiSystem.defaultLineColor, align='center', font=uiSystem.defaultFont, applyMaxWidth=true)
|
|
7887
|
+
drawText(text, pos, size, color=uiSystem.defaultColor, lineWidth=uiSystem.defaultLineWidth, lineColor=uiSystem.defaultLineColor, align='center', font=uiSystem.defaultFont, fontStyle='', applyMaxWidth=true)
|
|
7746
7888
|
{
|
|
7747
|
-
drawTextScreen(text, pos, size.y, color, lineWidth, lineColor, align, font, applyMaxWidth ? size.x : undefined, uiSystem.uiContext);
|
|
7889
|
+
drawTextScreen(text, pos, size.y, color, lineWidth, lineColor, align, font, fontStyle, applyMaxWidth ? size.x : undefined, uiSystem.uiContext);
|
|
7748
7890
|
}
|
|
7749
7891
|
|
|
7750
7892
|
/**
|
|
@@ -7805,17 +7947,21 @@ class UIObject
|
|
|
7805
7947
|
/** @property {boolean} - Is this object disabled? */
|
|
7806
7948
|
this.disabled = false;
|
|
7807
7949
|
/** @property {Color} - Color for text */
|
|
7808
|
-
this.textColor = uiSystem.defaultTextColor.copy()
|
|
7950
|
+
this.textColor = uiSystem.defaultTextColor.copy();
|
|
7809
7951
|
/** @property {Color} - Color used when hovering over the object */
|
|
7810
|
-
this.hoverColor = uiSystem.defaultHoverColor.copy()
|
|
7952
|
+
this.hoverColor = uiSystem.defaultHoverColor.copy();
|
|
7811
7953
|
/** @property {Color} - Color for line drawing */
|
|
7812
|
-
this.lineColor = uiSystem.defaultLineColor.copy()
|
|
7954
|
+
this.lineColor = uiSystem.defaultLineColor.copy();
|
|
7955
|
+
/** @property {Color} - Uses a gradient fill combined with color */
|
|
7956
|
+
this.gradientColor = uiSystem.defaultGradientColor ? uiSystem.defaultGradientColor.copy() : undefined;
|
|
7813
7957
|
/** @property {number} - Width for line drawing */
|
|
7814
7958
|
this.lineWidth = uiSystem.defaultLineWidth;
|
|
7815
7959
|
/** @property {number} - Corner radius for rounded rects */
|
|
7816
7960
|
this.cornerRadius = uiSystem.defaultCornerRadius;
|
|
7817
7961
|
/** @property {string} - Font for this objecct */
|
|
7818
7962
|
this.font = uiSystem.defaultFont;
|
|
7963
|
+
/** @property {string} - Font style for this object or undefined */
|
|
7964
|
+
this.fontStyle = undefined;
|
|
7819
7965
|
/** @property {number} - Override for text width */
|
|
7820
7966
|
this.textWidth = undefined;
|
|
7821
7967
|
/** @property {number} - Override for text height */
|
|
@@ -7840,6 +7986,8 @@ class UIObject
|
|
|
7840
7986
|
this.interactive = false;
|
|
7841
7987
|
/** @property {boolean} - Activate when dragged over with mouse held down */
|
|
7842
7988
|
this.dragActivate = false;
|
|
7989
|
+
/** @property {boolean} - True if this can be a hover object */
|
|
7990
|
+
this.canBeHover = true;
|
|
7843
7991
|
uiSystem.uiObjects.push(this);
|
|
7844
7992
|
}
|
|
7845
7993
|
|
|
@@ -7863,6 +8011,26 @@ class UIObject
|
|
|
7863
8011
|
child.parent = undefined;
|
|
7864
8012
|
}
|
|
7865
8013
|
|
|
8014
|
+
/** Check if the mouse is overlapping a box in screen space
|
|
8015
|
+
* @return {boolean} - True if overlapping
|
|
8016
|
+
*/
|
|
8017
|
+
isMouseOverlapping()
|
|
8018
|
+
{
|
|
8019
|
+
const size = !isTouchDevice ? this.size :
|
|
8020
|
+
this.size.add(vec2(this.extraTouchSize || 0));
|
|
8021
|
+
if (!uiSystem.nativeHeight)
|
|
8022
|
+
return isOverlapping(this.pos, size, mousePosScreen);
|
|
8023
|
+
|
|
8024
|
+
const s = mainCanvasSize.y / uiSystem.nativeHeight;
|
|
8025
|
+
const sInv = 1/s;
|
|
8026
|
+
let pos = mousePosScreen.copy();
|
|
8027
|
+
pos.x += s*mainCanvasSize.x/2;
|
|
8028
|
+
pos.x *= sInv;
|
|
8029
|
+
pos.y *= sInv;
|
|
8030
|
+
pos.x -= sInv*mainCanvasSize.x/2;
|
|
8031
|
+
return isOverlapping(this.pos, size, pos);
|
|
8032
|
+
}
|
|
8033
|
+
|
|
7866
8034
|
/** Update the object, called automatically by plugin once each frame */
|
|
7867
8035
|
update()
|
|
7868
8036
|
{
|
|
@@ -7870,13 +8038,10 @@ class UIObject
|
|
|
7870
8038
|
const isActive = this.isActiveObject();
|
|
7871
8039
|
const mouseDown = mouseIsDown(0);
|
|
7872
8040
|
const mousePress = this.dragActivate ? mouseDown : mouseWasPressed(0);
|
|
7873
|
-
if (
|
|
8041
|
+
if (this.canBeHover)
|
|
7874
8042
|
if (mousePress || isActive || (!mouseDown && !isTouchDevice))
|
|
7875
|
-
|
|
7876
|
-
|
|
7877
|
-
if (isOverlapping(this.pos, size, mousePosScreen))
|
|
7878
|
-
uiSystem.hoverObject = this;
|
|
7879
|
-
}
|
|
8043
|
+
if (!uiSystem.hoverObject && this.isMouseOverlapping())
|
|
8044
|
+
uiSystem.hoverObject = this;
|
|
7880
8045
|
if (this.isHoverObject())
|
|
7881
8046
|
{
|
|
7882
8047
|
if (!this.disabled)
|
|
@@ -8001,11 +8166,13 @@ class UIText extends UIObject
|
|
|
8001
8166
|
|
|
8002
8167
|
// make text not outlined by default
|
|
8003
8168
|
this.lineWidth = 0;
|
|
8169
|
+
// text can not be a hover object by default
|
|
8170
|
+
this.canBeHover = false;
|
|
8004
8171
|
}
|
|
8005
8172
|
render()
|
|
8006
8173
|
{
|
|
8007
8174
|
const textSize = this.getTextSize();
|
|
8008
|
-
uiSystem.drawText(this.text, this.pos, textSize, this.textColor, this.lineWidth, this.lineColor, this.align, this.font);
|
|
8175
|
+
uiSystem.drawText(this.text, this.pos, textSize, this.textColor, this.lineWidth, this.lineColor, this.align, this.font, this.fontStyle);
|
|
8009
8176
|
}
|
|
8010
8177
|
}
|
|
8011
8178
|
|
|
@@ -8071,7 +8238,7 @@ class UIButton extends UIObject
|
|
|
8071
8238
|
|
|
8072
8239
|
// set properties
|
|
8073
8240
|
this.text = text;
|
|
8074
|
-
this.color = color.copy()
|
|
8241
|
+
this.color = color.copy();
|
|
8075
8242
|
this.interactive = true;
|
|
8076
8243
|
}
|
|
8077
8244
|
render()
|
|
@@ -8081,7 +8248,7 @@ class UIButton extends UIObject
|
|
|
8081
8248
|
// draw the text scaled to fit
|
|
8082
8249
|
const textSize = this.getTextSize();
|
|
8083
8250
|
uiSystem.drawText(this.text, this.pos, textSize,
|
|
8084
|
-
this.textColor, 0, undefined, this.align, this.font);
|
|
8251
|
+
this.textColor, 0, undefined, this.align, this.font, this.fontStyle);
|
|
8085
8252
|
}
|
|
8086
8253
|
}
|
|
8087
8254
|
|
|
@@ -8135,7 +8302,7 @@ class UICheckbox extends UIObject
|
|
|
8135
8302
|
const textSize = this.getTextSize();
|
|
8136
8303
|
const pos = this.pos.add(vec2(this.size.x,0));
|
|
8137
8304
|
uiSystem.drawText(this.text, pos, textSize,
|
|
8138
|
-
this.textColor, 0, undefined, 'left', this.font, false);
|
|
8305
|
+
this.textColor, 0, undefined, 'left', this.font, this.fontStyle, false);
|
|
8139
8306
|
}
|
|
8140
8307
|
}
|
|
8141
8308
|
|
|
@@ -8213,14 +8380,13 @@ class UIScrollbar extends UIObject
|
|
|
8213
8380
|
const handlePos = isHorizontal ?
|
|
8214
8381
|
vec2(lerp(p1, p2, this.value), this.pos.y) :
|
|
8215
8382
|
vec2(this.pos.x, lerp(p2, p1, this.value))
|
|
8216
|
-
const handleColor = this.disabled ? this.disabledColor :
|
|
8217
|
-
this.interactive && this.isActiveObject() ? this.color : this.handleColor;
|
|
8383
|
+
const handleColor = this.disabled ? this.disabledColor : this.handleColor;
|
|
8218
8384
|
uiSystem.drawRect(handlePos, vec2(handleSize), handleColor, this.lineWidth, this.lineColor, this.cornerRadius);
|
|
8219
8385
|
|
|
8220
8386
|
// draw the text scaled to fit on the scrollbar
|
|
8221
8387
|
const textSize = this.getTextSize();
|
|
8222
8388
|
uiSystem.drawText(this.text, this.pos, textSize,
|
|
8223
|
-
this.textColor, 0, undefined, this.align, this.font);
|
|
8389
|
+
this.textColor, 0, undefined, this.align, this.font, this.fontStyle);
|
|
8224
8390
|
}
|
|
8225
8391
|
}
|
|
8226
8392
|
/**
|