three-simplecloth 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,11 +1,15 @@
1
1
  > Based on: https://github.com/mrdoob/three.js/blob/a58e9ecf225b50e4a28a934442e854878bc2a959/examples/webgpu_compute_cloth.html
2
2
 
3
3
  ## 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.
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.
5
+
6
+ >Play with the [online demo](https://bandinopla.github.io/three-simplecloth/)
5
7
 
6
8
  ## How does this work?
7
9
  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.
8
10
 
11
+ > Read: [Article explaining implementation](https://medium.com/@pablobandinopla/simple-cloth-simulation-with-three-js-and-compute-shaders-on-skeletal-animated-meshes-acb679a70d9f)
12
+
9
13
  ## Install
10
14
  ```bash
11
15
  npm install three-simplecloth
@@ -20,7 +24,7 @@ import { SimpleCloth } from "three-simplecloth";
20
24
  // this will modify the material of the "clothing" Skinned mesh
21
25
  // and return a handler you must call to update the cloth simulation.
22
26
  //
23
- const cloth = SimpleCloth.onSkinnedMesh( clothing, renderer );
27
+ const cloth = SimpleCloth.onSkinnedMesh( clothing, renderer, { ...config... } );
24
28
 
25
29
  function animate(delta: number) {
26
30
  cloth.update(delta);
@@ -41,3 +45,28 @@ The third parameter is a config object:
41
45
  | `windPerSecond` | `Vector3` | Wind DIRECTION in world space (noise will be used to add variation). |
42
46
  | `gravityPerSecond` | `Vector3` | Gravity force in world space. |
43
47
  | `updateMaterial` | `function` | A function to override the current skinned mesh material. It receives the material and 2 TSL nodes: vertexNode and normalNode.
48
+ | `magnets` | `number` | [Optional] How many magnets will interact with the cloth (used for grabbing vertices)
49
+
50
+ # Adding colliders
51
+ The code will scan for objects in `collidersRoot` with `userData.stickto="bonename"` OR `userData.clothCollider=true` properties. It will use the scale X and will asume uniform scale, because colliders are spheres. And on every run it will update the position of the colliders so you can move them via code and the cloth will react to them.
52
+
53
+ # Magnets: Grabbing the cloth
54
+ To create the interaction of grabbing and relesing the cloth the system is designed to, when provided a point in world space, find the closest vertex to that point and "grab" it. Then, you call a callback to release it.
55
+
56
+ ```javascript
57
+ // activate magnet at index 0
58
+ const grabHandler = yourCloth.activateMagnet( 0, pointInTheSceneOrObject3D );
59
+
60
+ // later at some point in your code, to move it...
61
+ grabHandler.update(); // use this if you originally passed an object3d that you are moving yourself... this method will sync the position.
62
+
63
+ // If you want to manually pass the values you can call
64
+ grabHandler.updatePosition(x,y,z);
65
+
66
+ //Then when you want to release it so the vertex go back to normal...
67
+ grabHandler.deactivate()
68
+ ```
69
+
70
+
71
+ # Collab / Improve
72
+ Pull requests welcome. If you can improve the math behind the physics, be my guest. I am not a physics expert, I just wanted to have a simple cloth simulation in three.js that I could use in my projects.
@@ -1,5 +1,23 @@
1
1
  import { Material, Object3D, Vector3, SkinnedMesh } from 'three';
2
2
  import { Node, NodeMaterial, WebGPURenderer } from 'three/webgpu';
3
+ type Collider = {
4
+ radius: number;
5
+ position: Object3D | Vector3;
6
+ };
7
+ export type MagnetAPI = {
8
+ /**
9
+ * Updates the world position of the magnet by re-reading the original vector 3 passed as position
10
+ */
11
+ update: VoidFunction;
12
+ /**
13
+ * Updates the world position of the magnet by setting it to these values
14
+ */
15
+ updatePosition: (x: number, y: number, z: number) => void;
16
+ /**
17
+ * clears every vertex from this magnet. This is the "release" action.
18
+ */
19
+ deactivate: VoidFunction;
20
+ };
3
21
  type ClothConfig = {
4
22
  /**
5
23
  * Usually it is "color" but sometimes it may be other like "color_1"
@@ -11,6 +29,8 @@ type ClothConfig = {
11
29
  logStats?: boolean;
12
30
  /**
13
31
  * The root object to search for colliders.
32
+ * Colliders are expected to be empty objects with uniform scale and `userData.stickto="bonename"` OR `userData.clothCollider=true` properties.
33
+ * The radius of the collider will be the absolute value of `scale.x * colliderRadiusMultiplier`.
14
34
  */
15
35
  collidersRoot?: Object3D;
16
36
  stiffness?: number;
@@ -29,26 +49,48 @@ type ClothConfig = {
29
49
  * Gravity force in world space
30
50
  */
31
51
  gravityPerSecond?: Vector3;
52
+ /**
53
+ * How many magnets to use ( cloch grab manipulators )
54
+ */
55
+ magnets: number;
32
56
  /**
33
57
  * The material used by the SkinnedMesh to turn into "cloth" must apply and use these TSL nodes...
34
58
  * By default it will try to guess the most common case when you import a model from blender...
35
59
  *
36
- * @param vertexNode
60
+ * @param positionNode
37
61
  * @param normalNode
38
62
  * @returns
39
63
  */
40
- updateMaterial: (base: Material, vertexNode: Node, normalNode: Node) => NodeMaterial;
64
+ updateMaterial: (base: Material, positionNode: Node, normalNode: Node) => NodeMaterial;
41
65
  };
42
66
  declare function setupClothOnSkinnedMesh(mesh: SkinnedMesh, renderer: WebGPURenderer, config?: Partial<ClothConfig>): {
43
67
  stiffnessUniform: import('three/webgpu').UniformNode<number | undefined>;
44
68
  dampeningUniform: import('three/webgpu').UniformNode<number | undefined>;
45
69
  gravityUniform: import('three/webgpu').UniformNode<Vector3 | undefined>;
46
70
  windUniform: import('three/webgpu').UniformNode<Vector3 | undefined>;
71
+ colliders: Collider[];
47
72
  /**
73
+ * Runs the cloth simulation...
74
+ *
48
75
  * @param delta seconds passed since last render
49
76
  * @param steps number of steps to run the simulation ( more steps = more "stable" but slower )
50
77
  */
51
78
  update: (delta: number, steps?: number) => void;
79
+ /**
80
+ * Grab the closest vertex to the given magnet. An object to update the magnet position is returned.
81
+ * This is designed so that when you call this, it is assumed to be during a "grab" action. You then
82
+ * should call the `deactivate` callback to produce the "release" action.
83
+ *
84
+ * @param magnetIndex Index of the magnet to use.
85
+ * @param worldPos World position. This object now controls the position of the magnet. you can change the XYZ.
86
+ * @param strength Strength of the magnet. Default is .5.
87
+ * @returns An object with update and release methods.
88
+ */
89
+ activateMagnet: (magnetIndex: number, worldPos: Vector3, strength?: number) => {
90
+ update: () => void;
91
+ updatePosition: (x: number, y: number, z: number) => void;
92
+ deactivate: () => void;
93
+ };
52
94
  };
53
95
  /**
54
96
  * 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,EAGH,QAAQ,EAGR,QAAQ,EAIR,OAAO,EACP,KAAK,WAAW,EACnB,MAAM,OAAO,CAAC;AA4Bf,OAAO,EAEH,IAAI,EACJ,YAAY,EAEZ,KAAK,cAAc,EACtB,MAAM,cAAc,CAAC;AAoMtB,KAAK,WAAW,GAAG;IAElB;;OAEG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;OAEG;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;;;;;;;OAOG;IACH,cAAc,EAAE,CAAE,IAAI,EAAC,QAAQ,EAAE,UAAU,EAAC,IAAI,EAAE,UAAU,EAAC,IAAI,KAAM,YAAY,CAAA;CACnF,CAAA;AA4GD,iBAAS,uBAAuB,CAC5B,IAAI,EAAE,WAAW,EACjB,QAAQ,EAAE,cAAc,EAC3B,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC;;;;;IAub5B;;;OAGG;oBACmB,MAAM;EAsB7B;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;;;;;;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"}
package/dist/index.js CHANGED
@@ -1,91 +1,86 @@
1
- var yt = Object.defineProperty;
2
- var xt = (t, a, l) => a in t ? yt(t, a, { enumerable: !0, configurable: !0, writable: !0, value: l }) : t[a] = l;
3
- var ot = (t, a, l) => xt(t, typeof a != "symbol" ? a + "" : a, l);
4
- import { Vector3 as W, BufferAttribute as st, Matrix4 as vt, DoubleSide as wt, Scene as At } from "three";
5
- import { uniform as D, instancedArray as P, objectWorldMatrix as St, computeSkinning as Mt, storage as Pt, Fn as F, If as N, instanceIndex as u, Return as K, mix as Vt, Loop as Ft, triNoise3D as Nt, time as It, float as kt, attribute as rt, cameraProjectionMatrix as zt, cameraViewMatrix as Tt, vec4 as Wt, cross as Ut, transformNormalToView as Bt, select as Lt, frontFacing as Rt } from "three/tsl";
6
- import { StorageInstancedBufferAttribute as Et, MeshPhysicalNodeMaterial as qt } from "three/webgpu";
7
- const $ = new W();
8
- function Ct(t, a = "color") {
9
- const l = t.geometry, e = l.attributes.position, p = l.getAttribute(a), m = e.count, h = /* @__PURE__ */ new Map(), x = /* @__PURE__ */ new Map(), y = new Uint32Array(m), b = [], U = [], j = [];
10
- let V = 0;
11
- const w = new W();
12
- for (let s = 0; s < m; s++) {
13
- const i = e.getX(s), d = e.getY(s), g = e.getZ(s), v = p ? p.getY(s) : 0, k = `${i},${d},${g}`;
14
- if (!h.has(k)) {
15
- h.set(k, V), w.set(i, d, g);
16
- const Z = `${w.toArray()} ---> ${t.scale.toArray()} /// `;
17
- w.applyMatrix4(t.matrixWorld), console.log(Z, w.toArray()), b.push(w.x, w.y, w.z, v), V++;
18
- }
19
- y[s] = h.get(k);
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";
6
+ import { StorageInstancedBufferAttribute as Xt, MeshPhysicalNodeMaterial as Zt } from "three/webgpu";
7
+ const L = new W();
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();
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);
20
15
  }
