reze-engine 0.16.2 → 0.17.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/dist/engine.d.ts +3 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +165 -26
- package/dist/model.d.ts +5 -1
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +14 -1
- package/dist/physics/body.d.ts +4 -1
- package/dist/physics/body.d.ts.map +1 -1
- package/dist/physics/body.js +86 -20
- package/dist/physics/constraint.d.ts +15 -2
- package/dist/physics/constraint.d.ts.map +1 -1
- package/dist/physics/constraint.js +49 -2
- package/dist/physics/physics.d.ts +3 -0
- package/dist/physics/physics.d.ts.map +1 -1
- package/dist/physics/physics.js +69 -1
- package/dist/physics/solver.d.ts.map +1 -1
- package/dist/physics/solver.js +289 -113
- package/dist/physics/types.d.ts +2 -1
- package/dist/physics/types.d.ts.map +1 -1
- package/dist/physics/types.js +5 -0
- package/dist/physics/world.d.ts +3 -0
- package/dist/physics/world.d.ts.map +1 -1
- package/dist/physics/world.js +19 -3
- package/dist/pmx-loader.d.ts +2 -0
- package/dist/pmx-loader.d.ts.map +1 -1
- package/dist/pmx-loader.js +38 -20
- 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 +90 -85
- 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 +1 -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/cloth_smooth.js +2 -2
- package/dist/shaders/materials/common.d.ts +1 -1
- package/dist/shaders/materials/common.d.ts.map +1 -1
- package/dist/shaders/materials/common.js +137 -112
- package/dist/shaders/materials/default.d.ts +1 -1
- package/dist/shaders/materials/default.d.ts.map +1 -1
- package/dist/shaders/materials/default.js +37 -32
- package/dist/shaders/materials/eye.d.ts +1 -1
- package/dist/shaders/materials/eye.d.ts.map +1 -1
- package/dist/shaders/materials/eye.js +54 -35
- 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 +90 -85
- package/dist/shaders/materials/hair.d.ts +1 -1
- package/dist/shaders/materials/hair.d.ts.map +1 -1
- package/dist/shaders/materials/hair.js +85 -75
- package/dist/shaders/materials/metal.d.ts +1 -1
- package/dist/shaders/materials/metal.d.ts.map +1 -1
- package/dist/shaders/materials/metal.js +1 -1
- package/dist/shaders/materials/mmd_classic.d.ts +2 -0
- package/dist/shaders/materials/mmd_classic.d.ts.map +1 -0
- package/dist/shaders/materials/mmd_classic.js +66 -0
- 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 +1 -1
- package/package.json +1 -1
- package/src/engine.ts +4198 -4030
- package/src/model.ts +21 -1
- package/src/physics/body.ts +87 -24
- package/src/physics/constraint.ts +80 -6
- package/src/physics/physics.ts +68 -1
- package/src/physics/solver.ts +915 -744
- package/src/physics/types.ts +10 -2
- package/src/physics/world.ts +20 -3
- package/src/pmx-loader.ts +39 -20
- package/src/shaders/materials/body.ts +97 -92
- package/src/shaders/materials/cloth_rough.ts +1 -1
- package/src/shaders/materials/cloth_smooth.ts +2 -2
- package/src/shaders/materials/common.ts +204 -179
- package/src/shaders/materials/default.ts +45 -40
- package/src/shaders/materials/eye.ts +62 -43
- package/src/shaders/materials/face.ts +97 -92
- package/src/shaders/materials/hair.ts +92 -82
- package/src/shaders/materials/metal.ts +1 -1
- package/src/shaders/materials/mmd_classic.ts +68 -0
- package/src/shaders/materials/stockings.ts +1 -1
package/dist/physics/solver.js
CHANGED
|
@@ -24,6 +24,32 @@ const MAX_ANGULAR_CORRECTION_VEL = 30; // rad/s
|
|
|
24
24
|
// instead of as a hard velocity snap.
|
|
25
25
|
const LIMIT_SOFTNESS_LINEAR = 0.7;
|
|
26
26
|
const LIMIT_SOFTNESS_ANGULAR = 0.5;
|
|
27
|
+
// Bullet spring-motor velocity factor: target velocity = (fps/numIterations)
|
|
28
|
+
// × spring force, with the applied impulse clamped to the real spring force
|
|
29
|
+
// k·|err|·dt. The big target makes the row deliver its full physical force
|
|
30
|
+
// every substep (stiff, MMD-authored hold); the clamp keeps it from ever
|
|
31
|
+
// exceeding it (no energy pumping). Matches btGeneric6DofSpringConstraint
|
|
32
|
+
// with springDamping=1 and the default 10 solver iterations.
|
|
33
|
+
const SPRING_SOLVER_ITERATIONS = 10;
|
|
34
|
+
// Slack on the spring impulse clamp: MMD rigs are authored against Bullet's
|
|
35
|
+
// spring motors, which deliver several times the naive k·err·dt per substep
|
|
36
|
+
// through their solver-row impulse bounds. Bounded slack keeps the hold
|
|
37
|
+
// stiff (breasts/hair rest near the authored pose) while still capping the
|
|
38
|
+
// energy any spring can inject per substep (the slow-boil guard).
|
|
39
|
+
const SPRING_FORCE_SLACK = 4;
|
|
40
|
+
// ERP scale for loop-closing constraints (see buildConstraints: joints that
|
|
41
|
+
// close a cycle in the joint graph, e.g. the horizontal ring welds of
|
|
42
|
+
// cross-linked skirt lattices). A loop over-determines positions — when
|
|
43
|
+
// contacts push the lattice, the ring's errors cannot all reach zero, and
|
|
44
|
+
// full-rate corrections chase each other around the cycle as violent
|
|
45
|
+
// chatter. Loop edges keep shape at a fraction of the correction rate while
|
|
46
|
+
// the spanning-tree chains stay stiff.
|
|
47
|
+
const LOOP_ERP_SCALE = 1.0;
|
|
48
|
+
// Loop-edge LOCKED axes are converted to force-clamped springs instead of
|
|
49
|
+
// weld rows: an equality row on a cycle fights the other cycle edges at any
|
|
50
|
+
// ERP (the velocity system is over-determined too). A spring bounded by its
|
|
51
|
+
// real force k·|err|·dt holds the ring's shape elastically without fighting.
|
|
52
|
+
const LOOP_SPRING_K = 900;
|
|
27
53
|
// Angular limit violations below this switch to per-axis euler rows; above
|
|
28
54
|
// it, the single geodesic row takes over (see setupConstraint).
|
|
29
55
|
const GEODESIC_THRESHOLD = 0.5; // rad
|
|
@@ -44,19 +70,21 @@ export function solveConstraints(store, constraints, contacts, dt, iterations) {
|
|
|
44
70
|
const lv = store.linearVelocities;
|
|
45
71
|
const av = store.angularVelocities;
|
|
46
72
|
const invMass = store.invMass;
|
|
47
|
-
|
|
73
|
+
// World-space inverse inertia tensors for this substep's poses.
|
|
74
|
+
store.updateInvInertiaWorld();
|
|
75
|
+
const W = store.invInertiaWorld;
|
|
48
76
|
for (let c = 0; c < constraints.length; c++) {
|
|
49
77
|
setupConstraint(constraints[c], store, dt, invDt);
|
|
50
78
|
}
|
|
51
79
|
for (let ci = 0; ci < contacts.count; ci++) {
|
|
52
|
-
setupContactRow(contacts.get(ci), lv, av, invMass,
|
|
80
|
+
setupContactRow(contacts.get(ci), lv, av, invMass, W);
|
|
53
81
|
}
|
|
54
82
|
for (let iter = 0; iter < iterations; iter++) {
|
|
55
83
|
for (let c = 0; c < constraints.length; c++) {
|
|
56
|
-
iterateConstraint(constraints[c], lv, av, invMass
|
|
84
|
+
iterateConstraint(constraints[c], lv, av, invMass);
|
|
57
85
|
}
|
|
58
86
|
for (let ci = 0; ci < contacts.count; ci++) {
|
|
59
|
-
iterateContactRow(contacts.get(ci), lv, av, invMass
|
|
87
|
+
iterateContactRow(contacts.get(ci), lv, av, invMass);
|
|
60
88
|
}
|
|
61
89
|
}
|
|
62
90
|
}
|
|
@@ -67,11 +95,13 @@ function setupConstraint(con, store, dt, invDt) {
|
|
|
67
95
|
const b = con.bodyB;
|
|
68
96
|
const imA = store.invMass[a];
|
|
69
97
|
const imB = store.invMass[b];
|
|
70
|
-
const
|
|
71
|
-
const
|
|
98
|
+
const W = store.invInertiaWorld;
|
|
99
|
+
const a9 = a * 9;
|
|
100
|
+
const b9 = b * 9;
|
|
72
101
|
con.cacheSkip = imA === 0 && imB === 0;
|
|
73
102
|
if (con.cacheSkip)
|
|
74
103
|
return;
|
|
104
|
+
const erpScale = con.isLoop ? LOOP_ERP_SCALE : 1.0;
|
|
75
105
|
buildBodyMat(store, a, _bodyMatA);
|
|
76
106
|
buildBodyMat(store, b, _bodyMatB);
|
|
77
107
|
Mat4.multiplyArrays(_bodyMatA, 0, con.frameA, 0, _TA, 0);
|
|
@@ -128,15 +158,23 @@ function setupConstraint(con, store, dt, invDt) {
|
|
|
128
158
|
const cBx = rBy * axz - rBz * axy;
|
|
129
159
|
const cBy = rBz * axx - rBx * axz;
|
|
130
160
|
const cBz = rBx * axy - rBy * axx;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
161
|
+
// Tensor-multiplied lever crosses: cache I⁻¹·(r×ax) for application;
|
|
162
|
+
// denominator = (r×ax)ᵀ·I⁻¹·(r×ax).
|
|
163
|
+
const wAx = W[a9 + 0] * cAx + W[a9 + 1] * cAy + W[a9 + 2] * cAz;
|
|
164
|
+
const wAy = W[a9 + 3] * cAx + W[a9 + 4] * cAy + W[a9 + 5] * cAz;
|
|
165
|
+
const wAz = W[a9 + 6] * cAx + W[a9 + 7] * cAy + W[a9 + 8] * cAz;
|
|
166
|
+
const wBx = W[b9 + 0] * cBx + W[b9 + 1] * cBy + W[b9 + 2] * cBz;
|
|
167
|
+
const wBy = W[b9 + 3] * cBx + W[b9 + 4] * cBy + W[b9 + 5] * cBz;
|
|
168
|
+
const wBz = W[b9 + 6] * cBx + W[b9 + 7] * cBy + W[b9 + 8] * cBz;
|
|
169
|
+
cA[o + 0] = wAx;
|
|
170
|
+
cA[o + 1] = wAy;
|
|
171
|
+
cA[o + 2] = wAz;
|
|
172
|
+
cB[o + 0] = wBx;
|
|
173
|
+
cB[o + 1] = wBy;
|
|
174
|
+
cB[o + 2] = wBz;
|
|
137
175
|
const denom = imA + imB +
|
|
138
|
-
(cAx *
|
|
139
|
-
(cBx *
|
|
176
|
+
(cAx * wAx + cAy * wAy + cAz * wAz) +
|
|
177
|
+
(cBx * wBx + cBy * wBy + cBz * wBz);
|
|
140
178
|
jac[i] = denom > 0 ? 1 / denom : 0;
|
|
141
179
|
const lo = con.linearMin[i];
|
|
142
180
|
const hi = con.linearMax[i];
|
|
@@ -156,7 +194,7 @@ function setupConstraint(con, store, dt, invDt) {
|
|
|
156
194
|
else if (err !== 0)
|
|
157
195
|
active = 2;
|
|
158
196
|
if (err !== 0) {
|
|
159
|
-
target = -err * STOP_ERP * invDt;
|
|
197
|
+
target = -err * STOP_ERP * erpScale * invDt;
|
|
160
198
|
if (target > MAX_LINEAR_CORRECTION_VEL)
|
|
161
199
|
target = MAX_LINEAR_CORRECTION_VEL;
|
|
162
200
|
else if (target < -MAX_LINEAR_CORRECTION_VEL)
|
|
@@ -166,17 +204,25 @@ function setupConstraint(con, store, dt, invDt) {
|
|
|
166
204
|
tgt[i] = target;
|
|
167
205
|
act[i] = denom > 0 ? active : 0;
|
|
168
206
|
con.cacheLinLimitImp[i] = 0;
|
|
169
|
-
|
|
207
|
+
// A spring on a locked axis is redundant — the bilateral limit row
|
|
208
|
+
// already welds the DOF, and driving it twice overshoots every
|
|
209
|
+
// iteration (PMX rigs routinely put k=100000 springs on locked axes,
|
|
210
|
+
// which turned welded weight-bodies into energy pumps).
|
|
211
|
+
if (con.springEnabled[i] && denom > 0 && lo !== hi) {
|
|
170
212
|
// Clamp k to the deadbeat limit: an explicit spring with k·dt² > 1
|
|
171
213
|
// overshoots equilibrium every step and pumps energy — the classic
|
|
172
|
-
// pre-Spring2 Bullet 6dof instability this port inherited.
|
|
173
|
-
|
|
174
|
-
const k = Math.min(con.springStiffness[i], 0.25 * invDt * invDt);
|
|
214
|
+
// pre-Spring2 Bullet 6dof instability this port inherited.
|
|
215
|
+
const k = Math.min(con.springStiffness[i], invDt * invDt);
|
|
175
216
|
const serr = curr - con.equilibriumPoint[i];
|
|
176
217
|
con.cacheLinSpringTarget[i] = -k * serr * dt;
|
|
218
|
+
// Boil guard: per-substep spring impulse bounded by (a slack over) the
|
|
219
|
+
// physical spring force — unbounded spring drives pump energy through
|
|
220
|
+
// the euler-axis mis-scale and slowly boil resting cloth.
|
|
221
|
+
con.cacheLinSpringMaxImp[i] = SPRING_FORCE_SLACK * Math.abs(k * serr) * dt;
|
|
222
|
+
con.cacheLinSpringImp[i] = 0;
|
|
177
223
|
con.cacheLinSpringActive[i] = 1;
|
|
178
224
|
}
|
|
179
|
-
else {
|
|
225
|
+
else if (!(lo === hi && con.isLoop)) {
|
|
180
226
|
con.cacheLinSpringActive[i] = 0;
|
|
181
227
|
}
|
|
182
228
|
}
|
|
@@ -231,25 +277,49 @@ function setupConstraint(con, store, dt, invDt) {
|
|
|
231
277
|
angAxes[6] = zx;
|
|
232
278
|
angAxes[7] = zy;
|
|
233
279
|
angAxes[8] = zz;
|
|
234
|
-
|
|
235
|
-
|
|
280
|
+
// Per-axis angular Jacobians with the full tensors: cache I⁻¹·axis per
|
|
281
|
+
// body plus 1/(axᵀ(I⁻¹A+I⁻¹B)ax) per axis.
|
|
282
|
+
const angJac = con.cacheAngJacInv;
|
|
283
|
+
const angWAs = con.cacheAngWA;
|
|
284
|
+
const angWBs = con.cacheAngWB;
|
|
285
|
+
for (let i = 0; i < 3; i++) {
|
|
286
|
+
const o = i * 3;
|
|
287
|
+
const axx = angAxes[o + 0], axy = angAxes[o + 1], axz = angAxes[o + 2];
|
|
288
|
+
const wAx = W[a9 + 0] * axx + W[a9 + 1] * axy + W[a9 + 2] * axz;
|
|
289
|
+
const wAy = W[a9 + 3] * axx + W[a9 + 4] * axy + W[a9 + 5] * axz;
|
|
290
|
+
const wAz = W[a9 + 6] * axx + W[a9 + 7] * axy + W[a9 + 8] * axz;
|
|
291
|
+
const wBx = W[b9 + 0] * axx + W[b9 + 1] * axy + W[b9 + 2] * axz;
|
|
292
|
+
const wBy = W[b9 + 3] * axx + W[b9 + 4] * axy + W[b9 + 5] * axz;
|
|
293
|
+
const wBz = W[b9 + 6] * axx + W[b9 + 7] * axy + W[b9 + 8] * axz;
|
|
294
|
+
angWAs[o + 0] = wAx;
|
|
295
|
+
angWAs[o + 1] = wAy;
|
|
296
|
+
angWAs[o + 2] = wAz;
|
|
297
|
+
angWBs[o + 0] = wBx;
|
|
298
|
+
angWBs[o + 1] = wBy;
|
|
299
|
+
angWBs[o + 2] = wBz;
|
|
300
|
+
const denom = axx * (wAx + wBx) + axy * (wAy + wBy) + axz * (wAz + wBz);
|
|
301
|
+
angJac[i] = denom > 0 ? 1 / denom : 0;
|
|
302
|
+
}
|
|
236
303
|
// Per-axis rows carry only the springs. Sign flip vs linear:
|
|
237
304
|
// d(angDiff)/dt = −(ω_B − ω_A)·ax.
|
|
238
305
|
const angTgt = con.cacheAngTargetVel;
|
|
239
306
|
const angAct = con.cacheAngActive;
|
|
240
307
|
for (let i = 0; i < 3; i++) {
|
|
241
308
|
const idx = i + 3;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
309
|
+
// Springs on locked axes are skipped — the limit row welds those, and
|
|
310
|
+
// double-driving a DOF overshoots every iteration (see the linear loop).
|
|
311
|
+
if (con.springEnabled[idx] && angJac[i] > 0 && con.angularMin[i] !== con.angularMax[i]) {
|
|
312
|
+
const k = Math.min(con.springStiffness[idx], invDt * invDt);
|
|
245
313
|
const serr = _angDiffScratch[i] - con.equilibriumPoint[idx];
|
|
246
314
|
angTgt[i] = k * serr * dt;
|
|
315
|
+
con.cacheAngSpringMaxImp[i] = SPRING_FORCE_SLACK * Math.abs(k * serr) * dt;
|
|
247
316
|
angAct[i] = 1;
|
|
248
317
|
}
|
|
249
318
|
else {
|
|
250
319
|
angTgt[i] = 0;
|
|
251
320
|
angAct[i] = 0;
|
|
252
321
|
}
|
|
322
|
+
con.cacheAngSpringImp[i] = 0;
|
|
253
323
|
}
|
|
254
324
|
// Angular limit handling is hybrid. Small violations (the resting-cloth
|
|
255
325
|
// regime) use per-axis euler rows — they converge cleanly and keep resting
|
|
@@ -259,7 +329,10 @@ function setupConstraint(con, store, dt, invDt) {
|
|
|
259
329
|
// the asin singularity they chase phantom errors and pump angular velocity
|
|
260
330
|
// into the chain instead of converging.
|
|
261
331
|
con.cacheAngLimActive = 0;
|
|
262
|
-
|
|
332
|
+
con.cacheAngPAActive[0] = 0;
|
|
333
|
+
con.cacheAngPAActive[1] = 0;
|
|
334
|
+
con.cacheAngPAActive[2] = 0;
|
|
335
|
+
if (imA > 0 || imB > 0) {
|
|
263
336
|
const ex = _angDiffScratch[0], ey = _angDiffScratch[1], ez = _angDiffScratch[2];
|
|
264
337
|
// Free axes (min > max) follow the current angle, i.e. no correction.
|
|
265
338
|
let tx = ex, ty = ey, tz = ez;
|
|
@@ -272,19 +345,25 @@ function setupConstraint(con, store, dt, invDt) {
|
|
|
272
345
|
const errX = ex - tx, errY = ey - ty, errZ = ez - tz;
|
|
273
346
|
const maxErr = Math.max(Math.abs(errX), Math.abs(errY), Math.abs(errZ));
|
|
274
347
|
if (maxErr > 0 && maxErr < GEODESIC_THRESHOLD) {
|
|
275
|
-
// Per-axis euler limit rows
|
|
276
|
-
//
|
|
348
|
+
// Per-axis euler limit rows. Locked axes are bilateral joints; ranged
|
|
349
|
+
// axes are unilateral stops (sign-clamped accumulation) — a bilateral
|
|
350
|
+
// row on a ranged axis brakes natural recovery every substep and
|
|
351
|
+
// pumps energy into swinging cloth, and the pump grows WITH solver
|
|
352
|
+
// convergence (more iterations enforce the brake harder).
|
|
277
353
|
for (let i = 0; i < 3; i++) {
|
|
278
354
|
const err = i === 0 ? errX : i === 1 ? errY : errZ;
|
|
279
|
-
|
|
355
|
+
con.cacheAngPAImp[i] = 0;
|
|
356
|
+
if (err === 0) {
|
|
357
|
+
con.cacheAngPAActive[i] = 0;
|
|
280
358
|
continue;
|
|
281
|
-
|
|
359
|
+
}
|
|
360
|
+
let target = err * STOP_ERP * erpScale * invDt;
|
|
282
361
|
if (target > MAX_ANGULAR_CORRECTION_VEL)
|
|
283
362
|
target = MAX_ANGULAR_CORRECTION_VEL;
|
|
284
363
|
else if (target < -MAX_ANGULAR_CORRECTION_VEL)
|
|
285
364
|
target = -MAX_ANGULAR_CORRECTION_VEL;
|
|
286
|
-
|
|
287
|
-
|
|
365
|
+
con.cacheAngPATarget[i] = target;
|
|
366
|
+
con.cacheAngPAActive[i] = con.angularMin[i] === con.angularMax[i] ? 1 : 2;
|
|
288
367
|
}
|
|
289
368
|
}
|
|
290
369
|
else if (maxErr > 0) {
|
|
@@ -321,7 +400,17 @@ function setupConstraint(con, store, dt, invDt) {
|
|
|
321
400
|
lim[0] = _TA[0] * axx + _TA[4] * axy + _TA[8] * axz;
|
|
322
401
|
lim[1] = _TA[1] * axx + _TA[5] * axy + _TA[9] * axz;
|
|
323
402
|
lim[2] = _TA[2] * axx + _TA[6] * axy + _TA[10] * axz;
|
|
324
|
-
|
|
403
|
+
const gWA = con.cacheAngLimWA;
|
|
404
|
+
const gWB = con.cacheAngLimWB;
|
|
405
|
+
gWA[0] = W[a9 + 0] * lim[0] + W[a9 + 1] * lim[1] + W[a9 + 2] * lim[2];
|
|
406
|
+
gWA[1] = W[a9 + 3] * lim[0] + W[a9 + 4] * lim[1] + W[a9 + 5] * lim[2];
|
|
407
|
+
gWA[2] = W[a9 + 6] * lim[0] + W[a9 + 7] * lim[1] + W[a9 + 8] * lim[2];
|
|
408
|
+
gWB[0] = W[b9 + 0] * lim[0] + W[b9 + 1] * lim[1] + W[b9 + 2] * lim[2];
|
|
409
|
+
gWB[1] = W[b9 + 3] * lim[0] + W[b9 + 4] * lim[1] + W[b9 + 5] * lim[2];
|
|
410
|
+
gWB[2] = W[b9 + 6] * lim[0] + W[b9 + 7] * lim[1] + W[b9 + 8] * lim[2];
|
|
411
|
+
const gDenom = lim[0] * (gWA[0] + gWB[0]) + lim[1] * (gWA[1] + gWB[1]) + lim[2] * (gWA[2] + gWB[2]);
|
|
412
|
+
con.cacheAngLimJacInv = gDenom > 0 ? 1 / gDenom : 0;
|
|
413
|
+
let target = angle * STOP_ERP * erpScale * invDt;
|
|
325
414
|
if (target > MAX_ANGULAR_CORRECTION_VEL)
|
|
326
415
|
target = MAX_ANGULAR_CORRECTION_VEL;
|
|
327
416
|
con.cacheAngLimTarget = target;
|
|
@@ -332,7 +421,7 @@ function setupConstraint(con, store, dt, invDt) {
|
|
|
332
421
|
con.cacheAngLimImp = 0;
|
|
333
422
|
}
|
|
334
423
|
// ITER: read cache, compute relVel from current lv/av, apply impulse.
|
|
335
|
-
function iterateConstraint(con, lv, av, invMass
|
|
424
|
+
function iterateConstraint(con, lv, av, invMass) {
|
|
336
425
|
if (con.cacheSkip)
|
|
337
426
|
return;
|
|
338
427
|
const a = con.bodyA;
|
|
@@ -341,8 +430,6 @@ function iterateConstraint(con, lv, av, invMass, invInertia) {
|
|
|
341
430
|
const bi = b * 3;
|
|
342
431
|
const imA = invMass[a];
|
|
343
432
|
const imB = invMass[b];
|
|
344
|
-
const iiA = invInertia[a];
|
|
345
|
-
const iiB = invInertia[b];
|
|
346
433
|
// Linear axes — relVel at the offset point: v_pivot = v_CG + ω × r.
|
|
347
434
|
const lA = con.cacheLeverA;
|
|
348
435
|
const lB = con.cacheLeverB;
|
|
@@ -365,6 +452,8 @@ function iterateConstraint(con, lv, av, invMass, invInertia) {
|
|
|
365
452
|
const dvz = vBz - vAz;
|
|
366
453
|
const sprAct = con.cacheLinSpringActive;
|
|
367
454
|
const sprTgt = con.cacheLinSpringTarget;
|
|
455
|
+
const sprMax = con.cacheLinSpringMaxImp;
|
|
456
|
+
const sprImp = con.cacheLinSpringImp;
|
|
368
457
|
const limImp = con.cacheLinLimitImp;
|
|
369
458
|
for (let i = 0; i < 3; i++) {
|
|
370
459
|
if (!act[i] && !sprAct[i])
|
|
@@ -392,9 +481,25 @@ function iterateConstraint(con, lv, av, invMass, invInertia) {
|
|
|
392
481
|
j += dImp;
|
|
393
482
|
}
|
|
394
483
|
// Spring row: velocity-target drive; the deadbeat k-clamp at setup
|
|
395
|
-
// bounds its aggression
|
|
484
|
+
// bounds its aggression, and maxImp bounds loop-edge springs by their
|
|
485
|
+
// real force. relVel is refreshed with the limit impulse applied just
|
|
486
|
+
// above (j·denom = j / jac) — driving the spring off the stale value
|
|
487
|
+
// double-corrects the DOF and overshoots every iteration.
|
|
396
488
|
if (sprAct[i]) {
|
|
397
|
-
j
|
|
489
|
+
const relVelNow = j !== 0 ? relVel + j / jac[i] : relVel;
|
|
490
|
+
let dImp = (sprTgt[i] - relVelNow) * jac[i];
|
|
491
|
+
const max = sprMax[i];
|
|
492
|
+
if (max !== Infinity) {
|
|
493
|
+
const old = sprImp[i];
|
|
494
|
+
let next = old + dImp;
|
|
495
|
+
if (next > max)
|
|
496
|
+
next = max;
|
|
497
|
+
else if (next < -max)
|
|
498
|
+
next = -max;
|
|
499
|
+
dImp = next - old;
|
|
500
|
+
sprImp[i] = next;
|
|
501
|
+
}
|
|
502
|
+
j += dImp;
|
|
398
503
|
}
|
|
399
504
|
if (j === 0)
|
|
400
505
|
continue;
|
|
@@ -402,49 +507,102 @@ function iterateConstraint(con, lv, av, invMass, invInertia) {
|
|
|
402
507
|
lv[ai + 0] -= j * imA * axx;
|
|
403
508
|
lv[ai + 1] -= j * imA * axy;
|
|
404
509
|
lv[ai + 2] -= j * imA * axz;
|
|
405
|
-
av[ai + 0] -= j *
|
|
406
|
-
av[ai + 1] -= j *
|
|
407
|
-
av[ai + 2] -= j *
|
|
510
|
+
av[ai + 0] -= j * cA[o + 0];
|
|
511
|
+
av[ai + 1] -= j * cA[o + 1];
|
|
512
|
+
av[ai + 2] -= j * cA[o + 2];
|
|
408
513
|
}
|
|
409
514
|
if (imB > 0) {
|
|
410
515
|
lv[bi + 0] += j * imB * axx;
|
|
411
516
|
lv[bi + 1] += j * imB * axy;
|
|
412
517
|
lv[bi + 2] += j * imB * axz;
|
|
413
|
-
av[bi + 0] += j *
|
|
414
|
-
av[bi + 1] += j *
|
|
415
|
-
av[bi + 2] += j *
|
|
518
|
+
av[bi + 0] += j * cB[o + 0];
|
|
519
|
+
av[bi + 1] += j * cB[o + 1];
|
|
520
|
+
av[bi + 2] += j * cB[o + 2];
|
|
416
521
|
}
|
|
417
522
|
}
|
|
418
523
|
// Angular axes — relAv = ω_B − ω_A.
|
|
419
|
-
const angJacInv = con.cacheAngJacInv;
|
|
420
|
-
if (angJacInv === 0)
|
|
421
|
-
return;
|
|
422
524
|
const angAxes = con.cacheAngAxes;
|
|
525
|
+
const angJac = con.cacheAngJacInv;
|
|
526
|
+
const angWAs = con.cacheAngWA;
|
|
527
|
+
const angWBs = con.cacheAngWB;
|
|
423
528
|
const angTgt = con.cacheAngTargetVel;
|
|
424
529
|
const angAct = con.cacheAngActive;
|
|
425
530
|
const dax = av[bi + 0] - av[ai + 0];
|
|
426
531
|
const day = av[bi + 1] - av[ai + 1];
|
|
427
532
|
const daz = av[bi + 2] - av[ai + 2];
|
|
533
|
+
const angSprMax = con.cacheAngSpringMaxImp;
|
|
534
|
+
const angSprImp = con.cacheAngSpringImp;
|
|
428
535
|
for (let i = 0; i < 3; i++) {
|
|
429
536
|
if (!angAct[i])
|
|
430
537
|
continue;
|
|
431
538
|
const o = i * 3;
|
|
432
539
|
const axx = angAxes[o + 0], axy = angAxes[o + 1], axz = angAxes[o + 2];
|
|
433
540
|
const relAv = dax * axx + day * axy + daz * axz;
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
541
|
+
let j = (angTgt[i] - relAv) * angJac[i];
|
|
542
|
+
{
|
|
543
|
+
const max = angSprMax[i];
|
|
544
|
+
const old = angSprImp[i];
|
|
545
|
+
let next = old + j;
|
|
546
|
+
if (next > max)
|
|
547
|
+
next = max;
|
|
548
|
+
else if (next < -max)
|
|
549
|
+
next = -max;
|
|
550
|
+
j = next - old;
|
|
551
|
+
angSprImp[i] = next;
|
|
552
|
+
}
|
|
437
553
|
if (j === 0)
|
|
438
554
|
continue;
|
|
439
|
-
if (
|
|
440
|
-
av[ai + 0] -= j *
|
|
441
|
-
av[ai + 1] -= j *
|
|
442
|
-
av[ai + 2] -= j *
|
|
555
|
+
if (imA > 0) {
|
|
556
|
+
av[ai + 0] -= j * angWAs[o + 0];
|
|
557
|
+
av[ai + 1] -= j * angWAs[o + 1];
|
|
558
|
+
av[ai + 2] -= j * angWAs[o + 2];
|
|
443
559
|
}
|
|
444
|
-
if (
|
|
445
|
-
av[bi + 0] += j *
|
|
446
|
-
av[bi + 1] += j *
|
|
447
|
-
av[bi + 2] += j *
|
|
560
|
+
if (imB > 0) {
|
|
561
|
+
av[bi + 0] += j * angWBs[o + 0];
|
|
562
|
+
av[bi + 1] += j * angWBs[o + 1];
|
|
563
|
+
av[bi + 2] += j * angWBs[o + 2];
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
// Per-axis angular limit rows (small-violation regime), on the derived
|
|
567
|
+
// euler axes. Sign convention matches the springs: positive target reduces
|
|
568
|
+
// positive error via d(angDiff)/dt = −(ω_B − ω_A)·ax.
|
|
569
|
+
const paAct = con.cacheAngPAActive;
|
|
570
|
+
if (paAct[0] || paAct[1] || paAct[2]) {
|
|
571
|
+
const paTgt = con.cacheAngPATarget;
|
|
572
|
+
const paImp = con.cacheAngPAImp;
|
|
573
|
+
for (let i = 0; i < 3; i++) {
|
|
574
|
+
if (!paAct[i])
|
|
575
|
+
continue;
|
|
576
|
+
const o = i * 3;
|
|
577
|
+
const axx = angAxes[o + 0], axy = angAxes[o + 1], axz = angAxes[o + 2];
|
|
578
|
+
const relAv = (av[bi + 0] - av[ai + 0]) * axx +
|
|
579
|
+
(av[bi + 1] - av[ai + 1]) * axy +
|
|
580
|
+
(av[bi + 2] - av[ai + 2]) * axz;
|
|
581
|
+
const target = paTgt[i];
|
|
582
|
+
// Locked axes (act 1) are welds — full gain, like the 0.16.3 fold;
|
|
583
|
+
// softness only tempers the unilateral stops.
|
|
584
|
+
const soft = paAct[i] === 2 ? LIMIT_SOFTNESS_ANGULAR : 1.0;
|
|
585
|
+
let j = soft * (target - relAv) * angJac[i];
|
|
586
|
+
if (paAct[i] === 2) {
|
|
587
|
+
const old = paImp[i];
|
|
588
|
+
let next = old + j;
|
|
589
|
+
if (target > 0 ? next < 0 : next > 0)
|
|
590
|
+
next = 0;
|
|
591
|
+
j = next - old;
|
|
592
|
+
paImp[i] = next;
|
|
593
|
+
}
|
|
594
|
+
if (j === 0)
|
|
595
|
+
continue;
|
|
596
|
+
if (imA > 0) {
|
|
597
|
+
av[ai + 0] -= j * angWAs[o + 0];
|
|
598
|
+
av[ai + 1] -= j * angWAs[o + 1];
|
|
599
|
+
av[ai + 2] -= j * angWAs[o + 2];
|
|
600
|
+
}
|
|
601
|
+
if (imB > 0) {
|
|
602
|
+
av[bi + 0] += j * angWBs[o + 0];
|
|
603
|
+
av[bi + 1] += j * angWBs[o + 1];
|
|
604
|
+
av[bi + 2] += j * angWBs[o + 2];
|
|
605
|
+
}
|
|
448
606
|
}
|
|
449
607
|
}
|
|
450
608
|
// Geodesic limit row: drive (ω_B − ω_A)·axis toward the correction target.
|
|
@@ -457,7 +615,7 @@ function iterateConstraint(con, lv, av, invMass, invInertia) {
|
|
|
457
615
|
const relAv = (av[bi + 0] - av[ai + 0]) * nx +
|
|
458
616
|
(av[bi + 1] - av[ai + 1]) * ny +
|
|
459
617
|
(av[bi + 2] - av[ai + 2]) * nz;
|
|
460
|
-
let j = LIMIT_SOFTNESS_ANGULAR * (con.cacheAngLimTarget - relAv) *
|
|
618
|
+
let j = LIMIT_SOFTNESS_ANGULAR * (con.cacheAngLimTarget - relAv) * con.cacheAngLimJacInv;
|
|
461
619
|
if (con.cacheAngLimActive === 2) {
|
|
462
620
|
const old = con.cacheAngLimImp;
|
|
463
621
|
let next = old + j;
|
|
@@ -467,15 +625,17 @@ function iterateConstraint(con, lv, av, invMass, invInertia) {
|
|
|
467
625
|
con.cacheAngLimImp = next;
|
|
468
626
|
}
|
|
469
627
|
if (j !== 0) {
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
av[ai +
|
|
628
|
+
const gWA = con.cacheAngLimWA;
|
|
629
|
+
const gWB = con.cacheAngLimWB;
|
|
630
|
+
if (imA > 0) {
|
|
631
|
+
av[ai + 0] -= j * gWA[0];
|
|
632
|
+
av[ai + 1] -= j * gWA[1];
|
|
633
|
+
av[ai + 2] -= j * gWA[2];
|
|
474
634
|
}
|
|
475
|
-
if (
|
|
476
|
-
av[bi + 0] += j *
|
|
477
|
-
av[bi + 1] += j *
|
|
478
|
-
av[bi + 2] += j *
|
|
635
|
+
if (imB > 0) {
|
|
636
|
+
av[bi + 0] += j * gWB[0];
|
|
637
|
+
av[bi + 1] += j * gWB[1];
|
|
638
|
+
av[bi + 2] += j * gWB[2];
|
|
479
639
|
}
|
|
480
640
|
}
|
|
481
641
|
}
|
|
@@ -483,32 +643,38 @@ function iterateConstraint(con, lv, av, invMass, invInertia) {
|
|
|
483
643
|
// SETUP: pre-compute Jacobians, friction basis, and the bounce reference
|
|
484
644
|
// from the *initial* closing velocity (Bullet's pattern — captures restitution
|
|
485
645
|
// before iter 1 zeroes out the approach).
|
|
486
|
-
function setupContactRow(c, lv, av, invMass,
|
|
646
|
+
function setupContactRow(c, lv, av, invMass, W) {
|
|
487
647
|
const ai = c.bodyA * 3;
|
|
488
648
|
const bi = c.bodyB * 3;
|
|
649
|
+
const a9 = c.bodyA * 9;
|
|
650
|
+
const b9 = c.bodyB * 9;
|
|
489
651
|
const imA = invMass[c.bodyA];
|
|
490
652
|
const imB = invMass[c.bodyB];
|
|
491
|
-
const iiA = invInertia[c.bodyA];
|
|
492
|
-
const iiB = invInertia[c.bodyB];
|
|
493
653
|
const rAx = c.rAx, rAy = c.rAy, rAz = c.rAz;
|
|
494
654
|
const rBx = c.rBx, rBy = c.rBy, rBz = c.rBz;
|
|
495
655
|
const nx = c.nx, ny = c.ny, nz = c.nz;
|
|
496
|
-
// Normal Jacobian.
|
|
656
|
+
// Normal Jacobian. Cached vectors are tensor-multiplied I⁻¹·(r×n).
|
|
497
657
|
const cAxN = rAy * nz - rAz * ny;
|
|
498
658
|
const cAyN = rAz * nx - rAx * nz;
|
|
499
659
|
const cAzN = rAx * ny - rAy * nx;
|
|
500
660
|
const cBxN = rBy * nz - rBz * ny;
|
|
501
661
|
const cByN = rBz * nx - rBx * nz;
|
|
502
662
|
const cBzN = rBx * ny - rBy * nx;
|
|
663
|
+
const wAxN = W[a9 + 0] * cAxN + W[a9 + 1] * cAyN + W[a9 + 2] * cAzN;
|
|
664
|
+
const wAyN = W[a9 + 3] * cAxN + W[a9 + 4] * cAyN + W[a9 + 5] * cAzN;
|
|
665
|
+
const wAzN = W[a9 + 6] * cAxN + W[a9 + 7] * cAyN + W[a9 + 8] * cAzN;
|
|
666
|
+
const wBxN = W[b9 + 0] * cBxN + W[b9 + 1] * cByN + W[b9 + 2] * cBzN;
|
|
667
|
+
const wByN = W[b9 + 3] * cBxN + W[b9 + 4] * cByN + W[b9 + 5] * cBzN;
|
|
668
|
+
const wBzN = W[b9 + 6] * cBxN + W[b9 + 7] * cByN + W[b9 + 8] * cBzN;
|
|
503
669
|
const denomN = imA + imB +
|
|
504
|
-
(cAxN *
|
|
505
|
-
(cBxN *
|
|
506
|
-
c.cAxN =
|
|
507
|
-
c.cAyN =
|
|
508
|
-
c.cAzN =
|
|
509
|
-
c.cBxN =
|
|
510
|
-
c.cByN =
|
|
511
|
-
c.cBzN =
|
|
670
|
+
(cAxN * wAxN + cAyN * wAyN + cAzN * wAzN) +
|
|
671
|
+
(cBxN * wBxN + cByN * wByN + cBzN * wBzN);
|
|
672
|
+
c.cAxN = wAxN;
|
|
673
|
+
c.cAyN = wAyN;
|
|
674
|
+
c.cAzN = wAzN;
|
|
675
|
+
c.cBxN = wBxN;
|
|
676
|
+
c.cByN = wByN;
|
|
677
|
+
c.cBzN = wBzN;
|
|
512
678
|
c.jacInvN = denomN > 0 ? 1 / denomN : 0;
|
|
513
679
|
// Restitution reference, captured from initial relVelN.
|
|
514
680
|
const vAx = lv[ai + 0] + av[ai + 1] * rAz - av[ai + 2] * rAy;
|
|
@@ -561,15 +727,21 @@ function setupContactRow(c, lv, av, invMass, invInertia) {
|
|
|
561
727
|
const cBxT1 = rBy * t1z - rBz * t1y;
|
|
562
728
|
const cByT1 = rBz * t1x - rBx * t1z;
|
|
563
729
|
const cBzT1 = rBx * t1y - rBy * t1x;
|
|
730
|
+
const wAxT1 = W[a9 + 0] * cAxT1 + W[a9 + 1] * cAyT1 + W[a9 + 2] * cAzT1;
|
|
731
|
+
const wAyT1 = W[a9 + 3] * cAxT1 + W[a9 + 4] * cAyT1 + W[a9 + 5] * cAzT1;
|
|
732
|
+
const wAzT1 = W[a9 + 6] * cAxT1 + W[a9 + 7] * cAyT1 + W[a9 + 8] * cAzT1;
|
|
733
|
+
const wBxT1 = W[b9 + 0] * cBxT1 + W[b9 + 1] * cByT1 + W[b9 + 2] * cBzT1;
|
|
734
|
+
const wByT1 = W[b9 + 3] * cBxT1 + W[b9 + 4] * cByT1 + W[b9 + 5] * cBzT1;
|
|
735
|
+
const wBzT1 = W[b9 + 6] * cBxT1 + W[b9 + 7] * cByT1 + W[b9 + 8] * cBzT1;
|
|
564
736
|
const denomT1 = imA + imB +
|
|
565
|
-
(cAxT1 *
|
|
566
|
-
(cBxT1 *
|
|
567
|
-
c.cAxT1 =
|
|
568
|
-
c.cAyT1 =
|
|
569
|
-
c.cAzT1 =
|
|
570
|
-
c.cBxT1 =
|
|
571
|
-
c.cByT1 =
|
|
572
|
-
c.cBzT1 =
|
|
737
|
+
(cAxT1 * wAxT1 + cAyT1 * wAyT1 + cAzT1 * wAzT1) +
|
|
738
|
+
(cBxT1 * wBxT1 + cByT1 * wByT1 + cBzT1 * wBzT1);
|
|
739
|
+
c.cAxT1 = wAxT1;
|
|
740
|
+
c.cAyT1 = wAyT1;
|
|
741
|
+
c.cAzT1 = wAzT1;
|
|
742
|
+
c.cBxT1 = wBxT1;
|
|
743
|
+
c.cByT1 = wByT1;
|
|
744
|
+
c.cBzT1 = wBzT1;
|
|
573
745
|
c.jacInvT1 = denomT1 > 0 ? 1 / denomT1 : 0;
|
|
574
746
|
const cAxT2 = rAy * t2z - rAz * t2y;
|
|
575
747
|
const cAyT2 = rAz * t2x - rAx * t2z;
|
|
@@ -577,27 +749,31 @@ function setupContactRow(c, lv, av, invMass, invInertia) {
|
|
|
577
749
|
const cBxT2 = rBy * t2z - rBz * t2y;
|
|
578
750
|
const cByT2 = rBz * t2x - rBx * t2z;
|
|
579
751
|
const cBzT2 = rBx * t2y - rBy * t2x;
|
|
752
|
+
const wAxT2 = W[a9 + 0] * cAxT2 + W[a9 + 1] * cAyT2 + W[a9 + 2] * cAzT2;
|
|
753
|
+
const wAyT2 = W[a9 + 3] * cAxT2 + W[a9 + 4] * cAyT2 + W[a9 + 5] * cAzT2;
|
|
754
|
+
const wAzT2 = W[a9 + 6] * cAxT2 + W[a9 + 7] * cAyT2 + W[a9 + 8] * cAzT2;
|
|
755
|
+
const wBxT2 = W[b9 + 0] * cBxT2 + W[b9 + 1] * cByT2 + W[b9 + 2] * cBzT2;
|
|
756
|
+
const wByT2 = W[b9 + 3] * cBxT2 + W[b9 + 4] * cByT2 + W[b9 + 5] * cBzT2;
|
|
757
|
+
const wBzT2 = W[b9 + 6] * cBxT2 + W[b9 + 7] * cByT2 + W[b9 + 8] * cBzT2;
|
|
580
758
|
const denomT2 = imA + imB +
|
|
581
|
-
(cAxT2 *
|
|
582
|
-
(cBxT2 *
|
|
583
|
-
c.cAxT2 =
|
|
584
|
-
c.cAyT2 =
|
|
585
|
-
c.cAzT2 =
|
|
586
|
-
c.cBxT2 =
|
|
587
|
-
c.cByT2 =
|
|
588
|
-
c.cBzT2 =
|
|
759
|
+
(cAxT2 * wAxT2 + cAyT2 * wAyT2 + cAzT2 * wAzT2) +
|
|
760
|
+
(cBxT2 * wBxT2 + cByT2 * wByT2 + cBzT2 * wBzT2);
|
|
761
|
+
c.cAxT2 = wAxT2;
|
|
762
|
+
c.cAyT2 = wAyT2;
|
|
763
|
+
c.cAzT2 = wAzT2;
|
|
764
|
+
c.cBxT2 = wBxT2;
|
|
765
|
+
c.cByT2 = wByT2;
|
|
766
|
+
c.cBzT2 = wBzT2;
|
|
589
767
|
c.jacInvT2 = denomT2 > 0 ? 1 / denomT2 : 0;
|
|
590
768
|
}
|
|
591
769
|
// ITER: one push-only normal row + two Coulomb friction rows. Friction
|
|
592
770
|
// bound depends on the *current* applied normal impulse, so it tightens
|
|
593
771
|
// as the normal row converges.
|
|
594
|
-
function iterateContactRow(c, lv, av, invMass
|
|
772
|
+
function iterateContactRow(c, lv, av, invMass) {
|
|
595
773
|
const imA = invMass[c.bodyA];
|
|
596
774
|
const imB = invMass[c.bodyB];
|
|
597
775
|
if (imA === 0 && imB === 0)
|
|
598
776
|
return;
|
|
599
|
-
const iiA = invInertia[c.bodyA];
|
|
600
|
-
const iiB = invInertia[c.bodyB];
|
|
601
777
|
const ai = c.bodyA * 3, bi = c.bodyB * 3;
|
|
602
778
|
const rAx = c.rAx, rAy = c.rAy, rAz = c.rAz;
|
|
603
779
|
const rBx = c.rBx, rBy = c.rBy, rBz = c.rBz;
|
|
@@ -630,17 +806,17 @@ function iterateContactRow(c, lv, av, invMass, invInertia) {
|
|
|
630
806
|
lv[ai + 0] -= dImpN * imA * nx;
|
|
631
807
|
lv[ai + 1] -= dImpN * imA * ny;
|
|
632
808
|
lv[ai + 2] -= dImpN * imA * nz;
|
|
633
|
-
av[ai + 0] -= dImpN *
|
|
634
|
-
av[ai + 1] -= dImpN *
|
|
635
|
-
av[ai + 2] -= dImpN *
|
|
809
|
+
av[ai + 0] -= dImpN * cAxN;
|
|
810
|
+
av[ai + 1] -= dImpN * cAyN;
|
|
811
|
+
av[ai + 2] -= dImpN * cAzN;
|
|
636
812
|
}
|
|
637
813
|
if (imB > 0) {
|
|
638
814
|
lv[bi + 0] += dImpN * imB * nx;
|
|
639
815
|
lv[bi + 1] += dImpN * imB * ny;
|
|
640
816
|
lv[bi + 2] += dImpN * imB * nz;
|
|
641
|
-
av[bi + 0] += dImpN *
|
|
642
|
-
av[bi + 1] += dImpN *
|
|
643
|
-
av[bi + 2] += dImpN *
|
|
817
|
+
av[bi + 0] += dImpN * cBxN;
|
|
818
|
+
av[bi + 1] += dImpN * cByN;
|
|
819
|
+
av[bi + 2] += dImpN * cBzN;
|
|
644
820
|
}
|
|
645
821
|
}
|
|
646
822
|
}
|
|
@@ -658,10 +834,10 @@ function iterateContactRow(c, lv, av, invMass, invInertia) {
|
|
|
658
834
|
const dvx2 = vBx2 - vAx2;
|
|
659
835
|
const dvy2 = vBy2 - vAy2;
|
|
660
836
|
const dvz2 = vBz2 - vAz2;
|
|
661
|
-
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,
|
|
662
|
-
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,
|
|
837
|
+
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, lv, av, 1);
|
|
838
|
+
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, lv, av, 2);
|
|
663
839
|
}
|
|
664
|
-
function applyFrictionTangent(c, ai, bi, dvx, dvy, dvz, tx, ty, tz, cAx, cAy, cAz, cBx, cBy, cBz, jacInv, muNormal, imA, imB,
|
|
840
|
+
function applyFrictionTangent(c, ai, bi, dvx, dvy, dvz, tx, ty, tz, cAx, cAy, cAz, cBx, cBy, cBz, jacInv, muNormal, imA, imB, lv, av, slot) {
|
|
665
841
|
if (jacInv <= 0)
|
|
666
842
|
return;
|
|
667
843
|
const relVel = dvx * tx + dvy * ty + dvz * tz;
|
|
@@ -686,17 +862,17 @@ function applyFrictionTangent(c, ai, bi, dvx, dvy, dvz, tx, ty, tz, cAx, cAy, cA
|
|
|
686
862
|
lv[ai + 0] -= dImp * imA * tx;
|
|
687
863
|
lv[ai + 1] -= dImp * imA * ty;
|
|
688
864
|
lv[ai + 2] -= dImp * imA * tz;
|
|
689
|
-
av[ai + 0] -= dImp *
|
|
690
|
-
av[ai + 1] -= dImp *
|
|
691
|
-
av[ai + 2] -= dImp *
|
|
865
|
+
av[ai + 0] -= dImp * cAx;
|
|
866
|
+
av[ai + 1] -= dImp * cAy;
|
|
867
|
+
av[ai + 2] -= dImp * cAz;
|
|
692
868
|
}
|
|
693
869
|
if (imB > 0) {
|
|
694
870
|
lv[bi + 0] += dImp * imB * tx;
|
|
695
871
|
lv[bi + 1] += dImp * imB * ty;
|
|
696
872
|
lv[bi + 2] += dImp * imB * tz;
|
|
697
|
-
av[bi + 0] += dImp *
|
|
698
|
-
av[bi + 1] += dImp *
|
|
699
|
-
av[bi + 2] += dImp *
|
|
873
|
+
av[bi + 0] += dImp * cBx;
|
|
874
|
+
av[bi + 1] += dImp * cBy;
|
|
875
|
+
av[bi + 2] += dImp * cBz;
|
|
700
876
|
}
|
|
701
877
|
}
|
|
702
878
|
function buildBodyMat(store, i, out) {
|