@woosh/meep-engine 2.43.16 → 2.43.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/core/assert.js +3 -1
  2. package/core/bvh2/aabb3/aabb3_intersects_ray.js +14 -9
  3. package/core/bvh2/aabb3/aabb3_intersects_ray_branchless.js +52 -0
  4. package/core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.d.ts +2 -0
  5. package/core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.js +214 -5
  6. package/core/bvh2/bvh3/bvh_query_leaves_ray.js +32 -29
  7. package/core/collection/array/typed/typed_array_copy.js +2 -2
  8. package/core/geom/3d/aabb/compute_aabb_from_points.js +4 -3
  9. package/core/geom/3d/compute_triangle_normal.js +76 -0
  10. package/core/geom/3d/topology/samples/sampleFloodFill.js +1 -1
  11. package/core/geom/3d/topology/simplify/compute_face_normal_change_dot_product.js +1 -1
  12. package/core/geom/3d/topology/simplify/quadratic/Quadratic3.js +1 -1
  13. package/core/geom/3d/topology/struct/TopoTriangle.js +1 -57
  14. package/core/geom/3d/topology/tm_face_normal.js +1 -1
  15. package/core/geom/3d/topology/tm_vertex_compute_normal.js +1 -1
  16. package/core/geom/3d/triangle/computeTriangleRayIntersection.js +195 -27
  17. package/core/geom/Vector3.js +12 -12
  18. package/core/math/physics/brdf/D_GGX.js +13 -0
  19. package/editor/tools/v2/prototypeTransformControls.js +14 -2
  20. package/engine/ecs/parent/EntityNode.js +80 -7
  21. package/engine/ecs/parent/EntityNodeFlags.js +8 -0
  22. package/engine/ecs/terrain/tiles/TerrainTile.js +7 -9
  23. package/engine/graphics/ecs/path/PathDisplaySystem.d.ts +3 -0
  24. package/engine/graphics/ecs/path/PathDisplaySystem.js +10 -0
  25. package/engine/graphics/geometry/AttributeSpec.js +18 -3
  26. package/engine/graphics/geometry/VertexDataSpec.js +53 -3
  27. package/engine/graphics/micron/format/VirtualGeometry.js +7 -0
  28. package/engine/graphics/micron/render/VirtualGeometryBuilder.js +1 -1
  29. package/engine/graphics/micron/render/refinement/get_geometry_patch_cut.js +5 -2
  30. package/engine/graphics/particles/particular/engine/parameter/sample/RGBA_LUT_HEATMAP_IR.js +11 -0
  31. package/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +2 -9
  32. package/engine/graphics/sh3/README.md +1 -0
  33. package/engine/graphics/sh3/path_tracer/GeometryBVHBatched.js +324 -0
  34. package/engine/graphics/sh3/path_tracer/PathTracedMesh.js +85 -0
  35. package/engine/graphics/sh3/path_tracer/PathTracer.js +469 -0
  36. package/engine/graphics/sh3/path_tracer/apply_texture_clamping_to_coordinate.js +22 -0
  37. package/engine/graphics/sh3/path_tracer/compute_triangle_group_aabb3.js +36 -0
  38. package/engine/graphics/sh3/path_tracer/getBiasedNormalSample.js +55 -0
  39. package/engine/graphics/sh3/path_tracer/make_sky_hosek.js +44 -0
  40. package/engine/graphics/sh3/path_tracer/make_sky_rtiw.js +15 -0
  41. package/engine/graphics/sh3/path_tracer/prototypePathTracer.js +619 -0
  42. package/engine/graphics/sh3/path_tracer/random_in_hemisphere.js +39 -0
  43. package/engine/graphics/sh3/path_tracer/ray_hit_apply_transform.js +42 -0
  44. package/engine/graphics/sh3/path_tracer/ray_reflect.js +27 -0
  45. package/engine/graphics/sh3/path_tracer/sample_triangle_attribute.js +35 -0
  46. package/engine/graphics/sh3/path_tracer/vec3_uint8_to_float.js +12 -0
  47. package/engine/graphics/sh3/sky/hosek/README.md +4 -0
  48. package/engine/graphics/sh3/sky/hosek/prototype_hosek.js +71 -0
  49. package/engine/graphics/sh3/sky/hosek/sky_hosek_compute_irradiance_by_direction.js +4171 -0
  50. package/engine/graphics/texture/sampler/convertTexture2Sampler2D.js +2 -0
  51. package/package.json +1 -1
  52. package/view/elements/progress/SmoothProgressBar.js +1 -1
  53. package/view/task/TaskProgressView.js +6 -8
