@woosh/meep-engine 2.39.40 → 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 (53) hide show
  1. package/core/binary/operations/ceilPowerOfTwo.spec.js +17 -0
  2. package/core/bvh2/binary/2/BinaryUint32BVH.js +7 -1
  3. package/core/bvh2/binary/2/BinaryUint32BVH.spec.js +31 -1
  4. package/core/bvh2/bvh3/query/compute_tight_near_far_clipping_planes.spec.js +2 -2
  5. package/core/collection/IteratorUtils.js +1 -0
  6. package/core/collection/queue/Deque.js +5 -2
  7. package/core/color/Color.js +22 -4
  8. package/core/color/hsluv/HSLuv.js +187 -0
  9. package/core/geom/3d/aabb/aabb3_matrix4_project.js +1 -0
  10. package/core/geom/3d/topology/simplify/simplifyTopoMesh2.js +1 -1
  11. package/core/primitives/strings/compareStrings.js +22 -0
  12. package/editor/Editor.js +2 -0
  13. package/editor/tools/v2/BlenderCameraOrientationGizmo.js +22 -11
  14. package/engine/EngineHarness.js +7 -0
  15. package/engine/asset/loaders/geometry/geometry/computeBufferAttributeHash.js +5 -6
  16. package/engine/asset/loaders/image/codec/Codec.js +9 -0
  17. package/engine/asset/loaders/image/codec/CodecWithFallback.js +52 -11
  18. package/engine/asset/loaders/image/codec/ThreadedImageDecoder.js +12 -0
  19. package/engine/asset/loaders/image/png/PNGReader.js +2 -1
  20. package/engine/asset/loaders/image/png/PNG_HEADER_BYTES.js +1 -0
  21. package/engine/computeStridedIntegerArrayHash.js +19 -0
  22. package/engine/ecs/EntityBuilder.d.ts +2 -0
  23. package/engine/ecs/gui/GUIElement.d.ts +3 -1
  24. package/engine/graphics/ecs/light/binding/fp/FPLightBinding.js +11 -2
  25. package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +2 -8
  26. package/engine/graphics/geometry/optimization/merge/merge_geometry_hierarchy.js +336 -0
  27. package/engine/graphics/geometry/optimization/merge/prototypeGeometryMerge.js +176 -0
  28. package/engine/graphics/micron/build/hierarchy/buildAbstractPatchHierarchy.js +7 -3
  29. package/engine/graphics/micron/build/hierarchy/merge_patches.js +0 -1
  30. package/engine/graphics/micron/prototypeVirtualGeometry.js +3 -3
  31. package/engine/graphics/particles/particular/engine/parameter/ParameterLookupTable.js +32 -1
  32. package/engine/graphics/render/forward_plus/LightManager.js +58 -13
  33. package/engine/graphics/render/forward_plus/debug/createScreenGrid.js +192 -9
  34. package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_ACCUMULATION.js +42 -1
  35. package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_PREAMBLE.js +1 -0
  36. package/engine/graphics/render/forward_plus/materials/ForwardPlusThreeMaterial.js +5 -0
  37. package/engine/graphics/render/forward_plus/materials/fp_build_fragment_shader.js +1 -4
  38. package/engine/graphics/render/forward_plus/model/Decal.js +33 -5
  39. package/engine/graphics/render/forward_plus/model/PointLight.js +1 -1
  40. package/engine/graphics/render/forward_plus/plugin/MaterialTransformer.js +4 -0
  41. package/engine/graphics/render/forward_plus/prototype/prototypeLightManager.js +480 -12
  42. package/engine/graphics/render/forward_plus/query/query_bvh_frustum_from_texture.js +49 -29
  43. package/engine/graphics/texture/atlas/AbstractTextureAtlas.js +11 -0
  44. package/engine/graphics/texture/atlas/CachingTextureAtlas.js +34 -0
  45. package/engine/graphics/texture/atlas/ReferencedTextureAtlas.js +9 -1
  46. package/engine/graphics/texture/atlas/TextureAtlas.js +29 -16
  47. package/engine/graphics/texture/atlas/TextureAtlasDebugger.js +75 -19
  48. package/engine/graphics/texture/sampler/Sampler2D.d.ts +2 -0
  49. package/engine/graphics/texture/sampler/Sampler2D.js +49 -9
  50. package/engine/sound/ecs/SoundListenerSystem.js +5 -0
  51. package/engine/sound/ecs/emitter/SoundEmitter.js +12 -1
  52. package/package.json +1 -1
  53. package/view/tooltip/TooltipManager.js +0 -95
