littlejsengine 1.13.1 → 1.13.4
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 +15 -12
- package/dist/littlejs.d.ts +94 -74
- package/dist/littlejs.esm.js +422 -306
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +416 -306
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +414 -304
- package/examples/box2d/game.js +1 -1
- package/examples/box2d/gameObjects.js +1 -1
- package/examples/box2d/scenes.js +1 -1
- package/examples/breakoutTutorial/README.md +44 -41
- package/examples/breakoutTutorial/game.js +2 -2
- package/examples/electron/build.bat +7 -0
- package/examples/electron/build.js +124 -0
- package/examples/electron/electron.js +35 -0
- package/examples/electron/game.js +140 -0
- package/examples/electron/index.html +13 -0
- package/examples/electron/package.json +12 -0
- package/examples/electron/tiles.png +0 -0
- package/examples/empty/game.js +1 -0
- package/examples/htmlMenu/game.js +1 -1
- package/examples/index.html +154 -93
- package/examples/module/game.js +5 -2
- package/examples/particles/index.html +12 -3
- package/examples/platformer/gameEffects.js +3 -3
- package/examples/shorts/base.html +1 -1
- package/examples/shorts/blending.js +9 -5
- package/examples/shorts/box2d.js +1 -1
- package/examples/shorts/box2dCar.js +1 -1
- package/examples/shorts/clock.js +1 -1
- package/examples/shorts/colors.js +2 -2
- package/examples/shorts/maze.js +1 -1
- package/examples/shorts/nineSlice.js +9 -9
- package/examples/shorts/piano.js +2 -2
- package/examples/shorts/raycasting.js +2 -2
- package/examples/shorts/shapes.js +9 -9
- package/examples/shorts/slidingPuzzle.js +2 -2
- package/examples/shorts/starfield.js +5 -7
- package/examples/shorts/systemFont.js +1 -1
- package/examples/shorts/uiSystem.js +1 -0
- package/examples/starter/build.js +9 -8
- package/examples/starter/game.js +5 -2
- package/examples/starter/index.html +2 -2
- package/examples/stress/index.html +5 -5
- package/examples/style.css +5 -2
- package/examples/typescript/build.js +1 -1
- package/examples/typescript/game.js +2 -2
- package/examples/typescript/game.ts +5 -2
- package/examples/uiSystem/game.js +2 -1
- package/package.json +2 -2
- package/plugins/box2d.js +19 -94
- package/plugins/drawUtilities.js +24 -18
- package/plugins/postProcess.js +7 -0
- package/plugins/uiSystem.js +4 -4
- package/src/engine.js +7 -2
- package/src/engineAudio.js +1 -1
- package/src/engineDebug.js +2 -2
- package/src/engineDraw.js +243 -150
- package/src/engineExport.js +6 -0
- package/src/engineInput.js +2 -2
- package/src/engineMedals.js +4 -4
- package/src/engineObject.js +3 -3
- package/src/engineParticles.js +8 -1
- package/src/engineSettings.js +55 -8
- package/src/engineUtilities.js +7 -7
- package/src/engineWebGL.js +25 -5
package/dist/littlejs.js
CHANGED
|
@@ -33,7 +33,7 @@ const engineName = 'LittleJS';
|
|
|
33
33
|
* @type {string}
|
|
34
34
|
* @default
|
|
35
35
|
* @memberof Engine */
|
|
36
|
-
const engineVersion = '1.13.
|
|
36
|
+
const engineVersion = '1.13.4';
|
|
37
37
|
|
|
38
38
|
/** Frames per second to update
|
|
39
39
|
* @type {number}
|
|
@@ -300,7 +300,6 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
300
300
|
'margin:0;' + // fill the window
|
|
301
301
|
'overflow:hidden;' + // no scroll bars
|
|
302
302
|
'background:#000;' + // set background color
|
|
303
|
-
(canvasPixelated ? 'image-rendering:pixelated;' : '') + // pixel art
|
|
304
303
|
'user-select:none;' + // prevent hold to select
|
|
305
304
|
'-webkit-user-select:none;' + // compatibility for ios
|
|
306
305
|
(!touchInputEnable ? '' : // no touch css settings
|
|
@@ -328,7 +327,13 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
328
327
|
mainCanvas.style.cssText = overlayCanvas.style.cssText = styleCanvas;
|
|
329
328
|
if (glCanvas)
|
|
330
329
|
glCanvas.style.cssText = styleCanvas;
|
|
330
|
+
setCanvasPixelated(canvasPixelated);
|
|
331
|
+
setOverlayCanvasPixelated(overlayCanvasPixelated);
|
|
331
332
|
updateCanvas();
|
|
333
|
+
|
|
334
|
+
// create offscreen canvas for image processing
|
|
335
|
+
workCanvas = new OffscreenCanvas(256, 256);
|
|
336
|
+
workContext = workCanvas.getContext('2d', { willReadFrequently: true });
|
|
332
337
|
|
|
333
338
|
// create promises for loading images
|
|
334
339
|
const promises = imageSources.map((src, textureIndex)=>
|
|
@@ -1120,7 +1125,7 @@ function debugRender()
|
|
|
1120
1125
|
overlayContext.fillText('6: Toggle Video Capture', x, y += h);
|
|
1121
1126
|
|
|
1122
1127
|
let keysPressed = '';
|
|
1123
|
-
for(const i in inputData[0])
|
|
1128
|
+
for (const i in inputData[0])
|
|
1124
1129
|
{
|
|
1125
1130
|
if (keyIsDown(i, 0))
|
|
1126
1131
|
keysPressed += i + ' ' ;
|
|
@@ -1129,7 +1134,7 @@ function debugRender()
|
|
|
1129
1134
|
|
|
1130
1135
|
let buttonsPressed = '';
|
|
1131
1136
|
if (inputData[1])
|
|
1132
|
-
for(const i in inputData[1])
|
|
1137
|
+
for (const i in inputData[1])
|
|
1133
1138
|
{
|
|
1134
1139
|
if (keyIsDown(i, 1))
|
|
1135
1140
|
buttonsPressed += i + ' ' ;
|
|
@@ -1370,11 +1375,12 @@ function isPowerOfTwo(value) { return !(value & (value - 1)); }
|
|
|
1370
1375
|
* @memberof Utilities */
|
|
1371
1376
|
function nearestPowerOfTwo(value) { return 2**Math.ceil(Math.log2(value)); }
|
|
1372
1377
|
|
|
1373
|
-
/** Returns true if two axis aligned bounding boxes are overlapping
|
|
1378
|
+
/** Returns true if two axis aligned bounding boxes are overlapping
|
|
1379
|
+
* this can be used for simple collision detection between objects
|
|
1374
1380
|
* @param {Vector2} posA - Center of box A
|
|
1375
1381
|
* @param {Vector2} sizeA - Size of box A
|
|
1376
1382
|
* @param {Vector2} posB - Center of box B
|
|
1377
|
-
* @param {Vector2} [sizeB=(0,0)] - Size of box B, a point if undefined
|
|
1383
|
+
* @param {Vector2} [sizeB=(0,0)] - Size of box B, uses a point if undefined
|
|
1378
1384
|
* @return {boolean} - True if overlapping
|
|
1379
1385
|
* @memberof Utilities */
|
|
1380
1386
|
function isOverlapping(posA, sizeA, posB, sizeB=vec2())
|
|
@@ -1428,10 +1434,11 @@ function isIntersecting(start, end, pos, size)
|
|
|
1428
1434
|
* @param {number} [frequency] - Frequency of the wave in Hz
|
|
1429
1435
|
* @param {number} [amplitude] - Amplitude (max height) of the wave
|
|
1430
1436
|
* @param {number} [t=time] - Value to use for time of the wave
|
|
1437
|
+
* @param {number} [offset] - Value to use for time offset of the wave
|
|
1431
1438
|
* @return {number} - Value waving between 0 and amplitude
|
|
1432
1439
|
* @memberof Utilities */
|
|
1433
|
-
function wave(frequency=1, amplitude=1, t=time)
|
|
1434
|
-
{ return amplitude/2 * (1 - Math.cos(t*frequency*2*PI)); }
|
|
1440
|
+
function wave(frequency=1, amplitude=1, t=time, offset=0)
|
|
1441
|
+
{ return amplitude/2 * (1 - Math.cos(offset + t*frequency*2*PI)); }
|
|
1435
1442
|
|
|
1436
1443
|
/** Formats seconds to mm:ss style for display purposes
|
|
1437
1444
|
* @param {number} t - time in seconds
|
|
@@ -2088,9 +2095,7 @@ class Color
|
|
|
2088
2095
|
/** Checks if this is a valid color
|
|
2089
2096
|
* @return {boolean} */
|
|
2090
2097
|
isValid()
|
|
2091
|
-
{
|
|
2092
|
-
return isNumber(this.r) && isNumber(this.g) && isNumber(this.b) && isNumber(this.a);
|
|
2093
|
-
}
|
|
2098
|
+
{ return isNumber(this.r) && isNumber(this.g) && isNumber(this.b) && isNumber(this.a); }
|
|
2094
2099
|
}
|
|
2095
2100
|
|
|
2096
2101
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -2253,6 +2258,13 @@ let cameraScale = 32;
|
|
|
2253
2258
|
///////////////////////////////////////////////////////////////////////////////
|
|
2254
2259
|
// Display settings
|
|
2255
2260
|
|
|
2261
|
+
/** Enable applying color to tiles when using canvas2d
|
|
2262
|
+
* - This is slower but should be the same as webgl rendering
|
|
2263
|
+
* @type {boolean}
|
|
2264
|
+
* @default
|
|
2265
|
+
* @memberof Settings */
|
|
2266
|
+
let canvasColorTiles = true;
|
|
2267
|
+
|
|
2256
2268
|
/** The max size of the canvas, centered if window is larger
|
|
2257
2269
|
* @type {Vector2}
|
|
2258
2270
|
* @default Vector2(1920,1080)
|
|
@@ -2266,14 +2278,21 @@ let canvasMaxSize = vec2(1920, 1080);
|
|
|
2266
2278
|
* @memberof Settings */
|
|
2267
2279
|
let canvasFixedSize = vec2();
|
|
2268
2280
|
|
|
2269
|
-
/** Use nearest
|
|
2270
|
-
* - Must be set before startup to take effect
|
|
2281
|
+
/** Use nearest canvas scaling for more pixelated look
|
|
2271
2282
|
* - If enabled sets css image-rendering:pixelated
|
|
2272
2283
|
* @type {boolean}
|
|
2273
2284
|
* @default
|
|
2274
2285
|
* @memberof Settings */
|
|
2275
2286
|
let canvasPixelated = true;
|
|
2276
2287
|
|
|
2288
|
+
/** Use nearest canvas scaling for more pixelated look
|
|
2289
|
+
* - If enabled sets css image-rendering:pixelated
|
|
2290
|
+
* - This defaults to false because text looks better with smoothing
|
|
2291
|
+
* @type {boolean}
|
|
2292
|
+
* @default
|
|
2293
|
+
* @memberof Settings */
|
|
2294
|
+
let overlayCanvasPixelated = false;
|
|
2295
|
+
|
|
2277
2296
|
/** Disables texture filtering for crisper pixel art
|
|
2278
2297
|
* @type {boolean}
|
|
2279
2298
|
* @default
|
|
@@ -2514,6 +2533,14 @@ function setCameraAngle(angle) { cameraAngle = angle; }
|
|
|
2514
2533
|
* @memberof Settings */
|
|
2515
2534
|
function setCameraScale(scale) { cameraScale = scale; }
|
|
2516
2535
|
|
|
2536
|
+
/** Set if tiles should be colorized when using canvas2d
|
|
2537
|
+
* This can be slower but results should look nearly identical to webgl rendering
|
|
2538
|
+
* It can be enabled/disabled at any time
|
|
2539
|
+
* Optimized for performance, and will use faster method if color is white or untextured
|
|
2540
|
+
* @param {boolean} colorTiles
|
|
2541
|
+
* @memberof Settings */
|
|
2542
|
+
function setCanvasColorTiles(colorTiles) { canvasColorTiles = colorTiles; }
|
|
2543
|
+
|
|
2517
2544
|
/** Set max size of the canvas
|
|
2518
2545
|
* @param {Vector2} size
|
|
2519
2546
|
* @memberof Settings */
|
|
@@ -2524,10 +2551,30 @@ function setCanvasMaxSize(size) { canvasMaxSize = size; }
|
|
|
2524
2551
|
* @memberof Settings */
|
|
2525
2552
|
function setCanvasFixedSize(size) { canvasFixedSize = size; }
|
|
2526
2553
|
|
|
2527
|
-
/** Use nearest
|
|
2554
|
+
/** Use nearest scaling algorithm for canvas for more pixelated look
|
|
2555
|
+
* - If enabled sets css image-rendering:pixelated
|
|
2556
|
+
* @param {boolean} pixelated
|
|
2557
|
+
* @memberof Settings */
|
|
2558
|
+
function setCanvasPixelated(pixelated)
|
|
2559
|
+
{
|
|
2560
|
+
canvasPixelated = pixelated;
|
|
2561
|
+
if (mainCanvas)
|
|
2562
|
+
mainCanvas.style.imageRendering = pixelated ? 'pixelated' : '';
|
|
2563
|
+
if (glCanvas)
|
|
2564
|
+
glCanvas.style.imageRendering = pixelated ? 'pixelated' : '';
|
|
2565
|
+
}
|
|
2566
|
+
|
|
2567
|
+
/** Use nearest scaling algorithm for canvas for more pixelated look
|
|
2568
|
+
* - If enabled sets css image-rendering:pixelated
|
|
2569
|
+
* - This defaults to false because text looks better with smoothing
|
|
2528
2570
|
* @param {boolean} pixelated
|
|
2529
2571
|
* @memberof Settings */
|
|
2530
|
-
function
|
|
2572
|
+
function setOverlayCanvasPixelated(pixelated)
|
|
2573
|
+
{
|
|
2574
|
+
overlayCanvasPixelated = pixelated;
|
|
2575
|
+
if (overlayCanvas)
|
|
2576
|
+
overlayCanvas.style.imageRendering = pixelated ? 'pixelated' : '';
|
|
2577
|
+
}
|
|
2531
2578
|
|
|
2532
2579
|
/** Disables texture filtering for crisper pixel art
|
|
2533
2580
|
* @param {boolean} pixelated
|
|
@@ -2552,7 +2599,12 @@ function setHeadlessMode(headless) { headlessMode = headless; }
|
|
|
2552
2599
|
/** Set if webgl rendering is enabled
|
|
2553
2600
|
* @param {boolean} enable
|
|
2554
2601
|
* @memberof Settings */
|
|
2555
|
-
function setGLEnable(enable)
|
|
2602
|
+
function setGLEnable(enable)
|
|
2603
|
+
{
|
|
2604
|
+
glEnable = enable;
|
|
2605
|
+
if (glCanvas) // hide glCanvas if webgl is disabled
|
|
2606
|
+
glCanvas.style.visibility = enable ? 'visible' : 'hidden';
|
|
2607
|
+
}
|
|
2556
2608
|
|
|
2557
2609
|
/** Set default size of tiles in pixels
|
|
2558
2610
|
* @param {Vector2} size
|
|
@@ -2916,7 +2968,7 @@ class EngineObject
|
|
|
2916
2968
|
if (o.mass) // push away if not fixed
|
|
2917
2969
|
o.velocity = o.velocity.subtract(velocity);
|
|
2918
2970
|
|
|
2919
|
-
|
|
2971
|
+
debugPhysics && debugOverlap(this.pos, this.size, o.pos, o.size, '#f00');
|
|
2920
2972
|
continue;
|
|
2921
2973
|
}
|
|
2922
2974
|
|
|
@@ -2978,7 +3030,7 @@ class EngineObject
|
|
|
2978
3030
|
else // bounce if other object is fixed
|
|
2979
3031
|
this.velocity.x *= -restitution;
|
|
2980
3032
|
}
|
|
2981
|
-
|
|
3033
|
+
debugPhysics && debugOverlap(this.pos, this.size, o.pos, o.size, '#f0f');
|
|
2982
3034
|
}
|
|
2983
3035
|
}
|
|
2984
3036
|
if (this.collideTiles)
|
|
@@ -3023,7 +3075,7 @@ class EngineObject
|
|
|
3023
3075
|
this.pos.x = oldPos.x;
|
|
3024
3076
|
this.velocity.x *= -this.restitution;
|
|
3025
3077
|
}
|
|
3026
|
-
|
|
3078
|
+
debugPhysics && debugRect(this.pos, this.size, '#f00');
|
|
3027
3079
|
}
|
|
3028
3080
|
}
|
|
3029
3081
|
}
|
|
@@ -3225,6 +3277,16 @@ let drawCanvas;
|
|
|
3225
3277
|
* @memberof Draw */
|
|
3226
3278
|
let drawContext;
|
|
3227
3279
|
|
|
3280
|
+
/** Offscreen canvas that can be used for image processing
|
|
3281
|
+
* @type {OffscreenCanvas}
|
|
3282
|
+
* @memberof Draw */
|
|
3283
|
+
let workCanvas;
|
|
3284
|
+
|
|
3285
|
+
/** Offscreen canvas that can be used for image processing
|
|
3286
|
+
* @type {OffscreenCanvasRenderingContext2D}
|
|
3287
|
+
* @memberof Draw */
|
|
3288
|
+
let workContext;
|
|
3289
|
+
|
|
3228
3290
|
/** The size of the main canvas (and other secondary canvases)
|
|
3229
3291
|
* @type {Vector2}
|
|
3230
3292
|
* @memberof Draw */
|
|
@@ -3244,10 +3306,10 @@ let drawCount;
|
|
|
3244
3306
|
* Create a tile info object using a grid based system
|
|
3245
3307
|
* - This can take vecs or floats for easier use and conversion
|
|
3246
3308
|
* - If an index is passed in, the tile size and index will determine the position
|
|
3247
|
-
* @param {Vector2|number} [pos=0]
|
|
3309
|
+
* @param {Vector2|number} [pos=0] - Index of tile in sheet
|
|
3248
3310
|
* @param {Vector2|number} [size=tileSizeDefault] - Size of tile in pixels
|
|
3249
|
-
* @param {number} [textureIndex]
|
|
3250
|
-
* @param {number} [padding]
|
|
3311
|
+
* @param {number} [textureIndex] - Texture index to use
|
|
3312
|
+
* @param {number} [padding] - How many pixels padding around tiles
|
|
3251
3313
|
* @return {TileInfo}
|
|
3252
3314
|
* @example
|
|
3253
3315
|
* tile(2) // a tile at index 2 using the default tile size of 16
|
|
@@ -3374,55 +3436,7 @@ class TextureInfo
|
|
|
3374
3436
|
}
|
|
3375
3437
|
|
|
3376
3438
|
///////////////////////////////////////////////////////////////////////////////
|
|
3377
|
-
|
|
3378
|
-
/** Convert from screen to world space coordinates
|
|
3379
|
-
* @param {Vector2} screenPos
|
|
3380
|
-
* @return {Vector2}
|
|
3381
|
-
* @memberof Draw */
|
|
3382
|
-
function screenToWorld(screenPos)
|
|
3383
|
-
{
|
|
3384
|
-
let cameraPosRelativeX = (screenPos.x - mainCanvasSize.x/2 + .5) / cameraScale;
|
|
3385
|
-
let cameraPosRelativeY = (screenPos.y - mainCanvasSize.y/2 + .5) / -cameraScale;
|
|
3386
|
-
if (cameraAngle)
|
|
3387
|
-
{
|
|
3388
|
-
// apply camera rotation
|
|
3389
|
-
const cos = Math.cos(-cameraAngle), sin = Math.sin(-cameraAngle);
|
|
3390
|
-
const rotatedX = cameraPosRelativeX * cos - cameraPosRelativeY * sin;
|
|
3391
|
-
const rotatedY = cameraPosRelativeX * sin + cameraPosRelativeY * cos;
|
|
3392
|
-
cameraPosRelativeX = rotatedX;
|
|
3393
|
-
cameraPosRelativeY = rotatedY;
|
|
3394
|
-
}
|
|
3395
|
-
return new Vector2(cameraPosRelativeX + cameraPos.x, cameraPosRelativeY + cameraPos.y);
|
|
3396
|
-
}
|
|
3397
|
-
|
|
3398
|
-
/** Convert from world to screen space coordinates
|
|
3399
|
-
* @param {Vector2} worldPos
|
|
3400
|
-
* @return {Vector2}
|
|
3401
|
-
* @memberof Draw */
|
|
3402
|
-
function worldToScreen(worldPos)
|
|
3403
|
-
{
|
|
3404
|
-
let cameraPosRelativeX = worldPos.x - cameraPos.x;
|
|
3405
|
-
let cameraPosRelativeY = worldPos.y - cameraPos.y;
|
|
3406
|
-
if (cameraAngle)
|
|
3407
|
-
{
|
|
3408
|
-
// apply inverse camera rotation
|
|
3409
|
-
const cos = Math.cos(cameraAngle), sin = Math.sin(cameraAngle);
|
|
3410
|
-
const rotatedX = cameraPosRelativeX * cos - cameraPosRelativeY * sin;
|
|
3411
|
-
const rotatedY = cameraPosRelativeX * sin + cameraPosRelativeY * cos;
|
|
3412
|
-
cameraPosRelativeX = rotatedX;
|
|
3413
|
-
cameraPosRelativeY = rotatedY;
|
|
3414
|
-
}
|
|
3415
|
-
return new Vector2
|
|
3416
|
-
(
|
|
3417
|
-
cameraPosRelativeX * cameraScale + mainCanvasSize.x/2 - .5,
|
|
3418
|
-
cameraPosRelativeY * -cameraScale + mainCanvasSize.y/2 - .5
|
|
3419
|
-
);
|
|
3420
|
-
}
|
|
3421
|
-
|
|
3422
|
-
/** Get the camera's visible area in world space
|
|
3423
|
-
* @return {Vector2}
|
|
3424
|
-
* @memberof Draw */
|
|
3425
|
-
function getCameraSize() { return mainCanvasSize.scale(1/cameraScale); }
|
|
3439
|
+
// Drawing functions
|
|
3426
3440
|
|
|
3427
3441
|
/** Draw textured tile centered in world space, with color applied if using WebGL
|
|
3428
3442
|
* @param {Vector2} pos - Center of the tile in world space
|
|
@@ -3445,6 +3459,9 @@ function drawTile(pos, size=new Vector2(1), tileInfo, color=new Color,
|
|
|
3445
3459
|
ASSERT(isColor(color) && (!additiveColor || isColor(additiveColor)), 'drawTile color is invalid');
|
|
3446
3460
|
ASSERT(isNumber(angle), 'drawTile angle should be a number');
|
|
3447
3461
|
|
|
3462
|
+
if (color.a <= 0 && (!additiveColor || additiveColor.a <= 0) || !size.x || !size.y)
|
|
3463
|
+
return; // completely invisible, skip render
|
|
3464
|
+
|
|
3448
3465
|
const textureInfo = tileInfo && tileInfo.textureInfo;
|
|
3449
3466
|
if (useWebGL)
|
|
3450
3467
|
{
|
|
@@ -3495,18 +3512,15 @@ function drawTile(pos, size=new Vector2(1), tileInfo, color=new Color,
|
|
|
3495
3512
|
if (textureInfo)
|
|
3496
3513
|
{
|
|
3497
3514
|
// calculate uvs and render
|
|
3498
|
-
const x = tileInfo.pos.x
|
|
3499
|
-
const
|
|
3500
|
-
|
|
3501
|
-
const h = tileInfo.size.y - 2*tileFixBleedScale;
|
|
3502
|
-
context.globalAlpha = color.a; // only alpha is supported
|
|
3503
|
-
context.drawImage(textureInfo.image, x, y, w, h, -.5, -.5, 1, 1);
|
|
3504
|
-
context.globalAlpha = 1; // set back to full alpha
|
|
3515
|
+
const x = tileInfo.pos.x, y = tileInfo.pos.y;
|
|
3516
|
+
const w = tileInfo.size.x, h = tileInfo.size.y;
|
|
3517
|
+
drawImageColor(context, textureInfo.image, x, y, w, h, -.5, -.5, 1, 1, color, additiveColor);
|
|
3505
3518
|
}
|
|
3506
3519
|
else
|
|
3507
3520
|
{
|
|
3508
|
-
// if no tile info,
|
|
3509
|
-
|
|
3521
|
+
// if no tile info, use untextured rect
|
|
3522
|
+
const c = additiveColor ? color.add(additiveColor) : color;
|
|
3523
|
+
context.fillStyle = c.toString();
|
|
3510
3524
|
context.fillRect(-.5, -.5, 1, 1);
|
|
3511
3525
|
}
|
|
3512
3526
|
}, screenSpace, context);
|
|
@@ -3519,7 +3533,7 @@ function drawTile(pos, size=new Vector2(1), tileInfo, color=new Color,
|
|
|
3519
3533
|
* @param {Color} [color=(1,1,1,1)]
|
|
3520
3534
|
* @param {number} [angle]
|
|
3521
3535
|
* @param {boolean} [useWebGL=glEnable]
|
|
3522
|
-
* @param {boolean} [screenSpace
|
|
3536
|
+
* @param {boolean} [screenSpace]
|
|
3523
3537
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
|
|
3524
3538
|
* @memberof Draw */
|
|
3525
3539
|
function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
|
|
@@ -3532,73 +3546,90 @@ function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
|
|
|
3532
3546
|
* @param {Vector2} posB
|
|
3533
3547
|
* @param {number} [thickness]
|
|
3534
3548
|
* @param {Color} [color=(1,1,1,1)]
|
|
3549
|
+
* @param {Vector2} [pos=(0,0)] - Offset to apply
|
|
3550
|
+
* @param {number} [angle] - Angle to rotate by
|
|
3535
3551
|
* @param {boolean} [useWebGL=glEnable]
|
|
3536
|
-
* @param {boolean} [screenSpace
|
|
3552
|
+
* @param {boolean} [screenSpace]
|
|
3537
3553
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
|
|
3538
3554
|
* @memberof Draw */
|
|
3539
|
-
function drawLine(posA, posB, thickness=.1, color, useWebGL, screenSpace, context)
|
|
3555
|
+
function drawLine(posA, posB, thickness=.1, color, pos=vec2(), angle=0, useWebGL, screenSpace, context)
|
|
3540
3556
|
{
|
|
3541
3557
|
const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2);
|
|
3542
3558
|
const size = vec2(thickness, halfDelta.length()*2);
|
|
3543
|
-
|
|
3559
|
+
pos = pos.add(posA.add(halfDelta));
|
|
3560
|
+
angle += halfDelta.angle();
|
|
3561
|
+
drawRect(pos, size, color, angle, useWebGL, screenSpace, context);
|
|
3544
3562
|
}
|
|
3545
3563
|
|
|
3546
3564
|
/** Draw colored polygon using passed in points
|
|
3547
|
-
* @param {Array<Vector2>}
|
|
3565
|
+
* @param {Array<Vector2>} points - Array of Vector2 points
|
|
3548
3566
|
* @param {Color} [color=(1,1,1,1)]
|
|
3549
|
-
* @param {number} [lineWidth
|
|
3567
|
+
* @param {number} [lineWidth]
|
|
3550
3568
|
* @param {Color} [lineColor=(0,0,0,1)]
|
|
3551
|
-
* @param {
|
|
3569
|
+
* @param {Vector2} [pos=(0,0)] - Offset to apply
|
|
3570
|
+
* @param {number} [angle] - Angle to rotate by
|
|
3571
|
+
* @param {boolean} [useWebGL] - Webgl not supported
|
|
3572
|
+
* @param {boolean} [screenSpace]
|
|
3552
3573
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
3553
3574
|
* @memberof Draw */
|
|
3554
|
-
function drawPoly(points, color=new Color, lineWidth=0, lineColor=
|
|
3575
|
+
function drawPoly(points, color=new Color, lineWidth=0, lineColor=BLACK, pos=vec2(), angle=0, useWebGL=false, screenSpace=false, context=drawContext)
|
|
3555
3576
|
{
|
|
3556
|
-
ASSERT(
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
if (lineWidth)
|
|
3577
|
+
ASSERT(isVector2(pos) && pos.isValid(), 'drawPoly pos should be a vec2');
|
|
3578
|
+
ASSERT(Array.isArray(points), 'drawPoly points should be an array');
|
|
3579
|
+
ASSERT(isColor(color) && isColor(lineColor), 'drawPoly color is invalid');
|
|
3580
|
+
ASSERT(isNumber(lineWidth), 'drawPoly lineWidth should be a number');
|
|
3581
|
+
ASSERT(isNumber(angle), 'drawPoly angle should be a number');
|
|
3582
|
+
ASSERT(!useWebGL, 'drawPoly webgl not supported');
|
|
3583
|
+
drawCanvas2D(pos, vec2(1), angle, false, context=>
|
|
3564
3584
|
{
|
|
3565
|
-
context.
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3585
|
+
context.beginPath();
|
|
3586
|
+
for (const point of points)
|
|
3587
|
+
context.lineTo(point.x, point.y);
|
|
3588
|
+
context.closePath();
|
|
3589
|
+
context.fillStyle = color.toString();
|
|
3590
|
+
context.fill();
|
|
3591
|
+
if (lineWidth)
|
|
3592
|
+
{
|
|
3593
|
+
context.strokeStyle = lineColor.toString();
|
|
3594
|
+
context.lineWidth = lineWidth;
|
|
3595
|
+
context.stroke();
|
|
3596
|
+
}
|
|
3597
|
+
}, screenSpace, context);
|
|
3569
3598
|
}
|
|
3570
3599
|
|
|
3571
3600
|
/** Draw colored ellipse using passed in point
|
|
3572
3601
|
* @param {Vector2} pos
|
|
3573
|
-
* @param {
|
|
3574
|
-
* @param {number} [height=1]
|
|
3575
|
-
* @param {number} [angle=0]
|
|
3602
|
+
* @param {Vector2} [size=(1,1)]
|
|
3576
3603
|
* @param {Color} [color=(1,1,1,1)]
|
|
3577
|
-
* @param {number} [
|
|
3604
|
+
* @param {number} [angle]
|
|
3605
|
+
* @param {number} [lineWidth]
|
|
3578
3606
|
* @param {Color} [lineColor=(0,0,0,1)]
|
|
3579
|
-
* @param {boolean} [
|
|
3607
|
+
* @param {boolean} [useWebGL] - Webgl not supported
|
|
3608
|
+
* @param {boolean} [screenSpace]
|
|
3580
3609
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
3581
3610
|
* @memberof Draw */
|
|
3582
|
-
function drawEllipse(pos,
|
|
3611
|
+
function drawEllipse(pos, size=vec2(1), color=new Color, angle=0, lineWidth=0, lineColor=BLACK, useWebGL=false, screenSpace=false, context=drawContext)
|
|
3583
3612
|
{
|
|
3584
|
-
ASSERT(
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
context.fillStyle = color.toString();
|
|
3593
|
-
context.beginPath();
|
|
3594
|
-
context.ellipse(pos.x, pos.y, width, height, angle, 0, 9);
|
|
3595
|
-
context.fill();
|
|
3596
|
-
if (lineWidth)
|
|
3613
|
+
ASSERT(isVector2(pos) && pos.isValid(), 'drawEllipse pos should be a vec2');
|
|
3614
|
+
ASSERT(isVector2(size) && size.isValid(), 'drawEllipse size should be a vec2');
|
|
3615
|
+
ASSERT(isColor(color) && isColor(lineColor), 'drawEllipse color is invalid');
|
|
3616
|
+
ASSERT(isNumber(angle), 'drawEllipse angle should be a number');
|
|
3617
|
+
ASSERT(isNumber(lineWidth), 'drawEllipse lineWidth should be a number');
|
|
3618
|
+
ASSERT(lineWidth>=0 && lineWidth < size.x && lineWidth < size.y, 'drawEllipse invalid lineWidth');
|
|
3619
|
+
ASSERT(!useWebGL, 'drawEllipse webgl not supported');
|
|
3620
|
+
drawCanvas2D(pos, vec2(1), angle, false, context=>
|
|
3597
3621
|
{
|
|
3598
|
-
context.
|
|
3599
|
-
context.
|
|
3600
|
-
context.
|
|
3601
|
-
|
|
3622
|
+
context.beginPath();
|
|
3623
|
+
context.ellipse(0, 0, size.y, size.x, 0, 0, 9);
|
|
3624
|
+
context.fillStyle = color.toString();
|
|
3625
|
+
context.fill();
|
|
3626
|
+
if (lineWidth)
|
|
3627
|
+
{
|
|
3628
|
+
context.strokeStyle = lineColor.toString();
|
|
3629
|
+
context.lineWidth = lineWidth;
|
|
3630
|
+
context.stroke();
|
|
3631
|
+
}
|
|
3632
|
+
}, screenSpace, context);
|
|
3602
3633
|
}
|
|
3603
3634
|
|
|
3604
3635
|
/** Draw colored circle using passed in point
|
|
@@ -3607,11 +3638,12 @@ function drawEllipse(pos, width=1, height=1, angle=0, color=new Color, lineWidth
|
|
|
3607
3638
|
* @param {Color} [color=(1,1,1,1)]
|
|
3608
3639
|
* @param {number} [lineWidth=0]
|
|
3609
3640
|
* @param {Color} [lineColor=(0,0,0,1)]
|
|
3641
|
+
* @param {boolean} [useWebGL] - Webgl not supported
|
|
3610
3642
|
* @param {boolean} [screenSpace=false]
|
|
3611
3643
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
3612
3644
|
* @memberof Draw */
|
|
3613
|
-
function drawCircle(pos, radius=1, color=new Color, lineWidth=0, lineColor=
|
|
3614
|
-
{ drawEllipse(pos, radius,
|
|
3645
|
+
function drawCircle(pos, radius=1, color=new Color, lineWidth=0, lineColor=BLACK, useWebGL=false, screenSpace, context=drawContext)
|
|
3646
|
+
{ drawEllipse(pos, vec2(radius), color, 0, lineWidth, lineColor, useWebGL, screenSpace, context); }
|
|
3615
3647
|
|
|
3616
3648
|
/** Draw directly to a 2d canvas context in world space
|
|
3617
3649
|
* @param {Vector2} pos
|
|
@@ -3638,6 +3670,9 @@ function drawCanvas2D(pos, size, angle=0, mirror=false, drawFunction, screenSpac
|
|
|
3638
3670
|
context.restore();
|
|
3639
3671
|
}
|
|
3640
3672
|
|
|
3673
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
3674
|
+
// Text Drawing Functions
|
|
3675
|
+
|
|
3641
3676
|
/** Draw text on main canvas in world space
|
|
3642
3677
|
* Automatically splits new lines into rows
|
|
3643
3678
|
* @param {string} text
|
|
@@ -3686,7 +3721,7 @@ function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textA
|
|
|
3686
3721
|
* @param {number} [maxWidth]
|
|
3687
3722
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=overlayContext]
|
|
3688
3723
|
* @memberof Draw */
|
|
3689
|
-
function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=
|
|
3724
|
+
function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=BLACK, textAlign='center', font=fontDefault, maxWidth=undefined, context=overlayContext)
|
|
3690
3725
|
{
|
|
3691
3726
|
context.fillStyle = color.toString();
|
|
3692
3727
|
context.strokeStyle = lineColor.toString();
|
|
@@ -3707,22 +3742,67 @@ function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineCol
|
|
|
3707
3742
|
});
|
|
3708
3743
|
}
|
|
3709
3744
|
|
|
3745
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
3746
|
+
// Drawing utilities
|
|
3747
|
+
|
|
3748
|
+
/** Convert from screen to world space coordinates
|
|
3749
|
+
* @param {Vector2} screenPos
|
|
3750
|
+
* @return {Vector2}
|
|
3751
|
+
* @memberof Draw */
|
|
3752
|
+
function screenToWorld(screenPos)
|
|
3753
|
+
{
|
|
3754
|
+
let cameraPosRelativeX = (screenPos.x - mainCanvasSize.x/2 + .5) / cameraScale;
|
|
3755
|
+
let cameraPosRelativeY = (screenPos.y - mainCanvasSize.y/2 + .5) / -cameraScale;
|
|
3756
|
+
if (cameraAngle)
|
|
3757
|
+
{
|
|
3758
|
+
// apply camera rotation
|
|
3759
|
+
const cos = Math.cos(-cameraAngle), sin = Math.sin(-cameraAngle);
|
|
3760
|
+
const rotatedX = cameraPosRelativeX * cos - cameraPosRelativeY * sin;
|
|
3761
|
+
const rotatedY = cameraPosRelativeX * sin + cameraPosRelativeY * cos;
|
|
3762
|
+
cameraPosRelativeX = rotatedX;
|
|
3763
|
+
cameraPosRelativeY = rotatedY;
|
|
3764
|
+
}
|
|
3765
|
+
return new Vector2(cameraPosRelativeX + cameraPos.x, cameraPosRelativeY + cameraPos.y);
|
|
3766
|
+
}
|
|
3767
|
+
|
|
3768
|
+
/** Convert from world to screen space coordinates
|
|
3769
|
+
* @param {Vector2} worldPos
|
|
3770
|
+
* @return {Vector2}
|
|
3771
|
+
* @memberof Draw */
|
|
3772
|
+
function worldToScreen(worldPos)
|
|
3773
|
+
{
|
|
3774
|
+
let cameraPosRelativeX = worldPos.x - cameraPos.x;
|
|
3775
|
+
let cameraPosRelativeY = worldPos.y - cameraPos.y;
|
|
3776
|
+
if (cameraAngle)
|
|
3777
|
+
{
|
|
3778
|
+
// apply inverse camera rotation
|
|
3779
|
+
const cos = Math.cos(cameraAngle), sin = Math.sin(cameraAngle);
|
|
3780
|
+
const rotatedX = cameraPosRelativeX * cos - cameraPosRelativeY * sin;
|
|
3781
|
+
const rotatedY = cameraPosRelativeX * sin + cameraPosRelativeY * cos;
|
|
3782
|
+
cameraPosRelativeX = rotatedX;
|
|
3783
|
+
cameraPosRelativeY = rotatedY;
|
|
3784
|
+
}
|
|
3785
|
+
return new Vector2
|
|
3786
|
+
(
|
|
3787
|
+
cameraPosRelativeX * cameraScale + mainCanvasSize.x/2 - .5,
|
|
3788
|
+
cameraPosRelativeY * -cameraScale + mainCanvasSize.y/2 - .5
|
|
3789
|
+
);
|
|
3790
|
+
}
|
|
3791
|
+
|
|
3792
|
+
/** Get the camera's visible area in world space
|
|
3793
|
+
* @return {Vector2}
|
|
3794
|
+
* @memberof Draw */
|
|
3795
|
+
function getCameraSize() { return mainCanvasSize.scale(1/cameraScale); }
|
|
3796
|
+
|
|
3710
3797
|
/** Enable normal or additive blend mode
|
|
3711
3798
|
* @param {boolean} [additive]
|
|
3712
|
-
* @param {boolean} [useWebGL=glEnable]
|
|
3713
3799
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
|
|
3714
3800
|
* @memberof Draw */
|
|
3715
|
-
function setBlendMode(additive=false,
|
|
3801
|
+
function setBlendMode(additive=false, context)
|
|
3716
3802
|
{
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
else
|
|
3721
|
-
{
|
|
3722
|
-
if (!context)
|
|
3723
|
-
context = drawContext;
|
|
3724
|
-
context.globalCompositeOperation = additive ? 'lighter' : 'source-over';
|
|
3725
|
-
}
|
|
3803
|
+
glAdditive = additive;
|
|
3804
|
+
context ||= drawContext;
|
|
3805
|
+
context.globalCompositeOperation = additive ? 'lighter' : 'source-over';
|
|
3726
3806
|
}
|
|
3727
3807
|
|
|
3728
3808
|
/** Combines all LittleJS canvases onto the main canvas and clears them
|
|
@@ -3739,6 +3819,102 @@ function combineCanvases()
|
|
|
3739
3819
|
overlayCanvas.width |= 0;
|
|
3740
3820
|
}
|
|
3741
3821
|
|
|
3822
|
+
/** Helper function to draw an image with color and additive color applied
|
|
3823
|
+
* This is slower then normal drawImage when color is applied
|
|
3824
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} context
|
|
3825
|
+
* @param {HTMLImageElement|OffscreenCanvas} image
|
|
3826
|
+
* @param {number} sx
|
|
3827
|
+
* @param {number} sy
|
|
3828
|
+
* @param {number} sWidth
|
|
3829
|
+
* @param {number} sHeight
|
|
3830
|
+
* @param {number} dx
|
|
3831
|
+
* @param {number} dy
|
|
3832
|
+
* @param {number} dWidth
|
|
3833
|
+
* @param {number} dHeight
|
|
3834
|
+
* @param {Color} color
|
|
3835
|
+
* @param {Color} [additiveColor]
|
|
3836
|
+
* @memberof Draw */
|
|
3837
|
+
function drawImageColor(context, image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight, color, additiveColor)
|
|
3838
|
+
{
|
|
3839
|
+
function isWhite(c) { return c.r >= 1 && c.g >= 1 && c.b >= 1; }
|
|
3840
|
+
function isBlack(c) { return c.r <= 0 && c.g <= 0 && c.b <= 0 && c.a <= 0; }
|
|
3841
|
+
const sx2 = tileFixBleedScale;
|
|
3842
|
+
const sy2 = tileFixBleedScale;
|
|
3843
|
+
const sWidth2 = sWidth - 2*tileFixBleedScale;
|
|
3844
|
+
const sHeight2 = sHeight - 2*tileFixBleedScale;
|
|
3845
|
+
if (!canvasColorTiles || (additiveColor ? isWhite(color.add(additiveColor)) && additiveColor.a <= 0 : isWhite(color)))
|
|
3846
|
+
{
|
|
3847
|
+
// white texture with no additive alpha, no need to tint
|
|
3848
|
+
context.globalAlpha = color.a;
|
|
3849
|
+
context.drawImage(image, sx+sx2, sy+sy2, sWidth2, sHeight2, dx, dy, dWidth, dHeight);
|
|
3850
|
+
context.globalAlpha = 1;
|
|
3851
|
+
}
|
|
3852
|
+
else
|
|
3853
|
+
{
|
|
3854
|
+
// copy to offscreen canvas
|
|
3855
|
+
workCanvas.width = sWidth;
|
|
3856
|
+
workCanvas.height = sHeight;
|
|
3857
|
+
workContext.drawImage(image, sx, sy, sWidth, sHeight, 0, 0, sWidth, sHeight);
|
|
3858
|
+
|
|
3859
|
+
// tint image using offscreen work context
|
|
3860
|
+
const imageData = workContext.getImageData(0, 0, sWidth, sHeight);
|
|
3861
|
+
const data = imageData.data;
|
|
3862
|
+
if (additiveColor && !isBlack(additiveColor))
|
|
3863
|
+
{
|
|
3864
|
+
// slower path with additive color
|
|
3865
|
+
const colorMultiply = [color.r, color.g, color.b, color.a];
|
|
3866
|
+
const colorAdd = [additiveColor.r * 255, additiveColor.g * 255, additiveColor.b * 255, additiveColor.a * 255];
|
|
3867
|
+
for (let i = 0; i < data.length; ++i)
|
|
3868
|
+
data[i] = data[i] * colorMultiply[i&3] + colorAdd[i&3] |0;
|
|
3869
|
+
workContext.putImageData(imageData, 0, 0);
|
|
3870
|
+
context.drawImage(workCanvas, sx2, sy2, sWidth2, sHeight2, dx, dy, dWidth, dHeight);
|
|
3871
|
+
}
|
|
3872
|
+
else
|
|
3873
|
+
{
|
|
3874
|
+
// faster path with no additive color
|
|
3875
|
+
for (let i = 0; i < data.length; i+=4)
|
|
3876
|
+
{
|
|
3877
|
+
data[i ] *= color.r;
|
|
3878
|
+
data[i+1] *= color.g;
|
|
3879
|
+
data[i+2] *= color.b;
|
|
3880
|
+
}
|
|
3881
|
+
workContext.putImageData(imageData, 0, 0);
|
|
3882
|
+
context.globalAlpha = color.a;
|
|
3883
|
+
context.drawImage(workCanvas, sx2, sy2, sWidth2, sHeight2, dx, dy, dWidth, dHeight);
|
|
3884
|
+
context.globalAlpha = 1;
|
|
3885
|
+
}
|
|
3886
|
+
}
|
|
3887
|
+
}
|
|
3888
|
+
|
|
3889
|
+
|
|
3890
|
+
/** Returns true if fullscreen mode is active
|
|
3891
|
+
* @return {boolean}
|
|
3892
|
+
* @memberof Draw */
|
|
3893
|
+
function isFullscreen() { return !!document.fullscreenElement; }
|
|
3894
|
+
|
|
3895
|
+
/** Toggle fullscreen mode
|
|
3896
|
+
* @memberof Draw */
|
|
3897
|
+
function toggleFullscreen()
|
|
3898
|
+
{
|
|
3899
|
+
const rootElement = mainCanvas.parentElement;
|
|
3900
|
+
if (isFullscreen())
|
|
3901
|
+
{
|
|
3902
|
+
if (document.exitFullscreen)
|
|
3903
|
+
document.exitFullscreen();
|
|
3904
|
+
}
|
|
3905
|
+
else if (rootElement.requestFullscreen)
|
|
3906
|
+
rootElement.requestFullscreen();
|
|
3907
|
+
}
|
|
3908
|
+
|
|
3909
|
+
/** Set the cursor style
|
|
3910
|
+
* @param {string} cursorStyle - CSS cursor style (auto, none, crosshair, etc)
|
|
3911
|
+
* @memberof Draw */
|
|
3912
|
+
function setCursor(cursorStyle = 'auto')
|
|
3913
|
+
{
|
|
3914
|
+
const rootElement = mainCanvas.parentElement;
|
|
3915
|
+
rootElement.style.cursor = cursorStyle;
|
|
3916
|
+
}
|
|
3917
|
+
|
|
3742
3918
|
///////////////////////////////////////////////////////////////////////////////
|
|
3743
3919
|
|
|
3744
3920
|
let engineFontImage;
|
|
@@ -3806,7 +3982,7 @@ class FontImage
|
|
|
3806
3982
|
(text+'').split('\n').forEach((line, i)=>
|
|
3807
3983
|
{
|
|
3808
3984
|
const centerOffset = center ? line.length * size.x * scale / 2 |0 : 0;
|
|
3809
|
-
for(let j=line.length; j--;)
|
|
3985
|
+
for (let j=line.length; j--;)
|
|
3810
3986
|
{
|
|
3811
3987
|
// draw each character
|
|
3812
3988
|
let charCode = line[j].charCodeAt(0);
|
|
@@ -3825,37 +4001,6 @@ class FontImage
|
|
|
3825
4001
|
|
|
3826
4002
|
context.restore();
|
|
3827
4003
|
}
|
|
3828
|
-
}
|
|
3829
|
-
|
|
3830
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
3831
|
-
// Display functions
|
|
3832
|
-
|
|
3833
|
-
/** Returns true if fullscreen mode is active
|
|
3834
|
-
* @return {boolean}
|
|
3835
|
-
* @memberof Draw */
|
|
3836
|
-
function isFullscreen() { return !!document.fullscreenElement; }
|
|
3837
|
-
|
|
3838
|
-
/** Toggle fullscreen mode
|
|
3839
|
-
* @memberof Draw */
|
|
3840
|
-
function toggleFullscreen()
|
|
3841
|
-
{
|
|
3842
|
-
const rootElement = mainCanvas.parentElement;
|
|
3843
|
-
if (isFullscreen())
|
|
3844
|
-
{
|
|
3845
|
-
if (document.exitFullscreen)
|
|
3846
|
-
document.exitFullscreen();
|
|
3847
|
-
}
|
|
3848
|
-
else if (rootElement.requestFullscreen)
|
|
3849
|
-
rootElement.requestFullscreen();
|
|
3850
|
-
}
|
|
3851
|
-
|
|
3852
|
-
/** Set the cursor style
|
|
3853
|
-
* @param {string} cursorStyle - CSS cursor style (auto, none, crosshair, etc)
|
|
3854
|
-
* @memberof Draw */
|
|
3855
|
-
function setCursor(cursorStyle = 'auto')
|
|
3856
|
-
{
|
|
3857
|
-
const rootElement = mainCanvas.parentElement;
|
|
3858
|
-
rootElement.style.cursor = cursorStyle;
|
|
3859
4004
|
}
|
|
3860
4005
|
/**
|
|
3861
4006
|
* LittleJS Input System
|
|
@@ -4407,7 +4552,7 @@ function touchGamepadRender()
|
|
|
4407
4552
|
}
|
|
4408
4553
|
else // draw cross shaped gamepad
|
|
4409
4554
|
{
|
|
4410
|
-
for(let i=10; i--;)
|
|
4555
|
+
for (let i=10; i--;)
|
|
4411
4556
|
{
|
|
4412
4557
|
const angle = i*PI/4;
|
|
4413
4558
|
context.arc(leftCenter.x, leftCenter.y,touchGamepadSize*.6, angle + PI/8, angle + PI/8);
|
|
@@ -4451,7 +4596,7 @@ function pointerLockExit() { document.exitPointerLock && document.exitPointerLoc
|
|
|
4451
4596
|
/** Check if pointer is locked (true if locked)
|
|
4452
4597
|
* @return {boolean}
|
|
4453
4598
|
* @memberof Input */
|
|
4454
|
-
function pointerLockIsActive() { return document.pointerLockElement == mainCanvas }
|
|
4599
|
+
function pointerLockIsActive() { return document.pointerLockElement == mainCanvas; }
|
|
4455
4600
|
/**
|
|
4456
4601
|
* LittleJS Audio System
|
|
4457
4602
|
* - <a href=https://killedbyapixel.github.io/ZzFX/>ZzFX Sound Effects</a> - ZzFX Sound Effect Generator
|
|
@@ -4875,7 +5020,7 @@ function zzfxG
|
|
|
4875
5020
|
repeatTime = repeatTime * sampleRate | 0;
|
|
4876
5021
|
|
|
4877
5022
|
// generate waveform
|
|
4878
|
-
for(length = attack + decay + sustain + release + delay | 0;
|
|
5023
|
+
for (length = attack + decay + sustain + release + delay | 0;
|
|
4879
5024
|
i < length; b[i++] = s * volume) // sample
|
|
4880
5025
|
{
|
|
4881
5026
|
if (!(++crush%(bitCrush*100|0))) // bit crush
|
|
@@ -5768,10 +5913,17 @@ class ParticleEmitter extends EngineObject
|
|
|
5768
5913
|
const particle = new Particle(pos, this.tileInfo, angle, colorStart, colorEnd, particleTime, sizeStart, sizeEnd, this.fadeRate, this.additive, this.trailScale, this.localSpace && this, this.particleDestroyCallback);
|
|
5769
5914
|
particle.velocity = vec2().setAngle(velocityAngle, speed);
|
|
5770
5915
|
particle.angleVelocity = angleSpeed;
|
|
5916
|
+
if (!this.localSpace)
|
|
5917
|
+
{
|
|
5918
|
+
// apply emitter velocity to particle
|
|
5919
|
+
particle.velocity.x += this.velocity.x;
|
|
5920
|
+
particle.velocity.y += this.velocity.y;
|
|
5921
|
+
particle.angleVelocity += this.angleVelocity;
|
|
5922
|
+
}
|
|
5771
5923
|
particle.fadeRate = this.fadeRate;
|
|
5772
5924
|
particle.damping = this.damping;
|
|
5773
5925
|
particle.angleDamping = this.angleDamping;
|
|
5774
|
-
particle.restitution
|
|
5926
|
+
particle.restitution = this.restitution;
|
|
5775
5927
|
particle.friction = this.friction;
|
|
5776
5928
|
particle.gravityScale = this.gravityScale;
|
|
5777
5929
|
particle.collideTiles = this.collideTiles;
|
|
@@ -6058,7 +6210,7 @@ class Medal
|
|
|
6058
6210
|
context.save();
|
|
6059
6211
|
context.beginPath();
|
|
6060
6212
|
context.fillStyle = new Color(.9,.9,.9).toString();
|
|
6061
|
-
context.strokeStyle =
|
|
6213
|
+
context.strokeStyle = BLACK.toString();
|
|
6062
6214
|
context.lineWidth = 3;
|
|
6063
6215
|
context.rect(x, y, width, height);
|
|
6064
6216
|
context.fill();
|
|
@@ -6075,11 +6227,11 @@ class Medal
|
|
|
6075
6227
|
const descriptionSize = height*.3;
|
|
6076
6228
|
const pos = vec2(x + medalDisplayIconSize + 2*gap.x, y + gap.y*2 + nameSize/2);
|
|
6077
6229
|
const textWidth = width - medalDisplayIconSize - 3*gap.x;
|
|
6078
|
-
drawTextScreen(this.name, pos, nameSize,
|
|
6230
|
+
drawTextScreen(this.name, pos, nameSize, BLACK, 0, undefined, 'left', undefined, textWidth);
|
|
6079
6231
|
|
|
6080
6232
|
// draw the description
|
|
6081
6233
|
pos.y = y + height - gap.y*2 - descriptionSize/2;
|
|
6082
|
-
drawTextScreen(this.description, pos, descriptionSize,
|
|
6234
|
+
drawTextScreen(this.description, pos, descriptionSize, BLACK, 0, undefined, 'left', undefined, textWidth);
|
|
6083
6235
|
context.restore();
|
|
6084
6236
|
}
|
|
6085
6237
|
|
|
@@ -6093,7 +6245,7 @@ class Medal
|
|
|
6093
6245
|
if (this.image)
|
|
6094
6246
|
overlayContext.drawImage(this.image, pos.x-size/2, pos.y-size/2, size, size);
|
|
6095
6247
|
else
|
|
6096
|
-
drawTextScreen(this.icon, pos, size*.7,
|
|
6248
|
+
drawTextScreen(this.icon, pos, size*.7, BLACK);
|
|
6097
6249
|
}
|
|
6098
6250
|
|
|
6099
6251
|
// Get local storage key used by the medal
|
|
@@ -6102,12 +6254,13 @@ class Medal
|
|
|
6102
6254
|
/**
|
|
6103
6255
|
* LittleJS WebGL Interface
|
|
6104
6256
|
* - All webgl used by the engine is wrapped up here
|
|
6257
|
+
* - Will fall back to 2D canvas rendering if webgl is not supported
|
|
6105
6258
|
* - For normal stuff you won't need to see or call anything in this file
|
|
6106
6259
|
* - For advanced stuff there are helper functions to create shaders, textures, etc
|
|
6107
6260
|
* - Can be disabled with glEnable to revert to 2D canvas rendering
|
|
6108
6261
|
* - Batches sprite rendering on GPU for incredibly fast performance
|
|
6109
6262
|
* - Sprite transform math is done in the shader where possible
|
|
6110
|
-
* - Supports shadertoy style post processing shaders
|
|
6263
|
+
* - Supports shadertoy style post processing shaders via plugin
|
|
6111
6264
|
* @namespace WebGL
|
|
6112
6265
|
*/
|
|
6113
6266
|
|
|
@@ -6146,6 +6299,14 @@ function glInit()
|
|
|
6146
6299
|
glCanvas = document.createElement('canvas');
|
|
6147
6300
|
glContext = glCanvas.getContext('webgl2', {antialias:glAntialias});
|
|
6148
6301
|
|
|
6302
|
+
if (!glContext)
|
|
6303
|
+
{
|
|
6304
|
+
console.warn('WebGL2 not supported, falling back to 2D canvas rendering!');
|
|
6305
|
+
glCanvas = glContext = undefined;
|
|
6306
|
+
glEnable = false;
|
|
6307
|
+
return;
|
|
6308
|
+
}
|
|
6309
|
+
|
|
6149
6310
|
// create the webgl canvas
|
|
6150
6311
|
const rootElement = mainCanvas.parentElement;
|
|
6151
6312
|
rootElement.appendChild(glCanvas);
|
|
@@ -6195,7 +6356,7 @@ function glInit()
|
|
|
6195
6356
|
// Also used by tile layer rendering when redrawing tiles
|
|
6196
6357
|
function glPreRender()
|
|
6197
6358
|
{
|
|
6198
|
-
if (!glEnable ||
|
|
6359
|
+
if (!glEnable || !glContext) return;
|
|
6199
6360
|
|
|
6200
6361
|
// set up the shader and canvas
|
|
6201
6362
|
glClearCanvas();
|
|
@@ -6246,6 +6407,8 @@ function glPreRender()
|
|
|
6246
6407
|
* @memberof WebGL */
|
|
6247
6408
|
function glClearCanvas()
|
|
6248
6409
|
{
|
|
6410
|
+
if (!glContext) return;
|
|
6411
|
+
|
|
6249
6412
|
// clear and set to same size as main canvas
|
|
6250
6413
|
glContext.viewport(0, 0, glCanvas.width=drawCanvas.width, glCanvas.height=drawCanvas.height);
|
|
6251
6414
|
glContext.clear(glContext.COLOR_BUFFER_BIT);
|
|
@@ -6259,7 +6422,7 @@ function glClearCanvas()
|
|
|
6259
6422
|
function glSetTexture(texture, wrap=false)
|
|
6260
6423
|
{
|
|
6261
6424
|
// must flush cache with the old texture to set a new one
|
|
6262
|
-
if (
|
|
6425
|
+
if (!glContext || texture == glActiveTexture)
|
|
6263
6426
|
return;
|
|
6264
6427
|
|
|
6265
6428
|
glFlush();
|
|
@@ -6278,6 +6441,8 @@ function glSetTexture(texture, wrap=false)
|
|
|
6278
6441
|
* @memberof WebGL */
|
|
6279
6442
|
function glCompileShader(source, type)
|
|
6280
6443
|
{
|
|
6444
|
+
if (!glContext) return;
|
|
6445
|
+
|
|
6281
6446
|
// build the shader
|
|
6282
6447
|
const shader = glContext.createShader(type);
|
|
6283
6448
|
glContext.shaderSource(shader, source);
|
|
@@ -6296,6 +6461,8 @@ function glCompileShader(source, type)
|
|
|
6296
6461
|
* @memberof WebGL */
|
|
6297
6462
|
function glCreateProgram(vsSource, fsSource)
|
|
6298
6463
|
{
|
|
6464
|
+
if (!glContext) return;
|
|
6465
|
+
|
|
6299
6466
|
// build the program
|
|
6300
6467
|
const program = glContext.createProgram();
|
|
6301
6468
|
glContext.attachShader(program, glCompileShader(vsSource, glContext.VERTEX_SHADER));
|
|
@@ -6314,6 +6481,8 @@ function glCreateProgram(vsSource, fsSource)
|
|
|
6314
6481
|
* @memberof WebGL */
|
|
6315
6482
|
function glCreateTexture(image)
|
|
6316
6483
|
{
|
|
6484
|
+
if (!glContext) return;
|
|
6485
|
+
|
|
6317
6486
|
// build the texture
|
|
6318
6487
|
const texture = glContext.createTexture();
|
|
6319
6488
|
glContext.bindTexture(glContext.TEXTURE_2D, texture);
|
|
@@ -6349,6 +6518,7 @@ function glCreateTexture(image)
|
|
|
6349
6518
|
* @memberof WebGL */
|
|
6350
6519
|
function glDeleteTexture(texture)
|
|
6351
6520
|
{
|
|
6521
|
+
if (!glContext) return;
|
|
6352
6522
|
glContext.deleteTexture(texture);
|
|
6353
6523
|
}
|
|
6354
6524
|
|
|
@@ -6358,6 +6528,8 @@ function glDeleteTexture(texture)
|
|
|
6358
6528
|
* @memberof WebGL */
|
|
6359
6529
|
function glSetTextureData(texture, image)
|
|
6360
6530
|
{
|
|
6531
|
+
if (!glContext) return;
|
|
6532
|
+
|
|
6361
6533
|
// build the texture
|
|
6362
6534
|
ASSERT(!!image && image.width > 0, 'Invalid image data.');
|
|
6363
6535
|
glContext.bindTexture(glContext.TEXTURE_2D, texture);
|
|
@@ -6368,7 +6540,7 @@ function glSetTextureData(texture, image)
|
|
|
6368
6540
|
* @memberof WebGL */
|
|
6369
6541
|
function glFlush()
|
|
6370
6542
|
{
|
|
6371
|
-
if (!glEnable || !glInstanceCount) return;
|
|
6543
|
+
if (!glEnable || !glContext || !glInstanceCount) return;
|
|
6372
6544
|
|
|
6373
6545
|
const destBlend = glBatchAdditive ? glContext.ONE : glContext.ONE_MINUS_SRC_ALPHA;
|
|
6374
6546
|
glContext.blendFuncSeparate(glContext.SRC_ALPHA, destBlend, glContext.ONE, destBlend);
|
|
@@ -6388,7 +6560,7 @@ function glFlush()
|
|
|
6388
6560
|
* @memberof WebGL */
|
|
6389
6561
|
function glCopyToContext(context)
|
|
6390
6562
|
{
|
|
6391
|
-
if (!glEnable)
|
|
6563
|
+
if (!glEnable || !glContext)
|
|
6392
6564
|
return;
|
|
6393
6565
|
|
|
6394
6566
|
glFlush();
|
|
@@ -6643,6 +6815,13 @@ class PostProcessPlugin
|
|
|
6643
6815
|
postProcess = this;
|
|
6644
6816
|
|
|
6645
6817
|
if (headlessMode) return;
|
|
6818
|
+
|
|
6819
|
+
if (!glEnable)
|
|
6820
|
+
{
|
|
6821
|
+
console.warn('PostProcessPlugin: WebGL not enabled!');
|
|
6822
|
+
return;
|
|
6823
|
+
}
|
|
6824
|
+
|
|
6646
6825
|
if (!shaderCode) // default shader pass through
|
|
6647
6826
|
shaderCode = 'void mainImage(out vec4 c,vec2 p){c=texture(iChannel0,p/iResolution.xy);}';
|
|
6648
6827
|
|
|
@@ -6952,7 +7131,7 @@ class UISystemPlugin
|
|
|
6952
7131
|
|
|
6953
7132
|
function updateInvisible(o)
|
|
6954
7133
|
{
|
|
6955
|
-
for(const c of o.children)
|
|
7134
|
+
for (const c of o.children)
|
|
6956
7135
|
updateInvisible(c);
|
|
6957
7136
|
o.updateInvisible();
|
|
6958
7137
|
}
|
|
@@ -6968,7 +7147,7 @@ class UISystemPlugin
|
|
|
6968
7147
|
if (o.parent)
|
|
6969
7148
|
o.pos = o.localPos.add(o.parent.pos);
|
|
6970
7149
|
// update in reverse order to detect mouse enter/leave
|
|
6971
|
-
for(let i=o.children.length; i--;)
|
|
7150
|
+
for (let i=o.children.length; i--;)
|
|
6972
7151
|
updateObject(o.children[i]);
|
|
6973
7152
|
o.update();
|
|
6974
7153
|
}
|
|
@@ -6986,7 +7165,7 @@ class UISystemPlugin
|
|
|
6986
7165
|
if (o.parent)
|
|
6987
7166
|
o.pos = o.localPos.add(o.parent.pos);
|
|
6988
7167
|
o.render();
|
|
6989
|
-
for(const c of o.children)
|
|
7168
|
+
for (const c of o.children)
|
|
6990
7169
|
renderObject(c);
|
|
6991
7170
|
}
|
|
6992
7171
|
uiSystem.uiObjects.forEach(o=> o.parent || renderObject(o));
|
|
@@ -7043,7 +7222,7 @@ class UISystemPlugin
|
|
|
7043
7222
|
* @param {boolean} [mirror] */
|
|
7044
7223
|
drawTile(pos, size, tileInfo, color=uiSystem.defaultColor, angle=0, mirror=false)
|
|
7045
7224
|
{
|
|
7046
|
-
drawTile(pos, size, tileInfo, color, angle, mirror,
|
|
7225
|
+
drawTile(pos, size, tileInfo, color, angle, mirror, CLEAR_BLACK, false, true, uiSystem.uiContext);
|
|
7047
7226
|
}
|
|
7048
7227
|
|
|
7049
7228
|
/** Draw text to the UI context
|
|
@@ -7493,7 +7672,7 @@ class Box2dObject extends EngineObject
|
|
|
7493
7672
|
bodyDef.set_angle(-angle);
|
|
7494
7673
|
this.body = box2d.world.CreateBody(bodyDef);
|
|
7495
7674
|
this.body.object = this;
|
|
7496
|
-
this.
|
|
7675
|
+
this.lineColor = BLACK;
|
|
7497
7676
|
}
|
|
7498
7677
|
|
|
7499
7678
|
/** Destroy this object and it's physics body */
|
|
@@ -7520,7 +7699,7 @@ class Box2dObject extends EngineObject
|
|
|
7520
7699
|
if (this.tileInfo)
|
|
7521
7700
|
super.render();
|
|
7522
7701
|
else
|
|
7523
|
-
this.drawFixtures(this.color, this.
|
|
7702
|
+
this.drawFixtures(this.color, this.lineColor, this.lineWidth, mainContext);
|
|
7524
7703
|
}
|
|
7525
7704
|
|
|
7526
7705
|
/** Render debug info */
|
|
@@ -7534,13 +7713,13 @@ class Box2dObject extends EngineObject
|
|
|
7534
7713
|
|
|
7535
7714
|
/** Draws all this object's fixtures
|
|
7536
7715
|
* @param {Color} [color]
|
|
7537
|
-
* @param {Color} [
|
|
7716
|
+
* @param {Color} [lineColor]
|
|
7538
7717
|
* @param {number} [lineWidth]
|
|
7539
7718
|
* @param {CanvasRenderingContext2D} [context] */
|
|
7540
|
-
drawFixtures(color=WHITE,
|
|
7719
|
+
drawFixtures(color=WHITE, lineColor, lineWidth=.1, context)
|
|
7541
7720
|
{
|
|
7542
7721
|
this.getFixtureList().forEach(fixture=>
|
|
7543
|
-
box2d.drawFixture(fixture, this.pos, this.angle, color,
|
|
7722
|
+
box2d.drawFixture(fixture, this.pos, this.angle, color, lineColor, lineWidth, context));
|
|
7544
7723
|
}
|
|
7545
7724
|
|
|
7546
7725
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -9021,10 +9200,10 @@ class Box2dPlugin
|
|
|
9021
9200
|
* @param {Vector2} pos
|
|
9022
9201
|
* @param {number} angle
|
|
9023
9202
|
* @param {Color} [color]
|
|
9024
|
-
* @param {Color} [
|
|
9203
|
+
* @param {Color} [lineColor]
|
|
9025
9204
|
* @param {number} [lineWidth]
|
|
9026
9205
|
* @param {CanvasRenderingContext2D} [context] */
|
|
9027
|
-
drawFixture(fixture, pos, angle, color=WHITE,
|
|
9206
|
+
drawFixture(fixture, pos, angle, color=WHITE, lineColor=BLACK, lineWidth=.1, context=drawContext)
|
|
9028
9207
|
{
|
|
9029
9208
|
const shape = box2d.castObjectType(fixture.GetShape());
|
|
9030
9209
|
switch (shape.GetType())
|
|
@@ -9034,101 +9213,25 @@ class Box2dPlugin
|
|
|
9034
9213
|
let points = [];
|
|
9035
9214
|
for (let i=shape.GetVertexCount(); i--;)
|
|
9036
9215
|
points.push(box2d.vec2From(shape.GetVertex(i)));
|
|
9037
|
-
|
|
9216
|
+
drawPoly(points, color, lineWidth, lineColor, pos, angle, false, false, context);
|
|
9038
9217
|
break;
|
|
9039
9218
|
}
|
|
9040
9219
|
case box2d.instance.b2Shape.e_circle:
|
|
9041
9220
|
{
|
|
9042
9221
|
const radius = shape.get_m_radius();
|
|
9043
|
-
|
|
9222
|
+
drawCircle(pos, radius, color, lineWidth, lineColor, false, false, context);
|
|
9044
9223
|
break;
|
|
9045
9224
|
}
|
|
9046
9225
|
case box2d.instance.b2Shape.e_edge:
|
|
9047
9226
|
{
|
|
9048
9227
|
const v1 = box2d.vec2From(shape.get_m_vertex1());
|
|
9049
9228
|
const v2 = box2d.vec2From(shape.get_m_vertex2());
|
|
9050
|
-
|
|
9229
|
+
drawLine(v1, v2, lineWidth, lineColor, pos, angle, false, false, context);
|
|
9051
9230
|
break;
|
|
9052
9231
|
}
|
|
9053
9232
|
}
|
|
9054
9233
|
}
|
|
9055
9234
|
|
|
9056
|
-
/** draws a circle
|
|
9057
|
-
* @param {Vector2} pos
|
|
9058
|
-
* @param {number} radius
|
|
9059
|
-
* @param {Color} [color]
|
|
9060
|
-
* @param {Color} [outlineColor]
|
|
9061
|
-
* @param {number} [lineWidth]
|
|
9062
|
-
* @param {CanvasRenderingContext2D} [context] */
|
|
9063
|
-
drawCircle(pos, radius, color=WHITE, outlineColor=BLACK, lineWidth=.1, context=drawContext)
|
|
9064
|
-
{
|
|
9065
|
-
drawCanvas2D(pos, vec2(1), 0, 0, context=>
|
|
9066
|
-
{
|
|
9067
|
-
context.beginPath();
|
|
9068
|
-
context.arc(0, 0, radius, 0, 9);
|
|
9069
|
-
box2d.drawFillStroke(color, outlineColor, lineWidth, context);
|
|
9070
|
-
}, 0, context);
|
|
9071
|
-
}
|
|
9072
|
-
|
|
9073
|
-
/** draws a polygon
|
|
9074
|
-
* @param {Vector2} pos
|
|
9075
|
-
* @param {number} angle
|
|
9076
|
-
* @param {Array<Vector2>} points
|
|
9077
|
-
* @param {Color} [color]
|
|
9078
|
-
* @param {Color} [outlineColor]
|
|
9079
|
-
* @param {number} [lineWidth]
|
|
9080
|
-
* @param {CanvasRenderingContext2D} [context] */
|
|
9081
|
-
drawPoly(pos, angle, points, color=WHITE, outlineColor=BLACK, lineWidth=.1, context=drawContext)
|
|
9082
|
-
{
|
|
9083
|
-
drawCanvas2D(pos, vec2(1), angle, 0, context=>
|
|
9084
|
-
{
|
|
9085
|
-
context.beginPath();
|
|
9086
|
-
points.forEach(p=>context.lineTo(p.x, p.y));
|
|
9087
|
-
context.closePath();
|
|
9088
|
-
box2d.drawFillStroke(color, outlineColor, lineWidth, context);
|
|
9089
|
-
}, 0, context);
|
|
9090
|
-
}
|
|
9091
|
-
|
|
9092
|
-
/** draws a line
|
|
9093
|
-
* @param {Vector2} pos
|
|
9094
|
-
* @param {number} angle
|
|
9095
|
-
* @param {Vector2} posA
|
|
9096
|
-
* @param {Vector2} posB
|
|
9097
|
-
* @param {Color} [color]
|
|
9098
|
-
* @param {number} [lineWidth]
|
|
9099
|
-
* @param {CanvasRenderingContext2D} [context] */
|
|
9100
|
-
drawLine(pos, angle, posA, posB, color=WHITE, lineWidth=.1, context=drawContext)
|
|
9101
|
-
{
|
|
9102
|
-
drawCanvas2D(pos, vec2(1), angle, 0, context=>
|
|
9103
|
-
{
|
|
9104
|
-
context.beginPath();
|
|
9105
|
-
context.lineTo(posA.x, posA.y);
|
|
9106
|
-
context.lineTo(posB.x, posB.y);
|
|
9107
|
-
box2d.drawFillStroke(0, color, lineWidth, context);
|
|
9108
|
-
}, 0, context);
|
|
9109
|
-
}
|
|
9110
|
-
|
|
9111
|
-
/** performs a fill or stroke as a helper to the other draw functions
|
|
9112
|
-
* @param {Color} [color]
|
|
9113
|
-
* @param {Color} [outlineColor]
|
|
9114
|
-
* @param {number} [lineWidth]
|
|
9115
|
-
* @param {CanvasRenderingContext2D} [context] */
|
|
9116
|
-
drawFillStroke(color=WHITE, outlineColor=BLACK, lineWidth=.1, context=drawContext)
|
|
9117
|
-
{
|
|
9118
|
-
if (color)
|
|
9119
|
-
{
|
|
9120
|
-
context.fillStyle = color.toString();
|
|
9121
|
-
context.fill();
|
|
9122
|
-
}
|
|
9123
|
-
if (outlineColor && lineWidth)
|
|
9124
|
-
{
|
|
9125
|
-
context.lineWidth = lineWidth;
|
|
9126
|
-
context.lineJoin = context.lineCap = 'round';
|
|
9127
|
-
context.strokeStyle = outlineColor.toString();
|
|
9128
|
-
context.stroke();
|
|
9129
|
-
}
|
|
9130
|
-
}
|
|
9131
|
-
|
|
9132
9235
|
///////////////////////////////////////////////////////////////////////////////
|
|
9133
9236
|
// helper functions
|
|
9134
9237
|
|
|
@@ -9229,6 +9332,7 @@ async function box2dInit()
|
|
|
9229
9332
|
function setupDebugDraw()
|
|
9230
9333
|
{
|
|
9231
9334
|
// setup debug draw
|
|
9335
|
+
const debugLineWidth = .1;
|
|
9232
9336
|
const debugDraw = new box2d.instance.JSDraw();
|
|
9233
9337
|
const box2dColor = (c)=> new Color(c.get_r(), c.get_g(), c.get_b());
|
|
9234
9338
|
const box2dColorPointer = (c)=>
|
|
@@ -9246,33 +9350,33 @@ async function box2dInit()
|
|
|
9246
9350
|
color = getDebugColor(color);
|
|
9247
9351
|
point1 = box2d.vec2FromPointer(point1);
|
|
9248
9352
|
point2 = box2d.vec2FromPointer(point2);
|
|
9249
|
-
|
|
9353
|
+
drawLine(point1, point2, debugLineWidth, color, vec2(), 0, false, false, overlayContext);
|
|
9250
9354
|
};
|
|
9251
9355
|
debugDraw.DrawPolygon = function(vertices, vertexCount, color)
|
|
9252
9356
|
{
|
|
9253
9357
|
color = getDebugColor(color);
|
|
9254
9358
|
const points = getPointsList(vertices, vertexCount);
|
|
9255
|
-
|
|
9359
|
+
drawPoly(points, CLEAR_WHITE, debugLineWidth, color, vec2(), 0, false, false, overlayContext);
|
|
9256
9360
|
};
|
|
9257
9361
|
debugDraw.DrawSolidPolygon = function(vertices, vertexCount, color)
|
|
9258
9362
|
{
|
|
9259
9363
|
color = getDebugColor(color);
|
|
9260
9364
|
const points = getPointsList(vertices, vertexCount);
|
|
9261
|
-
|
|
9365
|
+
drawPoly(points, color, 0, color, vec2(), 0, false, false, overlayContext);
|
|
9262
9366
|
};
|
|
9263
9367
|
debugDraw.DrawCircle = function(center, radius, color)
|
|
9264
9368
|
{
|
|
9265
9369
|
color = getDebugColor(color);
|
|
9266
9370
|
center = box2d.vec2FromPointer(center);
|
|
9267
|
-
|
|
9371
|
+
drawCircle(center, radius, CLEAR_WHITE, debugLineWidth, color, false, false, overlayContext);
|
|
9268
9372
|
};
|
|
9269
9373
|
debugDraw.DrawSolidCircle = function(center, radius, axis, color)
|
|
9270
9374
|
{
|
|
9271
9375
|
color = getDebugColor(color);
|
|
9272
9376
|
center = box2d.vec2FromPointer(center);
|
|
9273
9377
|
axis = box2d.vec2FromPointer(axis).scale(radius);
|
|
9274
|
-
|
|
9275
|
-
|
|
9378
|
+
drawCircle(center, radius, color, debugLineWidth, color, false, false, overlayContext);
|
|
9379
|
+
drawLine(vec2(), axis, debugLineWidth, color, center, 0, false, false, overlayContext);
|
|
9276
9380
|
};
|
|
9277
9381
|
debugDraw.DrawTransform = function(transform)
|
|
9278
9382
|
{
|
|
@@ -9281,8 +9385,8 @@ async function box2dInit()
|
|
|
9281
9385
|
const angle = -transform.get_q().GetAngle();
|
|
9282
9386
|
const p1 = vec2(1,0), c1 = rgb(.75,0,0,.8);
|
|
9283
9387
|
const p2 = vec2(0,1), c2 = rgb(0,.75,0,.8);
|
|
9284
|
-
|
|
9285
|
-
|
|
9388
|
+
drawLine(vec2(), p1, debugLineWidth, c1, pos, angle, false, false, overlayContext);
|
|
9389
|
+
drawLine(vec2(), p2, debugLineWidth, c2, pos, angle, false, false, overlayContext);
|
|
9286
9390
|
}
|
|
9287
9391
|
|
|
9288
9392
|
debugDraw.AppendFlags(box2d.instance.b2Draw.e_shapeBit);
|
|
@@ -9307,12 +9411,13 @@ async function box2dInit()
|
|
|
9307
9411
|
* @param {Vector2} pos - Screen space position
|
|
9308
9412
|
* @param {Vector2} size - Screen space size
|
|
9309
9413
|
* @param {TileInfo} startTile - Starting tile for the nine-slice pattern
|
|
9310
|
-
* @param {number} [borderSize
|
|
9311
|
-
* @param {number} [extraSpace
|
|
9414
|
+
* @param {number} [borderSize] - Width of the border sections
|
|
9415
|
+
* @param {number} [extraSpace] - Extra spacing adjustment
|
|
9416
|
+
* @param {number} [angle] - Angle to rotate by
|
|
9312
9417
|
* @memberof DrawUtilities */
|
|
9313
|
-
function drawNineSliceScreen(pos, size, startTile, borderSize=32, extraSpace=
|
|
9418
|
+
function drawNineSliceScreen(pos, size, startTile, borderSize=32, extraSpace=2, angle=0)
|
|
9314
9419
|
{
|
|
9315
|
-
drawNineSlice(pos, size, startTile, WHITE, borderSize, BLACK, extraSpace, false, true, overlayContext);
|
|
9420
|
+
drawNineSlice(pos, size, startTile, WHITE, borderSize, BLACK, extraSpace, angle, false, true, overlayContext);
|
|
9316
9421
|
}
|
|
9317
9422
|
|
|
9318
9423
|
/** Draw a scalable nine-slice UI element in world space
|
|
@@ -9321,14 +9426,15 @@ function drawNineSliceScreen(pos, size, startTile, borderSize=32, extraSpace=1)
|
|
|
9321
9426
|
* @param {Vector2} size - World space size
|
|
9322
9427
|
* @param {TileInfo} startTile - Starting tile for the nine-slice pattern
|
|
9323
9428
|
* @param {Color} [color] - Color to modulate with
|
|
9324
|
-
* @param {number} [borderSize
|
|
9429
|
+
* @param {number} [borderSize] - Width of the border sections
|
|
9325
9430
|
* @param {Color} [additiveColor] - Additive color
|
|
9326
|
-
* @param {number} [extraSpace
|
|
9431
|
+
* @param {number} [extraSpace] - Extra spacing adjustment
|
|
9432
|
+
* @param {number} [angle] - Angle to rotate by
|
|
9327
9433
|
* @param {boolean} [useWebGL=glEnable] - Use WebGL for rendering
|
|
9328
9434
|
* @param {boolean} [screenSpace] - Use screen space coordinates
|
|
9329
9435
|
* @param {CanvasRenderingContext2D} [context] - Canvas context to use
|
|
9330
9436
|
* @memberof DrawUtilities */
|
|
9331
|
-
function drawNineSlice(pos, size, startTile, color, borderSize=1, additiveColor, extraSpace=.
|
|
9437
|
+
function drawNineSlice(pos, size, startTile, color, borderSize=1, additiveColor, extraSpace=.05, angle=0, useWebGL=glEnable, screenSpace, context)
|
|
9332
9438
|
{
|
|
9333
9439
|
// setup nine slice tiles
|
|
9334
9440
|
const centerTile = startTile.offset(startTile.size);
|
|
@@ -9336,26 +9442,27 @@ function drawNineSlice(pos, size, startTile, color, borderSize=1, additiveColor,
|
|
|
9336
9442
|
const cornerSize = vec2(borderSize);
|
|
9337
9443
|
const cornerOffset = size.scale(.5).subtract(cornerSize.scale(.5));
|
|
9338
9444
|
const flip = screenSpace ? -1 : 1;
|
|
9445
|
+
const rotateAngle = screenSpace ? -angle : angle;
|
|
9339
9446
|
|
|
9340
9447
|
// center
|
|
9341
|
-
drawTile(pos, centerSize, centerTile, color,
|
|
9342
|
-
for(let i=4; i--;)
|
|
9448
|
+
drawTile(pos, centerSize, centerTile, color, angle, false, additiveColor, useWebGL, screenSpace, context);
|
|
9449
|
+
for (let i=4; i--;)
|
|
9343
9450
|
{
|
|
9344
9451
|
// sides
|
|
9345
9452
|
const horizontal = i%2;
|
|
9346
9453
|
const sidePos = cornerOffset.multiply(vec2(horizontal?i==1?1:-1:0, horizontal?0:i?-1:1));
|
|
9347
9454
|
const sideSize = vec2(horizontal ? borderSize : centerSize.x, horizontal ? centerSize.y : borderSize);
|
|
9348
9455
|
const sideTile = centerTile.offset(startTile.size.multiply(vec2(i==1?1:i==3?-1:0,i==0?-flip:i==2?flip:0)))
|
|
9349
|
-
drawTile(pos.add(sidePos), sideSize, sideTile, color,
|
|
9456
|
+
drawTile(pos.add(sidePos.rotate(rotateAngle)), sideSize, sideTile, color, angle, false, additiveColor, useWebGL, screenSpace, context);
|
|
9350
9457
|
}
|
|
9351
|
-
for(let i=4; i--;)
|
|
9458
|
+
for (let i=4; i--;)
|
|
9352
9459
|
{
|
|
9353
9460
|
// corners
|
|
9354
9461
|
const flipX = i>1;
|
|
9355
9462
|
const flipY = i && i<3;
|
|
9356
9463
|
const cornerPos = cornerOffset.multiply(vec2(flipX?-1:1, flipY?-1:1));
|
|
9357
9464
|
const cornerTile = centerTile.offset(startTile.size.multiply(vec2(flipX?-1:1,flipY?flip:-flip)));
|
|
9358
|
-
drawTile(pos.add(cornerPos), cornerSize, cornerTile, color,
|
|
9465
|
+
drawTile(pos.add(cornerPos.rotate(rotateAngle)), cornerSize, cornerTile, color, angle, false, additiveColor, useWebGL, screenSpace, context);
|
|
9359
9466
|
}
|
|
9360
9467
|
}
|
|
9361
9468
|
|
|
@@ -9364,12 +9471,13 @@ function drawNineSlice(pos, size, startTile, color, borderSize=1, additiveColor,
|
|
|
9364
9471
|
* @param {Vector2} pos - Screen space position
|
|
9365
9472
|
* @param {Vector2} size - Screen space size
|
|
9366
9473
|
* @param {TileInfo} startTile - Starting tile for the three-slice pattern
|
|
9367
|
-
* @param {number} [borderSize
|
|
9368
|
-
* @param {number} [extraSpace
|
|
9474
|
+
* @param {number} [borderSize] - Width of the border sections
|
|
9475
|
+
* @param {number} [extraSpace] - Extra spacing adjustment
|
|
9476
|
+
* @param {number} [angle] - Angle to rotate by
|
|
9369
9477
|
* @memberof DrawUtilities */
|
|
9370
|
-
function drawThreeSliceScreen(pos, size, startTile, borderSize=32, extraSpace=
|
|
9478
|
+
function drawThreeSliceScreen(pos, size, startTile, borderSize=32, extraSpace=2, angle=0)
|
|
9371
9479
|
{
|
|
9372
|
-
drawThreeSlice(pos, size, startTile, WHITE, borderSize, BLACK, extraSpace, false, true, overlayContext);
|
|
9480
|
+
drawThreeSlice(pos, size, startTile, WHITE, borderSize, BLACK, extraSpace, angle, false, true, overlayContext);
|
|
9373
9481
|
}
|
|
9374
9482
|
|
|
9375
9483
|
/** Draw a scalable three-slice UI element in world space
|
|
@@ -9378,14 +9486,15 @@ function drawThreeSliceScreen(pos, size, startTile, borderSize=32, extraSpace=1)
|
|
|
9378
9486
|
* @param {Vector2} size - World space size
|
|
9379
9487
|
* @param {TileInfo} startTile - Starting tile for the three-slice pattern
|
|
9380
9488
|
* @param {Color} [color] - Color to modulate with
|
|
9381
|
-
* @param {number} [borderSize
|
|
9489
|
+
* @param {number} [borderSize] - Width of the border sections
|
|
9382
9490
|
* @param {Color} [additiveColor] - Additive color
|
|
9383
|
-
* @param {number} [extraSpace
|
|
9491
|
+
* @param {number} [extraSpace] - Extra spacing adjustment
|
|
9492
|
+
* @param {number} [angle] - Angle to rotate by
|
|
9384
9493
|
* @param {boolean} [useWebGL=glEnable] - Use WebGL for rendering
|
|
9385
9494
|
* @param {boolean} [screenSpace] - Use screen space coordinates
|
|
9386
9495
|
* @param {CanvasRenderingContext2D} [context] - Canvas context to use
|
|
9387
9496
|
* @memberof DrawUtilities */
|
|
9388
|
-
function drawThreeSlice(pos, size, startTile, color, borderSize=1, additiveColor, extraSpace=.
|
|
9497
|
+
function drawThreeSlice(pos, size, startTile, color, borderSize=1, additiveColor, extraSpace=.05, angle=0, useWebGL=glEnable, screenSpace, context)
|
|
9389
9498
|
{
|
|
9390
9499
|
// setup three slice tiles
|
|
9391
9500
|
const cornerTile = startTile.frame(0);
|
|
@@ -9395,25 +9504,26 @@ function drawThreeSlice(pos, size, startTile, color, borderSize=1, additiveColor
|
|
|
9395
9504
|
const cornerSize = vec2(borderSize);
|
|
9396
9505
|
const cornerOffset = size.scale(.5).subtract(cornerSize.scale(.5));
|
|
9397
9506
|
const flip = screenSpace ? -1 : 1;
|
|
9507
|
+
const rotateAngle = screenSpace ? -angle : angle;
|
|
9398
9508
|
|
|
9399
9509
|
// center
|
|
9400
|
-
drawTile(pos, centerSize, centerTile, color,
|
|
9401
|
-
for(let i=4; i--;)
|
|
9510
|
+
drawTile(pos, centerSize, centerTile, color, angle, false, additiveColor, useWebGL, screenSpace, context);
|
|
9511
|
+
for (let i=4; i--;)
|
|
9402
9512
|
{
|
|
9403
9513
|
// sides
|
|
9404
|
-
const
|
|
9514
|
+
const a = angle + i*PI/2;
|
|
9405
9515
|
const horizontal = i%2;
|
|
9406
9516
|
const sidePos = cornerOffset.multiply(vec2(horizontal?i==1?1:-1:0, horizontal?0:i?-flip:flip));
|
|
9407
9517
|
const sideSize = vec2(horizontal ? centerSize.y : centerSize.x, borderSize);
|
|
9408
|
-
drawTile(pos.add(sidePos), sideSize, sideTile, color,
|
|
9518
|
+
drawTile(pos.add(sidePos.rotate(rotateAngle)), sideSize, sideTile, color, a, false, additiveColor, useWebGL, screenSpace, context);
|
|
9409
9519
|
}
|
|
9410
|
-
for(let i=4; i--;)
|
|
9520
|
+
for (let i=4; i--;)
|
|
9411
9521
|
{
|
|
9412
9522
|
// corners
|
|
9413
|
-
const
|
|
9523
|
+
const a = angle + i*PI/2;
|
|
9414
9524
|
const flipX = !i || i>2;
|
|
9415
9525
|
const flipY = i>1;
|
|
9416
9526
|
const cornerPos = cornerOffset.multiply(vec2(flipX?-1:1, flipY?-flip:flip));
|
|
9417
|
-
drawTile(pos.add(cornerPos), cornerSize, cornerTile, color,
|
|
9527
|
+
drawTile(pos.add(cornerPos.rotate(rotateAngle)), cornerSize, cornerTile, color, a, false, additiveColor, useWebGL, screenSpace, context);
|
|
9418
9528
|
}
|
|
9419
9529
|
}
|