@@ -3,63 +3,7 @@ import { array_push_if_unique } from "../../../../collection/array/array_push_if
3
3
  import { assert } from "../../../../assert.js";
4
4
  import { array_remove_first } from "../../../../collection/array/array_remove_first.js";
5
5
  import { vec3 } from "gl-matrix";
6
- import { v3_length_sqr } from "../../../v3_length_sqr.js";
7
-
8
- /**
9
- *
10
- * @param {number[]|vec3} result
11
- * @param {Vector3|Vector3Like|TopoVertex|{x:number,y:number,z:number}} vA
12
- * @param {Vector3|Vector3Like|TopoVertex|{x:number,y:number,z:number}} vB
13
- * @param {Vector3|Vector3Like|TopoVertex|{x:number,y:number,z:number}} vC
14
- */
15
- export function compute_triangle_normal(result, vA, vB, vC) {
16
-
17
- const vAx = vA.x
18
- const vAy = vA.y
19
- const vAz = vA.z
20
-
21
- const vBx = vB.x
22
- const vBy = vB.y
23
- const vBz = vB.z
24
-
25
- const vCx = vC.x
26
- const vCy = vC.y
27
- const vCz = vC.z
28
-
29
- //compute CB and AB vectors
30
- const vCBx = vCx - vBx;
31
- const vCBy = vCy - vBy;
32
- const vCBz = vCz - vBz;
33
-
34
- const vABx = vAx - vBx;
35
- const vABy = vAy - vBy;
36
- const vABz = vAz - vBz;
37
-
38
- //compute triangle normal
39
- const crossX = vCBy * vABz - vCBz * vABy;
40
- const crossY = vCBz * vABx - vCBx * vABz;
41
- const crossZ = vCBx * vABy - vCBy * vABx;
42
-
43
- const l_sqr = v3_length_sqr(crossX, crossY, crossZ);
44
-
45
- if (l_sqr === 0) {
46
- // degenerate triangle, provide arbitrary invalid result
47
- vec3.set(result, 0, 0, 0);
48
- return;
49
- }
50
-
51
- const l_inv = 1 / Math.sqrt(l_sqr);
52
-
53
- const x = crossX * l_inv;
54
- const y = crossY * l_inv;
55
- const z = crossZ * l_inv;
56
-
57
-
58
- vec3.set(
59
- result,
60
- x, y, z
61
- );
62
- }
6
+ import { compute_triangle_normal } from "../../compute_triangle_normal.js";
63
7
 
64
8
  let index_counter = 0;
65
9
 
@@ -1,4 +1,4 @@
1
- import { compute_triangle_normal } from "./struct/TopoTriangle.js";
1
+ import { compute_triangle_normal } from "../compute_triangle_normal.js";
2
2
 
