three-simplecloth 0.0.4 → 0.0.6
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/SimpleCloth.d.ts +14 -6
- package/dist/SimpleCloth.d.ts.map +1 -1
- package/dist/index.js +192 -180
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/SimpleCloth.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ type ClothConfig = {
|
|
|
35
35
|
collidersRoot?: Object3D;
|
|
36
36
|
stiffness?: number;
|
|
37
37
|
dampening?: number;
|
|
38
|
+
maskMultiplier?: number;
|
|
38
39
|
/**
|
|
39
40
|
* you can tweak the radius of the colliders ( which are spheres attached to bones )
|
|
40
41
|
*
|
|
@@ -53,6 +54,10 @@ type ClothConfig = {
|
|
|
53
54
|
* How many magnets to use ( cloch grab manipulators )
|
|
54
55
|
*/
|
|
55
56
|
magnets: number;
|
|
57
|
+
/**
|
|
58
|
+
* The distance at which a teleport is assumed
|
|
59
|
+
*/
|
|
60
|
+
teleportDistance?: number;
|
|
56
61
|
/**
|
|
57
62
|
* The material used by the SkinnedMesh to turn into "cloth" must apply and use these TSL nodes...
|
|
58
63
|
* By default it will try to guess the most common case when you import a model from blender...
|
|
@@ -63,12 +68,19 @@ type ClothConfig = {
|
|
|
63
68
|
*/
|
|
64
69
|
updateMaterial: (base: Material, positionNode: Node, normalNode: Node) => NodeMaterial;
|
|
65
70
|
};
|
|
71
|
+
export type MagnetHandler = {
|
|
72
|
+
update: () => void;
|
|
73
|
+
updatePosition: (x: number, y: number, z: number) => void;
|
|
74
|
+
deactivate: () => void;
|
|
75
|
+
};
|
|
66
76
|
declare function setupClothOnSkinnedMesh(mesh: SkinnedMesh, renderer: WebGPURenderer, config?: Partial<ClothConfig>): {
|
|
67
77
|
stiffnessUniform: import('three/webgpu').UniformNode<number | undefined>;
|
|
68
78
|
dampeningUniform: import('three/webgpu').UniformNode<number | undefined>;
|
|
69
|
-
gravityUniform: import('three/webgpu').UniformNode<Vector3
|
|
79
|
+
gravityUniform: import('three/webgpu').UniformNode<Vector3>;
|
|
70
80
|
windUniform: import('three/webgpu').UniformNode<Vector3 | undefined>;
|
|
71
81
|
colliders: Collider[];
|
|
82
|
+
mesh: SkinnedMesh<import('three').BufferGeometry<import('three').NormalBufferAttributes, import('three').BufferGeometryEventMap>, Material | Material[], import('three').Object3DEventMap>;
|
|
83
|
+
reset: () => Promise<void> | undefined;
|
|
72
84
|
/**
|
|
73
85
|
* Runs the cloth simulation...
|
|
74
86
|
*
|
|
@@ -86,11 +98,7 @@ declare function setupClothOnSkinnedMesh(mesh: SkinnedMesh, renderer: WebGPURend
|
|
|
86
98
|
* @param strength Strength of the magnet. Default is .5.
|
|
87
99
|
* @returns An object with update and release methods.
|
|
88
100
|
*/
|
|
89
|
-
activateMagnet: (magnetIndex: number, worldPos: Vector3 | Object3D, strength?: number) =>
|
|
90
|
-
update: () => void;
|
|
91
|
-
updatePosition: (x: number, y: number, z: number) => void;
|
|
92
|
-
deactivate: () => void;
|
|
93
|
-
};
|
|
101
|
+
activateMagnet: (magnetIndex: number, worldPos: Vector3 | Object3D, strength?: number) => MagnetHandler;
|
|
94
102
|
};
|
|
95
103
|
/**
|
|
96
104
|
* A SIMPLE cloth simulation. Goal is to have a minimal interface to just get some mesh to act kind of like a cloth.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SimpleCloth.d.ts","sourceRoot":"","sources":["../src/SimpleCloth.ts"],"names":[],"mappings":"AAAA,OAAO,EAIH,QAAQ,EAGR,QAAQ,EAGR,OAAO,EACP,KAAK,WAAW,EACnB,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"SimpleCloth.d.ts","sourceRoot":"","sources":["../src/SimpleCloth.ts"],"names":[],"mappings":"AAAA,OAAO,EAIH,QAAQ,EAGR,QAAQ,EAGR,OAAO,EACP,KAAK,WAAW,EACnB,MAAM,OAAO,CAAC;AA0Bf,OAAO,EAEH,IAAI,EACJ,YAAY,EAEZ,KAAK,cAAc,EACtB,MAAM,cAAc,CAAC;AAItB,KAAK,QAAQ,GAAG;IACf,MAAM,EAAC,MAAM,CAAC;IACd,QAAQ,EAAC,QAAQ,GAAC,OAAO,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACvB;;OAEG;IACH,MAAM,EAAC,YAAY,CAAA;IAEnB;;OAEG;IACH,cAAc,EAAC,CAAC,CAAC,EAAC,MAAM,EAAE,CAAC,EAAC,MAAM,EAAE,CAAC,EAAC,MAAM,KAAK,IAAI,CAAA;IAErD;;OAEG;IACH,UAAU,EAAC,YAAY,CAAA;CACvB,CAAA;AAiLD,KAAK,WAAW,GAAG;IAElB;;OAEG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,QAAQ,CAAC;IAEzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;;OAIG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAElC;;OAEG;IACH,aAAa,CAAC,EAAC,OAAO,CAAA;IAEtB;;OAEG;IACH,gBAAgB,CAAC,EAAC,OAAO,CAAA;IAEzB;;OAEG;IACH,OAAO,EAAC,MAAM,CAAC;IAEf;;OAEG;IACH,gBAAgB,CAAC,EAAC,MAAM,CAAC;IAEzB;;;;;;;OAOG;IACH,cAAc,EAAE,CAAE,IAAI,EAAC,QAAQ,EAAE,YAAY,EAAC,IAAI,EAAE,UAAU,EAAC,IAAI,KAAM,YAAY,CAAA;CACrF,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC3B,MAAM,EAAC,MAAI,IAAI,CAAC;IAChB,cAAc,EAAC,CAAC,CAAC,EAAC,MAAM,EAAE,CAAC,EAAC,MAAM,EAAE,CAAC,EAAC,MAAM,KAAG,IAAI,CAAC;IACpD,UAAU,EAAC,MAAI,IAAI,CAAC;CACpB,CAAA;AAiHD,iBAAS,uBAAuB,CAC5B,IAAI,EAAE,WAAW,EACjB,QAAQ,EAAE,cAAc,EAC3B,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC;;;;;;;;IA6gB5B;;;;;OAKG;oBACmB,MAAM;IAgC5B;;;;;;;;;OASG;kCAC2B,MAAM,YAAW,OAAO,GAAC,QAAQ,wBAgDzD,aAAa;EAGpB;AAGD;;;;GAIG;AACH,qBAAa,WAAW;IAEvB;;;;;;;;;OASG;IACH,MAAM,CAAC,aAAa,iCAA2B;CAC/C"}
|
package/dist/index.js
CHANGED
|
@@ -1,86 +1,86 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
import { Vector3 as
|
|
5
|
-
import { uniform as
|
|
6
|
-
import { StorageInstancedBufferAttribute as
|
|
7
|
-
const
|
|
8
|
-
function
|
|
9
|
-
const
|
|
10
|
-
let
|
|
11
|
-
const
|
|
12
|
-
for (let
|
|
13
|
-
const c =
|
|
14
|
-
|
|
1
|
+
var kt = Object.defineProperty;
|
|
2
|
+
var Dt = (t, a, u) => a in t ? kt(t, a, { enumerable: !0, configurable: !0, writable: !0, value: u }) : t[a] = u;
|
|
3
|
+
var dt = (t, a, u) => Dt(t, typeof a != "symbol" ? a + "" : a, u);
|
|
4
|
+
import { Vector3 as I, BufferAttribute as pt, Matrix4 as qt, DoubleSide as Ct, Scene as Et } from "three";
|
|
5
|
+
import { uniform as T, instancedArray as z, objectWorldMatrix as Rt, computeSkinning as Bt, storage as Lt, Fn as N, If as w, instanceIndex as p, Return as O, vec3 as bt, mix as Yt, select as et, uint as gt, bitcast as ft, Loop as ht, triNoise3D as $t, time as jt, float as Xt, attribute as xt, cross as Zt, transformNormalToView as Gt, frontFacing as Ot } from "three/tsl";
|
|
6
|
+
import { StorageInstancedBufferAttribute as _t, MeshPhysicalNodeMaterial as Ht } from "three/webgpu";
|
|
7
|
+
const X = new I();
|
|
8
|
+
function Jt(t, a = "color") {
|
|
9
|
+
const u = t.geometry, n = u.attributes.position, d = u.getAttribute(a), g = n.count, l = /* @__PURE__ */ new Map(), A = /* @__PURE__ */ new Map(), f = new Uint32Array(g), _ = [], q = [], C = [];
|
|
10
|
+
let k = 0;
|
|
11
|
+
const U = new I();
|
|
12
|
+
for (let o = 0; o < g; o++) {
|
|
13
|
+
const c = n.getX(o), v = n.getY(o), P = n.getZ(o), y = d ? d.getY(o) : 0, W = `${c},${v},${P}`;
|
|
14
|
+
l.has(W) || (l.set(W, k), U.set(c, v, P), U.applyMatrix4(t.matrixWorld), _.push(U.x, U.y, U.z, y), k++), f[o] = l.get(W);
|
|
15
15
|
}
|
|
16
|
-
const
|
|
17
|
-
new
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
},
|
|
21
|
-
const v =
|
|
22
|
-
if (
|
|
16
|
+
const E = [];
|
|
17
|
+
new I(), new I();
|
|
18
|
+
const R = /* @__PURE__ */ new Set(), D = (o, c) => {
|
|
19
|
+
A.has(o) ? A.get(o).push(c) : A.set(o, [c]);
|
|
20
|
+
}, Z = (o, c) => {
|
|
21
|
+
const v = o + "-" + c, P = c + "-" + o;
|
|
22
|
+
if (R.has(v) || R.has(P))
|
|
23
23
|
return;
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
},
|
|
27
|
-
for (let
|
|
28
|
-
const c =
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
24
|
+
const y = E.length / 4;
|
|
25
|
+
E.push(o, c, 0, 0), D(o, y), D(c, y), R.add(v);
|
|
26
|
+
}, V = u.index.array;
|
|
27
|
+
for (let o = 0; o < V.length; o += 3) {
|
|
28
|
+
const c = f[V[o]], v = f[V[o + 1]], P = f[V[o + 2]];
|
|
29
|
+
Z(c, v), Z(P, v), Z(c, P);
|
|
30
|
+
const y = C.length;
|
|
31
|
+
C.push([c, v, P]), q[V[o]] = y, q[V[o + 1]] = y, q[V[o + 2]] = y;
|
|
32
32
|
}
|
|
33
|
-
const
|
|
34
|
-
for (let
|
|
35
|
-
|
|
36
|
-
const c =
|
|
33
|
+
const B = [], h = new Uint32Array(k);
|
|
34
|
+
for (let o = 0; o < k; o++) {
|
|
35
|
+
h[o] = B.length;
|
|
36
|
+
const c = A.get(o) || [];
|
|
37
37
|
if (c.length == 0)
|
|
38
38
|
debugger;
|
|
39
|
-
|
|
39
|
+
B.push(c.length, ...c);
|
|
40
40
|
}
|
|
41
41
|
return {
|
|
42
|
-
vFaces: new Uint32Array(
|
|
42
|
+
vFaces: new Uint32Array(C.flat()),
|
|
43
43
|
//vec3[]
|
|
44
|
-
vVertexToFace: new Uint32Array(
|
|
44
|
+
vVertexToFace: new Uint32Array(q),
|
|
45
45
|
// uint
|
|
46
|
-
springsPointer: new Uint32Array(
|
|
47
|
-
springsPerVertexArray: new Uint32Array(
|
|
48
|
-
uniqueCount:
|
|
46
|
+
springsPointer: new Uint32Array(h),
|
|
47
|
+
springsPerVertexArray: new Uint32Array(B),
|
|
48
|
+
uniqueCount: k,
|
|
49
49
|
// unique vertices
|
|
50
|
-
indices:
|
|
50
|
+
indices: f,
|
|
51
51
|
// for each vertice, stores the index of the unique one in the vPos array
|
|
52
|
-
vPos: new Float32Array(
|
|
52
|
+
vPos: new Float32Array(_),
|
|
53
53
|
// 4 components, a vector 3 positions of the unique vertices ( in world space ) + the weight
|
|
54
|
-
springs: new Uint32Array(
|
|
54
|
+
springs: new Uint32Array(E)
|
|
55
55
|
// Pair of index points in vPos representing a spring
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
|
-
function
|
|
59
|
-
const
|
|
58
|
+
function Kt(t, a, u = 1) {
|
|
59
|
+
const n = [];
|
|
60
60
|
let d;
|
|
61
61
|
return t.traverseAncestors((g) => {
|
|
62
|
-
g instanceof
|
|
62
|
+
g instanceof Et && (d = g);
|
|
63
63
|
}), t.traverse((g) => {
|
|
64
|
-
(g.userData.stickto || g.userData.clothCollider) &&
|
|
64
|
+
(g.userData.stickto || g.userData.clothCollider) && n.push({
|
|
65
65
|
position: g,
|
|
66
66
|
radius: 0
|
|
67
67
|
});
|
|
68
|
-
}),
|
|
69
|
-
var
|
|
70
|
-
const
|
|
71
|
-
let
|
|
72
|
-
if (
|
|
73
|
-
throw new Error("Bone not found for collider: " +
|
|
74
|
-
d.attach(
|
|
75
|
-
}),
|
|
68
|
+
}), n.forEach((g) => {
|
|
69
|
+
var f;
|
|
70
|
+
const l = g.position;
|
|
71
|
+
let A;
|
|
72
|
+
if (l.userData.stickto && (A = (f = a.getBoneByName) == null ? void 0 : f.call(a, l.userData.stickto.replaceAll(/[\.\:]/g, "")), !A))
|
|
73
|
+
throw new Error("Bone not found for collider: " + l.userData.stickto) + " ???";
|
|
74
|
+
d.attach(l), g.radius = Math.abs(l.getWorldScale(X).x) * u, A == null || A.attach(l);
|
|
75
|
+
}), n;
|
|
76
76
|
}
|
|
77
|
-
function
|
|
77
|
+
function Qt(t, a, u) {
|
|
78
78
|
if ("isNodeMaterial" in t) {
|
|
79
|
-
const
|
|
80
|
-
return
|
|
79
|
+
const n = t;
|
|
80
|
+
return n.positionNode = a, n.normalNode = u, n;
|
|
81
81
|
} else {
|
|
82
|
-
const
|
|
83
|
-
side:
|
|
82
|
+
const n = new Ht({
|
|
83
|
+
side: Ct,
|
|
84
84
|
// wireframe:true,
|
|
85
85
|
wireframe: t.wireframe,
|
|
86
86
|
flatShading: t.flatShading,
|
|
@@ -88,151 +88,163 @@ function _t(t, i, l) {
|
|
|
88
88
|
depthWrite: t.depthWrite,
|
|
89
89
|
depthTest: t.depthTest
|
|
90
90
|
});
|
|
91
|
-
return
|
|
91
|
+
return n.positionNode = a, n.normalNode = u, n.color = t.color, n.map = t.map, n.emissive = t.emissive, n.emissiveMap = t.emissiveMap, n.emissiveIntensity = t.emissiveIntensity, n.roughness = t.roughness, n.roughnessMap = t.roughnessMap, n.metalness = t.metalness, n.metalnessMap = t.metalnessMap, n.normalMap = t.normalMap, n.normalScale = t.normalScale, n.alphaMap = t.alphaMap, n.opacity = t.opacity, n.transparent = t.transparent, n.aoMap = t.aoMap, n;
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
|
-
function
|
|
94
|
+
function te(t, a, u) {
|
|
95
95
|
t.updateWorldMatrix(!0, !1);
|
|
96
|
-
const
|
|
96
|
+
const n = {
|
|
97
97
|
colorAttributeName: "color",
|
|
98
98
|
logStats: !1,
|
|
99
|
-
stiffness: 0.
|
|
100
|
-
dampening: 0.
|
|
99
|
+
stiffness: 0.2,
|
|
100
|
+
dampening: 0.96,
|
|
101
101
|
colliderRadiusMultiplier: 1,
|
|
102
|
-
windPerSecond: new
|
|
103
|
-
gravityPerSecond: new
|
|
104
|
-
updateMaterial:
|
|
102
|
+
windPerSecond: new I(0, 0, 0),
|
|
103
|
+
gravityPerSecond: new I(0, -9.8, 0),
|
|
104
|
+
updateMaterial: Qt,
|
|
105
105
|
magnets: 0,
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
maskMultiplier: 1,
|
|
107
|
+
teleportDistance: 2,
|
|
108
|
+
...u
|
|
109
|
+
}, d = u != null && u.collidersRoot ? Kt(u.collidersRoot, t.skeleton, n.colliderRadiusMultiplier) : [], {
|
|
108
110
|
indices: g,
|
|
109
|
-
uniqueCount:
|
|
110
|
-
vPos:
|
|
111
|
-
springs:
|
|
112
|
-
springsPointer:
|
|
113
|
-
springsPerVertexArray:
|
|
114
|
-
vFaces:
|
|
115
|
-
vVertexToFace:
|
|
116
|
-
} =
|
|
117
|
-
if (
|
|
118
|
-
console.group("Stats"), console.log("vertices",
|
|
119
|
-
for (let e = 0; e <
|
|
120
|
-
let
|
|
121
|
-
for (let r = 0; r <
|
|
122
|
-
if (
|
|
123
|
-
|
|
111
|
+
uniqueCount: l,
|
|
112
|
+
vPos: A,
|
|
113
|
+
springs: f,
|
|
114
|
+
springsPointer: _,
|
|
115
|
+
springsPerVertexArray: q,
|
|
116
|
+
vFaces: C,
|
|
117
|
+
vVertexToFace: k
|
|
118
|
+
} = Jt(t, n.colorAttributeName);
|
|
119
|
+
if (n.logStats) {
|
|
120
|
+
console.group("Stats"), console.log("vertices", l), console.log("edges", f.length / 4), console.log("faces", C.length / 3);
|
|
121
|
+
for (let e = 0; e < l; e++) {
|
|
122
|
+
let s = !1;
|
|
123
|
+
for (let r = 0; r < f.length; r += 4)
|
|
124
|
+
if (f[r] === e || f[r + 1] === e) {
|
|
125
|
+
s = !0;
|
|
124
126
|
break;
|
|
125
127
|
}
|
|
126
|
-
|
|
128
|
+
s || console.log("WARNING!: vertex", e, "has no strings! wtf?");
|
|
127
129
|
}
|
|
128
130
|
console.groupEnd();
|
|
129
131
|
}
|
|
130
|
-
t.geometry.setAttribute("uniqueIndex", new
|
|
131
|
-
const
|
|
132
|
-
for (let e = 0; e <
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
|
|
132
|
+
t.geometry.setAttribute("uniqueIndex", new pt(g, 1)), t.geometry.setAttribute("faceIndex", new pt(k, 1));
|
|
133
|
+
const U = T(n.stiffness), E = T(n.dampening), R = T(n.maskMultiplier), D = T(0), Z = T(n.teleportDistance), V = T(n.gravityPerSecond, "vec3"), B = T(n.windPerSecond, "vec3"), h = z(A, "vec4"), o = z(l, "vec3"), c = new Uint32Array(l * 2);
|
|
134
|
+
for (let e = 0; e < l; e++)
|
|
135
|
+
c[e * 2] = _[e], c[e * 2 + 1] = 0;
|
|
136
|
+
const v = z(c, "uvec2"), P = z(
|
|
137
|
+
q,
|
|
136
138
|
"uint"
|
|
137
|
-
),
|
|
138
|
-
|
|
139
|
-
const e =
|
|
140
|
-
|
|
141
|
-
})().compute(
|
|
142
|
-
|
|
143
|
-
const e =
|
|
144
|
-
|
|
145
|
-
})().compute(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
139
|
+
), y = f.length / 4, W = g.length, K = z(f, "uvec4"), nt = z(y * 3, "vec3"), vt = z(C, "uvec3"), st = z(g, "uint"), L = z(Math.max(1, n.magnets), "vec4"), b = T(0, "uint"), ot = Rt(t), rt = Bt(t), yt = new Float32Array(Math.max(1, (d == null ? void 0 : d.length) ?? 0) * 4), Q = new _t(yt, 4), it = Lt(Q, "vec4"), H = T(new qt()), Mt = N(() => {
|
|
140
|
+
w(p.greaterThanEqual(W), () => O());
|
|
141
|
+
const e = st.element(p), s = h.element(e), r = o.element(e), i = ot.mul(rt);
|
|
142
|
+
s.xyz.assign(i), r.assign(bt(0, 0, 0));
|
|
143
|
+
})().compute(W).setName("Initialize skinning points"), St = N(() => {
|
|
144
|
+
w(p.greaterThanEqual(W), () => O());
|
|
145
|
+
const e = st.element(p), s = h.element(e), r = s.w.mul(R).toVar(), i = ot.mul(rt), x = s.xyz.sub(i).length().greaterThan(Z);
|
|
146
|
+
s.xyz.assign(Yt(s.xyz, i, et(x, 1, r)));
|
|
147
|
+
})().compute(W).setName("Update skinning points"), wt = N(() => {
|
|
148
|
+
w(p.greaterThanEqual(gt(y)), () => O());
|
|
149
|
+
const e = K.element(p), s = ft(e.z, "float"), r = h.element(e.x), m = h.element(e.y).sub(r).toVar(), x = m.length().max(1e-6).toVar(), S = x.sub(s).mul(U).mul(m).mul(0.5).div(x);
|
|
150
|
+
nt.element(p).assign(S);
|
|
151
|
+
})().compute(y).setName("computeSpringForces"), At = N(() => {
|
|
152
|
+
w(p.greaterThanEqual(l), () => O());
|
|
153
|
+
const e = h.element(p), s = e.xyz, r = e.w.oneMinus().mul(R);
|
|
154
|
+
w(r.equal(0), () => O());
|
|
155
|
+
const i = o.element(p);
|
|
156
|
+
i.mulAssign(E);
|
|
157
|
+
const { x: m, y: x } = v.element(p), S = P.element(m), M = m.add(1).toVar("ptrStart"), G = M.add(S).toVar("ptrEnd");
|
|
158
|
+
ht({ start: M, end: G, type: "uint", condition: "<" }, ({ i: F }) => {
|
|
159
|
+
const Y = P.element(F).toVar("springId"), $ = K.element(Y).toVar(), j = nt.element(Y), J = et($.x.equal(p), 1, -1);
|
|
160
|
+
i.addAssign(j.mul(J));
|
|
161
|
+
}), i.addAssign(V.mul(D).mul(D));
|
|
162
|
+
const It = $t(s, 1, jt).sub(0.2).mul(0.1).mul(B);
|
|
163
|
+
if (i.addAssign(It.mul(D)), d)
|
|
164
|
+
for (let F = 0; F < d.length; F++) {
|
|
165
|
+
const Y = it.element(F).xyz, $ = it.element(F).w, j = s.add(i).sub(Y), J = j.length(), Wt = $.sub(J).max(0).mul(j).div(J);
|
|
166
|
+
i.addAssign(Wt.mul(0.5));
|
|
162
167
|
}
|
|
163
|
-
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
168
|
+
i.mulAssign(r);
|
|
169
|
+
const Ut = i.length(), mt = 0.01;
|
|
170
|
+
w(Ut.greaterThan(mt), () => {
|
|
171
|
+
i.assign(i.normalize().mul(mt));
|
|
172
|
+
}), o.element(p).assign(i), w(x.greaterThan(0), () => {
|
|
173
|
+
const F = L.element(x.sub(1)), Y = F.xyz, $ = F.w;
|
|
174
|
+
w($.greaterThan(0), () => {
|
|
175
|
+
const j = Y.sub(s).mul($);
|
|
176
|
+
s.addAssign(j);
|
|
170
177
|
});
|
|
171
178
|
}).Else(() => {
|
|
172
|
-
|
|
179
|
+
s.addAssign(i);
|
|
173
180
|
});
|
|
174
|
-
})().compute(
|
|
175
|
-
|
|
176
|
-
const e =
|
|
177
|
-
|
|
181
|
+
})().compute(l), Pt = N(() => {
|
|
182
|
+
w(p.lessThan(y), () => {
|
|
183
|
+
const e = K.element(p), s = e.z, r = e.x, i = e.y, m = h.element(r).xyz, M = h.element(i).xyz.sub(m).length().max(1e-6);
|
|
184
|
+
s.assign(ft(M, "uint"));
|
|
178
185
|
});
|
|
179
|
-
})().compute(
|
|
180
|
-
const e =
|
|
181
|
-
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
r.assign(
|
|
186
|
+
})().compute(y).setName("calculate Rest Lengths"), Vt = N(() => {
|
|
187
|
+
const e = L.element(b).xyz, s = gt(0).toVar(), r = Xt(1e4).toVar("minDist");
|
|
188
|
+
ht(l, ({ i }) => {
|
|
189
|
+
const S = h.element(i).xyz.sub(e).length(), M = v.element(i).y;
|
|
190
|
+
w(M.equal(0).and(S.lessThan(r)), () => {
|
|
191
|
+
r.assign(S), s.assign(i);
|
|
185
192
|
});
|
|
186
|
-
}),
|
|
187
|
-
|
|
193
|
+
}), w(r.lessThan(1e4), () => {
|
|
194
|
+
v.element(s).y.assign(b.add(1));
|
|
188
195
|
});
|
|
189
|
-
})().compute(1).setName("assign Vertices To Magnet"),
|
|
190
|
-
|
|
191
|
-
const e =
|
|
192
|
-
|
|
196
|
+
})().compute(1).setName("assign Vertices To Magnet"), Ft = N(() => {
|
|
197
|
+
w(p.lessThan(l), () => {
|
|
198
|
+
const e = v.element(p);
|
|
199
|
+
w(e.y.equal(b.toUint().add(1)), () => {
|
|
193
200
|
e.y.assign(0);
|
|
194
201
|
});
|
|
195
202
|
});
|
|
196
|
-
})().compute(
|
|
197
|
-
const e = xt("uniqueIndex", "uint"),
|
|
198
|
-
return
|
|
203
|
+
})().compute(l).setName("remove Vertices From Magnet"), Tt = N(() => {
|
|
204
|
+
const e = xt("uniqueIndex", "uint"), s = h.element(e).xyz;
|
|
205
|
+
return H.mul(s);
|
|
199
206
|
})();
|
|
200
207
|
t.isSkinnedMesh = !1;
|
|
201
|
-
const
|
|
202
|
-
const e = xt("faceIndex", "uint"),
|
|
203
|
-
return
|
|
204
|
-
})().toVarying(),
|
|
208
|
+
const at = N(() => {
|
|
209
|
+
const e = xt("faceIndex", "uint"), s = vt.element(e), r = h.element(s.x).toVar(), i = h.element(s.y).toVar(), m = h.element(s.z).toVar(), x = i.sub(r), S = m.sub(r), M = Zt(x, S).normalize(), G = H.transformDirection(M);
|
|
210
|
+
return Gt(G);
|
|
211
|
+
})().toVarying(), zt = et(Ot, at, at.negate()), Nt = () => {
|
|
205
212
|
if (!(d != null && d.length))
|
|
206
213
|
return;
|
|
207
|
-
const e =
|
|
208
|
-
for (let
|
|
209
|
-
const r = d[
|
|
210
|
-
r.position instanceof
|
|
214
|
+
const e = Q.array;
|
|
215
|
+
for (let s = 0; s < d.length; s++) {
|
|
216
|
+
const r = d[s];
|
|
217
|
+
r.position instanceof I ? X.copy(r.position) : (r.position.updateMatrixWorld(!0), r.position.getWorldPosition(X)), e[s * 4] = X.x, e[s * 4 + 1] = X.y, e[s * 4 + 2] = X.z, e[s * 4 + 3] = r.radius;
|
|
211
218
|
}
|
|
212
|
-
|
|
219
|
+
Q.needsUpdate = !0;
|
|
213
220
|
};
|
|
214
|
-
t.material =
|
|
215
|
-
let
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
221
|
+
t.material = n.updateMaterial(t.material, Tt, zt), H.value.copy(t.matrixWorld).invert();
|
|
222
|
+
let ct = !1;
|
|
223
|
+
const lt = () => a.compute(Mt);
|
|
224
|
+
requestAnimationFrame(() => {
|
|
225
|
+
lt(), a.compute(Pt), ct = !0;
|
|
226
|
+
});
|
|
227
|
+
let tt = 0;
|
|
228
|
+
return {
|
|
229
|
+
stiffnessUniform: U,
|
|
230
|
+
dampeningUniform: E,
|
|
231
|
+
gravityUniform: V,
|
|
232
|
+
windUniform: B,
|
|
223
233
|
colliders: d,
|
|
234
|
+
mesh: t,
|
|
235
|
+
reset: lt,
|
|
224
236
|
/**
|
|
225
237
|
* Runs the cloth simulation...
|
|
226
238
|
*
|
|
227
239
|
* @param delta seconds passed since last render
|
|
228
240
|
* @param steps number of steps to run the simulation ( more steps = more "stable" but slower )
|
|
229
241
|
*/
|
|
230
|
-
update: (e,
|
|
231
|
-
if (
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
242
|
+
update: (e, s = 360) => {
|
|
243
|
+
if (!ct) return;
|
|
244
|
+
t.updateMatrixWorld(), H.value.copy(t.matrixWorld).invert(), Nt();
|
|
245
|
+
const r = Math.min(e, 1 / 60), m = 1 / s;
|
|
246
|
+
for (D.value = m, tt += r, a.compute(St); tt >= m; )
|
|
247
|
+
tt -= m, a.compute(wt), a.compute(At);
|
|
236
248
|
},
|
|
237
249
|
/**
|
|
238
250
|
* Grab the closest vertex to the given magnet. An object to update the magnet position is returned.
|
|
@@ -244,26 +256,26 @@ function Ht(t, i, l) {
|
|
|
244
256
|
* @param strength Strength of the magnet. Default is .5.
|
|
245
257
|
* @returns An object with update and release methods.
|
|
246
258
|
*/
|
|
247
|
-
activateMagnet: (e,
|
|
248
|
-
const
|
|
249
|
-
|
|
259
|
+
activateMagnet: (e, s, r = 1) => {
|
|
260
|
+
const i = new I(), m = () => (s instanceof I ? i.copy(s) : s.getWorldPosition(i), i), x = (M, G, ut) => {
|
|
261
|
+
L.value.setXYZW(e, M, G, ut, r), L.value.needsUpdate = !0;
|
|
250
262
|
};
|
|
251
|
-
t.updateMatrixWorld(!0),
|
|
252
|
-
const
|
|
253
|
-
return
|
|
263
|
+
t.updateMatrixWorld(!0), b.value = e, b.needsUpdate = !0;
|
|
264
|
+
const S = m();
|
|
265
|
+
return x(S.x, S.y, S.z), a.compute(Vt), {
|
|
254
266
|
update: () => {
|
|
255
|
-
const
|
|
256
|
-
|
|
267
|
+
const M = m();
|
|
268
|
+
x(M.x, M.y, M.z);
|
|
257
269
|
},
|
|
258
|
-
updatePosition:
|
|
270
|
+
updatePosition: x,
|
|
259
271
|
deactivate: () => {
|
|
260
|
-
|
|
272
|
+
L.value.setXYZW(e, 0, 0, 0, 0), L.value.needsUpdate = !0, b.value = e, a.compute(Ft);
|
|
261
273
|
}
|
|
262
274
|
};
|
|
263
275
|
}
|
|
264
276
|
};
|
|
265
277
|
}
|
|
266
|
-
class
|
|
278
|
+
class ee {
|
|
267
279
|
}
|
|
268
280
|
/**
|
|
269
281
|
* Turns the vertex painted parts of a skinned mesh into a cloth simulation.
|
|
@@ -275,8 +287,8 @@ class Jt {
|
|
|
275
287
|
* @param options The options for the simulation.
|
|
276
288
|
* @returns The cloth simulation's API.
|
|
277
289
|
*/
|
|
278
|
-
|
|
290
|
+
dt(ee, "onSkinnedMesh", te);
|
|
279
291
|
export {
|
|
280
|
-
|
|
292
|
+
ee as SimpleCloth
|
|
281
293
|
};
|
|
282
294
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/SimpleCloth.ts"],"sourcesContent":["import {\n\tBone,\n BufferAttribute,\n DoubleSide,\n Material,\n Matrix4,\n Mesh,\n Object3D,\n Scene,\n Skeleton,\n Vector3,\n type SkinnedMesh,\n} from \"three\";\nimport { ShaderCallNodeInternal } from \"three/src/nodes/TSL.js\";\nimport {\n attribute,\n computeSkinning,\n Fn,\n If,\n instancedArray,\n instanceIndex,\n Loop,\n mix,\n objectWorldMatrix,\n Return,\n select,\n storage,\n time,\n triNoise3D,\n uniform,\n float,\n\tfrontFacing,\n\tcross,\n\ttransformNormalToView,\n\tuint,\n\tbitcast,\n} from \"three/tsl\";\nimport {\n\tMeshPhysicalNodeMaterial,\n Node,\n NodeMaterial,\n StorageInstancedBufferAttribute,\n type WebGPURenderer,\n} from \"three/webgpu\";\n\nconst v = new Vector3();\n\ntype Collider = {\n\tradius:number,\n\tposition:Object3D|Vector3\n}\n\nexport type MagnetAPI = {\n\t/**\n\t * Updates the world position of the magnet by re-reading the original vector 3 passed as position\n\t */\n\tupdate:VoidFunction\n\n\t/**\n\t * Updates the world position of the magnet by setting it to these values\n\t */\n\tupdatePosition:(x:number, y:number, z:number) => void \n\n\t/**\n\t * clears every vertex from this magnet. This is the \"release\" action.\n\t */\n\tdeactivate:VoidFunction\n}\n\n// function hash3(a: number, b: number, c: number) {\n// const q = 1e6; // precision control\n// let x = (a * q) | 0;\n// let y = (b * q) | 0;\n// let z = (c * q) | 0;\n\n// let h = x * 374761393 + y * 668265263 + z * 2147483647;\n// h = (h ^ (h >> 13)) * 1274126177;\n// h = h ^ (h >> 16);\n\n// return h >>> 0;\n// } \n\n/**\n * Here we calculate the geometry of the cloth.\n * We need to know the unique vertices, the springs (edges), and the faces.\n * \n * @param mesh The mesh to calculate the geometry of.\n * @param $maskAttribute The attribute to use as a mask (default: \"color\").\n * @returns An object containing the information for the cloth\n */\nfunction calculateGeometry(mesh: Mesh, $maskAttribute = \"color\") {\n const geometry = mesh.geometry;\n const pos = geometry.attributes.position;\n\n\t/**\n\t * The vertex paint info. We assume you pain in red, and blender's default background is white. \n\t * So we will use the blue channel to determine the weight of the cloth.\n\t */\n const mask = geometry.getAttribute($maskAttribute);\n\n const count = pos.count;\n\n // Map of position string -> unique index\n const map = new Map<string, number>();\n const springPerVertex = new Map<number, number[]>();\n\n // Array to store the unique index for each vertex\n const indices = new Uint32Array(count);\n\n const vPos: number[] = [];\n \n const vVertexToFace: number[] = [];\n const vFaces: number[][] = [];\n\n let uniqueCount = 0;\n const v = new Vector3();\n\n //\n // identify unique vertices (because some may be repeated but actually the same spacial vertex)\n //\n for (let i = 0; i < count; i++) {\n const x = pos.getX(i);\n const y = pos.getY(i);\n const z = pos.getZ(i);\n // Defaults to 0 if mask is missing. \n // Logic: 1 = Pinned (Skin), 0 = Simulated (Cloth)\n const weight = mask ? mask.getY(i) : 0; \n\n // Create a unique key for the position\n const key = `${x},${y},${z}`;// hash3(x, y, z);\n\n if (!map.has(key)) {\n map.set(key, uniqueCount);\n\n //\n // convert to world space.\n //\n v.set(x, y, z); \n \n\t\t\tv.applyMatrix4(mesh.matrixWorld) \n\n vPos.push(v.x, v.y, v.z, weight);\n \n uniqueCount++;\n }\n\n indices[i] = map.get(key)!;\n }\n\n //\n // now, create the \"springs\" ( the connections between them )\n //\n const springs: number[] = []; // unique index id of a A-B vertex pair + rest length float + unused \n const vA = new Vector3();\n const vB = new Vector3();\n const springDefined = new Set<string>();\n\n const addSpringToVertex = (vertex: number, springID: number) => {\n if (springPerVertex.has(vertex)) {\n springPerVertex.get(vertex)!.push(springID);\n } else {\n springPerVertex.set(vertex, [springID]);\n }\n };\n\n const addSpring = (A: number, B: number) => {\n const hash = A + \"-\" + B; //(A * 73856093) ^ (B * 19349663);\n const hash2 = B + \"-\" + A; //(B * 73856093) ^ (A * 19349663);\n\n if (springDefined.has(hash) || springDefined.has(hash2)) {\n return;\n }\n\n const springID = springs.length/4;\n\n springs.push(A, B, 0, 0);\n\n addSpringToVertex(A, springID);\n addSpringToVertex(B, springID); \n\n springDefined.add(hash);\n };\n\n //\n // now for each triangle, create a spring from each edge...\n // \n const gIndices = geometry.index!.array;\n\n\n for (let i = 0; i < gIndices.length; i += 3) {\n const Ai = indices[gIndices[i]];\n const Bi = indices[gIndices[i + 1]];\n const Ci = indices[gIndices[i + 2]];\n\n //AB BC CA | rest length + force\n addSpring(Ai, Bi);\n addSpring( Ci, Bi,);\n addSpring( Ai , Ci, );\n\n // This is a face... make all indices point to these 3 indices...\n const faceIndex = vFaces.length;\n\n // Add the \"face\" to the face index\n vFaces.push([Ai, Bi, Ci]);\n\n\t\tvVertexToFace[gIndices[i]] = faceIndex;\n\t\tvVertexToFace[gIndices[i+1]] = faceIndex;\n\t\tvVertexToFace[gIndices[i+2]] = faceIndex;\n } \n\n //\n // build springs pointer array (adjacency list)\n // This allows O(1) lookup of springs for each vertex in the shader\n //\n const springsPerVertexArray: number[] = [];\n const springsPointer = new Uint32Array(uniqueCount);\n \n for (let i = 0; i < uniqueCount; i++) {\n springsPointer[i] = springsPerVertexArray.length;\n const springsForV = springPerVertex.get(i) || [];\n // Store count, then the spring IDs\n\t\tif( springsForV.length==0 )\n\t\t{\n\t\t\tdebugger\n\t\t}\n springsPerVertexArray.push(springsForV.length, ...springsForV);\n }\n \n\n return {\n vFaces: new Uint32Array(vFaces.flat()), //vec3[]\n vVertexToFace: new Uint32Array(vVertexToFace), // uint\n\n springsPointer: new Uint32Array(springsPointer),\n springsPerVertexArray: new Uint32Array(springsPerVertexArray),\n\n uniqueCount, // unique vertices\n indices, // for each vertice, stores the index of the unique one in the vPos array\n vPos: new Float32Array(vPos), // 4 components, a vector 3 positions of the unique vertices ( in world space ) + the weight\n\n springs: new Uint32Array(springs), // Pair of index points in vPos representing a spring \n };\n}\n\ntype ClothConfig = {\n\n\t/**\n\t * Usually it is \"color\" but sometimes it may be other like \"color_1\"\n\t */\n\tcolorAttributeName?: string;\n\n\t/**\n\t * Log stats to the console ( about the cloth mesh )\n\t */\n\tlogStats?: boolean;\n\n\t/**\n\t * The root object to search for colliders. \n\t * Colliders are expected to be empty objects with uniform scale and `userData.stickto=\"bonename\"` OR `userData.clothCollider=true` properties.\n\t * The radius of the collider will be the absolute value of `scale.x * colliderRadiusMultiplier`.\n\t */\n\tcollidersRoot?: Object3D;\n\n\tstiffness?: number;\n\tdampening?: number;\n\n\t/**\n\t * you can tweak the radius of the colliders ( which are spheres attached to bones )\n\t * \n\t * @param radius 1.0 is the default\n\t */\n\tcolliderRadiusMultiplier?: number; \n\n\t/**\n\t * Wind DIRECTION in world space (noise will be used to add variation)\n\t */\n\twindPerSecond?:Vector3\n\n\t/**\n\t * Gravity force in world space\n\t */\n\tgravityPerSecond?:Vector3 \n\n\t/**\n\t * How many magnets to use ( cloch grab manipulators )\n\t */\n\tmagnets:number;\n\n\t/**\n\t * The material used by the SkinnedMesh to turn into \"cloth\" must apply and use these TSL nodes...\n\t * By default it will try to guess the most common case when you import a model from blender...\n\t * \n\t * @param positionNode \n\t * @param normalNode \n\t * @returns \n\t */\n\tupdateMaterial: ( base:Material, positionNode:Node, normalNode:Node ) => NodeMaterial\n}\n \n\n/**\n * Looks for objects ( empty uniformlyscaled Spheres from blender ) with a userData.stickto = \"name of a bone\"\n * @param root \n * @returns \n */\nfunction getCollidersFrom( root:Object3D, skeleton:Skeleton, multiplier:number =1)\n{\n\tconst colliders:Collider[] = []; \n\tlet scene:Scene;\n\n\troot.traverseAncestors((o)=>{\n\t\tif( o instanceof Scene ){\n\t\t\tscene = o;\n\t\t}\n\t});\n\n\t//\n\t// collect colliders\n\t//\n\troot.traverse((o)=>{\n\t\tif( o.userData.stickto || o.userData.clothCollider )\n\t\t{\n\t\t\tcolliders.push({\n\t\t\t\tposition: o,\n\t\t\t\tradius: 0, \n\t\t\t});\n\t\t}\n\t}); \n\n\t//\n\t// attatch to skeleton and calculate world dimension\n\t//\n\tcolliders.forEach( col => {\n\t\tconst obj = col.position as Object3D;\n\t\tlet bone :Bone|undefined;\n\t\t\n\t\tif( obj.userData.stickto )\n\t\t{\n\t\t\tbone = skeleton.getBoneByName?.( obj.userData.stickto.replaceAll(/[\\.\\:]/g,\"\") );\n\n\t\t\tif( !bone ){\n\t\t\t\tthrow new Error(\"Bone not found for collider: \" + obj.userData.stickto)+ \" ???\";\n\t\t\t} \n\t\t} \n\t\t\n\t\tscene.attach(obj);\n\t\t\n\t\t//\n\t\t// the world scale is the radius of the collider ( uniform scale assumed!! )\n\t\t//\n\t\tcol.radius = Math.abs( obj.getWorldScale(v).x ) * multiplier ;\n\t \n\n\t\tbone?.attach( obj ); \n\n\t} );\n\n\n\treturn colliders;\n}\n\nfunction assumeTheModelCameFromBlenderAndWasTexturedNormally(base:Material, posNode:ShaderCallNodeInternal, normalNode:ShaderCallNodeInternal ) {\n\tif( \"isNodeMaterial\" in base )\n\t{\n\t\tconst m = base as NodeMaterial;\n\t\tm.positionNode = posNode;\n\t\tm.normalNode = normalNode;\n\t\treturn m;\n\t}\n\telse \n\t{\n\t\tconst m = new MeshPhysicalNodeMaterial({ \n\t\t\tside: DoubleSide,\n\t\t\t// wireframe:true, \n\t\t\twireframe: base.wireframe,\n\t\t\tflatShading: base.flatShading,\n\t\t\ttransparent: base.transparent,\n\t\t\tdepthWrite: base.depthWrite,\n\t\t\tdepthTest: base.depthTest,\n\t\t});\n\t\tm.positionNode = posNode;\n\t\tm.normalNode = normalNode;\n\n\t\tm.color = base.color;\n\t\tm.map = base.map;\n\n\t\tm.emissive = base.emissive;\n\t\tm.emissiveMap = base.emissiveMap;\n\t\tm.emissiveIntensity = base.emissiveIntensity;\n\n\t\tm.roughness = base.roughness;\n\t\tm.roughnessMap = base.roughnessMap;\n\n\t\tm.metalness = base.metalness;\n\t\tm.metalnessMap = base.metalnessMap;\n\n\t\tm.normalMap = base.normalMap;\n\t\tm.normalScale = base.normalScale;\n\n\t\tm.alphaMap = base.alphaMap;\n\t\tm.opacity = base.opacity;\n\t\tm.transparent = base.transparent;\n\n\t\tm.aoMap = base.aoMap;\n\n\n\t\treturn m; \n\t} \n}\n\nfunction setupClothOnSkinnedMesh(\n mesh: SkinnedMesh,\n renderer: WebGPURenderer,\n\tconfig?: Partial<ClothConfig>, \n) {\n\tmesh.updateWorldMatrix(true, false);\n\n\t\n\tconst $cfg : ClothConfig = {\n\t\tcolorAttributeName: \"color\",\n\t\tlogStats: false,\n\t\tstiffness: 0.4,\n\t\tdampening: 0.5,\n\t\tcolliderRadiusMultiplier:1,\n\t\twindPerSecond: new Vector3(0,0,0),\n\t\tgravityPerSecond: new Vector3(0, -0.3,0), \n\t\tupdateMaterial: assumeTheModelCameFromBlenderAndWasTexturedNormally,\n\t\tmagnets: 0,\n\t\t...config\n\t}\n\n\t//\n\t// -- Look for colliders\n\t//\n\tconst colliders:Collider[] = config?.collidersRoot ? getCollidersFrom(config.collidersRoot, mesh.skeleton, $cfg.colliderRadiusMultiplier) : []\n \n const {\n indices,\n uniqueCount,\n vPos,\n springs, \n springsPointer,\n springsPerVertexArray,\n\t\tvFaces,\n\t\tvVertexToFace\n } = calculateGeometry(mesh, $cfg.colorAttributeName!);\n\n\tif( $cfg.logStats ){\n\t\tconsole.group(\"Stats\") \n\t\tconsole.log(\"vertices\", uniqueCount); \n\t\tconsole.log(\"edges\", springs.length/4); \n\t\tconsole.log(\"faces\", vFaces.length/3); \n\t\n\n\t\tfor(let i=0; i<uniqueCount; i++){\n\t\t\tlet hasString = false;\n\t\t\tfor( let j=0; j<springs.length; j+=4){\n\t\t\t\tif(springs[j] === i || springs[j+1] === i){\n\t\t\t\t\thasString = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(!hasString){\n\t\t\t\tconsole.log(\"WARNING!: vertex\", i, \"has no strings! wtf?\");\n\t\t\t}\n\t\t} \n\n\t\tconsole.groupEnd();\n\t}\n \n\t// for each vertex position, this will help us know which unique spatial vertex on the cloth this represents.\n mesh.geometry.setAttribute(\"uniqueIndex\", new BufferAttribute(indices, 1));\n\n\t// from what face this vertex is part of.\n mesh.geometry.setAttribute(\"faceIndex\", new BufferAttribute(vVertexToFace, 1));\n\t\t \n\n const stiffnessUniform = uniform($cfg.stiffness); \n const dampeningUniform = uniform($cfg.dampening); \n \n\t/**\n\t * Delta time (updated on .update)\n\t */\n const dt = uniform(0);\n\n\t/**\n\t * Gravity ( force acting on the cloth on every compute )\n\t */\n const gravityUniform = uniform( $cfg.gravityPerSecond, \"vec3\");\n\t\n\t/**\n\t * Wind (updated on .update)\n\t */\n const windUniform = uniform( $cfg.windPerSecond, \"vec3\"); \n\n\t/**\n\t * position of each unique spatial index XYZ and the W is the value of the vertex paint mask ( 1:skinned, ..., 0:physics )\n\t */\n const vPosStore = instancedArray(vPos, \"vec4\") ;\n\n\t/**\n\t * The force vector acting on each unique spatial index.\n\t */\n const vForceStore = instancedArray(uniqueCount, \"vec3\") ; \n\n\t/**\n\t * (I know this is messy...)\n\t * For each unique vertex, this is a pointer to the location in `vSpringsPerVertexArray` where the list of springs for that vertex begins.\n\t * It starts with \"count\" followed by the IDs of each spring in the `springsStore` array\n\t * --\n\t * .x = pointer to the location in `vSpringsPerVertexArray` where the list of springs for that vertex begins.\n\t * .y = magnet index\n\t */\n\tconst vVertexIntsArray = new Uint32Array( uniqueCount * 2 );\n\tfor(let i=0; i<uniqueCount; i++){\n\t\tvVertexIntsArray[i*2] = springsPointer[i];\n\t\tvVertexIntsArray[i*2+1] = 0;\n\t}\n\n\t/**\n\t * 2 Ids packed. \n\t * .x = pointer to the location in `vSpringsPerVertexArray` where the list of springs for that vertex begins.\n\t * .y = magnet index\n\t */\n const vVertexInts = instancedArray(vVertexIntsArray, \"uvec2\");\n\n\t/**\n\t * Contains [ count, ID1, ID2, ID3, count, ID1, ID2, count, ID etc... ]\n\t * Count then a serie of IDs\n\t */\n const vSpringsPerVertexArray = instancedArray(\n springsPerVertexArray,\n \"uint\",\n );\n\n\t/**\n\t * How many springs (edges) the cloth has. It will equal the number of edges of the mesh.\n\t */\n const totalSprings = springs.length / 4; // because each spring has 2 vertices + 1 rest length + 1 unused\n\n\t/**\n\t * Total number of vertices in the mesh (not the unique vertices, the actual mesh like the one you see in Blender's stats)\n\t */\n const countOfPoints = indices.length;\n\n\t/**\n\t * Stores:\n\t * XY: a pair of A,B IDs. (id of the unique vertices that this spring will try to keep at a certain distance )\n\t * Z: rest length of the spring ( in world space )\n\t * W: unused\n\t */\n const springsStore = instancedArray(springs, \"uvec4\"); \n\n\t/**\n\t * array triplets defining a face ( a triangle )\n\t */\n\tconst vFacesStore = instancedArray(vFaces, \"uvec3\"); \n \n\t/**\n\t * basically a map from each mesh's vertex to the ID of the unique spatial vertlet. ( since many vertices may exist in the space spatial position )\n\t */\n const indicesStore = instancedArray(indices, \"uint\");\n\n\n\tconst magnet = instancedArray(Math.max(1, $cfg.magnets), \"vec4\"); \n\tconst magnetToAssign = uniform(0,\"uint\");\n\n\n const worldMatrix = objectWorldMatrix(mesh);\n\n\t/**\n\t * This puppy is the node that runs the skinning process setting the positions of the vertices to match the skeleton.\n\t */\n const skinningPosition = computeSkinning(mesh);\n\n\t/**\n\t * Position XYZ and Radius W\n\t */\n\tconst collidersArray = new Float32Array(Math.max(1, colliders?.length ?? 0) * 4);\n\tconst colliderAttr = new StorageInstancedBufferAttribute(collidersArray, 4);\n\tconst collidersStore = storage(colliderAttr, \"vec4\"); \n\n\tconst worldMatrixInverseUniform = uniform(new Matrix4());\n\n /** \n * Initial setup\n */\n const initializeSkinningPosition = Fn(() => {\n If(instanceIndex.greaterThanEqual(countOfPoints), () => Return());\n\n const uIndex = indicesStore.element(instanceIndex);\n const wPos = vPosStore.element(uIndex); \n \n const skinningWorldPosition = worldMatrix.mul(skinningPosition) ;\n wPos.xyz.assign(skinningWorldPosition) ; \n\n })()\n .compute(countOfPoints)\n .setName(\"Initialize skinning points\"); \n\n /**\n * < SYNC TO SKINNED MESH >\n * Skinning --> unique vertices\n * Runs once per frame before physics steps.\n * Updates positions of pinned vertices to match the animation.\n */\n const updateSkinningPoints = Fn(() => {\n If(instanceIndex.greaterThanEqual(countOfPoints), () => Return());\n\n const uIndex = indicesStore.element(instanceIndex);\n const wPos = vPosStore.element(uIndex); \n const factor = wPos.w;//.pow(2); // 1 = skinned (Pinned), 0 = cloth (Simulated)\n\n const skinningWorldPosition = worldMatrix.mul(skinningPosition) ;\n\n // Only update if factor > 0 (partially or fully skinned)\n // If fully cloth (0), we leave it to physics\n // mix(currentPos, skinningPos, factor). If factor=1, we force it to skinningPos.\n\t \n\t\twPos.xyz.assign(mix(wPos.xyz, skinningWorldPosition, factor)); \n\n })()\n .compute(countOfPoints)\n .setName(\"Update skinning points\"); \n\n\n\t/**\n\t * < CALCULATE SPRING FORCES > \n\t * Calculates the force of each spring.\n * This iterates per SPRING (linear with number of springs).\n\t */\n \n\n /**\n * < COMPUTE VERTEX FORCES >\n * Integrates forces and updates position.\n * Iterates per VERTEX.\n */\n\tconst computeVertexForces = Fn(()=>{\n\n\t\tIf(instanceIndex.greaterThanEqual(uniqueCount), () => {\n\t\t\tReturn();\n\t\t});\n\n\t\tconst position = vPosStore.element( instanceIndex );\n\t\tconst force = vForceStore.element( instanceIndex ) ; \n\t\tconst mask = (position.w).oneMinus(); //.pow(2); // If w=1 (pinned), mask=0. If w=0 (simulated), mask=1.\n \n\t\n\t\n\t\tconst vertexInts = vVertexInts.element(instanceIndex);\n\t\tconst springPointer = vertexInts.x;\n\t\t\n // springsPerVertexArray layout: [count, id1, id2, ...]\n const springCount = vSpringsPerVertexArray.element(springPointer);\n\n const ptrStart = springPointer.add(1).toVar(\"ptrStart\");\n const ptrEnd = ptrStart.add(springCount).toVar(\"ptrEnd\");\n\n\t\tforce.mulAssign(dampeningUniform);\n\n\t\t\tLoop(\n\t { start: ptrStart, end: ptrEnd, type: \"uint\", condition: \"<\" },\n\t ({ i }) => {\n\t const springIndex = vSpringsPerVertexArray.element(i).toVar(\"springId\"); \n\t const spring = springsStore.element(springIndex).toVar();\n\t \n\t\t\t\t\t//\n\t\t\t\t\t// Inline spring force calculation\n\t\t\t\t\t//\n\t\t\t\t\tconst restLength = bitcast(spring.z, \"float\") ;\n\t\t\t\t\tconst Ai = spring.x;\n\t\t\t\t\tconst Bi = spring.y;\n\n\t\t\t\t\t// identify neighbor\n\t\t\t\t\tconst neighborIndex = select(Ai.equal(instanceIndex), Bi, Ai);\n\n\t\t\t\t\tconst posNeighbor = vPosStore.element(neighborIndex).xyz;\n\t\t\t\t\tconst delta = posNeighbor.sub(position.xyz);\n\t\t\t\t\tconst dist = delta.length().max(0.000001);\n\t\t\t\t\tconst dir = delta.div(dist);\n\t\t\t\t\t\n\t\t\t\t\t// Force\n\t\t\t\t\tconst f = dist.sub(restLength).mul(stiffnessUniform).mul(0.5);\n\t\t\t\t\t\n\t\t\t\t\tforce.addAssign(dir.mul(f));\n\t },\n\t );\n\n\t\t// // Wind\n const noise = triNoise3D(position, 1, time).sub(0.2).mul(0.1);\n const windForce = noise.mul(windUniform);\n force.addAssign(windForce);\n\n\t\t// Sphere collisions\n\t\tif (colliders) {\n\t\t\tfor (let i = 0; i < colliders.length; i++) {\n\t\t\t\tconst cPos = collidersStore.element(i).xyz;\n const cRad = float(collidersStore.element(i).w);\n \n // Vector from collider center to vertex\n\t\t\t\tconst deltaSphere = position.add(force).sub(cPos); \n\t\t\t\tconst dist = deltaSphere.length();\n \n\t\t\t\t\n\n // If inside sphere (dist < radius)\n\t\t\t\tconst sphereForce = cRad\n\t\t\t\t\t.sub(dist)\n\t\t\t\t\t.max(0)\n\t\t\t\t\t.mul(deltaSphere.normalize())\n\t\t\t\t\t.mul(3)\n // Push out\n\t\t\t\tforce.addAssign(sphereForce);\n\t\t\t}\n\t\t}\n\n\t\t//force.mulAssign(mask);\n\n\t\tforce.addAssign(gravityUniform.mul(mask).mul(dt ) ); \n\n\t\t//\n\t\t// Magnet \"attraction\"\n\t\t//\n\t\tconst magnetIndex = vVertexInts.element(instanceIndex).y;\n\n\t\tIf( magnetIndex.greaterThan(0), ()=>{\n\n\t\t\tconst magnetData = magnet.element(magnetIndex.sub(1)) ;\n\t\t\tconst magnetPosition = magnetData.xyz;\n\t\t\tconst magnetStrength = magnetData.w;\n\n\t\t\tIf( magnetStrength.greaterThan(0), ()=>{\n\t\t\t\tconst magnetForce = magnetPosition.sub(position.xyz).mul(magnetStrength) ;\n\t\t\t\tposition.xyz.addAssign(magnetForce);\n\t\t\t}) \n\n\t\t}) \n\t\t.Else(()=>{\n\t\t\tposition.xyz.addAssign( force );\n\t\t}) \n \n\t\t\n\t \n\t \t//force.assign( vec3(0,0,0) );\n \n\t\t\n\t})().compute( uniqueCount ).setName(\"compute Vertex Forces\"); \n\n\t/**\n\t * < CALCULATE REST LENGTHS>\n\t * the leght of each spring is calculated and stored in the Z component of the spring data.\n\t */\n\tconst calculateRestLengths = Fn(()=>{\n\t\tIf(instanceIndex.lessThan(totalSprings), () => {\n\t\t\tconst vertexIds = springsStore.element( instanceIndex );\n\t\t\tconst restLength = vertexIds.z ;\n\n\t\t\tconst Ai = vertexIds.x;\n\t\t\tconst Bi = vertexIds.y; \n\n\t\t\tconst posA = vPosStore.element(Ai).xyz;\n\t\t\tconst posB = vPosStore.element(Bi).xyz;\n\n\t\t\tconst delta = posB.sub(posA);\n\t\t\tconst dist = delta.length().max(0.000001);\n\n\t\t\trestLength.assign( bitcast(dist, \"uint\") );\n \n\n\t\t});\n\t})().compute( totalSprings ).setName(\"calculate Rest Lengths\"); \n\n\t/**\n\t * Scans all vertices and assigns the nearest vertex to the magnet.\n\t * Check the length of each spring and pick the closest one, then assign that one to the magnet.\n\t */\n\tconst assignVerticesToMagnet = Fn(() => { \n\n\t const magnetPosition = magnet.element(magnetToAssign).xyz;\n\t const selectedIndex = uint(0).toVar();\n\t const minDist = float(10000).toVar(\"minDist\"); // sentinel — no special casing needed\n\n\t\tLoop( uniqueCount, ({ i })=>{\n\n\t\t\tconst vertlet = vPosStore.element(i) ; \n\t\t\tconst position = vertlet.xyz ; \n\t\t\tconst dist = position.sub(magnetPosition).length() ; \n\t\t\tconst currentMagnet = vVertexInts.element(i).y ;\n\n\t\t\t// If it already has a magnet, ignore it.\n\t\t\tIf( currentMagnet.equal(0).and( dist.lessThan(minDist) ) , ()=>{\n\t\t\t\tminDist.assign(dist);\n\t\t\t\tselectedIndex.assign(i); \n\t\t\t})\n\t\t\t\n\t\t}) ;\n\n\t\tIf( minDist.lessThan(10000), () => {\n\t\t\tconst ids = vVertexInts.element(selectedIndex);\n\t\t\tids.y.assign(magnetToAssign.add(1));\n\t\t}) \n\n\t})().compute(1).setName(\"assign Vertices To Magnet\"); \n\n\t/**\n\t * Removes vertices from the magnet.\n\t * Check the y component of the vertex data and if it is equal to the magnetToAssign uniform, \n\t * then remove the vertex from the magnet.\n\t */\n\tconst removeVerticesFromMagnet = Fn(() => {\n\n\t\tIf( instanceIndex.lessThan(uniqueCount), ()=>{\n \n\t const ids = vVertexInts.element(instanceIndex);\n\n\t If(ids.y.equal(magnetToAssign.toUint().add(1)), () => {\n\t ids.y.assign(0);\n\t });\n\t\t});\n \n\t})().compute(uniqueCount).setName(\"remove Vertices From Magnet\");\n \n\n\tconst positionNode_ = Fn(()=>{\n\t\tconst uIndex = attribute(\"uniqueIndex\", \"uint\");\n\t\tconst position = vPosStore.element(uIndex).xyz;\n\t\treturn worldMatrixInverseUniform.mul(position);\n\t})();\n\t// hack(?) to make it NOT re-skin after positionNode changes.\n\t// without this, after the positionNode runs, the skinning process will change the positions based on the skeleton.\n\t// but we already did that in the updateSkinningPoints compute node. Setting this to false apparently skips that... \n\tmesh.isSkinnedMesh = false;\n\t\n\tconst calculateNormal = Fn(() => { \n\t\tconst uIndex = attribute(\"faceIndex\", \"uint\");\n\t\tconst face = vFacesStore.element(uIndex);\n\t\tconst v0 = vPosStore.element(face.x).toVar();\n\t\tconst v1 = vPosStore.element(face.y).toVar();\n\t\tconst v2 = vPosStore.element(face.z).toVar(); \n\n\t\t// Compute edges from the actual vertices\n\t\tconst edge1 = v1.sub(v0);\n\t\tconst edge2 = v2.sub(v0);\n\t\t\n\t\t// Cross product gives the normal\n\t\tconst normal = cross(edge1, edge2).normalize();\n\t\t\n\t\tconst localNormal = worldMatrixInverseUniform.transformDirection(normal);\n\t\treturn transformNormalToView(localNormal);\n\t});\n\n\tconst vNormal = calculateNormal().toVarying();\n\tconst normalNode = select(frontFacing, vNormal, vNormal.negate());\n\n\t/**\n\t * updates the positions of the colliders in the colliderAttr array.\n\t * This is called every frame.\n\t */\n\tconst updateCollidersPositions = ()=>{\n\t\tif(!colliders?.length){\n\t\t\treturn;\n\t\t}\n\n\t\tconst collidersArray = colliderAttr.array;\n\n\t\tfor(let i = 0; i < colliders.length; i++){ \n\t\t\tconst col = colliders[i];\n\t\t\tif( col.position instanceof Vector3 )\n\t\t\t{\n\t\t\t\tv.copy(col.position)\n\t\t\t}\n\t\t\telse \n\t\t\t{\n\t\t\t\tcol.position.updateMatrixWorld(true);\n\t\t\t\tcol.position.getWorldPosition(v);\n\t\t\t}\n\n\t\t\tcollidersArray[i * 4] = v.x;\n\t\t\tcollidersArray[i * 4 + 1] = v.y;\n\t\t\tcollidersArray[i * 4 + 2] = v.z;\n\t\t\tcollidersArray[i * 4 + 3] = col.radius ;\n\t\t} \n\t\t\n\t\tcolliderAttr.needsUpdate = true;\n\t}\n\n\tmesh.material = $cfg.updateMaterial!(mesh.material as Material, positionNode_, normalNode); \n\t\n\t\n\tworldMatrixInverseUniform.value.copy(mesh.matrixWorld).invert();\n\n\tlet init = false;\n\n\t//\n\t// I do it this way because if you set the position of the rig before adding it to the scene it will calculate all wrong\n\t// and I haven't figured out how to fix that yet.\n\t//\n\trequestAnimationFrame(()=>{\n\t\trenderer.compute( initializeSkinningPosition );\n\t\trenderer.compute( calculateRestLengths ); \n\t\tinit = true;\n\t})\n\n\n return {\n\n\t\tstiffnessUniform,\n\t\tdampeningUniform,\n\t\tgravityUniform,\n\t\twindUniform,\n\t\tcolliders, \n\n\t\t/** \n\t\t * Runs the cloth simulation...\n\t\t * \n\t\t * @param delta seconds passed since last render\n\t\t * @param steps number of steps to run the simulation ( more steps = more \"stable\" but slower ) \n\t\t */\n update: (delta: number, steps=11) => {\n\n\t\t\tif( !init ) return;\n\n\t\t\tmesh.updateMatrixWorld();\n\n\t\t\tworldMatrixInverseUniform.value.copy(mesh.matrixWorld).invert();\n \n\t\t\trenderer.compute(updateSkinningPoints); \n\n\t\t\t//\n\t\t\t// extract the position of the colliders and send them to the GPU\n\t\t\t//\n\t\t\tupdateCollidersPositions(); \n\n\t\t\tdt.value = delta/steps;\n\n\t\t\tfor(let i=0; i<steps; i++ )\n\t\t\t{\n\t\t\t\trenderer.compute(computeVertexForces);\n\t\t\t} \n },\n\n\t\t/**\n\t\t * Grab the closest vertex to the given magnet. An object to update the magnet position is returned. \n\t\t * This is designed so that when you call this, it is assumed to be during a \"grab\" action. You then\n\t\t * should call the `deactivate` callback to produce the \"release\" action.\n\t\t * \t\n\t\t * @param magnetIndex Index of the magnet to use.\n\t\t * @param worldPos World position. This object now controls the position of the magnet. you can change the XYZ.\n\t\t * @param strength Strength of the magnet. Default is .5.\n\t\t * @returns An object with update and release methods.\n\t\t */\n\t\tactivateMagnet: ( magnetIndex:number, worldPos:Vector3|Object3D, strength = 1 ) => { \n \n\t\t\tconst v = new Vector3();\n\t\t\tconst getWPos = ()=>{\n\t\t\t\tif(worldPos instanceof Vector3)\n\t\t\t\t{\n\t\t\t\t\tv.copy(worldPos);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tworldPos.getWorldPosition(v);\n\t\t\t\t}\n\t\t\t\treturn v;\n\t\t\t}\n\n\t\t\t\n\n\n\t\t\tconst updateMagnetPosition = (x:number, y:number, z:number)=>{\n\t\t\t\t//\n\t\t\t\t// set the value of the magnet to use\n\t\t\t\t//\n\t\t\t\tmagnet.value.setXYZW(magnetIndex , x, y, z, strength);\n\t\t\t\tmagnet.value.needsUpdate = true; \n\t\t\t}\n\n\t\t\tmesh.updateMatrixWorld(true);\n\n\t\t\tmagnetToAssign.value = magnetIndex; \n\t\t\tmagnetToAssign.needsUpdate = true;\n\n\t\t\tconst pos = getWPos();\n\t\t\tupdateMagnetPosition(pos.x, pos.y, pos.z);\n\t\t\t\n\t\t\trenderer.compute(assignVerticesToMagnet); \n\n\t\t\treturn {\n\t\t\t\tupdate: ()=>{\n\t\t\t\t\tconst pos = getWPos();\n\t\t\t\t\tupdateMagnetPosition(pos.x, pos.y, pos.z);\n\t\t\t\t},\n\t\t\t\tupdatePosition: updateMagnetPosition,\n\t\t\t\tdeactivate: ()=>{\n\t\t\t\t\tmagnet.value.setXYZW(magnetIndex, 0, 0, 0, 0);\n\t\t\t\t\tmagnet.value.needsUpdate = true;\n\t\t\t\t\tmagnetToAssign.value = magnetIndex;\n\t\t\t\t\trenderer.compute(removeVerticesFromMagnet);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n };\n}\n\n\n/**\n * A SIMPLE cloth simulation. Goal is to have a minimal interface to just get some mesh to act kind of like a cloth. \n * Adaptation/Based on the Three's Official examples: https://github.com/mrdoob/three.js/blob/a58e9ecf225b50e4a28a934442e854878bc2a959/examples/webgpu_compute_cloth.html\n * \n */\nexport class SimpleCloth {\n \n\t/**\n\t * Turns the vertex painted parts of a skinned mesh into a cloth simulation.\n\t * Red paint is assumed to be the part of the mesh that should be simulated.\n\t * The rest is assumed to be white. \n\t * \n \t * @param mesh The skinned mesh to turn into a cloth simulation.\n\t * @param renderer The renderer ( because we need to run compute shaders )\n\t * @param options The options for the simulation.\n\t * @returns The cloth simulation's API.\n\t */\n\tstatic onSkinnedMesh = setupClothOnSkinnedMesh;\n}\n"],"names":["v","Vector3","calculateGeometry","mesh","$maskAttribute","geometry","pos","mask","count","map","springPerVertex","indices","vPos","vVertexToFace","vFaces","uniqueCount","i","x","y","z","weight","key","springs","springDefined","addSpringToVertex","vertex","springID","addSpring","A","B","hash","hash2","gIndices","Ai","Bi","Ci","faceIndex","springsPerVertexArray","springsPointer","springsForV","getCollidersFrom","root","skeleton","multiplier","colliders","scene","o","Scene","col","obj","bone","_a","assumeTheModelCameFromBlenderAndWasTexturedNormally","base","posNode","normalNode","m","MeshPhysicalNodeMaterial","DoubleSide","setupClothOnSkinnedMesh","renderer","config","$cfg","hasString","j","BufferAttribute","stiffnessUniform","uniform","dampeningUniform","dt","gravityUniform","windUniform","vPosStore","instancedArray","vForceStore","vVertexIntsArray","vVertexInts","vSpringsPerVertexArray","totalSprings","countOfPoints","springsStore","vFacesStore","indicesStore","magnet","magnetToAssign","worldMatrix","objectWorldMatrix","skinningPosition","computeSkinning","collidersArray","colliderAttr","StorageInstancedBufferAttribute","collidersStore","storage","worldMatrixInverseUniform","Matrix4","initializeSkinningPosition","Fn","If","instanceIndex","Return","uIndex","wPos","skinningWorldPosition","updateSkinningPoints","factor","mix","computeVertexForces","position","force","springPointer","springCount","ptrStart","ptrEnd","Loop","springIndex","spring","restLength","bitcast","neighborIndex","select","delta","dist","dir","f","windForce","triNoise3D","time","cPos","cRad","float","deltaSphere","sphereForce","magnetIndex","magnetData","magnetPosition","magnetStrength","magnetForce","calculateRestLengths","vertexIds","posA","assignVerticesToMagnet","selectedIndex","uint","minDist","currentMagnet","removeVerticesFromMagnet","ids","positionNode_","attribute","vNormal","face","v0","v1","v2","edge1","edge2","normal","cross","localNormal","transformNormalToView","frontFacing","updateCollidersPositions","init","steps","worldPos","strength","getWPos","updateMagnetPosition","SimpleCloth","__publicField"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/SimpleCloth.ts"],"sourcesContent":["import {\n\tBone,\n BufferAttribute,\n DoubleSide,\n Material,\n Matrix4,\n Mesh,\n Object3D,\n Scene,\n Skeleton,\n Vector3,\n type SkinnedMesh,\n} from \"three\";\nimport { ShaderCallNodeInternal } from \"three/src/nodes/TSL.js\";\nimport {\n attribute,\n computeSkinning,\n Fn,\n If,\n instancedArray,\n instanceIndex,\n Loop,\n mix,\n objectWorldMatrix,\n Return,\n select,\n storage,\n time,\n triNoise3D,\n uniform,\n float,\n\tfrontFacing,\n\tcross,\n\ttransformNormalToView,\n\tuint,\n\tbitcast,\n\tvec3,\n} from \"three/tsl\";\nimport {\n\tMeshPhysicalNodeMaterial,\n Node,\n NodeMaterial,\n StorageInstancedBufferAttribute,\n type WebGPURenderer,\n} from \"three/webgpu\";\n\nconst v = new Vector3();\n\ntype Collider = {\n\tradius:number,\n\tposition:Object3D|Vector3\n}\n\nexport type MagnetAPI = {\n\t/**\n\t * Updates the world position of the magnet by re-reading the original vector 3 passed as position\n\t */\n\tupdate:VoidFunction\n\n\t/**\n\t * Updates the world position of the magnet by setting it to these values\n\t */\n\tupdatePosition:(x:number, y:number, z:number) => void \n\n\t/**\n\t * clears every vertex from this magnet. This is the \"release\" action.\n\t */\n\tdeactivate:VoidFunction\n}\n\n// function hash3(a: number, b: number, c: number) {\n// const q = 1e6; // precision control\n// let x = (a * q) | 0;\n// let y = (b * q) | 0;\n// let z = (c * q) | 0;\n\n// let h = x * 374761393 + y * 668265263 + z * 2147483647;\n// h = (h ^ (h >> 13)) * 1274126177;\n// h = h ^ (h >> 16);\n\n// return h >>> 0;\n// } \n\n/**\n * Here we calculate the geometry of the cloth.\n * We need to know the unique vertices, the springs (edges), and the faces.\n * \n * @param mesh The mesh to calculate the geometry of.\n * @param $maskAttribute The attribute to use as a mask (default: \"color\").\n * @returns An object containing the information for the cloth\n */\nfunction calculateGeometry(mesh: Mesh, $maskAttribute = \"color\") {\n const geometry = mesh.geometry;\n const pos = geometry.attributes.position;\n\n\t/**\n\t * The vertex paint info. We assume you pain in red, and blender's default background is white. \n\t * So we will use the blue channel to determine the weight of the cloth.\n\t */\n const mask = geometry.getAttribute($maskAttribute);\n\n const count = pos.count;\n\n // Map of position string -> unique index\n const map = new Map<string, number>();\n const springPerVertex = new Map<number, number[]>();\n\n // Array to store the unique index for each vertex\n const indices = new Uint32Array(count);\n\n const vPos: number[] = [];\n \n const vVertexToFace: number[] = [];\n const vFaces: number[][] = [];\n\n let uniqueCount = 0;\n const v = new Vector3();\n\n //\n // identify unique vertices (because some may be repeated but actually the same spacial vertex)\n //\n for (let i = 0; i < count; i++) {\n const x = pos.getX(i);\n const y = pos.getY(i);\n const z = pos.getZ(i);\n // Defaults to 0 if mask is missing. \n // Logic: 1 = Pinned (Skin), 0 = Simulated (Cloth)\n const weight = mask ? mask.getY(i) : 0; \n\n // Create a unique key for the position\n const key = `${x},${y},${z}`;// hash3(x, y, z);\n\n if (!map.has(key)) {\n map.set(key, uniqueCount);\n\n //\n // convert to world space.\n //\n v.set(x, y, z); \n \n\t\t\tv.applyMatrix4(mesh.matrixWorld) \n\n vPos.push(v.x, v.y, v.z, weight);\n \n uniqueCount++;\n }\n\n indices[i] = map.get(key)!;\n }\n\n //\n // now, create the \"springs\" ( the connections between them )\n //\n const springs: number[] = []; // unique index id of a A-B vertex pair + rest length float + unused \n const vA = new Vector3();\n const vB = new Vector3();\n const springDefined = new Set<string>();\n\n const addSpringToVertex = (vertex: number, springID: number) => {\n if (springPerVertex.has(vertex)) {\n springPerVertex.get(vertex)!.push(springID);\n } else {\n springPerVertex.set(vertex, [springID]);\n }\n };\n\n const addSpring = (A: number, B: number) => {\n const hash = A + \"-\" + B; //(A * 73856093) ^ (B * 19349663);\n const hash2 = B + \"-\" + A; //(B * 73856093) ^ (A * 19349663);\n\n if (springDefined.has(hash) || springDefined.has(hash2)) {\n return;\n }\n\n const springID = springs.length/4;\n\n springs.push(A, B, 0, 0);\n\n addSpringToVertex(A, springID);\n addSpringToVertex(B, springID); \n\n springDefined.add(hash);\n };\n\n //\n // now for each triangle, create a spring from each edge...\n // \n const gIndices = geometry.index!.array;\n\n\n for (let i = 0; i < gIndices.length; i += 3) {\n const Ai = indices[gIndices[i]];\n const Bi = indices[gIndices[i + 1]];\n const Ci = indices[gIndices[i + 2]];\n\n //AB BC CA | rest length + force\n addSpring(Ai, Bi);\n addSpring( Ci, Bi,);\n addSpring( Ai , Ci, );\n\n // This is a face... make all indices point to these 3 indices...\n const faceIndex = vFaces.length;\n\n // Add the \"face\" to the face index\n vFaces.push([Ai, Bi, Ci]);\n\n\t\tvVertexToFace[gIndices[i]] = faceIndex;\n\t\tvVertexToFace[gIndices[i+1]] = faceIndex;\n\t\tvVertexToFace[gIndices[i+2]] = faceIndex;\n } \n\n //\n // build springs pointer array (adjacency list)\n // This allows O(1) lookup of springs for each vertex in the shader\n //\n const springsPerVertexArray: number[] = [];\n const springsPointer = new Uint32Array(uniqueCount);\n \n for (let i = 0; i < uniqueCount; i++) {\n springsPointer[i] = springsPerVertexArray.length;\n const springsForV = springPerVertex.get(i) || [];\n // Store count, then the spring IDs\n\t\tif( springsForV.length==0 )\n\t\t{\n\t\t\tdebugger\n\t\t}\n springsPerVertexArray.push(springsForV.length, ...springsForV);\n }\n \n\n return {\n vFaces: new Uint32Array(vFaces.flat()), //vec3[]\n vVertexToFace: new Uint32Array(vVertexToFace), // uint\n\n springsPointer: new Uint32Array(springsPointer),\n springsPerVertexArray: new Uint32Array(springsPerVertexArray),\n\n uniqueCount, // unique vertices\n indices, // for each vertice, stores the index of the unique one in the vPos array\n vPos: new Float32Array(vPos), // 4 components, a vector 3 positions of the unique vertices ( in world space ) + the weight\n\n springs: new Uint32Array(springs), // Pair of index points in vPos representing a spring \n };\n}\n\ntype ClothConfig = {\n\n\t/**\n\t * Usually it is \"color\" but sometimes it may be other like \"color_1\"\n\t */\n\tcolorAttributeName?: string;\n\n\t/**\n\t * Log stats to the console ( about the cloth mesh )\n\t */\n\tlogStats?: boolean;\n\n\t/**\n\t * The root object to search for colliders. \n\t * Colliders are expected to be empty objects with uniform scale and `userData.stickto=\"bonename\"` OR `userData.clothCollider=true` properties.\n\t * The radius of the collider will be the absolute value of `scale.x * colliderRadiusMultiplier`.\n\t */\n\tcollidersRoot?: Object3D;\n\n\tstiffness?: number;\n\tdampening?: number;\n\n\tmaskMultiplier?: number;\n\n\t/**\n\t * you can tweak the radius of the colliders ( which are spheres attached to bones )\n\t * \n\t * @param radius 1.0 is the default\n\t */\n\tcolliderRadiusMultiplier?: number; \n\n\t/**\n\t * Wind DIRECTION in world space (noise will be used to add variation)\n\t */\n\twindPerSecond?:Vector3\n\n\t/**\n\t * Gravity force in world space\n\t */\n\tgravityPerSecond?:Vector3 \n\n\t/**\n\t * How many magnets to use ( cloch grab manipulators )\n\t */\n\tmagnets:number;\n\n\t/**\n\t * The distance at which a teleport is assumed\n\t */\n\tteleportDistance?:number;\n\n\t/**\n\t * The material used by the SkinnedMesh to turn into \"cloth\" must apply and use these TSL nodes...\n\t * By default it will try to guess the most common case when you import a model from blender...\n\t * \n\t * @param positionNode \n\t * @param normalNode \n\t * @returns \n\t */\n\tupdateMaterial: ( base:Material, positionNode:Node, normalNode:Node ) => NodeMaterial\n}\n\nexport type MagnetHandler = {\n\tupdate:()=>void;\n\tupdatePosition:(x:number, y:number, z:number)=>void;\n\tdeactivate:()=>void;\n}\n \n\n/**\n * Looks for objects ( empty uniformlyscaled Spheres from blender ) with a userData.stickto = \"name of a bone\"\n * @param root \n * @returns \n */\nfunction getCollidersFrom( root:Object3D, skeleton:Skeleton, multiplier:number =1)\n{\n\tconst colliders:Collider[] = []; \n\tlet scene:Scene;\n\n\troot.traverseAncestors((o)=>{\n\t\tif( o instanceof Scene ){\n\t\t\tscene = o;\n\t\t}\n\t});\n\n\t//\n\t// collect colliders\n\t//\n\troot.traverse((o)=>{\n\t\tif( o.userData.stickto || o.userData.clothCollider )\n\t\t{\n\t\t\tcolliders.push({\n\t\t\t\tposition: o,\n\t\t\t\tradius: 0, \n\t\t\t});\n\t\t}\n\t}); \n\n\t//\n\t// attatch to skeleton and calculate world dimension\n\t//\n\tcolliders.forEach( col => {\n\t\tconst obj = col.position as Object3D;\n\t\tlet bone :Bone|undefined;\n\t\t\n\t\tif( obj.userData.stickto )\n\t\t{\n\t\t\tbone = skeleton.getBoneByName?.( obj.userData.stickto.replaceAll(/[\\.\\:]/g,\"\") );\n\n\t\t\tif( !bone ){\n\t\t\t\tthrow new Error(\"Bone not found for collider: \" + obj.userData.stickto)+ \" ???\";\n\t\t\t} \n\t\t} \n\t\t\n\t\tscene.attach(obj);\n\t\t\n\t\t//\n\t\t// the world scale is the radius of the collider ( uniform scale assumed!! )\n\t\t//\n\t\tcol.radius = Math.abs( obj.getWorldScale(v).x ) * multiplier ;\n\t \n\n\t\tbone?.attach( obj ); \n\n\t} );\n\n\n\treturn colliders;\n}\n\nfunction assumeTheModelCameFromBlenderAndWasTexturedNormally(base:Material, posNode:ShaderCallNodeInternal, normalNode:ShaderCallNodeInternal ) {\n\tif( \"isNodeMaterial\" in base )\n\t{\n\t\tconst m = base as NodeMaterial;\n\t\tm.positionNode = posNode;\n\t\tm.normalNode = normalNode;\n\t\treturn m;\n\t}\n\telse \n\t{\n\t\tconst m = new MeshPhysicalNodeMaterial({ \n\t\t\tside: DoubleSide,\n\t\t\t// wireframe:true, \n\t\t\twireframe: base.wireframe,\n\t\t\tflatShading: base.flatShading,\n\t\t\ttransparent: base.transparent,\n\t\t\tdepthWrite: base.depthWrite,\n\t\t\tdepthTest: base.depthTest,\n\t\t});\n\t\tm.positionNode = posNode;\n\t\tm.normalNode = normalNode;\n\n\t\tm.color = base.color;\n\t\tm.map = base.map;\n\n\t\tm.emissive = base.emissive;\n\t\tm.emissiveMap = base.emissiveMap;\n\t\tm.emissiveIntensity = base.emissiveIntensity;\n\n\t\tm.roughness = base.roughness;\n\t\tm.roughnessMap = base.roughnessMap;\n\n\t\tm.metalness = base.metalness;\n\t\tm.metalnessMap = base.metalnessMap;\n\n\t\tm.normalMap = base.normalMap;\n\t\tm.normalScale = base.normalScale;\n\n\t\tm.alphaMap = base.alphaMap;\n\t\tm.opacity = base.opacity;\n\t\tm.transparent = base.transparent;\n\n\t\tm.aoMap = base.aoMap;\n\n\n\t\treturn m; \n\t} \n}\n\nfunction setupClothOnSkinnedMesh(\n mesh: SkinnedMesh,\n renderer: WebGPURenderer,\n\tconfig?: Partial<ClothConfig>, \n) {\n\tmesh.updateWorldMatrix(true, false);\n\n\t\n\tconst $cfg : ClothConfig = {\n\t\tcolorAttributeName: \"color\",\n\t\tlogStats: false,\n\t\tstiffness: 0.2,\n\t\tdampening: 0.96,\n\t\tcolliderRadiusMultiplier:1,\n\t\twindPerSecond: new Vector3(0,0,0),\n\t\tgravityPerSecond: new Vector3(0, -9.8,0), \n\t\tupdateMaterial: assumeTheModelCameFromBlenderAndWasTexturedNormally,\n\t\tmagnets: 0,\n\t\tmaskMultiplier: 1,\n\t\tteleportDistance: 2,\n\t\t...config\n\t}\n\n\t//\n\t// -- Look for colliders\n\t//\n\tconst colliders:Collider[] = config?.collidersRoot ? getCollidersFrom(config.collidersRoot, mesh.skeleton, $cfg.colliderRadiusMultiplier) : []\n \n const {\n indices,\n uniqueCount,\n vPos,\n springs, \n springsPointer,\n springsPerVertexArray,\n\t\tvFaces,\n\t\tvVertexToFace\n } = calculateGeometry(mesh, $cfg.colorAttributeName!);\n\n\tif( $cfg.logStats ){\n\t\tconsole.group(\"Stats\") \n\t\tconsole.log(\"vertices\", uniqueCount); \n\t\tconsole.log(\"edges\", springs.length/4); \n\t\tconsole.log(\"faces\", vFaces.length/3); \n\t\n\n\t\tfor(let i=0; i<uniqueCount; i++){\n\t\t\tlet hasString = false;\n\t\t\tfor( let j=0; j<springs.length; j+=4){\n\t\t\t\tif(springs[j] === i || springs[j+1] === i){\n\t\t\t\t\thasString = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(!hasString){\n\t\t\t\tconsole.log(\"WARNING!: vertex\", i, \"has no strings! wtf?\");\n\t\t\t}\n\t\t} \n\n\t\tconsole.groupEnd();\n\t}\n \n\t// for each vertex position, this will help us know which unique spatial vertex on the cloth this represents.\n mesh.geometry.setAttribute(\"uniqueIndex\", new BufferAttribute(indices, 1));\n\n\t// from what face this vertex is part of.\n mesh.geometry.setAttribute(\"faceIndex\", new BufferAttribute(vVertexToFace, 1));\n\t\t \n\n const stiffnessUniform = uniform($cfg.stiffness); \n const dampeningUniform = uniform($cfg.dampening); \n\tconst maskMultiplierUniform = uniform($cfg.maskMultiplier); \n \n\t/**\n\t * Delta time (updated on .update)\n\t */\n const dt = uniform(0); \n\n\t/**\n\t * distance at which we consider the point has been artificially teleported\n\t */\n\tconst teleportDistanceUniform = uniform($cfg.teleportDistance!);\n\n\t/**\n\t * Gravity ( force acting on the cloth on every compute )\n\t */\n const gravityUniform = uniform( $cfg.gravityPerSecond!, \"vec3\"); \n\t\n\t/**\n\t * Wind (updated on .update)\n\t */\n const windUniform = uniform( $cfg.windPerSecond, \"vec3\"); \n\n\t/**\n\t * position of each unique spatial index XYZ and the W is the value of the vertex paint mask ( 1:skinned, ..., 0:physics )\n\t */\n const vPosStore = instancedArray(vPos, \"vec4\") ;\n\n\t/**\n\t * The force vector acting on each unique spatial index.\n\t */\n const vForceStore = instancedArray(uniqueCount, \"vec3\") ; \n\n\t/**\n\t * (I know this is messy...)\n\t * For each unique vertex, this is a pointer to the location in `vSpringsPerVertexArray` where the list of springs for that vertex begins.\n\t * It starts with \"count\" followed by the IDs of each spring in the `springsStore` array\n\t * --\n\t * .x = pointer to the location in `vSpringsPerVertexArray` where the list of springs for that vertex begins.\n\t * .y = magnet index\n\t */\n\tconst vVertexIntsArray = new Uint32Array( uniqueCount * 2 );\n\tfor(let i=0; i<uniqueCount; i++){\n\t\tvVertexIntsArray[i*2] = springsPointer[i];\n\t\tvVertexIntsArray[i*2+1] = 0;\n\t}\n\n\t/**\n\t * 2 Ids packed. \n\t * .x = pointer to the location in `vSpringsPerVertexArray` where the list of springs for that vertex begins.\n\t * .y = magnet index\n\t */\n const vVertexInts = instancedArray(vVertexIntsArray, \"uvec2\");\n\n\t/**\n\t * Contains [ count, ID1, ID2, ID3, count, ID1, ID2, count, ID etc... ]\n\t * Count then a serie of IDs\n\t */\n const vSpringsPerVertexArray = instancedArray(\n springsPerVertexArray,\n \"uint\",\n );\n\n\t/**\n\t * How many springs (edges) the cloth has. It will equal the number of edges of the mesh.\n\t */\n const totalSprings = springs.length / 4; // because each spring has 2 vertices + 1 rest length + 1 unused\n\n\t/**\n\t * Total number of vertices in the mesh (not the unique vertices, the actual mesh like the one you see in Blender's stats)\n\t */\n const countOfPoints = indices.length;\n\n\t/**\n\t * Stores:\n\t * XY: a pair of A,B IDs. (id of the unique vertices that this spring will try to keep at a certain distance )\n\t * Z: rest length of the spring ( in world space )\n\t * W: unused\n\t */\n const springsStore = instancedArray(springs, \"uvec4\"); \n\tconst springForceBuffer = instancedArray(totalSprings * 3, \"vec3\");\n\n\t/**\n\t * array triplets defining a face ( a triangle )\n\t */\n\tconst vFacesStore = instancedArray(vFaces, \"uvec3\"); \n \n\t/**\n\t * basically a map from each mesh's vertex to the ID of the unique spatial vertlet. ( since many vertices may exist in the space spatial position )\n\t */\n const indicesStore = instancedArray(indices, \"uint\");\n\n\n\tconst magnet = instancedArray(Math.max(1, $cfg.magnets), \"vec4\"); \n\tconst magnetToAssign = uniform(0,\"uint\");\n\n\n const worldMatrix = objectWorldMatrix(mesh);\n\n\t/**\n\t * This puppy is the node that runs the skinning process setting the positions of the vertices to match the skeleton.\n\t */\n const skinningPosition = computeSkinning(mesh);\n\n\t/**\n\t * Position XYZ and Radius W\n\t */\n\tconst collidersArray = new Float32Array(Math.max(1, colliders?.length ?? 0) * 4);\n\tconst colliderAttr = new StorageInstancedBufferAttribute(collidersArray, 4);\n\tconst collidersStore = storage(colliderAttr, \"vec4\"); \n\n\tconst worldMatrixInverseUniform = uniform(new Matrix4());\n\n /** \n * Initial setup\n */\n const initializeSkinningPosition = Fn(() => {\n If(instanceIndex.greaterThanEqual(countOfPoints), () => Return());\n\n const uIndex = indicesStore.element(instanceIndex);\n const wPos = vPosStore.element(uIndex); \n\n\t\tconst force = vForceStore.element( uIndex ) ; \n \n const skinningWorldPosition = worldMatrix.mul(skinningPosition) ;\n wPos.xyz.assign(skinningWorldPosition) ; \n\t\tforce.assign( vec3(0,0,0) );\n\n })()\n .compute(countOfPoints)\n .setName(\"Initialize skinning points\"); \n\n /**\n * < SYNC TO SKINNED MESH >\n * Skinning --> unique vertices\n * Runs once per frame before physics steps.\n * Updates positions of pinned vertices to match the animation.\n */\n const updateSkinningPoints = Fn(() => {\n If(instanceIndex.greaterThanEqual(countOfPoints), () => Return());\n\n const uIndex = indicesStore.element(instanceIndex);\n const wPos = vPosStore.element(uIndex); \n const factor = wPos.w.mul(maskMultiplierUniform).toVar() ;//.pow(2); // 1 = skinned (Pinned), 0 = cloth (Simulated)\n\n const skinningWorldPosition = worldMatrix.mul(skinningPosition) ; \n\n\t\tconst dist = wPos.xyz.sub(skinningWorldPosition).length();\n\t\tconst teleportFactor = dist.greaterThan(teleportDistanceUniform);\n\n\t\t// Handover \n\t\twPos.xyz.assign(mix(wPos.xyz, skinningWorldPosition, select(teleportFactor, 1, factor))); \n\n })()\n .compute(countOfPoints)\n .setName(\"Update skinning points\"); \n\n\n\t/**\n\t * Calculates the force of each spring\n\t */\n\tconst computeSpringForces = Fn(()=>{\n\n\t\tIf( instanceIndex.greaterThanEqual( uint( totalSprings ) ), () => Return() );\n\n\t\tconst vertexIds = springsStore.element( instanceIndex );\n\t\tconst restLength = bitcast( vertexIds.z, \"float\" );\n\n\t\tconst vertex0Position = vPosStore.element( vertexIds.x );\n\t\tconst vertex1Position = vPosStore.element( vertexIds.y );\n\n\t\tconst delta = vertex1Position.sub( vertex0Position ).toVar();\n\t\tconst dist = delta.length().max( 0.000001 ).toVar();\n\t\tconst force = dist.sub( restLength ).mul( stiffnessUniform ).mul( delta ).mul( 0.5 ).div( dist );\n\t\tspringForceBuffer.element( instanceIndex ).assign( force );\n\t\t\n\t})().compute( totalSprings ).setName(\"computeSpringForces\"); \n\n\n\t/**\n\t * Applies the forces to each point + solves folliders & magnets\n\t */\n\tconst computeVertexForces = Fn( () => {\n\n\t If( instanceIndex.greaterThanEqual( uniqueCount ), () => Return() );\n\n\t\tconst vertex = vPosStore.element( instanceIndex )\n\t const position = vertex.xyz ;\n\t\tconst clothMask = vertex.w.oneMinus().mul(maskMultiplierUniform) ; \n\n\t If( clothMask.equal(0), () => Return() ); \n\t \n\t const force = vForceStore.element( instanceIndex ) ; \n\t \n\t\tforce.mulAssign( dampeningUniform );\n\n\n\t\tconst { x:springPointer, y:magnetIndex } = vVertexInts.element(instanceIndex); \n\t\t \n const springCount = vSpringsPerVertexArray.element(springPointer);\n\n const ptrStart = springPointer.add(1).toVar(\"ptrStart\");\n const ptrEnd = ptrStart.add(springCount).toVar(\"ptrEnd\");\n\n\t Loop( { start: ptrStart, end: ptrEnd, type: 'uint', condition: '<' }, ( { i } ) => {\n\n\t const springIndex = vSpringsPerVertexArray.element(i).toVar(\"springId\"); \n\t const spring = springsStore.element(springIndex).toVar();\n\n\t const springForce = springForceBuffer.element( springIndex ); \n\t const factor = select( spring.x.equal( instanceIndex ), 1.0, - 1.0 );\n\t force.addAssign( springForce.mul( factor ) );\n\n\t } );\n\n\t \n\t\tforce.addAssign(gravityUniform.mul(dt).mul(dt)); \n\n\n\t\t// ---------------\n\t\t// Wind\n\t\t//\n const noise = triNoise3D(position, 1, time).sub(0.2).mul(0.1);\n const windForce = noise.mul(windUniform);\n force.addAssign(windForce.mul(dt) );\n\n\n\t\t//------------------\n\t\t// Sphere collisions\n\t\t//\n\t\tif (colliders) {\n\t\t\tfor (let i = 0; i < colliders.length; i++) {\n\n\t\t\t\t// collision with sphere\n\t\t\t\tconst cPos = collidersStore.element(i).xyz;\n\t\t\t\tconst cRad = collidersStore.element(i).w;\n\t\t\t\tconst deltaSphere = position.add( force ).sub( cPos );\n\t\t\t\tconst dist = deltaSphere.length();\n\t\t\t\tconst sphereForce = cRad.sub( dist ).max( 0 ).mul( deltaSphere ).div( dist ) ;\n\t\t\t\tforce.addAssign( sphereForce.mul( 0.5) ); \n\t\t\t}\n\t\t}\n\n\t\t//\n\t\t// Mask the force so the non-cloth vertices are not affected\n\t\t//\n\t\tforce.mulAssign( clothMask ); \n\t\t\n\n\t\t//\n\t\t// Cap the force so it doesnt act too crazy\n\t\t//\n\t\tconst forceLength = force.length();\n\t\tconst maxForce = 0.01 ;\n\t\tIf( forceLength.greaterThan( maxForce ), () => {\n\t\t\tforce.assign( force.normalize().mul( maxForce ) );\n\t\t});\n\n\t\t//\n\t\t// save the force\n\t\t//\n\t vForceStore.element( instanceIndex ).assign( force ); \n\n\t\t//\n\t\t// Magnet \"attraction\": if it is being controlled by a magnet, ignore the force.\n\t\t// \n\n\t\tIf( magnetIndex.greaterThan(0), ()=>{\n\n\t\t\tconst magnetData = magnet.element(magnetIndex.sub(1)) ;\n\t\t\tconst magnetPosition = magnetData.xyz;\n\t\t\tconst magnetStrength = magnetData.w;\n\n\t\t\tIf( magnetStrength.greaterThan(0), ()=>{\n\t\t\t\tconst magnetForce = magnetPosition.sub(position).mul(magnetStrength) ;\n\t\t\t\tposition.addAssign(magnetForce);\n\t\t\t}) \n\n\t\t}) \n\t\t.Else(()=>{\n\t\t\tposition.addAssign( force );\n\t\t}) ;\n\n\n\t} )().compute( uniqueCount ) \n \n\t/**\n\t * < CALCULATE REST LENGTHS>\n\t * the leght of each spring is calculated and stored in the Z component of the spring data.\n\t */\n\tconst calculateRestLengths = Fn(()=>{\n\t\tIf(instanceIndex.lessThan(totalSprings), () => {\n\t\t\tconst vertexIds = springsStore.element( instanceIndex );\n\t\t\tconst restLength = vertexIds.z ;\n\n\t\t\tconst Ai = vertexIds.x;\n\t\t\tconst Bi = vertexIds.y; \n\n\t\t\tconst posA = vPosStore.element(Ai).xyz;\n\t\t\tconst posB = vPosStore.element(Bi).xyz;\n\n\t\t\tconst delta = posB.sub(posA);\n\t\t\tconst dist = delta.length().max(0.000001);\n\n\t\t\trestLength.assign( bitcast(dist, \"uint\") ); \n\n\t\t});\n\t})().compute( totalSprings ).setName(\"calculate Rest Lengths\"); \n\n\t/**\n\t * Scans all vertices and assigns the nearest vertex to the magnet.\n\t * Check the length of each spring and pick the closest one, then assign that one to the magnet.\n\t */\n\tconst assignVerticesToMagnet = Fn(() => { \n\n\t const magnetPosition = magnet.element(magnetToAssign).xyz;\n\t const selectedIndex = uint(0).toVar();\n\t const minDist = float(10000).toVar(\"minDist\"); // sentinel — no special casing needed\n\n\t\tLoop( uniqueCount, ({ i })=>{\n\n\t\t\tconst vertlet = vPosStore.element(i) ; \n\t\t\tconst position = vertlet.xyz ; \n\t\t\tconst dist = position.sub(magnetPosition).length() ; \n\t\t\tconst currentMagnet = vVertexInts.element(i).y ;\n\n\t\t\t// If it already has a magnet, ignore it.\n\t\t\tIf( currentMagnet.equal(0).and( dist.lessThan(minDist) ) , ()=>{\n\t\t\t\tminDist.assign(dist);\n\t\t\t\tselectedIndex.assign(i); \n\t\t\t})\n\t\t\t\n\t\t}) ;\n\n\t\tIf( minDist.lessThan(10000), () => {\n\t\t\tconst ids = vVertexInts.element(selectedIndex);\n\t\t\tids.y.assign(magnetToAssign.add(1));\n\t\t}) \n\n\t})().compute(1).setName(\"assign Vertices To Magnet\"); \n\n\t/**\n\t * Removes vertices from the magnet.\n\t * Check the y component of the vertex data and if it is equal to the magnetToAssign uniform, \n\t * then remove the vertex from the magnet.\n\t */\n\tconst removeVerticesFromMagnet = Fn(() => {\n\n\t\tIf( instanceIndex.lessThan(uniqueCount), ()=>{\n \n\t const ids = vVertexInts.element(instanceIndex);\n\n\t If(ids.y.equal(magnetToAssign.toUint().add(1)), () => {\n\t ids.y.assign(0);\n\t });\n\t\t});\n \n\t})().compute(uniqueCount).setName(\"remove Vertices From Magnet\");\n \n\n\tconst positionNode_ = Fn(()=>{\n\t\tconst uIndex = attribute(\"uniqueIndex\", \"uint\");\n\t\tconst position = vPosStore.element(uIndex).xyz;\n\t\treturn worldMatrixInverseUniform.mul(position);\n\t})();\n\t// hack(?) to make it NOT re-skin after positionNode changes.\n\t// without this, after the positionNode runs, the skinning process will change the positions based on the skeleton.\n\t// but we already did that in the updateSkinningPoints compute node. Setting this to false apparently skips that... \n\tmesh.isSkinnedMesh = false;\n\t\n\tconst calculateNormal = Fn(() => { \n\t\tconst uIndex = attribute(\"faceIndex\", \"uint\");\n\t\tconst face = vFacesStore.element(uIndex);\n\t\tconst v0 = vPosStore.element(face.x).toVar();\n\t\tconst v1 = vPosStore.element(face.y).toVar();\n\t\tconst v2 = vPosStore.element(face.z).toVar(); \n\n\t\t// Compute edges from the actual vertices\n\t\tconst edge1 = v1.sub(v0);\n\t\tconst edge2 = v2.sub(v0);\n\t\t\n\t\t// Cross product gives the normal\n\t\tconst normal = cross(edge1, edge2).normalize();\n\t\t\n\t\tconst localNormal = worldMatrixInverseUniform.transformDirection(normal);\n\t\treturn transformNormalToView(localNormal);\n\t});\n\n\tconst vNormal = calculateNormal().toVarying();\n\tconst normalNode = select(frontFacing, vNormal, vNormal.negate());\n\n\t/**\n\t * updates the positions of the colliders in the colliderAttr array.\n\t * This is called every frame.\n\t */\n\tconst updateCollidersPositions = ()=>{\n\t\tif(!colliders?.length){\n\t\t\treturn;\n\t\t}\n\n\t\tconst collidersArray = colliderAttr.array;\n\n\t\tfor(let i = 0; i < colliders.length; i++){ \n\t\t\tconst col = colliders[i];\n\t\t\tif( col.position instanceof Vector3 )\n\t\t\t{\n\t\t\t\tv.copy(col.position)\n\t\t\t}\n\t\t\telse \n\t\t\t{\n\t\t\t\tcol.position.updateMatrixWorld(true);\n\t\t\t\tcol.position.getWorldPosition(v);\n\t\t\t}\n\n\t\t\tcollidersArray[i * 4] = v.x;\n\t\t\tcollidersArray[i * 4 + 1] = v.y;\n\t\t\tcollidersArray[i * 4 + 2] = v.z;\n\t\t\tcollidersArray[i * 4 + 3] = col.radius ;\n\t\t} \n\t\t\n\t\tcolliderAttr.needsUpdate = true;\n\t}\n\n\tmesh.material = $cfg.updateMaterial!(mesh.material as Material, positionNode_, normalNode); \n\t\n\t\n\tworldMatrixInverseUniform.value.copy(mesh.matrixWorld).invert();\n\n\tlet init = false;\n\n\tconst resetClothPosition = ()=>renderer.compute( initializeSkinningPosition )\n \n\n\t//\n\t// I do it this way because if you set the position of the rig before adding it to the scene it will calculate all wrong\n\t// and I haven't figured out how to fix that yet.\n\t//\n\trequestAnimationFrame(()=>{\n\t\tresetClothPosition();\n\t\trenderer.compute( calculateRestLengths ); \n\t\tinit = true; \n\t})\n\n\tlet timeSinceLastStep = 0;\n\tlet timestamp = 0;\n\t\n\n return {\n\n\t\tstiffnessUniform,\n\t\tdampeningUniform,\n\t\tgravityUniform,\n\t\twindUniform,\n\t\tcolliders, \n\t\tmesh,\n\n\t\treset: resetClothPosition,\n\n\t\t/** \n\t\t * Runs the cloth simulation...\n\t\t * \n\t\t * @param delta seconds passed since last render\n\t\t * @param steps number of steps to run the simulation ( more steps = more \"stable\" but slower ) \n\t\t */\n update: (delta: number, steps=360) => {\n\n\t\t\tif( !init ) return;\n\n\t\t\tmesh.updateMatrixWorld(); \n\t\t\tworldMatrixInverseUniform.value.copy(mesh.matrixWorld).invert(); \n\n\t\t\t//\n\t\t\t// extract the position of the colliders and send them to the GPU\n\t\t\t//\n\t\t\tupdateCollidersPositions(); \n\n\t\t\tconst deltaTime = Math.min( delta, 1 / 60 ); // don't advance the time too far, for example when the window is out of focus\n\t\t\tconst stepsPerSecond = steps; // ensure the same amount of simulation steps per second on all systems, independent of refresh rate\n\t\t\tconst timePerStep = 1 / stepsPerSecond;\n\n\t\t\tdt.value = timePerStep;\n\n\t\t\ttimeSinceLastStep += deltaTime;\n\n\t\t\trenderer.compute( updateSkinningPoints );\n\n\t\t\twhile ( timeSinceLastStep >= timePerStep ) {\n\t\t\t\t// run a verlet system simulation step\n\t\t\t\ttimestamp += timePerStep;\n\t\t\t\ttimeSinceLastStep -= timePerStep;\n\t\t\t\t \n\t\t\t\trenderer.compute( computeSpringForces );\n\t\t\t\trenderer.compute( computeVertexForces );\n\t\t\t}\n },\n\n\t\t/**\n\t\t * Grab the closest vertex to the given magnet. An object to update the magnet position is returned. \n\t\t * This is designed so that when you call this, it is assumed to be during a \"grab\" action. You then\n\t\t * should call the `deactivate` callback to produce the \"release\" action.\n\t\t * \t\n\t\t * @param magnetIndex Index of the magnet to use.\n\t\t * @param worldPos World position. This object now controls the position of the magnet. you can change the XYZ.\n\t\t * @param strength Strength of the magnet. Default is .5.\n\t\t * @returns An object with update and release methods.\n\t\t */\n\t\tactivateMagnet: ( magnetIndex:number, worldPos:Vector3|Object3D, strength = 1 ) => { \n \n\t\t\tconst v = new Vector3();\n\t\t\tconst getWPos = ()=>{\n\t\t\t\tif(worldPos instanceof Vector3)\n\t\t\t\t{\n\t\t\t\t\tv.copy(worldPos);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tworldPos.getWorldPosition(v);\n\t\t\t\t}\n\t\t\t\treturn v;\n\t\t\t}\n\n\t\t\t\n\n\n\t\t\tconst updateMagnetPosition = (x:number, y:number, z:number)=>{\n\t\t\t\t//\n\t\t\t\t// set the value of the magnet to use\n\t\t\t\t//\n\t\t\t\tmagnet.value.setXYZW(magnetIndex , x, y, z, strength);\n\t\t\t\tmagnet.value.needsUpdate = true; \n\t\t\t}\n\n\t\t\tmesh.updateMatrixWorld(true);\n\n\t\t\tmagnetToAssign.value = magnetIndex; \n\t\t\tmagnetToAssign.needsUpdate = true;\n\n\t\t\tconst pos = getWPos();\n\t\t\tupdateMagnetPosition(pos.x, pos.y, pos.z);\n\t\t\t\n\t\t\trenderer.compute(assignVerticesToMagnet); \n\n\t\t\treturn {\n\t\t\t\tupdate: ()=>{\n\t\t\t\t\tconst pos = getWPos();\n\t\t\t\t\tupdateMagnetPosition(pos.x, pos.y, pos.z);\n\t\t\t\t},\n\t\t\t\tupdatePosition: updateMagnetPosition,\n\t\t\t\tdeactivate: ()=>{\n\t\t\t\t\tmagnet.value.setXYZW(magnetIndex, 0, 0, 0, 0);\n\t\t\t\t\tmagnet.value.needsUpdate = true;\n\t\t\t\t\tmagnetToAssign.value = magnetIndex;\n\t\t\t\t\trenderer.compute(removeVerticesFromMagnet);\n\t\t\t\t}\n\t\t\t} as MagnetHandler;\n\t\t}\n };\n}\n\n\n/**\n * A SIMPLE cloth simulation. Goal is to have a minimal interface to just get some mesh to act kind of like a cloth. \n * Adaptation/Based on the Three's Official examples: https://github.com/mrdoob/three.js/blob/a58e9ecf225b50e4a28a934442e854878bc2a959/examples/webgpu_compute_cloth.html\n * \n */\nexport class SimpleCloth {\n \n\t/**\n\t * Turns the vertex painted parts of a skinned mesh into a cloth simulation.\n\t * Red paint is assumed to be the part of the mesh that should be simulated.\n\t * The rest is assumed to be white. \n\t * \n \t * @param mesh The skinned mesh to turn into a cloth simulation.\n\t * @param renderer The renderer ( because we need to run compute shaders )\n\t * @param options The options for the simulation.\n\t * @returns The cloth simulation's API.\n\t */\n\tstatic onSkinnedMesh = setupClothOnSkinnedMesh;\n}\n"],"names":["v","Vector3","calculateGeometry","mesh","$maskAttribute","geometry","pos","mask","count","map","springPerVertex","indices","vPos","vVertexToFace","vFaces","uniqueCount","i","x","y","z","weight","key","springs","springDefined","addSpringToVertex","vertex","springID","addSpring","A","B","hash","hash2","gIndices","Ai","Bi","Ci","faceIndex","springsPerVertexArray","springsPointer","springsForV","getCollidersFrom","root","skeleton","multiplier","colliders","scene","o","Scene","col","obj","bone","_a","assumeTheModelCameFromBlenderAndWasTexturedNormally","base","posNode","normalNode","m","MeshPhysicalNodeMaterial","DoubleSide","setupClothOnSkinnedMesh","renderer","config","$cfg","hasString","j","BufferAttribute","stiffnessUniform","uniform","dampeningUniform","maskMultiplierUniform","dt","teleportDistanceUniform","gravityUniform","windUniform","vPosStore","instancedArray","vForceStore","vVertexIntsArray","vVertexInts","vSpringsPerVertexArray","totalSprings","countOfPoints","springsStore","springForceBuffer","vFacesStore","indicesStore","magnet","magnetToAssign","worldMatrix","objectWorldMatrix","skinningPosition","computeSkinning","collidersArray","colliderAttr","StorageInstancedBufferAttribute","collidersStore","storage","worldMatrixInverseUniform","Matrix4","initializeSkinningPosition","Fn","If","instanceIndex","Return","uIndex","wPos","force","skinningWorldPosition","vec3","updateSkinningPoints","factor","teleportFactor","mix","select","computeSpringForces","uint","vertexIds","restLength","bitcast","vertex0Position","delta","dist","computeVertexForces","position","clothMask","springPointer","magnetIndex","springCount","ptrStart","ptrEnd","Loop","springIndex","spring","springForce","windForce","triNoise3D","time","cPos","cRad","deltaSphere","sphereForce","forceLength","maxForce","magnetData","magnetPosition","magnetStrength","magnetForce","calculateRestLengths","posA","assignVerticesToMagnet","selectedIndex","minDist","float","currentMagnet","removeVerticesFromMagnet","ids","positionNode_","attribute","vNormal","face","v0","v1","v2","edge1","edge2","normal","cross","localNormal","transformNormalToView","frontFacing","updateCollidersPositions","init","resetClothPosition","timeSinceLastStep","steps","deltaTime","timePerStep","worldPos","strength","getWPos","updateMagnetPosition","SimpleCloth","__publicField"],"mappings":";;;;;;AA8CA,MAAMA,IAAI,IAAIC,EAAA;AA6Cd,SAASC,GAAkBC,GAAYC,IAAiB,SAAS;AAC7D,QAAMC,IAAWF,EAAK,UAChBG,IAAMD,EAAS,WAAW,UAM1BE,IAAOF,EAAS,aAAaD,CAAc,GAE3CI,IAAQF,EAAI,OAGZG,wBAAU,IAAA,GACVC,wBAAsB,IAAA,GAGtBC,IAAU,IAAI,YAAYH,CAAK,GAE/BI,IAAiB,CAAA,GAEjBC,IAA0B,CAAA,GAC1BC,IAAqB,CAAA;AAE3B,MAAIC,IAAc;AAClB,QAAMf,IAAI,IAAIC,EAAA;AAKd,WAASe,IAAI,GAAGA,IAAIR,GAAOQ,KAAK;AAC5B,UAAMC,IAAIX,EAAI,KAAKU,CAAC,GACdE,IAAIZ,EAAI,KAAKU,CAAC,GACdG,IAAIb,EAAI,KAAKU,CAAC,GAGdI,IAASb,IAAOA,EAAK,KAAKS,CAAC,IAAI,GAG/BK,IAAM,GAAGJ,CAAC,IAAIC,CAAC,IAAIC,CAAC;AAE1B,IAAKV,EAAI,IAAIY,CAAG,MACZZ,EAAI,IAAIY,GAAKN,CAAW,GAKxBf,EAAE,IAAIiB,GAAGC,GAAGC,CAAC,GAEtBnB,EAAE,aAAaG,EAAK,WAAW,GAEtBS,EAAK,KAAKZ,EAAE,GAAGA,EAAE,GAAGA,EAAE,GAAGoB,CAAM,GAE/BL,MAGJJ,EAAQK,CAAC,IAAIP,EAAI,IAAIY,CAAG;AAAA,EAC5B;AAKA,QAAMC,IAAoB,CAAA;AACf,MAAIrB,EAAA,GACJ,IAAIA,EAAA;AACf,QAAMsB,wBAAoB,IAAA,GAEpBC,IAAoB,CAACC,GAAgBC,MAAqB;AAC5D,IAAIhB,EAAgB,IAAIe,CAAM,IAC1Bf,EAAgB,IAAIe,CAAM,EAAG,KAAKC,CAAQ,IAE1ChB,EAAgB,IAAIe,GAAQ,CAACC,CAAQ,CAAC;AAAA,EAE9C,GAEMC,IAAY,CAACC,GAAWC,MAAc;AACxC,UAAMC,IAAOF,IAAI,MAAMC,GACjBE,IAAQF,IAAI,MAAMD;AAExB,QAAIL,EAAc,IAAIO,CAAI,KAAKP,EAAc,IAAIQ,CAAK;AAClD;AAGJ,UAAML,IAAWJ,EAAQ,SAAO;AAEhC,IAAAA,EAAQ,KAAKM,GAAGC,GAAG,GAAG,CAAC,GAEvBL,EAAkBI,GAAGF,CAAQ,GAC7BF,EAAkBK,GAAGH,CAAQ,GAE7BH,EAAc,IAAIO,CAAI;AAAA,EAC1B,GAKME,IAAW3B,EAAS,MAAO;AAGjC,WAASW,IAAI,GAAGA,IAAIgB,EAAS,QAAQhB,KAAK,GAAG;AACzC,UAAMiB,IAAKtB,EAAQqB,EAAShB,CAAC,CAAC,GACxBkB,IAAKvB,EAAQqB,EAAShB,IAAI,CAAC,CAAC,GAC5BmB,IAAKxB,EAAQqB,EAAShB,IAAI,CAAC,CAAC;AAGlC,IAAAW,EAAUM,GAAIC,CAAE,GAChBP,EAAYQ,GAAID,CAAG,GACnBP,EAAWM,GAAKE,CAAI;AAGpB,UAAMC,IAAYtB,EAAO;AAGzB,IAAAA,EAAO,KAAK,CAACmB,GAAIC,GAAIC,CAAE,CAAC,GAE9BtB,EAAcmB,EAAShB,CAAC,CAAC,IAAIoB,GAC7BvB,EAAcmB,EAAShB,IAAE,CAAC,CAAC,IAAIoB,GAC/BvB,EAAcmB,EAAShB,IAAE,CAAC,CAAC,IAAIoB;AAAA,EAC7B;AAMA,QAAMC,IAAkC,CAAA,GAClCC,IAAiB,IAAI,YAAYvB,CAAW;AAElD,WAASC,IAAI,GAAGA,IAAID,GAAaC,KAAK;AAClC,IAAAsB,EAAetB,CAAC,IAAIqB,EAAsB;AAC1C,UAAME,IAAc7B,EAAgB,IAAIM,CAAC,KAAK,CAAA;AAEpD,QAAIuB,EAAY,UAAQ;AAEvB;AAEK,IAAAF,EAAsB,KAAKE,EAAY,QAAQ,GAAGA,CAAW;AAAA,EACjE;AAGA,SAAO;AAAA,IACH,QAAQ,IAAI,YAAYzB,EAAO,MAAM;AAAA;AAAA,IACrC,eAAe,IAAI,YAAYD,CAAa;AAAA;AAAA,IAE5C,gBAAgB,IAAI,YAAYyB,CAAc;AAAA,IAC9C,uBAAuB,IAAI,YAAYD,CAAqB;AAAA,IAE5D,aAAAtB;AAAA;AAAA,IACA,SAAAJ;AAAA;AAAA,IACA,MAAM,IAAI,aAAaC,CAAI;AAAA;AAAA,IAE3B,SAAS,IAAI,YAAYU,CAAO;AAAA;AAAA,EAAA;AAExC;AA4EA,SAASkB,GAAkBC,GAAeC,GAAmBC,IAAmB,GAChF;AACC,QAAMC,IAAuB,CAAA;AAC7B,MAAIC;AAEJ,SAAAJ,EAAK,kBAAkB,CAACK,MAAI;AAC3B,IAAIA,aAAaC,OAChBF,IAAQC;AAAA,EAEV,CAAC,GAKDL,EAAK,SAAS,CAACK,MAAI;AAClB,KAAIA,EAAE,SAAS,WAAWA,EAAE,SAAS,kBAEpCF,EAAU,KAAK;AAAA,MACd,UAAUE;AAAA,MACV,QAAQ;AAAA,IAAA,CACR;AAAA,EAEH,CAAC,GAKDF,EAAU,QAAS,CAAAI,MAAO;;AACzB,UAAMC,IAAMD,EAAI;AAChB,QAAIE;AAEJ,QAAID,EAAI,SAAS,YAEhBC,KAAOC,IAAAT,EAAS,kBAAT,gBAAAS,EAAA,KAAAT,GAA0BO,EAAI,SAAS,QAAQ,WAAW,WAAU,EAAE,IAEzE,CAACC;AACJ,YAAM,IAAI,MAAM,kCAAkCD,EAAI,SAAS,OAAO,IAAG;AAI3E,IAAAJ,EAAM,OAAOI,CAAG,GAKhBD,EAAI,SAAS,KAAK,IAAKC,EAAI,cAAcjD,CAAC,EAAE,CAAE,IAAI2C,GAGlDO,KAAA,QAAAA,EAAM,OAAQD;AAAA,EAEf,CAAE,GAGKL;AACR;AAEA,SAASQ,GAAoDC,GAAeC,GAAgCC,GAAoC;AAC/I,MAAI,oBAAoBF,GACxB;AACC,UAAMG,IAAIH;AACV,WAAAG,EAAE,eAAeF,GACjBE,EAAE,aAAaD,GACRC;AAAA,EACR,OAEA;AACC,UAAMA,IAAI,IAAIC,GAAyB;AAAA,MACtC,MAAMC;AAAA;AAAA,MAEN,WAAWL,EAAK;AAAA,MAChB,aAAaA,EAAK;AAAA,MAClB,aAAaA,EAAK;AAAA,MAClB,YAAYA,EAAK;AAAA,MACjB,WAAWA,EAAK;AAAA,IAAA,CAChB;AACD,WAAAG,EAAE,eAAeF,GACjBE,EAAE,aAAaD,GAEfC,EAAE,QAAQH,EAAK,OACfG,EAAE,MAAMH,EAAK,KAEbG,EAAE,WAAWH,EAAK,UAClBG,EAAE,cAAcH,EAAK,aACrBG,EAAE,oBAAoBH,EAAK,mBAE3BG,EAAE,YAAYH,EAAK,WACnBG,EAAE,eAAeH,EAAK,cAEtBG,EAAE,YAAYH,EAAK,WACnBG,EAAE,eAAeH,EAAK,cAEtBG,EAAE,YAAYH,EAAK,WACnBG,EAAE,cAAcH,EAAK,aAErBG,EAAE,WAAWH,EAAK,UAClBG,EAAE,UAAUH,EAAK,SACjBG,EAAE,cAAcH,EAAK,aAErBG,EAAE,QAAQH,EAAK,OAGRG;AAAA,EACR;AACD;AAEA,SAASG,GACLxD,GACAyD,GACHC,GACC;AACD,EAAA1D,EAAK,kBAAkB,IAAM,EAAK;AAGlC,QAAM2D,IAAqB;AAAA,IAC1B,oBAAoB;AAAA,IACpB,UAAU;AAAA,IACV,WAAW;AAAA,IACX,WAAW;AAAA,IACX,0BAAyB;AAAA,IACzB,eAAe,IAAI7D,EAAQ,GAAE,GAAE,CAAC;AAAA,IAChC,kBAAkB,IAAIA,EAAQ,GAAG,MAAK,CAAC;AAAA,IACvC,gBAAgBmD;AAAA,IAChB,SAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,kBAAkB;AAAA,IAClB,GAAGS;AAAA,EAAA,GAMEjB,IAAuBiB,KAAA,QAAAA,EAAQ,gBAAgBrB,GAAiBqB,EAAO,eAAe1D,EAAK,UAAU2D,EAAK,wBAAwB,IAAI,CAAA,GAEnI;AAAA,IACF,SAAAnD;AAAA,IACA,aAAAI;AAAA,IACA,MAAAH;AAAA,IACA,SAAAU;AAAA,IACA,gBAAAgB;AAAA,IACA,uBAAAD;AAAA,IACN,QAAAvB;AAAA,IACA,eAAAD;AAAA,EAAA,IACMX,GAAkBC,GAAM2D,EAAK,kBAAmB;AAEvD,MAAIA,EAAK,UAAU;AAClB,YAAQ,MAAM,OAAO,GACrB,QAAQ,IAAI,YAAY/C,CAAW,GACnC,QAAQ,IAAI,SAASO,EAAQ,SAAO,CAAC,GACrC,QAAQ,IAAI,SAASR,EAAO,SAAO,CAAC;AAGpC,aAAQE,IAAE,GAAGA,IAAED,GAAaC,KAAI;AAC/B,UAAI+C,IAAY;AAChB,eAASC,IAAE,GAAGA,IAAE1C,EAAQ,QAAQ0C,KAAG;AAClC,YAAG1C,EAAQ0C,CAAC,MAAMhD,KAAKM,EAAQ0C,IAAE,CAAC,MAAMhD,GAAE;AACzC,UAAA+C,IAAY;AACZ;AAAA,QACD;AAED,MAAIA,KACH,QAAQ,IAAI,oBAAoB/C,GAAG,sBAAsB;AAAA,IAE3D;AAEA,YAAQ,SAAA;AAAA,EACT;AAGG,EAAAb,EAAK,SAAS,aAAa,eAAe,IAAI8D,GAAgBtD,GAAS,CAAC,CAAC,GAGzER,EAAK,SAAS,aAAa,aAAa,IAAI8D,GAAgBpD,GAAe,CAAC,CAAC;AAG7E,QAAMqD,IAAmBC,EAAQL,EAAK,SAAS,GACzCM,IAAmBD,EAAQL,EAAK,SAAS,GAC5CO,IAAwBF,EAAQL,EAAK,cAAc,GAKhDQ,IAAKH,EAAQ,CAAC,GAKjBI,IAA0BJ,EAAQL,EAAK,gBAAiB,GAKrDU,IAAiBL,EAASL,EAAK,kBAAmB,MAAM,GAKxDW,IAAcN,EAASL,EAAK,eAAe,MAAM,GAKjDY,IAAYC,EAAe/D,GAAM,MAAM,GAKvCgE,IAAcD,EAAe5D,GAAa,MAAM,GAUnD8D,IAAmB,IAAI,YAAa9D,IAAc,CAAE;AAC1D,WAAQC,IAAE,GAAGA,IAAED,GAAaC;AAC3B,IAAA6D,EAAiB7D,IAAE,CAAC,IAAIsB,EAAetB,CAAC,GACxC6D,EAAiB7D,IAAE,IAAE,CAAC,IAAI;AAQxB,QAAM8D,IAAcH,EAAeE,GAAkB,OAAO,GAMtDE,IAAyBJ;AAAA,IAC3BtC;AAAA,IACA;AAAA,EAAA,GAME2C,IAAe1D,EAAQ,SAAS,GAKhC2D,IAAgBtE,EAAQ,QAQxBuE,IAAeP,EAAerD,GAAS,OAAO,GACjD6D,KAAoBR,EAAeK,IAAe,GAAG,MAAM,GAK3DI,KAAcT,EAAe7D,GAAQ,OAAO,GAKzCuE,KAAeV,EAAehE,GAAS,MAAM,GAGhD2E,IAASX,EAAe,KAAK,IAAI,GAAGb,EAAK,OAAO,GAAG,MAAM,GACzDyB,IAAiBpB,EAAQ,GAAE,MAAM,GAG9BqB,KAAcC,GAAkBtF,CAAI,GAKpCuF,KAAmBC,GAAgBxF,CAAI,GAK1CyF,KAAiB,IAAI,aAAa,KAAK,IAAI,IAAGhD,KAAA,gBAAAA,EAAW,WAAU,CAAC,IAAI,CAAC,GACzEiD,IAAe,IAAIC,GAAgCF,IAAgB,CAAC,GACpEG,KAAiBC,GAAQH,GAAc,MAAM,GAE7CI,IAA4B9B,EAAQ,IAAI+B,IAAS,GAK9CC,KAA6BC,EAAG,MAAM;AACxC,IAAAC,EAAGC,EAAc,iBAAiBrB,CAAa,GAAG,MAAMsB,GAAQ;AAEhE,UAAMC,IAASnB,GAAa,QAAQiB,CAAa,GAC3CG,IAAO/B,EAAU,QAAQ8B,CAAM,GAErCE,IAAQ9B,EAAY,QAAS4B,CAAO,GAE9BG,IAAwBnB,GAAY,IAAIE,EAAgB;AAC9D,IAAAe,EAAK,IAAI,OAAOE,CAAqB,GAC3CD,EAAM,OAAQE,GAAK,GAAE,GAAE,CAAC,CAAE;AAAA,EAExB,CAAC,EAAA,EACA,QAAQ3B,CAAa,EACrB,QAAQ,4BAA4B,GAQ/B4B,KAAuBT,EAAG,MAAM;AAClC,IAAAC,EAAGC,EAAc,iBAAiBrB,CAAa,GAAG,MAAMsB,GAAQ;AAEhE,UAAMC,IAASnB,GAAa,QAAQiB,CAAa,GAC3CG,IAAO/B,EAAU,QAAQ8B,CAAM,GAC/BM,IAASL,EAAK,EAAE,IAAIpC,CAAqB,EAAE,MAAA,GAE3CsC,IAAwBnB,GAAY,IAAIE,EAAgB,GAG9DqB,IADON,EAAK,IAAI,IAAIE,CAAqB,EAAE,OAAA,EACrB,YAAYpC,CAAuB;AAG/D,IAAAkC,EAAK,IAAI,OAAOO,GAAIP,EAAK,KAAKE,GAAuBM,GAAOF,GAAgB,GAAGD,CAAM,CAAC,CAAC;AAAA,EAErF,CAAC,EAAA,EACA,QAAQ7B,CAAa,EACrB,QAAQ,wBAAwB,GAM9BiC,KAAsBd,EAAG,MAAI;AAElC,IAAAC,EAAIC,EAAc,iBAAkBa,GAAMnC,CAAa,CAAE,GAAG,MAAMuB,GAAS;AAE3E,UAAMa,IAAYlC,EAAa,QAASoB,CAAc,GAChDe,IAAaC,GAASF,EAAU,GAAG,OAAQ,GAE3CG,IAAkB7C,EAAU,QAAS0C,EAAU,CAAE,GAGjDI,IAFkB9C,EAAU,QAAS0C,EAAU,CAAE,EAEzB,IAAKG,CAAgB,EAAE,MAAA,GAC/CE,IAAOD,EAAM,OAAA,EAAS,IAAK,IAAS,EAAE,MAAA,GACtCd,IAAQe,EAAK,IAAKJ,CAAW,EAAE,IAAKnD,CAAiB,EAAE,IAAKsD,CAAM,EAAE,IAAK,GAAI,EAAE,IAAKC,CAAK;AAC/F,IAAAtC,GAAkB,QAASmB,CAAc,EAAE,OAAQI,CAAM;AAAA,EAE1D,CAAC,EAAA,EAAI,QAAS1B,CAAa,EAAE,QAAQ,qBAAqB,GAMpD0C,KAAsBtB,EAAI,MAAM;AAElC,IAAAC,EAAIC,EAAc,iBAAkBvF,CAAY,GAAG,MAAMwF,GAAS;AAErE,UAAM9E,IAASiD,EAAU,QAAS4B,CAAc,GACvCqB,IAAWlG,EAAO,KACrBmG,IAAYnG,EAAO,EAAE,SAAA,EAAW,IAAI4C,CAAqB;AAE5D,IAAAgC,EAAIuB,EAAU,MAAM,CAAC,GAAG,MAAMrB,GAAS;AAEvC,UAAMG,IAAQ9B,EAAY,QAAS0B,CAAc;AAEpD,IAAAI,EAAM,UAAWtC,CAAiB;AAGlC,UAAM,EAAE,GAAEyD,GAAe,GAAEC,MAAgBhD,EAAY,QAAQwB,CAAa,GAEhEyB,IAAchD,EAAuB,QAAQ8C,CAAa,GAE1DG,IAAWH,EAAc,IAAI,CAAC,EAAE,MAAM,UAAU,GAChDI,IAASD,EAAS,IAAID,CAAW,EAAE,MAAM,QAAQ;AAE1D,IAAAG,GAAM,EAAE,OAAOF,GAAU,KAAKC,GAAQ,MAAM,QAAQ,WAAW,IAAA,GAAO,CAAE,EAAE,GAAAjH,QAAS;AAE/E,YAAMmH,IAAcpD,EAAuB,QAAQ/D,CAAC,EAAE,MAAM,UAAU,GAChEoH,IAASlD,EAAa,QAAQiD,CAAW,EAAE,MAAA,GAE3CE,IAAclD,GAAkB,QAASgD,CAAY,GACrDrB,IAASG,GAAQmB,EAAO,EAAE,MAAO9B,CAAc,GAAG,GAAK,EAAM;AACnE,MAAAI,EAAM,UAAW2B,EAAY,IAAKvB,CAAO,CAAE;AAAA,IAE/C,CAAE,GAGLJ,EAAM,UAAUlC,EAAe,IAAIF,CAAE,EAAE,IAAIA,CAAE,CAAC;AAOxC,UAAMgE,KADQC,GAAWZ,GAAU,GAAGa,EAAI,EAAE,IAAI,GAAG,EAAE,IAAI,GAAG,EACpC,IAAI/D,CAAW;AAO7C,QANMiC,EAAM,UAAU4B,GAAU,IAAIhE,CAAE,CAAE,GAMpC1B;AACH,eAAS5B,IAAI,GAAGA,IAAI4B,EAAU,QAAQ5B,KAAK;AAG1C,cAAMyH,IAAO1C,GAAe,QAAQ/E,CAAC,EAAE,KACjC0H,IAAO3C,GAAe,QAAQ/E,CAAC,EAAE,GACjC2H,IAAchB,EAAS,IAAKjB,CAAM,EAAE,IAAK+B,CAAK,GAC9ChB,IAAOkB,EAAY,OAAA,GACnBC,KAAcF,EAAK,IAAKjB,CAAK,EAAE,IAAK,CAAE,EAAE,IAAKkB,CAAY,EAAE,IAAKlB,CAAK;AAC3E,QAAAf,EAAM,UAAWkC,GAAY,IAAK,GAAG,CAAG;AAAA,MACzC;AAMD,IAAAlC,EAAM,UAAWkB,CAAU;AAM3B,UAAMiB,KAAcnC,EAAM,OAAA,GACpBoC,KAAW;AACjB,IAAAzC,EAAIwC,GAAY,YAAaC,EAAS,GAAG,MAAM;AAC9C,MAAApC,EAAM,OAAQA,EAAM,UAAA,EAAY,IAAKoC,EAAS,CAAE;AAAA,IACjD,CAAC,GAKElE,EAAY,QAAS0B,CAAc,EAAE,OAAQI,CAAM,GAMtDL,EAAIyB,EAAY,YAAY,CAAC,GAAG,MAAI;AAEnC,YAAMiB,IAAazD,EAAO,QAAQwC,EAAY,IAAI,CAAC,CAAC,GAC9CkB,IAAiBD,EAAW,KAC5BE,IAAiBF,EAAW;AAElC,MAAA1C,EAAI4C,EAAe,YAAY,CAAC,GAAG,MAAI;AACtC,cAAMC,IAAcF,EAAe,IAAIrB,CAAQ,EAAE,IAAIsB,CAAc;AACnE,QAAAtB,EAAS,UAAUuB,CAAW;AAAA,MAC/B,CAAC;AAAA,IAEF,CAAC,EACA,KAAK,MAAI;AACT,MAAAvB,EAAS,UAAWjB,CAAM;AAAA,IAC3B,CAAC;AAAA,EAGF,CAAE,EAAA,EAAI,QAAS3F,CAAY,GAMrBoI,KAAuB/C,EAAG,MAAI;AACnC,IAAAC,EAAGC,EAAc,SAAStB,CAAY,GAAG,MAAM;AAC9C,YAAMoC,IAAYlC,EAAa,QAASoB,CAAc,GAChDe,IAAaD,EAAU,GAEvBnF,IAAKmF,EAAU,GACflF,IAAKkF,EAAU,GAEfgC,IAAO1E,EAAU,QAAQzC,CAAE,EAAE,KAI7BwF,IAHO/C,EAAU,QAAQxC,CAAE,EAAE,IAEhB,IAAIkH,CAAI,EACR,OAAA,EAAS,IAAI,IAAQ;AAExC,MAAA/B,EAAW,OAAQC,GAAQG,GAAM,MAAM,CAAE;AAAA,IAE1C,CAAC;AAAA,EACF,CAAC,EAAA,EAAI,QAASzC,CAAa,EAAE,QAAQ,wBAAwB,GAMvDqE,KAAyBjD,EAAG,MAAM;AAEpC,UAAM4C,IAAiB1D,EAAO,QAAQC,CAAc,EAAE,KAChD+D,IAAgBnC,GAAK,CAAC,EAAE,MAAA,GACxBoC,IAAUC,GAAM,GAAK,EAAE,MAAM,SAAS;AAE/C,IAAAtB,GAAMnH,GAAa,CAAC,EAAE,QAAM;AAI3B,YAAM0G,IAFU/C,EAAU,QAAQ,CAAC,EACV,IACH,IAAIsE,CAAc,EAAE,OAAA,GACpCS,IAAgB3E,EAAY,QAAQ,CAAC,EAAE;AAG7C,MAAAuB,EAAIoD,EAAc,MAAM,CAAC,EAAE,IAAKhC,EAAK,SAAS8B,CAAO,CAAE,GAAI,MAAI;AAC9D,QAAAA,EAAQ,OAAO9B,CAAI,GACnB6B,EAAc,OAAO,CAAC;AAAA,MACvB,CAAC;AAAA,IAEF,CAAC,GAEDjD,EAAIkD,EAAQ,SAAS,GAAK,GAAG,MAAM;AAElC,MADYzE,EAAY,QAAQwE,CAAa,EACzC,EAAE,OAAO/D,EAAe,IAAI,CAAC,CAAC;AAAA,IACnC,CAAC;AAAA,EAEF,CAAC,EAAA,EAAI,QAAQ,CAAC,EAAE,QAAQ,2BAA2B,GAO7CmE,KAA2BtD,EAAG,MAAM;AAEzC,IAAAC,EAAIC,EAAc,SAASvF,CAAW,GAAG,MAAI;AAEtC,YAAM4I,IAAM7E,EAAY,QAAQwB,CAAa;AAE7C,MAAAD,EAAGsD,EAAI,EAAE,MAAMpE,EAAe,OAAA,EAAS,IAAI,CAAC,CAAC,GAAG,MAAM;AAClD,QAAAoE,EAAI,EAAE,OAAO,CAAC;AAAA,MAClB,CAAC;AAAA,IACR,CAAC;AAAA,EAEF,CAAC,EAAA,EAAI,QAAQ5I,CAAW,EAAE,QAAQ,6BAA6B,GAGzD6I,KAAgBxD,EAAG,MAAI;AAC5B,UAAMI,IAASqD,GAAU,eAAe,MAAM,GACxClC,IAAWjD,EAAU,QAAQ8B,CAAM,EAAE;AAC3C,WAAOP,EAA0B,IAAI0B,CAAQ;AAAA,EAC9C,CAAC,EAAA;AAID,EAAAxH,EAAK,gBAAgB;AAoBrB,QAAM2J,KAlBkB1D,EAAG,MAAM;AAChC,UAAMI,IAASqD,GAAU,aAAa,MAAM,GACtCE,IAAO3E,GAAY,QAAQoB,CAAM,GACjCwD,IAAKtF,EAAU,QAAQqF,EAAK,CAAC,EAAE,MAAA,GAC/BE,IAAKvF,EAAU,QAAQqF,EAAK,CAAC,EAAE,MAAA,GAC/BG,IAAKxF,EAAU,QAAQqF,EAAK,CAAC,EAAE,MAAA,GAG/BI,IAAQF,EAAG,IAAID,CAAE,GACjBI,IAAQF,EAAG,IAAIF,CAAE,GAGjBK,IAASC,GAAMH,GAAOC,CAAK,EAAE,UAAA,GAE7BG,IAActE,EAA0B,mBAAmBoE,CAAM;AACvE,WAAOG,GAAsBD,CAAW;AAAA,EACzC,CAAC,EAEe,EAAkB,UAAA,GAC5BhH,KAAa0D,GAAOwD,IAAaX,IAASA,GAAQ,QAAQ,GAM1DY,KAA2B,MAAI;AACpC,QAAG,EAAC9H,KAAA,QAAAA,EAAW;AACd;AAGD,UAAMgD,IAAiBC,EAAa;AAEpC,aAAQ7E,IAAI,GAAGA,IAAI4B,EAAU,QAAQ5B,KAAI;AACxC,YAAMgC,IAAMJ,EAAU5B,CAAC;AACvB,MAAIgC,EAAI,oBAAoB/C,IAE3BD,EAAE,KAAKgD,EAAI,QAAQ,KAInBA,EAAI,SAAS,kBAAkB,EAAI,GACnCA,EAAI,SAAS,iBAAiBhD,CAAC,IAGhC4F,EAAe5E,IAAI,CAAC,IAAIhB,EAAE,GAC1B4F,EAAe5E,IAAI,IAAI,CAAC,IAAIhB,EAAE,GAC9B4F,EAAe5E,IAAI,IAAI,CAAC,IAAIhB,EAAE,GAC9B4F,EAAe5E,IAAI,IAAI,CAAC,IAAIgC,EAAI;AAAA,IACjC;AAEA,IAAA6C,EAAa,cAAc;AAAA,EAC5B;AAEA,EAAA1F,EAAK,WAAW2D,EAAK,eAAgB3D,EAAK,UAAsByJ,IAAerG,EAAU,GAGzF0C,EAA0B,MAAM,KAAK9F,EAAK,WAAW,EAAE,OAAA;AAEvD,MAAIwK,KAAO;AAEX,QAAMC,KAAqB,MAAIhH,EAAS,QAASuC,EAA2B;AAO5E,wBAAsB,MAAI;AACzB,IAAAyE,GAAA,GACAhH,EAAS,QAASuF,EAAqB,GACvCwB,KAAO;AAAA,EACR,CAAC;AAED,MAAIE,KAAoB;AAIrB,SAAO;AAAA,IAET,kBAAA3G;AAAA,IACA,kBAAAE;AAAA,IACA,gBAAAI;AAAA,IACA,aAAAC;AAAA,IACA,WAAA7B;AAAA,IACA,MAAAzC;AAAA,IAEA,OAAOyK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQD,QAAQ,CAACpD,GAAesD,IAAM,QAAQ;AAE3C,UAAI,CAACH,GAAO;AAEZ,MAAAxK,EAAK,kBAAA,GACL8F,EAA0B,MAAM,KAAK9F,EAAK,WAAW,EAAE,OAAA,GAKvDuK,GAAA;AAEA,YAAMK,IAAY,KAAK,IAAKvD,GAAO,IAAI,EAAG,GAEpCwD,IAAc,IADGF;AASvB,WANAxG,EAAG,QAAQ0G,GAEXH,MAAqBE,GAErBnH,EAAS,QAASiD,EAAqB,GAE/BgE,MAAqBG;AAG5B,QAAAH,MAAqBG,GAErBpH,EAAS,QAASsD,EAAoB,GACtCtD,EAAS,QAAS8D,EAAoB;AAAA,IAElC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAYN,gBAAgB,CAAEI,GAAoBmD,GAA2BC,IAAW,MAAO;AAElF,YAAMlL,IAAI,IAAIC,EAAA,GACRkL,IAAU,OACZF,aAAoBhL,IAEtBD,EAAE,KAAKiL,CAAQ,IAIfA,EAAS,iBAAiBjL,CAAC,GAErBA,IAMFoL,IAAuB,CAACnK,GAAUC,GAAUC,OAAW;AAI5D,QAAAmE,EAAO,MAAM,QAAQwC,GAAc7G,GAAGC,GAAGC,IAAG+J,CAAQ,GACpD5F,EAAO,MAAM,cAAc;AAAA,MAC5B;AAEA,MAAAnF,EAAK,kBAAkB,EAAI,GAE3BoF,EAAe,QAAQuC,GACvBvC,EAAe,cAAc;AAE7B,YAAMjF,IAAM6K,EAAA;AACZ,aAAAC,EAAqB9K,EAAI,GAAGA,EAAI,GAAGA,EAAI,CAAC,GAExCsD,EAAS,QAAQyF,EAAsB,GAEhC;AAAA,QACN,QAAQ,MAAI;AACX,gBAAM/I,IAAM6K,EAAA;AACZ,UAAAC,EAAqB9K,EAAI,GAAGA,EAAI,GAAGA,EAAI,CAAC;AAAA,QACzC;AAAA,QACA,gBAAgB8K;AAAA,QAChB,YAAY,MAAI;AACf,UAAA9F,EAAO,MAAM,QAAQwC,GAAa,GAAG,GAAG,GAAG,CAAC,GAC5CxC,EAAO,MAAM,cAAc,IAC3BC,EAAe,QAAQuC,GACvBlE,EAAS,QAAQ8F,EAAwB;AAAA,QAC1C;AAAA,MAAA;AAAA,IAEF;AAAA,EAAA;AAEF;AAQO,MAAM2B,GAAY;AAazB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AADCC,GAZYD,IAYL,iBAAgB1H;"}
|