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.esm.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
|
}
|
|
@@ -3571,16 +3585,15 @@ class TileInfo
|
|
|
3571
3585
|
}
|
|
3572
3586
|
|
|
3573
3587
|
/**
|
|
3574
|
-
* Set this tile to use a full image
|
|
3575
|
-
* @param {
|
|
3576
|
-
* @param {WebGLTexture} [glTexture] - WebGL texture
|
|
3588
|
+
* Set this tile to use a full image in a texture info
|
|
3589
|
+
* @param {TextureInfo} textureInfo
|
|
3577
3590
|
* @return {TileInfo}
|
|
3578
3591
|
*/
|
|
3579
|
-
setFullImage(
|
|
3592
|
+
setFullImage(textureInfo)
|
|
3580
3593
|
{
|
|
3581
3594
|
this.pos = new Vector2;
|
|
3582
|
-
this.size =
|
|
3583
|
-
this.textureInfo =
|
|
3595
|
+
this.size = textureInfo.size.copy();
|
|
3596
|
+
this.textureInfo = textureInfo;
|
|
3584
3597
|
// do not use padding or bleed
|
|
3585
3598
|
this.bleedScale = this.padding = 0;
|
|
3586
3599
|
return this;
|
|
@@ -3596,26 +3609,30 @@ class TextureInfo
|
|
|
3596
3609
|
/**
|
|
3597
3610
|
* Create a TextureInfo, called automatically by the engine
|
|
3598
3611
|
* @param {HTMLImageElement|OffscreenCanvas} image
|
|
3599
|
-
* @param {
|
|
3612
|
+
* @param {boolean} [useWebGL] - Should use WebGL if available?
|
|
3600
3613
|
*/
|
|
3601
|
-
constructor(image,
|
|
3614
|
+
constructor(image, useWebGL=true)
|
|
3602
3615
|
{
|
|
3603
|
-
/** @property {HTMLImageElement} - image source */
|
|
3616
|
+
/** @property {HTMLImageElement|OffscreenCanvas} - image source */
|
|
3604
3617
|
this.image = image;
|
|
3605
3618
|
/** @property {Vector2} - size of the image */
|
|
3606
|
-
this.size = vec2(image.width, image.height);
|
|
3619
|
+
this.size = image ? vec2(image.width, image.height) : vec2();
|
|
3607
3620
|
/** @property {Vector2} - inverse of the size, cached for rendering */
|
|
3608
|
-
this.sizeInverse = vec2(1/image.width, 1/image.height);
|
|
3621
|
+
this.sizeInverse = image ? vec2(1/image.width, 1/image.height) : vec2();
|
|
3609
3622
|
/** @property {WebGLTexture} - WebGL texture */
|
|
3610
|
-
this.glTexture =
|
|
3623
|
+
this.glTexture = undefined;
|
|
3624
|
+
useWebGL && this.createWebGLTexture();
|
|
3611
3625
|
}
|
|
3612
3626
|
|
|
3613
|
-
|
|
3614
|
-
{
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3627
|
+
/** Creates the WebGL texture, updates if already created */
|
|
3628
|
+
createWebGLTexture() { glRegisterTextureInfo(this); }
|
|
3629
|
+
|
|
3630
|
+
/** Destroys the WebGL texture */
|
|
3631
|
+
destroyWebGLTexture() { glUnregisterTextureInfo(this); }
|
|
3632
|
+
|
|
3633
|
+
/** Check if the texture is webgl enabled
|
|
3634
|
+
* @return {boolean} */
|
|
3635
|
+
hasWebGL() { return !!this.glTexture; }
|
|
3619
3636
|
}
|
|
3620
3637
|
|
|
3621
3638
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -4048,12 +4065,13 @@ function drawCanvas2D(pos, size, angle=0, mirror=false, drawFunction, screenSpac
|
|
|
4048
4065
|
* @param {Color} [lineColor=(0,0,0,1)]
|
|
4049
4066
|
* @param {CanvasTextAlign} [textAlign='center']
|
|
4050
4067
|
* @param {string} [font=fontDefault]
|
|
4068
|
+
* @param {string} [fontStyle]
|
|
4051
4069
|
* @param {number} [maxWidth]
|
|
4052
4070
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
4053
4071
|
* @memberof Draw */
|
|
4054
|
-
function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, maxWidth, context=drawContext)
|
|
4072
|
+
function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, fontStyle, maxWidth, context=drawContext)
|
|
4055
4073
|
{
|
|
4056
|
-
drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, maxWidth, context);
|
|
4074
|
+
drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, fontStyle, maxWidth, context);
|
|
4057
4075
|
}
|
|
4058
4076
|
|
|
4059
4077
|
/** Draw text on overlay canvas in world space
|
|
@@ -4066,11 +4084,12 @@ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, f
|
|
|
4066
4084
|
* @param {Color} [lineColor=(0,0,0,1)]
|
|
4067
4085
|
* @param {CanvasTextAlign} [textAlign='center']
|
|
4068
4086
|
* @param {string} [font=fontDefault]
|
|
4087
|
+
* @param {string} [fontStyle]
|
|
4069
4088
|
* @param {number} [maxWidth]
|
|
4070
4089
|
* @memberof Draw */
|
|
4071
|
-
function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, maxWidth)
|
|
4090
|
+
function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, fontStyle, maxWidth)
|
|
4072
4091
|
{
|
|
4073
|
-
drawText(text, pos, size, color, lineWidth, lineColor, textAlign, font, maxWidth, overlayContext);
|
|
4092
|
+
drawText(text, pos, size, color, lineWidth, lineColor, textAlign, font, fontStyle, maxWidth, overlayContext);
|
|
4074
4093
|
}
|
|
4075
4094
|
|
|
4076
4095
|
/** Draw text on overlay canvas in screen space
|
|
@@ -4083,10 +4102,11 @@ function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textA
|
|
|
4083
4102
|
* @param {Color} [lineColor=(0,0,0,1)]
|
|
4084
4103
|
* @param {CanvasTextAlign} [textAlign]
|
|
4085
4104
|
* @param {string} [font=fontDefault]
|
|
4105
|
+
* @param {string} [fontStyle]
|
|
4086
4106
|
* @param {number} [maxWidth]
|
|
4087
4107
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=overlayContext]
|
|
4088
4108
|
* @memberof Draw */
|
|
4089
|
-
function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, textAlign='center', font=fontDefault, maxWidth, context=overlayContext)
|
|
4109
|
+
function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, textAlign='center', font=fontDefault, fontStyle='', maxWidth, context=overlayContext)
|
|
4090
4110
|
{
|
|
4091
4111
|
ASSERT(isString(text), 'text must be a string');
|
|
4092
4112
|
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
@@ -4094,15 +4114,15 @@ function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=B
|
|
|
4094
4114
|
ASSERT(isColor(color), 'color must be a color');
|
|
4095
4115
|
ASSERT(isNumber(lineWidth), 'lineWidth must be a number');
|
|
4096
4116
|
ASSERT(isColor(lineColor), 'lineColor must be a color');
|
|
4097
|
-
ASSERT(isColor(lineColor), 'lineColor must be a color');
|
|
4098
4117
|
ASSERT(['left','center','right'].includes(textAlign), 'align must be left, center, or right');
|
|
4099
4118
|
ASSERT(isString(font), 'font must be a string');
|
|
4119
|
+
ASSERT(isString(fontStyle), 'fontStyle must be a string');
|
|
4100
4120
|
|
|
4101
4121
|
context.fillStyle = color.toString();
|
|
4102
4122
|
context.strokeStyle = lineColor.toString();
|
|
4103
4123
|
context.lineWidth = lineWidth;
|
|
4104
4124
|
context.textAlign = textAlign;
|
|
4105
|
-
context.font = size + 'px '+ font;
|
|
4125
|
+
context.font = fontStyle + ' ' + size + 'px '+ font;
|
|
4106
4126
|
context.textBaseline = 'middle';
|
|
4107
4127
|
|
|
4108
4128
|
const lines = (text+'').split('\n');
|
|
@@ -4310,11 +4330,11 @@ let engineFontImage;
|
|
|
4310
4330
|
class FontImage
|
|
4311
4331
|
{
|
|
4312
4332
|
/** Create an image font
|
|
4313
|
-
* @param {HTMLImageElement} [image]
|
|
4314
|
-
* @param {Vector2} [tileSize=(8,8)]
|
|
4315
|
-
* @param {Vector2} [paddingSize=(0,1)] - How much
|
|
4333
|
+
* @param {HTMLImageElement} [image] - Image for the font, default if undefined
|
|
4334
|
+
* @param {Vector2} [tileSize=(8,8)] - Size of the font source tiles
|
|
4335
|
+
* @param {Vector2} [paddingSize=(0,1)] - How much space between characters
|
|
4316
4336
|
*/
|
|
4317
|
-
constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1)
|
|
4337
|
+
constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1))
|
|
4318
4338
|
{
|
|
4319
4339
|
// load default font image
|
|
4320
4340
|
if (!engineFontImage)
|
|
@@ -5327,8 +5347,16 @@ class SoundInstance
|
|
|
5327
5347
|
this.stop();
|
|
5328
5348
|
this.gainNode = audioContext.createGain();
|
|
5329
5349
|
this.source = playSamples(this.sound.sampleChannels, this.volume, this.rate, this.pan, this.loop, this.sound.sampleRate, this.gainNode, offset, this.onendedCallback);
|
|
5330
|
-
this.
|
|
5331
|
-
|
|
5350
|
+
if (this.source)
|
|
5351
|
+
{
|
|
5352
|
+
this.startTime = audioContext.currentTime - offset;
|
|
5353
|
+
this.pausedTime = undefined;
|
|
5354
|
+
}
|
|
5355
|
+
else
|
|
5356
|
+
{
|
|
5357
|
+
this.startTime = undefined;
|
|
5358
|
+
this.pausedTime = 0;
|
|
5359
|
+
}
|
|
5332
5360
|
}
|
|
5333
5361
|
|
|
5334
5362
|
/** Set the volume of this sound instance
|
|
@@ -5477,12 +5505,19 @@ function getNoteFrequency(semitoneOffset, rootFrequency=220)
|
|
|
5477
5505
|
* @param {GainNode} [gainNode] - Optional gain node for volume control while playing
|
|
5478
5506
|
* @param {number} [offset] - Offset in seconds to start playback from
|
|
5479
5507
|
* @param {AudioEndedCallback} [onended] - Callback for when the sound ends
|
|
5480
|
-
* @return {AudioBufferSourceNode} - The
|
|
5508
|
+
* @return {AudioBufferSourceNode} - The source node of the sound played, may be undefined if play fails
|
|
5481
5509
|
* @memberof Audio */
|
|
5482
5510
|
function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sampleRate=audioDefaultSampleRate, gainNode, offset=0, onended)
|
|
5483
5511
|
{
|
|
5484
5512
|
if (!soundEnable || headlessMode) return;
|
|
5485
5513
|
|
|
5514
|
+
if (!audioIsRunning())
|
|
5515
|
+
{
|
|
5516
|
+
// fix stalled audio, this sound won't be able to play
|
|
5517
|
+
audioContext.resume();
|
|
5518
|
+
return;
|
|
5519
|
+
}
|
|
5520
|
+
|
|
5486
5521
|
// create buffer and source
|
|
5487
5522
|
const channelCount = sampleChannels.length;
|
|
5488
5523
|
const sampleLength = sampleChannels[0].length;
|
|
@@ -5508,14 +5543,9 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
|
|
|
5508
5543
|
if (onended)
|
|
5509
5544
|
source.addEventListener('ended', ()=> onended(source));
|
|
5510
5545
|
|
|
5546
|
+
// play and return sound
|
|
5511
5547
|
const startOffset = offset * rate;
|
|
5512
|
-
|
|
5513
|
-
{
|
|
5514
|
-
// fix stalled audio and start
|
|
5515
|
-
audioContext.resume().then(()=>source.start(0, startOffset));
|
|
5516
|
-
}
|
|
5517
|
-
else
|
|
5518
|
-
source.start(0, startOffset);
|
|
5548
|
+
source.start(0, startOffset);
|
|
5519
5549
|
return source;
|
|
5520
5550
|
}
|
|
5521
5551
|
|
|
@@ -5871,10 +5901,13 @@ class CanvasLayer extends EngineObject
|
|
|
5871
5901
|
/** @property {HTMLCanvasElement} - The canvas used by this layer */
|
|
5872
5902
|
this.canvas = headlessMode ? undefined : new OffscreenCanvas(canvasSize.x, canvasSize.y);
|
|
5873
5903
|
/** @property {OffscreenCanvasRenderingContext2D} - The 2D canvas context used by this layer */
|
|
5874
|
-
this.context =
|
|
5875
|
-
/** @property {
|
|
5876
|
-
|
|
5877
|
-
this.
|
|
5904
|
+
this.context = this.canvas?.getContext('2d');
|
|
5905
|
+
/** @property {TextureInfo} - Texture info to use for this object rendering */
|
|
5906
|
+
const useWebGL = false; // do not use webgl by default
|
|
5907
|
+
this.textureInfo = new TextureInfo(this.canvas, useWebGL);
|
|
5908
|
+
|
|
5909
|
+
// disable physics by default
|
|
5910
|
+
this.mass = this.gravityScale = this.friction = this.restitution = 0;
|
|
5878
5911
|
}
|
|
5879
5912
|
|
|
5880
5913
|
/** Destroy this canvas layer */
|
|
@@ -5883,9 +5916,7 @@ class CanvasLayer extends EngineObject
|
|
|
5883
5916
|
if (this.destroyed)
|
|
5884
5917
|
return;
|
|
5885
5918
|
|
|
5886
|
-
|
|
5887
|
-
if (this.glTexture)
|
|
5888
|
-
glDeleteTexture(this.glTexture);
|
|
5919
|
+
this.textureInfo.destroyWebGLTexture();
|
|
5889
5920
|
super.destroy();
|
|
5890
5921
|
}
|
|
5891
5922
|
|
|
@@ -5908,8 +5939,8 @@ class CanvasLayer extends EngineObject
|
|
|
5908
5939
|
draw(pos, size, angle=0, color=WHITE, mirror=false, additiveColor, screenSpace=false, context)
|
|
5909
5940
|
{
|
|
5910
5941
|
// draw the canvas layer as a single tile that uses the whole texture
|
|
5911
|
-
const useWebGL = glEnable && this.
|
|
5912
|
-
const tileInfo = new TileInfo().setFullImage(this.
|
|
5942
|
+
const useWebGL = glEnable && this.textureInfo.hasWebGL();
|
|
5943
|
+
const tileInfo = new TileInfo().setFullImage(this.textureInfo);
|
|
5913
5944
|
drawTile(pos, size, tileInfo, color, angle, mirror, additiveColor, useWebGL, screenSpace, context);
|
|
5914
5945
|
}
|
|
5915
5946
|
|
|
@@ -5927,6 +5958,8 @@ class CanvasLayer extends EngineObject
|
|
|
5927
5958
|
* @param {Canvas2DDrawCallback} drawFunction */
|
|
5928
5959
|
drawCanvas2D(pos, size, angle, mirror, drawFunction)
|
|
5929
5960
|
{
|
|
5961
|
+
if (!this.context) return;
|
|
5962
|
+
|
|
5930
5963
|
const context = this.context;
|
|
5931
5964
|
context.save();
|
|
5932
5965
|
pos = pos.subtract(this.pos).multiply(this.tileInfo.size);
|
|
@@ -5979,15 +6012,10 @@ class CanvasLayer extends EngineObject
|
|
|
5979
6012
|
* @param {boolean} [enable] - enable WebGL rendering and update the texture */
|
|
5980
6013
|
useWebGL(enable=true)
|
|
5981
6014
|
{
|
|
5982
|
-
if (
|
|
5983
|
-
|
|
5984
|
-
if (this.glTexture)
|
|
5985
|
-
glSetTextureData(this.glTexture, this.canvas);
|
|
5986
|
-
else
|
|
5987
|
-
this.glTexture = glCreateTexture(this.canvas);
|
|
5988
|
-
}
|
|
6015
|
+
if (enable)
|
|
6016
|
+
this.textureInfo.createWebGLTexture();
|
|
5989
6017
|
else
|
|
5990
|
-
this.
|
|
6018
|
+
this.textureInfo.destroyWebGLTexture();
|
|
5991
6019
|
}
|
|
5992
6020
|
}
|
|
5993
6021
|
|
|
@@ -6011,24 +6039,14 @@ class TileLayer extends CanvasLayer
|
|
|
6011
6039
|
* @param {Vector2} size - World space size
|
|
6012
6040
|
* @param {TileInfo} [tileInfo] - Default tile info for layer (used for size and texture)
|
|
6013
6041
|
* @param {number} [renderOrder] - Objects are sorted by renderOrder
|
|
6014
|
-
* @param {boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering
|
|
6015
6042
|
*/
|
|
6016
|
-
constructor(position, size, tileInfo=tile(), renderOrder=0
|
|
6043
|
+
constructor(position, size, tileInfo=tile(), renderOrder=0)
|
|
6017
6044
|
{
|
|
6018
|
-
super(position, size, 0, renderOrder, size);
|
|
6019
|
-
|
|
6020
|
-
this.tileInfo = tileInfo;
|
|
6021
6045
|
const canvasSize = size.multiply(tileInfo.size);
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
this.
|
|
6026
|
-
/** @property {WebGLTexture} - Texture if using WebGL for this layer */
|
|
6027
|
-
this.glTexture = useWebGL ? glCreateTexture(this.canvas) : undefined;
|
|
6028
|
-
// set no friction by default, applied friction is max of both objects
|
|
6029
|
-
this.friction = 0;
|
|
6030
|
-
// set no restitution by default, applied restitution is max of both objects
|
|
6031
|
-
this.restitution = 0;
|
|
6046
|
+
super(position, size, 0, renderOrder, canvasSize);
|
|
6047
|
+
|
|
6048
|
+
// set tile info
|
|
6049
|
+
this.tileInfo = tileInfo;
|
|
6032
6050
|
|
|
6033
6051
|
// init tile data
|
|
6034
6052
|
this.data = [];
|
|
@@ -6078,10 +6096,10 @@ class TileLayer extends CanvasLayer
|
|
|
6078
6096
|
ASSERT(drawContext !== this.context, 'must call redrawEnd() after drawing tiles!');
|
|
6079
6097
|
|
|
6080
6098
|
// draw the tile layer as a single tile
|
|
6081
|
-
const tileInfo = new TileInfo().setFullImage(this.
|
|
6099
|
+
const tileInfo = new TileInfo().setFullImage(this.textureInfo);
|
|
6082
6100
|
const size = this.drawSize || this.size;
|
|
6083
6101
|
const pos = this.pos.add(size.scale(.5));
|
|
6084
|
-
const useWebGL = glEnable && this.
|
|
6102
|
+
const useWebGL = glEnable && this.textureInfo.hasWebGL();
|
|
6085
6103
|
drawTile(pos, size, tileInfo, WHITE, 0, false, CLEAR_BLACK, useWebGL);
|
|
6086
6104
|
}
|
|
6087
6105
|
|
|
@@ -6094,8 +6112,7 @@ class TileLayer extends CanvasLayer
|
|
|
6094
6112
|
for (let y = this.size.y; y--;)
|
|
6095
6113
|
this.drawTileData(vec2(x,y), false);
|
|
6096
6114
|
this.redrawEnd();
|
|
6097
|
-
|
|
6098
|
-
this.useWebGL(); // update WebGL texture
|
|
6115
|
+
this.useWebGL();
|
|
6099
6116
|
}
|
|
6100
6117
|
|
|
6101
6118
|
/** Call to start the redraw process
|
|
@@ -6103,6 +6120,8 @@ class TileLayer extends CanvasLayer
|
|
|
6103
6120
|
* @param {boolean} [clear] - Should it clear the canvas before drawing */
|
|
6104
6121
|
redrawStart(clear=false)
|
|
6105
6122
|
{
|
|
6123
|
+
if (!this.context) return;
|
|
6124
|
+
|
|
6106
6125
|
// save current render settings
|
|
6107
6126
|
/** @type {[HTMLCanvasElement|OffscreenCanvas, CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D, Vector2, Vector2, number]} */
|
|
6108
6127
|
this.savedRenderSettings = [drawCanvas, drawContext, mainCanvasSize, cameraPos, cameraScale];
|
|
@@ -6131,6 +6150,8 @@ class TileLayer extends CanvasLayer
|
|
|
6131
6150
|
/** Call to end the redraw process */
|
|
6132
6151
|
redrawEnd()
|
|
6133
6152
|
{
|
|
6153
|
+
if (!this.context) return;
|
|
6154
|
+
|
|
6134
6155
|
ASSERT(drawContext === this.context, 'must call redrawStart() before drawing tiles');
|
|
6135
6156
|
glCopyToContext(drawContext);
|
|
6136
6157
|
//debugSaveCanvas(this.canvas);
|
|
@@ -6147,6 +6168,8 @@ class TileLayer extends CanvasLayer
|
|
|
6147
6168
|
*/
|
|
6148
6169
|
drawTileData(layerPos, clear=true)
|
|
6149
6170
|
{
|
|
6171
|
+
if (!this.context) return;
|
|
6172
|
+
|
|
6150
6173
|
// clear out where the tile was, for full opaque tiles this can be skipped
|
|
6151
6174
|
const s = this.tileInfo.size;
|
|
6152
6175
|
if (clear)
|
|
@@ -6183,11 +6206,10 @@ class TileCollisionLayer extends TileLayer
|
|
|
6183
6206
|
* @param {Vector2} size - World space size
|
|
6184
6207
|
* @param {TileInfo} [tileInfo] - Tile info for layer
|
|
6185
6208
|
* @param {number} [renderOrder] - Objects are sorted by renderOrder
|
|
6186
|
-
* @param {boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering
|
|
6187
6209
|
*/
|
|
6188
|
-
constructor(position, size, tileInfo=tile(), renderOrder=0
|
|
6210
|
+
constructor(position, size, tileInfo=tile(), renderOrder=0)
|
|
6189
6211
|
{
|
|
6190
|
-
super(position, size.floor(), tileInfo, renderOrder
|
|
6212
|
+
super(position, size.floor(), tileInfo, renderOrder);
|
|
6191
6213
|
|
|
6192
6214
|
/** @property {Array<number>} - The tile collision grid */
|
|
6193
6215
|
this.collisionData = [];
|
|
@@ -6355,7 +6377,7 @@ class TileCollisionLayer extends TileLayer
|
|
|
6355
6377
|
* tile(0, 16), // tileInfo
|
|
6356
6378
|
* rgb(1,1,1,1), rgb(0,0,0,1), // colorStartA, colorStartB
|
|
6357
6379
|
* rgb(1,1,1,0), rgb(0,0,0,0), // colorEndA, colorEndB
|
|
6358
|
-
*
|
|
6380
|
+
* 1, .2, .2, .1, .05, // particleTime, sizeStart, sizeEnd, particleSpeed, particleAngleSpeed
|
|
6359
6381
|
* .99, 1, 1, PI, .05, // damping, angleDamping, gravityScale, particleCone, fadeRate,
|
|
6360
6382
|
* .5, 1 // randomness, collide, additive, randomColorLinear, renderOrder
|
|
6361
6383
|
* );
|
|
@@ -6929,7 +6951,7 @@ let glContext;
|
|
|
6929
6951
|
let glAntialias = true;
|
|
6930
6952
|
|
|
6931
6953
|
// WebGL internal variables not exposed to documentation
|
|
6932
|
-
let glShader, glPolyShader, glPolyMode, glAdditive, glBatchAdditive, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glBatchCount;
|
|
6954
|
+
let glShader, glPolyShader, glPolyMode, glAdditive, glBatchAdditive, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glBatchCount, glTextureInfos;
|
|
6933
6955
|
|
|
6934
6956
|
// WebGL internal constants
|
|
6935
6957
|
const gl_ARRAY_BUFFER_SIZE = 5e5;
|
|
@@ -6945,6 +6967,9 @@ const gl_MAX_POLY_VERTEXES = gl_ARRAY_BUFFER_SIZE / gl_POLY_VERTEX_BYTE_STRIDE |
|
|
|
6945
6967
|
// Initialize WebGL, called automatically by the engine
|
|
6946
6968
|
function glInit()
|
|
6947
6969
|
{
|
|
6970
|
+
// keep set of texture infos so they can be restored if context is lost
|
|
6971
|
+
glTextureInfos = new Set;
|
|
6972
|
+
|
|
6948
6973
|
if (!glEnable || headlessMode) return;
|
|
6949
6974
|
|
|
6950
6975
|
// create the canvas and textures
|
|
@@ -6962,68 +6987,101 @@ function glInit()
|
|
|
6962
6987
|
// create the WebGL canvas
|
|
6963
6988
|
const rootElement = mainCanvas.parentElement;
|
|
6964
6989
|
rootElement.appendChild(glCanvas);
|
|
6990
|
+
|
|
6991
|
+
// startup webgl
|
|
6992
|
+
initWebGL();
|
|
6993
|
+
|
|
6994
|
+
// setup context lost and restore handlers
|
|
6995
|
+
glCanvas.addEventListener('webglcontextlost', (e)=>
|
|
6996
|
+
{
|
|
6997
|
+
glEnable = false; // disable WebGL rendering
|
|
6998
|
+
glCanvas.style.display = 'none'; // hide the gl canvas
|
|
6999
|
+
e.preventDefault(); // prevent default to allow restoration
|
|
7000
|
+
LOG('WebGL context lost! Switching to Canvas2d rendering.');
|
|
7001
|
+
|
|
7002
|
+
// remove WebGL textures
|
|
7003
|
+
for (const info of glTextureInfos)
|
|
7004
|
+
info.glTexture = undefined;
|
|
7005
|
+
glActiveTexture = undefined;
|
|
7006
|
+
pluginList.forEach(plugin=>plugin.glContextLost?.());
|
|
7007
|
+
});
|
|
7008
|
+
glCanvas.addEventListener('webglcontextrestored', ()=>
|
|
7009
|
+
{
|
|
7010
|
+
glEnable = true; // disable WebGL rendering
|
|
7011
|
+
glCanvas.style.display = ''; // show the gl canvas
|
|
7012
|
+
LOG('WebGL context restored, reinitializing...');
|
|
6965
7013
|
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
|
|
6969
|
-
|
|
6970
|
-
|
|
6971
|
-
|
|
6972
|
-
'in vec4 p,u,c,a;'+ // in: position/size, uvs, color, additiveColor
|
|
6973
|
-
'in float r;'+ // in: rotation
|
|
6974
|
-
'out vec2 v;'+ // out: uv
|
|
6975
|
-
'out vec4 d,e;'+ // out: color, additiveColor
|
|
6976
|
-
'void main(){'+ // shader entry point
|
|
6977
|
-
'vec2 s=(g-.5)*p.zw;'+ // get size offset
|
|
6978
|
-
'gl_Position=m*vec4(p.xy+s*cos(r)-vec2(-s.y,s)*sin(r),1,1);'+ // transform position
|
|
6979
|
-
'v=mix(u.xw,u.zy,g);'+ // pass uv to fragment shader
|
|
6980
|
-
'd=c;e=a;'+ // pass colors to fragment shader
|
|
6981
|
-
'}' // end of shader
|
|
6982
|
-
,
|
|
6983
|
-
'#version 300 es\n' + // specify GLSL ES version
|
|
6984
|
-
'precision highp float;'+ // use highp for better accuracy
|
|
6985
|
-
'uniform sampler2D s;'+ // texture
|
|
6986
|
-
'in vec2 v;'+ // in: uv
|
|
6987
|
-
'in vec4 d,e;'+ // in: color, additiveColor
|
|
6988
|
-
'out vec4 c;'+ // out: color
|
|
6989
|
-
'void main(){'+ // shader entry point
|
|
6990
|
-
'c=texture(s,v)*d+e;'+ // modulate texture by color plus additive
|
|
6991
|
-
'}' // end of shader
|
|
6992
|
-
);
|
|
7014
|
+
// reinit WebGL and restore textures
|
|
7015
|
+
initWebGL();
|
|
7016
|
+
for (const info of glTextureInfos)
|
|
7017
|
+
info.glTexture = glCreateTexture(info.image);
|
|
7018
|
+
pluginList.forEach(plugin=>plugin.glContextRestored?.());
|
|
7019
|
+
});
|
|
6993
7020
|
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
|
|
6998
|
-
|
|
6999
|
-
|
|
7000
|
-
|
|
7001
|
-
|
|
7002
|
-
|
|
7003
|
-
|
|
7004
|
-
|
|
7005
|
-
|
|
7006
|
-
|
|
7007
|
-
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
|
|
7013
|
-
|
|
7014
|
-
|
|
7021
|
+
function initWebGL()
|
|
7022
|
+
{
|
|
7023
|
+
// setup instanced rendering shader program
|
|
7024
|
+
glShader = glCreateProgram(
|
|
7025
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
7026
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
7027
|
+
'uniform mat4 m;'+ // transform matrix
|
|
7028
|
+
'in vec2 g;'+ // in: geometry
|
|
7029
|
+
'in vec4 p,u,c,a;'+ // in: position/size, uvs, color, additiveColor
|
|
7030
|
+
'in float r;'+ // in: rotation
|
|
7031
|
+
'out vec2 v;'+ // out: uv
|
|
7032
|
+
'out vec4 d,e;'+ // out: color, additiveColor
|
|
7033
|
+
'void main(){'+ // shader entry point
|
|
7034
|
+
'vec2 s=(g-.5)*p.zw;'+ // get size offset
|
|
7035
|
+
'gl_Position=m*vec4(p.xy+s*cos(r)-vec2(-s.y,s)*sin(r),1,1);'+ // transform position
|
|
7036
|
+
'v=mix(u.xw,u.zy,g);'+ // pass uv to fragment shader
|
|
7037
|
+
'd=c;e=a;'+ // pass colors to fragment shader
|
|
7038
|
+
'}' // end of shader
|
|
7039
|
+
,
|
|
7040
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
7041
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
7042
|
+
'uniform sampler2D s;'+ // texture
|
|
7043
|
+
'in vec2 v;'+ // in: uv
|
|
7044
|
+
'in vec4 d,e;'+ // in: color, additiveColor
|
|
7045
|
+
'out vec4 c;'+ // out: color
|
|
7046
|
+
'void main(){'+ // shader entry point
|
|
7047
|
+
'c=texture(s,v)*d+e;'+ // modulate texture by color plus additive
|
|
7048
|
+
'}' // end of shader
|
|
7049
|
+
);
|
|
7050
|
+
|
|
7051
|
+
// setup poly rendering shaders
|
|
7052
|
+
glPolyShader = glCreateProgram(
|
|
7053
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
7054
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
7055
|
+
'uniform mat4 m;'+ // transform matrix
|
|
7056
|
+
'in vec2 p;'+ // in: position
|
|
7057
|
+
'in vec4 c;'+ // in: color
|
|
7058
|
+
'out vec4 d;'+ // out: color
|
|
7059
|
+
'void main(){'+ // shader entry point
|
|
7060
|
+
'gl_Position=m*vec4(p,1,1);'+ // transform position
|
|
7061
|
+
'd=c;'+ // pass color to fragment shader
|
|
7062
|
+
'}' // end of shader
|
|
7063
|
+
,
|
|
7064
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
7065
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
7066
|
+
'in vec4 d;'+ // in: color
|
|
7067
|
+
'out vec4 c;'+ // out: color
|
|
7068
|
+
'void main(){'+ // shader entry point
|
|
7069
|
+
'c=d;'+ // set color
|
|
7070
|
+
'}' // end of shader
|
|
7071
|
+
);
|
|
7015
7072
|
|
|
7016
|
-
|
|
7017
|
-
|
|
7018
|
-
|
|
7019
|
-
|
|
7020
|
-
|
|
7021
|
-
|
|
7073
|
+
// init buffers
|
|
7074
|
+
const glInstanceData = new ArrayBuffer(gl_ARRAY_BUFFER_SIZE);
|
|
7075
|
+
glPositionData = new Float32Array(glInstanceData);
|
|
7076
|
+
glColorData = new Uint32Array(glInstanceData);
|
|
7077
|
+
glArrayBuffer = glContext.createBuffer();
|
|
7078
|
+
glGeometryBuffer = glContext.createBuffer();
|
|
7022
7079
|
|
|
7023
|
-
|
|
7024
|
-
|
|
7025
|
-
|
|
7026
|
-
|
|
7080
|
+
// create the geometry buffer, triangle strip square
|
|
7081
|
+
const geometry = new Float32Array([glBatchCount=0,0,1,0,0,1,1,1]);
|
|
7082
|
+
glContext.bindBuffer(glContext.ARRAY_BUFFER, glGeometryBuffer);
|
|
7083
|
+
glContext.bufferData(glContext.ARRAY_BUFFER, geometry, glContext.STATIC_DRAW);
|
|
7084
|
+
}
|
|
7027
7085
|
}
|
|
7028
7086
|
|
|
7029
7087
|
function glSetInstancedMode()
|
|
@@ -7130,7 +7188,7 @@ function glPreRender()
|
|
|
7130
7188
|
// start with additive blending off
|
|
7131
7189
|
glAdditive = glBatchAdditive = false;
|
|
7132
7190
|
|
|
7133
|
-
// force it to
|
|
7191
|
+
// force it to set instanced mode by first setting poly mode true
|
|
7134
7192
|
glPolyMode = true;
|
|
7135
7193
|
glSetInstancedMode();
|
|
7136
7194
|
}
|
|
@@ -7271,6 +7329,41 @@ function glSetTextureData(texture, image)
|
|
|
7271
7329
|
glContext.bindTexture(glContext.TEXTURE_2D, glActiveTexture); // rebind active texture
|
|
7272
7330
|
}
|
|
7273
7331
|
|
|
7332
|
+
/** Tells WebGL to create or update the glTexture and start tracking it
|
|
7333
|
+
* @param {TextureInfo} textureInfo
|
|
7334
|
+
* @memberof WebGL */
|
|
7335
|
+
function glRegisterTextureInfo(textureInfo)
|
|
7336
|
+
{
|
|
7337
|
+
if (headlessMode) return;
|
|
7338
|
+
|
|
7339
|
+
// add texture info to tracking list even if gl is not enabled
|
|
7340
|
+
glTextureInfos.add(textureInfo);
|
|
7341
|
+
|
|
7342
|
+
if (!glContext) return;
|
|
7343
|
+
|
|
7344
|
+
// create or set the texture data
|
|
7345
|
+
if (textureInfo.glTexture)
|
|
7346
|
+
glSetTextureData(textureInfo.glTexture, textureInfo.image);
|
|
7347
|
+
else
|
|
7348
|
+
textureInfo.glTexture = glCreateTexture(textureInfo.image);
|
|
7349
|
+
}
|
|
7350
|
+
|
|
7351
|
+
/** Tells WebGL to destroy the glTexture and stop tracking it
|
|
7352
|
+
* @param {TextureInfo} textureInfo
|
|
7353
|
+
* @memberof WebGL */
|
|
7354
|
+
function glUnregisterTextureInfo(textureInfo)
|
|
7355
|
+
{
|
|
7356
|
+
if (headlessMode) return;
|
|
7357
|
+
|
|
7358
|
+
// delete texture info from tracking list even if gl is not enabled
|
|
7359
|
+
glTextureInfos.delete(textureInfo);
|
|
7360
|
+
|
|
7361
|
+
// unset and destroy the texture
|
|
7362
|
+
const glTexture = textureInfo.glTexture;
|
|
7363
|
+
textureInfo.glTexture = undefined;
|
|
7364
|
+
glDeleteTexture(glTexture);
|
|
7365
|
+
}
|
|
7366
|
+
|
|
7274
7367
|
/** Draw all sprites and clear out the buffer, called automatically by the system whenever necessary
|
|
7275
7368
|
* @memberof WebGL */
|
|
7276
7369
|
function glFlush()
|
|
@@ -7861,80 +7954,97 @@ class PostProcessPlugin
|
|
|
7861
7954
|
/** Create global post processing shader
|
|
7862
7955
|
* @param {string} shaderCode
|
|
7863
7956
|
* @param {boolean} [includeOverlay]
|
|
7957
|
+
* @param {boolean} [includeMainCanvas]
|
|
7864
7958
|
* @example
|
|
7865
7959
|
* // create the post process plugin object
|
|
7866
7960
|
* new PostProcessPlugin(shaderCode);
|
|
7867
7961
|
*/
|
|
7868
|
-
constructor(shaderCode, includeOverlay=false)
|
|
7962
|
+
constructor(shaderCode, includeOverlay=false, includeMainCanvas=true)
|
|
7869
7963
|
{
|
|
7870
7964
|
ASSERT(!postProcess, 'Post process already initialized');
|
|
7871
7965
|
postProcess = this;
|
|
7872
7966
|
|
|
7873
|
-
if (headlessMode) return;
|
|
7874
|
-
|
|
7875
|
-
if (!glEnable)
|
|
7876
|
-
{
|
|
7877
|
-
console.warn('PostProcessPlugin: WebGL not enabled!');
|
|
7878
|
-
return;
|
|
7879
|
-
}
|
|
7880
|
-
|
|
7881
7967
|
if (!shaderCode) // default shader pass through
|
|
7882
7968
|
shaderCode = 'void mainImage(out vec4 c,vec2 p){c=texture(iChannel0,p/iResolution.xy);}';
|
|
7883
7969
|
|
|
7884
7970
|
/** @property {WebGLProgram} - Shader for post processing */
|
|
7885
|
-
this.shader =
|
|
7886
|
-
'#version 300 es\n' + // specify GLSL ES version
|
|
7887
|
-
'precision highp float;'+ // use highp for better accuracy
|
|
7888
|
-
'in vec2 p;'+ // position
|
|
7889
|
-
'void main(){'+ // shader entry point
|
|
7890
|
-
'gl_Position=vec4(p+p-1.,1,1);'+ // set position
|
|
7891
|
-
'}' // end of shader
|
|
7892
|
-
,
|
|
7893
|
-
'#version 300 es\n' + // specify GLSL ES version
|
|
7894
|
-
'precision highp float;'+ // use highp for better accuracy
|
|
7895
|
-
'uniform sampler2D iChannel0;'+ // input texture
|
|
7896
|
-
'uniform vec3 iResolution;'+ // size of output texture
|
|
7897
|
-
'uniform float iTime;'+ // time
|
|
7898
|
-
'out vec4 c;'+ // out color
|
|
7899
|
-
'\n' + shaderCode + '\n'+ // insert custom shader code
|
|
7900
|
-
'void main(){'+ // shader entry point
|
|
7901
|
-
'mainImage(c,gl_FragCoord.xy);'+ // call post process function
|
|
7902
|
-
'c.a=1.;'+ // always use full alpha
|
|
7903
|
-
'}' // end of shader
|
|
7904
|
-
);
|
|
7971
|
+
this.shader = undefined;
|
|
7905
7972
|
|
|
7906
7973
|
/** @property {WebGLTexture} - Texture for post processing */
|
|
7907
|
-
this.texture =
|
|
7974
|
+
this.texture = undefined;
|
|
7908
7975
|
|
|
7909
|
-
|
|
7910
|
-
|
|
7976
|
+
// setup the post processing plugin
|
|
7977
|
+
initPostProcess();
|
|
7978
|
+
engineAddPlugin(undefined, postProcessRender, postProcessContextLost, postProcessContextRestored);
|
|
7911
7979
|
|
|
7912
|
-
|
|
7913
|
-
engineAddPlugin(undefined, postProcessRender);
|
|
7914
|
-
function postProcessRender()
|
|
7980
|
+
function initPostProcess()
|
|
7915
7981
|
{
|
|
7916
7982
|
if (headlessMode) return;
|
|
7917
|
-
|
|
7918
|
-
|
|
7919
|
-
if (glEnable)
|
|
7920
|
-
{
|
|
7921
|
-
glFlush(); // clear out the buffer
|
|
7922
|
-
mainContext.drawImage(glCanvas, 0, 0); // copy to the main canvas
|
|
7923
|
-
}
|
|
7924
|
-
else
|
|
7983
|
+
|
|
7984
|
+
if (!glEnable)
|
|
7925
7985
|
{
|
|
7926
|
-
|
|
7927
|
-
|
|
7986
|
+
console.warn('PostProcessPlugin: WebGL not enabled!');
|
|
7987
|
+
return;
|
|
7928
7988
|
}
|
|
7929
7989
|
|
|
7930
|
-
|
|
7990
|
+
// create resources
|
|
7991
|
+
postProcess.texture = glCreateTexture();
|
|
7992
|
+
postProcess.shader = glCreateProgram(
|
|
7993
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
7994
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
7995
|
+
'in vec2 p;'+ // position
|
|
7996
|
+
'void main(){'+ // shader entry point
|
|
7997
|
+
'gl_Position=vec4(p+p-1.,1,1);'+ // set position
|
|
7998
|
+
'}' // end of shader
|
|
7999
|
+
,
|
|
8000
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
8001
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
8002
|
+
'uniform sampler2D iChannel0;'+ // input texture
|
|
8003
|
+
'uniform vec3 iResolution;'+ // size of output texture
|
|
8004
|
+
'uniform float iTime;'+ // time
|
|
8005
|
+
'out vec4 c;'+ // out color
|
|
8006
|
+
'\n' + shaderCode + '\n'+ // insert custom shader code
|
|
8007
|
+
'void main(){'+ // shader entry point
|
|
8008
|
+
'mainImage(c,gl_FragCoord.xy);'+ // call post process function
|
|
8009
|
+
'c.a=1.;'+ // always use full alpha
|
|
8010
|
+
'}' // end of shader
|
|
8011
|
+
);
|
|
8012
|
+
}
|
|
8013
|
+
function postProcessContextLost()
|
|
8014
|
+
{
|
|
8015
|
+
postProcess.shader = undefined;
|
|
8016
|
+
postProcess.texture = undefined;
|
|
8017
|
+
LOG('PostProcessPlugin: WebGL context lost');
|
|
8018
|
+
}
|
|
8019
|
+
function postProcessContextRestored()
|
|
8020
|
+
{
|
|
8021
|
+
initPostProcess();
|
|
8022
|
+
LOG('PostProcessPlugin: WebGL context restored');
|
|
8023
|
+
}
|
|
8024
|
+
function postProcessRender()
|
|
8025
|
+
{
|
|
8026
|
+
if (headlessMode) return;
|
|
8027
|
+
|
|
8028
|
+
if (!glEnable)
|
|
8029
|
+
return;
|
|
8030
|
+
|
|
8031
|
+
// clear out the buffer
|
|
8032
|
+
glFlush();
|
|
8033
|
+
|
|
8034
|
+
if (includeMainCanvas || includeOverlay)
|
|
7931
8035
|
{
|
|
7932
|
-
// copy
|
|
7933
|
-
mainContext.drawImage(
|
|
7934
|
-
|
|
8036
|
+
// copy WebGL to the main canvas
|
|
8037
|
+
mainContext.drawImage(glCanvas, 0, 0);
|
|
8038
|
+
|
|
8039
|
+
if (includeOverlay)
|
|
8040
|
+
{
|
|
8041
|
+
// copy overlay canvas so it will be included in post processing
|
|
8042
|
+
mainContext.drawImage(overlayCanvas, 0, 0);
|
|
8043
|
+
overlayCanvas.width |= 0; // clear overlay canvas
|
|
8044
|
+
}
|
|
7935
8045
|
}
|
|
7936
8046
|
|
|
7937
|
-
// setup shader program to draw
|
|
8047
|
+
// setup shader program to draw a quad
|
|
7938
8048
|
glContext.useProgram(postProcess.shader);
|
|
7939
8049
|
glContext.bindBuffer(glContext.ARRAY_BUFFER, glGeometryBuffer);
|
|
7940
8050
|
glContext.pixelStorei(glContext.UNPACK_FLIP_Y_WEBGL, 1);
|
|
@@ -7943,7 +8053,10 @@ class PostProcessPlugin
|
|
|
7943
8053
|
// set textures, pass in the 2d canvas and gl canvas in separate texture channels
|
|
7944
8054
|
glContext.activeTexture(glContext.TEXTURE0);
|
|
7945
8055
|
glContext.bindTexture(glContext.TEXTURE_2D, postProcess.texture);
|
|
7946
|
-
|
|
8056
|
+
if (includeMainCanvas || includeOverlay)
|
|
8057
|
+
{
|
|
8058
|
+
glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, glContext.RGBA, glContext.UNSIGNED_BYTE, mainCanvas);
|
|
8059
|
+
}
|
|
7947
8060
|
|
|
7948
8061
|
// set vertex position attribute
|
|
7949
8062
|
const vertexByteStride = 8;
|
|
@@ -8173,6 +8286,8 @@ class UISystemPlugin
|
|
|
8173
8286
|
this.defaultHoverColor = hsl(0,0,.9);
|
|
8174
8287
|
/** @property {Color} - Default color for disabled UI elements */
|
|
8175
8288
|
this.defaultDisabledColor = hsl(0,0,.3);
|
|
8289
|
+
/** @property {Color} - Uses a gradient fill combined with color */
|
|
8290
|
+
this.defaultGradientColor = undefined;
|
|
8176
8291
|
/** @property {number} - Default line width for UI elements */
|
|
8177
8292
|
this.defaultLineWidth = 4;
|
|
8178
8293
|
/** @property {number} - Default rounded rect corner radius for UI elements */
|
|
@@ -8180,7 +8295,7 @@ class UISystemPlugin
|
|
|
8180
8295
|
/** @property {number} - Default scale to use for fitting text to object */
|
|
8181
8296
|
this.defaultTextScale = .8;
|
|
8182
8297
|
/** @property {string} - Default font for UI elements */
|
|
8183
|
-
this.defaultFont =
|
|
8298
|
+
this.defaultFont = fontDefault;
|
|
8184
8299
|
/** @property {Sound} - Default sound when interactive UI element is pressed */
|
|
8185
8300
|
this.defaultSoundPress = undefined;
|
|
8186
8301
|
/** @property {Sound} - Default sound when interactive UI element is released */
|
|
@@ -8197,7 +8312,9 @@ class UISystemPlugin
|
|
|
8197
8312
|
this.hoverObject = undefined;
|
|
8198
8313
|
/** @property {UIObject} - Hover object at start of update */
|
|
8199
8314
|
this.lastHoverObject = undefined;
|
|
8200
|
-
|
|
8315
|
+
/** @property {number} - If set ui coords will be renormalized to this canvas height */
|
|
8316
|
+
this.nativeHeight = 0;
|
|
8317
|
+
|
|
8201
8318
|
engineAddPlugin(uiUpdate, uiRender);
|
|
8202
8319
|
|
|
8203
8320
|
// setup recursive update and render
|
|
@@ -8237,6 +8354,17 @@ class UISystemPlugin
|
|
|
8237
8354
|
}
|
|
8238
8355
|
function uiRender()
|
|
8239
8356
|
{
|
|
8357
|
+
const context = uiSystem.uiContext;
|
|
8358
|
+
context.save();
|
|
8359
|
+
if (uiSystem.nativeHeight)
|
|
8360
|
+
{
|
|
8361
|
+
// convert to native height
|
|
8362
|
+
const s = mainCanvasSize.y / uiSystem.nativeHeight;
|
|
8363
|
+
context.translate(-s*mainCanvasSize.x/2,0);
|
|
8364
|
+
context.scale(s,s);
|
|
8365
|
+
context.translate(mainCanvasSize.x/2/s,0);
|
|
8366
|
+
}
|
|
8367
|
+
|
|
8240
8368
|
function renderObject(o)
|
|
8241
8369
|
{
|
|
8242
8370
|
if (!o.visible)
|
|
@@ -8248,6 +8376,7 @@ class UISystemPlugin
|
|
|
8248
8376
|
renderObject(c);
|
|
8249
8377
|
}
|
|
8250
8378
|
uiSystem.uiObjects.forEach(o=> o.parent || renderObject(o));
|
|
8379
|
+
context.restore();
|
|
8251
8380
|
}
|
|
8252
8381
|
}
|
|
8253
8382
|
|
|
@@ -8257,8 +8386,9 @@ class UISystemPlugin
|
|
|
8257
8386
|
* @param {Color} [color=uiSystem.defaultColor]
|
|
8258
8387
|
* @param {number} [lineWidth=uiSystem.defaultLineWidth]
|
|
8259
8388
|
* @param {Color} [lineColor=uiSystem.defaultLineColor]
|
|
8260
|
-
* @param {number} [cornerRadius=uiSystem.defaultCornerRadius]
|
|
8261
|
-
|
|
8389
|
+
* @param {number} [cornerRadius=uiSystem.defaultCornerRadius]
|
|
8390
|
+
* @param {Color} [gradientColor=uiSystem.defaultGradientColor] */
|
|
8391
|
+
drawRect(pos, size, color=uiSystem.defaultColor, lineWidth=uiSystem.defaultLineWidth, lineColor=uiSystem.defaultLineColor, cornerRadius=uiSystem.defaultCornerRadius, gradientColor=uiSystem.defaultGradientColor)
|
|
8262
8392
|
{
|
|
8263
8393
|
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
8264
8394
|
ASSERT(isVector2(size), 'size must be a vec2');
|
|
@@ -8268,7 +8398,18 @@ class UISystemPlugin
|
|
|
8268
8398
|
ASSERT(isNumber(cornerRadius), 'cornerRadius must be a number');
|
|
8269
8399
|
|
|
8270
8400
|
const context = uiSystem.uiContext;
|
|
8271
|
-
|
|
8401
|
+
if (gradientColor)
|
|
8402
|
+
{
|
|
8403
|
+
const g = context.createLinearGradient(
|
|
8404
|
+
pos.x, pos.y-size.y/2, pos.x, pos.y+size.y/2);
|
|
8405
|
+
const c = color.toString();
|
|
8406
|
+
g.addColorStop(0, c);
|
|
8407
|
+
g.addColorStop(.5, gradientColor.toString());
|
|
8408
|
+
g.addColorStop(1, c);
|
|
8409
|
+
context.fillStyle = g;
|
|
8410
|
+
}
|
|
8411
|
+
else
|
|
8412
|
+
context.fillStyle = color.toString();
|
|
8272
8413
|
context.beginPath();
|
|
8273
8414
|
if (cornerRadius && context['roundRect'])
|
|
8274
8415
|
context['roundRect'](pos.x-size.x/2, pos.y-size.y/2, size.x, size.y, cornerRadius);
|
|
@@ -8325,10 +8466,11 @@ class UISystemPlugin
|
|
|
8325
8466
|
* @param {Color} [lineColor=uiSystem.defaultLineColor]
|
|
8326
8467
|
* @param {string} [align]
|
|
8327
8468
|
* @param {string} [font=uiSystem.defaultFont]
|
|
8469
|
+
* @param {string} [fontStyle]
|
|
8328
8470
|
* @param {boolean} [applyMaxWidth=true] */
|
|
8329
|
-
drawText(text, pos, size, color=uiSystem.defaultColor, lineWidth=uiSystem.defaultLineWidth, lineColor=uiSystem.defaultLineColor, align='center', font=uiSystem.defaultFont, applyMaxWidth=true)
|
|
8471
|
+
drawText(text, pos, size, color=uiSystem.defaultColor, lineWidth=uiSystem.defaultLineWidth, lineColor=uiSystem.defaultLineColor, align='center', font=uiSystem.defaultFont, fontStyle='', applyMaxWidth=true)
|
|
8330
8472
|
{
|
|
8331
|
-
drawTextScreen(text, pos, size.y, color, lineWidth, lineColor, align, font, applyMaxWidth ? size.x : undefined, uiSystem.uiContext);
|
|
8473
|
+
drawTextScreen(text, pos, size.y, color, lineWidth, lineColor, align, font, fontStyle, applyMaxWidth ? size.x : undefined, uiSystem.uiContext);
|
|
8332
8474
|
}
|
|
8333
8475
|
|
|
8334
8476
|
/**
|
|
@@ -8389,17 +8531,21 @@ class UIObject
|
|
|
8389
8531
|
/** @property {boolean} - Is this object disabled? */
|
|
8390
8532
|
this.disabled = false;
|
|
8391
8533
|
/** @property {Color} - Color for text */
|
|
8392
|
-
this.textColor = uiSystem.defaultTextColor.copy()
|
|
8534
|
+
this.textColor = uiSystem.defaultTextColor.copy();
|
|
8393
8535
|
/** @property {Color} - Color used when hovering over the object */
|
|
8394
|
-
this.hoverColor = uiSystem.defaultHoverColor.copy()
|
|
8536
|
+
this.hoverColor = uiSystem.defaultHoverColor.copy();
|
|
8395
8537
|
/** @property {Color} - Color for line drawing */
|
|
8396
|
-
this.lineColor = uiSystem.defaultLineColor.copy()
|
|
8538
|
+
this.lineColor = uiSystem.defaultLineColor.copy();
|
|
8539
|
+
/** @property {Color} - Uses a gradient fill combined with color */
|
|
8540
|
+
this.gradientColor = uiSystem.defaultGradientColor ? uiSystem.defaultGradientColor.copy() : undefined;
|
|
8397
8541
|
/** @property {number} - Width for line drawing */
|
|
8398
8542
|
this.lineWidth = uiSystem.defaultLineWidth;
|
|
8399
8543
|
/** @property {number} - Corner radius for rounded rects */
|
|
8400
8544
|
this.cornerRadius = uiSystem.defaultCornerRadius;
|
|
8401
8545
|
/** @property {string} - Font for this objecct */
|
|
8402
8546
|
this.font = uiSystem.defaultFont;
|
|
8547
|
+
/** @property {string} - Font style for this object or undefined */
|
|
8548
|
+
this.fontStyle = undefined;
|
|
8403
8549
|
/** @property {number} - Override for text width */
|
|
8404
8550
|
this.textWidth = undefined;
|
|
8405
8551
|
/** @property {number} - Override for text height */
|
|
@@ -8424,6 +8570,8 @@ class UIObject
|
|
|
8424
8570
|
this.interactive = false;
|
|
8425
8571
|
/** @property {boolean} - Activate when dragged over with mouse held down */
|
|
8426
8572
|
this.dragActivate = false;
|
|
8573
|
+
/** @property {boolean} - True if this can be a hover object */
|
|
8574
|
+
this.canBeHover = true;
|
|
8427
8575
|
uiSystem.uiObjects.push(this);
|
|
8428
8576
|
}
|
|
8429
8577
|
|
|
@@ -8447,6 +8595,26 @@ class UIObject
|
|
|
8447
8595
|
child.parent = undefined;
|
|
8448
8596
|
}
|
|
8449
8597
|
|
|
8598
|
+
/** Check if the mouse is overlapping a box in screen space
|
|
8599
|
+
* @return {boolean} - True if overlapping
|
|
8600
|
+
*/
|
|
8601
|
+
isMouseOverlapping()
|
|
8602
|
+
{
|
|
8603
|
+
const size = !isTouchDevice ? this.size :
|
|
8604
|
+
this.size.add(vec2(this.extraTouchSize || 0));
|
|
8605
|
+
if (!uiSystem.nativeHeight)
|
|
8606
|
+
return isOverlapping(this.pos, size, mousePosScreen);
|
|
8607
|
+
|
|
8608
|
+
const s = mainCanvasSize.y / uiSystem.nativeHeight;
|
|
8609
|
+
const sInv = 1/s;
|
|
8610
|
+
let pos = mousePosScreen.copy();
|
|
8611
|
+
pos.x += s*mainCanvasSize.x/2;
|
|
8612
|
+
pos.x *= sInv;
|
|
8613
|
+
pos.y *= sInv;
|
|
8614
|
+
pos.x -= sInv*mainCanvasSize.x/2;
|
|
8615
|
+
return isOverlapping(this.pos, size, pos);
|
|
8616
|
+
}
|
|
8617
|
+
|
|
8450
8618
|
/** Update the object, called automatically by plugin once each frame */
|
|
8451
8619
|
update()
|
|
8452
8620
|
{
|
|
@@ -8454,13 +8622,10 @@ class UIObject
|
|
|
8454
8622
|
const isActive = this.isActiveObject();
|
|
8455
8623
|
const mouseDown = mouseIsDown(0);
|
|
8456
8624
|
const mousePress = this.dragActivate ? mouseDown : mouseWasPressed(0);
|
|
8457
|
-
if (
|
|
8625
|
+
if (this.canBeHover)
|
|
8458
8626
|
if (mousePress || isActive || (!mouseDown && !isTouchDevice))
|
|
8459
|
-
|
|
8460
|
-
|
|
8461
|
-
if (isOverlapping(this.pos, size, mousePosScreen))
|
|
8462
|
-
uiSystem.hoverObject = this;
|
|
8463
|
-
}
|
|
8627
|
+
if (!uiSystem.hoverObject && this.isMouseOverlapping())
|
|
8628
|
+
uiSystem.hoverObject = this;
|
|
8464
8629
|
if (this.isHoverObject())
|
|
8465
8630
|
{
|
|
8466
8631
|
if (!this.disabled)
|
|
@@ -8585,11 +8750,13 @@ class UIText extends UIObject
|
|
|
8585
8750
|
|
|
8586
8751
|
// make text not outlined by default
|
|
8587
8752
|
this.lineWidth = 0;
|
|
8753
|
+
// text can not be a hover object by default
|
|
8754
|
+
this.canBeHover = false;
|
|
8588
8755
|
}
|
|
8589
8756
|
render()
|
|
8590
8757
|
{
|
|
8591
8758
|
const textSize = this.getTextSize();
|
|
8592
|
-
uiSystem.drawText(this.text, this.pos, textSize, this.textColor, this.lineWidth, this.lineColor, this.align, this.font);
|
|
8759
|
+
uiSystem.drawText(this.text, this.pos, textSize, this.textColor, this.lineWidth, this.lineColor, this.align, this.font, this.fontStyle);
|
|
8593
8760
|
}
|
|
8594
8761
|
}
|
|
8595
8762
|
|
|
@@ -8655,7 +8822,7 @@ class UIButton extends UIObject
|
|
|
8655
8822
|
|
|
8656
8823
|
// set properties
|
|
8657
8824
|
this.text = text;
|
|
8658
|
-
this.color = color.copy()
|
|
8825
|
+
this.color = color.copy();
|
|
8659
8826
|
this.interactive = true;
|
|
8660
8827
|
}
|
|
8661
8828
|
render()
|
|
@@ -8665,7 +8832,7 @@ class UIButton extends UIObject
|
|
|
8665
8832
|
// draw the text scaled to fit
|
|
8666
8833
|
const textSize = this.getTextSize();
|
|
8667
8834
|
uiSystem.drawText(this.text, this.pos, textSize,
|
|
8668
|
-
this.textColor, 0, undefined, this.align, this.font);
|
|
8835
|
+
this.textColor, 0, undefined, this.align, this.font, this.fontStyle);
|
|
8669
8836
|
}
|
|
8670
8837
|
}
|
|
8671
8838
|
|
|
@@ -8719,7 +8886,7 @@ class UICheckbox extends UIObject
|
|
|
8719
8886
|
const textSize = this.getTextSize();
|
|
8720
8887
|
const pos = this.pos.add(vec2(this.size.x,0));
|
|
8721
8888
|
uiSystem.drawText(this.text, pos, textSize,
|
|
8722
|
-
this.textColor, 0, undefined, 'left', this.font, false);
|
|
8889
|
+
this.textColor, 0, undefined, 'left', this.font, this.fontStyle, false);
|
|
8723
8890
|
}
|
|
8724
8891
|
}
|
|
8725
8892
|
|
|
@@ -8797,14 +8964,13 @@ class UIScrollbar extends UIObject
|
|
|
8797
8964
|
const handlePos = isHorizontal ?
|
|
8798
8965
|
vec2(lerp(p1, p2, this.value), this.pos.y) :
|
|
8799
8966
|
vec2(this.pos.x, lerp(p2, p1, this.value))
|
|
8800
|
-
const handleColor = this.disabled ? this.disabledColor :
|
|
8801
|
-
this.interactive && this.isActiveObject() ? this.color : this.handleColor;
|
|
8967
|
+
const handleColor = this.disabled ? this.disabledColor : this.handleColor;
|
|
8802
8968
|
uiSystem.drawRect(handlePos, vec2(handleSize), handleColor, this.lineWidth, this.lineColor, this.cornerRadius);
|
|
8803
8969
|
|
|
8804
8970
|
// draw the text scaled to fit on the scrollbar
|
|
8805
8971
|
const textSize = this.getTextSize();
|
|
8806
8972
|
uiSystem.drawText(this.text, this.pos, textSize,
|
|
8807
|
-
this.textColor, 0, undefined, this.align, this.font);
|
|
8973
|
+
this.textColor, 0, undefined, this.align, this.font, this.fontStyle);
|
|
8808
8974
|
}
|
|
8809
8975
|
}
|
|
8810
8976
|
/**
|