emeraldengine 2.2.1 → 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 -582
  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
@@ -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,52 @@ 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
+ }
97
+
98
+ /**
99
+ * @method setStatic
100
+ * @description Toggles static mode (see constructor notes).
101
+ * @param {boolean} isStatic - Whether instances are non-moving
102
+ */
103
+ setStatic(isStatic) {
104
+ this.static = isStatic;
105
+ this._matricesDirty = true;
106
+ }
107
+
108
+ /**
109
+ * @method markDirty
110
+ * @description Forces an instance-matrix rebuild on the next draw.
111
+ */
112
+ markDirty() {
113
+ this._matricesDirty = true;
94
114
  }
95
115
 
96
116
  /**
@@ -107,6 +127,7 @@ class InstancedTexture extends Drawable {
107
127
  if (this.instances.length < this.instanceCount) {
108
128
  this.instances.push(instance);
109
129
  instance.setParent(this);
130
+ this._matricesDirty = true;
110
131
  } else {
111
132
  console.warn("Max instance count reached.");
112
133
  }
@@ -122,18 +143,17 @@ class InstancedTexture extends Drawable {
122
143
  (instance) => instance.id === instanceID
123
144
  );
124
145
  if (index !== -1) {
125
- this.instances.splice(index, 1);
146
+ const last = this.instances.length - 1;
147
+ if (index !== last) {
148
+ this.instances[index] = this.instances[last];
149
+ }
150
+ this.instances.pop();
126
151
 
127
- // Clean up event listeners for this instance
128
152
  this.instanceClickListeners.delete(instanceID);
129
153
  this.instanceHoverListeners.delete(instanceID);
154
+ IDManager.release(instanceID);
130
155
 
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();
156
+ this._matricesDirty = true;
137
157
  } else {
138
158
  console.warn("Instance not found:", instanceID);
139
159
  }
@@ -144,14 +164,15 @@ class InstancedTexture extends Drawable {
144
164
  * @description Clears all instances from the instanced texture
145
165
  */
146
166
  clearInstances() {
167
+ for (const inst of this.instances) IDManager.release(inst.id);
147
168
  this.instances = [];
148
169
 
149
- // Clear all event listeners
150
170
  this.instanceClickListeners.clear();
151
171
  this.instanceHoverListeners.clear();
152
172
 
153
- this.instanceMatrices = new Float32Array(this.instanceCount * 16); // Reset matrices
154
- this.instanceTexCoords = new Float32Array(this.instanceCount * 8); // Reset texture coordinates
173
+ this.instanceMatrices = new Float32Array(this.instanceCount * 16);
174
+ this.instanceTexCoords = new Float32Array(this.instanceCount * 8);
175
+ this.instanceColors = new Float32Array(this.instanceCount * 4).fill(1);
155
176
 
156
177
  this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instanceMatrixBuffer);
157
178
  this.gl.bufferData(
@@ -166,6 +187,13 @@ class InstancedTexture extends Drawable {
166
187
  this.instanceTexCoords,
167
188
  this.gl.DYNAMIC_DRAW
168
189
  );
190
+
191
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instanceColorBuffer);
192
+ this.gl.bufferData(
193
+ this.gl.ARRAY_BUFFER,
194
+ this.instanceColors,
195
+ this.gl.DYNAMIC_DRAW
196
+ );
169
197
  }
170
198
 
