littlejsengine 1.18.28 → 1.19.3
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/FAQ.md +5 -0
- package/README.md +207 -199
- package/dist/littlejs.d.ts +2099 -48
- package/dist/littlejs.esm.js +16251 -10315
- package/dist/littlejs.esm.min.js +13 -1
- package/dist/littlejs.js +15747 -9888
- package/dist/littlejs.min.js +13 -1
- package/dist/littlejs.release.js +15719 -9867
- package/package.json +1 -1
- package/plugins/audioEffects.js +371 -0
- package/plugins/lightSystem.js +5 -3
- package/plugins/math3d.js +870 -0
- package/plugins/medalSystem.js +280 -280
- package/plugins/pluginExport.js +181 -115
- package/plugins/postProcess.js +241 -166
- package/plugins/render3d.js +4006 -0
- package/plugins/textureSheet.js +458 -458
- package/plugins/threejs.js +6 -1
- package/plugins/tweenSystem.js +528 -526
- package/plugins/zzfxm.js +159 -166
- package/src/engine.js +172 -137
- package/src/engineAudio.js +918 -775
- package/src/engineBuild.mjs +3 -0
- package/src/engineDebug.js +15 -8
- package/src/engineDraw.js +1672 -1510
- package/src/engineExport.js +407 -396
- package/src/engineInput.js +1 -1
- package/src/engineLogo.js +4 -3
- package/src/engineMath.js +69 -0
- package/src/engineObject.js +560 -551
- package/src/engineSettings.js +759 -725
- package/src/engineTileLayer.js +3 -3
- package/src/engineUtilities.js +13 -0
- package/src/engineWebGL.js +1005 -941
package/plugins/postProcess.js
CHANGED
|
@@ -1,166 +1,241 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LittleJS Post Processing Plugin
|
|
3
|
-
* - Supports shadertoy style post processing shaders
|
|
4
|
-
* - call new PostProcessPlugin() to setup post processing
|
|
5
|
-
* - can be enabled to pass other canvases through a final shader
|
|
6
|
-
*
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
{
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
{
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
'
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
'
|
|
75
|
-
'
|
|
76
|
-
'
|
|
77
|
-
'
|
|
78
|
-
'
|
|
79
|
-
'
|
|
80
|
-
|
|
81
|
-
'
|
|
82
|
-
'
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
//
|
|
125
|
-
glContext.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
1
|
+
/**
|
|
2
|
+
* LittleJS Post Processing Plugin
|
|
3
|
+
* - Supports shadertoy style post processing shaders
|
|
4
|
+
* - call new PostProcessPlugin() to setup post processing
|
|
5
|
+
* - can be enabled to pass other canvases through a final shader
|
|
6
|
+
* - iResolution is the canvas backing store, so it grows with canvasPixelRatio
|
|
7
|
+
* like shadertoy does. Effects that use it only for uv (p/iResolution.xy) are
|
|
8
|
+
* unaffected, but ones that set a feature size from it, like scan lines, get
|
|
9
|
+
* finer as the ratio rises. Divide by getCanvasPixelRatio() to pin them.
|
|
10
|
+
* @namespace PostProcess
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
16
|
+
|
|
17
|
+
/** Global Post Process plugin object
|
|
18
|
+
* @type {PostProcessPlugin}
|
|
19
|
+
* @memberof PostProcess */
|
|
20
|
+
let postProcess;
|
|
21
|
+
|
|
22
|
+
/////////////////////////////////////////////////////////////////////////
|
|
23
|
+
/**
|
|
24
|
+
* Post Process Plugin - Applies a full screen shader to the rendered output
|
|
25
|
+
* - Create it after any plugin that draws, since plugins render in the order they are made
|
|
26
|
+
* and this one shades what is on the canvas when its turn comes
|
|
27
|
+
* @memberof PostProcess
|
|
28
|
+
*/
|
|
29
|
+
class PostProcessPlugin
|
|
30
|
+
{
|
|
31
|
+
/** Create global post processing shader
|
|
32
|
+
* @param {string} shaderCode
|
|
33
|
+
* @param {boolean} [includeMainCanvas] - combine mainCanvas onto glCanvas
|
|
34
|
+
* @param {boolean} [feedbackTexture] - use glCanvas from previous frame as the texture
|
|
35
|
+
* @example
|
|
36
|
+
* // create the post process plugin object
|
|
37
|
+
* new PostProcessPlugin(shaderCode);
|
|
38
|
+
*/
|
|
39
|
+
constructor(shaderCode, includeMainCanvas=false, feedbackTexture=false)
|
|
40
|
+
{
|
|
41
|
+
ASSERT(!postProcess, 'Post process already initialized');
|
|
42
|
+
ASSERT(!(includeMainCanvas && feedbackTexture), 'Post process cannot both include main canvas and use feedback texture');
|
|
43
|
+
postProcess = this;
|
|
44
|
+
|
|
45
|
+
if (!shaderCode) // default shader pass through
|
|
46
|
+
shaderCode = 'void mainImage(out vec4 c,vec2 p){c=texture(iChannel0,p/iResolution.xy);}';
|
|
47
|
+
|
|
48
|
+
/** @property {WebGLProgram|undefined} - Shader for post processing
|
|
49
|
+
* @type {WebGLProgram|undefined} */
|
|
50
|
+
this.shader = undefined;
|
|
51
|
+
/** @property {WebGLTexture|undefined} - Texture for post processing
|
|
52
|
+
* @type {WebGLTexture|undefined} */
|
|
53
|
+
this.texture = undefined;
|
|
54
|
+
/** @property {WebGLVertexArrayObject|undefined} - Vertex array object
|
|
55
|
+
* @type {WebGLVertexArrayObject|undefined} */
|
|
56
|
+
this.vao = undefined;
|
|
57
|
+
|
|
58
|
+
// setup the post processing plugin
|
|
59
|
+
initPostProcess();
|
|
60
|
+
engineAddPlugin(undefined, postProcessRender, postProcessContextLost, postProcessContextRestored);
|
|
61
|
+
|
|
62
|
+
function initPostProcess()
|
|
63
|
+
{
|
|
64
|
+
if (headlessMode) return;
|
|
65
|
+
if (!glEnable)
|
|
66
|
+
{
|
|
67
|
+
console.warn('PostProcessPlugin: WebGL not enabled!');
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// create resources
|
|
72
|
+
postProcess.texture = glCreateTexture();
|
|
73
|
+
postProcess.shader = glCreateProgram(
|
|
74
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
75
|
+
'precision highp float;'+ // use highp for accuracy
|
|
76
|
+
'in vec2 p;'+ // position
|
|
77
|
+
'void main(){'+ // shader entry point
|
|
78
|
+
'gl_Position=vec4(p+p-1.,1,1);'+ // set position
|
|
79
|
+
'}' // end of shader
|
|
80
|
+
,
|
|
81
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
82
|
+
'precision highp float;'+ // use highp for accuracy
|
|
83
|
+
'uniform sampler2D iChannel0;'+ // input texture
|
|
84
|
+
'uniform vec3 iResolution;'+ // size of output texture
|
|
85
|
+
'uniform float iTime;'+ // time
|
|
86
|
+
'out vec4 c;'+ // out color
|
|
87
|
+
'\n' + shaderCode + '\n'+ // insert custom shader code
|
|
88
|
+
'void main(){'+ // shader entry point
|
|
89
|
+
'mainImage(c,gl_FragCoord.xy);'+ // call post process function
|
|
90
|
+
'c.a=1.;'+ // always use full alpha
|
|
91
|
+
'}' // end of shader
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
// setup VAO for post processing
|
|
95
|
+
postProcess.vao = glContext.createVertexArray();
|
|
96
|
+
glContext.bindVertexArray(postProcess.vao);
|
|
97
|
+
glContext.bindBuffer(glContext.ARRAY_BUFFER, glGeometryBuffer);
|
|
98
|
+
|
|
99
|
+
// configure vertex attributes
|
|
100
|
+
const vertexByteStride = 8;
|
|
101
|
+
const pLocation = glContext.getAttribLocation(postProcess.shader, 'p');
|
|
102
|
+
glContext.enableVertexAttribArray(pLocation);
|
|
103
|
+
glContext.vertexAttribPointer(pLocation, 2, glContext.FLOAT, false, vertexByteStride, 0);
|
|
104
|
+
}
|
|
105
|
+
function postProcessContextLost()
|
|
106
|
+
{
|
|
107
|
+
postProcess.shader = undefined;
|
|
108
|
+
postProcess.texture = undefined;
|
|
109
|
+
LOG('PostProcessPlugin: WebGL context lost');
|
|
110
|
+
}
|
|
111
|
+
function postProcessContextRestored()
|
|
112
|
+
{
|
|
113
|
+
initPostProcess();
|
|
114
|
+
LOG('PostProcessPlugin: WebGL context restored');
|
|
115
|
+
}
|
|
116
|
+
function postProcessRender()
|
|
117
|
+
{
|
|
118
|
+
if (headlessMode || !glEnable) return;
|
|
119
|
+
|
|
120
|
+
// clear out the buffer
|
|
121
|
+
glFlush();
|
|
122
|
+
|
|
123
|
+
// ensure we render to the default framebuffer (in case any earlier
|
|
124
|
+
// caller this frame left a render target bound)
|
|
125
|
+
glContext.bindFramebuffer(glContext.FRAMEBUFFER, null);
|
|
126
|
+
|
|
127
|
+
// setup shader program to draw a quad
|
|
128
|
+
glContext.useProgram(postProcess.shader);
|
|
129
|
+
glContext.bindVertexArray(postProcess.vao);
|
|
130
|
+
glContext.pixelStorei(glContext.UNPACK_FLIP_Y_WEBGL, true);
|
|
131
|
+
glContext.disable(glContext.BLEND);
|
|
132
|
+
|
|
133
|
+
// setup texture
|
|
134
|
+
glContext.activeTexture(glContext.TEXTURE0);
|
|
135
|
+
glContext.bindTexture(glContext.TEXTURE_2D, postProcess.texture);
|
|
136
|
+
if (includeMainCanvas)
|
|
137
|
+
{
|
|
138
|
+
// copy main canvas to work canvas at the backing store size,
|
|
139
|
+
// mainCanvasSize is css pixels so it would lose resolution
|
|
140
|
+
workCanvas.width = mainCanvas.width;
|
|
141
|
+
workCanvas.height = mainCanvas.height;
|
|
142
|
+
glCopyToContext(workContext);
|
|
143
|
+
workContext.drawImage(mainCanvas, 0, 0);
|
|
144
|
+
mainCanvas.width |= 0; // setting size clears the main canvas
|
|
145
|
+
|
|
146
|
+
// that also reset the transform, restore it so anything drawn
|
|
147
|
+
// later this frame is still in css pixels
|
|
148
|
+
const dpr = getCanvasPixelRatio();
|
|
149
|
+
mainContext.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
150
|
+
|
|
151
|
+
// copy work canvas to texture
|
|
152
|
+
glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, glContext.RGBA, glContext.UNSIGNED_BYTE, workCanvas);
|
|
153
|
+
}
|
|
154
|
+
else if (!feedbackTexture)
|
|
155
|
+
{
|
|
156
|
+
// copy glCanvas to texture
|
|
157
|
+
glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, glContext.RGBA, glContext.UNSIGNED_BYTE, glCanvas);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// set uniforms and draw
|
|
161
|
+
const uniformLocation = (name)=>glContext.getUniformLocation(postProcess.shader, name);
|
|
162
|
+
glContext.uniform1i(uniformLocation('iChannel0'), 0);
|
|
163
|
+
glContext.uniform1f(uniformLocation('iTime'), time);
|
|
164
|
+
glContext.uniform3f(uniformLocation('iResolution'), mainCanvas.width, mainCanvas.height, 1);
|
|
165
|
+
glContext.drawArrays(glContext.TRIANGLE_STRIP, 0, 4);
|
|
166
|
+
|
|
167
|
+
if (feedbackTexture)
|
|
168
|
+
{
|
|
169
|
+
// pass glCanvas back to overlay texture
|
|
170
|
+
glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, glContext.RGBA, glContext.UNSIGNED_BYTE, glCanvas);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// restore default so subsequent dynamic texture uploads aren't flipped
|
|
174
|
+
glContext.pixelStorei(glContext.UNPACK_FLIP_Y_WEBGL, false);
|
|
175
|
+
|
|
176
|
+
// force it to set instanced mode
|
|
177
|
+
glSetInstancedMode(true);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Shader code for a bloom effect, the bright parts of the image blurred back over it
|
|
186
|
+
* - Pass it to PostProcessPlugin, or edit the string to build an effect on top of it
|
|
187
|
+
* @param {number} [threshold] - Brightness where the glow starts, 0 is everything and 1 is only pure white
|
|
188
|
+
* @param {number} [strength] - How much glow to add
|
|
189
|
+
* @param {number} [size] - How far the glow spreads in pixels, which also sets how many samples it takes
|
|
190
|
+
* @return {string}
|
|
191
|
+
* @memberof PostProcess
|
|
192
|
+
*/
|
|
193
|
+
function postProcessBloomShader(threshold=.6, strength=1, size=6)
|
|
194
|
+
{
|
|
195
|
+
ASSERT(isNumber(threshold) && isNumber(strength) && isNumber(size), 'bloom settings must be numbers');
|
|
196
|
+
ASSERT(size > 0, 'bloom size must be above zero');
|
|
197
|
+
ASSERT(size <= 32, 'a bloom this wide takes a sample every few pixels of every ring, which is hundreds of samples a pixel', size);
|
|
198
|
+
|
|
199
|
+
// Taps on three rings over a disc of the given size, one every three pixels or so of each ring
|
|
200
|
+
// so there is no gap wide enough to show. The count follows the ring all the way out: hold it
|
|
201
|
+
// still and a wider glow only spreads the same taps further apart, until they show up as the
|
|
202
|
+
// ring of evenly spaced copies a single ring of eight leaves around anything bright.
|
|
203
|
+
// Each ring has its own count, odd and unequal, and its own turn off the last, so the little
|
|
204
|
+
// the taps do miss comes out as fine ripple instead of a shape of its own.
|
|
205
|
+
const rings = 3;
|
|
206
|
+
let code = '', taps = 0;
|
|
207
|
+
for (let j = 0; j < rings; ++j)
|
|
208
|
+
{
|
|
209
|
+
const radius = ((j + .5) / rings) ** .5 * size; // equal area per ring
|
|
210
|
+
const count = max(5 + 2 * j, round(2 * radius)) | 1;
|
|
211
|
+
taps += count;
|
|
212
|
+
code += `
|
|
213
|
+
for (int k = 0; k < ${count}; ++k)
|
|
214
|
+
{
|
|
215
|
+
float a = float(k) * ${(2 * PI / count).toFixed(7)}${j ? ' + ' + (j * 2.3999632).toFixed(7) : ''};
|
|
216
|
+
glow += max(vec3(0), texture(iChannel0, uv + vec2(cos(a), sin(a)) * ${radius.toFixed(4)} / iResolution.xy).rgb - ${threshold.toFixed(4)});
|
|
217
|
+
}`;
|
|
218
|
+
}
|
|
219
|
+
return `
|
|
220
|
+
void mainImage(out vec4 color, vec2 pixel)
|
|
221
|
+
{
|
|
222
|
+
vec2 uv = pixel / iResolution.xy;
|
|
223
|
+
color = texture(iChannel0, uv);
|
|
224
|
+
vec3 glow = vec3(0);${code}
|
|
225
|
+
color.rgb += glow * ${(strength / taps).toFixed(6)};
|
|
226
|
+
}`;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Set up post processing with a bloom effect, so bright colors and lights glow
|
|
231
|
+
* @param {number} [threshold] - Brightness where the glow starts, 0 is everything and 1 is only pure white
|
|
232
|
+
* @param {number} [strength] - How much glow to add
|
|
233
|
+
* @param {number} [size] - How far the glow spreads in pixels
|
|
234
|
+
* @param {boolean} [includeMainCanvas] - Glow the 2D canvas too, off by default so HUD text stays crisp
|
|
235
|
+
* @return {PostProcessPlugin}
|
|
236
|
+
* @memberof PostProcess
|
|
237
|
+
* @example
|
|
238
|
+
* postProcessBloom(); // in gameInit, after any Render3DPlugin
|
|
239
|
+
*/
|
|
240
|
+
function postProcessBloom(threshold=.6, strength=1, size=6, includeMainCanvas=false)
|
|
241
|
+
{ return new PostProcessPlugin(postProcessBloomShader(threshold, strength, size), includeMainCanvas); }
|