@woosh/meep-engine 2.39.39 → 2.39.42

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 (62) hide show
  1. package/core/binary/operations/ceilPowerOfTwo.spec.js +17 -0
  2. package/core/bvh2/aabb3/AABB3.js +6 -6
  3. package/core/bvh2/binary/2/BinaryUint32BVH.js +7 -1
  4. package/core/bvh2/binary/2/BinaryUint32BVH.spec.js +31 -1
  5. package/core/bvh2/bvh3/bvh_query_user_data_nearest_to_point.js +5 -5
  6. package/core/bvh2/bvh3/bvh_query_user_data_nearest_to_point.spec.js +70 -0
  7. package/core/bvh2/bvh3/query/compute_tight_near_far_clipping_planes.spec.js +2 -2
  8. package/core/cache/Cache.js +6 -0
  9. package/core/collection/IteratorUtils.js +1 -0
  10. package/core/collection/queue/Deque.js +5 -2
  11. package/core/color/Color.js +22 -4
  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/primitives/strings/compareStrings.js +22 -0
  16. package/editor/Editor.js +2 -0
  17. package/editor/ecs/component/editors/ecs/terrain/TerrainEditor.js +12 -6
  18. package/editor/tools/v2/BlenderCameraOrientationGizmo.js +22 -11
  19. package/engine/EngineHarness.js +7 -0
  20. package/engine/asset/loaders/geometry/geometry/computeBufferAttributeHash.js +5 -6
  21. package/engine/asset/loaders/image/codec/Codec.js +9 -0
  22. package/engine/asset/loaders/image/codec/CodecWithFallback.js +52 -11
  23. package/engine/asset/loaders/image/codec/ThreadedImageDecoder.js +12 -0
  24. package/engine/asset/loaders/image/png/PNGReader.js +2 -1
  25. package/engine/asset/loaders/image/png/PNG_HEADER_BYTES.js +1 -0
  26. package/engine/computeStridedIntegerArrayHash.js +19 -0
  27. package/engine/ecs/EntityBuilder.d.ts +2 -0
  28. package/engine/ecs/gui/GUIElement.d.ts +3 -1
  29. package/engine/ecs/terrain/ecs/layers/TerrainLayers.js +5 -3
  30. package/engine/graphics/ecs/light/binding/fp/FPLightBinding.js +11 -2
  31. package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +2 -8
  32. package/engine/graphics/geometry/buffered/query/GeometrySpatialQueryAccelerator.d.ts +7 -0
  33. package/engine/graphics/geometry/buffered/query/GeometrySpatialQueryAccelerator.js +17 -1
  34. package/engine/graphics/geometry/optimization/merge/merge_geometry_hierarchy.js +336 -0
  35. package/engine/graphics/geometry/optimization/merge/prototypeGeometryMerge.js +176 -0
  36. package/engine/graphics/micron/build/hierarchy/buildAbstractPatchHierarchy.js +7 -3
  37. package/engine/graphics/micron/build/hierarchy/merge_patches.js +0 -1
  38. package/engine/graphics/micron/prototypeVirtualGeometry.js +3 -3
  39. package/engine/graphics/particles/particular/engine/parameter/ParameterLookupTable.js +32 -1
  40. package/engine/graphics/render/forward_plus/LightManager.js +58 -13
  41. package/engine/graphics/render/forward_plus/debug/createScreenGrid.js +192 -9
  42. package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_ACCUMULATION.js +42 -1
  43. package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_PREAMBLE.js +1 -0
  44. package/engine/graphics/render/forward_plus/materials/ForwardPlusThreeMaterial.js +5 -0
  45. package/engine/graphics/render/forward_plus/materials/fp_build_fragment_shader.js +1 -4
  46. package/engine/graphics/render/forward_plus/model/Decal.js +33 -5
  47. package/engine/graphics/render/forward_plus/model/PointLight.js +1 -1
  48. package/engine/graphics/render/forward_plus/plugin/MaterialTransformer.js +4 -0
  49. package/engine/graphics/render/forward_plus/prototype/prototypeLightManager.js +480 -12
  50. package/engine/graphics/render/forward_plus/query/query_bvh_frustum_from_texture.js +49 -29
  51. package/engine/graphics/texture/atlas/AbstractTextureAtlas.js +11 -0
  52. package/engine/graphics/texture/atlas/CachingTextureAtlas.js +34 -0
  53. package/engine/graphics/texture/atlas/ReferencedTextureAtlas.js +9 -1
  54. package/engine/graphics/texture/atlas/TextureAtlas.js +29 -16
  55. package/engine/graphics/texture/atlas/TextureAtlasDebugger.js +75 -19
  56. package/engine/graphics/texture/sampler/Sampler2D.d.ts +2 -0
  57. package/engine/graphics/texture/sampler/Sampler2D.js +51 -10
  58. package/engine/sound/ecs/SoundListenerSystem.js +5 -0
  59. package/engine/sound/ecs/emitter/SoundEmitter.js +12 -1
  60. package/package.json +1 -1
  61. package/samples/terrain/from_image_2.js +8 -2
  62. 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
