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
@@ -3,6 +3,9 @@ import Instance from "./Instance.js";
3
3
  import Drawable from "./Drawable.js";
4
4
  import { mat4 } from "gl-matrix";
5
5
  import GLManager from "./managers/GLManager.js";
6
+ import GLState from "./managers/GLState.js";
7
+ import IDManager from "./managers/IDManager.js";
8
+ import RenderStats from "./managers/RenderStats.js";
6
9
  import RigidBody from "./components/RigidBody.js";
7
10
  import Collider from "./components/Collider.js";
8
11
 
@@ -35,16 +38,7 @@ class InstancedTexture extends Drawable {
35
38
  ) {
36
39
  const vertices = [1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0];
37
40
  const verticesBuffer = initVertexBuffer(GLManager.getGL(), vertices);
38
- const textureCoordinates = [
39
- 1.0,
40
- 1.0, // Top-right
41
- 0.0,
42
- 1.0, // Top-left
43
- 1.0,
44
- 0.0, // Bottom-right
45
- 0.0,
46
- 0.0, // Bottom-left
47
- ];
41
+ const textureCoordinates = [1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0];
48
42
  const texCoordBuffer = initVertexBuffer(
49
43
  GLManager.getGL(),
50
44
  textureCoordinates
@@ -71,26 +65,54 @@ class InstancedTexture extends Drawable {
71
65
  this.instanceCount = instanceCount;
72
66
 
73
67
  this.instances = [];
74
- this.instanceMatrices = new Float32Array(instanceCount * 16); // 4x4 matrix for each instance
75
- this.instanceTexCoords = new Float32Array(instanceCount * 8); // 4 vertices * 2 coords per instance
68
+ this.instanceMatrices = new Float32Array(instanceCount * 16);
69
+ this.instanceTexCoords = new Float32Array(instanceCount * 8);
70
+ this.instanceColors = new Float32Array(instanceCount * 4).fill(1);
76
71
 
77
- // Create instanced buffer for matrices
78
72
  this.instanceMatrixBuffer = initInstancedBuffer(
79
73
  GLManager.getGL(),
80
74
  this.instanceMatrices,
81
75
  16
82
76
  );
83
77
 
84
- // Create instanced buffer for texture coordinates
85
78
  this.instanceTexCoordBuffer = initInstancedBuffer(
86
79
  GLManager.getGL(),
87
80
  this.instanceTexCoords,
88
81
  8
89
82
  );
90
83
 
91
- // Event handling for instances
84
+ this.instanceColorBuffer = initInstancedBuffer(
85
+ GLManager.getGL(),
86
+ this.instanceColors,
87
+ 4
88
+ );
89
+
92
90
  this.instanceClickListeners = new Map();
93
91
  this.instanceHoverListeners = new Map();
92
+
93
+ this.static = false;
94
+ /** @private */
95
+ this._matricesDirty = true;
96
+ /** The animation every instance starts with; set by playAnimation/playAnimationOnce. @private */
97
+ this._defaultAnimation = null;
98
+ }
99
+
100
+ /**
101
+ * @method setStatic
102
+ * @description Toggles static mode (see constructor notes).
103
+ * @param {boolean} isStatic - Whether instances are non-moving
104
+ */
105
+ setStatic(isStatic) {
106
+ this.static = isStatic;
107
+ this._matricesDirty = true;
108
+ }
109
+
110
+ /**
111
+ * @method markDirty
112
+ * @description Forces an instance-matrix rebuild on the next draw.
113
+ */
114
+ markDirty() {
115
+ this._matricesDirty = true;
94
116
  }
95
117
 
96
118
  /**
@@ -107,6 +129,12 @@ class InstancedTexture extends Drawable {
107
129
  if (this.instances.length < this.instanceCount) {
108
130
  this.instances.push(instance);
109
131
  instance.setParent(this);
132
+ if (this._defaultAnimation) {
133
+ const { frames, speed, once } = this._defaultAnimation;
134
+ if (once) instance.playAnimationOnce(frames, speed);
135
+ else instance.playAnimation(frames, speed);
136
+ }
137
+ this._matricesDirty = true;
110
138
  } else {
111
139
  console.warn("Max instance count reached.");
112
140
  }
@@ -122,18 +150,17 @@ class InstancedTexture extends Drawable {
122
150
  (instance) => instance.id === instanceID
123
151
  );
124
152
  if (index !== -1) {
125
- this.instances.splice(index, 1);
153
+ const last = this.instances.length - 1;
154
+ if (index !== last) {
155
+ this.instances[index] = this.instances[last];
156
+ }
157
+ this.instances.pop();
126
158
 
127
- // Clean up event listeners for this instance
128
159
  this.instanceClickListeners.delete(instanceID);
129
160
  this.instanceHoverListeners.delete(instanceID);
161
+ IDManager.release(instanceID);
130
162
 
131
- this.instanceMatrices = new Float32Array(this.instanceCount * 16);
132
- this.instanceTexCoords = new Float32Array(this.instanceCount * 8);
133
-
134
- // Update instance matrices and texture coordinates
135
- this.updateAllInstanceMatrices();
136
- this.updateAllInstanceTexCoords();
163
+ this._matricesDirty = true;
137
164
  } else {
138
165
  console.warn("Instance not found:", instanceID);
139
166
  }
@@ -144,14 +171,15 @@ class InstancedTexture extends Drawable {
144
171
  * @description Clears all instances from the instanced texture
145
172
  */
146
173
  clearInstances() {
174
+ for (const inst of this.instances) IDManager.release(inst.id);
147
175
  this.instances = [];
148
176
 
149
- // Clear all event listeners
150
177
  this.instanceClickListeners.clear();
151
178
  this.instanceHoverListeners.clear();
152
179
 
153
- this.instanceMatrices = new Float32Array(this.instanceCount * 16); // Reset matrices
154
- this.instanceTexCoords = new Float32Array(this.instanceCount * 8); // Reset texture coordinates
180
+ this.instanceMatrices = new Float32Array(this.instanceCount * 16);
181
+ this.instanceTexCoords = new Float32Array(this.instanceCount * 8);
182
+ this.instanceColors = new Float32Array(this.instanceCount * 4).fill(1);
155
183
 
156
184
  this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instanceMatrixBuffer);
157
185
  this.gl.bufferData(
@@ -166,6 +194,13 @@ class InstancedTexture extends Drawable {
166
194
  this.instanceTexCoords,
167
195
  this.gl.DYNAMIC_DRAW
168
196
  );
197
+
198
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instanceColorBuffer);
199
+ this.gl.bufferData(
200
+ this.gl.ARRAY_BUFFER,
201
+ this.instanceColors,
202
+ this.gl.DYNAMIC_DRAW
203
+ );
169
204
  }
170
205
 
171
206
  /**
@@ -177,6 +212,7 @@ class InstancedTexture extends Drawable {
177
212
  this.instanceCount = newCount;
178
213
  this.instanceMatrices = new Float32Array(newCount * 16);
179
214
  this.instanceTexCoords = new Float32Array(newCount * 8);
215
+ this.instanceColors = new Float32Array(newCount * 4).fill(1);
180
216
  this.instanceMatrixBuffer = initInstancedBuffer(
181
217
  this.gl,
182
218
  this.instanceMatrices,
@@ -187,6 +223,11 @@ class InstancedTexture extends Drawable {
187
223
  this.instanceTexCoords,
188
224
  8
189
225
  );
226
+ this.instanceColorBuffer = initInstancedBuffer(
227
+ this.gl,
228
+ this.instanceColors,
229
+ 4
230
+ );
190
231
  }
191
232
 
192
233
  /**
@@ -219,7 +260,6 @@ class InstancedTexture extends Drawable {
219
260
  mat4.rotate(matrix, matrix, rotation, [0, 0, 1]);
220
261
  mat4.scale(matrix, matrix, scale);
221
262
 
222
- // Copy matrix values to the appropriate place in instanceMatrices
223
263
  const offset = index * 16;
224
264
  for (let i = 0; i < 16; i++) {
225
265
  this.instanceMatrices[offset + i] = matrix[i];
@@ -236,7 +276,6 @@ class InstancedTexture extends Drawable {
236
276
  this.updateInstanceMatrix(i);
237
277
  }
238
278
 
239
- // Update the instance buffer with new matrices
240
279
  this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instanceMatrixBuffer);
241
280
  this.gl.bufferData(
242
281
  this.gl.ARRAY_BUFFER,
@@ -244,8 +283,47 @@ class InstancedTexture extends Drawable {
244
283
  this.gl.DYNAMIC_DRAW
245
284
  );
246
285
 
247
- // Also update texture coordinates
248
286
  this.updateAllInstanceTexCoords();
287
+ this.updateAllInstanceColors();
288
+ }
289
+
290
+ /**
291
+ * @method updateInstanceColor
292
+ * @description Writes a single instance's RGBA tint into the color array.
293
+ * Reads `instance._tint` ([r,g,b,a] in 0..1) when set, else opaque white.
294
+ * @param {number} index - The instance index
295
+ */
296
+ updateInstanceColor(index) {
297
+ if (index < 0 || index >= this.instances.length) return;
298
+ const tint = this.instances[index]._tint;
299
+ const offset = index * 4;
300
+ if (tint) {
301
+ this.instanceColors[offset] = tint[0];
302
+ this.instanceColors[offset + 1] = tint[1];
303
+ this.instanceColors[offset + 2] = tint[2];
304
+ this.instanceColors[offset + 3] = tint[3];
305
+ } else {
306
+ this.instanceColors[offset] = 1;
307
+ this.instanceColors[offset + 1] = 1;
308
+ this.instanceColors[offset + 2] = 1;
309
+ this.instanceColors[offset + 3] = 1;
310
+ }
311
+ }
312
+
313
+ /**
314
+ * @method updateAllInstanceColors
315
+ * @description Rebuilds and uploads the per-instance color buffer.
316
+ */
317
+ updateAllInstanceColors() {
318
+ for (let i = 0; i < this.instances.length; i++) {
319
+ this.updateInstanceColor(i);
320
+ }
321
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instanceColorBuffer);
322
+ this.gl.bufferData(
323
+ this.gl.ARRAY_BUFFER,
324
+ this.instanceColors,
325
+ this.gl.DYNAMIC_DRAW
326
+ );
249
327
  }