@@ -0,0 +1,17 @@
1
+ import { ceilPowerOfTwo } from "./ceilPowerOfTwo.js";
2
+
3
+ test('test', () => {
4
+ expect(ceilPowerOfTwo(1)).toBe(1);
5
+
6
+ expect(ceilPowerOfTwo(2)).toBe(2);
7
+
8
+ expect(ceilPowerOfTwo(3)).toBe(4);
9
+ expect(ceilPowerOfTwo(4)).toBe(4);
10
+
11
+ expect(ceilPowerOfTwo(5)).toBe(8);
12
+ expect(ceilPowerOfTwo(7)).toBe(8);
13
+ expect(ceilPowerOfTwo(8)).toBe(8);
14
+
15
+ expect(ceilPowerOfTwo(9)).toBe(16);
16
+ expect(ceilPowerOfTwo(15)).toBe(16);
17
+ });
@@ -201,7 +201,13 @@ export class BinaryUint32BVH {
201
201
 
202
202
  const twoLeafLimit = ceilPowerOfTwo(count);
203
203
 
204
- this.__node_count_binary = twoLeafLimit - 1;
204
+ if (count <= 1) {
205
+ // special case
206
+ this.__node_count_binary = twoLeafLimit;
207
+ } else {
208
+ this.__node_count_binary = twoLeafLimit - 1;
209
+ }
210
+
205
211
  }
206
212
 
207
213
  /**
@@ -12,6 +12,36 @@ test('1 leaf build does not throw', () => {
12
12
  bvh.build();
13
13
  });
14
14
 
15
+ test('0 leaf tree must have a no binary nodes', () => {
16
+ const bvh = new BinaryUint32BVH();
17
+
18
+ bvh.setLeafCount(0);
19
+ bvh.initialize_structure();
20
+ bvh.build();
21
+
22
+ expect(bvh.getBinaryNodeCount()).toBe(0);
23
+ });
24
+
25
+ test('1 leaf tree must have a single binary node', () => {
26
+ const bvh = new BinaryUint32BVH();
27
+
28
+ bvh.setLeafCount(1);
29
+ bvh.initialize_structure();
30
+ bvh.build();
31
+
32
+ expect(bvh.getBinaryNodeCount()).toBe(1);
33
+ });
34
+
35
+ test('4 leaf tree must have a 3 binary nodes', () => {
36
+ const bvh = new BinaryUint32BVH();
37
+
38
+ bvh.setLeafCount(4);
39
+ bvh.initialize_structure();
40
+ bvh.build();
41
+
42
+ expect(bvh.getBinaryNodeCount()).toBe(3);
43
+ });
44
+
15
45
  test('read/write 1 leaf', () => {
16
46
  const bvh = new BinaryUint32BVH();
17
47
 
@@ -23,7 +53,7 @@ test('read/write 1 leaf', () => {
23
53
  expect(bvh.readLeafPayload(0)).toBe(7);
24
54
 
25
55
  const bounds = [];
26
- bvh.readBounds(0, bounds, 0);
56
+ bvh.readBounds(bvh.getLeafBlockAddress(), bounds, 0);
27
57
 
28
58
  expect(bounds[0]).toBeCloseTo(-3);
29
59
  expect(bounds[1]).toBeCloseTo(-7);
@@ -13,7 +13,7 @@ function orthographic_frustum(left, right, up, down, near, far) {
13
13
  ]);
14
14
  }
15
15
 
16
- test('Empty', () => {
16
+ test('Empty does not throw', () => {
17
17
  const bvh = new ExplicitBinaryBoundingVolumeHierarchy();
18
18
 
19
19
  const res = [];
@@ -23,7 +23,7 @@ test('Empty', () => {
23
23
  compute_tight_near_far_clipping_planes(res, 0, 0b000011, bvh, frustum);
24
24
  });
25
25
 
26
- test('Axis-aligned, orthographic tight fit around a single small node', () => {
26
+ test.skip('Axis-aligned, orthographic tight fit around a single small node', () => {
27
27
  const frustum = orthographic_frustum(-1, 1, -1, 1, -1, 1);
28
28
 
29
29
  const bvh = new ExplicitBinaryBoundingVolumeHierarchy();
@@ -13,4 +13,5 @@ export function collectIteratorValueToArray(result, iterator) {
13
13
  result.push(it.value);
14
14
  }
15
15
 
16
+ return result;
16
17
  }
@@ -200,11 +200,14 @@ export class Deque {
200
200
  */