171
199
  /**
@@ -177,6 +205,7 @@ class InstancedTexture extends Drawable {
177
205
  this.instanceCount = newCount;
178
206
  this.instanceMatrices = new Float32Array(newCount * 16);
179
207
  this.instanceTexCoords = new Float32Array(newCount * 8);
208
+ this.instanceColors = new Float32Array(newCount * 4).fill(1);
180
209
  this.instanceMatrixBuffer = initInstancedBuffer(
181
210
  this.gl,
182
211
  this.instanceMatrices,
@@ -187,6 +216,11 @@ class InstancedTexture extends Drawable {
187
216
  this.instanceTexCoords,
188
217
  8
189
218
  );
219
+ this.instanceColorBuffer = initInstancedBuffer(
220
+ this.gl,
221
+ this.instanceColors,
222
+ 4
223
+ );
190
224
  }
191
225
 
192
226
  /**
@@ -219,7 +253,6 @@ class InstancedTexture extends Drawable {
219
253
  mat4.rotate(matrix, matrix, rotation, [0, 0, 1]);
220
254
  mat4.scale(matrix, matrix, scale);
221
255
 
222
- // Copy matrix values to the appropriate place in instanceMatrices
223
256
  const offset = index * 16;
224
257
  for (let i = 0; i < 16; i++) {
225
258
  this.instanceMatrices[offset + i] = matrix[i];
@@ -236,7 +269,6 @@ class InstancedTexture extends Drawable {
236
269
  this.updateInstanceMatrix(i);
237
270
  }
238
271
 
239
- // Update the instance buffer with new matrices
240
272
  this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instanceMatrixBuffer);
241
273
  this.gl.bufferData(
242
274
  this.gl.ARRAY_BUFFER,
@@ -244,8 +276,47 @@ class InstancedTexture extends Drawable {
244
276
  this.gl.DYNAMIC_DRAW
245
277
  );
246
278
 
247
- // Also update texture coordinates
248
279
  this.updateAllInstanceTexCoords();
280
+ this.updateAllInstanceColors();
281
+ }
282
+
283
+ /**
284
+ * @method updateInstanceColor
285
+ * @description Writes a single instance's RGBA tint into the color array.
286
+ * Reads `instance._tint` ([r,g,b,a] in 0..1) when set, else opaque white.
287
+ * @param {number} index - The instance index
288
+ */
289
+ updateInstanceColor(index) {
290
+ if (index < 0 || index >= this.instances.length) return;
291
+ const tint = this.instances[index]._tint;
292
+ const offset = index * 4;
293
+ if (tint) {
294
+ this.instanceColors[offset] = tint[0];
295
+ this.instanceColors[offset + 1] = tint[1];
296
+ this.instanceColors[offset + 2] = tint[2];
297
+ this.instanceColors[offset + 3] = tint[3];
298
+ } else {
299
+ this.instanceColors[offset] = 1;
300
+ this.instanceColors[offset + 1] = 1;
301
+ this.instanceColors[offset + 2] = 1;
302
+ this.instanceColors[offset + 3] = 1;
303
+ }
304
+ }
305
+
306
+ /**
307
+ * @method updateAllInstanceColors
308
+ * @description Rebuilds and uploads the per-instance color buffer.
309
+ */
310
+ updateAllInstanceColors() {
311
+ for (let i = 0; i < this.instances.length; i++) {
312
+ this.updateInstanceColor(i);
313
+ }
314
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instanceColorBuffer);
315
+ this.gl.bufferData(
316
+ this.gl.ARRAY_BUFFER,
317
+ this.instanceColors,
318
+ this.gl.DYNAMIC_DRAW
319
+ );
249
320
  }
250
321
 