250
328
 
251
329
  /**
@@ -259,8 +337,9 @@ class InstancedTexture extends Drawable {
259
337
  index < this.instanceCount &&
260
338
  index < this.instances.length
261
339
  ) {
262
- const frame = this.instances[index].frame;
263
- const texCoords = this.getFrameTexCoords(frame);
340
+ const instance = this.instances[index];
341
+ const texCoords =
342
+ instance._regionUV || this.getFrameTexCoords(instance.frame);
264
343
 
265
344
  const offset = index * 8;
266
345
  for (let i = 0; i < 8; i++) {
@@ -278,7 +357,6 @@ class InstancedTexture extends Drawable {
278
357
  this.updateInstanceTexCoords(i);
279
358
  }
280
359
 
281
- // Update the instance texture coordinate buffer
282
360
  this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instanceTexCoordBuffer);
283
361
  this.gl.bufferData(
284
362
  this.gl.ARRAY_BUFFER,
@@ -305,7 +383,6 @@ class InstancedTexture extends Drawable {
305
383
  update(currentTime) {
306
384
  let needsUpdate = false;
307
385
 
308
- // Update animations for all instances
309
386
  for (let i = 0; i < this.instances.length; i++) {
310
387
  const instance = this.instances[i];
311
388
  if (instance.updateAnimation(currentTime)) {
@@ -313,7 +390,6 @@ class InstancedTexture extends Drawable {
313
390
  }
314
391
  }
315
392
 
316
- // If any instance frame changed, update texture coordinates
317
393
  if (needsUpdate) {
318
394
  this.updateAllInstanceTexCoords();
319
395
  }
@@ -406,47 +482,115 @@ class InstancedTexture extends Drawable {
406
482
  let texLeft, texRight, texTop, texBottom;
407
483
 
408
484
  if (this.frameWidth > 0 && this.frameHeight > 0) {
409
- // Spritesheet animation
410
485
  const col = frame % this.framesPerRow;
411
486
  const row = Math.floor(frame / this.framesPerRow);
412
487
 
413
- texLeft = ((col + 1) * this.frameWidth) / this.textureWidth;
414
- texRight = (col * this.frameWidth) / this.textureWidth;
488
+ const ix = 0.5 / this.textureWidth;
489
+ const iy = 0.5 / this.textureHeight;
490
+
491
+ texLeft = ((col + 1) * this.frameWidth) / this.textureWidth - ix;
492
+ texRight = (col * this.frameWidth) / this.textureWidth + ix;
415
493
  texTop =
416
494
  (this.textureHeight - row * this.frameHeight - this.frameHeight) /
417
- this.textureHeight;
495
+ this.textureHeight +
496
+ iy;
418
497
  texBottom =
419
- (this.textureHeight - row * this.frameHeight) / this.textureHeight;
498
+ (this.textureHeight - row * this.frameHeight) / this.textureHeight - iy;
420
499
  } else {
421
- // Static image
422
500
  texLeft = 1.0;
423
501
  texRight = 0.0;
424
502
  texTop = 0.0;
425
503
  texBottom = 1.0;
426
504
  }
427
505
 
428
- // Return texture coordinates (considering mirroring)
429
506
  return this.mirrored
430
507
  ? [
431
- texRight,
432
- texBottom,
433
- texLeft,
434
- texBottom,
435
- texRight,
436
- texTop,
437
- texLeft,
438
- texTop,
439
- ]
508
+ texRight,
509
+ texBottom,
510
+ texLeft,
511
+ texBottom,
512
+ texRight,
513
+ texTop,
514
+ texLeft,
515
+ texTop,
516
+ ]
440
517
  : [
441
- texLeft,
442
- texBottom,
443
- texRight,
444
- texBottom,
445
- texLeft,
446
- texTop,
447
- texRight,
448
- texTop,
449
- ];
518
+ texLeft,
519
+ texBottom,
520
+ texRight,
521
+ texBottom,
522
+ texLeft,
523
+ texTop,
524
+ texRight,
525
+ texTop,
526
+ ];
527
+ }
528
+
529
+ /**
530
+ * @method _restoreGL
531
+ * @description Rebuilds the per-instance buffers from their CPU-side arrays
532
+ * after a WebGL context loss, on top of the base Drawable restore.
533
+ * @private
534
+ */
535
+ _restoreGL() {
536
+ if (this._disposed) return;
537
+ super._restoreGL();
538
+ const gl = this.gl;
539
+ if (!gl) return;
540
+ this.instanceMatrixBuffer = initInstancedBuffer(
541
+ gl,
542
+ this.instanceMatrices,
543
+ 16
544
+ );
545
+ this.instanceTexCoordBuffer = initInstancedBuffer(
546
+ gl,
547
+ this.instanceTexCoords,
548
+ 8
549
+ );
550
+ this.instanceColorBuffer = initInstancedBuffer(gl, this.instanceColors, 4);
551
+ this._matricesDirty = true;
552
+ }
553
+
554
+ /**
555
+ * @method dispose
556
+ * @description Frees the per-instance GPU buffers on top of the base
557
+ * Drawable disposal. Safe to call twice.
558
+ */
559
+ dispose() {
560
+ if (this._disposed) return;
561
+ const gl = this.gl;
562
+ if (gl) {
563
+ if (this.instanceMatrixBuffer) gl.deleteBuffer(this.instanceMatrixBuffer);
564
+ if (this.instanceTexCoordBuffer)
565
+ gl.deleteBuffer(this.instanceTexCoordBuffer);
566
+ if (this.instanceColorBuffer) gl.deleteBuffer(this.instanceColorBuffer);
567
+ }
568
+ this.instanceMatrixBuffer = null;
569
+ this.instanceTexCoordBuffer = null;
570
+ this.instanceColorBuffer = null;
571
+ this.instances = [];
572
+ super.dispose();
573
+ }
574
+
575
+ /**
576
+ * @method syncPhysics
577
+ * @description Copies simulation state (position + rotation) from each
578
+ * instance's dynamic rigidbody onto the instance transform. Driven once per
579
+ * frame by the owning GameObject's syncPhysics(), so draw() stays read-only.
580
+ */
581
+ syncPhysics() {
582
+ for (let i = 0; i < this.instances.length; i++) {
583
+ const instance = this.instances[i];
584
+ const rigidBody = instance.getComponent(RigidBody);
585
+ if (!rigidBody || rigidBody.getType() !== "dynamic") continue;
586
+ rigidBody.syncTransform(instance.transform);
587
+ const collider =
588
+ rigidBody.getCollider() || instance.getComponent(Collider);
589
+ if (collider && typeof collider.syncDebugShape === "function") {
590
+ collider.syncDebugShape(instance.transform);
591
+ }
592
+ this._matricesDirty = true;
593
+ }
450
594
  }
