@woosh/meep-engine 2.39.38 → 2.39.41

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 (69) hide show
  1. package/core/binary/Base64.js +58 -31
  2. package/core/binary/Base64.spec.js +14 -0
  3. package/core/binary/operations/ceilPowerOfTwo.spec.js +17 -0
  4. package/core/bvh2/binary/2/BinaryUint32BVH.js +7 -1
  5. package/core/bvh2/binary/2/BinaryUint32BVH.spec.js +31 -1
  6. package/core/bvh2/bvh3/bvh_query_user_data_nearest_to_point.js +5 -5
  7. package/core/bvh2/bvh3/bvh_query_user_data_nearest_to_point.spec.js +70 -0
  8. package/core/bvh2/bvh3/query/compute_tight_near_far_clipping_planes.spec.js +2 -2
  9. package/core/collection/IteratorUtils.js +1 -0
  10. package/core/collection/queue/Deque.js +5 -2
  11. package/core/color/Color.js +29 -7
  12. package/core/color/hsluv/HSLuv.js +187 -0
  13. package/core/geom/3d/aabb/aabb3_matrix4_project.js +1 -0
  14. package/core/geom/3d/topology/simplify/simplifyTopoMesh2.js +1 -1
  15. package/core/geom/Quaternion.js +52 -38
  16. package/core/model/ModuleRegistry.js +1 -1
  17. package/core/model/reactive/model/terminal/ReactiveReference.js +1 -1
  18. package/core/model/reactive/trigger/ReactiveTrigger.js +14 -4
  19. package/core/primitives/strings/compareStrings.js +22 -0
  20. package/editor/Editor.js +2 -0
  21. package/editor/ecs/component/editors/ImagePathEditor.js +83 -0
  22. package/editor/ecs/component/editors/ecs/terrain/TerrainEditor.js +12 -6
  23. package/editor/tools/v2/BlenderCameraOrientationGizmo.js +22 -11
  24. package/engine/EngineHarness.js +7 -0
  25. package/engine/asset/loaders/geometry/geometry/computeBufferAttributeHash.js +5 -6
  26. package/engine/asset/loaders/image/codec/Codec.js +9 -0
  27. package/engine/asset/loaders/image/codec/CodecWithFallback.js +52 -11
  28. package/engine/asset/loaders/image/codec/ThreadedImageDecoder.js +12 -0
  29. package/engine/asset/loaders/image/png/PNGReader.js +2 -1
  30. package/engine/asset/loaders/image/png/PNG_HEADER_BYTES.js +1 -0
  31. package/engine/computeStridedIntegerArrayHash.js +19 -0
  32. package/engine/ecs/EntityBuilder.d.ts +2 -0
  33. package/engine/ecs/gui/GUIElement.d.ts +3 -1
  34. package/engine/ecs/terrain/ecs/layers/TerrainLayers.js +5 -3
  35. package/engine/ecs/terrain/ecs/splat/SplatMapping.js +27 -4
  36. package/engine/graphics/ecs/light/binding/fp/FPLightBinding.js +11 -2
  37. package/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +14 -6
  38. package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +2 -8
  39. package/engine/graphics/ecs/mesh-v2/aggregate/SGMesh.js +2 -1
  40. package/engine/graphics/ecs/mesh-v2/aggregate/SGMeshSystem.js +17 -2
  41. package/engine/graphics/geometry/optimization/merge/merge_geometry_hierarchy.js +336 -0
  42. package/engine/graphics/geometry/optimization/merge/prototypeGeometryMerge.js +176 -0
  43. package/engine/graphics/micron/build/hierarchy/buildAbstractPatchHierarchy.js +7 -3
  44. package/engine/graphics/micron/build/hierarchy/merge_patches.js +0 -1
  45. package/engine/graphics/micron/prototypeVirtualGeometry.js +3 -3
  46. package/engine/graphics/particles/particular/engine/parameter/ParameterLookupTable.js +32 -1
  47. package/engine/graphics/render/forward_plus/LightManager.js +58 -13
  48. package/engine/graphics/render/forward_plus/debug/createScreenGrid.js +192 -9
  49. package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_ACCUMULATION.js +42 -1
  50. package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_PREAMBLE.js +1 -0
  51. package/engine/graphics/render/forward_plus/materials/ForwardPlusThreeMaterial.js +5 -0
  52. package/engine/graphics/render/forward_plus/materials/fp_build_fragment_shader.js +1 -4
  53. package/engine/graphics/render/forward_plus/model/Decal.js +33 -5
  54. package/engine/graphics/render/forward_plus/model/PointLight.js +1 -1
  55. package/engine/graphics/render/forward_plus/plugin/MaterialTransformer.js +4 -0
  56. package/engine/graphics/render/forward_plus/prototype/prototypeLightManager.js +480 -12
  57. package/engine/graphics/render/forward_plus/query/query_bvh_frustum_from_texture.js +49 -29
  58. package/engine/graphics/texture/atlas/AbstractTextureAtlas.js +11 -0
  59. package/engine/graphics/texture/atlas/CachingTextureAtlas.js +34 -0
  60. package/engine/graphics/texture/atlas/ReferencedTextureAtlas.js +9 -1
  61. package/engine/graphics/texture/atlas/TextureAtlas.js +29 -16
  62. package/engine/graphics/texture/atlas/TextureAtlasDebugger.js +75 -19
  63. package/engine/graphics/texture/sampler/Sampler2D.d.ts +2 -0
  64. package/engine/graphics/texture/sampler/Sampler2D.js +72 -11
  65. package/engine/sound/ecs/SoundListenerSystem.js +5 -0
  66. package/engine/sound/ecs/emitter/SoundEmitter.js +12 -1
  67. package/package.json +1 -1
  68. package/view/elements/button/ButtonView.js +12 -1
  69. package/view/tooltip/TooltipManager.js +0 -95
