@woosh/meep-engine 2.33.25 → 2.33.29

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 (65) hide show
  1. package/core/binary/BitSet.js +151 -167
  2. package/core/binary/BitSet.spec.js +99 -1
  3. package/core/binary/ValidatingBitSetWrapper.js +81 -0
  4. package/core/bvh2/aabb3/AABB3.js +3 -10
  5. package/core/cache/Cache.d.ts +4 -2
  6. package/core/cache/Cache.js +5 -2
  7. package/core/collection/HashMap.d.ts +9 -2
  8. package/core/collection/HashSet.d.ts +7 -2
  9. package/core/collection/HashSet.js +7 -2
  10. package/core/collection/array/typed/typed_array_copy.js +1 -1
  11. package/core/geom/3d/frustum/array_setFromProjectionMatrix.js +3 -3
  12. package/core/geom/3d/shape/AbstractShape3D.js +28 -1
  13. package/core/geom/3d/shape/TransformedShape3D.js +65 -10
  14. package/core/geom/3d/shape/TransformedShape3D.spec.js +33 -0
  15. package/core/geom/3d/shape/{CompositeShape.js → UnionShape3D.js} +27 -12
  16. package/core/geom/3d/shape/UnitCubeShape3D.js +77 -0
  17. package/core/geom/3d/shape/UnitCubeShape3D.spec.js +42 -0
  18. package/core/geom/3d/shape/UnitSphereShape3D.js +32 -1
  19. package/core/geom/3d/shape/util/compute_signed_distance_gradient_by_sampling.js +48 -0
  20. package/core/geom/3d/shape/util/shape_to_visual_entity.js +3 -3
  21. package/core/model/ObservedBoolean.d.ts +2 -0
  22. package/core/parser/simple/Token.d.ts +13 -0
  23. package/core/parser/simple/Token.js +61 -27
  24. package/editor/tools/FoliagePaintTool.js +4 -4
  25. package/engine/asset/loaders/GLTFAssetLoader.js +52 -1
  26. package/engine/asset/loaders/image/ImageDecoderWorker.js +4 -3
  27. package/engine/asset/loaders/image/ImageRGBADataAsset.js +3 -2
  28. package/engine/asset/loaders/image/ImageRGBADataLoader.js +3 -3
  29. package/engine/asset/loaders/image/codec/NativeImageDecoder.js +2 -1
  30. package/engine/asset/loaders/image/png/PNG.js +113 -6
  31. package/engine/asset/loaders/image/png/PNGReader.js +5 -0
  32. package/engine/asset/loaders/material/bitmap2sampler_gl.js +1 -1
  33. package/engine/asset/loaders/material/computeImageBitmapEquality.js +6 -1
  34. package/engine/asset/loaders/material/computeTextureEquality.js +40 -49
  35. package/engine/ecs/EntityComponentDataset.js +1 -1
  36. package/engine/ecs/foliage/InstancedFoliage.js +277 -266
  37. package/engine/ecs/foliage/ecs/Foliage2System.js +7 -7
  38. package/engine/ecs/foliage/ecs/InstancedMeshComponent.js +37 -0
  39. package/engine/ecs/foliage/ecs/{Foliage2.js → InstancedMeshLayer.js} +3 -45
  40. package/engine/ecs/foliage/ecs/InstancedMeshSerializationAdapter.js +6 -5
  41. package/engine/ecs/foliage/ecs/InstancedMeshUtils.js +7 -6
  42. package/engine/ecs/storage/binary/BinarySerializationRegistry.js +1 -1
  43. package/engine/ecs/terrain/ecs/layers/TerrainLayer.js +12 -16
  44. package/engine/graphics/ecs/highlight/plugin/OutlineRenderPlugin.js +2 -2
  45. package/engine/graphics/geometry/instancing/InstancedMeshGroup.js +14 -72
  46. package/engine/graphics/geometry/instancing/rewriteMaterial.js +70 -0
  47. package/engine/graphics/material/manager/MaterialManager.js +2 -1
  48. package/engine/graphics/micron/render/instanced/AttributeDataTexture.js +6 -4
  49. package/engine/graphics/micron/render/instanced/ThreeInstancedAdapter.js +0 -2
  50. package/engine/graphics/particles/particular/engine/shader/ShaderManager.js +1 -2
  51. package/engine/graphics/particles/particular/engine/shader/debugAtlas.js +1 -1
  52. package/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +12 -9
  53. package/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js +3 -4
  54. package/engine/graphics/render/forward_plus/materials/FPlusDebugMaterial.js +3 -3
  55. package/engine/graphics/render/forward_plus/materials/ForwardPlusThreeMaterial.js +1 -1
  56. package/engine/graphics/render/forward_plus/prototypeLightManager.js +4 -4
  57. package/engine/graphics/render/forward_plus/query/point_light_inside_volume.js +10 -9
  58. package/engine/graphics/shadows/ShadowMapRenderer.js +8 -2
  59. package/engine/graphics/texture/atlas/ManagedTextureAtlas.js +12 -1
  60. package/engine/graphics/texture/sampler/Sampler2D.js +3 -1
  61. package/engine/graphics/texture/sampler/convertTexture2Sampler2D.js +1 -1
  62. package/engine/graphics/texture/sampler/copy_Sampler2D_channel_data.js +66 -0
  63. package/engine/input/devices/KeyCodes.d.ts +1 -0
  64. package/package.json +1 -1
  65. package/view/minimap/gl/MinimapWorldGL.js +12 -5
