littlejsengine 1.25.0 → 1.26.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 +87 -0
- package/FAQ.md +49 -11
- package/README.md +6 -2
- package/REFERENCE.md +120 -18
- package/dist/littlejs.d.ts +464 -99
- package/dist/littlejs.esm.js +2853 -704
- package/dist/littlejs.esm.min.js +3 -3
- package/dist/littlejs.js +2844 -701
- package/dist/littlejs.min.js +2 -2
- package/dist/littlejs.release.js +2658 -512
- package/package.json +4 -4
- package/plugins/box2d.js +110 -34
- package/plugins/gltf.js +74 -22
- package/plugins/lightSystem.js +410 -28
- package/plugins/math3d.js +4 -2
- package/plugins/medalSystem.js +19 -7
- package/plugins/newgrounds.js +4 -2
- package/plugins/parallax.js +1 -0
- package/plugins/particleEffects.js +44 -10
- package/plugins/pathFinder.js +9 -11
- package/plugins/pluginExport.js +9 -0
- package/plugins/postProcess.js +4 -2
- package/plugins/render3d.js +485 -2385
- package/plugins/render3dDebug.js +5 -8
- package/plugins/render3dEditor.js +81 -6
- package/plugins/render3dExtras.js +103 -31
- package/plugins/render3dLevel.js +53 -5
- package/plugins/render3dMesh.js +1158 -0
- package/plugins/render3dObject.js +1451 -0
- package/plugins/render3dVoxels.js +15 -3
- package/plugins/textureSheet.js +9 -8
- package/plugins/threejs.js +1 -1
- package/plugins/tweenSystem.js +76 -51
- package/plugins/uiSystem.js +37 -8
- package/plugins/wavedash.js +229 -0
- package/src/engine.js +184 -36
- package/src/engineAudio.js +37 -21
- package/src/engineBuild.mjs +6 -1
- package/src/engineDebug.js +22 -162
- package/src/engineDraw.js +139 -34
- package/src/engineEditor.js +79 -18
- package/src/engineExport.js +0 -3
- package/src/engineInput.js +27 -18
- package/src/engineMath.js +35 -14
- package/src/engineObject.js +60 -41
- package/src/engineParticles.js +58 -11
- package/src/engineRelease.js +1 -5
- package/src/engineSettings.js +48 -9
- package/src/engineTileLayer.js +11 -4
- package/src/engineUtilities.js +41 -9
- package/src/engineWebGL.js +37 -13
package/plugins/medalSystem.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* - Medal class with name, description, icon, and unlock tracking
|
|
5
5
|
* - Automatic saving to local storage, unless a service like Newgrounds holds the medal (see Medal.isLocal)
|
|
6
6
|
* - Visual display queue with slide-in notifications
|
|
7
|
-
* - The Newgrounds plugin extends it with NewgroundsMedal, held on the server while logged in
|
|
8
|
-
* - Setting debugMedals = true in the game code before medalsInit skips the load and the save, and in the debug build logs the Newgrounds traffic; it is not exported, so only a script tag build can set it
|
|
7
|
+
* - The Newgrounds plugin extends it with NewgroundsMedal, held on the server while logged in, and the Wavedash plugin with WavedashMedal, saved here and sent to Wavedash
|
|
8
|
+
* - Setting debugMedals = true in the game code before medalsInit skips the load and the save, sends nothing to Wavedash, and in the debug build logs the Newgrounds traffic; it is not exported, so only a script tag build can set it
|
|
9
9
|
* @namespace Medals
|
|
10
10
|
*/
|
|
11
11
|
|
|
@@ -54,13 +54,25 @@ let medalsDisplayQueue = [], medalsSaveName, medalsDisplayTimeLast, medalsRender
|
|
|
54
54
|
* - Call this after creating all medals
|
|
55
55
|
* - Loads which medals are unlocked from the save, and writes the catalog back
|
|
56
56
|
* - A medal a service like Newgrounds holds is left as it is, see Medal.isLocal
|
|
57
|
+
* - A medal unlocked before the first call stays unlocked and is saved by it
|
|
57
58
|
* @param {string} saveName - The localStorage key the medals are kept under, a different one from the game's own
|
|
58
59
|
* readSaveData and writeSaveData, or each would overwrite the other
|
|
59
60
|
* @memberof Medals */
|
|
60
61
|
function medalsInit(saveName)
|
|
61
62
|
{
|
|
63
|
+
// unlocks from before the first call, which had nowhere to be saved, are kept: medals favour the player; a call
|
|
64
|
+
// after that, another player's save, starts from its own
|
|
65
|
+
const early = [];
|
|
66
|
+
medalsSaveName || medalsForEach((medal)=> medal.unlocked && medal.isLocal() && early.push(medal));
|
|
62
67
|
medalsSaveName = saveName;
|
|
63
68
|
medalsLoad();
|
|
69
|
+
if (early.length)
|
|
70
|
+
{
|
|
71
|
+
early.forEach((medal)=> medal.unlocked = true);
|
|
72
|
+
medalsSave();
|
|
73
|
+
}
|
|
74
|
+
// the saved unlocks of Wavedash medals go to Wavedash, when that plugin is in the build and started
|
|
75
|
+
typeof wavedash != 'undefined' && wavedash?.sendAchievements();
|
|
64
76
|
|
|
65
77
|
// add the medal display once, however often this is called
|
|
66
78
|
if (!medalsRenderAdded)
|
|
@@ -130,10 +142,11 @@ function medalsReset()
|
|
|
130
142
|
medalsForEach(medal=> medal.isLocal() && (medal.unlocked = false));
|
|
131
143
|
if (medalsSaveName && !debugMedals)
|
|
132
144
|
{
|
|
133
|
-
// the saved unlocks of medals not made yet are cleared too,
|
|
145
|
+
// the saved unlocks of the local medals and of medals not made yet are cleared too, since a save keeps an
|
|
146
|
+
// unlock another tab wrote, and those not made yet read theirs when they are made
|
|
134
147
|
const saved = readSaveData(medalsSaveName);
|
|
135
148
|
for (const id in saved)
|
|
136
|
-
if (!medals[id] && saved[id] && typeof saved[id] === 'object')
|
|
149
|
+
if ((!medals[id] || medals[id].isLocal()) && saved[id] && typeof saved[id] === 'object')
|
|
137
150
|
saved[id].unlocked = false;
|
|
138
151
|
writeSaveData(medalsSaveName, saved);
|
|
139
152
|
}
|
|
@@ -159,7 +172,7 @@ function medalsSave()
|
|
|
159
172
|
name: medal.name,
|
|
160
173
|
description: medal.description,
|
|
161
174
|
icon: medal.icon,
|
|
162
|
-
unlocked: medal.unlocked,
|
|
175
|
+
unlocked: medal.unlocked || !!saved[medal.id]?.unlocked, // another tab may have unlocked it since
|
|
163
176
|
};
|
|
164
177
|
if (medal.image) entry.src = medal.image.src;
|
|
165
178
|
data[medal.id] = entry;
|
|
@@ -229,8 +242,7 @@ class Medal
|
|
|
229
242
|
{
|
|
230
243
|
if (!medalsPreventUnlock && !this.unlocked)
|
|
231
244
|
{
|
|
232
|
-
|
|
233
|
-
this.unlocked = true;
|
|
245
|
+
this.unlocked = true; // saved now, or by medalsInit when it comes
|
|
234
246
|
medalsSave();
|
|
235
247
|
medalsDisplayQueue.push(this);
|
|
236
248
|
}
|
package/plugins/newgrounds.js
CHANGED
|
@@ -157,9 +157,11 @@ class NewgroundsPlugin
|
|
|
157
157
|
const hasLocation = typeof location != 'undefined';
|
|
158
158
|
/** @property {string} - Hostname sent with the view the plugin logs when it starts */
|
|
159
159
|
this.host = hasLocation ? location.hostname : '';
|
|
160
|
-
/** @property {Array} - Medals fetched from Newgrounds, empty until ready, with the unlocks only when logged in
|
|
160
|
+
/** @property {Array<Object>} - Medals fetched from Newgrounds, empty until ready, with the unlocks only when logged in
|
|
161
|
+
* @type {Array<Object>} */
|
|
161
162
|
this.medals = [];
|
|
162
|
-
/** @property {Array} - Scoreboards fetched from Newgrounds, empty until ready
|
|
163
|
+
/** @property {Array<Object>} - Scoreboards fetched from Newgrounds, empty until ready
|
|
164
|
+
* @type {Array<Object>} */
|
|
163
165
|
this.scoreboards = [];
|
|
164
166
|
/** @property {{id: number, name: string, url: string, supporter: boolean}|null} - The logged in player once ready, null when not logged in
|
|
165
167
|
* @type {{id: number, name: string, url: string, supporter: boolean}|null} */
|
package/plugins/parallax.js
CHANGED
|
@@ -47,6 +47,7 @@ class ParallaxLayer extends CanvasLayer
|
|
|
47
47
|
ASSERT(isNumber(parallax) || isVector2(parallax), 'parallax must be a number or a Vector2');
|
|
48
48
|
ASSERT(typeof drawFunction == 'function', 'drawFunction must be a function');
|
|
49
49
|
super(pos, size, 0, renderOrder, canvasSize);
|
|
50
|
+
this.castShadow = false; // a backdrop, it would throw huge shadows in the light system, a sun's above all
|
|
50
51
|
|
|
51
52
|
/** @property {Vector2} - How much of the camera's movement it follows on each axis, 0 stays with the world
|
|
52
53
|
* and 1 with the screen */
|
|
@@ -85,9 +85,9 @@ particleEffectAddSetting('randomColorLinear', 'checkbox', true, 0, 0, 0,
|
|
|
85
85
|
|
|
86
86
|
particleEffectSettingGroup = 'Motion';
|
|
87
87
|
particleEffectAddSetting('speed', 'number', .1, 0, .5, .005,
|
|
88
|
-
'Start speed, world units per frame',
|
|
88
|
+
'Start speed, world units per frame', -1e9, 1e9); // negative goes back against the cone, as the emitter takes it
|
|
89
89
|
particleEffectAddSetting('angleSpeed', 'number', .05, 0, .5, .005,
|
|
90
|
-
'Spin speed, radians per frame',
|
|
90
|
+
'Spin speed, radians per frame', -1e9, 1e9);
|
|
91
91
|
particleEffectAddSetting('damping', 'number', 1, .8, 1, .001,
|
|
92
92
|
'Speed kept each frame, 1 keeps it all', 0, 1);
|
|
93
93
|
particleEffectAddSetting('angleDamping', 'number', 1, .8, 1, .001,
|
|
@@ -107,7 +107,7 @@ particleEffectAddSetting('collideLevel', 'checkbox', false, 0, 0, 0,
|
|
|
107
107
|
particleEffectAddSetting('restitution', 'number', 0, 0, 1, .01,
|
|
108
108
|
'Bounce when hitting tiles');
|
|
109
109
|
particleEffectAddSetting('friction', 'number', .8, 0, 1, .01,
|
|
110
|
-
'Speed kept sliding along tiles');
|
|
110
|
+
'Speed kept sliding along tiles, the larger of this and the friction of the layer, as for an object');
|
|
111
111
|
|
|
112
112
|
particleEffectSettingGroup = 'Texture';
|
|
113
113
|
particleEffectAddSetting('shape', 'shape', 'soft', 0, 0, 0,
|
|
@@ -160,7 +160,8 @@ function particleEffectSanitize(raw)
|
|
|
160
160
|
{
|
|
161
161
|
// one line of text, line breaks and control characters made spaces
|
|
162
162
|
const text = typeof raw?.name === 'string' ? raw.name.replace(/[\x00-\x1f\x7f]+/g, ' ').trim() : '';
|
|
163
|
-
|
|
163
|
+
// whole characters, and no space left where it was cut
|
|
164
|
+
const name = text ? textGraphemes(text).slice(0, 60).join('').trim() : 'Effect';
|
|
164
165
|
const input = raw?.settings && typeof raw.settings === 'object' ? {...raw.settings} : {};
|
|
165
166
|
// a library saved before shapes names its tile and no shape, it keeps its tile
|
|
166
167
|
if (input.shape === undefined && isNumber(input.tileIndex))
|
|
@@ -414,7 +415,7 @@ function particleEffectShapeTile(name)
|
|
|
414
415
|
* @param {string} name
|
|
415
416
|
* @param {function(Particle, number): void} update - Pushes a 2D particle, given the strength
|
|
416
417
|
* @param {function(Particle3D, number): void} [update3D] - The same for a 3D particle, none leaves 3D alone
|
|
417
|
-
* @param {number} [min] - Strength range the designer offers
|
|
418
|
+
* @param {number} [min] - Strength range the designer offers, and the range a strength is clamped to
|
|
418
419
|
* @param {number} [max]
|
|
419
420
|
* @param {number} [value] - Strength when first added
|
|
420
421
|
* @param {string} [description]
|
|
@@ -478,17 +479,45 @@ function particleEffectSet(emitter, effect)
|
|
|
478
479
|
emitter.particleUpdateCallback = particleEffectUpdateCallback(effect.behaviors);
|
|
479
480
|
}
|
|
480
481
|
|
|
482
|
+
// the options a play takes besides the settings
|
|
483
|
+
const PARTICLE_EFFECT_OPTIONS = ['hue', 'saturation', 'scale', 'angle', 'tileInfo', 'flatten'];
|
|
484
|
+
|
|
485
|
+
// whether an option is a value its setting takes, as particleEffectSanitize keeps one
|
|
486
|
+
function particleEffectOptionValid(setting, value)
|
|
487
|
+
{
|
|
488
|
+
return setting.kind === 'checkbox' ? typeof value === 'boolean' || value === 0 || value === 1 :
|
|
489
|
+
setting.kind === 'color' ? !!particleEffectColor(value) :
|
|
490
|
+
setting.kind === 'shape' ? value === '' || particleEffectShapes.includes(value) : isNumber(value);
|
|
491
|
+
}
|
|
492
|
+
|
|
481
493
|
// an effect from a name or an effect, recolored by the options
|
|
482
494
|
function particleEffectResolve(nameOrEffect, options)
|
|
483
495
|
{
|
|
484
496
|
const found = typeof nameOrEffect == 'string' ? particleEffectsGet(nameOrEffect) : nameOrEffect;
|
|
485
497
|
ASSERT(!!found, 'no particle effect named ' + nameOrEffect);
|
|
486
498
|
if (!found) return;
|
|
487
|
-
//
|
|
499
|
+
// a vec2 emitSize is a rectangle, as ParticleEmitter takes one, unless emitRect is false, which makes a circle as
|
|
500
|
+
// wide as its x
|
|
501
|
+
if (isVector2(options.emitSize))
|
|
502
|
+
options = {...options, emitSize: options.emitSize.x, emitHeight: options.emitSize.y,
|
|
503
|
+
emitRect: options.emitRect ?? true};
|
|
504
|
+
if (debug)
|
|
505
|
+
for (const key in options)
|
|
506
|
+
ASSERT(PARTICLE_EFFECT_OPTIONS.includes(key) || particleEffectSettings.some((setting)=> setting.name === key),
|
|
507
|
+
'particleEffect: no option named ' + key);
|
|
508
|
+
// any setting in the options replaces the effect's own for this play, emitTime for a burst or a loop; one that is
|
|
509
|
+
// not a value of its kind keeps the effect's own, where the sanitizing would put the library's default
|
|
488
510
|
const settings = {...found.settings};
|
|
489
511
|
for (const setting of particleEffectSettings)
|
|
490
|
-
|
|
491
|
-
|
|
512
|
+
{
|
|
513
|
+
const value = options[setting.name];
|
|
514
|
+
if (value === undefined) continue;
|
|
515
|
+
const valid = particleEffectOptionValid(setting, value);
|
|
516
|
+
ASSERT(valid, `particleEffect: ${setting.name} must be a ${setting.kind === 'checkbox' ? 'boolean' : setting.kind}`,
|
|
517
|
+
value);
|
|
518
|
+
if (valid)
|
|
519
|
+
settings[setting.name] = value;
|
|
520
|
+
}
|
|
492
521
|
// sanitized each time, an effect may be written by hand or changed after it was added
|
|
493
522
|
const effect = particleEffectSanitize({...found, settings}), {hue=0, saturation=1} = options;
|
|
494
523
|
return hue || saturation != 1 ? particleEffectRecolor(effect, hue, saturation) : effect;
|
|
@@ -513,6 +542,7 @@ function particleEffectResolve(nameOrEffect, options)
|
|
|
513
542
|
* @memberof ParticleEffects */
|
|
514
543
|
function particleEffect(nameOrEffect, pos=vec2(), options={})
|
|
515
544
|
{
|
|
545
|
+
options ||= {}; // null is no options too
|
|
516
546
|
const effect = particleEffectResolve(nameOrEffect, options);
|
|
517
547
|
if (!effect) return;
|
|
518
548
|
const emitter = new ParticleEmitter(pos.copy());
|
|
@@ -527,8 +557,9 @@ function particleEffect(nameOrEffect, pos=vec2(), options={})
|
|
|
527
557
|
}
|
|
528
558
|
|
|
529
559
|
/** An effect with a 2D emitter's settings, to save, build again, or build in 3D with particleEffect3D; its tile is
|
|
530
|
-
* left out, since a hand made emitter's tile is its own texture and not one an effect can name, and so
|
|
531
|
-
* scale, which an effect does not keep: pass it again with options.scale
|
|
560
|
+
* left out, since a hand made emitter's tile is its own texture and not one an effect can name, and so are its
|
|
561
|
+
* scale, which an effect does not keep: pass it again with options.scale, and its renderOrder, which an effect takes
|
|
562
|
+
* from additive; a finished emitter's emit time is the time it emitted for
|
|
532
563
|
* @param {ParticleEmitter} emitter
|
|
533
564
|
* @param {string} [name]
|
|
534
565
|
* @return {Object}
|
|
@@ -539,6 +570,8 @@ function particleEffectFromEmitter(emitter, name='Effect')
|
|
|
539
570
|
for (const setting of particleEffectSettings)
|
|
540
571
|
if (!particleEffectIndirect.includes(setting.name) && emitter[setting.name] !== undefined)
|
|
541
572
|
settings[setting.name] = emitter[setting.name]; // a Color is taken as its channels by the sanitizer
|
|
573
|
+
if (emitter.emitTime < 0) // destroyed, its last particles going: as long as it emitted, not 0, for ever
|
|
574
|
+
settings.emitTime = emitter.emitElapsed;
|
|
542
575
|
settings.emitRect = !emitter.emitCircle;
|
|
543
576
|
settings.emitSize = emitter.emitSize.x;
|
|
544
577
|
settings.emitHeight = emitter.emitSize.y;
|
|
@@ -629,6 +662,7 @@ function particleEffectSet3D(emitter, effect)
|
|
|
629
662
|
* @memberof ParticleEffects */
|
|
630
663
|
function particleEffect3D(nameOrEffect, pos3D=vec3(), options={})
|
|
631
664
|
{
|
|
665
|
+
options ||= {}; // null is no options too
|
|
632
666
|
const effect = particleEffectResolve(nameOrEffect, options);
|
|
633
667
|
if (!effect) return;
|
|
634
668
|
const e = new ParticleEmitter3D(pos3D.copy());
|
package/plugins/pathFinder.js
CHANGED
|
@@ -64,10 +64,10 @@ class PathFinderNode
|
|
|
64
64
|
this.isOpen = this.isClosed = false;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
/** Reset per-search state and walkability (called by buildNodeData)
|
|
67
|
+
/** Reset per-search state and walkability (called by buildNodeData) */
|
|
68
68
|
reset() { this.walkable = false; this.cost = 0; this.resetSearch(); }
|
|
69
69
|
|
|
70
|
-
/** True if walkable and not blocked by cost
|
|
70
|
+
/** True if walkable and not blocked by cost
|
|
71
71
|
* @return {boolean} */
|
|
72
72
|
isClear()
|
|
73
73
|
{
|
|
@@ -77,7 +77,7 @@ class PathFinderNode
|
|
|
77
77
|
|
|
78
78
|
///////////////////////////////////////////////////////////////////////////////
|
|
79
79
|
|
|
80
|
-
/** Grid pathfinder using A* with two optional smoothing passes
|
|
80
|
+
/** Grid pathfinder using A* with two optional smoothing passes
|
|
81
81
|
* @memberof PathFinding
|
|
82
82
|
* @example
|
|
83
83
|
* // Tile-layer driven (most common):
|
|
@@ -177,7 +177,7 @@ class PathFinder
|
|
|
177
177
|
return 0;
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
-
/** Get the node at tile coords, or null if out of bounds
|
|
180
|
+
/** Get the node at tile coords, or null if out of bounds
|
|
181
181
|
* @param {number} x
|
|
182
182
|
* @param {number} y
|
|
183
183
|
* @return {PathFinderNode|null} */
|
|
@@ -187,7 +187,7 @@ class PathFinder
|
|
|
187
187
|
return this.nodes[x + y * this.size.x];
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
/** Convert a world-space position to integer tile coords (no clamping)
|
|
190
|
+
/** Convert a world-space position to integer tile coords (no clamping)
|
|
191
191
|
* @param {Vector2} worldPos
|
|
192
192
|
* @return {Vector2} */
|
|
193
193
|
worldToTile(worldPos)
|
|
@@ -197,7 +197,7 @@ class PathFinder
|
|
|
197
197
|
return vec2(floor(worldPos.x - ox), floor(worldPos.y - oy));
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
/** Convert integer tile coords to the world-space center of that tile
|
|
200
|
+
/** Convert integer tile coords to the world-space center of that tile
|
|
201
201
|
* @param {number} x
|
|
202
202
|
* @param {number} y
|
|
203
203
|
* @return {Vector2} */
|
|
@@ -822,13 +822,11 @@ function pathFinderNearestNode(finder, worldPos, searchRange, test)
|
|
|
822
822
|
const bound = max(0, offset - .5);
|
|
823
823
|
if (nearest && bound * bound >= nearestDistSq) break;
|
|
824
824
|
|
|
825
|
+
// only the ring itself, the inside was searched already: its top and bottom rows whole, its two ends between,
|
|
826
|
+
// in the same row by row order a full walk of the square visits them in, so ties pick the same node
|
|
825
827
|
for (let dy = -offset; dy <= offset; ++dy)
|
|
826
|
-
for (let dx = -offset; dx <= offset;
|
|
828
|
+
for (let dx = -offset, step = abs(dy) === offset ? 1 : 2*offset; dx <= offset; dx += step)
|
|
827
829
|
{
|
|
828
|
-
// only the ring itself, the inside was searched already
|
|
829
|
-
if (offset > 0 && abs(dx) !== offset && abs(dy) !== offset)
|
|
830
|
-
continue;
|
|
831
|
-
|
|
832
830
|
const node = finder.getNode(centerX + dx, centerY + dy);
|
|
833
831
|
if (!node || !test(node)) continue;
|
|
834
832
|
|
package/plugins/pluginExport.js
CHANGED
|
@@ -24,6 +24,11 @@ export
|
|
|
24
24
|
NewgroundsPlugin,
|
|
25
25
|
NewgroundsMedal,
|
|
26
26
|
|
|
27
|
+
// Wavedash
|
|
28
|
+
wavedash,
|
|
29
|
+
WavedashPlugin,
|
|
30
|
+
WavedashMedal,
|
|
31
|
+
|
|
27
32
|
// Post Process
|
|
28
33
|
postProcess,
|
|
29
34
|
PostProcessPlugin,
|
|
@@ -45,6 +50,7 @@ export
|
|
|
45
50
|
lightSystem,
|
|
46
51
|
LightSystemPlugin,
|
|
47
52
|
Light,
|
|
53
|
+
DirectionalLight,
|
|
48
54
|
|
|
49
55
|
// Audio Effects
|
|
50
56
|
AudioEffect,
|
|
@@ -151,6 +157,9 @@ export
|
|
|
151
157
|
// Render3D
|
|
152
158
|
render3D,
|
|
153
159
|
Render3DPlugin,
|
|
160
|
+
CubeMap,
|
|
161
|
+
makeCubeMap,
|
|
162
|
+
loadCubeMap,
|
|
154
163
|
Camera3D,
|
|
155
164
|
EngineObject3D,
|
|
156
165
|
Mesh,
|
package/plugins/postProcess.js
CHANGED
|
@@ -310,11 +310,13 @@ function postProcessFragmentSource(shaderCode, values={})
|
|
|
310
310
|
'return iDepthRange.z>0.?(d*(f-n)+f+n)/2.:f>0.?2.*n*f/(f+n-d*(f-n)):2.*n/max(1.-d,1e-7);}'+
|
|
311
311
|
// whether there is depth to read: a range of all 0 is none, an orthographic near plane may be behind
|
|
312
312
|
'\n#define LJS_HAS_DEPTH (iDepthRange != vec3(0))\n'+ // a define needs a line of its own
|
|
313
|
-
|
|
313
|
+
// main first, the code's mainImage declared for it, so a define in the code can not reach main
|
|
314
|
+
'void mainImage(out vec4,vec2);'+
|
|
314
315
|
'void main(){'+ // shader entry point
|
|
315
316
|
'mainImage(c,gl_FragCoord.xy);'+ // call post process function
|
|
316
317
|
'c.a=1.;'+ // always use full alpha
|
|
317
|
-
'}'
|
|
318
|
+
'}'+ // end of shader
|
|
319
|
+
'\n' + shaderCode + '\n'; // insert custom shader code
|
|
318
320
|
}
|
|
319
321
|
|
|
320
322
|
/**
|