@woosh/meep-engine 3.63.3 → 3.64.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 (139) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/package.json +1 -1
  3. package/src/core/color/xyz/m3_chromatic_adaptation_bradford.d.ts +14 -0
  4. package/src/core/color/xyz/m3_chromatic_adaptation_bradford.d.ts.map +1 -0
  5. package/src/core/color/xyz/m3_chromatic_adaptation_bradford.js +51 -0
  6. package/src/core/math/linalg/eigen/matrix_symmetric_3x3_eigen.d.ts +2 -1
  7. package/src/core/math/linalg/eigen/matrix_symmetric_3x3_eigen.d.ts.map +1 -1
  8. package/src/core/math/linalg/eigen/matrix_symmetric_3x3_eigen.js +51 -70
  9. package/src/core/math/linalg/eigen/matrix_symmetric_4x4_eigen.d.ts.map +1 -1
  10. package/src/core/math/linalg/eigen/matrix_symmetric_4x4_eigen.js +8 -64
  11. package/src/core/math/linalg/eigen/matrix_symmetric_jacobi_rotate_in_place.d.ts +17 -0
  12. package/src/core/math/linalg/eigen/matrix_symmetric_jacobi_rotate_in_place.d.ts.map +1 -0
  13. package/src/core/math/linalg/eigen/matrix_symmetric_jacobi_rotate_in_place.js +64 -0
  14. package/src/core/math/linalg/solve_box_constrained_qp.d.ts +30 -0
  15. package/src/core/math/linalg/solve_box_constrained_qp.d.ts.map +1 -0
  16. package/src/core/math/linalg/solve_box_constrained_qp.js +205 -0
  17. package/src/engine/physics/constraint/soft_spring_coefficients.d.ts +24 -0
  18. package/src/engine/physics/constraint/soft_spring_coefficients.d.ts.map +1 -0
  19. package/src/engine/physics/constraint/soft_spring_coefficients.js +38 -0
  20. package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -1
  21. package/src/engine/physics/constraint/solve_constraints.js +16 -8
  22. package/src/engine/physics/ecs/PhysicsSystem.d.ts +54 -0
  23. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  24. package/src/engine/physics/ecs/PhysicsSystem.js +192 -7
  25. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  26. package/src/engine/physics/inertia/world_inverse_inertia.d.ts +3 -5
  27. package/src/engine/physics/inertia/world_inverse_inertia.d.ts.map +1 -1
  28. package/src/engine/physics/inertia/world_inverse_inertia.js +3 -5
  29. package/src/engine/physics/vehicle/RaycastVehicle.d.ts +183 -12
  30. package/src/engine/physics/vehicle/RaycastVehicle.d.ts.map +1 -1
  31. package/src/engine/physics/vehicle/RaycastVehicle.js +1046 -121
  32. package/src/shade/playground/tonemap_sdr/README.md +11 -8
  33. package/src/shade/playground/tonemap_sdr/ToneMapCompare.js +2 -2
  34. package/src/shade/renderer/Renderer.d.ts +5 -6
  35. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  36. package/src/shade/renderer/Renderer.js +15 -88
  37. package/src/shade/renderer/animation/chunk_animation_curve_evaluate_keys.d.ts +14 -0
  38. package/src/shade/renderer/animation/chunk_animation_curve_evaluate_keys.d.ts.map +1 -0
  39. package/src/shade/renderer/animation/chunk_animation_curve_evaluate_keys.js +62 -0
  40. package/src/shade/renderer/deferred/tonemap/ColorGrade.d.ts +158 -0
  41. package/src/shade/renderer/deferred/tonemap/ColorGrade.d.ts.map +1 -0
  42. package/src/shade/renderer/deferred/tonemap/ColorGrade.js +337 -0
  43. package/src/shade/renderer/deferred/tonemap/SAMPLER_TONEMAP_LUT.d.ts +9 -0
  44. package/src/shade/renderer/deferred/tonemap/SAMPLER_TONEMAP_LUT.d.ts.map +1 -0
  45. package/src/shade/renderer/deferred/tonemap/SAMPLER_TONEMAP_LUT.js +15 -0
  46. package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_ACES.d.ts +6 -0
  47. package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_ACES.d.ts.map +1 -0
  48. package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_ACES.js +84 -0
  49. package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_GT7.d.ts +10 -0
  50. package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_GT7.d.ts.map +1 -0
  51. package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_GT7.js +25 -0
  52. package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_NONE.d.ts +9 -0
  53. package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_NONE.d.ts.map +1 -0
  54. package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_NONE.js +20 -0
  55. package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_PBR_NEUTRAL.d.ts +14 -0
  56. package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_PBR_NEUTRAL.d.ts.map +1 -0
  57. package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_PBR_NEUTRAL.js +30 -0
  58. package/src/shade/renderer/deferred/tonemap/ToneMapOperator.d.ts +54 -0
  59. package/src/shade/renderer/deferred/tonemap/ToneMapOperator.d.ts.map +1 -0
  60. package/src/shade/renderer/deferred/tonemap/ToneMapOperator.js +63 -0
  61. package/src/shade/renderer/deferred/tonemap/ToneMapper.d.ts +106 -0
  62. package/src/shade/renderer/deferred/tonemap/ToneMapper.d.ts.map +1 -0
  63. package/src/shade/renderer/deferred/tonemap/ToneMapper.js +474 -0
  64. package/src/shade/renderer/deferred/tonemap/TonemapAces2Tables.d.ts +5 -13
  65. package/src/shade/renderer/deferred/tonemap/TonemapAces2Tables.d.ts.map +1 -1
  66. package/src/shade/renderer/deferred/tonemap/TonemapAces2Tables.js +5 -44
  67. package/src/shade/renderer/deferred/tonemap/TonemapDisplaySettings.d.ts +7 -25
  68. package/src/shade/renderer/deferred/tonemap/TonemapDisplaySettings.d.ts.map +1 -1
  69. package/src/shade/renderer/deferred/tonemap/TonemapDisplaySettings.js +11 -51
  70. package/src/shade/renderer/deferred/tonemap/m3_white_balance_rec709.d.ts +20 -0
  71. package/src/shade/renderer/deferred/tonemap/m3_white_balance_rec709.d.ts.map +1 -0
  72. package/src/shade/renderer/deferred/tonemap/m3_white_balance_rec709.js +103 -0
  73. package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_lut.d.ts +9 -0
  74. package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_lut.d.ts.map +1 -0
  75. package/src/shade/renderer/deferred/tonemap/{shader_tonemap_HDR_none.js → shader_tonemap_HDR_lut.js} +22 -18
  76. package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_lut.d.ts +9 -0
  77. package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_lut.d.ts.map +1 -0
  78. package/src/shade/renderer/deferred/tonemap/{shader_tonemap_LDR_aces.js → shader_tonemap_LDR_lut.js} +24 -24
  79. package/src/shade/renderer/deferred/tonemap/shader_tonemap_lut_bake.d.ts +23 -0
  80. package/src/shade/renderer/deferred/tonemap/shader_tonemap_lut_bake.d.ts.map +1 -0
  81. package/src/shade/renderer/deferred/tonemap/shader_tonemap_lut_bake.js +92 -0
  82. package/src/shade/renderer/deferred/tonemap/shader_tonemap_lut_sdr_from_hdr.d.ts +15 -0
  83. package/src/shade/renderer/deferred/tonemap/shader_tonemap_lut_sdr_from_hdr.d.ts.map +1 -0
  84. package/src/shade/renderer/deferred/tonemap/shader_tonemap_lut_sdr_from_hdr.js +73 -0
  85. package/src/shade/renderer/fluid/GPUFluidCascade.d.ts +3 -3
  86. package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +1 -1
  87. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +6 -6
  88. package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.d.ts +1 -1
  89. package/src/shade/renderer/fluid/query/GPUFluidQueries.d.ts +1 -1
  90. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts +1 -1
  91. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  92. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  93. package/src/shade/renderer/shader/chunk/color/conversion/chunk_CRM_REC709_AP1.d.ts +22 -0
  94. package/src/shade/renderer/shader/chunk/color/conversion/chunk_CRM_REC709_AP1.d.ts.map +1 -0
  95. package/src/shade/renderer/shader/chunk/color/conversion/chunk_CRM_REC709_AP1.js +62 -0
  96. package/src/shade/renderer/shader/chunk/color/grade/chunk_color_grade.d.ts +13 -0
  97. package/src/shade/renderer/shader/chunk/color/grade/chunk_color_grade.d.ts.map +1 -0
  98. package/src/shade/renderer/shader/chunk/color/grade/chunk_color_grade.js +140 -0
  99. package/src/shade/renderer/shader/chunk/color/oklab/chunk_oklab_compute_max_saturation.d.ts +11 -0
  100. package/src/shade/renderer/shader/chunk/color/oklab/chunk_oklab_compute_max_saturation.d.ts.map +1 -0
  101. package/src/shade/renderer/shader/chunk/color/oklab/chunk_oklab_compute_max_saturation.js +65 -0
  102. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_bt2390_eetf.d.ts +14 -0
  103. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_bt2390_eetf.d.ts.map +1 -0
  104. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_bt2390_eetf.js +38 -0
  105. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_lut.d.ts +16 -0
  106. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_lut.d.ts.map +1 -0
  107. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_lut.js +41 -0
  108. package/src/shade/renderer/water/query/GPUWaterQueries.d.ts +1 -1
  109. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +8 -8
  110. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
  111. package/src/shade/wgsl/emulator/WGSLLanguageCore.js +2 -0
  112. package/src/shade/renderer/ShadeToneMappingMode.d.ts +0 -15
  113. package/src/shade/renderer/ShadeToneMappingMode.d.ts.map +0 -1
  114. package/src/shade/renderer/ShadeToneMappingMode.js +0 -41
  115. package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR.d.ts +0 -9
  116. package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR.d.ts.map +0 -1
  117. package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR.js +0 -79
  118. package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_aces.d.ts +0 -17
  119. package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_aces.d.ts.map +0 -1
  120. package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_aces.js +0 -101
  121. package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_none.d.ts +0 -9
  122. package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_none.d.ts.map +0 -1
  123. package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_pbr_neutral.d.ts +0 -10
  124. package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_pbr_neutral.d.ts.map +0 -1
  125. package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_pbr_neutral.js +0 -80
  126. package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR.d.ts +0 -9
  127. package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR.d.ts.map +0 -1
  128. package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR.js +0 -80
  129. package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_aces.d.ts +0 -10
  130. package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_aces.d.ts.map +0 -1
  131. package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_none.d.ts +0 -9
  132. package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_none.d.ts.map +0 -1
  133. package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_none.js +0 -71
  134. package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_pbr_neutral.d.ts +0 -10
  135. package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_pbr_neutral.d.ts.map +0 -1
  136. package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_pbr_neutral.js +0 -76
  137. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_hdr_bt2390.d.ts +0 -3
  138. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_hdr_bt2390.d.ts.map +0 -1
  139. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_hdr_bt2390.js +0 -44