+ });
@@ -553,12 +553,12 @@ export class AABB3 {
553
553
 
554
554
  /**
555
555
  * @source http://stackoverflow.com/questions/3106666/intersection-of-line-segment-with-axis-aligned-box-in-c-sharp
556
- * @param startX
557
- * @param startY
558
- * @param startZ
559
- * @param endX
560
- * @param endY
561
- * @param endZ
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
562
  * @returns {boolean}
563
563
  */
564
564
  intersectSegment2(startX, startY, startZ, endX, endY, endZ) {
@@ -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);
@@ -41,7 +41,7 @@ export function bvh_query_user_data_nearest_to_point(bvh, x, y, z, max_distance
41
41
  traversal_stack[traversal_cursor++] = root;
42
42
 
43
43
  let best_leaf_data = -1;
44
- let best_distance = max_distance;
44
+ let best_distance_sqr = max_distance * max_distance;
45
45
 
46
46
  while (traversal_cursor > stack_frame_address) {
47
47
  traversal_cursor--;
@@ -55,13 +55,13 @@ export function bvh_query_user_data_nearest_to_point(bvh, x, y, z, max_distance
55
55
  bvh.node_get_aabb(node, scratch_aabb);
56
56
 
57
57
  // compute distance to box
58
- const distance = max2(0, aabb3_signed_distance_sqr_to_point(
58
+ const distance_sqr = max2(0, aabb3_signed_distance_sqr_to_point(
59
59
  scratch_aabb[0], scratch_aabb[1], scratch_aabb[2],
60
60
  scratch_aabb[3], scratch_aabb[4], scratch_aabb[5],
61
61
  x, y, z
62
62
  ));
63
63
 
64
- if (distance > best_distance) {
64
+ if (distance_sqr > best_distance_sqr) {
65
65
  // node is too far
66
66
  continue;
67
67
  }
@@ -81,8 +81,8 @@ export function bvh_query_user_data_nearest_to_point(bvh, x, y, z, max_distance
81
81
  } else {
82
82
 
83
83
  // leaf node
84
- if (distance < best_distance) {
85
- best_distance = distance;
84
+ if (distance_sqr < best_distance_sqr) {
85
+ best_distance_sqr = distance_sqr;
86
86
  best_leaf_data = bvh.node_get_user_data(node);
87
87
  }
88
88
 
@@ -0,0 +1,70 @@
1
+ import { ExplicitBinaryBoundingVolumeHierarchy } from "./ExplicitBinaryBoundingVolumeHierarchy.js";
2
+ import { bvh_query_user_data_nearest_to_point } from "./bvh_query_user_data_nearest_to_point.js";
3
+
4
+ /**
5
+ *
6
+ * @param {number[]} boxes
7
+ * @return {ExplicitBinaryBoundingVolumeHierarchy}
8
+ */
9
+ function makeBvh(...boxes) {
10
+ const r = new ExplicitBinaryBoundingVolumeHierarchy();
11
+
12
+ for (let i = 0; i < boxes.length; i++) {
13
+ const box = boxes[i];
14
+
15
+ const node = r.allocate_node();
16
+
17
+ r.node_set_aabb(node, box);
18
+ r.node_set_user_data(node, i);
19
+
20
+ r.insert_leaf(node);
21
+ }
22
+
23
+ return r;
24
+ }
25
+
26
+ test('no data, infinite distance', () => {
27
+ const bvh = makeBvh();
28
+
29
+ const result = bvh_query_user_data_nearest_to_point(bvh, 0, 0, 0, Infinity);
30
+
31
+ expect(result).toBe(-1);
32
+ });
33
+
34
+ test('single point, distance too short', () => {
35
+ const bvh = makeBvh([1, 0, 0, 1, 0, 0]);
36
+
37
+ const result = bvh_query_user_data_nearest_to_point(bvh, 0, 0, 0, 0.5);
38
+
39
+ expect(result).toBe(-1);
40
+ });
41
+
42
+ test('single point, distance sufficient', () => {
43
+ const bvh = makeBvh([1, 0, 0, 1, 0, 0]);
44
+
45
+ const result = bvh_query_user_data_nearest_to_point(bvh, 0, 0, 0, 1.5);
46
+
47
+ expect(result).not.toBe(-1);
48
+ });
49
+
50
+ test('two point, distance sufficient to one', () => {
51
+ const bvh = makeBvh(
52
+ [1, 0, 0, 1, 0, 0],
53
+ [2, 0, 0, 2, 0, 0],
54
+ );
55
+
56
+ const result = bvh_query_user_data_nearest_to_point(bvh, 0, 0, 0, 1.5);
57
+
58
+ expect(result).toBe(0);
59
+ });
60
+
61
+ test('two point, distance sufficient to both', () => {
62
+ const bvh = makeBvh(
63
+ [1, 0, 0, 1, 0, 0],
64
+ [2, 0, 0, 2, 0, 0],
65
+ );
66
+
67
+ const result = bvh_query_user_data_nearest_to_point(bvh, 0, 0, 0, 2.5);
68
+
69
+ expect(result).toBe(0);
70
+ });
@@ -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();
@@ -264,6 +264,12 @@ export class Cache {
264
264
  */
265
265
  const weightTarget = this.maxWeight - elementWeight;
266
266
 
267
+ if (weightTarget < 0) {
268
+ // Special case
269
+ // element does not fit into cache, attempting to insert it forcibly would result in a full flush and overflow
270
+ return;
271
+ }
272
+
267
273
  //evict elements until there is enough space for the element
268
274
  this.evictUntilWeight(weightTarget);
269
275
 
@@ -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
@@ -27,20 +27,26 @@ export class TerrainEditor extends ObjectEditor {
27
27
 
28
28
  // splats
29
29
  view.bindSignal(terrain.layers.layers.on.added, () => {
30
- terrain.splat.addWeightLayer();
31
- updateLayers();
30
+ if (terrain.getFlag(TerrainFlags.Built)) {
31
+ terrain.splat.addWeightLayer();
32
+ updateLayers();
33
+ }
32
34
  });
33
35
  view.bindSignal(terrain.layers.layers.on.removed, (a, index) => {
34
- terrain.splat.removeWeightLayer(index);
36
+ if (terrain.getFlag(TerrainFlags.Built)) {
37
+ terrain.splat.removeWeightLayer(index);
35
38
 
36
- updateLayers();
39
+ updateLayers();
40
+ }
37
41
  });
38
42
 
39
43
  // mapping sizes
40
44
  view.bindSignal(terrain.splat.size.onChanged, (x, y) => {
41
- terrain.splat.resize(x, y, terrain.splat.depth);
45
+ if (terrain.getFlag(TerrainFlags.Built)) {
46
+ terrain.splat.resize(x, y, terrain.splat.depth);
42
47
 
43
- terrain.updateMaterial();
48
+ terrain.updateMaterial();
49
+ }
44
50
  });
45
51
  }
46
52
 
@@ -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 (