@woosh/meep-engine 2.43.1 → 2.43.4
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/BinaryBuffer.js +13 -1
- package/core/binary/BitSet.js +2 -2
- package/core/bvh2/aabb3/aabb3_array_combine.js +2 -2
- package/core/collection/RingBuffer.js +4 -2
- package/core/collection/RingBuffer.spec.js +59 -0
- package/core/collection/array/ArrayIteratorRandom.js +1 -1
- package/core/collection/{ArrayUtils.spec.js → array/arrayPickBestElement.spec.js} +1 -1
- package/core/collection/array/arrayPickBestElements.js +51 -0
- package/core/collection/array/arrayPickMinElement.js +43 -0
- package/core/collection/array/arrayQuickSort.js +1 -1
- package/core/collection/array/arraySetSortingDiff.js +1 -1
- package/core/collection/array/arraySwapElements.js +12 -0
- package/core/collection/array/array_range_equal_strict.js +22 -0
- package/core/collection/array/groupArrayBy.js +42 -0
- package/core/collection/array/isArrayEqual.js +50 -0
- package/core/collection/array/randomMultipleFromArray.js +34 -0
- package/core/collection/array/randomizeArrayElementOrder.js +23 -0
- package/core/color/sRGB_to_linear.js +9 -4
- package/core/geom/2d/convex-hull/convex_hull_monotone_2d.js +1 -1
- package/core/geom/3d/aabb/aabb3_build_frustum.js +1 -1
- package/core/geom/3d/aabb/compute_aabb_from_points.js +1 -1
- package/core/geom/3d/frustum/frustum3_computeNearestPointToPoint.js +3 -1
- package/core/geom/3d/morton/v3_morton_encode_transformed.spec.js +20 -0
- package/core/geom/3d/plane/orient3d_fast.js +11 -10
- package/core/geom/3d/plane/orient3d_robust.js +41 -0
- package/core/geom/3d/plane/plane_computeConvex3PlaneIntersection.js +0 -23
- package/core/geom/3d/plane/plane_three_compute_convex3_plane_intersection.js +24 -0
- package/core/geom/3d/shape/UnionShape3D.js +1 -1
- package/core/geom/3d/sphere/harmonics/README.md +15 -0
- package/core/geom/3d/sphere/harmonics/sh3_add.js +21 -0
- package/core/geom/3d/sphere/harmonics/sh3_dering_optimize_positive.js +618 -0
- package/core/geom/3d/sphere/harmonics/sh3_sample_by_direction.js +49 -0
- package/core/geom/3d/sphere/harmonics/sh3_sample_irradiance_by_direction.js +53 -0
- package/core/geom/3d/tetrahedra/README.md +10 -1
- package/core/geom/3d/tetrahedra/TetrahedralMesh.js +650 -0
- package/core/geom/3d/tetrahedra/TetrahedralMesh.spec.js +233 -0
- package/core/geom/3d/tetrahedra/build_tetrahedral_mesh_buffer_geometry.js +75 -0
- package/core/geom/3d/tetrahedra/compute_bounding_simplex_3d.js +2 -2
- package/core/geom/3d/tetrahedra/compute_bounding_simplex_3d.spec.js +4 -4
- package/core/geom/3d/tetrahedra/delaunay/Cavity.js +49 -7
- package/core/geom/3d/tetrahedra/delaunay/compute_delaunay_tetrahedral_mesh.js +51 -17
- package/core/geom/3d/tetrahedra/delaunay/debug_validate_mesh.js +19 -0
- package/core/geom/3d/tetrahedra/delaunay/fill_in_a_cavity.js +191 -0
- package/core/geom/3d/tetrahedra/delaunay/push_boundary_with_validation.js +27 -0
- package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_compute_cavity.js +59 -43
- package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_compute_sub_determinant.js +77 -0
- package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_compute_sub_determinant.spec.js +30 -0
- package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_walk_towards_containing_tetrahedron.js +58 -0
- package/core/geom/3d/tetrahedra/delaunay/validate_cavity_boundary.js +60 -0
- package/core/geom/3d/tetrahedra/{point_in_tetrahedron_circumsphere.js → in_sphere_fast.js} +11 -13
- package/core/geom/3d/tetrahedra/in_sphere_robust.js +53 -0
- package/core/geom/3d/tetrahedra/prototypeTetrahedraBuilder.js +44 -35
- package/core/geom/3d/tetrahedra/tetrahedron_compute_signed_volume.js +83 -0
- package/core/geom/3d/tetrahedra/tetrahedron_compute_signed_volume.spec.js +24 -0
- package/core/geom/3d/tetrahedra/tetrahedron_contains_point.spec.js +66 -0
- package/core/geom/3d/tetrahedra/validate_tetrahedral_mesh.js +166 -0
- package/core/geom/3d/util/make_justified_point_grid.js +31 -0
- package/core/geom/Bezier.js +0 -27
- package/core/geom/Plane.js +0 -4
- package/core/geom/packing/miniball/Subspan.js +2 -2
- package/core/geom/v3_lerp.js +6 -1
- package/core/math/isqrt.js +28 -0
- package/core/math/isqrt.spec.js +9 -0
- package/core/math/max.spec.js +25 -0
- package/core/math/min2.spec.js +25 -0
- package/core/model/node-graph/node/NodeInstance.js +3 -3
- package/core/primitives/strings/prefixTree/PrefixTreeLeaf.js +1 -1
- package/core/process/delay.js +5 -0
- package/core/process/task/util/randomCountTask.js +1 -1
- package/editor/Editor.js +3 -0
- package/editor/ecs/component/editors/ecs/ParameterLookupTableEditor.js +195 -11
- package/editor/ecs/component/editors/ecs/ParameterTrackSetEditor.js +16 -0
- package/editor/ecs/component/editors/ecs/ParticleEmitterLayerEditor.js +4 -0
- package/editor/ecs/component/editors/primitive/ArrayEditor.js +1 -1
- package/editor/tools/v2/BlenderCameraOrientationGizmo.js +6 -0
- package/editor/view/ecs/components/common/AutoCanvasView.js +13 -25
- package/engine/EngineHarness.js +11 -5
- package/engine/asset/AssetManager.d.ts +5 -1
- package/engine/asset/AssetManager.js +50 -15
- package/engine/asset/AssetManager.spec.js +17 -11
- package/engine/asset/AssetRequest.js +57 -0
- package/engine/asset/loaders/ArrayBufferLoader.js +22 -0
- package/engine/asset/loaders/AssetLoader.js +1 -1
- package/engine/ecs/System.js +1 -1
- package/engine/ecs/dynamic_actions/DynamicActorSystem.js +1 -1
- package/engine/ecs/terrain/ecs/TerrainSystem.js +7 -1
- package/engine/ecs/transform/copy_three_transform.js +15 -0
- package/engine/graphics/FrameRunner.js +5 -9
- package/engine/graphics/ecs/animation/animator/AnimationClipDefinition.js +1 -1
- package/engine/graphics/ecs/animation/animator/graph/definition/AnimationGraphDefinition.js +1 -1
- package/engine/graphics/ecs/camera/Camera.js +1 -10
- package/engine/graphics/ecs/camera/CameraSystem.js +8 -8
- package/engine/graphics/ecs/camera/ProjectionType.js +9 -0
- package/engine/graphics/ecs/camera/build_three_camera_object.js +3 -3
- package/engine/graphics/ecs/decal/v2/prototypeDecalSystem.js +59 -4
- package/engine/graphics/ecs/light/Light.js +6 -1
- package/engine/graphics/ecs/light/LightSystem.d.ts +1 -1
- package/engine/graphics/ecs/mesh-v2/three_object_to_entity_composition.js +2 -17
- package/engine/graphics/geometry/VertexDataSpec.js +1 -1
- package/engine/graphics/geometry/instancing/InstancedMeshGroup.js +2 -2
- package/engine/graphics/impostors/octahedral/prototypeBaker.js +3 -3
- package/engine/graphics/micron/plugin/GLTFAssetTransformer.js +1 -1
- package/engine/graphics/micron/plugin/MicronRenderPlugin.js +3 -1
- package/engine/graphics/particles/node-based/codegen/modules/FunctionSignature.js +1 -1
- package/engine/graphics/render/forward_plus/LightManager.js +1 -1
- package/engine/graphics/render/forward_plus/LightManager.spec.js +4 -0
- package/engine/graphics/render/forward_plus/computeFrustumCorners.js +4 -2
- package/engine/graphics/render/forward_plus/prototype/prototypeLightManager.js +2 -2
- package/engine/graphics/render/layers/RenderLayerUtils.js +2 -2
- package/engine/graphics/sh3/LightProbeVolume.js +595 -0
- package/engine/graphics/sh3/SH3VisualisationMaterial.js +79 -0
- package/engine/graphics/sh3/prototypeSH3Probe.js +427 -0
- package/engine/graphics/sh3/visualise_probe.js +40 -0
- package/engine/graphics/shaders/DenoiseShader.js +1 -1
- package/engine/graphics/texture/atlas/AtlasPatch.js +11 -3
- package/engine/graphics/texture/atlas/CachingTextureAtlas.js +2 -2
- package/engine/graphics/texture/atlas/TextureAtlas.js +22 -4
- package/engine/graphics/texture/atlas/TextureAtlas.spec.js +22 -0
- package/engine/graphics/texture/sampler/Sampler2D.js +0 -64
- package/engine/graphics/texture/sampler/Sampler2D.spec.js +2 -1
- package/engine/graphics/texture/sampler/sampler2d_combine.js +67 -0
- package/engine/intelligence/behavior/ecs/BehaviorSystem.spec.js +0 -3
- package/engine/intelligence/blackboard/AbstractBlackboard.d.ts +1 -1
- package/engine/network/PriorityFetch.js +192 -0
- package/engine/simulation/DormandPrince.js +1 -1
- package/engine/ui/DraggableAspect.js +0 -1
- package/generation/grid/generation/road/GridTaskGenerateRoads.js +1 -1
- package/package.json +2 -1
- package/samples/terrain/from_image_2.js +127 -82
- package/view/elements/CanvasView.js +7 -1
- package/view/elements/image/HTMLElementCacheKey.js +1 -1
- package/view/util/DomSizeObserver.js +3 -5
- package/core/collection/ArrayUtils.js +0 -263
- package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_walk_toward_cavity.js +0 -48
- package/core/geom/3d/tetrahedra/hxt/a.js +0 -524
- package/core/geom/3d/tetrahedra/hxt/hxt.js +0 -140
- package/core/geom/3d/tetrahedra/hxt/hxt.wasm +0 -0
- package/core/geom/3d/tetrahedra/tetrahedra_collection.js +0 -383
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { plane_computeConvex3PlaneIntersection } from "./plane_computeConvex3PlaneIntersection.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param {number[]} result
|
|
6
|
+
* @param {number} result_offset
|
|
7
|
+
* @param {Plane} a
|
|
8
|
+
* @param {Plane} b
|
|
9
|
+
* @param {Plane} c
|
|
10
|
+
* @returns {boolean}
|
|
11
|
+
*/
|
|
12
|
+
export function plane_three_computeConvex3PlaneIntersection(result, result_offset, a, b, c) {
|
|
13
|
+
const a_normal = a.normal;
|
|
14
|
+
const b_normal = b.normal;
|
|
15
|
+
const c_normal = c.normal;
|
|
16
|
+
|
|
17
|
+
return plane_computeConvex3PlaneIntersection(
|
|
18
|
+
result,
|
|
19
|
+
result_offset,
|
|
20
|
+
a_normal.x, a_normal.y, a_normal.z, a.constant,
|
|
21
|
+
b_normal.x, b_normal.y, b_normal.z, b.constant,
|
|
22
|
+
c_normal.x, c_normal.y, c_normal.z, c.constant,
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -4,7 +4,7 @@ import { binarySearchHighIndex } from "../../../collection/array/binarySearchHig
|
|
|
4
4
|
import { compareNumbers } from "../../../primitives/numbers/compareNumbers.js";
|
|
5
5
|
import { min2 } from "../../../math/min2.js";
|
|
6
6
|
import { compute_signed_distance_gradient_by_sampling } from "./util/compute_signed_distance_gradient_by_sampling.js";
|
|
7
|
-
import { isArrayEqual } from "../../../collection/
|
|
7
|
+
import { isArrayEqual } from "../../../collection/array/isArrayEqual.js";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* To avoid severe performance overhead, we limit number of possible rejections
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Tools for working with spherical harmonics.
|
|
2
|
+
|
|
3
|
+
Mostly intended for shading and global illumination.
|
|
4
|
+
|
|
5
|
+
## De-ringing
|
|
6
|
+
There are basically 3 approaches,
|
|
7
|
+
* one is filtering using some kind of a smoothing function such as Lanczos
|
|
8
|
+
* Optimization of the polynomial (generally to remove negative values)
|
|
9
|
+
* Piece-wise reconstruction
|
|
10
|
+
|
|
11
|
+
### References:
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
* 2008 - GDC - Stupid Spherical Harmonics (SH) Tricks by Peter-Pike Sloan
|
|
15
|
+
* [Computing product of 2 sphecial harmonics](https://github.com/ruba/cortex-vfx/blob/c256b7274516a02fa64a0260351b86f1d6ebe8c2/include/IECore/SphericalHarmonicsAlgo.inl)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Add two spherical harmonics with 3 bands (9 coefficients)
|
|
5
|
+
*
|
|
6
|
+
* @param {number[]} result
|
|
7
|
+
* @param {number} result_offset
|
|
8
|
+
* @param {number[]} input_0
|
|
9
|
+
* @param {number} input_0_offset
|
|
10
|
+
* @param {number[]} input_1
|
|
11
|
+
* @param {number} input_1_offset
|
|
12
|
+
* @param {number} dimension_count
|
|
13
|
+
*/
|
|
14
|
+
export function sh3_add(result, result_offset, input_0, input_0_offset, input_1, input_1_offset, dimension_count) {
|
|
15
|
+
const n = 9 * dimension_count;
|
|
16
|
+
|
|
17
|
+
for (let i = 0; i < n; i++) {
|
|
18
|
+
result[result_offset + i] = input_0[input_0_offset + i] + input_1[input_1_offset + i];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
}
|
|
@@ -0,0 +1,618 @@
|
|
|
1
|
+
import { max2 } from "../../../../math/max2.js";
|
|
2
|
+
import { mat3, vec3 } from "gl-matrix";
|
|
3
|
+
import { array_copy } from "../../../../collection/array/copyArray.js";
|
|
4
|
+
import { min2 } from "../../../../math/min2.js";
|
|
5
|
+
import { assert } from "../../../../assert.js";
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
@see https://github.com/Bestmaker602/olacziy/blob/212b64ea5f1856b390cdf7629801243f76a4466d/libs/ibl/src/CubemapSH.cpp
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
*
|
|
13
|
+
* @param {number[]} M 5x5 matrix
|
|
14
|
+
* @param {number[]} x vec5
|
|
15
|
+
* @return {number[]}
|
|
16
|
+
*/
|
|
17
|
+
function multiply_5d(M, x) {
|
|
18
|
+
return [
|
|
19
|
+
M[0] * x[0] + M[5] * x[1] + M[10] * x[2] + M[15] * x[3] + M[20] * x[4],
|
|
20
|
+
M[1] * x[0] + M[6] * x[1] + M[11] * x[2] + M[16] * x[3] + M[21] * x[4],
|
|
21
|
+
M[2] * x[0] + M[7] * x[1] + M[12] * x[2] + M[17] * x[3] + M[22] * x[4],
|
|
22
|
+
M[3] * x[0] + M[8] * x[1] + M[13] * x[2] + M[18] * x[3] + M[23] * x[4],
|
|
23
|
+
M[4] * x[0] + M[9] * x[1] + M[14] * x[2] + M[19] * x[3] + M[24] * x[4]
|
|
24
|
+
];
|
|
25
|
+
}
|
|
26
|
+
|
|
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_PI = 0.636619772367581343075535053490057448;
|
|
57
|
+
const F_2_SQRTPI = 1.12837916709551257389615890312154517;
|
|
58
|
+
const F_SQRT2 = 1.41421356237309504880168872420969808;
|
|
59
|
+
const F_SQRT1_2 = 0.707106781186547524400844362104849039;
|
|
60
|
+
const M_SQRT_3 = 1.7320508076;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* SH scaling factors:
|
|
64
|
+
* returns sqrt((2*l + 1) / 4*pi) * sqrt( (l-|m|)! / (l+|m|)! )
|
|
65
|
+
*/
|
|
66
|
+
function Kml(m, l) {
|
|
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
|
+
/**
|
|
73
|
+
* compute Index of spherical harmonics coefficient
|
|
74
|
+
* @param {number} m
|
|
75
|
+
* @param {number} l
|
|
76
|
+
* @return {number}
|
|
77
|
+
*/
|
|
78
|
+
function SHindex(m, l) {
|
|
79
|
+
return l * (l + 1) + m;
|
|
80
|
+
}
|
|
81
|
+
|
|
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
|
+
|
|
204
|
+
/*
|
|
205
|
+
* utilities to rotate very low order spherical harmonics (up to 3rd band)
|
|
206
|
+
* @param {number[]} band1
|
|
207
|
+
* @param {number[]} M 3x3 matrix
|
|
208
|
+
* @returns {number[]}
|
|
209
|
+
*/
|
|
210
|
+
function rotateShericalHarmonicBand1(band1, M) {
|
|
211
|
+
|
|
212
|
+
// inverse() is not constexpr -- so we pre-calculate it in mathematica
|
|
213
|
+
//
|
|
214
|
+
// constexpr float3 N0{ 1, 0, 0 };
|
|
215
|
+
// constexpr float3 N1{ 0, 1, 0 };
|
|
216
|
+
// constexpr float3 N2{ 0, 0, 1 };
|
|
217
|
+
//
|
|
218
|
+
// constexpr mat3f A1 = { // this is the projection of N0, N1, N2 to SH space
|
|
219
|
+
// float3{ -N0.y, N0.z, -N0.x },
|
|
220
|
+
// float3{ -N1.y, N1.z, -N1.x },
|
|
221
|
+
// float3{ -N2.y, N2.z, -N2.x }
|
|
222
|
+
// };
|
|
223
|
+
//
|
|
224
|
+
// const mat3f invA1 = inverse(A1);
|
|
225
|
+
|
|
226
|
+
const invA1TimesK = [
|
|
227
|
+
0, -1, 0,
|
|
228
|
+
0, 0, 1,
|
|
229
|
+
-1, 0, 0
|
|
230
|
+
];
|
|
231
|
+
|
|
232
|
+
const R1OverK = [
|
|
233
|
+
-M[1], M[2], -M[0],
|
|
234
|
+
-M[4], M[5], -M[3],
|
|
235
|
+
-M[7], M[8], -M[6]
|
|
236
|
+
];
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
mat3.scale(invA1TimesK, invA1TimesK, band1);
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
mat3.multiply(R1OverK, R1OverK, invA1TimesK );
|
|
243
|
+
|
|
244
|
+
return R1OverK;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* This projects a vec3 to SH2/k space (i.e. we premultiply by 1/k)
|
|
249
|
+
* below can't be constexpr
|
|
250
|
+
* @return {number[]} vec5
|
|
251
|
+
* @param {number} x
|
|
252
|
+
* @param {number} y
|
|
253
|
+
* @param {number} z
|
|
254
|
+
*/
|
|
255
|
+
function project_v3_to_sh(x, y, z) {
|
|
256
|
+
return [
|
|
257
|
+
(y * x),
|
|
258
|
+
-(y * z),
|
|
259
|
+
1 / (2 * M_SQRT_3) * ((3 * z * z - 1)),
|
|
260
|
+
-(z * x),
|
|
261
|
+
0.5 * ((x * x - y * y))
|
|
262
|
+
];
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
*
|
|
267
|
+
* @param {number[]} band2 vec5
|
|
268
|
+
* @param {number[]} M mat3
|
|
269
|
+
* @return {*}
|
|
270
|
+
*/
|
|
271
|
+
function rotateShericalHarmonicBand2(band2, M) {
|
|
272
|
+
const n = F_SQRT1_2;
|
|
273
|
+
|
|
274
|
+
// Below we precompute (with help of Mathematica):
|
|
275
|
+
// constexpr float3 N0{ 1, 0, 0 };
|
|
276
|
+
// constexpr float3 N1{ 0, 0, 1 };
|
|
277
|
+
// constexpr float3 N2{ n, n, 0 };
|
|
278
|
+
// constexpr float3 N3{ n, 0, n };
|
|
279
|
+
// constexpr float3 N4{ 0, n, n };
|
|
280
|
+
// constexpr float M_SQRT_PI = 1.7724538509f;
|
|
281
|
+
// constexpr float M_SQRT_15 = 3.8729833462f;
|
|
282
|
+
// constexpr float k = M_SQRT_15 / (2.0f * M_SQRT_PI);
|
|
283
|
+
// --> k * inverse(mat5{project(N0), project(N1), project(N2), project(N3), project(N4)})
|
|
284
|
+
const invATimesK = [
|
|
285
|
+
0, 1, 2, 0, 0,
|
|
286
|
+
-1, 0, 0, 0, -2,
|
|
287
|
+
0, M_SQRT_3, 0, 0, 0,
|
|
288
|
+
1, 1, 0, -2, 0,
|
|
289
|
+
2, 1, 0, 0, 0
|
|
290
|
+
];
|
|
291
|
+
|
|
292
|
+
// this is: invA * k * band2
|
|
293
|
+
// 5x5 matrix by vec5 (this a lot of zeroes and constants, which the compiler should eliminate)
|
|
294
|
+
const invATimesKTimesBand2 = multiply_5d(invATimesK, band2);
|
|
295
|
+
|
|
296
|
+
// this is: mat5{project(N0), project(N1), project(N2), project(N3), project(N4)} / k
|
|
297
|
+
// (the 1/k comes from project(), see above)
|
|
298
|
+
const ROverK =
|
|
299
|
+
project_v3_to_sh(M[0], M[1], M[2]) // M * N0
|
|
300
|
+
.concat(project_v3_to_sh(M[6], M[7], M[8])) // M * N1
|
|
301
|
+
.concat(project_v3_to_sh(n * (M[0] + M[3]), n * (M[1] + M[4]), n * (M[2] + M[5]))) // M * N2
|
|
302
|
+
.concat(project_v3_to_sh(n * (M[0] + M[6]), n * (M[1] + M[7]), n * (M[2] + M[8]))) // M * N3
|
|
303
|
+
.concat(project_v3_to_sh(n * (M[3] + M[6]), n * (M[4] + M[7]), n * (M[5] + M[8]))) // M * N4
|
|
304
|
+
;
|
|
305
|
+
|
|
306
|
+
// notice how "k" disappears
|
|
307
|
+
// this is: (R / k) * (invA * k) * band2 == R * invA * band2
|
|
308
|
+
const result = multiply_5d(ROverK, invATimesKTimesBand2);
|
|
309
|
+
|
|
310
|
+
return result;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/*
|
|
314
|
+
* SH from environment with high dynamic range (or high frequencies -- high dynamic range creates
|
|
315
|
+
* high frequencies) exhibit "ringing" and negative values when reconstructed.
|
|
316
|
+
* To mitigate this, we need to low-pass the input image -- or equivalently window the SH by
|
|
317
|
+
* coefficient that tapper towards zero with the band.
|
|
318
|
+
*
|
|
319
|
+
* We use ideas and techniques from
|
|
320
|
+
* Stupid Spherical Harmonics (SH)
|
|
321
|
+
* Deringing Spherical Harmonics
|
|
322
|
+
* by Peter-Pike Sloan
|
|
323
|
+
* https://www.ppsloan.org/publications/shdering.pdf
|
|
324
|
+
*
|
|
325
|
+
*/
|
|
326
|
+
function sincWindow(l, w) {
|
|
327
|
+
if (l === 0) {
|
|
328
|
+
return 1.0;
|
|
329
|
+
} else if (l >= w) {
|
|
330
|
+
return 0.0;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// we use a sinc window scaled to the desired window size in bands units
|
|
334
|
+
// a sinc window only has zonal harmonics
|
|
335
|
+
let x = ((F_PI) * l) / w;
|
|
336
|
+
|
|
337
|
+
x = Math.sin(x) / x;
|
|
338
|
+
|
|
339
|
+
// The convolution of a SH function f and a ZH function h is just the product of both
|
|
340
|
+
// scaled by 1 / K(0,l) -- the window coefficients include this scale factor.
|
|
341
|
+
|
|
342
|
+
// Taking the window to power N is equivalent to applying the filter N times
|
|
343
|
+
return Math.pow(x, 4);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
*
|
|
348
|
+
* @param {number[]|Float32Array} result sh3
|
|
349
|
+
* @param {number[]} sh input
|
|
350
|
+
* @param {number[]} M mat3
|
|
351
|
+
*/
|
|
352
|
+
function rotate_sh3_bands(result, sh, M) {
|
|
353
|
+
|
|
354
|
+
const b0 = sh[0];
|
|
355
|
+
const band1 = [sh[1], sh[2], sh[3]];
|
|
356
|
+
const b1 = rotateShericalHarmonicBand1(band1, M);
|
|
357
|
+
const band2 = [sh[4], sh[5], sh[6], sh[7], sh[8]];
|
|
358
|
+
const b2 = rotateShericalHarmonicBand2(band2, M);
|
|
359
|
+
|
|
360
|
+
result[0] = b0;
|
|
361
|
+
|
|
362
|
+
result[1] = b1[0];
|
|
363
|
+
result[2] = b1[1];
|
|
364
|
+
result[3] = b1[2];
|
|
365
|
+
|
|
366
|
+
result[4] = b2[0];
|
|
367
|
+
result[5] = b2[1];
|
|
368
|
+
result[6] = b2[2];
|
|
369
|
+
result[7] = b2[3];
|
|
370
|
+
result[8] = b2[4];
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
*
|
|
375
|
+
* @param {number[]} input_sh3 sh3
|
|
376
|
+
* @returns {number}
|
|
377
|
+
*/
|
|
378
|
+
function shmin(input_sh3) {
|
|
379
|
+
// See "Deringing Spherical Harmonics" by Peter-Pike Sloan
|
|
380
|
+
// https://www.ppsloan.org/publications/shdering.pdf
|
|
381
|
+
|
|
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
|
+
const A = [
|
|
387
|
+
1.0 / (2.0 * M_SQRT_PI), // 0: 0 0
|
|
388
|
+
-M_SQRT_3 / (2.0 * M_SQRT_PI), // 1: 1 -1
|
|
389
|
+
M_SQRT_3 / (2.0 * M_SQRT_PI), // 2: 1 0
|
|
390
|
+
-M_SQRT_3 / (2.0 * M_SQRT_PI), // 3: 1 1
|
|
391
|
+
M_SQRT_15 / (2.0 * M_SQRT_PI), // 4: 2 -2
|
|
392
|
+
-M_SQRT_15 / (2.0 * M_SQRT_PI), // 5: 2 -1
|
|
393
|
+
M_SQRT_5 / (4.0 * M_SQRT_PI), // 6: 2 0
|
|
394
|
+
-M_SQRT_15 / (2.0 * M_SQRT_PI), // 7: 2 1
|
|
395
|
+
M_SQRT_15 / (4.0 * M_SQRT_PI) // 8: 2 2
|
|
396
|
+
];
|
|
397
|
+
|
|
398
|
+
// first this to do is to rotate the SH to align Z with the optimal linear direction
|
|
399
|
+
const dir = vec3.fromValues(-input_sh3[3], -input_sh3[1], input_sh3[2]);
|
|
400
|
+
|
|
401
|
+
vec3.normalize(dir, dir);
|
|
402
|
+
|
|
403
|
+
const z_axis = vec3.create();
|
|
404
|
+
vec3.negate(z_axis, dir);
|
|
405
|
+
|
|
406
|
+
const x_axis = vec3.create();
|
|
407
|
+
vec3.cross(x_axis, z_axis, vec3.fromValues(0, 1, 0));
|
|
408
|
+
vec3.normalize(x_axis, x_axis);
|
|
409
|
+
|
|
410
|
+
const y_axis = vec3.create();
|
|
411
|
+
vec3.cross(y_axis, x_axis, z_axis);
|
|
412
|
+
|
|
413
|
+
const M = mat3.create();
|
|
414
|
+
array_copy(x_axis, 0, M, 0, 3);
|
|
415
|
+
array_copy(y_axis, 0, M, 3, 3);
|
|
416
|
+
array_copy(dir, 0, M, 6, 3);
|
|
417
|
+
|
|
418
|
+
mat3.transpose(M, M);
|
|
419
|
+
|
|
420
|
+
const f = new Float32Array(9);
|
|
421
|
+
|
|
422
|
+
rotate_sh3_bands(f, input_sh3, M);
|
|
423
|
+
// here we're guaranteed to have normalize(float3{ -f[3], -f[1], f[2] }) == { 0, 0, 1 }
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
// Find the min for |m| = 2
|
|
427
|
+
// ------------------------
|
|
428
|
+
//
|
|
429
|
+
// Peter-Pike Sloan shows that the minimum can be expressed as a function
|
|
430
|
+
// of z such as: m2min = -m2max * (1 - z^2) = m2max * z^2 - m2max
|
|
431
|
+
// with m2max = A[8] * std::sqrt(f[8] * f[8] + f[4] * f[4]);
|
|
432
|
+
// We can therefore include this in the ZH min computation (which is function of z^2 as well)
|
|
433
|
+
const m2max = A[8] * Math.sqrt(f[8] * f[8] + f[4] * f[4]);
|
|
434
|
+
|
|
435
|
+
// Find the min of the zonal harmonics
|
|
436
|
+
// -----------------------------------
|
|
437
|
+
//
|
|
438
|
+
// This comes from minimizing the function:
|
|
439
|
+
// ZH(z) = (A[0] * f[0])
|
|
440
|
+
// + (A[2] * f[2]) * z
|
|
441
|
+
// + (A[6] * f[6]) * (3 * s.z * s.z - 1)
|
|
442
|
+
//
|
|
443
|
+
// We do that by finding where it's derivative d/dz is zero:
|
|
444
|
+
// dZH(z)/dz = a * z^2 + b * z + c
|
|
445
|
+
// which is zero for z = -b / 2 * a
|
|
446
|
+
//
|
|
447
|
+
// We also needs to check that -1 < z < 1, otherwise the min is either in z = -1 or 1
|
|
448
|
+
//
|
|
449
|
+
const a = 3 * A[6] * f[6] + m2max;
|
|
450
|
+
const b = A[2] * f[2];
|
|
451
|
+
const c = A[0] * f[0] - A[6] * f[6] - m2max;
|
|
452
|
+
|
|
453
|
+
const zmin = -b / (2.0 * a);
|
|
454
|
+
const m0min_z = a * zmin * zmin + b * zmin + c;
|
|
455
|
+
const m0min_b = min2(a + b + c, a - b + c);
|
|
456
|
+
|
|
457
|
+
const m0min = (a > 0 && zmin >= -1 && zmin <= 1) ? m0min_z : m0min_b;
|
|
458
|
+
|
|
459
|
+
// Find the min for l = 2, |m| = 1
|
|
460
|
+
// -------------------------------
|
|
461
|
+
//
|
|
462
|
+
// Note l = 1, |m| = 1 is guaranteed to be 0 because of the rotation step
|
|
463
|
+
//
|
|
464
|
+
// The function considered is:
|
|
465
|
+
// Y(x, y, z) = A[5] * f[5] * s.y * s.z
|
|
466
|
+
// + A[7] * f[7] * s.z * s.x
|
|
467
|
+
const d = A[4] * Math.sqrt(f[5] * f[5] + f[7] * f[7]);
|
|
468
|
+
|
|
469
|
+
// the |m|=1 function is minimal in -0.5 -- use that to skip the Newton's loop when possible
|
|
470
|
+
let minimum = m0min - 0.5 * d;
|
|
471
|
+
|
|
472
|
+
if (minimum < 0) {
|
|
473
|
+
// We could be negative, to find the minimum we will use Newton's method
|
|
474
|
+
// See https://en.wikipedia.org/wiki/Newton%27s_method_in_optimization
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
let dz;
|
|
478
|
+
let z = -F_SQRT1_2; // we start guessing at the min of |m|=1 function
|
|
479
|
+
do {
|
|
480
|
+
minimum = func_(z, a, b, c, d); // evaluate our function
|
|
481
|
+
dz = increment_(z, a, b, d); // refine our guess by this amount
|
|
482
|
+
z = z - dz;
|
|
483
|
+
// exit if z goes out of range, or if we have reached enough precision
|
|
484
|
+
} while (Math.abs(z) <= 1 && Math.abs(dz) > 1e-5);
|
|
485
|
+
|
|
486
|
+
if (Math.abs(z) > 1) {
|
|
487
|
+
// z was out of range
|
|
488
|
+
minimum = min2(func_(1, a, b, c, d), func_(-1, a, b, c, d));
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
return minimum;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* this is the function we're trying to minimize
|
|
497
|
+
* @param {number} x
|
|
498
|
+
* @param {number} a
|
|
499
|
+
* @param {number} b
|
|
500
|
+
* @param {number} c
|
|
501
|
+
* @param {number} d
|
|
502
|
+
* @return {number}
|
|
503
|
+
*/
|
|
504
|
+
function func_(x, a, b, c, d) {
|
|
505
|
+
// first term accounts for ZH + |m| = 2, second terms for |m| = 1
|
|
506
|
+
return (a * x * x + b * x + c) + (d * x * Math.sqrt(1 - x * x));
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* This is func' / func'' -- this was computed with Mathematica
|
|
511
|
+
* @param {number} x
|
|
512
|
+
* @param {number} a
|
|
513
|
+
* @param {number} b
|
|
514
|
+
* @param {number} d
|
|
515
|
+
* @return {number}
|
|
516
|
+
*/
|
|
517
|
+
function increment_(x, a, b, d) {
|
|
518
|
+
return (x * x - 1) * (d - 2 * d * x * x + (b + 2 * a * x) * Math.sqrt(1 - x * x))
|
|
519
|
+
/ (3 * d * x - 2 * d * x * x * x - 2 * a * Math.pow(1 - x * x, 1.5));
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
*
|
|
524
|
+
* @param {number[]|Float32Array} f sh3
|
|
525
|
+
* @param {number} cutoff
|
|
526
|
+
* @param {number} numBands
|
|
527
|
+
*/
|
|
528
|
+
function windowing(f, cutoff, numBands) {
|
|
529
|
+
for (let l = 0; l < numBands; l++) {
|
|
530
|
+
const w = sincWindow(l, cutoff);
|
|
531
|
+
f[SHindex(0, l)] *= w;
|
|
532
|
+
for (let m = 1; m <= l; m++) {
|
|
533
|
+
f[SHindex(-m, l)] *= w;
|
|
534
|
+
f[SHindex(m, l)] *= w;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return f;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
*
|
|
542
|
+
* @param {number[]} output sh3
|
|
543
|
+
* @param {number} output_offset
|
|
544
|
+
* @param {number[]} input
|
|
545
|
+
* @param {number} input_offset
|
|
546
|
+
* @param {number} numBands
|
|
547
|
+
* @param {number} channel_count
|
|
548
|
+
* @param {number} cutoff
|
|
549
|
+
*/
|
|
550
|
+
function windowSH(output, output_offset, input, input_offset, numBands, channel_count, cutoff) {
|
|
551
|
+
assert.isNonNegativeInteger(channel_count, 'channel_count');
|
|
552
|
+
assert.greaterThan(channel_count, 0, 'channel_count must be greater than 0');
|
|
553
|
+
|
|
554
|
+
assert.greaterThanOrEqual(cutoff, 0, 'cutoff must be >= 0');
|
|
555
|
+
|
|
556
|
+
if (cutoff === 0) {
|
|
557
|
+
// auto windowing (default)
|
|
558
|
+
|
|
559
|
+
if (numBands > 3) {
|
|
560
|
+
// auto-windowing works only for 1, 2 or 3 bands
|
|
561
|
+
throw new Error("--sh-window=auto can't work with more than 3 bands. Disabling.");
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
cutoff = numBands * 4 + 1;// start at a large band
|
|
565
|
+
// We need to process each channel separately
|
|
566
|
+
const SH = new Float32Array(9);
|
|
567
|
+
|
|
568
|
+
for (let channel = 0; channel < channel_count; channel++) {
|
|
569
|
+
|
|
570
|
+
for (let i = 0; i < numBands * numBands; i++) {
|
|
571
|
+
SH[i] = output[output_offset + i * channel_count + channel];
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// find a cut-off band that works
|
|
575
|
+
let l = numBands;
|
|
576
|
+
let r = cutoff;
|
|
577
|
+
for (let i = 0; i < 16 && l + 0.1 < r; i++) {
|
|
578
|
+
const m = 0.5 * (l + r);
|
|
579
|
+
if (shmin(windowing(SH, m, numBands)) < 0) {
|
|
580
|
+
r = m;
|
|
581
|
+
} else {
|
|
582
|
+
l = m;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
cutoff = min2(cutoff, l);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
array_copy(input, input_offset, output, output_offset, numBands * numBands * channel_count);
|
|
591
|
+
|
|
592
|
+
for (let l = 0; l < numBands; l++) {
|
|
593
|
+
let w = sincWindow(l, cutoff);
|
|
594
|
+
|
|
595
|
+
for (let i = 0; i < channel_count; i++) {
|
|
596
|
+
output[output_offset + SHindex(0, l) * channel_count + i] = w;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
for (let m = 1; m <= l; m++) {
|
|
600
|
+
for (let i = 0; i < channel_count; i++) {
|
|
601
|
+
output[output_offset + SHindex(-m, l) * channel_count + i] *= w;
|
|
602
|
+
output[output_offset + SHindex(m, l) * channel_count + i] *= w;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
*
|
|
610
|
+
* @param {number[]} result
|
|
611
|
+
* @param {number} result_offset
|
|
612
|
+
* @param {number[]} harmonics
|
|
613
|
+
* @param {number} harmonics_offset
|
|
614
|
+
* @param {number} dimension_count
|
|
615
|
+
*/
|
|
616
|
+
export function sh3_dering_optimize_positive(result, result_offset, harmonics, harmonics_offset, dimension_count = 3) {
|
|
617
|
+
windowSH(result, result_offset, harmonics, harmonics_offset, 3, dimension_count, 0);
|
|
618
|
+
}
|