@woosh/meep-engine 2.39.43 → 2.40.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 (38) hide show
  1. package/core/binary/BinaryBuffer.js +14 -1
  2. package/core/bvh2/aabb3/AABB3.js +0 -133
  3. package/core/bvh2/aabb3/aabb3_intersects_line_segment.js +1 -1
  4. package/core/geom/3d/matrix/MATRIX_4_IDENTITY.js +9 -0
  5. package/core/geom/Quaternion.js +8 -8
  6. package/core/geom/Quaternion.spec.js +13 -0
  7. package/core/geom/Vector1.d.ts +2 -0
  8. package/core/parser/simple/DataType.js +2 -2
  9. package/editor/tools/v2/BlenderCameraOrientationGizmo.js +28 -4
  10. package/editor/view/EditorView.js +25 -22
  11. package/editor/view/ecs/components/common/NumberController.js +56 -9
  12. package/engine/EngineHarness.js +2 -4
  13. package/engine/asset/loaders/image/png/PNG.js +6 -0
  14. package/engine/asset/loaders/image/png/PNGReader.js +82 -2
  15. package/engine/asset/loaders/image/png/crc.js +66 -0
  16. package/engine/ecs/speaker/VoiceSystem.js +1 -1
  17. package/engine/ecs/terrain/ecs/Terrain.js +58 -36
  18. package/engine/ecs/terrain/ecs/TerrainSystem.js +22 -4
  19. package/engine/ecs/terrain/tiles/TerrainTile.js +109 -131
  20. package/engine/ecs/terrain/tiles/TerrainTileManager.js +100 -121
  21. package/engine/graphics/ecs/camera/Camera.js +1 -1
  22. package/engine/graphics/ecs/light/binding/fp/FPLightBinding.js +4 -4
  23. package/engine/graphics/geometry/bvh/buffered/BVHGeometryRaycaster.js +0 -78
  24. package/engine/graphics/geometry/optimization/merge/merge_geometry_hierarchy.js +0 -9
  25. package/engine/graphics/render/forward_plus/LightManager.js +25 -0
  26. package/engine/graphics/render/forward_plus/data/TextureBackedMemoryRegion.js +6 -2
  27. package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_ACCUMULATION.js +6 -11
  28. package/engine/graphics/render/forward_plus/prototype/prototypeLightManager.js +268 -2
  29. package/engine/graphics/texture/atlas/AtlasPatch.js +12 -6
  30. package/engine/intelligence/behavior/util/LogMessageBehavior.js +29 -0
  31. package/engine/logging/ConsoleLoggerBackend.js +15 -0
  32. package/engine/logging/Logger.js +24 -1
  33. package/engine/logging/LoggerBackend.js +1 -0
  34. package/engine/physics/fluid/FluidField.js +9 -0
  35. package/engine/physics/fluid/effector/AbstractFluidEffector.js +6 -0
  36. package/engine/physics/fluid/effector/GlobalFluidEffector.js +12 -0
  37. package/engine/physics/fluid/effector/WakeFluidEffector.js +8 -0
  38. package/package.json +1 -1
@@ -85,6 +85,14 @@ export class BinaryBuffer {
85
85
  this.__growFactor = 1.1;
86
86
  }
87
87
 
88
+ /**
89
+ * Access raw underlying bytes attached to the buffer
90
+ * @return {Uint8Array}
91
+ */
92
+ get raw_bytes() {
93
+ return this.__data_uint8;
94
+ }
95
+
88
96
  /**
89
97
  * @param {ArrayBuffer} arrayBuffer
90
98
  */