201
201
  __index_of(e) {
202
202
  const size = this.size();
203
- const capacity = this.__data.length;
203
+
204
+ const data = this.__data;
205
+ const capacity = data.length;
206
+
204
207
  for (let i = 0; i < size; i++) {
205
208
  const index = (this.__head + i) % capacity;
206
209
 
207
- const el = this.__data[index];
210
+ const el = data[index];
208
211
 
209
212
  if (el === e) {
210
213
  return i;
@@ -157,7 +157,8 @@ export class Color {
157
157
  let _h = h % 1;
158
158
 
159
159
  if (_h < 0) {
160
- _h = h + 1;
160
+ // move into positive range
161
+ _h = _h + Math.ceil(Math.abs(_h));
161
162
  }
162
163
 
163
164
  const k_r = (_h * 12) % 12;
@@ -184,7 +185,8 @@ export class Color {
184
185
  let _h = h % 1;
185
186
 
186
187
  if (_h < 0) {
187
- _h = h + 1;
188
+ // move into positive range
189
+ _h = _h + Math.ceil(Math.abs(_h));
188
190
  }
189
191
 
190
192
  const h_face = _h * 6;
@@ -244,7 +246,8 @@ export class Color {
244
246
  let _h = h % 1;
245
247
 
246
248
  if (_h < 0) {
247
- _h = h + 1;
249
+ // move into positive range
250
+ _h = _h + Math.ceil(Math.abs(_h));
248
251
  }
249
252
 
250
253
  const h_face = _h * 6;
@@ -315,7 +318,7 @@ export class Color {
315
318
 
316
319
  let r, g, b;
317
320
 
318
- const i = Math.floor(h * 6);
321
+ const i = Math.floor(_h * 6);
319
322
  const f = _h * 6 - i;
320
323
  const p = _v * (1 - _s);
321
324
  const q = _v * (1 - f * _s);
@@ -534,6 +537,21 @@ export class Color {
534
537
  return new Color(r, g, b);
535
538
  }
536
539
 
540
+ /**
541
+ *
542
+ * @param {number} h
543
+ * @param {number} s
544
+ * @param {number} v
545
+ * @return {Color}
546
+ */
547
+ static fromHSV(h, s, v) {
548
+ const color = new Color();
549
+
550
+ color.setHSV(h, s, v);
551
+
552
+ return color;
553
+ }
554
+
537
555
 
538
556
  /**
539
557
  *
@@ -0,0 +1,187 @@
1
+ /**
2
+ * @see https://github.com/hsluv/hsluv-c/blob/master/src/hsluv.c
3
+ */
4
+
5
+ const temp_0 = new Float32Array(3);
6
+ const temp_1 = new Float32Array(3);
7
+ /* for RGB */
8
+ const m = new Float32Array([
9
+ 3.24096994190452134377, -1.53738317757009345794, -0.49861076029300328366,
10
+ -0.96924363628087982613, 1.87596750150772066772, 0.04155505740717561247,
11
+ 0.05563007969699360846, -0.20397695888897656435, 1.05697151424287856072
12
+ ]);
13
+
14
+ /* for XYZ */
15
+ const m_inv = new Float32Array([
16
+ 0.41239079926595948129, 0.35758433938387796373, 0.18048078840183428751,
17
+ 0.21263900587151035754, 0.71516867876775592746, 0.07219231536073371500,
18
+ 0.01933081871559185069, 0.11919477979462598791, 0.95053215224966058086
19
+ ]);
20
+
21
+ function dot_product(t1, offset_1, t2, offset_2) {
22
+ return t1[offset_1] * t2[offset_2] + t1[offset_1 + 1] * t2[offset_2 + 1] + t1[offset_1 + 2] * t2[offset_2 + 2];
23
+ }
24
+
25
+ /* Used for rgb conversions */
26
+
27
+ function from_linear(c) {
28
+ if (c <= 0.0031308)
29
+ return 12.92 * c;
30
+ else
31
+ return 1.055 * Math.pow(c, 1.0 / 2.4) - 0.055;
32
+ }
33
+
34
+ function to_linear(c) {
35
+ if (c > 0.04045)
36
+ return Math.pow((c + 0.055) / 1.055, 2.4);
37
+ else
38
+ return c / 12.92;
39
+ }
40
+
41
+ const ref_u = 0.19783000664283680764;
42
+ const ref_v = 0.46831999493879100370;
43
+
44
+ const kappa = 903.29629629629629629630;
45
+ const epsilon = 0.00885645167903563082;
46
+
47
+
48
+ /**
49
+ *
50
+ * @param {number} theta
51
+ * @param {number[]|Float32Array} line
52
+ * @param {number} line_address
53
+ * @return {number}
54
+ */
55
+ function ray_length_until_intersect(theta, line, line_address) {
56
+ return line[line_address + 1] / (Math.sin(theta) - line[line_address] * Math.cos(theta));
57
+ }
58
+
59
+ function get_bounds( l, bounds)
60
+ {
61
+ const tl = l + 16.0;
62
+ const sub1 = (tl * tl * tl) / 1560896.0;
63
+ const sub2 = (sub1 > epsilon ? sub1 : (l / kappa));
64
+ let channel;
65
+ let t;
66
+
67
+ for(channel = 0; channel < 3; channel++) {
68
+ const ch3 = channel*3;
69
+ let m1 = m[ch3];
70
+ let m2 = m[ch3+1];
71
+ let m3 = m[ch3+2];
72
+
73
+ for (t = 0; t < 2; t++) {
74
+ const top1 = (284517.0 * m1 - 94839.0 * m3) * sub2;
75
+ const top2 = (838422.0 * m3 + 769860.0 * m2 + 731718.0 * m1) * l * sub2 - 769860.0 * t * l;
76
+ const bottom = (632260.0 * m3 - 126452.0 * m2) * sub2 + 126452.0 * t;
77
+
78
+ const bounds_index = channel * 2 + t;
79
+ const bounds_address = bounds_index*2;
80
+
81
+ bounds[bounds_address] = top1 / bottom;
82
+ bounds[bounds_address] = top2 / bottom;
83
+ }
84
+ }
85
+ }
86
+
87
+ function max_chroma_for_lh(l, h) {
88
+ let min_len = Infinity;
89
+ const hrad = h * 0.01745329251994329577; /* (2 * pi / 360) */
90
+ const bounds = new Float32Array(12);
91
+ let i;
92
+
93
+ get_bounds(l, bounds);
94
+
95
+ for (i = 0; i < 6; i++) {
96
+ const len = ray_length_until_intersect(hrad, bounds, i * 2);
97
+
98
+ if (len >= 0 && len < min_len)
99
+ min_len = len;
100
+ }
101
+
102
+ return min_len;
103
+ }
104
+
105
+ function hsluv2lch(input, output) {
106
+ let h = input[0];
107
+ const s = input[1];
108
+ const l = input[2];
109
+ let c;
110
+
111
+ /* White and black: disambiguate chroma */
112
+ if (l > 99.9999999 || l < 0.00000001)
113
+ c = 0.0;
114
+ else
115
+ c = max_chroma_for_lh(l, h) / 100.0 * s;
116
+
117
+ /* Grays: disambiguate hue */
118
+ if (s < 0.00000001)
119
+ h = 0.0;
120
+
121
+ output[0] = l;
122
+ output[1] = c;
123
+ output[2] = h;
124
+ }
125
+
126
+ function lch2luv(input, output) {
127
+
128
+ }
129
+
130
+ function luv2xyz(input, output) {
131
+
132
+ }
133
+
134
+ function xyz2rgb(input, output) {
135
+ const r = from_linear(dot_product(m, 0, input, 0));
136
+
137
+ const g = from_linear(dot_product(m, 3, input, 0));
138
+
139
+ const b = from_linear(dot_product(m, 6, input, 0));
140
+
141
+ output[0] = r;
142
+ output[1] = g;
143
+ output[2] = b;
144
+ }
145
+
146
+ function rgb2xyz(input, output) {
147
+
148
+ }
149
+
150
+ function xyz2luv(input, output) {
151
+
152
+ }
153
+
154
+ function luv2lch(input, output) {
155
+
156
+ }
157
+
158
+ function lch2hsluv(input, output) {
159
+
160
+ }
161
+
162
+
163
+ /**
164
+ *
165
+ * @param {number[]} input
166
+ * @param {number[]} output
167
+ */
168
+ export function rgb2hsluv(input, output) {
169
+ rgb2xyz(input, temp_0);
170
+ xyz2luv(temp_0, temp_1);
171
+ luv2lch(temp_1, temp_0);
172
+ lch2hsluv(temp_0, output);
173
+ }
174
+
175
+ /**
176
+ *
177
+ * @param {number[]} input
178
+ * @param {number[]} output
179
+ */
180
+ export function hsluv2rgb(input, output) {
181
+ hsluv2lch(input, temp_0);
182
+ lch2luv(temp_0, temp_1);
183
+ luv2xyz(temp_1, temp_0);
184
+ xyz2rgb(temp_0, output);
185
+ }
186
+
187
+
@@ -1,5 +1,6 @@
1
1
  /**
2
2
  * NOTE: Based on Transforming Axis-Aligned Bounding Boxes by Jim Arvo from "Graphics Gems", Academic Press, 1990
3
+ * NOTE: {@link result} and {@link aabb} must be different objects
3
4
  * @param {ArrayLike<number>|number[]|Float32Array} result
4
5
  * @param {ArrayLike<number>|number[]|Float32Array} aabb
5
6
  * @param {number[]} matrix 4x4 matrix
@@ -52,7 +52,7 @@ export function edge_collapse_reduce_face_count(mesh, num_faces_to_remove, heap,
52
52
  const target_face_count = max2(0, mesh_faces.size - num_faces_to_remove);
53
53
 
54
54
  let cycle_count = 0;
55
- let cycle_limit = max2(heap.size, num_faces_to_remove * 4);
55
+ let cycle_limit = max2(heap.size, num_faces_to_remove * 10) + 10;
56
56
 
57
57
  while (
58
58
  !heap.is_empty()
@@ -5,6 +5,28 @@
5
5
  * @returns {number}
6
6
  */
7
7
  export function compareStrings(a, b) {
8
+ // null check
9
+ if (a === null) {
10
+ if (b === null) {
11
+ return 0;
12
+ } else {
13
+ return 1;
14
+ }
15
+ } else if (b === null) {
16
+ return -1;
17
+ }
18
+
19
+ // undefined check
20
+ if (a === undefined) {
21
+ if (b === undefined) {
22
+ return 0;
23
+ } else {
24
+ return 1;
25
+ }
26
+ } else if (b === undefined) {
27
+ return -1;
28
+ }
29
+
8
30
  const n = a.length;
9
31
 
10
32
  const m = b.length;
package/editor/Editor.js CHANGED
@@ -69,6 +69,8 @@ import Signal from "../core/events/signal/Signal.js";
69
69
  import ObservedInteger from "../core/model/ObservedInteger.js";
70
70
  import { ObservedIntegerEditor } from "./ecs/component/editors/ObservedIntegerEditor.js";
71
71
 
72
+ import '../../../../css/editor/EntityEditorView.scss';
73
+ import '../../../../css/editor/EditorView.scss';
72
74
 
73
75
  /**
74
76
  * @template T
@@ -217,7 +217,7 @@ export class BlenderCameraOrientationGizmo extends CanvasView {
217
217
  *
218
218
  * @type {Vector3|null}
219
219
  */
220
- this.mouse = null;
220
+ this.input_pointer_position = null;
221
221
 
222
222
  this.center = new Vector3(this.options.size / 2, this.options.size / 2, 0);
223
223
  this.selectedAxis = null;
@@ -240,16 +240,18 @@ export class BlenderCameraOrientationGizmo extends CanvasView {
240
240
  super.link();
241
241
 
242
242
  const el = this.el;
243
- el.addEventListener('mousemove', this.onMouseMove, false);
244
- el.addEventListener('mouseout', this.onMouseOut, false);
245
- el.addEventListener('click', this.onMouseClick, false);
243
+
244
+ el.addEventListener('mousemove', this.onMouseMove);
245
+ el.addEventListener('mouseout', this.onMouseOut);
246
+ el.addEventListener('click', this.onMouseClick);
246
247
  }
247
248
 
248
249
  unlink() {
249
250
  const el = this.el;
250
- el.removeEventListener('mousemove', this.onMouseMove, false);
251
- el.removeEventListener('mouseout', this.onMouseOut, false);
252
- el.removeEventListener('click', this.onMouseClick, false);
251
+
252
+ el.removeEventListener('mousemove', this.onMouseMove);
253
+ el.removeEventListener('mouseout', this.onMouseOut);
254
+ el.removeEventListener('click', this.onMouseClick);
253
255
 
254
256
  super.unlink();
255
257
  }
@@ -259,10 +261,19 @@ export class BlenderCameraOrientationGizmo extends CanvasView {
259
261
  * @param {MouseEvent} evt
260
262
  */
261
263
  onMouseMove(evt) {
264
+ this._setPointerPositionFromEvent(evt);
265
+ }
266
+
267
+ /**
268
+ *
269
+ * @param {MouseEvent} evt
270
+ * @private
271
+ */
272
+ _setPointerPositionFromEvent(evt) {
262
273
  const v2 = new Vector2();
263
274
  readPositionFromMouseEvent(v2, evt);
264
275
 
265
- this.mouse = new Vector3(v2.x, v2.y, 0);
276
+ this.input_pointer_position = new Vector3(v2.x, v2.y, 0);
266
277
  }
267
278
 
268
279
  /**
@@ -270,7 +281,7 @@ export class BlenderCameraOrientationGizmo extends CanvasView {
270
281
  * @param {MouseEvent} evt
271
282
  */
272
283
  onMouseOut(evt) {
273
- this.mouse = null;
284
+ this.input_pointer_position = null;
274
285
  }
275
286
 
276
287
  /**
@@ -362,13 +373,13 @@ export class BlenderCameraOrientationGizmo extends CanvasView {
362
373
  // If the mouse is over the gizmo, find the closest axis and highlight it
363
374
  this.selectedAxis = null;
364
375
 
365
- if (this.mouse) {
376
+ if (this.input_pointer_position) {
366
377
  let closestDist = Infinity;
367
378
  let closestZ = -Infinity;
368
379
 
369
380
  // Loop through each layer
370
381
  for (let bubble of layers) {
371
- const distance = v2_distance(this.mouse.x, this.mouse.y, bubble.position.x, bubble.position.y);
382
+ const distance = v2_distance(this.input_pointer_position.x, this.input_pointer_position.y, bubble.position.x, bubble.position.y);
372
383
 
373
384
  // Only select the axis if its closer to the mouse than the previous or if its within its bubble circle
374
385
  if (
@@ -27,6 +27,8 @@ import { JsonAssetLoader } from "./asset/loaders/JsonAssetLoader.js";
27
27
  import { logger } from "./logging/GlobalLogger.js";
28
28
  import { ConsoleLoggerBackend } from "./logging/ConsoleLoggerBackend.js";
29
29
  import { noop } from "../core/function/Functions.js";
30
+ import SoundListenerSystem from "./sound/ecs/SoundListenerSystem.js";
31
+ import SoundListener from "./sound/ecs/SoundListener.js";
30
32
 
31
33
  /**
32
34
  *
@@ -174,6 +176,10 @@ export class EngineHarness {
174
176
  em.addSystem(new CameraSystem(engine.graphics.scene, engine.graphics))
175
177
  }
176
178
 
179
+ if (em.getSystem(SoundListenerSystem) === null) {
180
+ em.addSystem(new SoundListenerSystem(engine.sound.context));
181
+ }
182
+
177
183
  if (!ecd.isComponentTypeRegistered(Tag)) {
178
184
  ecd.registerComponentType(Tag);
179
185
  }
@@ -199,6 +205,7 @@ export class EngineHarness {
199
205
  .add(transform)
200
206
  .add(cameraController)
201
207
  .add(camera)
208
+ .add(new SoundListener())
202
209
  .add(Tag.fromJSON(["Camera"]))
203
210
  .build(ecd);
204
211
 
@@ -1,3 +1,5 @@
1
+ import { computeStridedIntegerArrayHash } from "../../../../computeStridedIntegerArrayHash.js";
2
+
1
3
  /**
2
4
  *
3
5
  * @param {THREE.BufferAttribute} attribute
@@ -15,14 +17,11 @@ export function computeBufferAttributeHash(attribute) {
15
17
  // compute stride so that we don't have to iterate over the entire buffer, instead picking at most X(509) values to consider
16
18
  const stride = Math.max(1, Math.ceil(index_data_size / 509));
17
19
 
18
- for (let i = 0; i < index_data_size; i += stride) {
19
- const value = index_data[i];
20
-
21
- result = ((result * 31) + value) | 0;
22
-
23
- }
20
+ result ^= computeStridedIntegerArrayHash(index_data, 0, index_data_size, stride);
24
21
 
25
22
  }
26
23
 
27
24
  return result;
28
25
  }
26
+
27
+
@@ -7,4 +7,13 @@ export class Codec {
7
7
  async decode(data) {
8
8
  throw new Error('Unsupported Operation');
9
9
  }
10
+
11
+ /**
12
+ * Whether codec is able to decode this data, typically checks headers in the data
13
+ * @param {Uint8Array} data
14
+ * @return {Promise<boolean>} true if able, false otherwise
15
+ */
16
+ async test(data) {
17
+ return true;
18
+ }
10
19
  }
@@ -1,28 +1,69 @@
1
1
  import { Codec } from "./Codec.js";
2
- import { logger } from "../../../../logging/GlobalLogger.js";
3
2
 
4
3
  export class CodecWithFallback extends Codec {
5
4
  /**
6
5
  *
7
- * @param {Codec} main
8
- * @param {Codec} fallback
6
+ * @param {Codec} codecs
9
7
  */
10
- constructor(main, fallback) {
8
+ constructor(...codecs) {
11
9
  super();
12
10
 
13
- this.main = main;
14
- this.fallback = fallback;
11
+ this.children = codecs.slice();
12
+
13
+ if (this.children.length < 1) {
14
+ throw new Error('At least one codec must be provided');
15
+ }
16
+ }
17
+
18
+ async test(data) {
19
+ const codecs = this.children;
20
+ const codec_count = codecs.length;
21
+
22
+ for (let i = 0; i < codec_count; i++) {
23
+ const codec = codecs[i];
24
+
25
+ if (codec.test(data)) {
26
+ return true;
27
+ }
28
+ }
29
+
30
+ return false;
15
31
  }
16
32
 
17
33
  async decode(data) {
18
34
 
19
- try {
20
- return await this.main.decode(data);
21
- } catch (e) {
22
- logger.warn(`Main codec failed, will attempt fallback. Error: ${e}`);
35
+ const codecs = this.children;
36
+ const codec_count = codecs.length;
23
37
 
24
- return this.fallback.decode(data);
38
+ const errors = [];
39
+
40
+ let result;
41
+
42
+ for (let i = 0; i < codec_count; i++) {
43
+ const codec = codecs[i];
44
+
45
+ if (!codec.test(data)) {
46
+ errors.push({
47
+ index: i, error: "Codec doesn't support this data"
48
+ });
49
+ continue;
50
+ }
51
+
52
+ try {
53
+ result = await codec.decode(data);
54
+ } catch (e) {
55
+
56
+ errors.push({
57
+ index: i, error: e
58
+ });
59
+
60
+ continue;
61
+ }
62
+
63
+ return result;
25
64
  }
26
65
 
66
+ throw new Error(`All codecs failed, errors: ${errors.map(a => JSON.stringify(a)).join('\n')}`);
67
+
27
68
  }
28
69
  }
@@ -1,6 +1,7 @@
1
1
  import { Codec } from "./Codec.js";
2
2
  import WorkerBuilder from "../../../../../core/process/worker/WorkerBuilder.js";
3
3
  import { OnDemandWorkerManager } from "../../../../../core/process/worker/OnDemandWorkerManager.js";
4
+ import { PNG_HEADER_BYTES } from "../png/PNG_HEADER_BYTES.js";
4
5
 
5
6
  export class ThreadedImageDecoder extends Codec {
6
7
  constructor() {
@@ -23,6 +24,17 @@ export class ThreadedImageDecoder extends Codec {
23
24
  this.worker.setTimeout(200);
24
25
  }
25
26
 
27
+ async test(data) {
28
+
29
+ for (let i = 0; i < PNG_HEADER_BYTES.length; i++) {
30
+ if (data[i] !== PNG_HEADER_BYTES[i]) {
31
+ return false;
32
+ }
33
+ }
34
+
35
+ return true;
36
+ }
37
+
26
38
  async decode(data) {
27
39
  const result = await this.worker.request('decode', [data.buffer]);
28
40
  return result;