@woosh/meep-engine 2.60.1 → 2.62.0

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 (87) hide show
  1. package/build/meep.cjs +20244 -20262
  2. package/build/meep.min.js +1 -1
  3. package/build/meep.module.js +20244 -20262
  4. package/package.json +1 -1
  5. package/src/core/binary/BitSet.js +1 -1
  6. package/src/core/bvh2/BinaryNode.js +16 -13
  7. package/src/core/bvh2/LeafNode.js +6 -3
  8. package/src/core/bvh2/bvh3/query/bvh_query_user_data_overlaps_sphere.js +81 -0
  9. package/src/core/geom/3d/aabb/AABB3.js +24 -36
  10. package/src/core/geom/3d/aabb/aabb3_array_compute_from_sphere.js +22 -0
  11. package/src/core/geom/3d/aabb/aabb3_array_intersects_sphere.js +22 -0
  12. package/src/core/geom/3d/aabb/aabb3_array_intersects_sphere_array.js +11 -0
  13. package/src/core/geom/3d/aabb/aabb3_signed_distance_to_aabb3.js +28 -0
  14. package/src/core/geom/3d/aabb/serializeAABB3Quantized16Uint.js +19 -10
  15. package/src/core/geom/3d/sphere/harmonics/sh3_dering_optimize_positive.js +30 -182
  16. package/src/core/geom/3d/sphere/harmonics/sh3_dering_optimize_positive.spec.js +27 -1
  17. package/src/core/geom/3d/tetrahedra/delaunay/Cavity.js +3 -4
  18. package/src/core/geom/ConicRay.js +16 -21
  19. package/src/core/geom/ConicRay.spec.js +24 -0
  20. package/src/core/geom/packing/miniball/Miniball.js +68 -117
  21. package/src/core/geom/packing/miniball/Miniball.spec.js +3 -3
  22. package/src/core/geom/packing/miniball/Subspan.js +47 -34
  23. package/src/core/geom/packing/miniball/miniball_compute_quality.js +64 -0
  24. package/src/core/math/bessel_3.js +1 -0
  25. package/src/core/math/random/randomBytes.js +2 -2
  26. package/src/core/math/sqr.js +8 -0
  27. package/src/core/model/node-graph/Connection.js +21 -23
  28. package/src/core/model/node-graph/DataType.js +16 -17
  29. package/src/core/model/node-graph/NodeGraph.js +49 -50
  30. package/src/core/model/node-graph/node/NodeDescription.js +42 -44
  31. package/src/core/model/node-graph/node/NodeInstance.js +59 -60
  32. package/src/core/model/node-graph/node/NodeInstancePortReference.js +27 -29
  33. package/src/core/model/node-graph/node/NodeRegistry.js +16 -18
  34. package/src/core/model/node-graph/node/Port.js +35 -37
  35. package/src/core/model/node-graph/node/parameter/NodeParameterDescription.js +27 -13
  36. package/src/core/path/computePathDirectory.spec.js +8 -0
  37. package/src/core/process/BaseProcess.d.ts +5 -0
  38. package/src/core/process/WatchDog.js +76 -75
  39. package/src/core/process/action/AsynchronousAction.js +24 -22
  40. package/src/core/process/executor/profile/Profile.js +34 -24
  41. package/src/core/process/executor/profile/TraceEvent.js +75 -75
  42. package/src/core/process/worker/OnDemandWorkerManager.js +27 -30
  43. package/src/core/process/worker/WorkerBuilder.js +149 -149
  44. package/src/core/process/worker/WorkerProxy.js +25 -21
  45. package/src/core/process/worker/extractTransferables.js +2 -2
  46. package/src/engine/Engine.js +58 -53
  47. package/src/engine/EngineConfiguration.d.ts +4 -4
  48. package/src/engine/ecs/EntityManager.js +517 -614
  49. package/src/engine/ecs/System.js +2 -2
  50. package/src/engine/ecs/foliage/ecs/Foliage2System.js +3 -0
  51. package/src/engine/ecs/foliage/ecs/InstancedMeshComponent.js +4 -1
  52. package/src/engine/ecs/foliage/ecs/convertInstancedMeshComponents2Entities.js +64 -0
  53. package/src/engine/ecs/foliage/ecs/{InstancedMeshUtils.js → optimizeIndividualMeshesEntitiesToInstances.js} +11 -70
  54. package/src/engine/ecs/transform/Transform.d.ts +7 -5
  55. package/src/engine/ecs/transform/Transform.js +30 -16
  56. package/src/engine/ecs/validateSystem.js +89 -0
  57. package/src/engine/graphics/GraphicsEngine.js +433 -483
  58. package/src/engine/graphics/camera/testClippingPlaneComputation.js +42 -46
  59. package/src/engine/graphics/ecs/decal/v2/FPDecalSystem.js +2 -2
  60. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +2 -2
  61. package/src/engine/graphics/ecs/mesh-v2/aggregate/SGMeshSystem.js +2 -2
  62. package/src/engine/graphics/ecs/path/testPathDisplaySystem.js +49 -52
  63. package/src/engine/graphics/ecs/path/tube/prototypeAnimatedPathMask.js +52 -56
  64. package/src/engine/graphics/ecs/water2/shader/testWaterShader.js +20 -22
  65. package/src/engine/graphics/particles/ecs/ParticleEmitterSystem.js +43 -25
  66. package/src/engine/graphics/particles/particular/engine/ParticularEngine.js +160 -180
  67. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.js +37 -41
  68. package/src/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +63 -67
  69. package/src/engine/graphics/render/buffer/buffers/prototypeNormalFrameBuffer.js +46 -50
  70. package/src/engine/graphics/render/forward_plus/plugin/ptototypeFPPlugin.js +61 -65
  71. package/src/engine/graphics/render/visibility/hiz/prototypeHiZ.js +48 -52
  72. package/src/engine/graphics/shadows/testShadowMapRendering.js +28 -32
  73. package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_lanczos.spec.js +22 -20
  74. package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_linear.js +10 -13
  75. package/src/engine/graphics/texture/virtual/VirtualTexture.spec.js +1 -1
  76. package/src/engine/plugin/EnginePluginManager.d.ts +6 -1
  77. package/src/engine/sound/dB2Volume.js +8 -0
  78. package/src/engine/sound/ecs/emitter/SoundEmitter.js +125 -99
  79. package/src/engine/sound/ecs/emitter/SoundEmitterComponentContext.js +4 -42
  80. package/src/engine/sound/ecs/emitter/SoundEmitterSystem.js +31 -121
  81. package/src/engine/sound/volume2dB.js +8 -0
  82. package/src/generation/theme/ThemeEngine.js +19 -53
  83. package/src/engine/ecs/components/ClingToHeightMap.js +0 -19
  84. package/src/engine/ecs/components/SynchronizePosition.js +0 -15
  85. package/src/engine/ecs/systems/ClingToHeightMapSystem.js +0 -170
  86. package/src/engine/ecs/systems/SynchronizePositionSystem.js +0 -43
  87. package/src/engine/graphics/geometry/bvh/buffered/BVHFromBufferGeometry.js +0 -133
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "description": "Fully featured ECS game engine written in JavaScript",
6
6
  "type": "module",