251
322
  /**
@@ -259,8 +330,9 @@ class InstancedTexture extends Drawable {
259
330
  index < this.instanceCount &&
260
331
  index < this.instances.length
261
332
  ) {
262
- const frame = this.instances[index].frame;
263
- const texCoords = this.getFrameTexCoords(frame);
333
+ const instance = this.instances[index];
334
+ const texCoords =
335
+ instance._regionUV || this.getFrameTexCoords(instance.frame);
264
336
 
265
337
  const offset = index * 8;
266
338
  for (let i = 0; i < 8; i++) {
@@ -278,7 +350,6 @@ class InstancedTexture extends Drawable {
278
350
  this.updateInstanceTexCoords(i);
279
351
  }
280
352
 
281
- // Update the instance texture coordinate buffer
282
353
  this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instanceTexCoordBuffer);
283
354
  this.gl.bufferData(
284
355
  this.gl.ARRAY_BUFFER,
@@ -305,7 +376,6 @@ class InstancedTexture extends Drawable {
305
376
  update(currentTime) {
306
377
  let needsUpdate = false;
307
378
 
308
- // Update animations for all instances
309
379
  for (let i = 0; i < this.instances.length; i++) {
310
380
  const instance = this.instances[i];
311
381
  if (instance.updateAnimation(currentTime)) {
@@ -313,7 +383,6 @@ class InstancedTexture extends Drawable {
313
383
  }
314
384
  }
315
385
 
316
- // If any instance frame changed, update texture coordinates
317
386
  if (needsUpdate) {
318
387
  this.updateAllInstanceTexCoords();
319
388
  }
@@ -406,47 +475,115 @@ class InstancedTexture extends Drawable {
406
475
  let texLeft, texRight, texTop, texBottom;
407
476
 
408
477
  if (this.frameWidth > 0 && this.frameHeight > 0) {
409
- // Spritesheet animation
410
478
  const col = frame % this.framesPerRow;
411
479
  const row = Math.floor(frame / this.framesPerRow);
412
480
 
413
- texLeft = ((col + 1) * this.frameWidth) / this.textureWidth;
414
- texRight = (col * this.frameWidth) / this.textureWidth;
481
+ const ix = 0.5 / this.textureWidth;
482
+ const iy = 0.5 / this.textureHeight;
483
+
484
+ texLeft = ((col + 1) * this.frameWidth) / this.textureWidth - ix;
485
+ texRight = (col * this.frameWidth) / this.textureWidth + ix;
415
486
  texTop =
416
487
  (this.textureHeight - row * this.frameHeight - this.frameHeight) /
417
- this.textureHeight;
488
+ this.textureHeight +
489
+ iy;
418
490
  texBottom =
419
- (this.textureHeight - row * this.frameHeight) / this.textureHeight;
491
+ (this.textureHeight - row * this.frameHeight) / this.textureHeight - iy;
420
492
  } else {
421
- // Static image
422
493
  texLeft = 1.0;
423
494
  texRight = 0.0;
424
495
  texTop = 0.0;
425
496
  texBottom = 1.0;
426
497
  }
427
498
 
428
- // Return texture coordinates (considering mirroring)
429
499
  return this.mirrored
430
500
  ? [
431
- texRight,
432
- texBottom,
433
- texLeft,
434
- texBottom,
435
- texRight,
436
- texTop,
437
- texLeft,
438
- texTop,
439
- ]
501
+ texRight,
502
+ texBottom,
503
+ texLeft,
504
+ texBottom,
505
+ texRight,
506
+ texTop,
507
+ texLeft,
508
+ texTop,
509
+ ]
440
510
  : [
441
- texLeft,
442
- texBottom,
443
- texRight,
444
- texBottom,
445
- texLeft,
446
- texTop,
447
- texRight,
448
- texTop,
449
- ];
511
+ texLeft,
512
+ texBottom,
513
+ texRight,
514
+ texBottom,
515
+ texLeft,
516
+ texTop,
517
+ texRight,
518
+ texTop,
519
+ ];
520
+ }
521
+
522
+ /**
523
+ * @method _restoreGL
524
+ * @description Rebuilds the per-instance buffers from their CPU-side arrays
525
+ * after a WebGL context loss, on top of the base Drawable restore.
526
+ * @private
527
+ */
528
+ _restoreGL() {
529
+ if (this._disposed) return;
530
+ super._restoreGL();
531
+ const gl = this.gl;
532
+ if (!gl) return;
533
+ this.instanceMatrixBuffer = initInstancedBuffer(
534
+ gl,
535
+ this.instanceMatrices,
536
+ 16
537
+ );
538
+ this.instanceTexCoordBuffer = initInstancedBuffer(
539
+ gl,
540
+ this.instanceTexCoords,
541
+ 8
542
+ );
543
+ this.instanceColorBuffer = initInstancedBuffer(gl, this.instanceColors, 4);
544
+ this._matricesDirty = true;
545
+ }
546
+
547
+ /**
548
+ * @method dispose
549
+ * @description Frees the per-instance GPU buffers on top of the base
550
+ * Drawable disposal. Safe to call twice.
551
+ */
552
+ dispose() {
553
+ if (this._disposed) return;
554
+ const gl = this.gl;
555
+ if (gl) {
556
+ if (this.instanceMatrixBuffer) gl.deleteBuffer(this.instanceMatrixBuffer);
557
+ if (this.instanceTexCoordBuffer)
558
+ gl.deleteBuffer(this.instanceTexCoordBuffer);
559
+ if (this.instanceColorBuffer) gl.deleteBuffer(this.instanceColorBuffer);
560
+ }
561
+ this.instanceMatrixBuffer = null;
562
+ this.instanceTexCoordBuffer = null;
563
+ this.instanceColorBuffer = null;
564
+ this.instances = [];
565
+ super.dispose();
566
+ }
567
+
568
+ /**
569
+ * @method syncPhysics
570
+ * @description Copies simulation state (position + rotation) from each
571
+ * instance's dynamic rigidbody onto the instance transform. Driven once per
572
+ * frame by the owning GameObject's syncPhysics(), so draw() stays read-only.
573
+ */
574
+ syncPhysics() {
575
+ for (let i = 0; i < this.instances.length; i++) {
576
+ const instance = this.instances[i];
577
+ const rigidBody = instance.getComponent(RigidBody);
578
+ if (!rigidBody || rigidBody.getType() !== "dynamic") continue;
579
+ rigidBody.syncTransform(instance.transform);
580
+ const collider =
581
+ rigidBody.getCollider() || instance.getComponent(Collider);
582
+ if (collider && typeof collider.syncDebugShape === "function") {
583
+ collider.syncDebugShape(instance.transform);
584
+ }
585
+ this._matricesDirty = true;
586
+ }
450
587
  }
