import { Document } from '@gltf-transform/core'; export interface SplitFaceResult { split: boolean; faceTris: number; bodyTris: number; /** joints in the head subtree that drove the classification */ headJoints: number; } /** * Slices the head geometry out of a single-mesh character into its own 'FaceMesh' node, so the * runtime's first-person mode can hide it (CameraRig `fpHideMeshes` matches mesh names containing * 'FaceMesh'; the stock character ships its face as a separate part — imports are one welded mesh). * * The split is WEIGHT-driven and therefore rig-agnostic: after conform the skeleton is canonical, * so a triangle belongs to the face part when its mean skin weight into the NECK-AND-UP subtree * (neck_01 -> neck_02 -> head -> facial root; falls back to 'head' when no neck joint exists) * exceeds 0.5. Rooting at the neck matters on auto-rigged imports: their face skin blends head WITH * neck (head alone rarely crosses 0.5 outside the skull), and the stock face part includes a neck * skirt too (see CameraRig's skull-measure). Scalp-anchored hair/hats ride along; LONG draped hair * that auto-rigs weight to the clavicles stays on the body — in first person it behaves like * shoulder-wear, swaying at the screen edges the way the visible shoulders do. Only the index * buffer splits — the face primitive SHARES the body's attribute accessors, so the file grows by * one index accessor and one node per scene. The MATERIAL however must be a distinct instance * (textures still shared): the runtime hides the face by mutating material.colorWrite, and * GLTFLoader gives prims that reference one glTF material the same THREE.Material — a shared * material would hide the body. For that same reason this stage MUST run AFTER merge-lods: its * dedup compares materials by value (names ignored) and would fold the face clone back into the * body material. Every scene of the merged document is split (scene i = LOD level i). */ export declare function splitFacePrimitive(document: Document): SplitFaceResult;