# `SkinBinding`

> Contract 2.0.0 · package sha256 `a857eca95b35921109faf997094bb4192f12527d9d0fcd4be7e97f06425bad6e` · generated from `configurator-runtime.d.ts` by `services/onsite-scripter/scripts/build-contract-docs.mjs`; edit the source, not this file.

Type · `import type { SkinBinding } from '@holostep-configurators/runtime-client/contract';` · labs, `@experimental` · since 2.0.0 · section 20. Labs

Binds a collection mesh to a loaded model's skeleton. `node`: the ModelNode; `skeletonOf`: the
authored name of a skinned mesh in it whose skeleton is shared. Per-vertex `joints` (4 per
vertex, indices into that skeleton) and `weights`, or one `rigidJoint` for the whole mesh.
- Bind space: the collection mesh's positions are in `skeletonOf`'s bind space, the space
  [`labs.geometry.read(…, ['position'], { from: 'authored' })`](Labs.geometry.read.md) returns for that mesh.
- A vertex whose four weights are all 0 is rigid to the ModelNode's root: it moves with the model
  and with no joint (a static panel edge beside a moving door).
- While bound, the item's placement in its collection (`at`, `parent`) is ignored: a weighted
  vertex is placed by its joints, and an all-zero one through `skeletonOf`'s bind matrix into the
  ModelNode's root frame, where it lies in the model's bind pose.
- Lifetime: the binding stays on the item and follows `node`. When that ModelNode's
  model is replaced (a new asset version, a twin swap, a write or undo that restores the node),
  the runtime binds the item again before the first frame that draws it, mapping each joint
  index through the old skeleton's joint names to the new skeleton (`rigidJoint` by its name).
  A name the new skeleton lacks drops the binding, records 'target-unmatched' (`node`,
  `skeletonOf`) and gives the item its placement back. Positions are not rebuilt: after a new
  bind pose, set new ones and bind again. While `node` is absent, the item is hidden. A
  [`Collection.set`](Collection.set.md) that changes the item in place keeps the binding while its vertex count is
  unchanged; a new count drops it and records 'invalid-input' at the item's path. Removing the
  item ends it.

## Declaration

```ts
export type SkinBinding = {
  node: string;
  skeletonOf: string;
  joints: Uint16Array;
  weights: Float32Array;
} | {
  node: string;
  skeletonOf: string;
  rigidJoint: string;
};
```

## Generic-API test (spec 3.1)

| Rule | Result |
| --- | --- |
| 1. Neutral name | pass: `SkinBinding` names no product |
| 2. A precedent | pass: [glTF 2.0 skins](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#skins) (row `G22`); [Unity SkinnedMeshRenderer.bones](https://docs.unity3d.com/ScriptReference/SkinnedMeshRenderer-bones.html) (row `G22`); [Unity SkinnedMeshRenderer](https://docs.unity3d.com/ScriptReference/SkinnedMeshRenderer.html) (row `K55`) |
| 3. Two apps, or labs | labs (`@experimental` at a stable path, reached through `has()` or `supports()`): it graduates when a second app uses it; apps today: app-bcb7ef21da |
| 4. The cheaper fix first | decided per need in Appendix B: rows `G22`, `K55` at tier labs (a member only where no rule, data, asset or app code serves the need) |