7
7
  "author": "Alexander Goldring",
8
- "version": "2.60.1",
8
+ "version": "2.62.0",
9
9
  "main": "build/meep.module.js",
10
10
  "module": "build/meep.module.js",
11
11
  "exports": {
@@ -457,7 +457,7 @@ BitSet.prototype.get = function (bitIndex) {
457
457
  assert.isNonNegativeInteger(bitIndex, "bitIndex");
458
458
 
459
459
  if (bitIndex >= this.__length) {
460
- //bit is outside of the recorded region
460
+ //bit is outside the recorded region
461
461
  return false;
462
462
  }
463
463
 
@@ -3,19 +3,19 @@
3
3
  */
4
4
 
5
5
 
6
- import { assert } from "../assert.js";
7
- import { arrayQuickSort } from "../collection/array/arrayQuickSort.js";
8
- import { max2 } from "../math/max2.js";
9
- import { min2 } from "../math/min2.js";
10
- import { computeSampleStandardDeviation } from "../math/statistics/computeSampleStandardDeviation.js";
11
- import { isLeaf, LeafNode } from "./LeafNode.js";
12
- import { Node } from "./Node.js";
13
- import { surfaceAreaHeuristic } from "./sah/surfaceAreaHeuristic.js";
14
- import { BVHVisitor } from "./traversal/BVHVisitor.js";
15
- import { traverseBinaryNodeUsingVisitor } from "./traversal/traverseBinaryNodeUsingVisitor.js";
16
- import { aabb3_intersects_aabb3 } from "../geom/3d/aabb/aabb3_intersects_aabb3.js";
17
- import { aabb3_box_surface_area_2 } from "../geom/3d/aabb/aabb3_box_surface_area_2.js";
18
- import { aabb3_combined_surface_area } from "../geom/3d/aabb/aabb3_combined_surface_area.js";
6
+ import {assert} from "../assert.js";
7
+ import {arrayQuickSort} from "../collection/array/arrayQuickSort.js";
8
+ import {aabb3_box_surface_area_2} from "../geom/3d/aabb/aabb3_box_surface_area_2.js";
9
+ import {aabb3_combined_surface_area} from "../geom/3d/aabb/aabb3_combined_surface_area.js";
10
+ import {aabb3_intersects_aabb3} from "../geom/3d/aabb/aabb3_intersects_aabb3.js";
11
+ import {max2} from "../math/max2.js";
12
+ import {min2} from "../math/min2.js";
13
+ import {computeSampleStandardDeviation} from "../math/statistics/computeSampleStandardDeviation.js";
14
+ import {isLeaf, LeafNode} from "./LeafNode.js";
15
+ import {Node} from "./Node.js";
16
+ import {surfaceAreaHeuristic} from "./sah/surfaceAreaHeuristic.js";
17
+ import {BVHVisitor} from "./traversal/BVHVisitor.js";
18
+ import {traverseBinaryNodeUsingVisitor} from "./traversal/traverseBinaryNodeUsingVisitor.js";
19
19
 
20
20
 
21
21
  /**
@@ -36,6 +36,9 @@ let stackPointer = 0;
36
36
  */
37
37
  const stack = [];
38
38
 
39
+ /**
40
+ * @deprecated use {@link ExplicitBinaryBoundingVolumeHierarchy} instead
41
+ */
39
42
  export class BinaryNode extends Node {
40
43
  constructor() {
41
44
  super();
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Created by Alex on 17/11/2014.
3
3
  */
4
- import { Node } from "./Node.js";
5
- import { serializeAABB3 } from "../geom/3d/aabb/serializeAABB3.js";
6
- import { deserializeAABB3 } from "../geom/3d/aabb/deserializeAABB3.js";
4
+ import {deserializeAABB3} from "../geom/3d/aabb/deserializeAABB3.js";
5
+ import {serializeAABB3} from "../geom/3d/aabb/serializeAABB3.js";
6
+ import {Node} from "./Node.js";
7
7
 
8
8
 
9
9
  /**
@@ -15,6 +15,9 @@ function isLeaf(node) {
15
15
  return node.isLeafNode;
16
16
  }
17
17
 
18
+ /**
19
+ * @deprecated use {@link ExplicitBinaryBoundingVolumeHierarchy} and {@link EBBVHLeafProxy} respectively
20
+ */
18
21
  export class LeafNode extends Node {
19
22
  /**
20
23
  *
@@ -0,0 +1,81 @@
1
+ import {SCRATCH_UINT32_TRAVERSAL_STACK} from "../../../collection/SCRATCH_UINT32_TRAVERSAL_STACK.js";
2
+ import {aabb3_array_intersects_sphere_array} from "../../../geom/3d/aabb/aabb3_array_intersects_sphere_array.js";
3
+ import {NULL_NODE} from "../ExplicitBinaryBoundingVolumeHierarchy.js";
4
+ import {bvh_collect_user_data} from "./bvh_collect_user_data.js";
5
+
6
+ const stack = SCRATCH_UINT32_TRAVERSAL_STACK;
7
+
8
+ const scratch_aabb = new Float32Array(6);
9
+
10
+ /**
11
+ *
12
+ * @param {number[]} result
13
+ * @param {number} result_offset
14
+ * @param {ExplicitBinaryBoundingVolumeHierarchy} bvh
15
+ * @param {number[]|Float32Array} sphere
16
+ */
17
+ export function bvh_query_user_data_overlaps_sphere(
18
+ result,
19
+ result_offset,
20
+ bvh,
21
+ sphere
22
+ ) {
23
+ const root = bvh.root;
24
+
25
+ if (root === NULL_NODE) {
26
+ return 0;
27
+ }
28
+
29
+ /**
30
+ *
31
+ * @type {number}
32
+ */
33
+ const stack_top = stack.pointer++;
34
+
35
+ stack[stack_top] = root;
36
+
37
+ let result_cursor = result_offset;
38
+
39
+ while (stack.pointer > stack_top) {
40
+ stack.pointer--;
41
+
42
+ /**
43
+ *
44
+ * @type {number}
45
+ */
46
+ const node = stack[stack.pointer];
47
+
48
+ // test node against the ray
49
+ bvh.node_get_aabb(node, scratch_aabb);
50
+ const intersection = aabb3_array_intersects_sphere_array(scratch_aabb, sphere);
51
+
52
+ if (intersection === 0) {
53
+ // fully outside
54
+ continue;
55
+ }
56
+
57
+ const node_is_leaf = bvh.node_is_leaf(node);
58
+
59
+ if (node_is_leaf) {
60
+ // leaf node
61
+ result[result_cursor++] = bvh.node_get_user_data(node);
62
+
63
+ } else if (intersection === 2) {
64
+ // fully inside, fast collection path
65
+ result_cursor += bvh_collect_user_data(result, result_cursor, bvh, node);
66
+ } else {
67
+ // partially inside
68
+ // read in-order
69
+ const child1 = bvh.node_get_child1(node);
70
+ const child2 = bvh.node_get_child2(node);
71
+
72
+ // write to stack in reverse order, so that fist child ends up being visited first
73
+ stack[stack.pointer++] = child1;
74
+ stack[stack.pointer++] = child2;
75
+ }
76
+ }
77
+
78
+ // drop stack frame
79
+
80
+ return result_cursor - result_offset;
81
+ }
@@ -3,22 +3,24 @@
3
3
  */
4
4
 
5
5
 
6
- import { aabb3_compute_distance_above_plane_max } from "./aabb3_compute_distance_above_plane_max.js";
7
- import { aabb3_compute_plane_side } from "./aabb3_compute_plane_side.js";
6
+ import {assert} from "../../../assert.js";
8
7
  import computeMortonCode from "../morton/Morton.js";
9
- import { aabb3_build_corners } from "./aabb3_build_corners.js";
10
- import { assert } from "../../../assert.js";
11
- import { aabb3_intersects_frustum_degree } from "./aabb3_intersects_frustum_degree.js";
12
- import { aabb3_intersects_line_segment } from "./aabb3_intersects_line_segment.js";
13
- import { aabb3_intersects_ray } from "./aabb3_intersects_ray.js";
14
- import { aabb3_compute_surface_area } from "./aabb3_compute_surface_area.js";
15
- import { aabb3_intersects_frustum_array } from "./aabb3_intersects_frustum_array.js";
16
- import { aabb3_matrix4_project } from "./aabb3_matrix4_project.js";
17
- import { aabb3_signed_distance_sqr_to_point } from "./aabb3_signed_distance_sqr_to_point.js";
18
- import { aabb3_array_intersects_point } from "./aabb3_array_intersects_point.js";
8
+ import {aabb3_array_intersects_point} from "./aabb3_array_intersects_point.js";
9
+ import {aabb3_build_corners} from "./aabb3_build_corners.js";
10
+ import {aabb3_compute_distance_above_plane_max} from "./aabb3_compute_distance_above_plane_max.js";
11
+ import {aabb3_compute_plane_side} from "./aabb3_compute_plane_side.js";
12
+ import {aabb3_compute_surface_area} from "./aabb3_compute_surface_area.js";
13
+ import {aabb3_intersects_frustum_array} from "./aabb3_intersects_frustum_array.js";
14
+ import {aabb3_intersects_frustum_degree} from "./aabb3_intersects_frustum_degree.js";
15
+ import {aabb3_intersects_line_segment} from "./aabb3_intersects_line_segment.js";
16
+ import {aabb3_intersects_ray} from "./aabb3_intersects_ray.js";
17
+ import {aabb3_matrix4_project} from "./aabb3_matrix4_project.js";
18
+ import {aabb3_signed_distance_sqr_to_point} from "./aabb3_signed_distance_sqr_to_point.js";
19
+ import {aabb3_signed_distance_to_aabb3} from "./aabb3_signed_distance_to_aabb3.js";
19
20
 
20
21
  /**
21
22
  * Axis-Aligned bounding box in 3D
23
+ * NOTE: In cases where all you want is raw performance - prefer to use typed arrays instead along with `aabb3_` functions
22
24
  */
23
25
  export class AABB3 {
24
26
  /**
@@ -238,7 +240,12 @@ export class AABB3 {
238
240
  * @returns {boolean}
239
241
  */
240
242
  _equals(x0, y0, z0, x1, y1, z1) {
241
- return this.x0 === x0 && this.y0 === y0 && this.z0 === z0 && this.x1 === x1 && this.y1 === y1 && this.z1 === z1;
243
+ return this.x0 === x0
244
+ && this.y0 === y0
245
+ && this.z0 === z0
246
+ && this.x1 === x1
247
+ && this.y1 === y1
248
+ && this.z1 === z1;
242
249
  }
243
250
 
244
251
  /**
@@ -332,28 +339,10 @@ export class AABB3 {
332
339
  const _y1 = this.y1;
333
340
  const _z1 = this.z1;
334
341
 
335
- //do projection
336
- const xp0 = _x0 - x1;
337
- const xp1 = x0 - _x1;
338
- const yp0 = _y0 - y1;
339
- const yp1 = y0 - _y1;
340
- const zp0 = _z0 - z1;
341
- const zp1 = z0 - _z1;
342
-
343
- //calculate separation in each axis
344
- const dx = Math.max(xp0, xp1);
345
- const dy = Math.max(yp0, yp1);
346
- const dz = Math.max(zp0, zp1);
347
-
348
- //straight-line distance
349
- let distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
350
-
351
- if (dx < 0 && dy < 0 && dz < 0) {
352
- //penetration
353
- return -distance;
354
- } else {
355
- return distance;
356
- }
342
+ return aabb3_signed_distance_to_aabb3(
343
+ _x0, _y0, _z0, _x1, _y1, _z1,
344
+ x0, y0, z0, x1, y1, z1
345
+ );
357
346
  }
358
347
 
359
348
  /**
@@ -778,7 +767,6 @@ export class AABB3 {
778
767
  }
779
768
 
780
769
  /**
781
- *
782
770
  * @param {Plane[]} clippingPlanes
783
771
  * @returns {boolean}
784
772
  */
@@ -0,0 +1,22 @@
1
+ /**
2
+ *
3
+ * @param {number[]} output
4
+ * @param {number} output_offset
5
+ * @param {number} x
6
+ * @param {number} y
7
+ * @param {number} z
8
+ * @param {number} radius
9
+ */
10
+ export function aabb3_array_compute_from_sphere(
11
+ output, output_offset,
12
+ x, y, z, radius
13
+ ) {
14
+
15
+ output[output_offset] = x - radius;
16
+ output[output_offset + 1] = y - radius;
17
+ output[output_offset + 2] = z - radius;
18
+ output[output_offset + 3] = x + radius;
19
+ output[output_offset + 4] = y + radius;
20
+ output[output_offset + 5] = z + radius;
21
+
22
+ }
@@ -0,0 +1,22 @@
1
+ import {aabb3_signed_distance_sqr_to_point} from "./aabb3_signed_distance_sqr_to_point.js";
2
+
3
+ /**
4
+ *
5
+ * @param {number[]} aabb
6
+ * @param {number} x
7
+ * @param {number} y
8
+ * @param {number} z
9
+ * @param {number} radius
10
+ * @returns {boolean}
11
+ */
12
+ export function aabb3_array_intersects_sphere(aabb, x, y, z, radius) {
13
+ const distance_sqr = aabb3_signed_distance_sqr_to_point(
14
+ aabb[0], aabb[1], aabb[2],
15
+ aabb[3], aabb[4], aabb[5],
16
+ x, y, z
17
+ );
18
+
19
+ const radius2 = radius * radius;
20
+
21
+ return distance_sqr <= radius2;
22
+ }
@@ -0,0 +1,11 @@
1
+ import {aabb3_array_intersects_sphere} from "./aabb3_array_intersects_sphere.js";
2
+
3
+ /**
4
+ *
5
+ * @param {number[]|Float32Array} aabb
6
+ * @param {number[]|Float32Array} sphere [x,y,z, radius]
7
+ * @returns {boolean}
8
+ */
9
+ export function aabb3_array_intersects_sphere_array(aabb, sphere) {
10
+ return aabb3_array_intersects_sphere(aabb, sphere[0], sphere[1], sphere[2], sphere[3]);
11
+ }
@@ -0,0 +1,28 @@
1
+ export function aabb3_signed_distance_to_aabb3(
2
+ ax0,ay0,az0,ax1,ay1,az1,
3
+ bx0,by0,bz0, bx1,by1,bz1
4
+ ){
5
+
6
+ //do projection
7
+ const xp0 = bx0 - ax1;
8
+ const xp1 = ax0 - bx1;
9
+ const yp0 = by0 - ay1;
10
+ const yp1 = ay0 - by1;
11
+ const zp0 = bz0 - az1;
12
+ const zp1 = az0 - bz1;
13
+
14
+ //calculate separation in each axis
15
+ const dx = Math.max(xp0, xp1);
16
+ const dy = Math.max(yp0, yp1);
17
+ const dz = Math.max(zp0, zp1);
18
+
19
+ //straight-line distance
20
+ let distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
21
+
22
+ if (dx < 0 && dy < 0 && dz < 0) {
23
+ //penetration
24
+ return -distance;
25
+ } else {
26
+ return distance;
27
+ }
28
+ }
@@ -9,20 +9,29 @@
9
9
  * @param {number} y1
10
10
  * @param {number} z1
11
11
  */
12
- export function serializeAABB3Quantized16Uint(buffer, box, x0, y0, z0, x1, y1, z1) {
12
+ export function serializeAABB3Quantized16Uint(
13
+ buffer, box,
14
+ x0, y0, z0, x1, y1, z1
15
+ ) {
16
+
13
17
  //compute value ranges
14
- const xD = x1 - x0;
15
- const yD = y1 - y0;
16
- const zD = z1 - z0;
18
+ const dx = x1 - x0;
19
+ const dy = y1 - y0;
20
+ const dz = z1 - z0;
21
+
22
+ // scale delta to Uin16 range, and avoid division by 0
23
+ const mx = dx > 0 ? 65535 / dx : 0;
24
+ const my = dy > 0 ? 65535 / dy : 0;
25
+ const mz = dz > 0 ? 65535 / dz : 0;
17
26
 
18
27
  //quantize all values
19
- const _x0 = (((box.x0 - x0) / xD) * 65535) | 0;
20
- const _y0 = (((box.y0 - y0) / yD) * 65535) | 0;
21
- const _z0 = (((box.z0 - z0) / zD) * 65535) | 0;
28
+ const _x0 = Math.floor((box.x0 - x0) * mx);
29
+ const _y0 = Math.floor((box.y0 - y0) * my);
30
+ const _z0 = Math.floor((box.z0 - z0) * mz);
22
31
 
23
- const _x1 = (((box.x1 - x0) / xD) * 65535) | 0;
24
- const _y1 = (((box.y1 - y0) / yD) * 65535) | 0;
25
- const _z1 = (((box.z1 - z0) / zD) * 65535) | 0;
32
+ const _x1 = Math.ceil((box.x1 - x0) * mx);
33
+ const _y1 = Math.ceil((box.y1 - y0) * my);
34
+ const _z1 = Math.ceil((box.z1 - z0) * mz);
26
35
 
27
36
  buffer.writeUint16(_x0);
28
37
  buffer.writeUint16(_y0);
@@ -1,18 +1,26 @@
1
- import { max2 } from "../../../../math/max2.js";
2
- import { mat3, vec3 } from "gl-matrix";
3
- import { array_copy } from "../../../../collection/array/array_copy.js";
4
- import { min2 } from "../../../../math/min2.js";
5
- import { assert } from "../../../../assert.js";
1
+ import {mat3, vec3} from "gl-matrix";
2
+ import {assert} from "../../../../assert.js";
3
+ import {array_copy} from "../../../../collection/array/array_copy.js";
4
+ import {min2} from "../../../../math/min2.js";
6
5
 
7
6
  /*
8
7
  @see https://github.com/Bestmaker602/olacziy/blob/212b64ea5f1856b390cdf7629801243f76a4466d/libs/ibl/src/CubemapSH.cpp
9
8
  */
10
9
 
10
+
11
+ const F_PI = 3.14159265358979323846264338327950288;
12
+ const F_SQRT1_2 = 0.707106781186547524400844362104849039;
13
+ const M_SQRT_3 = 1.7320508076;
14
+
15
+ const M_SQRT_PI = 1.7724538509;
16
+ const M_SQRT_5 = 2.2360679775;
17
+ const M_SQRT_15 = 3.8729833462;
18
+
11
19
  /**
12
20
  *
13
21
  * @param {number[]} M 5x5 matrix
14
22
  * @param {number[]} x vec5
15
- * @return {number[]}
23
+ * @return {number[]} vec5
16
24
  */
17
25
  function multiply_5d(M, x) {
18
26
  return [
@@ -24,51 +32,6 @@ function multiply_5d(M, x) {
24
32
  ];
25
33
  }
26
34
 
27
-
28
- /**
29
- * returns n! / d!
30
- * @param {number} n
31
- * @param {number} d
32
- * @return {number}
33
- */
34
- function factorial(n, d = 1) {
35
- let _d = max2(1, d);
36
- let _n = max2(1, n);
37
-
38
- let r = 1.0;
39
-
40
- if (_n === _d) {
41
- // intentionally left blank
42
- } else if (_n > _d) {
43
- for (; _n > _d; _n--) {
44
- r *= _n;
45
- }
46
- } else {
47
- for (; _d > _n; _d--) {
48
- r *= _d;
49
- }
50
- r = 1.0 / r;
51
- }
52
- return r;
53
- }
54
-
55
- const F_PI = 3.14159265358979323846264338327950288;
56
- const F_2_SQRTPI = 1.12837916709551257389615890312154517;
57
- const F_SQRT2 = 1.41421356237309504880168872420969808;
58
- const F_SQRT1_2 = 0.707106781186547524400844362104849039;
59
- const M_SQRT_3 = 1.7320508076;
60
-
61
- /**
62
- * SH scaling factors
63
- */
64
- function Kml(m, l) {
65
- // returns sqrt((2*l + 1) / 4*pi) * sqrt( (l-|m|)! / (l+|m|)! )
66
-
67
- m = m < 0 ? -m : m; // abs() is not constexpr
68
- const K = (2 * l + 1) * factorial((l - m), (l + m));
69
- return Math.sqrt(K) * (F_2_SQRTPI * 0.25);
70
- }
71
-
72
35
  /**
73
36
  * compute Index of spherical harmonics coefficient
74
37
  * @param {number} m signed offset from zonal harmonic
@@ -79,127 +42,6 @@ function SHindex(m, l) {
79
42
  return l * (l + 1) + m;
80
43
  }
81
44
 
82
- /**
83
- *
84
- * @param {number[]} result
85
- * @param {number} result_offset
86
- * @param {number} numBands
87
- */
88
- function Ki(result, result_offset, numBands) {
89
- for (let l = 0; l < numBands; l++) {
90
- result[SHindex(0, l) + result_offset] = Kml(0, l);
91
- for (let m = 1; m <= l; m++) {
92
- result[SHindex(m, l) + result_offset] =
93
- result[SHindex(-m, l) + result_offset] = F_SQRT2 * Kml(m, l);
94
- }
95
- }
96
- }
97
-
98
- /**
99
- * < cos(theta) > SH coefficients pre-multiplied by 1 / K(0,l)
100
- * @param {number} l
101
- * @returns {number}
102
- */
103
- function computeTruncatedCosSh(l) {
104
- if (l === 0) {
105
- return F_PI;
106
- } else if (l === 1) {
107
- return 2 * F_PI / 3;
108
- } else if ((l & 1) !== 0) {
109
- return 0;
110
- }
111
-
112
- const l_2 = l / 2;
113
- const A0 = ((l_2 & 1) ? 1.0 : -1.0) / ((l + 2) * (l - 1));
114
- const A1 = factorial(l, l_2) / (factorial(l_2) * (1 << l));
115
- return 2 * F_PI * A0 * A1;
116
- }
117
-
118
- /*
119
- * Calculates non-normalized SH bases, i.e.:
120
- * m > 0, cos(m*phi) * P(m,l)
121
- * m < 0, sin(|m|*phi) * P(|m|,l)
122
- * m = 0, P(0,l)
123
- */
124
- function computeShBasis(SHb, result_offset, numBands, sx, sy, sz) {
125
-
126
- /*
127
- * TODO: all the Legendre computation below is identical for all faces, so it
128
- * might make sense to pre-compute it once. Also note that there is
129
- * a fair amount of symmetry within a face (which we could take advantage of
130
- * to reduce the pre-compute table).
131
- */
132
-
133
- /*
134
- * Below, we compute the associated Legendre polynomials using recursion.
135
- * see: http://mathworld.wolfram.com/AssociatedLegendrePolynomial.html
136
- *
137
- * Note [0]: sz == cos(theta) ==> we only need to compute P(sz)
138
- *
139
- * Note [1]: We in fact compute P(sz) / sin(theta)^|m|, by removing
140
- * the "sqrt(1 - sz*sz)" [i.e.: sin(theta)] factor from the recursion.
141
- * This is later corrected in the ( cos(m*phi), sin(m*phi) ) recursion.
142
- */
143
-
144
- // s = (x, y, z) = (sin(theta)*cos(phi), sin(theta)*sin(phi), cos(theta))
145
-
146
- // handle m=0 separately, since it produces only one coefficient
147
- let Pml_2 = 0;
148
- let Pml_1 = 1;
149
- SHb[result_offset + 0] = Pml_1;
150
- for (let l = 1; l < numBands; l++) {
151
- const Pml = ((2 * l - 1.0) * Pml_1 * sz - (l - 1.0) * Pml_2) / l;
152
- Pml_2 = Pml_1;
153
- Pml_1 = Pml;
154
- SHb[result_offset + SHindex(0, l)] = Pml;
155
- }
156
- let Pmm = 1;
157
- for (let m = 1; m < numBands; m++) {
158
- Pmm = (1.0 - 2 * m) * Pmm; // See [1], divide by sqrt(1 - sz*sz);
159
- Pml_2 = Pmm;
160
- Pml_1 = (2 * m + 1.0) * Pmm * sz;
161
- // l == m
162
- SHb[result_offset + SHindex(-m, m)] = Pml_2;
163
- SHb[result_offset + SHindex(m, m)] = Pml_2;
164
- if (m + 1 < numBands) {
165
- // l == m+1
166
- SHb[result_offset + SHindex(-m, m + 1)] = Pml_1;
167
- SHb[result_offset + SHindex(m, m + 1)] = Pml_1;
168
- for (let l = m + 2; l < numBands; l++) {
169
- const Pml = ((2 * l - 1.0) * Pml_1 * sz - (l + m - 1.0) * Pml_2) / (l - m);
170
- Pml_2 = Pml_1;
171
- Pml_1 = Pml;
172
- SHb[result_offset + SHindex(-m, l)] = Pml;
173
- SHb[result_offset + SHindex(m, l)] = Pml;
174
- }
175
- }
176
- }
177
-
178
- // At this point, SHb contains the associated Legendre polynomials divided
179
- // by sin(theta)^|m|. Below we compute the SH basis.
180
- //
181
- // ( cos(m*phi), sin(m*phi) ) recursion:
182
- // cos(m*phi + phi) == cos(m*phi)*cos(phi) - sin(m*phi)*sin(phi)
183
- // sin(m*phi + phi) == sin(m*phi)*cos(phi) + cos(m*phi)*sin(phi)
184
- // cos[m+1] == cos[m]*sx - sin[m]*sy
185
- // sin[m+1] == sin[m]*sx + cos[m]*sy
186
- //
187
- // Note that (d.x, d.y) == (cos(phi), sin(phi)) * sin(theta), so the
188
- // code below actually evaluates:
189
- // (cos((m*phi), sin(m*phi)) * sin(theta)^|m|
190
- let Cm = sx;
191
- let Sm = sy;
192
- for (let m = 1; m <= numBands; m++) {
193
- for (let l = m; l < numBands; l++) {
194
- SHb[result_offset + SHindex(-m, l)] *= Sm;
195
- SHb[result_offset + SHindex(m, l)] *= Cm;
196
- }
197
- const Cm1 = Cm * sx - Sm * sy;
198
- const Sm1 = Sm * sx + Cm * sy;
199
- Cm = Cm1;
200
- Sm = Sm1;
201
- }
202
- }
203
45
 
204
46
  /*
205
47
  * utilities to rotate very low order spherical harmonics (up to 3rd band)
@@ -247,10 +89,10 @@ function rotateShericalHarmonicBand1(band1, M) {
247
89
  /**
248
90
  * This projects a vec3 to SH2/k space (i.e. we premultiply by 1/k)
249
91
  * below can't be constexpr
250
- * @return {number[]} vec5
251
92
  * @param {number} x
252
93
  * @param {number} y
253
94
  * @param {number} z
95
+ * @return {number[]} vec5
254
96
  */
255
97
  function project_v3_to_sh(x, y, z) {
256
98
  return [
@@ -266,7 +108,7 @@ function project_v3_to_sh(x, y, z) {
266
108
  *
267
109
  * @param {number[]} band2 vec5
268
110
  * @param {number[]} M mat3
269
- * @return {*}
111
+ * @return {number[]} vec5
270
112
  */
271
113
  function rotateShericalHarmonicBand2(band2, M) {
272
114
  const n = F_SQRT1_2;
@@ -310,7 +152,7 @@ function rotateShericalHarmonicBand2(band2, M) {
310
152
  return result;
311
153
  }
312
154
 
313
- /*
155
+ /**
314
156
  * SH from environment with high dynamic range (or high frequencies -- high dynamic range creates
315
157
  * high frequencies) exhibit "ringing" and negative values when reconstructed.
316
158
  * To mitigate this, we need to low-pass the input image -- or equivalently window the SH by
@@ -321,7 +163,9 @@ function rotateShericalHarmonicBand2(band2, M) {
321
163
  * Deringing Spherical Harmonics
322
164
  * by Peter-Pike Sloan
323
165
  * https://www.ppsloan.org/publications/shdering.pdf
324
- *
166
+ * @param {number} w
167
+ * @param {number} l
168
+ * @returns {number}
325
169
  */
326
170
  function sincWindow(l, w) {
327
171
  if (l === 0) {
@@ -379,10 +223,6 @@ function shmin(input_sh3) {
379
223
  // See "Deringing Spherical Harmonics" by Peter-Pike Sloan
380
224
  // https://www.ppsloan.org/publications/shdering.pdf
381
225
 
382
- const M_SQRT_PI = 1.7724538509;
383
- const M_SQRT_3 = 1.7320508076;
384
- const M_SQRT_5 = 2.2360679775;
385
- const M_SQRT_15 = 3.8729833462;
386
226
  const A = [
387
227
  1.0 / (2.0 * M_SQRT_PI), // 0: 0 0
388
228
  -M_SQRT_3 / (2.0 * M_SQRT_PI), // 1: 1 -1
@@ -545,9 +385,13 @@ function windowing(f, cutoff, numBands) {
545
385
  * @param {number} input_offset
546
386
  * @param {number} numBands
547
387
  * @param {number} channel_count
548
- * @param {number} cutoff
388
+ * @param {number} [cutoff]
549
389
  */
550
- function windowSH(output, output_offset, input, input_offset, numBands, channel_count, cutoff) {
390
+ function windowSH(
391
+ output, output_offset,
392
+ input, input_offset,
393
+ numBands, channel_count, cutoff = 0
394
+ ) {
551
395
  assert.isNonNegativeInteger(channel_count, 'channel_count');
552
396
  assert.greaterThan(channel_count, 0, 'channel_count must be greater than 0');
553
397
 
@@ -575,13 +419,17 @@ function windowSH(output, output_offset, input, input_offset, numBands, channel_
575
419
  // find a cut-off band that works
576
420
  let l = numBands;
577
421
  let r = cutoff;
422
+
578
423
  for (let i = 0; i < 16 && l + 0.1 < r; i++) {
424
+
579
425
  const m = 0.5 * (l + r);
426
+
580
427
  if (shmin(windowing(SH, m, numBands)) < 0) {
581
428
  r = m;
582
429
  } else {
583
430
  l = m;
584
431
  }
432
+
585
433
  }
586
434
 
587
435
  // record minimum cutoff