451
595
 
452
596
  /**
@@ -454,62 +598,48 @@ class InstancedTexture extends Drawable {
454
598
  * @description Draws the instanced texture
455
599
  */
456
600
  draw() {
457
- this.updateAllInstanceMatrices();
458
- this.update(performance.now());
459
- for (let i = 0; i < this.instances.length; i++) {
460
- let instance = this.instances[i];
461
- let rigidBody = instance.getComponent(RigidBody);
462
- let collider = instance.getComponent(Collider);
463
-
464
- if (rigidBody) {
465
- if (rigidBody.getType() === "dynamic") {
466
- const updatedPos = rigidBody.getBody().getPosition();
467
- const rigidBodyOffset = rigidBody.getOffset();
468
- instance.transform.position.x =
469
- updatedPos.x * rigidBody.getPhysics().getScale() -
470
- rigidBodyOffset.x;
471
- instance.transform.position.y =
472
- updatedPos.y * rigidBody.getPhysics().getScale() -
473
- rigidBodyOffset.y;
474
- if (collider) {
475
- collider.debugShape.gameObject.transform.position.x =
476
- instance.transform.position.x;
477
- collider.debugShape.gameObject.transform.position.y =
478
- instance.transform.position.y;
479
- }
480
- }
481
- }
601
+ if (!this.instances || this.instances.length === 0) return;
602
+
603
+ if (!this.static || this._matricesDirty) {
604
+ this.updateAllInstanceMatrices();
605
+ this._matricesDirty = false;
482
606
  }
483
- this.gl.uniform1i(
607
+ this.update(performance.now());
608
+ GLState.uniform1i(
609
+ this.gl,
484
610
  this.programInfo.uniformLocations.useTexture,
485
611
  this.useTexture
486
612
  );
487
613
 
488
- this.gl.uniform1i(this.programInfo.uniformLocations.useInstances, true);
614
+ GLState.uniform1i(
615
+ this.gl,
616
+ this.programInfo.uniformLocations.useInstances,
617
+ true
618
+ );
489
619
 
490
- // Set lighting uniform
491
- this.gl.uniform1i(
620
+ GLState.uniform1i(
621
+ this.gl,
492
622
  this.programInfo.uniformLocations.useLighting,
493
623
  this.useLighting
494
624
  );
495
625
 
496
- // Always set the color uniform for tinting
497
- this.gl.uniform4fv(this.programInfo.uniformLocations.color, this.color);
626
+ GLState.uniform4fv(
627
+ this.gl,
628
+ this.programInfo.uniformLocations.color,
629
+ this.color
630
+ );
498
631
 
499
- // Set per-GameObject opacity if available (defaults to 1.0)
500
632
  const ownerGameObject = this.getParent();
501
633
  const ownerOpacity =
502
634
  ownerGameObject && typeof ownerGameObject.opacity === "number"
503
635
  ? ownerGameObject.opacity
504
636
  : 1.0;
505
- if (this.programInfo.uniformLocations.uOpacity) {
506
- this.gl.uniform1f(
507
- this.programInfo.uniformLocations.uOpacity,
508
- ownerOpacity
509
- );
510
- }
637
+ GLState.uniform1f(
638
+ this.gl,
639
+ this.programInfo.uniformLocations.uOpacity,
640
+ ownerOpacity
641
+ );
511
642
 
512
- // Bind and set vertex position buffer
513
643
  this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.verticesBuffer);
514
644
  this.gl.vertexAttribPointer(
515
645
  this.programInfo.attribLocations.vertexPosition,
@@ -523,7 +653,6 @@ class InstancedTexture extends Drawable {
523
653
  this.programInfo.attribLocations.vertexPosition
524
654
  );
525
655
 
526
- // Bind and set texture coordinate buffer (for non-instanced rendering)
527
656
  if (this.useTexture) {
528
657
  this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.texCoordBuffer);
529
658
  this.gl.vertexAttribPointer(
@@ -540,12 +669,11 @@ class InstancedTexture extends Drawable {
540
669
 
541
670
  if (this.texture) {
542
671
  this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);
672
+ RenderStats.textureBinds++;
543
673
  }
544
674
 
545
- // Set up per-instance texture coordinate attributes
546
675
  this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instanceTexCoordBuffer);
547
676
 
548
- // Each instance has 4 texture coordinates (for 4 vertices)
549
677
  const texCoordAttribs = [
550
678
  this.programInfo.attribLocations.instanceTexCoord0,
551
679
  this.programInfo.attribLocations.instanceTexCoord1,
@@ -562,15 +690,22 @@ class InstancedTexture extends Drawable {
562
690
  2,
563
691
  this.gl.FLOAT,
564
692
  false,
565
- 8 * 4, // 8 floats per instance (4 vertices * 2 coords)
566
- i * 2 * 4 // offset for each vertex
693
+ 8 * 4,
694
+ i * 2 * 4
567
695
  );
568
- this.gl.vertexAttribDivisor(loc, 1); // This makes it instanced
696
+ this.gl.vertexAttribDivisor(loc, 1);
569
697
  }
570
698
  }
571
699
  }
572
700
 
573
- // Set up instance matrix attributes (4 vec4s for each matrix)
701
+ const colorLoc = this.programInfo.attribLocations.instanceColor;
702
+ if (colorLoc !== undefined && colorLoc !== -1) {
703
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instanceColorBuffer);
704
+ this.gl.enableVertexAttribArray(colorLoc);
705
+ this.gl.vertexAttribPointer(colorLoc, 4, this.gl.FLOAT, false, 0, 0);
706
+ this.gl.vertexAttribDivisor(colorLoc, 1);
707
+ }
708
+
574
709
  this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instanceMatrixBuffer);