@@ -10,6 +10,48 @@ import { BVH_BINARY_NODE_SIZE, BVH_LEAF_NODE_SIZE } from "../../../../../core/bv
10
10
  */
11
11
  const stack = [];
12
12
 
13
+ /**
14
+ *
15
+ * @param {DataView} data_view
16
+ * @param {number} address
17
+ * @param {ArrayLike<number>|number[]} planes
18
+ * @return {boolean}
19
+ */
20
+ function frustum_check(data_view, address, planes) {
21
+ const n_x0 = data_view.getFloat32(address);
22
+ const n_y0 = data_view.getFloat32(address + 4);
23
+ const n_z0 = data_view.getFloat32(address + 8);
24
+ const n_x1 = data_view.getFloat32(address + 12);
25
+ const n_y1 = data_view.getFloat32(address + 16);
26
+ const n_z1 = data_view.getFloat32(address + 20);
27
+
28
+ for (let plane_address = 0; plane_address < 24; plane_address += 4) {
29
+
30
+ const plane_normal_x = planes[plane_address];
31
+ const plane_normal_y = planes[plane_address + 1];
32
+ const plane_normal_z = planes[plane_address + 2];
33
+ const plane_constant = planes[plane_address + 3];
34
+
35
+ const distanceAbovePlane = aabb3_computeDistanceAbovePlane_max(
36
+ plane_normal_x, plane_normal_y, plane_normal_z, plane_constant,
37
+ n_x0, n_y0, n_z0,
38
+ n_x1, n_y1, n_z1
39
+ );
40
+
41
+ if (distanceAbovePlane <= 0) {
42
+ // node is below the plane
43
+
44
+ // query_bvh_frustum_from_objects.rejection_count++;
45
+
46
+ return false;
47
+ }
48
+
49
+ // TODO under current scheme we can reserve 6 bit in what we push onto the stack for plane mask (allows us to skip some planar tests)
50
+ }
51
+
52
+ return true;
53
+ }
54
+
13
55
  /**
14
56
  * Most of the data is in flat continuous array for cache coherence
15
57
  * @param {number[]} destination
@@ -57,35 +99,8 @@ export function query_bvh_frustum_from_texture(
57
99
  // is intermediate node
58
100
  const node_address = node_index * BVH_BINARY_NODE_SIZE;
59
101
 
60
- const n_x0 = data_view.getFloat32(node_address);
61
- const n_y0 = data_view.getFloat32(node_address + 4);
62
- const n_z0 = data_view.getFloat32(node_address + 8);
63
- const n_x1 = data_view.getFloat32(node_address + 12);
64
- const n_y1 = data_view.getFloat32(node_address + 16);
65
- const n_z1 = data_view.getFloat32(node_address + 20);
66
-
67
- for (plane_address = 0; plane_address < 24; plane_address += 4) {
68
-
69
- const plane_normal_x = planes[plane_address];
70
- const plane_normal_y = planes[plane_address + 1];
71
- const plane_normal_z = planes[plane_address + 2];
72
- const plane_constant = planes[plane_address + 3];
73
-
74
- const distanceAbovePlane = aabb3_computeDistanceAbovePlane_max(
75
- plane_normal_x, plane_normal_y, plane_normal_z, plane_constant,
76
- n_x0, n_y0, n_z0,
77
- n_x1, n_y1, n_z1
78
- );
79
-
80
- if (distanceAbovePlane < 0) {
81
- // node is below the plane
82
-
83
- // query_bvh_frustum_from_objects.rejection_count++;
84
-
85
- continue stack_loop;
86
- }
87
-
88
- // TODO under current scheme we can reserve 6 bit in what we push onto the stack for plane mask (allows us to skip some planar tests)
102
+ if (!frustum_check(data_view, node_address, planes)) {
103
+ continue;
89
104
  }
90
105
 
91
106
  const left_index = (node_index << 1) + 1;
@@ -130,6 +145,11 @@ export function query_bvh_frustum_from_texture(
130
145
 
131
146
  } else if (light_type === 3) {
132
147
  // decal
148
+
149
+ if (!frustum_check(data_view, node_address, planes)) {
150
+ continue;
151
+ }
152
+
133
153
  } else {
134
154
  throw new Error(`Unsupported light type ${light_type}`);
135
155
  }
@@ -16,4 +16,15 @@ export class AbstractTextureAtlas {
16
16
  remove(patch) {
17
17
  throw new Error('Not Implemented')
18
18
  }
19
+
20
+ get sampler() {
21
+ throw new Error('Not Implemented')
22
+ }
23
+
24
+ /**
25
+ * @throws {Error} if update fails
26
+ */
27
+ update() {
28
+ throw new Error('Not Implemented')
29
+ }
19
30
  }
