emeraldengine 2.2.1 → 3.1.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 (208) hide show
  1. package/README.md +2198 -968
  2. package/dist/types/index.d.ts +75 -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} +6 -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} +75 -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 +81 -0
  23. package/dist/types/src/ParticleEmitter.d.ts +131 -0
  24. package/dist/types/src/Physics.d.ts +337 -0
  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 +102 -0
  52. package/dist/types/src/components/GameObject.d.ts +135 -0
  53. package/dist/types/src/components/PolygonCollider.d.ts +33 -0
  54. package/dist/types/src/components/RigidBody.d.ts +456 -0
  55. package/dist/types/src/importers/Aseprite.d.ts +79 -0
  56. package/dist/types/src/importers/ForgeLevel.d.ts +97 -0
  57. package/dist/types/src/importers/TiledMap.d.ts +62 -0
  58. package/dist/types/{lights → src/lights}/DirectionalLight.d.ts +7 -9
  59. package/dist/types/{lights → src/lights}/PointLight.d.ts +6 -9
  60. package/dist/types/src/managers/AssetManager.d.ts +116 -0
  61. package/dist/types/src/managers/AudioManager.d.ts +259 -0
  62. package/dist/types/{managers → src/managers}/CameraManager.d.ts +8 -8
  63. package/dist/types/{managers → src/managers}/EventManager.d.ts +46 -32
  64. package/dist/types/src/managers/GLManager.d.ts +84 -0
  65. package/dist/types/src/managers/GLState.d.ts +37 -0
  66. package/dist/types/src/managers/Gamepad.d.ts +102 -0
  67. package/dist/types/src/managers/IDManager.d.ts +31 -0
  68. package/dist/types/src/managers/InputManager.d.ts +381 -0
  69. package/dist/types/src/managers/NetworkManager.d.ts +93 -0
  70. package/dist/types/src/managers/RenderStats.d.ts +34 -0
  71. package/dist/types/src/managers/SceneManager.d.ts +44 -0
  72. package/dist/types/src/managers/ShaderManager.d.ts +55 -0
  73. package/dist/types/src/managers/TextureManager.d.ts +102 -0
  74. package/dist/types/src/particlesystem/Particle.d.ts +64 -0
  75. package/dist/types/{particlesystem → src/particlesystem}/ParticleSettings.d.ts +32 -24
  76. package/dist/types/{particlesystem → src/particlesystem}/Particles.d.ts +20 -12
  77. package/dist/types/src/physics/AABB.d.ts +92 -0
  78. package/dist/types/src/physics/Body.d.ts +435 -0
  79. package/dist/types/src/physics/BodyType.d.ts +6 -0
  80. package/dist/types/src/physics/BroadPhase.d.ts +210 -0
  81. package/dist/types/src/physics/Collision.d.ts +102 -0
  82. package/dist/types/src/physics/Contact.d.ts +206 -0
  83. package/dist/types/src/physics/ContactSolver.d.ts +108 -0
  84. package/dist/types/src/physics/Distance.d.ts +54 -0
  85. package/dist/types/src/physics/DistanceJoint.d.ts +90 -0
  86. package/dist/types/src/physics/Fixture.d.ts +221 -0
  87. package/dist/types/src/physics/Island.d.ts +52 -0
  88. package/dist/types/src/physics/Joint.d.ts +59 -0
  89. package/dist/types/src/physics/Math2D.d.ts +371 -0
  90. package/dist/types/src/physics/RevoluteJoint.d.ts +119 -0
  91. package/dist/types/src/physics/Settings.d.ts +22 -0
  92. package/dist/types/src/physics/Shapes.d.ts +207 -0
  93. package/dist/types/src/physics/TimeOfImpact.d.ts +22 -0
  94. package/dist/types/src/physics/World.d.ts +274 -0
  95. package/dist/types/src/physics/index.d.ts +34 -0
  96. package/index.js +78 -0
  97. package/package.json +73 -60
  98. package/src/Animator.js +95 -0
  99. package/src/BitmapText.js +6 -5
  100. package/src/Camera.js +183 -0
  101. package/src/CameraController.js +192 -0
  102. package/src/CanvasText.js +281 -0
  103. package/src/CollisionLayers.js +86 -0
  104. package/src/Color.js +26 -18
  105. package/src/Coroutine.js +259 -0
  106. package/src/DebugOverlay.js +246 -0
  107. package/src/Drawable.js +842 -582
  108. package/src/Easing.js +57 -0
  109. package/src/Emerald.js +1150 -459
  110. package/src/EmeraldDB.js +328 -0
  111. package/src/FPSCounter.js +43 -43
  112. package/src/GLUtils.js +60 -67
  113. package/src/Instance.js +41 -5
  114. package/src/InstancedTexture.js +308 -127
  115. package/src/Interpolator.js +124 -0
  116. package/src/Material.js +202 -0
  117. package/src/MathUtils.js +134 -0
  118. package/src/ParticleEmitter.js +284 -0
  119. package/src/Physics.js +431 -40
  120. package/src/Pool.js +85 -0
  121. package/src/PostEffects.js +296 -0
  122. package/src/PostProcessor.js +304 -0
  123. package/src/RenderTarget.js +134 -0
  124. package/src/Scene.js +115 -83
  125. package/src/ScreenEffects.js +266 -0
  126. package/src/Serializer.js +131 -0
  127. package/src/Shaders.js +150 -165
  128. package/src/Shapes.js +118 -129
  129. package/src/SpatialGrid.js +111 -0
  130. package/src/SpriteBatch.js +299 -0
  131. package/src/StateMachine.js +82 -0
  132. package/src/Storage.js +175 -47
  133. package/src/Texture.js +58 -67
  134. package/src/TextureAtlas.js +96 -0
  135. package/src/Tilemap.js +274 -0
  136. package/src/Time.js +51 -6
  137. package/src/Timer.js +99 -0
  138. package/src/Transform.js +100 -7
  139. package/src/Tween.js +160 -0
  140. package/src/UI.js +394 -0
  141. package/src/components/Behaviour.js +90 -0
  142. package/src/components/BoxCollider.js +29 -12
  143. package/src/components/BoxColliderDebug.js +3 -4
  144. package/src/components/CircleCollider.js +26 -12
  145. package/src/components/CircleColliderDebug.js +25 -24
  146. package/src/components/Collider.js +150 -34
  147. package/src/components/GameObject.js +130 -21
  148. package/src/components/PolygonCollider.js +55 -0
  149. package/src/components/RigidBody.js +556 -8
  150. package/src/importers/Aseprite.js +142 -0
  151. package/src/importers/ForgeLevel.js +581 -0
  152. package/src/importers/TiledMap.js +158 -0
  153. package/src/lights/DirectionalLight.js +6 -15
  154. package/src/lights/PointLight.js +4 -4
  155. package/src/managers/AssetManager.js +239 -0
  156. package/src/managers/AudioManager.js +565 -146
  157. package/src/managers/EventManager.js +488 -477
  158. package/src/managers/GLManager.js +57 -0
  159. package/src/managers/GLState.js +70 -0
  160. package/src/managers/Gamepad.js +126 -0
  161. package/src/managers/IDManager.js +24 -2
  162. package/src/managers/InputManager.js +905 -0
  163. package/src/managers/NetworkManager.js +178 -0
  164. package/src/managers/RenderStats.js +34 -0
  165. package/src/managers/SceneManager.js +30 -0
  166. package/src/managers/ShaderManager.js +0 -2
  167. package/src/managers/TextureManager.js +218 -0
  168. package/src/particlesystem/Particle.js +82 -7
  169. package/src/particlesystem/ParticleSettings.js +21 -3
  170. package/src/particlesystem/Particles.js +80 -31
  171. package/src/physics/AABB.js +207 -0
  172. package/src/physics/Body.js +862 -0
  173. package/src/physics/BodyType.js +16 -0
  174. package/src/physics/BroadPhase.js +641 -0
  175. package/src/physics/Collision.js +534 -0
  176. package/src/physics/Contact.js +500 -0
  177. package/src/physics/ContactSolver.js +526 -0
  178. package/src/physics/Distance.js +403 -0
  179. package/src/physics/DistanceJoint.js +227 -0
  180. package/src/physics/Fixture.js +346 -0
  181. package/src/physics/Island.js +203 -0
  182. package/src/physics/Joint.js +78 -0
  183. package/src/physics/Math2D.js +573 -0
  184. package/src/physics/RevoluteJoint.js +278 -0
  185. package/src/physics/Settings.js +78 -0
  186. package/src/physics/Shapes.js +549 -0
  187. package/src/physics/TimeOfImpact.js +87 -0
  188. package/src/physics/World.js +731 -0
  189. package/src/physics/index.js +79 -0
  190. package/dist/types/Drawable.d.ts +0 -157
  191. package/dist/types/Emerald.d.ts +0 -73
  192. package/dist/types/GLUtils.d.ts +0 -6
  193. package/dist/types/Physics.d.ts +0 -139
  194. package/dist/types/Scene.d.ts +0 -39
  195. package/dist/types/Shaders.d.ts +0 -4
  196. package/dist/types/Storage.d.ts +0 -46
  197. package/dist/types/Time.d.ts +0 -22
  198. package/dist/types/Transform.d.ts +0 -41
  199. package/dist/types/components/BoxColliderDebug.d.ts +0 -19
  200. package/dist/types/components/CircleColliderDebug.d.ts +0 -19
  201. package/dist/types/components/Collider.d.ts +0 -53
  202. package/dist/types/components/GameObject.d.ts +0 -72
  203. package/dist/types/components/RigidBody.d.ts +0 -104
  204. package/dist/types/managers/AudioManager.d.ts +0 -60
  205. package/dist/types/managers/GLManager.d.ts +0 -47
  206. package/dist/types/managers/IDManager.d.ts +0 -21
  207. package/dist/types/managers/SceneManager.d.ts +0 -22
  208. package/dist/types/particlesystem/Particle.d.ts +0 -42