@@ -0,0 +1,81 @@
1
+ //
2
+
3
+ /**
4
+ * Debug tool to verify correct behavior of a BitSet
5
+ */
6
+ export class ValidatingBitSetWrapper {
7
+ /**
8
+ *
9
+ * @param {BitSet} source
10
+ */
11
+ constructor(source) {
12
+ this.__source = source;
13
+ }
14
+
15
+ preventShrink() {
16
+ this.__source.preventShrink();
17
+ }
18
+
19
+ setShrinkFactor(x) {
20
+ this.__source.setShrinkFactor(x);
21
+ }
22
+
23
+ setCapacity(x) {
24
+ this.__source.setCapacity(x);
25
+ }
26
+
27
+ size() {
28
+ return this.__source.size();
29
+ }
30
+
31
+ capacity() {
32
+ return this.__source.capacity();
33
+ }
34
+
35
+ previousSetBit(i) {
36
+ const bit = this.__source.previousSetBit(i);
37
+
38
+ if (bit !== -1 && (this.__source.get(bit) !== true || bit > i)) {
39
+ debugger;
40
+ }
41
+
42
+ return bit;
43
+ }
44
+
45
+ nextSetBit(i) {
46
+ const bit = this.__source.nextSetBit(i);
47
+
48
+ if (bit !== -1 && (this.__source.get(bit) !== true || bit < i)) {
49
+ debugger;
50
+ }
51
+
52
+ return bit;
53
+ }
54
+
55
+ nextClearBit(i) {
56
+ const bit = this.__source.nextClearBit(i);
57
+
58
+ if (bit !== -1 && (this.__source.get(bit) !== false || bit < i)) {
59
+ debugger;
60
+ }
61
+
62
+ return bit;
63
+ }
64
+
65
+ set(x, y) {
66
+ this.__source.set(x, y);
67
+ }
68
+
69
+ clear(i) {
70
+ this.__source.clear(i);
71
+ }
72
+
73
+ get(i) {
74
+ return this.__source.get(i);
75
+ }
76
+
77
+
78
+ reset() {
79
+ this.__source.reset();
80
+ }
81
+ }
@@ -761,16 +761,9 @@ AABB3.prototype.computePlaneSide = function computePlaneSide(plane) {
761
761
  const normal = plane.normal;
762
762
 
763
763
  return computeAABB3PlaneSide(
764
- normal.x,
765
- normal.y,
766
- normal.z,
767
- plane.constant,
768
- this.x0,
769
- this.y0,
770
- this.z0,
771
- this.x1,
772
- this.y1,
773
- this.z1
764
+ normal.x, normal.y, normal.z, plane.constant,
765
+ this.x0, this.y0, this.z0,
766
+ this.x1, this.y1, this.z1
774
767
  );
775
768
  };
776
769
 