@@ -951,14 +959,19 @@ export class BinaryBuffer {
951
959
  /**
952
960
  *
953
961
  * @param {number} length
962
+ * @param {boolean} [null_terminated]
954
963
  * @returns {string}
955
964
  */
956
- readASCIICharacters(length) {
965
+ readASCIICharacters(length, null_terminated = false) {
957
966
  let result = "";
958
967
 
959
968
  for (let i = 0; i < length; i++) {
960
969
  const code = this.readUint8();
961
970
 
971
+ if (null_terminated && code === 0) {
972
+ break;
973
+ }
974
+
962
975
  result += String.fromCharCode(code);
963
976
  }
964
977
 
@@ -551,139 +551,6 @@ export class AABB3 {
551
551
  return aabb3_intersects_line_segment(this.x0, this.y0, this.z0, this.x1, this.y1, this.z1, startX, startY, startZ, endX, endY, endZ);
552
552
  }
553
553
 
554
- /**
555
- * @source http://stackoverflow.com/questions/3106666/intersection-of-line-segment-with-axis-aligned-box-in-c-sharp
556
- * @param {number} startX
557
- * @param {number} startY
558
- * @param {number} startZ
559
- * @param {number} endX
560
- * @param {number} endY
561
- * @param {number} endZ
562
- * @returns {boolean}
563
- */
564
- intersectSegment2(startX, startY, startZ, endX, endY, endZ) {
565
- //var beginToEnd = segmentEnd - segmentBegin;
566
- const deltaX = endX - startX,
567
- deltaY = endY - startY,
568
- deltaZ = endZ - startZ;
569
- //var minToMax = new Vector3D(boxSize.X, boxSize.Y, boxSize.Z);
570
-
571
- //var min = boxCenter - minToMax / 2;
572
- //var max = boxCenter + minToMax / 2;
573
- //var beginToMin = min - segmentBegin;
574
- //var beginToMax = max - segmentBegin;
575
- //var tNear = double.MinValue;
576
- let tNear = Number.NEGATIVE_INFINITY;
577
- //var tFar = double.MaxValue;
578
- let tFar = Number.POSITIVE_INFINITY;
579
- let t1, t2, tMin, tMax;
580
- //var intersections = new List<Point3D>();
581
- //var intersections = [];
582
- let beginToMin = this.x0 - startX;
583
- let beginToMax = this.x1 - startX;
584
- if (deltaX === 0) {//parallel
585
- if (beginToMin > 0 || beginToMax < 0) {
586
- return false; //segment is not between planes
587
- }
588
- } else {
589
- t1 = beginToMin / deltaX;
590
- t2 = beginToMax / deltaX;
591
- tMin = Math.min(t1, t2);
592
- tMax = Math.max(t1, t2);
593
- if (tMin > tNear) {
594
- tNear = tMin;
595
- }
596
- if (tMax < tFar) {
597
- tFar = tMax;
598
- }
599
- if (tNear > tFar || tFar < 0) {
600
- return false;
601
- }
602
- }
603
- beginToMin = this.y0 - startY;
604
- beginToMax = this.y1 - startY;
605
- if (deltaY === 0) {//parallel
606
- if (beginToMin > 0 || beginToMax < 0) {
607
- return false; //segment is not between planes
608
- }
609
- } else {
610
- t1 = beginToMin / deltaY;
611
- t2 = beginToMax / deltaY;
612
- tMin = Math.min(t1, t2);
613
- tMax = Math.max(t1, t2);
614
- if (tMin > tNear) {
615
- tNear = tMin;
616
- }
617
- if (tMax < tFar) {
618
- tFar = tMax;
619
- }
620
- if (tNear > tFar || tFar < 0) {
621
- return false;
622
- }
623
- }
624
- beginToMin = this.z0 - startZ;
625
- beginToMax = this.z1 - startZ;
626
- if (deltaZ === 0) {//parallel
627
- if (beginToMin > 0 || beginToMax < 0) {
628
- return false; //segment is not between planes
629
- }
630
- } else {
631
- t1 = beginToMin / deltaZ;
632
- t2 = beginToMax / deltaZ;
633
- tMin = Math.min(t1, t2);
634
- tMax = Math.max(t1, t2);
635
- if (tMin > tNear) {
636
- tNear = tMin;
637
- }
638
- if (tMax < tFar) {
639
- tFar = tMax;
640
- }
641
- if (tNear > tFar || tFar < 0) {
642
- return false;
643
- }
644
- }
645
- //
646
- if (tNear >= 0 && tNear <= 1) {
647
- //intersections.push({
648
- // x: startX + deltaX * tNear,
649
- // y: startY + deltaY * tNear,
650
- // z: startZ + deltaZ * tNear
651
- //});
652
- return true;
653
- }
654
- if (tFar >= 0 && tFar <= 1) {
655
- //intersections.push({
656
- // x: startX + deltaX * tFar,
657
- // y: startY + deltaY * tFar,
658
- // z: startZ + deltaZ * tFar
659
- //});
660
- return true;
661
- }
662
- return false;
663
- //foreach (Axis axis in Enum.GetValues(typeof(Axis)))
664
- //{
665
- // if (beginToEnd.GetCoordinate(axis) == 0) // parallel
666
- // {
667
- // if (beginToMin.GetCoordinate(axis) > 0 || beginToMax.GetCoordinate(axis) < 0)
668
- // return intersections; // segment is not between planes
669
- // }
670
- // else
671
- // {
672
- // var t1 = beginToMin.GetCoordinate(axis) / beginToEnd.GetCoordinate(axis);
673
- // var t2 = beginToMax.GetCoordinate(axis) / beginToEnd.GetCoordinate(axis);
674
- // var tMin = Math.Min(t1, t2);
675
- // var tMax = Math.Max(t1, t2);
676
- // if (tMin > tNear) tNear = tMin;
677
- // if (tMax < tFar) tFar = tMax;
678
- // if (tNear > tFar || tFar < 0) return intersections;
679
- //
680
- // }
681
- //}
682
- //if (tNear >= 0 && tNear <= 1) intersections.Add(segmentBegin + beginToEnd * tNear);
683
- //if (tFar >= 0 && tFar <= 1) intersections.Add(segmentBegin + beginToEnd * tFar);
684
- //return intersections;
685
- }
686
-
687
554
  /**
688
555
  *
689
556
  * @param {THREE.Box} box
@@ -1,7 +1,7 @@
1
1
  import { fabsf } from "../../math/fabsf.js";
2
2
 
3
3
  /**
4
- *
4
+ * http://stackoverflow.com/questions/3106666/intersection-of-line-segment-with-axis-aligned-box-in-c-sharp
5
5
  * @param {number} x0
6
6
  * @param {number} y0
7
7
  * @param {number} z0
@@ -0,0 +1,9 @@
1
+ /**
2
+ *
3
+ * @type {number[]|mat4}
4
+ */
5
+ export const MATRIX_4_IDENTITY = Object.freeze([1, 0, 0, 0,
6
+ 0, 1, 0, 0,
7
+ 0, 0, 1, 0,
8
+ 0, 0, 0, 1
9
+ ]);
@@ -1470,7 +1470,7 @@ class Quaternion {
1470
1470
 
1471
1471
  /**
1472
1472
  * Based on GDC talk from Bungie on destiny, compressing quaternions for animation
1473
- * @param value
1473
+ * @param {number} value
1474
1474
  */
1475
1475
  decodeFromUint32(value) {
1476
1476
  //read components
@@ -1481,9 +1481,9 @@ class Quaternion {
1481
1481
  const iv2 = (value >> 22) & 0x3FF;
1482
1482
 
1483
1483
  //scale components back to quaternion range
1484
- const v0 = (iv0 / 511.5 - 1) * K_CONST;
1485
- const v1 = (iv1 / 511.5 - 1) * K_CONST;
1486
- const v2 = (iv2 / 511.5 - 1) * K_CONST;
1484
+ const v0 = (iv0 / 511 - 1) * K_CONST;
1485
+ const v1 = (iv1 / 511 - 1) * K_CONST;
1486
+ const v2 = (iv2 / 511 - 1) * K_CONST;
1487
1487
 
1488
1488
  //restore dropped component using quaternion identity: x^2 + y^2 + z^2 + w^2 = 1
1489
1489
  const dropped_2 = 1 - v0 * v0 - v1 * v1 - v2 * v2;
@@ -1501,7 +1501,7 @@ class Quaternion {
1501
1501
  }
1502
1502
 
1503
1503
  /**
1504
- *
1504
+ * Based on GDC talk from Bungie on destiny, compressing quaternions for animation
1505
1505
  * @returns {number}
1506
1506
  */
1507
1507
  encodeToUint32() {
@@ -1590,9 +1590,9 @@ class Quaternion {
1590
1590
  const m = 1 / (l * K_CONST);
1591
1591
 
1592
1592
  //re-normalize the remaining components to 10 bit UINT value
1593
- const oV0 = ((v0 * m + 1) * 511.5) | 0;
1594
- const oV1 = ((v1 * m + 1) * 511.5) | 0;
1595
- const oV2 = ((v2 * m + 1) * 511.5) | 0;
1593
+ const oV0 = Math.round((v0 * m + 1) * 511);
1594
+ const oV1 = Math.round((v1 * m + 1) * 511);
1595
+ const oV2 = Math.round((v2 * m + 1) * 511);
1596
1596
 
1597
1597
  assert.ok(oV0 <= 1023 && oV0 >= 0, `expected 0 <= ov0 <= 1023, instead was '${oV0}'`);
1598
1598
  assert.ok(oV1 <= 1023 && oV1 >= 0, `expected 0 <= ov1 <= 1023, instead was '${oV1}'`);
@@ -154,6 +154,19 @@ test('encoding to Uint32 consistency', () => {
154
154
  check(1, 0, 0, 0);
155
155
  });
156
156
 
157
+ test('encoding to Uint32 and decoding identity correctly', () => {
158
+ const a = new Quaternion();
159
+ a.copy(Quaternion.identity);
160
+
161
+ const b = new Quaternion();
162
+
163
+ const encoded = a.encodeToUint32();
164
+
165
+ b.decodeFromUint32(encoded);
166
+
167
+ expect(b.toJSON()).toEqual(a.toJSON());
168
+ });
169
+
157
170
 
158
171
  test('to/from euler YXZ consistency', () => {
159
172
  function check(x, y, z) {
@@ -10,4 +10,6 @@ export default class Vector1 {
10
10
  _sub(v: number): void
11
11
 
12
12
  isZero(): boolean
13
+
14
+ process(callback: (x) => any): void
13
15
  }
@@ -3,7 +3,7 @@
3
3
  * @enum {string}
4
4
  * @readonly
5
5
  */
6
- const DataType = {
6
+ export const DataType = {
7
7
  String: "string",
8
8
  Number: "number",
9
9
  Boolean: "boolean",
@@ -12,4 +12,4 @@ const DataType = {
12
12
  };
13
13
 
14
14
 
15
- export default DataType;
15
+ export default DataType;
@@ -234,6 +234,13 @@ export class BlenderCameraOrientationGizmo extends CanvasView {
234
234
  * @type {Signal<string,Vector3>}
235
235
  */
236
236
  this.on.axisSelected = new Signal();
237
+
238
+ /**
239
+ *
240
+ * @type {boolean}
241
+ * @private
242
+ */
243
+ this.__needs_update = true;
237
244
  }
238
245
 
239
246
  link() {
@@ -262,6 +269,7 @@ export class BlenderCameraOrientationGizmo extends CanvasView {
262
269
  */
263
270
  onMouseMove(evt) {
264
271
  this._setPointerPositionFromEvent(evt);
272
+ this.__try_update();
265
273
  }
266
274
 
267
275
  /**
@@ -278,6 +286,11 @@ export class BlenderCameraOrientationGizmo extends CanvasView {
278
286
 
279
287
  this.input_pointer_position = new Vector3(v2.x, v2.y, 0);
280
288
 
289
+
290
+ if (position_changed) {
291
+ this.__needs_update = true;
292
+ }
293
+
281
294
  return position_changed;
282
295
  }
283
296
 
@@ -287,6 +300,8 @@ export class BlenderCameraOrientationGizmo extends CanvasView {
287
300
  */
288
301
  onMouseOut(evt) {
289
302
  this.input_pointer_position = null;
303
+ this.__needs_update = true;
304
+ this.__try_update();
290
305
  }
291
306
 
292
307
  /**
@@ -295,10 +310,9 @@ export class BlenderCameraOrientationGizmo extends CanvasView {
295
310
  */
296
311
  onMouseClick(evt) {
297
312
 
298
- if (this._setPointerPositionFromEvent(evt)) {
299
- // positions do not match, force update
300
- this.update();
301
- }
313
+ this._setPointerPositionFromEvent(evt);
314
+
315
+ this.__try_update();
302
316
 
303
317
  if (this.selectedAxis !== null) {
304
318
  this.on.axisSelected.send2(
@@ -337,7 +351,17 @@ export class BlenderCameraOrientationGizmo extends CanvasView {
337
351
  ctx.closePath();
338
352
  }
339
353
 
354
+ __try_update() {
355
+ if (!this.__needs_update) {
356
+ return;
357
+ }
358
+ this.update();
359
+ }
360
+
340
361
  update() {
362
+ // reset flag
363
+ this.__needs_update = false;
364
+
341
365
  this.clear();
342
366
 
343
367
  // Calculate the rotation matrix from the camera
@@ -28,10 +28,11 @@ import SelectionClearAction from "../actions/concrete/SelectionClearAction.js";
28
28
  import EntityCreateAction from "../actions/concrete/EntityCreateAction.js";
29
29
  import ComponentAddAction from "../actions/concrete/ComponentAddAction.js";
30
30
  import SelectionAddAction from "../actions/concrete/SelectionAddAction.js";
31
- import { noop, passThrough } from "../../core/function/Functions.js";
31
+ import { passThrough } from "../../core/function/Functions.js";
32
32
  import { ProcessState } from "../../core/process/ProcessState.js";
33
33
  import { MeshEvents } from "../../engine/graphics/ecs/mesh/MeshEvents.js";
34
34
  import { obtainTerrain } from "../../engine/ecs/terrain/util/obtainTerrain.js";
35
+ import { SurfacePoint3 } from "../../core/geom/3d/SurfacePoint3.js";
35
36
 
36
37
  class ViewManager extends View {
37
38
  constructor() {
@@ -204,35 +205,37 @@ function prepareMeshLibrary(editor) {
204
205
  const entityCreateAction = new EntityCreateAction();
205
206
  actions.do(entityCreateAction);
206
207
 
207
- terrain.raycast(source, direction, function (hit, normal, geometry) {
208
- function handleMeshSetEvent() {
209
- const bb = mesh.boundingBox;
208
+ const sp3 = new SurfacePoint3();
210
209
 
211
- const c0 = new Vector3(bb.x0, bb.y0, bb.z0);
212
- const c1 = new Vector3(bb.x1, bb.y1, bb.z1);
210
+ const hit_found = terrain.raycastFirstSync(sp3, source.x, source.y, source.z, direction.x, direction.y, direction.z);
213
211
 
214
- const diagonal = c0.distanceTo(c1);
212
+ function handleMeshSetEvent() {
213
+ const bb = mesh.boundingBox;
215
214
 
216
- const offset = direction.clone().multiplyScalar(diagonal);
215
+ const c0 = new Vector3(bb.x0, bb.y0, bb.z0);
216
+ const c1 = new Vector3(bb.x1, bb.y1, bb.z1);
217
217
 
218
- transform.position.add(offset);
218
+ const diagonal = c0.distanceTo(c1);
219
219
 
220
- //remove listener
221
- ecd.removeEntityEventListener(entityCreateAction.entity, MeshEvents.DataSet, handleMeshSetEvent);
222
- }
220
+ const offset = direction.clone().multiplyScalar(diagonal);
223
221
 
224
- if (hit !== null) {
225
- //got a terrain ray hit, set world placement position to that point
226
- worldPosition.copy(hit);
227
- } else {
228
- //set position to the source of the ray pick if there's nothing else available
229
- worldPosition.copy(source);
222
+ transform.position.add(offset);
230
223
 
224
+ //remove listener
225
+ ecd.removeEntityEventListener(entityCreateAction.entity, MeshEvents.DataSet, handleMeshSetEvent);
226
+ }
231
227
 
232
- //wait for mesh to load
233
- ecd.addEntityEventListener(entityCreateAction.entity, MeshEvents.DataSet, handleMeshSetEvent);
234
- }
235
- }, noop);
228
+ if (hit_found) {
229
+ //got a terrain ray hit, set world placement position to that point
230
+ worldPosition.copy(sp3.position);
231
+ } else {
232
+ //set position to the source of the ray pick if there's nothing else available
233
+ worldPosition.copy(source);
234
+
235
+
236
+ //wait for mesh to load
237
+ ecd.addEntityEventListener(entityCreateAction.entity, MeshEvents.DataSet, handleMeshSetEvent);
238
+ }
236
239
 
237
240
  transform.position.copy(worldPosition);
238
241
 
@@ -1,6 +1,8 @@
1
1
  import View from "../../../../../view/View.js";
2
2
  import Vector1 from "../../../../../core/geom/Vector1.js";
3
3
 
4
+ const DEFAULT_VALUE = 0;
5
+
4
6
  export class NumberController extends View {
5
7
  /**
6
8
  *
@@ -22,7 +24,7 @@ export class NumberController extends View {
22
24
  const el = document.createElement('input');
23
25
  this.el = el;
24
26
 
25
- classList.forEach(c => this.addClass(c));
27
+ this.addClasses(classList);
26
28
 
27
29
  el.setAttribute('type', 'text');
28
30
  el.setAttribute('spellcheck', false);
@@ -32,6 +34,42 @@ export class NumberController extends View {
32
34
 
33
35
  let lockForward = false;
34
36
 
37
+
38
+ /**
39
+ *
40
+ * @param {number} num_value
41
+ * @return {string}
42
+ */
43
+ function format_value_string(num_value) {
44
+
45
+ let str = String(num_value);
46
+
47
+ if (Math.abs(num_value) > 0 && Math.abs(num_value % 1).toString().length > (figures + 2)) {
48
+ const long_form = num_value.toFixed(figures);
49
+
50
+ const separator_index = long_form.indexOf('.');
51
+
52
+ if (separator_index !== -1) {
53
+ // has a decimal fraction, remove trailing zeroes
54
+ let i = long_form.length - 1;
55
+
56
+ while (i > 0 && long_form.charAt(i) === "0") {
57
+ i--;
58
+ }
59
+
60
+ if (long_form.charAt(i) === '.') {
61
+ i--;
62
+ }
63
+
64
+ str = long_form.slice(0, i + 1);
65
+ } else {
66
+ str = long_form;
67
+ }
68
+ }
69
+
70
+ return str;
71
+ }
72
+
35
73
  function data2view() {
36
74
 
37
75
  if (lockForward) {
@@ -40,26 +78,35 @@ export class NumberController extends View {
40
78
 
41
79
  const num_value = _value.getValue();
42
80
 
43
- let str = num_value;
44
-
45
- if (Math.abs(num_value) > 0 && Math.abs(num_value % 1).toString().length > (figures + 2)) {
46
- str = num_value.toFixed(figures);
47
- }
48
-
49
- el.value = str;
81
+ el.value = format_value_string(num_value);
50
82
 
51
83
  }
52
84
 
53
85
  function view2data() {
54
86
  lockForward = true;
55
87
 
56
- _value.set(parseFloat(el.value));
88
+ const value = parseFloat(el.value);
89
+
90
+ if (Number.isNaN(value)) {
91
+ _value.set(DEFAULT_VALUE);
92
+ } else {
93
+ _value.set(value);
94
+ }
57
95
 
58
96
  lockForward = false;
59
97
  }
60
98
 
99
+ /**
100
+ * Input field stops being edited
101
+ */
102
+ function handle_blur() {
103
+ // reformat contents of the input field
104
+ el.value = format_value_string(_value.getValue());
105
+ }
106
+
61
107
  _value.process(data2view);
62
108
 
63
109
  el.addEventListener('input', view2data);
110
+ el.addEventListener('blur', handle_blur);
64
111
  }
65
112
  }
@@ -9,7 +9,6 @@ import Vector3 from "../core/geom/Vector3.js";
9
9
  import Terrain from "./ecs/terrain/ecs/Terrain.js";
10
10
  import Vector2 from "../core/geom/Vector2.js";
11
11
  import Water from "./graphics/ecs/water/Water.js";
12
- import { loadGameClassRegistry } from "../../model/game/GameClassRegistry.js";
13
12
  import { WebEnginePlatform } from "./platform/WebEnginePlatform.js";
14
13
  import Tag from "./ecs/components/Tag.js";
15
14
  import { SerializationMetadata } from "./ecs/components/SerializationMetadata.js";
@@ -82,7 +81,7 @@ export class EngineHarness {
82
81
 
83
82
  window.engine = this.engine;
84
83
 
85
- logger.addBackend(new ConsoleLoggerBackend());
84
+ logger.addBackend(ConsoleLoggerBackend.INSTANCE);
86
85
 
87
86
  this.p = null;
88
87
  }
@@ -116,8 +115,6 @@ export class EngineHarness {
116
115
 
117
116
  await setLocale(engine);
118
117
 
119
- loadGameClassRegistry(engine.classRegistry);
120
-
121
118
  engine.sceneManager.create("test");
122
119
  engine.sceneManager.set("test");
123
120
 
@@ -440,6 +437,7 @@ export class EngineHarness {
440
437
 
441
438
  const eb = new EntityBuilder();
442
439
 
440
+ eb.add(new Transform())
443
441
  eb.add(terrain);
444
442
 
445
443
  if (enableWater) {
@@ -40,6 +40,12 @@ export function PNG() {
40
40
  */
41
41
  this.trns = null;
42
42
 
43
+
44
+ /**
45
+ * Text metadata coming from tEXt chunks
46
+ * @type {Object<string>}
47
+ */
48
+ this.text = {};
43
49
  }
44
50
 
45
51
  PNG.prototype.getWidth = function () {