# 0.12.3

<!--{ "parent": "CHANGELOG", "order": 9987997 }-->

Characters out to other engines and any mesh rigged well: a GLB read with no engine, any rigged body written as a skinned GLB that plays UAL's clips by name in three.js, Unity, Godot and Blender, and its springs as `VRMC_springBone`; the rig tool's fixes for AI-made meshes; draped cloth in the tailor; and scenes as agent tools and for a screen reader.

Size: barrel (min, packages external) 1596.0 KB raw / 555.0 KB gz, up 85.6 KB / 31.3 KB gz (+5.7%): scenes as agent tools, widget semantics, the screen-reader mirror and the scene description, and the drape and remesh are in the barrel; `glb-rig`, `rigged-body-glb`, `ual-rest` and `spring-vrm` are subpaths.

## Added

- **`tosijs-3d/glb-rig`**: `glbRigTriangles(bytes, { pick })` reads a mesh file's triangles for rigging with no engine: the file's frame, a skinned mesh at rest, one surface per material carrying its glTF material and its pictures as bytes, eyes split out. It agrees with `loadRigTriangles`, and `bodyFromMarkers` on it rigs a body with no scene (the same bytes and markers give the same weights).
- **`tosijs-3d/rigged-body-glb`**: `riggedBodyGlb(body, { naming, restPose, height, recipe, extras })` writes any rigged body (from the rig tool, a file's own skin, MakeHuman) as a skinned GLB in Dramatis's export profile: UAL's 65 joints with UAL's rest frames, so UAL's clips play on it by name; a T-pose by default (or the body as rigged); the soles at y = 0 under the origin; `body` and `eyes` meshes; the file's own materials and pictures; UE or Mixamo joint names. `tosijs-3d/ual-rest` is UAL's rest pose as data (`bin/ual-rest.ts` regenerates it).
- **`tosijs-3d/spring-vrm`**: a rig recipe's springs (a tail, strands hung from a bone, with colliders) go out as `VRMC_springBone`, which three-vrm, UniVRM and Godot's VRM importer read with no setup, and as `TOSIJS_spring_chains` with our own settings (`springChainsSchema()`). The page says where VRM's spring model differs from ours.
- `writeSkinnedGlb` writes emissive materials and root-level `extensions`.
- **Draped cloth in the tailor** (`tosijs-3d/cloth-drape`, `drapeGarment`): an outline becomes a front and a back piece laid over the body, sewn up its steep sides, bridged over hollows and settled under gravity. Pick it as "made by: drape". The tailor cuts from an even surface laid over the body at a density you pick, finer where the body is thin.
- **Character builder**: Tonio's AI-made characters (Keisha and Kevin, in underwear and in spacesuits) are bodies, one picked at random on a first visit; a **View** menu on the system bar, shared with the hosted rig tool, with a right-hand view that follows the palm through the clip; **clips from a file** (any skinned GLB's animations, Mixamo's or Meshy's, retargeted onto the body shown); every clip in the library; **hold** an item in the right hand, placed by its grip and saved with the character (`handSocket`, `deriveGrip`, `heldGrip`); **body detail** draws MakeHuman's own lighter body proxies; revert, delete, and the selector shows the character on screen.
- **Rig tool**: markers colour-coded with a reference mannequin; a dragged wrist or knee carries its hand or foot, a dragged shoulder or elbow the markers below it; joints mode shows every joint.
- **Scenes as agent tools** (`b3d-agent`): each `<tosi-b3d>` offers tools on WebMCP (`registerTool` only) and on `window.b3dAgent`: `b3d_<scene>_scene` and `_places` (named things and beacons, in world metres, with distance and bearing), `_snapshot`, `_control_go` (drives the vehicle in focus to a named place with its own controls; the player's input cancels it), `_control_hold`, `_control_status`, `_control_stop`, and the settings panel (`_panel`, `_panel_set`, `_panel_activate`). Closed by default: `enableSceneAgents({ expose: 'all' | 'read' })` opens the page, and a scene's new `agent` attribute (`read`, `on`, `off`) overrides it. `b3d.originOffset` is how far the floating origin has moved. Changing a scene's `agent` (or its name) after it is on the page updates its tools (#3699).
- **`bun run xr --flat --script`** now runs the script (it was ignored without VR), and a script that throws fails the run.
- **`semantics()` on every panel widget** (`tosijs-3d/widget-semantics`): a control says what it is (role, name, value) and can be set or pressed through its own path, as a hand would. `panel.controls()` reads a panel as a tree of named controls, including an open popup's form; `findControl` and `nameProblems` (unnamed or duplicate controls) work on it. `inputField` and `vector3d` take a `name`; `tabs3d` a `name`. This is the base the WebMCP tools and the screen-reader mirror are built on (#3699).
- **A screen reader can use the settings panel** (`tosijs-3d/aria-mirror`): an open flat panel is mirrored as visually hidden HTML controls (buttons, switches, range inputs, selects, text and number fields; sections as headings), which set or press the panel's own widgets. The system bar's HTML buttons now stay in the accessibility tree in scene mode (they were `visibility: hidden`) and appear while one has keyboard focus, so Tab goes from the owl into the settings. `ariaMirror(host, read)` mirrors any panel (#3699).
- **Every demo's settings panel passes the name test** (63 pages, about 1000 controls). The big editors describe themselves too: `curve3d` and `footprint3d` are editors whose value is their points and whose options are their presets (set either), and `curveProgram3d`, `lightEditor3d` and `terrainEditor3d` list their rows. A `list3d` item may carry a `title`, its accessible name when the label is not unique; an indented label is trimmed (#3699).
- **A keyboard and screen-reader pass over the panels** (`bun run screen-reader <page> --steps …`, a driver that reads only Chrome's accessibility tree and presses real keys). Fixes from it: the owl says whether its panel is open (`aria-expanded`); a slider announces its formatted value (`semantics().valueText`, `aria-valuetext`: "50 km", "±13 °C"); a log-scale slider is stepped along its track, not in equal steps of its value; focus stays in place when the control it was on is renamed (Pause becoming Resume); a toolbar with no name is "tools". `button3d` takes a `title` (its name and tooltip when the label is a glyph), and the name test now reports a control named only by a glyph such as `⋯` (#3699).
- **The 3D view for a screen reader**: the canvas is an image named "<title>, 3D scene", described in words (`describeScene`, `tosijs-3d/scene-description`: what you are in, which way you face, the time of day, the named things nearby with distance and direction). Built only when asked for: once when the scene appears, when the canvas takes focus, and by a visually hidden "Describe the scene" button that speaks it; never on a timer (#3699).
- **Assistive features cost nothing until used, and sit where things are drawn**: a scene builds no mirror, description or pad labels until focus first lands in it (not focus from a mouse or finger press: clicking the scene focuses its canvas) or one of its hidden buttons is pressed for real (iOS VoiceOver presses without focusing) (`b3d.assistive`), and runs no timer even then (values are re-read when a control takes focus). The mirrored controls are placed exactly over the drawn panel, modal and system bar (`ariaMirror`'s `place`; `ControlNode.box`, `WidgetSemantics.box` / `childBoxes`), so VoiceOver's touch exploration on a phone finds "Continue" where it is drawn and a double-tap lands on it (#3699).
- **Modals for a screen reader**: a modal (`showModal`: the pause, a respawn panel) is mirrored as a hidden dialog that takes focus on its first control (Continue) and gives focus back when it closes, so a pause can be dismissed with no pointer. It is always mirrored, not only once a scene is in use, because a modal blocks the page; its title ("Paused") is also announced. `ariaMirror` takes `role: 'dialog'`. Pressing a mirrored copy no longer counts as a press outside the settings panel (that closed the panel before the copy's click). Placed copies are real touch targets over the drawn controls, unpainted but opaque and, where the main pointer is a finger, touchable (`UNPAINTED`; with a mouse every press goes through to the drawn panel): iOS VoiceOver finds what a finger is on by hit-testing and passed transparent, untouchable copies over for the canvas. In use, the system bar's real buttons are touch targets the same way, and a copy with keyboard focus shows a ring over the drawn control. While a modal is open the system bar's buttons and the panel's copies are inert, so a reader cannot open the settings from behind a pause (#3699).
- **The glass gamepad says where its controls are and what they do**: each control is named for a screen reader ("A button, bottom right: handbrake"), from the driven entity's new `inputLabels` (car, tank, aircraft and biped carry their mapping's labels), updated when you change what you control. The pad also stays hidden while the scene is loading.
- **A weapon in a hand holds the same way on any rig**: a launcher with `socket: 'right-hand'` or `'left-hand'` rides a node under the hand oriented by the hand's anatomy at rest (forearm, thumb side), calibrated so UAL's hand is unchanged. A character from the character builder held the playground's pistol at a wrong angle and mirrored, because its hand bone's axes and mirror differ from UAL's.
- **Character builder ↔ Biped Playground**: the builder is under Demos, and **play in Biped Playground** takes the figure you are working on straight there (`/playground/?character=…`). The playground lists that figure as "from the character builder", loads the character it shows (the last one picked, remembered in this browser), and links back with **make your own character…**.
- **A sideways trackpad drag over a scene no longer swipes the page back or forward**: a scene cancels the default of a mostly horizontal wheel over it (the camera and panels still get the event).
- **Input trace in development**: with the debug sink on (the dev server), a scene posts each press it receives (touch, pointer, click, focus: real target, position, whether genuine) to the sink as `input`, so how a phone's screen reader presses things can be read rather than guessed (#3699).
- **Clouds drift on the sky's clock**: a cloud deck under a sky sped up with `realtimeScale` drifts and evolves that much faster, so a time-lapse carries its clouds; a stopped sky leaves them in real time (`skyRate(owner)`).

## Changed

- **Bone heat with visibility**: a bone heats only the skin it can see, radiating along its whole length.
- **Arm space follows the clip**: how far a clip hangs the arm decides how much arm space applies.
- A biped's character loads out of sight and appears standing where it belongs.

## Fixed

- **The rig tool stands a model on the floor when its file's origin is its middle** (Meshy's are), not its feet: on 0.12.2 these stood half under the floor.
- **Auto-rig**: a body with its arms held out keeps the arm bones off its ribcage and the collarbones off the chest; a held-out arm's shoulder clears the body; the thighs stay out of the belly, so a walk no longer squashes the torso; the elbow is found on arms held steeply down.
- **Done in the rig tool no longer makes the character vanish.**
- The tailor cuts a file's body as one surface (seams split for UVs are welded first).
- A character in the Biped Playground wears its clothes' saved colours, not white; a reload of the builder remembers which saved character you were editing.
- Panels fold their own sections, and the rig tool wears a mesh's own materials.
