littlejsengine 1.3.8 → 1.4.5
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/README.md +30 -9
- package/build.bat +1 -1
- package/buildSetup.bat +2 -2
- package/engine/engine.all.js +276 -167
- package/engine/engine.all.min.js +1 -1
- package/engine/engine.all.module.js +4812 -0
- package/engine/engine.all.release.js +259 -143
- package/engine/engine.all.release.module.js +4457 -0
- package/engine/engine.js +12 -8
- package/engine/engineBuild.bat +1 -1
- package/engine/engineBuildModule.mjs +14 -0
- package/engine/engineDebug.js +17 -24
- package/engine/engineDraw.js +16 -16
- package/engine/engineExportModule.js +333 -0
- package/engine/engineInput.js +9 -8
- package/engine/engineMedals.js +10 -6
- package/engine/engineObject.js +4 -4
- package/engine/engineParticles.js +45 -21
- package/engine/engineTileLayer.js +2 -2
- package/engine/engineUtilities.js +47 -25
- package/engine/engineWebGL.js +114 -54
- package/engine/index.d.ts +64 -30
- package/examples/breakout/game.js +63 -11
- package/examples/breakout/gameObjects.js +14 -12
- package/examples/breakout/index.html +3 -3
- package/examples/empty/game.js +35 -0
- package/examples/empty/index.html +9 -0
- package/examples/empty/tiles.png +0 -0
- package/examples/module/game.js +122 -0
- package/examples/module/index.html +9 -0
- package/examples/module/tiles.png +0 -0
- package/examples/particles/index.html +14 -14
- package/examples/particles/tiles.png +0 -0
- package/examples/platformer/game.js +1 -1
- package/examples/platformer/gameObjects.js +7 -7
- package/examples/platformer/gamePlayer.js +7 -5
- package/examples/platformer/index.html +6 -6
- package/examples/puzzle/index.html +2 -2
- package/examples/stress/index.html +2 -2
- package/game.js +10 -12
- package/index.html +13 -13
- package/package.json +36 -32
|
@@ -355,6 +355,28 @@ class Vector2
|
|
|
355
355
|
|
|
356
356
|
///////////////////////////////////////////////////////////////////////////////
|
|
357
357
|
|
|
358
|
+
/**
|
|
359
|
+
* Create a color object with RGBA values
|
|
360
|
+
* @param {Number} [r=1]
|
|
361
|
+
* @param {Number} [g=1]
|
|
362
|
+
* @param {Number} [b=1]
|
|
363
|
+
* @param {Number} [a=1]
|
|
364
|
+
* @return {Color}
|
|
365
|
+
* @memberof Utilities
|
|
366
|
+
*/
|
|
367
|
+
const colorRGBA = (r, g, b, a)=> new Color(r, g, b, a);
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Create a color object with HSLA values
|
|
371
|
+
* @param {Number} [h=0]
|
|
372
|
+
* @param {Number} [s=0]
|
|
373
|
+
* @param {Number} [l=1]
|
|
374
|
+
* @param {Number} [a=1]
|
|
375
|
+
* @return {Color}
|
|
376
|
+
* @memberof Utilities
|
|
377
|
+
*/
|
|
378
|
+
const colorHSLA = (h, s, l, a)=> new Color().setHSLA(h, s, l, a);
|
|
379
|
+
|
|
358
380
|
/**
|
|
359
381
|
* Color object (red, green, blue, alpha) with some helpful functions
|
|
360
382
|
* @example
|
|
@@ -446,10 +468,10 @@ class Color
|
|
|
446
468
|
* @return {Array} */
|
|
447
469
|
getHSLA()
|
|
448
470
|
{
|
|
449
|
-
const r = this.r;
|
|
450
|
-
const g = this.g;
|
|
451
|
-
const b = this.b;
|
|
452
|
-
const a = this.a;
|
|
471
|
+
const r = clamp(this.r);
|
|
472
|
+
const g = clamp(this.g);
|
|
473
|
+
const b = clamp(this.b);
|
|
474
|
+
const a = clamp(this.a);
|
|
453
475
|
const max = Math.max(r, g, b);
|
|
454
476
|
const min = Math.min(r, g, b);
|
|
455
477
|
const l = (max + min) / 2;
|
|
@@ -485,20 +507,13 @@ class Color
|
|
|
485
507
|
).clamp();
|
|
486
508
|
}
|
|
487
509
|
|
|
488
|
-
/** Returns this color expressed as
|
|
510
|
+
/** Returns this color expressed as a hex color code
|
|
511
|
+
* @param {Boolean} [useAlpha=1] - if alpha should be included in result
|
|
489
512
|
* @return {String} */
|
|
490
|
-
toString()
|
|
513
|
+
toString(useAlpha = 1)
|
|
491
514
|
{
|
|
492
|
-
|
|
493
|
-
return
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
/** Returns this color expressed as 32 bit integer RGBA value
|
|
497
|
-
* @return {Number} */
|
|
498
|
-
rgbaInt()
|
|
499
|
-
{
|
|
500
|
-
ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1 && this.a>=0 && this.a<=1);
|
|
501
|
-
return (this.r*255|0) + (this.g*255<<8) + (this.b*255<<16) + (this.a*255<<24);
|
|
515
|
+
const toHex = (c)=> ((c=c*255|0)<16 ? '0' : '') + c.toString(16);
|
|
516
|
+
return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b) + (useAlpha ? toHex(this.a) : '');
|
|
502
517
|
}
|
|
503
518
|
|
|
504
519
|
/** Set this color from a hex code
|
|
@@ -506,21 +521,24 @@ class Color
|
|
|
506
521
|
* @return {Color} */
|
|
507
522
|
setHex(hex)
|
|
508
523
|
{
|
|
509
|
-
const fromHex = (
|
|
524
|
+
const fromHex = (c)=> clamp(parseInt(hex.slice(c,c+2),16)/255);
|
|
510
525
|
this.r = fromHex(1);
|
|
511
526
|
this.g = fromHex(3),
|
|
512
527
|
this.b = fromHex(5);
|
|
513
|
-
this.a = 1;
|
|
514
|
-
ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1);
|
|
528
|
+
this.a = hex.length > 7 ? fromHex(7) : 1;
|
|
515
529
|
return this;
|
|
516
530
|
}
|
|
517
|
-
|
|
518
|
-
/** Returns this color expressed as
|
|
519
|
-
* @return {
|
|
520
|
-
|
|
531
|
+
|
|
532
|
+
/** Returns this color expressed as 32 bit RGBA value
|
|
533
|
+
* @return {Number} */
|
|
534
|
+
rgbaInt()
|
|
521
535
|
{
|
|
522
|
-
const
|
|
523
|
-
|
|
536
|
+
const toByte = (c)=> clamp(c)*255|0;
|
|
537
|
+
const r = toByte(this.r);
|
|
538
|
+
const g = toByte(this.g)<<8;
|
|
539
|
+
const b = toByte(this.b)<<16;
|
|
540
|
+
const a = toByte(this.a)<<24;
|
|
541
|
+
return r + g + b + a;
|
|
524
542
|
}
|
|
525
543
|
}
|
|
526
544
|
|
|
@@ -571,6 +589,10 @@ class Timer
|
|
|
571
589
|
/** Returns this timer expressed as a string
|
|
572
590
|
* @return {String} */
|
|
573
591
|
toString() { if (debug) { return this.unset() ? 'unset' : Math.abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ); } }
|
|
592
|
+
|
|
593
|
+
/** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
|
|
594
|
+
* @return {Number} */
|
|
595
|
+
valueOf() { return this.get(); }
|
|
574
596
|
}
|
|
575
597
|
/**
|
|
576
598
|
* LittleJS Engine Settings
|
|
@@ -819,7 +841,7 @@ let medalDisplayIconSize = 50;
|
|
|
819
841
|
const engineName = 'LittleJS';
|
|
820
842
|
|
|
821
843
|
/** Version of engine */
|
|
822
|
-
const engineVersion = '1.
|
|
844
|
+
const engineVersion = '1.4.5';
|
|
823
845
|
|
|
824
846
|
/** Frames per second to update objects
|
|
825
847
|
* @default */
|
|
@@ -854,8 +876,7 @@ let frameTimeLastMS = 0, frameTimeBufferMS = 0, tileImageSize, tileImageFixBleed
|
|
|
854
876
|
let averageFPS, drawCount;
|
|
855
877
|
|
|
856
878
|
// css text used for elements created by engine
|
|
857
|
-
const styleBody = 'margin:0;overflow:hidden;background:#000'
|
|
858
|
-
';touch-action:none;user-select:none;-webkit-user-select:none;-moz-user-select:none';
|
|
879
|
+
const styleBody = 'margin:0;overflow:hidden;background:#000';
|
|
859
880
|
const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
|
|
860
881
|
|
|
861
882
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -885,7 +906,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
885
906
|
|
|
886
907
|
// init stuff and start engine
|
|
887
908
|
debugInit();
|
|
888
|
-
glInit();
|
|
909
|
+
glEnable && glInit();
|
|
889
910
|
|
|
890
911
|
// create overlay canvas for hud to appear above gl canvas
|
|
891
912
|
document.body.appendChild(overlayCanvas = document.createElement('canvas'));
|
|
@@ -905,10 +926,14 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
905
926
|
frameTimeLastMS = frameTimeMS;
|
|
906
927
|
if (debug || showWatermark)
|
|
907
928
|
averageFPS = lerp(.05, averageFPS || 0, 1e3/(frameTimeDeltaMS||1));
|
|
929
|
+
const debugSpeedUp = debug && keyIsDown(107); // +
|
|
930
|
+
const debugSpeedDown = debug && keyIsDown(109); // -
|
|
908
931
|
if (debug)
|
|
909
|
-
frameTimeDeltaMS *=
|
|
932
|
+
frameTimeDeltaMS *= debugSpeedUp ? 5 : debugSpeedDown ? .2 : 1; // +/- to speed/slow time
|
|
910
933
|
timeReal += frameTimeDeltaMS / 1e3;
|
|
911
|
-
frameTimeBufferMS
|
|
934
|
+
frameTimeBufferMS += !paused * frameTimeDeltaMS;
|
|
935
|
+
if (!debugSpeedUp)
|
|
936
|
+
frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp incase of slow framerate
|
|
912
937
|
|
|
913
938
|
if (canvasFixedSize.x)
|
|
914
939
|
{
|
|
@@ -981,10 +1006,11 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
981
1006
|
for (const o of engineObjects)
|
|
982
1007
|
o.destroyed || o.render();
|
|
983
1008
|
gameRenderPost();
|
|
1009
|
+
glRenderPostProcess();
|
|
984
1010
|
medalsRender();
|
|
985
1011
|
touchGamepadRender();
|
|
986
1012
|
debugRender();
|
|
987
|
-
glCopyToContext(mainContext);
|
|
1013
|
+
glEnable && glCopyToContext(mainContext);
|
|
988
1014
|
|
|
989
1015
|
if (showWatermark)
|
|
990
1016
|
{
|
|
@@ -1019,7 +1045,7 @@ function enginePreRender()
|
|
|
1019
1045
|
mainContext.imageSmoothingEnabled = !cavasPixelated;
|
|
1020
1046
|
|
|
1021
1047
|
// setup gl rendering if enabled
|
|
1022
|
-
glPreRender(mainCanvas.width, mainCanvas.height, cameraPos.x, cameraPos.y, cameraScale);
|
|
1048
|
+
glEnable && glPreRender(mainCanvas.width, mainCanvas.height, cameraPos.x, cameraPos.y, cameraScale);
|
|
1023
1049
|
}
|
|
1024
1050
|
|
|
1025
1051
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -1380,7 +1406,7 @@ class EngineObject
|
|
|
1380
1406
|
* @return {Boolean} - true if the raycast should hit */
|
|
1381
1407
|
collideWithTileRaycast(tileData, pos) { return tileData > 0; }
|
|
1382
1408
|
|
|
1383
|
-
/** Called to check if a
|
|
1409
|
+
/** Called to check if a object collision should be resolved
|
|
1384
1410
|
* @param {EngineObject} object - the object to test against
|
|
1385
1411
|
* @return {Boolean} - true if the collision should be resolved
|
|
1386
1412
|
*/
|
|
@@ -1425,10 +1451,10 @@ class EngineObject
|
|
|
1425
1451
|
}
|
|
1426
1452
|
|
|
1427
1453
|
/** Set how this object collides
|
|
1428
|
-
* @param {boolean} [collideSolidObjects=
|
|
1429
|
-
* @param {boolean} [isSolid=
|
|
1454
|
+
* @param {boolean} [collideSolidObjects=1] - Does it collide with solid objects
|
|
1455
|
+
* @param {boolean} [isSolid=1] - Does it collide with and block other objects (expensive in large numbers)
|
|
1430
1456
|
* @param {boolean} [collideTiles=1] - Does it collide with the tile collision */
|
|
1431
|
-
setCollision(collideSolidObjects=
|
|
1457
|
+
setCollision(collideSolidObjects=1, isSolid=1, collideTiles=1)
|
|
1432
1458
|
{
|
|
1433
1459
|
ASSERT(collideSolidObjects || !isSolid); // solid objects must be set to collide
|
|
1434
1460
|
|
|
@@ -1645,7 +1671,7 @@ function drawLine(posA, posB, thickness=.1, color, useWebGL)
|
|
|
1645
1671
|
{
|
|
1646
1672
|
const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2);
|
|
1647
1673
|
const size = vec2(thickness, halfDelta.length()*2);
|
|
1648
|
-
drawRect(posA.add(halfDelta), size, color, halfDelta.angle(),
|
|
1674
|
+
drawRect(posA.add(halfDelta), size, color, halfDelta.angle(), useWebGL);
|
|
1649
1675
|
}
|
|
1650
1676
|
|
|
1651
1677
|
/** Draw directly to a 2d canvas context in world space
|
|
@@ -1662,7 +1688,7 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, context = mainCont
|
|
|
1662
1688
|
pos = worldToScreen(pos);
|
|
1663
1689
|
size = size.scale(cameraScale);
|
|
1664
1690
|
context.save();
|
|
1665
|
-
context.translate(pos.x+.5|0, pos.y
|
|
1691
|
+
context.translate(pos.x+.5|0, pos.y+.5|0);
|
|
1666
1692
|
context.rotate(angle);
|
|
1667
1693
|
context.scale(mirror ? -size.x : size.x, size.y);
|
|
1668
1694
|
drawFunction(context);
|
|
@@ -1691,21 +1717,21 @@ function setBlendMode(additive, useWebGL=glEnable)
|
|
|
1691
1717
|
* @param {Color} [lineColor=new Color(0,0,0)]
|
|
1692
1718
|
* @param {String} [textAlign='center']
|
|
1693
1719
|
* @memberof Draw */
|
|
1694
|
-
function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault)
|
|
1720
|
+
function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault, context=overlayContext)
|
|
1695
1721
|
{
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1722
|
+
context.fillStyle = color;
|
|
1723
|
+
context.lineWidth = lineWidth;
|
|
1724
|
+
context.strokeStyle = lineColor;
|
|
1725
|
+
context.textAlign = textAlign;
|
|
1726
|
+
context.font = size + 'px '+ font;
|
|
1727
|
+
context.textBaseline = 'middle';
|
|
1728
|
+
context.lineJoin = 'round';
|
|
1703
1729
|
|
|
1704
1730
|
pos = pos.copy();
|
|
1705
1731
|
(text+'').split('\n').forEach(line=>
|
|
1706
1732
|
{
|
|
1707
|
-
lineWidth &&
|
|
1708
|
-
|
|
1733
|
+
lineWidth && context.strokeText(line, pos.x, pos.y);
|
|
1734
|
+
context.fillText(line, pos.x, pos.y);
|
|
1709
1735
|
pos.y += size;
|
|
1710
1736
|
});
|
|
1711
1737
|
}
|
|
@@ -1722,7 +1748,7 @@ function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineCol
|
|
|
1722
1748
|
* @memberof Draw */
|
|
1723
1749
|
function drawText(text, pos, size=1, color, lineWidth, lineColor, textAlign, font)
|
|
1724
1750
|
{
|
|
1725
|
-
drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font);
|
|
1751
|
+
drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, mainContext);
|
|
1726
1752
|
}
|
|
1727
1753
|
|
|
1728
1754
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -1745,7 +1771,7 @@ let engineFontImage;
|
|
|
1745
1771
|
class FontImage
|
|
1746
1772
|
{
|
|
1747
1773
|
/** Create an image font
|
|
1748
|
-
* @param {
|
|
1774
|
+
* @param {HTMLImageElement} [image] - The image the font is stored in, if undefined the default font is used
|
|
1749
1775
|
* @param {Vector2} [tileSize=vec2(8)] - The size of the font source tiles
|
|
1750
1776
|
* @param {Vector2} [paddingSize=vec2(0,1)] - How much extra space to add between characters
|
|
1751
1777
|
* @param {Number} [startTileIndex=0] - Tile index in image where font starts
|
|
@@ -1782,7 +1808,7 @@ class FontImage
|
|
|
1782
1808
|
const size = this.tileSize;
|
|
1783
1809
|
const drawSize = size.add(this.paddingSize).scale(scale);
|
|
1784
1810
|
const cols = this.image.width / this.tileSize.x |0;
|
|
1785
|
-
text.split('\n').forEach((line, i)=>
|
|
1811
|
+
(text+'').split('\n').forEach((line, i)=>
|
|
1786
1812
|
{
|
|
1787
1813
|
const centerOffset = center ? line.length * size.x * scale / 2 |0 : 0;
|
|
1788
1814
|
for(let j=line.length; j--;)
|
|
@@ -1844,6 +1870,7 @@ function toggleFullscreen()
|
|
|
1844
1870
|
document.body.mozRequestFullScreen();
|
|
1845
1871
|
}
|
|
1846
1872
|
}
|
|
1873
|
+
|
|
1847
1874
|
/**
|
|
1848
1875
|
* LittleJS Input System
|
|
1849
1876
|
* <br> - Tracks key down, pressed, and released
|
|
@@ -1958,7 +1985,7 @@ let inputData = [[]];
|
|
|
1958
1985
|
function inputUpdate()
|
|
1959
1986
|
{
|
|
1960
1987
|
// clear input when lost focus (prevent stuck keys)
|
|
1961
|
-
document.hasFocus() || clearInput();
|
|
1988
|
+
isTouchDevice || document.hasFocus() || clearInput();
|
|
1962
1989
|
|
|
1963
1990
|
// update mouse world space position
|
|
1964
1991
|
mousePos = screenToWorld(mousePosScreen);
|
|
@@ -2033,7 +2060,7 @@ function gamepadsUpdate()
|
|
|
2033
2060
|
}
|
|
2034
2061
|
}
|
|
2035
2062
|
|
|
2036
|
-
if (!gamepadsEnable || !navigator.getGamepads || !document.hasFocus() && !debug)
|
|
2063
|
+
if (!gamepadsEnable || !navigator || !navigator.getGamepads || !document.hasFocus() && !debug)
|
|
2037
2064
|
return;
|
|
2038
2065
|
|
|
2039
2066
|
// poll gamepads
|
|
@@ -2082,7 +2109,7 @@ function gamepadsUpdate()
|
|
|
2082
2109
|
/** Pulse the vibration hardware if it exists
|
|
2083
2110
|
* @param {Number} [pattern=100] - a single value in miliseconds or vibration interval array
|
|
2084
2111
|
* @memberof Input */
|
|
2085
|
-
const vibrate = (pattern)=> vibrateEnable &&
|
|
2112
|
+
const vibrate = (pattern)=> vibrateEnable && navigator && navigator.vibrate && navigator.vibrate(pattern);
|
|
2086
2113
|
|
|
2087
2114
|
/** Cancel any ongoing vibration
|
|
2088
2115
|
* @memberof Input */
|
|
@@ -2100,7 +2127,7 @@ const isTouchDevice = window.ontouchstart !== undefined;
|
|
|
2100
2127
|
if (isTouchDevice)
|
|
2101
2128
|
{
|
|
2102
2129
|
// handle all touch events the same way
|
|
2103
|
-
let wasTouching,
|
|
2130
|
+
let wasTouching, hadTouch;
|
|
2104
2131
|
ontouchstart = ontouchmove = ontouchend = (e)=>
|
|
2105
2132
|
{
|
|
2106
2133
|
e.button = 0; // all touches are left click
|
|
@@ -2109,7 +2136,8 @@ if (isTouchDevice)
|
|
|
2109
2136
|
const touching = e.touches.length;
|
|
2110
2137
|
if (touching)
|
|
2111
2138
|
{
|
|
2112
|
-
|
|
2139
|
+
// fix mobile audio, force it to play a sound on first touch
|
|
2140
|
+
hadTouch || zzfx(0, hadTouch=1);
|
|
2113
2141
|
|
|
2114
2142
|
// set event pos and pass it along
|
|
2115
2143
|
e.x = e.touches[0].clientX;
|
|
@@ -2122,8 +2150,8 @@ if (isTouchDevice)
|
|
|
2122
2150
|
// set was touching
|
|
2123
2151
|
wasTouching = touching;
|
|
2124
2152
|
|
|
2125
|
-
//
|
|
2126
|
-
return
|
|
2153
|
+
// must return true so the document will get focus
|
|
2154
|
+
return true;
|
|
2127
2155
|
}
|
|
2128
2156
|
}
|
|
2129
2157
|
|
|
@@ -2208,7 +2236,7 @@ function touchGamepadRender()
|
|
|
2208
2236
|
return;
|
|
2209
2237
|
|
|
2210
2238
|
// fade off when not touching or paused
|
|
2211
|
-
const alpha = percent(touchGamepadTimer
|
|
2239
|
+
const alpha = percent(touchGamepadTimer, 4, 3);
|
|
2212
2240
|
if (!alpha || paused)
|
|
2213
2241
|
return;
|
|
2214
2242
|
|
|
@@ -2980,7 +3008,7 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
|
|
|
2980
3008
|
redrawEnd()
|
|
2981
3009
|
{
|
|
2982
3010
|
ASSERT(mainContext == this.context); // must call redrawStart() before drawing tiles
|
|
2983
|
-
glCopyToContext(mainContext, 1);
|
|
3011
|
+
glEnable && glCopyToContext(mainContext, 1);
|
|
2984
3012
|
//debugSaveCanvas(this.canvas);
|
|
2985
3013
|
|
|
2986
3014
|
// set stuff back to normal
|
|
@@ -3054,7 +3082,7 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
|
|
|
3054
3082
|
const cols = tileImage.width/tileSize.x;
|
|
3055
3083
|
context.globalAlpha = color.a; // only alpha, no color, is supported in this mode
|
|
3056
3084
|
context.drawImage(tileImage,
|
|
3057
|
-
(tileIndex%cols)*tileSize.x, (tileIndex/cols|0)*tileSize.
|
|
3085
|
+
(tileIndex%cols)*tileSize.x, (tileIndex/cols|0)*tileSize.y,
|
|
3058
3086
|
tileSize.x, tileSize.y, -.5, -.5, 1, 1);
|
|
3059
3087
|
}
|
|
3060
3088
|
});
|
|
@@ -3124,6 +3152,7 @@ class ParticleEmitter extends EngineObject
|
|
|
3124
3152
|
* @param {Boolean} [additive=0] - Should particles use addtive blend
|
|
3125
3153
|
* @param {Boolean} [randomColorLinear=1] - Should color be randomized linearly or across each component
|
|
3126
3154
|
* @param {Number} [renderOrder=0] - Render order for particles (additive is above other stuff by default)
|
|
3155
|
+
* @param {Boolean} [localSpace=0] - Should it be in local space of emitter (world space is default)
|
|
3127
3156
|
*/
|
|
3128
3157
|
constructor
|
|
3129
3158
|
(
|
|
@@ -3153,7 +3182,8 @@ class ParticleEmitter extends EngineObject
|
|
|
3153
3182
|
collideTiles,
|
|
3154
3183
|
additive,
|
|
3155
3184
|
randomColorLinear = 1,
|
|
3156
|
-
renderOrder = additive ? 1e9 : 0
|
|
3185
|
+
renderOrder = additive ? 1e9 : 0,
|
|
3186
|
+
localSpace
|
|
3157
3187
|
)
|
|
3158
3188
|
{
|
|
3159
3189
|
super(pos, new Vector2, tileIndex, tileSize, angle, undefined, renderOrder);
|
|
@@ -3207,6 +3237,8 @@ class ParticleEmitter extends EngineObject
|
|
|
3207
3237
|
this.collideTiles = collideTiles;
|
|
3208
3238
|
/** @property {Number} - Should particles use addtive blend */
|
|
3209
3239
|
this.additive = additive;
|
|
3240
|
+
/** @property {Boolean} - Should it be in local space of emitter */
|
|
3241
|
+
this.localSpace = localSpace;
|
|
3210
3242
|
/** @property {Number} - If set the partile is drawn as a trail, stretched in the drection of velocity */
|
|
3211
3243
|
this.trailScale = 0;
|
|
3212
3244
|
|
|
@@ -3242,30 +3274,38 @@ class ParticleEmitter extends EngineObject
|
|
|
3242
3274
|
emitParticle()
|
|
3243
3275
|
{
|
|
3244
3276
|
// spawn a particle
|
|
3245
|
-
|
|
3246
|
-
(new Vector2(rand(-.5,.5), rand(-.5,.5)))
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3277
|
+
let pos = this.emitSize.x != undefined ? // check if vec2 was used for size
|
|
3278
|
+
(new Vector2(rand(-.5,.5), rand(-.5,.5)))
|
|
3279
|
+
.multiply(this.emitSize).rotate(this.angle) // box emitter
|
|
3280
|
+
: randInCircle(this.emitSize * .5); // circle emitter
|
|
3281
|
+
let angle = rand(this.particleConeAngle, -this.particleConeAngle);
|
|
3282
|
+
if (!this.localSpace)
|
|
3283
|
+
{
|
|
3284
|
+
pos = this.pos.add(pos);
|
|
3285
|
+
angle += this.angle;
|
|
3286
|
+
}
|
|
3287
|
+
|
|
3288
|
+
const particle = new Particle(pos, this.tileIndex, this.tileSize, angle);
|
|
3250
3289
|
|
|
3251
3290
|
// randomness scales each paremeter by a percentage
|
|
3252
3291
|
const randomness = this.randomness;
|
|
3253
3292
|
const randomizeScale = (v)=> v + v*rand(randomness, -randomness);
|
|
3254
3293
|
|
|
3255
3294
|
// randomize particle settings
|
|
3256
|
-
const particleTime
|
|
3257
|
-
const sizeStart
|
|
3258
|
-
const sizeEnd
|
|
3259
|
-
const speed
|
|
3260
|
-
const angleSpeed
|
|
3261
|
-
const coneAngle
|
|
3262
|
-
const colorStart
|
|
3263
|
-
const colorEnd
|
|
3295
|
+
const particleTime = randomizeScale(this.particleTime);
|
|
3296
|
+
const sizeStart = randomizeScale(this.sizeStart);
|
|
3297
|
+
const sizeEnd = randomizeScale(this.sizeEnd);
|
|
3298
|
+
const speed = randomizeScale(this.speed);
|
|
3299
|
+
const angleSpeed = randomizeScale(this.angleSpeed) * randSign();
|
|
3300
|
+
const coneAngle = rand(this.emitConeAngle, -this.emitConeAngle);
|
|
3301
|
+
const colorStart = randColor(this.colorStartA, this.colorStartB, this.randomColorLinear);
|
|
3302
|
+
const colorEnd = randColor(this.colorEndA, this.colorEndB, this.randomColorLinear);
|
|
3303
|
+
const velocityAngle = this.localSpace ? coneAngle : this.angle + coneAngle;
|
|
3264
3304
|
|
|
3265
3305
|
// build particle settings
|
|
3266
3306
|
particle.colorStart = colorStart;
|
|
3267
3307
|
particle.colorEndDelta = colorEnd.subtract(colorStart);
|
|
3268
|
-
particle.velocity = (new Vector2).setAngle(
|
|
3308
|
+
particle.velocity = (new Vector2).setAngle(velocityAngle, speed);
|
|
3269
3309
|
particle.angleVelocity = angleSpeed;
|
|
3270
3310
|
particle.lifeTime = particleTime;
|
|
3271
3311
|
particle.sizeStart = sizeStart;
|
|
@@ -3281,6 +3321,7 @@ class ParticleEmitter extends EngineObject
|
|
|
3281
3321
|
particle.renderOrder = this.renderOrder;
|
|
3282
3322
|
particle.trailScale = this.trailScale;
|
|
3283
3323
|
particle.mirror = rand()<.5;
|
|
3324
|
+
particle.localSpaceEmitter = this.localSpace && this;
|
|
3284
3325
|
|
|
3285
3326
|
// setup callbacks for particles
|
|
3286
3327
|
particle.destroyCallback = this.particleDestroyCallback;
|
|
@@ -3327,20 +3368,31 @@ class Particle extends EngineObject
|
|
|
3327
3368
|
|
|
3328
3369
|
// draw the particle
|
|
3329
3370
|
this.additive && setBlendMode(1);
|
|
3371
|
+
|
|
3372
|
+
let pos = this.pos, angle = this.angle;
|
|
3373
|
+
if (this.localSpaceEmitter)
|
|
3374
|
+
{
|
|
3375
|
+
// in local space of emitter
|
|
3376
|
+
pos = this.localSpaceEmitter.pos.add(pos.rotate(-this.localSpaceEmitter.angle));
|
|
3377
|
+
angle += this.localSpaceEmitter.angle;
|
|
3378
|
+
}
|
|
3330
3379
|
if (this.trailScale)
|
|
3331
3380
|
{
|
|
3332
3381
|
// trail style particles
|
|
3333
|
-
|
|
3334
|
-
|
|
3382
|
+
let velocity = this.velocity;
|
|
3383
|
+
if (this.localSpaceEmitter)
|
|
3384
|
+
velocity = velocity.rotate(-this.localSpaceEmitter.angle);
|
|
3385
|
+
const speed = velocity.length();
|
|
3386
|
+
const direction = velocity.scale(1/speed);
|
|
3335
3387
|
const trailLength = speed * this.trailScale;
|
|
3336
3388
|
size.y = max(size.x, trailLength);
|
|
3337
|
-
|
|
3338
|
-
drawTile(
|
|
3389
|
+
angle = direction.angle();
|
|
3390
|
+
drawTile(pos.add(direction.multiply(vec2(0,-trailLength/2))), size, this.tileIndex, this.tileSize, color, angle, this.mirror);
|
|
3339
3391
|
}
|
|
3340
3392
|
else
|
|
3341
|
-
drawTile(
|
|
3393
|
+
drawTile(pos, size, this.tileIndex, this.tileSize, color, angle, this.mirror);
|
|
3342
3394
|
this.additive && setBlendMode();
|
|
3343
|
-
debugParticles && debugRect(
|
|
3395
|
+
debugParticles && debugRect(pos, size, '#f005', 0, angle);
|
|
3344
3396
|
|
|
3345
3397
|
if (p == 1)
|
|
3346
3398
|
{
|
|
@@ -3370,11 +3422,6 @@ const medals = [];
|
|
|
3370
3422
|
* @memberof Medals */
|
|
3371
3423
|
let medalsPreventUnlock;
|
|
3372
3424
|
|
|
3373
|
-
/** This can used to enable Newgrounds functionality
|
|
3374
|
-
* @type {Newgrounds}
|
|
3375
|
-
* @memberof Medals */
|
|
3376
|
-
let newgrounds;
|
|
3377
|
-
|
|
3378
3425
|
// Engine internal variables not exposed to documentation
|
|
3379
3426
|
let medalsDisplayQueue = [], medalsSaveName, medalsDisplayTimeLast;
|
|
3380
3427
|
|
|
@@ -3389,7 +3436,7 @@ function medalsInit(saveName)
|
|
|
3389
3436
|
{
|
|
3390
3437
|
// check if medals are unlocked
|
|
3391
3438
|
medalsSaveName = saveName;
|
|
3392
|
-
debugMedals || medals.forEach(medal=> localStorage[medal.storageKey()]);
|
|
3439
|
+
debugMedals || medals.forEach(medal=> medal.unlocked = (localStorage[medal.storageKey()] | 0));
|
|
3393
3440
|
}
|
|
3394
3441
|
|
|
3395
3442
|
/**
|
|
@@ -3522,6 +3569,15 @@ function medalsRender()
|
|
|
3522
3569
|
|
|
3523
3570
|
///////////////////////////////////////////////////////////////////////////////
|
|
3524
3571
|
|
|
3572
|
+
// global Newgrounds object
|
|
3573
|
+
let newgrounds;
|
|
3574
|
+
|
|
3575
|
+
/** This can used to enable Newgrounds functionality
|
|
3576
|
+
* @param {Number} app_id - The newgrounds App ID
|
|
3577
|
+
* @param {String} [cipher] - The encryption Key (AES-128/Base64)
|
|
3578
|
+
* @memberof Medals */
|
|
3579
|
+
function newgroundsInit(app_id, cipher) { newgrounds = new Newgrounds(app_id, cipher); }
|
|
3580
|
+
|
|
3525
3581
|
/**
|
|
3526
3582
|
* Newgrounds API wrapper object
|
|
3527
3583
|
* @example
|
|
@@ -3682,15 +3738,13 @@ let glContext;
|
|
|
3682
3738
|
let glTileTexture;
|
|
3683
3739
|
|
|
3684
3740
|
// WebGL internal variables not exposed to documentation
|
|
3685
|
-
let glActiveTexture, glShader, glPositionData, glColorData, glBatchCount, glBatchAdditive, glAdditive;
|
|
3741
|
+
let glActiveTexture, glShader, glArrayBuffer, glVertexData, glPositionData, glColorData, glBatchCount, glBatchAdditive, glAdditive;
|
|
3686
3742
|
|
|
3687
3743
|
///////////////////////////////////////////////////////////////////////////////
|
|
3688
3744
|
|
|
3689
3745
|
// Init WebGL, called automatically by the engine
|
|
3690
3746
|
function glInit()
|
|
3691
3747
|
{
|
|
3692
|
-
if (!glEnable) return;
|
|
3693
|
-
|
|
3694
3748
|
// create the canvas and tile texture
|
|
3695
3749
|
glCanvas = document.createElement('canvas');
|
|
3696
3750
|
glContext = glCanvas.getContext('webgl', {antialias: false});
|
|
@@ -3723,24 +3777,11 @@ function glInit()
|
|
|
3723
3777
|
);
|
|
3724
3778
|
|
|
3725
3779
|
// init buffers
|
|
3726
|
-
|
|
3727
|
-
|
|
3780
|
+
glVertexData = new ArrayBuffer(gl_MAX_BATCH * gl_VERTICES_PER_QUAD * gl_VERTEX_BYTE_STRIDE);
|
|
3781
|
+
glArrayBuffer = glContext.createBuffer();
|
|
3728
3782
|
glPositionData = new Float32Array(glVertexData);
|
|
3729
3783
|
glColorData = new Uint32Array(glVertexData);
|
|
3730
|
-
|
|
3731
|
-
// setup the vertex data array
|
|
3732
|
-
let offset = glBatchCount = 0;
|
|
3733
|
-
const initVertexAttribArray = (name, type, typeSize, size, normalize=0)=>
|
|
3734
|
-
{
|
|
3735
|
-
const location = glContext.getAttribLocation(glShader, name);
|
|
3736
|
-
glContext.enableVertexAttribArray(location);
|
|
3737
|
-
glContext.vertexAttribPointer(location, size, type, normalize, gl_VERTEX_BYTE_STRIDE, offset);
|
|
3738
|
-
offset += size*typeSize;
|
|
3739
|
-
}
|
|
3740
|
-
initVertexAttribArray('p', gl_FLOAT, 4, 2); // position
|
|
3741
|
-
initVertexAttribArray('t', gl_FLOAT, 4, 2); // texture coords
|
|
3742
|
-
initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4, 1); // color
|
|
3743
|
-
initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4, 1); // additiveColor
|
|
3784
|
+
glBatchCount = 0;
|
|
3744
3785
|
}
|
|
3745
3786
|
|
|
3746
3787
|
/** Set the WebGl blend mode, normally you should call setBlendMode instead
|
|
@@ -3748,8 +3789,6 @@ function glInit()
|
|
|
3748
3789
|
* @memberof WebGL */
|
|
3749
3790
|
function glSetBlendMode(additive)
|
|
3750
3791
|
{
|
|
3751
|
-
if (!glEnable) return;
|
|
3752
|
-
|
|
3753
3792
|
// setup blending
|
|
3754
3793
|
glAdditive = additive;
|
|
3755
3794
|
}
|
|
@@ -3760,8 +3799,6 @@ function glSetBlendMode(additive)
|
|
|
3760
3799
|
* @memberof WebGL */
|
|
3761
3800
|
function glSetTexture(texture=glTileTexture)
|
|
3762
3801
|
{
|
|
3763
|
-
if (!glEnable) return;
|
|
3764
|
-
|
|
3765
3802
|
// must flush cache with the old texture to set a new one
|
|
3766
3803
|
if (texture != glActiveTexture)
|
|
3767
3804
|
glFlush();
|
|
@@ -3776,8 +3813,6 @@ function glSetTexture(texture=glTileTexture)
|
|
|
3776
3813
|
* @memberof WebGL */
|
|
3777
3814
|
function glCompileShader(source, type)
|
|
3778
3815
|
{
|
|
3779
|
-
if (!glEnable) return;
|
|
3780
|
-
|
|
3781
3816
|
// build the shader
|
|
3782
3817
|
const shader = glContext.createShader(type);
|
|
3783
3818
|
glContext.shaderSource(shader, source);
|
|
@@ -3796,8 +3831,6 @@ function glCompileShader(source, type)
|
|
|
3796
3831
|
* @memberof WebGL */
|
|
3797
3832
|
function glCreateProgram(vsSource, fsSource)
|
|
3798
3833
|
{
|
|
3799
|
-
if (!glEnable) return;
|
|
3800
|
-
|
|
3801
3834
|
// build the program
|
|
3802
3835
|
const program = glContext.createProgram();
|
|
3803
3836
|
glContext.attachShader(program, glCompileShader(vsSource, gl_VERTEX_SHADER));
|
|
@@ -3810,35 +3843,16 @@ function glCreateProgram(vsSource, fsSource)
|
|
|
3810
3843
|
return program;
|
|
3811
3844
|
}
|
|
3812
3845
|
|
|
3813
|
-
/** Create WebGL buffer
|
|
3814
|
-
* @param bufferType
|
|
3815
|
-
* @param size
|
|
3816
|
-
* @param usage
|
|
3817
|
-
* @return {WebGLBuffer}
|
|
3818
|
-
* @memberof WebGL */
|
|
3819
|
-
function glCreateBuffer(bufferType, size, usage)
|
|
3820
|
-
{
|
|
3821
|
-
if (!glEnable) return;
|
|
3822
|
-
|
|
3823
|
-
// build the buffer
|
|
3824
|
-
const buffer = glContext.createBuffer();
|
|
3825
|
-
glContext.bindBuffer(bufferType, buffer);
|
|
3826
|
-
glContext.bufferData(bufferType, size, usage);
|
|
3827
|
-
return buffer;
|
|
3828
|
-
}
|
|
3829
|
-
|
|
3830
3846
|
/** Create WebGL texture from an image and set the texture settings
|
|
3831
3847
|
* @param {Image} image
|
|
3832
3848
|
* @return {WebGLTexture}
|
|
3833
3849
|
* @memberof WebGL */
|
|
3834
3850
|
function glCreateTexture(image)
|
|
3835
3851
|
{
|
|
3836
|
-
if (!glEnable || !image || !image.width) return;
|
|
3837
|
-
|
|
3838
3852
|
// build the texture
|
|
3839
3853
|
const texture = glContext.createTexture();
|
|
3840
3854
|
glContext.bindTexture(gl_TEXTURE_2D, texture);
|
|
3841
|
-
glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, image);
|
|
3855
|
+
image && image.width && glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, image);
|
|
3842
3856
|
|
|
3843
3857
|
// use point filtering for pixelated rendering
|
|
3844
3858
|
glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, cavasPixelated ? gl_NEAREST : gl_LINEAR);
|
|
@@ -3851,16 +3865,31 @@ function glCreateTexture(image)
|
|
|
3851
3865
|
// called automatically by engine before render
|
|
3852
3866
|
function glPreRender(width, height, cameraX, cameraY, cameraScale)
|
|
3853
3867
|
{
|
|
3854
|
-
if (!glEnable) return;
|
|
3855
|
-
|
|
3856
3868
|
// clear and set to same size as main canvas
|
|
3857
3869
|
glContext.viewport(0, 0, glCanvas.width = width, glCanvas.height = height);
|
|
3858
3870
|
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
3859
3871
|
|
|
3860
3872
|
// set up the shader
|
|
3861
|
-
glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = glTileTexture);
|
|
3862
3873
|
glContext.useProgram(glShader);
|
|
3874
|
+
glContext.activeTexture(gl_TEXTURE0);
|
|
3875
|
+
glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = glTileTexture);
|
|
3876
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glArrayBuffer);
|
|
3877
|
+
glContext.bufferData(gl_ARRAY_BUFFER, glVertexData.byteLength, gl_DYNAMIC_DRAW);
|
|
3863
3878
|
glSetBlendMode();
|
|
3879
|
+
|
|
3880
|
+
// set vertex attributes
|
|
3881
|
+
let offset = 0;
|
|
3882
|
+
const initVertexAttribArray = (name, type, typeSize, size, normalize=0)=>
|
|
3883
|
+
{
|
|
3884
|
+
const location = glContext.getAttribLocation(glShader, name);
|
|
3885
|
+
glContext.enableVertexAttribArray(location);
|
|
3886
|
+
glContext.vertexAttribPointer(location, size, type, normalize, gl_VERTEX_BYTE_STRIDE, offset);
|
|
3887
|
+
offset += size*typeSize;
|
|
3888
|
+
}
|
|
3889
|
+
initVertexAttribArray('p', gl_FLOAT, 4, 2); // position
|
|
3890
|
+
initVertexAttribArray('t', gl_FLOAT, 4, 2); // texture coords
|
|
3891
|
+
initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4, 1); // color
|
|
3892
|
+
initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4, 1); // additiveColor
|
|
3864
3893
|
|
|
3865
3894
|
// build the transform matrix
|
|
3866
3895
|
const sx = 2 * cameraScale / width;
|
|
@@ -3879,7 +3908,7 @@ function glPreRender(width, height, cameraX, cameraY, cameraScale)
|
|
|
3879
3908
|
* @memberof WebGL */
|
|
3880
3909
|
function glFlush()
|
|
3881
3910
|
{
|
|
3882
|
-
if (!
|
|
3911
|
+
if (!glBatchCount) return;
|
|
3883
3912
|
|
|
3884
3913
|
const destBlend = glBatchAdditive ? gl_ONE : gl_ONE_MINUS_SRC_ALPHA;
|
|
3885
3914
|
glContext.blendFuncSeparate(gl_SRC_ALPHA, destBlend, gl_ONE, destBlend);
|
|
@@ -3899,7 +3928,7 @@ function glFlush()
|
|
|
3899
3928
|
* @memberof WebGL */
|
|
3900
3929
|
function glCopyToContext(context, forceDraw)
|
|
3901
3930
|
{
|
|
3902
|
-
if (
|
|
3931
|
+
if (!glBatchCount && !forceDraw) return;
|
|
3903
3932
|
|
|
3904
3933
|
glFlush();
|
|
3905
3934
|
|
|
@@ -3923,8 +3952,6 @@ function glCopyToContext(context, forceDraw)
|
|
|
3923
3952
|
* @memberof WebGL */
|
|
3924
3953
|
function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba=0xffffffff, rgbaAdditive=0)
|
|
3925
3954
|
{
|
|
3926
|
-
if (!glEnable) return;
|
|
3927
|
-
|
|
3928
3955
|
// flush if there is no room for more verts or if different blend mode
|
|
3929
3956
|
if (glBatchCount == gl_MAX_BATCH || glBatchAdditive != glAdditive)
|
|
3930
3957
|
glFlush();
|
|
@@ -3973,6 +4000,90 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba=0xffffff
|
|
|
3973
4000
|
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3974
4001
|
}
|
|
3975
4002
|
|
|
4003
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
4004
|
+
// post processing - can be enabled to pass other canvases through a final shader
|
|
4005
|
+
|
|
4006
|
+
let glPostShader, glPostArrayBuffer, glPostTexture;
|
|
4007
|
+
|
|
4008
|
+
/** Set up a post processing shader
|
|
4009
|
+
* @param {String} shaderCode
|
|
4010
|
+
* @memberof WebGL */
|
|
4011
|
+
function glInitPostProcess(shaderCode)
|
|
4012
|
+
{
|
|
4013
|
+
ASSERT(!glPostShader); // can only have 1 post effects shader
|
|
4014
|
+
|
|
4015
|
+
if (!shaderCode) // default shader
|
|
4016
|
+
shaderCode = 'void mainImage(out vec4 c,vec2 p){c=texture2D(iChannel0,p/iResolution.xy);}';
|
|
4017
|
+
|
|
4018
|
+
// create the shader
|
|
4019
|
+
glPostShader = glCreateProgram(
|
|
4020
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
4021
|
+
'attribute vec2 p;'+ // position
|
|
4022
|
+
'void main(){'+ // shader entry point
|
|
4023
|
+
'gl_Position=vec4(p,1,1);'+ // set position
|
|
4024
|
+
'}' // end of shader
|
|
4025
|
+
,
|
|
4026
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
4027
|
+
'uniform sampler2D iChannel0;'+ // input texture
|
|
4028
|
+
'uniform vec3 iResolution;'+ // size of output texture
|
|
4029
|
+
'uniform float iTime;'+ // time passed
|
|
4030
|
+
'\n' + shaderCode + '\n'+ // insert custom shader code
|
|
4031
|
+
'void main(){'+ // shader entry point
|
|
4032
|
+
'mainImage(gl_FragColor,gl_FragCoord.xy);'+ // call post process function
|
|
4033
|
+
'gl_FragColor.a=1.;'+ // always use full alpha
|
|
4034
|
+
'}' // end of shader
|
|
4035
|
+
);
|
|
4036
|
+
|
|
4037
|
+
// create buffer and texture
|
|
4038
|
+
glPostArrayBuffer = glContext.createBuffer();
|
|
4039
|
+
glPostTexture = glCreateTexture();
|
|
4040
|
+
|
|
4041
|
+
// hide the original 2d canvas
|
|
4042
|
+
mainCanvas.style.visibility = 'hidden';
|
|
4043
|
+
}
|
|
4044
|
+
|
|
4045
|
+
// Render the post processing shader, called automatically by the engine
|
|
4046
|
+
function glRenderPostProcess()
|
|
4047
|
+
{
|
|
4048
|
+
if (!glPostShader)
|
|
4049
|
+
return;
|
|
4050
|
+
|
|
4051
|
+
// prepare to render post process shader
|
|
4052
|
+
const width = mainCanvas.width, height = mainCanvas.height;
|
|
4053
|
+
if (glEnable)
|
|
4054
|
+
{
|
|
4055
|
+
glFlush(); // clear out the buffer
|
|
4056
|
+
mainContext.drawImage(glCanvas, 0, 0); // copy to the main canvas
|
|
4057
|
+
}
|
|
4058
|
+
else
|
|
4059
|
+
glContext.viewport(0, 0, glCanvas.width = width, glCanvas.height = height); // set viewport
|
|
4060
|
+
|
|
4061
|
+
// setup shader program to draw one triangle
|
|
4062
|
+
glContext.useProgram(glPostShader);
|
|
4063
|
+
glContext.disable(gl_BLEND);
|
|
4064
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glPostArrayBuffer);
|
|
4065
|
+
glContext.bufferData(gl_ARRAY_BUFFER, new Float32Array([-3,1,1,-3,1,1]), gl_STATIC_DRAW);
|
|
4066
|
+
glContext.pixelStorei(gl_UNPACK_FLIP_Y_WEBGL, true);
|
|
4067
|
+
|
|
4068
|
+
// set textures, pass in the 2d canvas and gl canvas in separate texture channels
|
|
4069
|
+
glContext.activeTexture(gl_TEXTURE0);
|
|
4070
|
+
glContext.bindTexture(gl_TEXTURE_2D, glPostTexture);
|
|
4071
|
+
glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, mainCanvas);
|
|
4072
|
+
|
|
4073
|
+
// set vertex position attribute
|
|
4074
|
+
const vertexByteStride = 8;
|
|
4075
|
+
const pLocation = glContext.getAttribLocation(glPostShader, 'p');
|
|
4076
|
+
glContext.enableVertexAttribArray(pLocation);
|
|
4077
|
+
glContext.vertexAttribPointer(pLocation, 2, gl_FLOAT, 0, vertexByteStride, 0);
|
|
4078
|
+
|
|
4079
|
+
// set uniforms and draw
|
|
4080
|
+
const uniformLocation = (name)=>glContext.getUniformLocation(glPostShader, name);
|
|
4081
|
+
glContext.uniform1i(uniformLocation('iChannel0'), 0);
|
|
4082
|
+
glContext.uniform1f(uniformLocation('iTime'), time);
|
|
4083
|
+
glContext.uniform3f(uniformLocation('iResolution'), width, height, 1);
|
|
4084
|
+
glContext.drawArrays(gl_TRIANGLES, 0, 3);
|
|
4085
|
+
}
|
|
4086
|
+
|
|
3976
4087
|
///////////////////////////////////////////////////////////////////////////////
|
|
3977
4088
|
// store gl constants as integers so their name doesn't use space in minifed
|
|
3978
4089
|
const
|
|
@@ -3983,6 +4094,7 @@ gl_ONE_MINUS_SRC_ALPHA = 771,
|
|
|
3983
4094
|
gl_BLEND = 3042,
|
|
3984
4095
|
gl_TEXTURE_2D = 3553,
|
|
3985
4096
|
gl_UNSIGNED_BYTE = 5121,
|
|
4097
|
+
gl_BYTE = 5120,
|
|
3986
4098
|
gl_FLOAT = 5126,
|
|
3987
4099
|
gl_RGBA = 6408,
|
|
3988
4100
|
gl_NEAREST = 9728,
|
|
@@ -3993,12 +4105,16 @@ gl_TEXTURE_WRAP_S = 10242,
|
|
|
3993
4105
|
gl_TEXTURE_WRAP_T = 10243,
|
|
3994
4106
|
gl_COLOR_BUFFER_BIT = 16384,
|
|
3995
4107
|
gl_CLAMP_TO_EDGE = 33071,
|
|
4108
|
+
gl_TEXTURE0 = 33984,
|
|
4109
|
+
gl_TEXTURE1 = 33985,
|
|
3996
4110
|
gl_ARRAY_BUFFER = 34962,
|
|
4111
|
+
gl_STATIC_DRAW = 35044,
|
|
3997
4112
|
gl_DYNAMIC_DRAW = 35048,
|
|
3998
4113
|
gl_FRAGMENT_SHADER = 35632,
|
|
3999
4114
|
gl_VERTEX_SHADER = 35633,
|
|
4000
4115
|
gl_COMPILE_STATUS = 35713,
|
|
4001
4116
|
gl_LINK_STATUS = 35714,
|
|
4117
|
+
gl_UNPACK_FLIP_Y_WEBGL = 37440,
|
|
4002
4118
|
|
|
4003
4119
|
// constants for batch rendering
|
|
4004
4120
|
gl_VERTICES_PER_QUAD = 6,
|