emeraldengine 2.2.0 → 3.0.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.
Files changed (162) hide show
  1. package/README.md +2359 -968
  2. package/dist/types/index.d.ts +72 -32
  3. package/dist/types/src/Animator.d.ts +50 -0
  4. package/dist/types/{BitmapText.d.ts → src/BitmapText.d.ts} +19 -21
  5. package/dist/types/src/Camera.d.ts +122 -0
  6. package/dist/types/src/CameraController.d.ts +107 -0
  7. package/dist/types/src/CanvasText.d.ts +91 -0
  8. package/dist/types/src/CollisionLayers.d.ts +58 -0
  9. package/dist/types/{Color.d.ts → src/Color.d.ts} +5 -6
  10. package/dist/types/src/Coroutine.d.ts +111 -0
  11. package/dist/types/src/DebugOverlay.d.ts +86 -0
  12. package/dist/types/src/Drawable.d.ts +271 -0
  13. package/dist/types/src/Easing.d.ts +22 -0
  14. package/dist/types/src/Emerald.d.ts +420 -0
  15. package/dist/types/src/EmeraldDB.d.ts +159 -0
  16. package/dist/types/{FPSCounter.d.ts → src/FPSCounter.d.ts} +1 -3
  17. package/dist/types/src/GLUtils.d.ts +4 -0
  18. package/dist/types/{Instance.d.ts → src/Instance.d.ts} +37 -15
  19. package/dist/types/{InstancedTexture.d.ts → src/InstancedTexture.d.ts} +60 -23
  20. package/dist/types/src/Interpolator.d.ts +66 -0
  21. package/dist/types/src/Material.d.ts +87 -0
  22. package/dist/types/src/MathUtils.d.ts +80 -0
  23. package/dist/types/src/ParticleEmitter.d.ts +131 -0
  24. package/dist/types/{Physics.d.ts → src/Physics.d.ts} +88 -20
  25. package/dist/types/src/Pool.d.ts +53 -0
  26. package/dist/types/src/PostEffects.d.ts +68 -0
  27. package/dist/types/src/PostProcessor.d.ts +124 -0
  28. package/dist/types/src/RenderTarget.d.ts +56 -0
  29. package/dist/types/src/Scene.d.ts +62 -0
  30. package/dist/types/src/ScreenEffects.d.ts +111 -0
  31. package/dist/types/src/Serializer.d.ts +86 -0
  32. package/dist/types/src/Shaders.d.ts +2 -0
  33. package/dist/types/{Shapes.d.ts → src/Shapes.d.ts} +4 -6
  34. package/dist/types/src/SpatialGrid.d.ts +50 -0
  35. package/dist/types/src/SpriteBatch.d.ts +89 -0
  36. package/dist/types/src/StateMachine.d.ts +59 -0
  37. package/dist/types/src/Storage.d.ts +89 -0
  38. package/dist/types/{Texture.d.ts → src/Texture.d.ts} +3 -4
  39. package/dist/types/src/TextureAtlas.d.ts +55 -0
  40. package/dist/types/src/Tilemap.d.ts +91 -0
  41. package/dist/types/src/Time.d.ts +53 -0
  42. package/dist/types/src/Timer.d.ts +54 -0
  43. package/dist/types/src/Transform.d.ts +94 -0
  44. package/dist/types/src/Tween.d.ts +81 -0
  45. package/dist/types/src/UI.d.ts +121 -0
  46. package/dist/types/src/components/Behaviour.d.ts +71 -0
  47. package/dist/types/{components → src/components}/BoxCollider.d.ts +14 -14
  48. package/dist/types/src/components/BoxColliderDebug.d.ts +15 -0
  49. package/dist/types/{components → src/components}/CircleCollider.d.ts +14 -12
  50. package/dist/types/src/components/CircleColliderDebug.d.ts +15 -0
  51. package/dist/types/src/components/Collider.d.ts +96 -0
  52. package/dist/types/src/components/GameObject.d.ts +135 -0
  53. package/dist/types/src/components/RigidBody.d.ts +183 -0
  54. package/dist/types/src/importers/Aseprite.d.ts +79 -0
  55. package/dist/types/src/importers/TiledMap.d.ts +62 -0
  56. package/dist/types/{lights → src/lights}/DirectionalLight.d.ts +7 -9
  57. package/dist/types/{lights → src/lights}/PointLight.d.ts +6 -9
  58. package/dist/types/src/managers/AssetManager.d.ts +116 -0
  59. package/dist/types/src/managers/AudioManager.d.ts +259 -0
  60. package/dist/types/{managers → src/managers}/CameraManager.d.ts +8 -8
  61. package/dist/types/{managers → src/managers}/EventManager.d.ts +46 -32
  62. package/dist/types/src/managers/GLManager.d.ts +84 -0
  63. package/dist/types/src/managers/GLState.d.ts +37 -0
  64. package/dist/types/src/managers/IDManager.d.ts +31 -0
  65. package/dist/types/src/managers/InputManager.d.ts +290 -0
  66. package/dist/types/src/managers/NetworkManager.d.ts +93 -0
  67. package/dist/types/src/managers/RenderStats.d.ts +34 -0
  68. package/dist/types/src/managers/SceneManager.d.ts +44 -0
  69. package/dist/types/src/managers/ShaderManager.d.ts +55 -0
  70. package/dist/types/src/managers/TextureManager.d.ts +102 -0
  71. package/dist/types/src/particlesystem/Particle.d.ts +64 -0
  72. package/dist/types/{particlesystem → src/particlesystem}/ParticleSettings.d.ts +32 -24
  73. package/dist/types/{particlesystem → src/particlesystem}/Particles.d.ts +20 -12
  74. package/index.js +72 -0
  75. package/package.json +74 -60
  76. package/src/Animator.js +95 -0
  77. package/src/BitmapText.js +6 -5
  78. package/src/Camera.js +183 -0
  79. package/src/CameraController.js +192 -0
  80. package/src/CanvasText.js +281 -0
  81. package/src/CollisionLayers.js +86 -0
  82. package/src/Color.js +18 -18
  83. package/src/Coroutine.js +259 -0
  84. package/src/DebugOverlay.js +246 -0
  85. package/src/Drawable.js +842 -555
  86. package/src/Easing.js +57 -0
  87. package/src/Emerald.js +1150 -459
  88. package/src/EmeraldDB.js +328 -0
  89. package/src/FPSCounter.js +43 -43
  90. package/src/GLUtils.js +60 -67
  91. package/src/Instance.js +41 -5
  92. package/src/InstancedTexture.js +251 -118
  93. package/src/Interpolator.js +124 -0
  94. package/src/Material.js +202 -0
  95. package/src/MathUtils.js +133 -0
  96. package/src/ParticleEmitter.js +284 -0
  97. package/src/Physics.js +186 -5
  98. package/src/Pool.js +85 -0
  99. package/src/PostEffects.js +296 -0
  100. package/src/PostProcessor.js +304 -0
  101. package/src/RenderTarget.js +134 -0
  102. package/src/Scene.js +115 -83
  103. package/src/ScreenEffects.js +266 -0
  104. package/src/Serializer.js +131 -0
  105. package/src/Shaders.js +150 -165
  106. package/src/Shapes.js +118 -129
  107. package/src/SpatialGrid.js +111 -0
  108. package/src/SpriteBatch.js +299 -0
  109. package/src/StateMachine.js +82 -0
  110. package/src/Storage.js +175 -47
  111. package/src/Texture.js +58 -67
  112. package/src/TextureAtlas.js +96 -0
  113. package/src/Tilemap.js +274 -0
  114. package/src/Time.js +51 -6
  115. package/src/Timer.js +99 -0
  116. package/src/Transform.js +100 -7
  117. package/src/Tween.js +160 -0
  118. package/src/UI.js +394 -0
  119. package/src/components/Behaviour.js +90 -0
  120. package/src/components/BoxCollider.js +22 -3
  121. package/src/components/CircleCollider.js +19 -3
  122. package/src/components/CircleColliderDebug.js +24 -24
  123. package/src/components/Collider.js +140 -34
  124. package/src/components/GameObject.js +130 -21
  125. package/src/components/RigidBody.js +123 -2
  126. package/src/importers/Aseprite.js +142 -0
  127. package/src/importers/TiledMap.js +158 -0
  128. package/src/lights/DirectionalLight.js +6 -15
  129. package/src/lights/PointLight.js +4 -4
  130. package/src/managers/AssetManager.js +239 -0
  131. package/src/managers/AudioManager.js +565 -146
  132. package/src/managers/EventManager.js +488 -477
  133. package/src/managers/GLManager.js +57 -0
  134. package/src/managers/GLState.js +70 -0
  135. package/src/managers/IDManager.js +24 -2
  136. package/src/managers/InputManager.js +779 -0
  137. package/src/managers/NetworkManager.js +178 -0
  138. package/src/managers/RenderStats.js +34 -0
  139. package/src/managers/SceneManager.js +30 -0
  140. package/src/managers/ShaderManager.js +0 -2
  141. package/src/managers/TextureManager.js +218 -0
  142. package/src/particlesystem/Particle.js +82 -7
  143. package/src/particlesystem/ParticleSettings.js +21 -3
  144. package/src/particlesystem/Particles.js +80 -31
  145. package/dist/types/Drawable.d.ts +0 -157
  146. package/dist/types/Emerald.d.ts +0 -73
  147. package/dist/types/GLUtils.d.ts +0 -6
  148. package/dist/types/Scene.d.ts +0 -39
  149. package/dist/types/Shaders.d.ts +0 -4
  150. package/dist/types/Storage.d.ts +0 -46
  151. package/dist/types/Time.d.ts +0 -22
  152. package/dist/types/Transform.d.ts +0 -41
  153. package/dist/types/components/BoxColliderDebug.d.ts +0 -19
  154. package/dist/types/components/CircleColliderDebug.d.ts +0 -19
  155. package/dist/types/components/Collider.d.ts +0 -53
  156. package/dist/types/components/GameObject.d.ts +0 -72
  157. package/dist/types/components/RigidBody.d.ts +0 -104
  158. package/dist/types/managers/AudioManager.d.ts +0 -60
  159. package/dist/types/managers/GLManager.d.ts +0 -47
  160. package/dist/types/managers/IDManager.d.ts +0 -21
  161. package/dist/types/managers/SceneManager.d.ts +0 -22
  162. package/dist/types/particlesystem/Particle.d.ts +0 -42