@@ -0,0 +1,134 @@
1
+ import GLManager from "./managers/GLManager.js";
2
+
3
+ /**
4
+ * @class RenderTarget
5
+ * @description An offscreen render surface: a framebuffer backed by a color
6
+ * texture (and an optional depth buffer). Bind it to render the scene (or a
7
+ * post-processing pass) into a texture instead of the screen. The resulting
8
+ * `texture` can then be sampled by another pass or drawn to the canvas.
9
+ *
10
+ * Used by the post-processing pipeline, but also useful on its own for
11
+ * minimaps, mirrors, render-to-texture effects, or picture-in-picture.
12
+ *
13
+ * @example
14
+ * const rt = new RenderTarget(512, 512);
15
+ * rt.bind();
16
+ * // ...draw...
17
+ * rt.unbind();
18
+ * // rt.texture now holds the rendered image
19
+ */
20
+ class RenderTarget {
21
+ /**
22
+ * @param {number} width - Width in pixels
23
+ * @param {number} height - Height in pixels
24
+ * @param {Object} [options] - { depth = false, pixelart = false }
25
+ */
26
+ constructor(width, height, options = {}) {
27
+ this.gl = GLManager.getGL();
28
+ this.width = Math.max(1, width | 0);
29
+ this.height = Math.max(1, height | 0);
30
+ this.depth = !!options.depth;
31
+ this.pixelart = !!options.pixelart;
32
+
33
+ const gl = this.gl;
34
+ this.framebuffer = gl.createFramebuffer();
35
+ this.texture = gl.createTexture();
36
+ this.depthBuffer = this.depth ? gl.createRenderbuffer() : null;
37
+
38
+ this._allocate();
39
+ }
40
+
41
+ /** @private */
42
+ _allocate() {
43
+ const gl = this.gl;
44
+ gl.bindTexture(gl.TEXTURE_2D, this.texture);
45
+ gl.texImage2D(
46
+ gl.TEXTURE_2D,
47
+ 0,
48
+ gl.RGBA,
49
+ this.width,
50
+ this.height,
51
+ 0,
52
+ gl.RGBA,
53
+ gl.UNSIGNED_BYTE,
54
+ null
55
+ );
56
+ const filter = this.pixelart ? gl.NEAREST : gl.LINEAR;
57
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);
58
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter);
59
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
60
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
61
+
62
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
63
+ gl.framebufferTexture2D(
64
+ gl.FRAMEBUFFER,
65
+ gl.COLOR_ATTACHMENT0,
66
+ gl.TEXTURE_2D,
67
+ this.texture,
68
+ 0
69
+ );
70
+
71
+ if (this.depth) {
72
+ gl.bindRenderbuffer(gl.RENDERBUFFER, this.depthBuffer);
73
+ gl.renderbufferStorage(
74
+ gl.RENDERBUFFER,
75
+ gl.DEPTH_COMPONENT16,
76
+ this.width,
77
+ this.height
78
+ );
79
+ gl.framebufferRenderbuffer(
80
+ gl.FRAMEBUFFER,
81
+ gl.DEPTH_ATTACHMENT,
82
+ gl.RENDERBUFFER,
83
+ this.depthBuffer
84
+ );
85
+ }
86
+
87
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
88
+ }
89
+
90
+ /**
91
+ * @method resize
92
+ * @description Resizes the surface (reallocating storage) if dimensions change.
93
+ */
94
+ resize(width, height) {
95
+ const w = Math.max(1, width | 0);
96
+ const h = Math.max(1, height | 0);
97
+ if (w === this.width && h === this.height) return;
98
+ this.width = w;
99
+ this.height = h;
100
+ this._allocate();
101
+ }
102
+
103
+ /**
104
+ * @method bind
105
+ * @description Binds this framebuffer and sets the viewport to its full size.
106
+ */
107
+ bind() {
108
+ const gl = this.gl;
109
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
110
+ gl.viewport(0, 0, this.width, this.height);
111
+ }
112
+
113
+ /**
114
+ * @method unbind
115
+ * @description Restores the default framebuffer (the canvas).
116
+ */
117
+ unbind() {
118
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);
119
+ }
120
+
121
+ /**
122
+ * @method dispose
123
+ * @description Frees all GL resources held by this target.
124
+ */
125
+ dispose() {
126
+ const gl = this.gl;
127
+ if (this.framebuffer) gl.deleteFramebuffer(this.framebuffer);
128
+ if (this.texture) gl.deleteTexture(this.texture);
129
+ if (this.depthBuffer) gl.deleteRenderbuffer(this.depthBuffer);
130
+ this.framebuffer = this.texture = this.depthBuffer = null;
131
+ }
132
+ }
133
+
134
+ export default RenderTarget;
package/src/Scene.js CHANGED
@@ -1,83 +1,115 @@
1
- import GameObject from "./components/GameObject.js";
2
-
3
- /**
4
- * @class Scene
5
- * @description Represents a scene
6
- * @param {Array} objects - The objects in the scene
7
- */
8
- class Scene {
9
- constructor(objects = []) {
10
- this.objects = objects;
11
- }
12
-
13
- /**
14
- * @method add
15
- * @description Adds an object to the scene
16
- * @param {GameObject} object - The object to add
17
- */
18
- add(object) {
19
- if (!(object instanceof GameObject)) {
20
- throw new Error("Object is not a GameObject");
21
- }
22
- object.setIsActive(true);
23
- if (!this.objects.some((obj) => obj.id === object.id)) {
24
- this.objects.push(object);
25
- }
26
- }
27
-
28
- /**
29
- * @method remove
30
- * @description Removes an object from the scene
31
- * @param {GameObject} object - The object to remove
32
- */
33
- remove(object) {
34
- if (!(object instanceof GameObject)) {
35
- throw new Error("Object is not a GameObject");
36
- }
37
- object.setIsActive(false);
38
- this.objects = this.objects.filter(function (obj) {
39
- var objId = obj ? obj.id : undefined;
40
- var targetId = object ? object.id : undefined;
41
- return objId !== targetId;
42
- });
43
- }
44
-
45
- /**
46
- * @method setIsActive
47
- * @description Sets the active state of the scene
48
- * @param {boolean} bool - The active state
49
- */
50
- setIsActive(bool) {
51
- for (let object of this.objects) {
52
- this.setActiveRecursive(object, bool);
53
- }
54
- }
55
-
56
- /**
57
- * @method setActiveRecursive
58
- * @description Sets the active state of the scene recursively
59
- * @param {Object} object - The object to set the active state of
60
- * @param {boolean} bool - The active state
61
- */
62
- setActiveRecursive(object, bool) {
63
- object.setIsActive(bool);
64
- }
65
-
66
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator
67
- [Symbol.iterator]() {
68
- let index = -1;
69
- const data = this.objects;
70
-
71
- return {
72
- next: () => ({ value: data[++index], done: !(index in data) }),
73
- };
74
- }
75
-
76
- forEach(callback) {
77
- for (const obj of this.objects) {
78
- callback(obj);
79
- }
80
- }
81
- }
82
-
83
- export default Scene;
1
+ import GameObject from "./components/GameObject.js";
2
+
3
+ /**
4
+ * @class Scene
5
+ * @description Represents a scene
6
+ * @param {Array} objects - The objects in the scene
7
+ */
8
+ class Scene {
9
+ constructor(objects = []) {
10
+ this.objects = objects;
11
+ }
12
+
13
+ /**
14
+ * @method add
15
+ * @description Adds an object to the scene
16
+ * @param {GameObject} object - The object to add
17
+ */
18
+ add(object) {
19
+ if (!(object instanceof GameObject)) {
20
+ throw new Error("Object is not a GameObject");
21
+ }
22
+ object.setIsActive(true);
23
+ if (!this.objects.some((obj) => obj.id === object.id)) {
24
+ this.objects.push(object);
25
+ }
26
+ }
27
+
28
+ /**
29
+ * @method remove
30
+ * @description Removes an object from the scene. By default the object's GPU
31
+ * resources stay alive so it can be re-added later; pass { dispose: true }
32
+ * to also destroy it (free GL buffers/textures, physics bodies) when it is
33
+ * being removed for good.
34
+ * @param {GameObject} object - The object to remove
35
+ * @param {Object} [options] - { dispose = false }
36
+ */
37
+ remove(object, options = {}) {
38
+ if (!(object instanceof GameObject)) {
39
+ throw new Error("Object is not a GameObject");
40
+ }
41
+ object.setIsActive(false);
42
+ this.objects = this.objects.filter(function (obj) {
43
+ var objId = obj ? obj.id : undefined;
44
+ var targetId = object ? object.id : undefined;
45
+ return objId !== targetId;
46
+ });
47
+ if (options.dispose) object.destroy();
48
+ }
49
+
50
+ /**
51
+ * @method dispose
52
+ * @description Destroys every object in the scene (freeing their GPU
53
+ * resources and physics bodies) and empties it. Call when a level/screen is
54
+ * torn down for good; removing objects without disposing leaks GL buffers
55
+ * over repeated scene swaps.
56
+ */
57
+ dispose() {
58
+ for (const object of this.objects) {
59
+ if (object && typeof object.destroy === "function") object.destroy();
60
+ }
61
+ this.objects = [];
62
+ }
63
+
64
+ /**
65
+ * @method update
66
+ * @description Ticks every active object's component lifecycle. Call once per
67
+ * frame (before or after drawScene) to drive Behaviour components.
68
+ * @param {number} deltaTime - Seconds since the previous frame
69
+ */
70
+ update(deltaTime) {
71
+ for (const object of this.objects) {
72
+ if (object.isActive && typeof object.update === "function") {
73
+ object.update(deltaTime);
74
+ }
75
+ }
76
+ }
77
+
78
+ /**
79
+ * @method setIsActive
80
+ * @description Sets the active state of the scene
81
+ * @param {boolean} bool - The active state
82
+ */
83
+ setIsActive(bool) {
84
+ for (let object of this.objects) {
85
+ this.setActiveRecursive(object, bool);
86
+ }
87
+ }
88
+
89
+ /**
90
+ * @method setActiveRecursive
91
+ * @description Sets the active state of the scene recursively
92
+ * @param {Object} object - The object to set the active state of
93
+ * @param {boolean} bool - The active state
94
+ */
95
+ setActiveRecursive(object, bool) {
96
+ object.setIsActive(bool);
97
+ }
98
+
99
+ [Symbol.iterator]() {
100
+ let index = -1;
101
+ const data = this.objects;
102
+
103
+ return {
104
+ next: () => ({ value: data[++index], done: !(index in data) }),
105
+ };
106
+ }
107
+
108
+ forEach(callback) {
109
+ for (const obj of this.objects) {
110
+ callback(obj);
111
+ }
112
+ }
113
+ }
114
+
115
+ export default Scene;
@@ -0,0 +1,266 @@
1
+ import GameObject from "./components/GameObject.js";
2
+ import { Square2D } from "./Shapes.js";
3
+ import { Vector2, Vector3 } from "./Physics.js";
4
+ import Color from "./Color.js";
5
+ import Easing from "./Easing.js";
6
+
7
+ /**
8
+ * @class ScreenEffects
9
+ * @description In-engine full-screen camera/transition effects: fade to/from a
10
+ * color, screen flash, and cinematic letterbox bars. Everything is drawn with
11
+ * the engine's own screen-space quads (no DOM/CSS overlay), so it survives
12
+ * resolution changes, post-processing, and split-screen (the quads overfill
13
+ * every viewport and are clipped per camera).
14
+ *
15
+ * Add it to your scene's update loop by calling `update(dt)` each frame.
16
+ *
17
+ * @example
18
+ * const fx = new ScreenEffects(scene, { layer: 100000 });
19
+ * await fx.fadeOut(0.4, new Color(0,0,0,255)); // to black
20
+ * loadNextLevel();
21
+ * await fx.fadeIn(0.4);
22
+ * // bumps:
23
+ * fx.flash(new Color(255,255,255,255), 0.15);
24
+ * // in the loop, before drawScene: fx.update(dt);
25
+ */
26
+ class ScreenEffects {
27
+ /**
28
+ * @param {Scene} scene - The scene to draw the overlays into
29
+ * @param {Object} [options] - { layer = 100000, size = 5000 }
30
+ */
31
+ constructor(scene, options = {}) {
32
+ this.scene = scene;
33
+ const layer = options.layer ?? 100000;
34
+ const half = options.size ?? 5000;
35
+
36
+ /** @private */
37
+ this._fade = this._makeQuad(layer, half, new Color(0, 0, 0, 255));
38
+ /** @private */
39
+ this._flashQuad = this._makeQuad(
40
+ layer + 1,
41
+ half,
42
+ new Color(255, 255, 255, 255)
43
+ );
44
+
45
+ /** @private */
46
+ this._barTop = this._makeQuad(layer + 2, half, new Color(0, 0, 0, 255));
47
+ /** @private */
48
+ this._barBottom = this._makeQuad(layer + 2, half, new Color(0, 0, 0, 255));
49
+ this._barTop.setOpacity(0);
50
+ this._barBottom.setOpacity(0);
51
+ /** @private */
52
+ this._letterboxHeight = 0;
53
+ /** @private */
54
+ this._letterboxTarget = 0;
55
+ /** @private */
56
+ this._barHalf = half;
57
+
58
+ /** @private */
59
+ this._fadeAnim = null;
60
+ /** @private */
61
+ this._flashAnim = null;
62
+ }
63
+
64
+ /** @private */
65
+ _makeQuad(layer, half, color) {
66
+ const go = new GameObject(
67
+ "ScreenEffect",
68
+ new Vector3(0, 0, 0),
69
+ 0,
70
+ new Vector2(half, half)
71
+ );
72
+ const shape = new Square2D();
73
+ shape.setColor(color);
74
+ shape.setUseLighting(false);
75
+ go.addComponent(shape);
76
+ go.setScreenSpace(true);
77
+ go.setLayer(layer);
78
+ go.alwaysVisible = true;
79
+ go.setOpacity(0);
80
+ /** @private */
81
+ this._shapeOf = this._shapeOf || new WeakMap();
82
+ this._shapeOf.set(go, shape);
83
+ this.scene.add(go);
84
+ return go;
85
+ }
86
+
87
+ /** @private */
88
+ _setColor(go, color) {
89
+ const shape = this._shapeOf.get(go);
90
+ if (shape) shape.setColor(color);
91
+ }
92
+
93
+ /**
94
+ * @method fadeOut
95
+ * @description Fades the screen to a solid color (opacity 0 -> 1).
96
+ * @param {number} duration - Seconds
97
+ * @param {Color} [color] - Target color (default black)
98
+ * @param {Function} [easing] - Easing function (default linear)
99
+ * @returns {Promise<void>} - Resolves when the fade completes
100
+ */
101
+ fadeOut(
102
+ duration = 0.4,
103
+ color = new Color(0, 0, 0, 255),
104
+ easing = Easing.linear
105
+ ) {
106
+ this._setColor(this._fade, color);
107
+ return this._startFade(this._fade.getOpacity(), 1, duration, easing);
108
+ }
109
+
110
+ /**
111
+ * @method fadeIn
112
+ * @description Fades the screen back in from the current overlay (opacity -> 0).
113
+ * @param {number} duration - Seconds
114
+ * @param {Function} [easing] - Easing function (default linear)
115
+ * @returns {Promise<void>}
116
+ */
117
+ fadeIn(duration = 0.4, easing = Easing.linear) {
118
+ return this._startFade(this._fade.getOpacity(), 0, duration, easing);
119
+ }
120
+
121
+ /** @private */
122
+ _startFade(from, to, duration, easing) {
123
+ return new Promise((resolve) => {
124
+ if (this._fadeAnim && this._fadeAnim.resolve) this._fadeAnim.resolve();
125
+ if (duration <= 0) {
126
+ this._fade.setOpacity(to);
127
+ resolve();
128
+ this._fadeAnim = null;
129
+ return;
130
+ }
131
+ this._fadeAnim = { from, to, elapsed: 0, duration, easing, resolve };
132
+ });
133
+ }
134
+
135
+ /**
136
+ * @method flash
137
+ * @description Flashes a color that ramps to full then fades out.
138
+ * @param {Color} [color] - Flash color (default white)
139
+ * @param {number} duration - Total seconds for the flash
140
+ * @returns {Promise<void>}
141
+ */
142
+ flash(color = new Color(255, 255, 255, 255), duration = 0.2) {
143
+ this._setColor(this._flashQuad, color);
144
+ return new Promise((resolve) => {
145
+ if (this._flashAnim && this._flashAnim.resolve) this._flashAnim.resolve();
146
+ this._flashAnim = {
147
+ elapsed: 0,
148
+ duration: Math.max(0.0001, duration),
149
+ resolve,
150
+ };
151
+ });
152
+ }
153
+
154
+ /**
155
+ * @method transition
156
+ * @description Fades the screen out to a color, runs a swap callback at the
157
+ * darkest point (e.g. tear down the old scene and build the new one), then
158
+ * fades back in. Requires `update(dt)` to be pumped each frame by your loop.
159
+ * @param {Function} swap - Called (and awaited) while the screen is covered
160
+ * @param {Object} [options] - { duration = 0.4, color = black, outDuration,
161
+ * inDuration, easing }
162
+ * @returns {Promise<void>} - Resolves after the fade-in completes
163
+ */
164
+ async transition(swap, options = {}) {
165
+ const {
166
+ duration = 0.4,
167
+ color = new Color(0, 0, 0, 255),
168
+ outDuration,
169
+ inDuration,
170
+ easing = Easing.linear,
171
+ } = options;
172
+ await this.fadeOut(outDuration ?? duration, color, easing);
173
+ if (typeof swap === "function") await swap();
174
+ await this.fadeIn(inDuration ?? duration, easing);
175
+ }
176
+
177
+ /**
178
+ * @method setLetterbox
179
+ * @description Animates cinematic black bars to the given height (in pixels,
180
+ * per bar). Pass 0 to retract them.
181
+ * @param {number} heightPx - Target bar height in pixels
182
+ */
183
+ setLetterbox(heightPx) {
184
+ this._letterboxTarget = Math.max(0, heightPx);
185
+ }
186
+
187
+ /**
188
+ * @method update
189
+ * @description Advances all active transitions. Call once per frame.
190
+ * @param {number} dt - Seconds since last frame
191
+ */
192
+ update(dt) {
193
+ if (this._fadeAnim) {
194
+ const a = this._fadeAnim;
195
+ a.elapsed += dt;
196
+ const k = Math.min(1, a.elapsed / a.duration);
197
+ const eased = a.easing(k);
198
+ this._fade.setOpacity(a.from + (a.to - a.from) * eased);
199
+ if (k >= 1) {
200
+ const resolve = a.resolve;
201
+ this._fadeAnim = null;
202
+ if (resolve) resolve();
203
+ }
204
+ }
205
+
206
+ if (this._flashAnim) {
207
+ const a = this._flashAnim;
208
+ a.elapsed += dt;
209
+ const k = Math.min(1, a.elapsed / a.duration);
210
+ const opacity = k < 0.25 ? k / 0.25 : 1 - (k - 0.25) / 0.75;
211
+ this._flashQuad.setOpacity(Math.max(0, opacity));
212
+ if (k >= 1) {
213
+ const resolve = a.resolve;
214
+ this._flashAnim = null;
215
+ this._flashQuad.setOpacity(0);
216
+ if (resolve) resolve();
217
+ }
218
+ }
219
+
220
+ if (this._letterboxHeight !== this._letterboxTarget) {
221
+ const speed = 1200;
222
+ const dir = Math.sign(this._letterboxTarget - this._letterboxHeight);
223
+ this._letterboxHeight += dir * speed * dt;
224
+ if (
225
+ (dir > 0 && this._letterboxHeight > this._letterboxTarget) ||
226
+ (dir < 0 && this._letterboxHeight < this._letterboxTarget)
227
+ ) {
228
+ this._letterboxHeight = this._letterboxTarget;
229
+ }
230
+ }
231
+ const visible = this._letterboxHeight > 0.5;
232
+ this._barTop.setOpacity(visible ? 1 : 0);
233
+ this._barBottom.setOpacity(visible ? 1 : 0);
234
+ if (visible) {
235
+ const offset =
236
+ this._barHalf + this._screenHalfHeight() - this._letterboxHeight;
237
+ this._barTop.transform.position.y = offset;
238
+ this._barBottom.transform.position.y = -offset;
239
+ }
240
+ }
241
+
242
+ /** @private */
243
+ _screenHalfHeight() {
244
+ if (typeof window !== "undefined" && window.innerHeight) {
245
+ return window.innerHeight / 2;
246
+ }
247
+ return 360;
248
+ }
249
+
250
+ /**
251
+ * @method destroy
252
+ * @description Removes all overlay objects from the scene.
253
+ */
254
+ destroy() {
255
+ for (const go of [
256
+ this._fade,
257
+ this._flashQuad,
258
+ this._barTop,
259
+ this._barBottom,
260
+ ]) {
261
+ this.scene.remove(go);
262
+ }
263
+ }
264
+ }
265
+
266
+ export default ScreenEffects;