@plasius/gpu-model-renderer 0.2.0 → 0.4.0

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 CHANGED
@@ -61,6 +61,52 @@ objects and structured clones fail closed; verify clones through
61
61
  versions/module instances consistent across adapters and bridge consumers.
62
62
  The deprecated `packageBootstrap` identity export remains compatible.
63
63
 
64
+ This bridge requires the published `@plasius/gpu-model-core` **0.6.0** contract.
65
+ Source adapters and the bridge must resolve the same core module instance and
66
+ module format. An equal version in a separate install, worker, or ESM/CJS module
67
+ instance does not share the private verification brand. Inspect
68
+ `npm ls @plasius/gpu-model-core` when integrating; explicitly reverify transferred
69
+ clones with the receiving core and its trusted resource-verification port. Do not
70
+ cast a plain object to a verified document or disable the brand check.
71
+
72
+ `nodes` retains optional authored `restLocalTransform` (including quaternion
73
+ sign and signed scales) and each node's complete ordered `morphWeights`.
74
+ For `node-weights` animation, interpret the output in the target node's binding
75
+ order, which can differ between instances of one mesh. For K keys and N bindings,
76
+ step/linear outputs contain K*N scalar values; cubic-spline outputs contain
77
+ K*3*N scalars, ordered per key as N incoming tangents, N values, N outgoing
78
+ tangents. Signed weights and tangents are preserved. The host owns interpolation,
79
+ deformation and time-dependent bounds; the bridge retains core's validated base
80
+ geometry bounds. Legacy single-shape `weights` channels keep their existing
81
+ meaning. New documents must not be handed to an older core instance.
82
+
83
+ Published renderer **0.2.53** still accepts the mesh/material projection without
84
+ a dependency upgrade: that projection does not evaluate node morph animation.
85
+ Its test-only core0.5.0 dependency can remain nested because it never performs
86
+ the private document-brand check. Source producers and this bridge, which do
87
+ check the brand, must share core0.6.0. No resolution override is required.
88
+
89
+ Preparation retains the additional `thickness`, `specular`, `specularColor`,
90
+ `iridescence`, `iridescenceThickness`, and `anisotropy` bindings, including their
91
+ samplers, selected UV sets, transforms, colour spaces, and verified encoded image
92
+ resources. Extension factors remain bounded canonical metadata. The bridge
93
+ preserves these contracts; the source adapter owns KHR admission, factors,
94
+ defaults, channels, and export, while the renderer owns actual texture sampling.
95
+ Missing references, mismatched slot usage, image identity, samplers, colour
96
+ spaces, or absent UV sets must fail core verification before preparation.
97
+
98
+ Use published renderer **0.2.53** or a compatible later release for the six
99
+ additional channel mappings. Its canonical mapper accepts UV0 and identity
100
+ transforms, and explicitly rejects unsupported UV sets or nonidentity transforms.
101
+ The generic bridge retains the authored data so the host can choose an appropriate
102
+ consumer; it never removes a transform or substitutes UV0 to evade a rejection.
103
+ The owning renderer contract is tracked by
104
+ [gpu-renderer #252](https://github.com/Plasius-LTD/gpu-renderer/issues/252).
105
+ Retaining fields in prepared output does not establish physical rendering fidelity or
106
+ [glTF #14](https://github.com/Plasius-LTD/gpu-model-gltf/issues/14) completion.
107
+ Keep unsupported assets on the existing consumer path until the released
108
+ renderer passes the real source-adapter compatibility fixtures.
109
+
64
110
  Optional `diagnostics` uses the core `GpuModelDiagnosticInput` contract. Core
65
111
  owns strict/tolerant/forensic evaluation and bounded repair/loss/evidence
66
112
  ledgers. Strict is the default. Tolerant reports describe repairs already
@@ -92,7 +138,8 @@ Mutations never affect the canonical document or a subsequent preparation.
92
138
  decision as `conversionEnabled` on every preparation. Omission/false rejects.
93
139
  - Capability: none for this data-only package layer.
94
140
  - Rollback: disable the remote flag, cancel outstanding preparation via its
95
- signal, and retain the previous consumer loader and validated package pin.
141
+ signal, and retain the previous consumer loader and compatible producer/core/
142
+ bridge package pins together. Do not feed new node-weight documents to core0.5.
96
143
  No local environment value is used as the normal control plane.
97
144
 
98
145
  ## Development and release
@@ -118,11 +165,15 @@ prove the full format conformance matrix in site Story #1160: the current
118
165
  published GLB canonical importer supports a narrower texture-free static
119
166
  profile. Actual format-adapter support must be verified separately.
120
167
 
121
- Dependencies added for this API: runtime core `0.4.6` (canonical validation and
122
- diagnostics); test-only runtime `0.2.4` and renderer `0.2.51` (published consumer
168
+ Dependencies used by this API: runtime core `0.6.0` (canonical validation and
169
+ diagnostics); test-only runtime `0.2.4` and renderer `0.2.53` (published consumer
123
170
  contract checks). No local package references or format SDKs are introduced.
124
171
  See [ADR 0006](docs/adrs/adr-0006-canonical-renderer-preparation.md) and the
125
172
  [design and NFR evidence](docs/design/canonical-renderer-preparation.md).
173
+ The core adoption and module-instance constraints are specified by
174
+ [ADR 0007](docs/adrs/adr-0007-extended-material-core-alignment.md).
175
+ Node morph animation adoption and its limits are specified by
176
+ [ADR 0008](docs/adrs/adr-0008-node-morph-animation-core-alignment.md).
126
177
 
127
178
  Apache-2.0. See LICENSE, SECURITY.md, and legal/.
128
179
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/renderer-input.ts"],"sourcesContent":["/** Package identity and rollout metadata for the initial repository baseline. */\nexport const packageName = \"@plasius/gpu-model-renderer\" as const;\n\n/** @deprecated Retained for compatibility with the bootstrap package identity. */\nexport const packageBootstrap = Object.freeze({\n packageName,\n featureFlag: \"gpu.model.conversion.enabled\",\n status: \"bootstrap\",\n} as const);\n\nexport * from \"./renderer-input.js\";\n","import {\n evaluateGpuModelDiagnostics, isGpuModelDocument,\n type GpuModelAccessor, type GpuModelDiagnosticInput, type GpuModelDiagnosticReport,\n type GpuModelDocument, type GpuModelResource, type GpuModelTexture,\n} from \"@plasius/gpu-model-core\";\n\n/** Fixed allocation ceiling; callers may tighten it, never raise it. */\nexport const GPU_MODEL_RENDERER_MAX_DECODED_BYTES = 64 * 1024 * 1024;\n/** Remotely evaluated parent Feature flag. No environment override is read here. */\nexport const GPU_MODEL_RENDERER_FEATURE_FLAG = \"gpu.model.conversion.enabled\";\n\n/** Stable, payload-free failure categories for callers and worker boundaries. */\nexport type GpuModelRendererErrorCode = \"feature-disabled\" | \"unverified-document\" | \"invalid-options\" | \"budget-exceeded\" | \"aborted\" | \"diagnostics-rejected\" | \"resource-read-failed\";\n/** Bounded errors deliberately omit source paths, URIs and payload content. */\nexport class GpuModelRendererError extends Error {\n public constructor(public readonly code: GpuModelRendererErrorCode) {\n super(`GPU model renderer preparation failed: ${code}`);\n this.name = \"GpuModelRendererError\";\n }\n}\n\n/** Consumer-owned buffers; changing them never changes the canonical document. */\nexport type GpuModelRendererBuffer = Float32Array | Uint32Array;\n/** Decode policy. Gate decisions must come from the host's remote flag evaluator. */\nexport interface GpuModelRendererOptions {\n readonly conversionEnabled: boolean;\n readonly signal?: AbortSignal;\n readonly timeoutMs?: number;\n readonly maxDecodedBytes?: number;\n readonly diagnostics?: GpuModelDiagnosticInput;\n}\n/** Geometry in mesh-local coordinates, directly usable by the canonical renderer adapter. */\nexport interface GpuModelRendererGeometry {\n readonly primitiveId: string;\n readonly positions: Float32Array;\n readonly indices?: Uint32Array;\n readonly normals?: Float32Array;\n readonly texcoords?: Float32Array;\n}\n/** All attributes are retained, including UV sets, colours, joints and custom semantics. */\nexport interface GpuModelRendererPrimitive {\n readonly meshId: string;\n readonly primitiveId: string;\n readonly topology: GpuModelDocument[\"meshes\"][number][\"primitives\"][number][\"topology\"];\n /** Zero-based canonical material index; null denotes the renderer's default material. */\n readonly materialSlot: number | null;\n readonly attributes: Readonly<Record<string, GpuModelRendererBuffer>>;\n}\n/** Ordered joint palette and packed vertex influences; no animation is evaluated or baked. */\nexport interface GpuModelRendererSkin {\n readonly id: string;\n readonly skeletonId: string;\n readonly jointIds: readonly string[];\n readonly inverseBindMatrices: Float32Array;\n readonly weightSets: readonly {\n readonly primitiveId: string;\n readonly influencesPerVertex: number;\n readonly joints: Uint32Array;\n readonly weights: Float32Array;\n }[];\n}\n/** Canonical clip semantics with decoded, tightly packed sampler buffers. */\nexport interface GpuModelRendererAnimation {\n readonly id: string;\n readonly durationSeconds: number;\n readonly channels: GpuModelDocument[\"animations\"][number][\"channels\"];\n readonly samplers: readonly {\n readonly id: string;\n readonly interpolation: GpuModelDocument[\"animations\"][number][\"samplers\"][number][\"interpolation\"];\n readonly input: GpuModelRendererBuffer;\n readonly output: GpuModelRendererBuffer;\n }[];\n}\n/** Verified images remain encoded; the caller chooses its decoder/upload strategy. */\nexport interface GpuModelRendererTexture {\n readonly texture: GpuModelTexture;\n readonly resource: GpuModelResource;\n}\n/** Scene-aware preparation result. Geometry alone is not a baked world-space scene. */\nexport interface GpuModelRendererInput {\n readonly document: GpuModelDocument;\n readonly bounds: GpuModelDocument[\"bounds\"];\n readonly roots: GpuModelDocument[\"roots\"];\n readonly nodes: GpuModelDocument[\"nodes\"];\n readonly materials: GpuModelDocument[\"materials\"];\n readonly skeletons: GpuModelDocument[\"skeletons\"];\n readonly joints: GpuModelDocument[\"joints\"];\n readonly blendShapes: GpuModelDocument[\"blendShapes\"];\n readonly analyticGeometry: GpuModelDocument[\"analyticGeometry\"];\n readonly diagnostics: GpuModelDocument[\"diagnostics\"];\n readonly diagnosticReport?: GpuModelDiagnosticReport;\n readonly accessors: Readonly<Record<string, GpuModelRendererBuffer>>;\n readonly geometry: readonly GpuModelRendererGeometry[];\n readonly primitives: readonly GpuModelRendererPrimitive[];\n readonly textures: readonly GpuModelRendererTexture[];\n readonly skins: readonly GpuModelRendererSkin[];\n readonly animations: readonly GpuModelRendererAnimation[];\n}\n\nconst COMPONENT_BYTES = { i8: 1, u8: 1, i16: 2, u16: 2, u32: 4, f32: 4 } as const;\nconst COMPONENTS = { scalar: 1, vec2: 2, vec3: 3, vec4: 4, mat2: 4, mat3: 9, mat4: 16 } as const;\n\nconst readBlob = Blob.prototype.arrayBuffer;\n\nfunction decode(accessor: GpuModelAccessor, view: DataView, check: () => void): GpuModelRendererBuffer {\n const size = COMPONENT_BYTES[accessor.componentType];\n const width = COMPONENTS[accessor.elementType];\n const stride = accessor.byteStride ?? size * width;\n const output = [\"u8\", \"u16\", \"u32\"].includes(accessor.componentType) && !accessor.normalized\n ? new Uint32Array(accessor.count * width)\n : new Float32Array(accessor.count * width);\n for (let index = 0; index < output.length; index += 1) {\n if (index % 4096 === 0) check();\n const offset = accessor.byteOffset + Math.floor(index / width) * stride + (index % width) * size;\n let value: number;\n switch (accessor.componentType) {\n case \"i8\": value = view.getInt8(offset); break;\n case \"u8\": value = view.getUint8(offset); break;\n case \"i16\": value = view.getInt16(offset, true); break;\n case \"u16\": value = view.getUint16(offset, true); break;\n case \"u32\": value = view.getUint32(offset, true); break;\n case \"f32\": value = view.getFloat32(offset, true); break;\n }\n if (accessor.normalized) {\n const divisor = { i8: 127, u8: 255, i16: 32767, u16: 65535, u32: 4294967295, f32: 1 }[accessor.componentType];\n value = Math.max(-1, value / divisor);\n }\n output[index] = value;\n }\n return output;\n}\n\n/**\n * Prepares privately verified core output without parsing source formats, fetching\n * resources or importing a renderer. Structured clones must be reverified by core.\n * Run on a host worker for large inputs. Operations are sequential, bounded and\n * have no retries. The host owns scene transforms and animation evaluation.\n */\nexport async function prepareGpuModelRendererInput(\n value: unknown,\n options?: GpuModelRendererOptions,\n): Promise<GpuModelRendererInput> {\n if (options?.conversionEnabled !== true) throw new GpuModelRendererError(\"feature-disabled\");\n if (!isGpuModelDocument(value)) throw new GpuModelRendererError(\"unverified-document\");\n const document = value;\n const maxBytes = options.maxDecodedBytes ?? GPU_MODEL_RENDERER_MAX_DECODED_BYTES;\n const timeoutMs = options.timeoutMs ?? 30_000;\n if (!Number.isSafeInteger(maxBytes) || maxBytes < 1 || maxBytes > GPU_MODEL_RENDERER_MAX_DECODED_BYTES\n || !Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 30_000) {\n throw new GpuModelRendererError(\"invalid-options\");\n }\n const diagnosticReport = options.diagnostics === undefined ? undefined : evaluateGpuModelDiagnostics(options.diagnostics);\n if (diagnosticReport?.accepted === false || document.diagnostics.some(({ severity }) => severity === \"error\")) {\n throw new GpuModelRendererError(\"diagnostics-rejected\");\n }\n let allocation = 0;\n for (const accessor of document.accessors) allocation += accessor.count * COMPONENTS[accessor.elementType] * 4;\n for (const skin of document.skins) {\n allocation += skin.inverseBindMatrices.length * 16 * 4;\n for (const weights of skin.weightSets) allocation += weights.joints.length * weights.influencesPerVertex * 8;\n }\n // Include copies of encoded buffers in the peak budget; image bytes are not decoded here.\n for (const resource of document.resources) if (resource.kind === \"buffer\") allocation += resource.byteLength;\n if (allocation > maxBytes) throw new GpuModelRendererError(\"budget-exceeded\");\n const deadline = performance.now() + timeoutMs;\n const check = () => {\n if (options.signal?.aborted || performance.now() >= deadline) throw new GpuModelRendererError(\"aborted\");\n };\n check();\n const accessors: Record<string, GpuModelRendererBuffer> = Object.create(null);\n // One bounded read at a time. Abort/deadline rejects even if a host read stalls.\n const accessorsByResource = new Map<string, GpuModelAccessor[]>();\n for (const accessor of document.accessors) {\n const entries = accessorsByResource.get(accessor.resourceId) ?? [];\n entries.push(accessor);\n accessorsByResource.set(accessor.resourceId, entries);\n }\n for (const resource of document.resources) {\n const entries = accessorsByResource.get(resource.id);\n if (!entries) continue;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let abort: (() => void) | undefined;\n try {\n const buffer = await new Promise<ArrayBuffer>((resolve, reject) => {\n abort = () => reject(new GpuModelRendererError(\"aborted\"));\n options.signal?.addEventListener(\"abort\", abort, { once: true });\n timer = setTimeout(abort, Math.max(1, deadline - performance.now()));\n readBlob.call(resource.payload).then(resolve, () => reject(new GpuModelRendererError(\"resource-read-failed\")));\n });\n check();\n const view = new DataView(buffer);\n for (const accessor of entries) {\n check();\n accessors[accessor.id] = decode(accessor, view, check);\n }\n } finally {\n clearTimeout(timer);\n if (abort) options.signal?.removeEventListener(\"abort\", abort);\n }\n }\n const primitives: GpuModelRendererPrimitive[] = [];\n const geometry: GpuModelRendererGeometry[] = [];\n const materialSlots = new Map(document.materials.map((material, index) => [material.id, index]));\n for (const mesh of document.meshes) for (const primitive of mesh.primitives) {\n check();\n const attributes: Record<string, GpuModelRendererBuffer> = Object.create(null);\n for (const attribute of primitive.attributes) attributes[attribute.semantic] = accessors[attribute.accessorId]!;\n primitives.push(Object.freeze({ meshId: mesh.id, primitiveId: primitive.id, topology: primitive.topology,\n materialSlot: primitive.materialId === undefined ? null : materialSlots.get(primitive.materialId)!, attributes: Object.freeze(attributes) }));\n geometry.push(Object.freeze({ primitiveId: primitive.id, positions: attributes.POSITION! as Float32Array,\n ...(primitive.indicesAccessorId === undefined ? {} : { indices: accessors[primitive.indicesAccessorId]! as Uint32Array }),\n ...(attributes.NORMAL === undefined ? {} : { normals: attributes.NORMAL as Float32Array }),\n ...(attributes.TEXCOORD_0 === undefined ? {} : { texcoords: attributes.TEXCOORD_0 as Float32Array }) }));\n }\n const pack = <T extends Float32Array | Uint32Array>(rows: readonly (readonly number[])[], output: T): T => {\n let offset = 0;\n for (const row of rows) { check(); output.set(row, offset); offset += row.length; }\n return output;\n };\n const skins = document.skins.map(skin => {\n check();\n return Object.freeze({ id: skin.id, skeletonId: skin.skeletonId, jointIds: skin.jointIds,\n inverseBindMatrices: pack(skin.inverseBindMatrices, new Float32Array(skin.inverseBindMatrices.length * 16)),\n weightSets: Object.freeze(skin.weightSets.map(weights => Object.freeze({ primitiveId: weights.primitiveId,\n influencesPerVertex: weights.influencesPerVertex, joints: pack(weights.joints, new Uint32Array(weights.joints.length * weights.influencesPerVertex)), weights: pack(weights.weights, new Float32Array(weights.weights.length * weights.influencesPerVertex)) }))) });\n });\n const animations = document.animations.map(animation => Object.freeze({ id: animation.id, durationSeconds: animation.durationSeconds,\n channels: animation.channels, samplers: Object.freeze(animation.samplers.map(sampler => Object.freeze({ id: sampler.id,\n interpolation: sampler.interpolation, input: accessors[sampler.inputAccessorId]!, output: accessors[sampler.outputAccessorId]! }))) }));\n const resources = new Map(document.resources.map(resource => [resource.id, resource]));\n check();\n return Object.freeze({ document, bounds: document.bounds, roots: document.roots, nodes: document.nodes, materials: document.materials,\n skeletons: document.skeletons, joints: document.joints, blendShapes: document.blendShapes, analyticGeometry: document.analyticGeometry,\n diagnostics: document.diagnostics, ...(diagnosticReport === undefined ? {} : { diagnosticReport }), accessors: Object.freeze(accessors),\n primitives: Object.freeze(primitives), geometry: Object.freeze(geometry), skins: Object.freeze(skins), animations: Object.freeze(animations),\n textures: Object.freeze(document.textures.map(texture => Object.freeze({ texture, resource: resources.get(texture.imageResourceId)! }))) });\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/renderer-input.ts"],"sourcesContent":["/** Package identity and rollout metadata for the initial repository baseline. */\nexport const packageName = \"@plasius/gpu-model-renderer\" as const;\n\n/** @deprecated Retained for compatibility with the bootstrap package identity. */\nexport const packageBootstrap = Object.freeze({\n packageName,\n featureFlag: \"gpu.model.conversion.enabled\",\n status: \"bootstrap\",\n} as const);\n\nexport * from \"./renderer-input.js\";\n","import {\n evaluateGpuModelDiagnostics, isGpuModelDocument,\n type GpuModelAccessor, type GpuModelDiagnosticInput, type GpuModelDiagnosticReport,\n type GpuModelDocument, type GpuModelResource, type GpuModelTexture,\n} from \"@plasius/gpu-model-core\";\n\n/** Fixed allocation ceiling; callers may tighten it, never raise it. */\nexport const GPU_MODEL_RENDERER_MAX_DECODED_BYTES = 64 * 1024 * 1024;\n/** Remotely evaluated parent Feature flag. No environment override is read here. */\nexport const GPU_MODEL_RENDERER_FEATURE_FLAG = \"gpu.model.conversion.enabled\";\n\n/** Stable, payload-free failure categories for callers and worker boundaries. */\nexport type GpuModelRendererErrorCode = \"feature-disabled\" | \"unverified-document\" | \"invalid-options\" | \"budget-exceeded\" | \"aborted\" | \"diagnostics-rejected\" | \"resource-read-failed\";\n/** Bounded errors deliberately omit source paths, URIs and payload content. */\nexport class GpuModelRendererError extends Error {\n public constructor(public readonly code: GpuModelRendererErrorCode) {\n super(`GPU model renderer preparation failed: ${code}`);\n this.name = \"GpuModelRendererError\";\n }\n}\n\n/** Consumer-owned buffers; changing them never changes the canonical document. */\nexport type GpuModelRendererBuffer = Float32Array | Uint32Array;\n/** Decode policy. Gate decisions must come from the host's remote flag evaluator. */\nexport interface GpuModelRendererOptions {\n readonly conversionEnabled: boolean;\n readonly signal?: AbortSignal;\n readonly timeoutMs?: number;\n readonly maxDecodedBytes?: number;\n readonly diagnostics?: GpuModelDiagnosticInput;\n}\n/** Geometry in mesh-local coordinates, directly usable by the canonical renderer adapter. */\nexport interface GpuModelRendererGeometry {\n readonly primitiveId: string;\n readonly positions: Float32Array;\n readonly indices?: Uint32Array;\n readonly normals?: Float32Array;\n readonly texcoords?: Float32Array;\n}\n/** All attributes are retained, including UV sets, colours, joints and custom semantics. */\nexport interface GpuModelRendererPrimitive {\n readonly meshId: string;\n readonly primitiveId: string;\n readonly topology: GpuModelDocument[\"meshes\"][number][\"primitives\"][number][\"topology\"];\n /** Zero-based canonical material index; null denotes the renderer's default material. */\n readonly materialSlot: number | null;\n readonly attributes: Readonly<Record<string, GpuModelRendererBuffer>>;\n}\n/** Ordered joint palette and packed vertex influences; no animation is evaluated or baked. */\nexport interface GpuModelRendererSkin {\n readonly id: string;\n readonly skeletonId: string;\n readonly jointIds: readonly string[];\n readonly inverseBindMatrices: Float32Array;\n readonly weightSets: readonly {\n readonly primitiveId: string;\n readonly influencesPerVertex: number;\n readonly joints: Uint32Array;\n readonly weights: Float32Array;\n }[];\n}\n/**\n * Canonical clip semantics with decoded, tightly packed sampler buffers.\n * A node-weights channel uses its target node's morphWeights order. Cubic\n * output retains per-key incoming tangents, values, then outgoing tangents;\n * preparation never evaluates, clamps, or normalizes those values.\n */\nexport interface GpuModelRendererAnimation {\n readonly id: string;\n readonly durationSeconds: number;\n readonly channels: GpuModelDocument[\"animations\"][number][\"channels\"];\n readonly samplers: readonly {\n readonly id: string;\n readonly interpolation: GpuModelDocument[\"animations\"][number][\"samplers\"][number][\"interpolation\"];\n readonly input: GpuModelRendererBuffer;\n readonly output: GpuModelRendererBuffer;\n }[];\n}\n/** Verified images remain encoded; the caller chooses its decoder/upload strategy. */\nexport interface GpuModelRendererTexture {\n readonly texture: GpuModelTexture;\n readonly resource: GpuModelResource;\n}\n/** Scene-aware preparation result. Geometry alone is not a baked world-space scene. */\nexport interface GpuModelRendererInput {\n readonly document: GpuModelDocument;\n readonly bounds: GpuModelDocument[\"bounds\"];\n readonly roots: GpuModelDocument[\"roots\"];\n /** Retains authored rest TRS and independent ordered per-node morph weights. */\n readonly nodes: GpuModelDocument[\"nodes\"];\n readonly materials: GpuModelDocument[\"materials\"];\n readonly skeletons: GpuModelDocument[\"skeletons\"];\n readonly joints: GpuModelDocument[\"joints\"];\n readonly blendShapes: GpuModelDocument[\"blendShapes\"];\n readonly analyticGeometry: GpuModelDocument[\"analyticGeometry\"];\n readonly diagnostics: GpuModelDocument[\"diagnostics\"];\n readonly diagnosticReport?: GpuModelDiagnosticReport;\n readonly accessors: Readonly<Record<string, GpuModelRendererBuffer>>;\n readonly geometry: readonly GpuModelRendererGeometry[];\n readonly primitives: readonly GpuModelRendererPrimitive[];\n readonly textures: readonly GpuModelRendererTexture[];\n readonly skins: readonly GpuModelRendererSkin[];\n readonly animations: readonly GpuModelRendererAnimation[];\n}\n\nconst COMPONENT_BYTES = { i8: 1, u8: 1, i16: 2, u16: 2, u32: 4, f32: 4 } as const;\nconst COMPONENTS = { scalar: 1, vec2: 2, vec3: 3, vec4: 4, mat2: 4, mat3: 9, mat4: 16 } as const;\n\nconst readBlob = Blob.prototype.arrayBuffer;\n\nfunction decode(accessor: GpuModelAccessor, view: DataView, check: () => void): GpuModelRendererBuffer {\n const size = COMPONENT_BYTES[accessor.componentType];\n const width = COMPONENTS[accessor.elementType];\n const stride = accessor.byteStride ?? size * width;\n const output = [\"u8\", \"u16\", \"u32\"].includes(accessor.componentType) && !accessor.normalized\n ? new Uint32Array(accessor.count * width)\n : new Float32Array(accessor.count * width);\n for (let index = 0; index < output.length; index += 1) {\n if (index % 4096 === 0) check();\n const offset = accessor.byteOffset + Math.floor(index / width) * stride + (index % width) * size;\n let value: number;\n switch (accessor.componentType) {\n case \"i8\": value = view.getInt8(offset); break;\n case \"u8\": value = view.getUint8(offset); break;\n case \"i16\": value = view.getInt16(offset, true); break;\n case \"u16\": value = view.getUint16(offset, true); break;\n case \"u32\": value = view.getUint32(offset, true); break;\n case \"f32\": value = view.getFloat32(offset, true); break;\n }\n if (accessor.normalized) {\n const divisor = { i8: 127, u8: 255, i16: 32767, u16: 65535, u32: 4294967295, f32: 1 }[accessor.componentType];\n value = Math.max(-1, value / divisor);\n }\n output[index] = value;\n }\n return output;\n}\n\n/**\n * Prepares privately verified core output without parsing source formats, fetching\n * resources or importing a renderer. Structured clones must be reverified by core.\n * Run on a host worker for large inputs. Operations are sequential, bounded and\n * have no retries. The host owns scene transforms and animation evaluation.\n */\nexport async function prepareGpuModelRendererInput(\n value: unknown,\n options?: GpuModelRendererOptions,\n): Promise<GpuModelRendererInput> {\n if (options?.conversionEnabled !== true) throw new GpuModelRendererError(\"feature-disabled\");\n if (!isGpuModelDocument(value)) throw new GpuModelRendererError(\"unverified-document\");\n const document = value;\n const maxBytes = options.maxDecodedBytes ?? GPU_MODEL_RENDERER_MAX_DECODED_BYTES;\n const timeoutMs = options.timeoutMs ?? 30_000;\n if (!Number.isSafeInteger(maxBytes) || maxBytes < 1 || maxBytes > GPU_MODEL_RENDERER_MAX_DECODED_BYTES\n || !Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 30_000) {\n throw new GpuModelRendererError(\"invalid-options\");\n }\n const diagnosticReport = options.diagnostics === undefined ? undefined : evaluateGpuModelDiagnostics(options.diagnostics);\n if (diagnosticReport?.accepted === false || document.diagnostics.some(({ severity }) => severity === \"error\")) {\n throw new GpuModelRendererError(\"diagnostics-rejected\");\n }\n let allocation = 0;\n for (const accessor of document.accessors) allocation += accessor.count * COMPONENTS[accessor.elementType] * 4;\n for (const skin of document.skins) {\n allocation += skin.inverseBindMatrices.length * 16 * 4;\n for (const weights of skin.weightSets) allocation += weights.joints.length * weights.influencesPerVertex * 8;\n }\n // Include copies of encoded buffers in the peak budget; image bytes are not decoded here.\n for (const resource of document.resources) if (resource.kind === \"buffer\") allocation += resource.byteLength;\n if (allocation > maxBytes) throw new GpuModelRendererError(\"budget-exceeded\");\n const deadline = performance.now() + timeoutMs;\n const check = () => {\n if (options.signal?.aborted || performance.now() >= deadline) throw new GpuModelRendererError(\"aborted\");\n };\n check();\n const accessors: Record<string, GpuModelRendererBuffer> = Object.create(null);\n // One bounded read at a time. Abort/deadline rejects even if a host read stalls.\n const accessorsByResource = new Map<string, GpuModelAccessor[]>();\n for (const accessor of document.accessors) {\n const entries = accessorsByResource.get(accessor.resourceId) ?? [];\n entries.push(accessor);\n accessorsByResource.set(accessor.resourceId, entries);\n }\n for (const resource of document.resources) {\n const entries = accessorsByResource.get(resource.id);\n if (!entries) continue;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let abort: (() => void) | undefined;\n try {\n const buffer = await new Promise<ArrayBuffer>((resolve, reject) => {\n abort = () => reject(new GpuModelRendererError(\"aborted\"));\n options.signal?.addEventListener(\"abort\", abort, { once: true });\n timer = setTimeout(abort, Math.max(1, deadline - performance.now()));\n readBlob.call(resource.payload).then(resolve, () => reject(new GpuModelRendererError(\"resource-read-failed\")));\n });\n check();\n const view = new DataView(buffer);\n for (const accessor of entries) {\n check();\n accessors[accessor.id] = decode(accessor, view, check);\n }\n } finally {\n clearTimeout(timer);\n if (abort) options.signal?.removeEventListener(\"abort\", abort);\n }\n }\n const primitives: GpuModelRendererPrimitive[] = [];\n const geometry: GpuModelRendererGeometry[] = [];\n const materialSlots = new Map(document.materials.map((material, index) => [material.id, index]));\n for (const mesh of document.meshes) for (const primitive of mesh.primitives) {\n check();\n const attributes: Record<string, GpuModelRendererBuffer> = Object.create(null);\n for (const attribute of primitive.attributes) attributes[attribute.semantic] = accessors[attribute.accessorId]!;\n primitives.push(Object.freeze({ meshId: mesh.id, primitiveId: primitive.id, topology: primitive.topology,\n materialSlot: primitive.materialId === undefined ? null : materialSlots.get(primitive.materialId)!, attributes: Object.freeze(attributes) }));\n geometry.push(Object.freeze({ primitiveId: primitive.id, positions: attributes.POSITION! as Float32Array,\n ...(primitive.indicesAccessorId === undefined ? {} : { indices: accessors[primitive.indicesAccessorId]! as Uint32Array }),\n ...(attributes.NORMAL === undefined ? {} : { normals: attributes.NORMAL as Float32Array }),\n ...(attributes.TEXCOORD_0 === undefined ? {} : { texcoords: attributes.TEXCOORD_0 as Float32Array }) }));\n }\n const pack = <T extends Float32Array | Uint32Array>(rows: readonly (readonly number[])[], output: T): T => {\n let offset = 0;\n for (const row of rows) { check(); output.set(row, offset); offset += row.length; }\n return output;\n };\n const skins = document.skins.map(skin => {\n check();\n return Object.freeze({ id: skin.id, skeletonId: skin.skeletonId, jointIds: skin.jointIds,\n inverseBindMatrices: pack(skin.inverseBindMatrices, new Float32Array(skin.inverseBindMatrices.length * 16)),\n weightSets: Object.freeze(skin.weightSets.map(weights => Object.freeze({ primitiveId: weights.primitiveId,\n influencesPerVertex: weights.influencesPerVertex, joints: pack(weights.joints, new Uint32Array(weights.joints.length * weights.influencesPerVertex)), weights: pack(weights.weights, new Float32Array(weights.weights.length * weights.influencesPerVertex)) }))) });\n });\n const animations = document.animations.map(animation => Object.freeze({ id: animation.id, durationSeconds: animation.durationSeconds,\n channels: animation.channels, samplers: Object.freeze(animation.samplers.map(sampler => Object.freeze({ id: sampler.id,\n interpolation: sampler.interpolation, input: accessors[sampler.inputAccessorId]!, output: accessors[sampler.outputAccessorId]! }))) }));\n const resources = new Map(document.resources.map(resource => [resource.id, resource]));\n check();\n return Object.freeze({ document, bounds: document.bounds, roots: document.roots, nodes: document.nodes, materials: document.materials,\n skeletons: document.skeletons, joints: document.joints, blendShapes: document.blendShapes, analyticGeometry: document.analyticGeometry,\n diagnostics: document.diagnostics, ...(diagnosticReport === undefined ? {} : { diagnosticReport }), accessors: Object.freeze(accessors),\n primitives: Object.freeze(primitives), geometry: Object.freeze(geometry), skins: Object.freeze(skins), animations: Object.freeze(animations),\n textures: Object.freeze(document.textures.map(texture => Object.freeze({ texture, resource: resources.get(texture.imageResourceId)! }))) });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,4BAIO;AAGA,IAAM,uCAAuC,KAAK,OAAO;AAEzD,IAAM,kCAAkC;AAKxC,IAAM,wBAAN,cAAoC,MAAM;AAAA,EACxC,YAA4B,MAAiC;AAClE,UAAM,0CAA0C,IAAI,EAAE;AADrB;AAEjC,SAAK,OAAO;AAAA,EACd;AAAA,EAHmC;AAIrC;AAsFA,IAAM,kBAAkB,EAAE,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,EAAE;AACvE,IAAM,aAAa,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG;AAEtF,IAAM,WAAW,KAAK,UAAU;AAEhC,SAAS,OAAO,UAA4B,MAAgB,OAA2C;AACrG,QAAM,OAAO,gBAAgB,SAAS,aAAa;AACnD,QAAM,QAAQ,WAAW,SAAS,WAAW;AAC7C,QAAM,SAAS,SAAS,cAAc,OAAO;AAC7C,QAAM,SAAS,CAAC,MAAM,OAAO,KAAK,EAAE,SAAS,SAAS,aAAa,KAAK,CAAC,SAAS,aAC9E,IAAI,YAAY,SAAS,QAAQ,KAAK,IACtC,IAAI,aAAa,SAAS,QAAQ,KAAK;AAC3C,WAAS,QAAQ,GAAG,QAAQ,OAAO,QAAQ,SAAS,GAAG;AACrD,QAAI,QAAQ,SAAS,EAAG,OAAM;AAC9B,UAAM,SAAS,SAAS,aAAa,KAAK,MAAM,QAAQ,KAAK,IAAI,SAAU,QAAQ,QAAS;AAC5F,QAAI;AACJ,YAAQ,SAAS,eAAe;AAAA,MAC9B,KAAK;AAAM,gBAAQ,KAAK,QAAQ,MAAM;AAAG;AAAA,MACzC,KAAK;AAAM,gBAAQ,KAAK,SAAS,MAAM;AAAG;AAAA,MAC1C,KAAK;AAAO,gBAAQ,KAAK,SAAS,QAAQ,IAAI;AAAG;AAAA,MACjD,KAAK;AAAO,gBAAQ,KAAK,UAAU,QAAQ,IAAI;AAAG;AAAA,MAClD,KAAK;AAAO,gBAAQ,KAAK,UAAU,QAAQ,IAAI;AAAG;AAAA,MAClD,KAAK;AAAO,gBAAQ,KAAK,WAAW,QAAQ,IAAI;AAAG;AAAA,IACrD;AACA,QAAI,SAAS,YAAY;AACvB,YAAM,UAAU,EAAE,IAAI,KAAK,IAAI,KAAK,KAAK,OAAO,KAAK,OAAO,KAAK,YAAY,KAAK,EAAE,EAAE,SAAS,aAAa;AAC5G,cAAQ,KAAK,IAAI,IAAI,QAAQ,OAAO;AAAA,IACtC;AACA,WAAO,KAAK,IAAI;AAAA,EAClB;AACA,SAAO;AACT;AAQA,eAAsB,6BACpB,OACA,SACgC;AAChC,MAAI,SAAS,sBAAsB,KAAM,OAAM,IAAI,sBAAsB,kBAAkB;AAC3F,MAAI,KAAC,0CAAmB,KAAK,EAAG,OAAM,IAAI,sBAAsB,qBAAqB;AACrF,QAAM,WAAW;AACjB,QAAM,WAAW,QAAQ,mBAAmB;AAC5C,QAAM,YAAY,QAAQ,aAAa;AACvC,MAAI,CAAC,OAAO,cAAc,QAAQ,KAAK,WAAW,KAAK,WAAW,wCAC7D,CAAC,OAAO,cAAc,SAAS,KAAK,YAAY,KAAK,YAAY,KAAQ;AAC5E,UAAM,IAAI,sBAAsB,iBAAiB;AAAA,EACnD;AACA,QAAM,mBAAmB,QAAQ,gBAAgB,SAAY,aAAY,mDAA4B,QAAQ,WAAW;AACxH,MAAI,kBAAkB,aAAa,SAAS,SAAS,YAAY,KAAK,CAAC,EAAE,SAAS,MAAM,aAAa,OAAO,GAAG;AAC7G,UAAM,IAAI,sBAAsB,sBAAsB;AAAA,EACxD;AACA,MAAI,aAAa;AACjB,aAAW,YAAY,SAAS,UAAW,eAAc,SAAS,QAAQ,WAAW,SAAS,WAAW,IAAI;AAC7G,aAAW,QAAQ,SAAS,OAAO;AACjC,kBAAc,KAAK,oBAAoB,SAAS,KAAK;AACrD,eAAW,WAAW,KAAK,WAAY,eAAc,QAAQ,OAAO,SAAS,QAAQ,sBAAsB;AAAA,EAC7G;AAEA,aAAW,YAAY,SAAS,UAAW,KAAI,SAAS,SAAS,SAAU,eAAc,SAAS;AAClG,MAAI,aAAa,SAAU,OAAM,IAAI,sBAAsB,iBAAiB;AAC5E,QAAM,WAAW,YAAY,IAAI,IAAI;AACrC,QAAM,QAAQ,MAAM;AAClB,QAAI,QAAQ,QAAQ,WAAW,YAAY,IAAI,KAAK,SAAU,OAAM,IAAI,sBAAsB,SAAS;AAAA,EACzG;AACA,QAAM;AACN,QAAM,YAAoD,uBAAO,OAAO,IAAI;AAE5E,QAAM,sBAAsB,oBAAI,IAAgC;AAChE,aAAW,YAAY,SAAS,WAAW;AACzC,UAAM,UAAU,oBAAoB,IAAI,SAAS,UAAU,KAAK,CAAC;AACjE,YAAQ,KAAK,QAAQ;AACrB,wBAAoB,IAAI,SAAS,YAAY,OAAO;AAAA,EACtD;AACA,aAAW,YAAY,SAAS,WAAW;AACzC,UAAM,UAAU,oBAAoB,IAAI,SAAS,EAAE;AACnD,QAAI,CAAC,QAAS;AACd,QAAI;AACJ,QAAI;AACJ,QAAI;AACF,YAAM,SAAS,MAAM,IAAI,QAAqB,CAAC,SAAS,WAAW;AACjE,gBAAQ,MAAM,OAAO,IAAI,sBAAsB,SAAS,CAAC;AACzD,gBAAQ,QAAQ,iBAAiB,SAAS,OAAO,EAAE,MAAM,KAAK,CAAC;AAC/D,gBAAQ,WAAW,OAAO,KAAK,IAAI,GAAG,WAAW,YAAY,IAAI,CAAC,CAAC;AACnE,iBAAS,KAAK,SAAS,OAAO,EAAE,KAAK,SAAS,MAAM,OAAO,IAAI,sBAAsB,sBAAsB,CAAC,CAAC;AAAA,MAC/G,CAAC;AACD,YAAM;AACN,YAAM,OAAO,IAAI,SAAS,MAAM;AAChC,iBAAW,YAAY,SAAS;AAC9B,cAAM;AACN,kBAAU,SAAS,EAAE,IAAI,OAAO,UAAU,MAAM,KAAK;AAAA,MACvD;AAAA,IACF,UAAE;AACA,mBAAa,KAAK;AAClB,UAAI,MAAO,SAAQ,QAAQ,oBAAoB,SAAS,KAAK;AAAA,IAC/D;AAAA,EACF;AACA,QAAM,aAA0C,CAAC;AACjD,QAAM,WAAuC,CAAC;AAC9C,QAAM,gBAAgB,IAAI,IAAI,SAAS,UAAU,IAAI,CAAC,UAAU,UAAU,CAAC,SAAS,IAAI,KAAK,CAAC,CAAC;AAC/F,aAAW,QAAQ,SAAS,OAAQ,YAAW,aAAa,KAAK,YAAY;AAC3E,UAAM;AACN,UAAM,aAAqD,uBAAO,OAAO,IAAI;AAC7E,eAAW,aAAa,UAAU,WAAY,YAAW,UAAU,QAAQ,IAAI,UAAU,UAAU,UAAU;AAC7G,eAAW,KAAK,OAAO,OAAO;AAAA,MAAE,QAAQ,KAAK;AAAA,MAAI,aAAa,UAAU;AAAA,MAAI,UAAU,UAAU;AAAA,MAC9F,cAAc,UAAU,eAAe,SAAY,OAAO,cAAc,IAAI,UAAU,UAAU;AAAA,MAAI,YAAY,OAAO,OAAO,UAAU;AAAA,IAAE,CAAC,CAAC;AAC9I,aAAS,KAAK,OAAO,OAAO;AAAA,MAAE,aAAa,UAAU;AAAA,MAAI,WAAW,WAAW;AAAA,MAC7E,GAAI,UAAU,sBAAsB,SAAY,CAAC,IAAI,EAAE,SAAS,UAAU,UAAU,iBAAiB,EAAkB;AAAA,MACvH,GAAI,WAAW,WAAW,SAAY,CAAC,IAAI,EAAE,SAAS,WAAW,OAAuB;AAAA,MACxF,GAAI,WAAW,eAAe,SAAY,CAAC,IAAI,EAAE,WAAW,WAAW,WAA2B;AAAA,IAAG,CAAC,CAAC;AAAA,EAC3G;AACA,QAAM,OAAO,CAAuC,MAAsC,WAAiB;AACzG,QAAI,SAAS;AACb,eAAW,OAAO,MAAM;AAAE,YAAM;AAAG,aAAO,IAAI,KAAK,MAAM;AAAG,gBAAU,IAAI;AAAA,IAAQ;AAClF,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,SAAS,MAAM,IAAI,UAAQ;AACvC,UAAM;AACN,WAAO,OAAO,OAAO;AAAA,MAAE,IAAI,KAAK;AAAA,MAAI,YAAY,KAAK;AAAA,MAAY,UAAU,KAAK;AAAA,MAC9E,qBAAqB,KAAK,KAAK,qBAAqB,IAAI,aAAa,KAAK,oBAAoB,SAAS,EAAE,CAAC;AAAA,MAC1G,YAAY,OAAO,OAAO,KAAK,WAAW,IAAI,aAAW,OAAO,OAAO;AAAA,QAAE,aAAa,QAAQ;AAAA,QAC5F,qBAAqB,QAAQ;AAAA,QAAqB,QAAQ,KAAK,QAAQ,QAAQ,IAAI,YAAY,QAAQ,OAAO,SAAS,QAAQ,mBAAmB,CAAC;AAAA,QAAG,SAAS,KAAK,QAAQ,SAAS,IAAI,aAAa,QAAQ,QAAQ,SAAS,QAAQ,mBAAmB,CAAC;AAAA,MAAE,CAAC,CAAC,CAAC;AAAA,IAAE,CAAC;AAAA,EACzQ,CAAC;AACD,QAAM,aAAa,SAAS,WAAW,IAAI,eAAa,OAAO,OAAO;AAAA,IAAE,IAAI,UAAU;AAAA,IAAI,iBAAiB,UAAU;AAAA,IACnH,UAAU,UAAU;AAAA,IAAU,UAAU,OAAO,OAAO,UAAU,SAAS,IAAI,aAAW,OAAO,OAAO;AAAA,MAAE,IAAI,QAAQ;AAAA,MAClH,eAAe,QAAQ;AAAA,MAAe,OAAO,UAAU,QAAQ,eAAe;AAAA,MAAI,QAAQ,UAAU,QAAQ,gBAAgB;AAAA,IAAG,CAAC,CAAC,CAAC;AAAA,EAAE,CAAC,CAAC;AAC1I,QAAM,YAAY,IAAI,IAAI,SAAS,UAAU,IAAI,cAAY,CAAC,SAAS,IAAI,QAAQ,CAAC,CAAC;AACrF,QAAM;AACN,SAAO,OAAO,OAAO;AAAA,IAAE;AAAA,IAAU,QAAQ,SAAS;AAAA,IAAQ,OAAO,SAAS;AAAA,IAAO,OAAO,SAAS;AAAA,IAAO,WAAW,SAAS;AAAA,IAC1H,WAAW,SAAS;AAAA,IAAW,QAAQ,SAAS;AAAA,IAAQ,aAAa,SAAS;AAAA,IAAa,kBAAkB,SAAS;AAAA,IACtH,aAAa,SAAS;AAAA,IAAa,GAAI,qBAAqB,SAAY,CAAC,IAAI,EAAE,iBAAiB;AAAA,IAAI,WAAW,OAAO,OAAO,SAAS;AAAA,IACtI,YAAY,OAAO,OAAO,UAAU;AAAA,IAAG,UAAU,OAAO,OAAO,QAAQ;AAAA,IAAG,OAAO,OAAO,OAAO,KAAK;AAAA,IAAG,YAAY,OAAO,OAAO,UAAU;AAAA,IAC3I,UAAU,OAAO,OAAO,SAAS,SAAS,IAAI,aAAW,OAAO,OAAO,EAAE,SAAS,UAAU,UAAU,IAAI,QAAQ,eAAe,EAAG,CAAC,CAAC,CAAC;AAAA,EAAE,CAAC;AAC9I;;;ADjPO,IAAM,cAAc;AAGpB,IAAM,mBAAmB,OAAO,OAAO;AAAA,EAC5C;AAAA,EACA,aAAa;AAAA,EACb,QAAQ;AACV,CAAU;","names":[]}
package/dist/index.d.cts CHANGED
@@ -51,7 +51,12 @@ interface GpuModelRendererSkin {
51
51
  readonly weights: Float32Array;
52
52
  }[];
53
53
  }
54
- /** Canonical clip semantics with decoded, tightly packed sampler buffers. */
54
+ /**
55
+ * Canonical clip semantics with decoded, tightly packed sampler buffers.
56
+ * A node-weights channel uses its target node's morphWeights order. Cubic
57
+ * output retains per-key incoming tangents, values, then outgoing tangents;
58
+ * preparation never evaluates, clamps, or normalizes those values.
59
+ */
55
60
  interface GpuModelRendererAnimation {
56
61
  readonly id: string;
57
62
  readonly durationSeconds: number;
@@ -73,6 +78,7 @@ interface GpuModelRendererInput {
73
78
  readonly document: GpuModelDocument;
74
79
  readonly bounds: GpuModelDocument["bounds"];
75
80
  readonly roots: GpuModelDocument["roots"];
81
+ /** Retains authored rest TRS and independent ordered per-node morph weights. */
76
82
  readonly nodes: GpuModelDocument["nodes"];
77
83
  readonly materials: GpuModelDocument["materials"];
78
84
  readonly skeletons: GpuModelDocument["skeletons"];
package/dist/index.d.ts CHANGED
@@ -51,7 +51,12 @@ interface GpuModelRendererSkin {
51
51
  readonly weights: Float32Array;
52
52
  }[];
53
53
  }
54
- /** Canonical clip semantics with decoded, tightly packed sampler buffers. */
54
+ /**
55
+ * Canonical clip semantics with decoded, tightly packed sampler buffers.
56
+ * A node-weights channel uses its target node's morphWeights order. Cubic
57
+ * output retains per-key incoming tangents, values, then outgoing tangents;
58
+ * preparation never evaluates, clamps, or normalizes those values.
59
+ */
55
60
  interface GpuModelRendererAnimation {
56
61
  readonly id: string;
57
62
  readonly durationSeconds: number;
@@ -73,6 +78,7 @@ interface GpuModelRendererInput {
73
78
  readonly document: GpuModelDocument;
74
79
  readonly bounds: GpuModelDocument["bounds"];
75
80
  readonly roots: GpuModelDocument["roots"];
81
+ /** Retains authored rest TRS and independent ordered per-node morph weights. */
76
82
  readonly nodes: GpuModelDocument["nodes"];
77
83
  readonly materials: GpuModelDocument["materials"];
78
84
  readonly skeletons: GpuModelDocument["skeletons"];
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/renderer-input.ts","../src/index.ts"],"sourcesContent":["import {\n evaluateGpuModelDiagnostics, isGpuModelDocument,\n type GpuModelAccessor, type GpuModelDiagnosticInput, type GpuModelDiagnosticReport,\n type GpuModelDocument, type GpuModelResource, type GpuModelTexture,\n} from \"@plasius/gpu-model-core\";\n\n/** Fixed allocation ceiling; callers may tighten it, never raise it. */\nexport const GPU_MODEL_RENDERER_MAX_DECODED_BYTES = 64 * 1024 * 1024;\n/** Remotely evaluated parent Feature flag. No environment override is read here. */\nexport const GPU_MODEL_RENDERER_FEATURE_FLAG = \"gpu.model.conversion.enabled\";\n\n/** Stable, payload-free failure categories for callers and worker boundaries. */\nexport type GpuModelRendererErrorCode = \"feature-disabled\" | \"unverified-document\" | \"invalid-options\" | \"budget-exceeded\" | \"aborted\" | \"diagnostics-rejected\" | \"resource-read-failed\";\n/** Bounded errors deliberately omit source paths, URIs and payload content. */\nexport class GpuModelRendererError extends Error {\n public constructor(public readonly code: GpuModelRendererErrorCode) {\n super(`GPU model renderer preparation failed: ${code}`);\n this.name = \"GpuModelRendererError\";\n }\n}\n\n/** Consumer-owned buffers; changing them never changes the canonical document. */\nexport type GpuModelRendererBuffer = Float32Array | Uint32Array;\n/** Decode policy. Gate decisions must come from the host's remote flag evaluator. */\nexport interface GpuModelRendererOptions {\n readonly conversionEnabled: boolean;\n readonly signal?: AbortSignal;\n readonly timeoutMs?: number;\n readonly maxDecodedBytes?: number;\n readonly diagnostics?: GpuModelDiagnosticInput;\n}\n/** Geometry in mesh-local coordinates, directly usable by the canonical renderer adapter. */\nexport interface GpuModelRendererGeometry {\n readonly primitiveId: string;\n readonly positions: Float32Array;\n readonly indices?: Uint32Array;\n readonly normals?: Float32Array;\n readonly texcoords?: Float32Array;\n}\n/** All attributes are retained, including UV sets, colours, joints and custom semantics. */\nexport interface GpuModelRendererPrimitive {\n readonly meshId: string;\n readonly primitiveId: string;\n readonly topology: GpuModelDocument[\"meshes\"][number][\"primitives\"][number][\"topology\"];\n /** Zero-based canonical material index; null denotes the renderer's default material. */\n readonly materialSlot: number | null;\n readonly attributes: Readonly<Record<string, GpuModelRendererBuffer>>;\n}\n/** Ordered joint palette and packed vertex influences; no animation is evaluated or baked. */\nexport interface GpuModelRendererSkin {\n readonly id: string;\n readonly skeletonId: string;\n readonly jointIds: readonly string[];\n readonly inverseBindMatrices: Float32Array;\n readonly weightSets: readonly {\n readonly primitiveId: string;\n readonly influencesPerVertex: number;\n readonly joints: Uint32Array;\n readonly weights: Float32Array;\n }[];\n}\n/** Canonical clip semantics with decoded, tightly packed sampler buffers. */\nexport interface GpuModelRendererAnimation {\n readonly id: string;\n readonly durationSeconds: number;\n readonly channels: GpuModelDocument[\"animations\"][number][\"channels\"];\n readonly samplers: readonly {\n readonly id: string;\n readonly interpolation: GpuModelDocument[\"animations\"][number][\"samplers\"][number][\"interpolation\"];\n readonly input: GpuModelRendererBuffer;\n readonly output: GpuModelRendererBuffer;\n }[];\n}\n/** Verified images remain encoded; the caller chooses its decoder/upload strategy. */\nexport interface GpuModelRendererTexture {\n readonly texture: GpuModelTexture;\n readonly resource: GpuModelResource;\n}\n/** Scene-aware preparation result. Geometry alone is not a baked world-space scene. */\nexport interface GpuModelRendererInput {\n readonly document: GpuModelDocument;\n readonly bounds: GpuModelDocument[\"bounds\"];\n readonly roots: GpuModelDocument[\"roots\"];\n readonly nodes: GpuModelDocument[\"nodes\"];\n readonly materials: GpuModelDocument[\"materials\"];\n readonly skeletons: GpuModelDocument[\"skeletons\"];\n readonly joints: GpuModelDocument[\"joints\"];\n readonly blendShapes: GpuModelDocument[\"blendShapes\"];\n readonly analyticGeometry: GpuModelDocument[\"analyticGeometry\"];\n readonly diagnostics: GpuModelDocument[\"diagnostics\"];\n readonly diagnosticReport?: GpuModelDiagnosticReport;\n readonly accessors: Readonly<Record<string, GpuModelRendererBuffer>>;\n readonly geometry: readonly GpuModelRendererGeometry[];\n readonly primitives: readonly GpuModelRendererPrimitive[];\n readonly textures: readonly GpuModelRendererTexture[];\n readonly skins: readonly GpuModelRendererSkin[];\n readonly animations: readonly GpuModelRendererAnimation[];\n}\n\nconst COMPONENT_BYTES = { i8: 1, u8: 1, i16: 2, u16: 2, u32: 4, f32: 4 } as const;\nconst COMPONENTS = { scalar: 1, vec2: 2, vec3: 3, vec4: 4, mat2: 4, mat3: 9, mat4: 16 } as const;\n\nconst readBlob = Blob.prototype.arrayBuffer;\n\nfunction decode(accessor: GpuModelAccessor, view: DataView, check: () => void): GpuModelRendererBuffer {\n const size = COMPONENT_BYTES[accessor.componentType];\n const width = COMPONENTS[accessor.elementType];\n const stride = accessor.byteStride ?? size * width;\n const output = [\"u8\", \"u16\", \"u32\"].includes(accessor.componentType) && !accessor.normalized\n ? new Uint32Array(accessor.count * width)\n : new Float32Array(accessor.count * width);\n for (let index = 0; index < output.length; index += 1) {\n if (index % 4096 === 0) check();\n const offset = accessor.byteOffset + Math.floor(index / width) * stride + (index % width) * size;\n let value: number;\n switch (accessor.componentType) {\n case \"i8\": value = view.getInt8(offset); break;\n case \"u8\": value = view.getUint8(offset); break;\n case \"i16\": value = view.getInt16(offset, true); break;\n case \"u16\": value = view.getUint16(offset, true); break;\n case \"u32\": value = view.getUint32(offset, true); break;\n case \"f32\": value = view.getFloat32(offset, true); break;\n }\n if (accessor.normalized) {\n const divisor = { i8: 127, u8: 255, i16: 32767, u16: 65535, u32: 4294967295, f32: 1 }[accessor.componentType];\n value = Math.max(-1, value / divisor);\n }\n output[index] = value;\n }\n return output;\n}\n\n/**\n * Prepares privately verified core output without parsing source formats, fetching\n * resources or importing a renderer. Structured clones must be reverified by core.\n * Run on a host worker for large inputs. Operations are sequential, bounded and\n * have no retries. The host owns scene transforms and animation evaluation.\n */\nexport async function prepareGpuModelRendererInput(\n value: unknown,\n options?: GpuModelRendererOptions,\n): Promise<GpuModelRendererInput> {\n if (options?.conversionEnabled !== true) throw new GpuModelRendererError(\"feature-disabled\");\n if (!isGpuModelDocument(value)) throw new GpuModelRendererError(\"unverified-document\");\n const document = value;\n const maxBytes = options.maxDecodedBytes ?? GPU_MODEL_RENDERER_MAX_DECODED_BYTES;\n const timeoutMs = options.timeoutMs ?? 30_000;\n if (!Number.isSafeInteger(maxBytes) || maxBytes < 1 || maxBytes > GPU_MODEL_RENDERER_MAX_DECODED_BYTES\n || !Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 30_000) {\n throw new GpuModelRendererError(\"invalid-options\");\n }\n const diagnosticReport = options.diagnostics === undefined ? undefined : evaluateGpuModelDiagnostics(options.diagnostics);\n if (diagnosticReport?.accepted === false || document.diagnostics.some(({ severity }) => severity === \"error\")) {\n throw new GpuModelRendererError(\"diagnostics-rejected\");\n }\n let allocation = 0;\n for (const accessor of document.accessors) allocation += accessor.count * COMPONENTS[accessor.elementType] * 4;\n for (const skin of document.skins) {\n allocation += skin.inverseBindMatrices.length * 16 * 4;\n for (const weights of skin.weightSets) allocation += weights.joints.length * weights.influencesPerVertex * 8;\n }\n // Include copies of encoded buffers in the peak budget; image bytes are not decoded here.\n for (const resource of document.resources) if (resource.kind === \"buffer\") allocation += resource.byteLength;\n if (allocation > maxBytes) throw new GpuModelRendererError(\"budget-exceeded\");\n const deadline = performance.now() + timeoutMs;\n const check = () => {\n if (options.signal?.aborted || performance.now() >= deadline) throw new GpuModelRendererError(\"aborted\");\n };\n check();\n const accessors: Record<string, GpuModelRendererBuffer> = Object.create(null);\n // One bounded read at a time. Abort/deadline rejects even if a host read stalls.\n const accessorsByResource = new Map<string, GpuModelAccessor[]>();\n for (const accessor of document.accessors) {\n const entries = accessorsByResource.get(accessor.resourceId) ?? [];\n entries.push(accessor);\n accessorsByResource.set(accessor.resourceId, entries);\n }\n for (const resource of document.resources) {\n const entries = accessorsByResource.get(resource.id);\n if (!entries) continue;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let abort: (() => void) | undefined;\n try {\n const buffer = await new Promise<ArrayBuffer>((resolve, reject) => {\n abort = () => reject(new GpuModelRendererError(\"aborted\"));\n options.signal?.addEventListener(\"abort\", abort, { once: true });\n timer = setTimeout(abort, Math.max(1, deadline - performance.now()));\n readBlob.call(resource.payload).then(resolve, () => reject(new GpuModelRendererError(\"resource-read-failed\")));\n });\n check();\n const view = new DataView(buffer);\n for (const accessor of entries) {\n check();\n accessors[accessor.id] = decode(accessor, view, check);\n }\n } finally {\n clearTimeout(timer);\n if (abort) options.signal?.removeEventListener(\"abort\", abort);\n }\n }\n const primitives: GpuModelRendererPrimitive[] = [];\n const geometry: GpuModelRendererGeometry[] = [];\n const materialSlots = new Map(document.materials.map((material, index) => [material.id, index]));\n for (const mesh of document.meshes) for (const primitive of mesh.primitives) {\n check();\n const attributes: Record<string, GpuModelRendererBuffer> = Object.create(null);\n for (const attribute of primitive.attributes) attributes[attribute.semantic] = accessors[attribute.accessorId]!;\n primitives.push(Object.freeze({ meshId: mesh.id, primitiveId: primitive.id, topology: primitive.topology,\n materialSlot: primitive.materialId === undefined ? null : materialSlots.get(primitive.materialId)!, attributes: Object.freeze(attributes) }));\n geometry.push(Object.freeze({ primitiveId: primitive.id, positions: attributes.POSITION! as Float32Array,\n ...(primitive.indicesAccessorId === undefined ? {} : { indices: accessors[primitive.indicesAccessorId]! as Uint32Array }),\n ...(attributes.NORMAL === undefined ? {} : { normals: attributes.NORMAL as Float32Array }),\n ...(attributes.TEXCOORD_0 === undefined ? {} : { texcoords: attributes.TEXCOORD_0 as Float32Array }) }));\n }\n const pack = <T extends Float32Array | Uint32Array>(rows: readonly (readonly number[])[], output: T): T => {\n let offset = 0;\n for (const row of rows) { check(); output.set(row, offset); offset += row.length; }\n return output;\n };\n const skins = document.skins.map(skin => {\n check();\n return Object.freeze({ id: skin.id, skeletonId: skin.skeletonId, jointIds: skin.jointIds,\n inverseBindMatrices: pack(skin.inverseBindMatrices, new Float32Array(skin.inverseBindMatrices.length * 16)),\n weightSets: Object.freeze(skin.weightSets.map(weights => Object.freeze({ primitiveId: weights.primitiveId,\n influencesPerVertex: weights.influencesPerVertex, joints: pack(weights.joints, new Uint32Array(weights.joints.length * weights.influencesPerVertex)), weights: pack(weights.weights, new Float32Array(weights.weights.length * weights.influencesPerVertex)) }))) });\n });\n const animations = document.animations.map(animation => Object.freeze({ id: animation.id, durationSeconds: animation.durationSeconds,\n channels: animation.channels, samplers: Object.freeze(animation.samplers.map(sampler => Object.freeze({ id: sampler.id,\n interpolation: sampler.interpolation, input: accessors[sampler.inputAccessorId]!, output: accessors[sampler.outputAccessorId]! }))) }));\n const resources = new Map(document.resources.map(resource => [resource.id, resource]));\n check();\n return Object.freeze({ document, bounds: document.bounds, roots: document.roots, nodes: document.nodes, materials: document.materials,\n skeletons: document.skeletons, joints: document.joints, blendShapes: document.blendShapes, analyticGeometry: document.analyticGeometry,\n diagnostics: document.diagnostics, ...(diagnosticReport === undefined ? {} : { diagnosticReport }), accessors: Object.freeze(accessors),\n primitives: Object.freeze(primitives), geometry: Object.freeze(geometry), skins: Object.freeze(skins), animations: Object.freeze(animations),\n textures: Object.freeze(document.textures.map(texture => Object.freeze({ texture, resource: resources.get(texture.imageResourceId)! }))) });\n}\n","/** Package identity and rollout metadata for the initial repository baseline. */\nexport const packageName = \"@plasius/gpu-model-renderer\" as const;\n\n/** @deprecated Retained for compatibility with the bootstrap package identity. */\nexport const packageBootstrap = Object.freeze({\n packageName,\n featureFlag: \"gpu.model.conversion.enabled\",\n status: \"bootstrap\",\n} as const);\n\nexport * from \"./renderer-input.js\";\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/renderer-input.ts","../src/index.ts"],"sourcesContent":["import {\n evaluateGpuModelDiagnostics, isGpuModelDocument,\n type GpuModelAccessor, type GpuModelDiagnosticInput, type GpuModelDiagnosticReport,\n type GpuModelDocument, type GpuModelResource, type GpuModelTexture,\n} from \"@plasius/gpu-model-core\";\n\n/** Fixed allocation ceiling; callers may tighten it, never raise it. */\nexport const GPU_MODEL_RENDERER_MAX_DECODED_BYTES = 64 * 1024 * 1024;\n/** Remotely evaluated parent Feature flag. No environment override is read here. */\nexport const GPU_MODEL_RENDERER_FEATURE_FLAG = \"gpu.model.conversion.enabled\";\n\n/** Stable, payload-free failure categories for callers and worker boundaries. */\nexport type GpuModelRendererErrorCode = \"feature-disabled\" | \"unverified-document\" | \"invalid-options\" | \"budget-exceeded\" | \"aborted\" | \"diagnostics-rejected\" | \"resource-read-failed\";\n/** Bounded errors deliberately omit source paths, URIs and payload content. */\nexport class GpuModelRendererError extends Error {\n public constructor(public readonly code: GpuModelRendererErrorCode) {\n super(`GPU model renderer preparation failed: ${code}`);\n this.name = \"GpuModelRendererError\";\n }\n}\n\n/** Consumer-owned buffers; changing them never changes the canonical document. */\nexport type GpuModelRendererBuffer = Float32Array | Uint32Array;\n/** Decode policy. Gate decisions must come from the host's remote flag evaluator. */\nexport interface GpuModelRendererOptions {\n readonly conversionEnabled: boolean;\n readonly signal?: AbortSignal;\n readonly timeoutMs?: number;\n readonly maxDecodedBytes?: number;\n readonly diagnostics?: GpuModelDiagnosticInput;\n}\n/** Geometry in mesh-local coordinates, directly usable by the canonical renderer adapter. */\nexport interface GpuModelRendererGeometry {\n readonly primitiveId: string;\n readonly positions: Float32Array;\n readonly indices?: Uint32Array;\n readonly normals?: Float32Array;\n readonly texcoords?: Float32Array;\n}\n/** All attributes are retained, including UV sets, colours, joints and custom semantics. */\nexport interface GpuModelRendererPrimitive {\n readonly meshId: string;\n readonly primitiveId: string;\n readonly topology: GpuModelDocument[\"meshes\"][number][\"primitives\"][number][\"topology\"];\n /** Zero-based canonical material index; null denotes the renderer's default material. */\n readonly materialSlot: number | null;\n readonly attributes: Readonly<Record<string, GpuModelRendererBuffer>>;\n}\n/** Ordered joint palette and packed vertex influences; no animation is evaluated or baked. */\nexport interface GpuModelRendererSkin {\n readonly id: string;\n readonly skeletonId: string;\n readonly jointIds: readonly string[];\n readonly inverseBindMatrices: Float32Array;\n readonly weightSets: readonly {\n readonly primitiveId: string;\n readonly influencesPerVertex: number;\n readonly joints: Uint32Array;\n readonly weights: Float32Array;\n }[];\n}\n/**\n * Canonical clip semantics with decoded, tightly packed sampler buffers.\n * A node-weights channel uses its target node's morphWeights order. Cubic\n * output retains per-key incoming tangents, values, then outgoing tangents;\n * preparation never evaluates, clamps, or normalizes those values.\n */\nexport interface GpuModelRendererAnimation {\n readonly id: string;\n readonly durationSeconds: number;\n readonly channels: GpuModelDocument[\"animations\"][number][\"channels\"];\n readonly samplers: readonly {\n readonly id: string;\n readonly interpolation: GpuModelDocument[\"animations\"][number][\"samplers\"][number][\"interpolation\"];\n readonly input: GpuModelRendererBuffer;\n readonly output: GpuModelRendererBuffer;\n }[];\n}\n/** Verified images remain encoded; the caller chooses its decoder/upload strategy. */\nexport interface GpuModelRendererTexture {\n readonly texture: GpuModelTexture;\n readonly resource: GpuModelResource;\n}\n/** Scene-aware preparation result. Geometry alone is not a baked world-space scene. */\nexport interface GpuModelRendererInput {\n readonly document: GpuModelDocument;\n readonly bounds: GpuModelDocument[\"bounds\"];\n readonly roots: GpuModelDocument[\"roots\"];\n /** Retains authored rest TRS and independent ordered per-node morph weights. */\n readonly nodes: GpuModelDocument[\"nodes\"];\n readonly materials: GpuModelDocument[\"materials\"];\n readonly skeletons: GpuModelDocument[\"skeletons\"];\n readonly joints: GpuModelDocument[\"joints\"];\n readonly blendShapes: GpuModelDocument[\"blendShapes\"];\n readonly analyticGeometry: GpuModelDocument[\"analyticGeometry\"];\n readonly diagnostics: GpuModelDocument[\"diagnostics\"];\n readonly diagnosticReport?: GpuModelDiagnosticReport;\n readonly accessors: Readonly<Record<string, GpuModelRendererBuffer>>;\n readonly geometry: readonly GpuModelRendererGeometry[];\n readonly primitives: readonly GpuModelRendererPrimitive[];\n readonly textures: readonly GpuModelRendererTexture[];\n readonly skins: readonly GpuModelRendererSkin[];\n readonly animations: readonly GpuModelRendererAnimation[];\n}\n\nconst COMPONENT_BYTES = { i8: 1, u8: 1, i16: 2, u16: 2, u32: 4, f32: 4 } as const;\nconst COMPONENTS = { scalar: 1, vec2: 2, vec3: 3, vec4: 4, mat2: 4, mat3: 9, mat4: 16 } as const;\n\nconst readBlob = Blob.prototype.arrayBuffer;\n\nfunction decode(accessor: GpuModelAccessor, view: DataView, check: () => void): GpuModelRendererBuffer {\n const size = COMPONENT_BYTES[accessor.componentType];\n const width = COMPONENTS[accessor.elementType];\n const stride = accessor.byteStride ?? size * width;\n const output = [\"u8\", \"u16\", \"u32\"].includes(accessor.componentType) && !accessor.normalized\n ? new Uint32Array(accessor.count * width)\n : new Float32Array(accessor.count * width);\n for (let index = 0; index < output.length; index += 1) {\n if (index % 4096 === 0) check();\n const offset = accessor.byteOffset + Math.floor(index / width) * stride + (index % width) * size;\n let value: number;\n switch (accessor.componentType) {\n case \"i8\": value = view.getInt8(offset); break;\n case \"u8\": value = view.getUint8(offset); break;\n case \"i16\": value = view.getInt16(offset, true); break;\n case \"u16\": value = view.getUint16(offset, true); break;\n case \"u32\": value = view.getUint32(offset, true); break;\n case \"f32\": value = view.getFloat32(offset, true); break;\n }\n if (accessor.normalized) {\n const divisor = { i8: 127, u8: 255, i16: 32767, u16: 65535, u32: 4294967295, f32: 1 }[accessor.componentType];\n value = Math.max(-1, value / divisor);\n }\n output[index] = value;\n }\n return output;\n}\n\n/**\n * Prepares privately verified core output without parsing source formats, fetching\n * resources or importing a renderer. Structured clones must be reverified by core.\n * Run on a host worker for large inputs. Operations are sequential, bounded and\n * have no retries. The host owns scene transforms and animation evaluation.\n */\nexport async function prepareGpuModelRendererInput(\n value: unknown,\n options?: GpuModelRendererOptions,\n): Promise<GpuModelRendererInput> {\n if (options?.conversionEnabled !== true) throw new GpuModelRendererError(\"feature-disabled\");\n if (!isGpuModelDocument(value)) throw new GpuModelRendererError(\"unverified-document\");\n const document = value;\n const maxBytes = options.maxDecodedBytes ?? GPU_MODEL_RENDERER_MAX_DECODED_BYTES;\n const timeoutMs = options.timeoutMs ?? 30_000;\n if (!Number.isSafeInteger(maxBytes) || maxBytes < 1 || maxBytes > GPU_MODEL_RENDERER_MAX_DECODED_BYTES\n || !Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 30_000) {\n throw new GpuModelRendererError(\"invalid-options\");\n }\n const diagnosticReport = options.diagnostics === undefined ? undefined : evaluateGpuModelDiagnostics(options.diagnostics);\n if (diagnosticReport?.accepted === false || document.diagnostics.some(({ severity }) => severity === \"error\")) {\n throw new GpuModelRendererError(\"diagnostics-rejected\");\n }\n let allocation = 0;\n for (const accessor of document.accessors) allocation += accessor.count * COMPONENTS[accessor.elementType] * 4;\n for (const skin of document.skins) {\n allocation += skin.inverseBindMatrices.length * 16 * 4;\n for (const weights of skin.weightSets) allocation += weights.joints.length * weights.influencesPerVertex * 8;\n }\n // Include copies of encoded buffers in the peak budget; image bytes are not decoded here.\n for (const resource of document.resources) if (resource.kind === \"buffer\") allocation += resource.byteLength;\n if (allocation > maxBytes) throw new GpuModelRendererError(\"budget-exceeded\");\n const deadline = performance.now() + timeoutMs;\n const check = () => {\n if (options.signal?.aborted || performance.now() >= deadline) throw new GpuModelRendererError(\"aborted\");\n };\n check();\n const accessors: Record<string, GpuModelRendererBuffer> = Object.create(null);\n // One bounded read at a time. Abort/deadline rejects even if a host read stalls.\n const accessorsByResource = new Map<string, GpuModelAccessor[]>();\n for (const accessor of document.accessors) {\n const entries = accessorsByResource.get(accessor.resourceId) ?? [];\n entries.push(accessor);\n accessorsByResource.set(accessor.resourceId, entries);\n }\n for (const resource of document.resources) {\n const entries = accessorsByResource.get(resource.id);\n if (!entries) continue;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let abort: (() => void) | undefined;\n try {\n const buffer = await new Promise<ArrayBuffer>((resolve, reject) => {\n abort = () => reject(new GpuModelRendererError(\"aborted\"));\n options.signal?.addEventListener(\"abort\", abort, { once: true });\n timer = setTimeout(abort, Math.max(1, deadline - performance.now()));\n readBlob.call(resource.payload).then(resolve, () => reject(new GpuModelRendererError(\"resource-read-failed\")));\n });\n check();\n const view = new DataView(buffer);\n for (const accessor of entries) {\n check();\n accessors[accessor.id] = decode(accessor, view, check);\n }\n } finally {\n clearTimeout(timer);\n if (abort) options.signal?.removeEventListener(\"abort\", abort);\n }\n }\n const primitives: GpuModelRendererPrimitive[] = [];\n const geometry: GpuModelRendererGeometry[] = [];\n const materialSlots = new Map(document.materials.map((material, index) => [material.id, index]));\n for (const mesh of document.meshes) for (const primitive of mesh.primitives) {\n check();\n const attributes: Record<string, GpuModelRendererBuffer> = Object.create(null);\n for (const attribute of primitive.attributes) attributes[attribute.semantic] = accessors[attribute.accessorId]!;\n primitives.push(Object.freeze({ meshId: mesh.id, primitiveId: primitive.id, topology: primitive.topology,\n materialSlot: primitive.materialId === undefined ? null : materialSlots.get(primitive.materialId)!, attributes: Object.freeze(attributes) }));\n geometry.push(Object.freeze({ primitiveId: primitive.id, positions: attributes.POSITION! as Float32Array,\n ...(primitive.indicesAccessorId === undefined ? {} : { indices: accessors[primitive.indicesAccessorId]! as Uint32Array }),\n ...(attributes.NORMAL === undefined ? {} : { normals: attributes.NORMAL as Float32Array }),\n ...(attributes.TEXCOORD_0 === undefined ? {} : { texcoords: attributes.TEXCOORD_0 as Float32Array }) }));\n }\n const pack = <T extends Float32Array | Uint32Array>(rows: readonly (readonly number[])[], output: T): T => {\n let offset = 0;\n for (const row of rows) { check(); output.set(row, offset); offset += row.length; }\n return output;\n };\n const skins = document.skins.map(skin => {\n check();\n return Object.freeze({ id: skin.id, skeletonId: skin.skeletonId, jointIds: skin.jointIds,\n inverseBindMatrices: pack(skin.inverseBindMatrices, new Float32Array(skin.inverseBindMatrices.length * 16)),\n weightSets: Object.freeze(skin.weightSets.map(weights => Object.freeze({ primitiveId: weights.primitiveId,\n influencesPerVertex: weights.influencesPerVertex, joints: pack(weights.joints, new Uint32Array(weights.joints.length * weights.influencesPerVertex)), weights: pack(weights.weights, new Float32Array(weights.weights.length * weights.influencesPerVertex)) }))) });\n });\n const animations = document.animations.map(animation => Object.freeze({ id: animation.id, durationSeconds: animation.durationSeconds,\n channels: animation.channels, samplers: Object.freeze(animation.samplers.map(sampler => Object.freeze({ id: sampler.id,\n interpolation: sampler.interpolation, input: accessors[sampler.inputAccessorId]!, output: accessors[sampler.outputAccessorId]! }))) }));\n const resources = new Map(document.resources.map(resource => [resource.id, resource]));\n check();\n return Object.freeze({ document, bounds: document.bounds, roots: document.roots, nodes: document.nodes, materials: document.materials,\n skeletons: document.skeletons, joints: document.joints, blendShapes: document.blendShapes, analyticGeometry: document.analyticGeometry,\n diagnostics: document.diagnostics, ...(diagnosticReport === undefined ? {} : { diagnosticReport }), accessors: Object.freeze(accessors),\n primitives: Object.freeze(primitives), geometry: Object.freeze(geometry), skins: Object.freeze(skins), animations: Object.freeze(animations),\n textures: Object.freeze(document.textures.map(texture => Object.freeze({ texture, resource: resources.get(texture.imageResourceId)! }))) });\n}\n","/** Package identity and rollout metadata for the initial repository baseline. */\nexport const packageName = \"@plasius/gpu-model-renderer\" as const;\n\n/** @deprecated Retained for compatibility with the bootstrap package identity. */\nexport const packageBootstrap = Object.freeze({\n packageName,\n featureFlag: \"gpu.model.conversion.enabled\",\n status: \"bootstrap\",\n} as const);\n\nexport * from \"./renderer-input.js\";\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plasius/gpu-model-renderer",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Canonical GPU model to renderer-ready buffers and animation inputs bridge.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -70,7 +70,7 @@
70
70
  "devDependencies": {
71
71
  "@eslint/js": "^10.0.1",
72
72
  "@plasius/gpu-model-runtime": "0.2.4",
73
- "@plasius/gpu-renderer": "0.2.51",
73
+ "@plasius/gpu-renderer": "0.2.53",
74
74
  "@types/node": "^26.0.1",
75
75
  "@typescript-eslint/eslint-plugin": "^8.59.3",
76
76
  "@typescript-eslint/parser": "^8.59.3",
@@ -106,6 +106,6 @@
106
106
  "node": ">=24"
107
107
  },
108
108
  "dependencies": {
109
- "@plasius/gpu-model-core": "0.4.6"
109
+ "@plasius/gpu-model-core": "0.6.0"
110
110
  }
111
111
  }