@helix3/helix-cli 0.1.4-helix3.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +64 -0
- package/dist/ability.d.ts +73 -0
- package/dist/ability.js +120 -0
- package/dist/ability.js.map +1 -0
- package/dist/api.d.ts +24 -0
- package/dist/api.js +83 -0
- package/dist/api.js.map +1 -0
- package/dist/browserLogin.d.ts +22 -0
- package/dist/browserLogin.js +148 -0
- package/dist/browserLogin.js.map +1 -0
- package/dist/bundle.d.ts +15 -0
- package/dist/bundle.js +98 -0
- package/dist/bundle.js.map +1 -0
- package/dist/character/canonicalSkeleton.d.ts +3 -0
- package/dist/character/canonicalSkeleton.js +23 -0
- package/dist/character/canonicalSkeleton.js.map +1 -0
- package/dist/character/canonicalSkeletonData.d.ts +6 -0
- package/dist/character/canonicalSkeletonData.js +92 -0
- package/dist/character/canonicalSkeletonData.js.map +1 -0
- package/dist/character/esm.d.ts +1 -0
- package/dist/character/esm.js +6 -0
- package/dist/character/esm.js.map +1 -0
- package/dist/character/importCharacter.d.ts +88 -0
- package/dist/character/importCharacter.js +293 -0
- package/dist/character/importCharacter.js.map +1 -0
- package/dist/character/mat4.d.ts +29 -0
- package/dist/character/mat4.js +103 -0
- package/dist/character/mat4.js.map +1 -0
- package/dist/character/meshyToHelixMap.d.ts +2 -0
- package/dist/character/meshyToHelixMap.js +40 -0
- package/dist/character/meshyToHelixMap.js.map +1 -0
- package/dist/character/quat.d.ts +15 -0
- package/dist/character/quat.js +137 -0
- package/dist/character/quat.js.map +1 -0
- package/dist/character/stages/bakeTexture.d.ts +20 -0
- package/dist/character/stages/bakeTexture.js +396 -0
- package/dist/character/stages/bakeTexture.js.map +1 -0
- package/dist/character/stages/conform.d.ts +56 -0
- package/dist/character/stages/conform.js +292 -0
- package/dist/character/stages/conform.js.map +1 -0
- package/dist/character/stages/ktx2.d.ts +22 -0
- package/dist/character/stages/ktx2.js +97 -0
- package/dist/character/stages/ktx2.js.map +1 -0
- package/dist/character/stages/mergeLods.d.ts +15 -0
- package/dist/character/stages/mergeLods.js +42 -0
- package/dist/character/stages/mergeLods.js.map +1 -0
- package/dist/character/stages/report.d.ts +2 -0
- package/dist/character/stages/report.js +19 -0
- package/dist/character/stages/report.js.map +1 -0
- package/dist/character/stages/resizeTextures.d.ts +11 -0
- package/dist/character/stages/resizeTextures.js +62 -0
- package/dist/character/stages/resizeTextures.js.map +1 -0
- package/dist/character/stages/retarget.d.ts +49 -0
- package/dist/character/stages/retarget.js +105 -0
- package/dist/character/stages/retarget.js.map +1 -0
- package/dist/character/stages/retargetClip.d.ts +27 -0
- package/dist/character/stages/retargetClip.js +237 -0
- package/dist/character/stages/retargetClip.js.map +1 -0
- package/dist/character/stages/simplifyMesh.d.ts +15 -0
- package/dist/character/stages/simplifyMesh.js +37 -0
- package/dist/character/stages/simplifyMesh.js.map +1 -0
- package/dist/character/stages/splitFacePrimitive.d.ts +30 -0
- package/dist/character/stages/splitFacePrimitive.js +115 -0
- package/dist/character/stages/splitFacePrimitive.js.map +1 -0
- package/dist/character/stages/weldedSimplify.d.ts +32 -0
- package/dist/character/stages/weldedSimplify.js +202 -0
- package/dist/character/stages/weldedSimplify.js.map +1 -0
- package/dist/character/testutil/syntheticRig.d.ts +9 -0
- package/dist/character/testutil/syntheticRig.js +84 -0
- package/dist/character/testutil/syntheticRig.js.map +1 -0
- package/dist/config.d.ts +25 -0
- package/dist/config.js +103 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +378 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +1 -0
- package/dist/init.js +87 -0
- package/dist/init.js.map +1 -0
- package/dist/initAbility.d.ts +1 -0
- package/dist/initAbility.js +71 -0
- package/dist/initAbility.js.map +1 -0
- package/dist/install.d.ts +51 -0
- package/dist/install.js +201 -0
- package/dist/install.js.map +1 -0
- package/dist/lib.d.ts +88 -0
- package/dist/lib.js +347 -0
- package/dist/lib.js.map +1 -0
- package/dist/package.d.ts +11 -0
- package/dist/package.js +136 -0
- package/dist/package.js.map +1 -0
- package/dist/prompt.d.ts +2 -0
- package/dist/prompt.js +55 -0
- package/dist/prompt.js.map +1 -0
- package/dist/resolve.d.ts +57 -0
- package/dist/resolve.js +103 -0
- package/dist/resolve.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/package.json +58 -0
- package/vendor/ability-manifest.mjs +558 -0
- package/vendor/installed-index.mjs +71 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// PORTED from helix-web-engine-new/tools/character-pipeline — the source of truth for the character
|
|
3
|
+
// pipeline. SYNC OBLIGATION: fix bugs there first, then mirror the change here; do not diverge.
|
|
4
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
5
|
+
if (k2 === undefined) k2 = k;
|
|
6
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
7
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
8
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
9
|
+
}
|
|
10
|
+
Object.defineProperty(o, k2, desc);
|
|
11
|
+
}) : (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
o[k2] = m[k];
|
|
14
|
+
}));
|
|
15
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
16
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
17
|
+
}) : function(o, v) {
|
|
18
|
+
o["default"] = v;
|
|
19
|
+
});
|
|
20
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
21
|
+
var ownKeys = function(o) {
|
|
22
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
23
|
+
var ar = [];
|
|
24
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
25
|
+
return ar;
|
|
26
|
+
};
|
|
27
|
+
return ownKeys(o);
|
|
28
|
+
};
|
|
29
|
+
return function (mod) {
|
|
30
|
+
if (mod && mod.__esModule) return mod;
|
|
31
|
+
var result = {};
|
|
32
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
33
|
+
__setModuleDefault(result, mod);
|
|
34
|
+
return result;
|
|
35
|
+
};
|
|
36
|
+
})();
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.resizeTextures = resizeTextures;
|
|
39
|
+
/**
|
|
40
|
+
* Downsizes every embedded texture whose longest edge exceeds maxSize (PNG re-encode via sharp).
|
|
41
|
+
* Covers textures that arrive embedded in the UE export (the baked eye materials) — the head/body
|
|
42
|
+
* bake goes through its own TGA-decode resize in the CLI before it ever enters the document.
|
|
43
|
+
*/
|
|
44
|
+
async function resizeTextures(document, maxSize) {
|
|
45
|
+
const sharp = (await Promise.resolve().then(() => __importStar(require('sharp')))).default;
|
|
46
|
+
const resized = [];
|
|
47
|
+
for (const texture of document.getRoot().listTextures()) {
|
|
48
|
+
const image = texture.getImage();
|
|
49
|
+
if (image === null)
|
|
50
|
+
continue;
|
|
51
|
+
const meta = await sharp(image).metadata();
|
|
52
|
+
const width = meta.width ?? 0;
|
|
53
|
+
const height = meta.height ?? 0;
|
|
54
|
+
if (width <= 0 || height <= 0 || Math.max(width, height) <= maxSize)
|
|
55
|
+
continue;
|
|
56
|
+
const out = await sharp(image).resize(maxSize, maxSize, { fit: 'inside' }).png().toBuffer();
|
|
57
|
+
texture.setImage(new Uint8Array(out)).setMimeType('image/png');
|
|
58
|
+
resized.push(`${texture.getName()} ${width}x${height} -> <=${maxSize}`);
|
|
59
|
+
}
|
|
60
|
+
return { resized };
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=resizeTextures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resizeTextures.js","sourceRoot":"","sources":["../../../src/character/stages/resizeTextures.ts"],"names":[],"mappings":";AAAA,oGAAoG;AACpG,gGAAgG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAchG,wCAeC;AApBD;;;;GAIG;AACI,KAAK,UAAU,cAAc,CAAC,QAAkB,EAAE,OAAe;IACtE,MAAM,KAAK,GAAG,CAAC,wDAAa,OAAO,GAAC,CAAC,CAAC,OAAO,CAAC;IAC9C,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC,YAAY,EAAE,EAAE,CAAC;QACxD,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;QACjC,IAAI,KAAK,KAAK,IAAI;YAAE,SAAS;QAC7B,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC;QAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;QAChC,IAAI,KAAK,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,OAAO;YAAE,SAAS;QAC9E,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;QAC5F,OAAO,CAAC,QAAQ,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;QAC/D,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,OAAO,EAAE,IAAI,KAAK,IAAI,MAAM,SAAS,OAAO,EAAE,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Document } from '@gltf-transform/core';
|
|
2
|
+
export interface BoneMap {
|
|
3
|
+
name: string;
|
|
4
|
+
version: string;
|
|
5
|
+
comment?: string;
|
|
6
|
+
/** source bone name -> helix-humanoid@1 bone name */
|
|
7
|
+
map: Record<string, string>;
|
|
8
|
+
/** source bones with no helix target, removed if they are childless leaves */
|
|
9
|
+
drop?: string[];
|
|
10
|
+
}
|
|
11
|
+
export interface RetargetResult {
|
|
12
|
+
/** source name -> helix name, for every joint actually renamed */
|
|
13
|
+
renamed: Record<string, string>;
|
|
14
|
+
/** source bones present in the rig but absent from the map (left untouched) */
|
|
15
|
+
unmapped: string[];
|
|
16
|
+
/** dropped leaf helper bones */
|
|
17
|
+
dropped: string[];
|
|
18
|
+
}
|
|
19
|
+
export interface MapCoverage {
|
|
20
|
+
/** number of skin joints found on the source rig (0 = not a skinned character at all) */
|
|
21
|
+
sourceJoints: number;
|
|
22
|
+
/** map keys present on the source rig */
|
|
23
|
+
matched: string[];
|
|
24
|
+
/** canonical (helix) bone names the matched sources cover */
|
|
25
|
+
coveredTargets: Set<string>;
|
|
26
|
+
/** source joints in neither map nor drop (kept as-is by retargetSkeleton; deformation unverified) */
|
|
27
|
+
extra: string[];
|
|
28
|
+
/** true when the rig uses raw Mixamo download naming ('mixamorig:' prefixes) — the bundled map expects bare names */
|
|
29
|
+
mixamoPrefixed: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Preflight for retarget/conform: how much of the source rig does a bone map actually cover?
|
|
33
|
+
* Coverage is judged in CANONICAL terms (which helix bones receive a source match), so callers can
|
|
34
|
+
* enforce the same requirements against any custom map, not just the bundled Meshy one.
|
|
35
|
+
*/
|
|
36
|
+
export declare function checkMapCoverage(document: Document, boneMap: BoneMap): MapCoverage;
|
|
37
|
+
/**
|
|
38
|
+
* Renames a source biped rig's joints to `helix-humanoid@1` bone names by an explicit map.
|
|
39
|
+
*
|
|
40
|
+
* This is the NAME half of the retarget (architecture §6.2): glTF animation channels target nodes
|
|
41
|
+
* by INDEX, so a joint rename automatically re-homes every clip track and the skin's joint list —
|
|
42
|
+
* the mesh's inverse bind matrices are untouched, so the body renders identically, now driven by
|
|
43
|
+
* helix bone names. The REST-POSE correction half (aligning the source rest orientation to the
|
|
44
|
+
* MetaHuman rest so the platform's own clips look right) is a separate stage; this alone is enough
|
|
45
|
+
* to drive the rig with clips authored IN ITS OWN rest frame (its bundled walk).
|
|
46
|
+
*
|
|
47
|
+
* Drop-list leaves (Meshy's head_end/headfront helpers) are removed from the graph + skin + IBMs.
|
|
48
|
+
*/
|
|
49
|
+
export declare function retargetSkeleton(document: Document, boneMap: BoneMap): RetargetResult;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// PORTED from helix-web-engine-new/tools/character-pipeline — the source of truth for the character
|
|
3
|
+
// pipeline. SYNC OBLIGATION: fix bugs there first, then mirror the change here; do not diverge.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.checkMapCoverage = checkMapCoverage;
|
|
6
|
+
exports.retargetSkeleton = retargetSkeleton;
|
|
7
|
+
const core_1 = require("@gltf-transform/core");
|
|
8
|
+
function parentOf(node) {
|
|
9
|
+
for (const parent of node.listParents()) {
|
|
10
|
+
if (parent.propertyType === core_1.PropertyType.NODE)
|
|
11
|
+
return parent;
|
|
12
|
+
if (parent.propertyType === core_1.PropertyType.SCENE)
|
|
13
|
+
return parent;
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Preflight for retarget/conform: how much of the source rig does a bone map actually cover?
|
|
19
|
+
* Coverage is judged in CANONICAL terms (which helix bones receive a source match), so callers can
|
|
20
|
+
* enforce the same requirements against any custom map, not just the bundled Meshy one.
|
|
21
|
+
*/
|
|
22
|
+
function checkMapCoverage(document, boneMap) {
|
|
23
|
+
const joints = new Set();
|
|
24
|
+
for (const skin of document.getRoot().listSkins())
|
|
25
|
+
for (const j of skin.listJoints())
|
|
26
|
+
joints.add(j.getName());
|
|
27
|
+
const drop = new Set(boneMap.drop ?? []);
|
|
28
|
+
const matched = Object.keys(boneMap.map).filter((k) => joints.has(k));
|
|
29
|
+
return {
|
|
30
|
+
sourceJoints: joints.size,
|
|
31
|
+
matched,
|
|
32
|
+
coveredTargets: new Set(matched.map((k) => boneMap.map[k])),
|
|
33
|
+
extra: [...joints].filter((n) => !(n in boneMap.map) && !drop.has(n)),
|
|
34
|
+
mixamoPrefixed: [...joints].some((n) => n.startsWith('mixamorig')),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Renames a source biped rig's joints to `helix-humanoid@1` bone names by an explicit map.
|
|
39
|
+
*
|
|
40
|
+
* This is the NAME half of the retarget (architecture §6.2): glTF animation channels target nodes
|
|
41
|
+
* by INDEX, so a joint rename automatically re-homes every clip track and the skin's joint list —
|
|
42
|
+
* the mesh's inverse bind matrices are untouched, so the body renders identically, now driven by
|
|
43
|
+
* helix bone names. The REST-POSE correction half (aligning the source rest orientation to the
|
|
44
|
+
* MetaHuman rest so the platform's own clips look right) is a separate stage; this alone is enough
|
|
45
|
+
* to drive the rig with clips authored IN ITS OWN rest frame (its bundled walk).
|
|
46
|
+
*
|
|
47
|
+
* Drop-list leaves (Meshy's head_end/headfront helpers) are removed from the graph + skin + IBMs.
|
|
48
|
+
*/
|
|
49
|
+
function retargetSkeleton(document, boneMap) {
|
|
50
|
+
const root = document.getRoot();
|
|
51
|
+
const map = boneMap.map;
|
|
52
|
+
const dropSet = new Set(boneMap.drop ?? []);
|
|
53
|
+
const renamed = {};
|
|
54
|
+
const unmapped = [];
|
|
55
|
+
const dropped = [];
|
|
56
|
+
// Collect joint nodes from every skin (a source name may appear once; rename is idempotent per node).
|
|
57
|
+
const jointNodes = new Set();
|
|
58
|
+
for (const skin of root.listSkins())
|
|
59
|
+
for (const j of skin.listJoints())
|
|
60
|
+
jointNodes.add(j);
|
|
61
|
+
// 1. Drop childless leaf helpers first (so they never enter the rename/IBM bookkeeping).
|
|
62
|
+
for (const skin of root.listSkins()) {
|
|
63
|
+
const joints = skin.listJoints();
|
|
64
|
+
const dropIdx = joints
|
|
65
|
+
.map((j, i) => ({ j, i }))
|
|
66
|
+
.filter(({ j }) => dropSet.has(j.getName()) && j.listChildren().length === 0);
|
|
67
|
+
if (dropIdx.length === 0)
|
|
68
|
+
continue;
|
|
69
|
+
const oldIbm = skin.getInverseBindMatrices();
|
|
70
|
+
const keep = joints.filter((j) => !(dropSet.has(j.getName()) && j.listChildren().length === 0));
|
|
71
|
+
if (oldIbm !== null) {
|
|
72
|
+
const oldData = oldIbm.getArray();
|
|
73
|
+
const newData = new Float32Array(keep.length * 16);
|
|
74
|
+
keep.forEach((j, newIndex) => {
|
|
75
|
+
const oldIndex = joints.indexOf(j);
|
|
76
|
+
newData.set(oldData.subarray(oldIndex * 16, oldIndex * 16 + 16), newIndex * 16);
|
|
77
|
+
});
|
|
78
|
+
oldIbm.setArray(newData);
|
|
79
|
+
}
|
|
80
|
+
for (const { j } of dropIdx) {
|
|
81
|
+
skin.removeJoint(j);
|
|
82
|
+
if (!dropped.includes(j.getName()))
|
|
83
|
+
dropped.push(j.getName());
|
|
84
|
+
jointNodes.delete(j);
|
|
85
|
+
const parent = parentOf(j);
|
|
86
|
+
if (parent !== null)
|
|
87
|
+
for (const child of [...j.listChildren()])
|
|
88
|
+
parent.addChild(child);
|
|
89
|
+
j.dispose();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// 2. Rename mapped joints. Channels target nodes by index → clip tracks follow the rename for free.
|
|
93
|
+
for (const node of jointNodes) {
|
|
94
|
+
const src = node.getName();
|
|
95
|
+
const dst = map[src];
|
|
96
|
+
if (dst === undefined) {
|
|
97
|
+
unmapped.push(src);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
node.setName(dst);
|
|
101
|
+
renamed[src] = dst;
|
|
102
|
+
}
|
|
103
|
+
return { renamed, unmapped, dropped };
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=retarget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retarget.js","sourceRoot":"","sources":["../../../src/character/stages/retarget.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Document } from '@gltf-transform/core';
|
|
2
|
+
import type { BoneMap } from './retarget';
|
|
3
|
+
import type { CanonicalSkeleton } from './conform';
|
|
4
|
+
export interface RetargetClipResult {
|
|
5
|
+
document: Document;
|
|
6
|
+
meta: {
|
|
7
|
+
name: string;
|
|
8
|
+
duration: number;
|
|
9
|
+
loop: boolean;
|
|
10
|
+
strideSpeed: null;
|
|
11
|
+
additive: false;
|
|
12
|
+
};
|
|
13
|
+
frames: number;
|
|
14
|
+
/** helix bones that received a retargeted rotation track */
|
|
15
|
+
animatedBones: string[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Retargets a source animation CLIP onto the canonical `helix-humanoid@1` skeleton via world-space
|
|
19
|
+
* motion transfer (the architecture §6.2 / three-vrm approach): each mapped bone's WORLD-space rotation
|
|
20
|
+
* delta from the SOURCE rest is re-applied to the TARGET rest, then converted back to the target's local
|
|
21
|
+
* frame. Because the motion is transferred as a delta-from-rest in world space, the source↔target
|
|
22
|
+
* bone-axis convention cancels — the same reason the mesh embed is convention-proof. Root (pelvis)
|
|
23
|
+
* translation is retargeted as an in-place bob/sway scaled to the target's hip height. Unmapped target
|
|
24
|
+
* bones (spine_02/04, twists, fingers) stay at rest. Output is a clip-only GLB (canonical skeleton
|
|
25
|
+
* nodes + one animation, no mesh/skin) — the platform clip format.
|
|
26
|
+
*/
|
|
27
|
+
export declare function retargetClip(source: Document, canon: CanonicalSkeleton, boneMap: BoneMap, clipName: string): RetargetClipResult;
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// PORTED from helix-web-engine-new/tools/character-pipeline — the source of truth for the character
|
|
3
|
+
// pipeline. SYNC OBLIGATION: fix bugs there first, then mirror the change here; do not diverge.
|
|
4
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
5
|
+
if (k2 === undefined) k2 = k;
|
|
6
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
7
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
8
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
9
|
+
}
|
|
10
|
+
Object.defineProperty(o, k2, desc);
|
|
11
|
+
}) : (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
o[k2] = m[k];
|
|
14
|
+
}));
|
|
15
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
16
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
17
|
+
}) : function(o, v) {
|
|
18
|
+
o["default"] = v;
|
|
19
|
+
});
|
|
20
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
21
|
+
var ownKeys = function(o) {
|
|
22
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
23
|
+
var ar = [];
|
|
24
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
25
|
+
return ar;
|
|
26
|
+
};
|
|
27
|
+
return ownKeys(o);
|
|
28
|
+
};
|
|
29
|
+
return function (mod) {
|
|
30
|
+
if (mod && mod.__esModule) return mod;
|
|
31
|
+
var result = {};
|
|
32
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
33
|
+
__setModuleDefault(result, mod);
|
|
34
|
+
return result;
|
|
35
|
+
};
|
|
36
|
+
})();
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.retargetClip = retargetClip;
|
|
39
|
+
const core_1 = require("@gltf-transform/core");
|
|
40
|
+
const mat4_1 = require("../mat4");
|
|
41
|
+
const q = __importStar(require("../quat"));
|
|
42
|
+
function jointParent(node, jointSet) {
|
|
43
|
+
for (const p of node.listParents())
|
|
44
|
+
if (p.propertyType === core_1.PropertyType.NODE && jointSet.has(p))
|
|
45
|
+
return p;
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Retargets a source animation CLIP onto the canonical `helix-humanoid@1` skeleton via world-space
|
|
50
|
+
* motion transfer (the architecture §6.2 / three-vrm approach): each mapped bone's WORLD-space rotation
|
|
51
|
+
* delta from the SOURCE rest is re-applied to the TARGET rest, then converted back to the target's local
|
|
52
|
+
* frame. Because the motion is transferred as a delta-from-rest in world space, the source↔target
|
|
53
|
+
* bone-axis convention cancels — the same reason the mesh embed is convention-proof. Root (pelvis)
|
|
54
|
+
* translation is retargeted as an in-place bob/sway scaled to the target's hip height. Unmapped target
|
|
55
|
+
* bones (spine_02/04, twists, fingers) stay at rest. Output is a clip-only GLB (canonical skeleton
|
|
56
|
+
* nodes + one animation, no mesh/skin) — the platform clip format.
|
|
57
|
+
*/
|
|
58
|
+
function retargetClip(source, canon, boneMap, clipName) {
|
|
59
|
+
// ---- source rig: hierarchy (DFS parent-first), rest local/world rotations, and clip tracks --------
|
|
60
|
+
const srcSkin = source.getRoot().listSkins()[0];
|
|
61
|
+
if (srcSkin === undefined)
|
|
62
|
+
throw new Error('retargetClip: source has no skin');
|
|
63
|
+
const srcJoints = srcSkin.listJoints();
|
|
64
|
+
const jointSet = new Set(srcJoints);
|
|
65
|
+
const srcRoot = srcSkin.getSkeleton() ?? srcJoints.find((j) => jointParent(j, jointSet) === null) ?? srcJoints[0];
|
|
66
|
+
const srcOrder = [];
|
|
67
|
+
(function dfs(n) { srcOrder.push(n); for (const c of n.listChildren())
|
|
68
|
+
if (jointSet.has(c))
|
|
69
|
+
dfs(c); })(srcRoot);
|
|
70
|
+
const srcParentName = new Map();
|
|
71
|
+
for (const j of srcOrder)
|
|
72
|
+
srcParentName.set(j.getName(), jointParent(j, jointSet)?.getName() ?? null);
|
|
73
|
+
const srcRestLocalQuat = new Map(srcOrder.map((j) => [j.getName(), j.getRotation()]));
|
|
74
|
+
const srcRestLocalTrans = new Map(srcOrder.map((j) => [j.getName(), [...j.getTranslation()]]));
|
|
75
|
+
const srcRestWorldQuat = new Map();
|
|
76
|
+
for (const j of srcOrder) {
|
|
77
|
+
const p = srcParentName.get(j.getName());
|
|
78
|
+
srcRestWorldQuat.set(j.getName(), q.normalize(p === null ? srcRestLocalQuat.get(j.getName()) : q.mul(srcRestWorldQuat.get(p), srcRestLocalQuat.get(j.getName()))));
|
|
79
|
+
}
|
|
80
|
+
const anim = source.getRoot().listAnimations()[0];
|
|
81
|
+
if (anim === undefined)
|
|
82
|
+
throw new Error('retargetClip: source has no animation');
|
|
83
|
+
const rotTrack = new Map();
|
|
84
|
+
const transTrack = new Map();
|
|
85
|
+
const allTimes = new Set();
|
|
86
|
+
let hasRot = false;
|
|
87
|
+
for (const ch of anim.listChannels()) {
|
|
88
|
+
const node = ch.getTargetNode();
|
|
89
|
+
const s = ch.getSampler();
|
|
90
|
+
if (node === null || s === null)
|
|
91
|
+
continue;
|
|
92
|
+
const times = s.getInput().getArray();
|
|
93
|
+
const out = s.getOutput().getArray();
|
|
94
|
+
// Master timeline = union of rotation ∪ translation input times, so a bob key at a different density
|
|
95
|
+
// than the rotation keys isn't aliased away (scale/STEP tracks are dropped and don't contribute).
|
|
96
|
+
if (ch.getTargetPath() === 'rotation') {
|
|
97
|
+
rotTrack.set(node.getName(), { times, quats: out });
|
|
98
|
+
hasRot = true;
|
|
99
|
+
for (const t of times)
|
|
100
|
+
allTimes.add(t);
|
|
101
|
+
}
|
|
102
|
+
else if (ch.getTargetPath() === 'translation') {
|
|
103
|
+
transTrack.set(node.getName(), { times, vals: out });
|
|
104
|
+
for (const t of times)
|
|
105
|
+
allTimes.add(t);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (!hasRot)
|
|
109
|
+
throw new Error('retargetClip: source animation has no rotation channels');
|
|
110
|
+
const srcTimes = [...allTimes].sort((a, b) => a - b);
|
|
111
|
+
const t0 = srcTimes[0];
|
|
112
|
+
const outTimes = srcTimes.map((t) => t - t0);
|
|
113
|
+
const frames = srcTimes.length;
|
|
114
|
+
// ---- target rig: canonical rest local/world rotations + pelvis rest ------------------------------
|
|
115
|
+
const canonParent = new Map(canon.nodes.map((n) => [n.name, n.parent]));
|
|
116
|
+
const canonLocalMat = new Map(canon.nodes.map((n) => [n.name, n.local]));
|
|
117
|
+
const canonRestLocalQuat = new Map(canon.nodes.map((n) => [n.name, q.fromMat((0, mat4_1.rigidize)(n.local))]));
|
|
118
|
+
const canonRestWorldQuat = new Map();
|
|
119
|
+
for (const n of canon.nodes) {
|
|
120
|
+
const p = n.parent;
|
|
121
|
+
canonRestWorldQuat.set(n.name, q.normalize(p === null ? canonRestLocalQuat.get(n.name) : q.mul(canonRestWorldQuat.get(p), canonRestLocalQuat.get(n.name))));
|
|
122
|
+
}
|
|
123
|
+
const pelvisLocal = canonLocalMat.get('pelvis');
|
|
124
|
+
const canonPelvisRestLocalTrans = [pelvisLocal[12], pelvisLocal[13], pelvisLocal[14]];
|
|
125
|
+
const canonPelvisRestWorldY = canon.worldByName.get('pelvis')[13];
|
|
126
|
+
// invert the bone map (helix -> source), direct entries only
|
|
127
|
+
const invMap = new Map();
|
|
128
|
+
for (const [src, helix] of Object.entries(boneMap.map))
|
|
129
|
+
invMap.set(helix, src);
|
|
130
|
+
// pelvis bob scale: source hips local translation (armature-scaled, ~cm) -> canonical-meter bob.
|
|
131
|
+
const hipsSrcName = invMap.get('pelvis');
|
|
132
|
+
const hipsNode = srcJoints.find((j) => j.getName() === hipsSrcName);
|
|
133
|
+
const hipsWorldRestY = hipsNode.getWorldMatrix()[13];
|
|
134
|
+
const hipsLocalRest = srcRestLocalTrans.get(hipsSrcName);
|
|
135
|
+
const bobScale = Math.abs(hipsLocalRest[1]) > 1e-6 ? canonPelvisRestWorldY / hipsLocalRest[1] : 1;
|
|
136
|
+
// Rotation that carries a vector from the source pelvis-parent local frame into the target pelvis-parent
|
|
137
|
+
// local frame (via world): conj(dstParentWorld)·srcParentWorld. Without it a vertical bob in the source
|
|
138
|
+
// frame lands on the wrong target axis (the two rigs' conventions differ) → a sideways shove.
|
|
139
|
+
const srcHipsParentName = srcParentName.get(hipsSrcName) ?? null;
|
|
140
|
+
const srcHipsParentWorldRot = srcHipsParentName !== null ? srcRestWorldQuat.get(srcHipsParentName) : [0, 0, 0, 1];
|
|
141
|
+
const canonPelvisParentName = canonParent.get('pelvis') ?? null;
|
|
142
|
+
const canonPelvisParentWorldRot = canonPelvisParentName !== null ? canonRestWorldQuat.get(canonPelvisParentName) : [0, 0, 0, 1];
|
|
143
|
+
const bobRot = q.mul(q.conj(canonPelvisParentWorldRot), srcHipsParentWorldRot);
|
|
144
|
+
// Clavicle rest-correction (mirror of the embed lane). The rigs' clavicle RESTS differ ~9° (Meshy
|
|
145
|
+
// shoulder ~horizontal, MetaHuman droops). The transfer maps source-rest → canonical-rest, so a Meshy
|
|
146
|
+
// shoulder held horizontal lands ~9° too low (dropped shoulders). For the two clavicles, use a rest
|
|
147
|
+
// REFERENCE aligned to the SOURCE clavicle direction, so the imported animation preserves the source's
|
|
148
|
+
// shoulder carriage. Only the clavicle bone shifts — the child upperarm's local compensates (its world
|
|
149
|
+
// orientation is unchanged), so the arm just rides the lifted shoulder instead of tilting.
|
|
150
|
+
const ORIGIN = [0, 0, 0];
|
|
151
|
+
const posOf = (m) => [m[12], m[13], m[14]];
|
|
152
|
+
const restRef = new Map(canon.nodes.map((n) => [n.name, canonRestWorldQuat.get(n.name)]));
|
|
153
|
+
for (const [clav, upper] of [['clavicle_l', 'upperarm_l'], ['clavicle_r', 'upperarm_r']]) {
|
|
154
|
+
const srcClav = invMap.get(clav), srcUpper = invMap.get(upper);
|
|
155
|
+
const scn = srcClav !== undefined ? srcJoints.find((j) => j.getName() === srcClav) : undefined;
|
|
156
|
+
const sun = srcUpper !== undefined ? srcJoints.find((j) => j.getName() === srcUpper) : undefined;
|
|
157
|
+
if (scn === undefined || sun === undefined)
|
|
158
|
+
continue;
|
|
159
|
+
const srcDir = (0, mat4_1.normalize3)((0, mat4_1.sub3)(posOf(sun.getWorldMatrix()), posOf(scn.getWorldMatrix())));
|
|
160
|
+
const canonDir = (0, mat4_1.normalize3)((0, mat4_1.sub3)(posOf(canon.worldByName.get(upper)), posOf(canon.worldByName.get(clav))));
|
|
161
|
+
const O = q.fromMat((0, mat4_1.mul4)((0, mat4_1.frameFromAxis)(canonDir, ORIGIN), (0, mat4_1.invert4)((0, mat4_1.frameFromAxis)(srcDir, ORIGIN)))); // src->canon world rot
|
|
162
|
+
restRef.set(clav, q.normalize(q.mul(q.conj(O), canonRestWorldQuat.get(clav)))); // canon rest, re-aimed at source dir
|
|
163
|
+
}
|
|
164
|
+
// ---- per-frame retarget --------------------------------------------------------------------------
|
|
165
|
+
const animatedBones = [...invMap.keys()].filter((c) => canonParent.has(c));
|
|
166
|
+
const outRot = new Map(animatedBones.map((c) => [c, new Float32Array(frames * 4)]));
|
|
167
|
+
const outPelvis = new Float32Array(frames * 3);
|
|
168
|
+
for (let f = 0; f < frames; f++) {
|
|
169
|
+
const tOrig = srcTimes[f];
|
|
170
|
+
// 1. source FK pose
|
|
171
|
+
const srcWorld = new Map();
|
|
172
|
+
for (const j of srcOrder) {
|
|
173
|
+
const name = j.getName();
|
|
174
|
+
const track = rotTrack.get(name);
|
|
175
|
+
const local = track ? q.sampleQuat(track.times, track.quats, tOrig) : srcRestLocalQuat.get(name);
|
|
176
|
+
const p = srcParentName.get(name);
|
|
177
|
+
srcWorld.set(name, q.normalize(p === null ? local : q.mul(srcWorld.get(p), local)));
|
|
178
|
+
}
|
|
179
|
+
// 2+3. target world rotations. Mapped bones follow the source's world delta-from-rest. UNMAPPED
|
|
180
|
+
// bones PROPAGATE their (already-retargeted) parent's motion while keeping their canonical rest
|
|
181
|
+
// LOCAL — matching how they behave at playback (no track ⇒ rest-local under a moving parent). Freezing
|
|
182
|
+
// them at rest WORLD instead would make a mapped child counter-rotate and shear the spine.
|
|
183
|
+
const dstWorld = new Map();
|
|
184
|
+
for (const n of canon.nodes) {
|
|
185
|
+
const s = invMap.get(n.name);
|
|
186
|
+
if (s !== undefined && srcWorld.has(s)) {
|
|
187
|
+
const delta = q.mul(srcWorld.get(s), q.conj(srcRestWorldQuat.get(s)));
|
|
188
|
+
dstWorld.set(n.name, q.normalize(q.mul(delta, restRef.get(n.name)))); // restRef = canon rest, clavicle re-aimed at source
|
|
189
|
+
}
|
|
190
|
+
else if (n.parent === null) {
|
|
191
|
+
dstWorld.set(n.name, canonRestWorldQuat.get(n.name));
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
dstWorld.set(n.name, q.normalize(q.mul(dstWorld.get(n.parent), canonRestLocalQuat.get(n.name))));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// 4. mapped bones -> local rotation track
|
|
198
|
+
for (const c of animatedBones) {
|
|
199
|
+
const p = canonParent.get(c);
|
|
200
|
+
const local = p === null ? dstWorld.get(c) : q.mul(q.conj(dstWorld.get(p)), dstWorld.get(c));
|
|
201
|
+
outRot.get(c).set(local, f * 4);
|
|
202
|
+
}
|
|
203
|
+
// 5. pelvis in-place bob — route the source-local delta through world into the target-local frame.
|
|
204
|
+
const hipsTrack = transTrack.get(hipsSrcName);
|
|
205
|
+
const hips = hipsTrack ? q.sampleVec3(hipsTrack.times, hipsTrack.vals, tOrig) : hipsLocalRest;
|
|
206
|
+
const bob = q.rotateVec(bobRot, [bobScale * (hips[0] - hipsLocalRest[0]), bobScale * (hips[1] - hipsLocalRest[1]), bobScale * (hips[2] - hipsLocalRest[2])]);
|
|
207
|
+
outPelvis[f * 3] = canonPelvisRestLocalTrans[0] + bob[0];
|
|
208
|
+
outPelvis[f * 3 + 1] = canonPelvisRestLocalTrans[1] + bob[1];
|
|
209
|
+
outPelvis[f * 3 + 2] = canonPelvisRestLocalTrans[2] + bob[2];
|
|
210
|
+
}
|
|
211
|
+
// ---- assemble the clip-only GLB: canonical skeleton nodes + one animation ------------------------
|
|
212
|
+
const doc = new core_1.Document();
|
|
213
|
+
const buffer = doc.createBuffer();
|
|
214
|
+
const scene = doc.createScene('clip');
|
|
215
|
+
const created = new Map();
|
|
216
|
+
for (const def of canon.nodes)
|
|
217
|
+
created.set(def.name, doc.createNode(def.name).setMatrix(def.local));
|
|
218
|
+
for (const def of canon.nodes)
|
|
219
|
+
if (def.parent !== null)
|
|
220
|
+
created.get(def.parent).addChild(created.get(def.name));
|
|
221
|
+
scene.addChild(created.get(canon.skeletonRootName));
|
|
222
|
+
const clip = doc.createAnimation(clipName);
|
|
223
|
+
const input = doc.createAccessor().setType('SCALAR').setArray(new Float32Array(outTimes)).setBuffer(buffer);
|
|
224
|
+
for (const c of animatedBones) {
|
|
225
|
+
const output = doc.createAccessor().setType('VEC4').setArray(outRot.get(c)).setBuffer(buffer);
|
|
226
|
+
const sampler = doc.createAnimationSampler().setInput(input).setOutput(output).setInterpolation('LINEAR');
|
|
227
|
+
clip.addSampler(sampler);
|
|
228
|
+
clip.addChannel(doc.createAnimationChannel().setTargetNode(created.get(c)).setTargetPath('rotation').setSampler(sampler));
|
|
229
|
+
}
|
|
230
|
+
const pelvisOut = doc.createAccessor().setType('VEC3').setArray(outPelvis).setBuffer(buffer);
|
|
231
|
+
const pelvisSampler = doc.createAnimationSampler().setInput(input).setOutput(pelvisOut).setInterpolation('LINEAR');
|
|
232
|
+
clip.addSampler(pelvisSampler);
|
|
233
|
+
clip.addChannel(doc.createAnimationChannel().setTargetNode(created.get('pelvis')).setTargetPath('translation').setSampler(pelvisSampler));
|
|
234
|
+
const duration = outTimes[frames - 1] - outTimes[0];
|
|
235
|
+
return { document: doc, meta: { name: clipName, duration: Math.round(duration * 1000) / 1000, loop: true, strideSpeed: null, additive: false }, frames, animatedBones };
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=retargetClip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retargetClip.js","sourceRoot":"","sources":["../../../src/character/stages/retargetClip.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Document } from '@gltf-transform/core';
|
|
2
|
+
export interface SimplifyResult {
|
|
3
|
+
before: number;
|
|
4
|
+
after: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Attribute-preserving simplification to a triangle target via meshopt. `lockBorder` additionally
|
|
8
|
+
* locks open-border vertices (architecture §6: never re-UV — the platform's identity-as-textures
|
|
9
|
+
* depends on the original layout), so it stays TRUE for first-party bodies; importers with real open
|
|
10
|
+
* borders (hair cards, garments) may pass false. Note meshopt ALWAYS preserves attribute-seam
|
|
11
|
+
* vertices regardless of these knobs — a micro-chart baked atlas (Meshy) therefore floors at its
|
|
12
|
+
* chart-border ring (~34% on the reference warrior) whatever the error budget; that case needs
|
|
13
|
+
* weldedSimplify, not bigger `errorCap` (0.001 first-party; ~0.02 for imports).
|
|
14
|
+
*/
|
|
15
|
+
export declare function simplifyToTarget(document: Document, targetTris: number, errorCap?: number, lockBorder?: boolean): Promise<SimplifyResult>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// PORTED from helix-web-engine-new/tools/character-pipeline — the source of truth for the character
|
|
3
|
+
// pipeline. SYNC OBLIGATION: fix bugs there first, then mirror the change here; do not diverge.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.simplifyToTarget = simplifyToTarget;
|
|
6
|
+
const functions_1 = require("@gltf-transform/functions");
|
|
7
|
+
const meshoptimizer_1 = require("meshoptimizer");
|
|
8
|
+
const report_1 = require("./report");
|
|
9
|
+
/**
|
|
10
|
+
* Attribute-preserving simplification to a triangle target via meshopt. `lockBorder` additionally
|
|
11
|
+
* locks open-border vertices (architecture §6: never re-UV — the platform's identity-as-textures
|
|
12
|
+
* depends on the original layout), so it stays TRUE for first-party bodies; importers with real open
|
|
13
|
+
* borders (hair cards, garments) may pass false. Note meshopt ALWAYS preserves attribute-seam
|
|
14
|
+
* vertices regardless of these knobs — a micro-chart baked atlas (Meshy) therefore floors at its
|
|
15
|
+
* chart-border ring (~34% on the reference warrior) whatever the error budget; that case needs
|
|
16
|
+
* weldedSimplify, not bigger `errorCap` (0.001 first-party; ~0.02 for imports).
|
|
17
|
+
*/
|
|
18
|
+
async function simplifyToTarget(document, targetTris, errorCap = 0.001, lockBorder = true) {
|
|
19
|
+
const before = (0, report_1.countTriangles)(document);
|
|
20
|
+
if (before <= targetTris)
|
|
21
|
+
return { before, after: before };
|
|
22
|
+
await meshoptimizer_1.MeshoptSimplifier.ready;
|
|
23
|
+
await document.transform((0, functions_1.weld)());
|
|
24
|
+
// meshopt treats ratio as a hint (lockBorder / the error budget raise the floor), so iterate until
|
|
25
|
+
// at/under target or the count stops moving — the floor is then the honest result. Seam-locked
|
|
26
|
+
// imports (micro-chart atlases) floor by attempt 2 no matter what; weldedSimplify is the tool there.
|
|
27
|
+
let current = before;
|
|
28
|
+
for (let attempt = 0; attempt < 4 && current > targetTris; attempt++) {
|
|
29
|
+
await document.transform((0, functions_1.simplify)({ simplifier: meshoptimizer_1.MeshoptSimplifier, ratio: targetTris / current, error: errorCap, lockBorder }));
|
|
30
|
+
const after = (0, report_1.countTriangles)(document);
|
|
31
|
+
if (after >= current)
|
|
32
|
+
break;
|
|
33
|
+
current = after;
|
|
34
|
+
}
|
|
35
|
+
return { before, after: current };
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=simplifyMesh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"simplifyMesh.js","sourceRoot":"","sources":["../../../src/character/stages/simplifyMesh.ts"],"names":[],"mappings":";AAAA,oGAAoG;AACpG,gGAAgG;;AAqBhG,4CAiBC;AAnCD,yDAA2D;AAC3D,iDAAkD;AAClD,qCAA0C;AAO1C;;;;;;;;GAQG;AACI,KAAK,UAAU,gBAAgB,CAAC,QAAkB,EAAE,UAAkB,EAAE,QAAQ,GAAG,KAAK,EAAE,UAAU,GAAG,IAAI;IAChH,MAAM,MAAM,GAAG,IAAA,uBAAc,EAAC,QAAQ,CAAC,CAAC;IACxC,IAAI,MAAM,IAAI,UAAU;QAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAE3D,MAAM,iCAAiB,CAAC,KAAK,CAAC;IAC9B,MAAM,QAAQ,CAAC,SAAS,CAAC,IAAA,gBAAI,GAAE,CAAC,CAAC;IACjC,mGAAmG;IACnG,+FAA+F;IAC/F,qGAAqG;IACrG,IAAI,OAAO,GAAG,MAAM,CAAC;IACrB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,OAAO,GAAG,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;QACrE,MAAM,QAAQ,CAAC,SAAS,CAAC,IAAA,oBAAQ,EAAC,EAAE,UAAU,EAAE,iCAAiB,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;QAChI,MAAM,KAAK,GAAG,IAAA,uBAAc,EAAC,QAAQ,CAAC,CAAC;QACvC,IAAI,KAAK,IAAI,OAAO;YAAE,MAAM;QAC5B,OAAO,GAAG,KAAK,CAAC;IAClB,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Document } from '@gltf-transform/core';
|
|
2
|
+
export interface SplitFaceResult {
|
|
3
|
+
split: boolean;
|
|
4
|
+
faceTris: number;
|
|
5
|
+
bodyTris: number;
|
|
6
|
+
/** joints in the head subtree that drove the classification */
|
|
7
|
+
headJoints: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Slices the head geometry out of a single-mesh character into its own 'FaceMesh' node, so the
|
|
11
|
+
* runtime's first-person mode can hide it (CameraRig `fpHideMeshes` matches mesh names containing
|
|
12
|
+
* 'FaceMesh'; the stock character ships its face as a separate part — imports are one welded mesh).
|
|
13
|
+
*
|
|
14
|
+
* The split is WEIGHT-driven and therefore rig-agnostic: after conform the skeleton is canonical,
|
|
15
|
+
* so a triangle belongs to the face part when its mean skin weight into the NECK-AND-UP subtree
|
|
16
|
+
* (neck_01 -> neck_02 -> head -> facial root; falls back to 'head' when no neck joint exists)
|
|
17
|
+
* exceeds 0.5. Rooting at the neck matters on auto-rigged imports: their face skin blends head WITH
|
|
18
|
+
* neck (head alone rarely crosses 0.5 outside the skull), and the stock face part includes a neck
|
|
19
|
+
* skirt too (see CameraRig's skull-measure). Scalp-anchored hair/hats ride along; LONG draped hair
|
|
20
|
+
* that auto-rigs weight to the clavicles stays on the body — in first person it behaves like
|
|
21
|
+
* shoulder-wear, swaying at the screen edges the way the visible shoulders do. Only the index
|
|
22
|
+
* buffer splits — the face primitive SHARES the body's attribute accessors, so the file grows by
|
|
23
|
+
* one index accessor and one node per scene. The MATERIAL however must be a distinct instance
|
|
24
|
+
* (textures still shared): the runtime hides the face by mutating material.colorWrite, and
|
|
25
|
+
* GLTFLoader gives prims that reference one glTF material the same THREE.Material — a shared
|
|
26
|
+
* material would hide the body. For that same reason this stage MUST run AFTER merge-lods: its
|
|
27
|
+
* dedup compares materials by value (names ignored) and would fold the face clone back into the
|
|
28
|
+
* body material. Every scene of the merged document is split (scene i = LOD level i).
|
|
29
|
+
*/
|
|
30
|
+
export declare function splitFacePrimitive(document: Document): SplitFaceResult;
|