cozyclay 2.1.1 → 3.0.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/CHANGELOG.md +60 -0
- package/README.md +12 -30
- package/bin/agent/agent-routes.mjs +40 -113
- package/bin/agent/agent-runner.mjs +9 -10
- package/bin/agent/cliproxy-provider.mjs +135 -0
- package/bin/agent/credential-store.mjs +30 -7
- package/bin/agent/pi-tools.mjs +4 -1
- package/bin/agent/provider-keys.mjs +32 -0
- package/bin/agent/provider-login.mjs +110 -0
- package/bin/agent/provider-routes.mjs +99 -0
- package/bin/agent/providers.mjs +19 -97
- package/bin/agent/script-tool.mjs +17 -4
- package/bin/agent/studio-motion-reference.mjs +23 -0
- package/bin/agent/studio-prompt.mjs +1 -1
- package/bin/agent/studio-rebase-text.mjs +32 -0
- package/bin/agent/studio-rebase.mjs +26 -39
- package/bin/agent/studio-tools.mjs +9 -28
- package/bin/agent/tool-arguments.mjs +49 -0
- package/bin/agent/turn-frames.mjs +66 -0
- package/bin/codex-auth.mjs +9 -2
- package/bin/cozyclay.mjs +10 -116
- package/bin/live/cli.mjs +3 -3
- package/bin/mcp-runtime.mjs +2 -12
- package/bin/package-signature.mjs +0 -4
- package/dist/app/index.html +7 -6
- package/dist/assets/EXRLoader-CvVIvGVE.js +1 -0
- package/dist/assets/analytics-C9sUnndd.js +1 -0
- package/dist/assets/app-CCh--1Hc.js +5087 -0
- package/dist/assets/app-f-fkV5uP.css +1 -0
- package/dist/assets/compile-worker-CkxiD6lH.js +5 -0
- package/dist/assets/fflate.module-KbGj5-H6.js +5 -0
- package/dist/assets/{first-shot-handoff-msLI71g1.js → first-shot-handoff-CC4xe4HM.js} +1 -1
- package/dist/assets/{landing-ChUvB_zL.js → landing-DjHD8u7k.js} +1 -1
- package/dist/assets/scene-asset-sync-Hx_FzdOH.js +20 -0
- package/dist/assets/scene-asset-sync-_S8NQl5B.css +1 -0
- package/dist/assets/workflow-C4yKH1p4.js +179 -0
- package/dist/cozyclay-package.json +1 -1
- package/dist/index.html +3 -3
- package/dist/sw.js +3 -6
- package/dist/workflow/index.html +5 -4
- package/mcp/LIVE-PROTOCOL.md +7 -47
- package/mcp/README.md +16 -26
- package/mcp/live-hub.mjs +6 -10
- package/mcp/package.json +1 -3
- package/mcp/tool-handlers.mjs +4 -168
- package/package.json +3 -22
- package/src/App.jsx +150 -2624
- package/src/agent-progress.js +79 -0
- package/src/agent-turn-mark.js +30 -0
- package/src/analytics.js +1 -19
- package/src/app-context.js +5 -0
- package/src/app-stage.jsx +2 -34
- package/src/asset-pane.jsx +6 -5
- package/src/camera-block.js +21 -0
- package/src/camera-direction.js +120 -0
- package/src/character-kind.js +3 -9
- package/src/command-bus.js +8 -6
- package/src/commands/cast.js +5 -34
- package/src/commands/elements/character.js +1 -1
- package/src/commands/elements/motion.js +1 -6
- package/src/commands/elements/object.js +6 -2
- package/src/commands/elements/shot.js +4 -1
- package/src/commands/elements.js +38 -14
- package/src/commands/index.js +3 -1
- package/src/commands/kits.js +37 -0
- package/src/commands/motion-timing.js +57 -0
- package/src/commands/motion.js +9 -152
- package/src/commands/motion3-compile.js +65 -0
- package/src/commands/motion3.js +207 -0
- package/src/commands/objects.js +6 -4
- package/src/commands/shot.js +49 -11
- package/src/commands/stage.js +14 -1
- package/src/commands/view.js +4 -1
- package/src/document-mirrors.js +42 -0
- package/src/document-store.js +4 -0
- package/src/domains/cast.js +19 -194
- package/src/domains/motion/motion-range-pin-rules.js +1 -1
- package/src/domains/motion/take-source.js +51 -0
- package/src/domains/motion.js +60 -1838
- package/src/domains/scenes.js +12 -55
- package/src/domains/shots.js +2 -2
- package/src/domains/stage.js +4 -2
- package/src/draft-ghost.js +46 -0
- package/src/draft-ghost.jsx +129 -0
- package/src/dualview.jsx +4 -3
- package/src/effects/EffectsLayer.jsx +112 -0
- package/src/effects/effect-meshes.js +232 -0
- package/src/effects/effect-record.js +103 -0
- package/src/effects/emitter.js +59 -0
- package/src/effects/simulate.js +261 -0
- package/src/effects/state.js +59 -0
- package/src/execution-telemetry.js +2 -3
- package/src/first-success-guide.jsx +5 -5
- package/src/ghost-set.js +257 -0
- package/src/held-object-edit.jsx +113 -0
- package/src/held-object-pose.js +175 -0
- package/src/hierarchy-model.js +1 -1
- package/src/hierarchy-panel.jsx +1 -1
- package/src/imported-mesh.jsx +103 -0
- package/src/kits/held.js +84 -0
- package/src/kits/index.js +70 -0
- package/src/kits/smoking.js +57 -0
- package/src/light-blockers.js +93 -0
- package/src/motion/beat-board-model.js +84 -0
- package/src/motion/beat-board.css +124 -0
- package/src/motion/beat-board.jsx +59 -0
- package/src/motion/center-of-mass.js +66 -0
- package/src/motion/fix-collisions.js +850 -0
- package/src/motion/physics-review.js +118 -0
- package/src/{ardy → motion}/physics-support.js +1 -76
- package/src/motion/rig-snapshot.js +22 -0
- package/src/motion/take-segments.jsx +140 -0
- package/src/{ardy → motion}/timeline-coordinates.js +0 -21
- package/src/{ardy → motion}/timeline.css +1 -2
- package/src/{ardy → motion}/timeline.jsx +42 -413
- package/src/motion/waypoints.js +76 -0
- package/src/motion3/beats.js +83 -0
- package/src/motion3/body.js +158 -0
- package/src/motion3/bones.js +47 -0
- package/src/motion3/carried.js +47 -0
- package/src/motion3/compile-job.js +75 -0
- package/src/motion3/compile-worker.js +10 -0
- package/src/motion3/compiler/anchors.js +105 -0
- package/src/motion3/compiler/arm-orient.js +128 -0
- package/src/motion3/compiler/channels.js +125 -0
- package/src/motion3/compiler/compile.js +205 -0
- package/src/motion3/compiler/gait-rhythm.js +97 -0
- package/src/motion3/compiler/grip.js +171 -0
- package/src/motion3/compiler/head-steady.js +48 -0
- package/src/motion3/compiler/hold.js +134 -0
- package/src/motion3/compiler/idle.js +113 -0
- package/src/motion3/compiler/limbs.js +170 -0
- package/src/motion3/compiler/math.js +72 -0
- package/src/motion3/compiler/pose.js +162 -0
- package/src/motion3/compiler/quat.js +49 -0
- package/src/motion3/compiler/skeleton.js +74 -0
- package/src/motion3/compiler/validate-common.js +32 -0
- package/src/motion3/compiler/validate-goals.js +118 -0
- package/src/motion3/compiler/validate-objects.js +143 -0
- package/src/motion3/compiler/validate-parts.js +116 -0
- package/src/motion3/compiler/validate.js +122 -0
- package/src/motion3/compiler/vocab.js +73 -0
- package/src/motion3/compiler/walk-pelvis.js +275 -0
- package/src/motion3/compiler/walk.js +207 -0
- package/src/motion3/compiler/wrist-clamp.js +32 -0
- package/src/motion3/delta.js +47 -0
- package/src/motion3/describe.js +174 -0
- package/src/motion3/docs.js +27 -0
- package/src/motion3/edit-carry.js +21 -0
- package/src/motion3/fix/ease.js +16 -0
- package/src/motion3/fix/ground.js +37 -0
- package/src/motion3/fix/index.js +52 -0
- package/src/motion3/fix/push.js +71 -0
- package/src/motion3/fix/reach.js +46 -0
- package/src/motion3/fix/retime.js +79 -0
- package/src/motion3/fix/self.js +211 -0
- package/src/motion3/fix/slide.js +70 -0
- package/src/motion3/fix/turn.js +51 -0
- package/src/motion3/follow.js +39 -0
- package/src/motion3/free-motion.js +173 -0
- package/src/motion3/grip-choice.js +130 -0
- package/src/motion3/hold-plan.js +157 -0
- package/src/motion3/json-patch.js +77 -0
- package/src/motion3/key-aim.js +105 -0
- package/src/motion3/key-edit.js +82 -0
- package/src/motion3/lenient.js +127 -0
- package/src/motion3/object-keys.js +236 -0
- package/src/motion3/object-pose.js +182 -0
- package/src/motion3/object-tracks.js +12 -0
- package/src/motion3/pipeline.js +102 -0
- package/src/motion3/playback.js +51 -0
- package/src/motion3/probes.js +91 -0
- package/src/motion3/qa/body-checks.js +34 -0
- package/src/motion3/qa/check-names.js +14 -0
- package/src/motion3/qa/checks.js +121 -0
- package/src/motion3/qa/contact-checks.js +51 -0
- package/src/motion3/qa/explain.js +80 -0
- package/src/motion3/qa/fmt.js +5 -0
- package/src/motion3/qa/foot-checks.js +72 -0
- package/src/motion3/qa/geometry.js +77 -0
- package/src/motion3/qa/goals.js +191 -0
- package/src/motion3/qa/ground.js +60 -0
- package/src/motion3/qa/hints-contact.js +179 -0
- package/src/motion3/qa/hints-goal.js +160 -0
- package/src/motion3/qa/hints-knee.js +24 -0
- package/src/motion3/qa/hints-motion.js +209 -0
- package/src/motion3/qa/hints-object.js +91 -0
- package/src/motion3/qa/index.js +42 -0
- package/src/motion3/qa/key-checks.js +82 -0
- package/src/motion3/qa/near.js +96 -0
- package/src/motion3/qa/object-checks.js +135 -0
- package/src/motion3/qa/patch.js +161 -0
- package/src/motion3/qa/severity.js +38 -0
- package/src/motion3/qa/spec-checks.js +65 -0
- package/src/motion3/qa/style.js +218 -0
- package/src/motion3/qa/wrist-checks.js +69 -0
- package/src/motion3/qa/wrist-obs.js +60 -0
- package/src/motion3/refs.js +120 -0
- package/src/motion3/repair-tune.js +121 -0
- package/src/motion3/repair.js +83 -0
- package/src/motion3/report.js +67 -0
- package/src/motion3/rigs/index.js +14 -0
- package/src/motion3/rigs/x-bot-tpose-body.json +1 -0
- package/src/motion3/rigs/x-bot-tpose.json +1 -0
- package/src/motion3/rigs/y-bot-tpose-body.json +1 -0
- package/src/motion3/rigs/y-bot-tpose.json +1 -0
- package/src/motion3/scene.js +60 -0
- package/src/motion3/schema/brief.js +89 -0
- package/src/motion3/schema/fields.js +44 -0
- package/src/motion3/schema/spec-examples.js +46 -0
- package/src/motion3/schema/topics.js +111 -0
- package/src/motion3/schema/types.js +147 -0
- package/src/motion3/search.js +117 -0
- package/src/motion3/second-hand.js +112 -0
- package/src/motion3/skills/basic.js +82 -0
- package/src/motion3/skills/book.js +76 -0
- package/src/motion3/skills/car.js +145 -0
- package/src/motion3/skills/frames-auto.js +29 -0
- package/src/motion3/skills/index.js +83 -0
- package/src/motion3/skills/info.js +65 -0
- package/src/motion3/skills/smoke-kit.js +109 -0
- package/src/motion3/skills/smoke.js +160 -0
- package/src/motion3/spec-store.js +59 -0
- package/src/motion3/take-edit.js +155 -0
- package/src/motion3/take.js +63 -0
- package/src/motion3/timeline.js +127 -0
- package/src/motion3/views.js +121 -0
- package/src/motion3/walk-plan.js +119 -0
- package/src/object-catalog.jsx +7 -1
- package/src/object-colors.js +98 -0
- package/src/object-gizmo-parts.js +115 -0
- package/src/object-gizmo.jsx +37 -122
- package/src/object-glow.js +139 -0
- package/src/object-hidden.js +33 -0
- package/src/object-look.js +196 -0
- package/src/object-size.js +10 -0
- package/src/object-travel.js +71 -6
- package/src/panels/CameraPanel.jsx +7 -7
- package/src/panels/HdrEnvironmentControls.jsx +59 -0
- package/src/panels/LightPanel.jsx +25 -4
- package/src/panels/ObjectLookPanel.jsx +88 -0
- package/src/panels/PosePanel.jsx +2 -17
- package/src/panels/RigControlPanel.jsx +5 -53
- package/src/panels/SubjectBox.jsx +4 -1
- package/src/panels/details.css +14 -0
- package/src/posestudio.jsx +4 -27
- package/src/project-browser.jsx +3 -4
- package/src/project-resources.js +18 -31
- package/src/project.js +13 -104
- package/src/props.jsx +25 -280
- package/src/rail-dot-drag.js +23 -0
- package/src/render-passes.js +10 -0
- package/src/resource-status.jsx +1 -0
- package/src/result-modal.jsx +4 -19
- package/src/room.jsx +20 -12
- package/src/scene-assets.js +8 -6
- package/src/scene-light-place.js +72 -0
- package/src/scene-lights.jsx +148 -0
- package/src/scene-objects.js +31 -139
- package/src/scenes.js +15 -105
- package/src/semantic-edit.js +2 -5
- package/src/set-pieces.jsx +183 -0
- package/src/settings-menu.jsx +3 -11
- package/src/shell/BottomDock.jsx +9 -51
- package/src/shell/DetailsSlot.jsx +16 -200
- package/src/shell/GhostsMenuGroup.jsx +74 -0
- package/src/shell/LibrarySlot.jsx +1 -3
- package/src/shell/MenuBar.jsx +3 -3
- package/src/shell/PreferencesDialog.jsx +15 -264
- package/src/shell/PreferencesSlot.jsx +2 -8
- package/src/shell/TopBar.jsx +5 -84
- package/src/shell/ViewportToolbar.jsx +10 -70
- package/src/shell/agent-glass.css +19 -6
- package/src/shell/details-selection.js +42 -0
- package/src/shell/ghosts-menu.css +22 -0
- package/src/shell/glass-regions.css +29 -3
- package/src/shell/preferences.css +6 -1
- package/src/shot-authoring.js +4 -7
- package/src/soft-shadows.js +37 -0
- package/src/stage-attach-frames.js +12 -12
- package/src/stage-canvas-rigs.jsx +3 -3
- package/src/stage-capture.jsx +42 -8
- package/src/stage-characters.jsx +22 -216
- package/src/stage-editor-gizmos.jsx +6 -6
- package/src/stage-environment.js +93 -0
- package/src/stage-hdr-builtins.js +107 -0
- package/src/stage-hdr-image.js +116 -0
- package/src/stage-hdr-library.js +70 -0
- package/src/stage-hdr-map.js +94 -0
- package/src/stage-hdr.js +130 -0
- package/src/stage-lighting.js +149 -0
- package/src/stage-occlusion.js +210 -0
- package/src/stage-sky.jsx +159 -0
- package/src/stage-startup-defaults.js +5 -9
- package/src/studio-actions.js +12 -14
- package/src/studio-agent-commands.js +19 -16
- package/src/studio-agent-motion.js +9 -9
- package/src/studio-agent-protocol.js +41 -71
- package/src/studio-app-binding.js +25 -45
- package/src/studio-asset-list.js +17 -0
- package/src/studio-contact-sheet.js +37 -5
- package/src/studio-elements.js +27 -7
- package/src/studio-entity-bounds.js +18 -0
- package/src/studio-error-codes.js +8 -0
- package/src/studio-export-framing.js +152 -0
- package/src/studio-lighting-check.js +103 -0
- package/src/studio-motion-visual.js +150 -0
- package/src/studio-schema-errors.js +80 -0
- package/src/studio-support-gap.js +74 -0
- package/src/styles.css +1 -19
- package/src/timeline-defaults.js +5 -0
- package/src/timeline-extent.js +8 -20
- package/src/tutorial-walk.js +17 -0
- package/src/ui-scrub.js +1 -1
- package/src/use-shot-camera-tracks.js +12 -0
- package/src/view-ghosts.js +97 -0
- package/src/view-ghosts.jsx +49 -0
- package/src/workflow/AgentPanel.jsx +47 -166
- package/src/workflow/AgentProgressCard.jsx +107 -0
- package/src/workflow/AgentReplyText.jsx +37 -0
- package/src/workflow/AgentTurnSteps.jsx +84 -0
- package/src/workflow/LoginPanel.jsx +140 -0
- package/src/workflow/WorkflowBuilder.jsx +7 -23
- package/src/workflow/agent-client.js +54 -51
- package/src/workflow/agent-panel.css +71 -1
- package/src/workflow/agent-progress-card.css +167 -0
- package/src/workflow/agent-progress-view.js +47 -0
- package/src/workflow/agent-turn-steps.css +113 -0
- package/src/workflow/agent-turn.js +191 -0
- package/src/workflow/login-client.js +34 -0
- package/src/workflow/motion-input.js +18 -31
- package/src/workflow/node-schema.js +1 -1
- package/src/workflow/reply-markdown.js +69 -0
- package/src/workflow/scene-asset-sync.js +8 -8
- package/tools/agent-session-stats.mjs +61 -0
- package/tools/dev-full.mjs +5 -56
- package/tools/eslint-baseline.json +12 -13
- package/tools/knip-baseline.json +0 -179
- package/tools/loc-baseline.json +31 -81
- package/tools/motion3/extract-body.mjs +103 -0
- package/tools/motion3/extract-rig.mjs +85 -0
- package/tools/process-supervisor.mjs +0 -88
- package/tools/run-tests.mjs +8 -124
- package/bin/agent/motion-runtime.mjs +0 -76
- package/dist/ardy/cskel27-rest.json +0 -557
- package/dist/assets/analytics-C9pMpW7s.js +0 -1
- package/dist/assets/app-B_2XB9rw.css +0 -1
- package/dist/assets/app-DAuvKPP8.js +0 -4920
- package/dist/assets/config-Bx7pYsD9.js +0 -1
- package/dist/assets/config-DeoFTYzb.css +0 -1
- package/dist/assets/demo-CvKuBgy5.js +0 -1
- package/dist/assets/scene-asset-sync-DKkpjKBV.js +0 -19
- package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
- package/dist/assets/ticket-BPk2Ni95.js +0 -1
- package/dist/assets/workflow-jJRBR94Z.js +0 -179
- package/dist/demo/walk-then-stop.npz +0 -0
- package/mcp/ardy-prompts.mjs +0 -218
- package/src/ardy/auto-fix-panel.css +0 -108
- package/src/ardy/auto-physics.js +0 -1194
- package/src/ardy/client.js +0 -153
- package/src/ardy/convert.js +0 -313
- package/src/ardy/cskel27-neutral.js +0 -39
- package/src/ardy/cskel27.js +0 -77
- package/src/ardy/export.js +0 -165
- package/src/ardy/fix-collisions.js +0 -2257
- package/src/ardy/foot-lock.js +0 -230
- package/src/ardy/motion-calibration.js +0 -118
- package/src/ardy/motion-edit.js +0 -213
- package/src/ardy/motion-url.js +0 -55
- package/src/ardy/mp4-duration.js +0 -102
- package/src/ardy/npz.js +0 -595
- package/src/ardy/physics-panel.css +0 -21
- package/src/ardy/physics-panel.jsx +0 -89
- package/src/ardy/physics-refine.js +0 -111
- package/src/ardy/physics-review.js +0 -567
- package/src/ardy/physics-temporal.js +0 -54
- package/src/ardy/platform-fit-panel.css +0 -64
- package/src/ardy/platform-fit-panel.jsx +0 -41
- package/src/ardy/platform-fit.js +0 -326
- package/src/ardy/playback.js +0 -711
- package/src/ardy/pose-pin.js +0 -97
- package/src/ardy/prompt-clips.js +0 -30
- package/src/ardy/retime.js +0 -211
- package/src/ardy/root-drop.js +0 -369
- package/src/ardy/timeline-resize.js +0 -13
- package/src/ardy/to-cskel27.js +0 -225
- package/src/ardy/trim.js +0 -33
- package/src/ardy/waypoints.js +0 -369
- package/src/ardy-bridge-contract.js +0 -281
- package/src/domains/motion/motion-video-capture.js +0 -664
- package/src/i2v-motion-client.js +0 -77
- package/src/i2v-motion-studio.jsx +0 -180
- package/src/line-edit.js +0 -1348
- package/src/motion/generation.js +0 -105
- package/src/motion-readiness-ui.jsx +0 -67
- package/src/motion-readiness.js +0 -40
- package/src/motion-resources.js +0 -177
- package/src/motion-store.js +0 -65
- package/src/motion-trail.js +0 -601
- package/src/multimodel-ingest.js +0 -394
- package/src/panels/PromptBlocksPanel.jsx +0 -444
- package/src/panels/VideoCapturePanel.jsx +0 -181
- package/src/pose-extract/browser.js +0 -42
- package/src/pose-extract/detector.js +0 -100
- package/src/pose-extract/fit.js +0 -227
- package/src/pose-extract/image-frame.js +0 -51
- package/src/pose-extract/index.js +0 -58
- package/src/pose-extract/keyframes.js +0 -74
- package/src/pose-extract/landmarks.js +0 -162
- package/src/pose-extract/mirror.js +0 -153
- package/src/pose-extract/take.js +0 -131
- package/src/pose-extract/video-frames.js +0 -91
- package/src/take-recipe.js +0 -165
- package/src/trail-key-conflicts.js +0 -39
- package/src/trail-pick.js +0 -59
- package/src/use-i2v-capture-modal.js +0 -15
- package/tools/ardy/BRIDGE.md +0 -37
- package/tools/ardy/GVHMR-NPZ.md +0 -68
- package/tools/ardy/README.md +0 -124
- package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
- package/tools/ardy/artifacts.mjs +0 -34
- package/tools/ardy/bridge.mjs +0 -1732
- package/tools/ardy/bvh-cskel27.mjs +0 -1265
- package/tools/ardy/cclay_constrained_generate.py +0 -561
- package/tools/ardy/cclay_gvhmr_worker.py +0 -264
- package/tools/ardy/cclay_motion_edit.py +0 -510
- package/tools/ardy/cclay_sequence_generate.py +0 -705
- package/tools/ardy/cclay_worker.py +0 -304
- package/tools/ardy/dump-npz.py +0 -205
- package/tools/ardy/extract-rest.mjs +0 -299
- package/tools/ardy/extract.mjs +0 -507
- package/tools/ardy/footage.mjs +0 -467
- package/tools/ardy/gvhmr-floor.mjs +0 -102
- package/tools/ardy/gvhmr-to-cskel27.mjs +0 -21
- package/tools/ardy/gvhmr_fastpath.py +0 -168
- package/tools/ardy/gvhmr_trajectory.py +0 -240
- package/tools/ardy/merge-text-encoder.py +0 -160
- package/tools/ardy/mocap-metrics.mjs +0 -200
- package/tools/ardy/mocap-quality-gate.mjs +0 -110
- package/tools/ardy/motion-stabilize.mjs +0 -348
- package/tools/ardy/npz.mjs +0 -414
- package/tools/ardy/pose-to-npz.mjs +0 -106
- package/tools/ardy/prompt-limits.mjs +0 -5
- package/tools/ardy/run-edit-on-box.sh +0 -73
- package/tools/ardy/run-local.mjs +0 -233
- package/tools/ardy/run-on-box.sh +0 -593
- package/tools/ardy/run-sequence-on-box.sh +0 -177
- package/tools/ardy/runners/gvhmr-worker.mjs +0 -206
- package/tools/ardy/runners/index.mjs +0 -18
- package/tools/ardy/runners/local.mjs +0 -455
- package/tools/ardy/runners/proc.mjs +0 -125
- package/tools/ardy/runners/remote.mjs +0 -252
- package/tools/ardy/setup-local.mjs +0 -189
- package/tools/ardy/setup-text-encoder-on-box.sh +0 -93
- package/tools/ardy/setup-text-encoder.py +0 -306
- package/tools/ardy/smpl-cskel27.mjs +0 -178
- package/tools/ardy/visual-qa.mjs +0 -196
- package/tools/ardy/vq-car.mjs +0 -52
- package/tools/bench/EXP3.md +0 -61
- package/tools/bench/cclay_bench_extract_incam.py +0 -125
- package/tools/bench/cclay_bench_extract_obs.py +0 -204
- package/tools/bench/cclay_bench_runner.py +0 -42
- package/tools/bench/cube-contact.mjs +0 -162
- package/tools/bench/exp3.mjs +0 -152
- package/tools/bench/extract-bench-lib.mjs +0 -216
- package/tools/bench/extract-bench.mjs +0 -302
- package/tools/bench/fal-generate.mjs +0 -88
- package/tools/bench/fit/README.md +0 -189
- package/tools/bench/fit/camera.mjs +0 -59
- package/tools/bench/fit/contact.mjs +0 -419
- package/tools/bench/fit/footlock.mjs +0 -234
- package/tools/bench/fit/motion.mjs +0 -85
- package/tools/bench/fit/pin.mjs +0 -27
- package/tools/bench/fit/remote.mjs +0 -105
- package/tools/bench/fit-bench.mjs +0 -126
- package/tools/bench/fit-sanity.mjs +0 -45
- package/tools/bench/metrics.mjs +0 -313
- package/tools/bench/obs/depth.mjs +0 -258
- package/tools/bench/obs/extrinsics.mjs +0 -175
- package/tools/bench/obs/fit_mannequin_betas.py +0 -340
- package/tools/bench/obs/ground.mjs +0 -294
- package/tools/bench/obs/heading.mjs +0 -151
- package/tools/bench/obs/ladder.mjs +0 -555
- package/tools/bench/obs/mannequin-betas.json +0 -124
- package/tools/bench/obs/remote.mjs +0 -83
- package/tools/bench/obs/rest_joints.py +0 -52
- package/tools/bench/obs/ybot-targets.mjs +0 -49
- package/tools/bench/obs-bench.mjs +0 -520
- package/tools/bench/score.mjs +0 -343
- package/tools/bench/summarize.mjs +0 -86
- package/tools/demo-worker/README.md +0 -137
- package/tools/demo-worker/api-client.mjs +0 -233
- package/tools/demo-worker/generate.mjs +0 -301
- package/tools/demo-worker/index.mjs +0 -466
- package/tools/demo-worker/sign-core.mjs +0 -112
- package/tools/demo-worker/sign.mjs +0 -161
- package/tools/gt-render/browser.mjs +0 -182
- package/tools/gt-render/camera-math.mjs +0 -276
- package/tools/gt-render/page.mjs +0 -267
- package/tools/gt-render/render.mjs +0 -514
- package/tools/gt-render/scene-box.mjs +0 -31
- package/tools/gt-render/take-transform.mjs +0 -87
- package/tools/kimodo/compare-seam.mjs +0 -108
- package/tools/kimodo/constraints.mjs +0 -244
- package/tools/kimodo/edit.mjs +0 -259
- package/tools/kimodo/effector-constraints.mjs +0 -200
- package/tools/kimodo/generate.mjs +0 -646
- package/tools/kimodo/local-output.mjs +0 -265
- package/tools/kimodo/measure-pose.mjs +0 -65
- package/tools/kimodo/measure-preserve.mjs +0 -899
- package/tools/kimodo/measure-waypoints.mjs +0 -55
- package/tools/kimodo/mlx-generate.py +0 -111
- package/tools/kimodo/pose-constraints.mjs +0 -248
- package/tools/kimodo/preserve-mask.mjs +0 -543
- package/tools/kimodo/read-npz.mjs +0 -189
- package/tools/kimodo/run-edit-on-box.mjs +0 -155
- package/tools/kimodo/run-sequence-on-box.mjs +0 -178
- package/tools/kimodo/runner.mjs +0 -284
- package/tools/kimodo/seam-batch.mjs +0 -77
- package/tools/kimodo/setup-local.mjs +0 -30
- package/tools/kimodo/setup-on-box.sh +0 -339
- package/tools/kimodo/soma77-to-cskel27.mjs +0 -305
- package/tools/morphgs/assets/gen_truth.py +0 -39
- package/tools/morphgs/assets/mesh_ori_rig.txt +0 -27
- package/tools/morphgs/assets/playback-check.mjs +0 -30
- package/tools/morphgs/demo-gate.sh +0 -26
- package/tools/morphgs/fbx2morphgs.mjs +0 -68
- package/tools/morphgs/morphgs-to-cskel27.mjs +0 -105
- package/tools/morphgs/patches/preprocess_src-none-mode.patch +0 -76
- package/tools/morphgs/setup-on-cluster.sh +0 -126
- package/tools/projflow/driver.py +0 -1069
- package/tools/projflow/generate.mjs +0 -805
- package/tools/projflow/hml22-to-cskel27.mjs +0 -735
- package/tools/projflow/line-edit-job.mjs +0 -547
- package/tools/projflow/replay.mjs +0 -452
- package/tools/projflow/runner.mjs +0 -269
- package/tools/projflow/service.mjs +0 -742
- package/tools/track/DESIGN.md +0 -513
- package/tools/track/backfill-provenance.mjs +0 -116
- package/tools/track/budget.mjs +0 -8
- package/tools/track/check-rig.mjs +0 -209
- package/tools/track/diagnostics.schema.json +0 -60
- package/tools/track/export-rig.mjs +0 -256
- package/tools/track/fallback.mjs +0 -7
- package/tools/track/fk-parity-fixture.mjs +0 -161
- package/tools/track/gate.mjs +0 -349
- package/tools/track/masks.mjs +0 -318
- package/tools/track/metrics.mjs +0 -201
- package/tools/track/publish-obs.mjs +0 -108
- package/tools/track/py/check_env.py +0 -61
- package/tools/track/py/eval_lr.py +0 -229
- package/tools/track/py/lr_viterbi.py +0 -294
- package/tools/track/py/masks.py +0 -405
- package/tools/track/py/objective.py +0 -382
- package/tools/track/py/rig.py +0 -508
- package/tools/track/py/scene.py +0 -326
- package/tools/track/py/test_joint_indices.py +0 -123
- package/tools/track/py/test_lr_viterbi.py +0 -195
- package/tools/track/py/test_masks.py +0 -229
- package/tools/track/py/test_rig.py +0 -288
- package/tools/track/py/test_scene.py +0 -354
- package/tools/track/py/test_track.py +0 -328
- package/tools/track/py/track.py +0 -411
- package/tools/track/remote.mjs +0 -140
- package/tools/track/rig-dump.mjs +0 -227
- package/tools/track/run-box-tests.mjs +0 -14
- package/tools/track/run-box.mjs +0 -168
- package/tools/track/setup-box.sh +0 -59
- package/tools/track/study-2d.mjs +0 -728
- /package/src/{ardy → motion}/collision-blockers.js +0 -0
- /package/src/{ardy → motion}/ground.js +0 -0
- /package/src/{ardy → motion}/ik-drag.js +0 -0
- /package/src/{ardy → motion}/ik-key-json.js +0 -0
- /package/src/{ardy → motion}/ik.js +0 -0
- /package/src/{ardy → motion}/physics-surface.js +0 -0
- /package/src/{ardy → motion}/playback-clock.js +0 -0
- /package/src/{ardy → motion}/range-pin.js +0 -0
package/src/ardy/playback.js
DELETED
|
@@ -1,711 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Drive a CozyClay rig from a decoded ARDY motion (see npz.js) with
|
|
3
|
-
* POSITIONAL SKINNING — the same method the viser demo's Mixamo avatar uses
|
|
4
|
-
* (interactive_demo/mixamo_avatar.py compute_bone_transforms):
|
|
5
|
-
*
|
|
6
|
-
* boneWorldPos[j] = posed[f][j] + R[f][j] @ (mixamoBind[j] - ardyNeutral[j])
|
|
7
|
-
* boneWorldQuat[j] = R[f][j] @ bindQuat[j]
|
|
8
|
-
*
|
|
9
|
-
* where R[f][j] is the ARDY joint's GLOBAL rotation (FK over local_rot_mats)
|
|
10
|
-
* and ardyNeutral is CoreSkeleton27's neutral pose (cskel27-neutral.js,
|
|
11
|
-
* exported from the box). The Mixamo bind offset is measured against the
|
|
12
|
-
* ARDY neutral joint, so no assumption about the rig's local rotation axes
|
|
13
|
-
* or rest-frame orientation is ever made — the failure mode of the old
|
|
14
|
-
* local-rotation retarget (basis = Rb^T @ L @ Rb), which rotated limbs
|
|
15
|
-
* around slightly wrong axes and let the proportion gap drift the feet off
|
|
16
|
-
* the npz's own contact points.
|
|
17
|
-
*
|
|
18
|
-
* Two deliberate deviations from the viser avatar, both scene-related:
|
|
19
|
-
*
|
|
20
|
-
* 1. Uniform scale s. The avatar assets are metre-scale like ARDY; the
|
|
21
|
-
* CozyClay rigs keep their own proportions instead of deforming onto
|
|
22
|
-
* the ARDY skeleton's. s = rig leg height / ARDY neutral leg height
|
|
23
|
-
* (measured per rig from the bind pose), so posed_joints map into rig
|
|
24
|
-
* units with feet planting exactly when the npz puts them on the floor.
|
|
25
|
-
* 2. Anchoring. The viser demo plays every clip at its own origin;
|
|
26
|
-
* CozyClay anchors the clip's root to the blocking (waypoint /
|
|
27
|
-
* Subject 1 position). The whole clip is shifted so the anchor frame's
|
|
28
|
-
* root sits at the rig origin — the Character group stays static at the
|
|
29
|
-
* scene anchor during playback (rotation still comes from the group's
|
|
30
|
-
* `rot`, the clip-to-scene yaw), and the full root trajectory, vertical
|
|
31
|
-
* included, now lives in the bones.
|
|
32
|
-
*
|
|
33
|
-
* Joint->bone mapping follows the viser avatar (_MIXAMO_TO_CORE): Mixamo's
|
|
34
|
-
* three spine bones map onto core Spine1/Spine2/Spine3 (core Spine and the
|
|
35
|
-
* HandEnd leaves drive no bone — their motion folds into the mapped joints'
|
|
36
|
-
* global transforms, and HeadTop_End/Toe_End/finger bones simply follow
|
|
37
|
-
* their parents). Bones the map does not touch keep their bind transforms.
|
|
38
|
-
* Shoulder and arm chains keep their Mixamo bind translations and consume
|
|
39
|
-
* ARDY rotations only, avoiding clavicle shear between incongruent rigs.
|
|
40
|
-
* Path-fix takes opt into a reference-pose correction: edited wrists retain
|
|
41
|
-
* their rendered orientation and edited legs retain their rendered lengths.
|
|
42
|
-
*/
|
|
43
|
-
|
|
44
|
-
import * as THREE from "three";
|
|
45
|
-
import { CSKEL27_JOINTS, CSKEL27_PARENTS } from "./cskel27.js";
|
|
46
|
-
import { CSKEL27_NEUTRAL } from "./cskel27-neutral.js";
|
|
47
|
-
import { globalRotations } from "./convert.js";
|
|
48
|
-
import { normalizeBoneName } from "../poses.js";
|
|
49
|
-
|
|
50
|
-
/** cskel27 joint -> Mixamo bone name, mirroring the viser avatar's
|
|
51
|
-
* _MIXAMO_TO_CORE (null = no bone is driven by this joint).
|
|
52
|
-
*
|
|
53
|
-
* Deliberate deviation: the wrist (Hand) and thumb root (HandThumb1) are
|
|
54
|
-
* NOT driven. The viser avatar collapses all hand skin weights rigidly, so
|
|
55
|
-
* ARDY's wrist orientation is invisible there; on the real finger rig it
|
|
56
|
-
* rotates the whole hand and presents the curled fingers from a different
|
|
57
|
-
* palm angle than the rest state (a constant convention offset, most
|
|
58
|
-
* visible as "fist on one side, flat hand on the other"). Riding the
|
|
59
|
-
* forearm at the bind-relative wrist keeps the hand's presentation exactly
|
|
60
|
-
* the natural relaxed hand at all times; the lost wrist flexion is a few
|
|
61
|
-
* degrees in these clips and reads worse than it helps. */
|
|
62
|
-
const SKINNING_MAP = CSKEL27_JOINTS.map((name) => {
|
|
63
|
-
switch (name) {
|
|
64
|
-
case "Spine":
|
|
65
|
-
case "RightHandEnd":
|
|
66
|
-
case "LeftHandEnd":
|
|
67
|
-
case "RightHand":
|
|
68
|
-
case "LeftHand":
|
|
69
|
-
case "RightHandThumb1":
|
|
70
|
-
case "LeftHandThumb1":
|
|
71
|
-
return null;
|
|
72
|
-
case "Spine1":
|
|
73
|
-
return "Spine";
|
|
74
|
-
case "Spine2":
|
|
75
|
-
return "Spine1";
|
|
76
|
-
case "Spine3":
|
|
77
|
-
return "Spine2";
|
|
78
|
-
default:
|
|
79
|
-
return name;
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
// CoreSkeleton27 and the Mixamo bots are not congruent around the clavicles:
|
|
84
|
-
// their neutral shoulder directions differ by about 30 degrees. Positional
|
|
85
|
-
// skinning therefore moves the arm root away from the direction encoded by
|
|
86
|
-
// the parent bone's rotation and visibly shears the shoulder. Keep the
|
|
87
|
-
// Mixamo arm chains' authored local translations and retarget rotations only;
|
|
88
|
-
// the rest of the body still uses ARDY positional skinning for root motion
|
|
89
|
-
// and foot placement.
|
|
90
|
-
export const HIERARCHY_PRESERVED_JOINTS = new Set([
|
|
91
|
-
"RightShoulder", "RightArm", "RightForeArm",
|
|
92
|
-
"LeftShoulder", "LeftArm", "LeftForeArm",
|
|
93
|
-
]);
|
|
94
|
-
|
|
95
|
-
export const ARDY_NEUTRAL_MIN_Y = -0.9544128; // toe depth under the hips-origin neutral pose
|
|
96
|
-
|
|
97
|
-
/* No finger forcing: ARDY's cskel27 stops at the wrist, and the fingers
|
|
98
|
-
* simply keep the rig's own bind pose at all times (the same presentation
|
|
99
|
-
* as the viser avatar, which has no finger articulation either). Nothing
|
|
100
|
-
* in this module writes finger bones. */
|
|
101
|
-
|
|
102
|
-
/* --- rig preparation ------------------------------------------------------ */
|
|
103
|
-
|
|
104
|
-
/** The project's bone-matching rule: normalised names equal, or one is a
|
|
105
|
-
* suffix of the other (`mixamorighips` vs bare `hips`). First depth-first
|
|
106
|
-
* match wins — Mixamo FBX nests an identity "skinned" copy of every bone
|
|
107
|
-
* under the control bone, and the nested copies are never written (same
|
|
108
|
-
* rule poses.js and export.js use). */
|
|
109
|
-
function findBone(rig, mixamoName) {
|
|
110
|
-
const target = normalizeBoneName(mixamoName);
|
|
111
|
-
let found = null;
|
|
112
|
-
rig.traverse((object) => {
|
|
113
|
-
if (found || !object.isBone) return;
|
|
114
|
-
const norm = normalizeBoneName(object.name);
|
|
115
|
-
if (norm === target || norm.endsWith(target)) found = object;
|
|
116
|
-
});
|
|
117
|
-
return found;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/** Bind-pose quaternions per bone, from the poseBind snapshot primed at clone
|
|
121
|
-
* time; a lazy fallback mirrors export.js's restOf() for rigs that never
|
|
122
|
-
* mounted Character. */
|
|
123
|
-
const bindFallback = new WeakMap();
|
|
124
|
-
|
|
125
|
-
function bindsOf(rig) {
|
|
126
|
-
const primed = rig.userData && rig.userData.poseBind;
|
|
127
|
-
if (primed) return primed;
|
|
128
|
-
let map = bindFallback.get(rig);
|
|
129
|
-
if (!map) {
|
|
130
|
-
map = new Map();
|
|
131
|
-
rig.traverse((object) => {
|
|
132
|
-
if (object.isBone) {
|
|
133
|
-
const q = object.quaternion;
|
|
134
|
-
map.set(object, { x: q.x, y: q.y, z: q.z, w: q.w });
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
bindFallback.set(rig, map);
|
|
138
|
-
}
|
|
139
|
-
return map;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/** Per-rig positional-skinning preparation, computed once from the bind
|
|
143
|
-
* pose and cached. */
|
|
144
|
-
const rigPreps = new WeakMap();
|
|
145
|
-
|
|
146
|
-
export function prepOf(rig) {
|
|
147
|
-
let prep = rigPreps.get(rig);
|
|
148
|
-
if (prep) return prep;
|
|
149
|
-
const binds = bindsOf(rig);
|
|
150
|
-
|
|
151
|
-
// Rig-space bind transform of every node on the path to each mapped
|
|
152
|
-
// bone: bind local rotations (poseBind) composed with the current —
|
|
153
|
-
// never re-positioned outside this module — local translations. Computed
|
|
154
|
-
// BEFORE this module ever writes a bone transform, so positions are the
|
|
155
|
-
// FBX bind translations.
|
|
156
|
-
// Rig-space = the space BELOW the rig root: the root's own transform
|
|
157
|
-
// (Character's position/rotation and the 0.01 centimetre scale) is
|
|
158
|
-
// applied by the scene on top and must never leak into bind transforms —
|
|
159
|
-
// with a scaled root it would shrink every bind matrix and bone locals
|
|
160
|
-
// would be written in metres into a centimetre hierarchy.
|
|
161
|
-
const worldByNode = new Map();
|
|
162
|
-
// Bind TRANSLATIONS come from the same primed snapshot as the rotations.
|
|
163
|
-
// This module writes bone positions (positional skinning, stretched
|
|
164
|
-
// leaves), so a prep computed after any playback — a second take on the
|
|
165
|
-
// same character, a body swap — would read a posed rig as its bind and
|
|
166
|
-
// build every offset on it (measured: hips 101.6 vs bind 104.3, arms
|
|
167
|
-
// 8.9 vs 10.8, and the skin sat off the bones for the rest of the
|
|
168
|
-
// session). Only a rig that was never primed falls back to live values.
|
|
169
|
-
const bindPositionOf = (node) => {
|
|
170
|
-
const b = node.isBone ? binds.get(node) : null;
|
|
171
|
-
return b?.position ? new THREE.Vector3(b.position.x, b.position.y, b.position.z) : node.position;
|
|
172
|
-
};
|
|
173
|
-
const walk = (node, parentMat) => {
|
|
174
|
-
const b = node.isBone ? binds.get(node) : null;
|
|
175
|
-
const q = b
|
|
176
|
-
? new THREE.Quaternion(b.x, b.y, b.z, b.w)
|
|
177
|
-
: node.quaternion;
|
|
178
|
-
const local = new THREE.Matrix4().compose(bindPositionOf(node), q, node.scale);
|
|
179
|
-
const world = parentMat.clone().multiply(local);
|
|
180
|
-
worldByNode.set(node, world);
|
|
181
|
-
for (const child of node.children) walk(child, world);
|
|
182
|
-
};
|
|
183
|
-
for (const child of rig.children) walk(child, new THREE.Matrix4());
|
|
184
|
-
|
|
185
|
-
// Mapped bones and their bind transforms, in cskel27 index order.
|
|
186
|
-
const bones = new Array(CSKEL27_JOINTS.length).fill(null);
|
|
187
|
-
const bindPos = new Array(CSKEL27_JOINTS.length).fill(null);
|
|
188
|
-
const bindQuat = new Array(CSKEL27_JOINTS.length).fill(null);
|
|
189
|
-
const bindScale = new Array(CSKEL27_JOINTS.length).fill(null);
|
|
190
|
-
const bindLocalPos = new Array(CSKEL27_JOINTS.length).fill(null);
|
|
191
|
-
const parentBindWorld = new Array(CSKEL27_JOINTS.length).fill(null);
|
|
192
|
-
for (let j = 0; j < CSKEL27_JOINTS.length; j += 1) {
|
|
193
|
-
const mixamoName = SKINNING_MAP[j];
|
|
194
|
-
if (!mixamoName) continue;
|
|
195
|
-
const bone = findBone(rig, mixamoName);
|
|
196
|
-
if (!bone) continue;
|
|
197
|
-
const world = worldByNode.get(bone);
|
|
198
|
-
bones[j] = bone;
|
|
199
|
-
bindPos[j] = new THREE.Vector3().setFromMatrixPosition(world);
|
|
200
|
-
bindQuat[j] = new THREE.Quaternion().setFromRotationMatrix(world);
|
|
201
|
-
bindScale[j] = bone.scale.clone();
|
|
202
|
-
bindLocalPos[j] = bindPositionOf(bone).clone();
|
|
203
|
-
parentBindWorld[j] = worldByNode.get(bone.parent) ?? new THREE.Matrix4();
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Uniform scale s: the rig's own leg height over ARDY's, so posed_joints
|
|
207
|
-
// map into rig units keeping the character's authored proportions (the
|
|
208
|
-
// viser avatar deforms onto the ARDY skeleton instead; both plant the
|
|
209
|
-
// feet exactly). Leg height = hips bind Y minus the lowest mapped bind
|
|
210
|
-
// Y, against ARDY's neutral toe depth of 0.9544128 m.
|
|
211
|
-
const hipsIndex = CSKEL27_JOINTS.indexOf("Hips");
|
|
212
|
-
const hipsY = bindPos[hipsIndex] ? bindPos[hipsIndex].y : 0;
|
|
213
|
-
let lowestY = hipsY;
|
|
214
|
-
for (let j = 0; j < bindPos.length; j += 1) {
|
|
215
|
-
if (bindPos[j] && bindPos[j].y < lowestY) lowestY = bindPos[j].y;
|
|
216
|
-
}
|
|
217
|
-
const legHeight = hipsY - lowestY;
|
|
218
|
-
const scale = legHeight > 1e-6 ? legHeight / -ARDY_NEUTRAL_MIN_Y : 1;
|
|
219
|
-
|
|
220
|
-
// Per-joint bind offset against the floor-shifted ARDY neutral pose
|
|
221
|
-
// (viser: bind_joints[:, 1] -= bind_joints[:, 1].min()).
|
|
222
|
-
const offsets = new Array(CSKEL27_JOINTS.length).fill(null);
|
|
223
|
-
for (let j = 0; j < CSKEL27_JOINTS.length; j += 1) {
|
|
224
|
-
if (!bones[j]) continue;
|
|
225
|
-
const neutral = CSKEL27_NEUTRAL[j];
|
|
226
|
-
offsets[j] = new THREE.Vector3(
|
|
227
|
-
bindPos[j].x - scale * neutral[0],
|
|
228
|
-
bindPos[j].y - scale * (neutral[1] - ARDY_NEUTRAL_MIN_Y),
|
|
229
|
-
bindPos[j].z - scale * neutral[2],
|
|
230
|
-
);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Chain parents for local conversion: the cskel parent is NOT always the
|
|
234
|
-
// bone's parent (core Spine drives no bone, so the driven Spine bone's
|
|
235
|
-
// real parent is the driven Hips bone). For every mapped bone find the
|
|
236
|
-
// nearest MAPPED ancestor bone and the bind-space relative matrix from
|
|
237
|
-
// that ancestor to the bone's parent; bones without a mapped ancestor
|
|
238
|
-
// hang off a static node whose bind world stays valid all playback.
|
|
239
|
-
const boneIndex = new Map();
|
|
240
|
-
for (let j = 0; j < bones.length; j += 1) {
|
|
241
|
-
if (bones[j]) boneIndex.set(bones[j], j);
|
|
242
|
-
}
|
|
243
|
-
const chainParent = new Array(CSKEL27_JOINTS.length).fill(-1);
|
|
244
|
-
const chainRel = new Array(CSKEL27_JOINTS.length).fill(null);
|
|
245
|
-
for (let j = 0; j < bones.length; j += 1) {
|
|
246
|
-
const bone = bones[j];
|
|
247
|
-
if (!bone) continue;
|
|
248
|
-
for (let node = bone.parent; node && node !== rig; node = node.parent) {
|
|
249
|
-
if (node.isBone && boneIndex.has(node)) {
|
|
250
|
-
chainParent[j] = boneIndex.get(node);
|
|
251
|
-
chainRel[j] = new THREE.Matrix4()
|
|
252
|
-
.copy(worldByNode.get(node))
|
|
253
|
-
.invert()
|
|
254
|
-
.multiply(worldByNode.get(bone.parent));
|
|
255
|
-
break;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// The wrists are deliberately not driven (SKINNING_MAP), but a hand bone's
|
|
261
|
-
// local translation IS the forearm's length, so a mocap take's forearm
|
|
262
|
-
// factor has to be written there or the hand stays at the canonical
|
|
263
|
-
// distance while posedJoints say otherwise. Kept separately so the
|
|
264
|
-
// mocap supplies no wrist rotation. Explicitly restore their bind rotation
|
|
265
|
-
// before the IK layer: otherwise a based wrist correction is multiplied
|
|
266
|
-
// onto yesterday's correction on every seek, accumulating a hand flip.
|
|
267
|
-
const stretchedLeaves = [];
|
|
268
|
-
for (const name of ["LeftHand", "RightHand"]) {
|
|
269
|
-
const bone = findBone(rig, name);
|
|
270
|
-
if (bone) {
|
|
271
|
-
const bind = binds.get(bone);
|
|
272
|
-
stretchedLeaves.push({ bone, joint: CSKEL27_JOINTS.indexOf(name), bindLocalPos: bindPositionOf(bone).clone(),
|
|
273
|
-
bindWorldQuat: new THREE.Quaternion().setFromRotationMatrix(worldByNode.get(bone)),
|
|
274
|
-
parentBindQuat: new THREE.Quaternion().setFromRotationMatrix(worldByNode.get(bone.parent)),
|
|
275
|
-
bindLocalQuat: bind ? new THREE.Quaternion(bind.x, bind.y, bind.z, bind.w) : bone.quaternion.clone() });
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// Bone lengths in rig units for the re-basing above: the canonical cskel27
|
|
280
|
-
// bone (neutral pose, scaled into the rig) and the rig's own bind bone.
|
|
281
|
-
const canonicalBoneLength = new Array(CSKEL27_JOINTS.length).fill(0);
|
|
282
|
-
const rigBoneLength = new Array(CSKEL27_JOINTS.length).fill(0);
|
|
283
|
-
for (let j = 1; j < CSKEL27_JOINTS.length; j += 1) {
|
|
284
|
-
const parent = CSKEL27_PARENTS[j];
|
|
285
|
-
const a = CSKEL27_NEUTRAL[j];
|
|
286
|
-
const b = CSKEL27_NEUTRAL[parent];
|
|
287
|
-
canonicalBoneLength[j] = scale * Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]);
|
|
288
|
-
const bone = bones[j] ?? stretchedLeaves.find((leaf) => leaf.joint === j)?.bone;
|
|
289
|
-
if (bone) rigBoneLength[j] = bindPositionOf(bone).length();
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
prep = {
|
|
293
|
-
bones,
|
|
294
|
-
bindPos,
|
|
295
|
-
stretchedLeaves,
|
|
296
|
-
canonicalBoneLength,
|
|
297
|
-
rigBoneLength,
|
|
298
|
-
bindQuat,
|
|
299
|
-
bindScale,
|
|
300
|
-
bindLocalPos,
|
|
301
|
-
parentBindWorld,
|
|
302
|
-
chainParent,
|
|
303
|
-
chainRel,
|
|
304
|
-
scale,
|
|
305
|
-
offsets,
|
|
306
|
-
};
|
|
307
|
-
rigPreps.set(rig, prep);
|
|
308
|
-
return prep;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/** cskel27 index -> control Bone (null where no bone is driven), kept as a
|
|
312
|
-
* public introspection hook for tests and snapshot/restore. */
|
|
313
|
-
export function motionBones(rig) {
|
|
314
|
-
return prepOf(rig).bones;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/** Recover the current ARDY-space root from the live retargeted rig.
|
|
318
|
-
*
|
|
319
|
-
* Positional skinning writes each mapped bone as
|
|
320
|
-
* bone = scale * root + globalRotation * bindOffset
|
|
321
|
-
* in rig space. Inverting that equation captures generated root motion plus
|
|
322
|
-
* any IK hips translation, so pose constraints never borrow placement from an
|
|
323
|
-
* unrelated reference clip. */
|
|
324
|
-
export function captureArdyRoot(rig) {
|
|
325
|
-
const prep = prepOf(rig);
|
|
326
|
-
const rootIndex = CSKEL27_JOINTS.indexOf("Hips");
|
|
327
|
-
const bone = prep.bones[rootIndex];
|
|
328
|
-
if (!bone) throw new Error("captureArdyRoot: mapped Hips bone is unavailable");
|
|
329
|
-
|
|
330
|
-
rig.updateMatrixWorld(true);
|
|
331
|
-
const rigInverse = new THREE.Matrix4().copy(rig.matrixWorld).invert();
|
|
332
|
-
const boneInRig = new THREE.Matrix4().multiplyMatrices(rigInverse, bone.matrixWorld);
|
|
333
|
-
const position = new THREE.Vector3();
|
|
334
|
-
const boneRotation = new THREE.Quaternion();
|
|
335
|
-
boneInRig.decompose(position, boneRotation, new THREE.Vector3());
|
|
336
|
-
|
|
337
|
-
const globalRotation = boneRotation.multiply(prep.bindQuat[rootIndex].clone().invert()).normalize();
|
|
338
|
-
const bindOffset = prep.offsets[rootIndex].clone().applyQuaternion(globalRotation);
|
|
339
|
-
// The scale the last applyMotionFrame used (1 for a performer-sized take).
|
|
340
|
-
const s = prep.lastScale ?? prep.scale;
|
|
341
|
-
return [
|
|
342
|
-
(position.x - bindOffset.x) / s,
|
|
343
|
-
(position.y - bindOffset.y) / s,
|
|
344
|
-
(position.z - bindOffset.z) / s,
|
|
345
|
-
];
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
/** Bind offsets against the canonical neutral grown by a take's bone
|
|
349
|
-
* factors: neutral joint j = parent + boneScale[j] * (neutral[j] - neutral[parent]),
|
|
350
|
-
* floor-shifted the same way prepOf does, in rig units via prep.scale.
|
|
351
|
-
* Cached per (rig, boneScale array) — one take, one skeleton. */
|
|
352
|
-
const scaledOffsetCache = new WeakMap();
|
|
353
|
-
function scaledOffsets(prep, boneScale) {
|
|
354
|
-
let cached = scaledOffsetCache.get(boneScale);
|
|
355
|
-
if (cached && cached.prep === prep) return cached.offsets;
|
|
356
|
-
const grown = new Array(CSKEL27_JOINTS.length);
|
|
357
|
-
for (let j = 0; j < CSKEL27_JOINTS.length; j += 1) {
|
|
358
|
-
const parent = CSKEL27_PARENTS[j];
|
|
359
|
-
const n = CSKEL27_NEUTRAL[j];
|
|
360
|
-
if (parent === null) { grown[j] = [n[0], n[1], n[2]]; continue; }
|
|
361
|
-
const q = CSKEL27_NEUTRAL[parent];
|
|
362
|
-
const g = grown[parent];
|
|
363
|
-
grown[j] = [g[0] + boneScale[j] * (n[0] - q[0]), g[1] + boneScale[j] * (n[1] - q[1]), g[2] + boneScale[j] * (n[2] - q[2])];
|
|
364
|
-
}
|
|
365
|
-
// Same frame and scale as prepOf's offsets: only the per-bone growth
|
|
366
|
-
// differs, so an all-ones boneScale reproduces prep.offsets exactly.
|
|
367
|
-
const offsets = new Array(CSKEL27_JOINTS.length).fill(null);
|
|
368
|
-
for (let j = 0; j < CSKEL27_JOINTS.length; j += 1) {
|
|
369
|
-
if (!prep.bones[j]) continue;
|
|
370
|
-
const g = grown[j];
|
|
371
|
-
offsets[j] = new THREE.Vector3(
|
|
372
|
-
prep.bindPos[j].x - prep.scale * g[0],
|
|
373
|
-
prep.bindPos[j].y - prep.scale * (g[1] - ARDY_NEUTRAL_MIN_Y),
|
|
374
|
-
prep.bindPos[j].z - prep.scale * g[2],
|
|
375
|
-
);
|
|
376
|
-
}
|
|
377
|
-
scaledOffsetCache.set(boneScale, { prep, offsets });
|
|
378
|
-
return offsets;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/** Factor to apply to a rotation-driven bone's BIND translation so the bone
|
|
382
|
-
* comes out at the performer's length. boneScale is performer/canonical; the
|
|
383
|
-
* rig's own bind bone is not the canonical length (Mixamo forearm 27.6 cm
|
|
384
|
-
* against cskel27's 23.3), so the factor is re-based onto the rig:
|
|
385
|
-
* performer / rigBind = boneScale * canonical / rigBind, all in rig units. */
|
|
386
|
-
export function boneStretch(prep, motion, j) {
|
|
387
|
-
if (!motion.boneScale) return 1;
|
|
388
|
-
// The shoulder girdle is character geometry, like the neck: the rig's
|
|
389
|
-
// clavicle+arm-root (21 cm straight) is deliberately shorter than
|
|
390
|
-
// cskel27's 34 cm (HIERARCHY_PRESERVED_JOINTS exists to keep it), so
|
|
391
|
-
// re-basing onto the canonical length would push a 33 cm-wide performer
|
|
392
|
-
// out to 41 cm. The performer/canonical ratio applies to the rig's own
|
|
393
|
-
// girdle as-is: a narrow performer narrows the character's shoulders.
|
|
394
|
-
if (GIRDLE_JOINTS.has(CSKEL27_JOINTS[j])) return motion.boneScale[j];
|
|
395
|
-
const canonical = prep.canonicalBoneLength[j];
|
|
396
|
-
const rigBind = prep.rigBoneLength[j];
|
|
397
|
-
if (!(canonical > 1e-6) || !(rigBind > 1e-6)) return motion.boneScale[j];
|
|
398
|
-
return (motion.boneScale[j] * canonical) / rigBind;
|
|
399
|
-
}
|
|
400
|
-
export const GIRDLE_JOINTS = new Set(["LeftShoulder", "RightShoulder", "LeftArm", "RightArm"]);
|
|
401
|
-
|
|
402
|
-
/* --- per-frame application -------------------------------------------------- */
|
|
403
|
-
|
|
404
|
-
const frameLocals = new Array(CSKEL27_JOINTS.length);
|
|
405
|
-
for (let j = 0; j < frameLocals.length; j += 1) {
|
|
406
|
-
frameLocals[j] = [
|
|
407
|
-
[0, 0, 0],
|
|
408
|
-
[0, 0, 0],
|
|
409
|
-
[0, 0, 0],
|
|
410
|
-
];
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
const mGlobal = new THREE.Matrix3();
|
|
414
|
-
const mWorld = new THREE.Matrix4();
|
|
415
|
-
const mLocal = new THREE.Matrix4();
|
|
416
|
-
const mParentInv = new THREE.Matrix4();
|
|
417
|
-
const qGlobal = new THREE.Quaternion();
|
|
418
|
-
const qWorld = new THREE.Quaternion();
|
|
419
|
-
const vOffset = new THREE.Vector3();
|
|
420
|
-
const vWorld = new THREE.Vector3();
|
|
421
|
-
const vDecompPos = new THREE.Vector3();
|
|
422
|
-
const qDecomp = new THREE.Quaternion();
|
|
423
|
-
const vDecompScale = new THREE.Vector3();
|
|
424
|
-
const desiredWorld = new Array(CSKEL27_JOINTS.length).fill(null);
|
|
425
|
-
|
|
426
|
-
function rotationsAt(motion, f) {
|
|
427
|
-
for (let j = 0; j < frameLocals.length; j += 1) {
|
|
428
|
-
const o = (f * frameLocals.length + j) * 9;
|
|
429
|
-
const L = frameLocals[j];
|
|
430
|
-
for (let r = 0; r < 3; r += 1) for (let c = 0; c < 3; c += 1) L[r][c] = motion.rotMats[o + r * 3 + c];
|
|
431
|
-
}
|
|
432
|
-
return globalRotations(frameLocals);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
function rotationQuat(G, target = new THREE.Quaternion()) {
|
|
436
|
-
mGlobal.set(...G[0], ...G[1], ...G[2]);
|
|
437
|
-
return target.setFromRotationMatrix(mWorld.setFromMatrix3(mGlobal));
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
/** Reference corrections need FK and a few transforms, not a second rig pose. */
|
|
441
|
-
function trailReference(prep, { base, chains }, f) {
|
|
442
|
-
const globals = rotationsAt(base, f);
|
|
443
|
-
const offsets = base.boneScale ? scaledOffsets(prep, base.boneScale) : prep.offsets;
|
|
444
|
-
const anchor = Math.max(0, Math.min(base.anchorFrame || 0, base.frames - 1)) * 27 * 3;
|
|
445
|
-
const worlds = new Map(), positions = new Map(), wrists = new Map();
|
|
446
|
-
const world = (j) => {
|
|
447
|
-
if (!worlds.has(j)) {
|
|
448
|
-
const q = rotationQuat(globals[j]);
|
|
449
|
-
const p = new THREE.Vector3().fromArray(base.posedJoints, (f * 27 + j) * 3);
|
|
450
|
-
p.x -= base.posedJoints[anchor]; p.z -= base.posedJoints[anchor + 2];
|
|
451
|
-
p.multiplyScalar(prep.scale).add(offsets[j].clone().applyQuaternion(q));
|
|
452
|
-
worlds.set(j, new THREE.Matrix4().compose(p, q.multiply(prep.bindQuat[j]), prep.bindScale[j]));
|
|
453
|
-
}
|
|
454
|
-
return worlds.get(j);
|
|
455
|
-
};
|
|
456
|
-
for (const track of chains) {
|
|
457
|
-
const side = track.startsWith("left") ? "Left" : "Right";
|
|
458
|
-
if (track.endsWith("Hand")) {
|
|
459
|
-
const leaf = prep.stretchedLeaves.find((item) => CSKEL27_JOINTS[item.joint] === `${side}Hand`);
|
|
460
|
-
if (leaf) wrists.set(leaf.bone, rotationQuat(globals[CSKEL27_PARENTS[leaf.joint]]).multiply(leaf.bindWorldQuat));
|
|
461
|
-
} else {
|
|
462
|
-
for (const suffix of ["UpLeg", "Leg", "Foot", "ToeBase"]) {
|
|
463
|
-
const j = CSKEL27_JOINTS.indexOf(side + suffix), bone = prep.bones[j];
|
|
464
|
-
if (!bone) continue;
|
|
465
|
-
const parent = prep.chainParent[j];
|
|
466
|
-
const parentWorld = parent < 0 ? prep.parentBindWorld[j].clone() : world(parent).clone().multiply(prep.chainRel[j]);
|
|
467
|
-
positions.set(bone, new THREE.Vector3().setFromMatrixPosition(world(j)).applyMatrix4(parentWorld.invert()));
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
return { positions, wrists };
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
* Apply one motion frame to the rig with positional skinning: every mapped
|
|
476
|
-
* bone's rig-space world transform is set to
|
|
477
|
-
*
|
|
478
|
-
* pos = s * (posed[f][j] - anchorRootXZ) + R[f][j] @ offset[j]
|
|
479
|
-
* quat = R[f][j] @ bindQuat[j]
|
|
480
|
-
*
|
|
481
|
-
* and then converted to bone-local against the (already updated) parent.
|
|
482
|
-
* `frame` is clamped into range. The clip-to-scene yaw is NOT applied here —
|
|
483
|
-
* the Character group's `rot` carries it, exactly like before.
|
|
484
|
-
*/
|
|
485
|
-
export function applyMotionFrame(rig, motion, frame) {
|
|
486
|
-
if (!rig || !motion) return;
|
|
487
|
-
const f = Math.max(0, Math.min(Math.round(frame) || 0, motion.frames - 1));
|
|
488
|
-
const prep = prepOf(rig);
|
|
489
|
-
const joints = CSKEL27_JOINTS.length;
|
|
490
|
-
// Opt-in only; unedited takes keep their existing retargeting policy.
|
|
491
|
-
const trail = motion.trailRetarget ? trailReference(prep, motion.trailRetarget, f) : null;
|
|
492
|
-
const globals = rotationsAt(motion, f);
|
|
493
|
-
|
|
494
|
-
// Anchor: the anchor frame's root stays at the rig origin (the Character
|
|
495
|
-
// group sits at the scene anchor); height stays floor-absolute.
|
|
496
|
-
const anchorFrame = Math.max(0, Math.min(motion.anchorFrame || 0, motion.frames - 1));
|
|
497
|
-
const anchorX = motion.posedJoints[(anchorFrame * joints) * 3];
|
|
498
|
-
const anchorZ = motion.posedJoints[(anchorFrame * joints) * 3 + 2];
|
|
499
|
-
// A take with boneScale already IS a body: its posedJoints are the
|
|
500
|
-
// performer's metres, so they map into the rig 1:1 (0.01 cm units aside,
|
|
501
|
-
// which the root applies). prep.scale exists to keep the CANONICAL body
|
|
502
|
-
// on this rig's leg length and would shrink a filmed performer by the
|
|
503
|
-
// rig/canonical ratio (9 % on the x-bot).
|
|
504
|
-
// The same unit conversion for BOTH kinds of take. prep.scale carries the
|
|
505
|
-
// rig's leg-length over the canonical one (109 % on the x-bot, 102 % on
|
|
506
|
-
// the y-bot): every bind offset below was measured in that scaled frame,
|
|
507
|
-
// and a performer-sized take mapped 1:1 in metres instead lands its hips
|
|
508
|
-
// 9 cm above the rig's own pelvis geometry on the x-bot (the y-bot hid it
|
|
509
|
-
// at 1.6 cm). The performer's PROPORTIONS still come through untouched —
|
|
510
|
-
// this is a uniform factor, the rig's own size — and the bone factors
|
|
511
|
-
// keep them.
|
|
512
|
-
const s = prep.scale;
|
|
513
|
-
prep.lastScale = s;
|
|
514
|
-
// The bind offsets were measured against the CANONICAL neutral; a
|
|
515
|
-
// performer-sized take moves every joint by its bone factors, and an
|
|
516
|
-
// offset that still points at the canonical joint pulls the skin off
|
|
517
|
-
// the bone by the difference (measured: neck/head 7-10 cm low on a
|
|
518
|
-
// 0.84x torso). Re-measure against the neutral grown by boneScale.
|
|
519
|
-
const offsets = motion.boneScale ? scaledOffsets(prep, motion.boneScale) : prep.offsets;
|
|
520
|
-
|
|
521
|
-
for (let j = 0; j < joints; j += 1) {
|
|
522
|
-
const bone = prep.bones[j];
|
|
523
|
-
desiredWorld[j] = null;
|
|
524
|
-
if (!bone) continue;
|
|
525
|
-
|
|
526
|
-
const G = globals[j];
|
|
527
|
-
mGlobal.set(
|
|
528
|
-
G[0][0], G[0][1], G[0][2],
|
|
529
|
-
G[1][0], G[1][1], G[1][2],
|
|
530
|
-
G[2][0], G[2][1], G[2][2],
|
|
531
|
-
);
|
|
532
|
-
qGlobal.setFromRotationMatrix(mWorld.setFromMatrix3(mGlobal));
|
|
533
|
-
|
|
534
|
-
const po = (f * joints + j) * 3;
|
|
535
|
-
vOffset.copy(offsets[j]).applyQuaternion(qGlobal);
|
|
536
|
-
vWorld.set(
|
|
537
|
-
s * (motion.posedJoints[po] - anchorX) + vOffset.x,
|
|
538
|
-
s * motion.posedJoints[po + 1] + vOffset.y,
|
|
539
|
-
s * (motion.posedJoints[po + 2] - anchorZ) + vOffset.z,
|
|
540
|
-
);
|
|
541
|
-
qWorld.copy(qGlobal).multiply(prep.bindQuat[j]);
|
|
542
|
-
|
|
543
|
-
// Convert the rig-space world transform to bone-local. The parent
|
|
544
|
-
// world is the nearest mapped ancestor's desired world (already
|
|
545
|
-
// written this frame — cskel index order is topological along every
|
|
546
|
-
// Mixamo chain) times the bind-relative chain matrix, or the static
|
|
547
|
-
// bind parent world when no mapped ancestor exists.
|
|
548
|
-
const chainParentJ = prep.chainParent[j];
|
|
549
|
-
if (chainParentJ >= 0) {
|
|
550
|
-
mParentInv.copy(desiredWorld[chainParentJ]).multiply(prep.chainRel[j]);
|
|
551
|
-
} else {
|
|
552
|
-
mParentInv.copy(prep.parentBindWorld[j]);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
if (trail?.positions.has(bone)) {
|
|
556
|
-
// Swing the original rendered segment, rather than re-placing its
|
|
557
|
-
// endpoints with different rotated bind offsets (which stretches it).
|
|
558
|
-
vWorld.copy(trail.positions.get(bone)).applyMatrix4(mParentInv);
|
|
559
|
-
} else if (HIERARCHY_PRESERVED_JOINTS.has(CSKEL27_JOINTS[j])) {
|
|
560
|
-
// A mocap take carries the performer's bone lengths (boneScale, see
|
|
561
|
-
// npz.js). Positionally skinned bones already sit where the scaled
|
|
562
|
-
// posedJoints put them; the arm chain rides its own bind
|
|
563
|
-
// translation, so the performer's arm length is applied HERE by
|
|
564
|
-
// stretching that translation by the factor of the bone ending at
|
|
565
|
-
// this joint. Bone scale is never touched: it would scale the skin.
|
|
566
|
-
vWorld.copy(prep.bindLocalPos[j]).multiplyScalar(boneStretch(prep, motion, j)).applyMatrix4(mParentInv);
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
mWorld.compose(vWorld, qWorld, prep.bindScale[j]);
|
|
570
|
-
desiredWorld[j] = mWorld.clone();
|
|
571
|
-
mLocal.copy(mParentInv).invert().multiply(mWorld);
|
|
572
|
-
mLocal.decompose(vDecompPos, qDecomp, vDecompScale);
|
|
573
|
-
bone.position.copy(vDecompPos);
|
|
574
|
-
bone.quaternion.copy(qDecomp);
|
|
575
|
-
}
|
|
576
|
-
for (const leaf of prep.stretchedLeaves) {
|
|
577
|
-
leaf.bone.position.copy(leaf.bindLocalPos).multiplyScalar(boneStretch(prep, motion, leaf.joint));
|
|
578
|
-
const orientation = trail?.wrists.get(leaf.bone);
|
|
579
|
-
if (orientation) {
|
|
580
|
-
// Work below the rig root: placement, yaw and export clones must
|
|
581
|
-
// not leak into the wrist's local compensation.
|
|
582
|
-
rotationQuat(globals[CSKEL27_PARENTS[leaf.joint]], qGlobal).multiply(leaf.parentBindQuat);
|
|
583
|
-
leaf.bone.quaternion.copy(qGlobal.invert().multiply(orientation));
|
|
584
|
-
} else leaf.bone.quaternion.copy(leaf.bindLocalQuat);
|
|
585
|
-
}
|
|
586
|
-
rig.updateMatrixWorld(true);
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
/* --- read-only sampling ---------------------------------------------------- */
|
|
590
|
-
|
|
591
|
-
/** Bones-only copy of a rig for off-screen sampling, keyed to the live rig.
|
|
592
|
-
* It carries the live rig's bind snapshot, so its prep is the live prep. */
|
|
593
|
-
const samplerCopies = new WeakMap();
|
|
594
|
-
|
|
595
|
-
function samplerCopyOf(rig) {
|
|
596
|
-
let copy = samplerCopies.get(rig);
|
|
597
|
-
if (copy) return copy;
|
|
598
|
-
// Never create the live rig's fallback here: that would freeze its bind
|
|
599
|
-
// at whatever pose it holds now. Unprimed rigs read the current rotations.
|
|
600
|
-
const binds = rig.userData?.poseBind ?? bindFallback.get(rig) ?? new Map();
|
|
601
|
-
const copyBinds = new Map();
|
|
602
|
-
const hasBone = (node) => node.isBone || node.children.some(hasBone);
|
|
603
|
-
const clone = (node) => {
|
|
604
|
-
const out = node.isBone ? new THREE.Bone() : new THREE.Object3D();
|
|
605
|
-
out.name = node.name;
|
|
606
|
-
out.position.copy(node.position);
|
|
607
|
-
out.quaternion.copy(node.quaternion);
|
|
608
|
-
out.scale.copy(node.scale);
|
|
609
|
-
const bind = node.isBone ? binds.get(node) : null;
|
|
610
|
-
const q = node.quaternion;
|
|
611
|
-
// Without a recorded bind position the copied live position stands in,
|
|
612
|
-
// exactly as bindPositionOf does for the live rig.
|
|
613
|
-
if (node.isBone) copyBinds.set(out, bind ?? { x: q.x, y: q.y, z: q.z, w: q.w });
|
|
614
|
-
for (const child of node.children) if (hasBone(child)) out.add(clone(child));
|
|
615
|
-
return out;
|
|
616
|
-
};
|
|
617
|
-
// The root stays identity: samples are in rig space, below the root.
|
|
618
|
-
copy = new THREE.Object3D();
|
|
619
|
-
for (const child of rig.children) if (hasBone(child)) copy.add(clone(child));
|
|
620
|
-
copy.userData.poseBind = copyBinds;
|
|
621
|
-
samplerCopies.set(rig, copy);
|
|
622
|
-
return copy;
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
/** Per (rig, motion) cache of rendered joint positions. */
|
|
626
|
-
const jointSamples = new WeakMap();
|
|
627
|
-
|
|
628
|
-
/** Pose the sampler copy at frame f and write every driven bone's rig-space
|
|
629
|
-
* position into out[offset..offset+81] (NaN where no bone is driven). */
|
|
630
|
-
function sampleFrame(rig, motion, f, out, offset) {
|
|
631
|
-
const copy = samplerCopyOf(rig);
|
|
632
|
-
const prep = prepOf(copy);
|
|
633
|
-
if (!prep.driven) {
|
|
634
|
-
prep.driven = CSKEL27_JOINTS.map((_, j) => prep.bones[j] ?? prep.stretchedLeaves.find((leaf) => leaf.joint === j)?.bone ?? null);
|
|
635
|
-
}
|
|
636
|
-
applyMotionFrame(copy, motion, f);
|
|
637
|
-
for (let j = 0; j < prep.driven.length; j += 1) {
|
|
638
|
-
const bone = prep.driven[j];
|
|
639
|
-
const o = offset + j * 3;
|
|
640
|
-
if (!bone) {
|
|
641
|
-
out[o] = out[o + 1] = out[o + 2] = NaN;
|
|
642
|
-
continue;
|
|
643
|
-
}
|
|
644
|
-
const e = bone.matrixWorld.elements;
|
|
645
|
-
out[o] = e[12];
|
|
646
|
-
out[o + 1] = e[13];
|
|
647
|
-
out[o + 2] = e[14];
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
/** One frame of motionJointPositions (uncached): Float32Array(27*3), rig space. */
|
|
652
|
-
export function motionJointPositionsAt(rig, motion, frame) {
|
|
653
|
-
if (!rig || !motion?.rotMats || !(motion.frames > 0)) return null;
|
|
654
|
-
const out = new Float32Array(CSKEL27_JOINTS.length * 3);
|
|
655
|
-
sampleFrame(rig, motion, Math.max(0, Math.min(Math.round(frame) || 0, motion.frames - 1)), out, 0);
|
|
656
|
-
return out;
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
/**
|
|
660
|
-
* Rig-space position of every driven cskel27 joint's bone at every frame,
|
|
661
|
-
* exactly as applyMotionFrame places it, without touching the live rig.
|
|
662
|
-
* Returns { positions: Float32Array(frames*27*3) } (NaN where no bone is
|
|
663
|
-
* driven). Cached per rig + motion; treat as read-only.
|
|
664
|
-
*/
|
|
665
|
-
export function motionJointPositions(rig, motion) {
|
|
666
|
-
if (!rig || !motion?.rotMats || !(motion.frames > 0)) return null;
|
|
667
|
-
let byRig = jointSamples.get(motion);
|
|
668
|
-
if (!byRig) {
|
|
669
|
-
byRig = new WeakMap();
|
|
670
|
-
jointSamples.set(motion, byRig);
|
|
671
|
-
}
|
|
672
|
-
const cached = byRig.get(rig);
|
|
673
|
-
if (cached) return cached;
|
|
674
|
-
const joints = CSKEL27_JOINTS.length;
|
|
675
|
-
const positions = new Float32Array(motion.frames * joints * 3);
|
|
676
|
-
for (let f = 0; f < motion.frames; f += 1) sampleFrame(rig, motion, f, positions, f * joints * 3);
|
|
677
|
-
const result = { positions };
|
|
678
|
-
byRig.set(rig, result);
|
|
679
|
-
return result;
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
/* --- snapshot / restore ----------------------------------------------------- */
|
|
683
|
-
|
|
684
|
-
/**
|
|
685
|
-
* Capture the current transform of every bone playback touches — the
|
|
686
|
-
* skinning-mapped bones (position AND quaternion: applyMotionFrame drives
|
|
687
|
-
* both) — so clearing a motion restores the prior CozyClay pose exactly.
|
|
688
|
-
*/
|
|
689
|
-
export function snapshotPlaybackBones(rig) {
|
|
690
|
-
const out = [];
|
|
691
|
-
const prep = prepOf(rig);
|
|
692
|
-
for (const bone of [...prep.bones, ...prep.stretchedLeaves.map((leaf) => leaf.bone)]) {
|
|
693
|
-
if (!bone) continue;
|
|
694
|
-
out.push([
|
|
695
|
-
bone,
|
|
696
|
-
bone.quaternion.x, bone.quaternion.y, bone.quaternion.z, bone.quaternion.w,
|
|
697
|
-
bone.position.x, bone.position.y, bone.position.z,
|
|
698
|
-
]);
|
|
699
|
-
}
|
|
700
|
-
return out;
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
/** Restore transforms captured by snapshotPlaybackBones. */
|
|
704
|
-
export function restorePlaybackBones(rig, snapshot) {
|
|
705
|
-
if (!rig || !snapshot) return;
|
|
706
|
-
for (const entry of snapshot) {
|
|
707
|
-
entry[0].quaternion.set(entry[1], entry[2], entry[3], entry[4]);
|
|
708
|
-
if (entry.length > 5) entry[0].position.set(entry[5], entry[6], entry[7]);
|
|
709
|
-
}
|
|
710
|
-
rig.updateMatrixWorld(true);
|
|
711
|
-
}
|