575
710
 
576
711
  const matrixLoc = this.programInfo.attribLocations.instanceMatrix;
@@ -585,26 +720,32 @@ class InstancedTexture extends Drawable {
585
720
  16 * 4,
586
721
  i * 4 * 4
587
722
  );
588
- this.gl.vertexAttribDivisor(loc, 1); // This makes it instanced
723
+ this.gl.vertexAttribDivisor(loc, 1);
589
724
  }
590
725
 
591
- // Draw all instances
592
726
  const drawMode =
593
727
  this.vertices.length === 8 ? this.gl.TRIANGLE_STRIP : this.gl.TRIANGLES;
728
+ this._applyBlend();
594
729
  this.gl.drawArraysInstanced(
595
730
  drawMode,
596
731
  0,
597
732
  this.vertices.length / 2,
598
- this.instances.length // Use actual instance count, not max
733
+ this.instances.length
599
734
  );
735
+ this._restoreBlend();
736
+ RenderStats.drawCalls++;
737
+ RenderStats.quads += this.instances.length;
600
738
 
601
- // Reset attribute divisors
602
739
  for (let i = 0; i < 4; i++) {
603
740
  this.gl.vertexAttribDivisor(matrixLoc + i, 0);
604
741
  this.gl.disableVertexAttribArray(matrixLoc + i);
605
742
  }
