@lovelace_lol/polymer 0.0.6 → 0.0.8
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 +434 -12
- package/dist/index.d.ts +1945 -244
- package/dist/index.js +1686 -1017
- package/dist/vision/face-worker.js +296 -0
- package/dist/vision/face_landmarker.task +0 -0
- package/dist/vision/manifest.edn +1 -0
- package/dist/vision/vision_bundle.js +2 -0
- package/dist/vision/wasm/vision_wasm_internal.js +8841 -0
- package/dist/vision/wasm/vision_wasm_internal.wasm +0 -0
- package/dist/vision/wasm/vision_wasm_module_internal.js +8837 -0
- package/dist/vision/wasm/vision_wasm_module_internal.wasm +0 -0
- package/dist/vision/wasm/vision_wasm_nosimd_internal.js +8832 -0
- package/dist/vision/wasm/vision_wasm_nosimd_internal.wasm +0 -0
- package/dist/worker/agency-worker.js +1276 -962
- package/dist/worker/manifest.edn +1 -1
- package/package.json +33 -20
- package/dist/test.cjs +0 -1519
- package/shadow-cljs.edn +0 -173
- package/src/polymer/animation/agency.cljs +0 -84
- package/src/polymer/animation/domain.cljs +0 -110
- package/src/polymer/animation/planner.cljs +0 -88
- package/src/polymer/animation/runtime.cljs +0 -137
- package/src/polymer/animation/scheduler.cljs +0 -178
- package/src/polymer/animation/service.cljs +0 -981
- package/src/polymer/animation/state.cljs +0 -112
- package/src/polymer/blink/agency.cljs +0 -131
- package/src/polymer/blink/planner.cljs +0 -115
- package/src/polymer/blink/scheduler.cljs +0 -90
- package/src/polymer/blink/snippet.cljs +0 -50
- package/src/polymer/blink/state.cljs +0 -102
- package/src/polymer/camera_context/agency.cljs +0 -132
- package/src/polymer/camera_context/domain.cljs +0 -186
- package/src/polymer/camera_context/planner.cljs +0 -50
- package/src/polymer/camera_context/scheduler.cljs +0 -113
- package/src/polymer/camera_context/state.cljs +0 -121
- package/src/polymer/character.cljs +0 -403
- package/src/polymer/conversation/agency.cljs +0 -83
- package/src/polymer/conversation/domain.cljs +0 -35
- package/src/polymer/conversation/effector.cljs +0 -130
- package/src/polymer/conversation/planner.cljs +0 -178
- package/src/polymer/conversation/scheduler.cljs +0 -202
- package/src/polymer/conversation/service.cljs +0 -329
- package/src/polymer/conversation/state.cljs +0 -186
- package/src/polymer/core.cljs +0 -172
- package/src/polymer/eye_head/agency.cljs +0 -75
- package/src/polymer/eye_head/domain.cljs +0 -133
- package/src/polymer/eye_head/goap.cljs +0 -106
- package/src/polymer/eye_head/scheduler.cljs +0 -176
- package/src/polymer/eye_head/service.cljs +0 -259
- package/src/polymer/eye_head/snippet.cljs +0 -92
- package/src/polymer/eye_head/state.cljs +0 -167
- package/src/polymer/gaze/agency.cljs +0 -85
- package/src/polymer/gaze/domain.cljs +0 -211
- package/src/polymer/gaze/goap.cljs +0 -208
- package/src/polymer/gaze/scheduler.cljs +0 -184
- package/src/polymer/gaze/state.cljs +0 -202
- package/src/polymer/gesture/agency.cljs +0 -185
- package/src/polymer/gesture/goap.cljs +0 -461
- package/src/polymer/gesture/scheduler.cljs +0 -122
- package/src/polymer/gesture/snippet.cljs +0 -198
- package/src/polymer/gesture/state.cljs +0 -273
- package/src/polymer/lipsync/agency.cljs +0 -593
- package/src/polymer/lipsync/articulation/jaw.cljs +0 -258
- package/src/polymer/lipsync/articulation/snippet.cljs +0 -605
- package/src/polymer/lipsync/articulation/tongue.cljs +0 -418
- package/src/polymer/lipsync/articulation/visemes.cljs +0 -429
- package/src/polymer/lipsync/goap.cljs +0 -132
- package/src/polymer/lipsync/pronunciation/effector.cljs +0 -51
- package/src/polymer/lipsync/scheduler.cljs +0 -302
- package/src/polymer/lipsync/state.cljs +0 -202
- package/src/polymer/nlp/phonemes.cljs +0 -164
- package/src/polymer/prosodic/agency.cljs +0 -196
- package/src/polymer/prosodic/goap.cljs +0 -122
- package/src/polymer/prosodic/scheduler.cljs +0 -99
- package/src/polymer/prosodic/snippet.cljs +0 -106
- package/src/polymer/prosodic/state.cljs +0 -139
- package/src/polymer/runtime/embody.cljs +0 -117
- package/src/polymer/runtime/embody_wasm.cljs +0 -177
- package/src/polymer/runtime/rust_embody_host.cljs +0 -647
- package/src/polymer/society.cljs +0 -253
- package/src/polymer/stream.cljs +0 -130
- package/src/polymer/transcription/agency.cljs +0 -69
- package/src/polymer/transcription/domain.cljs +0 -68
- package/src/polymer/transcription/effector.cljs +0 -611
- package/src/polymer/transcription/planner.cljs +0 -203
- package/src/polymer/transcription/scheduler.cljs +0 -225
- package/src/polymer/transcription/service.cljs +0 -209
- package/src/polymer/transcription/state.cljs +0 -239
- package/src/polymer/tts/agency.cljs +0 -503
- package/src/polymer/tts/azure.cljs +0 -290
- package/src/polymer/tts/planner.cljs +0 -178
- package/src/polymer/tts/runtime.cljs +0 -624
- package/src/polymer/tts/scheduler.cljs +0 -162
- package/src/polymer/tts/state.cljs +0 -191
- package/src/polymer/worker.cljs +0 -81
package/README.md
CHANGED
|
@@ -1,8 +1,47 @@
|
|
|
1
1
|
# Polymer
|
|
2
2
|
|
|
3
|
+
Character runtime hosts expose `getViseme(index)` and `getVisemeJawScale(index)`
|
|
4
|
+
to snapshot direct mouth-shape controls before a temporary preview. These are
|
|
5
|
+
read-only views of Embody's canonical state, not the current mixer pose. Unset,
|
|
6
|
+
cleared, or out-of-range slots return `0` and `1`. Restore a snapshot with
|
|
7
|
+
`setViseme(index, value, jawScale)`; restoring zero releases the direct override
|
|
8
|
+
and allows any underlying clip to keep playing.
|
|
9
|
+
|
|
3
10
|
Clean ClojureScript character agency package for Character Loom.
|
|
4
11
|
|
|
5
|
-
|
|
12
|
+
Git stores Polymer source only. CI builds and tests each commit once. Verified
|
|
13
|
+
`main` builds publish versioned packages to npm for production; PRs and requested
|
|
14
|
+
preview SHAs publish compiled packages to [pkg.pr.new](https://pkg.pr.new/). Install the
|
|
15
|
+
[pkg-pr-new GitHub App](https://github.com/apps/pkg-pr-new) on this repository.
|
|
16
|
+
Production consumers pin an exact npm version. Work testing an upstream PR pins
|
|
17
|
+
the immutable full-SHA preview URL emitted by CI. Git-SHA installs are unsupported
|
|
18
|
+
because compiled JavaScript is not checked in.
|
|
19
|
+
|
|
20
|
+
Polymer consumes the public npm release `@lovelace_lol/embody@0.0.8`, built
|
|
21
|
+
from Embody main source commit
|
|
22
|
+
`a55e89edbb22b26059a63ce002a2c3e25caf0cf0`:
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
@lovelace_lol/embody@0.0.8
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
This release retains the renderer lifecycle support and speech AU 103 in
|
|
29
|
+
the existing `JAW.pitch.aus` list in CC4's `compositeRotations`. LipSync can send
|
|
30
|
+
the AU through Animation and use its saved bone and morph outputs.
|
|
31
|
+
|
|
32
|
+
The CC4 body-control catalog places hip bend, twist and side bend in **Whole
|
|
33
|
+
body**, separate from the waist, spine and chest controls in **Torso**. These
|
|
34
|
+
existing animation controls rotate the hip bone, which carries both torso and
|
|
35
|
+
legs. They differ from import/setup rotation, which rotates the entire model
|
|
36
|
+
object and saves its orientation as `modelRotation`. Hosts using
|
|
37
|
+
`getBodyControls` inherit the catalog labels and sections unless a profile
|
|
38
|
+
explicitly overrides them. Action IDs and bone motion stay unchanged.
|
|
39
|
+
|
|
40
|
+
The character host restores `profile.meshes` visibility and material settings
|
|
41
|
+
through Embody when binding a model or applying `setProfile`. Explicit hidden
|
|
42
|
+
meshes, zero opacity, disabled depth flags, and render order survive a saved
|
|
43
|
+
profile round trip. Hosts supply the selected character's profile; they do not
|
|
44
|
+
need a second mesh-settings replay path.
|
|
6
45
|
|
|
7
46
|
Polymer is intentionally separate from Latticework and Polyester. It is built as
|
|
8
47
|
a Society of Mind agency system: small CLJS agencies collaborate through streams,
|
|
@@ -11,6 +50,52 @@ responsible for performing them.
|
|
|
11
50
|
|
|
12
51
|
See [docs/agency-architecture.md](docs/agency-architecture.md) for the agency
|
|
13
52
|
collaboration, scheduler, side-effect, stream, and runtime-target rules.
|
|
53
|
+
The Rust host's live-control release and mixer ownership contract is documented
|
|
54
|
+
in [docs/live-control-ownership.md](docs/live-control-ownership.md).
|
|
55
|
+
|
|
56
|
+
For gaze tracking, head following takes the full target turn from reference neutral,
|
|
57
|
+
bounded by the profile's head AU limits;
|
|
58
|
+
camera-facing preference requires `lockHeadToCamera: true`. With geometry-backed focus, live eye/head strength
|
|
59
|
+
changes move continuously toward the latest value in [0, 1], including when a
|
|
60
|
+
slider reverses direction. See the [eye-contact contract](docs/gaze-tracking-contract.md)
|
|
61
|
+
for geometry, timing, channel ownership, and authored motion behavior.
|
|
62
|
+
|
|
63
|
+
## Camera and annotation runtime
|
|
64
|
+
|
|
65
|
+
Polymer directly re-exports Embody's Three.js camera and annotation runtime.
|
|
66
|
+
`DPthreeCameraController`, `DPthree3DMarkers`, `DPthreeHTMLMarkers`, and the
|
|
67
|
+
`DPthree` convenience wrapper are the same constructors provided by
|
|
68
|
+
`@lovelace_lol/embody/three`. Embody owns marker creation, surface projection,
|
|
69
|
+
rendering, camera transitions, visibility timing, and teardown. Polymer does not
|
|
70
|
+
maintain a second implementation or introduce an annotation agency.
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
import { DPthree } from '@lovelace_lol/polymer';
|
|
74
|
+
|
|
75
|
+
const annotations = new DPthree({ scene, camera, domElement: canvas });
|
|
76
|
+
annotations.setModel(model);
|
|
77
|
+
await annotations.loadCharacter(characterConfig);
|
|
78
|
+
await annotations.focus('head');
|
|
79
|
+
// Call annotations.update() from the host's existing render loop.
|
|
80
|
+
// Call annotations.dispose() when its scene is destroyed.
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Hosts own editor UI, persistence, scene attachment, and external input wiring.
|
|
84
|
+
Use `createMarkerVisibilityLifecycle` for automatic reveal/hide and
|
|
85
|
+
`createRuntimeAnnotationPreviewLifecycle` for transient editor previews rather
|
|
86
|
+
than implementing host timers. TypeScript declarations are forwarded from
|
|
87
|
+
Embody so runtime classes and their public signatures stay aligned. The package
|
|
88
|
+
contract checks constructor identity against the pinned Embody preview.
|
|
89
|
+
|
|
90
|
+
For a newly attached model, await the existing camera controller's
|
|
91
|
+
`prepareModelForRender(model, { signal })` before reporting render readiness.
|
|
92
|
+
Embody coordinates compilation and a full-scene preparation draw with its
|
|
93
|
+
existing loop; Polymer forwards the method without another rendering scheduler.
|
|
94
|
+
See [model render readiness](docs/character-host-readiness.md#model-render-readiness).
|
|
95
|
+
|
|
96
|
+
Pure [unscored questionnaire content contracts](docs/questionnaire-content.md)
|
|
97
|
+
cover authored choice/text validation, participant projection and editor metadata.
|
|
98
|
+
They do not yet provide a Questionnaire runtime agency or participant UI.
|
|
14
99
|
|
|
15
100
|
## Agency API
|
|
16
101
|
|
|
@@ -50,7 +135,7 @@ schedulers.
|
|
|
50
135
|
|
|
51
136
|
Polymer currently includes early Blink, Animation, Gaze, Eye/Head Tracking,
|
|
52
137
|
TTS, LipSync, Prosodic Expression, and Gesture agencies, plus first slices for
|
|
53
|
-
Conversation, Camera Context, and Transcription. These are implementation
|
|
138
|
+
Conversation, Camera Context, User Perception, and Transcription. These are implementation
|
|
54
139
|
milestones, not the limits of the architecture. Each agency should follow the
|
|
55
140
|
same local GOAP/planner, scheduler, stream, transform, and effector pattern as
|
|
56
141
|
the package grows.
|
|
@@ -58,6 +143,68 @@ the package grows.
|
|
|
58
143
|
The text-only LipSync pronunciation fallback and its accuracy limits are
|
|
59
144
|
documented in [`docs/lipsync-pronunciation-fallback.md`](docs/lipsync-pronunciation-fallback.md).
|
|
60
145
|
|
|
146
|
+
The five supported live Prosodic settings, revision receipts, safe decision
|
|
147
|
+
projection, and routing-disable lifecycle are documented in
|
|
148
|
+
[`docs/prosodic-configuration-pilot.md`](docs/prosodic-configuration-pilot.md).
|
|
149
|
+
This bounded pilot preserves the other eleven agencies and does not implement a
|
|
150
|
+
global configuration controller or captured-session replay.
|
|
151
|
+
|
|
152
|
+
Gesture, Gaze, and Camera Context also expose portable definitions and
|
|
153
|
+
agency-local revisioned settings under Action, Cognition, and Perception
|
|
154
|
+
namespaces. Their supported consumers, apply boundaries, and safe diagnostic
|
|
155
|
+
projections are documented in
|
|
156
|
+
[`docs/agency-configuration-families.md`](docs/agency-configuration-families.md).
|
|
157
|
+
Public agency IDs and constructors remain unchanged.
|
|
158
|
+
|
|
159
|
+
The opt-in [Gaze Lab POC](docs/gaze-attention-poc.md) retains static candidates,
|
|
160
|
+
uses actual routed Conversation credit or explicitly synthetic fixture influence,
|
|
161
|
+
and measures final-goal optical alignment through Animation. It is a bounded
|
|
162
|
+
feedback demonstration, not general moving-peer arbitration or learning; normal
|
|
163
|
+
mouse/webcam behavior remains unchanged.
|
|
164
|
+
|
|
165
|
+
User Perception's camera, worker, model assets, compact fact contract, and
|
|
166
|
+
privacy boundary are documented in
|
|
167
|
+
[`docs/user-perception.md`](docs/user-perception.md).
|
|
168
|
+
|
|
169
|
+
LipSync receives rig articulation from the resolved Embody preset/profile.
|
|
170
|
+
After `initEmbodyCore()`, call `resolveVisemeProfile(runtime.getProfile())` once
|
|
171
|
+
when binding the character and after each profile change. The synchronous result
|
|
172
|
+
contains `visemeSlots` in exact runtime order (missing order is zero; ties keep
|
|
173
|
+
source order), weighted/numeric binding targets, resolved jaw amounts and tongue
|
|
174
|
+
AU targets. Use its array indices for manual `setViseme(index, value)` preview and
|
|
175
|
+
configure LipSync with `{ visemeProfile: descriptor }`. This is a host profile
|
|
176
|
+
projection boundary, not work to repeat every frame. A live descriptor change
|
|
177
|
+
rebuilds the current clip at its current audio position. Explicitly configure this
|
|
178
|
+
field after every runtime `setProfile`, even when the descriptor is unchanged:
|
|
179
|
+
profile rebinding can clear animation actions. Polymer re-emits the active clip
|
|
180
|
+
and seeks to the current audio position without restarting audio.
|
|
181
|
+
|
|
182
|
+
For custom slots, Azure speech matches `providerIds.azure`; generated English
|
|
183
|
+
text matches `phonemes` using CMU/ARPABET symbols with optional stress digits.
|
|
184
|
+
External timelines can carry `provider` and `providerId`, or a `phoneme`.
|
|
185
|
+
A timeline containing only canonical `visemeId` needs an explicit
|
|
186
|
+
`providerIds.canonical` route in a custom profile. An unknown, ambiguous, or
|
|
187
|
+
unbound route retains neutral timing rather than selecting a guessed slot.
|
|
188
|
+
Azure zero reaches a declared zero route; otherwise it is neutral. Custom slots
|
|
189
|
+
can exceed 15: their model indices never become AU or jaw control IDs.
|
|
190
|
+
|
|
191
|
+
The descriptor overrides both `visemeJawAmounts` and `visemeTongueTargets` so
|
|
192
|
+
articulation uses the same runtime slot order. Without a descriptor, the existing
|
|
193
|
+
canonical 15-slot behavior and separately supplied tables remain supported.
|
|
194
|
+
Keys-only profiles and the unchanged stock CC4 routing metadata also
|
|
195
|
+
retain that behavior, including stock Azure refinement/diphthong rules. The
|
|
196
|
+
stock metadata contains overlapping aliases, so replacing its routing metadata
|
|
197
|
+
opts into explicit matching rather than silently changing legacy speech. Reordering
|
|
198
|
+
all stock slots preserves canonical sound semantics through `canonicalSlotIndices`,
|
|
199
|
+
which maps canonical sounds to their new runtime positions.
|
|
200
|
+
|
|
201
|
+
Resolution describes authored bindings, not successful deformation: a referenced
|
|
202
|
+
morph can still be missing from the loaded model. Polymer does not infer a VRM
|
|
203
|
+
loader, a complete system recipe, or target correctness from a slot's name.
|
|
204
|
+
Weighted targets are resolved by Embody, and manual and speech channels use that
|
|
205
|
+
same binding. Polymer owns timing, grouping, and curve planning; the profile owns
|
|
206
|
+
jaw apertures and tongue poses. With no profile jaw amounts, no jaw track is authored.
|
|
207
|
+
|
|
61
208
|
Runtime-specific work belongs at the edge. The current animation path can target
|
|
62
209
|
the existing web runtime, but Polymer core should remain able to support other
|
|
63
210
|
runtime targets such as Babylon, Unity, Godot, robotics, or future animation
|
|
@@ -134,10 +281,45 @@ The host supplies the gesture library and may observe streams for diagnostics,
|
|
|
134
281
|
but Gesture's own scheduler is responsible for replacement, cancellation, and
|
|
135
282
|
completion cleanup before Animation owns the runtime side effect.
|
|
136
283
|
|
|
284
|
+
### Animation snippet looping
|
|
285
|
+
|
|
286
|
+
Animation snippets default to repeating when no loop preference is supplied.
|
|
287
|
+
Authored `mixerLoopMode` (`repeat`, `pingpong`, or `once`) takes precedence over
|
|
288
|
+
the snippet's `loop` boolean. A `scheduleSnippet` command's `options.loopMode`
|
|
289
|
+
or `options.loop` overrides that authored preference for the scheduled play;
|
|
290
|
+
an explicit options mode takes precedence over the options boolean.
|
|
291
|
+
The compatibility service's `schedule` method accepts the same options.
|
|
292
|
+
|
|
293
|
+
Animation uses that effective loop policy for the runtime, schedule snapshots,
|
|
294
|
+
and compatibility UI state. Repeating clips stay scheduled until removed or
|
|
295
|
+
stopped, including when a one-shot is switched to repeat before its original
|
|
296
|
+
completion callback resolves. Explicit one-shots retain completion cleanup.
|
|
297
|
+
|
|
137
298
|
## Character host (Embody scene + model loading)
|
|
138
299
|
|
|
139
|
-
Polymer
|
|
140
|
-
up a character without depending on Embody
|
|
300
|
+
Polymer composes Embody's scene/model utilities with its animation host so
|
|
301
|
+
LoomLarge and other hosts can set up a character without depending on Embody
|
|
302
|
+
directly. Owned scenes await initial renderer preparation before the host
|
|
303
|
+
resolves. The async default is `auto`: Embody tries native WebGPU and releases
|
|
304
|
+
a failed initialization attempt before preparing WebGL. Later scene-preparation
|
|
305
|
+
errors remain errors. Use `scene: { renderer: 'webgl' }` to
|
|
306
|
+
force WebGL or `'webgpu'` to require native WebGPU without fallback. The synchronous scene
|
|
307
|
+
factory stays WebGL-only; `createDefaultCharacterSceneAsync` exposes the
|
|
308
|
+
scene-only Promise API. Three and its declarations use the coordinated r184
|
|
309
|
+
contract.
|
|
310
|
+
|
|
311
|
+
The new `createDefaultCharacterSceneRuntime` export and `host.rendering`
|
|
312
|
+
forward Embody's renderer-settings controller. It owns switching and recovery;
|
|
313
|
+
Polymer keeps the scene, camera, model, and animation engine intact and exposes
|
|
314
|
+
current renderer/backend getters. This source integration awaits an Embody
|
|
315
|
+
preview with the new controller API; the existing published pin is retained.
|
|
316
|
+
See [renderer settings and replacement](docs/character-host-readiness.md#renderer-settings-and-replacement)
|
|
317
|
+
for subscriptions, capture leases, and ownership.
|
|
318
|
+
|
|
319
|
+
Pass `signal` to cancel host creation cooperatively, then call `host.dispose()`
|
|
320
|
+
for the resolved host's lifetime. Borrowed external scenes remain caller-owned.
|
|
321
|
+
See [readiness and cleanup semantics](docs/character-host-readiness.md), including
|
|
322
|
+
which GPU/model stages are outside this readiness contract.
|
|
141
323
|
|
|
142
324
|
Default path — pass a container element and a character URL:
|
|
143
325
|
|
|
@@ -210,19 +392,221 @@ if (supportsRustEmbodyPreset('cc4')) {
|
|
|
210
392
|
|
|
211
393
|
The CLJS host owns the JavaScript API and render-loop bridge. Rust owns preset
|
|
212
394
|
lookup, profile merge and intake, compiled bindings, transitions, and packed
|
|
213
|
-
frame evaluation.
|
|
395
|
+
frame evaluation. Rust selects active procedural channels. The host preserves the underlying
|
|
396
|
+
renderer values while applying those packed writes, so releasing live controls
|
|
397
|
+
reveals paused or unchanged mixer samples. Host mixers own clip playback.
|
|
398
|
+
Unsupported presets must stay on the compatibility host until
|
|
214
399
|
they are explicitly embedded; they are never treated as CC4 aliases.
|
|
215
400
|
|
|
216
|
-
|
|
401
|
+
Authoring controls can read procedural state with `engine.getAU(id)`,
|
|
402
|
+
`engine.getAUBalance(id)` (left/right balance in `-1..1`), and
|
|
403
|
+
`engine.getContinuum(negativeId, positiveId)`. These read Rust's current values,
|
|
404
|
+
including neutral values after `clear()` or `setProfile()`, so a remounted
|
|
405
|
+
control can resume the current pose without maintaining a second state cache.
|
|
406
|
+
|
|
407
|
+
### Semantic humanoid skeleton
|
|
408
|
+
|
|
409
|
+
`getHumanoidSpecification()` exposes Embody's canonical VRM 1.0 catalog: all
|
|
410
|
+
55 bone roles, the 15 required roles, and parent/optional-parent requirements.
|
|
411
|
+
Call it after `initEmbodyCore()`, like the other synchronous profile queries.
|
|
412
|
+
`resolveHumanoidCharacterization(profile)` resolves authored role aliases;
|
|
413
|
+
`validateHumanoidCharacterization(model, profile)` additionally checks the
|
|
414
|
+
actual model hierarchy, unique assignments, and supported transform constraints.
|
|
415
|
+
|
|
416
|
+
`setHumanoidRoleBinding(profile, role, boneName)` returns a new profile that
|
|
417
|
+
assigns an actual model bone; pass `null` to clear a role. Persist the returned
|
|
418
|
+
characterization as a complete replacement, together with changed bone aliases,
|
|
419
|
+
action/composite mappings, and continuum pairs, then reconfigure the runtime. Authored catalogs
|
|
420
|
+
remain authoritative across preset upgrades. Clearing a role makes its semantic
|
|
421
|
+
bone outputs dormant while preserving the rest of the profile.
|
|
422
|
+
|
|
423
|
+
The CC4 preset supplies a complete role map and controls across torso, head,
|
|
424
|
+
arms, hands/fingers, legs, and feet/toes. Bound UI controls use
|
|
425
|
+
`engine.getBodyControls()` to read descriptors cached from the configured Rust
|
|
426
|
+
profile/model, avoiding scene traversal and repeated profile parsing. The standalone
|
|
427
|
+
`getBodyControls(profile, model)` query remains available for unbound authoring.
|
|
428
|
+
`engine.resetBodyControls()` clears the configured catalog's action values and
|
|
429
|
+
balances in one Rust call and applies one frame, preserving unrelated facial actions
|
|
430
|
+
and morph-strength settings. Head, eye, and jaw controls
|
|
431
|
+
share existing facial actuators; all controls use the existing AU/continuum
|
|
432
|
+
host methods. Applications should show the full role inventory even when a
|
|
433
|
+
model omits optional bones, and allow users to repair its assignments.
|
|
434
|
+
|
|
435
|
+
Every configured Body action, including each direction of a signed control,
|
|
436
|
+
can drive authored left/right/center morph mappings. Morph strength scales
|
|
437
|
+
those outputs even when the action has no bone bindings. Persist the action's
|
|
438
|
+
`auToMorphs` and `auMixDefaults`, and retain its mesh category: head, eye, and
|
|
439
|
+
jaw controls may share facial actions with different target meshes. The package
|
|
440
|
+
contract tests exercise all six sections through the actual Rust-backed host,
|
|
441
|
+
including mesh selection, balance, zero strength, and mapping removal.
|
|
442
|
+
An explicitly configured mesh list restricts these Body actions to that list;
|
|
443
|
+
an empty list disables their morph outputs rather than selecting other meshes.
|
|
444
|
+
After a temporary direct morph preview, call `engine.releaseMorph(key, meshes)`
|
|
445
|
+
to let current AU/viseme/clip output resume. Setting a morph to zero is still an
|
|
446
|
+
explicit override and intentionally continues to suppress those other sources.
|
|
447
|
+
|
|
448
|
+
`setAUMorphTargets(profile, auId, side, targets)` edits one action side's
|
|
449
|
+
morph targets without removing the same targets from other actions. Sides are
|
|
450
|
+
`left`, `right`, or `center`; targets are names or numeric morph indices. The
|
|
451
|
+
returned profile includes the complete edited AU row, with other sides preserved
|
|
452
|
+
and explicit empty arrays retained across preset reloads. Hosts can persist just
|
|
453
|
+
that row as an override and reconfigure the runtime.
|
|
454
|
+
|
|
455
|
+
Head and eye semantic controls use the existing FACS AU IDs, mappings, balances,
|
|
456
|
+
and morph-strength settings. CC4 neck controls seed the corresponding head tissue
|
|
457
|
+
morphs and ratios while keeping separate neck bone actions and profile overrides.
|
|
458
|
+
Neck morphs use the face mesh route; limb muscle morphs use their authored route.
|
|
459
|
+
`auMixDefaults` scales morph deformation without reducing bone movement. Editors
|
|
460
|
+
should expose these settings even when configured morph targets are missing, so
|
|
461
|
+
users can repair the assignment or choose another model.
|
|
462
|
+
|
|
463
|
+
VRM defines anatomical roles and hierarchy. Motion axes/ranges and muscle morphs
|
|
464
|
+
remain authored rig data. Editing an unmapped import does not silently give it
|
|
465
|
+
the CC4 preset. Polymer provides this compatibility API as a thin Rust adapter;
|
|
466
|
+
it adds no second role catalog, pose evaluator, or character state cache.
|
|
467
|
+
Use `engine.getAU(id)` and `engine.getAUBalance(id)` together when saving a live
|
|
468
|
+
AU pose. The balance reader returns Rust's current control value in `[-1, 1]`
|
|
469
|
+
(`-1` left, `0` both sides, `1` right), or `0` for an unset or cleared AU.
|
|
470
|
+
Save that value in the snippet's `snippetBalanceMap` to preserve sided playback
|
|
471
|
+
through AnimationService. These readers report live control state, not the
|
|
472
|
+
sampled pose of a playing mixer clip.
|
|
473
|
+
|
|
474
|
+
### Stable character reference poses
|
|
475
|
+
|
|
476
|
+
The first `onReady` captures the model's loaded transforms and morph weights,
|
|
477
|
+
before Polymer applies the profile or starts playback. `setProfile`, `bindModel`,
|
|
478
|
+
and subsequent `onReady` calls for that model reuse the capture. Animation
|
|
479
|
+
intensity is measured from that same reference. Live inherited animation starts
|
|
480
|
+
still sample the rendered pose when a clip is materialized.
|
|
481
|
+
|
|
482
|
+
Call `onReady` while a newly loaded model is intentionally in its reference pose.
|
|
483
|
+
This does not infer a skin bind pose or use animation frame zero. If the same
|
|
484
|
+
model survives engine recreation, retain an explicit capture with the model:
|
|
217
485
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
486
|
+
```js
|
|
487
|
+
import { captureModelReferencePose } from '@lovelace_lol/polymer';
|
|
488
|
+
const referencePose = captureModelReferencePose(model); // before playback/edits
|
|
489
|
+
engine.onReady({ model, referencePose });
|
|
490
|
+
// Pass referencePose again when creating a replacement engine for this model.
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
Intentional skeleton additions must append new children without renaming,
|
|
494
|
+
removing, or reordering existing nodes. After insertion, call
|
|
495
|
+
`engine.extendReferencePose()` before applying new profile mappings. This returns
|
|
496
|
+
a new immutable capture that includes the added nodes and preserves every old
|
|
497
|
+
reference transform; retain it if the model will outlive its engine. Other
|
|
498
|
+
hierarchy edits fail explicitly before the active profile or playback is cleared.
|
|
499
|
+
Ordinary profile, transform, material, and morph-target edits need no recapture.
|
|
500
|
+
|
|
501
|
+
Baked-animation pause and resume also publish the current runtime playhead, so
|
|
502
|
+
the paused timeline remains at the displayed pose instead of reverting to an old
|
|
503
|
+
time. These are discrete state updates; playback progress stays in the runtime.
|
|
504
|
+
|
|
505
|
+
### Optional baked animation assets
|
|
506
|
+
|
|
507
|
+
Use `engine.loadAnimationClips(initialClips)` during initialization. After the
|
|
508
|
+
model is bound, `engine.registerAnimationClips([clip])` adds a downloaded clip
|
|
509
|
+
without starting it, resetting the mixer, or interrupting native, snippet, or
|
|
510
|
+
hair playback. Re-registering the same clip object does no serialization work.
|
|
511
|
+
A new source under an existing name replaces the next playback; the current
|
|
512
|
+
action continues until playback is explicitly requested. Registered sources
|
|
513
|
+
survive model rebinding, and `removeAnimationClip(name)` also removes them from
|
|
514
|
+
the sources retained for rebinding. Treat registered source objects as immutable.
|
|
515
|
+
|
|
516
|
+
The Animation agency's existing `loadBakedClips` command accepts lightweight
|
|
517
|
+
`{name, duration, channels}` catalog entries before optional tracks are loaded.
|
|
518
|
+
Hosts can expose that catalog immediately, fetch and register a selected asset,
|
|
519
|
+
then request `playBaked` through Animation. Registration is asset setup; it does
|
|
520
|
+
not introduce an additional scheduler or playback owner.
|
|
521
|
+
|
|
522
|
+
## Package Distribution Contract
|
|
523
|
+
|
|
524
|
+
Only source and the hand-authored `types/index.d.ts` declarations are committed.
|
|
525
|
+
`pnpm build` recreates `dist`, builds the npm and worker targets once, and copies
|
|
526
|
+
the declarations into that staging directory. `pnpm verify` checks the
|
|
527
|
+
source-only rule, validates the runtime exports and packed file list, and runs
|
|
528
|
+
the CLJS and worker tests.
|
|
221
529
|
|
|
222
530
|
The package intentionally has no `prepare`, `install`, or `postinstall` build
|
|
223
|
-
lifecycle.
|
|
224
|
-
|
|
225
|
-
build
|
|
531
|
+
lifecycle. Consumers install an already-built npm release or a pkg.pr.new preview
|
|
532
|
+
for a specific commit and therefore do not need Java, Shadow CLJS, or a local
|
|
533
|
+
Polymer build. For work testing an upstream PR, use the full URL reported by CI:
|
|
534
|
+
|
|
535
|
+
```text
|
|
536
|
+
https://pkg.pr.new/meekmachine/Polymer/@lovelace_lol/polymer@<40-character-sha>
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
On pushes to `main`, CI prepares a stable npm version and validates the **committed**
|
|
540
|
+
Embody dependency before the single build. The source manifest must already pin
|
|
541
|
+
an exact stable Embody version, with a synchronized lockfile. CI reads public npm
|
|
542
|
+
metadata anonymously and checks that the selected release records a commit in
|
|
543
|
+
Embody's `main` history. It rejects preview URLs anywhere in the production
|
|
544
|
+
lockfile. Publication never replaces a preview with whatever npm calls `latest`: adopting a release is
|
|
545
|
+
an ordinary dependency PR, so the dependency tested by PR CI is the one shipped.
|
|
546
|
+
The existing preview pin remains until a fresh Embody npm release is available;
|
|
547
|
+
stable publication is intentionally blocked during that bootstrap.
|
|
548
|
+
|
|
549
|
+
After all checks pass, CI packs the verified output and uploads it to a separate
|
|
550
|
+
publish job using the main-only `npm` environment's `NPM_KEY`. That job publishes
|
|
551
|
+
the same tarball with public access and provenance disabled, without building
|
|
552
|
+
again. The tarball must also declare public access. GitHub source access remains
|
|
553
|
+
separate from distribution of the public compiled npm package.
|
|
554
|
+
|
|
555
|
+
Package metadata and dependency installation use the public registry without a
|
|
556
|
+
read token. Registry lookups also stay anonymous when a publishing credential is
|
|
557
|
+
present in the environment, and reject redirects to another registry endpoint.
|
|
558
|
+
Only the publish step receives `NPM_KEY`; PR installs and checks receive no npm
|
|
559
|
+
credentials. Same-repository PRs can publish previews; fork PRs can run checks
|
|
560
|
+
without receiving publication or coordination write credentials.
|
|
561
|
+
|
|
562
|
+
The package allowlist remains `dist` and `README.md`, alongside npm's package
|
|
563
|
+
metadata. The existing CI package checks require staged runtime files and reject
|
|
564
|
+
repository source/test inputs. Build output, source maps, minification and
|
|
565
|
+
obfuscation are unchanged by this release setup.
|
|
566
|
+
|
|
567
|
+
Successful stable and same-repository PR preview publication dispatch
|
|
568
|
+
`upstream-package-published` to LoomLarge. The write credential
|
|
569
|
+
`CROSS_REPO_CI_TOKEN` belongs in the main-only `release-coordination` environment;
|
|
570
|
+
it must read Embody for release ancestry and dispatch to LoomLarge. PR jobs only
|
|
571
|
+
upload notification JSON and never receive this credential. The separate
|
|
572
|
+
`workflow_run` consumer checks out **main**, validates the completed build job,
|
|
573
|
+
the JSON package identity, and the current open same-repository PR head, then
|
|
574
|
+
sends the event. It never executes a PR script or downloaded artifact. The build
|
|
575
|
+
job must succeed even when the independent production-readiness job rejects a
|
|
576
|
+
preview dependency. During migration the consumer accepts both the former
|
|
577
|
+
`CI and package preview` name and `CI and package publishing`, while requiring
|
|
578
|
+
the exact CI workflow path. Missing credentials or failed dispatch fail visibly.
|
|
579
|
+
|
|
580
|
+
Stable events include the exact version and source SHA; preview events include
|
|
581
|
+
the PR number and actual published URL, expanded to its full source SHA and
|
|
582
|
+
checked before dispatch. LoomLarge's coordinator owns downstream manifest/lockfile
|
|
583
|
+
PR updates; release-system companion PR links alone do not create package-stack
|
|
584
|
+
dependencies. The coordinator and trusted notification consumer must be on their
|
|
585
|
+
respective main branches before activation. Explicit preview requests without a
|
|
586
|
+
PR publish previews but do not imply a downstream stack to update.
|
|
587
|
+
|
|
588
|
+
Release versions and source commits are recorded in npm; rerunning a published
|
|
589
|
+
commit retains its version and exact Embody dependency. If a concurrent publisher
|
|
590
|
+
claims that source/version with different runtime dependencies, publication fails
|
|
591
|
+
instead of sending a success notification for a package the job did not verify.
|
|
592
|
+
Main release runs are serialized. A manual run on `main` with no alternate ref can retry publication
|
|
593
|
+
and notification; PRs, repository-dispatch preview requests, and alternate refs
|
|
594
|
+
cannot publish stable npm packages.
|
|
595
|
+
|
|
596
|
+
The first restored release reads Polymer `0.0.7`'s source from the verified
|
|
597
|
+
canonical `v0.0.7` tag because that legacy npm version has no `gitHead` metadata.
|
|
598
|
+
The tag must still point to `73feb34f8afa5a36962e86bc0f3e91f9d91205ef`, and the
|
|
599
|
+
new release must contain that commit. Subsequent releases use npm's recorded
|
|
600
|
+
`gitHead`; missing or conflicting source information stops publication.
|
|
601
|
+
|
|
602
|
+
The separate `Production npm dependency` job rejects preview pins and verifies
|
|
603
|
+
the committed Embody registry version before merge. A trusted main-only job checks
|
|
604
|
+
Embody main ancestry before publication. PR build jobs can still publish their
|
|
605
|
+
immutable previews independently. Bootstrap requires publishing and coordinator
|
|
606
|
+
credentials, an Embody main release, adoption of that actual version in Polymer's
|
|
607
|
+
manifest and lockfile, Polymer's main release, then adoption in LoomLarge.
|
|
608
|
+
Upstream feature stacks keep using immutable previews before this release sequence. Production hosts adopt
|
|
609
|
+
actual released versions in dependency PRs whose CI checks the selected packages.
|
|
226
610
|
|
|
227
611
|
## Worker Protocol Artifact
|
|
228
612
|
|
|
@@ -238,3 +622,41 @@ not use this artifact yet, and this PR does not decide which agencies should be
|
|
|
238
622
|
placed in workers. Selective placement, profiling, transferables, and worker
|
|
239
623
|
pool decisions remain tracked in
|
|
240
624
|
[issue #62](https://github.com/meekmachine/Polymer/issues/62).
|
|
625
|
+
|
|
626
|
+
For generator modules bound to a shared character, see
|
|
627
|
+
[Conversation sessions and interruption](docs/conversation-interruption.md).
|
|
628
|
+
|
|
629
|
+
Presentation and Questionnaire share pure lifecycle, viewed/active context,
|
|
630
|
+
request correlation and opaque checkpoint helpers. See
|
|
631
|
+
[shared interaction contracts](docs/interaction-contracts.md) for their module
|
|
632
|
+
composition interface, account/mount identity and scoped cleanup rules. These
|
|
633
|
+
contracts do not enable a feature runtime or persist conversation/run data.
|
|
634
|
+
|
|
635
|
+
### Semantic pose capture and generated animations
|
|
636
|
+
|
|
637
|
+
The Rust host exposes `getSemanticPoseCatalog()`, `captureSemanticPose()`,
|
|
638
|
+
`validateSemanticPose(pose)` and `applySemanticPose(pose)`. The catalog follows
|
|
639
|
+
the configured profile and actual model, including independent direction support,
|
|
640
|
+
VRM role names, owned bone/morph names and authored default tissue strengths.
|
|
641
|
+
Captures preserve both opposing directions, bilateral balance and morph strength;
|
|
642
|
+
they represent manual semantic state, not inverse inference of a baked pose.
|
|
643
|
+
Validation is atomic and lives in Embody. Known missing outputs may be saved for
|
|
644
|
+
partial rigs; generation should offer only supported catalog directions.
|
|
645
|
+
|
|
646
|
+
Normal `AnimationService.schedule` snippets may include `semanticAnimation`:
|
|
647
|
+
`{version:1,durationSeconds,tracks:[{controlId,direction,balance?,morphStrength?,
|
|
648
|
+
keyframes:[{time,intensity}]}]}`. Existing `curves` are non-Body facial AUs.
|
|
649
|
+
Embody compiles both sets together into bone/morph tracks, preserving each tissue
|
|
650
|
+
strength without changing live controls. Service save, pause, resume and cleanup
|
|
651
|
+
use the existing snippet lifecycle. `validateSemanticAnimation(animation, options)`
|
|
652
|
+
compiles without retaining clips. `semanticAnimationToClip(name, animation,
|
|
653
|
+
options)` builds an ordinary playback handle; `options.faceCurves` adds facial
|
|
654
|
+
curves for direct callers. IDs and direction labels come from the catalog, amounts
|
|
655
|
+
are normalized `0..1`, balance is character-relative `-1` left / `+1` right, and
|
|
656
|
+
times are seconds. Unknown controls, unsupported directions and invalid tracks fail.
|
|
657
|
+
|
|
658
|
+
Manual `setAU` / `transitionAU` selects one configured continuum direction:
|
|
659
|
+
nonzero input clears its opposite, negative input selects the opposite, and
|
|
660
|
+
zero releases only the named action. `setContinuum` controls both directions
|
|
661
|
+
explicitly. Body and individual AU/morph previews therefore share the same
|
|
662
|
+
live state; separate axes remain independent.
|