21
- const B = [], G = [];
16
+ const q = [];
22
17
  new W(), new W();
23
- const L = /* @__PURE__ */ new Set(), O = (s, i) => {
24
- x.has(s) ? x.get(s).push(i) : x.set(s, [i]);
25
- }, R = (s, i) => {
26
- const d = s + "-" + i, g = i + "-" + s;
27
- if (L.has(d) || L.has(g))
18
+ const C = /* @__PURE__ */ new Set(), Y = (s, c) => {
19
+ x.has(s) ? x.get(s).push(c) : x.set(s, [c]);
20
+ }, E = (s, c) => {
21
+ const y = s + "-" + c, f = c + "-" + s;
22
+ if (C.has(y) || C.has(f))
28
23
  return;
29
- const v = B.length / 2;
30
- B.push(s, i), O(s, v), O(i, v), G.push(0), L.add(d);
31
- }, c = l.index.array;
32
- for (let s = 0; s < c.length; s += 3) {
33
- const i = y[c[s]], d = y[c[s + 1]], g = y[c[s + 2]];
34
- R(i, d), R(g, d), R(i, g);
35
- const v = j.length;
36
- j.push([i, d, g]), U[c[s]] = v, U[c[s + 1]] = v, U[c[s + 2]] = v;
24
+ const h = q.length / 4;
25
+ q.push(s, c, 0, 0), Y(s, h), Y(c, h), C.add(y);
26
+ }, u = l.index.array;
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;
37
32
  }
38
- const I = [], Y = new Uint32Array(V);
39
- for (let s = 0; s < V; s++) {
40
- Y[s] = I.length;
41
- const i = x.get(s) || [];
42
- if (i.length == 0)
33
+ const $ = [], R = new Uint32Array(F);
34
+ for (let s = 0; s < F; s++) {
35
+ R[s] = $.length;
36
+ const c = x.get(s) || [];
37
+ if (c.length == 0)
43
38
  debugger;
44
- I.push(i.length, ...i);
39
+ $.push(c.length, ...c);
45
40
  }
46
41
  return {
47
- vFaces: new Uint32Array(j.flat()),
42
+ vFaces: new Uint32Array(D.flat()),
48
43
  //vec3[]
49
- vVertexToFace: new Uint32Array(U),
44
+ vVertexToFace: new Uint32Array(k),
50
45
  // uint
51
- springsPointer: new Uint32Array(Y),
52
- springsPerVertexArray: new Uint32Array(I),
53
- uniqueCount: V,
46
+ springsPointer: new Uint32Array(R),
47
+ springsPerVertexArray: new Uint32Array($),
48
+ uniqueCount: F,
54
49
  // unique vertices
55
- indices: y,
50
+ indices: m,
56
51
  // for each vertice, stores the index of the unique one in the vPos array
57
- vPos: new Float32Array(b),
52
+ vPos: new Float32Array(Z),
58
53
  // 4 components, a vector 3 positions of the unique vertices ( in world space ) + the weight
59
- springs: new Uint32Array(B),
60
- // Pair of index points in vPos representing a spring
61
- restLengths: new Float32Array(G)
62
- //rest lengths of each spring ( in world space )
54
+ springs: new Uint32Array(q)
55
+ // Pair of index points in vPos representing a spring
63
56
  };
64
57
  }
65
- function Dt(t, a, l = 1) {
66
- const e = [];
67
- let p;
68
- return t.traverseAncestors((m) => {
69
- m instanceof At && (p = m);
70
- }), t.traverse((m) => {
71
- m.userData.stickto && e.push({
72
- position: m,
58
+ function Ot(t, i, l = 1) {
59
+ const o = [];
60
+ let d;
61
+ return t.traverseAncestors((g) => {
62
+ g instanceof Dt && (d = g);
63
+ }), t.traverse((g) => {
64
+ (g.userData.stickto || g.userData.clothCollider) && o.push({
65
+ position: g,
73
66
  radius: 0
74
67
  });
75
- }), e.forEach((m) => {
76
- const h = a.getBoneByName(m.position.userData.stickto.replaceAll(/[\.\:]/g, "")), x = m.position;
77
- if (!h)
78
- throw new Error("Bone not found for collider: " + x.userData.stickto) + " ???";
79
- p.attach(x), m.radius = Math.abs(x.getWorldScale($).x) * l, h.attach(x);
80
- }), e;
68
+ }), o.forEach((g) => {
69
+ var m;
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))
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);
75
+ }), o;
81
76
  }
