littlejsengine 1.24.2 → 1.25.0
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/CHANGELOG.md +55 -0
- package/EDITOR.md +2 -2
- package/FAQ.md +6 -0
- package/REFERENCE.md +86 -41
- package/dist/littlejs.d.ts +600 -323
- package/dist/littlejs.esm.js +2412 -1797
- package/dist/littlejs.esm.min.js +38 -24
- package/dist/littlejs.js +2410 -1797
- package/dist/littlejs.min.js +37 -23
- package/dist/littlejs.release.js +2393 -1826
- package/package.json +1 -1
- package/plugins/box2d.js +41 -28
- package/plugins/drawUtilities.js +374 -380
- package/plugins/gltf.js +331 -55
- package/plugins/lightSystem.js +2 -2
- package/plugins/math3d.js +1013 -1014
- package/plugins/newgrounds.js +1 -4
- package/plugins/particleEffects.js +3 -4
- package/plugins/pathFinder.js +52 -28
- package/plugins/pluginExport.js +2 -0
- package/plugins/postProcess.js +196 -65
- package/plugins/render3d.js +8 -6
- package/plugins/render3dEditor.js +3 -3
- package/plugins/render3dExtras.js +31 -10
- package/plugins/render3dLevel.js +15 -4
- package/plugins/textureSheet.js +15 -7
- package/plugins/tweenSystem.js +43 -38
- package/plugins/uiSystem.js +25 -20
- package/src/engine.js +39 -21
- package/src/engineAudio.js +13 -9
- package/src/engineBuild.mjs +49 -2
- package/src/engineDebug.js +3 -2
- package/src/engineDraw.js +20 -18
- package/src/engineEditor.js +75 -23
- package/src/engineInput.js +10 -10
- package/src/engineMath.js +3 -6
- package/src/engineObject.js +8 -9
- package/src/engineParticles.js +8 -7
- package/src/engineRelease.js +17 -10
- package/src/engineTileLayer.js +33 -14
- package/src/engineUtilities.js +15 -2
- package/src/engineWebGL.js +34 -12
package/src/engineParticles.js
CHANGED
|
@@ -37,13 +37,13 @@
|
|
|
37
37
|
* let pos = vec2(2,3);
|
|
38
38
|
* let particleEmitter = new ParticleEmitter
|
|
39
39
|
* (
|
|
40
|
-
* pos, 0, 1, 0, 500, PI, // pos, angle, emitSize, emitTime, emitRate,
|
|
40
|
+
* pos, 0, 1, 0, 500, PI, // pos, angle, emitSize, emitTime, emitRate, emitConeAngle
|
|
41
41
|
* tile(0, 16), // tileInfo
|
|
42
42
|
* rgb(1,1,1,1), rgb(0,0,0,1), // colorStartA, colorStartB
|
|
43
43
|
* rgb(1,1,1,0), rgb(0,0,0,0), // colorEndA, colorEndB
|
|
44
|
-
* 1, .2, .2, .1, .05, // particleTime, sizeStart, sizeEnd,
|
|
45
|
-
* .99, 1, 1, PI, .05, // damping, angleDamping, gravityScale,
|
|
46
|
-
* .5, true // randomness,
|
|
44
|
+
* 1, .2, .2, .1, .05, // particleTime, sizeStart, sizeEnd, speed, angleSpeed
|
|
45
|
+
* .99, 1, 1, PI, .05, // damping, angleDamping, gravityScale, particleConeAngle, fadeRate
|
|
46
|
+
* .5, true // randomness, collideLevel
|
|
47
47
|
* );
|
|
48
48
|
*/
|
|
49
49
|
class ParticleEmitter extends EngineObject
|
|
@@ -336,7 +336,8 @@ class ParticleEmitter extends EngineObject
|
|
|
336
336
|
this.additive && setAdditiveBlendMode(false);
|
|
337
337
|
}
|
|
338
338
|
|
|
339
|
-
/** is emitter actively spawning
|
|
339
|
+
/** is emitter actively spawning
|
|
340
|
+
* @return {boolean} */
|
|
340
341
|
isActive() { return !this.emitTime || this.getAliveTime() < this.emitTime; }
|
|
341
342
|
|
|
342
343
|
/** Destroy the particle emitter
|
|
@@ -548,8 +549,8 @@ class Particle
|
|
|
548
549
|
}
|
|
549
550
|
if (isBlockedY || !isBlockedX)
|
|
550
551
|
{
|
|
551
|
-
// down is the world's gravity
|
|
552
|
-
const down =
|
|
552
|
+
// down is the way it falls, the world's gravity by its gravityScale and the emitter's own
|
|
553
|
+
const down = gravityY;
|
|
553
554
|
const wasFalling = this.velocity.y < 0 && down < 0 || this.velocity.y > 0 && down > 0;
|
|
554
555
|
if (wasFalling)
|
|
555
556
|
this.groundObject = hitLayer;
|
package/src/engineRelease.js
CHANGED
|
@@ -10,18 +10,17 @@
|
|
|
10
10
|
|
|
11
11
|
'use strict';
|
|
12
12
|
|
|
13
|
-
let debugWatermark =
|
|
13
|
+
let debugWatermark = false;
|
|
14
14
|
let debugKey = '';
|
|
15
15
|
let debugKeysAlways = false;
|
|
16
16
|
let debugTweakables = false;
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const debugSound = 0;
|
|
17
|
+
const debug = false;
|
|
18
|
+
const debugOverlay = false;
|
|
19
|
+
const debugPhysics = false;
|
|
20
|
+
const debugParticles = false;
|
|
21
|
+
const debugRaycast = false;
|
|
22
|
+
const debugGamepads = false;
|
|
23
|
+
const debugSound = false;
|
|
25
24
|
const debugPointSize = .5;
|
|
26
25
|
|
|
27
26
|
// debug commands are automatically removed from the final build
|
|
@@ -52,6 +51,14 @@ function tweak(){}
|
|
|
52
51
|
function tweakButton(){}
|
|
53
52
|
function tweakDivider(){}
|
|
54
53
|
function tweakEngineDefaults(){}
|
|
54
|
+
// the 2D editor's edit functions in a release build, which has no editor: there is nothing to edit and nothing
|
|
55
|
+
// changes, and a game that calls them needs no guard; edit3D stays undefined, as it is with no 3D plugins
|
|
56
|
+
const editorEdit2DRelease =
|
|
57
|
+
{
|
|
58
|
+
map: undefined, layer: undefined, hover: undefined, get objects() { return []; }, get selection() { return new Set; },
|
|
59
|
+
paint(){ return false; }, changeObjects(){ return false; }, strokeEnd(){}, strokeCancel(){}, bulk(){},
|
|
60
|
+
undo(){ return false; }, toJSON(){ return ''; },
|
|
61
|
+
};
|
|
55
62
|
class LevelEditor
|
|
56
63
|
{
|
|
57
64
|
constructor()
|
|
@@ -61,7 +68,7 @@ class LevelEditor
|
|
|
61
68
|
}
|
|
62
69
|
get isOpen() { return false; }
|
|
63
70
|
get is3D() { return false; }
|
|
64
|
-
get edit2D() { return
|
|
71
|
+
get edit2D() { return editorEdit2DRelease; }
|
|
65
72
|
get edit3D() { return undefined; }
|
|
66
73
|
open(){} close(){} addKey(){} addButton(){} addTool(){}
|
|
67
74
|
onTile(){} onRestart(){} onPlayFrom(){} onOpen(){} onClose(){} onUpdate(){} onDraw(){} onPanel(){}
|
package/src/engineTileLayer.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* - TileLayer for rendering, TileCollisionLayer for physics
|
|
8
8
|
* - Collision callbacks for tile interactions with objects
|
|
9
9
|
* - Optimized raycast support for tile-based physics
|
|
10
|
-
* - Integration with Box2D physics via
|
|
10
|
+
* - Integration with Box2D physics via the Box2dTileLayer plugin
|
|
11
11
|
* @namespace TileLayers
|
|
12
12
|
*/
|
|
13
13
|
|
|
@@ -16,6 +16,28 @@
|
|
|
16
16
|
///////////////////////////////////////////////////////////////////////////////
|
|
17
17
|
// Tile Layer System
|
|
18
18
|
|
|
19
|
+
/** A Tiled map as Tiled saves it as JSON, what tileLayersLoad and objectLayersLoad take and tileLayersFromLDtk
|
|
20
|
+
* makes; the fields read are listed, and the rest of the file is kept as it is
|
|
21
|
+
* @typedef {Object} TiledMap
|
|
22
|
+
* @property {number} width - Cells across
|
|
23
|
+
* @property {number} height - Cells up
|
|
24
|
+
* @property {number} [tilewidth] - A tile's width in pixels
|
|
25
|
+
* @property {number} [tileheight] - A tile's height in pixels
|
|
26
|
+
* @property {Array<Object>} layers - Tile layers, object layers and groups of them, bottom first
|
|
27
|
+
* @property {Array<Object>} [tilesets] - The first one's margin, spacing and columns are read
|
|
28
|
+
* @property {number} [nextlayerid]
|
|
29
|
+
* @property {number} [nextobjectid]
|
|
30
|
+
* @property {string} [orientation]
|
|
31
|
+
* @property {string} [renderorder]
|
|
32
|
+
* @property {boolean} [infinite]
|
|
33
|
+
* @property {string} [type]
|
|
34
|
+
* @property {string|number} [version] - A number in files from Tiled before 1.6
|
|
35
|
+
* @property {string} [tiledversion]
|
|
36
|
+
* @property {number} [compressionlevel]
|
|
37
|
+
* @property {string} [backgroundcolor]
|
|
38
|
+
* @property {Array<Object>} [properties]
|
|
39
|
+
* @memberof TileLayers */
|
|
40
|
+
|
|
19
41
|
/** Keep track of all tile layers with collision
|
|
20
42
|
* @type {Array<TileCollisionLayer>}
|
|
21
43
|
* @memberof TileLayers */
|
|
@@ -90,7 +112,7 @@ function tileCollisionTest(pos, size=vec2(), callbackObject, solidOnly=true)
|
|
|
90
112
|
* @param {Vector2} posEnd
|
|
91
113
|
* @param {EngineObject|TileCollisionCallback} [callbackObject] - Callback, engine object, or undefined
|
|
92
114
|
* @param {Vector2} [normal] - Optional normal of the surface hit
|
|
93
|
-
* @param {boolean} [solidOnly
|
|
115
|
+
* @param {boolean} [solidOnly] - Only check solid layers?
|
|
94
116
|
* @return {Vector2|undefined} - where the ray meets the first tile hit, nudged just inside it, or undefined if no hit
|
|
95
117
|
* @memberof TileLayers */
|
|
96
118
|
function tileCollisionRaycast(posStart, posEnd, callbackObject, normal, solidOnly=true)
|
|
@@ -151,7 +173,7 @@ function tileLayerTileInfo(t, tile)
|
|
|
151
173
|
* - An object or image layer keeps its index, with its slot in the returned array left empty
|
|
152
174
|
* - A hidden layer (visible false) is loaded, its collision included, but not drawn; its render
|
|
153
175
|
* is a no-op, delete that and call redraw() to show it
|
|
154
|
-
* @param {
|
|
176
|
+
* @param {TiledMap} [tileMapData] - Level data from exported data, a 50 by 50 empty level when left out
|
|
155
177
|
* @param {TileInfo} [tileInfo] - Default tile info (used for size and texture), tile() by default, none when no image is loaded
|
|
156
178
|
* @param {number} [renderOrder] - Render order of the top layer
|
|
157
179
|
* @param {number|string} [collisionLayer] - Layer to use for collision if any, by its index or its name
|
|
@@ -165,10 +187,7 @@ function tileLayersLoad(tileMapData, tileInfo=tileLayerDefaultTile(), renderOrde
|
|
|
165
187
|
{
|
|
166
188
|
// default level data if loading failed
|
|
167
189
|
const s = 50;
|
|
168
|
-
tileMapData = {};
|
|
169
|
-
tileMapData.height = tileMapData.width = s;
|
|
170
|
-
tileMapData.layers = [{}];
|
|
171
|
-
tileMapData.layers[0].data = new Array(s*s).fill(0);
|
|
190
|
+
tileMapData = {width: s, height: s, layers: [{data: new Array(s*s).fill(0)}]};
|
|
172
191
|
}
|
|
173
192
|
|
|
174
193
|
// the editor, in debug builds, keeps the map as the source of its edits and brings back autosaved ones
|
|
@@ -310,7 +329,7 @@ const tileLayersLDtkTypes = {Int: 'int', Float: 'float', Bool: 'bool', String: '
|
|
|
310
329
|
* - The level editor edits the map this returns, and saves it as a Tiled map
|
|
311
330
|
* @param {Object} ldtk - The LDtk project, its JSON
|
|
312
331
|
* @param {number|string} [level] - Which level, by its index or its identifier
|
|
313
|
-
* @return {
|
|
332
|
+
* @return {TiledMap} - A Tiled map: width, height, tilewidth, tileheight, tilesets and layers
|
|
314
333
|
* @example
|
|
315
334
|
* const map = tileLayersFromLDtk(await fetchJSON('world.ldtk'), 'Level_0');
|
|
316
335
|
* const layers = tileLayersLoad(map, tile(0, 16), 0, 'Collisions'); // its IntGrid layer is solid
|
|
@@ -441,7 +460,7 @@ const objectLayersTypes = new Map;
|
|
|
441
460
|
* - Adding a name again replaces it
|
|
442
461
|
* @param {string} name - The type the objects have in Tiled
|
|
443
462
|
* @param {Function} make - A class made at each object's position, or a function called with it
|
|
444
|
-
* @param {Object} [defaults] - Properties set on each one made, the level editor shows inputs for them
|
|
463
|
+
* @param {Object<string, any>} [defaults] - Properties set on each one made, the level editor shows inputs for them
|
|
445
464
|
* @param {TileInfo} [tileInfo] - An icon for the level editor
|
|
446
465
|
* @memberof TileLayers
|
|
447
466
|
* @example
|
|
@@ -462,7 +481,7 @@ function objectLayersAddType(name, make, defaults={}, tileInfo)
|
|
|
462
481
|
* - The object's properties in Tiled are set over the type's defaults: numbers, booleans, strings, and colors,
|
|
463
482
|
* and for a Vector2 default the string x,y
|
|
464
483
|
* - An object whose type was not added is skipped, with a warning in debug builds
|
|
465
|
-
* @param {
|
|
484
|
+
* @param {TiledMap} tileMapData - The same Tiled map given to tileLayersLoad
|
|
466
485
|
* @return {Array<any>} - What each object's type made, a function that made nothing is left out
|
|
467
486
|
* @memberof TileLayers */
|
|
468
487
|
function objectLayersLoad(tileMapData)
|
|
@@ -556,7 +575,7 @@ class TileLayerData
|
|
|
556
575
|
/** Create a tile layer data object, one for each tile in a TileLayer
|
|
557
576
|
* @param {number} [tile] - The tile to use, from 0 like tile(); undefined is an empty cell that draws nothing
|
|
558
577
|
* @param {number} [direction] - Integer direction of tile, in 90 degree increments
|
|
559
|
-
* @param {boolean} [mirror] - If the tile
|
|
578
|
+
* @param {boolean} [mirror] - If the tile is flipped left to right
|
|
560
579
|
* @param {Color} [color] - Color of the tile */
|
|
561
580
|
constructor(tile, direction=0, mirror=false, color=new Color)
|
|
562
581
|
{
|
|
@@ -565,7 +584,7 @@ class TileLayerData
|
|
|
565
584
|
this.tile = tile;
|
|
566
585
|
/** @property {number} - Integer direction of tile, in 90 degree increments */
|
|
567
586
|
this.direction = direction;
|
|
568
|
-
/** @property {boolean} - If the tile
|
|
587
|
+
/** @property {boolean} - If the tile is flipped left to right */
|
|
569
588
|
this.mirror = mirror;
|
|
570
589
|
/** @property {Color} - Color of the tile */
|
|
571
590
|
this.color = color.copy();
|
|
@@ -641,7 +660,7 @@ class CanvasLayer extends EngineObject
|
|
|
641
660
|
* @param {Vector2} [size] - Size in world space
|
|
642
661
|
* @param {Color} [color] - Color to modulate with
|
|
643
662
|
* @param {number} [angle] - Angle to rotate by
|
|
644
|
-
* @param {boolean} [mirror] - If true image is flipped
|
|
663
|
+
* @param {boolean} [mirror] - If true the image is flipped left to right
|
|
645
664
|
* @param {Color} [additiveColor] - Additive color to be applied if any
|
|
646
665
|
* @param {boolean} [screenSpace=drawScreenSpace] - If true the pos and size are in screen space
|
|
647
666
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context] - Canvas 2D context to draw to */
|
|
@@ -931,7 +950,7 @@ class TileLayer extends CanvasLayer
|
|
|
931
950
|
* @param {TileInfo} [tileInfo] - Tile info to use, untextured if undefined
|
|
932
951
|
* @param {Color} [color=WHITE] - Color to modulate with
|
|
933
952
|
* @param {number} [angle] - Angle to rotate by
|
|
934
|
-
* @param {boolean} [mirror] - Is image flipped
|
|
953
|
+
* @param {boolean} [mirror] - Is the image flipped left to right?
|
|
935
954
|
* @param {Color} [additiveColor] - Additive color to be applied if any */
|
|
936
955
|
drawLayerTile(pos, size=vec2(1), tileInfo, color=WHITE,
|
|
937
956
|
angle=0, mirror, additiveColor)
|
package/src/engineUtilities.js
CHANGED
|
@@ -117,18 +117,29 @@ function formatTime(t)
|
|
|
117
117
|
|
|
118
118
|
/** Fetches a JSON file from a URL and returns the parsed JSON object. Must be used with await!
|
|
119
119
|
* @param {string} url - URL of JSON file
|
|
120
|
-
* @return {Promise<
|
|
120
|
+
* @return {Promise<any>} - The parsed JSON, any shape, as response.json() gives it
|
|
121
121
|
* @memberof Utilities */
|
|
122
122
|
async function fetchJSON(url)
|
|
123
123
|
{
|
|
124
124
|
const response = await fetch(url);
|
|
125
125
|
if (!response.ok)
|
|
126
126
|
throw new Error(`Failed to fetch JSON from ${url}: ${response.status} ${response.statusText}`);
|
|
127
|
-
const
|
|
127
|
+
const text = await response.text();
|
|
128
|
+
let json;
|
|
129
|
+
try { json = JSON.parse(text); }
|
|
130
|
+
catch (e)
|
|
131
|
+
{
|
|
132
|
+
// a dev server answers a mistyped path with its own page, which is no JSON, so the error says which file
|
|
133
|
+
throw new Error(`fetchJSON: ${url} is not JSON` + (loadIsWebPage(text) ?
|
|
134
|
+
', it is a web page, so the path may be wrong' : ': ' + e.message));
|
|
135
|
+
}
|
|
128
136
|
editorJSONFetched(url, json); // debug builds remember the file a level came from, to save it by that name
|
|
129
137
|
return json;
|
|
130
138
|
}
|
|
131
139
|
|
|
140
|
+
// is a file's text a web page, as a dev server sends for a path it does not have
|
|
141
|
+
const loadIsWebPage = (text)=> /^\s*</.test(text);
|
|
142
|
+
|
|
132
143
|
///////////////////////////////////////////////////////////////////////////////
|
|
133
144
|
|
|
134
145
|
/** Save a text file to disk
|
|
@@ -239,6 +250,8 @@ function readSaveData(saveName, defaultSaveData)
|
|
|
239
250
|
{
|
|
240
251
|
try { loadedData = JSON.parse(data); }
|
|
241
252
|
catch { LOG('readSaveData: corrupt JSON for', saveName, '— using defaults'); }
|
|
253
|
+
if (!loadedData || typeof loadedData !== 'object' || isArray(loadedData))
|
|
254
|
+
loadedData = {}; // only an object of saved values, never a string spread into its letters
|
|
242
255
|
}
|
|
243
256
|
}
|
|
244
257
|
catch { LOG('readSaveData: localStorage unavailable — using defaults'); }
|
package/src/engineWebGL.js
CHANGED
|
@@ -46,11 +46,12 @@ let glAntialias = true;
|
|
|
46
46
|
|
|
47
47
|
// WebGL internal variables not exposed to documentation
|
|
48
48
|
let glMipmappedTextures = new WeakSet, glMipmapsUntilTarget = new WeakSet, glMipmapsStale = new Set, glPremultipliedTextures = new WeakSet, glShaderPremultiplied, glEnableBeforeLoss = true, glShader, glPolyShader, glPolyMode, glAdditive, glBatchAdditive, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glBatchCount, glTextureInfos = new Set, glInstancedVAO, glPolyVAO, glFramebuffer, glRenderTarget, glShaderObjects = [], glCustomShader, glBatchShader, glProgramCustom, glTransform, glRenderTargetSaved, glUniformLocations = new WeakMap, glCanBeEnabled = true;
|
|
49
|
+
let glFailedPrograms = new WeakSet; // programs that did not build in a release build, which nothing draws with
|
|
49
50
|
// ANDed onto every packed color as a draw is queued; the light system's shadow pass sets 0xff000000
|
|
50
51
|
// to draw everything black with its alpha kept (rgbaInt packs alpha in the top byte)
|
|
51
52
|
let glColorMask = -1;
|
|
52
53
|
// ORed onto the additive color of every quad and onto every poly point's color as a draw is queued; the light system's
|
|
53
|
-
// emissive pass sets a
|
|
54
|
+
// emissive pass sets a gray with the mask at 0xff000000, so a draw comes out that gray in its own shape
|
|
54
55
|
let glColorAdditive = 0;
|
|
55
56
|
// a texture drawn into with the canvas's own transform and its size, the light system's lightmap while its pass
|
|
56
57
|
// runs: a target set and ended inside it, like a tile layer redrawn in a renderLight, goes back to it, not the canvas
|
|
@@ -218,6 +219,14 @@ function glInit(rootElement)
|
|
|
218
219
|
'}' // end of shader
|
|
219
220
|
);
|
|
220
221
|
|
|
222
|
+
// a device that can not build the engine's own programs draws with Canvas2D, as after a lost context
|
|
223
|
+
if (glFailedPrograms.has(glShader) || glFailedPrograms.has(glPolyShader))
|
|
224
|
+
{
|
|
225
|
+
console.error('LittleJS: WebGL can not draw on this device, using Canvas2D');
|
|
226
|
+
glEnable = glCanBeEnabled = false;
|
|
227
|
+
glCanvas.style.display = 'none';
|
|
228
|
+
}
|
|
229
|
+
|
|
221
230
|
// init buffers
|
|
222
231
|
const glInstanceData = new ArrayBuffer(gl_ARRAY_BUFFER_SIZE);
|
|
223
232
|
glPositionData = new Float32Array(glInstanceData);
|
|
@@ -436,7 +445,7 @@ function glSetTextureWrap(texture, wrap=true)
|
|
|
436
445
|
/** Compile WebGL shader of the given type, will throw errors if in debug mode
|
|
437
446
|
* @param {string} source
|
|
438
447
|
* @param {number} type
|
|
439
|
-
* @return {WebGLShader}
|
|
448
|
+
* @return {WebGLShader|undefined} - undefined with no WebGL
|
|
440
449
|
* @memberof WebGL */
|
|
441
450
|
function glCompileShader(source, type)
|
|
442
451
|
{
|
|
@@ -456,7 +465,7 @@ function glCompileShader(source, type)
|
|
|
456
465
|
/** Create WebGL program with given shaders
|
|
457
466
|
* @param {string} vsSource
|
|
458
467
|
* @param {string} fsSource
|
|
459
|
-
* @return {WebGLProgram}
|
|
468
|
+
* @return {WebGLProgram|undefined} - undefined with no WebGL
|
|
460
469
|
* @memberof WebGL */
|
|
461
470
|
function glCreateProgram(vsSource, fsSource)
|
|
462
471
|
{
|
|
@@ -464,13 +473,23 @@ function glCreateProgram(vsSource, fsSource)
|
|
|
464
473
|
|
|
465
474
|
// build the program
|
|
466
475
|
const program = glContext.createProgram();
|
|
467
|
-
|
|
468
|
-
|
|
476
|
+
const vertexShader = glCompileShader(vsSource, glContext.VERTEX_SHADER);
|
|
477
|
+
const fragmentShader = glCompileShader(fsSource, glContext.FRAGMENT_SHADER);
|
|
478
|
+
glContext.attachShader(program, vertexShader);
|
|
479
|
+
glContext.attachShader(program, fragmentShader);
|
|
469
480
|
glContext.linkProgram(program);
|
|
470
481
|
|
|
471
|
-
// check for errors
|
|
472
|
-
|
|
473
|
-
|
|
482
|
+
// check for errors in every build, as a driver may refuse what another takes: a debug build throws, a release
|
|
483
|
+
// one says why once and marks the program, which the engine's draws then do without
|
|
484
|
+
if (!glContext.getProgramParameter(program, glContext.LINK_STATUS))
|
|
485
|
+
{
|
|
486
|
+
const log = glContext.getProgramInfoLog(program) + '\n' +
|
|
487
|
+
glContext.getShaderInfoLog(vertexShader) + glContext.getShaderInfoLog(fragmentShader);
|
|
488
|
+
if (debug)
|
|
489
|
+
throw log;
|
|
490
|
+
console.error('LittleJS: a shader failed to build\n' + log);
|
|
491
|
+
glFailedPrograms.add(program);
|
|
492
|
+
}
|
|
474
493
|
return program;
|
|
475
494
|
}
|
|
476
495
|
|
|
@@ -486,7 +505,7 @@ function glUniformLocation(program, name)
|
|
|
486
505
|
// color, then the sprite's color and additive color apply as the engine's own fragment shader does
|
|
487
506
|
function glShaderProgram(shader)
|
|
488
507
|
{
|
|
489
|
-
|
|
508
|
+
const program = shader.program ||= glCreateProgram(gl_VERTEX_SOURCE,
|
|
490
509
|
'#version 300 es\n' +
|
|
491
510
|
'precision highp float;' +
|
|
492
511
|
'uniform sampler2D iChannel0;' + // the texture
|
|
@@ -497,6 +516,7 @@ function glShaderProgram(shader)
|
|
|
497
516
|
'#define localUV l\n' +
|
|
498
517
|
shader.fragmentCode + '\n' +
|
|
499
518
|
'void main(){vec4 t;mainImage(t,v);' + gl_FRAGMENT_TINT_SOURCE + '}');
|
|
519
|
+
return glFailedPrograms.has(program) ? glShader : program;
|
|
500
520
|
}
|
|
501
521
|
|
|
502
522
|
/** Create WebGL texture from an image and init the texture settings
|
|
@@ -504,7 +524,7 @@ function glShaderProgram(shader)
|
|
|
504
524
|
* @param {HTMLImageElement|HTMLCanvasElement|OffscreenCanvas|ImageBitmap} [image]
|
|
505
525
|
* @param {boolean|Array<number>} [wrap] - true for REPEAT, false for CLAMP_TO_EDGE, or the WebGL modes across and down
|
|
506
526
|
* @param {boolean} [pixelated] - Hard edged or smooth, undefined follows tilesPixelated
|
|
507
|
-
* @return {WebGLTexture}
|
|
527
|
+
* @return {WebGLTexture|undefined} - undefined with no WebGL
|
|
508
528
|
* @memberof WebGL */
|
|
509
529
|
function glCreateTexture(image, wrap=false, pixelated=tilesPixelated)
|
|
510
530
|
{
|
|
@@ -673,6 +693,8 @@ function glFlush()
|
|
|
673
693
|
const height = glRenderTarget ? mainCanvasSize.y : glCanvas.height;
|
|
674
694
|
glContext.uniform3f(uniform('iResolution'), width, height, 1);
|
|
675
695
|
glContext.uniform1i(uniform('premultipliedTexture'), +premultiplied);
|
|
696
|
+
if (program === glShader)
|
|
697
|
+
glShaderPremultiplied = premultiplied; // a Shader that did not build draws with the engine's
|
|
676
698
|
}
|
|
677
699
|
}
|
|
678
700
|
if (!glPolyMode && !glBatchShader && glShaderPremultiplied !== premultiplied)
|
|
@@ -730,8 +752,8 @@ function glSetAntialias(antialias=true)
|
|
|
730
752
|
* @param {number} [uv0Y]
|
|
731
753
|
* @param {number} [uv1X]
|
|
732
754
|
* @param {number} [uv1Y]
|
|
733
|
-
* @param {number} [rgba
|
|
734
|
-
* @param {number} [rgbaAdditive
|
|
755
|
+
* @param {number} [rgba] - white is -1
|
|
756
|
+
* @param {number} [rgbaAdditive] - black is 0
|
|
735
757
|
* @memberof WebGL */
|
|
736
758
|
function glDraw(x, y, sizeX, sizeY, angle=0, uv0X=0, uv0Y=0, uv1X=1, uv1Y=1, rgba=-1, rgbaAdditive=0)
|
|
737
759
|
{
|