@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.
- package/CHANGELOG.md +51 -0
- package/package.json +1 -1
- package/src/core/color/xyz/m3_chromatic_adaptation_bradford.d.ts +14 -0
- package/src/core/color/xyz/m3_chromatic_adaptation_bradford.d.ts.map +1 -0
- package/src/core/color/xyz/m3_chromatic_adaptation_bradford.js +51 -0
- package/src/core/math/linalg/eigen/matrix_symmetric_3x3_eigen.d.ts +2 -1
- package/src/core/math/linalg/eigen/matrix_symmetric_3x3_eigen.d.ts.map +1 -1
- package/src/core/math/linalg/eigen/matrix_symmetric_3x3_eigen.js +51 -70
- package/src/core/math/linalg/eigen/matrix_symmetric_4x4_eigen.d.ts.map +1 -1
- package/src/core/math/linalg/eigen/matrix_symmetric_4x4_eigen.js +8 -64
- package/src/core/math/linalg/eigen/matrix_symmetric_jacobi_rotate_in_place.d.ts +17 -0
- package/src/core/math/linalg/eigen/matrix_symmetric_jacobi_rotate_in_place.d.ts.map +1 -0
- package/src/core/math/linalg/eigen/matrix_symmetric_jacobi_rotate_in_place.js +64 -0
- package/src/core/math/linalg/solve_box_constrained_qp.d.ts +30 -0
- package/src/core/math/linalg/solve_box_constrained_qp.d.ts.map +1 -0
- package/src/core/math/linalg/solve_box_constrained_qp.js +205 -0
- package/src/engine/physics/constraint/soft_spring_coefficients.d.ts +24 -0
- package/src/engine/physics/constraint/soft_spring_coefficients.d.ts.map +1 -0
- package/src/engine/physics/constraint/soft_spring_coefficients.js +38 -0
- package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -1
- package/src/engine/physics/constraint/solve_constraints.js +16 -8
- package/src/engine/physics/ecs/PhysicsSystem.d.ts +54 -0
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +192 -7
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/engine/physics/inertia/world_inverse_inertia.d.ts +3 -5
- package/src/engine/physics/inertia/world_inverse_inertia.d.ts.map +1 -1
- package/src/engine/physics/inertia/world_inverse_inertia.js +3 -5
- package/src/engine/physics/vehicle/RaycastVehicle.d.ts +183 -12
- package/src/engine/physics/vehicle/RaycastVehicle.d.ts.map +1 -1
- package/src/engine/physics/vehicle/RaycastVehicle.js +1046 -121
- package/src/shade/playground/tonemap_sdr/README.md +11 -8
- package/src/shade/playground/tonemap_sdr/ToneMapCompare.js +2 -2
- package/src/shade/renderer/Renderer.d.ts +5 -6
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +15 -88
- package/src/shade/renderer/animation/chunk_animation_curve_evaluate_keys.d.ts +14 -0
- package/src/shade/renderer/animation/chunk_animation_curve_evaluate_keys.d.ts.map +1 -0
- package/src/shade/renderer/animation/chunk_animation_curve_evaluate_keys.js +62 -0
- package/src/shade/renderer/deferred/tonemap/ColorGrade.d.ts +158 -0
- package/src/shade/renderer/deferred/tonemap/ColorGrade.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/ColorGrade.js +337 -0
- package/src/shade/renderer/deferred/tonemap/SAMPLER_TONEMAP_LUT.d.ts +9 -0
- package/src/shade/renderer/deferred/tonemap/SAMPLER_TONEMAP_LUT.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/SAMPLER_TONEMAP_LUT.js +15 -0
- package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_ACES.d.ts +6 -0
- package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_ACES.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_ACES.js +84 -0
- package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_GT7.d.ts +10 -0
- package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_GT7.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_GT7.js +25 -0
- package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_NONE.d.ts +9 -0
- package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_NONE.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_NONE.js +20 -0
- package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_PBR_NEUTRAL.d.ts +14 -0
- package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_PBR_NEUTRAL.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_PBR_NEUTRAL.js +30 -0
- package/src/shade/renderer/deferred/tonemap/ToneMapOperator.d.ts +54 -0
- package/src/shade/renderer/deferred/tonemap/ToneMapOperator.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/ToneMapOperator.js +63 -0
- package/src/shade/renderer/deferred/tonemap/ToneMapper.d.ts +106 -0
- package/src/shade/renderer/deferred/tonemap/ToneMapper.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/ToneMapper.js +474 -0
- package/src/shade/renderer/deferred/tonemap/TonemapAces2Tables.d.ts +5 -13
- package/src/shade/renderer/deferred/tonemap/TonemapAces2Tables.d.ts.map +1 -1
- package/src/shade/renderer/deferred/tonemap/TonemapAces2Tables.js +5 -44
- package/src/shade/renderer/deferred/tonemap/TonemapDisplaySettings.d.ts +7 -25
- package/src/shade/renderer/deferred/tonemap/TonemapDisplaySettings.d.ts.map +1 -1
- package/src/shade/renderer/deferred/tonemap/TonemapDisplaySettings.js +11 -51
- package/src/shade/renderer/deferred/tonemap/m3_white_balance_rec709.d.ts +20 -0
- package/src/shade/renderer/deferred/tonemap/m3_white_balance_rec709.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/m3_white_balance_rec709.js +103 -0
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_lut.d.ts +9 -0
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_lut.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/{shader_tonemap_HDR_none.js → shader_tonemap_HDR_lut.js} +22 -18
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_lut.d.ts +9 -0
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_lut.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/{shader_tonemap_LDR_aces.js → shader_tonemap_LDR_lut.js} +24 -24
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_lut_bake.d.ts +23 -0
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_lut_bake.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_lut_bake.js +92 -0
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_lut_sdr_from_hdr.d.ts +15 -0
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_lut_sdr_from_hdr.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_lut_sdr_from_hdr.js +73 -0
- package/src/shade/renderer/fluid/GPUFluidCascade.d.ts +3 -3
- package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +1 -1
- package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +6 -6
- package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.d.ts +1 -1
- package/src/shade/renderer/fluid/query/GPUFluidQueries.d.ts +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts +1 -1
- package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
- package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
- package/src/shade/renderer/shader/chunk/color/conversion/chunk_CRM_REC709_AP1.d.ts +22 -0
- package/src/shade/renderer/shader/chunk/color/conversion/chunk_CRM_REC709_AP1.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/color/conversion/chunk_CRM_REC709_AP1.js +62 -0
- package/src/shade/renderer/shader/chunk/color/grade/chunk_color_grade.d.ts +13 -0
- package/src/shade/renderer/shader/chunk/color/grade/chunk_color_grade.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/color/grade/chunk_color_grade.js +140 -0
- package/src/shade/renderer/shader/chunk/color/oklab/chunk_oklab_compute_max_saturation.d.ts +11 -0
- package/src/shade/renderer/shader/chunk/color/oklab/chunk_oklab_compute_max_saturation.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/color/oklab/chunk_oklab_compute_max_saturation.js +65 -0
- package/src/shade/renderer/shader/chunk/color/tonemap/chunk_bt2390_eetf.d.ts +14 -0
- package/src/shade/renderer/shader/chunk/color/tonemap/chunk_bt2390_eetf.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/color/tonemap/chunk_bt2390_eetf.js +38 -0
- package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_lut.d.ts +16 -0
- package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_lut.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_lut.js +41 -0
- package/src/shade/renderer/water/query/GPUWaterQueries.d.ts +1 -1
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +8 -8
- package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
- package/src/shade/wgsl/emulator/WGSLLanguageCore.js +2 -0
- package/src/shade/renderer/ShadeToneMappingMode.d.ts +0 -15
- package/src/shade/renderer/ShadeToneMappingMode.d.ts.map +0 -1
- package/src/shade/renderer/ShadeToneMappingMode.js +0 -41
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR.d.ts +0 -9
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR.d.ts.map +0 -1
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR.js +0 -79
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_aces.d.ts +0 -17
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_aces.d.ts.map +0 -1
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_aces.js +0 -101
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_none.d.ts +0 -9
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_none.d.ts.map +0 -1
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_pbr_neutral.d.ts +0 -10
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_pbr_neutral.d.ts.map +0 -1
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_pbr_neutral.js +0 -80
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR.d.ts +0 -9
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR.d.ts.map +0 -1
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR.js +0 -80
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_aces.d.ts +0 -10
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_aces.d.ts.map +0 -1
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_none.d.ts +0 -9
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_none.d.ts.map +0 -1
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_none.js +0 -71
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_pbr_neutral.d.ts +0 -10
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_pbr_neutral.d.ts.map +0 -1
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_pbr_neutral.js +0 -76
- package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_hdr_bt2390.d.ts +0 -3
- package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_hdr_bt2390.d.ts.map +0 -1
- 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":"
|
|
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"
|
|
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
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
if (
|
|
561
|
-
|
|
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] =
|
|
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":"
|
|
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}
|
|
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
|
|
78
|
-
*
|
|
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
|
-
|
|
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
|
|