@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.
- package/core/binary/BitSet.js +151 -167
- package/core/binary/BitSet.spec.js +99 -1
- package/core/binary/ValidatingBitSetWrapper.js +81 -0
- package/core/bvh2/aabb3/AABB3.js +3 -10
- package/core/cache/Cache.d.ts +4 -2
- package/core/cache/Cache.js +5 -2
- package/core/collection/HashMap.d.ts +9 -2
- package/core/collection/HashSet.d.ts +7 -2
- package/core/collection/HashSet.js +7 -2
- package/core/collection/array/typed/typed_array_copy.js +1 -1
- package/core/geom/3d/frustum/array_setFromProjectionMatrix.js +3 -3
- package/core/geom/3d/shape/AbstractShape3D.js +28 -1
- package/core/geom/3d/shape/TransformedShape3D.js +65 -10
- package/core/geom/3d/shape/TransformedShape3D.spec.js +33 -0
- package/core/geom/3d/shape/{CompositeShape.js → UnionShape3D.js} +27 -12
- package/core/geom/3d/shape/UnitCubeShape3D.js +77 -0
- package/core/geom/3d/shape/UnitCubeShape3D.spec.js +42 -0
- package/core/geom/3d/shape/UnitSphereShape3D.js +32 -1
- package/core/geom/3d/shape/util/compute_signed_distance_gradient_by_sampling.js +48 -0
- package/core/geom/3d/shape/util/shape_to_visual_entity.js +3 -3
- package/core/model/ObservedBoolean.d.ts +2 -0
- package/core/parser/simple/Token.d.ts +13 -0
- package/core/parser/simple/Token.js +61 -27
- package/editor/tools/FoliagePaintTool.js +4 -4
- package/engine/asset/loaders/GLTFAssetLoader.js +52 -1
- package/engine/asset/loaders/image/ImageDecoderWorker.js +4 -3
- package/engine/asset/loaders/image/ImageRGBADataAsset.js +3 -2
- package/engine/asset/loaders/image/ImageRGBADataLoader.js +3 -3
- package/engine/asset/loaders/image/codec/NativeImageDecoder.js +2 -1
- package/engine/asset/loaders/image/png/PNG.js +113 -6
- package/engine/asset/loaders/image/png/PNGReader.js +5 -0
- package/engine/asset/loaders/material/bitmap2sampler_gl.js +1 -1
- package/engine/asset/loaders/material/computeImageBitmapEquality.js +6 -1
- package/engine/asset/loaders/material/computeTextureEquality.js +40 -49
- package/engine/ecs/EntityComponentDataset.js +1 -1
- package/engine/ecs/foliage/InstancedFoliage.js +277 -266
- package/engine/ecs/foliage/ecs/Foliage2System.js +7 -7
- package/engine/ecs/foliage/ecs/InstancedMeshComponent.js +37 -0
- package/engine/ecs/foliage/ecs/{Foliage2.js → InstancedMeshLayer.js} +3 -45
- package/engine/ecs/foliage/ecs/InstancedMeshSerializationAdapter.js +6 -5
- package/engine/ecs/foliage/ecs/InstancedMeshUtils.js +7 -6
- package/engine/ecs/storage/binary/BinarySerializationRegistry.js +1 -1
- package/engine/ecs/terrain/ecs/layers/TerrainLayer.js +12 -16
- package/engine/graphics/ecs/highlight/plugin/OutlineRenderPlugin.js +2 -2
- package/engine/graphics/geometry/instancing/InstancedMeshGroup.js +14 -72
- package/engine/graphics/geometry/instancing/rewriteMaterial.js +70 -0
- package/engine/graphics/material/manager/MaterialManager.js +2 -1
- package/engine/graphics/micron/render/instanced/AttributeDataTexture.js +6 -4
- package/engine/graphics/micron/render/instanced/ThreeInstancedAdapter.js +0 -2
- package/engine/graphics/particles/particular/engine/shader/ShaderManager.js +1 -2
- package/engine/graphics/particles/particular/engine/shader/debugAtlas.js +1 -1
- package/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +12 -9
- package/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js +3 -4
- package/engine/graphics/render/forward_plus/materials/FPlusDebugMaterial.js +3 -3
- package/engine/graphics/render/forward_plus/materials/ForwardPlusThreeMaterial.js +1 -1
- package/engine/graphics/render/forward_plus/prototypeLightManager.js +4 -4
- package/engine/graphics/render/forward_plus/query/point_light_inside_volume.js +10 -9
- package/engine/graphics/shadows/ShadowMapRenderer.js +8 -2
- package/engine/graphics/texture/atlas/ManagedTextureAtlas.js +12 -1
- package/engine/graphics/texture/sampler/Sampler2D.js +3 -1
- package/engine/graphics/texture/sampler/convertTexture2Sampler2D.js +1 -1
- package/engine/graphics/texture/sampler/copy_Sampler2D_channel_data.js +66 -0
- package/engine/input/devices/KeyCodes.d.ts +1 -0
- package/package.json +1 -1
- 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
|
+
}
|
package/core/bvh2/aabb3/AABB3.js
CHANGED
|
@@ -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
|
-
|
|
766
|
-
|
|
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
|
|
package/core/cache/Cache.d.ts
CHANGED
|
@@ -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
|
package/core/cache/Cache.js
CHANGED
|
@@ -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({
|
|
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({
|
|
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({
|
|
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
|
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
102
|
+
mat4.getScaling(scratch_v3_0, this.__matrix);
|
|
102
103
|
|
|
103
|
-
const scale_modifier =
|
|
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(
|
|
165
|
+
vec3.transformMat4(scratch_v3_0, point, this.__inverse_matrix);
|
|
111
166
|
|
|
112
|
-
return this.__subject.contains_point(
|
|
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(
|
|
171
|
+
this.__subject.sample_random_point_in_volume(scratch_v3_0, 0, random);
|
|
117
172
|
|
|
118
173
|
// apply transform
|
|
119
|
-
vec3.transformMat4(
|
|
174
|
+
vec3.transformMat4(scratch_v3_0, scratch_v3_0, this.__matrix);
|
|
120
175
|
|
|
121
176
|
// write out
|
|
122
|
-
result[0] =
|
|
123
|
-
result[1] =
|
|
124
|
-
result[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
|
|
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) <
|
|
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) {
|