451
588
 
452
589
  /**
@@ -454,62 +591,48 @@ class InstancedTexture extends Drawable {
454
591
  * @description Draws the instanced texture
455
592
  */
456
593
  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
- }
594
+ if (!this.instances || this.instances.length === 0) return;
595
+
596
+ if (!this.static || this._matricesDirty) {
597
+ this.updateAllInstanceMatrices();
598
+ this._matricesDirty = false;
482
599
  }
483
- this.gl.uniform1i(
600
+ this.update(performance.now());
601
+ GLState.uniform1i(
602
+ this.gl,
484
603
  this.programInfo.uniformLocations.useTexture,
485
604
  this.useTexture
486
605
  );
487
606
 
488
- this.gl.uniform1i(this.programInfo.uniformLocations.useInstances, true);
607
+ GLState.uniform1i(
608
+ this.gl,
609
+ this.programInfo.uniformLocations.useInstances,
610
+ true
611
+ );
489
612
 
490
- // Set lighting uniform
491
- this.gl.uniform1i(
613
+ GLState.uniform1i(
614
+ this.gl,
492
615
  this.programInfo.uniformLocations.useLighting,
493
616
  this.useLighting
494
617
  );
495
618
 
496
- // Always set the color uniform for tinting
497
- this.gl.uniform4fv(this.programInfo.uniformLocations.color, this.color);
619
+ GLState.uniform4fv(
620
+ this.gl,
621
+ this.programInfo.uniformLocations.color,
622
+ this.color
623
+ );
498
624
 
499
- // Set per-GameObject opacity if available (defaults to 1.0)
500
625
  const ownerGameObject = this.getParent();
501
626
  const ownerOpacity =
502
627
  ownerGameObject && typeof ownerGameObject.opacity === "number"
503
628
  ? ownerGameObject.opacity
504
629
  : 1.0;
505
- if (this.programInfo.uniformLocations.uOpacity) {
506
- this.gl.uniform1f(
507
- this.programInfo.uniformLocations.uOpacity,
508
- ownerOpacity
509
- );
510
- }
630
+ GLState.uniform1f(
631
+ this.gl,
632
+ this.programInfo.uniformLocations.uOpacity,
633
+ ownerOpacity
634
+ );
511
635
 
512
- // Bind and set vertex position buffer
513
636
  this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.verticesBuffer);
514
637
  this.gl.vertexAttribPointer(
515
638
  this.programInfo.attribLocations.vertexPosition,
@@ -523,7 +646,6 @@ class InstancedTexture extends Drawable {
523
646
  this.programInfo.attribLocations.vertexPosition
524
647
  );
525
648
 
526
- // Bind and set texture coordinate buffer (for non-instanced rendering)
527
649
  if (this.useTexture) {
528
650
  this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.texCoordBuffer);
529
651
  this.gl.vertexAttribPointer(
@@ -540,12 +662,11 @@ class InstancedTexture extends Drawable {
540
662
 
541
663
  if (this.texture) {
542
664
  this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);
665
+ RenderStats.textureBinds++;
543
666
  }
544
667
 
545
- // Set up per-instance texture coordinate attributes
546
668
  this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instanceTexCoordBuffer);
547
669
 
548
- // Each instance has 4 texture coordinates (for 4 vertices)
549
670
  const texCoordAttribs = [
550
671
  this.programInfo.attribLocations.instanceTexCoord0,
551
672
  this.programInfo.attribLocations.instanceTexCoord1,
@@ -562,15 +683,22 @@ class InstancedTexture extends Drawable {
562
683
  2,
563
684
  this.gl.FLOAT,
564
685
  false,
565
- 8 * 4, // 8 floats per instance (4 vertices * 2 coords)
566
- i * 2 * 4 // offset for each vertex
686
+ 8 * 4,
687
+ i * 2 * 4
567
688
  );
568
- this.gl.vertexAttribDivisor(loc, 1); // This makes it instanced
689
+ this.gl.vertexAttribDivisor(loc, 1);
569
690
  }
570
691
  }
571
692
  }
572
693
 
573
- // Set up instance matrix attributes (4 vec4s for each matrix)
694
+ const colorLoc = this.programInfo.attribLocations.instanceColor;
695
+ if (colorLoc !== undefined && colorLoc !== -1) {
696
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instanceColorBuffer);
697
+ this.gl.enableVertexAttribArray(colorLoc);
698
+ this.gl.vertexAttribPointer(colorLoc, 4, this.gl.FLOAT, false, 0, 0);
699
+ this.gl.vertexAttribDivisor(colorLoc, 1);
700
+ }
701
+
574
702
  this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instanceMatrixBuffer);
