emeraldengine 2.2.0 → 2.2.1

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/Drawable.js +535 -508
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emeraldengine",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "2D graphics library for Web",
5
5
  "type": "module",
6
6
  "main": "index.js",
package/src/Drawable.js CHANGED
@@ -22,534 +22,561 @@ import IDManager from "./managers/IDManager.js";
22
22
  * @param {boolean} useLighting - Whether to use lighting
23
23
  */
24
24
  class Drawable {
25
- constructor(
26
- gl,
27
- programInfo,
28
- verticesBuffer,
29
- texCoordBuffer,
30
- vertices,
31
- useTexture,
32
- texturePath,
33
- frameWidth = 0,
34
- frameHeight = 0,
35
- framesPerRow = 1,
36
- totalFrames = 1,
37
- animationSpeed = 1000,
38
- autoplay = true,
39
- pixelart = false,
40
- useLighting = true
41
- ) {
42
- this.gl = gl;
43
- this.programInfo = programInfo;
44
- this.id = IDManager.generateUniqueID();
45
- this.verticesBuffer = verticesBuffer;
46
- this.texCoordBuffer = texCoordBuffer;
47
- this.vertices = vertices;
48
- this.color = vec4.fromValues(1.0, 1.0, 1.0, 1.0);
49
- this.texturePath = texturePath;
50
- this.texture = null;
51
- this.useTexture = useTexture;
52
- if (useTexture) {
53
- this.initTexture().then(() => { });
54
- }
55
- this.frameWidth = frameWidth;
56
- this.frameHeight = frameHeight;
57
- this.framesPerRow = framesPerRow;
58
- this.totalFrames = totalFrames;
59
- this.currentFrame = 0;
60
- this.animationSpeed = animationSpeed;
61
- this.lastFrameTime = 0;
62
- this.textureWidth = 0;
63
- this.textureHeight = 0;
64
- this.isPlaying = autoplay;
65
- this.playOnce = false;
66
- this.originalTexture = null;
67
- this.mirrored = false;
68
- this.animationType = "texturePath";
69
- this.currentAnimationArray = [];
70
- this.currentAnimationIndex = 0;
71
- this.shouldRevertAfterPlayingOnce = false;
72
- this.revertAnimation = [];
73
- this.parentObject = null;
74
- this.isActive = false;
75
- this.isWireframe = false;
76
- this.callbackFunction = () => { };
77
- this.pixelart = pixelart;
78
- this.useLighting = useLighting; // Default to true to maintain existing behavior
79
- }
80
-
81
- /**
82
- * @method setIsWireframe
83
- * @description Sets the wireframe mode
84
- * @param {boolean} bool - Whether to enable wireframe mode
85
- */
86
- setIsWireframe(bool) {
87
- this.isWireframe = bool;
88
- }
89
-
90
- /**
91
- * @method setUseLighting
92
- * @description Sets the lighting mode
93
- * @param {boolean} useLighting - Whether to enable lighting reaction
94
- */
95
- setUseLighting(useLighting) {
96
- this.useLighting = useLighting;
97
- }
98
-
99
- /**
100
- * @method setIsActive
101
- * @description Sets the active state
102
- * @param {boolean} bool - Whether to enable the active state
103
- */
104
- setIsActive(bool) {
105
- this.isActive = bool;
106
- }
107
-
108
- /**
109
- * @method setParent
110
- * @description Sets the parent object
111
- * @param {Object} parent - The parent object
112
- */
113
- setParent(parent) {
114
- this.parentObject = parent;
115
- }
116
-
117
- /**
118
- * @method getParent
119
- * @description Gets the parent object
120
- * @returns {Object} - The parent object
121
- */
122
- getParent() {
123
- return this.parentObject;
124
- }
125
-
126
- /**
127
- * @method setColor
128
- * @description Sets the color of the object
129
- * @param {Color} color - The color to set
130
- */
131
- setColor(color) {
132
- if (color instanceof Color) {
133
- vec4.set(
134
- this.color,
135
- color.r / 255,
136
- color.g / 255,
137
- color.b / 255,
138
- color.a / 255
139
- );
140
- } else {
141
- console.error("[Drawable.js] > color is not an instance of Color class.");
142
- }
143
- }
144
-
145
- /**
146
- * @method initTexture
147
- * @description Initializes the texture
148
- */
149
- async initTexture() {
150
- const image = await this.loadImage(this.texturePath);
151
- this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, true);
152
-
153
- this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
154
-
155
- this.texture = this.gl.createTexture();
156
- this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);
157
- this.gl.texParameteri(
158
- this.gl.TEXTURE_2D,
159
- this.gl.TEXTURE_WRAP_S,
160
- this.gl.CLAMP_TO_EDGE
161
- );
162
- this.gl.texParameteri(
163
- this.gl.TEXTURE_2D,
164
- this.gl.TEXTURE_WRAP_T,
165
- this.gl.CLAMP_TO_EDGE
166
- );
167
- this.gl.texParameteri(
168
- this.gl.TEXTURE_2D,
169
- this.gl.TEXTURE_MIN_FILTER,
170
- this.pixelart ? this.gl.NEAREST : this.gl.LINEAR
171
- );
172
- this.gl.texParameteri(
173
- this.gl.TEXTURE_2D,
174
- this.gl.TEXTURE_MAG_FILTER,
175
- this.pixelart ? this.gl.NEAREST : this.gl.LINEAR
176
- );
177
- this.gl.texImage2D(
178
- this.gl.TEXTURE_2D,
179
- 0,
180
- this.gl.RGBA,
181
- this.gl.RGBA,
182
- this.gl.UNSIGNED_BYTE,
183
- image
184
- );
185
- this.textureWidth = image.width;
186
- this.textureHeight = image.height;
187
- this.originalTexture = this.texture;
188
- }
189
-
190
- /**
191
- * @method updateAnimation
192
- * @description Updates the animation
193
- * @param {number} currentTime - The current time
194
- */
195
- updateAnimation(currentTime) {
196
- if (
197
- this.totalFrames > 1 &&
198
- this.isPlaying &&
199
- currentTime - this.lastFrameTime > this.animationSpeed
25
+ constructor(
26
+ gl,
27
+ programInfo,
28
+ verticesBuffer,
29
+ texCoordBuffer,
30
+ vertices,
31
+ useTexture,
32
+ texturePath,
33
+ frameWidth = 0,
34
+ frameHeight = 0,
35
+ framesPerRow = 1,
36
+ totalFrames = 1,
37
+ animationSpeed = 1000,
38
+ autoplay = true,
39
+ pixelart = false,
40
+ useLighting = true,
200
41
  ) {
201
- if (this.animationType === "texturePath") {
202
- this.currentFrame = (this.currentFrame + 1) % this.totalFrames;
203
- } else if (this.animationType === "array") {
204
- this.currentFrame =
205
- this.currentAnimationArray[this.currentAnimationIndex];
206
- this.currentAnimationIndex++;
207
- if (this.currentAnimationIndex >= this.currentAnimationArray.length) {
208
- if (this.playOnce) {
209
- if (this.shouldRevertAfterPlayingOnce) {
210
- this.currentAnimationIndex = 0;
211
- this.currentAnimationArray = this.revertAnimation;
212
- } else {
213
- this.isPlaying = false;
214
- }
215
- this.playOnce = false;
216
- this.callbackFunction();
217
- } else {
218
- this.currentAnimationIndex = 0;
219
- }
42
+ this.gl = gl;
43
+ this.programInfo = programInfo;
44
+ this.id = IDManager.generateUniqueID();
45
+ this.verticesBuffer = verticesBuffer;
46
+ this.texCoordBuffer = texCoordBuffer;
47
+ this.vertices = vertices;
48
+ this.color = vec4.fromValues(1.0, 1.0, 1.0, 1.0);
49
+ this.texturePath = texturePath;
50
+ this.texture = null;
51
+ this.useTexture = useTexture;
52
+ if (useTexture) {
53
+ this.initTexture().then(() => {});
220
54
  }
221
- }
222
- this.lastFrameTime = currentTime;
55
+ this.frameWidth = frameWidth;
56
+ this.frameHeight = frameHeight;
57
+ this.framesPerRow = framesPerRow;
58
+ this.totalFrames = totalFrames;
59
+ this.currentFrame = 0;
60
+ this.animationSpeed = animationSpeed;
61
+ this.lastFrameTime = 0;
62
+ this.textureWidth = 0;
63
+ this.textureHeight = 0;
64
+ this.isPlaying = autoplay;
65
+ this.playOnce = false;
66
+ this.originalTexture = null;
67
+ this.mirrored = false;
68
+ this.animationType = "texturePath";
69
+ this.currentAnimationArray = [];
70
+ this.currentAnimationIndex = 0;
71
+ this.shouldRevertAfterPlayingOnce = false;
72
+ this.revertAnimation = [];
73
+ this.parentObject = null;
74
+ this.isActive = false;
75
+ this.isWireframe = false;
76
+ this.callbackFunction = () => {};
77
+ this.pixelart = pixelart;
78
+ this.useLighting = useLighting; // Default to true to maintain existing behavior
223
79
  }
224
- }
225
-
226
- /**
227
- * @method getAnimation
228
- * @description Gets the animation
229
- * @returns {Array} - The animation
230
- */
231
- getAnimation() {
232
- if (this.animationType === "texturePath") {
233
- return this.texturePath;
234
- } else if (this.animationType === "array") {
235
- return this.currentAnimationArray;
80
+
81
+ /**
82
+ * @method setIsWireframe
83
+ * @description Sets the wireframe mode
84
+ * @param {boolean} bool - Whether to enable wireframe mode
85
+ */
86
+ setIsWireframe(bool) {
87
+ this.isWireframe = bool;
236
88
  }
237
- return null;
238
- }
239
-
240
- /**
241
- * @method playAnimation
242
- * @description Plays an animation
243
- * @param {Array} animation - The animation to play
244
- * @param {number} animationSpeed - The speed of the animation
245
- */
246
- playAnimation(animation, animationSpeed = 1000) {
247
- if (animation.length !== 0) {
248
- this.animationSpeed = animationSpeed;
249
- this.animationType = "array";
250
- this.currentAnimationArray = animation;
251
- this.currentAnimationIndex = 0;
252
- // Not tested
253
- // this.currentFrame = 0;
254
- this.isPlaying = true;
255
- this.playOnce = false;
256
- } else {
257
- console.error(
258
- "[Drawable.js] | [playAnimation] > Animation cannot be empty!"
259
- );
89
+
90
+ /**
91
+ * @method setUseLighting
92
+ * @description Sets the lighting mode
93
+ * @param {boolean} useLighting - Whether to enable lighting reaction
94
+ */
95
+ setUseLighting(useLighting) {
96
+ this.useLighting = useLighting;
260
97
  }
261
- }
262
-
263
- /**
264
- * @method setFrame
265
- * @description Sets the frame
266
- * @param {number} frame - The frame to set
267
- */
268
- setFrame(frame) {
269
- this.currentFrame = frame;
270
- this.isPlaying = false;
271
- }
272
-
273
- /**
274
- * @method playAnimationOnce
275
- * @description Plays an animation once
276
- * @param {Array} animation - The animation to play
277
- * @param {Array} defaultAnimation - The default animation
278
- * @param {number} animationSpeed - The speed of the animation
279
- * @param {function} callbackFunction - The callback function
280
- */
281
- playAnimationOnce(
282
- animation,
283
- defaultAnimation = null,
284
- animationSpeed = 1000,
285
- callbackFunction = null
286
- ) {
287
- if (animation.length !== 0) {
288
- this.animationSpeed = animationSpeed;
289
- this.animationType = "array";
290
- this.currentAnimationArray = animation;
291
- this.currentAnimationIndex = 0;
292
- // Not tested
293
- // this.currentFrame = 0;
294
- this.isPlaying = true;
295
- this.playOnce = true;
296
- if (defaultAnimation !== null && defaultAnimation.length > 0) {
297
- this.shouldRevertAfterPlayingOnce = true;
298
- this.revertAnimation = defaultAnimation;
299
- } else {
300
- this.shouldRevertAfterPlayingOnce = false;
301
- this.revertAnimation = [];
302
- }
303
-
304
- if (callbackFunction) {
305
- this.callbackFunction = callbackFunction;
306
- }
307
- } else {
308
- console.error(
309
- "[Drawable.js] | [playAnimationOnce] > Animation cannot be empty!"
310
- );
98
+
99
+ /**
100
+ * @method setIsActive
101
+ * @description Sets the active state
102
+ * @param {boolean} bool - Whether to enable the active state
103
+ */
104
+ setIsActive(bool) {
105
+ this.isActive = bool;
106
+ }
107
+
108
+ /**
109
+ * @method setParent
110
+ * @description Sets the parent object
111
+ * @param {Object} parent - The parent object
112
+ */
113
+ setParent(parent) {
114
+ this.parentObject = parent;
311
115
  }
312
- }
313
-
314
- /**
315
- * @method stopAnimation
316
- * @description Stops the animation
317
- */
318
- stopAnimation() {
319
- this.currentFrame = 0;
320
- this.isPlaying = false;
321
- this.playOnce = false;
322
- this.texture = this.originalTexture;
323
- }
324
-
325
- /**
326
- * @method draw
327
- * @description Draws the object
328
- * @param {mat4} globalViewMatrix - The global view matrix
329
- * @param {WebGLUniformLocation} uniformLocation - The uniform location
330
- * @param {number} currentTime - The current time
331
- * @param {Object} objectTransform - The object transform
332
- */
333
- draw(globalViewMatrix, uniformLocation, currentTime, objectTransform) {
334
- let objectTransformMatrix = mat4.create();
335
- mat4.translate(
336
- objectTransformMatrix,
337
- objectTransformMatrix,
338
- vec3.fromValues(
339
- this.pixelart
340
- ? Math.round(objectTransform.position.x)
341
- : objectTransform.position.x,
342
- this.pixelart
343
- ? Math.round(objectTransform.position.y)
344
- : objectTransform.position.y,
345
- objectTransform.position.z
346
- )
347
- );
348
- mat4.rotate(objectTransformMatrix, objectTransformMatrix, 0, [1, 0, 0]);
349
- mat4.rotate(objectTransformMatrix, objectTransformMatrix, 0, [0, 1, 0]);
350
- mat4.rotate(
351
- objectTransformMatrix,
352
- objectTransformMatrix,
353
- objectTransform.rotation,
354
- [0, 0, 1]
355
- );
356
-
357
- // The scale values here represent half-extents
358
- mat4.scale(
359
- objectTransformMatrix,
360
- objectTransformMatrix,
361
- vec3.fromValues(objectTransform.scale.x, objectTransform.scale.y, 1.0)
362
- );
363
-
364
- let finalTransformMatrix = mat4.create();
365
- mat4.mul(finalTransformMatrix, globalViewMatrix, objectTransformMatrix);
366
- this.gl.uniform1i(
367
- this.programInfo.uniformLocations.useTexture,
368
- this.useTexture
369
- );
370
- this.gl.uniform1i(this.programInfo.uniformLocations.useInstances, false);
371
-
372
- // Set lighting uniform
373
- this.gl.uniform1i(
374
- this.programInfo.uniformLocations.useLighting,
375
- this.useLighting
376
- );
377
-
378
- // Always set the color uniform for tinting
379
- this.gl.uniform4fv(this.programInfo.uniformLocations.color, this.color);
380
-
381
- // Set per-GameObject opacity if available (defaults to 1.0)
382
- const parentOpacity = this.parentObject && typeof this.parentObject.opacity === "number"
383
- ? this.parentObject.opacity
384
- : 1.0;
385
- if (this.programInfo.uniformLocations.uOpacity) {
386
- this.gl.uniform1f(this.programInfo.uniformLocations.uOpacity, parentOpacity);
116
+
117
+ /**
118
+ * @method getParent
119
+ * @description Gets the parent object
120
+ * @returns {Object} - The parent object
121
+ */
122
+ getParent() {
123
+ return this.parentObject;
124
+ }
125
+
126
+ /**
127
+ * @method setColor
128
+ * @description Sets the color of the object
129
+ * @param {Color} color - The color to set
130
+ */
131
+ setColor(color) {
132
+ if (color instanceof Color) {
133
+ vec4.set(
134
+ this.color,
135
+ color.r / 255,
136
+ color.g / 255,
137
+ color.b / 255,
138
+ color.a / 255,
139
+ );
140
+ } else {
141
+ console.error(
142
+ "[Drawable.js] > color is not an instance of Color class.",
143
+ );
144
+ }
387
145
  }
388
146
 
389
- this.gl.uniformMatrix4fv(
390
- uniformLocation,
391
- false,
392
- new Float32Array(finalTransformMatrix)
393
- );
394
-
395
- // Bind and set vertex position buffer
396
- this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.verticesBuffer);
397
- this.gl.vertexAttribPointer(
398
- this.programInfo.attribLocations.vertexPosition,
399
- 2,
400
- this.gl.FLOAT,
401
- false,
402
- 0,
403
- 0
404
- );
405
- this.gl.enableVertexAttribArray(
406
- this.programInfo.attribLocations.vertexPosition
407
- );
408
-
409
- // Bind and set texture coordinate buffer
410
- if (this.useTexture) {
411
- this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.texCoordBuffer);
412
- this.gl.vertexAttribPointer(
413
- this.programInfo.attribLocations.aTexCoord,
414
- 2,
415
- this.gl.FLOAT,
416
- false,
417
- 0,
418
- 0
419
- );
420
- this.gl.enableVertexAttribArray(
421
- this.programInfo.attribLocations.aTexCoord
422
- );
423
-
424
- if (this.texture) {
147
+ /**
148
+ * @method initTexture
149
+ * @description Initializes the texture
150
+ */
151
+ async initTexture() {
152
+ const image = await this.loadImage(this.texturePath);
153
+ this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, true);
154
+
155
+ this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
156
+
157
+ this.texture = this.gl.createTexture();
425
158
  this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);
426
- }
427
-
428
- this.updateAnimation(currentTime);
429
-
430
- let texLeft, texRight, texTop, texBottom;
431
-
432
- if (this.frameWidth > 0 && this.frameHeight > 0) {
433
- // Spritesheet animation
434
- const col = this.currentFrame % this.framesPerRow;
435
- const row = Math.floor(this.currentFrame / this.framesPerRow);
436
-
437
- texLeft = ((col + 1) * this.frameWidth) / this.textureWidth;
438
- texRight = (col * this.frameWidth) / this.textureWidth;
439
- texTop =
440
- (this.textureHeight - row * this.frameHeight - this.frameHeight) /
441
- this.textureHeight;
442
- texBottom =
443
- (this.textureHeight - row * this.frameHeight) / this.textureHeight;
444
- } else {
445
- // Static image
446
- texLeft = 1.0;
447
- texRight = 0.0;
448
- texTop = 0.0;
449
- texBottom = 1.0;
450
- }
451
- // If mirrored change texLeft and texRight in places
452
- const texCoords = this.mirrored
453
- ? new Float32Array([
454
- texRight,
455
- texBottom,
456
- texLeft,
457
- texBottom,
458
- texRight,
459
- texTop,
460
- texLeft,
461
- texTop,
462
- ])
463
- : new Float32Array([
464
- texLeft,
465
- texBottom,
466
- texRight,
467
- texBottom,
468
- texLeft,
469
- texTop,
470
- texRight,
471
- texTop,
472
- ]);
473
- // Update texture coordinate buffer
474
- this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.texCoordBuffer);
475
- this.gl.bufferData(this.gl.ARRAY_BUFFER, texCoords, this.gl.STATIC_DRAW);
476
- } else {
477
- // If not using texture, disable the texture attribute
478
- this.gl.disableVertexAttribArray(
479
- this.programInfo.attribLocations.aTexCoord
480
- );
481
- this.gl.bindTexture(this.gl.TEXTURE_2D, null);
159
+ this.gl.texParameteri(
160
+ this.gl.TEXTURE_2D,
161
+ this.gl.TEXTURE_WRAP_S,
162
+ this.gl.CLAMP_TO_EDGE,
163
+ );
164
+ this.gl.texParameteri(
165
+ this.gl.TEXTURE_2D,
166
+ this.gl.TEXTURE_WRAP_T,
167
+ this.gl.CLAMP_TO_EDGE,
168
+ );
169
+ this.gl.texParameteri(
170
+ this.gl.TEXTURE_2D,
171
+ this.gl.TEXTURE_MIN_FILTER,
172
+ this.pixelart ? this.gl.NEAREST : this.gl.LINEAR,
173
+ );
174
+ this.gl.texParameteri(
175
+ this.gl.TEXTURE_2D,
176
+ this.gl.TEXTURE_MAG_FILTER,
177
+ this.pixelart ? this.gl.NEAREST : this.gl.LINEAR,
178
+ );
179
+ this.gl.texImage2D(
180
+ this.gl.TEXTURE_2D,
181
+ 0,
182
+ this.gl.RGBA,
183
+ this.gl.RGBA,
184
+ this.gl.UNSIGNED_BYTE,
185
+ image,
186
+ );
187
+ this.textureWidth = image.width;
188
+ this.textureHeight = image.height;
189
+ this.originalTexture = this.texture;
190
+ }
191
+
192
+ /**
193
+ * @method updateAnimation
194
+ * @description Updates the animation
195
+ * @param {number} currentTime - The current time
196
+ */
197
+ updateAnimation(currentTime) {
198
+ if (
199
+ this.totalFrames > 1 &&
200
+ this.isPlaying &&
201
+ currentTime - this.lastFrameTime > this.animationSpeed
202
+ ) {
203
+ if (this.animationType === "texturePath") {
204
+ this.currentFrame = (this.currentFrame + 1) % this.totalFrames;
205
+ } else if (this.animationType === "array") {
206
+ this.currentFrame =
207
+ this.currentAnimationArray[this.currentAnimationIndex];
208
+ this.currentAnimationIndex++;
209
+ if (
210
+ this.currentAnimationIndex >=
211
+ this.currentAnimationArray.length
212
+ ) {
213
+ if (this.playOnce) {
214
+ if (this.shouldRevertAfterPlayingOnce) {
215
+ this.currentAnimationIndex = 0;
216
+ this.currentAnimationArray = this.revertAnimation;
217
+ } else {
218
+ this.isPlaying = false;
219
+ }
220
+ this.playOnce = false;
221
+ this.callbackFunction();
222
+ } else {
223
+ this.currentAnimationIndex = 0;
224
+ }
225
+ }
226
+ }
227
+ this.lastFrameTime = currentTime;
228
+ }
229
+ }
230
+
231
+ /**
232
+ * @method getAnimation
233
+ * @description Gets the animation
234
+ * @returns {Array} - The animation
235
+ */
236
+ getAnimation() {
237
+ if (this.animationType === "texturePath") {
238
+ return this.texturePath;
239
+ } else if (this.animationType === "array") {
240
+ return this.currentAnimationArray;
241
+ }
242
+ return null;
243
+ }
244
+
245
+ /**
246
+ * @method playAnimation
247
+ * @description Plays an animation
248
+ * @param {Array} animation - The animation to play
249
+ * @param {number} animationSpeed - The speed of the animation
250
+ */
251
+ playAnimation(animation, animationSpeed = 1000) {
252
+ if (animation.length !== 0) {
253
+ this.animationSpeed = animationSpeed;
254
+ this.animationType = "array";
255
+ this.currentAnimationArray = animation;
256
+ this.currentAnimationIndex = 0;
257
+ // Not tested
258
+ // this.currentFrame = 0;
259
+ this.isPlaying = true;
260
+ this.playOnce = false;
261
+ } else {
262
+ console.error(
263
+ "[Drawable.js] | [playAnimation] > Animation cannot be empty!",
264
+ );
265
+ }
266
+ }
267
+
268
+ /**
269
+ * @method setFrame
270
+ * @description Sets the frame
271
+ * @param {number} frame - The frame to set
272
+ */
273
+ setFrame(frame) {
274
+ this.currentFrame = frame;
275
+ this.isPlaying = false;
482
276
  }
483
277
 
484
- let drawMode, vertexCount;
485
-
486
- if (this.vertices.length === 8) {
487
- // Square/Rectangle - Triangle strip
488
- drawMode = this.gl.TRIANGLE_STRIP;
489
- vertexCount = 4;
490
- } else if (this.vertices.length === 6) {
491
- // Triangle
492
- drawMode = this.gl.TRIANGLES;
493
- vertexCount = 3;
494
- } else {
495
- // Circle - Triangle fan
496
- drawMode = this.gl.TRIANGLE_FAN;
497
- vertexCount = this.vertices.length / 2;
278
+ /**
279
+ * @method playAnimationOnce
280
+ * @description Plays an animation once
281
+ * @param {Array} animation - The animation to play
282
+ * @param {Array} defaultAnimation - The default animation
283
+ * @param {number} animationSpeed - The speed of the animation
284
+ * @param {function} callbackFunction - The callback function
285
+ */
286
+ playAnimationOnce(
287
+ animation,
288
+ defaultAnimation = null,
289
+ animationSpeed = 1000,
290
+ callbackFunction = null,
291
+ ) {
292
+ if (animation.length !== 0) {
293
+ this.animationSpeed = animationSpeed;
294
+ this.animationType = "array";
295
+ this.currentAnimationArray = animation;
296
+ this.currentAnimationIndex = 0;
297
+ // Not tested
298
+ // this.currentFrame = 0;
299
+ this.isPlaying = true;
300
+ this.playOnce = true;
301
+ if (defaultAnimation !== null && defaultAnimation.length > 0) {
302
+ this.shouldRevertAfterPlayingOnce = true;
303
+ this.revertAnimation = defaultAnimation;
304
+ } else {
305
+ this.shouldRevertAfterPlayingOnce = false;
306
+ this.revertAnimation = [];
307
+ }
308
+
309
+ if (callbackFunction) {
310
+ this.callbackFunction = callbackFunction;
311
+ }
312
+ } else {
313
+ console.error(
314
+ "[Drawable.js] | [playAnimationOnce] > Animation cannot be empty!",
315
+ );
316
+ }
498
317
  }
499
318
 
500
- if (this.isWireframe) {
501
- // Fix the wireframe for square
502
- if (this.vertices.length === 8) {
503
- let tempBuffer = this.gl.createBuffer();
504
- this.gl.bindBuffer(this.gl.ARRAY_BUFFER, tempBuffer);
505
-
506
- const rectOutline = new Float32Array([
507
- this.vertices[0],
508
- this.vertices[1], // Bottom-left (0)
509
- this.vertices[2],
510
- this.vertices[3], // Bottom-right (1)
511
- this.vertices[6],
512
- this.vertices[7], // Top-right (2)
513
- this.vertices[4],
514
- this.vertices[5], // Top-left (3)
515
- ]);
516
-
517
- this.gl.bufferData(
518
- this.gl.ARRAY_BUFFER,
519
- rectOutline,
520
- this.gl.STATIC_DRAW
319
+ /**
320
+ * @method stopAnimation
321
+ * @description Stops the animation
322
+ */
323
+ stopAnimation() {
324
+ this.currentFrame = 0;
325
+ this.isPlaying = false;
326
+ this.playOnce = false;
327
+ this.texture = this.originalTexture;
328
+ }
329
+
330
+ /**
331
+ * @method draw
332
+ * @description Draws the object
333
+ * @param {mat4} globalViewMatrix - The global view matrix
334
+ * @param {WebGLUniformLocation} uniformLocation - The uniform location
335
+ * @param {number} currentTime - The current time
336
+ * @param {Object} objectTransform - The object transform
337
+ */
338
+ draw(globalViewMatrix, uniformLocation, currentTime, objectTransform) {
339
+ let objectTransformMatrix = mat4.create();
340
+ mat4.translate(
341
+ objectTransformMatrix,
342
+ objectTransformMatrix,
343
+ vec3.fromValues(
344
+ this.pixelart
345
+ ? Math.round(objectTransform.position.x)
346
+ : objectTransform.position.x,
347
+ this.pixelart
348
+ ? Math.round(objectTransform.position.y)
349
+ : objectTransform.position.y,
350
+ objectTransform.position.z,
351
+ ),
352
+ );
353
+ mat4.rotate(objectTransformMatrix, objectTransformMatrix, 0, [1, 0, 0]);
354
+ mat4.rotate(objectTransformMatrix, objectTransformMatrix, 0, [0, 1, 0]);
355
+ mat4.rotate(
356
+ objectTransformMatrix,
357
+ objectTransformMatrix,
358
+ objectTransform.rotation,
359
+ [0, 0, 1],
360
+ );
361
+
362
+ // The scale values here represent half-extents
363
+ mat4.scale(
364
+ objectTransformMatrix,
365
+ objectTransformMatrix,
366
+ vec3.fromValues(
367
+ objectTransform.scale.x,
368
+ objectTransform.scale.y,
369
+ 1.0,
370
+ ),
521
371
  );
522
372
 
373
+ let finalTransformMatrix = mat4.create();
374
+ mat4.mul(finalTransformMatrix, globalViewMatrix, objectTransformMatrix);
375
+ this.gl.uniform1i(
376
+ this.programInfo.uniformLocations.useTexture,
377
+ this.useTexture,
378
+ );
379
+ this.gl.uniform1i(
380
+ this.programInfo.uniformLocations.useInstances,
381
+ false,
382
+ );
383
+
384
+ // Set lighting uniform
385
+ this.gl.uniform1i(
386
+ this.programInfo.uniformLocations.useLighting,
387
+ this.useLighting,
388
+ );
389
+
390
+ // Always set the color uniform for tinting
391
+ this.gl.uniform4fv(this.programInfo.uniformLocations.color, this.color);
392
+
393
+ // Set per-GameObject opacity if available (defaults to 1.0)
394
+ const parentOpacity =
395
+ this.parentObject && typeof this.parentObject.opacity === "number"
396
+ ? this.parentObject.opacity
397
+ : 1.0;
398
+ if (this.programInfo.uniformLocations.uOpacity) {
399
+ this.gl.uniform1f(
400
+ this.programInfo.uniformLocations.uOpacity,
401
+ parentOpacity,
402
+ );
403
+ }
404
+
405
+ this.gl.uniformMatrix4fv(
406
+ uniformLocation,
407
+ false,
408
+ new Float32Array(finalTransformMatrix),
409
+ );
410
+
411
+ // Bind and set vertex position buffer
412
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.verticesBuffer);
523
413
  this.gl.vertexAttribPointer(
524
- this.programInfo.attribLocations.vertexPosition,
525
- 2,
526
- this.gl.FLOAT,
527
- false,
528
- 0,
529
- 0
414
+ this.programInfo.attribLocations.vertexPosition,
415
+ 2,
416
+ this.gl.FLOAT,
417
+ false,
418
+ 0,
419
+ 0,
530
420
  );
421
+ this.gl.enableVertexAttribArray(
422
+ this.programInfo.attribLocations.vertexPosition,
423
+ );
424
+
425
+ // Bind and set texture coordinate buffer
426
+ if (this.useTexture) {
427
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.texCoordBuffer);
428
+ this.gl.vertexAttribPointer(
429
+ this.programInfo.attribLocations.aTexCoord,
430
+ 2,
431
+ this.gl.FLOAT,
432
+ false,
433
+ 0,
434
+ 0,
435
+ );
436
+ this.gl.enableVertexAttribArray(
437
+ this.programInfo.attribLocations.aTexCoord,
438
+ );
439
+
440
+ if (this.texture) {
441
+ this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);
442
+ }
443
+
444
+ this.updateAnimation(currentTime);
445
+
446
+ let texLeft, texRight, texTop, texBottom;
447
+
448
+ if (this.frameWidth > 0 && this.frameHeight > 0) {
449
+ // Spritesheet animation
450
+ const col = this.currentFrame % this.framesPerRow;
451
+ const row = Math.floor(this.currentFrame / this.framesPerRow);
452
+
453
+ texLeft = ((col + 1) * this.frameWidth) / this.textureWidth;
454
+ texRight = (col * this.frameWidth) / this.textureWidth;
455
+ texTop =
456
+ (this.textureHeight -
457
+ row * this.frameHeight -
458
+ this.frameHeight) /
459
+ this.textureHeight;
460
+ texBottom =
461
+ (this.textureHeight - row * this.frameHeight) /
462
+ this.textureHeight;
463
+ } else {
464
+ // Static image
465
+ texLeft = 1.0;
466
+ texRight = 0.0;
467
+ texTop = 0.0;
468
+ texBottom = 1.0;
469
+ }
470
+ // If mirrored change texLeft and texRight in places
471
+ const texCoords = this.mirrored
472
+ ? new Float32Array([
473
+ texRight,
474
+ texBottom,
475
+ texLeft,
476
+ texBottom,
477
+ texRight,
478
+ texTop,
479
+ texLeft,
480
+ texTop,
481
+ ])
482
+ : new Float32Array([
483
+ texLeft,
484
+ texBottom,
485
+ texRight,
486
+ texBottom,
487
+ texLeft,
488
+ texTop,
489
+ texRight,
490
+ texTop,
491
+ ]);
492
+ // Update texture coordinate buffer
493
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.texCoordBuffer);
494
+ this.gl.bufferData(
495
+ this.gl.ARRAY_BUFFER,
496
+ texCoords,
497
+ this.gl.STATIC_DRAW,
498
+ );
499
+ } else {
500
+ // If not using texture, disable the texture attribute
501
+ this.gl.disableVertexAttribArray(
502
+ this.programInfo.attribLocations.aTexCoord,
503
+ );
504
+ this.gl.bindTexture(this.gl.TEXTURE_2D, null);
505
+ }
506
+
507
+ let drawMode, vertexCount;
508
+
509
+ if (this.vertices.length === 8) {
510
+ // Square/Rectangle - Triangle strip
511
+ drawMode = this.gl.TRIANGLE_STRIP;
512
+ vertexCount = 4;
513
+ } else if (this.vertices.length === 6) {
514
+ // Triangle
515
+ drawMode = this.gl.TRIANGLES;
516
+ vertexCount = 3;
517
+ } else {
518
+ // Circle - Triangle fan
519
+ drawMode = this.gl.TRIANGLE_FAN;
520
+ vertexCount = this.vertices.length / 2;
521
+ }
531
522
 
532
- }
533
- drawMode = this.gl.LINE_LOOP;
523
+ if (this.isWireframe) {
524
+ // Fix the wireframe for square
525
+ if (this.vertices.length === 8) {
526
+ let tempBuffer = this.gl.createBuffer();
527
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, tempBuffer);
528
+
529
+ const rectOutline = new Float32Array([
530
+ this.vertices[0],
531
+ this.vertices[1], // Bottom-left (0)
532
+ this.vertices[2],
533
+ this.vertices[3], // Bottom-right (1)
534
+ this.vertices[6],
535
+ this.vertices[7], // Top-right (2)
536
+ this.vertices[4],
537
+ this.vertices[5], // Top-left (3)
538
+ ]);
539
+
540
+ this.gl.bufferData(
541
+ this.gl.ARRAY_BUFFER,
542
+ rectOutline,
543
+ this.gl.STATIC_DRAW,
544
+ );
545
+
546
+ this.gl.vertexAttribPointer(
547
+ this.programInfo.attribLocations.vertexPosition,
548
+ 2,
549
+ this.gl.FLOAT,
550
+ false,
551
+ 0,
552
+ 0,
553
+ );
554
+ }
555
+ drawMode = this.gl.LINE_LOOP;
556
+ }
557
+
558
+ this.gl.drawArrays(drawMode, 0, vertexCount);
534
559
  }
535
560
 
536
- this.gl.drawArrays(drawMode, 0, vertexCount);
537
- }
538
-
539
- /**
540
- * @method loadImage
541
- * @description Loads an image
542
- * @param {string} path - The path to the image
543
- * @returns {Promise} - The promise
544
- */
545
- loadImage(path) {
546
- return new Promise(function (resolve, reject) {
547
- var image = new Image();
548
- image.addEventListener("load", function () { resolve(image); });
549
- image.addEventListener("error", function (err) { reject(err); });
550
- image.src = path;
551
- });
552
- }
561
+ /**
562
+ * @method loadImage
563
+ * @description Loads an image
564
+ * @param {string} path - The path to the image
565
+ * @returns {Promise} - The promise
566
+ */
567
+ loadImage(path) {
568
+ return new Promise(function (resolve, reject) {
569
+ var image = new Image();
570
+ image.crossOrigin = "Anonymous";
571
+ image.addEventListener("load", function () {
572
+ resolve(image);
573
+ });
574
+ image.addEventListener("error", function (err) {
575
+ reject(err);
576
+ });
577
+ image.src = path;
578
+ });
579
+ }
553
580
  }
554
581
 
555
582
  export default Drawable;