@@ -0,0 +1,205 @@
1
+ import { lu_factor_linear_system } from "./lu_factor_linear_system.js";
2
+ import { lu_solve_linear_system } from "./lu_solve_linear_system.js";
3
+
4
+ /** Solved for, anywhere between its bounds. */
5
+ const FREE = 0;
6
+ /** Held at its lower bound. */
7
+ const AT_LOWER = 1;
8
+ /** Held at its upper bound. */
9
+ const AT_UPPER = 2;
10
+
11
+ // Working storage, grown to the largest system solved so far.
12
+ let scratch_matrix = new Float64Array(64);
13
+ let scratch_target = new Float64Array(8);
14
+ let scratch_free = new Uint32Array(8);
15
+ let scratch_pivot = new Uint32Array(8);
16
+ let scratch_state = new Uint8Array(8);
17
+
18
+ /**
19
+ * Solve the bound-constrained quadratic program
20
+ *
21
+ * minimise ½·xᵀ·A·x − bᵀ·x subject to lo ≤ x ≤ hi
22
+ *
23
+ * for a symmetric positive-definite `A`; equivalently the box LCP: `x` within `[lo, hi]` where
24
+ * `w = A·x − b` is zero for every `x_i` strictly between its bounds, non-negative where `x_i = lo_i`
25
+ * and non-positive where `x_i = hi_i`. The solution is unique.
26
+ *
27
+ * Primal active-set method (Nocedal & Wright, *Numerical Optimization*, 2nd ed., §16.5). It starts
28
+ * from the point of the box nearest the origin, with every variable free. Each pass solves for the
29
+ * free variables with the held ones at their bounds, and steps toward that solution as far as the box
30
+ * allows; a variable that cuts the step short is held at the bound it reached. A full step is the
31
+ * minimum over the variables currently free: then the held variable whose `w` has the wrong sign by
32
+ * the most is freed, or, when none has, `x` is the solution. The objective falls from each such
33
+ * minimum to the next, so no set of held variables repeats and the search ends; it also ends where
34
+ * rounding stops the objective falling. When no bound is reached it is one factorisation of `A`.
35
+ *
36
+ * @param {ArrayLike<number>} A n×n, column-major (`A[j·n + i]` is row `i`, column `j`); being symmetric,
37
+ * row-major reads the same. Not modified.
38
+ * @param {ArrayLike<number>} b length n
39
+ * @param {ArrayLike<number>} lo lower bounds, length n; `-Infinity` where there is none
40
+ * @param {ArrayLike<number>} hi upper bounds, length n, none below its `lo`; `Infinity` where there is none
41
+ * @param {Float64Array|number[]} x receives the solution, length n
42
+ * @param {number} n
43
+ * @returns {boolean} true when solved; false when `A` did not factor as positive definite, `x` then
44
+ * holding the last point reached, within the bounds
45
+ */
46
+ export function solve_box_constrained_qp(A, b, lo, hi, x, n) {
47
+ if (scratch_state.length < n) {
48
+ scratch_matrix = new Float64Array(n * n);
49
+ scratch_target = new Float64Array(n);
50
+ scratch_free = new Uint32Array(n);
51
+ scratch_pivot = new Uint32Array(n);
52
+ scratch_state = new Uint8Array(n);
53
+ }
54
+
55
+ const state = scratch_state;
56
+
57
+ let diagonal_max = 0;
58
+
59
+ for (let i = 0; i < n; i++) {
60
+ const lower = lo[i];
61
+ const upper = hi[i];
62
+
63
+ x[i] = lower > 0 ? lower : (upper < 0 ? upper : 0);
64
+ state[i] = FREE;
65
+
66
+ const d = A[i * n + i];
67
+
68
+ if (d > diagonal_max) {
69
+ diagonal_max = d;
70
+ }
71
+ }
72
+
73
+ if (n === 0) {
74
+ return true;
75
+ }
76
+
77
+ if (!(diagonal_max < Infinity) || diagonal_max <= 0) {
78
+ return false;
79
+ }
80
+
81
+ // lu_factor_linear_system refuses a pivot under an absolute 1e-12; with the largest diagonal scaled
82
+ // to one, that is relative to A.
83
+ const scale = 1 / diagonal_max;
84
+
85
+ const matrix = scratch_matrix;
86
+ const target = scratch_target;
87
+ const free = scratch_free;
88
+ const pivot = scratch_pivot;
89
+
90
+ let last_minimum = Infinity;
91
+
92
+ for (; ;) {
93
+ let free_count = 0;
94
+
95
+ for (let i = 0; i < n; i++) {
96
+ if (state[i] === FREE) {
97
+ free[free_count++] = i;
98
+ }
99
+ }
100
+
101
+ if (free_count > 0) {
102
+ // A_FF·x_F = b_F − A_FH·x_H, with the held variables at their bounds.
103
+ for (let r = 0; r < free_count; r++) {
104
+ const i = free[r];
105
+
106
+ let rhs = b[i];
107
+
108
+ for (let j = 0; j < n; j++) {
109
+ if (state[j] !== FREE) {
110
+ rhs -= A[j * n + i] * x[j];
111
+ }
112
+ }
113
+
114
+ target[r] = rhs * scale;
115
+
116
+ for (let c = 0; c < free_count; c++) {
117
+ matrix[c * free_count + r] = A[free[c] * n + i] * scale;
118
+ }
119
+ }
120
+
121
+ if (!lu_factor_linear_system(matrix, pivot, free_count)) {
122
+ return false;
123
+ }
124
+
125
+ lu_solve_linear_system(matrix, pivot, target, free_count);
126
+
127
+ // The longest step toward the solution that stays inside the box.
128
+ let step = 1;
129
+ let blocking = -1;
130
+ let blocking_state = FREE;
131
+
132
+ for (let r = 0; r < free_count; r++) {
133
+ const i = free[r];
134
+ const t = target[r];
135
+ const xi = x[i];
136
+
137
+ if (t < lo[i]) {
138
+ const s = (xi - lo[i]) / (xi - t);
139
+
140
+ if (s < step) {
141
+ step = s;
142
+ blocking = i;
143
+ blocking_state = AT_LOWER;
144
+ }
145
+ } else if (t > hi[i]) {
146
+ const s = (hi[i] - xi) / (t - xi);
147
+
148
+ if (s < step) {
149
+ step = s;
150
+ blocking = i;
151
+ blocking_state = AT_UPPER;
152
+ }
153
+ }
154
+ }
155
+
156
+ if (blocking !== -1) {
157
+ for (let r = 0; r < free_count; r++) {
158
+ const i = free[r];
159
+ x[i] += step * (target[r] - x[i]);
160
+ }
161
+
162
+ x[blocking] = blocking_state === AT_LOWER ? lo[blocking] : hi[blocking];
163
+ state[blocking] = blocking_state;
164
+
165
+ continue;
166
+ }
167
+
168
+ for (let r = 0; r < free_count; r++) {
169
+ x[free[r]] = target[r];
170
+ }
171
+ }
172
+
173
+ // x is the minimum over the free variables. Free the held variable whose multiplier
174
+ // w_i = (A·x − b)_i has the wrong sign by the most.
175
+ let objective = 0;
176
+ let release = -1;
177
+ let worst = 0;
178
+
179
+ for (let i = 0; i < n; i++) {
180
+ let ax = 0;
181
+
182
+ for (let j = 0; j < n; j++) {
183
+ ax += A[j * n + i] * x[j];
184
+ }
185
+
186
+ objective += x[i] * (0.5 * ax - b[i]);
187
+
188
+ const w = ax - b[i];
189
+ const s = state[i];
190
+ const violation = s === AT_LOWER ? -w : (s === AT_UPPER ? w : 0);
191
+
192
+ if (violation > worst) {
193
+ worst = violation;
194
+ release = i;
195
+ }
196
+ }
197
+
198
+ if (release === -1 || !(objective < last_minimum)) {
199
+ return true;
200
+ }
201
+
202
+ last_minimum = objective;
203
+ state[release] = FREE;
204
+ }
205
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Coefficients of a spring-damper solved as a soft velocity constraint over a step `h` (Catto, "Soft
3
+ * Constraints", GDC 2011). For a row with Jacobian `J`, position `C` (zero at the spring's rest) and
4
+ * impulse `λ`, the constraint
5
+ *
6
+ * J·v⁺ + β·C + γ·λ = 0, β = k / (c + h·k), γ = 1 / (h·(c + h·k))
7
+ *
8
+ * is backward Euler for the spring force `−k·C − c·J·v`: its impulse is `λ = −h·(k·C⁺ + c·J·v⁺)`, with
9
+ * `C⁺ = C + h·J·v⁺` the position the step ends at. It is stable for every `k, c ≥ 0` and `h > 0`.
10
+ * `γ` is the row's compliance, added to its effective-mass denominator; `β` turns `C` into the
11
+ * velocity the spring restores at.
12
+ *
13
+ * Reads `[k, c, h]` from `params` and writes `[γ, β]` to `out`. The numbers travel in arrays because a
14
+ * number handed to a call V8 does not inline is boxed into a new heap object.
15
+ *
16
+ * @param {Float64Array|number[]} out receives `γ` at `out_offset` and `β` after it
17
+ * @param {number} out_offset
18
+ * @param {Float64Array|number[]} params stiffness `k` (N/m, or N·m/rad for an angular row), damping `c`
19
+ * (N·s/m, or N·m·s/rad) and step `h` (seconds), from `params_offset`
20
+ * @param {number} params_offset
21
+ * @returns {boolean} false, writing nothing, when the spring is inert: no stiffness and no damping
22
+ */
23
+ export function soft_spring_coefficients(out: Float64Array | number[], out_offset: number, params: Float64Array | number[], params_offset: number): boolean;
24
+ //# sourceMappingURL=soft_spring_coefficients.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"soft_spring_coefficients.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/constraint/soft_spring_coefficients.js"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,8CAPW,YAAY,GAAC,MAAM,EAAE,cACrB,MAAM,UACN,YAAY,GAAC,MAAM,EAAE,iBAErB,MAAM,GACJ,OAAO,CAiBnB"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Coefficients of a spring-damper solved as a soft velocity constraint over a step `h` (Catto, "Soft
3
+ * Constraints", GDC 2011). For a row with Jacobian `J`, position `C` (zero at the spring's rest) and
4
+ * impulse `λ`, the constraint
5
+ *
6
+ * J·v⁺ + β·C + γ·λ = 0, β = k / (c + h·k), γ = 1 / (h·(c + h·k))
7
+ *
8
+ * is backward Euler for the spring force `−k·C − c·J·v`: its impulse is `λ = −h·(k·C⁺ + c·J·v⁺)`, with
9
+ * `C⁺ = C + h·J·v⁺` the position the step ends at. It is stable for every `k, c ≥ 0` and `h > 0`.
10
+ * `γ` is the row's compliance, added to its effective-mass denominator; `β` turns `C` into the
11
+ * velocity the spring restores at.
12
+ *
13
+ * Reads `[k, c, h]` from `params` and writes `[γ, β]` to `out`. The numbers travel in arrays because a
14
+ * number handed to a call V8 does not inline is boxed into a new heap object.
15
+ *
16
+ * @param {Float64Array|number[]} out receives `γ` at `out_offset` and `β` after it
17
+ * @param {number} out_offset
18
+ * @param {Float64Array|number[]} params stiffness `k` (N/m, or N·m/rad for an angular row), damping `c`
19
+ * (N·s/m, or N·m·s/rad) and step `h` (seconds), from `params_offset`
20
+ * @param {number} params_offset
21
+ * @returns {boolean} false, writing nothing, when the spring is inert: no stiffness and no damping
22
+ */
23
+ export function soft_spring_coefficients(out, out_offset, params, params_offset) {
24
+ const stiffness = params[params_offset];
25
+ const damping = params[params_offset + 1];
26
+ const h = params[params_offset + 2];
27
+
28
+ const denom = damping + h * stiffness;
29
+
30
+ if (denom <= 0) {
31
+ return false;
32
+ }
33
+
34
+ out[out_offset] = 1 / (h * denom);
35
+ out[out_offset + 1] = stiffness / denom;
36
+
37
+ return true;
38
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"solve_constraints.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/constraint/solve_constraints.js"],"names":[],"mappings":"AAs6BA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qCAXW,OAAO,iCAEP,MAAM,SAEN,MAAM,YAGN,OAAO,QAgCjB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qCAJW,OAAO,mCAEP,OAAO,QAgBjB"}
1
+ {"version":3,"file":"solve_constraints.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/constraint/solve_constraints.js"],"names":[],"mappings":"AA86BA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qCAXW,OAAO,iCAEP,MAAM,SAEN,MAAM,YAGN,OAAO,QAgCjB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qCAJW,OAAO,mCAEP,OAAO,QAgBjB"}
@@ -27,6 +27,7 @@ import { JOINT_WORLD } from "../ecs/Joint.js";
27
27
  import { SleepState } from "../ecs/SleepState.js";