3
3
  /**
4
4
  *
@@ -1,4 +1,4 @@
1
- import { compute_triangle_normal } from "./struct/TopoTriangle.js";
1
+ import { compute_triangle_normal } from "../compute_triangle_normal.js";
2
2
  //
3
3
 
4
4
 
@@ -30,43 +30,43 @@ export function computeTriangleRayIntersection(
30
30
  bx, by, bz,
31
31
  cx, cy, cz
32
32
  ) {
33
- assert.isNumber(ax,'ax');
34
- assert.isNumber(ay,'ay');
35
- assert.isNumber(az,'az');
33
+ assert.isNumber(ax, 'ax');
34
+ assert.isNumber(ay, 'ay');
35
+ assert.isNumber(az, 'az');
36
36
 
37
- assert.isNumber(bx,'bx');
38
- assert.isNumber(by,'by');
39
- assert.isNumber(bz,'bz');
37
+ assert.isNumber(bx, 'bx');
38
+ assert.isNumber(by, 'by');
39
+ assert.isNumber(bz, 'bz');
40
40
 
41
- assert.isNumber(cx,'cx');
42
- assert.isNumber(cy,'cy');
43
- assert.isNumber(cz,'cz');
41
+ assert.isNumber(cx, 'cx');
42
+ assert.isNumber(cy, 'cy');
43
+ assert.isNumber(cz, 'cz');
44
44
 
45
45
  // nan checks
46
- assert.notNaN(ax,'ax');
47
- assert.notNaN(ay,'ay');
48
- assert.notNaN(az,'az');
46
+ assert.notNaN(ax, 'ax');
47
+ assert.notNaN(ay, 'ay');
48
+ assert.notNaN(az, 'az');
49
49
 
50
- assert.notNaN(bx,'bx');
51
- assert.notNaN(by,'by');
52
- assert.notNaN(bz,'bz');
50
+ assert.notNaN(bx, 'bx');
51
+ assert.notNaN(by, 'by');
52
+ assert.notNaN(bz, 'bz');
53
53
 
54
- assert.notNaN(cx,'cx');
55
- assert.notNaN(cy,'cy');
56
- assert.notNaN(cz,'cz');
54
+ assert.notNaN(cx, 'cx');
55
+ assert.notNaN(cy, 'cy');
56
+ assert.notNaN(cz, 'cz');
57
57
 
58
58
  // finate number check
59
- assert.isFiniteNumber(ax,'ax');
60
- assert.isFiniteNumber(ay,'ay');
61
- assert.isFiniteNumber(az,'az');
59
+ assert.isFiniteNumber(ax, 'ax');
60
+ assert.isFiniteNumber(ay, 'ay');
61
+ assert.isFiniteNumber(az, 'az');
62
62
 
63
- assert.isFiniteNumber(bx,'bx');
64
- assert.isFiniteNumber(by,'by');
65
- assert.isFiniteNumber(bz,'bz');
63
+ assert.isFiniteNumber(bx, 'bx');
64
+ assert.isFiniteNumber(by, 'by');
65
+ assert.isFiniteNumber(bz, 'bz');
66
66
 
67
- assert.isFiniteNumber(cx,'cx');
68
- assert.isFiniteNumber(cy,'cy');
69
- assert.isFiniteNumber(cz,'cz');
67
+ assert.isFiniteNumber(cx, 'cx');
68
+ assert.isFiniteNumber(cy, 'cy');
69
+ assert.isFiniteNumber(cz, 'cz');
70
70
 
71
71
 
72
72
  // edge1 = a - b
@@ -157,3 +157,171 @@ export function computeTriangleRayIntersection(
157
157
 
158
158
  return true;
159
159
  }
160
+
161
+ /**
162
+ * Compute barycentric coordinates for triangle intersection
163
+ * Operates on edges, this is optimized for raytracing, as same edges are required to reconstruct various attributes later on
164
+ * NOTE: most of the code is inlined for speed to avoid allocation and function calls
165
+ * @see https://github.com/erich666/jgt-code/blob/master/Volume_02/Number_1/Moller1997a/raytri.c
166
+ * @source https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm (Möller and Trumbore, « Fast, Minimum Storage Ray-Triangle Intersection », Journal of Graphics Tools, vol. 2,‎ 1997, p. 21–28)
167
+ * @param {number[]} result
168
+ * @param {number} origin_x
169
+ * @param {number} origin_y
170
+ * @param {number} origin_z
171
+ * @param {number} direction_x
172
+ * @param {number} direction_y
173
+ * @param {number} direction_z
174
+ * @param {number} ax
175
+ * @param {number} ay
176
+ * @param {number} az
177
+ * @param {number} edge1_x
178
+ * @param {number} edge1_y
179
+ * @param {number} edge1_z
180
+ * @param {number} edge2_x
181
+ * @param {number} edge2_y
182
+ * @param {number} edge2_z
183
+ * @return {boolean}
184
+ */
185
+ export function computeTriangleRayIntersectionBarycentricEdge(result, origin_x, origin_y, origin_z, direction_x, direction_y, direction_z, ax, ay, az, edge1_x, edge1_y, edge1_z, edge2_x, edge2_y, edge2_z) {
186
+ // begin calculating determinant - also used to calculate U parameter
187
+
188
+ // CROSS(pvec, dir, edge2)
189
+ const pvec_x = direction_y * edge2_z - direction_z * edge2_y;
190
+ const pvec_y = direction_z * edge2_x - direction_x * edge2_z;
191
+ const pvec_z = direction_x * edge2_y - direction_y * edge2_x;
192
+
193
+ //if determinant is near zero, ray lies in plane of triangle
194
+ const det = v3_dot(edge1_x, edge1_y, edge1_z, pvec_x, pvec_y, pvec_z);
195
+
196
+ if (det <= 0) {
197
+ // back-face culling
198
+ return false;
199
+ }
200
+
201
+ // calculate distance from vert0 to ray origin (not really, but okay)
202
+ const tvec_x = origin_x - ax;
203
+ const tvec_y = origin_y - ay;
204
+ const tvec_z = origin_z - az;
205
+
206
+ // calculate u
207
+ let u = v3_dot(tvec_x, tvec_y, tvec_z, pvec_x, pvec_y, pvec_z);
208
+
209
+ if (u < 0 || u > det) {
210
+ // outside of bounds of the triangle
211
+ return false;
212
+ }
213
+
214
+ // prepare to test V parameter
215
+ // CROSS(qvec, tvec, edge1)
216
+ const qvec_x = tvec_y * edge1_z - tvec_z * edge1_y;
217
+ const qvec_y = tvec_z * edge1_x - tvec_x * edge1_z;
218
+ const qvec_z = tvec_x * edge1_y - tvec_y * edge1_x;
219
+
220
+ // calculate V parameter
221
+ let v = v3_dot(direction_x, direction_y, direction_z, qvec_x, qvec_y, qvec_z);
222
+
223
+ if (v < 0 || u + v > det) {
224
+ // out of bounds
225
+ return false;
226
+ }
227
+
228
+
229
+ const inv_det = 1 / det;
230
+
231
+ // calculate t, scale parameter, ray intersects triangle
232
+ const t = v3_dot(edge2_x, edge2_y, edge2_z, qvec_x, qvec_y, qvec_z) * inv_det;
233
+
234
+ u *= inv_det;
235
+ v *= inv_det;
236
+
237
+ result[0] = t;
238
+ result[1] = u;
239
+ result[2] = v;
240
+
241
+ return true;
242
+ }
243
+
244
+ /**
245
+ * Compute barycentric coordinates for triangle intersection
246
+ * NOTE: most of the code is inlined for speed to avoid allocation and function calls
247
+ * @see https://github.com/erich666/jgt-code/blob/master/Volume_02/Number_1/Moller1997a/raytri.c
248
+ * @source https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm (Möller and Trumbore, « Fast, Minimum Storage Ray-Triangle Intersection », Journal of Graphics Tools, vol. 2,‎ 1997, p. 21–28)
249
+ * @param {number[]} result [t,u,v, normal_x, normal_y, normal_z] will be written here
250
+ * @param {number} origin_x
251
+ * @param {number} origin_y
252
+ * @param {number} origin_z
253
+ * @param {number} direction_x
254
+ * @param {number} direction_y
255
+ * @param {number} direction_z
256
+ * @param {number} ax
257
+ * @param {number} ay
258
+ * @param {number} az
259
+ * @param {number} bx
260
+ * @param {number} by
261
+ * @param {number} bz
262
+ * @param {number} cx
263
+ * @param {number} cy
264
+ * @param {number} cz
265
+ * @returns {boolean}
266
+ */
267
+ export function computeTriangleRayIntersectionBarycentric(
268
+ result,
269
+ origin_x, origin_y, origin_z,
270
+ direction_x, direction_y, direction_z,
271
+ ax, ay, az,
272
+ bx, by, bz,
273
+ cx, cy, cz
274
+ ) {
275
+ assert.isNumber(ax, 'ax');
276
+ assert.isNumber(ay, 'ay');
277
+ assert.isNumber(az, 'az');
278
+
279
+ assert.isNumber(bx, 'bx');
280
+ assert.isNumber(by, 'by');
281
+ assert.isNumber(bz, 'bz');
282
+
283
+ assert.isNumber(cx, 'cx');
284
+ assert.isNumber(cy, 'cy');
285
+ assert.isNumber(cz, 'cz');
286
+
287
+ // nan checks
288
+ assert.notNaN(ax, 'ax');
289
+ assert.notNaN(ay, 'ay');
290
+ assert.notNaN(az, 'az');
291
+
292
+ assert.notNaN(bx, 'bx');
293
+ assert.notNaN(by, 'by');
294
+ assert.notNaN(bz, 'bz');
295
+
296
+ assert.notNaN(cx, 'cx');
297
+ assert.notNaN(cy, 'cy');
298
+ assert.notNaN(cz, 'cz');
299
+
300
+ // finate number check
301
+ assert.isFiniteNumber(ax, 'ax');
302
+ assert.isFiniteNumber(ay, 'ay');
303
+ assert.isFiniteNumber(az, 'az');
304
+
305
+ assert.isFiniteNumber(bx, 'bx');
306
+ assert.isFiniteNumber(by, 'by');
307
+ assert.isFiniteNumber(bz, 'bz');
308
+
309
+ assert.isFiniteNumber(cx, 'cx');
310
+ assert.isFiniteNumber(cy, 'cy');
311
+ assert.isFiniteNumber(cz, 'cz');
312
+
313
+
314
+ // find vectors for two edges sharing vert
315
+
316
+ // edge1 = a - b
317
+ const edge1_x = bx - ax;
318
+ const edge1_y = by - ay;
319
+ const edge1_z = bz - az;
320
+
321
+ // edge2 = c - a
322
+ const edge2_x = cx - ax;
323
+ const edge2_y = cy - ay;
324
+ const edge2_z = cz - az;
325
+
326
+ return computeTriangleRayIntersectionBarycentricEdge(result, origin_x, origin_y, origin_z, direction_x, direction_y, direction_z, ax, ay, az, edge1_x, edge1_y, edge1_z, edge2_x, edge2_y, edge2_z);
327
+ }
@@ -305,6 +305,18 @@ class Vector3 {
305
305
  return this;
306
306
  }
