/** * GltfMeshExtractor — native (Three.js-free) .glb → SkinnedMeshData extractor. * * The opt-in photoreal body source: parses a binary glTF (.glb), pulls the first skinned * primitive's POSITION/NORMAL/JOINTS_0/WEIGHTS_0/indices (+ TANGENT or a placeholder, + * TEXCOORD_0 UVs and the bound material's PBR texture maps as encoded bytes when present), and * remaps the rig's joints onto the canonical HoloScript palette via SkeletonStandardRegistry * so it feeds the SAME CharacterHost/renderCharacter path as the procedural body. The WGSL * shader is single-influence, so the 4-bone glTF skin is reduced to the dominant joint * (argmax weight, weight forced to 1.0) — matching the rigid procedural body's contract. * * Pure data, zero GPU, zero Three.js → headless CI-testable. The procedural AgentAvatarMesh * stays the always-works default; this is the fidelity upgrade when a real GLB is supplied. * * Scope (documented limits): first skinned primitive only; no Draco/meshopt decode (fail * loud); no external/uri buffers; no sparse accessors. A full multi-primitive importer is a * later upgrade. * * @module character-render */ import type { SkinnedMeshData, MaterialTextureMaps } from '../native-render/draw-spec'; import { type SkeletonStandardId } from '../character/SkeletonStandardRegistry'; interface GltfJson { meshes?: Array<{ primitives: GltfPrimitive[]; }>; accessors?: GltfAccessor[]; bufferViews?: Array<{ buffer: number; byteOffset?: number; byteLength: number; byteStride?: number; }>; buffers?: Array<{ uri?: string; byteLength: number; }>; nodes?: Array<{ name?: string; mesh?: number; skin?: number; }>; skins?: Array<{ joints: number[]; inverseBindMatrices?: number; }>; materials?: GltfMaterial[]; textures?: Array<{ source?: number; }>; images?: Array<{ bufferView?: number; mimeType?: string; uri?: string; }>; } interface GltfTextureInfo { index: number; } interface GltfMaterial { pbrMetallicRoughness?: { baseColorTexture?: GltfTextureInfo; metallicRoughnessTexture?: GltfTextureInfo; }; normalTexture?: GltfTextureInfo; emissiveTexture?: GltfTextureInfo; } interface GltfPrimitive { attributes: Record; indices?: number; mode?: number; material?: number; extensions?: Record; } interface GltfAccessor { bufferView: number; byteOffset?: number; componentType: number; count: number; type: string; normalized?: boolean; sparse?: unknown; } export interface GltfSkinnedMesh extends SkinnedMeshData { jointCount: number; boneOrder: readonly string[]; /** Inverse-bind matrices re-indexed into palette order (JOINT_COUNT × 16), identity for unmapped. */ inverseBindMatrices: Float32Array; skeletonStandard: SkeletonStandardId; unmappedJoints: string[]; /** The bound material's PBR texture maps (encoded image bytes), when the primitive has a * material with texture maps. Undefined for an un-textured / material-less primitive. */ materialMaps?: MaterialTextureMaps; } /** Parse a .glb container into its JSON + BIN chunks. */ export declare function parseGlb(glb: ArrayBuffer): { json: GltfJson; bin: Uint8Array; }; /** * Extract the first skinned primitive of a .glb into a palette-remapped SkinnedMeshData. * When `onlyMeshIndex` is given, the search is restricted to that one glTF mesh — * the per-mesh entry point for the importer's multi-mesh carry. */ export declare function extractGltfSkinnedMesh(glb: ArrayBuffer, onlyMeshIndex?: number): GltfSkinnedMesh; /** A non-skinned mesh extracted with a rigid identity bind. Structurally a * {@link GltfSkinnedMesh} so it flows through the same SkinnedMeshData / `.holo` * mesh-shape pipeline — its "skin" is the no-deformation identity (joint 0, * weight 1, identity inverse-binds). */ export type GltfStaticMesh = GltfSkinnedMesh; /** * Extract a .glb as a STATIC mesh: real POSITION/NORMAL/TANGENT/TEXCOORD_0/indices (+ bound PBR * maps) carried with a rigid identity bind — every vertex rides palette root (joint 0) at weight * 1.0 with identity inverse-binds, i.e. undeformed at rest. This lets a NON-skinned GLB (the * majority of environment/object assets) carry its real surface through the exact same codec + * `.holo` `shape "" mesh { … }` block as a skinned one, instead of degrading to an external * text pointer. The rigid root bind is the same form the skinned extractor emits for an unmapped * vertex (jointIndices=0, weight=1). Any JOINTS_0/WEIGHTS_0 on the primitives are ignored — call * {@link extractGltfSkinnedMesh} for those. * * MULTI-PRIMITIVE: ALL triangle primitives of the first mesh that has any are MERGED into one * geometry (multi-primitive props — split by material — are common). Non-triangle primitives are * skipped; the first primitive's PBR maps are carried (per-primitive material groups are a further * layer). Limits: no Draco/meshopt (decode at the CLI import boundary, `glb-decompress.ts`), no * sparse/external buffers, first mesh only. */ export declare function extractGltfStaticMesh(glb: ArrayBuffer, onlyMeshIndex?: number): GltfStaticMesh; /** * Carry EVERY mesh of a .glb in a SINGLE container parse. For each glTF mesh, * prefers the skinned core (palette-remapped skin) and falls back to the static * core (rigid identity bind); a mesh neither can read (Draco/meshopt that wasn't * decompressed, or no triangle POSITION primitive) is skipped, not thrown. Returns * one entry per carryable mesh, keyed by its glTF mesh index — the importer's * multi-mesh entry point (one parse, vs. re-parsing the container per mesh). */ export declare function extractGltfMeshes(glb: ArrayBuffer): Array<{ meshIndex: number; mesh: GltfSkinnedMesh; }>; export {}; //# sourceMappingURL=GltfMeshExtractor.d.ts.map