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.
- package/ASSET_LICENSES.md +122 -0
- package/Avatar.js +860 -0
- package/LICENSE.md +21 -0
- package/README.md +407 -0
- package/anims/UAL1_Standard.glb +0 -0
- package/anims/UAL2_Standard.glb +0 -0
- package/anims/pirouette.bvh +867 -0
- package/attachments.js +388 -0
- package/avatarManager.js +110 -0
- package/blink.js +58 -0
- package/bvh.js +110 -0
- package/gltfAnim.js +271 -0
- package/index.js +61 -0
- package/licenses/AGPL-3.0.txt +661 -0
- package/models/body.glb +0 -0
- package/models/eyes.glb +0 -0
- package/models/feet.glb +0 -0
- package/models/hands.glb +0 -0
- package/models/head.glb +0 -0
- package/models/textures/android_face.png +0 -0
- package/models/textures/android_face_ao.jpg +0 -0
- package/models/textures/android_face_metallic.jpg +0 -0
- package/models/textures/android_face_normal.jpg +0 -0
- package/models/textures/android_face_roughness.jpg +0 -0
- package/models/textures/android_lower.png +0 -0
- package/models/textures/android_lower_ao.jpg +0 -0
- package/models/textures/android_lower_metallic.jpg +0 -0
- package/models/textures/android_lower_normal.jpg +0 -0
- package/models/textures/android_lower_roughness.jpg +0 -0
- package/models/textures/android_upper.png +0 -0
- package/models/textures/android_upper_ao.jpg +0 -0
- package/models/textures/android_upper_metallic.jpg +0 -0
- package/models/textures/android_upper_normal.jpg +0 -0
- package/models/textures/android_upper_roughness.jpg +0 -0
- package/models/textures/blue_eyes.png +0 -0
- package/models/textures/layers/cute_pants.png +0 -0
- package/models/textures/layers/cute_shirt.png +0 -0
- package/nipple.js +218 -0
- package/package.json +48 -0
- package/pbr.js +225 -0
- package/physics.js +313 -0
- package/skeleton.js +70 -0
- package/sliders.js +590 -0
- package/speech.js +130 -0
- package/visemes.js +66 -0
- 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
|