littlejsengine 1.14.2 → 1.14.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 +2 -2
- package/dist/littlejs.d.ts +57 -17
- package/dist/littlejs.esm.js +233 -84
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +227 -84
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +224 -81
- package/examples/box2d/game.js +4 -4
- package/examples/breakoutTutorial/README.md +1 -1
- package/examples/breakoutTutorial/game.js +1 -1
- package/examples/electron/build.js +1 -1
- package/examples/empty/game.js +1 -1
- package/examples/htmlMenu/index.html +7 -7
- package/examples/index.html +3 -3
- package/examples/platformer/gameEffects.js +1 -7
- package/examples/shorts/shapes.js +12 -10
- package/examples/stress/index.html +14 -12
- package/examples/typescript/build.js +1 -1
- package/package.json +1 -1
- package/plugins/box2d.js +3 -3
- package/reference.md +8 -6
- package/src/engine.js +7 -3
- package/src/engineBuild.js +2 -2
- package/src/engineDebug.js +3 -3
- package/src/engineDraw.js +140 -18
- package/src/engineExport.js +6 -0
- package/src/engineObject.js +3 -3
- package/src/engineTileLayer.js +0 -1
- package/src/engineUtilities.js +4 -4
- package/src/engineWebGL.js +66 -48
- package/copyToGithub.bat +0 -28
package/dist/littlejs.release.js
CHANGED
|
@@ -33,7 +33,7 @@ const engineName = 'LittleJS';
|
|
|
33
33
|
* @type {string}
|
|
34
34
|
* @default
|
|
35
35
|
* @memberof Engine */
|
|
36
|
-
const engineVersion = '1.14.
|
|
36
|
+
const engineVersion = '1.14.4';
|
|
37
37
|
|
|
38
38
|
/** Frames per second to update
|
|
39
39
|
* @type {number}
|
|
@@ -249,8 +249,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
249
249
|
overlayContext.fillStyle = '#fff';
|
|
250
250
|
overlayContext.fillText(text, mainCanvas.width-2, 2);
|
|
251
251
|
}
|
|
252
|
-
|
|
253
|
-
drawCount = 0;
|
|
252
|
+
drawCount = 0;
|
|
254
253
|
}
|
|
255
254
|
requestAnimationFrame(engineUpdate);
|
|
256
255
|
}
|
|
@@ -284,6 +283,11 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
284
283
|
|
|
285
284
|
// save canvas size
|
|
286
285
|
mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
|
|
286
|
+
|
|
287
|
+
// set default line join and cap
|
|
288
|
+
const lineJoin = 'round', lineCap = 'round';
|
|
289
|
+
mainContext.lineJoin = overlayContext.lineJoin = lineJoin;
|
|
290
|
+
mainContext.lineCap = overlayContext.lineCap = lineCap;
|
|
287
291
|
}
|
|
288
292
|
|
|
289
293
|
// wait for gameInit to load
|
|
@@ -1066,10 +1070,10 @@ function vec2(x=0, y) { return new Vector2(x, y === undefined ? x : y); }
|
|
|
1066
1070
|
* @param {any} v
|
|
1067
1071
|
* @return {boolean}
|
|
1068
1072
|
* @memberof Utilities */
|
|
1069
|
-
function isVector2(v) { return v instanceof Vector2; }
|
|
1073
|
+
function isVector2(v) { return v instanceof Vector2 && v.isValid(); }
|
|
1070
1074
|
|
|
1071
1075
|
// vector2 asserts
|
|
1072
|
-
function ASSERT_VECTOR2_VALID(v) { ASSERT(isVector2(v)
|
|
1076
|
+
function ASSERT_VECTOR2_VALID(v) { ASSERT(isVector2(v), 'Vector2 is invalid.', v); }
|
|
1073
1077
|
function ASSERT_NUMBER_VALID(n) { ASSERT(isNumber(n), 'Number is invalid.', n); }
|
|
1074
1078
|
function ASSERT_VECTOR2_NORMAL(v)
|
|
1075
1079
|
{
|
|
@@ -1327,10 +1331,10 @@ function hsl(h, s, l, a) { return new Color().setHSLA(h, s, l, a); }
|
|
|
1327
1331
|
* @param {any} c
|
|
1328
1332
|
* @return {boolean}
|
|
1329
1333
|
* @memberof Utilities */
|
|
1330
|
-
function isColor(c) { return c instanceof Color; }
|
|
1334
|
+
function isColor(c) { return c instanceof Color && c.isValid(); }
|
|
1331
1335
|
|
|
1332
1336
|
// color asserts
|
|
1333
|
-
function ASSERT_COLOR_VALID(c) { ASSERT(isColor(c)
|
|
1337
|
+
function ASSERT_COLOR_VALID(c) { ASSERT(isColor(c), 'Color is invalid.', c); }
|
|
1334
1338
|
|
|
1335
1339
|
/**
|
|
1336
1340
|
* Color object (red, green, blue, alpha) with some helpful functions
|
|
@@ -2263,11 +2267,11 @@ class EngineObject
|
|
|
2263
2267
|
constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color=new Color, renderOrder=0)
|
|
2264
2268
|
{
|
|
2265
2269
|
// check passed in params
|
|
2266
|
-
ASSERT(isVector2(pos)
|
|
2267
|
-
ASSERT(isVector2(size)
|
|
2270
|
+
ASSERT(isVector2(pos), 'object pos should be a vec2');
|
|
2271
|
+
ASSERT(isVector2(size), 'object size should be a vec2');
|
|
2268
2272
|
ASSERT(!tileInfo || tileInfo instanceof TileInfo, 'object tileInfo should be a TileInfo or undefined');
|
|
2269
2273
|
ASSERT(typeof angle === 'number' && isFinite(angle), 'object angle should be a number');
|
|
2270
|
-
ASSERT(isColor(color)
|
|
2274
|
+
ASSERT(isColor(color), 'object color should be a valid rgba color');
|
|
2271
2275
|
ASSERT(typeof renderOrder === 'number', 'object renderOrder should be a number');
|
|
2272
2276
|
|
|
2273
2277
|
/** @property {Vector2} - World space position of the object */
|
|
@@ -2761,7 +2765,9 @@ let mainCanvasSize = vec2();
|
|
|
2761
2765
|
* @memberof Draw */
|
|
2762
2766
|
let textureInfos = [];
|
|
2763
2767
|
|
|
2764
|
-
|
|
2768
|
+
/** Keeps track of how many draw calls there were each frame for debugging
|
|
2769
|
+
* @type {number}
|
|
2770
|
+
* @memberof Draw */
|
|
2765
2771
|
let drawCount;
|
|
2766
2772
|
|
|
2767
2773
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -2916,8 +2922,8 @@ class TextureInfo
|
|
|
2916
2922
|
function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
|
|
2917
2923
|
angle=0, mirror, additiveColor, useWebGL=glEnable, screenSpace, context)
|
|
2918
2924
|
{
|
|
2919
|
-
ASSERT(isVector2(pos)
|
|
2920
|
-
ASSERT(isVector2(size)
|
|
2925
|
+
ASSERT(isVector2(pos), 'drawTile pos should be a vec2');
|
|
2926
|
+
ASSERT(isVector2(size), 'drawTile size should be a vec2');
|
|
2921
2927
|
ASSERT(isColor(color) && (!additiveColor || isColor(additiveColor)), 'drawTile color is invalid');
|
|
2922
2928
|
ASSERT(isNumber(angle), 'drawTile angle should be a number');
|
|
2923
2929
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
@@ -2965,7 +2971,7 @@ function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
|
|
|
2965
2971
|
else
|
|
2966
2972
|
{
|
|
2967
2973
|
// normal canvas 2D rendering method (slower)
|
|
2968
|
-
|
|
2974
|
+
++drawCount;
|
|
2969
2975
|
size = new Vector2(size.x, -size.y); // fix upside down sprites
|
|
2970
2976
|
drawCanvas2D(pos, size, angle, mirror, (context)=>
|
|
2971
2977
|
{
|
|
@@ -3001,10 +3007,124 @@ function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
|
|
|
3001
3007
|
drawTile(pos, size, undefined, color, angle, false, undefined, useWebGL, screenSpace, context);
|
|
3002
3008
|
}
|
|
3003
3009
|
|
|
3010
|
+
/** Draw a rect centered on pos with a gradient from top to bottom
|
|
3011
|
+
* @param {Vector2} pos
|
|
3012
|
+
* @param {Vector2} [size=(1,1)]
|
|
3013
|
+
* @param {Color} [colorTop=(1,1,1,1)]
|
|
3014
|
+
* @param {Color} [colorBottom=(0,0,0,1)]
|
|
3015
|
+
* @param {number} [angle]
|
|
3016
|
+
* @param {boolean} [useWebGL=glEnable]
|
|
3017
|
+
* @param {boolean} [screenSpace]
|
|
3018
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
|
|
3019
|
+
* @memberof Draw */
|
|
3020
|
+
function drawRectGradient(pos, size, colorTop=WHITE, colorBottom=BLACK, angle=0, useWebGL=glEnable, screenSpace=false, context)
|
|
3021
|
+
{
|
|
3022
|
+
ASSERT(isVector2(pos), 'drawRectGradient pos should be a vec2');
|
|
3023
|
+
ASSERT(isVector2(size), 'drawRectGradient size should be a vec2');
|
|
3024
|
+
ASSERT(isColor(colorTop) && isColor(colorBottom), 'drawRectGradient color is invalid');
|
|
3025
|
+
ASSERT(isNumber(angle), 'drawRectGradient angle should be a number');
|
|
3026
|
+
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3027
|
+
if (useWebGL)
|
|
3028
|
+
{
|
|
3029
|
+
if (screenSpace)
|
|
3030
|
+
{
|
|
3031
|
+
// convert to world space
|
|
3032
|
+
pos = screenToWorld(pos);
|
|
3033
|
+
size = size.scale(1/cameraScale);
|
|
3034
|
+
}
|
|
3035
|
+
// build 4 corner points for the rectangle
|
|
3036
|
+
const points = [], colors = [];
|
|
3037
|
+
const halfSizeX = size.x/2, halfSizeY = size.y/2;
|
|
3038
|
+
const colorTopInt = colorTop.rgbaInt();
|
|
3039
|
+
const colorBottomInt = colorBottom.rgbaInt();
|
|
3040
|
+
const c = Math.cos(-angle), s = Math.sin(-angle);
|
|
3041
|
+
for (let i=4; i--;)
|
|
3042
|
+
{
|
|
3043
|
+
const x = i & 1 ? halfSizeX : -halfSizeX;
|
|
3044
|
+
const y = i & 2 ? halfSizeY : -halfSizeY;
|
|
3045
|
+
const rx = x * c - y * s;
|
|
3046
|
+
const ry = x * s + y * c;
|
|
3047
|
+
const color = i & 2 ? colorTopInt : colorBottomInt;
|
|
3048
|
+
points.push(vec2(pos.x + rx, pos.y + ry));
|
|
3049
|
+
colors.push(color);
|
|
3050
|
+
}
|
|
3051
|
+
glDrawColoredPoints(points, colors);
|
|
3052
|
+
}
|
|
3053
|
+
else
|
|
3054
|
+
{
|
|
3055
|
+
// normal canvas 2D rendering method (slower)
|
|
3056
|
+
++drawCount;
|
|
3057
|
+
size = new Vector2(size.x, -size.y); // fix upside down sprites
|
|
3058
|
+
drawCanvas2D(pos, size, angle, false, (context)=>
|
|
3059
|
+
{
|
|
3060
|
+
// if no tile info, use untextured rect
|
|
3061
|
+
const gradient = context.createLinearGradient(0, -.5, 0, .5);
|
|
3062
|
+
gradient.addColorStop(0, colorTop.toString());
|
|
3063
|
+
gradient.addColorStop(1, colorBottom.toString());
|
|
3064
|
+
context.fillStyle = gradient;
|
|
3065
|
+
context.fillRect(-.5, -.5, 1, 1);
|
|
3066
|
+
}, screenSpace, context);
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
|
|
3070
|
+
/** Draw connected lines between a series of points
|
|
3071
|
+
* @param {Array<Vector2>} points
|
|
3072
|
+
* @param {number} [width]
|
|
3073
|
+
* @param {Color} [color=(1,1,1,1)]
|
|
3074
|
+
* @param {boolean} [wrap] - Should the last point connect to the first?
|
|
3075
|
+
* @param {Vector2} [pos=(0,0)] - Offset to apply
|
|
3076
|
+
* @param {number} [angle] - Angle to rotate by
|
|
3077
|
+
* @param {boolean} [useWebGL=glEnable]
|
|
3078
|
+
* @param {boolean} [screenSpace]
|
|
3079
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
|
|
3080
|
+
* @memberof Draw */
|
|
3081
|
+
function drawLineList(points, width=.1, color, wrap=false, pos=vec2(), angle=0, useWebGL=glEnable, screenSpace, context)
|
|
3082
|
+
{
|
|
3083
|
+
ASSERT(Array.isArray(points), 'drawLineList points should be an array');
|
|
3084
|
+
ASSERT(isNumber(width), 'drawLineList width should be a number');
|
|
3085
|
+
ASSERT(isColor(color), 'drawLineList color is invalid');
|
|
3086
|
+
ASSERT(isVector2(pos), 'drawLineList pos should be a vec2');
|
|
3087
|
+
ASSERT(isNumber(angle), 'drawLineList angle should be a number');
|
|
3088
|
+
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3089
|
+
if (useWebGL)
|
|
3090
|
+
{
|
|
3091
|
+
let scale = 1;
|
|
3092
|
+
if (screenSpace)
|
|
3093
|
+
{
|
|
3094
|
+
// convert to world space
|
|
3095
|
+
pos = screenToWorld(pos);
|
|
3096
|
+
scale = 1/cameraScale;
|
|
3097
|
+
}
|
|
3098
|
+
glDrawOutlineTransform(points, color.rgbaInt(), width, pos.x, pos.y, scale, scale, angle, wrap);
|
|
3099
|
+
}
|
|
3100
|
+
else
|
|
3101
|
+
{
|
|
3102
|
+
// normal canvas 2D rendering method (slower)
|
|
3103
|
+
++drawCount;
|
|
3104
|
+
drawCanvas2D(pos, vec2(1), angle, false, (context)=>
|
|
3105
|
+
{
|
|
3106
|
+
context.strokeStyle = color.toString();
|
|
3107
|
+
context.lineWidth = width;
|
|
3108
|
+
context.beginPath();
|
|
3109
|
+
for (let i=0; i<points.length; ++i)
|
|
3110
|
+
{
|
|
3111
|
+
const point = points[i];
|
|
3112
|
+
if (i)
|
|
3113
|
+
context.lineTo(point.x, point.y);
|
|
3114
|
+
else
|
|
3115
|
+
context.moveTo(point.x, point.y);
|
|
3116
|
+
}
|
|
3117
|
+
if (wrap)
|
|
3118
|
+
context.closePath();
|
|
3119
|
+
context.stroke();
|
|
3120
|
+
}, screenSpace, context);
|
|
3121
|
+
}
|
|
3122
|
+
}
|
|
3123
|
+
|
|
3004
3124
|
/** Draw colored line between two points
|
|
3005
3125
|
* @param {Vector2} posA
|
|
3006
3126
|
* @param {Vector2} posB
|
|
3007
|
-
* @param {number} [
|
|
3127
|
+
* @param {number} [width]
|
|
3008
3128
|
* @param {Color} [color=(1,1,1,1)]
|
|
3009
3129
|
* @param {Vector2} [pos=(0,0)] - Offset to apply
|
|
3010
3130
|
* @param {number} [angle] - Angle to rotate by
|
|
@@ -3012,10 +3132,10 @@ function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
|
|
|
3012
3132
|
* @param {boolean} [screenSpace]
|
|
3013
3133
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
|
|
3014
3134
|
* @memberof Draw */
|
|
3015
|
-
function drawLine(posA, posB,
|
|
3135
|
+
function drawLine(posA, posB, width=.1, color, pos=vec2(), angle=0, useWebGL, screenSpace, context)
|
|
3016
3136
|
{
|
|
3017
3137
|
const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2);
|
|
3018
|
-
const size = vec2(
|
|
3138
|
+
const size = vec2(width, halfDelta.length()*2);
|
|
3019
3139
|
pos = pos.add(posA.add(halfDelta));
|
|
3020
3140
|
angle += halfDelta.angle();
|
|
3021
3141
|
drawRect(pos, size, color, angle, useWebGL, screenSpace, context);
|
|
@@ -3035,12 +3155,16 @@ function drawLine(posA, posB, thickness=.1, color, pos=vec2(), angle=0, useWebGL
|
|
|
3035
3155
|
* @memberof Draw */
|
|
3036
3156
|
function drawRegularPoly(pos, size=vec2(1), sides=3, color=WHITE, lineWidth=0, lineColor=BLACK, angle=0, useWebGL=glEnable, screenSpace=false, context)
|
|
3037
3157
|
{
|
|
3158
|
+
ASSERT(isVector2(size), 'drawRegularPoly size should be a vec2');
|
|
3159
|
+
ASSERT(isNumber(sides), 'drawRegularPoly sides should be a number');
|
|
3160
|
+
|
|
3038
3161
|
// build regular polygon points
|
|
3039
3162
|
const points = [];
|
|
3163
|
+
const sizeX = size.x/2, sizeY = size.y/2;
|
|
3040
3164
|
for (let i=sides; i--;)
|
|
3041
3165
|
{
|
|
3042
3166
|
const a = (i/sides)*PI*2;
|
|
3043
|
-
points.push(vec2(Math.sin(a)*
|
|
3167
|
+
points.push(vec2(Math.sin(a)*sizeX, Math.cos(a)*sizeY));
|
|
3044
3168
|
}
|
|
3045
3169
|
drawPoly(points, color, lineWidth, lineColor, pos, angle, useWebGL, screenSpace, context);
|
|
3046
3170
|
}
|
|
@@ -3058,7 +3182,7 @@ function drawRegularPoly(pos, size=vec2(1), sides=3, color=WHITE, lineWidth=0, l
|
|
|
3058
3182
|
* @memberof Draw */
|
|
3059
3183
|
function drawPoly(points, color=WHITE, lineWidth=0, lineColor=BLACK, pos=vec2(), angle=0, useWebGL=glEnable, screenSpace=false, context=undefined)
|
|
3060
3184
|
{
|
|
3061
|
-
ASSERT(isVector2(pos)
|
|
3185
|
+
ASSERT(isVector2(pos), 'drawPoly pos should be a vec2');
|
|
3062
3186
|
ASSERT(Array.isArray(points), 'drawPoly points should be an array');
|
|
3063
3187
|
ASSERT(isColor(color) && isColor(lineColor), 'drawPoly color is invalid');
|
|
3064
3188
|
ASSERT(isNumber(lineWidth), 'drawPoly lineWidth should be a number');
|
|
@@ -3099,7 +3223,7 @@ function drawPoly(points, color=WHITE, lineWidth=0, lineColor=BLACK, pos=vec2(),
|
|
|
3099
3223
|
|
|
3100
3224
|
/** Draw colored ellipse using passed in point
|
|
3101
3225
|
* @param {Vector2} pos
|
|
3102
|
-
* @param {Vector2} [size=(1,1)]
|
|
3226
|
+
* @param {Vector2} [size=(1,1)] - Width and height diameter
|
|
3103
3227
|
* @param {Color} [color=(1,1,1,1)]
|
|
3104
3228
|
* @param {number} [angle]
|
|
3105
3229
|
* @param {number} [lineWidth]
|
|
@@ -3110,8 +3234,8 @@ function drawPoly(points, color=WHITE, lineWidth=0, lineColor=BLACK, pos=vec2(),
|
|
|
3110
3234
|
* @memberof Draw */
|
|
3111
3235
|
function drawEllipse(pos, size=vec2(1), color=WHITE, angle=0, lineWidth=0, lineColor=BLACK, useWebGL=glEnable, screenSpace=false, context)
|
|
3112
3236
|
{
|
|
3113
|
-
ASSERT(isVector2(pos)
|
|
3114
|
-
ASSERT(isVector2(size)
|
|
3237
|
+
ASSERT(isVector2(pos), 'drawEllipse pos should be a vec2');
|
|
3238
|
+
ASSERT(isVector2(size), 'drawEllipse size should be a vec2');
|
|
3115
3239
|
ASSERT(isColor(color) && isColor(lineColor), 'drawEllipse color is invalid');
|
|
3116
3240
|
ASSERT(isNumber(angle), 'drawEllipse angle should be a number');
|
|
3117
3241
|
ASSERT(isNumber(lineWidth), 'drawEllipse lineWidth should be a number');
|
|
@@ -3129,7 +3253,7 @@ function drawEllipse(pos, size=vec2(1), color=WHITE, angle=0, lineWidth=0, lineC
|
|
|
3129
3253
|
{
|
|
3130
3254
|
context.fillStyle = color.toString();
|
|
3131
3255
|
context.beginPath();
|
|
3132
|
-
context.ellipse(0, 0, size.x, size.y, 0, 0, 9);
|
|
3256
|
+
context.ellipse(0, 0, size.x/2, size.y/2, 0, 0, 9);
|
|
3133
3257
|
context.fill();
|
|
3134
3258
|
if (lineWidth)
|
|
3135
3259
|
{
|
|
@@ -3143,7 +3267,7 @@ function drawEllipse(pos, size=vec2(1), color=WHITE, angle=0, lineWidth=0, lineC
|
|
|
3143
3267
|
|
|
3144
3268
|
/** Draw colored circle using passed in point
|
|
3145
3269
|
* @param {Vector2} pos
|
|
3146
|
-
* @param {number} [
|
|
3270
|
+
* @param {number} [size=1] - Diameter
|
|
3147
3271
|
* @param {Color} [color=(1,1,1,1)]
|
|
3148
3272
|
* @param {number} [lineWidth=0]
|
|
3149
3273
|
* @param {Color} [lineColor=(0,0,0,1)]
|
|
@@ -3151,8 +3275,11 @@ function drawEllipse(pos, size=vec2(1), color=WHITE, angle=0, lineWidth=0, lineC
|
|
|
3151
3275
|
* @param {boolean} [screenSpace]
|
|
3152
3276
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
|
|
3153
3277
|
* @memberof Draw */
|
|
3154
|
-
function drawCircle(pos,
|
|
3155
|
-
{
|
|
3278
|
+
function drawCircle(pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, useWebGL=glEnable, screenSpace=false, context)
|
|
3279
|
+
{
|
|
3280
|
+
ASSERT(isNumber(size), 'drawCircle size should be a number');
|
|
3281
|
+
drawEllipse(pos, vec2(size), color, 0, lineWidth, lineColor, useWebGL, screenSpace, context);
|
|
3282
|
+
}
|
|
3156
3283
|
|
|
3157
3284
|
/** Draw directly to a 2d canvas context in world space
|
|
3158
3285
|
* @param {Vector2} pos
|
|
@@ -3238,7 +3365,6 @@ function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=B
|
|
|
3238
3365
|
context.textAlign = textAlign;
|
|
3239
3366
|
context.font = size + 'px '+ font;
|
|
3240
3367
|
context.textBaseline = 'middle';
|
|
3241
|
-
context.lineJoin = 'round';
|
|
3242
3368
|
|
|
3243
3369
|
const lines = (text+'').split('\n');
|
|
3244
3370
|
let posY = pos.y;
|
|
@@ -3438,7 +3564,7 @@ let engineFontImage;
|
|
|
3438
3564
|
* const font = new FontImage;
|
|
3439
3565
|
*
|
|
3440
3566
|
* // draw text
|
|
3441
|
-
* font.drawTextScreen(
|
|
3567
|
+
* font.drawTextScreen('LittleJS\nHello World!', vec2(200, 50));
|
|
3442
3568
|
*/
|
|
3443
3569
|
class FontImage
|
|
3444
3570
|
{
|
|
@@ -4761,7 +4887,6 @@ function zzfxG
|
|
|
4761
4887
|
* - Caches arrays of tiles to off screen canvas for fast rendering
|
|
4762
4888
|
* - Unlimited numbers of layers, allocates canvases as needed
|
|
4763
4889
|
* - Tile layers can be drawn to using their context with canvas2d
|
|
4764
|
-
* - Drawn directly to the main canvas without using WebGL
|
|
4765
4890
|
* - Tile layers can also have collision with EngineObjects
|
|
4766
4891
|
* @namespace TileCollision
|
|
4767
4892
|
*/
|
|
@@ -5963,7 +6088,7 @@ let glAntialias = true;
|
|
|
5963
6088
|
let glShader, glPolyShader, glPolyMode, glAdditive, glBatchAdditive, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glBatchCount;
|
|
5964
6089
|
|
|
5965
6090
|
// WebGL internal constants
|
|
5966
|
-
const gl_ARRAY_BUFFER_SIZE =
|
|
6091
|
+
const gl_ARRAY_BUFFER_SIZE = 5e5;
|
|
5967
6092
|
const gl_INDICES_PER_INSTANCE = 11;
|
|
5968
6093
|
const gl_INSTANCE_BYTE_STRIDE = gl_INDICES_PER_INSTANCE * 4;
|
|
5969
6094
|
const gl_MAX_INSTANCES = gl_ARRAY_BUFFER_SIZE / gl_INSTANCE_BYTE_STRIDE | 0;
|
|
@@ -6057,9 +6182,9 @@ function glInit()
|
|
|
6057
6182
|
glContext.bufferData(glContext.ARRAY_BUFFER, geometry, glContext.STATIC_DRAW);
|
|
6058
6183
|
}
|
|
6059
6184
|
|
|
6060
|
-
function glSetInstancedMode(
|
|
6185
|
+
function glSetInstancedMode()
|
|
6061
6186
|
{
|
|
6062
|
-
if (!glPolyMode
|
|
6187
|
+
if (!glPolyMode)
|
|
6063
6188
|
return;
|
|
6064
6189
|
|
|
6065
6190
|
// setup instanced mode
|
|
@@ -6140,7 +6265,7 @@ function glPreRender()
|
|
|
6140
6265
|
1, 1, 1, 0,
|
|
6141
6266
|
p.x, p.y, 0, 1];
|
|
6142
6267
|
|
|
6143
|
-
// set the same matrix for both shaders
|
|
6268
|
+
// set the same transform matrix for both shaders
|
|
6144
6269
|
const initUniform = (program, uniform, value) =>
|
|
6145
6270
|
{
|
|
6146
6271
|
glContext.useProgram(program);
|
|
@@ -6158,9 +6283,12 @@ function glPreRender()
|
|
|
6158
6283
|
glContext.bindTexture(glContext.TEXTURE_2D, glActiveTexture);
|
|
6159
6284
|
}
|
|
6160
6285
|
|
|
6161
|
-
// start
|
|
6162
|
-
glAdditive = glBatchAdditive =
|
|
6163
|
-
|
|
6286
|
+
// start with additive blending off
|
|
6287
|
+
glAdditive = glBatchAdditive = false;
|
|
6288
|
+
|
|
6289
|
+
// force it to enter instanced mode
|
|
6290
|
+
glPolyMode = true;
|
|
6291
|
+
glSetInstancedMode();
|
|
6164
6292
|
}
|
|
6165
6293
|
|
|
6166
6294
|
/** Clear the canvas and setup the viewport
|
|
@@ -6170,7 +6298,9 @@ function glClearCanvas()
|
|
|
6170
6298
|
if (!glContext) return;
|
|
6171
6299
|
|
|
6172
6300
|
// clear and set to same size as main canvas
|
|
6173
|
-
|
|
6301
|
+
glCanvas.width = drawCanvas.width;
|
|
6302
|
+
glCanvas.height = drawCanvas.height;
|
|
6303
|
+
glContext.viewport(0, 0, glCanvas.width, glCanvas.height);
|
|
6174
6304
|
glContext.clear(glContext.COLOR_BUFFER_BIT);
|
|
6175
6305
|
}
|
|
6176
6306
|
|
|
@@ -6305,15 +6435,17 @@ function glFlush()
|
|
|
6305
6435
|
const destBlend = glBatchAdditive ? glContext.ONE : glContext.ONE_MINUS_SRC_ALPHA;
|
|
6306
6436
|
glContext.blendFuncSeparate(glContext.SRC_ALPHA, destBlend, glContext.ONE, destBlend);
|
|
6307
6437
|
glContext.enable(glContext.BLEND);
|
|
6308
|
-
|
|
6438
|
+
|
|
6439
|
+
const byteLength = glBatchCount *
|
|
6440
|
+
(glPolyMode ? gl_INDICES_PER_POLY_VERTEX : gl_INDICES_PER_INSTANCE);
|
|
6441
|
+
glContext.bufferSubData(glContext.ARRAY_BUFFER, 0, glPositionData, 0, byteLength);
|
|
6309
6442
|
|
|
6310
6443
|
// draw the batch
|
|
6311
6444
|
if (glPolyMode)
|
|
6312
6445
|
glContext.drawArrays(glContext.TRIANGLE_STRIP, 0, glBatchCount);
|
|
6313
6446
|
else
|
|
6314
6447
|
glContext.drawArraysInstanced(glContext.TRIANGLE_STRIP, 0, 4, glBatchCount);
|
|
6315
|
-
|
|
6316
|
-
drawCount += glBatchCount;
|
|
6448
|
+
drawCount += glBatchCount;
|
|
6317
6449
|
glBatchCount = 0;
|
|
6318
6450
|
}
|
|
6319
6451
|
glBatchAdditive = glAdditive;
|
|
@@ -6377,7 +6509,7 @@ function glDraw(x, y, sizeX, sizeY, angle=0, uv0X=0, uv0Y=0, uv1X=1, uv1Y=1, rgb
|
|
|
6377
6509
|
}
|
|
6378
6510
|
|
|
6379
6511
|
/** Transform and add a polygon to the gl draw list
|
|
6380
|
-
* @param {Array} points - Array of Vector2 points
|
|
6512
|
+
* @param {Array<Vector2>} points - Array of Vector2 points
|
|
6381
6513
|
* @param {number} rgba - Color of the polygon as a 32-bit integer
|
|
6382
6514
|
* @param {number} x
|
|
6383
6515
|
* @param {number} y
|
|
@@ -6403,7 +6535,7 @@ function glDrawPointsTransform(points, rgba, x, y, sx, sy, angle, tristrip=true)
|
|
|
6403
6535
|
}
|
|
6404
6536
|
|
|
6405
6537
|
/** Transform and add a polygon to the gl draw list
|
|
6406
|
-
* @param {Array} points - Array of Vector2 points
|
|
6538
|
+
* @param {Array<Vector2>} points - Array of Vector2 points
|
|
6407
6539
|
* @param {number} rgba - Color of the polygon as a 32-bit integer
|
|
6408
6540
|
* @param {number} lineWidth - Width of the outline
|
|
6409
6541
|
* @param {number} x
|
|
@@ -6411,61 +6543,73 @@ function glDrawPointsTransform(points, rgba, x, y, sx, sy, angle, tristrip=true)
|
|
|
6411
6543
|
* @param {number} sx
|
|
6412
6544
|
* @param {number} sy
|
|
6413
6545
|
* @param {number} angle
|
|
6546
|
+
* @param {boolean} [wrap] - Should the outline connect the first and last points
|
|
6414
6547
|
* @memberof WebGL */
|
|
6415
|
-
function glDrawOutlineTransform(points, rgba, lineWidth, x, y, sx, sy, angle)
|
|
6548
|
+
function glDrawOutlineTransform(points, rgba, lineWidth, x, y, sx, sy, angle, wrap=true)
|
|
6416
6549
|
{
|
|
6417
|
-
const outlinePoints = glMakeOutline(points, lineWidth);
|
|
6550
|
+
const outlinePoints = glMakeOutline(points, lineWidth, wrap);
|
|
6418
6551
|
glDrawPointsTransform(outlinePoints, rgba, x, y, sx, sy, angle, false);
|
|
6419
6552
|
}
|
|
6420
6553
|
|
|
6421
|
-
/** Add a
|
|
6422
|
-
* @param {Array} points - Array of Vector2 points in
|
|
6423
|
-
* @param {number} rgba - Color
|
|
6554
|
+
/** Add a list of points to the gl draw list
|
|
6555
|
+
* @param {Array<Vector2>} points - Array of Vector2 points in tri strip order
|
|
6556
|
+
* @param {number} rgba - Color as a 32-bit integer
|
|
6424
6557
|
* @memberof WebGL */
|
|
6425
6558
|
function glDrawPoints(points, rgba)
|
|
6426
6559
|
{
|
|
6427
6560
|
if (!glEnable || points.length < 3)
|
|
6428
6561
|
return; // needs at least 3 points to have area
|
|
6429
6562
|
|
|
6430
|
-
// add 2 degenerate verts if batching with existing polys to separate them
|
|
6431
|
-
const needsBridge = glPolyMode && glBatchCount > 0;
|
|
6432
|
-
const bridgeVerts = needsBridge ? 2 : 0;
|
|
6433
|
-
const vertCount = points.length + bridgeVerts;
|
|
6434
|
-
|
|
6435
6563
|
// flush if there is not enough room or if different blend mode
|
|
6436
|
-
|
|
6564
|
+
const vertCount = points.length + 2;
|
|
6565
|
+
if (glBatchCount+vertCount >= gl_MAX_POLY_VERTEXES || glBatchAdditive !== glAdditive)
|
|
6437
6566
|
glFlush();
|
|
6438
6567
|
glSetPolyMode();
|
|
6439
6568
|
|
|
6569
|
+
// setup triangle strip with degenerate verts at start and end
|
|
6440
6570
|
let offset = glBatchCount * gl_INDICES_PER_POLY_VERTEX;
|
|
6441
|
-
|
|
6442
|
-
|
|
6443
|
-
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
glPositionData[offset++] = glPositionData[prevOffset];
|
|
6448
|
-
glPositionData[offset++] = glPositionData[prevOffset + 1];
|
|
6449
|
-
glColorData[offset++] = glColorData[prevOffset + 2];
|
|
6450
|
-
|
|
6451
|
-
// repeat first vertex of new poly
|
|
6452
|
-
glPositionData[offset++] = points[0].x;
|
|
6453
|
-
glPositionData[offset++] = points[0].y;
|
|
6571
|
+
for(let i = vertCount; i--;)
|
|
6572
|
+
{
|
|
6573
|
+
const j = clamp(i-1, 0, vertCount-3);
|
|
6574
|
+
const point = points[j];
|
|
6575
|
+
glPositionData[offset++] = point.x;
|
|
6576
|
+
glPositionData[offset++] = point.y;
|
|
6454
6577
|
glColorData[offset++] = rgba;
|
|
6455
6578
|
}
|
|
6579
|
+
glBatchCount += vertCount;
|
|
6580
|
+
}
|
|
6581
|
+
|
|
6582
|
+
/** Add a list of colored points to the gl draw list
|
|
6583
|
+
* @param {Array<Vector2>} points - Array of Vector2 points in tri strip order
|
|
6584
|
+
* @param {Array<number>} pointColors - Array of 32-bit integer colors
|
|
6585
|
+
* @memberof WebGL */
|
|
6586
|
+
function glDrawColoredPoints(points, pointColors)
|
|
6587
|
+
{
|
|
6588
|
+
if (!glEnable || points.length < 3)
|
|
6589
|
+
return; // needs at least 3 points to have area
|
|
6456
6590
|
|
|
6457
|
-
//
|
|
6458
|
-
|
|
6591
|
+
// flush if there is not enough room or if different blend mode
|
|
6592
|
+
const vertCount = points.length + 2;
|
|
6593
|
+
if (glBatchCount+vertCount >= gl_MAX_POLY_VERTEXES || glBatchAdditive !== glAdditive)
|
|
6594
|
+
glFlush();
|
|
6595
|
+
glSetPolyMode();
|
|
6596
|
+
|
|
6597
|
+
// setup triangle strip with degenerate verts at start and end
|
|
6598
|
+
let offset = glBatchCount * gl_INDICES_PER_POLY_VERTEX;
|
|
6599
|
+
for(let i = vertCount; i--;)
|
|
6459
6600
|
{
|
|
6601
|
+
const j = clamp(i-1, 0, vertCount-3);
|
|
6602
|
+
const point = points[j];
|
|
6603
|
+
const color = pointColors[j];
|
|
6460
6604
|
glPositionData[offset++] = point.x;
|
|
6461
6605
|
glPositionData[offset++] = point.y;
|
|
6462
|
-
glColorData[offset++] =
|
|
6606
|
+
glColorData[offset++] = color;
|
|
6463
6607
|
}
|
|
6464
6608
|
glBatchCount += vertCount;
|
|
6465
6609
|
}
|
|
6466
6610
|
|
|
6467
6611
|
// WebGL internal function to convert polygon to outline triangle strip
|
|
6468
|
-
function glMakeOutline(points, width)
|
|
6612
|
+
function glMakeOutline(points, width, wrap=true)
|
|
6469
6613
|
{
|
|
6470
6614
|
if (points.length < 2)
|
|
6471
6615
|
return [];
|
|
@@ -6474,12 +6618,13 @@ function glMakeOutline(points, width)
|
|
|
6474
6618
|
const strip = [];
|
|
6475
6619
|
const n = points.length;
|
|
6476
6620
|
const e = 1e-6;
|
|
6621
|
+
const miterLimit = width*100;
|
|
6477
6622
|
for (let i = 0; i < n; i++)
|
|
6478
6623
|
{
|
|
6479
6624
|
// for each vertex, calculate normal based on adjacent edges
|
|
6480
|
-
const prev = points[(i - 1 + n) % n];
|
|
6625
|
+
const prev = points[wrap ? (i - 1 + n) % n : max(i - 1, 0)];
|
|
6481
6626
|
const curr = points[i];
|
|
6482
|
-
const next = points[(i + 1) % n];
|
|
6627
|
+
const next = points[wrap ? (i + 1) % n : min(i + 1, n - 1)];
|
|
6483
6628
|
|
|
6484
6629
|
// direction from previous to current
|
|
6485
6630
|
const dx1 = curr.x - prev.x;
|
|
@@ -6518,8 +6663,8 @@ function glMakeOutline(points, width)
|
|
|
6518
6663
|
const dot = nx1 * nx + ny1 * ny;
|
|
6519
6664
|
if (dot > e)
|
|
6520
6665
|
{
|
|
6521
|
-
// scale normal by miter length
|
|
6522
|
-
const miterLength = 1 / dot;
|
|
6666
|
+
// scale normal by miter length, clamped to miterLimit
|
|
6667
|
+
const miterLength = min(1 / dot, miterLimit);
|
|
6523
6668
|
nx *= miterLength;
|
|
6524
6669
|
ny *= miterLength;
|
|
6525
6670
|
}
|
|
@@ -6531,7 +6676,7 @@ function glMakeOutline(points, width)
|
|
|
6531
6676
|
strip.push(inner);
|
|
6532
6677
|
strip.push(outer);
|
|
6533
6678
|
}
|
|
6534
|
-
if (strip.length > 1)
|
|
6679
|
+
if (strip.length > 1 && wrap)
|
|
6535
6680
|
{
|
|
6536
6681
|
// close the loop
|
|
6537
6682
|
strip.push(strip[0]);
|
|
@@ -6566,10 +6711,8 @@ function glPolyStrip(points)
|
|
|
6566
6711
|
if (signedArea(points) < 0)
|
|
6567
6712
|
points = points.reverse();
|
|
6568
6713
|
|
|
6569
|
-
// tolerance constants
|
|
6570
|
-
const e = 1e-10;
|
|
6571
|
-
|
|
6572
6714
|
// check if point is inside triangle
|
|
6715
|
+
const e = 1e-9;
|
|
6573
6716
|
const pointInTriangle = (p, a, b, c)=>
|
|
6574
6717
|
{
|
|
6575
6718
|
const c1 = cross(a, b, p);
|
|
@@ -9305,7 +9448,7 @@ class Box2dPlugin
|
|
|
9305
9448
|
case box2d.instance.b2Shape.e_circle:
|
|
9306
9449
|
{
|
|
9307
9450
|
const radius = shape.get_m_radius();
|
|
9308
|
-
drawCircle(pos, radius, color, lineWidth, lineColor);
|
|
9451
|
+
drawCircle(pos, radius*2, color, lineWidth, lineColor);
|
|
9309
9452
|
break;
|
|
9310
9453
|
}
|
|
9311
9454
|
case box2d.instance.b2Shape.e_edge:
|
|
@@ -9456,14 +9599,14 @@ async function box2dInit()
|
|
|
9456
9599
|
{
|
|
9457
9600
|
color = getDebugColor(color);
|
|
9458
9601
|
center = box2d.vec2FromPointer(center);
|
|
9459
|
-
drawCircle(center, radius, CLEAR_WHITE, debugLineWidth, color, false, false, overlayContext);
|
|
9602
|
+
drawCircle(center, radius*2, CLEAR_WHITE, debugLineWidth, color, false, false, overlayContext);
|
|
9460
9603
|
};
|
|
9461
9604
|
debugDraw.DrawSolidCircle = function(center, radius, axis, color)
|
|
9462
9605
|
{
|
|
9463
9606
|
color = getDebugColor(color);
|
|
9464
9607
|
center = box2d.vec2FromPointer(center);
|
|
9465
9608
|
axis = box2d.vec2FromPointer(axis).scale(radius);
|
|
9466
|
-
drawCircle(center, radius, color, debugLineWidth, color, false, false, overlayContext);
|
|
9609
|
+
drawCircle(center, radius*2, color, debugLineWidth, color, false, false, overlayContext);
|
|
9467
9610
|
drawLine(vec2(), axis, debugLineWidth, color, center, 0, false, false, overlayContext);
|
|
9468
9611
|
};
|
|
9469
9612
|
debugDraw.DrawTransform = function(transform)
|
package/examples/box2d/game.js
CHANGED
|
@@ -136,8 +136,8 @@ function gameUpdatePost()
|
|
|
136
136
|
///////////////////////////////////////////////////////////////////////////////
|
|
137
137
|
function gameRender()
|
|
138
138
|
{
|
|
139
|
-
// draw a grey square in the background
|
|
140
|
-
LJS.drawRect(vec2(20,8), vec2(100), hsl(0,0,.8)
|
|
139
|
+
// draw a grey square in the background
|
|
140
|
+
LJS.drawRect(vec2(20,8), vec2(100), hsl(0,0,.8));
|
|
141
141
|
|
|
142
142
|
if (Scenes.scene == 5)
|
|
143
143
|
{
|
|
@@ -168,8 +168,8 @@ function gameRenderPost()
|
|
|
168
168
|
|
|
169
169
|
// draw to overlay canvas for hud rendering
|
|
170
170
|
const pos = vec2(LJS.mainCanvasSize.x/2, 50);
|
|
171
|
-
drawText('LittleJS Box2D Demo',
|
|
172
|
-
drawText(Scenes.sceneName,
|
|
171
|
+
drawText('LittleJS Box2D Demo', 65, 70);
|
|
172
|
+
drawText(Scenes.sceneName, 50, 100);
|
|
173
173
|
if (Scenes.scene == 0)
|
|
174
174
|
{
|
|
175
175
|
drawText('Mouse Left = Grab');
|
|
@@ -336,7 +336,7 @@ Then increment it in the Brick.collideWithObject function.
|
|
|
336
336
|
And change that drawTextScreen code we had commented out in gameRenderPost to display the score. We can adjust the position and size of the text to fit at the top of the screen.
|
|
337
337
|
|
|
338
338
|
```javascript
|
|
339
|
-
LJS.drawTextScreen(
|
|
339
|
+
LJS.drawTextScreen('Score ' + score, vec2(LJS.mainCanvasSize.x/2, 70), 50); // show score
|
|
340
340
|
```
|
|
341
341
|
|
|
342
342
|

|
|
@@ -183,7 +183,7 @@ function gameRender()
|
|
|
183
183
|
///////////////////////////////////////////////////////////////////////////////
|
|
184
184
|
function gameRenderPost()
|
|
185
185
|
{
|
|
186
|
-
LJS.drawTextScreen(
|
|
186
|
+
LJS.drawTextScreen('Score ' + score, vec2(LJS.mainCanvasSize.x/2, 70), 50); // show score
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -105,7 +105,7 @@ function htmlBuildStep(filename)
|
|
|
105
105
|
buffer += '<meta charset=utf-8>';
|
|
106
106
|
buffer += '</head>';
|
|
107
107
|
buffer += '<body>';
|
|
108
|
-
buffer +=
|
|
108
|
+
buffer += `<script src='index.js'></script>`;
|
|
109
109
|
|
|
110
110
|
// output html file
|
|
111
111
|
fs.writeFileSync(`${BUILD_FOLDER}/index.html`, buffer, {flag: 'w+'});
|