metaverse-avatar 0.1.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.
Files changed (46) hide show
  1. package/ASSET_LICENSES.md +122 -0
  2. package/Avatar.js +860 -0
  3. package/LICENSE.md +21 -0
  4. package/README.md +407 -0
  5. package/anims/UAL1_Standard.glb +0 -0
  6. package/anims/UAL2_Standard.glb +0 -0
  7. package/anims/pirouette.bvh +867 -0
  8. package/attachments.js +388 -0
  9. package/avatarManager.js +110 -0
  10. package/blink.js +58 -0
  11. package/bvh.js +110 -0
  12. package/gltfAnim.js +271 -0
  13. package/index.js +61 -0
  14. package/licenses/AGPL-3.0.txt +661 -0
  15. package/models/body.glb +0 -0
  16. package/models/eyes.glb +0 -0
  17. package/models/feet.glb +0 -0
  18. package/models/hands.glb +0 -0
  19. package/models/head.glb +0 -0
  20. package/models/textures/android_face.png +0 -0
  21. package/models/textures/android_face_ao.jpg +0 -0
  22. package/models/textures/android_face_metallic.jpg +0 -0
  23. package/models/textures/android_face_normal.jpg +0 -0
  24. package/models/textures/android_face_roughness.jpg +0 -0
  25. package/models/textures/android_lower.png +0 -0
  26. package/models/textures/android_lower_ao.jpg +0 -0
  27. package/models/textures/android_lower_metallic.jpg +0 -0
  28. package/models/textures/android_lower_normal.jpg +0 -0
  29. package/models/textures/android_lower_roughness.jpg +0 -0
  30. package/models/textures/android_upper.png +0 -0
  31. package/models/textures/android_upper_ao.jpg +0 -0
  32. package/models/textures/android_upper_metallic.jpg +0 -0
  33. package/models/textures/android_upper_normal.jpg +0 -0
  34. package/models/textures/android_upper_roughness.jpg +0 -0
  35. package/models/textures/blue_eyes.png +0 -0
  36. package/models/textures/layers/cute_pants.png +0 -0
  37. package/models/textures/layers/cute_shirt.png +0 -0
  38. package/nipple.js +218 -0
  39. package/package.json +48 -0
  40. package/pbr.js +225 -0
  41. package/physics.js +313 -0
  42. package/skeleton.js +70 -0
  43. package/sliders.js +590 -0
  44. package/speech.js +130 -0
  45. package/visemes.js +66 -0
  46. package/voice.js +75 -0