@@ -4,13 +4,15 @@ export class Cache<K, V> {
4
4
  keyWeigher,
5
5
  valueWeigher,
6
6
  keyHashFunction,
7
- keyEqualityFunction
7
+ keyEqualityFunction,
8
+ capacity
8
9
  }?: {
9
10
  maxWeight?: number,
10
11
  keyWeigher?: (key: K) => number,
11
12
  valueWeigher?: (value: V) => number,
12
13
  keyHashFunction?: (key: K) => number,
13
- keyEqualityFunction?: (a: K, b: K) => boolean
14
+ keyEqualityFunction?: (a: K, b: K) => boolean,
15
+ capacity?: number
14
16
  })
15
17
 
16
18
  size(): number
@@ -16,6 +16,7 @@ export class Cache {
16
16
  * @param {function(key:Key, value:Value)} [removeListener] will be notified when element is either explicitly removed or evicted
17
17
  * @param {function(Key):number} [keyHashFunction]
18
18
  * @param {function(Key, Key):boolean} [keyEqualityFunction]
19
+ * @param {number} capacity initial capacity for the cache, helps prevent early resizing
19
20
  * @constructor
20
21
  * @template Key, Value
21
22
  */
@@ -25,7 +26,8 @@ export class Cache {
25
26
  valueWeigher = returnOne,
26
27
  removeListener = noop,
27
28
  keyHashFunction = returnZero,
28
- keyEqualityFunction = strictEquals
29
+ keyEqualityFunction = strictEquals,
30
+ capacity
29
31
  } = {}) {
30
32
 
31
33
  assert.isNumber(maxWeight, 'maxWeight');
@@ -95,7 +97,8 @@ export class Cache {
95
97
  */
96
98
  this.data = new HashMap({
97
99
  keyHashFunction,
98
- keyEqualityFunction
100
+ keyEqualityFunction,
101
+ capacity
99
102
  });
100
103
 
101
104
  /**
@@ -1,7 +1,14 @@
1
1
  export class HashMap<K, V> implements Iterable<[V, K]> {
2
- constructor({keyHashFunction, keyEqualityFunction}: {
2
+ constructor({
3
+ keyHashFunction,
4
+ keyEqualityFunction,
5
+ capacity,
6
+ loadFactor
7
+ }: {
3
8
  keyHashFunction: (k: K) => number,
4
- keyEqualityFunction: (a: K, b: K) => boolean
9
+ keyEqualityFunction: (a: K, b: K) => boolean,
10
+ capacity?: number,
11
+ loadFactor?: number
5
12
  })
6
13
 
7
14
  constructor()
@@ -1,7 +1,12 @@
1
1
  export class HashSet<T> {
2
- constructor({keyHashFunction, keyEqualityFunction}: {
2
+ constructor({
3
+ keyHashFunction,
4
+ keyEqualityFunction,
5
+ capacity
6
+ }: {
3
7
  keyHashFunction: (k: T) => number,
4
- keyEqualityFunction: (a: T, b: T) => boolean
8
+ keyEqualityFunction: (a: T, b: T) => boolean,
9
+ capacity?: number
5
10
  })
6
11
 
7
12
  constructor()
@@ -11,7 +11,11 @@ export class HashSet {
11
11
  /**
12
12
  * @template T
13
13
  */
14
- constructor({ keyHashFunction = invokeObjectHash, keyEqualityFunction = invokeObjectEquals } = {}) {
14
+ constructor({
15
+ keyHashFunction = invokeObjectHash,
16
+ keyEqualityFunction = invokeObjectEquals,
17
+ capacity
18
+ } = {}) {
15
19
 
16
20
  /**
17
21
  * Using HashMap as a backing, this is not the most efficient approach, but it works
@@ -20,7 +24,8 @@ export class HashSet {
20
24
  */
21
25
  this.__map = new HashMap({
22
26
  keyHashFunction,
23
- keyEqualityFunction
27
+ keyEqualityFunction,
28
+ capacity
24
29
  });
25
30
  }
26
31
 
@@ -2,7 +2,7 @@ import { array_copy } from "../copyArray.js";
2
2
 
3
3
 
4
4
  /**
5
- *
5
+ * Assumes arrays have the same type
6
6
  * @param {TypedArray|Float32Array} source
7
7
  * @param {TypedArray|Float32Array} destination
8
8
  */
@@ -1,5 +1,3 @@
1
- import { v3_length } from "../../v3_length.js";
2
-
3
1
  /**
4
2
  *
5
3
  * @param {number[]} destination
@@ -10,10 +8,12 @@ import { v3_length } from "../../v3_length.js";
10
8
  * @param {number} w
11
9
  */
12
10
  function array_set_plane_normalized(destination, destination_offset, x, y, z, w) {
13
- const length = v3_length(x, y, z);
11
+ // compute vector length
12
+ const length = Math.hypot(x, y, z);
14
13
 
15
14
  const inverseNormalLength = 1.0 / length;
16
15
 
16
+ // apply normalization
17
17
  destination[destination_offset] = x * inverseNormalLength;
18
18
  destination[destination_offset + 1] = y * inverseNormalLength;
19
19
  destination[destination_offset + 2] = z * inverseNormalLength;
@@ -1,5 +1,31 @@
1
1
  export class AbstractShape3D {
2
- constructor() {
2
+ /**
3
+ * Signed distance to boundary of the shape from the given point
4
+ * @param {number[]} point
5
+ * @returns {number}
6
+ */
7
+ signed_distance_at_point(point) {
8
+ throw new Error('Not Implemented');
9
+ }
10
+
11
+ /**
12
+ * Samples both distances as well as the gradient
13
+ * @param {number[]} result resulting gradient is written here
14
+ * @param {number[]} point sampling point
15
+ * @returns {number}
16
+ */
17
+ signed_distance_gradient_at_point(result, point) {
18
+ throw new Error('Not Implemented');
19
+ }
20
+
21
+ /**
22
+ *
23
+ * @param {number[]} result where the resulting point is stored
24
+ * @param {number[]} reference reference point from which to go
25
+ * @returns {void}
26
+ */
27
+ nearest_point_on_surface(result, reference) {
28
+ throw new Error('Not Implemented');
3
29
  }
4
30
 
5
31
  /**
@@ -7,6 +33,7 @@ export class AbstractShape3D {
7
33
  * @param {number[]} result
8
34
  * @param {number} result_offset
9
35
  * @param {function} random
36
+ * @returns {void}
10
37
  */
11
38
  sample_random_point_in_volume(result, result_offset, random) {
12
39
  throw new Error('Not Implemented');
@@ -1,11 +1,12 @@
1
1
  import { mat4, vec3 } from "gl-matrix";
2
2
  import { AbstractShape3D } from "./AbstractShape3D.js";
3
+ import { sign } from "../../../math/sign.js";
3
4
 
4
5
  /**
5
6
  *
6
7
  * @type {number[]|Float32Array|vec3}
7
8
  */
8
- const scratch_v3 = [];
9
+ const scratch_v3_0 = [];
9
10
 
10
11
  export class TransformedShape3D extends AbstractShape3D {
11
12
  constructor() {
@@ -98,29 +99,83 @@ export class TransformedShape3D extends AbstractShape3D {
98
99
  }
99
100
 
100
101
  get volume() {
101
- mat4.getScaling(scratch_v3, this.__matrix);
102
+ mat4.getScaling(scratch_v3_0, this.__matrix);
102
103
 
103
- const scale_modifier = scratch_v3[0] * scratch_v3[1] * scratch_v3[2];
104
+ const scale_modifier = scratch_v3_0[0] * scratch_v3_0[1] * scratch_v3_0[2];
104
105
 
105
106
  return this.__subject.volume * scale_modifier;
106
107
  }
107
108
 
109
+ signed_distance_gradient_at_point(result, point) {
110
+ // transform point to subject's local space
111
+ vec3.transformMat4(scratch_v3_0, point, this.__inverse_matrix);
112
+
113
+ const subject = this.__subject;
114
+
115
+ // sample gradient at the point
116
+ const distance = subject.signed_distance_gradient_at_point(scratch_v3_0, scratch_v3_0);
117
+
118
+ const m = this.__matrix;
119
+
120
+ const m11 = m[0];
121
+ const m12 = m[1];
122
+ const m13 = m[2];
123
+
124
+ const m21 = m[4];
125
+ const m22 = m[5];
126
+ const m23 = m[6];
127
+
128
+ const m31 = m[8];
129
+ const m32 = m[9];
130
+ const m33 = m[10];
131
+
132
+ const scale_x = Math.hypot(m11, m12, m13);
133
+ const scale_y = Math.hypot(m21, m22, m23);
134
+ const scale_z = Math.hypot(m31, m32, m33);
135
+
136
+ const scaled_gx = scratch_v3_0[0] * scale_x;
137
+ const scaled_gy = scratch_v3_0[1] * scale_y;
138
+ const scaled_gz = scratch_v3_0[2] * scale_z;
139
+
140
+ // normalize gradient
141
+ const gd = distance / Math.hypot(scratch_v3_0[0], scratch_v3_0[1], scratch_v3_0[2]);
142
+
143
+ const scaled_dx = scaled_gx * gd ;
144
+ const scaled_dy = scaled_gy * gd ;
145
+ const scaled_dz = scaled_gz * gd ;
146
+
147
+ // determine sign
148
+ const result_sign = sign(distance);
149
+
150
+ const scaled_distance = result_sign * Math.sqrt(scaled_dx * scaled_dx + scaled_dy * scaled_dy + scaled_dz * scaled_dz);
151
+
152
+ result[0] = scaled_gx;
153
+ result[1] = scaled_gy;
154
+ result[2] = scaled_gz;
155
+
156
+ return scaled_distance;
157
+ }
158
+
159
+ signed_distance_at_point(point) {
160
+ return this.signed_distance_gradient_at_point(scratch_v3_0, point);
161
+ }
162
+
108
163
  contains_point(point) {
109
164
  // transform point to subject's local space
110
- vec3.transformMat4(scratch_v3, point, this.__inverse_matrix);
165
+ vec3.transformMat4(scratch_v3_0, point, this.__inverse_matrix);
111
166
 
112
- return this.__subject.contains_point(scratch_v3);
167
+ return this.__subject.contains_point(scratch_v3_0);
113
168
  }
114
169
 
115
170
  sample_random_point_in_volume(result, result_offset, random) {
116
- this.__subject.sample_random_point_in_volume(scratch_v3, 0, random);
171
+ this.__subject.sample_random_point_in_volume(scratch_v3_0, 0, random);
117
172
 
118
173
  // apply transform
119
- vec3.transformMat4(scratch_v3, scratch_v3, this.__matrix);
174
+ vec3.transformMat4(scratch_v3_0, scratch_v3_0, this.__matrix);
120
175
 
121
176
  // write out
122
- result[0] = scratch_v3[0];
123
- result[1] = scratch_v3[1];
124
- result[2] = scratch_v3[2];
177
+ result[0] = scratch_v3_0[0];
178
+ result[1] = scratch_v3_0[1];
179
+ result[2] = scratch_v3_0[2];
125
180
  }
126
181
  }
@@ -0,0 +1,33 @@
1
+ import { UnitCubeShape3D } from "./UnitCubeShape3D.js";
2
+ import { TransformedShape3D } from "./TransformedShape3D.js";
3
+
4
+ test('signed distance identity transform', () => {
5
+
6
+ const s = TransformedShape3D.from_translation_scale(new UnitCubeShape3D(), [0, 0, 0], [1, 1, 1]);
7
+
8
+ expect(s.signed_distance_at_point([0, 0, 0])).toBeCloseTo(-0.5);
9
+
10
+ expect(s.signed_distance_at_point([1, 0, 0])).toBeCloseTo(0.5);
11
+ });
12
+
13
+ test('signed distance translation', () => {
14
+
15
+ const s = TransformedShape3D.from_translation_scale(new UnitCubeShape3D(), [1, 0, 0], [1, 1, 1]);
16
+
17
+ expect(s.signed_distance_at_point([0, 0, 0])).toBeCloseTo(0.5);
18
+
19
+ expect(s.signed_distance_at_point([-1, 0, 0])).toBeCloseTo(1.5);
20
+
21
+ expect(s.signed_distance_at_point([1, 0, 0])).toBeCloseTo(-0.5);
22
+ });
23
+
24
+ test('signed distance scale', () => {
25
+
26
+ const s = TransformedShape3D.from_translation_scale(new UnitCubeShape3D(), [0, 0, 0], [2, 2, 2]);
27
+
28
+ expect(s.signed_distance_at_point([0, 0, 0])).toBeCloseTo(-1);
29
+
30
+ expect(s.signed_distance_at_point([1, 0, 0])).toBeCloseTo(0);
31
+
32
+ expect(s.signed_distance_at_point([2, 0, 0])).toBeCloseTo(1);
33
+ });
@@ -2,6 +2,8 @@ import { AbstractShape3D } from "./AbstractShape3D.js";
2
2
  import { seededRandom } from "../../../math/random/seededRandom.js";
3
3
  import { binarySearchHighIndex } from "../../../collection/array/binarySearchHighIndex.js";
4
4
  import { compareNumbers } from "../../../primitives/numbers/compareNumbers.js";
5
+ import { min2 } from "../../../math/min2.js";
6
+ import { compute_signed_distance_gradient_by_sampling } from "./util/compute_signed_distance_gradient_by_sampling.js";
5
7
 
6
8
  /**
7
9
  * To avoid severe performance overhead, we limit number of possible rejections
@@ -18,16 +20,7 @@ const VOLUME_OVERLAP_ESTIMATION_SUB_TAPS = 4;
18
20
 
19
21
  const scratch_array = [];
20
22
 
21
- /**
22
- *
23
- * @param {AbstractShape3D} shape
24
- * @return {number}
25
- */
26
- function getVolume(shape) {
27
- return shape.volume;
28
- }
29
-
30
- export class CompositeShape3D extends AbstractShape3D {
23
+ export class UnionShape3D extends AbstractShape3D {
31
24
  constructor() {
32
25
  super();
33
26
 
@@ -50,6 +43,10 @@ export class CompositeShape3D extends AbstractShape3D {
50
43
  this.__child_volumes = [];
51
44
  }
52
45
 
46
+ /**
47
+ *
48
+ * @private
49
+ */
53
50
  __compute_volume_variables() {
54
51
  this.__volume_sum = 0;
55
52
 
@@ -123,7 +120,7 @@ export class CompositeShape3D extends AbstractShape3D {
123
120
  }
124
121
 
125
122
  static from(children) {
126
- const r = new CompositeShape3D();
123
+ const r = new UnionShape3D();
127
124
 
128
125
  r.children = children;
129
126
 
@@ -169,6 +166,25 @@ export class CompositeShape3D extends AbstractShape3D {
169
166
  return r;
170
167
  }
171
168
 
169
+ signed_distance_gradient_at_point(result, point) {
170
+ return compute_signed_distance_gradient_by_sampling(result, this, point);
171
+ }
172
+
173
+ signed_distance_at_point(point) {
174
+ let r = Infinity;
175
+
176
+ const children = this.children;
177
+ const child_count = children.length;
178
+
179
+ for (let i = 0; i < child_count; i++) {
180
+ const child = children[i];
181
+
182
+ r = min2(child.signed_distance_at_point(point), r);
183
+ }
184
+
185
+ return r;
186
+ }
187
+
172
188
  contains_point(point) {
173
189
  const children = this.children;
174
190
  const n = children.length;
@@ -183,7 +199,6 @@ export class CompositeShape3D extends AbstractShape3D {
183
199
  return false;
184
200
  }
185
201
 
186
-
187
202
  sample_random_point_in_volume(result, result_offset, random) {
188
203
  const children = this.children;
189
204
  const child_count = children.length;
@@ -1,5 +1,11 @@
1
1
  import { AbstractShape3D } from "./AbstractShape3D.js";
2
2
  import { randomPointInBox } from "../../random/randomPointInBox.js";
3
+ import { v3_length } from "../../v3_length.js";
4
+ import { max2 } from "../../../math/max2.js";
5
+ import { max3 } from "../../../math/max3.js";
6
+ import { min2 } from "../../../math/min2.js";
7
+ import { clamp } from "../../../math/clamp.js";
8
+ import { compute_signed_distance_gradient_by_sampling } from "./util/compute_signed_distance_gradient_by_sampling.js";
3
9
 
4
10
  export class UnitCubeShape3D extends AbstractShape3D {
5
11
 
@@ -7,6 +13,77 @@ export class UnitCubeShape3D extends AbstractShape3D {
7
13
  return 1;
8
14
  }
9
15
 
16
+ nearest_point_on_surface(result, reference) {
17
+ const r_x = reference[0];
18
+ const r_y = reference[1];
19
+ const r_z = reference[2];
20
+
21
+ // figure out closest face to the reference point
22
+ const ar_x = Math.abs(r_x);
23
+ const ar_y = Math.abs(r_y);
24
+ const ar_z = Math.abs(r_z);
25
+
26
+ if (ar_x > ar_y && ar_x > ar_z) {
27
+ // X is smallest, project onto X axis
28
+ if (r_x <= 0) {
29
+ result[0] = -0.5;
30
+ } else {
31
+ result[0] = 0.5;
32
+ }
33
+
34
+ result[1] = clamp(r_y, -0.5, 0.5);
35
+ result[2] = clamp(r_z, -0.5, 0.5);
36
+ } else if (ar_y > ar_x && ar_y > ar_z) {
37
+ // Y is smallest, project onto X axis
38
+ if (r_y <= 0) {
39
+ result[1] = -0.5;
40
+ } else {
41
+ result[1] = 0.5;
42
+ }
43
+
44
+ result[0] = clamp(r_x, -0.5, 0.5);
45
+ result[2] = clamp(r_z, -0.5, 0.5);
46
+ } else {
47
+ // project onto Z axis
48
+
49
+ if (r_z <= 0) {
50
+ result[2] = -0.5;
51
+ } else {
52
+ result[2] = 0.5;
53
+ }
54
+
55
+ result[0] = clamp(r_x, -0.5, 0.5);
56
+ result[1] = clamp(r_y, -0.5, 0.5);
57
+ }
58
+ }
59
+
60
+ signed_distance_gradient_at_point(result, point) {
61
+ return compute_signed_distance_gradient_by_sampling(result,this, point);
62
+ }
63
+
64
+ signed_distance_at_point(point) {
65
+ /*
66
+ based on https://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm
67
+ original shader code:
68
+ ------------
69
+ float sdBox( vec3 p, vec3 b )
70
+ {
71
+ vec3 q = abs(p) - b;
72
+ return length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0);
73
+ }
74
+ ------------
75
+ */
76
+ const q_x = Math.abs(point[0]) - 0.5;
77
+ const q_y = Math.abs(point[1]) - 0.5;
78
+ const q_z = Math.abs(point[2]) - 0.5;
79
+
80
+ return v3_length(
81
+ max2(q_x, 0),
82
+ max2(q_y, 0),
83
+ max2(q_z, 0)
84
+ ) + min2(max3(q_x, q_y, q_z), 0);
85
+ }
86
+
10
87
  contains_point(point) {
11
88
  const x = point[0];
12
89
  const y = point[1];
@@ -0,0 +1,42 @@
1
+ import { UnitCubeShape3D } from "./UnitCubeShape3D.js";
2
+
3
+ test('signed distance', () => {
4
+ const s = new UnitCubeShape3D();
5
+
6
+ expect(s.signed_distance_at_point([0, 0, 0])).toBeCloseTo(-0.5);
7
+
8
+ expect(s.signed_distance_at_point([0.5, 0, 0])).toBeCloseTo(0);
9
+ expect(s.signed_distance_at_point([0, 0.5, 0])).toBeCloseTo(0);
10
+ expect(s.signed_distance_at_point([0, 0, 0.5])).toBeCloseTo(0);
11
+
12
+ expect(s.signed_distance_at_point([-0.5, 0, 0])).toBeCloseTo(0);
13
+ expect(s.signed_distance_at_point([0, -0.5, 0])).toBeCloseTo(0);
14
+ expect(s.signed_distance_at_point([0, 0, -0.5])).toBeCloseTo(0);
15
+
16
+ expect(s.signed_distance_at_point([0.5, 0.5, 0])).toBeCloseTo(0);
17
+ expect(s.signed_distance_at_point([0, 0.5, 0.5])).toBeCloseTo(0);
18
+ expect(s.signed_distance_at_point([0.5, 0, 0.5])).toBeCloseTo(0);
19
+
20
+ expect(s.signed_distance_at_point([-0.5, 0.5, 0])).toBeCloseTo(0);
21
+ expect(s.signed_distance_at_point([0.5, -0.5, 0])).toBeCloseTo(0);
22
+ expect(s.signed_distance_at_point([-0.5, -0.5, 0])).toBeCloseTo(0);
23
+
24
+ expect(s.signed_distance_at_point([0, -0.5, 0.5])).toBeCloseTo(0);
25
+ expect(s.signed_distance_at_point([0, 0.5, -0.5])).toBeCloseTo(0);
26
+ expect(s.signed_distance_at_point([0, -0.5, -0.5])).toBeCloseTo(0);
27
+
28
+ expect(s.signed_distance_at_point([-0.5, 0, 0.5])).toBeCloseTo(0);
29
+ expect(s.signed_distance_at_point([0.5, 0, -0.5])).toBeCloseTo(0);
30
+ expect(s.signed_distance_at_point([-0.5, 0, -0.5])).toBeCloseTo(0);
31
+
32
+ expect(s.signed_distance_at_point([0.5, 0.5, 0.5])).toBeCloseTo(0);
33
+ expect(s.signed_distance_at_point([-0.5, 0.5, 0.5])).toBeCloseTo(0);
34
+ expect(s.signed_distance_at_point([0.5, -0.5, 0.5])).toBeCloseTo(0);
35
+ expect(s.signed_distance_at_point([0.5, 0.5, -0.5])).toBeCloseTo(0);
36
+ expect(s.signed_distance_at_point([-0.5, -0.5, 0.5])).toBeCloseTo(0);
37
+ expect(s.signed_distance_at_point([0.5, -0.5, -0.5])).toBeCloseTo(0);
38
+ expect(s.signed_distance_at_point([-0.5, 0.5, -0.5])).toBeCloseTo(0);
39
+ expect(s.signed_distance_at_point([-0.5, -0.5, -0.5])).toBeCloseTo(0);
40
+
41
+ expect(s.signed_distance_at_point([1, 0, 0])).toBeCloseTo(0.5);
42
+ });
@@ -1,6 +1,11 @@
1
1
  import { AbstractShape3D } from "./AbstractShape3D.js";
2
2
  import { randomPointInSphere } from "../../random/randomPointInSphere.js";
3
+ import { v3_length } from "../../v3_length.js";
4
+ import { compute_signed_distance_gradient_by_sampling } from "./util/compute_signed_distance_gradient_by_sampling.js";
3
5
 
6
+ /**
7
+ * Sphere with diameter of 1 (radius = 0.5)
8
+ */
4
9
  export class UnitSphereShape3D extends AbstractShape3D {
5
10
  get volume() {
6
11
  return 0.523598776;
@@ -10,12 +15,38 @@ export class UnitSphereShape3D extends AbstractShape3D {
10
15
  return 3.14159265;
11
16
  }
12
17
 
18
+ nearest_point_on_surface(result, reference) {
19
+
20
+ const r_x = reference[0];
21
+ const r_y = reference[1];
22
+ const r_z = reference[2];
23
+
24
+ // normalize vector to 0.5 radius
25
+ const d = 0.5 / Math.hypot(r_x, r_y, r_z);
26
+
27
+ result[0] = r_x * d;
28
+ result[1] = r_y * d;
29
+ result[2] = r_z * d;
30
+ }
31
+
32
+ signed_distance_gradient_at_point(result, point) {
33
+ return compute_signed_distance_gradient_by_sampling(result,this, point);
34
+ }
35
+
36
+ signed_distance_at_point(point) {
37
+ return v3_length(
38
+ point[0],
39
+ point[1],
40
+ point[2]
41
+ ) - 0.5;
42
+ }
43
+
13
44
  contains_point(point) {
14
45
  const x = point[0];
15
46
  const y = point[1];
16
47
  const z = point[2];
17
48
 
18
- return (x * x + y * y + z * z) < 1;
49
+ return (x * x + y * y + z * z) < 0.25;
19
50
  }
20
51
 
21
52
  sample_random_point_in_volume(result, result_offset, random) {