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
package/engine/engine.all.js
CHANGED
|
@@ -233,55 +233,49 @@ const debugRender = ()=>
|
|
|
233
233
|
|
|
234
234
|
if (debugOverlay)
|
|
235
235
|
{
|
|
236
|
+
const saveContext = mainContext;
|
|
237
|
+
mainContext = overlayContext;
|
|
238
|
+
|
|
236
239
|
// mouse pick
|
|
237
240
|
let bestDistance = Infinity, bestObject;
|
|
238
241
|
for (const o of engineObjects)
|
|
239
242
|
{
|
|
240
243
|
if (o.canvas || o.destroyed)
|
|
241
|
-
continue;
|
|
242
|
-
|
|
243
|
-
const size = o.size.copy();
|
|
244
|
-
if (!size.x || !size.y)
|
|
244
|
+
continue;
|
|
245
|
+
if (!o.size.x || !o.size.y)
|
|
245
246
|
continue;
|
|
246
247
|
|
|
247
248
|
const distance = mousePos.distanceSquared(o.pos);
|
|
248
249
|
if (distance < bestDistance)
|
|
249
250
|
{
|
|
250
251
|
bestDistance = distance;
|
|
251
|
-
bestObject = o
|
|
252
|
+
bestObject = o;
|
|
252
253
|
}
|
|
253
254
|
|
|
254
255
|
// show object info
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
size.y = max(size.y, .2);
|
|
262
|
-
drawRect(o.pos, size, color);
|
|
263
|
-
drawRect(o.pos, size.scale(.8), o.parent ? new Color(1,1,1,.5) : new Color(0,0,0,.8));
|
|
264
|
-
o.parent && drawLine(o.pos, o.parent.pos, .1, new Color(0,0,1,.5));
|
|
256
|
+
const size = vec2(max(o.size.x, .2), max(o.size.y, .2));
|
|
257
|
+
const color1 = new Color(!!o.collideTiles, !!o.collideSolidObjects, !!o.isSolid, o.parent?.2:.5);
|
|
258
|
+
const color2 = o.parent ? new Color(1,1,1,.5) : new Color(0,0,0,.8);
|
|
259
|
+
drawRect(o.pos, size, color1, o.angle, 0);
|
|
260
|
+
drawRect(o.pos, size.scale(.8), color2, o.angle, 0);
|
|
261
|
+
o.parent && drawLine(o.pos, o.parent.pos, .1, new Color(0,0,1,.5), 0);
|
|
265
262
|
}
|
|
266
263
|
|
|
267
264
|
if (bestObject)
|
|
268
265
|
{
|
|
269
|
-
const saveContext = mainContext;
|
|
270
|
-
mainContext = overlayContext
|
|
271
266
|
const raycastHitPos = tileCollisionRaycast(bestObject.pos, mousePos);
|
|
272
267
|
raycastHitPos && drawRect(raycastHitPos.floor().add(vec2(.5)), vec2(1), new Color(0,1,1,.3));
|
|
273
|
-
drawRect(mousePos.floor().add(vec2(.5)), vec2(1), new Color(0,0,1,.5));
|
|
274
|
-
drawLine(mousePos, bestObject.pos, .1,
|
|
268
|
+
drawRect(mousePos.floor().add(vec2(.5)), vec2(1), new Color(0,0,1,.5), 0, 0);
|
|
269
|
+
drawLine(mousePos, bestObject.pos, .1, raycastHitPos ? new Color(1,0,0,.5) : new Color(0,1,0,.5), 0);
|
|
275
270
|
|
|
276
271
|
const debugText = 'mouse pos = ' + mousePos +
|
|
277
272
|
'\nmouse collision = ' + getTileCollisionData(mousePos) +
|
|
278
273
|
'\n\n--- object info ---\n' +
|
|
279
274
|
bestObject.toString();
|
|
280
|
-
drawTextScreen(debugText, mousePosScreen, 24, new Color, .05,
|
|
281
|
-
mainContext = saveContext;
|
|
275
|
+
drawTextScreen(debugText, mousePosScreen, 24, new Color, .05, 0, 0, 'monospace');
|
|
282
276
|
}
|
|
283
277
|
|
|
284
|
-
glCopyToContext(mainContext);
|
|
278
|
+
glCopyToContext(mainContext = saveContext);
|
|
285
279
|
}
|
|
286
280
|
|
|
287
281
|
{
|
|
@@ -294,7 +288,6 @@ const debugRender = ()=>
|
|
|
294
288
|
|
|
295
289
|
// create canvas transform from world space to screen space
|
|
296
290
|
const pos = worldToScreen(p.pos);
|
|
297
|
-
|
|
298
291
|
overlayContext.translate(pos.x|0, pos.y|0);
|
|
299
292
|
overlayContext.rotate(p.angle);
|
|
300
293
|
overlayContext.fillStyle = overlayContext.strokeStyle = p.color;
|
|
@@ -332,7 +325,7 @@ const debugRender = ()=>
|
|
|
332
325
|
});
|
|
333
326
|
|
|
334
327
|
// remove expired pritives
|
|
335
|
-
debugPrimitives = debugPrimitives.filter(r=>r.time
|
|
328
|
+
debugPrimitives = debugPrimitives.filter(r=>r.time<0);
|
|
336
329
|
}
|
|
337
330
|
|
|
338
331
|
{
|
|
@@ -717,6 +710,28 @@ class Vector2
|
|
|
717
710
|
|
|
718
711
|
///////////////////////////////////////////////////////////////////////////////
|
|
719
712
|
|
|
713
|
+
/**
|
|
714
|
+
* Create a color object with RGBA values
|
|
715
|
+
* @param {Number} [r=1]
|
|
716
|
+
* @param {Number} [g=1]
|
|
717
|
+
* @param {Number} [b=1]
|
|
718
|
+
* @param {Number} [a=1]
|
|
719
|
+
* @return {Color}
|
|
720
|
+
* @memberof Utilities
|
|
721
|
+
*/
|
|
722
|
+
const colorRGBA = (r, g, b, a)=> new Color(r, g, b, a);
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* Create a color object with HSLA values
|
|
726
|
+
* @param {Number} [h=0]
|
|
727
|
+
* @param {Number} [s=0]
|
|
728
|
+
* @param {Number} [l=1]
|
|
729
|
+
* @param {Number} [a=1]
|
|
730
|
+
* @return {Color}
|
|
731
|
+
* @memberof Utilities
|
|
732
|
+
*/
|
|
733
|
+
const colorHSLA = (h, s, l, a)=> new Color().setHSLA(h, s, l, a);
|
|
734
|
+
|
|
720
735
|
/**
|
|
721
736
|
* Color object (red, green, blue, alpha) with some helpful functions
|
|
722
737
|
* @example
|
|
@@ -808,10 +823,10 @@ class Color
|
|
|
808
823
|
* @return {Array} */
|
|
809
824
|
getHSLA()
|
|
810
825
|
{
|
|
811
|
-
const r = this.r;
|
|
812
|
-
const g = this.g;
|
|
813
|
-
const b = this.b;
|
|
814
|
-
const a = this.a;
|
|
826
|
+
const r = clamp(this.r);
|
|
827
|
+
const g = clamp(this.g);
|
|
828
|
+
const b = clamp(this.b);
|
|
829
|
+
const a = clamp(this.a);
|
|
815
830
|
const max = Math.max(r, g, b);
|
|
816
831
|
const min = Math.min(r, g, b);
|
|
817
832
|
const l = (max + min) / 2;
|
|
@@ -847,20 +862,13 @@ class Color
|
|
|
847
862
|
).clamp();
|
|
848
863
|
}
|
|
849
864
|
|
|
850
|
-
/** Returns this color expressed as
|
|
865
|
+
/** Returns this color expressed as a hex color code
|
|
866
|
+
* @param {Boolean} [useAlpha=1] - if alpha should be included in result
|
|
851
867
|
* @return {String} */
|
|
852
|
-
toString()
|
|
868
|
+
toString(useAlpha = 1)
|
|
853
869
|
{
|
|
854
|
-
|
|
855
|
-
return
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
/** Returns this color expressed as 32 bit integer RGBA value
|
|
859
|
-
* @return {Number} */
|
|
860
|
-
rgbaInt()
|
|
861
|
-
{
|
|
862
|
-
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);
|
|
863
|
-
return (this.r*255|0) + (this.g*255<<8) + (this.b*255<<16) + (this.a*255<<24);
|
|
870
|
+
const toHex = (c)=> ((c=c*255|0)<16 ? '0' : '') + c.toString(16);
|
|
871
|
+
return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b) + (useAlpha ? toHex(this.a) : '');
|
|
864
872
|
}
|
|
865
873
|
|
|
866
874
|
/** Set this color from a hex code
|
|
@@ -868,21 +876,24 @@ class Color
|
|
|
868
876
|
* @return {Color} */
|
|
869
877
|
setHex(hex)
|
|
870
878
|
{
|
|
871
|
-
const fromHex = (
|
|
879
|
+
const fromHex = (c)=> clamp(parseInt(hex.slice(c,c+2),16)/255);
|
|
872
880
|
this.r = fromHex(1);
|
|
873
881
|
this.g = fromHex(3),
|
|
874
882
|
this.b = fromHex(5);
|
|
875
|
-
this.a = 1;
|
|
876
|
-
ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1);
|
|
883
|
+
this.a = hex.length > 7 ? fromHex(7) : 1;
|
|
877
884
|
return this;
|
|
878
885
|
}
|
|
879
|
-
|
|
880
|
-
/** Returns this color expressed as
|
|
881
|
-
* @return {
|
|
882
|
-
|
|
886
|
+
|
|
887
|
+
/** Returns this color expressed as 32 bit RGBA value
|
|
888
|
+
* @return {Number} */
|
|
889
|
+
rgbaInt()
|
|
883
890
|
{
|
|
884
|
-
const
|
|
885
|
-
|
|
891
|
+
const toByte = (c)=> clamp(c)*255|0;
|
|
892
|
+
const r = toByte(this.r);
|
|
893
|
+
const g = toByte(this.g)<<8;
|
|
894
|
+
const b = toByte(this.b)<<16;
|
|
895
|
+
const a = toByte(this.a)<<24;
|
|
896
|
+
return r + g + b + a;
|
|
886
897
|
}
|
|
887
898
|
}
|
|
888
899
|
|
|
@@ -933,6 +944,10 @@ class Timer
|
|
|
933
944
|
/** Returns this timer expressed as a string
|
|
934
945
|
* @return {String} */
|
|
935
946
|
toString() { if (debug) { return this.unset() ? 'unset' : Math.abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ); } }
|
|
947
|
+
|
|
948
|
+
/** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
|
|
949
|
+
* @return {Number} */
|
|
950
|
+
valueOf() { return this.get(); }
|
|
936
951
|
}
|
|
937
952
|
/**
|
|
938
953
|
* LittleJS Engine Settings
|
|
@@ -1455,7 +1470,7 @@ class EngineObject
|
|
|
1455
1470
|
* @return {Boolean} - true if the raycast should hit */
|
|
1456
1471
|
collideWithTileRaycast(tileData, pos) { return tileData > 0; }
|
|
1457
1472
|
|
|
1458
|
-
/** Called to check if a
|
|
1473
|
+
/** Called to check if a object collision should be resolved
|
|
1459
1474
|
* @param {EngineObject} object - the object to test against
|
|
1460
1475
|
* @return {Boolean} - true if the collision should be resolved
|
|
1461
1476
|
*/
|
|
@@ -1500,10 +1515,10 @@ class EngineObject
|
|
|
1500
1515
|
}
|
|
1501
1516
|
|
|
1502
1517
|
/** Set how this object collides
|
|
1503
|
-
* @param {boolean} [collideSolidObjects=
|
|
1504
|
-
* @param {boolean} [isSolid=
|
|
1518
|
+
* @param {boolean} [collideSolidObjects=1] - Does it collide with solid objects
|
|
1519
|
+
* @param {boolean} [isSolid=1] - Does it collide with and block other objects (expensive in large numbers)
|
|
1505
1520
|
* @param {boolean} [collideTiles=1] - Does it collide with the tile collision */
|
|
1506
|
-
setCollision(collideSolidObjects=
|
|
1521
|
+
setCollision(collideSolidObjects=1, isSolid=1, collideTiles=1)
|
|
1507
1522
|
{
|
|
1508
1523
|
ASSERT(collideSolidObjects || !isSolid); // solid objects must be set to collide
|
|
1509
1524
|
|
|
@@ -1720,7 +1735,7 @@ function drawLine(posA, posB, thickness=.1, color, useWebGL)
|
|
|
1720
1735
|
{
|
|
1721
1736
|
const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2);
|
|
1722
1737
|
const size = vec2(thickness, halfDelta.length()*2);
|
|
1723
|
-
drawRect(posA.add(halfDelta), size, color, halfDelta.angle(),
|
|
1738
|
+
drawRect(posA.add(halfDelta), size, color, halfDelta.angle(), useWebGL);
|
|
1724
1739
|
}
|
|
1725
1740
|
|
|
1726
1741
|
/** Draw directly to a 2d canvas context in world space
|
|
@@ -1737,7 +1752,7 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, context = mainCont
|
|
|
1737
1752
|
pos = worldToScreen(pos);
|
|
1738
1753
|
size = size.scale(cameraScale);
|
|
1739
1754
|
context.save();
|
|
1740
|
-
context.translate(pos.x+.5|0, pos.y
|
|
1755
|
+
context.translate(pos.x+.5|0, pos.y+.5|0);
|
|
1741
1756
|
context.rotate(angle);
|
|
1742
1757
|
context.scale(mirror ? -size.x : size.x, size.y);
|
|
1743
1758
|
drawFunction(context);
|
|
@@ -1766,21 +1781,21 @@ function setBlendMode(additive, useWebGL=glEnable)
|
|
|
1766
1781
|
* @param {Color} [lineColor=new Color(0,0,0)]
|
|
1767
1782
|
* @param {String} [textAlign='center']
|
|
1768
1783
|
* @memberof Draw */
|
|
1769
|
-
function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault)
|
|
1784
|
+
function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault, context=overlayContext)
|
|
1770
1785
|
{
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1786
|
+
context.fillStyle = color;
|
|
1787
|
+
context.lineWidth = lineWidth;
|
|
1788
|
+
context.strokeStyle = lineColor;
|
|
1789
|
+
context.textAlign = textAlign;
|
|
1790
|
+
context.font = size + 'px '+ font;
|
|
1791
|
+
context.textBaseline = 'middle';
|
|
1792
|
+
context.lineJoin = 'round';
|
|
1778
1793
|
|
|
1779
1794
|
pos = pos.copy();
|
|
1780
1795
|
(text+'').split('\n').forEach(line=>
|
|
1781
1796
|
{
|
|
1782
|
-
lineWidth &&
|
|
1783
|
-
|
|
1797
|
+
lineWidth && context.strokeText(line, pos.x, pos.y);
|
|
1798
|
+
context.fillText(line, pos.x, pos.y);
|
|
1784
1799
|
pos.y += size;
|
|
1785
1800
|
});
|
|
1786
1801
|
}
|
|
@@ -1797,7 +1812,7 @@ function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineCol
|
|
|
1797
1812
|
* @memberof Draw */
|
|
1798
1813
|
function drawText(text, pos, size=1, color, lineWidth, lineColor, textAlign, font)
|
|
1799
1814
|
{
|
|
1800
|
-
drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font);
|
|
1815
|
+
drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, mainContext);
|
|
1801
1816
|
}
|
|
1802
1817
|
|
|
1803
1818
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -1820,7 +1835,7 @@ let engineFontImage;
|
|
|
1820
1835
|
class FontImage
|
|
1821
1836
|
{
|
|
1822
1837
|
/** Create an image font
|
|
1823
|
-
* @param {
|
|
1838
|
+
* @param {HTMLImageElement} [image] - The image the font is stored in, if undefined the default font is used
|
|
1824
1839
|
* @param {Vector2} [tileSize=vec2(8)] - The size of the font source tiles
|
|
1825
1840
|
* @param {Vector2} [paddingSize=vec2(0,1)] - How much extra space to add between characters
|
|
1826
1841
|
* @param {Number} [startTileIndex=0] - Tile index in image where font starts
|
|
@@ -1857,7 +1872,7 @@ class FontImage
|
|
|
1857
1872
|
const size = this.tileSize;
|
|
1858
1873
|
const drawSize = size.add(this.paddingSize).scale(scale);
|
|
1859
1874
|
const cols = this.image.width / this.tileSize.x |0;
|
|
1860
|
-
text.split('\n').forEach((line, i)=>
|
|
1875
|
+
(text+'').split('\n').forEach((line, i)=>
|
|
1861
1876
|
{
|
|
1862
1877
|
const centerOffset = center ? line.length * size.x * scale / 2 |0 : 0;
|
|
1863
1878
|
for(let j=line.length; j--;)
|
|
@@ -1919,6 +1934,7 @@ function toggleFullscreen()
|
|
|
1919
1934
|
document.body.mozRequestFullScreen();
|
|
1920
1935
|
}
|
|
1921
1936
|
}
|
|
1937
|
+
|
|
1922
1938
|
/**
|
|
1923
1939
|
* LittleJS Input System
|
|
1924
1940
|
* <br> - Tracks key down, pressed, and released
|
|
@@ -2033,7 +2049,7 @@ let inputData = [[]];
|
|
|
2033
2049
|
function inputUpdate()
|
|
2034
2050
|
{
|
|
2035
2051
|
// clear input when lost focus (prevent stuck keys)
|
|
2036
|
-
document.hasFocus() || clearInput();
|
|
2052
|
+
isTouchDevice || document.hasFocus() || clearInput();
|
|
2037
2053
|
|
|
2038
2054
|
// update mouse world space position
|
|
2039
2055
|
mousePos = screenToWorld(mousePosScreen);
|
|
@@ -2108,7 +2124,7 @@ function gamepadsUpdate()
|
|
|
2108
2124
|
}
|
|
2109
2125
|
}
|
|
2110
2126
|
|
|
2111
|
-
if (!gamepadsEnable || !navigator.getGamepads || !document.hasFocus() && !debug)
|
|
2127
|
+
if (!gamepadsEnable || !navigator || !navigator.getGamepads || !document.hasFocus() && !debug)
|
|
2112
2128
|
return;
|
|
2113
2129
|
|
|
2114
2130
|
// poll gamepads
|
|
@@ -2157,7 +2173,7 @@ function gamepadsUpdate()
|
|
|
2157
2173
|
/** Pulse the vibration hardware if it exists
|
|
2158
2174
|
* @param {Number} [pattern=100] - a single value in miliseconds or vibration interval array
|
|
2159
2175
|
* @memberof Input */
|
|
2160
|
-
const vibrate = (pattern)=> vibrateEnable &&
|
|
2176
|
+
const vibrate = (pattern)=> vibrateEnable && navigator && navigator.vibrate && navigator.vibrate(pattern);
|
|
2161
2177
|
|
|
2162
2178
|
/** Cancel any ongoing vibration
|
|
2163
2179
|
* @memberof Input */
|
|
@@ -2175,7 +2191,7 @@ const isTouchDevice = window.ontouchstart !== undefined;
|
|
|
2175
2191
|
if (isTouchDevice)
|
|
2176
2192
|
{
|
|
2177
2193
|
// handle all touch events the same way
|
|
2178
|
-
let wasTouching,
|
|
2194
|
+
let wasTouching, hadTouch;
|
|
2179
2195
|
ontouchstart = ontouchmove = ontouchend = (e)=>
|
|
2180
2196
|
{
|
|
2181
2197
|
e.button = 0; // all touches are left click
|
|
@@ -2184,7 +2200,8 @@ if (isTouchDevice)
|
|
|
2184
2200
|
const touching = e.touches.length;
|
|
2185
2201
|
if (touching)
|
|
2186
2202
|
{
|
|
2187
|
-
|
|
2203
|
+
// fix mobile audio, force it to play a sound on first touch
|
|
2204
|
+
hadTouch || zzfx(0, hadTouch=1);
|
|
2188
2205
|
|
|
2189
2206
|
// set event pos and pass it along
|
|
2190
2207
|
e.x = e.touches[0].clientX;
|
|
@@ -2197,8 +2214,8 @@ if (isTouchDevice)
|
|
|
2197
2214
|
// set was touching
|
|
2198
2215
|
wasTouching = touching;
|
|
2199
2216
|
|
|
2200
|
-
//
|
|
2201
|
-
return
|
|
2217
|
+
// must return true so the document will get focus
|
|
2218
|
+
return true;
|
|
2202
2219
|
}
|
|
2203
2220
|
}
|
|
2204
2221
|
|
|
@@ -2283,7 +2300,7 @@ function touchGamepadRender()
|
|
|
2283
2300
|
return;
|
|
2284
2301
|
|
|
2285
2302
|
// fade off when not touching or paused
|
|
2286
|
-
const alpha = percent(touchGamepadTimer
|
|
2303
|
+
const alpha = percent(touchGamepadTimer, 4, 3);
|
|
2287
2304
|
if (!alpha || paused)
|
|
2288
2305
|
return;
|
|
2289
2306
|
|
|
@@ -3055,7 +3072,7 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
|
|
|
3055
3072
|
redrawEnd()
|
|
3056
3073
|
{
|
|
3057
3074
|
ASSERT(mainContext == this.context); // must call redrawStart() before drawing tiles
|
|
3058
|
-
glCopyToContext(mainContext, 1);
|
|
3075
|
+
glEnable && glCopyToContext(mainContext, 1);
|
|
3059
3076
|
//debugSaveCanvas(this.canvas);
|
|
3060
3077
|
|
|
3061
3078
|
// set stuff back to normal
|
|
@@ -3129,7 +3146,7 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
|
|
|
3129
3146
|
const cols = tileImage.width/tileSize.x;
|
|
3130
3147
|
context.globalAlpha = color.a; // only alpha, no color, is supported in this mode
|
|
3131
3148
|
context.drawImage(tileImage,
|
|
3132
|
-
(tileIndex%cols)*tileSize.x, (tileIndex/cols|0)*tileSize.
|
|
3149
|
+
(tileIndex%cols)*tileSize.x, (tileIndex/cols|0)*tileSize.y,
|
|
3133
3150
|
tileSize.x, tileSize.y, -.5, -.5, 1, 1);
|
|
3134
3151
|
}
|
|
3135
3152
|
});
|
|
@@ -3199,6 +3216,7 @@ class ParticleEmitter extends EngineObject
|
|
|
3199
3216
|
* @param {Boolean} [additive=0] - Should particles use addtive blend
|
|
3200
3217
|
* @param {Boolean} [randomColorLinear=1] - Should color be randomized linearly or across each component
|
|
3201
3218
|
* @param {Number} [renderOrder=0] - Render order for particles (additive is above other stuff by default)
|
|
3219
|
+
* @param {Boolean} [localSpace=0] - Should it be in local space of emitter (world space is default)
|
|
3202
3220
|
*/
|
|
3203
3221
|
constructor
|
|
3204
3222
|
(
|
|
@@ -3228,7 +3246,8 @@ class ParticleEmitter extends EngineObject
|
|
|
3228
3246
|
collideTiles,
|
|
3229
3247
|
additive,
|
|
3230
3248
|
randomColorLinear = 1,
|
|
3231
|
-
renderOrder = additive ? 1e9 : 0
|
|
3249
|
+
renderOrder = additive ? 1e9 : 0,
|
|
3250
|
+
localSpace
|
|
3232
3251
|
)
|
|
3233
3252
|
{
|
|
3234
3253
|
super(pos, new Vector2, tileIndex, tileSize, angle, undefined, renderOrder);
|
|
@@ -3282,6 +3301,8 @@ class ParticleEmitter extends EngineObject
|
|
|
3282
3301
|
this.collideTiles = collideTiles;
|
|
3283
3302
|
/** @property {Number} - Should particles use addtive blend */
|
|
3284
3303
|
this.additive = additive;
|
|
3304
|
+
/** @property {Boolean} - Should it be in local space of emitter */
|
|
3305
|
+
this.localSpace = localSpace;
|
|
3285
3306
|
/** @property {Number} - If set the partile is drawn as a trail, stretched in the drection of velocity */
|
|
3286
3307
|
this.trailScale = 0;
|
|
3287
3308
|
|
|
@@ -3317,30 +3338,38 @@ class ParticleEmitter extends EngineObject
|
|
|
3317
3338
|
emitParticle()
|
|
3318
3339
|
{
|
|
3319
3340
|
// spawn a particle
|
|
3320
|
-
|
|
3321
|
-
(new Vector2(rand(-.5,.5), rand(-.5,.5)))
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3341
|
+
let pos = this.emitSize.x != undefined ? // check if vec2 was used for size
|
|
3342
|
+
(new Vector2(rand(-.5,.5), rand(-.5,.5)))
|
|
3343
|
+
.multiply(this.emitSize).rotate(this.angle) // box emitter
|
|
3344
|
+
: randInCircle(this.emitSize * .5); // circle emitter
|
|
3345
|
+
let angle = rand(this.particleConeAngle, -this.particleConeAngle);
|
|
3346
|
+
if (!this.localSpace)
|
|
3347
|
+
{
|
|
3348
|
+
pos = this.pos.add(pos);
|
|
3349
|
+
angle += this.angle;
|
|
3350
|
+
}
|
|
3351
|
+
|
|
3352
|
+
const particle = new Particle(pos, this.tileIndex, this.tileSize, angle);
|
|
3325
3353
|
|
|
3326
3354
|
// randomness scales each paremeter by a percentage
|
|
3327
3355
|
const randomness = this.randomness;
|
|
3328
3356
|
const randomizeScale = (v)=> v + v*rand(randomness, -randomness);
|
|
3329
3357
|
|
|
3330
3358
|
// randomize particle settings
|
|
3331
|
-
const particleTime
|
|
3332
|
-
const sizeStart
|
|
3333
|
-
const sizeEnd
|
|
3334
|
-
const speed
|
|
3335
|
-
const angleSpeed
|
|
3336
|
-
const coneAngle
|
|
3337
|
-
const colorStart
|
|
3338
|
-
const colorEnd
|
|
3359
|
+
const particleTime = randomizeScale(this.particleTime);
|
|
3360
|
+
const sizeStart = randomizeScale(this.sizeStart);
|
|
3361
|
+
const sizeEnd = randomizeScale(this.sizeEnd);
|
|
3362
|
+
const speed = randomizeScale(this.speed);
|
|
3363
|
+
const angleSpeed = randomizeScale(this.angleSpeed) * randSign();
|
|
3364
|
+
const coneAngle = rand(this.emitConeAngle, -this.emitConeAngle);
|
|
3365
|
+
const colorStart = randColor(this.colorStartA, this.colorStartB, this.randomColorLinear);
|
|
3366
|
+
const colorEnd = randColor(this.colorEndA, this.colorEndB, this.randomColorLinear);
|
|
3367
|
+
const velocityAngle = this.localSpace ? coneAngle : this.angle + coneAngle;
|
|
3339
3368
|
|
|
3340
3369
|
// build particle settings
|
|
3341
3370
|
particle.colorStart = colorStart;
|
|
3342
3371
|
particle.colorEndDelta = colorEnd.subtract(colorStart);
|
|
3343
|
-
particle.velocity = (new Vector2).setAngle(
|
|
3372
|
+
particle.velocity = (new Vector2).setAngle(velocityAngle, speed);
|
|
3344
3373
|
particle.angleVelocity = angleSpeed;
|
|
3345
3374
|
particle.lifeTime = particleTime;
|
|
3346
3375
|
particle.sizeStart = sizeStart;
|
|
@@ -3356,6 +3385,7 @@ class ParticleEmitter extends EngineObject
|
|
|
3356
3385
|
particle.renderOrder = this.renderOrder;
|
|
3357
3386
|
particle.trailScale = this.trailScale;
|
|
3358
3387
|
particle.mirror = rand()<.5;
|
|
3388
|
+
particle.localSpaceEmitter = this.localSpace && this;
|
|
3359
3389
|
|
|
3360
3390
|
// setup callbacks for particles
|
|
3361
3391
|
particle.destroyCallback = this.particleDestroyCallback;
|
|
@@ -3402,20 +3432,31 @@ class Particle extends EngineObject
|
|
|
3402
3432
|
|
|
3403
3433
|
// draw the particle
|
|
3404
3434
|
this.additive && setBlendMode(1);
|
|
3435
|
+
|
|
3436
|
+
let pos = this.pos, angle = this.angle;
|
|
3437
|
+
if (this.localSpaceEmitter)
|
|
3438
|
+
{
|
|
3439
|
+
// in local space of emitter
|
|
3440
|
+
pos = this.localSpaceEmitter.pos.add(pos.rotate(-this.localSpaceEmitter.angle));
|
|
3441
|
+
angle += this.localSpaceEmitter.angle;
|
|
3442
|
+
}
|
|
3405
3443
|
if (this.trailScale)
|
|
3406
3444
|
{
|
|
3407
3445
|
// trail style particles
|
|
3408
|
-
|
|
3409
|
-
|
|
3446
|
+
let velocity = this.velocity;
|
|
3447
|
+
if (this.localSpaceEmitter)
|
|
3448
|
+
velocity = velocity.rotate(-this.localSpaceEmitter.angle);
|
|
3449
|
+
const speed = velocity.length();
|
|
3450
|
+
const direction = velocity.scale(1/speed);
|
|
3410
3451
|
const trailLength = speed * this.trailScale;
|
|
3411
3452
|
size.y = max(size.x, trailLength);
|
|
3412
|
-
|
|
3413
|
-
drawTile(
|
|
3453
|
+
angle = direction.angle();
|
|
3454
|
+
drawTile(pos.add(direction.multiply(vec2(0,-trailLength/2))), size, this.tileIndex, this.tileSize, color, angle, this.mirror);
|
|
3414
3455
|
}
|
|
3415
3456
|
else
|
|
3416
|
-
drawTile(
|
|
3457
|
+
drawTile(pos, size, this.tileIndex, this.tileSize, color, angle, this.mirror);
|
|
3417
3458
|
this.additive && setBlendMode();
|
|
3418
|
-
debugParticles && debugRect(
|
|
3459
|
+
debugParticles && debugRect(pos, size, '#f005', 0, angle);
|
|
3419
3460
|
|
|
3420
3461
|
if (p == 1)
|
|
3421
3462
|
{
|
|
@@ -3445,11 +3486,6 @@ const medals = [];
|
|
|
3445
3486
|
* @memberof Medals */
|
|
3446
3487
|
let medalsPreventUnlock;
|
|
3447
3488
|
|
|
3448
|
-
/** This can used to enable Newgrounds functionality
|
|
3449
|
-
* @type {Newgrounds}
|
|
3450
|
-
* @memberof Medals */
|
|
3451
|
-
let newgrounds;
|
|
3452
|
-
|
|
3453
3489
|
// Engine internal variables not exposed to documentation
|
|
3454
3490
|
let medalsDisplayQueue = [], medalsSaveName, medalsDisplayTimeLast;
|
|
3455
3491
|
|
|
@@ -3464,7 +3500,7 @@ function medalsInit(saveName)
|
|
|
3464
3500
|
{
|
|
3465
3501
|
// check if medals are unlocked
|
|
3466
3502
|
medalsSaveName = saveName;
|
|
3467
|
-
debugMedals || medals.forEach(medal=> localStorage[medal.storageKey()]);
|
|
3503
|
+
debugMedals || medals.forEach(medal=> medal.unlocked = (localStorage[medal.storageKey()] | 0));
|
|
3468
3504
|
}
|
|
3469
3505
|
|
|
3470
3506
|
/**
|
|
@@ -3597,6 +3633,15 @@ function medalsRender()
|
|
|
3597
3633
|
|
|
3598
3634
|
///////////////////////////////////////////////////////////////////////////////
|
|
3599
3635
|
|
|
3636
|
+
// global Newgrounds object
|
|
3637
|
+
let newgrounds;
|
|
3638
|
+
|
|
3639
|
+
/** This can used to enable Newgrounds functionality
|
|
3640
|
+
* @param {Number} app_id - The newgrounds App ID
|
|
3641
|
+
* @param {String} [cipher] - The encryption Key (AES-128/Base64)
|
|
3642
|
+
* @memberof Medals */
|
|
3643
|
+
function newgroundsInit(app_id, cipher) { newgrounds = new Newgrounds(app_id, cipher); }
|
|
3644
|
+
|
|
3600
3645
|
/**
|
|
3601
3646
|
* Newgrounds API wrapper object
|
|
3602
3647
|
* @example
|
|
@@ -3757,15 +3802,13 @@ let glContext;
|
|
|
3757
3802
|
let glTileTexture;
|
|
3758
3803
|
|
|
3759
3804
|
// WebGL internal variables not exposed to documentation
|
|
3760
|
-
let glActiveTexture, glShader, glPositionData, glColorData, glBatchCount, glBatchAdditive, glAdditive;
|
|
3805
|
+
let glActiveTexture, glShader, glArrayBuffer, glVertexData, glPositionData, glColorData, glBatchCount, glBatchAdditive, glAdditive;
|
|
3761
3806
|
|
|
3762
3807
|
///////////////////////////////////////////////////////////////////////////////
|
|
3763
3808
|
|
|
3764
3809
|
// Init WebGL, called automatically by the engine
|
|
3765
3810
|
function glInit()
|
|
3766
3811
|
{
|
|
3767
|
-
if (!glEnable) return;
|
|
3768
|
-
|
|
3769
3812
|
// create the canvas and tile texture
|
|
3770
3813
|
glCanvas = document.createElement('canvas');
|
|
3771
3814
|
glContext = glCanvas.getContext('webgl', {antialias: false});
|
|
@@ -3798,24 +3841,11 @@ function glInit()
|
|
|
3798
3841
|
);
|
|
3799
3842
|
|
|
3800
3843
|
// init buffers
|
|
3801
|
-
|
|
3802
|
-
|
|
3844
|
+
glVertexData = new ArrayBuffer(gl_MAX_BATCH * gl_VERTICES_PER_QUAD * gl_VERTEX_BYTE_STRIDE);
|
|
3845
|
+
glArrayBuffer = glContext.createBuffer();
|
|
3803
3846
|
glPositionData = new Float32Array(glVertexData);
|
|
3804
3847
|
glColorData = new Uint32Array(glVertexData);
|
|
3805
|
-
|
|
3806
|
-
// setup the vertex data array
|
|
3807
|
-
let offset = glBatchCount = 0;
|
|
3808
|
-
const initVertexAttribArray = (name, type, typeSize, size, normalize=0)=>
|
|
3809
|
-
{
|
|
3810
|
-
const location = glContext.getAttribLocation(glShader, name);
|
|
3811
|
-
glContext.enableVertexAttribArray(location);
|
|
3812
|
-
glContext.vertexAttribPointer(location, size, type, normalize, gl_VERTEX_BYTE_STRIDE, offset);
|
|
3813
|
-
offset += size*typeSize;
|
|
3814
|
-
}
|
|
3815
|
-
initVertexAttribArray('p', gl_FLOAT, 4, 2); // position
|
|
3816
|
-
initVertexAttribArray('t', gl_FLOAT, 4, 2); // texture coords
|
|
3817
|
-
initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4, 1); // color
|
|
3818
|
-
initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4, 1); // additiveColor
|
|
3848
|
+
glBatchCount = 0;
|
|
3819
3849
|
}
|
|
3820
3850
|
|
|
3821
3851
|
/** Set the WebGl blend mode, normally you should call setBlendMode instead
|
|
@@ -3823,8 +3853,6 @@ function glInit()
|
|
|
3823
3853
|
* @memberof WebGL */
|
|
3824
3854
|
function glSetBlendMode(additive)
|
|
3825
3855
|
{
|
|
3826
|
-
if (!glEnable) return;
|
|
3827
|
-
|
|
3828
3856
|
// setup blending
|
|
3829
3857
|
glAdditive = additive;
|
|
3830
3858
|
}
|
|
@@ -3835,8 +3863,6 @@ function glSetBlendMode(additive)
|
|
|
3835
3863
|
* @memberof WebGL */
|
|
3836
3864
|
function glSetTexture(texture=glTileTexture)
|
|
3837
3865
|
{
|
|
3838
|
-
if (!glEnable) return;
|
|
3839
|
-
|
|
3840
3866
|
// must flush cache with the old texture to set a new one
|
|
3841
3867
|
if (texture != glActiveTexture)
|
|
3842
3868
|
glFlush();
|
|
@@ -3851,8 +3877,6 @@ function glSetTexture(texture=glTileTexture)
|
|
|
3851
3877
|
* @memberof WebGL */
|
|
3852
3878
|
function glCompileShader(source, type)
|
|
3853
3879
|
{
|
|
3854
|
-
if (!glEnable) return;
|
|
3855
|
-
|
|
3856
3880
|
// build the shader
|
|
3857
3881
|
const shader = glContext.createShader(type);
|
|
3858
3882
|
glContext.shaderSource(shader, source);
|
|
@@ -3871,8 +3895,6 @@ function glCompileShader(source, type)
|
|
|
3871
3895
|
* @memberof WebGL */
|
|
3872
3896
|
function glCreateProgram(vsSource, fsSource)
|
|
3873
3897
|
{
|
|
3874
|
-
if (!glEnable) return;
|
|
3875
|
-
|
|
3876
3898
|
// build the program
|
|
3877
3899
|
const program = glContext.createProgram();
|
|
3878
3900
|
glContext.attachShader(program, glCompileShader(vsSource, gl_VERTEX_SHADER));
|
|
@@ -3885,35 +3907,16 @@ function glCreateProgram(vsSource, fsSource)
|
|
|
3885
3907
|
return program;
|
|
3886
3908
|
}
|
|
3887
3909
|
|
|
3888
|
-
/** Create WebGL buffer
|
|
3889
|
-
* @param bufferType
|
|
3890
|
-
* @param size
|
|
3891
|
-
* @param usage
|
|
3892
|
-
* @return {WebGLBuffer}
|
|
3893
|
-
* @memberof WebGL */
|
|
3894
|
-
function glCreateBuffer(bufferType, size, usage)
|
|
3895
|
-
{
|
|
3896
|
-
if (!glEnable) return;
|
|
3897
|
-
|
|
3898
|
-
// build the buffer
|
|
3899
|
-
const buffer = glContext.createBuffer();
|
|
3900
|
-
glContext.bindBuffer(bufferType, buffer);
|
|
3901
|
-
glContext.bufferData(bufferType, size, usage);
|
|
3902
|
-
return buffer;
|
|
3903
|
-
}
|
|
3904
|
-
|
|
3905
3910
|
/** Create WebGL texture from an image and set the texture settings
|
|
3906
3911
|
* @param {Image} image
|
|
3907
3912
|
* @return {WebGLTexture}
|
|
3908
3913
|
* @memberof WebGL */
|
|
3909
3914
|
function glCreateTexture(image)
|
|
3910
3915
|
{
|
|
3911
|
-
if (!glEnable || !image || !image.width) return;
|
|
3912
|
-
|
|
3913
3916
|
// build the texture
|
|
3914
3917
|
const texture = glContext.createTexture();
|
|
3915
3918
|
glContext.bindTexture(gl_TEXTURE_2D, texture);
|
|
3916
|
-
glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, image);
|
|
3919
|
+
image && image.width && glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, image);
|
|
3917
3920
|
|
|
3918
3921
|
// use point filtering for pixelated rendering
|
|
3919
3922
|
glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, cavasPixelated ? gl_NEAREST : gl_LINEAR);
|
|
@@ -3926,16 +3929,31 @@ function glCreateTexture(image)
|
|
|
3926
3929
|
// called automatically by engine before render
|
|
3927
3930
|
function glPreRender(width, height, cameraX, cameraY, cameraScale)
|
|
3928
3931
|
{
|
|
3929
|
-
if (!glEnable) return;
|
|
3930
|
-
|
|
3931
3932
|
// clear and set to same size as main canvas
|
|
3932
3933
|
glContext.viewport(0, 0, glCanvas.width = width, glCanvas.height = height);
|
|
3933
3934
|
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
3934
3935
|
|
|
3935
3936
|
// set up the shader
|
|
3936
|
-
glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = glTileTexture);
|
|
3937
3937
|
glContext.useProgram(glShader);
|
|
3938
|
+
glContext.activeTexture(gl_TEXTURE0);
|
|
3939
|
+
glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = glTileTexture);
|
|
3940
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glArrayBuffer);
|
|
3941
|
+
glContext.bufferData(gl_ARRAY_BUFFER, glVertexData.byteLength, gl_DYNAMIC_DRAW);
|
|
3938
3942
|
glSetBlendMode();
|
|
3943
|
+
|
|
3944
|
+
// set vertex attributes
|
|
3945
|
+
let offset = 0;
|
|
3946
|
+
const initVertexAttribArray = (name, type, typeSize, size, normalize=0)=>
|
|
3947
|
+
{
|
|
3948
|
+
const location = glContext.getAttribLocation(glShader, name);
|
|
3949
|
+
glContext.enableVertexAttribArray(location);
|
|
3950
|
+
glContext.vertexAttribPointer(location, size, type, normalize, gl_VERTEX_BYTE_STRIDE, offset);
|
|
3951
|
+
offset += size*typeSize;
|
|
3952
|
+
}
|
|
3953
|
+
initVertexAttribArray('p', gl_FLOAT, 4, 2); // position
|
|
3954
|
+
initVertexAttribArray('t', gl_FLOAT, 4, 2); // texture coords
|
|
3955
|
+
initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4, 1); // color
|
|
3956
|
+
initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4, 1); // additiveColor
|
|
3939
3957
|
|
|
3940
3958
|
// build the transform matrix
|
|
3941
3959
|
const sx = 2 * cameraScale / width;
|
|
@@ -3954,7 +3972,7 @@ function glPreRender(width, height, cameraX, cameraY, cameraScale)
|
|
|
3954
3972
|
* @memberof WebGL */
|
|
3955
3973
|
function glFlush()
|
|
3956
3974
|
{
|
|
3957
|
-
if (!
|
|
3975
|
+
if (!glBatchCount) return;
|
|
3958
3976
|
|
|
3959
3977
|
const destBlend = glBatchAdditive ? gl_ONE : gl_ONE_MINUS_SRC_ALPHA;
|
|
3960
3978
|
glContext.blendFuncSeparate(gl_SRC_ALPHA, destBlend, gl_ONE, destBlend);
|
|
@@ -3974,7 +3992,7 @@ function glFlush()
|
|
|
3974
3992
|
* @memberof WebGL */
|
|
3975
3993
|
function glCopyToContext(context, forceDraw)
|
|
3976
3994
|
{
|
|
3977
|
-
if (
|
|
3995
|
+
if (!glBatchCount && !forceDraw) return;
|
|
3978
3996
|
|
|
3979
3997
|
glFlush();
|
|
3980
3998
|
|
|
@@ -3998,8 +4016,6 @@ function glCopyToContext(context, forceDraw)
|
|
|
3998
4016
|
* @memberof WebGL */
|
|
3999
4017
|
function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba=0xffffffff, rgbaAdditive=0)
|
|
4000
4018
|
{
|
|
4001
|
-
if (!glEnable) return;
|
|
4002
|
-
|
|
4003
4019
|
// flush if there is no room for more verts or if different blend mode
|
|
4004
4020
|
if (glBatchCount == gl_MAX_BATCH || glBatchAdditive != glAdditive)
|
|
4005
4021
|
glFlush();
|
|
@@ -4048,6 +4064,90 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba=0xffffff
|
|
|
4048
4064
|
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
4049
4065
|
}
|
|
4050
4066
|
|
|
4067
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
4068
|
+
// post processing - can be enabled to pass other canvases through a final shader
|
|
4069
|
+
|
|
4070
|
+
let glPostShader, glPostArrayBuffer, glPostTexture;
|
|
4071
|
+
|
|
4072
|
+
/** Set up a post processing shader
|
|
4073
|
+
* @param {String} shaderCode
|
|
4074
|
+
* @memberof WebGL */
|
|
4075
|
+
function glInitPostProcess(shaderCode)
|
|
4076
|
+
{
|
|
4077
|
+
ASSERT(!glPostShader); // can only have 1 post effects shader
|
|
4078
|
+
|
|
4079
|
+
if (!shaderCode) // default shader
|
|
4080
|
+
shaderCode = 'void mainImage(out vec4 c,vec2 p){c=texture2D(iChannel0,p/iResolution.xy);}';
|
|
4081
|
+
|
|
4082
|
+
// create the shader
|
|
4083
|
+
glPostShader = glCreateProgram(
|
|
4084
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
4085
|
+
'attribute vec2 p;'+ // position
|
|
4086
|
+
'void main(){'+ // shader entry point
|
|
4087
|
+
'gl_Position=vec4(p,1,1);'+ // set position
|
|
4088
|
+
'}' // end of shader
|
|
4089
|
+
,
|
|
4090
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
4091
|
+
'uniform sampler2D iChannel0;'+ // input texture
|
|
4092
|
+
'uniform vec3 iResolution;'+ // size of output texture
|
|
4093
|
+
'uniform float iTime;'+ // time passed
|
|
4094
|
+
'\n' + shaderCode + '\n'+ // insert custom shader code
|
|
4095
|
+
'void main(){'+ // shader entry point
|
|
4096
|
+
'mainImage(gl_FragColor,gl_FragCoord.xy);'+ // call post process function
|
|
4097
|
+
'gl_FragColor.a=1.;'+ // always use full alpha
|
|
4098
|
+
'}' // end of shader
|
|
4099
|
+
);
|
|
4100
|
+
|
|
4101
|
+
// create buffer and texture
|
|
4102
|
+
glPostArrayBuffer = glContext.createBuffer();
|
|
4103
|
+
glPostTexture = glCreateTexture();
|
|
4104
|
+
|
|
4105
|
+
// hide the original 2d canvas
|
|
4106
|
+
mainCanvas.style.visibility = 'hidden';
|
|
4107
|
+
}
|
|
4108
|
+
|
|
4109
|
+
// Render the post processing shader, called automatically by the engine
|
|
4110
|
+
function glRenderPostProcess()
|
|
4111
|
+
{
|
|
4112
|
+
if (!glPostShader)
|
|
4113
|
+
return;
|
|
4114
|
+
|
|
4115
|
+
// prepare to render post process shader
|
|
4116
|
+
const width = mainCanvas.width, height = mainCanvas.height;
|
|
4117
|
+
if (glEnable)
|
|
4118
|
+
{
|
|
4119
|
+
glFlush(); // clear out the buffer
|
|
4120
|
+
mainContext.drawImage(glCanvas, 0, 0); // copy to the main canvas
|
|
4121
|
+
}
|
|
4122
|
+
else
|
|
4123
|
+
glContext.viewport(0, 0, glCanvas.width = width, glCanvas.height = height); // set viewport
|
|
4124
|
+
|
|
4125
|
+
// setup shader program to draw one triangle
|
|
4126
|
+
glContext.useProgram(glPostShader);
|
|
4127
|
+
glContext.disable(gl_BLEND);
|
|
4128
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glPostArrayBuffer);
|
|
4129
|
+
glContext.bufferData(gl_ARRAY_BUFFER, new Float32Array([-3,1,1,-3,1,1]), gl_STATIC_DRAW);
|
|
4130
|
+
glContext.pixelStorei(gl_UNPACK_FLIP_Y_WEBGL, true);
|
|
4131
|
+
|
|
4132
|
+
// set textures, pass in the 2d canvas and gl canvas in separate texture channels
|
|
4133
|
+
glContext.activeTexture(gl_TEXTURE0);
|
|
4134
|
+
glContext.bindTexture(gl_TEXTURE_2D, glPostTexture);
|
|
4135
|
+
glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, mainCanvas);
|
|
4136
|
+
|
|
4137
|
+
// set vertex position attribute
|
|
4138
|
+
const vertexByteStride = 8;
|
|
4139
|
+
const pLocation = glContext.getAttribLocation(glPostShader, 'p');
|
|
4140
|
+
glContext.enableVertexAttribArray(pLocation);
|
|
4141
|
+
glContext.vertexAttribPointer(pLocation, 2, gl_FLOAT, 0, vertexByteStride, 0);
|
|
4142
|
+
|
|
4143
|
+
// set uniforms and draw
|
|
4144
|
+
const uniformLocation = (name)=>glContext.getUniformLocation(glPostShader, name);
|
|
4145
|
+
glContext.uniform1i(uniformLocation('iChannel0'), 0);
|
|
4146
|
+
glContext.uniform1f(uniformLocation('iTime'), time);
|
|
4147
|
+
glContext.uniform3f(uniformLocation('iResolution'), width, height, 1);
|
|
4148
|
+
glContext.drawArrays(gl_TRIANGLES, 0, 3);
|
|
4149
|
+
}
|
|
4150
|
+
|
|
4051
4151
|
///////////////////////////////////////////////////////////////////////////////
|
|
4052
4152
|
// store gl constants as integers so their name doesn't use space in minifed
|
|
4053
4153
|
const
|
|
@@ -4058,6 +4158,7 @@ gl_ONE_MINUS_SRC_ALPHA = 771,
|
|
|
4058
4158
|
gl_BLEND = 3042,
|
|
4059
4159
|
gl_TEXTURE_2D = 3553,
|
|
4060
4160
|
gl_UNSIGNED_BYTE = 5121,
|
|
4161
|
+
gl_BYTE = 5120,
|
|
4061
4162
|
gl_FLOAT = 5126,
|
|
4062
4163
|
gl_RGBA = 6408,
|
|
4063
4164
|
gl_NEAREST = 9728,
|
|
@@ -4068,12 +4169,16 @@ gl_TEXTURE_WRAP_S = 10242,
|
|
|
4068
4169
|
gl_TEXTURE_WRAP_T = 10243,
|
|
4069
4170
|
gl_COLOR_BUFFER_BIT = 16384,
|
|
4070
4171
|
gl_CLAMP_TO_EDGE = 33071,
|
|
4172
|
+
gl_TEXTURE0 = 33984,
|
|
4173
|
+
gl_TEXTURE1 = 33985,
|
|
4071
4174
|
gl_ARRAY_BUFFER = 34962,
|
|
4175
|
+
gl_STATIC_DRAW = 35044,
|
|
4072
4176
|
gl_DYNAMIC_DRAW = 35048,
|
|
4073
4177
|
gl_FRAGMENT_SHADER = 35632,
|
|
4074
4178
|
gl_VERTEX_SHADER = 35633,
|
|
4075
4179
|
gl_COMPILE_STATUS = 35713,
|
|
4076
4180
|
gl_LINK_STATUS = 35714,
|
|
4181
|
+
gl_UNPACK_FLIP_Y_WEBGL = 37440,
|
|
4077
4182
|
|
|
4078
4183
|
// constants for batch rendering
|
|
4079
4184
|
gl_VERTICES_PER_QUAD = 6,
|
|
@@ -4104,7 +4209,7 @@ gl_VERTEX_BYTE_STRIDE = (4 * 2) * 2 + (4) * 2; // vec2 * 2 + (char * 4) * 2
|
|
|
4104
4209
|
const engineName = 'LittleJS';
|
|
4105
4210
|
|
|
4106
4211
|
/** Version of engine */
|
|
4107
|
-
const engineVersion = '1.
|
|
4212
|
+
const engineVersion = '1.4.5';
|
|
4108
4213
|
|
|
4109
4214
|
/** Frames per second to update objects
|
|
4110
4215
|
* @default */
|
|
@@ -4139,8 +4244,7 @@ let frameTimeLastMS = 0, frameTimeBufferMS = 0, tileImageSize, tileImageFixBleed
|
|
|
4139
4244
|
let averageFPS, drawCount;
|
|
4140
4245
|
|
|
4141
4246
|
// css text used for elements created by engine
|
|
4142
|
-
const styleBody = 'margin:0;overflow:hidden;background:#000'
|
|
4143
|
-
';touch-action:none;user-select:none;-webkit-user-select:none;-moz-user-select:none';
|
|
4247
|
+
const styleBody = 'margin:0;overflow:hidden;background:#000';
|
|
4144
4248
|
const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
|
|
4145
4249
|
|
|
4146
4250
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -4170,7 +4274,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4170
4274
|
|
|
4171
4275
|
// init stuff and start engine
|
|
4172
4276
|
debugInit();
|
|
4173
|
-
glInit();
|
|
4277
|
+
glEnable && glInit();
|
|
4174
4278
|
|
|
4175
4279
|
// create overlay canvas for hud to appear above gl canvas
|
|
4176
4280
|
document.body.appendChild(overlayCanvas = document.createElement('canvas'));
|
|
@@ -4190,10 +4294,14 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4190
4294
|
frameTimeLastMS = frameTimeMS;
|
|
4191
4295
|
if (debug || showWatermark)
|
|
4192
4296
|
averageFPS = lerp(.05, averageFPS || 0, 1e3/(frameTimeDeltaMS||1));
|
|
4297
|
+
const debugSpeedUp = debug && keyIsDown(107); // +
|
|
4298
|
+
const debugSpeedDown = debug && keyIsDown(109); // -
|
|
4193
4299
|
if (debug)
|
|
4194
|
-
frameTimeDeltaMS *=
|
|
4300
|
+
frameTimeDeltaMS *= debugSpeedUp ? 5 : debugSpeedDown ? .2 : 1; // +/- to speed/slow time
|
|
4195
4301
|
timeReal += frameTimeDeltaMS / 1e3;
|
|
4196
|
-
frameTimeBufferMS
|
|
4302
|
+
frameTimeBufferMS += !paused * frameTimeDeltaMS;
|
|
4303
|
+
if (!debugSpeedUp)
|
|
4304
|
+
frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp incase of slow framerate
|
|
4197
4305
|
|
|
4198
4306
|
if (canvasFixedSize.x)
|
|
4199
4307
|
{
|
|
@@ -4266,10 +4374,11 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4266
4374
|
for (const o of engineObjects)
|
|
4267
4375
|
o.destroyed || o.render();
|
|
4268
4376
|
gameRenderPost();
|
|
4377
|
+
glRenderPostProcess();
|
|
4269
4378
|
medalsRender();
|
|
4270
4379
|
touchGamepadRender();
|
|
4271
4380
|
debugRender();
|
|
4272
|
-
glCopyToContext(mainContext);
|
|
4381
|
+
glEnable && glCopyToContext(mainContext);
|
|
4273
4382
|
|
|
4274
4383
|
if (showWatermark)
|
|
4275
4384
|
{
|
|
@@ -4304,7 +4413,7 @@ function enginePreRender()
|
|
|
4304
4413
|
mainContext.imageSmoothingEnabled = !cavasPixelated;
|
|
4305
4414
|
|
|
4306
4415
|
// setup gl rendering if enabled
|
|
4307
|
-
glPreRender(mainCanvas.width, mainCanvas.height, cameraPos.x, cameraPos.y, cameraScale);
|
|
4416
|
+
glEnable && glPreRender(mainCanvas.width, mainCanvas.height, cameraPos.x, cameraPos.y, cameraScale);
|
|
4308
4417
|
}
|
|
4309
4418
|
|
|
4310
4419
|
///////////////////////////////////////////////////////////////////////////////
|