package/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Richard Anaya
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,407 @@
1
+ > **Dedication.** This project exists because of the immense work of the
2
+ > [RuthAndRoth/Ruth](https://github.com/RuthAndRoth/Ruth) contributors — an
3
+ > open-source mesh avatar built over years of rigging, fitting, testing, and
4
+ > community iteration. *metaverse-avatar* is a small effort on top of that foundation
5
+ > made possible by a certain place and time with AI; the credit belongs mostly to them.
6
+
7
+ # metaverse-avatar
8
+
9
+ A reusable three.js avatar library for the [RuthAndRoth](https://github.com/RuthAndRoth/Ruth)
10
+ **Ruth2 RC3** open-source mesh avatar — one `Avatar` class with shape sliders,
11
+ BVH/glTF animation, PBR materials, physics, lip-sync, blinking, and attachments.
12
+ The library source sits at the repo root (the `*.js` files, plus the `models/`
13
+ mesh assets and the `anims/` animation clips); the apps live under `examples/`.
14
+ The published npm package bundles `models/` and `anims/`, so a consumer gets the
15
+ avatar and the sample clips out of the box (point loaders at the package path,
16
+ or copy them into your own served assets).
17
+
18
+ No build step — three.js r184 is loaded from the jsDelivr CDN via an import map.
19
+
20
+ ## Run
21
+
22
+ Serve the repo root with any static file server, then open one of the examples:
23
+
24
+ ```sh
25
+ python3 -m http.server 8413
26
+ # Studio (the full lab UI): http://localhost:8413/examples/studio/
27
+ # Minimal usage: http://localhost:8413/examples/simple/
28
+ ```
29
+
30
+ (ES modules need `http://`, not `file://`.)
31
+
32
+ ## Examples
33
+
34
+ - **`examples/studio/`** — the full lab UI: shape sliders, PBR textures, the BVH
35
+ pose editor, physics, multiple avatars, play mode, and more. Pure static; this
36
+ is what used to be the repo's root `index.html`.
37
+ - **`examples/simple/`** — the smallest way to use the avatar: three.js from a
38
+ CDN import map + the avatar loaded from this repo's local files, wired up like
39
+ the Play tab (keyboard driving + chase camera). Serve the repo and open
40
+ `/examples/simple/`.
41
+ - **`examples/mcp/`** — control an avatar over [MCP](https://modelcontextprotocol.io).
42
+ It's **self-contained** with its own `package.json` (express, ws, zod,
43
+ `@modelcontextprotocol/sdk`): a server exposes an MCP endpoint + WebSocket
44
+ bridge that relays tool calls into the avatar page.
45
+
46
+ ```sh
47
+ cd examples/mcp
48
+ npm install
49
+ npm run dev # http://localhost:4173/examples/mcp/ (page) + /mcp (MCP endpoint)
50
+ ```
51
+
52
+ Point your MCP client at `http://localhost:4173/mcp` and keep the page open.
53
+ Tools: `list_animations`, `trigger_animation`, `get_avatar_transform`,
54
+ `capture_screenshot`, `set_avatar_blink`, `set_avatar_look_at`,
55
+ `avatar_text_to_speech` (needs `XAI_API_KEY` in a `.env`), plus
56
+ `list_avatars` / `add_avatar` / `select_avatar`.
57
+
58
+ ## Use as a library — the `Avatar` class
59
+
60
+ The whole avatar is a **single class**: `new Avatar()` builds a self-contained,
61
+ independently-controllable figure — its own skeleton, materials, animation
62
+ state, pec/glute physics, blinking, mic/TTS lip-sync, prop attachments, and eye
63
+ look-at. Nothing is shared between instances, so you can load several into
64
+ one scene and drive each separately.
65
+
66
+ `three` is a **peer dependency** — the host app provides it (an import map in
67
+ the browser, or `node_modules` under a bundler), so every avatar shares the
68
+ app's single THREE instance. `index.js` is the package entry
69
+ (`main` / `module` / `exports`).
70
+
71
+ ```js
72
+ import { Avatar } from 'metaverse-avatar'; // or './index.js' without a bundler
73
+ ```
74
+
75
+ ### What an `Avatar` can do — at a glance
76
+
77
+ | area | API |
78
+ |---|---|
79
+ | **lifecycle** | `await load(basePath)` · `update(dt)` (advances everything) · `dispose()` · `group` (add to scene) |
80
+ | **shape** | `applyShape({ height, breasts, … })` — see `SLIDERS` / `SEX_PRESETS` |
81
+ | **appearance** | `setPartVisible(part, on)` · `setTextured(on)` · `setSkinMap(region, channel, img)` · clothing layers (`addClothingLayer` / `setLayerMap` / …) · `setGlobalRoughness` / `setGlobalMetalness` |
82
+ | **animation** | `playClip(clip)` · `crossFadeTo(clip, dur, loop)` · `setLayerStack([…])` · `setSpeed(s)` · `setPaused(on)` · `stop()` · `playing` |
83
+ | **blinking** | `setBlinking(on)` · `blinkNow()` · `blinker` |
84
+ | **lip-sync** | `startMic()` / `stopMic()` (mic) · `speak(url, visemeUrl)` / `stopSpeaking()` (TTS) · `voice`, `speech` |
85
+ | **eye look-at** | `setLookAt({ enabled, x, y, z })` · `getLookAt()` · `lookAt` |
86
+ | **attachments** | `attachBuiltin(name, bone, factory, offset)` · `attachFile(file, bone, offset)` · `attachments` |
87
+ | **physics** | `pecPhysics`, `glutePhysics` — `.enabled` / `.bounciness` / `.damping` / `.sag` |
88
+ | **low-level face** | `setMouthOpen(t)` · `setMouth({ open, round, wide })` · `setBlink(t)` |
89
+
90
+ Everything is per-instance — call any of these on each `new Avatar()` independently.
91
+ Locomotion (walk/run/jump/fly) is **not** on the avatar; it's an app-level concern
92
+ (see [§4](#4-moving-the-avatar-around-locomotion)).
93
+
94
+ ### 1. Construct and load
95
+
96
+ The constructor is cheap and synchronous; `load()` does the async work (fetches
97
+ the mesh parts + default textures, grafts the head, captures the rest pose) and
98
+ resolves to the same instance. **Add `avatar.group` to your scene** — it's the
99
+ avatar's single root `THREE.Group`.
100
+
101
+ ```js
102
+ const avatar = await new Avatar().load('models/'); // models/ = where the meshes + textures live
103
+ scene.add(avatar.group);
104
+ ```
105
+
106
+ It loads the `.glb` parts (`body.glb`, `hands.glb`, …) — glTF/GLB only. Override
107
+ the filenames per call to swap in your own meshes (same Z-up Ruth rig):
108
+
109
+ ```js
110
+ await new Avatar().load('models/', {
111
+ parts: { body: 'body.glb', hands: 'hands.glb', feet: 'feet.glb', head: 'head.glb', eyes: 'eyes.glb' },
112
+ });
113
+ ```
114
+
115
+ Each `parts` value may be a plain filename (joined with `basePath`) **or an
116
+ absolute URL** (`https:`, `/…`, `blob:`, `data:`) used as-is — so under a bundler
117
+ you can hand the avatar the exact, fingerprinted asset URLs it emits:
118
+
119
+ ```js
120
+ import bodyUrl from './models/body.glb'; // Vite/webpack → a hashed URL
121
+ import handsUrl from './models/hands.glb';
122
+ // …feet, head, eyes
123
+ await new Avatar().load('models/', { parts: { body: bodyUrl, hands: handsUrl, /* … */ } });
124
+ // NOTE: skin textures still resolve under basePath (not yet per-file URLs).
125
+
126
+ // optional: face the avatar toward +Z (the rig faces +X by default)
127
+ avatar.group.rotation.y = -Math.PI / 2;
128
+ ```
129
+
130
+ | call | what it does |
131
+ |---|---|
132
+ | `new Avatar()` | build (no I/O) |
133
+ | `await avatar.load(basePath)` | load the meshes/textures from `basePath` (default `'models/'`); returns `this` |
134
+ | `avatar.group` | the root `THREE.Group` — add this to your scene |
135
+ | `avatar.dispose()` | free all GPU resources and detach from the scene |
136
+
137
+ ### 2. Drive it each frame
138
+
139
+ One call advances everything the avatar does on its own — animation mixers,
140
+ physics (jiggle/sag), blinking, mic/TTS lip-sync, and eye look-at:
141
+
142
+ ```js
143
+ const clock = new THREE.Clock();
144
+ renderer.setAnimationLoop(() => {
145
+ avatar.update(clock.getDelta());
146
+ renderer.render(scene, camera);
147
+ });
148
+ ```
149
+
150
+ Locomotion is the one capability that's **input-driven**, so you tick it
151
+ yourself after setting its intent (see below).
152
+
153
+ ### 3. Capabilities
154
+
155
+ Each avatar owns one of each capability as a public member, plus thin verb
156
+ facades for the common actions:
157
+
158
+ | member | facade(s) | what it does |
159
+ |---|---|---|
160
+ | `avatar.blinker` | `setBlinking(on)`, `blinkNow()` | procedural eye blinking |
161
+ | `avatar.voice` | `startMic()`, `stopMic()` | microphone-driven jaw (lip-sync to live audio) |
162
+ | `avatar.speech` | `speak(url, visemeUrl)`, `stopSpeaking()` | play a TTS clip and lip-sync to it (per-viseme if a timing file is given) |
163
+ | `avatar.attachments` | `attachBuiltin(name, bone, factory, offset)`, `attachFile(file, bone, offset)` | parent props (sword, hat, …) to bones |
164
+ | `avatar.lookAt` | `setLookAt({ enabled, x, y, z })`, `getLookAt()` | aim the eyes at a world-space point |
165
+ | `avatar.pecPhysics`, `avatar.glutePhysics` | — | soft-body jiggle/sag (`.enabled`, `.bounciness`, `.damping`, `.sag`) |
166
+
167
+ ```js
168
+ avatar.setBlinking(true); // start idle blinking
169
+ await avatar.speak('/clip.mp3', '/clip.json'); // speak + lip-sync
170
+ avatar.setLookAt({ enabled: true, x: 1, y: 1.5, z: 2 }); // gaze at a point
171
+ avatar.attachBuiltin('Sword', 'mWristRight', createSword); // createSword is a metaverse-avatar export
172
+ ```
173
+
174
+ ### 4. Moving the avatar around (locomotion)
175
+
176
+ **Locomotion is intentionally not part of `Avatar`** — how a figure moves through
177
+ the world is the game's concern (a physics engine, a navmesh, networked input,
178
+ …). Move an avatar however you like by driving `avatar.group` (position /
179
+ rotation) and the animation methods directly.
180
+
181
+ Locomotion is **not** a library export — it's app-level, so the library doesn't
182
+ prescribe it. A ready-made controller (walk / run / turn / jump / fly + a
183
+ cross-faded state machine) lives in the examples at
184
+ [`examples/common/locomotion.js`](examples/common/locomotion.js); copy it into
185
+ your app and bind it to an avatar:
186
+
187
+ ```js
188
+ import { Locomotion } from './locomotion.js'; // copied from examples/common/
189
+
190
+ // glbFile is required — point it at your own locomotion clip GLB.
191
+ const loco = new Locomotion(avatar, { glbFile: 'anims/UAL1_Standard.glb' });
192
+ await loco.start(); // loads the clips, enters control
193
+ loco.setInput({ forward: true, left: true });
194
+ loco.setInput({ forward: true, run: true }); // hold Shift in Play mode
195
+ // also toggleFly(), jump(), toggleSit()
196
+ // per frame, after avatar.update(dt):
197
+ loco.update(dt);
198
+ // …later:
199
+ loco.stop();
200
+ ```
201
+
202
+ The `PlayMode` in `examples/studio/play.js` is one example viewer that wraps
203
+ `Locomotion` with a keyboard and a third-person chase camera.
204
+
205
+ ### 5. Appearance
206
+
207
+ ```js
208
+ // Shape — a map of slider id → value (roughly -1.5..1.5). See SLIDERS / SEX_PRESETS.
209
+ avatar.applyShape({ height: 0.8, breasts: 0.4 });
210
+
211
+ // Materials — per-region PBR maps (region: face|upper|lower|eyes, channel:
212
+ // albedo|normal|roughness|metallic|ao) and stacked clothing layers.
213
+ avatar.setSkinMap('upper', 'albedo', someImage);
214
+ const i = avatar.addClothingLayer('upper');
215
+ avatar.setLayerMap('upper', i, 'albedo', shirtImage);
216
+
217
+ avatar.setPartVisible('head', false); // toggle a body part
218
+ avatar.setTextured(false); // drop to flat material
219
+ ```
220
+
221
+ ### 6. Animation
222
+
223
+ ```js
224
+ import { loadBVH, retargetToRuth } from 'metaverse-avatar';
225
+
226
+ // loadBVH takes any URL/path your app serves the clip from; the package bundles
227
+ // a few sample clips under anims/ (serve them, or point at your own).
228
+ const clip = retargetToRuth(await loadBVH('anims/pirouette.bvh'), avatar.pelvisRestZ);
229
+ avatar.playClip(clip); // play one clip as the whole body
230
+ avatar.crossFadeTo(otherClip, 0.25); // blend to another over 0.25 s
231
+ avatar.setLayerStack([ // layered playback (index 0 = highest priority)
232
+ { id: 'rgrip', clip: gripClip, loop: true }, // hand-only layer on top…
233
+ { id: 'walk', clip: walkClip, loop: true }, // …full-body underneath
234
+ ]);
235
+ avatar.setSpeed(1.5); avatar.setPaused(true); avatar.stop();
236
+ ```
237
+
238
+ ### 7. Multiple avatars
239
+
240
+ Because instances share nothing, several can live in one scene and be driven
241
+ completely separately:
242
+
243
+ ```js
244
+ const a = await new Avatar().load('models/');
245
+ const b = await new Avatar().load('models/');
246
+ a.group.position.x = -1;
247
+ b.group.position.x = 1;
248
+ scene.add(a.group, b.group);
249
+
250
+ a.applyShape({ height: 0.8 });
251
+ b.setLookAt({ enabled: true, x: 0, y: 1.4, z: 3 });
252
+
253
+ const aLoco = new Locomotion(a, { glbFile: 'anims/UAL1_Standard.glb' }); // app-level, one per avatar
254
+ await aLoco.start();
255
+ aLoco.setInput({ forward: true });
256
+
257
+ renderer.setAnimationLoop(() => {
258
+ const dt = clock.getDelta();
259
+ a.update(dt); b.update(dt); // both animate, blink, gaze independently
260
+ aLoco.update(dt); // only a is walking
261
+ renderer.render(scene, camera);
262
+ });
263
+ ```
264
+
265
+ > The Studio app (`examples/studio/main.js`) layers a UI, a chase camera (`PlayMode`), the BVH
266
+ > `AnimEditor`, and the MCP bridge on top — all *scene-level* concerns that live
267
+ > in the host app, **not** in `Avatar`. `avatarManager.js` shows one way to
268
+ > track several avatars and switch which is "active".
269
+
270
+ ## Features
271
+
272
+ - **Model** — the Ruth2 RC3 release meshes (`Release3_BothLowerUpper_15.dae`,
273
+ `Release3_Hands_15.dae` bento hands, `Release3_FlatFeet_15.dae`), plus the
274
+ Ruth2 v4 bento head and eyeballs (`Ruth2v4Head.dae`, `Ruth2v4Eyeballs.dae`
275
+ from the RuthAndRoth/Ruth2 repo — the RC3 release has no head). The v4
276
+ head/eye exports have flat skeletons, so their bones are grafted into the
277
+ body's skeleton at load under a synthesized `mHead` bone, which makes them
278
+ follow BVH animation and sliders like native parts. Parts can be toggled.
279
+ - **PBR textures** — every region (face / upper / lower / eyes) is a full
280
+ metallic-roughness material with five map channels: Base Color, Normal,
281
+ Roughness, Metallic, and Ambient Occlusion. Maps are composited per channel
282
+ on 1024² canvases (`pbr.js`). Roughness/Metallic also have global
283
+ constant sliders used wherever no map is loaded (the "value instead of a
284
+ texture" path). Upper and lower regions additionally accept **stacked
285
+ clothing layers** — each layer is its own five-map set, masked by its
286
+ albedo alpha and composited over the skin; layers can be reordered, hidden,
287
+ and removed. A neutral studio environment (`RoomEnvironment`) lights the
288
+ metals. `aoMap` reuses the primary UVs (copied into `uv1` at load).
289
+ - **Play mode** — the Play tab drives the avatar with the keyboard,
290
+ keyboard controls (WASD/arrows to walk and turn, Shift run, Space jump,
291
+ F/Home toggle flight, E/C ascend/descend) with a third-person chase camera.
292
+ It blends UAL1 walk / jog / idle / jump / swim-idle / crouch idle / crouch
293
+ walk clips by state (`examples/studio/play.js`, `examples/common/locomotion.js`). Hold Shift to run;
294
+ tap X or the Crouch button to toggle crouch when grounded (WASD moves while
295
+ crouched).
296
+ - **Voice lip sync** — the Voice tab taps the microphone (Web Audio
297
+ `AnalyserNode`), measures audio RMS per frame, and drives the `mFaceJaw`
298
+ bone open/closed with an attack/release envelope (`voice.js`), with
299
+ Sensitivity and Max-open controls and a live level meter.
300
+ - **Shape sliders** — the RC3 body is fitted mesh: it is weighted to the
301
+ collision-volume bones (`BELLY`, `BUTT`, `CHEST`, `LEFT_PEC`, ...) that the
302
+ appearance sliders drive. Each slider scales/offsets a set of bones (see
303
+ `sliders.js`), the same mechanism used for fitted mesh bodies. Sliders
304
+ work live during animation.
305
+ - **BVH animation** — pick a bundled clip or load any `.bvh` file. Joint names
306
+ in both Poser style (`hip`, `abdomen`, `lShldr`, ... incl. bento fingers)
307
+ and CMU/MotionBuilder style (`Hips`, `LeftArm`, ...) are retargeted onto the
308
+ avatar skeleton (`bvh.js`).
309
+ - **BVH editor** — "Create BVH" (Animation tab) enters an authoring mode:
310
+ joint markers on the skeleton (blue = FK rotate gizmo, green = two-bone IK
311
+ drag for wrists/ankles, toggleable), a keyframe timeline at the bottom with
312
+ scrubbing, retimable keys and looped preview, and Save BVH exports an
313
+ Ruth-style `.bvh` (hip `Xpos Ypos Zpos Zrot Xrot Yrot`, joints `ZXY`, inches)
314
+ that round-trips through the loader (`examples/studio/animEditor.js`).
315
+ - **MCP control** — controlling the avatar over [MCP](https://modelcontextprotocol.io)
316
+ now lives in its own self-contained example, **`examples/mcp/`** (own
317
+ `package.json` + server). An unauthenticated MCP server at `/mcp` forwards each
318
+ tool call over a WebSocket to the open avatar page, which runs it and replies.
319
+ Tools include `trigger_animation`, `get_avatar_transform`, `capture_screenshot`,
320
+ `set_avatar_blink`, `set_avatar_look_at`, and `avatar_text_to_speech`. Every
321
+ avatar-touching tool takes an optional **`avatar`** selector (0-based index or
322
+ id); `list_avatars`, `select_avatar`, and `add_avatar` manage the set. See the
323
+ Examples section above.
324
+ - **Multiple avatars** — the panel's top bar (`Avatars 1 2 … + ✕`) spawns and
325
+ removes avatars and switches which one is *active*. The whole panel (shape,
326
+ materials, physics, blink, attachments, animation, play/editor) drives the
327
+ active avatar; each avatar keeps its own state, and all of them animate at
328
+ once. Backed by `avatarManager.js` over independent `Avatar` instances.
329
+
330
+ ## Files
331
+
332
+ **Library (repo root)**
333
+
334
+ | file | role |
335
+ |---|---|
336
+ | `index.js` | **library entry point** — exports `Avatar` + supporting pieces |
337
+ | `Avatar.js` | the `Avatar` class: loads the rigged parts and composes every per-avatar capability |
338
+ | `avatarManager.js` | tracks the live avatars in a scene + which one is active |
339
+ | `sliders.js` | slider definitions → bone scale/offset adjustments |
340
+ | `bvh.js` | BVH loading + retargeting to the avatar skeleton |
341
+ | `gltfAnim.js` | glTF/GLB animation loading + retargeting (cached per file) |
342
+ | `physics.js` | pec/glute soft-body: jiggle spring + kinematic gravity sag |
343
+ | `pbr.js` | per-region PBR map stack: channel compositing + layers |
344
+ | `voice.js` | mic-driven lip sync (jaw open from audio RMS) |
345
+ | `speech.js` | TTS-clip lip sync (per-viseme mouth shaping) |
346
+ | `blink.js` | procedural eye blinking |
347
+ | `attachments.js` | props parented to bones (built-in + GLB) |
348
+ | `examples/common/locomotion.js` | app-level `Locomotion` helper: walk/run/turn/jump/flight + state machine (example content, not a library export) |
349
+ | `skeleton.js` | maps each bone to the part that owns it; rest read/reset helpers |
350
+
351
+ **Apps (`examples/`)**
352
+
353
+ | file | role |
354
+ |---|---|
355
+ | `examples/studio/` | the full lab UI — `index.html` + `main.js` (scene/UI wiring) + `animEditor.js` (BVH pose editor) + `play.js` (keyboard play mode) |
356
+ | `examples/simple/` | minimal standalone usage (CDN three + local avatar; bundles its own `playMode.js`) |
357
+ | `examples/mcp/` | self-contained MCP example: its own `package.json` + server, bridge, browser glue (`avatarApi.js`, `mcpClient.js`) |
358
+
359
+ **Assets**
360
+
361
+ | file | role |
362
+ |---|---|
363
+ | `models/*.glb` | avatar meshes — glTF conversions of the RuthAndRoth/Ruth Collada originals, still **AGPL-3.0** (derivative works); bundled with the library |
364
+ | `examples/common/registry.js` | demo animation manifest (`ANIMATION_REGISTRY`) shared by the examples — **not** part of the published library |
365
+ | `anims/pirouette.bvh` | test clip from the [three.js examples](https://github.com/mrdoob/three.js/blob/master/examples/models/bvh/pirouette.bvh) (Poser-style joint names) |
366
+ | `anims/UAL1_Standard.glb` | [Quaternius UAL1](https://quaternius.com/packs/ultimateanimatedcharacter.html) locomotion + action clips (CC0) |
367
+ | `anims/UAL2_Standard.glb` | [Quaternius UAL2](https://quaternius.com/packs/universalanimationlibrary2.html) extended action set (CC0) |
368
+
369
+ ## Implementation notes
370
+
371
+ - The glTF part exports (converted from Ruth's Collada) keep the rig's Z-up,
372
+ **pure-translation joints** (no bone roll). Their node transforms do *not*
373
+ match the skin bind pose, so each skeleton's rest is recovered from the
374
+ inverse-bind matrices after loading; the parts are exported Z-up, so the load
375
+ also rotates each part −90°X to stand it up in three.js's Y-up world.
376
+ - Each part carries its own copy of (a subset of) the avatar skeleton. Rather
377
+ than re-binding everything to one skeleton, all part skeletons are driven in
378
+ sync by bone name (animation tracks are filtered per part).
379
+ - BVH rigs are Y-up facing +Z, arms on ±X (Poser convention); the avatar rig is
380
+ Z-up facing +X, arms on ±Y. Retargeting conjugates every joint quaternion by
381
+ that axis change (`Rz(90°)·Rx(90°)`) and rescales the hip translation from
382
+ BVH units to meters using the pelvis rest height.
383
+ - The MCP server holds no scene state — the browser tab is the source of truth,
384
+ and only one tab connects at a time. Bone rotations set via the MCP tools are
385
+ written in bone-local space and synced by name across each part's copy
386
+ of the skeleton (`syncBoneToAllParts` in `skeleton.js`), the same
387
+ drive-by-name mechanism the animation playback uses.
388
+
389
+ ## Licenses
390
+
391
+ This project's code and bundled `models/textures/` maps are **MIT** — see
392
+ [LICENSE.md](LICENSE.md). The bundled `models/*.glb` meshes are **AGPL-3.0**
393
+ (RuthAndRoth contributors) — they're glTF conversions of Ruth's original Collada
394
+ meshes and remain AGPL as derivative works. Bundled animations are **CC0**
395
+ (Quaternius UAL1/UAL2) or from
396
+ the **three.js** examples / **CMU mocap** (`pirouette.bvh`). See
397
+ [ASSET_LICENSES.md](ASSET_LICENSES.md) and [`licenses/`](licenses/) for
398
+ provenance and upstream license texts.
399
+
400
+ **Note on the npm `license` field.** `package.json` declares **`MIT`** — that
401
+ covers this project's own authored code and textures. The package *also* bundles
402
+ third-party assets under their own licenses (the AGPL Ruth meshes, plus
403
+ CC0 / three.js / CMU animation clips); those are documented per-file in
404
+ [ASSET_LICENSES.md](ASSET_LICENSES.md), with the full texts in
405
+ [`licenses/`](licenses/). In particular the **AGPL obligations attach only to the
406
+ bundled mesh files**, and only when you redistribute them — if you supply your
407
+ own avatar mesh, nothing in your use is AGPL-encumbered.
Binary file
Binary file