307
307
 
308
+ /**
309
+ *
310
+ * @param {Vector3} first
311
+ * @param {Vector3} second
312
+ */
313
+ crossVectors(first, second) {
314
+ const ax = first.x, ay = first.y, az = first.z;
315
+ const bx = second.x, by = second.y, bz = second.z;
316
+
317
+ this._crossVectors(ax, ay, az, bx, by, bz);
318
+ }
319
+
308
320
  /**
309
321
  *
310
322
  * @param {number} ax
@@ -322,18 +334,6 @@ class Vector3 {
322
334
  this.set(x, y, z);
323
335
  }
324
336
 
325
- /**
326
- *
327
- * @param {Vector3} first
328
- * @param {Vector3} second
329
- */
330
- crossVectors(first, second) {
331
- const ax = first.x, ay = first.y, az = first.z;
332
- const bx = second.x, by = second.y, bz = second.z;
333
-
334
- this._crossVectors(ax, ay, az, bx, by, bz);
335
- }
336
-
337
337
  /**
338
338
  * Sets all components of the vector to absolute value (positive)
339
339
  * @returns {Vector3}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * GGX diffuse distribution function
3
+ * @see https://google.github.io/filament/Filament.md.html#listing_diffusebrdf
4
+ * @param {number} NoH dot(n,h) where n is surface normal and h is half unit vector between incident light and outgoing light (view vector)
5
+ * @param {number} roughness
6
+ * @returns {number}
7
+ */
8
+ export function D_GGX(NoH, roughness) {
9
+ const a = NoH * roughness;
10
+ const k = roughness / (1 - NoH * NoH + a * a);
11
+
12
+ return k * k * (1 / Math.PI);
13
+ }
@@ -9,6 +9,10 @@ import { Camera } from "../../../engine/graphics/ecs/camera/Camera.js";
9
9
  import { TransformAttachmentSystem } from "../../../engine/ecs/transform-attachment/TransformAttachmentSystem.js";
