three-mediapipe-rig 0.1.0 → 0.1.2
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 +7 -2
- package/dist/face-tracker-utils-xt9__vBF.js +16 -0
- package/dist/meshcap/atlas-builder.d.ts +10 -0
- package/dist/meshcap/atlas-builder.d.ts.map +1 -0
- package/dist/meshcap/constants.d.ts +3 -0
- package/dist/meshcap/constants.d.ts.map +1 -0
- package/dist/meshcap/material.d.ts +53 -0
- package/dist/meshcap/material.d.ts.map +1 -0
- package/dist/meshcap/meshcap.d.ts +5 -0
- package/dist/meshcap/meshcap.d.ts.map +1 -0
- package/dist/meshcap/parse-mcap-file.d.ts +8 -0
- package/dist/meshcap/parse-mcap-file.d.ts.map +1 -0
- package/dist/meshcap/types.d.ts +53 -0
- package/dist/meshcap/types.d.ts.map +1 -0
- package/dist/meshcap/write-mcap-file.d.ts +3 -0
- package/dist/meshcap/write-mcap-file.d.ts.map +1 -0
- package/dist/meshcap.d.ts +2 -0
- package/dist/meshcap.js +316 -0
- package/dist/module.d.ts +1 -0
- package/dist/module.d.ts.map +1 -1
- package/dist/rigger.d.ts +2 -0
- package/dist/rigger.js +853 -0
- package/dist/tracking/BoneMapping.d.ts.map +1 -1
- package/dist/tracking/FaceTracker.d.ts +13 -2
- package/dist/tracking/FaceTracker.d.ts.map +1 -1
- package/dist/tracking/HandTracker.d.ts +3 -3
- package/dist/tracking/HandTracker.d.ts.map +1 -1
- package/dist/tracking/PoseTracker.d.ts +1 -1
- package/dist/tracking/PoseTracker.d.ts.map +1 -1
- package/dist/tracking/TrackerManager.d.ts +118 -0
- package/dist/tracking/TrackerManager.d.ts.map +1 -0
- package/dist/tracking/recoding/recorder.d.ts +10 -8
- package/dist/tracking/recoding/recorder.d.ts.map +1 -1
- package/dist/tracking/util/face-tracker-utils.d.ts +9 -0
- package/dist/tracking/util/face-tracker-utils.d.ts.map +1 -0
- package/package.json +17 -12
- package/dist/three-mediapipe-rig.js +0 -830
- package/dist/three-mediapipe-rig.js.map +0 -1
package/README.md
CHANGED
|
@@ -10,13 +10,15 @@ The motion from the webcam will be applied to a skeleton. Angle based so it work
|
|
|
10
10
|
This will run 3 models: face, body, hands. So expect a FPS drop.
|
|
11
11
|
|
|
12
12
|
Expolore the demos:
|
|
13
|
+
- New! [**MeshCap Editor**](https://github.com/bandinopla/three-mediapipe-rig/blob/main/MESHCAP.md)
|
|
13
14
|
- [Characters](https://bandinopla.github.io/three-mediapipe-rig)
|
|
14
15
|
- [Hands Demo](https://bandinopla.github.io/three-mediapipe-rig/?demo=hands)
|
|
15
|
-
- [Video to Face Geometry](https://bandinopla.github.io/three-mediapipe-rig/?demo=face-uv
|
|
16
|
+
- [**Video to Face Geometry** !!](https://bandinopla.github.io/three-mediapipe-rig/?demo=face-uv)
|
|
17
|
+
|
|
16
18
|
---
|
|
17
19
|
|
|
18
20
|
## Table of Contents
|
|
19
|
-
|
|
21
|
+
- New! [**MeshCap Editor**](https://github.com/bandinopla/three-mediapipe-rig/blob/main/MESHCAP.md)
|
|
20
22
|
- [Features](#features)
|
|
21
23
|
- [Installation](#installation)
|
|
22
24
|
- [Quick Start](#quick-start)
|
|
@@ -237,6 +239,9 @@ const face = tracker.faceTracker.bindGeometry( faceMesh );
|
|
|
237
239
|
face.update(delta)
|
|
238
240
|
```
|
|
239
241
|
|
|
242
|
+
### How does this work?
|
|
243
|
+
You use the canonical face model provided by media pipe ( this is important because it has the same number of vertices as the facial mesh ), this code will create a positionNode ( needs a NodeMaterial ) that will adjust the position of the vertices to match the facial mesh and also it will use the video feed as a texture for the mesh, so it will look like the face is deforming and moving with the webcam feed.
|
|
244
|
+
|
|
240
245
|
## Multiple Characters
|
|
241
246
|
|
|
242
247
|
You can bind **multiple rigs** to the same tracker. All of them will mirror the tracked motion:
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { BufferAttribute as c } from "three";
|
|
2
|
+
const d = 478, g = (r) => {
|
|
3
|
+
if (r.geometry.hasAttribute("landmarkIndex")) return;
|
|
4
|
+
const s = r.geometry, e = s.attributes.position, a = [], n = /* @__PURE__ */ new Map(), i = [];
|
|
5
|
+
for (let t = 0; t < e.count; t++) {
|
|
6
|
+
const o = Math.round(e.getX(t) * 1e6) + "," + Math.round(e.getY(t) * 1e6) + "," + Math.round(e.getZ(t) * 1e6);
|
|
7
|
+
n.has(o) || (n.set(o, a.length), a.push(t));
|
|
8
|
+
const u = n.get(o);
|
|
9
|
+
i.push(u < d ? u : 65535);
|
|
10
|
+
}
|
|
11
|
+
s.setAttribute("landmarkIndex", new c(new Uint16Array(i), 1));
|
|
12
|
+
};
|
|
13
|
+
export {
|
|
14
|
+
d as F,
|
|
15
|
+
g as c
|
|
16
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { MeshCapAtlas, RecordedClip } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Builds a texture atlas from a list of recorded clips.
|
|
4
|
+
* @param clips The clips to build the atlas from
|
|
5
|
+
* @param atlasSize Max size of the atlas (width or height)
|
|
6
|
+
* @param padding The padding to add between frames
|
|
7
|
+
* @returns The atlas
|
|
8
|
+
*/
|
|
9
|
+
export declare function buildMeshCapAtlas(clips: RecordedClip[], atlasSize: number, padding?: number): MeshCapAtlas;
|
|
10
|
+
//# sourceMappingURL=atlas-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atlas-builder.d.ts","sourceRoot":"","sources":["../../src/meshcap/atlas-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAY,YAAY,EAAW,MAAM,SAAS,CAAC;AA0DxE;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAE,KAAK,EAAC,YAAY,EAAE,EAAE,SAAS,EAAC,MAAM,EAAE,OAAO,GAAC,MAAQ,GAAG,YAAY,CAsGzG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/meshcap/constants.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,UAAU,aAAa,CAAC;AACrC,eAAO,MAAM,iBAAiB,IAAI,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Mesh, Texture } from 'three';
|
|
2
|
+
import { MCapClip } from './types';
|
|
3
|
+
import { NodeMaterial } from 'three/webgpu';
|
|
4
|
+
export type MeshCapMaterialHandler = {
|
|
5
|
+
/**
|
|
6
|
+
* Moves to a particular clip
|
|
7
|
+
* @param clipIndex The index of the clip to move to
|
|
8
|
+
*/
|
|
9
|
+
goto: (clipIndex: number | string, _loop?: boolean) => void;
|
|
10
|
+
/**
|
|
11
|
+
* Play a clip and when it reaches the end, it will loop back
|
|
12
|
+
* @param clipName Name of the clip
|
|
13
|
+
* @returns
|
|
14
|
+
*/
|
|
15
|
+
gotoAndLoop: (clipIndex: number | string) => void;
|
|
16
|
+
/**
|
|
17
|
+
* Play a clip and when it reaches the end, it will not loop back
|
|
18
|
+
* @param clipName Name of the clip
|
|
19
|
+
* @returns
|
|
20
|
+
*/
|
|
21
|
+
gotoAndPlay: (clipIndex: number | string) => void;
|
|
22
|
+
/**
|
|
23
|
+
* Updates the material with the given delta time
|
|
24
|
+
* @param delta The time to add to the current time
|
|
25
|
+
*/
|
|
26
|
+
update: (delta: number) => void;
|
|
27
|
+
/**
|
|
28
|
+
* The clips available to play
|
|
29
|
+
*/
|
|
30
|
+
clips: MCapClip[];
|
|
31
|
+
/**
|
|
32
|
+
* The texture atlas that contains the frames used by the clips
|
|
33
|
+
*/
|
|
34
|
+
atlasTexture: Texture;
|
|
35
|
+
/**
|
|
36
|
+
* Disposes the material and the texture atlas
|
|
37
|
+
*/
|
|
38
|
+
dispose: VoidFunction;
|
|
39
|
+
/**
|
|
40
|
+
* The material used
|
|
41
|
+
*/
|
|
42
|
+
material: NodeMaterial;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Creates or setups a MeshCap material handler (not the material itself) for a given mesh.
|
|
46
|
+
* @param atlasTexture The texture atlas that contains the frames used by the clips
|
|
47
|
+
* @param clips The clips previously obtained by loading an .mcap file
|
|
48
|
+
* @param targetMesh The mesh to apply the material to. (It will be updated with a landmarkIndex attribute if it doesn't have one)
|
|
49
|
+
* @param host Optional: The material to use as a base. Defaults to a MeshPhysicalNodeMaterial.
|
|
50
|
+
* @returns A handler that allows you to control the material.
|
|
51
|
+
*/
|
|
52
|
+
export declare function createMeshCapMaterial(atlasTexture: Texture, clips: MCapClip[], targetMesh: Mesh, host?: NodeMaterial): MeshCapMaterialHandler;
|
|
53
|
+
//# sourceMappingURL=material.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"material.d.ts","sourceRoot":"","sources":["../../src/meshcap/material.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAA4B,YAAY,EAAW,MAAM,cAAc,CAAC;AAM/E,MAAM,MAAM,sBAAsB,GAAG;IAEpC;;;OAGG;IACH,IAAI,EAAC,CAAE,SAAS,EAAC,MAAM,GAAC,MAAM,EAAE,KAAK,CAAC,EAAC,OAAO,KAAI,IAAI,CAAA;IAEtD;;;;OAIG;IACH,WAAW,EAAC,CAAE,SAAS,EAAC,MAAM,GAAC,MAAM,KAAI,IAAI,CAAA;IAE7C;;;;OAIG;IACH,WAAW,EAAC,CAAE,SAAS,EAAC,MAAM,GAAC,MAAM,KAAI,IAAI,CAAA;IAE7C;;;OAGG;IACH,MAAM,EAAC,CAAE,KAAK,EAAC,MAAM,KAAI,IAAI,CAAA;IAE7B;;OAEG;IACH,KAAK,EAAC,QAAQ,EAAE,CAAA;IAEhB;;OAEG;IACH,YAAY,EAAC,OAAO,CAAA;IAEpB;;OAEG;IACH,OAAO,EAAC,YAAY,CAAA;IAEpB;;OAEG;IACH,QAAQ,EAAC,YAAY,CAAA;CACrB,CAAA;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAE,YAAY,EAAC,OAAO,EAAE,KAAK,EAAC,QAAQ,EAAE,EAAE,UAAU,EAAC,IAAI,EAAE,IAAI,CAAC,EAAC,YAAY,GAAG,sBAAsB,CAkN1I"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meshcap.d.ts","sourceRoot":"","sources":["../../src/meshcap/meshcap.ts"],"names":[],"mappings":"AAAA,mBAAmB,SAAS,CAAA;AAC5B,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,YAAY,CAAA"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { MCapFile } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Loads a MeshCap (.mcap) file from a URL or File object. This is the file that contains the metadata for the clips.
|
|
4
|
+
* @param mcapFileSource URL or File object
|
|
5
|
+
* @returns
|
|
6
|
+
*/
|
|
7
|
+
export declare function loadMeshCapFile(mcapFileSource: string | File): Promise<MCapFile>;
|
|
8
|
+
//# sourceMappingURL=parse-mcap-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-mcap-file.d.ts","sourceRoot":"","sources":["../../src/meshcap/parse-mcap-file.ts"],"names":[],"mappings":"AACA,OAAO,EAAY,QAAQ,EAAyB,MAAM,SAAS,CAAC;AAOpE;;;;GAIG;AACH,wBAAsB,eAAe,CAAE,cAAc,EAAC,MAAM,GAAC,IAAI,GAAK,OAAO,CAAC,QAAQ,CAAC,CAmBtF"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Vector3Like } from 'three';
|
|
2
|
+
export interface UVCoord {
|
|
3
|
+
u: number;
|
|
4
|
+
v: number;
|
|
5
|
+
w: number;
|
|
6
|
+
h: number;
|
|
7
|
+
}
|
|
8
|
+
export interface Clip {
|
|
9
|
+
fps: number;
|
|
10
|
+
name: string;
|
|
11
|
+
landmarks: Vector3Like[][];
|
|
12
|
+
scale: number;
|
|
13
|
+
aspectRatio: number;
|
|
14
|
+
}
|
|
15
|
+
export interface RecordedClip extends Clip {
|
|
16
|
+
frames: {
|
|
17
|
+
canvas: HTMLCanvasElement;
|
|
18
|
+
cropUV: UVCoord;
|
|
19
|
+
}[];
|
|
20
|
+
}
|
|
21
|
+
export interface MCapClip extends Clip {
|
|
22
|
+
frames: {
|
|
23
|
+
cropUV: UVCoord;
|
|
24
|
+
frameUV: UVCoord;
|
|
25
|
+
}[];
|
|
26
|
+
}
|
|
27
|
+
export interface MCapFile {
|
|
28
|
+
clips: MCapClip[];
|
|
29
|
+
version: number;
|
|
30
|
+
atlasSize: number;
|
|
31
|
+
atlasPadding: number;
|
|
32
|
+
/**
|
|
33
|
+
* Extract the clips from the atlas image using the metadata as a guide to know where the clips are.
|
|
34
|
+
* @param atlas
|
|
35
|
+
*/
|
|
36
|
+
unpackClips: (atlas: File | string | HTMLImageElement | HTMLCanvasElement) => Promise<RecordedClip[]>;
|
|
37
|
+
}
|
|
38
|
+
export interface MeshCapAtlas {
|
|
39
|
+
canvas: HTMLCanvasElement;
|
|
40
|
+
/**
|
|
41
|
+
* the index of each will correspond to the provided recorded clip's frames array at the time of creation
|
|
42
|
+
*/
|
|
43
|
+
clips: MCapClip[];
|
|
44
|
+
/**
|
|
45
|
+
* padding used in the creation of the atlas
|
|
46
|
+
*/
|
|
47
|
+
padding: number;
|
|
48
|
+
/**
|
|
49
|
+
*
|
|
50
|
+
*/
|
|
51
|
+
save(downloadFile: boolean): Promise<Blob>;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/meshcap/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AAInC,MAAM,WAAW,OAAO;IAAE,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAA;CAAC;AAE9D,MAAM,WAAW,IAAI;IACpB,GAAG,EAAC,MAAM,CAAC;IACX,IAAI,EAAC,MAAM,CAAC;IACZ,SAAS,EAAC,WAAW,EAAE,EAAE,CAAC;IAC1B,KAAK,EAAC,MAAM,CAAA;IACZ,WAAW,EAAC,MAAM,CAAC;CACnB;AAGD,MAAM,WAAW,YAAa,SAAQ,IAAI;IACzC,MAAM,EAAC;QAAE,MAAM,EAAC,iBAAiB,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;CACvD;AAED,MAAM,WAAW,QAAS,SAAQ,IAAI;IACrC,MAAM,EAAE;QAAE,MAAM,EAAC,OAAO,CAAC;QAAC,OAAO,EAAC,OAAO,CAAA;KAAE,EAAE,CAAA;CAC7C;AAED,MAAM,WAAW,QAAQ;IACxB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAC,MAAM,CAAC;IACjB,YAAY,EAAC,MAAM,CAAC;IAEpB;;;OAGG;IACH,WAAW,EAAE,CAAC,KAAK,EAAC,IAAI,GAAC,MAAM,GAAC,gBAAgB,GAAC,iBAAiB,KAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;CAC7F;AAED,MAAM,WAAW,YAAY;IACzB,MAAM,EAAE,iBAAiB,CAAC;IAE7B;;OAEG;IACA,KAAK,EAAE,QAAQ,EAAE,CAAC;IAErB;;OAEG;IACH,OAAO,EAAC,MAAM,CAAA;IAEd;;OAEG;IACH,IAAI,CAAE,YAAY,EAAC,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-mcap-file.d.ts","sourceRoot":"","sources":["../../src/meshcap/write-mcap-file.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAIvC,wBAAsB,WAAW,CAAC,KAAK,EAAE,YAAY,EAAE,oBAAoB,GAAC,OAAc,iBA2JzF"}
|
package/dist/meshcap.js
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { inflate as ht, deflate as ft } from "fflate";
|
|
2
|
+
import { F as N, c as mt } from "./face-tracker-utils-xt9__vBF.js";
|
|
3
|
+
import { MeshPhysicalNodeMaterial as pt, Vector3 as W } from "three/webgpu";
|
|
4
|
+
import { instancedArray as B, texture as G, uniform as O, select as ut, attribute as gt, varying as Ut, vec3 as wt, float as yt } from "three/tsl";
|
|
5
|
+
const J = 1296253264, _ = 1;
|
|
6
|
+
async function Rt(m) {
|
|
7
|
+
if (typeof m == "string") {
|
|
8
|
+
const o = await (await fetch(m)).arrayBuffer();
|
|
9
|
+
return K(o);
|
|
10
|
+
} else
|
|
11
|
+
return new Promise((u, o) => {
|
|
12
|
+
const e = new FileReader();
|
|
13
|
+
e.onload = (v) => {
|
|
14
|
+
const x = v.target.result;
|
|
15
|
+
u(K(x));
|
|
16
|
+
}, e.onerror = (v) => {
|
|
17
|
+
o(v);
|
|
18
|
+
}, e.readAsArrayBuffer(m);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
async function K(m) {
|
|
22
|
+
const u = await new Promise((a, r) => {
|
|
23
|
+
ht(new Uint8Array(m), (p, y) => {
|
|
24
|
+
p ? r(p) : a(y);
|
|
25
|
+
});
|
|
26
|
+
}), o = new DataView(u.buffer);
|
|
27
|
+
let e = 0;
|
|
28
|
+
const v = o.getUint32(e);
|
|
29
|
+
if (e += 4, v !== J) throw new Error("Invalid file: not a MCAP file");
|
|
30
|
+
const x = o.getUint8(e);
|
|
31
|
+
if (e += 1, x !== _) throw new Error(`Unsupported version: ${x} != ${_}`);
|
|
32
|
+
const h = o.getUint8(e);
|
|
33
|
+
e += 1;
|
|
34
|
+
const s = o.getUint16(e);
|
|
35
|
+
e += 2;
|
|
36
|
+
const t = o.getUint8(e);
|
|
37
|
+
e += 1;
|
|
38
|
+
const f = [];
|
|
39
|
+
for (let a = 0; a < h; a++) {
|
|
40
|
+
const r = o.getUint8(e);
|
|
41
|
+
e += 1;
|
|
42
|
+
const p = new Uint8Array(u.buffer, e, r), y = new TextDecoder().decode(p);
|
|
43
|
+
e += r;
|
|
44
|
+
const i = o.getUint8(e);
|
|
45
|
+
e += 1;
|
|
46
|
+
const U = o.getUint8(e);
|
|
47
|
+
e += 1;
|
|
48
|
+
const n = o.getUint8(e) / 100;
|
|
49
|
+
e += 1;
|
|
50
|
+
const c = o.getUint8(e) / 100;
|
|
51
|
+
e += 1;
|
|
52
|
+
const l = [], g = [], b = [];
|
|
53
|
+
for (let M = 0; M < i; M++) {
|
|
54
|
+
const k = o.getUint16(e) / 1e3;
|
|
55
|
+
e += 2;
|
|
56
|
+
const A = o.getUint16(e) / 1e3;
|
|
57
|
+
e += 2;
|
|
58
|
+
const R = o.getUint16(e) / 1e3;
|
|
59
|
+
e += 2;
|
|
60
|
+
const L = o.getUint16(e) / 1e3;
|
|
61
|
+
e += 2, l.push({ u: k, v: A, w: R, h: L });
|
|
62
|
+
const Y = o.getUint16(e) / 1e3;
|
|
63
|
+
e += 2;
|
|
64
|
+
const P = o.getUint16(e) / 1e3;
|
|
65
|
+
e += 2;
|
|
66
|
+
const q = o.getUint16(e) / 1e3;
|
|
67
|
+
e += 2;
|
|
68
|
+
const T = o.getUint16(e) / 1e3;
|
|
69
|
+
e += 2, b.push({ u: Y, v: P, w: q, h: T });
|
|
70
|
+
const C = [], z = M > 0 ? g[M - 1] : null;
|
|
71
|
+
for (let V = 0; V < N; V++)
|
|
72
|
+
if (z === null) {
|
|
73
|
+
const F = o.getUint16(e) / 1e3;
|
|
74
|
+
e += 2;
|
|
75
|
+
const E = o.getUint16(e) / 1e3;
|
|
76
|
+
e += 2;
|
|
77
|
+
const X = o.getInt16(e) / 1e3;
|
|
78
|
+
e += 2, C.push({ x: F, y: E, z: X });
|
|
79
|
+
} else {
|
|
80
|
+
const F = o.getInt8(e) / 1e3;
|
|
81
|
+
e += 1;
|
|
82
|
+
const E = o.getInt8(e) / 1e3;
|
|
83
|
+
e += 1;
|
|
84
|
+
const X = o.getInt8(e) / 1e3;
|
|
85
|
+
e += 1, C.push({
|
|
86
|
+
x: z[V].x + F,
|
|
87
|
+
y: z[V].y + E,
|
|
88
|
+
z: z[V].z + X
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
g.push(C);
|
|
92
|
+
}
|
|
93
|
+
f.push({
|
|
94
|
+
name: y,
|
|
95
|
+
fps: U,
|
|
96
|
+
scale: n,
|
|
97
|
+
aspectRatio: c,
|
|
98
|
+
frames: b.map((M, k) => ({
|
|
99
|
+
cropUV: M,
|
|
100
|
+
frameUV: l[k]
|
|
101
|
+
})),
|
|
102
|
+
landmarks: g
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
clips: f,
|
|
107
|
+
version: x,
|
|
108
|
+
atlasSize: s,
|
|
109
|
+
atlasPadding: t,
|
|
110
|
+
unpackClips: async (a) => {
|
|
111
|
+
let r, p = !0;
|
|
112
|
+
if (a instanceof HTMLCanvasElement)
|
|
113
|
+
p = !1, r = a;
|
|
114
|
+
else {
|
|
115
|
+
let i = new Image();
|
|
116
|
+
if (typeof a == "string")
|
|
117
|
+
i.src = a;
|
|
118
|
+
else if (a instanceof File || a instanceof Blob) {
|
|
119
|
+
const n = URL.createObjectURL(a);
|
|
120
|
+
i.src = n, await new Promise((c, l) => {
|
|
121
|
+
i.onload = () => {
|
|
122
|
+
URL.revokeObjectURL(n), c();
|
|
123
|
+
}, i.onerror = l;
|
|
124
|
+
});
|
|
125
|
+
} else a instanceof HTMLImageElement && (i = a);
|
|
126
|
+
(!i.complete || !i.naturalWidth) && await new Promise((n, c) => {
|
|
127
|
+
i.onload = () => n(), i.onerror = c;
|
|
128
|
+
}), r = document.createElement("canvas"), r.width = i.width, r.height = i.height, r.getContext("2d").drawImage(i, 0, 0);
|
|
129
|
+
}
|
|
130
|
+
const y = f.map((i) => ({
|
|
131
|
+
...i,
|
|
132
|
+
frames: i.frames.map((U) => {
|
|
133
|
+
const n = document.createElement("canvas");
|
|
134
|
+
return n.width = U.frameUV.w * r.width, n.height = U.frameUV.h * r.height, n.getContext("2d").drawImage(
|
|
135
|
+
r,
|
|
136
|
+
U.frameUV.u * r.width,
|
|
137
|
+
U.frameUV.v * r.height,
|
|
138
|
+
U.frameUV.w * r.width,
|
|
139
|
+
U.frameUV.h * r.height,
|
|
140
|
+
0,
|
|
141
|
+
0,
|
|
142
|
+
n.width,
|
|
143
|
+
n.height
|
|
144
|
+
), {
|
|
145
|
+
canvas: n,
|
|
146
|
+
cropUV: U.cropUV
|
|
147
|
+
};
|
|
148
|
+
})
|
|
149
|
+
}));
|
|
150
|
+
return p && r.remove(), y;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
async function vt(m, u = !0) {
|
|
155
|
+
const o = new TextEncoder(), e = m.clips.map((f) => o.encode(f.name)), v = 9, x = e.reduce((f, a, r) => {
|
|
156
|
+
const p = m.clips[r].frames.length, y = 1 + a.byteLength, i = 1, U = 1, n = 1, c = 1, l = 16;
|
|
157
|
+
let g = N * 6;
|
|
158
|
+
return f + y + i + U + n + c + (u ? l + g + (l + N * 3) * (p - 1) : (l + g) * p);
|
|
159
|
+
}, 0), h = new ArrayBuffer(v + x), s = new DataView(h);
|
|
160
|
+
let t = 0;
|
|
161
|
+
s.setUint32(t, J), t += 4, s.setUint8(t, _), t += 1, s.setUint8(t, m.clips.length), t += 1, s.setUint16(t, m.canvas.width), t += 2, s.setUint8(t, m.padding), t += 1;
|
|
162
|
+
for (let f = 0; f < m.clips.length; f++) {
|
|
163
|
+
s.setUint8(t, e[f].byteLength), t += 1, new Uint8Array(h, t, e[f].byteLength).set(e[f]), t += e[f].byteLength, s.setUint8(t, m.clips[f].frames.length), t += 1, s.setUint8(t, m.clips[f].fps), t += 1, s.setUint8(t, Math.round(m.clips[f].scale * 100)), t += 1, s.setUint8(t, Math.round(m.clips[f].aspectRatio * 100)), t += 1;
|
|
164
|
+
const a = m.clips[f];
|
|
165
|
+
for (let r = 0; r < a.frames.length; r++) {
|
|
166
|
+
const p = a.frames[r].cropUV, y = a.frames[r].frameUV;
|
|
167
|
+
s.setUint16(t, Math.round(y.u * 1e3)), t += 2, s.setUint16(t, Math.round(y.v * 1e3)), t += 2, s.setUint16(t, Math.round(y.w * 1e3)), t += 2, s.setUint16(t, Math.round(y.h * 1e3)), t += 2, s.setUint16(t, Math.round(p.u * 1e3)), t += 2, s.setUint16(t, Math.round(p.v * 1e3)), t += 2, s.setUint16(t, Math.round(p.w * 1e3)), t += 2, s.setUint16(t, Math.round(p.h * 1e3)), t += 2;
|
|
168
|
+
const i = a.landmarks[r], U = r > 0 ? a.landmarks[r - 1] : null;
|
|
169
|
+
i.reduce((n, c) => Math.max(n, Math.abs(c.z)), 0);
|
|
170
|
+
for (let n = 0; n < N; n++) {
|
|
171
|
+
const c = Math.round(i[n].x * 1e3), l = Math.round(i[n].y * 1e3), g = Math.round(i[n].z * 1e3);
|
|
172
|
+
if (u)
|
|
173
|
+
if (U === null)
|
|
174
|
+
s.setUint16(t, c), t += 2, s.setUint16(t, l), t += 2, s.setInt16(t, g), t += 2;
|
|
175
|
+
else {
|
|
176
|
+
const b = Math.round(U[n].x * 1e3), M = Math.round(U[n].y * 1e3), k = Math.round(U[n].z * 1e3), A = c - b, R = l - M, L = g - k;
|
|
177
|
+
console.log("Deltas: ", A), s.setInt8(t, A), t += 1, s.setInt8(t, R), t += 1, s.setInt8(t, L), t += 1;
|
|
178
|
+
}
|
|
179
|
+
else
|
|
180
|
+
s.setUint16(t, c), t += 2, s.setUint16(t, l), t += 2, s.setInt16(t, g), t += 2;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return new Promise((f, a) => {
|
|
185
|
+
ft(new Uint8Array(h), { level: 9 }, (r, p) => {
|
|
186
|
+
if (r) return a(r);
|
|
187
|
+
const y = new Blob([p], { type: "application/octet-stream" });
|
|
188
|
+
f(y);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
function xt(m, u) {
|
|
193
|
+
const o = [], e = [];
|
|
194
|
+
let v = 0;
|
|
195
|
+
const x = [...m].sort((h, s) => s.height - h.height);
|
|
196
|
+
for (const h of x) {
|
|
197
|
+
let s = !1;
|
|
198
|
+
for (const t of o)
|
|
199
|
+
if (h.width <= u - t.currentX && h.height <= t.height) {
|
|
200
|
+
e.push({ id: h.id, x: t.currentX, y: t.y, width: h.width, height: h.height }), t.currentX += h.width, s = !0;
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
if (!s) {
|
|
204
|
+
const t = {
|
|
205
|
+
y: v,
|
|
206
|
+
height: h.height,
|
|
207
|
+
currentX: h.width
|
|
208
|
+
};
|
|
209
|
+
o.push(t), e.push({ id: h.id, x: 0, y: v, width: h.width, height: h.height }), v += h.height;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return e;
|
|
213
|
+
}
|
|
214
|
+
const Mt = 20;
|
|
215
|
+
function Lt(m, u, o = 0) {
|
|
216
|
+
const e = m.flatMap((n) => n.frames.map((c) => c.canvas)), v = Array.from(e.entries()).map(([n, c]) => ({
|
|
217
|
+
id: n,
|
|
218
|
+
// index of the frame after all the frames from all the clips have been flattened
|
|
219
|
+
width: c.width + o * 2,
|
|
220
|
+
height: c.height + o * 2
|
|
221
|
+
})), x = xt(v, u), h = Math.max(...x.map((n) => n.y + n.height)) + Mt, s = u, t = Math.min(1, u / s, u / h), f = Math.floor(s * t), a = Math.floor(h * t), r = document.createElement("canvas");
|
|
222
|
+
r.width = f, r.height = a;
|
|
223
|
+
const p = r.getContext("2d");
|
|
224
|
+
p.fillStyle = "#000000", p.fillRect(0, 0, r.width, r.height);
|
|
225
|
+
const y = [];
|
|
226
|
+
for (const n of x) {
|
|
227
|
+
const c = e[n.id], l = n.x + o, g = n.y + o, b = c.width, M = c.height;
|
|
228
|
+
p.drawImage(c, l * t, g * t, b * t, M * t), y[n.id] = {
|
|
229
|
+
u: l * t / f,
|
|
230
|
+
v: g * t / a,
|
|
231
|
+
w: b * t / f,
|
|
232
|
+
h: M * t / a
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
const i = [];
|
|
236
|
+
let U = 0;
|
|
237
|
+
for (const n of m) {
|
|
238
|
+
const c = {
|
|
239
|
+
...n,
|
|
240
|
+
frames: []
|
|
241
|
+
};
|
|
242
|
+
for (const l of n.frames)
|
|
243
|
+
c.frames.push({
|
|
244
|
+
frameUV: y[U++],
|
|
245
|
+
cropUV: l.cropUV
|
|
246
|
+
});
|
|
247
|
+
i.push(c);
|
|
248
|
+
}
|
|
249
|
+
return p.font = `${Math.max(6, Math.floor(12 * t))}px monospace`, p.fillStyle = "#ff0000", p.fillText("Created with MeshCap : https://bandinopla.github.io/three-mediapipe-rig/?app=meshcap", 0, a - Math.max(2, 4 * t)), {
|
|
250
|
+
canvas: r,
|
|
251
|
+
clips: i,
|
|
252
|
+
padding: o,
|
|
253
|
+
async save(n) {
|
|
254
|
+
const c = await vt(this);
|
|
255
|
+
if (n) {
|
|
256
|
+
const l = URL.createObjectURL(c), g = document.createElement("a");
|
|
257
|
+
g.href = l, g.download = "atlas.mcap", g.click(), URL.revokeObjectURL(l), g.remove();
|
|
258
|
+
}
|
|
259
|
+
return c;
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function zt(m, u, o, e) {
|
|
264
|
+
e ?? (e = new pt()), o.geometry.hasAttribute("landmarkIndex") || mt(o);
|
|
265
|
+
const v = [], x = [], h = [];
|
|
266
|
+
let s = 0, t = 0;
|
|
267
|
+
for (let d = 0; d < u.length; d++) {
|
|
268
|
+
const w = u[d];
|
|
269
|
+
v.push(s, t, w.frames.length, w.fps), s += w.frames.length, t += w.frames.length * N, x.push(w.aspectRatio), h.push(d == 0 ? 0 : h[d - 1] + u[d - 1].frames.length);
|
|
270
|
+
}
|
|
271
|
+
const f = B(new Float32Array(x), "float"), a = B(new Uint32Array(v), "uvec4"), r = new Float32Array(u.flatMap((d) => d.frames).reduce((d, w) => (d.push(w.frameUV.u, w.frameUV.v, w.frameUV.w, w.frameUV.h), d), [])), p = B(r, "vec4"), y = new Float32Array(u.flatMap((d) => d.frames.flatMap((w) => [w.cropUV.u, w.cropUV.v, w.cropUV.w, w.cropUV.h]))), i = B(y, "vec4"), U = new Float32Array(u.flatMap((d) => d.landmarks.flatMap((w) => w.flatMap((I) => [I.x, I.y, I.z]))));
|
|
272
|
+
u.reduce((d, w) => d + w.frames.length, 0);
|
|
273
|
+
const n = B(U, "vec3"), c = G(m), l = O(0), g = O(0), b = O(!0), M = f.element(l), k = a.element(l).w, A = a.element(l).z, R = g.mul(k).floor().toUint(), L = ut(
|
|
274
|
+
b.not().and(R.greaterThanEqual(A)),
|
|
275
|
+
// no loop + past end
|
|
276
|
+
A.sub(1),
|
|
277
|
+
// → clamp to last frame
|
|
278
|
+
R.mod(A)
|
|
279
|
+
// → wrap around
|
|
280
|
+
), Y = B(new Uint32Array(h), "uint"), P = L, q = Y.element(l).add(L), T = p.element(a.element(l).x.add(P)), C = a.element(l).y.add(P.mul(N)), z = gt("landmarkIndex", "uint").toUint(), V = C.add(z), F = n.element(V), E = i.element(q), Q = F.xy.sub(E.xy).div(E.zw), tt = Ut(T.xy.add(Q.mul(T.zw))), et = G(c, tt);
|
|
281
|
+
e.colorNode = et;
|
|
282
|
+
const nt = n.element(C.add(234)).xy, $ = n.element(C.add(93)).xy, ot = n.element(C.add(454)).xy, Z = n.element(C.add(323)).xy, D = nt.sub($).div(2).add($), st = ot.sub(Z).div(2).add(Z).sub(D).div(2).add(D), S = o.geometry.attributes.position, j = 116, H = 346, rt = new W().subVectors(
|
|
283
|
+
new W(S.getX(j), S.getY(j), S.getZ(j)),
|
|
284
|
+
new W(S.getX(H), S.getY(H), S.getZ(H))
|
|
285
|
+
).lengthSq(), at = O(rt), ct = n.element(C.add(H)).sub(n.element(C.add(j))).lengthSq(), it = at.div(ct).sqrt().mul(2), lt = F.sub(st).xzy.mul(wt(1, -1, yt(1).div(M))).mul(it);
|
|
286
|
+
return e.positionNode = lt, {
|
|
287
|
+
clips: u,
|
|
288
|
+
atlasTexture: m,
|
|
289
|
+
material: e,
|
|
290
|
+
goto(d, w = !0) {
|
|
291
|
+
let I = -1;
|
|
292
|
+
if (typeof d == "number")
|
|
293
|
+
I = d;
|
|
294
|
+
else if (I = u.findIndex((dt) => dt.name === d), I === -1)
|
|
295
|
+
throw new Error(`Clip ${d.toString()} not found`);
|
|
296
|
+
b.value = w, g.value = 0, l.value = I;
|
|
297
|
+
},
|
|
298
|
+
update(d) {
|
|
299
|
+
g.value += d;
|
|
300
|
+
},
|
|
301
|
+
dispose() {
|
|
302
|
+
m.dispose(), e.dispose();
|
|
303
|
+
},
|
|
304
|
+
gotoAndLoop(d) {
|
|
305
|
+
this.goto(d, !0);
|
|
306
|
+
},
|
|
307
|
+
gotoAndPlay(d) {
|
|
308
|
+
this.goto(d, !1);
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
export {
|
|
313
|
+
Lt as buildMeshCapAtlas,
|
|
314
|
+
zt as createMeshCapMaterial,
|
|
315
|
+
Rt as loadMeshCapFile
|
|
316
|
+
};
|
package/dist/module.d.ts
CHANGED
package/dist/module.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AACA,cAAc,2BAA2B,CAAA"}
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AACA,cAAc,2BAA2B,CAAA;AACzC,cAAc,oCAAoC,CAAA"}
|
package/dist/rigger.d.ts
ADDED