@@ -0,0 +1,296 @@
1
+ import { PostEffect } from "./PostProcessor.js";
2
+
3
+ /**
4
+ * @class BloomEffect
5
+ * @description Multi-pass bloom: extract bright pixels above a threshold, blur
6
+ * them with a separable Gaussian, then add the result back over the scene. Built
7
+ * via {@link PostEffects.bloom}.
8
+ */
9
+ class BloomEffect extends PostEffect {
10
+ constructor(options = {}) {
11
+ super("bloom", `void main(){ gl_FragColor = texture2D(uScene, vUV); }`);
12
+ this.threshold = options.threshold ?? 0.7;
13
+ this.intensity = options.intensity ?? 1.0;
14
+ this.spread = options.spread ?? 1.0;
15
+
16
+ /** @private */
17
+ this._threshold = new PostEffect(
18
+ "bloom:threshold",
19
+ `
20
+ uniform float uThreshold;
21
+ void main() {
22
+ vec4 c = texture2D(uScene, vUV);
23
+ float l = dot(c.rgb, vec3(0.2126, 0.7152, 0.0722));
24
+ gl_FragColor = l > uThreshold ? c : vec4(0.0, 0.0, 0.0, 1.0);
25
+ }`,
26
+ {
27
+ setUniforms: (gl, loc) =>
28
+ gl.uniform1f(loc("uThreshold"), this.threshold),
29
+ }
30
+ );
31
+
32
+ const blur = (horizontal) => `
33
+ uniform float uSpread;
34
+ void main() {
35
+ vec2 texel = (${horizontal ? "vec2(1.0, 0.0)" : "vec2(0.0, 1.0)"}) / uResolution * uSpread;
36
+ float w[5];
37
+ w[0] = 0.227027; w[1] = 0.1945946; w[2] = 0.1216216; w[3] = 0.054054; w[4] = 0.016216;
38
+ vec3 result = texture2D(uScene, vUV).rgb * w[0];
39
+ for (int i = 1; i < 5; i++) {
40
+ result += texture2D(uScene, vUV + texel * float(i)).rgb * w[i];
41
+ result += texture2D(uScene, vUV - texel * float(i)).rgb * w[i];
42
+ }
43
+ gl_FragColor = vec4(result, 1.0);
44
+ }`;
45
+ const blurUniforms = {
46
+ setUniforms: (gl, loc) => gl.uniform1f(loc("uSpread"), this.spread),
47
+ };
48
+ /** @private */
49
+ this._blurH = new PostEffect("bloom:blurH", blur(true), blurUniforms);
50
+ /** @private */
51
+ this._blurV = new PostEffect("bloom:blurV", blur(false), blurUniforms);
52
+
53
+ /** @private */
54
+ this._composite = new PostEffect(
55
+ "bloom:composite",
56
+ `
57
+ uniform sampler2D uBloom;
58
+ uniform float uIntensity;
59
+ void main() {
60
+ vec4 scene = texture2D(uScene, vUV);
61
+ vec3 bloom = texture2D(uBloom, vUV).rgb;
62
+ gl_FragColor = vec4(scene.rgb + bloom * uIntensity, scene.a);
63
+ }`,
64
+ {
65
+ setUniforms: (gl, loc) => {
66
+ gl.uniform1f(loc("uIntensity"), this.intensity);
67
+ gl.activeTexture(gl.TEXTURE1);
68
+ gl.bindTexture(gl.TEXTURE_2D, this._bloomTex);
69
+ gl.uniform1i(loc("uBloom"), 1);
70
+ gl.activeTexture(gl.TEXTURE0);
71
+ },
72
+ }
73
+ );
74
+ }
75
+
76
+ /** @private */
77
+ _compile(gl) {
78
+ super._compile(gl);
79
+ this._threshold._compile(gl);
80
+ this._blurH._compile(gl);
81
+ this._blurV._compile(gl);
82
+ this._composite._compile(gl);
83
+ }
84
+
85
+ render(ctx) {
86
+ const temp0 = ctx.acquireTemp(0);
87
+ const temp1 = ctx.acquireTemp(1);
88
+ ctx.blit(this._threshold, ctx.input, temp0);
89
+ ctx.blit(this._blurH, temp0.texture, temp1);
90
+ ctx.blit(this._blurV, temp1.texture, temp0);
91
+ /** @private */
92
+ this._bloomTex = temp0.texture;
93
+ ctx.blit(this._composite, ctx.input, ctx.output);
94
+ }
95
+ }
96
+
97
+ /**
98
+ * @namespace PostEffects
99
+ * @description Factory functions for the built-in post-processing effects. Pass
100
+ * the returned PostEffect to `emerald.addPostEffect(...)`.
101
+ *
102
+ * @example
103
+ * emerald.enablePostProcessing();
104
+ * emerald.addPostEffect(PostEffects.bloom({ threshold: 0.6, intensity: 1.2 }));
105
+ * emerald.addPostEffect(PostEffects.vignette({ intensity: 0.5 }));
106
+ * emerald.addPostEffect(PostEffects.crt());
107
+ */
108
+ const PostEffects = {
109
+ /**
110
+ * @method grayscale
111
+ * @description Desaturates the image.
112
+ */
113
+ grayscale() {
114
+ return new PostEffect(
115
+ "grayscale",
116
+ `void main() {
117
+ vec4 c = texture2D(uScene, vUV);
118
+ float l = dot(c.rgb, vec3(0.299, 0.587, 0.114));
119
+ gl_FragColor = vec4(vec3(l), c.a);
120
+ }`
121
+ );
122
+ },
123
+
124
+ /**
125
+ * @method vignette
126
+ * @description Darkens the edges of the screen.
127
+ * @param {Object} [options] - { intensity = 0.5, radius = 0.75, softness = 0.45 }
128
+ */
129
+ vignette(options = {}) {
130
+ const intensity = options.intensity ?? 0.5;
131
+ const radius = options.radius ?? 0.75;
132
+ const softness = options.softness ?? 0.45;
133
+ return new PostEffect(
134
+ "vignette",
135
+ `
136
+ uniform float uIntensity;
137
+ uniform float uRadius;
138
+ uniform float uSoftness;
139
+ void main() {
140
+ vec4 c = texture2D(uScene, vUV);
141
+ float d = distance(vUV, vec2(0.5));
142
+ float v = smoothstep(uRadius, uRadius - uSoftness, d);
143
+ c.rgb *= mix(1.0 - uIntensity, 1.0, v);
144
+ gl_FragColor = c;
145
+ }`,
146
+ {
147
+ setUniforms: (gl, loc) => {
148
+ gl.uniform1f(loc("uIntensity"), intensity);
149
+ gl.uniform1f(loc("uRadius"), radius);
150
+ gl.uniform1f(loc("uSoftness"), softness);
151
+ },
152
+ }
153
+ );
154
+ },
155
+
156
+ /**
157
+ * @method colorGrade
158
+ * @description Adjusts brightness, contrast and saturation.
159
+ * @param {Object} [options] - { brightness = 0, contrast = 1, saturation = 1 }
160
+ */
161
+ colorGrade(options = {}) {
162
+ const brightness = options.brightness ?? 0;
163
+ const contrast = options.contrast ?? 1;
164
+ const saturation = options.saturation ?? 1;
165
+ return new PostEffect(
166
+ "colorGrade",
167
+ `
168
+ uniform float uBrightness;
169
+ uniform float uContrast;
170
+ uniform float uSaturation;
171
+ void main() {
172
+ vec4 c = texture2D(uScene, vUV);
173
+ vec3 col = c.rgb + uBrightness;
174
+ col = (col - 0.5) * uContrast + 0.5;
175
+ float l = dot(col, vec3(0.299, 0.587, 0.114));
176
+ col = mix(vec3(l), col, uSaturation);
177
+ gl_FragColor = vec4(clamp(col, 0.0, 1.0), c.a);
178
+ }`,
179
+ {
180
+ setUniforms: (gl, loc) => {
181
+ gl.uniform1f(loc("uBrightness"), brightness);
182
+ gl.uniform1f(loc("uContrast"), contrast);
183
+ gl.uniform1f(loc("uSaturation"), saturation);
184
+ },
185
+ }
186
+ );
187
+ },
188
+
189
+ /**
190
+ * @method chromaticAberration
191
+ * @description Splits the RGB channels toward the screen edges.
192
+ * @param {Object} [options] - { amount = 0.003 }
193
+ */
194
+ chromaticAberration(options = {}) {
195
+ const amount = options.amount ?? 0.003;
196
+ return new PostEffect(
197
+ "chromaticAberration",
198
+ `
199
+ uniform float uAmount;
200
+ void main() {
201
+ vec2 dir = vUV - 0.5;
202
+ vec2 off = dir * uAmount;
203
+ float r = texture2D(uScene, vUV + off).r;
204
+ float g = texture2D(uScene, vUV).g;
205
+ float b = texture2D(uScene, vUV - off).b;
206
+ float a = texture2D(uScene, vUV).a;
207
+ gl_FragColor = vec4(r, g, b, a);
208
+ }`,
209
+ { setUniforms: (gl, loc) => gl.uniform1f(loc("uAmount"), amount) }
210
+ );
211
+ },
212
+
213
+ /**
214
+ * @method scanlines
215
+ * @description Overlays horizontal scanlines.
216
+ * @param {Object} [options] - { intensity = 0.15, count = 480 }
217
+ */
218
+ scanlines(options = {}) {
219
+ const intensity = options.intensity ?? 0.15;
220
+ const count = options.count ?? 480;
221
+ return new PostEffect(
222
+ "scanlines",
223
+ `
224
+ uniform float uIntensity;
225
+ uniform float uCount;
226
+ void main() {
227
+ vec4 c = texture2D(uScene, vUV);
228
+ float s = sin(vUV.y * uCount * 3.14159265);
229
+ c.rgb *= 1.0 - uIntensity * (0.5 + 0.5 * s) ;
230
+ gl_FragColor = c;
231
+ }`,
232
+ {
233
+ setUniforms: (gl, loc) => {
234
+ gl.uniform1f(loc("uIntensity"), intensity);
235
+ gl.uniform1f(loc("uCount"), count);
236
+ },
237
+ }
238
+ );
239
+ },
240
+
241
+ /**
242
+ * @method crt
243
+ * @description Retro CRT look: barrel distortion, scanlines and edge vignette.
244
+ * @param {Object} [options] - { curvature = 4.0, scanlineIntensity = 0.2, vignette = 0.3 }
245
+ */
246
+ crt(options = {}) {
247
+ const curvature = options.curvature ?? 4.0;
248
+ const scanlineIntensity = options.scanlineIntensity ?? 0.2;
249
+ const vignette = options.vignette ?? 0.3;
250
+ return new PostEffect(
251
+ "crt",
252
+ `
253
+ uniform float uCurvature;
254
+ uniform float uScanline;
255
+ uniform float uVignette;
256
+ vec2 curve(vec2 uv) {
257
+ uv = uv * 2.0 - 1.0;
258
+ vec2 offset = abs(uv.yx) / uCurvature;
259
+ uv = uv + uv * offset * offset;
260
+ return uv * 0.5 + 0.5;
261
+ }
262
+ void main() {
263
+ vec2 uv = curve(vUV);
264
+ if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) {
265
+ gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
266
+ return;
267
+ }
268
+ vec4 c = texture2D(uScene, uv);
269
+ float scan = sin(uv.y * uResolution.y * 3.14159265);
270
+ c.rgb *= 1.0 - uScanline * (0.5 + 0.5 * scan);
271
+ float d = distance(uv, vec2(0.5));
272
+ c.rgb *= 1.0 - uVignette * d * d * 2.0;
273
+ gl_FragColor = c;
274
+ }`,
275
+ {
276
+ setUniforms: (gl, loc) => {
277
+ gl.uniform1f(loc("uCurvature"), curvature);
278
+ gl.uniform1f(loc("uScanline"), scanlineIntensity);
279
+ gl.uniform1f(loc("uVignette"), vignette);
280
+ },
281
+ }
282
+ );
283
+ },
284
+
285
+ /**
286
+ * @method bloom
287
+ * @description Glow around bright areas (multi-pass).
288
+ * @param {Object} [options] - { threshold = 0.7, intensity = 1.0, spread = 1.0 }
289
+ */
290
+ bloom(options = {}) {
291
+ return new BloomEffect(options);
292
+ },
293
+ };
294
+
295
+ export default PostEffects;
296
+ export { BloomEffect };
@@ -0,0 +1,304 @@
1
+ import { initShaderProgram } from "./GLUtils.js";
2
+ import GLManager from "./managers/GLManager.js";
3
+ import RenderStats from "./managers/RenderStats.js";
4
+ import RenderTarget from "./RenderTarget.js";
5
+
6
+ const FULLSCREEN_VERTEX_SHADER = `
7
+ attribute vec2 aPos;
8
+ varying vec2 vUV;
9
+ void main() {
10
+ vUV = aPos * 0.5 + 0.5;
11
+ gl_Position = vec4(aPos, 0.0, 1.0);
12
+ }
13
+ `;
14
+
15
+ const FRAGMENT_HEADER = `
16
+ #ifdef GL_ES
17
+ precision highp float;
18
+ #endif
19
+ varying vec2 vUV;
20
+ uniform sampler2D uScene;
21
+ uniform vec2 uResolution;
22
+ uniform float uTime;
23
+ `;
24
+
25
+ /**
26
+ * @class PostEffect
27
+ * @description A single full-screen post-processing pass: a fragment shader that
28
+ * samples the previous pass (`uScene`) and writes the next image. The shader
29
+ * automatically has `vUV`, `uScene`, `uResolution`, and `uTime` available; add
30
+ * your own uniforms and set them with the `setUniforms` callback.
31
+ *
32
+ * @example
33
+ * const tint = new PostEffect("tint", `
34
+ * uniform vec3 uTint;
35
+ * void main() { gl_FragColor = texture2D(uScene, vUV) * vec4(uTint, 1.0); }
36
+ * `, { setUniforms: (gl, loc) => gl.uniform3f(loc("uTint"), 1.0, 0.8, 0.8) });
37
+ */
38
+ class PostEffect {
39
+ /**
40
+ * @param {string} name - A label (for debugging)
41
+ * @param {string} fragmentSource - Fragment shader body (declares void main)
42
+ * @param {Object} [options] - { setUniforms(gl, loc, ctx), enabled = true }
43
+ */
44
+ constructor(name, fragmentSource, options = {}) {
45
+ this.name = name;
46
+ this.fragmentSource = fragmentSource;
47
+ this.setUniforms = options.setUniforms || null;
48
+ this.enabled = options.enabled !== false;
49
+ this.program = null;
50
+ /** @private */
51
+ this._locCache = new Map();
52
+ }
53
+
54
+ /** @private */
55
+ _compile(gl) {
56
+ if (this.program) return;
57
+ this.program = initShaderProgram(
58
+ gl,
59
+ FULLSCREEN_VERTEX_SHADER,
60
+ FRAGMENT_HEADER + this.fragmentSource
61
+ );
62
+ }
63
+
64
+ /**
65
+ * @method _restoreGL
66
+ * @description Drops the dead program/locations after a context loss so the
67
+ * next _compile builds fresh ones.
68
+ * @private
69
+ */
70
+ _restoreGL() {
71
+ this.program = null;
72
+ this._locCache.clear();
73
+ }
74
+
75
+ loc(gl, name) {
76
+ if (!this._locCache.has(name)) {
77
+ this._locCache.set(name, gl.getUniformLocation(this.program, name));
78
+ }
79
+ return this._locCache.get(name);
80
+ }
81
+
82
+ /**
83
+ * @method render
84
+ * @description Renders this effect from ctx.input into ctx.output. Override for
85
+ * multi-pass effects.
86
+ * @param {Object} ctx - Provided by the PostProcessor
87
+ */
88
+ render(ctx) {
89
+ ctx.blit(this, ctx.input, ctx.output);
90
+ }
91
+ }
92
+
93
+ /**
94
+ * @class PostProcessor
95
+ * @description Drives a chain of full-screen {@link PostEffect}s. The scene is
96
+ * rendered into a texture, then each effect is applied in sequence (ping-ponging
97
+ * between two render targets), and the final result is drawn to the screen.
98
+ *
99
+ * Created and managed by Emerald when you call `emerald.enablePostProcessing()`;
100
+ * you usually just add effects via `emerald.addPostEffect(PostEffects.bloom())`.
101
+ */
102
+ class PostProcessor {
103
+ constructor() {
104
+ this.gl = GLManager.getGL();
105
+ this.effects = [];
106
+ this.enabled = true;
107
+
108
+ const gl = this.gl;
109
+ /** @private */
110
+ this._quad = gl.createBuffer();
111
+ gl.bindBuffer(gl.ARRAY_BUFFER, this._quad);
112
+ gl.bufferData(
113
+ gl.ARRAY_BUFFER,
114
+ new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]),
115
+ gl.STATIC_DRAW
116
+ );
117
+
118
+ /** @private */
119
+ this._rtA = new RenderTarget(2, 2);
120
+ /** @private */
121
+ this._rtB = new RenderTarget(2, 2);
122
+ /** @private */
123
+ this._temps = [];
124
+ /** @private */
125
+ this._width = 2;
126
+ /** @private */
127
+ this._height = 2;
128
+
129
+ GLManager.registerRestorable(this);
130
+ }
131
+
132
+ /**
133
+ * @method _restoreGL
134
+ * @description Rebuilds the fullscreen quad, ping-pong render targets, and
135
+ * every effect's program after a WebGL context loss.
136
+ * @private
137
+ */
138
+ _restoreGL() {
139
+ const gl = this.gl;
140
+ if (!gl) return;
141
+ this._quad = gl.createBuffer();
142
+ gl.bindBuffer(gl.ARRAY_BUFFER, this._quad);
143
+ gl.bufferData(
144
+ gl.ARRAY_BUFFER,
145
+ new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]),
146
+ gl.STATIC_DRAW
147
+ );
148
+ this._rtA = new RenderTarget(2, 2);
149
+ this._rtB = new RenderTarget(2, 2);
150
+ this._temps = [];
151
+ this._width = 2;
152
+ this._height = 2;
153
+ for (const effect of this.effects) {
154
+ effect._restoreGL();
155
+ effect._compile(gl);
156
+ }
157
+ }
158
+
159
+ /**
160
+ * @method addEffect
161
+ * @description Appends an effect to the chain.
162
+ * @param {PostEffect} effect
163
+ * @returns {PostProcessor} - this
164
+ */
165
+ addEffect(effect) {
166
+ effect._compile(this.gl);
167
+ this.effects.push(effect);
168
+ return this;
169
+ }
170
+
171
+ /**
172
+ * @method removeEffect
173
+ * @description Removes an effect from the chain.
174
+ */
175
+ removeEffect(effect) {
176
+ const i = this.effects.indexOf(effect);
177
+ if (i !== -1) this.effects.splice(i, 1);
178
+ return this;
179
+ }
180
+
181
+ /**
182
+ * @method clear
183
+ * @description Removes all effects.
184
+ */
185
+ clear() {
186
+ this.effects.length = 0;
187
+ return this;
188
+ }
189
+
190
+ /**
191
+ * @method hasEffects
192
+ * @returns {boolean} - Whether any enabled effects exist.
193
+ */
194
+ hasEffects() {
195
+ return this.enabled && this.effects.some((e) => e.enabled);
196
+ }
197
+
198
+ /** @private */
199
+ _acquireTemp(index) {
200
+ while (this._temps.length <= index) {
201
+ this._temps.push(new RenderTarget(this._width, this._height));
202
+ }
203
+ const t = this._temps[index];
204
+ t.resize(this._width, this._height);
205
+ return t;
206
+ }
207
+
208
+ /**
209
+ * @method process
210
+ * @description Applies the effect chain, drawing the final image to the canvas.
211
+ * @param {WebGLTexture} sceneTexture - The rendered scene
212
+ * @param {number} width - Canvas width in pixels
213
+ * @param {number} height - Canvas height in pixels
214
+ * @param {number} time - Seconds (for time-based effects)
215
+ */
216
+ process(sceneTexture, width, height, time) {
217
+ const gl = this.gl;
218
+ this._width = width;
219
+ this._height = height;
220
+ this._rtA.resize(width, height);
221
+ this._rtB.resize(width, height);
222
+
223
+ const active = this.effects.filter((e) => e.enabled);
224
+
225
+ gl.disable(gl.DEPTH_TEST);
226
+ gl.disable(gl.BLEND);
227
+ gl.disable(gl.SCISSOR_TEST);
228
+
229
+ const ctx = {
230
+ gl,
231
+ time,
232
+ width,
233
+ height,
234
+ processor: this,
235
+ input: sceneTexture,
236
+ output: null,
237
+ blit: (effect, input, output) => this._blit(effect, input, output, ctx),
238
+ acquireTemp: (i) => this._acquireTemp(i),
239
+ };
240
+
241
+ let input = sceneTexture;
242
+ for (let i = 0; i < active.length; i++) {
243
+ const isLast = i === active.length - 1;
244
+ const output = isLast ? null : i % 2 === 0 ? this._rtA : this._rtB;
245
+ ctx.input = input;
246
+ ctx.output = output;
247
+ active[i].render(ctx);
248
+ input = output ? output.texture : null;
249
+ }
250
+ }
251
+
252
+ /**
253
+ * @method _blit
254
+ * @description Renders one effect program from `input` into `output` (null =
255
+ * screen), setting the standard uniforms.
256
+ * @private
257
+ */
258
+ _blit(effect, input, output, ctx) {
259
+ const gl = this.gl;
260
+ if (output) {
261
+ output.bind();
262
+ } else {
263
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
264
+ gl.viewport(0, 0, this._width, this._height);
265
+ }
266
+
267
+ gl.useProgram(effect.program);
268
+
269
+ gl.activeTexture(gl.TEXTURE0);
270
+ gl.bindTexture(gl.TEXTURE_2D, input);
271
+ RenderStats.textureBinds++;
272
+ gl.uniform1i(effect.loc(gl, "uScene"), 0);
273
+
274
+ const resLoc = effect.loc(gl, "uResolution");
275
+ if (resLoc) gl.uniform2f(resLoc, this._width, this._height);
276
+ const timeLoc = effect.loc(gl, "uTime");
277
+ if (timeLoc) gl.uniform1f(timeLoc, ctx.time);
278
+
279
+ if (effect.setUniforms) {
280
+ effect.setUniforms(gl, (n) => effect.loc(gl, n), ctx);
281
+ }
282
+
283
+ gl.bindBuffer(gl.ARRAY_BUFFER, this._quad);
284
+ const posLoc = gl.getAttribLocation(effect.program, "aPos");
285
+ gl.enableVertexAttribArray(posLoc);
286
+ gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
287
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
288
+ RenderStats.drawCalls++;
289
+ }
290
+
291
+ /**
292
+ * @method dispose
293
+ * @description Frees the render targets owned by this processor.
294
+ */
295
+ dispose() {
296
+ this._rtA.dispose();
297
+ this._rtB.dispose();
298
+ for (const t of this._temps) t.dispose();
299
+ this._temps.length = 0;
300
+ }
301
+ }
302
+
303
+ export default PostProcessor;
304
+ export { PostEffect };