three-simplecloth 0.0.1
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 +43 -0
- package/dist/SimpleCloth.d.ts +72 -0
- package/dist/SimpleCloth.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +241 -0
- package/dist/index.js.map +1 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
> Based on: https://github.com/mrdoob/three.js/blob/a58e9ecf225b50e4a28a934442e854878bc2a959/examples/webgpu_compute_cloth.html
|
|
2
|
+
|
|
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.
|
|
5
|
+
|
|
6
|
+
## How does this work?
|
|
7
|
+
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
|
+
|
|
9
|
+
## Install
|
|
10
|
+
```bash
|
|
11
|
+
npm install three-simplecloth
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
import { SimpleCloth } from "three-simplecloth";
|
|
18
|
+
|
|
19
|
+
//
|
|
20
|
+
// this will modify the material of the "clothing" Skinned mesh
|
|
21
|
+
// and return a handler you must call to update the cloth simulation.
|
|
22
|
+
//
|
|
23
|
+
const cloth = SimpleCloth.onSkinnedMesh( clothing, renderer );
|
|
24
|
+
|
|
25
|
+
function animate(delta: number) {
|
|
26
|
+
cloth.update(delta);
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Config
|
|
31
|
+
The third parameter is a config object:
|
|
32
|
+
|
|
33
|
+
| Property | Type | Description |
|
|
34
|
+
| --- | --- | --- |
|
|
35
|
+
| `colorAttributeName` | `string` | Usually it is "color" but sometimes it may be other like "color_1". |
|
|
36
|
+
| `logStats` | `boolean` | Log stats to the console ( about the cloth mesh ). |
|
|
37
|
+
| `collidersRoot` | `Object3D` | The root object to search for colliders. |
|
|
38
|
+
| `stiffness` | `number` | Stiffness of the cloth (0.0 to 1.0). |
|
|
39
|
+
| `dampening` | `number` | Dampening of the cloth (0.0 to 1.0). |
|
|
40
|
+
| `colliderRadiusMultiplier` | `number` | Tweak the radius of the colliders ( which are spheres attached to bones ). Default is 1.0. |
|
|
41
|
+
| `windPerSecond` | `Vector3` | Wind DIRECTION in world space (noise will be used to add variation). |
|
|
42
|
+
| `gravityPerSecond` | `Vector3` | Gravity force in world space. |
|
|
43
|
+
| `updateMaterial` | `function` | A function to override the current skinned mesh material. It receives the material and 2 TSL nodes: vertexNode and normalNode.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Material, Object3D, Vector3, SkinnedMesh } from 'three';
|
|
2
|
+
import { Node, NodeMaterial, WebGPURenderer } from 'three/webgpu';
|
|
3
|
+
type ClothConfig = {
|
|
4
|
+
/**
|
|
5
|
+
* Usually it is "color" but sometimes it may be other like "color_1"
|
|
6
|
+
*/
|
|
7
|
+
colorAttributeName?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Log stats to the console ( about the cloth mesh )
|
|
10
|
+
*/
|
|
11
|
+
logStats?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* The root object to search for colliders.
|
|
14
|
+
*/
|
|
15
|
+
collidersRoot?: Object3D;
|
|
16
|
+
stiffness?: number;
|
|
17
|
+
dampening?: number;
|
|
18
|
+
/**
|
|
19
|
+
* you can tweak the radius of the colliders ( which are spheres attached to bones )
|
|
20
|
+
*
|
|
21
|
+
* @param radius 1.0 is the default
|
|
22
|
+
*/
|
|
23
|
+
colliderRadiusMultiplier?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Wind DIRECTION in world space (noise will be used to add variation)
|
|
26
|
+
*/
|
|
27
|
+
windPerSecond?: Vector3;
|
|
28
|
+
/**
|
|
29
|
+
* Gravity force in world space
|
|
30
|
+
*/
|
|
31
|
+
gravityPerSecond?: Vector3;
|
|
32
|
+
/**
|
|
33
|
+
* The material used by the SkinnedMesh to turn into "cloth" must apply and use these TSL nodes...
|
|
34
|
+
* By default it will try to guess the most common case when you import a model from blender...
|
|
35
|
+
*
|
|
36
|
+
* @param vertexNode
|
|
37
|
+
* @param normalNode
|
|
38
|
+
* @returns
|
|
39
|
+
*/
|
|
40
|
+
updateMaterial: (base: Material, vertexNode: Node, normalNode: Node) => NodeMaterial;
|
|
41
|
+
};
|
|
42
|
+
declare function setupClothOnSkinnedMesh(mesh: SkinnedMesh, renderer: WebGPURenderer, config?: Partial<ClothConfig>): {
|
|
43
|
+
stiffnessUniform: import('three/webgpu').UniformNode<number | undefined>;
|
|
44
|
+
dampeningUniform: import('three/webgpu').UniformNode<number | undefined>;
|
|
45
|
+
gravityUniform: import('three/webgpu').UniformNode<Vector3 | undefined>;
|
|
46
|
+
windUniform: import('three/webgpu').UniformNode<Vector3 | undefined>;
|
|
47
|
+
/**
|
|
48
|
+
* @param delta seconds passed since last render
|
|
49
|
+
* @param steps number of steps to run the simulation ( more steps = more "stable" but slower )
|
|
50
|
+
*/
|
|
51
|
+
update: (delta: number, steps?: number) => void;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* A SIMPLE cloth simulation. Goal is to have a minimal interface to just get some mesh to act kind of like a cloth.
|
|
55
|
+
* Adaptation/Based on the Three's Official examples: https://github.com/mrdoob/three.js/blob/a58e9ecf225b50e4a28a934442e854878bc2a959/examples/webgpu_compute_cloth.html
|
|
56
|
+
*
|
|
57
|
+
*/
|
|
58
|
+
export declare class SimpleCloth {
|
|
59
|
+
/**
|
|
60
|
+
* Turns the vertex painted parts of a skinned mesh into a cloth simulation.
|
|
61
|
+
* Red paint is assumed to be the part of the mesh that should be simulated.
|
|
62
|
+
* The rest is assumed to be white.
|
|
63
|
+
*
|
|
64
|
+
* @param mesh The skinned mesh to turn into a cloth simulation.
|
|
65
|
+
* @param renderer The renderer ( because we need to run compute shaders )
|
|
66
|
+
* @param options The options for the simulation.
|
|
67
|
+
* @returns The cloth simulation's API.
|
|
68
|
+
*/
|
|
69
|
+
static onSkinnedMesh: typeof setupClothOnSkinnedMesh;
|
|
70
|
+
}
|
|
71
|
+
export {};
|
|
72
|
+
//# sourceMappingURL=SimpleCloth.d.ts.map
|
|
@@ -0,0 +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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
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);
|
|
20
|
+
}
|
|
21
|
+
const B = [], G = [];
|
|
22
|
+
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))
|
|
28
|
+
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;
|
|
37
|
+
}
|
|
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)
|
|
43
|
+
debugger;
|
|
44
|
+
I.push(i.length, ...i);
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
vFaces: new Uint32Array(j.flat()),
|
|
48
|
+
//vec3[]
|
|
49
|
+
vVertexToFace: new Uint32Array(U),
|
|
50
|
+
// uint
|
|
51
|
+
springsPointer: new Uint32Array(Y),
|
|
52
|
+
springsPerVertexArray: new Uint32Array(I),
|
|
53
|
+
uniqueCount: V,
|
|
54
|
+
// unique vertices
|
|
55
|
+
indices: y,
|
|
56
|
+
// for each vertice, stores the index of the unique one in the vPos array
|
|
57
|
+
vPos: new Float32Array(b),
|
|
58
|
+
// 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 )
|
|
63
|
+
};
|
|
64
|
+
}
|
|
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,
|
|
73
|
+
radius: 0
|
|
74
|
+
});
|
|
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;
|
|
81
|
+
}
|
|
82
|
+
function $t(t, a, l) {
|
|
83
|
+
if ("isNodeMaterial" in t) {
|
|
84
|
+
const e = t;
|
|
85
|
+
return e.vertexNode = a, e.normalNode = l, e;
|
|
86
|
+
} else {
|
|
87
|
+
const e = new qt({
|
|
88
|
+
side: wt,
|
|
89
|
+
// wireframe:true,
|
|
90
|
+
wireframe: t.wireframe,
|
|
91
|
+
flatShading: t.flatShading,
|
|
92
|
+
transparent: t.transparent,
|
|
93
|
+
depthWrite: t.depthWrite,
|
|
94
|
+
depthTest: t.depthTest
|
|
95
|
+
});
|
|
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;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function bt(t, a, l) {
|
|
100
|
+
t.updateWorldMatrix(!0, !1);
|
|
101
|
+
const e = {
|
|
102
|
+
colorAttributeName: "color",
|
|
103
|
+
logStats: !1,
|
|
104
|
+
stiffness: 0.4,
|
|
105
|
+
dampening: 0.5,
|
|
106
|
+
colliderRadiusMultiplier: 1,
|
|
107
|
+
windPerSecond: new W(0, 0, 0),
|
|
108
|
+
gravityPerSecond: new W(0, -0.3, 0),
|
|
109
|
+
updateMaterial: $t,
|
|
110
|
+
...l
|
|
111
|
+
}, p = l != null && l.collidersRoot ? Dt(l.collidersRoot, t.skeleton, e.colliderRadiusMultiplier) : [], {
|
|
112
|
+
indices: m,
|
|
113
|
+
uniqueCount: h,
|
|
114
|
+
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++) {
|
|
125
|
+
let n = !1;
|
|
126
|
+
for (let r = 0; r < y.length; r += 2)
|
|
127
|
+
if (y[r] === o || y[r + 1] === o) {
|
|
128
|
+
n = !0;
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
n || console.log("WARNING!: vertex", o, "has no strings! wtf?");
|
|
132
|
+
}
|
|
133
|
+
console.groupEnd();
|
|
134
|
+
}
|
|
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,
|
|
138
|
+
"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);
|
|
142
|
+
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), () => {
|
|
156
|
+
K();
|
|
157
|
+
});
|
|
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
|
+
});
|
|
168
|
+
}
|
|
169
|
+
);
|
|
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);
|
|
175
|
+
}
|
|
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);
|
|
181
|
+
});
|
|
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);
|
|
188
|
+
});
|
|
189
|
+
});
|
|
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))
|
|
201
|
+
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;
|
|
206
|
+
}
|
|
207
|
+
H.needsUpdate = !0;
|
|
208
|
+
};
|
|
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,
|
|
214
|
+
/**
|
|
215
|
+
* @param delta seconds passed since last render
|
|
216
|
+
* @param steps number of steps to run the simulation ( more steps = more "stable" but slower )
|
|
217
|
+
*/
|
|
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);
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
class jt {
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Turns the vertex painted parts of a skinned mesh into a cloth simulation.
|
|
229
|
+
* Red paint is assumed to be the part of the mesh that should be simulated.
|
|
230
|
+
* The rest is assumed to be white.
|
|
231
|
+
*
|
|
232
|
+
* @param mesh The skinned mesh to turn into a cloth simulation.
|
|
233
|
+
* @param renderer The renderer ( because we need to run compute shaders )
|
|
234
|
+
* @param options The options for the simulation.
|
|
235
|
+
* @returns The cloth simulation's API.
|
|
236
|
+
*/
|
|
237
|
+
ot(jt, "onSkinnedMesh", bt);
|
|
238
|
+
export {
|
|
239
|
+
jt as SimpleCloth
|
|
240
|
+
};
|
|
241
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "three-simplecloth",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Simple cloth simulation for Three.js WebGPU",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/bandinopla/three-simplecloth.git"
|
|
9
|
+
},
|
|
10
|
+
"main": "./dist/index.js",
|
|
11
|
+
"module": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"dev": "vite --config vite.dev.config.ts",
|
|
24
|
+
"build": "vite build --config vite.lib.config.ts",
|
|
25
|
+
"build:web": "vite build --config vite.dev.config.ts",
|
|
26
|
+
"preview": "vite preview --config vite.dev.config.ts"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"three": "^0.182.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/three": "^0.182.0",
|
|
33
|
+
"three": "^0.182.0",
|
|
34
|
+
"typescript": "^5.7.0",
|
|
35
|
+
"vite": "^6.1.0",
|
|
36
|
+
"vite-plugin-dts": "^4.5.0"
|
|
37
|
+
},
|
|
38
|
+
"license": "MIT"
|
|
39
|
+
}
|