82
- function $t(t, a, l) {
77
+ function _t(t, i, l) {
83
78
  if ("isNodeMaterial" in t) {
84
- const e = t;
85
- return e.vertexNode = a, e.normalNode = l, e;
79
+ const o = t;
80
+ return o.positionNode = i, o.normalNode = l, o;
86
81
  } else {
87
- const e = new qt({
88
- side: wt,
82
+ const o = new Zt({
83
+ side: kt,
89
84
  // wireframe:true,
90
85
  wireframe: t.wireframe,
91
86
  flatShading: t.flatShading,
@@ -93,12 +88,12 @@ function $t(t, a, l) {
93
88
  depthWrite: t.depthWrite,
94
89
  depthTest: t.depthTest
95
90
  });
96
- return e.vertexNode = a, e.normalNode = l, e.color = t.color, e.map = t.map, e.emissive = t.emissive, e.emissiveMap = t.emissiveMap, e.emissiveIntensity = t.emissiveIntensity, e.roughness = t.roughness, e.roughnessMap = t.roughnessMap, e.metalness = t.metalness, e.metalnessMap = t.metalnessMap, e.normalMap = t.normalMap, e.normalScale = t.normalScale, e.alphaMap = t.alphaMap, e.opacity = t.opacity, e.transparent = t.transparent, e.aoMap = t.aoMap, e;
91
+ return o.positionNode = i, o.normalNode = l, o.color = t.color, o.map = t.map, o.emissive = t.emissive, o.emissiveMap = t.emissiveMap, o.emissiveIntensity = t.emissiveIntensity, o.roughness = t.roughness, o.roughnessMap = t.roughnessMap, o.metalness = t.metalness, o.metalnessMap = t.metalnessMap, o.normalMap = t.normalMap, o.normalScale = t.normalScale, o.alphaMap = t.alphaMap, o.opacity = t.opacity, o.transparent = t.transparent, o.aoMap = t.aoMap, o;
97
92
  }
98
93
  }
99
- function bt(t, a, l) {
94
+ function Ht(t, i, l) {
100
95
  t.updateWorldMatrix(!0, !1);
101
- const e = {
96
+ const o = {
102
97
  colorAttributeName: "color",
103
98
  logStats: !1,
104
99
  stiffness: 0.4,
@@ -106,123 +101,164 @@ function bt(t, a, l) {
106
101
  colliderRadiusMultiplier: 1,
107
102
  windPerSecond: new W(0, 0, 0),
108
103
  gravityPerSecond: new W(0, -0.3, 0),
109
- updateMaterial: $t,
104
+ updateMaterial: _t,
105
+ magnets: 0,
110
106
  ...l
111
- }, p = l != null && l.collidersRoot ? Dt(l.collidersRoot, t.skeleton, e.colliderRadiusMultiplier) : [], {
112
- indices: m,
113
- uniqueCount: h,
107
+ }, d = l != null && l.collidersRoot ? Ot(l.collidersRoot, t.skeleton, o.colliderRadiusMultiplier) : [], {
108
+ indices: g,
109
+ uniqueCount: a,
114
110
  vPos: x,
115
- springs: y,
116
- restLengths: b,
117
- springsPointer: U,
118
- springsPerVertexArray: j,
119
- vFaces: V,
120
- vVertexToFace: w
121
- } = Ct(t, e.colorAttributeName);
122
- if (e.logStats) {
123
- console.group("Stats"), console.log("vertices", h), console.log("edges", y.length / 2), console.log("faces", V.length / 3), console.log("rest lengths", b.length);
124
- for (let o = 0; o < h; o++) {
111
+ springs: m,
112
+ springsPointer: Z,
113
+ springsPerVertexArray: k,
114
+ vFaces: D,
115
+ vVertexToFace: F
116
+ } = Gt(t, o.colorAttributeName);
117
+ if (o.logStats) {
118
+ console.group("Stats"), console.log("vertices", a), console.log("edges", m.length / 4), console.log("faces", D.length / 3);
119
+ for (let e = 0; e < a; e++) {
125
120
  let n = !1;
126
- for (let r = 0; r < y.length; r += 2)
127
- if (y[r] === o || y[r + 1] === o) {
121
+ for (let r = 0; r < m.length; r += 4)
122
+ if (m[r] === e || m[r + 1] === e) {
128
123
  n = !0;
129
124
  break;
130
125
  }
131
- n || console.log("WARNING!: vertex", o, "has no strings! wtf?");
126
+ n || console.log("WARNING!: vertex", e, "has no strings! wtf?");
132
127
  }
133
128
  console.groupEnd();
134
129
  }
135
- t.geometry.setAttribute("uniqueIndex", new st(m, 1)), t.geometry.setAttribute("faceIndex", new st(w, 1));
136
- const B = D(e.stiffness), G = D(e.dampening), L = D(0), O = D(e.gravityPerSecond, "vec3"), R = D(e.windPerSecond, "vec3"), c = P(x, "vec4"), I = P(h, "vec3"), Y = P(U, "uint"), s = P(
137
- j,
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);
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(
135
+ k,
138
136
  "uint"
139
- ), i = y.length / 2, d = m.length, g = P(y, "ivec2"), v = P(i, "vec3"), k = P(b, "float"), Z = P(V, "ivec3"), Q = P(m, "uint"), _ = St(t), tt = Mt(t), it = new Float32Array(((p == null ? void 0 : p.length) ?? 0) * 4), H = new Et(it, 4), et = Pt(H, "vec4"), J = D(new vt()), ct = F(() => {
140
- N(u.greaterThanEqual(d), () => K());
141
- const o = Q.element(u), n = c.element(o), r = _.mul(tt);
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);
142
140
  n.xyz.assign(r);
143
- })().compute(d).setName("Initialize skinning points"), at = F(() => {
144
- N(u.greaterThanEqual(d), () => K());
145
- const o = Q.element(u), n = c.element(o), r = n.w.pow(2), f = _.mul(tt);
146
- n.xyz.assign(Vt(n.xyz, f, r));
147
- })().compute(d).setName("Update skinning points"), lt = F(() => {
148
- N(u.lessThan(i), () => {
149
- const o = g.element(u), n = k.element(u), r = o.x, f = o.y, A = c.element(r).xyz, S = c.element(f).xyz, E = I.element(r), q = I.element(f), C = S.sub(A), M = C.length().max(1e-6), z = C.div(M);
150
- q.sub(E).dot(z).mul(0.1);
151
- const T = M.sub(n).mul(B).mul(z).mul(0.5);
152
- v.element(u).assign(T);
153
- });
154
- })().compute(i).setName("compute Spring Forces"), ut = F(() => {
155
- N(u.greaterThanEqual(h), () => {
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), () => {
156
147
  K();
157
148
  });
158
- const o = c.element(u), n = I.element(u).toVar(), r = o.w.oneMinus().pow(2), f = Y.element(u), A = s.element(f), S = f.add(1).toVar("ptrStart"), E = S.add(A).toVar("ptrEnd");
159
- n.mulAssign(G), Ft(
160
- { start: S, end: E, type: "uint", condition: "<" },
161
- ({ i: M }) => {
162
- const z = s.element(M).toVar("springId"), X = g.element(z).toVar(), T = v.element(z);
163
- N(X.x.equal(u), () => {
164
- n.addAssign(T);
165
- }).Else(() => {
166
- n.subAssign(T);
167
- });
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);
154
+ n.addAssign(It.mul(Ft));
168
155
  }
169
156
  );
170
- const C = Nt(o, 1, It).sub(0.2).mul(0.1).mul(R);
171
- if (n.addAssign(C), p)
172
- for (let M = 0; M < p.length; M++) {
173
- const z = et.element(M).xyz, X = kt(et.element(M).w), T = o.add(n).sub(z), ft = T.length(), ht = X.sub(ft).max(0).mul(T.normalize()).mul(3);
174
- n.addAssign(ht);
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);
175
162
  }
176
- n.addAssign(O.mul(r).mul(L)), o.xyz.addAssign(n);
177
- })().compute(h).setName("compute Vertex Forces"), pt = F(() => {
178
- N(u.lessThan(i), () => {
179
- const o = g.element(u), n = k.element(u), r = o.x, f = o.y, A = c.element(r).xyz, q = c.element(f).xyz.sub(A).length().max(1e-6);
180
- n.assign(q);
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);
170
+ });
171
+ }).Else(() => {
172
+ e.xyz.addAssign(n);
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"));
181
178
  });
182
- })().compute(i).setName("calculate Rest Lengths");
183
- F(() => {
184
- N(u.lessThan(i), () => {
185
- const o = g.element(u), n = k.element(u), r = o.x, f = o.y, A = c.element(r), S = c.element(f);
186
- N(n.greaterThan(0), () => {
187
- A.y.assign(n), S.y.assign(n);
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);
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)), () => {
191
+ e.y.assign(0);
188
192
  });
189
193
  });
190
- })().compute(i).setName("discriminate");
191
- const mt = F(() => {
192
- const o = c.element(
193
- rt("uniqueIndex", "uint")
194
- );
195
- return zt.mul(Tt).mul(Wt(o.xyz, 1));
196
- })(), nt = F(() => {
197
- const o = rt("faceIndex", "uint"), n = Z.element(o), r = c.element(n.x).toVar(), f = c.element(n.y).toVar(), A = c.element(n.z).toVar(), S = f.sub(r), E = A.sub(r), q = Ut(S, E).normalize(), C = J.transformDirection(q);
198
- return Bt(C);
199
- })().toVarying(), dt = Lt(Rt, nt, nt.negate()), gt = () => {
200
- if (!(p != null && p.length))
194
+ })().compute(a).setName("remove Vertices From Magnet"), St = I(() => {
195
+ const e = ft("uniqueIndex", "uint"), n = u.element(e).xyz;
196
+ return O.mul(n);
197
+ })();
198
+ 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 = () => {
203
+ if (!(d != null && d.length))
201
204
  return;
202
- const o = H.array;
203
- for (let n = 0; n < p.length; n++) {
204
- const r = p[n];
205
- r.position instanceof W ? $.copy(r.position) : (r.position.updateMatrixWorld(!0), r.position.getWorldPosition($)), o[n * 4] = $.x, o[n * 4 + 1] = $.y, o[n * 4 + 2] = $.z, o[n * 4 + 3] = r.radius;
205
+ const e = H.array;
206
+ for (let n = 0; n < d.length; n++) {
207
+ 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;
206
209
  }
207
210
  H.needsUpdate = !0;
208
211
  };
209
- return t.material = e.updateMaterial(t.material, mt, dt), J.value.copy(t.matrixWorld).invert(), a.compute(ct), a.compute(pt), {
210
- stiffnessUniform: B,
211
- dampeningUniform: G,
212
- gravityUniform: O,
213
- windUniform: R,
212
+ t.material = o.updateMaterial(t.material, St, Pt), O.value.copy(t.matrixWorld).invert();
213
+ let st = !1;
214
+ return requestAnimationFrame(() => {
215
+ i.compute(yt), i.compute(Mt), st = !0;
216
+ }), {
217
+ stiffnessUniform: S,
218
+ dampeningUniform: q,
219
+ gravityUniform: Y,
220
+ windUniform: E,
221
+ colliders: d,
214
222
  /**
223
+ * Runs the cloth simulation...
224
+ *
215
225
  * @param delta seconds passed since last render
216
226
  * @param steps number of steps to run the simulation ( more steps = more "stable" but slower )
217
227
  */
218
- update: (o, n = 11) => {
219
- t.updateMatrixWorld(), J.value.copy(t.matrixWorld).invert(), a.compute(at), gt(), L.value = o / n;
220
- for (let r = 0; r < n; r++)
221
- a.compute(lt), a.compute(ut);
228
+ 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
+ for (let r = 0; r < n; r++)
232
+ i.compute(vt);
233
+ }
234
+ },
235
+ /**
236
+ * Grab the closest vertex to the given magnet. An object to update the magnet position is returned.
237
+ * This is designed so that when you call this, it is assumed to be during a "grab" action. You then
238
+ * should call the `deactivate` callback to produce the "release" action.
239
+ *
240
+ * @param magnetIndex Index of the magnet to use.
241
+ * @param worldPos World position. This object now controls the position of the magnet. you can change the XYZ.
242
+ * @param strength Strength of the magnet. Default is .5.
243
+ * @returns An object with update and release methods.
244
+ */
245
+ 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
+ };
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();
253
+ },
254
+ deactivate: () => {
255
+ b.value.setXYZW(e, 0, 0, 0, 0), b.value.needsUpdate = !0, G.value = e, i.compute(At);
256
+ }
257
+ };
222
258
  }
