@woosh/meep-engine 2.33.24 → 2.33.28
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/bvh2/binary/IndexedBinaryBVH.js +1 -1
- package/core/bvh2/traversal/ThreeClippingPlaneComputingBVHVisitor.js +5 -1
- package/core/bvh2/traversal/__detailed_box_volume_intersection.js +22 -5
- 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/binarySearchHighIndex.js +1 -1
- package/core/collection/array/copyArray.js +2 -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/normal/hemioct/unit_hemioct.spec.js +1 -1
- package/core/geom/3d/shape/AbstractShape3D.js +37 -1
- package/core/geom/3d/shape/TransformedShape3D.js +121 -10
- package/core/geom/3d/shape/TransformedShape3D.spec.js +33 -0
- package/core/geom/3d/shape/UnionShape3D.js +255 -0
- package/core/geom/3d/shape/UnitCubeShape3D.js +88 -0
- package/core/geom/3d/shape/UnitCubeShape3D.spec.js +42 -0
- package/core/geom/3d/shape/UnitSphereShape3D.js +39 -0
- 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 +159 -0
- package/core/geom/random/randomPointInSphere.js +3 -2
- package/core/json/resolvePathByArray.js +2 -2
- 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/{engine/graphics/render/forward_plus/materials → core/primitives/strings}/insert_after.js +3 -0
- package/core/primitives/strings/insert_after_or_replace.js +27 -0
- package/editor/process/symbolic/makeHelperBoxGeometry.js +44 -0
- package/editor/tools/FoliagePaintTool.js +4 -4
- package/editor/view/ecs/EntityEditor.js +2 -2
- package/editor/view/ecs/components/particles/ParticleEmitterController.js +2 -2
- package/engine/EngineHarness.js +1 -0
- package/engine/animation/AnimationUtils.js +5 -5
- 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/EntityBuilder.js +25 -5
- package/engine/ecs/EntityComponentDataset.js +1 -1
- package/engine/ecs/attachment/AttachmentBinding.js +8 -0
- package/engine/ecs/attachment/AttachmentSystem.js +4 -0
- package/engine/ecs/attachment/TransformAttachmentBinding.js +14 -44
- package/engine/ecs/components/Tag.js +27 -6
- 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/fow/FogOfWar.js +4 -0
- package/engine/ecs/fow/FogOfWarSystem.js +2 -0
- package/engine/ecs/gui/hud/HeadsUpDisplaySystem.js +2 -0
- package/engine/ecs/storage/binary/BinarySerializationRegistry.js +1 -1
- package/engine/ecs/system/AbstractContextSystem.js +1 -1
- package/engine/ecs/systems/TagSystem.js +17 -0
- package/engine/ecs/terrain/ecs/layers/TerrainLayer.js +12 -16
- package/engine/ecs/transform/Transform.js +19 -6
- package/engine/graphics/GraphicsEngine.js +11 -6
- package/engine/graphics/composit/CompositLayer.js +17 -4
- package/engine/graphics/composit/CompositingStages.js +8 -0
- package/engine/graphics/composit/LayerCompositer.js +35 -6
- package/engine/graphics/ecs/highlight/plugin/OutlineRenderPlugin.js +11 -7
- package/engine/graphics/ecs/light/Light.js +2 -0
- package/engine/graphics/ecs/light/binding/three/ThreeLightBinding.js +5 -1
- package/engine/graphics/ecs/mesh-v2/DrawMode.js +5 -0
- package/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +32 -18
- 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/ParticleEmitterLibrary.js +3 -0
- package/engine/graphics/particles/ecs/{ParticleEmitterSystem2.js → ParticleEmitterSystem.js} +17 -9
- package/engine/graphics/particles/particular/engine/ParticularEngine.js +8 -2
- package/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.js +14 -25
- package/engine/graphics/particles/particular/engine/emitter/ParticleEmitterFlag.js +5 -0
- package/engine/graphics/particles/particular/engine/emitter/ParticlePool.js +90 -2
- package/engine/graphics/particles/particular/engine/emitter/ParticlePool.spec.js +7 -7
- package/engine/graphics/particles/particular/engine/renderers/billboard/BILLBOARD_PARTICLE_SPECIFICATION.js +15 -12
- package/engine/graphics/particles/particular/engine/renderers/billboard/ParticleBillboardMaterial.js +262 -51
- package/engine/graphics/particles/particular/engine/shader/ShaderManager.js +81 -48
- package/engine/graphics/particles/particular/engine/shader/debugAtlas.js +14 -0
- package/engine/graphics/particles/particular/engine/utils/volume/AttributeValue.js +141 -0
- package/engine/graphics/particles/particular/engine/utils/volume/ParticleVolume.js +148 -54
- package/engine/graphics/particles/particular/engine/utils/volume/lut_to_sampler.js +27 -0
- package/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +544 -164
- package/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js +15 -10
- package/engine/graphics/render/forward_plus/materials/FP_INJECTION_POINT_ACCUMULATION.js +1 -0
- package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_ACCUMULATION.js +32 -0
- package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_DECODE_PARS.js +51 -0
- package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_PREAMBLE.js +25 -0
- 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/materials/fp_build_fragment_shader.js +31 -120
- package/engine/graphics/render/forward_plus/materials/fp_build_vertex_lighting_shared.js +35 -0
- package/engine/graphics/render/forward_plus/materials/fp_build_vertex_shader.js +4 -5
- package/engine/graphics/render/forward_plus/plugin/MaterialTransformer.js +10 -3
- package/engine/graphics/render/forward_plus/plugin/ptototypeFPPlugin.js +588 -53
- package/engine/graphics/render/forward_plus/prototypeLightManager.js +31 -6
- package/engine/graphics/render/forward_plus/query/point_light_inside_volume.js +10 -9
- package/engine/graphics/shaders/TerrainShader.js +4 -5
- 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/graphics/util/composeMatrix4.js +33 -23
- package/engine/graphics/util/decompose_matrix_4_array.js +61 -0
- package/engine/intelligence/behavior/ecs/BehaviorComponent.js +30 -14
- package/engine/knowledge/database/StaticKnowledgeDataTable.js +6 -1
- package/engine/navigation/ecs/components/Path.js +0 -5
- package/package.json +1 -1
- package/view/minimap/gl/MinimapWorldGL.js +12 -5
- package/core/geom/3d/shape/CompositeShape.js +0 -60
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { AbstractShape3D } from "./AbstractShape3D.js";
|
|
2
|
+
import { seededRandom } from "../../../math/random/seededRandom.js";
|
|
3
|
+
import { binarySearchHighIndex } from "../../../collection/array/binarySearchHighIndex.js";
|
|
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";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* To avoid severe performance overhead, we limit number of possible rejections
|
|
10
|
+
* @type {number}
|
|
11
|
+
*/
|
|
12
|
+
const SAMPLE_REJECTION_ATTEMPT_MAX = 4;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Number of samples taken to estimate overlap level
|
|
16
|
+
* @type {number}
|
|
17
|
+
*/
|
|
18
|
+
const VOLUME_OVERLAP_ESTIMATION_TAPS = 1024;
|
|
19
|
+
const VOLUME_OVERLAP_ESTIMATION_SUB_TAPS = 4;
|
|
20
|
+
|
|
21
|
+
const scratch_array = [];
|
|
22
|
+
|
|
23
|
+
export class UnionShape3D extends AbstractShape3D {
|
|
24
|
+
constructor() {
|
|
25
|
+
super();
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
*
|
|
29
|
+
* @type {AbstractShape3D[]}
|
|
30
|
+
*/
|
|
31
|
+
this.children = [];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Estimator of how much volume is in overlap,
|
|
35
|
+
* expressed as fraction of volume occupied by overlapping region of 2 shapes over the sum of volume occupied by each child
|
|
36
|
+
* @type {number}
|
|
37
|
+
* @private
|
|
38
|
+
*/
|
|
39
|
+
this.__volume_overlap_fraction = -1;
|
|
40
|
+
|
|
41
|
+
this.__volume_sum = -1;
|
|
42
|
+
this.__volume_sums = [];
|
|
43
|
+
this.__child_volumes = [];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
*
|
|
48
|
+
* @private
|
|
49
|
+
*/
|
|
50
|
+
__compute_volume_variables() {
|
|
51
|
+
this.__volume_sum = 0;
|
|
52
|
+
|
|
53
|
+
const children = this.children;
|
|
54
|
+
const child_count = children.length;
|
|
55
|
+
|
|
56
|
+
const child_volumes = this.__child_volumes = new Float32Array(child_count);
|
|
57
|
+
const child_volume_sums = this.__volume_sums = new Float32Array(child_count);
|
|
58
|
+
|
|
59
|
+
for (let i = 0; i < child_count; i++) {
|
|
60
|
+
const child_volume = children[i].volume;
|
|
61
|
+
|
|
62
|
+
child_volumes[i] = child_volume;
|
|
63
|
+
this.__volume_sum += child_volume;
|
|
64
|
+
|
|
65
|
+
child_volume_sums[i] = this.__volume_sum;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Monte-carlo-style volume overlap estimation
|
|
72
|
+
* @private
|
|
73
|
+
*/
|
|
74
|
+
__estimate_volume_overlap_fraction() {
|
|
75
|
+
|
|
76
|
+
if (this.__volume_sum === -1) {
|
|
77
|
+
this.__compute_volume_variables();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const children = this.children;
|
|
81
|
+
const child_count = children.length;
|
|
82
|
+
|
|
83
|
+
const random = seededRandom(1);
|
|
84
|
+
|
|
85
|
+
const child_volume_sums = this.__volume_sums;
|
|
86
|
+
const child_volume_sum = this.__volume_sum;
|
|
87
|
+
|
|
88
|
+
let i = 0, j = 0, k = 0, tap_overlaps = 0;
|
|
89
|
+
|
|
90
|
+
for (i = 0; i < VOLUME_OVERLAP_ESTIMATION_TAPS; i++) {
|
|
91
|
+
|
|
92
|
+
const target_weight = random() * child_volume_sum;
|
|
93
|
+
|
|
94
|
+
// weighted search for a sample candidate, preferring larger volumes, resulting in more uniform sample distribution
|
|
95
|
+
const target_child_index = binarySearchHighIndex(child_volume_sums, target_weight, compareNumbers, 0, child_count - 1);
|
|
96
|
+
|
|
97
|
+
const child_0 = children[target_child_index];
|
|
98
|
+
|
|
99
|
+
for (j = 0; j < VOLUME_OVERLAP_ESTIMATION_SUB_TAPS; j++, i++) {
|
|
100
|
+
|
|
101
|
+
child_0.sample_random_point_in_volume(scratch_array, 0, random);
|
|
102
|
+
|
|
103
|
+
for (k = 0; k < child_count; k++) {
|
|
104
|
+
if (target_child_index === k) {
|
|
105
|
+
// skip self
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const child_1 = children[k];
|
|
110
|
+
|
|
111
|
+
if (child_1.contains_point(scratch_array)) {
|
|
112
|
+
tap_overlaps++;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
this.__volume_overlap_fraction = tap_overlaps / (VOLUME_OVERLAP_ESTIMATION_TAPS);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
static from(children) {
|
|
123
|
+
const r = new UnionShape3D();
|
|
124
|
+
|
|
125
|
+
r.children = children;
|
|
126
|
+
|
|
127
|
+
return r;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
get volume() {
|
|
131
|
+
if (this.__volume_overlap_fraction < 0) {
|
|
132
|
+
this.__estimate_volume_overlap_fraction();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
let volume_sum = 0;
|
|
136
|
+
|
|
137
|
+
const children = this.children;
|
|
138
|
+
const child_count = children.length;
|
|
139
|
+
|
|
140
|
+
for (let i = 0; i < child_count; i++) {
|
|
141
|
+
const child = children[i];
|
|
142
|
+
|
|
143
|
+
volume_sum += child.volume;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Unbias the volume by taking overlap into account
|
|
148
|
+
* @type {number}
|
|
149
|
+
*/
|
|
150
|
+
const unbiased_volume = volume_sum / (this.__volume_overlap_fraction + 1);
|
|
151
|
+
|
|
152
|
+
return unbiased_volume;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
get surface_area() {
|
|
156
|
+
let r = 0;
|
|
157
|
+
|
|
158
|
+
const children = this.children;
|
|
159
|
+
const child_count = children.length;
|
|
160
|
+
for (let i = 0; i < child_count; i++) {
|
|
161
|
+
const child = children[i];
|
|
162
|
+
|
|
163
|
+
r += child.surface_area;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return r;
|
|
167
|
+
}
|
|
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
|
+
|
|
188
|
+
contains_point(point) {
|
|
189
|
+
const children = this.children;
|
|
190
|
+
const n = children.length;
|
|
191
|
+
for (let i = 0; i < n; i++) {
|
|
192
|
+
const child = children[i];
|
|
193
|
+
|
|
194
|
+
if (child.contains_point(point)) {
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
sample_random_point_in_volume(result, result_offset, random) {
|
|
203
|
+
const children = this.children;
|
|
204
|
+
const child_count = children.length;
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
const target_weight = random() * this.__volume_sum;
|
|
208
|
+
|
|
209
|
+
// weighted search for a sample candidate, preferring larger volumes, resulting in more uniform sample distribution
|
|
210
|
+
const target_child_index = binarySearchHighIndex(this.__volume_sums, target_weight, compareNumbers, 0, child_count - 1);
|
|
211
|
+
|
|
212
|
+
const child = children[target_child_index];
|
|
213
|
+
|
|
214
|
+
let collision_count, i, j;
|
|
215
|
+
|
|
216
|
+
for (i = 0; i < SAMPLE_REJECTION_ATTEMPT_MAX; i++) {
|
|
217
|
+
|
|
218
|
+
// sample shape
|
|
219
|
+
child.sample_random_point_in_volume(result, result_offset, random);
|
|
220
|
+
|
|
221
|
+
/*
|
|
222
|
+
because underlying shapes can overlap and we wish to offer a union, just returning the sample would result in over-sampling in areas where shapes overlap
|
|
223
|
+
to avoid this oversampling, we reject samples in such areas with probability proportional to number of overlapping shapes at the sampled point
|
|
224
|
+
*/
|
|
225
|
+
collision_count = 0;
|
|
226
|
+
|
|
227
|
+
for (j = 0; j < child_count; j++) {
|
|
228
|
+
const child_1 = children[j];
|
|
229
|
+
|
|
230
|
+
if (child_1 === child) {
|
|
231
|
+
// exclude sampled child itself
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (child_1.contains_point(result)) {
|
|
236
|
+
collision_count++;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (collision_count > 0) {
|
|
241
|
+
const rejection_chance = collision_count / (collision_count + 1);
|
|
242
|
+
|
|
243
|
+
const rejection_roll = random();
|
|
244
|
+
|
|
245
|
+
if (rejection_roll < rejection_chance) {
|
|
246
|
+
//reject
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// no collisions, or survived rejection
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
@@ -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,88 @@ 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
|
+
|
|
87
|
+
contains_point(point) {
|
|
88
|
+
const x = point[0];
|
|
89
|
+
const y = point[1];
|
|
90
|
+
const z = point[2];
|
|
91
|
+
|
|
92
|
+
return x > -0.5 && x < 0.5
|
|
93
|
+
&& y > -0.5 && y < 0.5
|
|
94
|
+
&& z > -0.5 && z < 0.5
|
|
95
|
+
;
|
|
96
|
+
}
|
|
97
|
+
|
|
10
98
|
sample_random_point_in_volume(result, result_offset, random) {
|
|
11
99
|
randomPointInBox(random, result, result_offset);
|
|
12
100
|
}
|
|
@@ -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,6 +15,40 @@ 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
|
+
|
|
44
|
+
contains_point(point) {
|
|
45
|
+
const x = point[0];
|
|
46
|
+
const y = point[1];
|
|
47
|
+
const z = point[2];
|
|
48
|
+
|
|
49
|
+
return (x * x + y * y + z * z) < 0.25;
|
|
50
|
+
}
|
|
51
|
+
|
|
13
52
|
sample_random_point_in_volume(result, result_offset, random) {
|
|
14
53
|
randomPointInSphere(random, result, result_offset);
|
|
15
54
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const scratch_v = [];
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param {number[]} result
|
|
6
|
+
* @param {AbstractShape3D} shape
|
|
7
|
+
* @param {number[]} point
|
|
8
|
+
* @returns {number}
|
|
9
|
+
*/
|
|
10
|
+
export function compute_signed_distance_gradient_by_sampling(result, shape, point) {
|
|
11
|
+
const v0 = shape.signed_distance_at_point(point);
|
|
12
|
+
|
|
13
|
+
scratch_v[0] = point[0] + 1e-5;
|
|
14
|
+
scratch_v[1] = point[1];
|
|
15
|
+
scratch_v[2] = point[2];
|
|
16
|
+
|
|
17
|
+
const vx = shape.signed_distance_at_point(scratch_v);
|
|
18
|
+
|
|
19
|
+
scratch_v[0] = point[0];
|
|
20
|
+
scratch_v[1] = point[1] + 1e-5;
|
|
21
|
+
scratch_v[2] = point[2];
|
|
22
|
+
|
|
23
|
+
const vy = shape.signed_distance_at_point(point);
|
|
24
|
+
|
|
25
|
+
scratch_v[0] = point[0];
|
|
26
|
+
scratch_v[1] = point[1];
|
|
27
|
+
scratch_v[2] = point[2] + 1e-5;
|
|
28
|
+
|
|
29
|
+
const vz = shape.signed_distance_at_point(point);
|
|
30
|
+
|
|
31
|
+
// convert absolute sample values to relative
|
|
32
|
+
const rx = vx - v0;
|
|
33
|
+
const ry = vy - v0;
|
|
34
|
+
const rz = vz - v0;
|
|
35
|
+
|
|
36
|
+
// normalize by sampling distance
|
|
37
|
+
const d = 1e-5 / 1.73205080757;
|
|
38
|
+
|
|
39
|
+
const nx = rx * d;
|
|
40
|
+
const ny = ry * d;
|
|
41
|
+
const nz = rz * d;
|
|
42
|
+
|
|
43
|
+
result[0] = nx;
|
|
44
|
+
result[1] = ny;
|
|
45
|
+
result[2] = nz;
|
|
46
|
+
|
|
47
|
+
return v0;
|
|
48
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { UnionShape3D } from "../UnionShape3D.js";
|
|
2
|
+
import { UnitCubeShape3D } from "../UnitCubeShape3D.js";
|
|
3
|
+
import { UnitSphereShape3D } from "../UnitSphereShape3D.js";
|
|
4
|
+
import { TransformedShape3D } from "../TransformedShape3D.js";
|
|
5
|
+
import EntityBuilder from "../../../../../engine/ecs/EntityBuilder.js";
|
|
6
|
+
import { Transform } from "../../../../../engine/ecs/transform/Transform.js";
|
|
7
|
+
import { AttachmentSockets } from "../../../../../engine/ecs/sockets/AttachmentSockets.js";
|
|
8
|
+
import { Attachment } from "../../../../../engine/ecs/attachment/Attachment.js";
|
|
9
|
+
import { ShadedGeometry } from "../../../../../engine/graphics/ecs/mesh-v2/ShadedGeometry.js";
|
|
10
|
+
import { LineBasicMaterial } from "three";
|
|
11
|
+
import { makeHelperSphereGeometry } from "../../../../../editor/process/symbolic/makeHelperSphereGeometry.js";
|
|
12
|
+
import { DrawMode } from "../../../../../engine/graphics/ecs/mesh-v2/DrawMode.js";
|
|
13
|
+
import { makeHelperBoxGeometry } from "../../../../../editor/process/symbolic/makeHelperBoxGeometry.js";
|
|
14
|
+
import Tag from "../../../../../engine/ecs/components/Tag.js";
|
|
15
|
+
|
|
16
|
+
const g_unit_sphere = makeHelperSphereGeometry(0.5, 32);
|
|
17
|
+
const g_unit_cube = makeHelperBoxGeometry(1, 1, 1);
|
|
18
|
+
|
|
19
|
+
const lineMaterial = new LineBasicMaterial({
|
|
20
|
+
depthTest: true,
|
|
21
|
+
depthWrite: false,
|
|
22
|
+
transparent: true,
|
|
23
|
+
linewidth: 1,
|
|
24
|
+
fog: false,
|
|
25
|
+
color: '#FFFFFF',
|
|
26
|
+
opacity: 1
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const SHAPE_VISUAL_ENTITY_TAG = 'util/vis/Shape3D';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
*
|
|
33
|
+
* @param {UnionShape3D} shape
|
|
34
|
+
* @param {EntityComponentDataset} ecd
|
|
35
|
+
* @returns {EntityBuilder}
|
|
36
|
+
*/
|
|
37
|
+
function composite_shape_to_entity(shape, ecd) {
|
|
38
|
+
|
|
39
|
+
const parent = new EntityBuilder();
|
|
40
|
+
parent
|
|
41
|
+
.add(new Transform())
|
|
42
|
+
.add(AttachmentSockets.fromJSON({
|
|
43
|
+
elements: [
|
|
44
|
+
{
|
|
45
|
+
type: 0,
|
|
46
|
+
id: 0
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
}))
|
|
50
|
+
.build(ecd);
|
|
51
|
+
|
|
52
|
+
shape.children.forEach(c => {
|
|
53
|
+
|
|
54
|
+
const child_entity = shape_to_visual_entity(c, ecd);
|
|
55
|
+
|
|
56
|
+
child_entity.add(Attachment.fromJSON({
|
|
57
|
+
socket: 0,
|
|
58
|
+
parent: parent.entity,
|
|
59
|
+
immediate: true
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
return parent;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
*
|
|
70
|
+
* @param {UnitCubeShape3D} shape
|
|
71
|
+
* @param {EntityComponentDataset} ecd
|
|
72
|
+
* @returns {EntityBuilder}
|
|
73
|
+
*/
|
|
74
|
+
function cube_shape_to_entity(shape, ecd) {
|
|
75
|
+
const eb = new EntityBuilder();
|
|
76
|
+
|
|
77
|
+
eb.add(new Transform());
|
|
78
|
+
eb.add(Tag.fromOne(SHAPE_VISUAL_ENTITY_TAG));
|
|
79
|
+
eb.add(ShadedGeometry.from(g_unit_cube, lineMaterial, DrawMode.LineSegments));
|
|
80
|
+
|
|
81
|
+
eb.build(ecd);
|
|
82
|
+
|
|
83
|
+
return eb;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
*
|
|
89
|
+
* @param {UnitSphereShape3D} shape
|
|
90
|
+
* @param {EntityComponentDataset} ecd
|
|
91
|
+
* @returns {EntityBuilder}
|
|
92
|
+
*/
|
|
93
|
+
function sphere_shape_to_entity(shape, ecd) {
|
|
94
|
+
const eb = new EntityBuilder();
|
|
95
|
+
|
|
96
|
+
eb.add(new Transform());
|
|
97
|
+
eb.add(Tag.fromOne(SHAPE_VISUAL_ENTITY_TAG));
|
|
98
|
+
eb.add(ShadedGeometry.from(g_unit_sphere, lineMaterial, DrawMode.Lines));
|
|
99
|
+
|
|
100
|
+
eb.build(ecd);
|
|
101
|
+
|
|
102
|
+
return eb;
|
|
103
|
+
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
*
|
|
108
|
+
* @param {TransformedShape3D} shape
|
|
109
|
+
* @param {EntityComponentDataset} ecd
|
|
110
|
+
* @returns {EntityBuilder}
|
|
111
|
+
*/
|
|
112
|
+
function transformed_shape_to_entity(shape, ecd) {
|
|
113
|
+
const subject = shape_to_visual_entity(shape.subject, ecd);
|
|
114
|
+
|
|
115
|
+
const parent = new EntityBuilder();
|
|
116
|
+
parent
|
|
117
|
+
.add(new Transform())
|
|
118
|
+
.add(AttachmentSockets.fromJSON({
|
|
119
|
+
elements: [
|
|
120
|
+
{
|
|
121
|
+
type: 0,
|
|
122
|
+
id: 0
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
}))
|
|
126
|
+
.build(ecd);
|
|
127
|
+
|
|
128
|
+
const attachment = Attachment.fromJSON({
|
|
129
|
+
socket: 0,
|
|
130
|
+
parent: parent.entity,
|
|
131
|
+
immediate: true
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
attachment.transform.fromMatrix4(shape.transform);
|
|
135
|
+
|
|
136
|
+
subject.add(attachment);
|
|
137
|
+
|
|
138
|
+
return parent;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
*
|
|
143
|
+
* @param {AbstractShape3D} shape
|
|
144
|
+
* @param {EntityComponentDataset} ecd
|
|
145
|
+
* @returns {EntityBuilder}
|
|
146
|
+
*/
|
|
147
|
+
export function shape_to_visual_entity(shape, ecd) {
|
|
148
|
+
if (shape instanceof UnionShape3D) {
|
|
149
|
+
return composite_shape_to_entity(shape, ecd);
|
|
150
|
+
} else if (shape instanceof UnitCubeShape3D) {
|
|
151
|
+
return cube_shape_to_entity(shape, ecd);
|
|
152
|
+
} else if (shape instanceof UnitSphereShape3D) {
|
|
153
|
+
return sphere_shape_to_entity(shape, ecd);
|
|
154
|
+
} else if (shape instanceof TransformedShape3D) {
|
|
155
|
+
return transformed_shape_to_entity(shape, ecd);
|
|
156
|
+
} else {
|
|
157
|
+
throw new Error('Unsupported shape type');
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -12,11 +12,12 @@ export function randomPointInSphere(random, result, result_offset) {
|
|
|
12
12
|
|
|
13
13
|
const cosTheta = random() * 2 - 1;
|
|
14
14
|
|
|
15
|
-
const u = random()
|
|
15
|
+
const u = random();
|
|
16
16
|
|
|
17
17
|
const theta = Math.acos(cosTheta);
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
// compute radius
|
|
20
|
+
const r = Math.cbrt(u) * 0.5;
|
|
20
21
|
|
|
21
22
|
const sinTheta = Math.sin(theta);
|
|
22
23
|
|
|
@@ -44,10 +44,10 @@ export function resolvePathByArray(object, parts, missingPropertyHandler) {
|
|
|
44
44
|
} else {
|
|
45
45
|
const options = buildErrorSuggestions(current);
|
|
46
46
|
|
|
47
|
-
throw new Error(`failed to resolve path
|
|
47
|
+
throw new Error(`failed to resolve path [${parts.join(',')}] at part ${i} [${part}]. Existing keys: ${options.join(", ")}`);
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
current = current[part];
|
|
51
51
|
}
|
|
52
52
|
return current;
|
|
53
|
-
}
|
|
53
|
+
}
|