/** * Dependency-free GLB (binary glTF 2.0) summariser. * * Parses only the JSON chunk (and minimal image headers from the BIN chunk) to * extract the facts asset verification needs: triangle count, skeleton bone * names, embedded animations, and texture dimensions. Avoids pulling a heavy * glTF runtime into consumers and works the same for avatars and static meshes. * * CANONICAL COPY. Extracted here (`@hypersoniclabs/helix-cli/glb`) from * helix-backend-api, which — like universal-item-validator-worker — now * imports this module instead of carrying a verbatim copy; both copies had * silently carried the same WebP + Draco parsing bugs because nothing compared * them. Fix bugs HERE, bump the version, and update the consumers' lockfiles. */ export interface GlbTextureInfo { width: number; height: number; format: "png" | "jpeg" | "webp" | "ktx2" | "unknown"; byteLength: number; mimeType?: string; } export interface GlbBoundingBox { /** World-space min corner [x, y, z], in glTF units (metres by convention). */ min: [number, number, number]; /** World-space max corner [x, y, z]. */ max: [number, number, number]; /** Extent along each axis = max - min. glTF is Y-up, so [width, height, depth]. */ sizeMeters: [number, number, number]; } export interface GlbSummary { /** Total file size in bytes. */ sizeBytes: number; glbVersion: number; meshCount: number; /** Total triangle count across every TRIANGLES primitive. */ triangleCount: number; skinCount: number; /** * Unique skeleton joint (bone) names, in declaration order. Taken from `skins[].joints`; an * animation-only clip carries no skin, so there it falls back to the animation channels' target * node names (see `summarizeGlb`). */ boneNames: string[]; /** Embedded animation clip names. */ animationNames: string[]; materialCount: number; textures: GlbTextureInfo[]; /** Largest texture edge across all embedded textures. */ maxTextureEdge: number; /** * World-space axis-aligned bounds, derived from each POSITION accessor's * min/max (present even for Draco-compressed meshes) transformed by its * node's world matrix. null when no positioned geometry carries bounds. */ boundingBox: GlbBoundingBox | null; /** * World-space Y-span of skin-joint rest positions across all scenes — the skeleton's rest * height. null when the file has no scene-reachable skin joints. On a healthy converted avatar * this ~= the bounding-box height; a cm-scale rig reads ~100× larger while still binding * pristinely (bone·IBM = I at rest), so this is the only static tell of the exploding-skeleton * pathology. */ skeletonSpanY: number | null; } export declare class GlbParseError extends Error { } /** Parse a `.glb` buffer into a verification summary. Throws GlbParseError on malformed input. */ export declare function summarizeGlb(buf: Buffer): GlbSummary;