575
703
 
576
704
  const matrixLoc = this.programInfo.attribLocations.instanceMatrix;
@@ -585,26 +713,32 @@ class InstancedTexture extends Drawable {
585
713
  16 * 4,
586
714
  i * 4 * 4
587
715
  );
588
- this.gl.vertexAttribDivisor(loc, 1); // This makes it instanced
716
+ this.gl.vertexAttribDivisor(loc, 1);
589
717
  }
590
718
 
591
- // Draw all instances
592
719
  const drawMode =
593
720
  this.vertices.length === 8 ? this.gl.TRIANGLE_STRIP : this.gl.TRIANGLES;
721
+ this._applyBlend();
594
722
  this.gl.drawArraysInstanced(
595
723
  drawMode,
596
724
  0,
597
725
  this.vertices.length / 2,
598
- this.instances.length // Use actual instance count, not max
726
+ this.instances.length
599
727
  );
728
+ this._restoreBlend();
729
+ RenderStats.drawCalls++;
730
+ RenderStats.quads += this.instances.length;
600
731
 
601
- // Reset attribute divisors
602
732
  for (let i = 0; i < 4; i++) {
603
733
  this.gl.vertexAttribDivisor(matrixLoc + i, 0);
604
734
  this.gl.disableVertexAttribArray(matrixLoc + i);
605
735
  }
606
736
 
607
- // Reset texture coordinate attribute divisors
737
+ if (colorLoc !== undefined && colorLoc !== -1) {
738
+ this.gl.vertexAttribDivisor(colorLoc, 0);
739
+ this.gl.disableVertexAttribArray(colorLoc);
740
+ }
741
+
608
742
  if (this.useTexture) {
609
743
  const texCoordAttribs = [
610
744
  this.programInfo.attribLocations.instanceTexCoord0,
@@ -622,7 +756,11 @@ class InstancedTexture extends Drawable {
622
756
  }
623
757
  }
624
758
 
625
- this.gl.uniform1i(this.programInfo.uniformLocations.useInstances, false);
759
+ GLState.uniform1i(
760
+ this.gl,
761
+ this.programInfo.uniformLocations.useInstances,
762
+ false
763
+ );
626
764
  }
627
765
 
628
766
  /**
@@ -781,9 +919,9 @@ class InstancedTexture extends Drawable {
781
919
  if (listeners) {
782
920
  listeners.forEach((func) => func(event, clickedInstance));
783
921
  }
784
- return true; // Event was handled
922
+ return true;
785
923
  }
786
- return false; // No instance was clicked
924
+ return false;
787
925
  }
788
926
 
789
927
  /**
@@ -798,7 +936,6 @@ class InstancedTexture extends Drawable {
798
936
  const hoveredInstance = this.getInstanceAtPoint(x, y);
799
937
 
800
938
  if (hoveredInstance !== lastHoveredInstance) {
801
- // Handle leave event for previous instance
802
939
  if (lastHoveredInstance) {
803
940
  const leaveListeners = this.instanceHoverListeners.get(
804
941
  lastHoveredInstance.id
@@ -810,7 +947,6 @@ class InstancedTexture extends Drawable {
810
947
  }
811
948
  }
812
949
 
813
- // Handle enter event for new instance
814
950
  if (hoveredInstance) {
815
951
  const enterListeners = this.instanceHoverListeners.get(
816
952
  hoveredInstance.id
@@ -831,7 +967,6 @@ class InstancedTexture extends Drawable {
831
967
  * @param {number} speed - The speed of the animation
832
968
  */
833
969
  playAnimation(frames, speed = 1000) {
834
- // Override with empty implementation
835
970
  console.warn("playAnimation is not implemented for InstancedTexture");
836
971
  }
837
972
 
@@ -842,7 +977,6 @@ class InstancedTexture extends Drawable {
842
977
  * @param {number} speed - The speed of the animation
843
978
  */
844
979
  playAnimationOnce(frames, speed = 1000) {
845
- // Override with empty implementation
846
980
  console.warn("playAnimationOnce is not implemented for InstancedTexture");
847
981
  }
848
982
 
@@ -852,7 +986,6 @@ class InstancedTexture extends Drawable {
852
986
  * @returns {Array} - The animation
853
987
  */
854
988
  getAnimation() {
855
- // Override with empty implementation
856
989
  console.warn("getAnimation is not implemented for InstancedTexture");
857
990
  }
858
991
  }