@rig-c/core 2.75.0 → 2.77.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.
@@ -0,0 +1,235 @@
1
+ /** Indexed in-place physics. Arithmetic and operation order mirror the measured oracle;
2
+ * state is allocated once per pose, never during an update. */
3
+ import type { CorePhysicsRecord } from '../core/constraints_physics.ts';
4
+ import { RUNTIME_PI } from '../core/world.ts';
5
+ const PI2 = RUNTIME_PI * 2;
6
+ const INV_PI2 = 1 / PI2;
7
+ export interface PhysicsState {
8
+ /** The next update takes the bone where it is and integrates nothing: set by a reset and on a constraint never updated. */
9
+ pending: boolean;
10
+ lastTime: number;
11
+ remaining: number;
12
+ ux: number;
13
+ uy: number;
14
+ cx: number;
15
+ cy: number;
16
+ tx: number;
17
+ ty: number;
18
+ xOffset: number;
19
+ xLag: number;
20
+ xVelocity: number;
21
+ yOffset: number;
22
+ yLag: number;
23
+ yVelocity: number;
24
+ rotateOffset: number;
25
+ rotateLag: number;
26
+ rotateVelocity: number;
27
+ scaleOffset: number;
28
+ scaleLag: number;
29
+ scaleVelocity: number;
30
+ }
31
+
32
+ /** A constraint's state before its first update: pending, its clock at 0. */
33
+ export function freshPhysicsState(): PhysicsState {
34
+ return { pending: true, lastTime: 0, remaining: 0, ux: 0, uy: 0, cx: 0, cy: 0, tx: 0, ty: 0, xOffset: 0, xLag: 0, xVelocity: 0, yOffset: 0, yLag: 0, yVelocity: 0, rotateOffset: 0, rotateLag: 0, rotateVelocity: 0, scaleOffset: 0, scaleLag: 0, scaleVelocity: 0 };
35
+ }
36
+
37
+ /** A reset: offsets, velocities and lags cleared, nothing remaining, the clock read at `time`, pending. */
38
+ export function resetPhysicsState(s: PhysicsState, time: number): void {
39
+ s.remaining = 0;
40
+ s.lastTime = time;
41
+ s.pending = true;
42
+ s.xOffset = s.xLag = s.xVelocity = 0;
43
+ s.yOffset = s.yLag = s.yVelocity = 0;
44
+ s.rotateOffset = s.rotateLag = s.rotateVelocity = 0;
45
+ s.scaleOffset = s.scaleLag = s.scaleVelocity = 0;
46
+ }
47
+
48
+ export function stepRuntimePhysics(r: CorePhysicsRecord, s: PhysicsState, w: Float64Array, boneOffset: number, length: number, time: number, reset: boolean, env: Float64Array): boolean {
49
+ const mix = r.mix;
50
+ if (mix === 0) return false;
51
+ const x = r.x > 0, y = r.y > 0, rotateOrShearX = r.rotate > 0 || r.shearX > 0, scaleX = r.scaleX > 0;
52
+ const l = length;
53
+ if (reset) resetPhysicsState(s, time);
54
+ const delta = Math.max(time - s.lastTime, 0);
55
+ // The interpolation weight of the previous update, before this one's time is added: the rotate and the scale-only inertia read the pose as it was shown.
56
+ const shown = Math.max(0, 1 - s.remaining / r.step);
57
+ s.remaining += delta;
58
+ s.lastTime = time;
59
+ const bx = w[boneOffset + 4];
60
+ const by = w[boneOffset + 5];
61
+ let z = 0;
62
+ if (s.pending) {
63
+ s.pending = false;
64
+ s.ux = bx;
65
+ s.uy = by;
66
+ } else {
67
+ let a = s.remaining;
68
+ const i = r.inertia;
69
+ const t = r.step;
70
+ const f = r.referenceScale;
71
+ let d = -1;
72
+ const qx = r.limit * delta;
73
+ const qy = qx;
74
+ if (x || y) {
75
+ if (x) {
76
+ const u = (s.ux - bx) * i;
77
+ s.xOffset += u > qx ? qx : u < -qx ? -qx : u;
78
+ s.ux = bx;
79
+ }
80
+ if (y) {
81
+ const u = (s.uy - by) * i;
82
+ s.yOffset += u > qy ? qy : u < -qy ? -qy : u;
83
+ s.uy = by;
84
+ }
85
+ if (a >= t) {
86
+ d = Math.pow(r.damping, 60 * t);
87
+ const m = r.massInverse * t;
88
+ const e = r.strength;
89
+ const wind = r.wind * f;
90
+ const g = r.gravity * f;
91
+ // The environment's directions (issue #1416), as the runtime weighs them; the skeleton's scale is 1 here, so its factor is left out.
92
+ const ax = wind * env[0] + g * env[2];
93
+ const ay = wind * env[1] + g * env[3];
94
+ const xs = s.xOffset;
95
+ const ys = s.yOffset;
96
+ do {
97
+ if (x) {
98
+ s.xVelocity += (ax - s.xOffset * e) * m;
99
+ s.xOffset += s.xVelocity * t;
100
+ s.xVelocity *= d;
101
+ }
102
+ if (y) {
103
+ s.yVelocity -= (ay + s.yOffset * e) * m;
104
+ s.yOffset += s.yVelocity * t;
105
+ s.yVelocity *= d;
106
+ }
107
+ a -= t;
108
+ } while (a >= t);
109
+ s.xLag = s.xOffset - xs;
110
+ s.yLag = s.yOffset - ys;
111
+ }
112
+ z = Math.max(0, 1 - a / t);
113
+ if (x) w[boneOffset + 4] += (s.xOffset - s.xLag * z) * mix * r.x;
114
+ if (y) w[boneOffset + 5] += (s.yOffset - s.yLag * z) * mix * r.y;
115
+ }
116
+ if (rotateOrShearX || scaleX) {
117
+ const ca = Math.atan2(w[boneOffset + 2], w[boneOffset + 0]);
118
+ let c = 0;
119
+ let sn = 0;
120
+ let mr = 0;
121
+ let dx = s.cx - w[boneOffset + 4];
122
+ let dy = s.cy - w[boneOffset + 5];
123
+ if (dx > qx) dx = qx;
124
+ else if (dx < -qx) dx = -qx;
125
+ if (dy > qy) dy = qy;
126
+ else if (dy < -qy) dy = -qy;
127
+ if (rotateOrShearX) {
128
+ mr = (r.rotate + r.shearX) * mix;
129
+ const lagged = s.rotateLag * shown;
130
+ let rr = Math.atan2(dy + s.ty, dx + s.tx) - ca - (s.rotateOffset - lagged) * mr;
131
+ s.rotateOffset += (rr - Math.ceil(rr * INV_PI2 - 0.5) * PI2) * i;
132
+ rr = (s.rotateOffset - lagged) * mr + ca;
133
+ c = Math.cos(rr);
134
+ sn = Math.sin(rr);
135
+ if (scaleX) {
136
+ rr = l * Math.sqrt(w[boneOffset + 0] * w[boneOffset + 0] + w[boneOffset + 2] * w[boneOffset + 2]);
137
+ if (rr > 0) s.scaleOffset += ((dx * c + dy * sn) * i) / rr;
138
+ }
139
+ } else {
140
+ c = Math.cos(ca);
141
+ sn = Math.sin(ca);
142
+ const rr = l * Math.sqrt(w[boneOffset + 0] * w[boneOffset + 0] + w[boneOffset + 2] * w[boneOffset + 2]) - s.scaleLag * shown;
143
+ if (rr > 0) s.scaleOffset += ((dx * c + dy * sn) * i) / rr;
144
+ }
145
+ a = s.remaining;
146
+ if (a >= t) {
147
+ if (d === -1) d = Math.pow(r.damping, 60 * t);
148
+ const m = r.massInverse * t;
149
+ const e = r.strength;
150
+ const ax = r.wind * env[0] + r.gravity * env[2];
151
+ const ay = r.wind * env[1] + r.gravity * env[3];
152
+ const h = l / f;
153
+ const rs = s.rotateOffset;
154
+ const ss = s.scaleOffset;
155
+ for (;;) {
156
+ a -= t;
157
+ if (scaleX) {
158
+ s.scaleVelocity += (ax * c - ay * sn - s.scaleOffset * e) * m;
159
+ s.scaleOffset += s.scaleVelocity * t;
160
+ s.scaleVelocity *= d;
161
+ }
162
+ if (rotateOrShearX) {
163
+ s.rotateVelocity -= ((ax * sn + ay * c) * h + s.rotateOffset * e) * m;
164
+ s.rotateOffset += s.rotateVelocity * t;
165
+ s.rotateVelocity *= d;
166
+ if (a < t) break;
167
+ const rr = s.rotateOffset * mr + ca;
168
+ c = Math.cos(rr);
169
+ sn = Math.sin(rr);
170
+ } else if (a < t) break;
171
+ }
172
+ s.rotateLag = s.rotateOffset - rs;
173
+ s.scaleLag = s.scaleOffset - ss;
174
+ }
175
+ z = Math.max(0, 1 - a / t);
176
+ }
177
+ s.remaining = a;
178
+ }
179
+ s.cx = w[boneOffset + 4];
180
+ s.cy = w[boneOffset + 5];
181
+ if (rotateOrShearX) {
182
+ let angle = (s.rotateOffset - s.rotateLag * z) * mix;
183
+ let sn = 0;
184
+ let c = 0;
185
+ let a = 0;
186
+ if (r.shearX > 0) {
187
+ let rr = 0;
188
+ if (r.rotate > 0) {
189
+ // The rotation's share turns the y column here, and the x column with the shear below.
190
+ rr = angle * r.rotate;
191
+ sn = Math.sin(rr);
192
+ c = Math.cos(rr);
193
+ a = w[boneOffset + 1];
194
+ w[boneOffset + 1] = c * a - sn * w[boneOffset + 3];
195
+ w[boneOffset + 3] = sn * a + c * w[boneOffset + 3];
196
+ }
197
+ rr += angle * r.shearX;
198
+ sn = Math.sin(rr);
199
+ c = Math.cos(rr);
200
+ a = w[boneOffset + 0];
201
+ w[boneOffset + 0] = c * a - sn * w[boneOffset + 2];
202
+ w[boneOffset + 2] = sn * a + c * w[boneOffset + 2];
203
+ } else {
204
+ angle *= r.rotate;
205
+ sn = Math.sin(angle);
206
+ c = Math.cos(angle);
207
+ a = w[boneOffset + 0];
208
+ w[boneOffset + 0] = c * a - sn * w[boneOffset + 2];
209
+ w[boneOffset + 2] = sn * a + c * w[boneOffset + 2];
210
+ a = w[boneOffset + 1];
211
+ w[boneOffset + 1] = c * a - sn * w[boneOffset + 3];
212
+ w[boneOffset + 3] = sn * a + c * w[boneOffset + 3];
213
+ }
214
+ }
215
+ if (scaleX) {
216
+ const k = 1 + (s.scaleOffset - s.scaleLag * z) * mix * r.scaleX;
217
+ w[boneOffset + 0] *= k;
218
+ w[boneOffset + 2] *= k;
219
+ // The transverse column follows the authored mode (issue #1453), after
220
+ // the X column: the native Volume branch bounds reciprocal compression
221
+ // below 0.7; it is not an area-preservation promise on that branch.
222
+ if (r.scaleYMode === 'Uniform') {
223
+ w[boneOffset + 1] *= k;
224
+ w[boneOffset + 3] *= k;
225
+ } else if (r.scaleYMode === 'Volume') {
226
+ const magnitude = Math.abs(k);
227
+ const transverse = magnitude >= 0.7 ? 1 / magnitude : 4 - 3.67347 * magnitude;
228
+ w[boneOffset + 1] *= transverse;
229
+ w[boneOffset + 3] *= transverse;
230
+ }
231
+ }
232
+ s.tx = l * w[boneOffset + 0];
233
+ s.ty = l * w[boneOffset + 2];
234
+ return true;
235
+ }