reze-engine 0.15.0 → 0.15.1
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/README.md +3 -3
- package/dist/gpu-profile.d.ts +19 -0
- package/dist/gpu-profile.d.ts.map +1 -0
- package/dist/gpu-profile.js +120 -0
- package/dist/physics/body.d.ts +2 -0
- package/dist/physics/body.d.ts.map +1 -1
- package/dist/physics/body.js +30 -0
- package/dist/physics/constraint.d.ts +13 -0
- package/dist/physics/constraint.d.ts.map +1 -1
- package/dist/physics/constraint.js +13 -0
- package/dist/physics/contact.d.ts +28 -0
- package/dist/physics/contact.d.ts.map +1 -1
- package/dist/physics/contact.js +27 -32
- package/dist/physics/physics.d.ts.map +1 -1
- package/dist/physics/physics.js +2 -4
- package/dist/physics/profile.d.ts +18 -0
- package/dist/physics/profile.d.ts.map +1 -0
- package/dist/physics/profile.js +44 -0
- package/dist/physics/solver.d.ts.map +1 -1
- package/dist/physics/solver.js +436 -293
- package/package.json +1 -1
- package/src/physics/body.ts +29 -0
- package/src/physics/constraint.ts +30 -0
- package/src/physics/contact.ts +43 -30
- package/src/physics/physics.ts +2 -4
- package/src/physics/solver.ts +431 -284
- package/dist/physics-debug.d.ts +0 -30
- package/dist/physics-debug.d.ts.map +0 -1
- package/dist/physics-debug.js +0 -526
- package/dist/shaders/passes/physics-debug.d.ts +0 -2
- package/dist/shaders/passes/physics-debug.d.ts.map +0 -1
- package/dist/shaders/passes/physics-debug.js +0 -69
package/dist/physics/solver.js
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
// Gauss-Seidel: per axis, target a relative velocity (limit correction +
|
|
3
3
|
// spring), apply the impulse needed to reach it. Friction is two Coulomb
|
|
4
4
|
// rows per contact, normal is push-only.
|
|
5
|
+
//
|
|
6
|
+
// Two passes per substep:
|
|
7
|
+
// 1. SETUP — for each constraint and contact, compute every quantity that
|
|
8
|
+
// doesn't depend on lv/av (world axes, lever arms, Jacobian denominators,
|
|
9
|
+
// target velocities, friction tangent bases, restitution reference).
|
|
10
|
+
// These are constant during solve since pos/ori/inertia don't change.
|
|
11
|
+
// 2. ITERATE — `iterations` passes that read the cache and apply impulses
|
|
12
|
+
// based on the current lv/av. ~2× faster than recomputing per iter.
|
|
5
13
|
import { Mat4 } from "../math";
|
|
6
14
|
import { STOP_ERP } from "./constraint";
|
|
7
15
|
const BOUNCE_THRESHOLD = 2.0;
|
|
@@ -10,12 +18,7 @@ const _TA = new Float32Array(16);
|
|
|
10
18
|
const _TB = new Float32Array(16);
|
|
11
19
|
const _bodyMatA = new Float32Array(16);
|
|
12
20
|
const _bodyMatB = new Float32Array(16);
|
|
13
|
-
const
|
|
14
|
-
const _angAxes = new Float32Array(9); // 3 angular axes × xyz
|
|
15
|
-
const _linDiff = new Float32Array(3);
|
|
16
|
-
const _angDiff = new Float32Array(3);
|
|
17
|
-
const _rA = new Float32Array(3);
|
|
18
|
-
const _rB = new Float32Array(3);
|
|
21
|
+
const _angDiffScratch = new Float32Array(3);
|
|
19
22
|
export function solveConstraints(store, constraints, contacts, dt, iterations) {
|
|
20
23
|
if (dt <= 0)
|
|
21
24
|
return;
|
|
@@ -24,250 +27,307 @@ export function solveConstraints(store, constraints, contacts, dt, iterations) {
|
|
|
24
27
|
const invDt = 1 / dt;
|
|
25
28
|
const lv = store.linearVelocities;
|
|
26
29
|
const av = store.angularVelocities;
|
|
27
|
-
const pos = store.positions;
|
|
28
30
|
const invMass = store.invMass;
|
|
29
31
|
const invInertia = store.invInertia;
|
|
32
|
+
for (let c = 0; c < constraints.length; c++) {
|
|
33
|
+
setupConstraint(constraints[c], store, dt, invDt);
|
|
34
|
+
}
|
|
35
|
+
for (let ci = 0; ci < contacts.count; ci++) {
|
|
36
|
+
setupContactRow(contacts.get(ci), lv, av, invMass, invInertia);
|
|
37
|
+
}
|
|
30
38
|
for (let iter = 0; iter < iterations; iter++) {
|
|
31
39
|
for (let c = 0; c < constraints.length; c++) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
lv[ai + 2] -= j * imA * axz;
|
|
130
|
-
av[ai + 0] -= j * iiA * cAx;
|
|
131
|
-
av[ai + 1] -= j * iiA * cAy;
|
|
132
|
-
av[ai + 2] -= j * iiA * cAz;
|
|
133
|
-
}
|
|
134
|
-
if (imB > 0) {
|
|
135
|
-
lv[bi + 0] += j * imB * axx;
|
|
136
|
-
lv[bi + 1] += j * imB * axy;
|
|
137
|
-
lv[bi + 2] += j * imB * axz;
|
|
138
|
-
av[bi + 0] += j * iiB * cBx;
|
|
139
|
-
av[bi + 1] += j * iiB * cBy;
|
|
140
|
-
av[bi + 2] += j * iiB * cBz;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
// Angular part: relative rotation TA^T·TB → Euler XYZ; axes from
|
|
145
|
-
// TA.col2 × TB.col0 (as Bullet's calculatedAxis derivation).
|
|
146
|
-
const r00 = _TA[0] * _TB[0] + _TA[1] * _TB[1] + _TA[2] * _TB[2];
|
|
147
|
-
const r01 = _TA[0] * _TB[4] + _TA[1] * _TB[5] + _TA[2] * _TB[6];
|
|
148
|
-
const r10 = _TA[4] * _TB[0] + _TA[5] * _TB[1] + _TA[6] * _TB[2];
|
|
149
|
-
const r11 = _TA[4] * _TB[4] + _TA[5] * _TB[5] + _TA[6] * _TB[6];
|
|
150
|
-
const r20 = _TA[8] * _TB[0] + _TA[9] * _TB[1] + _TA[10] * _TB[2];
|
|
151
|
-
const r21 = _TA[8] * _TB[4] + _TA[9] * _TB[5] + _TA[10] * _TB[6];
|
|
152
|
-
const r22 = _TA[8] * _TB[8] + _TA[9] * _TB[9] + _TA[10] * _TB[10];
|
|
153
|
-
matrixToEulerXYZ(r00, r01, r10, r11, r20, r21, r22, _angDiff);
|
|
154
|
-
const a2x = _TA[8], a2y = _TA[9], a2z = _TA[10];
|
|
155
|
-
const b0x = _TB[0], b0y = _TB[1], b0z = _TB[2];
|
|
156
|
-
// ax[1] = a2 × b0; ax[0] = ax[1] × a2; ax[2] = b0 × ax[1].
|
|
157
|
-
let yx = a2y * b0z - a2z * b0y;
|
|
158
|
-
let yy = a2z * b0x - a2x * b0z;
|
|
159
|
-
let yz = a2x * b0y - a2y * b0x;
|
|
160
|
-
let l = Math.hypot(yx, yy, yz);
|
|
161
|
-
if (l > 1e-8) {
|
|
162
|
-
const inv = 1 / l;
|
|
163
|
-
yx *= inv;
|
|
164
|
-
yy *= inv;
|
|
165
|
-
yz *= inv;
|
|
166
|
-
}
|
|
167
|
-
_angAxes[3] = yx;
|
|
168
|
-
_angAxes[4] = yy;
|
|
169
|
-
_angAxes[5] = yz;
|
|
170
|
-
let xx = yy * a2z - yz * a2y;
|
|
171
|
-
let xy = yz * a2x - yx * a2z;
|
|
172
|
-
let xz = yx * a2y - yy * a2x;
|
|
173
|
-
l = Math.hypot(xx, xy, xz);
|
|
174
|
-
if (l > 1e-8) {
|
|
175
|
-
const inv = 1 / l;
|
|
176
|
-
xx *= inv;
|
|
177
|
-
xy *= inv;
|
|
178
|
-
xz *= inv;
|
|
179
|
-
}
|
|
180
|
-
_angAxes[0] = xx;
|
|
181
|
-
_angAxes[1] = xy;
|
|
182
|
-
_angAxes[2] = xz;
|
|
183
|
-
let zx = b0y * yz - b0z * yy;
|
|
184
|
-
let zy = b0z * yx - b0x * yz;
|
|
185
|
-
let zz = b0x * yy - b0y * yx;
|
|
186
|
-
l = Math.hypot(zx, zy, zz);
|
|
187
|
-
if (l > 1e-8) {
|
|
188
|
-
const inv = 1 / l;
|
|
189
|
-
zx *= inv;
|
|
190
|
-
zy *= inv;
|
|
191
|
-
zz *= inv;
|
|
40
|
+
iterateConstraint(constraints[c], lv, av, invMass, invInertia);
|
|
41
|
+
}
|
|
42
|
+
for (let ci = 0; ci < contacts.count; ci++) {
|
|
43
|
+
iterateContactRow(contacts.get(ci), lv, av, invMass, invInertia);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// SETUP: compute everything that doesn't depend on velocities. Caller
|
|
48
|
+
// guarantees pos/ori don't change between this and the iter loop.
|
|
49
|
+
function setupConstraint(con, store, dt, invDt) {
|
|
50
|
+
const a = con.bodyA;
|
|
51
|
+
const b = con.bodyB;
|
|
52
|
+
const imA = store.invMass[a];
|
|
53
|
+
const imB = store.invMass[b];
|
|
54
|
+
const iiA = store.invInertia[a];
|
|
55
|
+
const iiB = store.invInertia[b];
|
|
56
|
+
con.cacheSkip = imA === 0 && imB === 0;
|
|
57
|
+
if (con.cacheSkip)
|
|
58
|
+
return;
|
|
59
|
+
buildBodyMat(store, a, _bodyMatA);
|
|
60
|
+
buildBodyMat(store, b, _bodyMatB);
|
|
61
|
+
Mat4.multiplyArrays(_bodyMatA, 0, con.frameA, 0, _TA, 0);
|
|
62
|
+
Mat4.multiplyArrays(_bodyMatB, 0, con.frameB, 0, _TB, 0);
|
|
63
|
+
// Mass-weighted shared anchor (Bullet 2.75 m_AnchorPos).
|
|
64
|
+
const pos = store.positions;
|
|
65
|
+
const ai = a * 3;
|
|
66
|
+
const bi = b * 3;
|
|
67
|
+
const weightA = imB === 0 ? 1 : imA / (imA + imB);
|
|
68
|
+
const weightB = 1 - weightA;
|
|
69
|
+
const anchorX = _TA[12] * weightA + _TB[12] * weightB;
|
|
70
|
+
const anchorY = _TA[13] * weightA + _TB[13] * weightB;
|
|
71
|
+
const anchorZ = _TA[14] * weightA + _TB[14] * weightB;
|
|
72
|
+
const rAx = anchorX - pos[ai + 0];
|
|
73
|
+
const rAy = anchorY - pos[ai + 1];
|
|
74
|
+
const rAz = anchorZ - pos[ai + 2];
|
|
75
|
+
const rBx = anchorX - pos[bi + 0];
|
|
76
|
+
const rBy = anchorY - pos[bi + 1];
|
|
77
|
+
const rBz = anchorZ - pos[bi + 2];
|
|
78
|
+
const lA = con.cacheLeverA;
|
|
79
|
+
const lB = con.cacheLeverB;
|
|
80
|
+
lA[0] = rAx;
|
|
81
|
+
lA[1] = rAy;
|
|
82
|
+
lA[2] = rAz;
|
|
83
|
+
lB[0] = rBx;
|
|
84
|
+
lB[1] = rBy;
|
|
85
|
+
lB[2] = rBz;
|
|
86
|
+
// linearDiff = TA.basis^T · (TB.origin − TA.origin); axes = TA columns 0/1/2.
|
|
87
|
+
const dxw = _TB[12] - _TA[12];
|
|
88
|
+
const dyw = _TB[13] - _TA[13];
|
|
89
|
+
const dzw = _TB[14] - _TA[14];
|
|
90
|
+
const linDiff0 = _TA[0] * dxw + _TA[1] * dyw + _TA[2] * dzw;
|
|
91
|
+
const linDiff1 = _TA[4] * dxw + _TA[5] * dyw + _TA[6] * dzw;
|
|
92
|
+
const linDiff2 = _TA[8] * dxw + _TA[9] * dyw + _TA[10] * dzw;
|
|
93
|
+
const axes = con.cacheLinAxes;
|
|
94
|
+
const cA = con.cacheLinCrossA;
|
|
95
|
+
const cB = con.cacheLinCrossB;
|
|
96
|
+
const jac = con.cacheLinJacInv;
|
|
97
|
+
const tgt = con.cacheLinTargetVel;
|
|
98
|
+
const act = con.cacheLinActive;
|
|
99
|
+
for (let i = 0; i < 3; i++) {
|
|
100
|
+
const o = i * 3;
|
|
101
|
+
const axx = i === 0 ? _TA[0] : i === 1 ? _TA[4] : _TA[8];
|
|
102
|
+
const axy = i === 0 ? _TA[1] : i === 1 ? _TA[5] : _TA[9];
|
|
103
|
+
const axz = i === 0 ? _TA[2] : i === 1 ? _TA[6] : _TA[10];
|
|
104
|
+
axes[o + 0] = axx;
|
|
105
|
+
axes[o + 1] = axy;
|
|
106
|
+
axes[o + 2] = axz;
|
|
107
|
+
const cAx = rAy * axz - rAz * axy;
|
|
108
|
+
const cAy = rAz * axx - rAx * axz;
|
|
109
|
+
const cAz = rAx * axy - rAy * axx;
|
|
110
|
+
const cBx = rBy * axz - rBz * axy;
|
|
111
|
+
const cBy = rBz * axx - rBx * axz;
|
|
112
|
+
const cBz = rBx * axy - rBy * axx;
|
|
113
|
+
cA[o + 0] = cAx;
|
|
114
|
+
cA[o + 1] = cAy;
|
|
115
|
+
cA[o + 2] = cAz;
|
|
116
|
+
cB[o + 0] = cBx;
|
|
117
|
+
cB[o + 1] = cBy;
|
|
118
|
+
cB[o + 2] = cBz;
|
|
119
|
+
const denom = imA + imB +
|
|
120
|
+
(cAx * cAx + cAy * cAy + cAz * cAz) * iiA +
|
|
121
|
+
(cBx * cBx + cBy * cBy + cBz * cBz) * iiB;
|
|
122
|
+
jac[i] = denom > 0 ? 1 / denom : 0;
|
|
123
|
+
const lo = con.linearMin[i];
|
|
124
|
+
const hi = con.linearMax[i];
|
|
125
|
+
const curr = i === 0 ? linDiff0 : i === 1 ? linDiff1 : linDiff2;
|
|
126
|
+
let target = 0;
|
|
127
|
+
let active = 0;
|
|
128
|
+
if (lo <= hi) {
|
|
129
|
+
let err = 0;
|
|
130
|
+
if (curr < lo)
|
|
131
|
+
err = curr - lo;
|
|
132
|
+
else if (curr > hi)
|
|
133
|
+
err = curr - hi;
|
|
134
|
+
if (err !== 0) {
|
|
135
|
+
target = -err * STOP_ERP * invDt;
|
|
136
|
+
active = 1;
|
|
192
137
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
138
|
+
}
|
|
139
|
+
if (con.springEnabled[i]) {
|
|
140
|
+
target += -con.springStiffness[i] * (curr - con.equilibriumPoint[i]) * dt;
|
|
141
|
+
active = 1;
|
|
142
|
+
}
|
|
143
|
+
tgt[i] = target;
|
|
144
|
+
act[i] = denom > 0 ? active : 0;
|
|
145
|
+
}
|
|
146
|
+
// Angular: TA^T · TB → Euler XYZ; axes from TA.col2 × TB.col0.
|
|
147
|
+
const r00 = _TA[0] * _TB[0] + _TA[1] * _TB[1] + _TA[2] * _TB[2];
|
|
148
|
+
const r01 = _TA[0] * _TB[4] + _TA[1] * _TB[5] + _TA[2] * _TB[6];
|
|
149
|
+
const r10 = _TA[4] * _TB[0] + _TA[5] * _TB[1] + _TA[6] * _TB[2];
|
|
150
|
+
const r11 = _TA[4] * _TB[4] + _TA[5] * _TB[5] + _TA[6] * _TB[6];
|
|
151
|
+
const r20 = _TA[8] * _TB[0] + _TA[9] * _TB[1] + _TA[10] * _TB[2];
|
|
152
|
+
const r21 = _TA[8] * _TB[4] + _TA[9] * _TB[5] + _TA[10] * _TB[6];
|
|
153
|
+
const r22 = _TA[8] * _TB[8] + _TA[9] * _TB[9] + _TA[10] * _TB[10];
|
|
154
|
+
matrixToEulerXYZ(r00, r01, r10, r11, r20, r21, r22, _angDiffScratch);
|
|
155
|
+
const a2x = _TA[8], a2y = _TA[9], a2z = _TA[10];
|
|
156
|
+
const b0x = _TB[0], b0y = _TB[1], b0z = _TB[2];
|
|
157
|
+
let yx = a2y * b0z - a2z * b0y;
|
|
158
|
+
let yy = a2z * b0x - a2x * b0z;
|
|
159
|
+
let yz = a2x * b0y - a2y * b0x;
|
|
160
|
+
let l = Math.hypot(yx, yy, yz);
|
|
161
|
+
if (l > 1e-8) {
|
|
162
|
+
const inv = 1 / l;
|
|
163
|
+
yx *= inv;
|
|
164
|
+
yy *= inv;
|
|
165
|
+
yz *= inv;
|
|
166
|
+
}
|
|
167
|
+
let xx = yy * a2z - yz * a2y;
|
|
168
|
+
let xy = yz * a2x - yx * a2z;
|
|
169
|
+
let xz = yx * a2y - yy * a2x;
|
|
170
|
+
l = Math.hypot(xx, xy, xz);
|
|
171
|
+
if (l > 1e-8) {
|
|
172
|
+
const inv = 1 / l;
|
|
173
|
+
xx *= inv;
|
|
174
|
+
xy *= inv;
|
|
175
|
+
xz *= inv;
|
|
176
|
+
}
|
|
177
|
+
let zx = b0y * yz - b0z * yy;
|
|
178
|
+
let zy = b0z * yx - b0x * yz;
|
|
179
|
+
let zz = b0x * yy - b0y * yx;
|
|
180
|
+
l = Math.hypot(zx, zy, zz);
|
|
181
|
+
if (l > 1e-8) {
|
|
182
|
+
const inv = 1 / l;
|
|
183
|
+
zx *= inv;
|
|
184
|
+
zy *= inv;
|
|
185
|
+
zz *= inv;
|
|
186
|
+
}
|
|
187
|
+
const angAxes = con.cacheAngAxes;
|
|
188
|
+
angAxes[0] = xx;
|
|
189
|
+
angAxes[1] = xy;
|
|
190
|
+
angAxes[2] = xz;
|
|
191
|
+
angAxes[3] = yx;
|
|
192
|
+
angAxes[4] = yy;
|
|
193
|
+
angAxes[5] = yz;
|
|
194
|
+
angAxes[6] = zx;
|
|
195
|
+
angAxes[7] = zy;
|
|
196
|
+
angAxes[8] = zz;
|
|
197
|
+
const angDenom = iiA + iiB;
|
|
198
|
+
con.cacheAngJacInv = angDenom > 0 ? 1 / angDenom : 0;
|
|
199
|
+
const angTgt = con.cacheAngTargetVel;
|
|
200
|
+
const angAct = con.cacheAngActive;
|
|
201
|
+
for (let i = 0; i < 3; i++) {
|
|
202
|
+
const idx = i + 3;
|
|
203
|
+
const lo = con.angularMin[i];
|
|
204
|
+
const hi = con.angularMax[i];
|
|
205
|
+
const curr = _angDiffScratch[i];
|
|
206
|
+
let target = 0;
|
|
207
|
+
let active = 0;
|
|
208
|
+
if (lo <= hi) {
|
|
209
|
+
let err = 0;
|
|
210
|
+
if (curr < lo)
|
|
211
|
+
err = curr - lo;
|
|
212
|
+
else if (curr > hi)
|
|
213
|
+
err = curr - hi;
|
|
214
|
+
// Sign flip vs linear: d(angDiff)/dt = −(ω_B − ω_A)·ax.
|
|
215
|
+
if (err !== 0) {
|
|
216
|
+
target = err * STOP_ERP * invDt;
|
|
217
|
+
active = 1;
|
|
243
218
|
}
|
|
244
219
|
}
|
|
245
|
-
|
|
246
|
-
|
|
220
|
+
if (con.springEnabled[idx]) {
|
|
221
|
+
target += con.springStiffness[idx] * (curr - con.equilibriumPoint[idx]) * dt;
|
|
222
|
+
active = 1;
|
|
247
223
|
}
|
|
224
|
+
angTgt[i] = target;
|
|
225
|
+
angAct[i] = angDenom > 0 ? active : 0;
|
|
248
226
|
}
|
|
249
227
|
}
|
|
250
|
-
//
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const ai = c.bodyA * 3, bi = c.bodyB * 3;
|
|
254
|
-
const imA = invMass[c.bodyA], imB = invMass[c.bodyB];
|
|
255
|
-
const iiA = invInertia[c.bodyA], iiB = invInertia[c.bodyB];
|
|
256
|
-
if (imA === 0 && imB === 0)
|
|
228
|
+
// ITER: read cache, compute relVel from current lv/av, apply impulse.
|
|
229
|
+
function iterateConstraint(con, lv, av, invMass, invInertia) {
|
|
230
|
+
if (con.cacheSkip)
|
|
257
231
|
return;
|
|
258
|
-
const
|
|
259
|
-
const
|
|
260
|
-
const
|
|
232
|
+
const a = con.bodyA;
|
|
233
|
+
const b = con.bodyB;
|
|
234
|
+
const ai = a * 3;
|
|
235
|
+
const bi = b * 3;
|
|
236
|
+
const imA = invMass[a];
|
|
237
|
+
const imB = invMass[b];
|
|
238
|
+
const iiA = invInertia[a];
|
|
239
|
+
const iiB = invInertia[b];
|
|
240
|
+
// Linear axes — relVel at the offset point: v_pivot = v_CG + ω × r.
|
|
241
|
+
const lA = con.cacheLeverA;
|
|
242
|
+
const lB = con.cacheLeverB;
|
|
243
|
+
const rAx = lA[0], rAy = lA[1], rAz = lA[2];
|
|
244
|
+
const rBx = lB[0], rBy = lB[1], rBz = lB[2];
|
|
245
|
+
const axes = con.cacheLinAxes;
|
|
246
|
+
const cA = con.cacheLinCrossA;
|
|
247
|
+
const cB = con.cacheLinCrossB;
|
|
248
|
+
const jac = con.cacheLinJacInv;
|
|
249
|
+
const tgt = con.cacheLinTargetVel;
|
|
250
|
+
const act = con.cacheLinActive;
|
|
261
251
|
const vAx = lv[ai + 0] + av[ai + 1] * rAz - av[ai + 2] * rAy;
|
|
262
252
|
const vAy = lv[ai + 1] + av[ai + 2] * rAx - av[ai + 0] * rAz;
|
|
263
253
|
const vAz = lv[ai + 2] + av[ai + 0] * rAy - av[ai + 1] * rAx;
|
|
264
254
|
const vBx = lv[bi + 0] + av[bi + 1] * rBz - av[bi + 2] * rBy;
|
|
265
255
|
const vBy = lv[bi + 1] + av[bi + 2] * rBx - av[bi + 0] * rBz;
|
|
266
256
|
const vBz = lv[bi + 2] + av[bi + 0] * rBy - av[bi + 1] * rBx;
|
|
267
|
-
const
|
|
268
|
-
const
|
|
269
|
-
const
|
|
270
|
-
|
|
257
|
+
const dvx = vBx - vAx;
|
|
258
|
+
const dvy = vBy - vAy;
|
|
259
|
+
const dvz = vBz - vAz;
|
|
260
|
+
for (let i = 0; i < 3; i++) {
|
|
261
|
+
if (!act[i])
|
|
262
|
+
continue;
|
|
263
|
+
const o = i * 3;
|
|
264
|
+
const axx = axes[o + 0], axy = axes[o + 1], axz = axes[o + 2];
|
|
265
|
+
const relVel = dvx * axx + dvy * axy + dvz * axz;
|
|
266
|
+
const j = (tgt[i] - relVel) * jac[i];
|
|
267
|
+
if (j === 0)
|
|
268
|
+
continue;
|
|
269
|
+
if (imA > 0) {
|
|
270
|
+
lv[ai + 0] -= j * imA * axx;
|
|
271
|
+
lv[ai + 1] -= j * imA * axy;
|
|
272
|
+
lv[ai + 2] -= j * imA * axz;
|
|
273
|
+
av[ai + 0] -= j * iiA * cA[o + 0];
|
|
274
|
+
av[ai + 1] -= j * iiA * cA[o + 1];
|
|
275
|
+
av[ai + 2] -= j * iiA * cA[o + 2];
|
|
276
|
+
}
|
|
277
|
+
if (imB > 0) {
|
|
278
|
+
lv[bi + 0] += j * imB * axx;
|
|
279
|
+
lv[bi + 1] += j * imB * axy;
|
|
280
|
+
lv[bi + 2] += j * imB * axz;
|
|
281
|
+
av[bi + 0] += j * iiB * cB[o + 0];
|
|
282
|
+
av[bi + 1] += j * iiB * cB[o + 1];
|
|
283
|
+
av[bi + 2] += j * iiB * cB[o + 2];
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// Angular axes — relAv = ω_B − ω_A.
|
|
287
|
+
const angJacInv = con.cacheAngJacInv;
|
|
288
|
+
if (angJacInv === 0)
|
|
289
|
+
return;
|
|
290
|
+
const angAxes = con.cacheAngAxes;
|
|
291
|
+
const angTgt = con.cacheAngTargetVel;
|
|
292
|
+
const angAct = con.cacheAngActive;
|
|
293
|
+
const dax = av[bi + 0] - av[ai + 0];
|
|
294
|
+
const day = av[bi + 1] - av[ai + 1];
|
|
295
|
+
const daz = av[bi + 2] - av[ai + 2];
|
|
296
|
+
for (let i = 0; i < 3; i++) {
|
|
297
|
+
if (!angAct[i])
|
|
298
|
+
continue;
|
|
299
|
+
const o = i * 3;
|
|
300
|
+
const axx = angAxes[o + 0], axy = angAxes[o + 1], axz = angAxes[o + 2];
|
|
301
|
+
const relAv = dax * axx + day * axy + daz * axz;
|
|
302
|
+
const j = (angTgt[i] - relAv) * angJacInv;
|
|
303
|
+
if (j === 0)
|
|
304
|
+
continue;
|
|
305
|
+
if (iiA > 0) {
|
|
306
|
+
av[ai + 0] -= j * iiA * axx;
|
|
307
|
+
av[ai + 1] -= j * iiA * axy;
|
|
308
|
+
av[ai + 2] -= j * iiA * axz;
|
|
309
|
+
}
|
|
310
|
+
if (iiB > 0) {
|
|
311
|
+
av[bi + 0] += j * iiB * axx;
|
|
312
|
+
av[bi + 1] += j * iiB * axy;
|
|
313
|
+
av[bi + 2] += j * iiB * axz;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// SETUP: pre-compute Jacobians, friction basis, and the bounce reference
|
|
318
|
+
// from the *initial* closing velocity (Bullet's pattern — captures restitution
|
|
319
|
+
// before iter 1 zeroes out the approach).
|
|
320
|
+
function setupContactRow(c, lv, av, invMass, invInertia) {
|
|
321
|
+
const ai = c.bodyA * 3;
|
|
322
|
+
const bi = c.bodyB * 3;
|
|
323
|
+
const imA = invMass[c.bodyA];
|
|
324
|
+
const imB = invMass[c.bodyB];
|
|
325
|
+
const iiA = invInertia[c.bodyA];
|
|
326
|
+
const iiB = invInertia[c.bodyB];
|
|
327
|
+
const rAx = c.rAx, rAy = c.rAy, rAz = c.rAz;
|
|
328
|
+
const rBx = c.rBx, rBy = c.rBy, rBz = c.rBz;
|
|
329
|
+
const nx = c.nx, ny = c.ny, nz = c.nz;
|
|
330
|
+
// Normal Jacobian.
|
|
271
331
|
const cAxN = rAy * nz - rAz * ny;
|
|
272
332
|
const cAyN = rAz * nx - rAx * nz;
|
|
273
333
|
const cAzN = rAx * ny - rAy * nx;
|
|
@@ -277,44 +337,25 @@ function solveContactRow(c, lv, av, invMass, invInertia) {
|
|
|
277
337
|
const denomN = imA + imB +
|
|
278
338
|
(cAxN * cAxN + cAyN * cAyN + cAzN * cAzN) * iiA +
|
|
279
339
|
(cBxN * cBxN + cByN * cByN + cBzN * cBzN) * iiB;
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
if (imA > 0) {
|
|
300
|
-
lv[ai + 0] -= dImpN * imA * nx;
|
|
301
|
-
lv[ai + 1] -= dImpN * imA * ny;
|
|
302
|
-
lv[ai + 2] -= dImpN * imA * nz;
|
|
303
|
-
av[ai + 0] -= dImpN * iiA * cAxN;
|
|
304
|
-
av[ai + 1] -= dImpN * iiA * cAyN;
|
|
305
|
-
av[ai + 2] -= dImpN * iiA * cAzN;
|
|
306
|
-
}
|
|
307
|
-
if (imB > 0) {
|
|
308
|
-
lv[bi + 0] += dImpN * imB * nx;
|
|
309
|
-
lv[bi + 1] += dImpN * imB * ny;
|
|
310
|
-
lv[bi + 2] += dImpN * imB * nz;
|
|
311
|
-
av[bi + 0] += dImpN * iiB * cBxN;
|
|
312
|
-
av[bi + 1] += dImpN * iiB * cByN;
|
|
313
|
-
av[bi + 2] += dImpN * iiB * cBzN;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
// Friction tangent basis. Pick the axis least aligned with n to avoid a
|
|
317
|
-
// near-zero cross product.
|
|
340
|
+
c.cAxN = cAxN;
|
|
341
|
+
c.cAyN = cAyN;
|
|
342
|
+
c.cAzN = cAzN;
|
|
343
|
+
c.cBxN = cBxN;
|
|
344
|
+
c.cByN = cByN;
|
|
345
|
+
c.cBzN = cBzN;
|
|
346
|
+
c.jacInvN = denomN > 0 ? 1 / denomN : 0;
|
|
347
|
+
// Restitution reference, captured from initial relVelN.
|
|
348
|
+
const vAx = lv[ai + 0] + av[ai + 1] * rAz - av[ai + 2] * rAy;
|
|
349
|
+
const vAy = lv[ai + 1] + av[ai + 2] * rAx - av[ai + 0] * rAz;
|
|
350
|
+
const vAz = lv[ai + 2] + av[ai + 0] * rAy - av[ai + 1] * rAx;
|
|
351
|
+
const vBx = lv[bi + 0] + av[bi + 1] * rBz - av[bi + 2] * rBy;
|
|
352
|
+
const vBy = lv[bi + 1] + av[bi + 2] * rBx - av[bi + 0] * rBz;
|
|
353
|
+
const vBz = lv[bi + 2] + av[bi + 0] * rBy - av[bi + 1] * rBx;
|
|
354
|
+
const relVelN0 = (vBx - vAx) * nx + (vBy - vAy) * ny + (vBz - vAz) * nz;
|
|
355
|
+
c.bounceVel = c.restitution > 0 && relVelN0 < -BOUNCE_THRESHOLD
|
|
356
|
+
? -c.restitution * relVelN0
|
|
357
|
+
: 0;
|
|
358
|
+
// Friction tangent basis. Pick the axis least aligned with n.
|
|
318
359
|
let t1x, t1y, t1z;
|
|
319
360
|
if (Math.abs(nx) < 0.7071) {
|
|
320
361
|
t1x = 0;
|
|
@@ -326,36 +367,138 @@ function solveContactRow(c, lv, av, invMass, invInertia) {
|
|
|
326
367
|
t1y = 0;
|
|
327
368
|
t1z = -nx;
|
|
328
369
|
}
|
|
329
|
-
const
|
|
330
|
-
if (
|
|
370
|
+
const tl = Math.hypot(t1x, t1y, t1z);
|
|
371
|
+
if (tl > 1e-8) {
|
|
372
|
+
const tInv = 1 / tl;
|
|
373
|
+
t1x *= tInv;
|
|
374
|
+
t1y *= tInv;
|
|
375
|
+
t1z *= tInv;
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
c.jacInvT1 = 0;
|
|
379
|
+
c.jacInvT2 = 0;
|
|
331
380
|
return;
|
|
332
|
-
|
|
333
|
-
t1x *= tInv;
|
|
334
|
-
t1y *= tInv;
|
|
335
|
-
t1z *= tInv;
|
|
381
|
+
}
|
|
336
382
|
const t2x = ny * t1z - nz * t1y;
|
|
337
383
|
const t2y = nz * t1x - nx * t1z;
|
|
338
384
|
const t2z = nx * t1y - ny * t1x;
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
385
|
+
c.t1x = t1x;
|
|
386
|
+
c.t1y = t1y;
|
|
387
|
+
c.t1z = t1z;
|
|
388
|
+
c.t2x = t2x;
|
|
389
|
+
c.t2y = t2y;
|
|
390
|
+
c.t2z = t2z;
|
|
391
|
+
// Friction Jacobians.
|
|
392
|
+
const cAxT1 = rAy * t1z - rAz * t1y;
|
|
393
|
+
const cAyT1 = rAz * t1x - rAx * t1z;
|
|
394
|
+
const cAzT1 = rAx * t1y - rAy * t1x;
|
|
395
|
+
const cBxT1 = rBy * t1z - rBz * t1y;
|
|
396
|
+
const cByT1 = rBz * t1x - rBx * t1z;
|
|
397
|
+
const cBzT1 = rBx * t1y - rBy * t1x;
|
|
398
|
+
const denomT1 = imA + imB +
|
|
399
|
+
(cAxT1 * cAxT1 + cAyT1 * cAyT1 + cAzT1 * cAzT1) * iiA +
|
|
400
|
+
(cBxT1 * cBxT1 + cByT1 * cByT1 + cBzT1 * cBzT1) * iiB;
|
|
401
|
+
c.cAxT1 = cAxT1;
|
|
402
|
+
c.cAyT1 = cAyT1;
|
|
403
|
+
c.cAzT1 = cAzT1;
|
|
404
|
+
c.cBxT1 = cBxT1;
|
|
405
|
+
c.cByT1 = cByT1;
|
|
406
|
+
c.cBzT1 = cBzT1;
|
|
407
|
+
c.jacInvT1 = denomT1 > 0 ? 1 / denomT1 : 0;
|
|
408
|
+
const cAxT2 = rAy * t2z - rAz * t2y;
|
|
409
|
+
const cAyT2 = rAz * t2x - rAx * t2z;
|
|
410
|
+
const cAzT2 = rAx * t2y - rAy * t2x;
|
|
411
|
+
const cBxT2 = rBy * t2z - rBz * t2y;
|
|
412
|
+
const cByT2 = rBz * t2x - rBx * t2z;
|
|
413
|
+
const cBzT2 = rBx * t2y - rBy * t2x;
|
|
414
|
+
const denomT2 = imA + imB +
|
|
415
|
+
(cAxT2 * cAxT2 + cAyT2 * cAyT2 + cAzT2 * cAzT2) * iiA +
|
|
416
|
+
(cBxT2 * cBxT2 + cByT2 * cByT2 + cBzT2 * cBzT2) * iiB;
|
|
417
|
+
c.cAxT2 = cAxT2;
|
|
418
|
+
c.cAyT2 = cAyT2;
|
|
419
|
+
c.cAzT2 = cAzT2;
|
|
420
|
+
c.cBxT2 = cBxT2;
|
|
421
|
+
c.cByT2 = cByT2;
|
|
422
|
+
c.cBzT2 = cBzT2;
|
|
423
|
+
c.jacInvT2 = denomT2 > 0 ? 1 / denomT2 : 0;
|
|
342
424
|
}
|
|
343
|
-
|
|
425
|
+
// ITER: one push-only normal row + two Coulomb friction rows. Friction
|
|
426
|
+
// bound depends on the *current* applied normal impulse, so it tightens
|
|
427
|
+
// as the normal row converges.
|
|
428
|
+
function iterateContactRow(c, lv, av, invMass, invInertia) {
|
|
429
|
+
const imA = invMass[c.bodyA];
|
|
430
|
+
const imB = invMass[c.bodyB];
|
|
431
|
+
if (imA === 0 && imB === 0)
|
|
432
|
+
return;
|
|
433
|
+
const iiA = invInertia[c.bodyA];
|
|
434
|
+
const iiB = invInertia[c.bodyB];
|
|
435
|
+
const ai = c.bodyA * 3, bi = c.bodyB * 3;
|
|
436
|
+
const rAx = c.rAx, rAy = c.rAy, rAz = c.rAz;
|
|
437
|
+
const rBx = c.rBx, rBy = c.rBy, rBz = c.rBz;
|
|
438
|
+
const vAx = lv[ai + 0] + av[ai + 1] * rAz - av[ai + 2] * rAy;
|
|
439
|
+
const vAy = lv[ai + 1] + av[ai + 2] * rAx - av[ai + 0] * rAz;
|
|
440
|
+
const vAz = lv[ai + 2] + av[ai + 0] * rAy - av[ai + 1] * rAx;
|
|
441
|
+
const vBx = lv[bi + 0] + av[bi + 1] * rBz - av[bi + 2] * rBy;
|
|
442
|
+
const vBy = lv[bi + 1] + av[bi + 2] * rBx - av[bi + 0] * rBz;
|
|
443
|
+
const vBz = lv[bi + 2] + av[bi + 0] * rBy - av[bi + 1] * rBx;
|
|
444
|
+
const dvx = vBx - vAx;
|
|
445
|
+
const dvy = vBy - vAy;
|
|
446
|
+
const dvz = vBz - vAz;
|
|
447
|
+
// Normal row.
|
|
448
|
+
const jacInvN = c.jacInvN;
|
|
449
|
+
if (jacInvN > 0) {
|
|
450
|
+
const nx = c.nx, ny = c.ny, nz = c.nz;
|
|
451
|
+
const relVelN = dvx * nx + dvy * ny + dvz * nz;
|
|
452
|
+
let dImpN = (c.bounceVel - relVelN) * jacInvN;
|
|
453
|
+
const oldN = c.appliedNormalImpulse;
|
|
454
|
+
let newN = oldN + dImpN;
|
|
455
|
+
if (newN < 0) {
|
|
456
|
+
newN = 0;
|
|
457
|
+
dImpN = -oldN;
|
|
458
|
+
}
|
|
459
|
+
c.appliedNormalImpulse = newN;
|
|
460
|
+
if (dImpN !== 0) {
|
|
461
|
+
const cAxN = c.cAxN, cAyN = c.cAyN, cAzN = c.cAzN;
|
|
462
|
+
const cBxN = c.cBxN, cByN = c.cByN, cBzN = c.cBzN;
|
|
463
|
+
if (imA > 0) {
|
|
464
|
+
lv[ai + 0] -= dImpN * imA * nx;
|
|
465
|
+
lv[ai + 1] -= dImpN * imA * ny;
|
|
466
|
+
lv[ai + 2] -= dImpN * imA * nz;
|
|
467
|
+
av[ai + 0] -= dImpN * iiA * cAxN;
|
|
468
|
+
av[ai + 1] -= dImpN * iiA * cAyN;
|
|
469
|
+
av[ai + 2] -= dImpN * iiA * cAzN;
|
|
470
|
+
}
|
|
471
|
+
if (imB > 0) {
|
|
472
|
+
lv[bi + 0] += dImpN * imB * nx;
|
|
473
|
+
lv[bi + 1] += dImpN * imB * ny;
|
|
474
|
+
lv[bi + 2] += dImpN * imB * nz;
|
|
475
|
+
av[bi + 0] += dImpN * iiB * cBxN;
|
|
476
|
+
av[bi + 1] += dImpN * iiB * cByN;
|
|
477
|
+
av[bi + 2] += dImpN * iiB * cBzN;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
// Friction. Bound = ±μ · current normal impulse.
|
|
482
|
+
const muNormal = c.friction * c.appliedNormalImpulse;
|
|
344
483
|
if (muNormal <= 0)
|
|
345
484
|
return;
|
|
346
|
-
|
|
347
|
-
const
|
|
348
|
-
const
|
|
349
|
-
const
|
|
350
|
-
const
|
|
351
|
-
const
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
485
|
+
// Re-read dv after the normal impulse possibly changed lv/av.
|
|
486
|
+
const vAx2 = lv[ai + 0] + av[ai + 1] * rAz - av[ai + 2] * rAy;
|
|
487
|
+
const vAy2 = lv[ai + 1] + av[ai + 2] * rAx - av[ai + 0] * rAz;
|
|
488
|
+
const vAz2 = lv[ai + 2] + av[ai + 0] * rAy - av[ai + 1] * rAx;
|
|
489
|
+
const vBx2 = lv[bi + 0] + av[bi + 1] * rBz - av[bi + 2] * rBy;
|
|
490
|
+
const vBy2 = lv[bi + 1] + av[bi + 2] * rBx - av[bi + 0] * rBz;
|
|
491
|
+
const vBz2 = lv[bi + 2] + av[bi + 0] * rBy - av[bi + 1] * rBx;
|
|
492
|
+
const dvx2 = vBx2 - vAx2;
|
|
493
|
+
const dvy2 = vBy2 - vAy2;
|
|
494
|
+
const dvz2 = vBz2 - vAz2;
|
|
495
|
+
applyFrictionTangent(c, ai, bi, dvx2, dvy2, dvz2, c.t1x, c.t1y, c.t1z, c.cAxT1, c.cAyT1, c.cAzT1, c.cBxT1, c.cByT1, c.cBzT1, c.jacInvT1, muNormal, imA, imB, iiA, iiB, lv, av, 1);
|
|
496
|
+
applyFrictionTangent(c, ai, bi, dvx2, dvy2, dvz2, c.t2x, c.t2y, c.t2z, c.cAxT2, c.cAyT2, c.cAzT2, c.cBxT2, c.cByT2, c.cBzT2, c.jacInvT2, muNormal, imA, imB, iiA, iiB, lv, av, 2);
|
|
497
|
+
}
|
|
498
|
+
function applyFrictionTangent(c, ai, bi, dvx, dvy, dvz, tx, ty, tz, cAx, cAy, cAz, cBx, cBy, cBz, jacInv, muNormal, imA, imB, iiA, iiB, lv, av, slot) {
|
|
499
|
+
if (jacInv <= 0)
|
|
356
500
|
return;
|
|
357
|
-
const
|
|
358
|
-
const relVel = dvX * tx + dvY * ty + dvZ * tz;
|
|
501
|
+
const relVel = dvx * tx + dvy * ty + dvz * tz;
|
|
359
502
|
let dImp = -relVel * jacInv;
|
|
360
503
|
const old = slot === 1 ? c.appliedFrictionImpulse1 : c.appliedFrictionImpulse2;
|
|
361
504
|
let next = old + dImp;
|