@@ -1,5 +1,6 @@
1
1
  import { Cache } from "../../../../core/cache/Cache.js";
2
2
  import { AbstractTextureAtlas } from "./AbstractTextureAtlas.js";
3
+ import { invokeObjectHash } from "../../../../core/model/object/invokeObjectHash.js";
3
4
 
4
5
  /**
5
6
  * 4Mb
@@ -26,6 +27,7 @@ export class CachingTextureAtlas extends AbstractTextureAtlas {
26
27
  */
27
28
  this.__cached_patches = new Cache({
28
29
  maxWeight: cache_size,
30
+ keyHashFunction: invokeObjectHash,
29
31
  keyWeigher(sampler) {
30
32
  return sampler.computeByteSize();
31
33
  }
@@ -41,6 +43,25 @@ export class CachingTextureAtlas extends AbstractTextureAtlas {
41
43
  this.__cached_patches.onEvicted.add(this.__handle_cache_eviction, this);
42
44
  }
43
45
 
46
+ update() {
47
+ try {
48
+ this.__atals.update();
49
+ } catch (e) {
50
+ if (this.__cached_patches.size() > 0) {
51
+
52
+ // has some caches patches, lets drop them
53
+ this.__cached_patches.clear();
54
+
55
+ // try to update again
56
+ this.__atals.update();
57
+
58
+ } else {
59
+ // just re-throw
60
+ throw e;
61
+ }
62
+ }
63
+ }
64
+
44
65
  /**
45
66
  *
46
67
  * @returns {AbstractTextureAtlas}
@@ -49,6 +70,14 @@ export class CachingTextureAtlas extends AbstractTextureAtlas {
49
70
  return this.__atals;
50
71
  }
51
72
 
73
+ /**
74
+ *
75
+ * @return {Sampler2D}
76
+ */
77
+ get sampler() {
78
+ return this.__atals.sampler;
79
+ }
80
+
52
81
  /**
53
82
  *
54
83
  * @param {Sampler2D} sampler
@@ -74,6 +103,11 @@ export class CachingTextureAtlas extends AbstractTextureAtlas {
74
103
 
75
104
  if (existing_patch !== null) {
76
105
  // cache hit
106
+
107
+ // remove from cache
108
+ this.__cached_patches.remove(sampler);
109
+
110
+ // return patch
77
111
  return existing_patch;
78
112
  }
79
113
 
@@ -26,6 +26,10 @@ export class ReferencedTextureAtlas extends AbstractTextureAtlas {
26
26
 
27
27
  }
28
28
 
29
+ update() {
30
+ this.__atlas.update();
31
+ }
32
+
29
33
  /**
30
34
  *
31
35
  * @param {Reference} ref
@@ -39,8 +43,10 @@ export class ReferencedTextureAtlas extends AbstractTextureAtlas {
39
43
  ref_counts.set(patch, ref_count - 1);
40
44
 
41
45
  if (ref_count === 1) {
42
- // clear patch
46
+ // last reference removed, clear patch
43
47
  this.__atlas.remove(patch);
48
+ ref_counts.delete(patch);
49
+ this.__patches.delete(patch.sampler);
44
50
  }
45
51
  }
46
52
 
@@ -61,6 +67,8 @@ export class ReferencedTextureAtlas extends AbstractTextureAtlas {
61
67
  if (patch === undefined) {
62
68
  patch = atlas.add(sampler);
63
69
  ref_counts.set(patch, 1)
70
+
71
+ this.__patches.set(sampler, patch);
64
72
  } else {
65
73
  ref_counts.set(patch, ref_counts.get(patch) + 1);
66
74
  }
@@ -9,7 +9,9 @@ import { AtlasPatchFlag } from "./AtlasPatchFlag.js";
9
9
  import Signal from "../../../../core/events/signal/Signal.js";
10
10
  import { assert } from "../../../../core/assert.js";
11
11
  import { DataType } from "../../../../core/collection/table/DataType.js";
12
- import { DataType2TypedArrayConstructorMapping } from "../../../../core/collection/table/DataType2TypedArrayConstructorMapping.js";
12
+ import {
13
+ DataType2TypedArrayConstructorMapping
14
+ } from "../../../../core/collection/table/DataType2TypedArrayConstructorMapping.js";
13
15
  import { AbstractTextureAtlas } from "./AbstractTextureAtlas.js";
14
16
  import { invokeObjectClone } from "../../../../core/model/object/invokeObjectClone.js";
15
17
 
@@ -47,10 +49,11 @@ export class TextureAtlas extends AbstractTextureAtlas {
47
49
  const channel_count = 4;
48
50
 
49
51
  /**
52
+ * @private
50
53
  * @readonly
51
54
  * @type {Sampler2D}
52
55
  */
53
- this.sampler = new Sampler2D(new TypeArrayConstructor(size * size * channel_count), channel_count, size, size);
56
+ this.__sampler = new Sampler2D(new TypeArrayConstructor(size * size * channel_count), channel_count, size, size);
54
57
 
55
58
 
56
59
  /**
@@ -71,6 +74,14 @@ export class TextureAtlas extends AbstractTextureAtlas {
71
74
  this.__needsUpdate = false;
72
75
  }
73
76
 
77
+ /**
78
+ *
79
+ * @return {Sampler2D}
80
+ */
81
+ get sampler() {
82
+ return this.__sampler;
83
+ }
84
+
74
85
  /**
75
86
  *
76
87
  * @param {Sampler2D} sampler
@@ -127,7 +138,7 @@ export class TextureAtlas extends AbstractTextureAtlas {
127
138
 
128
139
 
129
140
  //erase data
130
- this.sampler.data.fill(0);
141
+ this.__sampler.data.fill(0);
131
142
 
132
143
  this.__needsUpdate = true;
133
144
  }
@@ -175,7 +186,7 @@ export class TextureAtlas extends AbstractTextureAtlas {
175
186
 
176
187
  this.size.set(x, y);
177
188
  this.packer.resize(x, y);
178
- this.sampler.resize(x, y);
189
+ this.__sampler.resize(x, y);
179
190
 
180
191
  return true;
181
192
  }
@@ -188,17 +199,17 @@ export class TextureAtlas extends AbstractTextureAtlas {
188
199
  paintPatch(patch) {
189
200
  // console.time('TextureAtlas.paintPatch');
190
201
 
191
- const target = this.sampler;
202
+ const target = this.__sampler;
192
203
 
193
204
  const source = patch.sampler;
194
205
 
195
- const patchPosition = patch.position;
196
- const patchSize = patch.size;
206
+ const patch_position = patch.position;
207
+ const patch_size = patch.size;
197
208
 
198
209
  target.copy_sameItemSize(
199
210
  source,
200
211
  0, 0,
201
- patchPosition.x, patchPosition.y, patchSize.x, patchSize.y
212
+ patch_position.x, patch_position.y, patch_size.x, patch_size.y
202
213
  );
203
214
 
204
215
  patch.setFlag(AtlasPatchFlag.Painted);
@@ -222,7 +233,7 @@ export class TextureAtlas extends AbstractTextureAtlas {
222
233
 
223
234
  this.eraseArea(x0, y0, x1, y1);
224
235
 
225
-
236
+ // mark as non-painted
226
237
  patch.clearFlag(AtlasPatchFlag.Painted);
227
238
  }
228
239
 
@@ -234,9 +245,9 @@ export class TextureAtlas extends AbstractTextureAtlas {
234
245
  * @param {number} y1
235
246
  */
236
247
  eraseArea(x0, y0, x1, y1) {
237
- const sampler = this.sampler;
248
+ const sampler = this.__sampler;
238
249
 
239
- sampler.zeroFill(x0, y0, x1, y1);
250
+ sampler.zeroFill(x0, y0, x1 - x0, y1 - y0);
240
251
  }
241
252
 
242
253
  /**
@@ -246,6 +257,7 @@ export class TextureAtlas extends AbstractTextureAtlas {
246
257
  * @return {AtlasPatch}
247
258
  */
248
259
  add(sampler, padding = 4) {
260
+ // console.log('#add');
249
261
 
250
262
  const patch = new AtlasPatch();
251
263
 
@@ -272,6 +284,7 @@ export class TextureAtlas extends AbstractTextureAtlas {
272
284
  * @return {boolean}
273
285
  */
274
286
  remove(patch) {
287
+ // console.log('#remove');
275
288
  const patchIndex = this.patches.indexOf(patch);
276
289
 
277
290
  if (patchIndex === -1) {
@@ -282,15 +295,15 @@ export class TextureAtlas extends AbstractTextureAtlas {
282
295
  this.patches.splice(patchIndex, 1);
283
296
  this.idPool.release(patch.id);
284
297
 
285
- if (patch.getFlag(AtlasPatchFlag.Packed)) {
286
- this.packer.remove(patch.packing);
287
- }
288
-
289
298
  if (patch.getFlag(AtlasPatchFlag.Painted)) {
290
299
  //erase the patch
291
300
  this.erasePatch(patch);
292
301
  }
293
302
 
303
+ if (patch.getFlag(AtlasPatchFlag.Packed)) {
304
+ this.packer.remove(patch.packing);
305
+ }
306
+
294
307
  return true;
295
308
  }
296
309
 
@@ -513,7 +526,7 @@ export class TextureAtlas extends AbstractTextureAtlas {
513
526
  * Drops all the data
514
527
  */
515
528
  reset() {
516
- this.sampler.data.fill(0);
529
+ this.__sampler.data.fill(0);
517
530
 
518
531
  this.patches = [];
519
532
 
@@ -1,5 +1,6 @@
1
- import EmptyView from "../../../../view/elements/EmptyView.js";
2
1
  import Vector2 from "../../../../core/geom/Vector2.js";
2
+ import { CanvasView } from "../../../../view/elements/CanvasView.js";
3
+ import { FrameRunner } from "../../FrameRunner.js";
3
4
 
4
5
  /**
5
6
  * Visualizer for TextureAtlas, to help understand current state of the atlas
@@ -9,13 +10,19 @@ export class TextureAtlasDebugger {
9
10
  *
10
11
  * @param {TextureAtlas} atlas
11
12
  * @param {Vector2} [scale]
13
+ * @param {boolean} [draw_padding]
14
+ * @param {boolean} [draw_borders]
12
15
  */
13
- constructor(atlas, { scale = new Vector2(1, 1) } = {}) {
16
+ constructor(atlas, {
17
+ scale = new Vector2(1, 1),
18
+ draw_padding = true,
19
+ draw_borders = true
20
+ } = {}) {
14
21
  window.atlas = atlas;
15
22
 
16
- this.vAtlasContent = new EmptyView({ tag: 'canvas' });
23
+ const vCanvas = new CanvasView();
17
24
 
18
- this.vAtlasContent.css({
25
+ vCanvas.css({
19
26
  position: "absolute",
20
27
  top: "0",
21
28
  left: "0",
@@ -25,49 +32,98 @@ export class TextureAtlasDebugger {
25
32
  background: 'rgba(0,0,0,0.8)'
26
33
  });
27
34
 
28
- const ctx = this.vAtlasContent.el.getContext('2d');
35
+ const ctx = vCanvas.context2d;
36
+
37
+ let last_drawn_version = -1;
29
38
 
30
39
  function draw() {
31
40
 
32
41
  const size = atlas.size;
42
+
43
+
33
44
  if (size.x !== 0 && size.y !== 0) {
34
45
  ctx.clearRect(0, 0, size.x, size.y);
35
46
 
36
47
  const imageData = ctx.createImageData(size.x, size.y);
48
+
37
49
  imageData.data.set(atlas.sampler.data);
38
50
 
39
51
  ctx.putImageData(imageData, 0, 0);
40
52
 
41
- ctx.strokeStyle = 'red';
42
53
 
43
54
  //draw patch boundaries
44
- atlas.patches.forEach(patch => {
45
- const aabb2 = patch.packing;
55
+ for (let i = 0; i < atlas.patches.length; i++) {
56
+ const patch = atlas.patches[i];
46
57
 
47
- ctx.strokeRect(aabb2.x0, aabb2.y0, aabb2.getWidth(), aabb2.getHeight());
58
+ if (draw_padding) {
59
+ // draw padding area
60
+ ctx.strokeStyle = 'rgba(0,166,255,0.5)';
61
+ ctx.lineWidth = patch.padding;
48
62
 
49
- });
63
+ ctx.strokeRect(
64
+ patch.position.x - patch.padding * 0.5,
65
+ patch.position.y - patch.padding * 0.5,
66
+ patch.size.x + patch.padding,
67
+ patch.size.y + patch.padding,
68
+ );
69
+ }
70
+
71
+ if (draw_borders) {
72
+ const aabb2 = patch.packing;
73
+
74
+ // draw patch borders
75
+ ctx.strokeStyle = 'red';
76
+ ctx.lineWidth = 1;
77
+
78
+ ctx.strokeRect(aabb2.x0, aabb2.y0, aabb2.getWidth(), aabb2.getHeight());
79
+ }
80
+ }
50
81
 
51
82
  } else {
52
83
  ctx.clearRect(0, 0, 1, 1);
53
84
  }
85
+
86
+ // remember last drawn version
87
+ last_drawn_version = atlas.sampler.version;
88
+ }
89
+
90
+ vCanvas.size.set(0, 0);
91
+
92
+ function try_draw() {
93
+ if (last_drawn_version !== atlas.sampler.version) {
94
+ draw();
95
+ }
54
96
  }
55
97
 
56
- this.vAtlasContent.size.set(0, 0);
98
+ const write_dimensions = () => {
99
+ const x = atlas.size.x;
100
+ const y = atlas.size.y;
57
101
 
58
- atlas.size.onChanged.add((x, y) => {
59
- const v = this.vAtlasContent;
102
+ vCanvas.size.set(x, y);
103
+ vCanvas.scale.copy(scale);
104
+ vCanvas.transformOrigin.set(0, 0);
105
+ };
60
106
 
61
- const w = x * scale.x;
62
- const h = y * scale.y;
107
+ function full_update() {
108
+ write_dimensions();
109
+ draw();
110
+ }
111
+
112
+ const fr = new FrameRunner(try_draw);
63
113
 
64
- v.size.set(w, h);
65
- v.el.setAttribute('width', x);
66
- v.el.setAttribute('height', y);
114
+ atlas.size.onChanged.add(full_update);
67
115
 
116
+ vCanvas.on.linked.add(() => {
117
+ full_update();
118
+ fr.startup();
68
119
  });
69
120
 
70
- this.vAtlasContent.on.linked.add(draw);
121
+ vCanvas.on.unlinked.add(() => {
122
+ fr.shutdown();
123
+ })
124
+
71
125
  atlas.on.painted.add(draw);
126
+
127
+ this.vAtlasContent = vCanvas;
72
128
  }
73
129
  }
@@ -1,4 +1,6 @@
1
1
  export class Sampler2D {
2
+ constructor(data: ArrayLike<number>, itemSize?: number, width?: number, height?: number)
3
+
2
4
  readonly height: number
3
5
  readonly width: number
4
6
 
@@ -14,6 +14,9 @@ import { typedArrayToDataType } from "../../../../core/collection/array/typedArr
14
14
  import {
15
15
  compute_typed_array_constructor_from_data_type
16
16
  } from "../../../../core/collection/table/DataType2TypedArrayConstructorMapping.js";
17
+ import { Base64 } from "../../../../core/binary/Base64.js";
18
+ import { computeStridedIntegerArrayHash } from "../../../computeStridedIntegerArrayHash.js";
19
+ import { is_typed_array_equals } from "../../../../core/collection/array/typed/is_typed_array_equals.js";
17
20
 
18
21
  /**
19
22
  * @class
@@ -22,20 +25,20 @@ export class Sampler2D {
22
25
  /**
23
26
  *
24
27
  * @param {ArrayLike<number>|number[]|Uint8ClampedArray|Uint8Array|Uint16Array|Uint32Array|Int8Array|Int16Array|Int32Array|Float32Array|Float64Array} data
25
- * @param {int} itemSize
26
- * @param {int} width
27
- * @param {int} height
28
+ * @param {number} itemSize
29
+ * @param {number} width
30
+ * @param {number} height
28
31
  * @constructor
29
32
  */
30
33
  constructor(data = [], itemSize = 1, width = 0, height = 0) {
31
- if (!Number.isInteger(itemSize)) {
32
- throw new Error(`itemSize must be integer, instead was ${itemSize}`);
34
+ if (!Number.isInteger(itemSize) || itemSize < 0) {
35
+ throw new Error(`itemSize must be a non-negative integer, instead was ${itemSize}`);
33
36
  }
34
- if (!Number.isInteger(width)) {
35
- throw new Error(`width must be integer, instead was ${width}`);
37
+ if (!Number.isInteger(width) || itemSize < 0) {
38
+ throw new Error(`width must be a non-negative integer, instead was ${width}`);
36
39
  }
37
- if (!Number.isInteger(height)) {
38
- throw new Error(`height must be integer, instead was ${height}`);
40
+ if (!Number.isInteger(height) || itemSize < 0) {
41
+ throw new Error(`height must be a non-negative integer, instead was ${height}`);
39
42
  }
40
43
 
41
44
  if (data === undefined) {
@@ -522,6 +525,7 @@ export class Sampler2D {
522
525
  * @returns {number}
523
526
  */
524
527
  sampleChannelBilinear(x, y, channel) {
528
+ assert.isNonNegativeInteger(channel, 'channel');
525
529
 
526
530
  const itemSize = this.itemSize;
527
531
 
@@ -1098,12 +1102,16 @@ export class Sampler2D {
1098
1102
  * @param {Number} height
1099
1103
  */
1100
1104
  zeroFill(x, y, width, height) {
1105
+ assert.isNonNegativeInteger(width, 'width');
1106
+ assert.isNonNegativeInteger(height, 'height');
1101
1107
 
1102
1108
  const x0 = clamp(x, 0, this.width);
1103
1109
  const y0 = clamp(y, 0, this.height);
1104
1110
  const x1 = clamp(x + width, 0, this.width);
1105
1111
  const y1 = clamp(y + height, 0, this.height);
1106
1112
 
1113
+ // console.log(`#zerofill x:${x}, y:${y}, width:${width}, height:${height} \t -> \t x0:${x0}, y0:${y0}, x1:${x1}, y1:${y1}`); // DEBUG
1114
+
1107
1115
  const data = this.data;
1108
1116
  const itemSize = this.itemSize;
1109
1117
 
@@ -1431,6 +1439,40 @@ export class Sampler2D {
1431
1439
  }
1432
1440
  }
1433
1441
 
1442
+ /**
1443
+ *
1444
+ * @param {Sampler2D} other
1445
+ * @return {boolean}
1446
+ */
1447
+ equals(other) {
1448
+ if (
1449
+ this.width !== other.width
1450
+ || this.height !== other.height
1451
+ || this.itemSize !== other.itemSize
1452
+ ) {
1453
+ return false;
1454
+ }
1455
+
1456
+ return is_typed_array_equals(this.data, other.data);
1457
+ }
1458
+
1459
+ /**
1460
+ *
1461
+ * @return {number}
1462
+ */
1463
+ hash() {
1464
+ let hash = (((this.width & 0xFFFF) << 16) | (this.height & 0xFFFF)) ^ this.itemSize;
1465
+
1466
+ const length = this.data.length;
1467
+
1468
+ const stride = Math.max(1, Math.ceil(length / 509));
1469
+
1470
+ hash ^= computeStridedIntegerArrayHash(this.data, 0, length, stride);
1471
+
1472
+ return hash;
1473
+ }
1474
+
1475
+
1434
1476
  /**
1435
1477
  * @returns {Sampler2D}
1436
1478
  */
@@ -1450,19 +1492,38 @@ export class Sampler2D {
1450
1492
  }
1451
1493
 
1452
1494
  toJSON() {
1495
+ const encoded = Base64.encode(this.data.buffer);
1496
+
1453
1497
  return {
1454
1498
  height: this.height,
1455
1499
  width: this.width,
1456
1500
  itemSize: this.itemSize,
1457
1501
  type: typedArrayToDataType(this.data),
1458
- data: Array.from(this.data)
1502
+ data: encoded
1459
1503
  }
1460
1504
  }
1461
1505
 
1462
1506
  fromJSON({ height, width, itemSize, type, data }) {
1463
1507
  const CTOR = compute_typed_array_constructor_from_data_type(type);
1464
1508
 
1465
- this.data = new CTOR(data);
1509
+ if (typeof data === "string") {
1510
+
1511
+ const decoded = Base64.decode(data);
1512
+
1513
+ this.data = new CTOR(decoded);
1514
+
1515
+ } else if (Array.isArray(data)) {
1516
+ // deprecated
1517
+ console.warn('Array JSON format is deprecated, please upgrade your data as soon as possible');
1518
+
1519
+ this.data = new CTOR(data);
1520
+
1521
+ } else {
1522
+
1523
+ throw new Error('Unsupported data format');
1524
+
1525
+ }
1526
+
1466
1527
  this.height = height;
1467
1528
  this.width = width;
1468
1529
  this.itemSize = itemSize;
@@ -9,6 +9,7 @@ import { Transform } from '../../ecs/transform/Transform.js';
9
9
  import { browserInfo } from "../../Platform.js";
10
10
  import Vector3 from "../../../core/geom/Vector3.js";
11
11
  import { noop } from "../../../core/function/Functions.js";
12
+ import { assert } from "../../../core/assert.js";
12
13
 
13
14
  const v3 = new Vector3();
14
15
  const v3_up = new Vector3();
@@ -86,6 +87,10 @@ class SoundListenerSystem extends System {
86
87
  */
87
88
  constructor(context) {
88
89
  super();
90
+
91
+ assert.defined(context, 'context');
92
+ assert.isInstanceOf(context, AudioContext, 'context', 'AudioContext');
93
+
89
94
  this.dependencies = [SoundListener, Transform];
90
95
  //
91
96
  this.webAudioContext = context;