606
743
 
607
- // Reset texture coordinate attribute divisors
744
+ if (colorLoc !== undefined && colorLoc !== -1) {
745
+ this.gl.vertexAttribDivisor(colorLoc, 0);
746
+ this.gl.disableVertexAttribArray(colorLoc);
747
+ }
748
+
608
749
  if (this.useTexture) {
609
750
  const texCoordAttribs = [
610
751
  this.programInfo.attribLocations.instanceTexCoord0,
@@ -622,7 +763,11 @@ class InstancedTexture extends Drawable {
622
763
  }
623
764
  }
624
765
 
625
- this.gl.uniform1i(this.programInfo.uniformLocations.useInstances, false);
766
+ GLState.uniform1i(
767
+ this.gl,
768
+ this.programInfo.uniformLocations.useInstances,
769
+ false
770
+ );
626
771
  }
627
772
 
628
773
  /**
@@ -781,9 +926,9 @@ class InstancedTexture extends Drawable {
781
926
  if (listeners) {
782
927
  listeners.forEach((func) => func(event, clickedInstance));
783
928
  }
784
- return true; // Event was handled
929
+ return true;
785
930
  }
786
- return false; // No instance was clicked
931
+ return false;
787
932
  }
788
933
 
789
934
  /**
@@ -798,7 +943,6 @@ class InstancedTexture extends Drawable {
798
943
  const hoveredInstance = this.getInstanceAtPoint(x, y);
799
944
 
800
945
  if (hoveredInstance !== lastHoveredInstance) {
801
- // Handle leave event for previous instance
802
946
  if (lastHoveredInstance) {
803
947
  const leaveListeners = this.instanceHoverListeners.get(
804
948
  lastHoveredInstance.id
@@ -810,7 +954,6 @@ class InstancedTexture extends Drawable {
810
954
  }
811
955
  }
812
956
 
813
- // Handle enter event for new instance
814
957
  if (hoveredInstance) {
815
958
  const enterListeners = this.instanceHoverListeners.get(
816
959
  hoveredInstance.id
@@ -826,34 +969,72 @@ class InstancedTexture extends Drawable {
826
969
 
827
970
  /**
828
971
  * @method playAnimation
829
- * @description Plays an animation
972
+ * @description Plays the same looping animation, in lockstep, on every
973
+ * instance: the current ones immediately, and any added later via
974
+ * {@link InstancedTexture#addInstance}. For giving individual instances
975
+ * their own independent animation instead, use
976
+ * {@link InstancedTexture#animateInstance}.
830
977
  * @param {Array} frames - The frames to play
831
- * @param {number} speed - The speed of the animation
978
+ * @param {number} [speed=1000] - Milliseconds per frame
832
979
  */
