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