10
10
  import InputController from "../../../engine/input/ecs/components/InputController.js";
11
11
  import InputControllerSystem from "../../../engine/input/ecs/systems/InputControllerSystem.js";
12
+ import { GLTFAssetLoader } from "../../../engine/asset/loaders/GLTFAssetLoader.js";
13
+ import {
14
+ three_object_to_entity_composition
15
+ } from "../../../engine/graphics/ecs/mesh-v2/three_object_to_entity_composition.js";
12
16
 
13
17
  const harness = new EngineHarness();
14
18
 
@@ -17,11 +21,13 @@ const harness = new EngineHarness();
17
21
  * @param {Engine} engine
18
22
  */
19
23
  async function main(engine) {
20
- EngineHarness.buildBasics({
24
+ await EngineHarness.buildBasics({
21
25
  engine,
22
26
  cameraController: false
23
27
  });
24
28
 
29
+ const gltf_asset = await engine.assetManager.promise('data/models/sponza-pbr/gltf/sponza.glb', 'gltf');
30
+
25
31
 
26
32
  const ecd = engine.entityManager.dataset;
27
33
 
@@ -46,9 +52,13 @@ async function main(engine) {
46
52
 
47
53
  const controls = new TransformControls(camera.component.object, engine.gameView.el);
48
54
 
55
+ const entityNode = three_object_to_entity_composition(gltf_asset.create());
56
+ entityNode.build(ecd);
57
+ entityNode.transform.scale.multiplyScalar(0.1);
49
58
 
50
59
  controls.build(ecd); // add controls to the scene
51
- controls.attach(cube_entity); // make controls target the cube
60
+ // controls.attach(cube_entity); // make controls target the cube
61
+ controls.attach(entityNode.entity.entity); // make controls target the cube
52
62
 
53
63
  new EntityBuilder()
54
64
  .add(new InputController([{
@@ -76,5 +86,7 @@ harness.initialize({
76
86
  config.addSystem(new ShadedGeometrySystem(engine));
77
87
  config.addSystem(new TransformAttachmentSystem(engine));
78
88
  config.addSystem(new InputControllerSystem(engine.devices));
89
+
90
+ config.addLoader('gltf', new GLTFAssetLoader());
79
91
  }
80
92
  }).then(main);
@@ -6,6 +6,9 @@ import { array_push_if_unique } from "../../../core/collection/array/array_push_
6
6
  import { Transform } from "../transform/Transform.js";
7
7
  import { TransformAttachment, TransformAttachmentFlags } from "../transform-attachment/TransformAttachment.js";
8
8
  import Signal from "../../../core/events/signal/Signal.js";
9
+ import { EntityNodeFlags } from "./EntityNodeFlags.js";
10
+
11
+ const DEFAULT_FLAGS = EntityNodeFlags.LiveManagement;
9
12
 
10
13
  export class EntityNode {
11
14
  /**
@@ -45,6 +48,52 @@ export class EntityNode {
45
48
  built: new Signal(),
46
49
  destroyed: new Signal()
47
50
  };
51
+
52
+ /**
53
+ *
54
+ * @type {number}
55
+ */
56
+ this.flags = DEFAULT_FLAGS;
57
+ }
58
+
59
+ /**
60
+ *
61
+ * @param {number|EntityNodeFlags} flag
62
+ * @returns {void}
63
+ */
64
+ setFlag(flag) {
65
+ this.flags |= flag;
66
+ }
67
+
68
+ /**
69
+ *
70
+ * @param {number|EntityNodeFlags} flag
71
+ * @returns {void}
72
+ */
73
+ clearFlag(flag) {
74
+ this.flags &= ~flag;
75
+ }
76
+
77
+ /**
78
+ *
79
+ * @param {number|EntityNodeFlags} flag
80
+ * @param {boolean} value
81
+ */
82
+ writeFlag(flag, value) {
83
+ if (value) {
84
+ this.setFlag(flag);
85
+ } else {
86
+ this.clearFlag(flag);
87
+ }
88
+ }
89
+
90
+ /**
91
+ *
92
+ * @param {number|EntityNodeFlags} flag
93
+ * @returns {boolean}
94
+ */
95
+ getFlag(flag) {
96
+ return (this.flags & flag) === flag;
48
97
  }
49
98
 
50
99
  /**
@@ -263,6 +312,28 @@ export class EntityNode {
263
312
  return this.__entity.isBuilt;
264
313
  }
265
314
 
315
+ attachListeners() {
316
+ if (this.getFlag(EntityNodeFlags.TransformObserved)) {
317
+ // already observed
318
+ return;
319
+ }
320
+
321
+ this.__transform.position.onChanged.add(this.__transform_sync_down, this);
322
+ this.__transform.scale.onChanged.add(this.__transform_sync_down, this);
323
+ this.__transform.rotation.onChanged.add(this.__transform_sync_down, this);
324
+
325
+ this.setFlag(EntityNodeFlags.TransformObserved);
326
+ }
327
+
328
+ detachListeners() {
329
+
330
+ this.__transform.position.onChanged.remove(this.__transform_sync_down, this);
331
+ this.__transform.scale.onChanged.remove(this.__transform_sync_down, this);
332
+ this.__transform.rotation.onChanged.remove(this.__transform_sync_down, this);
333
+
334
+ this.clearFlag(EntityNodeFlags.TransformObserved);
335
+ }
336
+
266
337
  /**
267
338
  *
268
339
  * @param {EntityComponentDataset} ecd
@@ -303,9 +374,9 @@ export class EntityNode {
303
374
  this.__transform_sync_down();
304
375
 
305
376
  // attach listeners
306
- this.__transform.position.onChanged.add(this.__transform_sync_down, this);
307
- this.__transform.scale.onChanged.add(this.__transform_sync_down, this);
308
- this.__transform.rotation.onChanged.add(this.__transform_sync_down, this);
377
+ if (this.getFlag(EntityNodeFlags.LiveManagement)) {
378
+ this.attachListeners();
379
+ }
309
380
 
310
381
  this.__entity.build(ecd);
311
382
 
@@ -321,6 +392,7 @@ export class EntityNode {
321
392
  this.on.built.send0();
322
393
  }
323
394
 
395
+
324
396
  destroy() {
325
397
  if (!this.__entity.isBuilt) {
326
398
  // not built
@@ -329,10 +401,11 @@ export class EntityNode {
329
401
  return;
330
402
  }
331
403
 
332
- // remove listeners
333
- this.__transform.position.onChanged.remove(this.__transform_sync_down, this);
334
- this.__transform.scale.onChanged.remove(this.__transform_sync_down, this);
335
- this.__transform.rotation.onChanged.remove(this.__transform_sync_down, this);
404
+ if (this.getFlag(EntityNodeFlags.TransformObserved)) {
405
+
406
+ // remove listeners
407
+ this.detachListeners();
408
+ }
336
409
 
337
410
  // destroy children first
338
411
  const children = this.__children;
@@ -0,0 +1,8 @@
1
+ /**
2
+ *
3
+ * @enum {number}
4
+ */
5
+ export const EntityNodeFlags = {
6
+ LiveManagement: 1,
7
+ TransformObserved: 2
8
+ };
@@ -27,16 +27,12 @@ import { mat4 } from "gl-matrix";
27
27
  import { AABB3 } from "../../../../core/bvh2/aabb3/AABB3.js";
28
28
  import { NumericInterval } from "../../../../core/math/interval/NumericInterval.js";
29
29
  import { array_copy } from "../../../../core/collection/array/copyArray.js";
30
+ import { passThrough } from "../../../../core/function/Functions.js";
30
31
 
31
- function extractFaceIndexFromLeaf(leaf) {
32
- return leaf;
33
- }
34
-
35
-
32
+ /**
33
+ * terrain tile is a part of a 2d array
34
+ */
36
35
  class TerrainTile {
37
- /**
38
- * terrain tile is a part of a 2d array
39
- */
40
36
  constructor() {
41
37
  this.gridPosition = new Vector2();
42
38
  this.scale = new Vector2(1, 1);
@@ -103,6 +99,7 @@ class TerrainTile {
103
99
  this.onDestroyed = new Signal();
104
100
 
105
101
  /**
102
+ * Encodes whether stitching has been performed on per-neighbour basis
106
103
  * @private
107
104
  * @type {{bottomLeft: boolean, top: boolean, left: boolean, bottom: boolean, bottomRight: boolean, topLeft: boolean, topRight: boolean, right: boolean}}
108
105
  */
@@ -129,7 +126,7 @@ class TerrainTile {
129
126
 
130
127
  this.raycaster = new BVHGeometryRaycaster();
131
128
  //Binary BVH form doesn't have distinct leaf objects and stores face indices directly, this requires a special face index extractor that treats leaves as indices directly.
132
- this.raycaster.extractFaceIndexFromLeaf = extractFaceIndexFromLeaf;
129
+ this.raycaster.extractFaceIndexFromLeaf = passThrough;
133
130
  }
134
131
 
135
132
  /**
@@ -148,6 +145,7 @@ class TerrainTile {
148
145
  array_copy(m4, 0, this.mesh.matrixWorld.elements, 0, 16);
149
146
  this.computeBoundingBox();
150
147
 
148
+ // raycaster needs to store transform as well
151
149
  array_copy(m4, 0, this.raycaster.transform, 0, 16);
152
150
  }
153
151
 
@@ -1,7 +1,10 @@
1
1
  import Engine from "../../../Engine";
2
2
  import {System} from "../../../ecs/System";
3
3
  import {PathDisplay} from "./PathDisplay";
4
+ import Signal from "../../../../core/events/signal/Signal";
4
5
 
5
6
  export class PathDisplaySystem extends System<PathDisplay> {
6
7
  constructor(engine: Engine)
8
+
9
+ readonly onWorkDone: Signal<void>
7
10
  }
@@ -12,6 +12,7 @@ import { PathEvents } from "../../../navigation/ecs/components/PathEvents.js";
12
12
  import { assert } from "../../../../core/assert.js";
13
13
  import { TubePathBuilder } from "./tube/build/TubePathBuilder.js";
14
14
  import { Deque } from "../../../../core/collection/queue/Deque.js";
15
+ import Signal from "../../../../core/events/signal/Signal.js";
15
16
 
16
17
  const builders = {
17
18
  [PathDisplayType.None]: function (style, path, result) {
@@ -274,6 +275,12 @@ export class PathDisplaySystem extends AbstractContextSystem {
274
275
  * @private
275
276
  */
276
277
  this.__rebuild_queue = new Deque();
278
+
279
+ /**
280
+ *
281
+ * @type {Signal}
282
+ */
283
+ this.onWorkDone = new Signal();
277
284
  }
278
285
 
279
286
  /**
@@ -340,5 +347,8 @@ export class PathDisplaySystem extends AbstractContextSystem {
340
347
  ctx.rebuild();
341
348
 
342
349
  } while ((performance.now() - t0) < UPDATE_PROCESSING_BUDGET_MS && !queue.isEmpty());
350
+
351
+ // notify that some work was done
352
+ this.onWorkDone.send0();
343
353
  }
344
354
  }