833
980
  playAnimation(frames, speed = 1000) {
834
- // Override with empty implementation
835
- console.warn("playAnimation is not implemented for InstancedTexture");
981
+ if (!frames || frames.length === 0) {
982
+ console.error(
983
+ "[InstancedTexture] playAnimation: frames cannot be empty!"
984
+ );
985
+ return;
986
+ }
987
+ this._defaultAnimation = { frames, speed, once: false };
988
+ for (const instance of this.instances)
989
+ instance.playAnimation(frames, speed);
836
990
  }
837
991
 
838
992
  /**
839
993
  * @method playAnimationOnce
840
- * @description Plays an animation once
994
+ * @description Plays the same animation once, in lockstep, on every
995
+ * instance, then holds each on its last frame: the current instances
996
+ * immediately, and any added later via {@link InstancedTexture#addInstance}.
841
997
  * @param {Array} frames - The frames to play
842
- * @param {number} speed - The speed of the animation
998
+ * @param {number} [speed=1000] - Milliseconds per frame
843
999
  */
844
1000
  playAnimationOnce(frames, speed = 1000) {
845
- // Override with empty implementation
846
- console.warn("playAnimationOnce is not implemented for InstancedTexture");
1001
+ if (!frames || frames.length === 0) {
1002
+ console.error(
1003
+ "[InstancedTexture] playAnimationOnce: frames cannot be empty!"
1004
+ );
1005
+ return;
1006
+ }
1007
+ this._defaultAnimation = { frames, speed, once: true };
1008
+ for (const instance of this.instances) {
1009
+ instance.playAnimationOnce(frames, speed);
1010
+ }
1011
+ }
1012
+
1013
+ /**
1014
+ * @method stopAnimation
1015
+ * @description Stops the shared animation started by playAnimation/
1016
+ * playAnimationOnce on every current instance, and clears it so instances
1017
+ * added afterwards no longer start playing it either. Instances animated
1018
+ * individually via {@link InstancedTexture#animateInstance} are unaffected
1019
+ * unless you stop them the same way, through {@link InstancedTexture#stopInstanceAnimation}.
1020
+ * @param {boolean} [revertToOriginal=false] - Whether to reset each
1021
+ * instance back to the frame it had before playAnimation was called
1022
+ */
1023
+ stopAnimation(revertToOriginal = false) {
1024
+ this._defaultAnimation = null;
1025
+ for (const instance of this.instances) {
1026
+ instance.stopAnimation(revertToOriginal);
1027
+ }
847
1028
  }
848
1029
 
849
1030
  /**
850
1031
  * @method getAnimation
851
- * @description Gets the animation
852
- * @returns {Array} - The animation
1032
+ * @description Returns the frames of the shared animation set by
1033
+ * playAnimation/playAnimationOnce, or an empty array if none is playing.
1034
+ * @returns {Array} - The animation frames
853
1035
  */
854
1036
  getAnimation() {
855
- // Override with empty implementation
856
- console.warn("getAnimation is not implemented for InstancedTexture");
1037
+ return this._defaultAnimation ? this._defaultAnimation.frames : [];
857
1038
  }
858
1039
  }
859
1040