223
259
  };
224
260
  }
225
- class jt {
261
+ class Jt {
226
262
  }
227
263
  /**
228
264
  * Turns the vertex painted parts of a skinned mesh into a cloth simulation.
@@ -234,8 +270,8 @@ class jt {
234
270
  * @param options The options for the simulation.
235
271
  * @returns The cloth simulation's API.
236
272
  */
237
- ot(jt, "onSkinnedMesh", bt);
273
+ lt(Jt, "onSkinnedMesh", Ht);
238
274
  export {
239
- jt as SimpleCloth
275
+ Jt as SimpleCloth
240
276
  };
241
277
  //# 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 BufferAttribute,\n DoubleSide,\n Material,\n Matrix4,\n Mesh,\n Object3D,\n Scene,\n Skeleton,\n Texture,\n Vector3,\n type SkinnedMesh,\n} from \"three\";\nimport { ShaderCallNodeInternal } from \"three/src/nodes/TSL.js\";\nimport {\n attribute,\n cameraProjectionMatrix,\n cameraViewMatrix,\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 vec4,\n float,\n\tfrontFacing,\n\tcross,\n\ttransformNormalToView,\n\ttexture,\n\tcolor,\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\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\t\n\n\t\t\tconst w = `${v.toArray()} ---> ${mesh.scale.toArray()} /// `;\n \nv.applyMatrix4(mesh.matrixWorld)\n\t\t\tconsole.log(w, v.toArray());\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.\n const springRestLengths: number[] = []; // length that the A-B pair must keep from each other.\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/2;\n\n springs.push(A, B);\n\n addSpringToVertex(A, springID);\n addSpringToVertex(B, springID); \n\t\t \n springRestLengths.push(0);\n\n springDefined.add(hash);\n };\n\n //\n // now for each triangle, create a spring from each edge...\n //\n\t/**\n\t * \n\t */\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 restLengths: new Float32Array(springRestLengths), //rest lengths of each spring ( in world space )\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 */\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 * 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 vertexNode \n\t * @param normalNode \n\t * @returns \n\t */\n\tupdateMaterial: ( base:Material, vertexNode:Node, normalNode:Node ) => NodeMaterial\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 )\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 bone = skeleton.getBoneByName( (col.position as Object3D).userData.stickto.replaceAll(/[\\.\\:]/g,\"\") );\n\t\tconst obj = col.position as Object3D;\n\n\t\tif(!bone){\n\t\t\tthrow new Error(\"Bone not found for collider: \" + obj.userData.stickto)+ \" ???\";\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\t\t//test... \n\n\t} );\n\n\n\treturn colliders;\n}\n\nfunction assumeTheModelCameFromBlenderAndWasTexturedNormally(base:Material, vertexNode:ShaderCallNodeInternal, normalNode:ShaderCallNodeInternal ) {\n\tif( \"isNodeMaterial\" in base )\n\t{\n\t\tconst m = base as NodeMaterial;\n\t\tm.vertexNode = vertexNode;\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.vertexNode = vertexNode;\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\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 restLengths,\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/2); \n\t\tconsole.log(\"faces\", vFaces.length/3);\n\t\tconsole.log(\"rest lengths\", restLengths.length);\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+=2){\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 * 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 const vSpringsPointer = instancedArray(springsPointer, \"uint\");\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 / 2; // because each spring has 2 vertices\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 a pair of A,B ids. (id of the unique vertices that this spring will try to keep at a certain distance )\n\t */\n const springsStore = instancedArray(springs, \"ivec2\");\n\n\t/**\n\t * How strong the spring is pulling the vertices\n\t */\n const springForceStore = instancedArray(totalSprings, \"vec3\");\n\n\t/**\n\t * The target distance the spring will try to keep between the two vertices it connects.\n\t */\n const restLengthsStore = instancedArray(restLengths, \"float\");\n\n\t/**\n\t * array triplets defining a face ( a triangle )\n\t */\n\tconst vFacesStore = instancedArray(vFaces, \"ivec3\");\n\t//const vVertexToFaceStore = instancedArray(vVertexToFace, \"uint\");\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 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((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\tconst computeSpringForces = Fn(()=>{\n\n\t\tIf(instanceIndex.lessThan(totalSprings), () => {\n\t\t\tconst vertexIds = springsStore.element( instanceIndex );\n\t\t\tconst restLength = restLengthsStore.element( instanceIndex ) ;\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; // world space\n\t\t\tconst postB = vPosStore.element( Bi ).xyz; // world space \n\n\t\t\tconst fA = vForceStore.element(Ai);\n\t\t\tconst fB = vForceStore.element(Bi);\n\n\t\t\tconst delta = postB.sub(posA);\n\t\t\tconst dist = delta.length().max(0.000001);\n\t\t\tconst dir = delta.div(dist);\n\n\t\t\tconst relVelocity = fB.sub(fA);\n\t\t\tconst damping = relVelocity.dot(dir).mul(0.1);\n\n\t\t\tconst force = dist.sub(restLength).mul(stiffnessUniform) .mul(dir).mul(0.5);\n\n\t\t\tspringForceStore.element(instanceIndex).assign( force ); \n\t\t}); \n\n\t})().compute( totalSprings ).setName(\"compute Spring Forces\"); \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 ).toVar() ; \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\tconst springPointer = vSpringsPointer.element(instanceIndex);\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 const springForce = springForceStore.element(springIndex) ; \n\n\t\t\t\t\tIf( spring.x.equal(instanceIndex), ()=>{\n\n\t\t\t\t\t\tforce.addAssign(springForce);\n\t\t\t\t\t})\n\t\t\t\t\t.Else(()=>{\n\t\t\t\t\t\tforce.subAssign(springForce);\n\t\t\t\t\t})\n \n\t\t\t\t\t\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 // Zero out force if pinned (mask=0) so position doesn't drift\n // Position update: position += force * mask\n\t \tposition.xyz.addAssign( force );\n\t\t//position.y.addAssign(gravityUniform.mul(mask).mul(dt) );\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\tconst calculateRestLengths = Fn(()=>{\n\t\tIf(instanceIndex.lessThan(totalSprings), () => {\n\t\t\tconst vertexIds = springsStore.element( instanceIndex );\n\t\t\tconst restLength = restLengthsStore.element( instanceIndex ) ;\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\t\t\trestLength.assign(dist);\n \n\n\t\t});\n\t})().compute( totalSprings ).setName(\"calculate Rest Lengths\"); \n\n\tconst discriminate = Fn(() => {\n\n\t\tIf(instanceIndex.lessThan(totalSprings), () => {\n\n\t\t\tconst vertexIds = springsStore.element( instanceIndex );\n\t\t\tconst restLength = restLengthsStore.element( instanceIndex ) ;\n\n\t\t\tconst Ai = vertexIds.x;\n\t\t\tconst Bi = vertexIds.y; \n\n\t\t\tconst posA = vPosStore.element(Ai);\n\t\t\tconst posB = vPosStore.element(Bi);\n\n\t\t\tIf( restLength.greaterThan(0), ()=>{\n\t\t\t\tposA.y.assign(restLength);\n\t\t\t\tposB.y.assign(restLength);\n\t\t\t})\n\n\t\t});\n\t\t\n\t})().compute( totalSprings ).setName(\"discriminate\");\n \n // Visualization material\n const vertexNode = Fn(() => {\n const customPosition = vPosStore.element(\n attribute(\"uniqueIndex\", \"uint\"),\n );\n\n return cameraProjectionMatrix\n .mul(cameraViewMatrix)\n .mul(vec4(customPosition.xyz, 1.0));\n })();\n\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\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, vertexNode, normalNode); \n\t\n\tworldMatrixInverseUniform.value.copy(mesh.matrixWorld).invert();\n\n // Initialization compute\n\trenderer.compute( initializeSkinningPosition );\n\trenderer.compute( calculateRestLengths ); \n \n return {\n\n\t\tstiffnessUniform,\n\t\tdampeningUniform,\n\t\tgravityUniform,\n\t\twindUniform,\n\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\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(computeSpringForces);\n\t\t\t\trenderer.compute(computeVertexForces);\n\t\t\t} \n },\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","w","springs","springRestLengths","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","bone","obj","assumeTheModelCameFromBlenderAndWasTexturedNormally","base","vertexNode","normalNode","m","MeshPhysicalNodeMaterial","DoubleSide","setupClothOnSkinnedMesh","renderer","config","$cfg","restLengths","hasString","j","BufferAttribute","stiffnessUniform","uniform","dampeningUniform","dt","gravityUniform","windUniform","vPosStore","instancedArray","vForceStore","vSpringsPointer","vSpringsPerVertexArray","totalSprings","countOfPoints","springsStore","springForceStore","restLengthsStore","vFacesStore","indicesStore","worldMatrix","objectWorldMatrix","skinningPosition","computeSkinning","collidersArray","colliderAttr","StorageInstancedBufferAttribute","collidersStore","storage","worldMatrixInverseUniform","Matrix4","initializeSkinningPosition","Fn","If","instanceIndex","Return","uIndex","wPos","skinningWorldPosition","updateSkinningPoints","factor","mix","computeSpringForces","vertexIds","restLength","posA","postB","fA","fB","delta","dist","dir","force","computeVertexForces","position","springPointer","springCount","ptrStart","ptrEnd","Loop","springIndex","spring","springForce","windForce","triNoise3D","time","cPos","cRad","float","deltaSphere","sphereForce","calculateRestLengths","posB","customPosition","attribute","cameraProjectionMatrix","cameraViewMatrix","vec4","vNormal","face","v0","v1","v2","edge1","edge2","normal","cross","localNormal","transformNormalToView","select","frontFacing","updateCollidersPositions","steps","SimpleCloth","__publicField"],"mappings":";;;;;;AAgDA,MAAMA,IAAI,IAAIC,EAAA;AA4Bd,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,QAAI,CAACV,EAAI,IAAIY,CAAG,GAAG;AACf,MAAAZ,EAAI,IAAIY,GAAKN,CAAW,GAKxBf,EAAE,IAAIiB,GAAGC,GAAGC,CAAC;AAItB,YAAMG,IAAI,GAAGtB,EAAE,QAAA,CAAS,SAASG,EAAK,MAAM,QAAA,CAAS;AAExDH,MAAAA,EAAE,aAAaG,EAAK,WAAW,GAC5B,QAAQ,IAAImB,GAAGtB,EAAE,QAAA,CAAS,GAEjBY,EAAK,KAAKZ,EAAE,GAAGA,EAAE,GAAGA,EAAE,GAAGoB,CAAM,GAE/BL;AAAA,IACJ;AAEA,IAAAJ,EAAQK,CAAC,IAAIP,EAAI,IAAIY,CAAG;AAAA,EAC5B;AAKA,QAAME,IAAoB,CAAA,GACpBC,IAA8B,CAAA;AACzB,MAAIvB,EAAA,GACJ,IAAIA,EAAA;AACf,QAAMwB,wBAAoB,IAAA,GAEpBC,IAAoB,CAACC,GAAgBC,MAAqB;AAC5D,IAAIlB,EAAgB,IAAIiB,CAAM,IAC1BjB,EAAgB,IAAIiB,CAAM,EAAG,KAAKC,CAAQ,IAE1ClB,EAAgB,IAAIiB,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,IAAWL,EAAQ,SAAO;AAEhC,IAAAA,EAAQ,KAAKO,GAAGC,CAAC,GAEjBL,EAAkBI,GAAGF,CAAQ,GAC7BF,EAAkBK,GAAGH,CAAQ,GAE7BJ,EAAkB,KAAK,CAAC,GAExBC,EAAc,IAAIO,CAAI;AAAA,EAC1B,GAQME,IAAW7B,EAAS,MAAO;AAGjC,WAASW,IAAI,GAAGA,IAAIkB,EAAS,QAAQlB,KAAK,GAAG;AACzC,UAAMmB,IAAKxB,EAAQuB,EAASlB,CAAC,CAAC,GACxBoB,IAAKzB,EAAQuB,EAASlB,IAAI,CAAC,CAAC,GAC5BqB,IAAK1B,EAAQuB,EAASlB,IAAI,CAAC,CAAC;AAGlC,IAAAa,EAAUM,GAAIC,CAAE,GAChBP,EAAYQ,GAAID,CAAG,GACnBP,EAAWM,GAAKE,CAAI;AAGpB,UAAMC,IAAYxB,EAAO;AAGzB,IAAAA,EAAO,KAAK,CAACqB,GAAIC,GAAIC,CAAE,CAAC,GAE9BxB,EAAcqB,EAASlB,CAAC,CAAC,IAAIsB,GAC7BzB,EAAcqB,EAASlB,IAAE,CAAC,CAAC,IAAIsB,GAC/BzB,EAAcqB,EAASlB,IAAE,CAAC,CAAC,IAAIsB;AAAA,EAC7B;AAMA,QAAMC,IAAkC,CAAA,GAClCC,IAAiB,IAAI,YAAYzB,CAAW;AAElD,WAASC,IAAI,GAAGA,IAAID,GAAaC,KAAK;AAClC,IAAAwB,EAAexB,CAAC,IAAIuB,EAAsB;AAC1C,UAAME,IAAc/B,EAAgB,IAAIM,CAAC,KAAK,CAAA;AAEpD,QAAIyB,EAAY,UAAQ;AAEvB;AAEK,IAAAF,EAAsB,KAAKE,EAAY,QAAQ,GAAGA,CAAW;AAAA,EACjE;AAGA,SAAO;AAAA,IACH,QAAQ,IAAI,YAAY3B,EAAO,MAAM;AAAA;AAAA,IACrC,eAAe,IAAI,YAAYD,CAAa;AAAA;AAAA,IAE5C,gBAAgB,IAAI,YAAY2B,CAAc;AAAA,IAC9C,uBAAuB,IAAI,YAAYD,CAAqB;AAAA,IAE5D,aAAAxB;AAAA;AAAA,IACA,SAAAJ;AAAA;AAAA,IACA,MAAM,IAAI,aAAaC,CAAI;AAAA;AAAA,IAE3B,SAAS,IAAI,YAAYW,CAAO;AAAA;AAAA,IAChC,aAAa,IAAI,aAAaC,CAAiB;AAAA;AAAA,EAAA;AAEvD;AAuDA,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,IAAIA,EAAE,SAAS,WAEdF,EAAU,KAAK;AAAA,MACd,UAAUE;AAAA,MACV,QAAQ;AAAA,IAAA,CACR;AAAA,EAEH,CAAC,GAKDF,EAAU,QAAS,CAAAI,MAAO;AACzB,UAAMC,IAAOP,EAAS,cAAgBM,EAAI,SAAsB,SAAS,QAAQ,WAAW,WAAU,EAAE,CAAE,GACpGE,IAAMF,EAAI;AAEhB,QAAG,CAACC;AACH,YAAM,IAAI,MAAM,kCAAkCC,EAAI,SAAS,OAAO,IAAG;AAG1E,IAAAL,EAAM,OAAOK,CAAG,GAKhBF,EAAI,SAAS,KAAK,IAAKE,EAAI,cAAcpD,CAAC,EAAE,CAAE,IAAI6C,GAGlDM,EAAM,OAAQC,CAAI;AAAA,EAGnB,CAAE,GAGKN;AACR;AAEA,SAASO,GAAoDC,GAAeC,GAAmCC,GAAoC;AAClJ,MAAI,oBAAoBF,GACxB;AACC,UAAMG,IAAIH;AACV,WAAAG,EAAE,aAAaF,GACfE,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,aAAaF,GACfE,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,GACLzD,GACA0D,GACHC,GACC;AACD,EAAA3D,EAAK,kBAAkB,IAAM,EAAK;AAGlC,QAAM4D,IAAqB;AAAA,IAC1B,oBAAoB;AAAA,IACpB,UAAU;AAAA,IACV,WAAW;AAAA,IACX,WAAW;AAAA,IACX,0BAAyB;AAAA,IACzB,eAAe,IAAI9D,EAAQ,GAAE,GAAE,CAAC;AAAA,IAChC,kBAAkB,IAAIA,EAAQ,GAAG,MAAK,CAAC;AAAA,IACvC,gBAAgBoD;AAAA,IAChB,GAAGS;AAAA,EAAA,GAMEhB,IAAuBgB,KAAA,QAAAA,EAAQ,gBAAgBpB,GAAiBoB,EAAO,eAAe3D,EAAK,UAAU4D,EAAK,wBAAwB,IAAI,CAAA,GAEnI;AAAA,IACF,SAAApD;AAAA,IACA,aAAAI;AAAA,IACA,MAAAH;AAAA,IACA,SAAAW;AAAA,IACA,aAAAyC;AAAA,IACA,gBAAAxB;AAAA,IACA,uBAAAD;AAAA,IACN,QAAAzB;AAAA,IACA,eAAAD;AAAA,EAAA,IACMX,GAAkBC,GAAM4D,EAAK,kBAAmB;AAEvD,MAAIA,EAAK,UAAU;AAClB,YAAQ,MAAM,OAAO,GACrB,QAAQ,IAAI,YAAYhD,CAAW,GACnC,QAAQ,IAAI,SAASQ,EAAQ,SAAO,CAAC,GACrC,QAAQ,IAAI,SAAST,EAAO,SAAO,CAAC,GACpC,QAAQ,IAAI,gBAAgBkD,EAAY,MAAM;AAG9C,aAAQhD,IAAE,GAAGA,IAAED,GAAaC,KAAI;AAC/B,UAAIiD,IAAY;AAChB,eAASC,IAAE,GAAGA,IAAE3C,EAAQ,QAAQ2C,KAAG;AAClC,YAAG3C,EAAQ2C,CAAC,MAAMlD,KAAKO,EAAQ2C,IAAE,CAAC,MAAMlD,GAAE;AACzC,UAAAiD,IAAY;AACZ;AAAA,QACD;AAED,MAAIA,KACH,QAAQ,IAAI,oBAAoBjD,GAAG,sBAAsB;AAAA,IAE3D;AAEA,YAAQ,SAAA;AAAA,EACT;AAGG,EAAAb,EAAK,SAAS,aAAa,eAAe,IAAIgE,GAAgBxD,GAAS,CAAC,CAAC,GAGzER,EAAK,SAAS,aAAa,aAAa,IAAIgE,GAAgBtD,GAAe,CAAC,CAAC;AAG7E,QAAMuD,IAAmBC,EAAQN,EAAK,SAAS,GACzCO,IAAmBD,EAAQN,EAAK,SAAS,GAKzCQ,IAAKF,EAAQ,CAAC,GAKdG,IAAiBH,EAASN,EAAK,kBAAkB,MAAM,GAKvDU,IAAcJ,EAASN,EAAK,eAAe,MAAM,GAKjDW,IAAYC,EAAe/D,GAAM,MAAM,GAKvCgE,IAAcD,EAAe5D,GAAa,MAAM,GAMhD8D,IAAkBF,EAAenC,GAAgB,MAAM,GAMvDsC,IAAyBH;AAAA,IAC3BpC;AAAA,IACA;AAAA,EAAA,GAMEwC,IAAexD,EAAQ,SAAS,GAKhCyD,IAAgBrE,EAAQ,QAKxBsE,IAAeN,EAAepD,GAAS,OAAO,GAK9C2D,IAAmBP,EAAeI,GAAc,MAAM,GAKtDI,IAAmBR,EAAeX,GAAa,OAAO,GAKzDoB,IAAcT,EAAe7D,GAAQ,OAAO,GAMzCuE,IAAeV,EAAehE,GAAS,MAAM,GAC7C2E,IAAcC,GAAkBpF,CAAI,GAKpCqF,KAAmBC,GAAgBtF,CAAI,GAK1CuF,KAAiB,IAAI,eAAc5C,KAAA,gBAAAA,EAAW,WAAU,KAAK,CAAC,GAC9D6C,IAAe,IAAIC,GAAgCF,IAAgB,CAAC,GACpEG,KAAiBC,GAAQH,GAAc,MAAM,GAE7CI,IAA4B1B,EAAQ,IAAI2B,IAAS,GAK9CC,KAA6BC,EAAG,MAAM;AACxC,IAAAC,EAAGC,EAAc,iBAAiBpB,CAAa,GAAG,MAAMqB,GAAQ;AAEhE,UAAMC,IAASjB,EAAa,QAAQe,CAAa,GAC3CG,IAAO7B,EAAU,QAAQ4B,CAAM,GAE/BE,IAAwBlB,EAAY,IAAIE,EAAgB;AAC9D,IAAAe,EAAK,IAAI,OAAOC,CAAqB;AAAA,EAEzC,CAAC,EAAA,EACA,QAAQxB,CAAa,EACrB,QAAQ,4BAA4B,GAQ/ByB,KAAuBP,EAAG,MAAM;AAClC,IAAAC,EAAGC,EAAc,iBAAiBpB,CAAa,GAAG,MAAMqB,GAAQ;AAEhE,UAAMC,IAASjB,EAAa,QAAQe,CAAa,GAC3CG,IAAO7B,EAAU,QAAQ4B,CAAM,GAC/BI,IAASH,EAAK,EAAE,IAAI,CAAC,GAErBC,IAAwBlB,EAAY,IAAIE,EAAgB;AAMpE,IAAAe,EAAK,IAAI,OAAOI,GAAIJ,EAAK,KAAKC,GAAuBE,CAAM,CAAC;AAAA,EAE1D,CAAC,EAAA,EACA,QAAQ1B,CAAa,EACrB,QAAQ,wBAAwB,GAQ9B4B,KAAsBV,EAAG,MAAI;AAElC,IAAAC,EAAGC,EAAc,SAASrB,CAAY,GAAG,MAAM;AAC9C,YAAM8B,IAAY5B,EAAa,QAASmB,CAAc,GAChDU,IAAa3B,EAAiB,QAASiB,CAAc,GAErDjE,IAAK0E,EAAU,GACfzE,IAAKyE,EAAU,GAEfE,IAAOrC,EAAU,QAASvC,CAAG,EAAE,KAC/B6E,IAAQtC,EAAU,QAAStC,CAAG,EAAE,KAEhC6E,IAAKrC,EAAY,QAAQzC,CAAE,GAC3B+E,IAAKtC,EAAY,QAAQxC,CAAE,GAE3B+E,IAAQH,EAAM,IAAID,CAAI,GACtBK,IAAOD,EAAM,OAAA,EAAS,IAAI,IAAQ,GAClCE,IAAMF,EAAM,IAAIC,CAAI;AAGV,MADIF,EAAG,IAAID,CAAE,EACD,IAAII,CAAG,EAAE,IAAI,GAAG;AAE5C,YAAMC,IAAQF,EAAK,IAAIN,CAAU,EAAE,IAAI1C,CAAgB,EAAG,IAAIiD,CAAG,EAAE,IAAI,GAAG;AAE1E,MAAAnC,EAAiB,QAAQkB,CAAa,EAAE,OAAQkB,CAAM;AAAA,IACvD,CAAC;AAAA,EAEF,CAAC,EAAA,EAAI,QAASvC,CAAa,EAAE,QAAQ,uBAAuB,GAOtDwC,KAAsBrB,EAAG,MAAI;AAElC,IAAAC,EAAGC,EAAc,iBAAiBrF,CAAW,GAAG,MAAM;AACrD,MAAAsF,EAAA;AAAA,IACD,CAAC;AAED,UAAMmB,IAAW9C,EAAU,QAAS0B,CAAc,GAC5CkB,IAAQ1C,EAAY,QAASwB,CAAc,EAAE,MAAA,GAC7C7F,IAAQiH,EAAS,EAAG,SAAA,EAAW,IAAI,CAAC,GAGpCC,IAAgB5C,EAAgB,QAAQuB,CAAa,GAE/CsB,IAAc5C,EAAuB,QAAQ2C,CAAa,GAE1DE,IAAWF,EAAc,IAAI,CAAC,EAAE,MAAM,UAAU,GAChDG,IAASD,EAAS,IAAID,CAAW,EAAE,MAAM,QAAQ;AAE7D,IAAAJ,EAAM,UAAUhD,CAAgB,GAE/BuD;AAAA,MACU,EAAE,OAAOF,GAAU,KAAKC,GAAQ,MAAM,QAAQ,WAAW,IAAA;AAAA,MACzD,CAAC,EAAE,GAAA5G,EAAA,MAAQ;AACP,cAAM8G,IAAchD,EAAuB,QAAQ9D,CAAC,EAAE,MAAM,UAAU,GAChE+G,IAAS9C,EAAa,QAAQ6C,CAAW,EAAE,MAAA,GAC3CE,IAAc9C,EAAiB,QAAQ4C,CAAW;AAEpE,QAAA3B,EAAI4B,EAAO,EAAE,MAAM3B,CAAa,GAAG,MAAI;AAEtC,UAAAkB,EAAM,UAAUU,CAAW;AAAA,QAC5B,CAAC,EACA,KAAK,MAAI;AACT,UAAAV,EAAM,UAAUU,CAAW;AAAA,QAC5B,CAAC;AAAA,MAGO;AAAA,IAAA;AAKL,UAAMC,IADQC,GAAWV,GAAU,GAAGW,EAAI,EAAE,IAAI,GAAG,EAAE,IAAI,GAAG,EACpC,IAAI1D,CAAW;AAI7C,QAHM6C,EAAM,UAAUW,CAAS,GAG3BnF;AACH,eAAS9B,IAAI,GAAGA,IAAI8B,EAAU,QAAQ9B,KAAK;AAC1C,cAAMoH,IAAOvC,GAAe,QAAQ7E,CAAC,EAAE,KACrBqH,IAAOC,GAAMzC,GAAe,QAAQ7E,CAAC,EAAE,CAAC,GAGpDuH,IAAcf,EAAS,IAAIF,CAAK,EAAE,IAAIc,CAAI,GAC1ChB,KAAOmB,EAAY,OAAA,GAKnBC,KAAcH,EAClB,IAAIjB,EAAI,EACR,IAAI,CAAC,EACL,IAAImB,EAAY,UAAA,CAAW,EAC3B,IAAI,CAAC;AAEP,QAAAjB,EAAM,UAAUkB,EAAW;AAAA,MAC5B;AAKD,IAAAlB,EAAM,UAAU9C,EAAe,IAAIjE,CAAI,EAAE,IAAIgE,CAAG,CAAE,GAIjDiD,EAAS,IAAI,UAAWF,CAAS;AAAA,EAQnC,CAAC,EAAA,EAAI,QAASvG,CAAY,EAAE,QAAQ,uBAAuB,GAErD0H,KAAuBvC,EAAG,MAAI;AACnC,IAAAC,EAAGC,EAAc,SAASrB,CAAY,GAAG,MAAM;AAC9C,YAAM8B,IAAY5B,EAAa,QAASmB,CAAc,GAChDU,IAAa3B,EAAiB,QAASiB,CAAc,GAErDjE,IAAK0E,EAAU,GACfzE,IAAKyE,EAAU,GAEfE,IAAOrC,EAAU,QAAQvC,CAAE,EAAE,KAI7BiF,IAHO1C,EAAU,QAAQtC,CAAE,EAAE,IAEhB,IAAI2E,CAAI,EACR,OAAA,EAAS,IAAI,IAAQ;AACxC,MAAAD,EAAW,OAAOM,CAAI;AAAA,IAGvB,CAAC;AAAA,EACF,CAAC,EAAA,EAAI,QAASrC,CAAa,EAAE,QAAQ,wBAAwB;AAExC,EAAAmB,EAAG,MAAM;AAE7B,IAAAC,EAAGC,EAAc,SAASrB,CAAY,GAAG,MAAM;AAE9C,YAAM8B,IAAY5B,EAAa,QAASmB,CAAc,GAChDU,IAAa3B,EAAiB,QAASiB,CAAc,GAErDjE,IAAK0E,EAAU,GACfzE,IAAKyE,EAAU,GAEfE,IAAOrC,EAAU,QAAQvC,CAAE,GAC3BuG,IAAOhE,EAAU,QAAQtC,CAAE;AAEjC,MAAA+D,EAAIW,EAAW,YAAY,CAAC,GAAG,MAAI;AAClC,QAAAC,EAAK,EAAE,OAAOD,CAAU,GACxB4B,EAAK,EAAE,OAAO5B,CAAU;AAAA,MACzB,CAAC;AAAA,IAEF,CAAC;AAAA,EAEF,CAAC,EAAA,EAAI,QAAS/B,CAAa,EAAE,QAAQ,cAAc;AAGhD,QAAMxB,KAAa2C,EAAG,MAAM;AACpB,UAAMyC,IAAiBjE,EAAU;AAAA,MAC7BkE,GAAU,eAAe,MAAM;AAAA,IAAA;AAGnC,WAAOC,GACF,IAAIC,EAAgB,EACpB,IAAIC,GAAKJ,EAAe,KAAK,CAAG,CAAC;AAAA,EAC1C,CAAC,EAAA,GAoBFK,KAlBkB9C,EAAG,MAAM;AAChC,UAAMI,IAASsC,GAAU,aAAa,MAAM,GACtCK,IAAO7D,EAAY,QAAQkB,CAAM,GACjC4C,IAAKxE,EAAU,QAAQuE,EAAK,CAAC,EAAE,MAAA,GAC/BE,IAAKzE,EAAU,QAAQuE,EAAK,CAAC,EAAE,MAAA,GAC/BG,IAAK1E,EAAU,QAAQuE,EAAK,CAAC,EAAE,MAAA,GAG/BI,IAAQF,EAAG,IAAID,CAAE,GACjBI,IAAQF,EAAG,IAAIF,CAAE,GAGjBK,IAASC,GAAMH,GAAOC,CAAK,EAAE,UAAA,GAE7BG,IAAc1D,EAA0B,mBAAmBwD,CAAM;AACvE,WAAOG,GAAsBD,CAAW;AAAA,EACzC,CAAC,EAEe,EAAkB,UAAA,GAC5BjG,KAAamG,GAAOC,IAAaZ,IAASA,GAAQ,QAAQ,GAE1Da,KAA2B,MAAI;AACpC,QAAG,EAAC/G,KAAA,QAAAA,EAAW;AACd;AAGD,UAAM4C,IAAiBC,EAAa;AAEpC,aAAQ3E,IAAI,GAAGA,IAAI8B,EAAU,QAAQ9B,KAAI;AACxC,YAAMkC,IAAMJ,EAAU9B,CAAC;AACvB,MAAIkC,EAAI,oBAAoBjD,IAE3BD,EAAE,KAAKkD,EAAI,QAAQ,KAInBA,EAAI,SAAS,kBAAkB,EAAI,GACnCA,EAAI,SAAS,iBAAiBlD,CAAC,IAGhC0F,EAAe1E,IAAI,CAAC,IAAIhB,EAAE,GAC1B0F,EAAe1E,IAAI,IAAI,CAAC,IAAIhB,EAAE,GAC9B0F,EAAe1E,IAAI,IAAI,CAAC,IAAIhB,EAAE,GAC9B0F,EAAe1E,IAAI,IAAI,CAAC,IAAIkC,EAAI;AAAA,IACjC;AAEA,IAAAyC,EAAa,cAAc;AAAA,EAC5B;AAEA,SAAAxF,EAAK,WAAW4D,EAAK,eAAgB5D,EAAK,UAAsBoD,IAAYC,EAAU,GAEtFuC,EAA0B,MAAM,KAAK5F,EAAK,WAAW,EAAE,OAAA,GAGvD0D,EAAS,QAASoC,EAA2B,GAC7CpC,EAAS,QAAS4E,EAAqB,GAE7B;AAAA,IAET,kBAAArE;AAAA,IACA,kBAAAE;AAAA,IACA,gBAAAE;AAAA,IACA,aAAAC;AAAA;AAAA;AAAA;AAAA;AAAA,IAMM,QAAQ,CAAC0C,GAAe2C,IAAM,OAAO;AAE1C,MAAA3J,EAAK,kBAAA,GAEL4F,EAA0B,MAAM,KAAK5F,EAAK,WAAW,EAAE,OAAA,GAEvD0D,EAAS,QAAQ4C,EAAoB,GAKrCoD,GAAA,GAEAtF,EAAG,QAAQ4C,IAAM2C;AAEjB,eAAQ9I,IAAE,GAAGA,IAAE8I,GAAO9I;AAErB,QAAA6C,EAAS,QAAQ+C,EAAmB,GACpC/C,EAAS,QAAQ0D,EAAmB;AAAA,IAEhC;AAAA,EAAA;AAER;AAQO,MAAMwC,GAAY;AAazB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AADCC,GAZYD,IAYL,iBAAgBnG;"}
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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "three-simplecloth",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "description": "Simple cloth simulation for Three.js WebGPU",
6
6
  "repository": {