three-simplecloth 0.0.3 → 0.0.4

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/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2026 bandinopla
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md CHANGED
@@ -1,14 +1,20 @@
1
1
  > Based on: https://github.com/mrdoob/three.js/blob/a58e9ecf225b50e4a28a934442e854878bc2a959/examples/webgpu_compute_cloth.html
2
+
3
+ ![Cover](cover.jpg)
4
+
2
5
 
3
6
  ## What is this?
4
- A module you can use to turn pieces of meshes into cloth-like meshes that will move like cloth... kind of. It has support for colliders (spheres) and grabbing and interacting with the cloth.
7
+ A [Three.js module](https://threejs.org/) you can use to turn pieces of skinned meshes into cloth-like meshes that will move like cloth... kind of. It has support for colliders (spheres) and grabbing and interacting with the cloth.
5
8
 
6
9
  >Play with the [online demo](https://bandinopla.github.io/three-simplecloth/)
7
10
 
8
11
  ## How does this work?
9
12
  You skin your mesh normally but then you vertex paint in red color the parts that you want to turn into "cloth" and then when you use this module you basically pass a reference to the mesh that contains this painting and it will turn it into a "cloth like" mesh, blending between normal skinned and cloth using this color as a mask.
10
13
 
11
- > Read: [Article explaining implementation](https://medium.com/@pablobandinopla/simple-cloth-simulation-with-three-js-and-compute-shaders-on-skeletal-animated-meshes-acb679a70d9f)
14
+ It is probably advisable to have the cloth portions as separated meshes, not all one big giant mesh including your character. So for example, if only a small portion should be cloth, try to see if you can separate it and turning into a separated skinned mesh under the same rig. This will avoid calculating on the whole mesh and will be more performant. Obviously all part of the same skeleton as a child of the main rig.
15
+
16
+ > Read: [Article explaining implementation](https://medium.com/@pablobandinopla/simple-cloth-simulation-with-three-js-and-compute-shaders-on-skeletal-animated-meshes-acb679a70d9f)
17
+
12
18
 
13
19
  ## Install
14
20
  ```bash
@@ -16,6 +22,7 @@ npm install three-simplecloth
16
22
  ```
17
23
 
18
24
  ## Usage
25
+ Find the examples in the [playground](playground) folder. Initially I did the female bot dancing in main.ts then when I added more examples I wrote separated files for them. So main will contain part of the logic for the dancing robot demo and part of the logic to select the right demo.
19
26
 
20
27
  ```typescript
21
28
  import { SimpleCloth } from "three-simplecloth";
@@ -86,7 +86,7 @@ declare function setupClothOnSkinnedMesh(mesh: SkinnedMesh, renderer: WebGPURend
86
86
  * @param strength Strength of the magnet. Default is .5.
87
87
  * @returns An object with update and release methods.
88
88
  */
89
- activateMagnet: (magnetIndex: number, worldPos: Vector3, strength?: number) => {
89
+ activateMagnet: (magnetIndex: number, worldPos: Vector3 | Object3D, strength?: number) => {
90
90
  update: () => void;
91
91
  updatePosition: (x: number, y: number, z: number) => void;
92
92
  deactivate: () => void;
@@ -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;AAyBf,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;;;;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;;;;;;;OAOG;IACH,cAAc,EAAE,CAAE,IAAI,EAAC,QAAQ,EAAE,YAAY,EAAC,IAAI,EAAE,UAAU,EAAC,IAAI,KAAM,YAAY,CAAA;CACrF,CAAA;AAiHD,iBAAS,uBAAuB,CAC5B,IAAI,EAAE,WAAW,EACjB,QAAQ,EAAE,cAAc,EAC3B,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC;;;;;;IAif5B;;;;;OAKG;oBACmB,MAAM;IAuB5B;;;;;;;;;OASG;kCAC2B,MAAM,YAAW,OAAO;;4BAqBjC,MAAM,KAAI,MAAM,KAAI,MAAM;;;EAahD;AAGD;;;;GAIG;AACH,qBAAa,WAAW;IAEvB;;;;;;;;;OASG;IACH,MAAM,CAAC,aAAa,iCAA2B;CAC/C"}
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;AAyBf,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;;;;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;;;;;;;OAOG;IACH,cAAc,EAAE,CAAE,IAAI,EAAC,QAAQ,EAAE,YAAY,EAAC,IAAI,EAAE,UAAU,EAAC,IAAI,KAAM,YAAY,CAAA;CACrF,CAAA;AAiHD,iBAAS,uBAAuB,CAC5B,IAAI,EAAE,WAAW,EACjB,QAAQ,EAAE,cAAc,EAC3B,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC;;;;;;IAof5B;;;;;OAKG;oBACmB,MAAM;IAuB5B;;;;;;;;;OASG;kCAC2B,MAAM,YAAW,OAAO,GAAC,QAAQ;;4BAkB9B,MAAM,KAAI,MAAM,KAAI,MAAM;;;EAiC5D;AAGD;;;;GAIG;AACH,qBAAa,WAAW;IAEvB;;;;;;;;;OASG;IACH,MAAM,CAAC,aAAa,iCAA2B;CAC/C"}
package/dist/index.js CHANGED
@@ -1,42 +1,42 @@
1
1
  var Tt = Object.defineProperty;
2
- var Ut = (t, i, l) => i in t ? Tt(t, i, { enumerable: !0, configurable: !0, writable: !0, value: l }) : t[i] = l;
3
- var lt = (t, i, l) => Ut(t, typeof i != "symbol" ? i + "" : i, l);
4
- import { Vector3 as W, BufferAttribute as ut, Matrix4 as Wt, DoubleSide as kt, Scene as Dt } from "three";
5
- import { uniform as U, instancedArray as N, objectWorldMatrix as qt, computeSkinning as Ct, storage as Et, Fn as I, If as A, instanceIndex as p, Return as K, mix as Rt, Loop as dt, bitcast as gt, select as mt, triNoise3D as bt, time as Bt, float as pt, uint as Lt, attribute as ft, cross as Yt, transformNormalToView as $t, frontFacing as jt } from "three/tsl";
2
+ var Wt = (t, i, l) => i in t ? Tt(t, i, { enumerable: !0, configurable: !0, writable: !0, value: l }) : t[i] = l;
3
+ var ut = (t, i, l) => Wt(t, typeof i != "symbol" ? i + "" : i, l);
4
+ import { Vector3 as z, BufferAttribute as dt, Matrix4 as Ut, DoubleSide as kt, Scene as Dt } from "three";
5
+ import { uniform as U, instancedArray as F, objectWorldMatrix as qt, computeSkinning as Ct, storage as Et, Fn as T, If as S, instanceIndex as f, Return as tt, mix as Rt, Loop as gt, bitcast as mt, select as pt, triNoise3D as bt, time as Bt, float as ft, uint as Lt, attribute as xt, cross as Yt, transformNormalToView as $t, frontFacing as jt } from "three/tsl";
6
6
  import { StorageInstancedBufferAttribute as Xt, MeshPhysicalNodeMaterial as Zt } from "three/webgpu";
7
- const L = new W();
7
+ const Y = new z();
8
8
  function Gt(t, i = "color") {
9
- const l = t.geometry, o = l.attributes.position, d = l.getAttribute(i), g = o.count, a = /* @__PURE__ */ new Map(), x = /* @__PURE__ */ new Map(), m = new Uint32Array(g), Z = [], k = [], D = [];
10
- let F = 0;
11
- const S = new W();
9
+ const l = t.geometry, o = l.attributes.position, d = l.getAttribute(i), g = o.count, a = /* @__PURE__ */ new Map(), h = /* @__PURE__ */ new Map(), m = new Uint32Array(g), G = [], k = [], D = [];
10
+ let W = 0;
11
+ const N = new z();
12
12
  for (let s = 0; s < g; s++) {
13
- const c = o.getX(s), y = o.getY(s), f = o.getZ(s), h = d ? d.getY(s) : 0, j = `${c},${y},${f}`;
14
- a.has(j) || (a.set(j, F), S.set(c, y, f), S.applyMatrix4(t.matrixWorld), Z.push(S.x, S.y, S.z, h), F++), m[s] = a.get(j);
13
+ const c = o.getX(s), v = o.getY(s), x = o.getZ(s), w = d ? d.getY(s) : 0, X = `${c},${v},${x}`;
14
+ a.has(X) || (a.set(X, W), N.set(c, v, x), N.applyMatrix4(t.matrixWorld), G.push(N.x, N.y, N.z, w), W++), m[s] = a.get(X);
15
15
  }
16
16
  const q = [];
17
- new W(), new W();
18
- const C = /* @__PURE__ */ new Set(), Y = (s, c) => {
19
- x.has(s) ? x.get(s).push(c) : x.set(s, [c]);
17
+ new z(), new z();
18
+ const C = /* @__PURE__ */ new Set(), $ = (s, c) => {
19
+ h.has(s) ? h.get(s).push(c) : h.set(s, [c]);
20
20
  }, E = (s, c) => {
21
- const y = s + "-" + c, f = c + "-" + s;
22
- if (C.has(y) || C.has(f))
21
+ const v = s + "-" + c, x = c + "-" + s;
22
+ if (C.has(v) || C.has(x))
23
23
  return;
24
- const h = q.length / 4;
25
- q.push(s, c, 0, 0), Y(s, h), Y(c, h), C.add(y);
24
+ const w = q.length / 4;
25
+ q.push(s, c, 0, 0), $(s, w), $(c, w), C.add(v);
26
26
  }, u = l.index.array;
27
27
  for (let s = 0; s < u.length; s += 3) {
28
- const c = m[u[s]], y = m[u[s + 1]], f = m[u[s + 2]];
29
- E(c, y), E(f, y), E(c, f);
30
- const h = D.length;
31
- D.push([c, y, f]), k[u[s]] = h, k[u[s + 1]] = h, k[u[s + 2]] = h;
28
+ const c = m[u[s]], v = m[u[s + 1]], x = m[u[s + 2]];
29
+ E(c, v), E(x, v), E(c, x);
30
+ const w = D.length;
31
+ D.push([c, v, x]), k[u[s]] = w, k[u[s + 1]] = w, k[u[s + 2]] = w;
32
32
  }
33
- const $ = [], R = new Uint32Array(F);
34
- for (let s = 0; s < F; s++) {
35
- R[s] = $.length;
36
- const c = x.get(s) || [];
33
+ const j = [], R = new Uint32Array(W);
34
+ for (let s = 0; s < W; s++) {
35
+ R[s] = j.length;
36
+ const c = h.get(s) || [];
37
37
  if (c.length == 0)
38
38
  debugger;
39
- $.push(c.length, ...c);
39
+ j.push(c.length, ...c);
40
40
  }
41
41
  return {
42
42
  vFaces: new Uint32Array(D.flat()),
@@ -44,12 +44,12 @@ function Gt(t, i = "color") {
44
44
  vVertexToFace: new Uint32Array(k),
45
45
  // uint
46
46
  springsPointer: new Uint32Array(R),
47
- springsPerVertexArray: new Uint32Array($),
48
- uniqueCount: F,
47
+ springsPerVertexArray: new Uint32Array(j),
48
+ uniqueCount: W,
49
49
  // unique vertices
50
50
  indices: m,
51
51
  // for each vertice, stores the index of the unique one in the vPos array
52
- vPos: new Float32Array(Z),
52
+ vPos: new Float32Array(G),
53
53
  // 4 components, a vector 3 positions of the unique vertices ( in world space ) + the weight
54
54
  springs: new Uint32Array(q)
55
55
  // Pair of index points in vPos representing a spring
@@ -68,10 +68,10 @@ function Ot(t, i, l = 1) {
68
68
  }), o.forEach((g) => {
69
69
  var m;
70
70
  const a = g.position;
71
- let x;
72
- if (a.userData.stickto && (x = (m = i.getBoneByName) == null ? void 0 : m.call(i, a.userData.stickto.replaceAll(/[\.\:]/g, "")), !x))
71
+ let h;
72
+ if (a.userData.stickto && (h = (m = i.getBoneByName) == null ? void 0 : m.call(i, a.userData.stickto.replaceAll(/[\.\:]/g, "")), !h))
73
73
  throw new Error("Bone not found for collider: " + a.userData.stickto) + " ???";
74
- d.attach(a), g.radius = Math.abs(a.getWorldScale(L).x) * l, x == null || x.attach(a);
74
+ d.attach(a), g.radius = Math.abs(a.getWorldScale(Y).x) * l, h == null || h.attach(a);
75
75
  }), o;
76
76
  }
77
77
  function _t(t, i, l) {
@@ -99,20 +99,20 @@ function Ht(t, i, l) {
99
99
  stiffness: 0.4,
100
100
  dampening: 0.5,
101
101
  colliderRadiusMultiplier: 1,
102
- windPerSecond: new W(0, 0, 0),
103
- gravityPerSecond: new W(0, -0.3, 0),
102
+ windPerSecond: new z(0, 0, 0),
103
+ gravityPerSecond: new z(0, -0.3, 0),
104
104
  updateMaterial: _t,
105
105
  magnets: 0,
106
106
  ...l
107
107
  }, d = l != null && l.collidersRoot ? Ot(l.collidersRoot, t.skeleton, o.colliderRadiusMultiplier) : [], {
108
108
  indices: g,
109
109
  uniqueCount: a,
110
- vPos: x,
110
+ vPos: h,
111
111
  springs: m,
112
- springsPointer: Z,
112
+ springsPointer: G,
113
113
  springsPerVertexArray: k,
114
114
  vFaces: D,
115
- vVertexToFace: F
115
+ vVertexToFace: W
116
116
  } = Gt(t, o.colorAttributeName);
117
117
  if (o.logStats) {
118
118
  console.group("Stats"), console.log("vertices", a), console.log("edges", m.length / 4), console.log("faces", D.length / 3);
@@ -127,96 +127,98 @@ function Ht(t, i, l) {
127
127
  }
128
128
  console.groupEnd();
129
129
  }
130
- t.geometry.setAttribute("uniqueIndex", new ut(g, 1)), t.geometry.setAttribute("faceIndex", new ut(F, 1));
131
- const S = U(o.stiffness), q = U(o.dampening), C = U(0), Y = U(o.gravityPerSecond, "vec3"), E = U(o.windPerSecond, "vec3"), u = N(x, "vec4"), $ = N(a, "vec3"), R = new Uint32Array(a * 2);
130
+ t.geometry.setAttribute("uniqueIndex", new dt(g, 1)), t.geometry.setAttribute("faceIndex", new dt(W, 1));
131
+ const N = U(o.stiffness), q = U(o.dampening), C = U(0), $ = U(o.gravityPerSecond, "vec3"), E = U(o.windPerSecond, "vec3"), u = F(h, "vec4"), j = F(a, "vec3"), R = new Uint32Array(a * 2);
132
132
  for (let e = 0; e < a; e++)
133
- R[e * 2] = Z[e], R[e * 2 + 1] = 0;
134
- const s = N(R, "uvec2"), c = N(
133
+ R[e * 2] = G[e], R[e * 2 + 1] = 0;
134
+ const s = F(R, "uvec2"), c = F(
135
135
  k,
136
136
  "uint"
137
- ), y = m.length / 4, f = g.length, h = N(m, "uvec4"), j = N(D, "uvec3"), Q = N(g, "uint"), b = N(Math.max(1, o.magnets), "vec4"), G = U(0, "uint"), tt = qt(t), et = Ct(t), xt = new Float32Array(Math.max(1, (d == null ? void 0 : d.length) ?? 0) * 4), H = new Xt(xt, 4), nt = Et(H, "vec4"), O = U(new Wt()), yt = I(() => {
138
- A(p.greaterThanEqual(f), () => K());
139
- const e = Q.element(p), n = u.element(e), r = tt.mul(et);
137
+ ), v = m.length / 4, x = g.length, w = F(m, "uvec4"), X = F(D, "uvec3"), et = F(g, "uint"), b = F(Math.max(1, o.magnets), "vec4"), B = U(0, "uint"), nt = qt(t), ot = Ct(t), yt = new Float32Array(Math.max(1, (d == null ? void 0 : d.length) ?? 0) * 4), J = new Xt(yt, 4), st = Et(J, "vec4"), O = U(new Ut()), ht = T(() => {
138
+ S(f.greaterThanEqual(x), () => tt());
139
+ const e = et.element(f), n = u.element(e), r = nt.mul(ot);
140
140
  n.xyz.assign(r);
141
- })().compute(f).setName("Initialize skinning points"), ht = I(() => {
142
- A(p.greaterThanEqual(f), () => K());
143
- const e = Q.element(p), n = u.element(e), r = n.w, v = tt.mul(et);
144
- n.xyz.assign(Rt(n.xyz, v, r));
145
- })().compute(f).setName("Update skinning points"), vt = I(() => {
146
- A(p.greaterThanEqual(a), () => {
147
- K();
141
+ })().compute(x).setName("Initialize skinning points"), vt = T(() => {
142
+ S(f.greaterThanEqual(x), () => tt());
143
+ const e = et.element(f), n = u.element(e), r = n.w, p = nt.mul(ot);
144
+ n.xyz.assign(Rt(n.xyz, p, r));
145
+ })().compute(x).setName("Update skinning points"), Mt = T(() => {
146
+ S(f.greaterThanEqual(a), () => {
147
+ tt();
148
148
  });
149
- const e = u.element(p), n = $.element(p), r = e.w.oneMinus(), M = s.element(p).x, T = c.element(M), P = M.add(1).toVar("ptrStart"), V = P.add(T).toVar("ptrEnd");
150
- n.mulAssign(q), dt(
151
- { start: P, end: V, type: "uint", condition: "<" },
152
- ({ i: w }) => {
153
- const X = c.element(w).toVar("springId"), z = h.element(X).toVar(), B = gt(z.z, "float"), _ = z.x, J = z.y, Nt = mt(_.equal(p), J, _), at = u.element(Nt).xyz.sub(e.xyz), ct = at.length().max(1e-6), It = at.div(ct), Ft = ct.sub(B).mul(S).mul(0.5);
149
+ const e = u.element(f), n = j.element(f), r = e.w.oneMinus(), A = s.element(f).x, P = c.element(A), M = A.add(1).toVar("ptrStart"), y = M.add(P).toVar("ptrEnd");
150
+ n.mulAssign(q), gt(
151
+ { start: M, end: y, type: "uint", condition: "<" },
152
+ ({ i: V }) => {
153
+ const Z = c.element(V).toVar("springId"), I = w.element(Z).toVar(), L = mt(I.z, "float"), H = I.x, Q = I.y, Nt = pt(H.equal(f), Q, H), ct = u.element(Nt).xyz.sub(e.xyz), lt = ct.length().max(1e-6), It = ct.div(lt), Ft = lt.sub(L).mul(N).mul(0.5);
154
154
  n.addAssign(It.mul(Ft));
155
155
  }
156
156
  );
157
- const zt = bt(e, 1, Bt).sub(0.2).mul(0.1).mul(E);
158
- if (n.addAssign(zt), d)
159
- for (let w = 0; w < d.length; w++) {
160
- const X = nt.element(w).xyz, z = pt(nt.element(w).w), B = e.add(n).sub(X), _ = B.length(), J = z.sub(_).max(0).mul(B.normalize()).mul(3);
161
- n.addAssign(J);
157
+ const K = bt(e, 1, Bt).sub(0.2).mul(0.1).mul(E);
158
+ if (n.addAssign(K), d)
159
+ for (let V = 0; V < d.length; V++) {
160
+ const Z = st.element(V).xyz, I = ft(st.element(V).w), L = e.add(n).sub(Z), H = L.length(), Q = I.sub(H).max(0).mul(L.normalize()).mul(3);
161
+ n.addAssign(Q);
162
162
  }
163
- n.addAssign(Y.mul(r).mul(C));
164
- const it = s.element(p).y;
165
- A(it.greaterThan(0), () => {
166
- const w = b.element(it.sub(1)), X = w.xyz, z = w.w;
167
- A(z.greaterThan(0), () => {
168
- const B = X.sub(e.xyz).mul(z);
169
- e.xyz.addAssign(B);
163
+ n.addAssign($.mul(r).mul(C));
164
+ const at = s.element(f).y;
165
+ S(at.greaterThan(0), () => {
166
+ const V = b.element(at.sub(1)), Z = V.xyz, I = V.w;
167
+ S(I.greaterThan(0), () => {
168
+ const L = Z.sub(e.xyz).mul(I);
169
+ e.xyz.addAssign(L);
170
170
  });
171
171
  }).Else(() => {
172
172
  e.xyz.addAssign(n);
173
173
  });
174
- })().compute(a).setName("compute Vertex Forces"), Mt = I(() => {
175
- A(p.lessThan(y), () => {
176
- const e = h.element(p), n = e.z, r = e.x, v = e.y, M = u.element(r).xyz, V = u.element(v).xyz.sub(M).length().max(1e-6);
177
- n.assign(gt(V, "uint"));
174
+ })().compute(a).setName("compute Vertex Forces"), wt = T(() => {
175
+ S(f.lessThan(v), () => {
176
+ const e = w.element(f), n = e.z, r = e.x, p = e.y, A = u.element(r).xyz, y = u.element(p).xyz.sub(A).length().max(1e-6);
177
+ n.assign(mt(y, "uint"));
178
178
  });
179
- })().compute(y).setName("calculate Rest Lengths"), wt = I(() => {
180
- const e = b.element(0).xyz, n = Lt(0).toVar(), r = pt(1e4).toVar("minDist");
181
- dt(a, ({ i: M }) => {
182
- const V = u.element(M).xyz.sub(e).length();
183
- A(V.lessThan(r), () => {
184
- r.assign(V), n.assign(M);
179
+ })().compute(v).setName("calculate Rest Lengths"), At = T(() => {
180
+ const e = b.element(B).xyz, n = Lt(0).toVar(), r = ft(1e4).toVar("minDist");
181
+ gt(a, ({ i: p }) => {
182
+ const M = u.element(p).xyz.sub(e).length(), y = s.element(p).y;
183
+ S(y.equal(0).and(M.lessThan(r)), () => {
184
+ r.assign(M), n.assign(p);
185
185
  });
186
- }), s.element(n).y.assign(G.add(1));
187
- })().compute(1).setName("assign Vertices To Magnet"), At = I(() => {
188
- A(p.lessThan(a), () => {
189
- const e = s.element(p);
190
- A(e.y.equal(G.toUint().add(1)), () => {
186
+ }), S(r.lessThan(1e4), () => {
187
+ s.element(n).y.assign(B.add(1));
188
+ });
189
+ })().compute(1).setName("assign Vertices To Magnet"), St = T(() => {
190
+ S(f.lessThan(a), () => {
191
+ const e = s.element(f);
192
+ S(e.y.equal(B.toUint().add(1)), () => {
191
193
  e.y.assign(0);
192
194
  });
193
195
  });
194
- })().compute(a).setName("remove Vertices From Magnet"), St = I(() => {
195
- const e = ft("uniqueIndex", "uint"), n = u.element(e).xyz;
196
+ })().compute(a).setName("remove Vertices From Magnet"), Pt = T(() => {
197
+ const e = xt("uniqueIndex", "uint"), n = u.element(e).xyz;
196
198
  return O.mul(n);
197
199
  })();
198
200
  t.isSkinnedMesh = !1;
199
- const ot = I(() => {
200
- const e = ft("faceIndex", "uint"), n = j.element(e), r = u.element(n.x).toVar(), v = u.element(n.y).toVar(), M = u.element(n.z).toVar(), T = v.sub(r), P = M.sub(r), V = Yt(T, P).normalize(), rt = O.transformDirection(V);
201
- return $t(rt);
202
- })().toVarying(), Pt = mt(jt, ot, ot.negate()), Vt = () => {
201
+ const rt = T(() => {
202
+ const e = xt("faceIndex", "uint"), n = X.element(e), r = u.element(n.x).toVar(), p = u.element(n.y).toVar(), A = u.element(n.z).toVar(), P = p.sub(r), M = A.sub(r), y = Yt(P, M).normalize(), _ = O.transformDirection(y);
203
+ return $t(_);
204
+ })().toVarying(), Vt = pt(jt, rt, rt.negate()), zt = () => {
203
205
  if (!(d != null && d.length))
204
206
  return;
205
- const e = H.array;
207
+ const e = J.array;
206
208
  for (let n = 0; n < d.length; n++) {
207
209
  const r = d[n];
208
- r.position instanceof W ? L.copy(r.position) : (r.position.updateMatrixWorld(!0), r.position.getWorldPosition(L)), e[n * 4] = L.x, e[n * 4 + 1] = L.y, e[n * 4 + 2] = L.z, e[n * 4 + 3] = r.radius;
210
+ r.position instanceof z ? Y.copy(r.position) : (r.position.updateMatrixWorld(!0), r.position.getWorldPosition(Y)), e[n * 4] = Y.x, e[n * 4 + 1] = Y.y, e[n * 4 + 2] = Y.z, e[n * 4 + 3] = r.radius;
209
211
  }
210
- H.needsUpdate = !0;
212
+ J.needsUpdate = !0;
211
213
  };
212
- t.material = o.updateMaterial(t.material, St, Pt), O.value.copy(t.matrixWorld).invert();
213
- let st = !1;
214
+ t.material = o.updateMaterial(t.material, Pt, Vt), O.value.copy(t.matrixWorld).invert();
215
+ let it = !1;
214
216
  return requestAnimationFrame(() => {
215
- i.compute(yt), i.compute(Mt), st = !0;
217
+ i.compute(ht), i.compute(wt), it = !0;
216
218
  }), {
217
- stiffnessUniform: S,
219
+ stiffnessUniform: N,
218
220
  dampeningUniform: q,
219
- gravityUniform: Y,
221
+ gravityUniform: $,
220
222
  windUniform: E,
221
223
  colliders: d,
222
224
  /**
@@ -226,10 +228,10 @@ function Ht(t, i, l) {
226
228
  * @param steps number of steps to run the simulation ( more steps = more "stable" but slower )
227
229
  */
228
230
  update: (e, n = 11) => {
229
- if (st) {
230
- t.updateMatrixWorld(), O.value.copy(t.matrixWorld).invert(), i.compute(ht), Vt(), C.value = e / n;
231
+ if (it) {
232
+ t.updateMatrixWorld(), O.value.copy(t.matrixWorld).invert(), i.compute(vt), zt(), C.value = e / n;
231
233
  for (let r = 0; r < n; r++)
232
- i.compute(vt);
234
+ i.compute(Mt);
233
235
  }
234
236
  },
235
237
  /**
@@ -243,16 +245,19 @@ function Ht(t, i, l) {
243
245
  * @returns An object with update and release methods.
244
246
  */
245
247
  activateMagnet: (e, n, r = 1) => {
246
- const v = () => {
247
- b.value.setXYZW(e, n.x, n.y, n.z, r), b.value.needsUpdate = !0;
248
+ const p = new z(), A = () => (n instanceof z ? p.copy(n) : n.getWorldPosition(p), p), P = (y, _, K) => {
249
+ b.value.setXYZW(e, y, _, K, r), b.value.needsUpdate = !0;
248
250
  };
249
- return t.updateMatrixWorld(), G.value = e, v(), i.compute(wt), {
250
- update: v,
251
- updatePosition: (M, T, P) => {
252
- n.set(M, T, P), v();
251
+ t.updateMatrixWorld(!0), B.value = e, B.needsUpdate = !0;
252
+ const M = A();
253
+ return P(M.x, M.y, M.z), i.compute(At), {
254
+ update: () => {
255
+ const y = A();
256
+ P(y.x, y.y, y.z);
253
257
  },
258
+ updatePosition: P,
254
259
  deactivate: () => {
255
- b.value.setXYZW(e, 0, 0, 0, 0), b.value.needsUpdate = !0, G.value = e, i.compute(At);
260
+ b.value.setXYZW(e, 0, 0, 0, 0), b.value.needsUpdate = !0, B.value = e, i.compute(St);
256
261
  }
257
262
  };
258
263
  }
@@ -270,7 +275,7 @@ class Jt {
270
275
  * @param options The options for the simulation.
271
276
  * @returns The cloth simulation's API.
272
277
  */
273
- lt(Jt, "onSkinnedMesh", Ht);
278
+ ut(Jt, "onSkinnedMesh", Ht);
274
279
  export {
275
280
  Jt as SimpleCloth
276
281
  };
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(0).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\n\t\t\tIf( 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\tconst ids = vVertexInts.element(selectedIndex);\n\t\tids.y.assign(magnetToAssign.add(1));\n\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, strength = 1 ) => { \n \n \n\t\t\tconst updateMagnetPosition = ()=>{\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 , worldPos.x, worldPos.y, worldPos.z, strength);\n\t\t\t\tmagnet.value.needsUpdate = true; \n\t\t\t}\n\n\t\t\tmesh.updateMatrixWorld();\n\n\t\t\tmagnetToAssign.value = magnetIndex; \n\n\t\t\tupdateMagnetPosition()\n\t\t\t\n\t\t\trenderer.compute(assignVerticesToMagnet); \n\n\t\t\treturn {\n\t\t\t\tupdate: updateMagnetPosition,\n\t\t\t\tupdatePosition: (x:number, y:number, z:number)=>{\n\t\t\t\t\tworldPos.set(x, y, z);\n\t\t\t\t\tupdateMagnetPosition();\n\t\t\t\t},\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","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","removeVerticesFromMagnet","ids","positionNode_","attribute","vNormal","face","v0","v1","v2","edge1","edge2","normal","cross","localNormal","transformNormalToView","frontFacing","updateCollidersPositions","init","steps","worldPos","strength","updateMagnetPosition","y","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} 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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "three-simplecloth",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "type": "module",
5
5
  "description": "Simple cloth simulation for Three.js WebGPU",
6
6
  "repository": {