reze-engine 0.14.0 → 0.15.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 +81 -108
- package/dist/engine.d.ts +1 -7
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +4 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/physics/body.d.ts +30 -0
- package/dist/physics/body.d.ts.map +1 -0
- package/dist/physics/body.js +215 -0
- package/dist/physics/constraint.d.ts +17 -0
- package/dist/physics/constraint.d.ts.map +1 -0
- package/dist/physics/constraint.js +102 -0
- package/dist/physics/contact.d.ts +32 -0
- package/dist/physics/contact.d.ts.map +1 -0
- package/dist/physics/contact.js +728 -0
- package/dist/physics/index.d.ts +4 -0
- package/dist/physics/index.d.ts.map +1 -0
- package/dist/physics/index.js +3 -0
- package/dist/physics/physics.d.ts +31 -0
- package/dist/physics/physics.d.ts.map +1 -0
- package/dist/physics/physics.js +211 -0
- package/dist/physics/solver.d.ts +5 -0
- package/dist/physics/solver.d.ts.map +1 -0
- package/dist/physics/solver.js +416 -0
- package/dist/physics/types.d.ts +46 -0
- package/dist/physics/types.d.ts.map +1 -0
- package/dist/physics/types.js +12 -0
- package/dist/physics/world.d.ts +12 -0
- package/dist/physics/world.d.ts.map +1 -0
- package/dist/physics/world.js +146 -0
- package/dist/physics-debug.d.ts +30 -0
- package/dist/physics-debug.d.ts.map +1 -0
- package/dist/physics-debug.js +526 -0
- 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 +2 -2
- package/dist/shaders/passes/physics-debug.d.ts +2 -0
- package/dist/shaders/passes/physics-debug.d.ts.map +1 -0
- package/dist/shaders/passes/physics-debug.js +69 -0
- package/package.json +3 -6
- package/src/engine.ts +5 -9
- package/src/index.ts +1 -1
- package/src/physics/body.ts +305 -0
- package/src/physics/constraint.ts +151 -0
- package/src/physics/contact.ts +983 -0
- package/src/physics/index.ts +8 -0
- package/src/physics/physics.ts +255 -0
- package/src/physics/solver.ts +430 -0
- package/src/physics/types.ts +50 -0
- package/src/physics/world.ts +152 -0
- package/src/shaders/materials/hair.ts +2 -2
- package/dist/ammo-loader.d.ts +0 -3
- package/dist/ammo-loader.d.ts.map +0 -1
- package/dist/ammo-loader.js +0 -26
- package/dist/physics.d.ts +0 -86
- package/dist/physics.d.ts.map +0 -1
- package/dist/physics.js +0 -527
- package/dist/shaders/body.d.ts +0 -2
- package/dist/shaders/body.d.ts.map +0 -1
- package/dist/shaders/body.js +0 -199
- package/dist/shaders/classify.d.ts +0 -4
- package/dist/shaders/classify.d.ts.map +0 -1
- package/dist/shaders/classify.js +0 -12
- package/dist/shaders/cloth_rough.d.ts +0 -2
- package/dist/shaders/cloth_rough.d.ts.map +0 -1
- package/dist/shaders/cloth_rough.js +0 -178
- package/dist/shaders/cloth_smooth.d.ts +0 -2
- package/dist/shaders/cloth_smooth.d.ts.map +0 -1
- package/dist/shaders/cloth_smooth.js +0 -174
- package/dist/shaders/default.d.ts +0 -2
- package/dist/shaders/default.d.ts.map +0 -1
- package/dist/shaders/default.js +0 -171
- package/dist/shaders/eye.d.ts +0 -2
- package/dist/shaders/eye.d.ts.map +0 -1
- package/dist/shaders/eye.js +0 -146
- package/dist/shaders/face.d.ts +0 -2
- package/dist/shaders/face.d.ts.map +0 -1
- package/dist/shaders/face.js +0 -199
- package/dist/shaders/hair.d.ts +0 -2
- package/dist/shaders/hair.d.ts.map +0 -1
- package/dist/shaders/hair.js +0 -176
- package/dist/shaders/metal.d.ts +0 -2
- package/dist/shaders/metal.d.ts.map +0 -1
- package/dist/shaders/metal.js +0 -174
- package/dist/shaders/nodes.d.ts +0 -2
- package/dist/shaders/nodes.d.ts.map +0 -1
- package/dist/shaders/nodes.js +0 -456
- package/dist/shaders/stockings.d.ts +0 -2
- package/dist/shaders/stockings.d.ts.map +0 -1
- package/dist/shaders/stockings.js +0 -244
- package/src/ammo-loader.ts +0 -31
- package/src/physics.ts +0 -706
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/physics/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AACvC,OAAO,EACL,cAAc,EACd,aAAa,EACb,KAAK,SAAS,EACd,KAAK,KAAK,GACX,MAAM,SAAS,CAAA"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Vec3, Quat, Mat4 } from "../math";
|
|
2
|
+
import type { Rigidbody, Joint } from "./types";
|
|
3
|
+
import { RigidBodyStore } from "./body";
|
|
4
|
+
export declare class RezePhysics {
|
|
5
|
+
private rigidbodies;
|
|
6
|
+
private joints;
|
|
7
|
+
private store;
|
|
8
|
+
private world;
|
|
9
|
+
private constraints;
|
|
10
|
+
private contacts;
|
|
11
|
+
private firstFrame;
|
|
12
|
+
private timeAccum;
|
|
13
|
+
private readonly fixedTimeStep;
|
|
14
|
+
private readonly maxSubSteps;
|
|
15
|
+
constructor(rigidbodies: Rigidbody[], joints?: Joint[]);
|
|
16
|
+
setGravity(gravity: Vec3): void;
|
|
17
|
+
getGravity(): Vec3;
|
|
18
|
+
getRigidbodies(): Rigidbody[];
|
|
19
|
+
getJoints(): Joint[];
|
|
20
|
+
getStore(): RigidBodyStore;
|
|
21
|
+
getRigidbodyTransforms(): Array<{
|
|
22
|
+
position: Vec3;
|
|
23
|
+
rotation: Quat;
|
|
24
|
+
}>;
|
|
25
|
+
reset(boneWorldMatrices: Mat4[]): void;
|
|
26
|
+
step(dt: number, boneWorldMatrices: Mat4[], boneInverseBindMatrices: Float32Array): void;
|
|
27
|
+
private snapBodiesToBones;
|
|
28
|
+
private syncFromBones;
|
|
29
|
+
private applyDynamicsToBones;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=physics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"physics.d.ts","sourceRoot":"","sources":["../../src/physics/physics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAE/C,OAAO,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAYvC,qBAAa,WAAW;IACtB,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,KAAK,CAAO;IACpB,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,UAAU,CAAO;IAGzB,OAAO,CAAC,SAAS,CAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAK;gBAErB,WAAW,EAAE,SAAS,EAAE,EAAE,MAAM,GAAE,KAAK,EAAO;IAS1D,UAAU,CAAC,OAAO,EAAE,IAAI,GAAG,IAAI;IAG/B,UAAU,IAAI,IAAI;IAGlB,cAAc,IAAI,SAAS,EAAE;IAG7B,SAAS,IAAI,KAAK,EAAE;IAGpB,QAAQ,IAAI,cAAc;IAI1B,sBAAsB,IAAI,KAAK,CAAC;QAAE,QAAQ,EAAE,IAAI,CAAC;QAAC,QAAQ,EAAE,IAAI,CAAA;KAAE,CAAC;IAiBnE,KAAK,CAAC,iBAAiB,EAAE,IAAI,EAAE,GAAG,IAAI;IAKtC,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,iBAAiB,EAAE,IAAI,EAAE,EAAE,uBAAuB,EAAE,YAAY,GAAG,IAAI;IA6BxF,OAAO,CAAC,iBAAiB;IAuCzB,OAAO,CAAC,aAAa;IA6ErB,OAAO,CAAC,oBAAoB;CAiC7B"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { Vec3, Quat, Mat4 } from "../math";
|
|
2
|
+
import { RigidbodyType } from "./types";
|
|
3
|
+
import { RigidBodyStore } from "./body";
|
|
4
|
+
import { World } from "./world";
|
|
5
|
+
import { buildConstraints } from "./constraint";
|
|
6
|
+
import { ContactPool } from "./contact";
|
|
7
|
+
const _bodyMat = new Float32Array(16);
|
|
8
|
+
const _boneMat = new Float32Array(16);
|
|
9
|
+
const _scratchQuat = new Quat(0, 0, 0, 1);
|
|
10
|
+
// Static / kinematic bodies follow their bone via boneWorld × bodyOffset;
|
|
11
|
+
// dynamic bodies integrate under gravity + constraints and write their pose
|
|
12
|
+
// back via bodyWorld × bodyOffsetInverse.
|
|
13
|
+
export class RezePhysics {
|
|
14
|
+
constructor(rigidbodies, joints = []) {
|
|
15
|
+
this.firstFrame = true;
|
|
16
|
+
// Fixed-timestep accumulator: physics runs at 75 Hz regardless of render
|
|
17
|
+
// rate, so spring impulse, damping, and integration are deterministic.
|
|
18
|
+
this.timeAccum = 0;
|
|
19
|
+
this.fixedTimeStep = 1 / 75;
|
|
20
|
+
this.maxSubSteps = 10;
|
|
21
|
+
this.rigidbodies = rigidbodies;
|
|
22
|
+
this.joints = joints;
|
|
23
|
+
this.store = new RigidBodyStore(rigidbodies);
|
|
24
|
+
this.world = new World(new Vec3(0, -98, 0));
|
|
25
|
+
this.constraints = buildConstraints(rigidbodies, joints);
|
|
26
|
+
this.contacts = new ContactPool();
|
|
27
|
+
}
|
|
28
|
+
setGravity(gravity) {
|
|
29
|
+
this.world.setGravity(gravity);
|
|
30
|
+
}
|
|
31
|
+
getGravity() {
|
|
32
|
+
return this.world.gravity;
|
|
33
|
+
}
|
|
34
|
+
getRigidbodies() {
|
|
35
|
+
return this.rigidbodies;
|
|
36
|
+
}
|
|
37
|
+
getJoints() {
|
|
38
|
+
return this.joints;
|
|
39
|
+
}
|
|
40
|
+
getStore() {
|
|
41
|
+
return this.store;
|
|
42
|
+
}
|
|
43
|
+
getRigidbodyTransforms() {
|
|
44
|
+
const out = [];
|
|
45
|
+
const pos = this.store.positions;
|
|
46
|
+
const ori = this.store.orientations;
|
|
47
|
+
for (let i = 0; i < this.store.count; i++) {
|
|
48
|
+
const i3 = i * 3;
|
|
49
|
+
const i4 = i * 4;
|
|
50
|
+
out.push({
|
|
51
|
+
position: new Vec3(pos[i3 + 0], pos[i3 + 1], pos[i3 + 2]),
|
|
52
|
+
rotation: new Quat(ori[i4 + 0], ori[i4 + 1], ori[i4 + 2], ori[i4 + 3]),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
// Snap dynamic bodies back to their bone-driven pose, zero velocities.
|
|
58
|
+
// Used when the simulation diverged or the user scrubbed the timeline.
|
|
59
|
+
reset(boneWorldMatrices) {
|
|
60
|
+
if (this.firstFrame)
|
|
61
|
+
return;
|
|
62
|
+
this.snapBodiesToBones(boneWorldMatrices);
|
|
63
|
+
}
|
|
64
|
+
step(dt, boneWorldMatrices, boneInverseBindMatrices) {
|
|
65
|
+
if (this.firstFrame) {
|
|
66
|
+
this.store.computeBoneOffsets(boneInverseBindMatrices);
|
|
67
|
+
// Start at current bone pose, not the PMX bind pose, so animations
|
|
68
|
+
// that skip frame 0 don't pop bodies on first step.
|
|
69
|
+
this.snapBodiesToBones(boneWorldMatrices);
|
|
70
|
+
this.firstFrame = false;
|
|
71
|
+
}
|
|
72
|
+
// Sync once per render frame; kinematic targets don't change between
|
|
73
|
+
// substeps. Render dt is used to derive kinematic velocities from the
|
|
74
|
+
// bone-pose delta so joints feel the kinematic motion.
|
|
75
|
+
this.syncFromBones(boneWorldMatrices, dt);
|
|
76
|
+
// Fixed-timestep substeps. The maxSubSteps cap prevents runaway after
|
|
77
|
+
// a long stall (tab backgrounded, etc.).
|
|
78
|
+
this.timeAccum += dt;
|
|
79
|
+
let sub = 0;
|
|
80
|
+
while (this.timeAccum >= this.fixedTimeStep && sub < this.maxSubSteps) {
|
|
81
|
+
this.world.step(this.store, this.constraints, this.contacts, this.fixedTimeStep);
|
|
82
|
+
this.timeAccum -= this.fixedTimeStep;
|
|
83
|
+
sub++;
|
|
84
|
+
}
|
|
85
|
+
if (sub === this.maxSubSteps)
|
|
86
|
+
this.timeAccum = 0;
|
|
87
|
+
this.applyDynamicsToBones(boneWorldMatrices);
|
|
88
|
+
}
|
|
89
|
+
// Snap all bone-bound bodies to boneWorld × bodyOffset, zero velocities.
|
|
90
|
+
snapBodiesToBones(boneWorldMatrices) {
|
|
91
|
+
const N = this.store.count;
|
|
92
|
+
const offsets = this.store.bodyOffsetMatrix;
|
|
93
|
+
const positions = this.store.positions;
|
|
94
|
+
const orientations = this.store.orientations;
|
|
95
|
+
const lv = this.store.linearVelocities;
|
|
96
|
+
const av = this.store.angularVelocities;
|
|
97
|
+
const boneIdx = this.store.boneIndex;
|
|
98
|
+
for (let i = 0; i < N; i++) {
|
|
99
|
+
const b = boneIdx[i];
|
|
100
|
+
if (b < 0 || b >= boneWorldMatrices.length)
|
|
101
|
+
continue;
|
|
102
|
+
Mat4.multiplyArrays(boneWorldMatrices[b].values, 0, offsets, i * 16, _bodyMat, 0);
|
|
103
|
+
const i3 = i * 3;
|
|
104
|
+
const i4 = i * 4;
|
|
105
|
+
positions[i3 + 0] = _bodyMat[12];
|
|
106
|
+
positions[i3 + 1] = _bodyMat[13];
|
|
107
|
+
positions[i3 + 2] = _bodyMat[14];
|
|
108
|
+
Mat4.toQuatFromArrayInto(_bodyMat, 0, _scratchQuat);
|
|
109
|
+
orientations[i4 + 0] = _scratchQuat.x;
|
|
110
|
+
orientations[i4 + 1] = _scratchQuat.y;
|
|
111
|
+
orientations[i4 + 2] = _scratchQuat.z;
|
|
112
|
+
orientations[i4 + 3] = _scratchQuat.w;
|
|
113
|
+
lv[i3 + 0] = 0;
|
|
114
|
+
lv[i3 + 1] = 0;
|
|
115
|
+
lv[i3 + 2] = 0;
|
|
116
|
+
av[i3 + 0] = 0;
|
|
117
|
+
av[i3 + 1] = 0;
|
|
118
|
+
av[i3 + 2] = 0;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Pull Static / Kinematic bodies to their bones and derive velocities
|
|
122
|
+
// from the bone-pose delta — joints attached to fast limbs need to see
|
|
123
|
+
// the kinematic motion, not just the position jump, or dependent cloth
|
|
124
|
+
// bodies lag behind quick movement.
|
|
125
|
+
syncFromBones(boneWorldMatrices, dt) {
|
|
126
|
+
const N = this.store.count;
|
|
127
|
+
const offsets = this.store.bodyOffsetMatrix;
|
|
128
|
+
const positions = this.store.positions;
|
|
129
|
+
const orientations = this.store.orientations;
|
|
130
|
+
const lv = this.store.linearVelocities;
|
|
131
|
+
const av = this.store.angularVelocities;
|
|
132
|
+
const types = this.store.type;
|
|
133
|
+
const boneIdx = this.store.boneIndex;
|
|
134
|
+
const invDt = dt > 0 ? 1 / dt : 0;
|
|
135
|
+
for (let i = 0; i < N; i++) {
|
|
136
|
+
const t = types[i];
|
|
137
|
+
if (t !== RigidbodyType.Static && t !== RigidbodyType.Kinematic)
|
|
138
|
+
continue;
|
|
139
|
+
const b = boneIdx[i];
|
|
140
|
+
if (b < 0 || b >= boneWorldMatrices.length)
|
|
141
|
+
continue;
|
|
142
|
+
Mat4.multiplyArrays(boneWorldMatrices[b].values, 0, offsets, i * 16, _bodyMat, 0);
|
|
143
|
+
const i3 = i * 3;
|
|
144
|
+
const i4 = i * 4;
|
|
145
|
+
// Save previous transform for the velocity diff. invDt = 0 (first
|
|
146
|
+
// frame / reset) skips the diff and zeros velocities.
|
|
147
|
+
const oldPx = positions[i3 + 0], oldPy = positions[i3 + 1], oldPz = positions[i3 + 2];
|
|
148
|
+
const oldOx = orientations[i4 + 0], oldOy = orientations[i4 + 1];
|
|
149
|
+
const oldOz = orientations[i4 + 2], oldOw = orientations[i4 + 3];
|
|
150
|
+
positions[i3 + 0] = _bodyMat[12];
|
|
151
|
+
positions[i3 + 1] = _bodyMat[13];
|
|
152
|
+
positions[i3 + 2] = _bodyMat[14];
|
|
153
|
+
Mat4.toQuatFromArrayInto(_bodyMat, 0, _scratchQuat);
|
|
154
|
+
const newOx = _scratchQuat.x, newOy = _scratchQuat.y;
|
|
155
|
+
const newOz = _scratchQuat.z, newOw = _scratchQuat.w;
|
|
156
|
+
orientations[i4 + 0] = newOx;
|
|
157
|
+
orientations[i4 + 1] = newOy;
|
|
158
|
+
orientations[i4 + 2] = newOz;
|
|
159
|
+
orientations[i4 + 3] = newOw;
|
|
160
|
+
if (invDt === 0) {
|
|
161
|
+
lv[i3 + 0] = 0;
|
|
162
|
+
lv[i3 + 1] = 0;
|
|
163
|
+
lv[i3 + 2] = 0;
|
|
164
|
+
av[i3 + 0] = 0;
|
|
165
|
+
av[i3 + 1] = 0;
|
|
166
|
+
av[i3 + 2] = 0;
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
lv[i3 + 0] = (_bodyMat[12] - oldPx) * invDt;
|
|
170
|
+
lv[i3 + 1] = (_bodyMat[13] - oldPy) * invDt;
|
|
171
|
+
lv[i3 + 2] = (_bodyMat[14] - oldPz) * invDt;
|
|
172
|
+
// ω ≈ 2 · qDiff.xyz / dt with qDiff = qNew · conj(qOld). Shortest-
|
|
173
|
+
// arc sign keeps qDiff and −qDiff (same rotation) from doubling ω.
|
|
174
|
+
const cox = -oldOx, coy = -oldOy, coz = -oldOz, cow = oldOw;
|
|
175
|
+
const dx = newOw * cox + newOx * cow + newOy * coz - newOz * coy;
|
|
176
|
+
const dy = newOw * coy - newOx * coz + newOy * cow + newOz * cox;
|
|
177
|
+
const dz = newOw * coz + newOx * coy - newOy * cox + newOz * cow;
|
|
178
|
+
const dw = newOw * cow - newOx * cox - newOy * coy - newOz * coz;
|
|
179
|
+
const sign = dw < 0 ? -1 : 1;
|
|
180
|
+
av[i3 + 0] = 2 * sign * dx * invDt;
|
|
181
|
+
av[i3 + 1] = 2 * sign * dy * invDt;
|
|
182
|
+
av[i3 + 2] = 2 * sign * dz * invDt;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// Dynamic bodies write their transform back to the bone matrix:
|
|
187
|
+
// boneWorld = bodyWorld × bodyOffsetInverse.
|
|
188
|
+
applyDynamicsToBones(boneWorldMatrices) {
|
|
189
|
+
const N = this.store.count;
|
|
190
|
+
const inv = this.store.bodyOffsetInverse;
|
|
191
|
+
const positions = this.store.positions;
|
|
192
|
+
const orientations = this.store.orientations;
|
|
193
|
+
const types = this.store.type;
|
|
194
|
+
const boneIdx = this.store.boneIndex;
|
|
195
|
+
for (let i = 0; i < N; i++) {
|
|
196
|
+
if (types[i] !== RigidbodyType.Dynamic)
|
|
197
|
+
continue;
|
|
198
|
+
const b = boneIdx[i];
|
|
199
|
+
if (b < 0 || b >= boneWorldMatrices.length)
|
|
200
|
+
continue;
|
|
201
|
+
const i3 = i * 3;
|
|
202
|
+
const i4 = i * 4;
|
|
203
|
+
Mat4.fromPositionRotationInto(positions[i3 + 0], positions[i3 + 1], positions[i3 + 2], orientations[i4 + 0], orientations[i4 + 1], orientations[i4 + 2], orientations[i4 + 3], _bodyMat);
|
|
204
|
+
Mat4.multiplyArrays(_bodyMat, 0, inv, i * 16, _boneMat, 0);
|
|
205
|
+
// Sanity gate against NaN / extreme values — silently drop the update.
|
|
206
|
+
if (Number.isFinite(_boneMat[0]) && Math.abs(_boneMat[0]) < 1e6) {
|
|
207
|
+
boneWorldMatrices[b].values.set(_boneMat);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { RigidBodyStore } from "./body";
|
|
2
|
+
import type { SixDofSpringConstraint } from "./constraint";
|
|
3
|
+
import type { ContactPool } from "./contact";
|
|
4
|
+
export declare function solveConstraints(store: RigidBodyStore, constraints: SixDofSpringConstraint[], contacts: ContactPool, dt: number, iterations: number): void;
|
|
5
|
+
//# sourceMappingURL=solver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"solver.d.ts","sourceRoot":"","sources":["../../src/physics/solver.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAC5C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AAE1D,OAAO,KAAK,EAAW,WAAW,EAAE,MAAM,WAAW,CAAA;AAgBrD,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,cAAc,EACrB,WAAW,EAAE,sBAAsB,EAAE,EACrC,QAAQ,EAAE,WAAW,EACrB,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,MAAM,GACjB,IAAI,CAyNN"}
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
// 6DOF spring + contact constraint solver. Sequential-impulse projected
|
|
2
|
+
// Gauss-Seidel: per axis, target a relative velocity (limit correction +
|
|
3
|
+
// spring), apply the impulse needed to reach it. Friction is two Coulomb
|
|
4
|
+
// rows per contact, normal is push-only.
|
|
5
|
+
import { Mat4 } from "../math";
|
|
6
|
+
import { STOP_ERP } from "./constraint";
|
|
7
|
+
const BOUNCE_THRESHOLD = 2.0;
|
|
8
|
+
// Module-level scratch (no per-iter allocations).
|
|
9
|
+
const _TA = new Float32Array(16);
|
|
10
|
+
const _TB = new Float32Array(16);
|
|
11
|
+
const _bodyMatA = new Float32Array(16);
|
|
12
|
+
const _bodyMatB = new Float32Array(16);
|
|
13
|
+
const _linAxes = new Float32Array(9); // 3 linear axes × xyz
|
|
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);
|
|
19
|
+
export function solveConstraints(store, constraints, contacts, dt, iterations) {
|
|
20
|
+
if (dt <= 0)
|
|
21
|
+
return;
|
|
22
|
+
if (constraints.length === 0 && contacts.count === 0)
|
|
23
|
+
return;
|
|
24
|
+
const invDt = 1 / dt;
|
|
25
|
+
const lv = store.linearVelocities;
|
|
26
|
+
const av = store.angularVelocities;
|
|
27
|
+
const pos = store.positions;
|
|
28
|
+
const invMass = store.invMass;
|
|
29
|
+
const invInertia = store.invInertia;
|
|
30
|
+
for (let iter = 0; iter < iterations; iter++) {
|
|
31
|
+
for (let c = 0; c < constraints.length; c++) {
|
|
32
|
+
const con = constraints[c];
|
|
33
|
+
const a = con.bodyA;
|
|
34
|
+
const b = con.bodyB;
|
|
35
|
+
const imA = invMass[a];
|
|
36
|
+
const imB = invMass[b];
|
|
37
|
+
const iiA = invInertia[a];
|
|
38
|
+
const iiB = invInertia[b];
|
|
39
|
+
if (imA === 0 && imB === 0)
|
|
40
|
+
continue;
|
|
41
|
+
buildBodyMat(store, a, _bodyMatA);
|
|
42
|
+
buildBodyMat(store, b, _bodyMatB);
|
|
43
|
+
Mat4.multiplyArrays(_bodyMatA, 0, con.frameA, 0, _TA, 0);
|
|
44
|
+
Mat4.multiplyArrays(_bodyMatB, 0, con.frameB, 0, _TB, 0);
|
|
45
|
+
// Mass-weighted shared anchor: kinematic partner gets weight 1 so
|
|
46
|
+
// the anchor sits exactly on it; two dynamic bodies blend by inverse
|
|
47
|
+
// mass. Lever arms are measured from each CG to that shared anchor.
|
|
48
|
+
const ai = a * 3;
|
|
49
|
+
const bi = b * 3;
|
|
50
|
+
const weightA = imB === 0 ? 1 : imA / (imA + imB);
|
|
51
|
+
const weightB = 1 - weightA;
|
|
52
|
+
const anchorX = _TA[12] * weightA + _TB[12] * weightB;
|
|
53
|
+
const anchorY = _TA[13] * weightA + _TB[13] * weightB;
|
|
54
|
+
const anchorZ = _TA[14] * weightA + _TB[14] * weightB;
|
|
55
|
+
_rA[0] = anchorX - pos[ai + 0];
|
|
56
|
+
_rA[1] = anchorY - pos[ai + 1];
|
|
57
|
+
_rA[2] = anchorZ - pos[ai + 2];
|
|
58
|
+
_rB[0] = anchorX - pos[bi + 0];
|
|
59
|
+
_rB[1] = anchorY - pos[bi + 1];
|
|
60
|
+
_rB[2] = anchorZ - pos[bi + 2];
|
|
61
|
+
// Linear part: linearDiff = TA.basis^T · (TB.origin − TA.origin),
|
|
62
|
+
// axes = TA's columns 0/1/2 in world space.
|
|
63
|
+
const dxw = _TB[12] - _TA[12];
|
|
64
|
+
const dyw = _TB[13] - _TA[13];
|
|
65
|
+
const dzw = _TB[14] - _TA[14];
|
|
66
|
+
_linDiff[0] = _TA[0] * dxw + _TA[1] * dyw + _TA[2] * dzw;
|
|
67
|
+
_linDiff[1] = _TA[4] * dxw + _TA[5] * dyw + _TA[6] * dzw;
|
|
68
|
+
_linDiff[2] = _TA[8] * dxw + _TA[9] * dyw + _TA[10] * dzw;
|
|
69
|
+
_linAxes[0] = _TA[0];
|
|
70
|
+
_linAxes[1] = _TA[1];
|
|
71
|
+
_linAxes[2] = _TA[2];
|
|
72
|
+
_linAxes[3] = _TA[4];
|
|
73
|
+
_linAxes[4] = _TA[5];
|
|
74
|
+
_linAxes[5] = _TA[6];
|
|
75
|
+
_linAxes[6] = _TA[8];
|
|
76
|
+
_linAxes[7] = _TA[9];
|
|
77
|
+
_linAxes[8] = _TA[10];
|
|
78
|
+
for (let i = 0; i < 3; i++) {
|
|
79
|
+
const lo = con.linearMin[i];
|
|
80
|
+
const hi = con.linearMax[i];
|
|
81
|
+
const curr = _linDiff[i];
|
|
82
|
+
const off = i * 3;
|
|
83
|
+
const axx = _linAxes[off + 0];
|
|
84
|
+
const axy = _linAxes[off + 1];
|
|
85
|
+
const axz = _linAxes[off + 2];
|
|
86
|
+
// (rA × axis), (rB × axis): angular components of the linear Jacobian.
|
|
87
|
+
const cAx = _rA[1] * axz - _rA[2] * axy;
|
|
88
|
+
const cAy = _rA[2] * axx - _rA[0] * axz;
|
|
89
|
+
const cAz = _rA[0] * axy - _rA[1] * axx;
|
|
90
|
+
const cBx = _rB[1] * axz - _rB[2] * axy;
|
|
91
|
+
const cBy = _rB[2] * axx - _rB[0] * axz;
|
|
92
|
+
const cBz = _rB[0] * axy - _rB[1] * axx;
|
|
93
|
+
const cA2 = cAx * cAx + cAy * cAy + cAz * cAz;
|
|
94
|
+
const cB2 = cBx * cBx + cBy * cBy + cBz * cBz;
|
|
95
|
+
const denom = imA + imB + cA2 * iiA + cB2 * iiB;
|
|
96
|
+
if (denom <= 0)
|
|
97
|
+
continue;
|
|
98
|
+
const jacInv = 1 / denom;
|
|
99
|
+
// v_pivot = v_CG + ω × r.
|
|
100
|
+
const vAx = lv[ai + 0] + av[ai + 1] * _rA[2] - av[ai + 2] * _rA[1];
|
|
101
|
+
const vAy = lv[ai + 1] + av[ai + 2] * _rA[0] - av[ai + 0] * _rA[2];
|
|
102
|
+
const vAz = lv[ai + 2] + av[ai + 0] * _rA[1] - av[ai + 1] * _rA[0];
|
|
103
|
+
const vBx = lv[bi + 0] + av[bi + 1] * _rB[2] - av[bi + 2] * _rB[1];
|
|
104
|
+
const vBy = lv[bi + 1] + av[bi + 2] * _rB[0] - av[bi + 0] * _rB[2];
|
|
105
|
+
const vBz = lv[bi + 2] + av[bi + 0] * _rB[1] - av[bi + 1] * _rB[0];
|
|
106
|
+
const relVel = (vBx - vAx) * axx + (vBy - vAy) * axy + (vBz - vAz) * axz;
|
|
107
|
+
let targetVel = 0;
|
|
108
|
+
let active = false;
|
|
109
|
+
if (lo <= hi) {
|
|
110
|
+
let err = 0;
|
|
111
|
+
if (curr < lo)
|
|
112
|
+
err = curr - lo;
|
|
113
|
+
else if (curr > hi)
|
|
114
|
+
err = curr - hi;
|
|
115
|
+
if (err !== 0) {
|
|
116
|
+
targetVel = -err * STOP_ERP * invDt;
|
|
117
|
+
active = true;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (con.springEnabled[i]) {
|
|
121
|
+
targetVel += -con.springStiffness[i] * (curr - con.equilibriumPoint[i]) * dt;
|
|
122
|
+
active = true;
|
|
123
|
+
}
|
|
124
|
+
if (active) {
|
|
125
|
+
const j = (targetVel - relVel) * jacInv;
|
|
126
|
+
if (imA > 0) {
|
|
127
|
+
lv[ai + 0] -= j * imA * axx;
|
|
128
|
+
lv[ai + 1] -= j * imA * axy;
|
|
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;
|
|
192
|
+
}
|
|
193
|
+
_angAxes[6] = zx;
|
|
194
|
+
_angAxes[7] = zy;
|
|
195
|
+
_angAxes[8] = zz;
|
|
196
|
+
const angDenom = iiA + iiB;
|
|
197
|
+
if (angDenom > 0) {
|
|
198
|
+
const angJacInv = 1 / angDenom;
|
|
199
|
+
for (let i = 0; i < 3; i++) {
|
|
200
|
+
const idx = i + 3;
|
|
201
|
+
const lo = con.angularMin[i];
|
|
202
|
+
const hi = con.angularMax[i];
|
|
203
|
+
const curr = _angDiff[i];
|
|
204
|
+
const off = i * 3;
|
|
205
|
+
const axx = _angAxes[off + 0];
|
|
206
|
+
const axy = _angAxes[off + 1];
|
|
207
|
+
const axz = _angAxes[off + 2];
|
|
208
|
+
const relAv = (av[bi + 0] - av[ai + 0]) * axx +
|
|
209
|
+
(av[bi + 1] - av[ai + 1]) * axy +
|
|
210
|
+
(av[bi + 2] - av[ai + 2]) * axz;
|
|
211
|
+
// Sign flip vs linear: d(angDiff)/dt = −(ω_B − ω_A)·ax.
|
|
212
|
+
let targetVel = 0;
|
|
213
|
+
let active = false;
|
|
214
|
+
if (lo <= hi) {
|
|
215
|
+
let err = 0;
|
|
216
|
+
if (curr < lo)
|
|
217
|
+
err = curr - lo;
|
|
218
|
+
else if (curr > hi)
|
|
219
|
+
err = curr - hi;
|
|
220
|
+
if (err !== 0) {
|
|
221
|
+
targetVel = err * STOP_ERP * invDt;
|
|
222
|
+
active = true;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (con.springEnabled[idx]) {
|
|
226
|
+
targetVel += con.springStiffness[idx] * (curr - con.equilibriumPoint[idx]) * dt;
|
|
227
|
+
active = true;
|
|
228
|
+
}
|
|
229
|
+
if (active) {
|
|
230
|
+
const j = (targetVel - relAv) * angJacInv;
|
|
231
|
+
if (iiA > 0) {
|
|
232
|
+
av[ai + 0] -= j * iiA * axx;
|
|
233
|
+
av[ai + 1] -= j * iiA * axy;
|
|
234
|
+
av[ai + 2] -= j * iiA * axz;
|
|
235
|
+
}
|
|
236
|
+
if (iiB > 0) {
|
|
237
|
+
av[bi + 0] += j * iiB * axx;
|
|
238
|
+
av[bi + 1] += j * iiB * axy;
|
|
239
|
+
av[bi + 2] += j * iiB * axz;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
for (let ci = 0; ci < contacts.count; ci++) {
|
|
246
|
+
solveContactRow(contacts.get(ci), lv, av, invMass, invInertia);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// Per-contact: one push-only normal row + two Coulomb friction rows
|
|
251
|
+
// (impulse bound = ±μ·appliedNormalImpulse).
|
|
252
|
+
function solveContactRow(c, lv, av, invMass, invInertia) {
|
|
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)
|
|
257
|
+
return;
|
|
258
|
+
const rAx = c.rAx, rAy = c.rAy, rAz = c.rAz;
|
|
259
|
+
const rBx = c.rBx, rBy = c.rBy, rBz = c.rBz;
|
|
260
|
+
const nx = c.nx, ny = c.ny, nz = c.nz;
|
|
261
|
+
const vAx = lv[ai + 0] + av[ai + 1] * rAz - av[ai + 2] * rAy;
|
|
262
|
+
const vAy = lv[ai + 1] + av[ai + 2] * rAx - av[ai + 0] * rAz;
|
|
263
|
+
const vAz = lv[ai + 2] + av[ai + 0] * rAy - av[ai + 1] * rAx;
|
|
264
|
+
const vBx = lv[bi + 0] + av[bi + 1] * rBz - av[bi + 2] * rBy;
|
|
265
|
+
const vBy = lv[bi + 1] + av[bi + 2] * rBx - av[bi + 0] * rBz;
|
|
266
|
+
const vBz = lv[bi + 2] + av[bi + 0] * rBy - av[bi + 1] * rBx;
|
|
267
|
+
const dvX = vBx - vAx;
|
|
268
|
+
const dvY = vBy - vAy;
|
|
269
|
+
const dvZ = vBz - vAz;
|
|
270
|
+
// Normal row.
|
|
271
|
+
const cAxN = rAy * nz - rAz * ny;
|
|
272
|
+
const cAyN = rAz * nx - rAx * nz;
|
|
273
|
+
const cAzN = rAx * ny - rAy * nx;
|
|
274
|
+
const cBxN = rBy * nz - rBz * ny;
|
|
275
|
+
const cByN = rBz * nx - rBx * nz;
|
|
276
|
+
const cBzN = rBx * ny - rBy * nx;
|
|
277
|
+
const denomN = imA + imB +
|
|
278
|
+
(cAxN * cAxN + cAyN * cAyN + cAzN * cAzN) * iiA +
|
|
279
|
+
(cBxN * cBxN + cByN * cByN + cBzN * cBzN) * iiB;
|
|
280
|
+
if (denomN <= 0)
|
|
281
|
+
return;
|
|
282
|
+
const jacInvN = 1 / denomN;
|
|
283
|
+
const relVelN = dvX * nx + dvY * ny + dvZ * nz;
|
|
284
|
+
// Position correction is handled directly in world.ts (split impulse).
|
|
285
|
+
// Velocity row only removes approach + applies restitution above bounce.
|
|
286
|
+
let bounce = 0;
|
|
287
|
+
if (c.restitution > 0 && relVelN < -BOUNCE_THRESHOLD) {
|
|
288
|
+
bounce = -c.restitution * relVelN;
|
|
289
|
+
}
|
|
290
|
+
let dImpN = (bounce - relVelN) * jacInvN;
|
|
291
|
+
const oldN = c.appliedNormalImpulse;
|
|
292
|
+
let newN = oldN + dImpN;
|
|
293
|
+
if (newN < 0) {
|
|
294
|
+
newN = 0;
|
|
295
|
+
dImpN = -oldN;
|
|
296
|
+
}
|
|
297
|
+
c.appliedNormalImpulse = newN;
|
|
298
|
+
if (dImpN !== 0) {
|
|
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.
|
|
318
|
+
let t1x, t1y, t1z;
|
|
319
|
+
if (Math.abs(nx) < 0.7071) {
|
|
320
|
+
t1x = 0;
|
|
321
|
+
t1y = -nz;
|
|
322
|
+
t1z = ny;
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
t1x = nz;
|
|
326
|
+
t1y = 0;
|
|
327
|
+
t1z = -nx;
|
|
328
|
+
}
|
|
329
|
+
const l = Math.hypot(t1x, t1y, t1z);
|
|
330
|
+
if (l < 1e-8)
|
|
331
|
+
return;
|
|
332
|
+
const tInv = 1 / l;
|
|
333
|
+
t1x *= tInv;
|
|
334
|
+
t1y *= tInv;
|
|
335
|
+
t1z *= tInv;
|
|
336
|
+
const t2x = ny * t1z - nz * t1y;
|
|
337
|
+
const t2y = nz * t1x - nx * t1z;
|
|
338
|
+
const t2z = nx * t1y - ny * t1x;
|
|
339
|
+
const muNormal = c.friction * c.appliedNormalImpulse;
|
|
340
|
+
applyFrictionRow(c, ai, bi, t1x, t1y, t1z, rAx, rAy, rAz, rBx, rBy, rBz, dvX, dvY, dvZ, imA, imB, iiA, iiB, lv, av, muNormal, 1);
|
|
341
|
+
applyFrictionRow(c, ai, bi, t2x, t2y, t2z, rAx, rAy, rAz, rBx, rBy, rBz, dvX, dvY, dvZ, imA, imB, iiA, iiB, lv, av, muNormal, 2);
|
|
342
|
+
}
|
|
343
|
+
function applyFrictionRow(c, ai, bi, tx, ty, tz, rAx, rAy, rAz, rBx, rBy, rBz, dvX, dvY, dvZ, imA, imB, iiA, iiB, lv, av, muNormal, slot) {
|
|
344
|
+
if (muNormal <= 0)
|
|
345
|
+
return;
|
|
346
|
+
const cAx = rAy * tz - rAz * ty;
|
|
347
|
+
const cAy = rAz * tx - rAx * tz;
|
|
348
|
+
const cAz = rAx * ty - rAy * tx;
|
|
349
|
+
const cBx = rBy * tz - rBz * ty;
|
|
350
|
+
const cBy = rBz * tx - rBx * tz;
|
|
351
|
+
const cBz = rBx * ty - rBy * tx;
|
|
352
|
+
const denom = imA + imB +
|
|
353
|
+
(cAx * cAx + cAy * cAy + cAz * cAz) * iiA +
|
|
354
|
+
(cBx * cBx + cBy * cBy + cBz * cBz) * iiB;
|
|
355
|
+
if (denom <= 0)
|
|
356
|
+
return;
|
|
357
|
+
const jacInv = 1 / denom;
|
|
358
|
+
const relVel = dvX * tx + dvY * ty + dvZ * tz;
|
|
359
|
+
let dImp = -relVel * jacInv;
|
|
360
|
+
const old = slot === 1 ? c.appliedFrictionImpulse1 : c.appliedFrictionImpulse2;
|
|
361
|
+
let next = old + dImp;
|
|
362
|
+
if (next < -muNormal) {
|
|
363
|
+
next = -muNormal;
|
|
364
|
+
dImp = next - old;
|
|
365
|
+
}
|
|
366
|
+
else if (next > muNormal) {
|
|
367
|
+
next = muNormal;
|
|
368
|
+
dImp = next - old;
|
|
369
|
+
}
|
|
370
|
+
if (slot === 1)
|
|
371
|
+
c.appliedFrictionImpulse1 = next;
|
|
372
|
+
else
|
|
373
|
+
c.appliedFrictionImpulse2 = next;
|
|
374
|
+
if (dImp === 0)
|
|
375
|
+
return;
|
|
376
|
+
if (imA > 0) {
|
|
377
|
+
lv[ai + 0] -= dImp * imA * tx;
|
|
378
|
+
lv[ai + 1] -= dImp * imA * ty;
|
|
379
|
+
lv[ai + 2] -= dImp * imA * tz;
|
|
380
|
+
av[ai + 0] -= dImp * iiA * cAx;
|
|
381
|
+
av[ai + 1] -= dImp * iiA * cAy;
|
|
382
|
+
av[ai + 2] -= dImp * iiA * cAz;
|
|
383
|
+
}
|
|
384
|
+
if (imB > 0) {
|
|
385
|
+
lv[bi + 0] += dImp * imB * tx;
|
|
386
|
+
lv[bi + 1] += dImp * imB * ty;
|
|
387
|
+
lv[bi + 2] += dImp * imB * tz;
|
|
388
|
+
av[bi + 0] += dImp * iiB * cBx;
|
|
389
|
+
av[bi + 1] += dImp * iiB * cBy;
|
|
390
|
+
av[bi + 2] += dImp * iiB * cBz;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
function buildBodyMat(store, i, out) {
|
|
394
|
+
const i3 = i * 3, i4 = i * 4;
|
|
395
|
+
Mat4.fromPositionRotationInto(store.positions[i3 + 0], store.positions[i3 + 1], store.positions[i3 + 2], store.orientations[i4 + 0], store.orientations[i4 + 1], store.orientations[i4 + 2], store.orientations[i4 + 3], out);
|
|
396
|
+
}
|
|
397
|
+
// Euler XYZ from a 3×3 rotation matrix (row-major elements).
|
|
398
|
+
function matrixToEulerXYZ(r00, r01, r10, r11, r20, r21, r22, out) {
|
|
399
|
+
if (r20 < 1) {
|
|
400
|
+
if (r20 > -1) {
|
|
401
|
+
out[0] = Math.atan2(-r21, r22);
|
|
402
|
+
out[1] = Math.asin(r20);
|
|
403
|
+
out[2] = Math.atan2(-r10, r00);
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
out[0] = -Math.atan2(r01, r11);
|
|
407
|
+
out[1] = -Math.PI * 0.5;
|
|
408
|
+
out[2] = 0;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
out[0] = Math.atan2(r01, r11);
|
|
413
|
+
out[1] = Math.PI * 0.5;
|
|
414
|
+
out[2] = 0;
|
|
415
|
+
}
|
|
416
|
+
}
|