28
28
  import { angular_effective_mass_array, world_inverse_inertia_apply_array } from "../inertia/world_inverse_inertia.js";
29
29
  import { DofMode } from "./DofMode.js";
30
+ import { soft_spring_coefficients } from "./soft_spring_coefficients.js";
30
31
 
31
32
  /**
32
33
  * # 6-DOF constraint (joint) solver
@@ -150,6 +151,10 @@ const scratch_rel = new Float64Array(8);
150
151
  const scratch_ang_err = new Float64Array(3);
151
152
  /** One row's effective-mass denominator, body A's part at 0 and body B's at 1. */
152
153
  const scratch_keff = new Float64Array(2);
154
+ /** A SPRING row's stiffness, damping and substep, for {@link soft_spring_coefficients}. */
155
+ const scratch_spring_params = new Float64Array(3);
156
+ /** A SPRING row's compliance `γ` at 0 and bias rate `β` at 1, from {@link soft_spring_coefficients}. */
157
+ const scratch_spring = new Float64Array(2);
153
158
 
154
159
  // The numbers {@link fill_row} is given for one DOF, at these offsets in `scratch_dof`. They are computed
155
160
  // per row, and a computed number handed to a call that is not inlined (fill_row is past V8's inlining
@@ -459,8 +464,8 @@ function invert_sym3(K, out) {
459
464
  *
460
465
  * A spring drives the DOF position toward zero with stiffness `k` (N/m or
461
466
  * N·m/rad) and damping `c`, as a *regularised* row rather than a hard one. The
462
- * implicit-Euler soft constraint (Catto, "Soft Constraints") gives, per substep
463
- * `h`, with `denom = c + h·k`:
467
+ * implicit-Euler soft constraint (Catto, "Soft Constraints";
468
+ * {@link soft_spring_coefficients}) gives, per substep `h`, with `denom = c + h·k`:
464
469
  * `γ = 1 / (h·denom)` the compliance / regularisation (inverse mass),
465
470
  * `bias = (k/denom)·C` the restoring velocity, and a softened effective
466
471
  * `effMass = 1/(K + γ)` mass — so the row solves
@@ -554,14 +559,17 @@ function fill_row(joint, dofIndex, dof, spook_locked, spook_spec, dt_sub, out, o
554
559
  }
555
560
  if (mode === DofMode.SPRING) {
556
561
  if (keff === 0) { imp[dofIndex] = 0; return false; }
557
- const ks = joint.dofStiffness[dofIndex];
558
- const cs = joint.dofDamping[dofIndex];
559
- const denom = cs + dt_sub * ks;
560
- if (denom <= 0) { imp[dofIndex] = 0; return false; } // inert (no k, no c)
561
- const gamma = 1 / (dt_sub * denom);
562
+ scratch_spring_params[0] = joint.dofStiffness[dofIndex];
563
+ scratch_spring_params[1] = joint.dofDamping[dofIndex];
564
+ scratch_spring_params[2] = dt_sub;
565
+ if (!soft_spring_coefficients(scratch_spring, 0, scratch_spring_params, 0)) {
566
+ imp[dofIndex] = 0; // inert (no k, no c)
567
+ return false;
568
+ }
569
+ const gamma = scratch_spring[0];
562
570
  out[o + ROW_EFF] = 1 / (keff + gamma);
563
571
  // restoring velocity toward pos = 0
564
- out[o + ROW_BIAS] = out[o + ROW_RELAX_BIAS] = (ks / denom) * pos;
572
+ out[o + ROW_BIAS] = out[o + ROW_RELAX_BIAS] = scratch_spring[1] * pos;
565
573
  out[o + ROW_CLAMP_LO] = -Infinity;
566
574
  out[o + ROW_CLAMP_HI] = Infinity;
567
575
  out[o + ROW_GAMMA] = gamma;
@@ -704,6 +704,60 @@ export class PhysicsSystem extends System<any> {
704
704
  * @param {Vector3|ArrayLike<number>} force indexed as [x, y, z]
705
705
  */
706
706
  applyForce(rigidBody: RigidBody, force: Vector3 | ArrayLike<number>): void;
707
+ /**
708
+ * The mean velocity a body will have across the next {@link fixedUpdate}`(dt)` under what already
709
+ * acts on it: gravity, its damping, and the force and torque accumulated so far, with no contact or
710
+ * joint. Its displacement over that step is this times `dt`.
711
+ *
712
+ * The accumulated force and torque are spent before the substeps, as is an impulse applied before
713
+ * the step ({@link applyImpulseAt}): the mean keeps {@link predictStepMeanVelocityShare} of the
714
+ * velocity change each makes, all of it on a body nothing damps. Gravity enters at each of the `N`
715
+ * {@link substeps}: it adds `gravity·gravityScale·dt` to an undamped body's end velocity, but only
716
+ * `(N + 1)/(2N)` of that to its mean. A damping above 0 decays the velocity at each substep by
717
+ * `k = 1/(1 + damping·dt/N)`, so the mean keeps `(1/N)·Σᵢ kⁱ` of the velocity the substeps start
718
+ * from and `(1/N²)·Σᵢ Σⱼ₌₁…ᵢ kʲ` of the step's gravity, `i = 1…N`; the angular velocity decays the
719
+ * same way at the angular damping. A controller that works out its impulses from where the coming
720
+ * step will carry the body predicts that motion with this.
721
+ *
722
+ * A body that is not Dynamic is predicted to keep its velocity, as a kinematic body moving at the
723
+ * velocity it carries does. A KinematicPosition body given a target for the step
724
+ * ({@link setTargetPose}) takes the velocity that reaches the target instead, and a Static body does
725
+ * not move.
726
+ *
727
+ * @param {Float64Array|number[]} linear receives the mean linear velocity, indexed as [x, y, z]
728
+ * @param {Float64Array|number[]} angular receives the mean angular velocity, indexed as [x, y, z]
729
+ * @param {RigidBody} rigidBody
730
+ * @param {Transform64} transform the body's world transform
731
+ * @param {number} dt seconds, the step the next fixedUpdate takes
732
+ */
733
+ predictStepMeanVelocity(linear: Float64Array | number[], angular: Float64Array | number[], rigidBody: RigidBody, transform: Transform64, dt: number): void;
734
+ /**
735
+ * How much of a change to a body's velocity, made before the next {@link fixedUpdate}`(dt)`, its mean
736
+ * velocity across that step keeps, with no contact or joint: `out[0]` of a change to its linear
737
+ * velocity, `out[1]` of one to its angular velocity. An impulse applied before the step
738
+ * ({@link applyImpulseAt}) moves the {@link predictStepMeanVelocity} by these times the velocity
739
+ * change it makes. A controller that solves for impulses against that motion takes their effect on
740
+ * it from these.
741
+ *
742
+ * A damping above 0 decays the velocity at each of the `N` {@link substeps} by
743
+ * `k = 1/(1 + damping·dt/N)`, so the mean keeps `(1/N)·Σᵢ₌₁…ₙ kⁱ` of a change: 1 for a body nothing
744
+ * damps, less the more it is damped and the longer the step. A body that is not Dynamic: 1 and 1,
745
+ * nothing damps it — as {@link predictStepMeanVelocity} predicts it to keep its velocity, with the
746
+ * same exceptions.
747
+ *
748
+ * @param {Float64Array|number[]} out receives the linear share at [0] and the angular share at [1]
749
+ * @param {RigidBody} rigidBody
750
+ * @param {number} dt seconds, the step the next fixedUpdate takes
751
+ */
752
+ predictStepMeanVelocityShare(out: Float64Array | number[], rigidBody: RigidBody, dt: number): void;
753
+ /**
754
+ * The number of substeps {@link fixedUpdate} divides a step into: {@link substeps}, truncated, and 1
755
+ * for anything below 2 — a count of 0, negative or fractional would make the substep `dt / N`
756
+ * infinite and the loop bound meaningless.
757
+ * @private
758
+ * @returns {number}
759
+ */
760
+ private __substep_count;
707
761
  /**
708
762
  * Replace the linear velocity. Wakes the body if asleep.
709
763
  *
@@ -1 +1 @@
1
- {"version":3,"file":"PhysicsSystem.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/ecs/PhysicsSystem.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PhysicsSystem.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/ecs/PhysicsSystem.js"],"names":[],"mappings":"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"}
@@ -68,18 +68,71 @@ import { RigidBodyFlags } from "./RigidBodyFlags.js";
68
68
  import { SleepState } from "./SleepState.js";
69
69
 
70
70
  /**
71
- * Scratch for {@link applyImpulseAt}'s angular delta calculation.
71
+ * Scratch for the angular velocity change {@link applyImpulseAt} and
72
+ * {@link predictStepMeanVelocity} work out.
72
73
  * @type {Float64Array}
73
74
  */
74
75
  const scratch_angular_delta = new Float64Array(3);
75
76
 
76
77
  /**
77
- * Scratch for {@link applyImpulseAt}'s LockRot*-masked inverse-inertia
78
- * diagonal (the same mask {@link SolverBodyState#gather} applies).
78
+ * Scratch for the LockRot*-masked inverse-inertia diagonal (the same mask
79
+ * {@link SolverBodyState#gather} applies) of {@link applyImpulseAt} and
80
+ * {@link predictStepMeanVelocity}.
79
81
  * @type {Float64Array}
80
82
  */
81
83
  const scratch_locked_inertia = new Float64Array(3);
82
84
 
85
+ /**
86
+ * The damping and the substep {@link substep_mean_shares} reads.
87
+ * @type {Float64Array}
88
+ */
89
+ const scratch_mean_params = new Float64Array(2);
90
+
91
+ /**
92
+ * The shares {@link substep_mean_shares} writes.
93
+ * @type {Float64Array}
94
+ */
95
+ const scratch_mean_shares = new Float64Array(2);
96
+
97
+ /**
98
+ * The mean of the velocities that `count` substeps of {@link integrate_velocity_gravity} at `h` leave, one
99
+ * after each, as shares: `out[0]` of the velocity they start from, and `out[1]` of the gravity they add
100
+ * together, `gravity·gravityScale·h·count`.
101
+ *
102
+ * A substep adds `gravity·gravityScale·h` and then decays the velocity by `k = 1/(1 + damping·h)`, or not
103
+ * at all unless the damping is above 0. After substep `i` that leaves `kⁱ` of the velocity the substeps
104
+ * started from, and `Σⱼ₌₁…ᵢ kʲ` substeps' worth of gravity; so over `n = count` substeps
105
+ *
106
+ * out[0] = (1/n)·Σᵢ₌₁…ₙ kⁱ, out[1] = (1/n²)·Σᵢ₌₁…ₙ Σⱼ₌₁…ᵢ kʲ,
107
+ *
108
+ * 1 and `(n + 1)/(2n)` undamped. Summed term by term, as exact for a damping near 0 as for any other.
109
+ *
110
+ * Reads the damping (per second) at `params[0]` and `h` (seconds) at `params[1]`. They travel in an array
111
+ * because a number handed to a call V8 does not inline is boxed into a new heap object.
112
+ *
113
+ * @param {Float64Array} out
114
+ * @param {Float64Array} params
115
+ * @param {number} count substeps, at least 1
116
+ */
117
+ function substep_mean_shares(out, params, count) {
118
+ const damping = params[0];
119
+ const h = params[1];
120
+ const k = damping > 0 ? 1 / (1 + damping * h) : 1;
121
+
122
+ let power = 1; // kⁱ
123
+ let sum = 0; // Σⱼ₌₁…ᵢ kʲ
124
+ let sum_of_sums = 0;
125
+
126
+ for (let i = 0; i < count; i++) {
127
+ power *= k;
128
+ sum += power;
129
+ sum_of_sums += sum;
130
+ }
131
+
132
+ out[0] = sum / count;
133
+ out[1] = sum_of_sums / (count * count);
134
+ }
135
+
83
136
  /**
84
137
  * Reusable scratch buffer for world-AABB construction so the link path is
85
138
  * allocation-free in steady state.
@@ -1377,6 +1430,141 @@ export class PhysicsSystem extends System {
1377
1430
  this.__wake_body(rigidBody);
1378
1431
  }
1379
1432
 
1433
+ /**
1434
+ * The mean velocity a body will have across the next {@link fixedUpdate}`(dt)` under what already
1435
+ * acts on it: gravity, its damping, and the force and torque accumulated so far, with no contact or
1436
+ * joint. Its displacement over that step is this times `dt`.
1437
+ *
1438
+ * The accumulated force and torque are spent before the substeps, as is an impulse applied before
1439
+ * the step ({@link applyImpulseAt}): the mean keeps {@link predictStepMeanVelocityShare} of the
1440
+ * velocity change each makes, all of it on a body nothing damps. Gravity enters at each of the `N`
1441
+ * {@link substeps}: it adds `gravity·gravityScale·dt` to an undamped body's end velocity, but only
1442
+ * `(N + 1)/(2N)` of that to its mean. A damping above 0 decays the velocity at each substep by
1443
+ * `k = 1/(1 + damping·dt/N)`, so the mean keeps `(1/N)·Σᵢ kⁱ` of the velocity the substeps start
1444
+ * from and `(1/N²)·Σᵢ Σⱼ₌₁…ᵢ kʲ` of the step's gravity, `i = 1…N`; the angular velocity decays the
1445
+ * same way at the angular damping. A controller that works out its impulses from where the coming
1446
+ * step will carry the body predicts that motion with this.
1447
+ *
1448
+ * A body that is not Dynamic is predicted to keep its velocity, as a kinematic body moving at the
1449
+ * velocity it carries does. A KinematicPosition body given a target for the step
1450
+ * ({@link setTargetPose}) takes the velocity that reaches the target instead, and a Static body does
1451
+ * not move.
1452
+ *
1453
+ * @param {Float64Array|number[]} linear receives the mean linear velocity, indexed as [x, y, z]
1454
+ * @param {Float64Array|number[]} angular receives the mean angular velocity, indexed as [x, y, z]
1455
+ * @param {RigidBody} rigidBody
1456
+ * @param {Transform64} transform the body's world transform
1457
+ * @param {number} dt seconds, the step the next fixedUpdate takes
1458
+ */
1459
+ predictStepMeanVelocity(linear, angular, rigidBody, transform, dt) {
1460
+ const lv = rigidBody.linearVelocity;
1461
+ const av = rigidBody.angularVelocity;
1462
+
1463
+ if (rigidBody.kind !== BodyKind.Dynamic) {
1464
+ linear[0] = lv[0];
1465
+ linear[1] = lv[1];
1466
+ linear[2] = lv[2];
1467
+
1468
+ angular[0] = av[0];
1469
+ angular[1] = av[1];
1470
+ angular[2] = av[2];
1471
+
1472
+ return;
1473
+ }
1474
+
1475
+ const substeps = this.__substep_count();
1476
+ const inv_m = rigidBody.mass > 0 ? 1 / rigidBody.mass : 0;
1477
+ const af = rigidBody.accumulatedForce;
1478
+ const g = this.gravity;
1479
+
1480
+ scratch_mean_params[0] = rigidBody.linearDamping;
1481
+ scratch_mean_params[1] = dt / substeps;
1482
+ substep_mean_shares(scratch_mean_shares, scratch_mean_params, substeps);
1483
+
1484
+ const share = scratch_mean_shares[0];
1485
+ const gravity_share = rigidBody.gravityScale * dt * scratch_mean_shares[1];
1486
+
1487
+ linear[0] = share * (lv[0] + af[0] * inv_m * dt) + g[0] * gravity_share;
1488
+ linear[1] = share * (lv[1] + af[1] * inv_m * dt) + g[1] * gravity_share;
1489
+ linear[2] = share * (lv[2] + af[2] * inv_m * dt) + g[2] * gravity_share;
1490
+
1491
+ scratch_mean_params[0] = rigidBody.angularDamping;
1492
+ substep_mean_shares(scratch_mean_shares, scratch_mean_params, substeps);
1493
+
1494
+ const angular_share = scratch_mean_shares[0];
1495
+
1496
+ const at = rigidBody.accumulatedTorque;
1497
+
1498
+ if (at[0] !== 0 || at[1] !== 0 || at[2] !== 0) {
1499
+ locked_inverse_inertia(scratch_locked_inertia, 0, rigidBody);
1500
+ world_inverse_inertia_apply_raw(
1501
+ scratch_angular_delta, 0,
1502
+ scratch_locked_inertia[0], scratch_locked_inertia[1], scratch_locked_inertia[2],
1503
+ transform.rotation_x, transform.rotation_y, transform.rotation_z, transform.rotation_w,
1504
+ at[0], at[1], at[2]
1505
+ );
1506
+
1507
+ angular[0] = angular_share * (av[0] + scratch_angular_delta[0] * dt);
1508
+ angular[1] = angular_share * (av[1] + scratch_angular_delta[1] * dt);
1509
+ angular[2] = angular_share * (av[2] + scratch_angular_delta[2] * dt);
1510
+ } else {
1511
+ angular[0] = angular_share * av[0];
1512
+ angular[1] = angular_share * av[1];
1513
+ angular[2] = angular_share * av[2];
1514
+ }
1515
+ }
1516
+
1517
+ /**
1518
+ * How much of a change to a body's velocity, made before the next {@link fixedUpdate}`(dt)`, its mean
1519
+ * velocity across that step keeps, with no contact or joint: `out[0]` of a change to its linear
1520
+ * velocity, `out[1]` of one to its angular velocity. An impulse applied before the step
1521
+ * ({@link applyImpulseAt}) moves the {@link predictStepMeanVelocity} by these times the velocity
1522
+ * change it makes. A controller that solves for impulses against that motion takes their effect on
1523
+ * it from these.
1524
+ *
1525
+ * A damping above 0 decays the velocity at each of the `N` {@link substeps} by
1526
+ * `k = 1/(1 + damping·dt/N)`, so the mean keeps `(1/N)·Σᵢ₌₁…ₙ kⁱ` of a change: 1 for a body nothing
1527
+ * damps, less the more it is damped and the longer the step. A body that is not Dynamic: 1 and 1,
1528
+ * nothing damps it — as {@link predictStepMeanVelocity} predicts it to keep its velocity, with the
1529
+ * same exceptions.
1530
+ *
1531
+ * @param {Float64Array|number[]} out receives the linear share at [0] and the angular share at [1]
1532
+ * @param {RigidBody} rigidBody
1533
+ * @param {number} dt seconds, the step the next fixedUpdate takes
1534
+ */
1535
+ predictStepMeanVelocityShare(out, rigidBody, dt) {
1536
+ if (rigidBody.kind !== BodyKind.Dynamic) {
1537
+ out[0] = 1;
1538
+ out[1] = 1;
1539
+
1540
+ return;
1541
+ }
1542
+
1543
+ const substeps = this.__substep_count();
1544
+
1545
+ scratch_mean_params[0] = rigidBody.linearDamping;
1546
+ scratch_mean_params[1] = dt / substeps;
1547
+ substep_mean_shares(scratch_mean_shares, scratch_mean_params, substeps);
1548
+
1549
+ out[0] = scratch_mean_shares[0];
1550
+
1551
+ scratch_mean_params[0] = rigidBody.angularDamping;
1552
+ substep_mean_shares(scratch_mean_shares, scratch_mean_params, substeps);
1553
+
1554
+ out[1] = scratch_mean_shares[0];
1555
+ }
1556
+
1557
+ /**
1558
+ * The number of substeps {@link fixedUpdate} divides a step into: {@link substeps}, truncated, and 1
1559
+ * for anything below 2 — a count of 0, negative or fractional would make the substep `dt / N`
1560
+ * infinite and the loop bound meaningless.
1561
+ * @private
1562
+ * @returns {number}
1563
+ */
1564
+ __substep_count() {
1565
+ return this.substeps > 1 ? this.substeps | 0 : 1;
1566
+ }
1567
+
1380
1568
  /**
1381
1569
  * Replace the linear velocity. Wakes the body if asleep.
1382
1570
  *
@@ -2599,10 +2787,7 @@ export class PhysicsSystem extends System {
2599
2787
  // separate between substeps — the TGS stack-stability mechanism —
2600
2788
  // without re-running narrowphase. Restitution is applied after the
2601
2789
  // loop.
2602
- // Clamp the substep count: 0 (or a negative / fractional value) would
2603
- // feed h = dt / N = Infinity into prepare_contacts and a broken loop
2604
- // bound into the solver.
2605
- const N = this.substeps > 1 ? this.substeps | 0 : 1;
2790
+ const N = this.__substep_count();
2606
2791
  const h = dt / N;
2607
2792
  const count_after_wake = storage.awake_count;
2608
2793