@driftengine/physics 4.13.3 → 4.13.5
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/clothLimits.d.ts +5 -4
- package/dist/clothLimits.js +9 -4
- package/dist/skinnedCloth.d.ts +1 -0
- package/dist/skinnedCloth.js +8 -5
- package/dist/skinnedClothSetup.d.ts +12 -4
- package/dist/skinnedClothSetup.js +7 -0
- package/package.json +1 -1
- package/src/clothLimits.ts +9 -4
- package/src/skinnedCloth.ts +8 -4
- package/src/skinnedClothSetup.ts +18 -4
package/dist/clothLimits.d.ts
CHANGED
|
@@ -10,11 +10,12 @@
|
|
|
10
10
|
* Allocation-free, and each function a kernel the GPU solver mirrors.
|
|
11
11
|
*/
|
|
12
12
|
/**
|
|
13
|
-
* Every free particle drawn toward its skinned position
|
|
14
|
-
*
|
|
15
|
-
* there
|
|
13
|
+
* Every free particle drawn toward its skinned position under the garment's `pull`: `stiffness` is
|
|
14
|
+
* `pull · h²`, each particle's weighted by `weights` (all 1 where null), and a particle keeps
|
|
15
|
+
* `1 / (1 + w · stiffness)` of its offset. A kinematic particle is already there, and one weighted
|
|
16
|
+
* 0 is left as it is: `t + (p − t)` is not `p` in floats.
|
|
16
17
|
*/
|
|
17
|
-
export declare function applyPull(position: Float32Array, inverseMass: Float32Array, target: Float32Array,
|
|
18
|
+
export declare function applyPull(position: Float32Array, inverseMass: Float32Array, target: Float32Array, weights: Float32Array | null, stiffness: number): void;
|
|
18
19
|
/** A particle further than `maxDistance[i] · scale` from its skinned position is put back on that sphere. */
|
|
19
20
|
export declare function applyMaxDistance(position: Float32Array, inverseMass: Float32Array, target: Float32Array, maxDistance: Float32Array, scale: number): void;
|
|
20
21
|
/**
|
package/dist/clothLimits.js
CHANGED
|
@@ -10,14 +10,19 @@
|
|
|
10
10
|
* Allocation-free, and each function a kernel the GPU solver mirrors.
|
|
11
11
|
*/
|
|
12
12
|
/**
|
|
13
|
-
* Every free particle drawn toward its skinned position
|
|
14
|
-
*
|
|
15
|
-
* there
|
|
13
|
+
* Every free particle drawn toward its skinned position under the garment's `pull`: `stiffness` is
|
|
14
|
+
* `pull · h²`, each particle's weighted by `weights` (all 1 where null), and a particle keeps
|
|
15
|
+
* `1 / (1 + w · stiffness)` of its offset. A kinematic particle is already there, and one weighted
|
|
16
|
+
* 0 is left as it is: `t + (p − t)` is not `p` in floats.
|
|
16
17
|
*/
|
|
17
|
-
export function applyPull(position, inverseMass, target,
|
|
18
|
+
export function applyPull(position, inverseMass, target, weights, stiffness) {
|
|
18
19
|
for (let i = 0; i < inverseMass.length; i++) {
|
|
19
20
|
if (inverseMass[i] === 0)
|
|
20
21
|
continue;
|
|
22
|
+
const pull = weights === null ? stiffness : weights[i] * stiffness;
|
|
23
|
+
if (!(pull > 0))
|
|
24
|
+
continue;
|
|
25
|
+
const keep = 1 / (1 + pull);
|
|
21
26
|
const at = i * 3;
|
|
22
27
|
for (let k = at; k < at + 3; k++) {
|
|
23
28
|
const t = target[k];
|
package/dist/skinnedCloth.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ export declare class SkinnedCloth {
|
|
|
24
24
|
private readonly control;
|
|
25
25
|
private readonly targets;
|
|
26
26
|
private readonly limits;
|
|
27
|
+
private readonly pullWeights;
|
|
27
28
|
private readonly colliders;
|
|
28
29
|
/** The air the cloth drags toward, metres a second: the set-up's until `setWind` says otherwise. */
|
|
29
30
|
private readonly wind;
|
package/dist/skinnedCloth.js
CHANGED
|
@@ -51,6 +51,7 @@ export class SkinnedCloth {
|
|
|
51
51
|
control;
|
|
52
52
|
targets;
|
|
53
53
|
limits;
|
|
54
|
+
pullWeights;
|
|
54
55
|
colliders;
|
|
55
56
|
/** The air the cloth drags toward, metres a second: the set-up's until `setWind` says otherwise. */
|
|
56
57
|
wind;
|
|
@@ -70,6 +71,7 @@ export class SkinnedCloth {
|
|
|
70
71
|
: solvable('bending', bending.quads, 4, bending.rest, bending.compliance, this.inverseMass, bending.batches);
|
|
71
72
|
this.tethers = setup.tethers ?? null;
|
|
72
73
|
this.limits = setup.limits ?? {};
|
|
74
|
+
this.pullWeights = setup.pullWeights ?? null;
|
|
73
75
|
this.colliders = setup.colliders?.length ?? 0;
|
|
74
76
|
/* Made once, so a step builds nothing: the control calls back into the private step. */
|
|
75
77
|
const device = {
|
|
@@ -150,7 +152,7 @@ export class SkinnedCloth {
|
|
|
150
152
|
const keep = exactExp(damping * h);
|
|
151
153
|
const dragKeep = exactExp(drag * h);
|
|
152
154
|
/* A compliant constraint's step toward the skinned place: see `SkinnedClothParameters.pull`. */
|
|
153
|
-
const
|
|
155
|
+
const pullStep = this.parameters.pull * h * h;
|
|
154
156
|
for (let s = 0; s < substeps; s++) {
|
|
155
157
|
predictParticles(this.positions, this.substepStart, this.velocities, this.inverseMass, h, keep, dragKeep, gravity, wind);
|
|
156
158
|
this.distance.lambda.fill(0);
|
|
@@ -164,7 +166,7 @@ export class SkinnedCloth {
|
|
|
164
166
|
solveTethers(this.positions, tethers.particles, tethers.anchors, tethers.lengths);
|
|
165
167
|
}
|
|
166
168
|
}
|
|
167
|
-
this.limit(
|
|
169
|
+
this.limit(pullStep);
|
|
168
170
|
finishParticles(this.positions, this.substepStart, this.velocities, this.inverseMass, invH);
|
|
169
171
|
}
|
|
170
172
|
if (blend > 0)
|
|
@@ -174,11 +176,12 @@ export class SkinnedCloth {
|
|
|
174
176
|
* The pull, the limits and the colliders, after the constraints, so a garment pulling into the body
|
|
175
177
|
* loses. The pull first, so the hard limits have the last word.
|
|
176
178
|
*/
|
|
177
|
-
limit(
|
|
179
|
+
limit(pullStep) {
|
|
178
180
|
const { positions, inverseMass, targets, limits } = this;
|
|
179
181
|
const { maxDistanceScale, margin } = this.parameters;
|
|
180
|
-
if (
|
|
181
|
-
applyPull(positions, inverseMass, targets.position,
|
|
182
|
+
if (pullStep > 0) {
|
|
183
|
+
applyPull(positions, inverseMass, targets.position, this.pullWeights, pullStep);
|
|
184
|
+
}
|
|
182
185
|
if (limits.maxDistance !== undefined) {
|
|
183
186
|
applyMaxDistance(positions, inverseMass, targets.position, limits.maxDistance, maxDistanceScale);
|
|
184
187
|
}
|
|
@@ -114,10 +114,10 @@ export interface SkinnedClothParameters {
|
|
|
114
114
|
* A share scaled by the step as `damping` is would hold a sag proportional to the step: hair
|
|
115
115
|
* authored to keep 0.7 of its offset a 1/60 step sags 6.36, 3.49 and 1.83 mm at 1/60, 1/120 and
|
|
116
116
|
* 1/240 that way, where this holds 6.36 mm at all three. A share `k` kept a step at a step `s` is `(1 / k − 1) / s²` here, and keeps
|
|
117
|
-
* exactly `k` at that step.
|
|
118
|
-
*
|
|
119
|
-
* still takes a particle out of shape for a moment; and with
|
|
120
|
-
* rate a coarse step damps more than a fine one does.
|
|
117
|
+
* exactly `k` at that step. `SkinnedClothSetup.pullWeights` weighs it particle by particle, so a
|
|
118
|
+
* strap can be held at its root and hang free at its tip. **What it gives up**: it is a pull
|
|
119
|
+
* rather than a limit, so a hard blow still takes a particle out of shape for a moment; and with
|
|
120
|
+
* no `damping` a stiff one rings, at a rate a coarse step damps more than a fine one does.
|
|
121
121
|
*/
|
|
122
122
|
readonly pull?: number;
|
|
123
123
|
/**
|
|
@@ -166,6 +166,14 @@ export interface SkinnedClothSetup {
|
|
|
166
166
|
readonly inverseBind?: Float32Array;
|
|
167
167
|
readonly limits?: ClothLimits;
|
|
168
168
|
readonly colliders?: readonly ClothCollider[];
|
|
169
|
+
/**
|
|
170
|
+
* One a particle, 0 to 1: how much of the garment's `pull` draws it toward its skinned place,
|
|
171
|
+
* each substep keeping `1 / (1 + w · pull · h²)` of its offset. Absent, every particle takes the
|
|
172
|
+
* whole pull. **For a garment held unevenly**: a strap held at its root and free at its tip, a
|
|
173
|
+
* styled fringe held nearly whole while the body of a coat swings free. A weight of 0 is no pull
|
|
174
|
+
* at all, which is a free particle, not a kinematic one: `inverseMass` is what pins.
|
|
175
|
+
*/
|
|
176
|
+
readonly pullWeights?: Float32Array;
|
|
169
177
|
}
|
|
170
178
|
/** The parameters with every default filled in. */
|
|
171
179
|
export interface ResolvedClothParameters {
|
|
@@ -96,6 +96,13 @@ export function validateClothSetup(setup) {
|
|
|
96
96
|
per('limits.frontstop', setup.limits?.frontstop, 2);
|
|
97
97
|
per('limits.thickness', setup.limits?.thickness, 1);
|
|
98
98
|
per('limits.selfCollision', setup.limits?.selfCollision, 1);
|
|
99
|
+
per('pullWeights', setup.pullWeights, 1);
|
|
100
|
+
const weights = setup.pullWeights;
|
|
101
|
+
for (let i = 0; weights !== undefined && i < weights.length; i++) {
|
|
102
|
+
const w = weights[i];
|
|
103
|
+
if (!(w >= 0 && w <= 1))
|
|
104
|
+
fail(`pullWeights[${i}] is ${w}: a share of the pull, 0 to 1`);
|
|
105
|
+
}
|
|
99
106
|
if ((setup.joints === undefined) !== (setup.weights === undefined))
|
|
100
107
|
fail('joints without weights, or weights without joints');
|
|
101
108
|
if ((setup.joints2 === undefined) !== (setup.weights2 === undefined))
|
package/package.json
CHANGED
package/src/clothLimits.ts
CHANGED
|
@@ -11,18 +11,23 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* Every free particle drawn toward its skinned position
|
|
15
|
-
*
|
|
16
|
-
* there
|
|
14
|
+
* Every free particle drawn toward its skinned position under the garment's `pull`: `stiffness` is
|
|
15
|
+
* `pull · h²`, each particle's weighted by `weights` (all 1 where null), and a particle keeps
|
|
16
|
+
* `1 / (1 + w · stiffness)` of its offset. A kinematic particle is already there, and one weighted
|
|
17
|
+
* 0 is left as it is: `t + (p − t)` is not `p` in floats.
|
|
17
18
|
*/
|
|
18
19
|
export function applyPull(
|
|
19
20
|
position: Float32Array,
|
|
20
21
|
inverseMass: Float32Array,
|
|
21
22
|
target: Float32Array,
|
|
22
|
-
|
|
23
|
+
weights: Float32Array | null,
|
|
24
|
+
stiffness: number,
|
|
23
25
|
): void {
|
|
24
26
|
for (let i = 0; i < inverseMass.length; i++) {
|
|
25
27
|
if ((inverseMass[i] as number) === 0) continue;
|
|
28
|
+
const pull = weights === null ? stiffness : (weights[i] as number) * stiffness;
|
|
29
|
+
if (!(pull > 0)) continue;
|
|
30
|
+
const keep = 1 / (1 + pull);
|
|
26
31
|
const at = i * 3;
|
|
27
32
|
for (let k = at; k < at + 3; k++) {
|
|
28
33
|
const t = target[k] as number;
|
package/src/skinnedCloth.ts
CHANGED
|
@@ -86,6 +86,7 @@ export class SkinnedCloth {
|
|
|
86
86
|
private readonly control: ClothControl;
|
|
87
87
|
private readonly targets: ClothTargets;
|
|
88
88
|
private readonly limits: ClothLimits;
|
|
89
|
+
private readonly pullWeights: Float32Array | null;
|
|
89
90
|
private readonly colliders: number;
|
|
90
91
|
/** The air the cloth drags toward, metres a second: the set-up's until `setWind` says otherwise. */
|
|
91
92
|
private readonly wind: [number, number, number];
|
|
@@ -122,6 +123,7 @@ export class SkinnedCloth {
|
|
|
122
123
|
);
|
|
123
124
|
this.tethers = setup.tethers ?? null;
|
|
124
125
|
this.limits = setup.limits ?? {};
|
|
126
|
+
this.pullWeights = setup.pullWeights ?? null;
|
|
125
127
|
this.colliders = setup.colliders?.length ?? 0;
|
|
126
128
|
/* Made once, so a step builds nothing: the control calls back into the private step. */
|
|
127
129
|
const device: ClothDevice = {
|
|
@@ -219,7 +221,7 @@ export class SkinnedCloth {
|
|
|
219
221
|
const keep = exactExp(damping * h);
|
|
220
222
|
const dragKeep = exactExp(drag * h);
|
|
221
223
|
/* A compliant constraint's step toward the skinned place: see `SkinnedClothParameters.pull`. */
|
|
222
|
-
const
|
|
224
|
+
const pullStep = this.parameters.pull * h * h;
|
|
223
225
|
for (let s = 0; s < substeps; s++) {
|
|
224
226
|
predictParticles(
|
|
225
227
|
this.positions,
|
|
@@ -242,7 +244,7 @@ export class SkinnedCloth {
|
|
|
242
244
|
solveTethers(this.positions, tethers.particles, tethers.anchors, tethers.lengths);
|
|
243
245
|
}
|
|
244
246
|
}
|
|
245
|
-
this.limit(
|
|
247
|
+
this.limit(pullStep);
|
|
246
248
|
finishParticles(this.positions, this.substepStart, this.velocities, this.inverseMass, invH);
|
|
247
249
|
}
|
|
248
250
|
if (blend > 0) this.blend(blend);
|
|
@@ -252,10 +254,12 @@ export class SkinnedCloth {
|
|
|
252
254
|
* The pull, the limits and the colliders, after the constraints, so a garment pulling into the body
|
|
253
255
|
* loses. The pull first, so the hard limits have the last word.
|
|
254
256
|
*/
|
|
255
|
-
private limit(
|
|
257
|
+
private limit(pullStep: number): void {
|
|
256
258
|
const { positions, inverseMass, targets, limits } = this;
|
|
257
259
|
const { maxDistanceScale, margin } = this.parameters;
|
|
258
|
-
if (
|
|
260
|
+
if (pullStep > 0) {
|
|
261
|
+
applyPull(positions, inverseMass, targets.position, this.pullWeights, pullStep);
|
|
262
|
+
}
|
|
259
263
|
if (limits.maxDistance !== undefined) {
|
|
260
264
|
applyMaxDistance(
|
|
261
265
|
positions,
|
package/src/skinnedClothSetup.ts
CHANGED
|
@@ -120,10 +120,10 @@ export interface SkinnedClothParameters {
|
|
|
120
120
|
* A share scaled by the step as `damping` is would hold a sag proportional to the step: hair
|
|
121
121
|
* authored to keep 0.7 of its offset a 1/60 step sags 6.36, 3.49 and 1.83 mm at 1/60, 1/120 and
|
|
122
122
|
* 1/240 that way, where this holds 6.36 mm at all three. A share `k` kept a step at a step `s` is `(1 / k − 1) / s²` here, and keeps
|
|
123
|
-
* exactly `k` at that step.
|
|
124
|
-
*
|
|
125
|
-
* still takes a particle out of shape for a moment; and with
|
|
126
|
-
* rate a coarse step damps more than a fine one does.
|
|
123
|
+
* exactly `k` at that step. `SkinnedClothSetup.pullWeights` weighs it particle by particle, so a
|
|
124
|
+
* strap can be held at its root and hang free at its tip. **What it gives up**: it is a pull
|
|
125
|
+
* rather than a limit, so a hard blow still takes a particle out of shape for a moment; and with
|
|
126
|
+
* no `damping` a stiff one rings, at a rate a coarse step damps more than a fine one does.
|
|
127
127
|
*/
|
|
128
128
|
readonly pull?: number;
|
|
129
129
|
/**
|
|
@@ -173,6 +173,14 @@ export interface SkinnedClothSetup {
|
|
|
173
173
|
readonly inverseBind?: Float32Array;
|
|
174
174
|
readonly limits?: ClothLimits;
|
|
175
175
|
readonly colliders?: readonly ClothCollider[];
|
|
176
|
+
/**
|
|
177
|
+
* One a particle, 0 to 1: how much of the garment's `pull` draws it toward its skinned place,
|
|
178
|
+
* each substep keeping `1 / (1 + w · pull · h²)` of its offset. Absent, every particle takes the
|
|
179
|
+
* whole pull. **For a garment held unevenly**: a strap held at its root and free at its tip, a
|
|
180
|
+
* styled fringe held nearly whole while the body of a coat swings free. A weight of 0 is no pull
|
|
181
|
+
* at all, which is a free particle, not a kinematic one: `inverseMass` is what pins.
|
|
182
|
+
*/
|
|
183
|
+
readonly pullWeights?: Float32Array;
|
|
176
184
|
}
|
|
177
185
|
|
|
178
186
|
/** The parameters with every default filled in. */
|
|
@@ -284,6 +292,12 @@ export function validateClothSetup(setup: SkinnedClothSetup): void {
|
|
|
284
292
|
per('limits.frontstop', setup.limits?.frontstop, 2);
|
|
285
293
|
per('limits.thickness', setup.limits?.thickness, 1);
|
|
286
294
|
per('limits.selfCollision', setup.limits?.selfCollision, 1);
|
|
295
|
+
per('pullWeights', setup.pullWeights, 1);
|
|
296
|
+
const weights = setup.pullWeights;
|
|
297
|
+
for (let i = 0; weights !== undefined && i < weights.length; i++) {
|
|
298
|
+
const w = weights[i] as number;
|
|
299
|
+
if (!(w >= 0 && w <= 1)) fail(`pullWeights[${i}] is ${w}: a share of the pull, 0 to 1`);
|
|
300
|
+
}
|
|
287
301
|
if ((setup.joints === undefined) !== (setup.weights === undefined))
|
|
288
302
|
fail('joints without weights, or weights without joints');
|
|
289
303
|
if ((setup.joints2 === undefined) !== (setup.weights2 === undefined))
|