cozyclay 2.1.0 → 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 +65 -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 +22 -6
- 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-BlT33CGf.js → first-shot-handoff-CC4xe4HM.js} +1 -1
- package/dist/assets/{landing-BSesrCpL.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/llms.txt +14 -0
- 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 +174 -2639
- 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 +48 -419
- 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/PoseImagePanel.jsx +1 -1
- 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 +11 -53
- package/src/shell/CompositionGuideButton.jsx +20 -0
- 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/ShotPreviewHeader.jsx +17 -0
- package/src/shell/TopBar.jsx +5 -83
- package/src/shell/ViewportToolbar.jsx +23 -93
- 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/shell/viewport.css +12 -0
- 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 +46 -16
- package/src/studio-agent-motion.js +9 -9
- package/src/studio-agent-protocol.js +46 -73
- package/src/studio-app-binding.js +29 -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/headless-studio.mjs +85 -0
- package/tools/knip-baseline.json +0 -179
- package/tools/loc-baseline.json +33 -83
- 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 +9 -124
- package/bin/agent/motion-runtime.mjs +0 -76
- package/dist/ardy/cskel27-rest.json +0 -557
- package/dist/assets/analytics-B41FmPYI.js +0 -1
- package/dist/assets/app-Do-Og5Pu.css +0 -1
- package/dist/assets/app-DwP-tl2x.js +0 -4920
- package/dist/assets/config-Bx7pYsD9.js +0 -1
- package/dist/assets/config-DeoFTYzb.css +0 -1
- package/dist/assets/demo-Dt0qZfS7.js +0 -1
- package/dist/assets/scene-asset-sync-BCWDJB1d.js +0 -19
- package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
- package/dist/assets/ticket-jqMhqL88.js +0 -1
- package/dist/assets/workflow-Br-K7Srs.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 -159
- 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
|
@@ -1,735 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* hml22-to-cskel27.mjs — ProjFlow's HumanML3D 22-joint POSITION stream → the
|
|
3
|
-
* exact motion arrays the cclay npz encoder consumes ({ frames, fps, rotMats,
|
|
4
|
-
* rootPos, posedJoints }), i.e. the same shape tools/kimodo/soma77-to-cskel27.mjs
|
|
5
|
-
* and tools/ardy/bvh-cskel27.mjs produce. Everything downstream — the npz
|
|
6
|
-
* writer, measure-preserve, playback, the splice — is written against that
|
|
7
|
-
* shape, so this file's whole job is to end at it.
|
|
8
|
-
*
|
|
9
|
-
* WHY POSITIONS AND NOT ROTATIONS (the one structural difference from Kimodo).
|
|
10
|
-
* The checkpoint we target is `ACMDM_Raw_Flow_S_PatchSize22`. ACMDM's "Raw"
|
|
11
|
-
* family generates ABSOLUTE JOINT COORDINATES — the model's output tensor is
|
|
12
|
-
* [T, 22, 3] metres at 20 fps with no rotation channel at all (that is the
|
|
13
|
-
* backbone paper's thesis: "Absolute Coordinates Make Motion Generation Easy").
|
|
14
|
-
* soma77ToCskel27Motion is handed `global_rot_mats` and only has to re-express
|
|
15
|
-
* them; here the rotations DO NOT EXIST YET and must be lifted from positions.
|
|
16
|
-
* Plan C8 amendment (2026-08-29) froze that: input is positions, the converter
|
|
17
|
-
* lifts.
|
|
18
|
-
*
|
|
19
|
-
* THREE MOVES, deliberately the same three as soma77-to-cskel27.mjs:
|
|
20
|
-
* 1. NAME MAPPING with an explicit null for every cskel27 joint the source
|
|
21
|
-
* cannot author (CSKEL27_FROM_HML22).
|
|
22
|
-
* 2. NEAREST-MAPPED-ANCESTOR rule for those nulls: an unauthored joint borrows
|
|
23
|
-
* its nearest mapped ancestor's GLOBAL rotation, so its LOCAL comes out
|
|
24
|
-
* identity — the same "not authored" convention poseToCskel27 and
|
|
25
|
-
* bvhToCskel27Motion use.
|
|
26
|
-
* 3. EVERYTHING THROUGH GLOBAL SPACE. cskel27 and hml22 do not share a spine
|
|
27
|
-
* segmentation (cskel27 runs Hips→Spine→Spine1→Spine2→Spine3, four links
|
|
28
|
-
* before the shoulder carrier; hml22 runs pelvis→spine1→spine2→spine3,
|
|
29
|
-
* three), so a cskel27 local is only well defined as
|
|
30
|
-
* parentGlobalᵀ · global. Anything between two mapped joints is absorbed
|
|
31
|
-
* into the child's local and no rotation is lost.
|
|
32
|
-
*
|
|
33
|
-
* THE NEW MOVE: POSITIONS → GLOBALS ("aim + inherited twist").
|
|
34
|
-
* For every mapped cskel27 joint we know a rest bone vector (from the canonical
|
|
35
|
-
* cskel27 skeleton) and the observed direction to the same descendant in the
|
|
36
|
-
* hml22 frame. The global rotation is then
|
|
37
|
-
*
|
|
38
|
-
* G_j = swing( G_parent · restDir_j → obsDir_j ) · G_parent
|
|
39
|
-
*
|
|
40
|
-
* i.e. start from the parent's orientation, then apply the MINIMAL rotation that
|
|
41
|
-
* puts the bone on the observed direction. Twist is INHERITED from the parent
|
|
42
|
-
* rather than invented, which is the physically right default (the forearm's
|
|
43
|
-
* roll follows the upper arm unless something says otherwise) and — the reason
|
|
44
|
-
* it is the right ENGINEERING default too — it is a continuous function of the
|
|
45
|
-
* parent, so a smooth input clip produces a smooth output clip. Two joints get
|
|
46
|
-
* better than that: Hips and Spine3 each see TWO independent directions (the hip
|
|
47
|
-
* axis, the shoulder axis), so their full orientation including twist is
|
|
48
|
-
* recovered exactly by orthonormalising a two-vector frame. Those two carry the
|
|
49
|
-
* whole body's heading, which is why they are the ones worth pinning.
|
|
50
|
-
*
|
|
51
|
-
* TEMPORAL CONTINUITY IS PART OF THE CONTRACT, not a nicety. A twist flip
|
|
52
|
-
* between two frames reads as a limb snapping through 180° and would survive
|
|
53
|
-
* every positional gate, because positions are invariant to it. The lift is
|
|
54
|
-
* continuous everywhere except at two measure-zero branch points — an
|
|
55
|
-
* antiparallel swing (bone reversed exactly through its parent's rest aim) and a
|
|
56
|
-
* two-vector frame whose secondary collapses onto its primary. At both, this
|
|
57
|
-
* converter resolves the branch by continuing the PREVIOUS FRAME (`carry`),
|
|
58
|
-
* never by an arbitrary seed. See liftHml22PoseGlobals.
|
|
59
|
-
*
|
|
60
|
-
* WHAT THIS CANNOT RECOVER, by construction, from positions alone:
|
|
61
|
-
* - axial TWIST of any single-child bone (forearm pronation, humerus roll,
|
|
62
|
-
* shin roll, the head's roll about the neck axis). A joint chain's positions
|
|
63
|
-
* are invariant to twist of the parent bone about its own axis. Measured on
|
|
64
|
-
* real takes this is 26–55° on the arms. Positions are sub-millimetre;
|
|
65
|
-
* rotations are approximate. Plan GP4 carve-out: measure and report, do not
|
|
66
|
-
* gate on it in v1.
|
|
67
|
-
* - the 5 cskel27 joints hml22 has no source for (Spine2, L/R HandEnd,
|
|
68
|
-
* L/R HandThumb1) — filled with identity locals, 4.7–6.1 cm positional
|
|
69
|
-
* error, accepted losses per GP4. None of them is a line-edit target that
|
|
70
|
-
* hml22 can author.
|
|
71
|
-
* Neither loss moves a MAPPED joint's position, which is what a line edit is
|
|
72
|
-
* scored on.
|
|
73
|
-
*
|
|
74
|
-
* PITFALL THIS FILE EXISTS TO DOCUMENT (the hml22 analogue of the soma77 leg
|
|
75
|
-
* shift): hml22 uses SMPL naming, where `left_collar` is the CLAVICLE and
|
|
76
|
-
* `left_shoulder` is the UPPER-ARM root. cskel27 calls those `LeftShoulder` and
|
|
77
|
-
* `LeftArm`. Mapping by matching name text would put the shoulder's rotation on
|
|
78
|
-
* the clavicle and shear the whole arm. Likewise hml22's `left_foot` (joint 10)
|
|
79
|
-
* is the TOE BASE — the ankle is `left_ankle` (7).
|
|
80
|
-
*
|
|
81
|
-
* FPS: the output keeps the source rate (20 fps, ACMDM's own). Retiming onto the
|
|
82
|
-
* 24 fps app timeline happens DOWNSTREAM, exactly as it does for Kimodo takes —
|
|
83
|
-
* this file must not retime, or the retimer would run twice.
|
|
84
|
-
*/
|
|
85
|
-
|
|
86
|
-
import { CSKEL27_JOINTS, CSKEL27_PARENTS } from "../../src/ardy/cskel27.js";
|
|
87
|
-
import { deriveBoneOffsets, forwardKinematics, matMul, matTranspose } from "../../src/ardy/convert.js";
|
|
88
|
-
import { canonicalCskel27Reference } from "../../src/ardy/to-cskel27.js";
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* The HumanML3D / t2m 22-joint skeleton, in npz index order.
|
|
92
|
-
*
|
|
93
|
-
* SOURCE OF TRUTH, two independent confirmations (S2, 2026-08-29):
|
|
94
|
-
* 1. github.com/EricGuo5513/HumanML3D `paramUtil.py` → `t2m_kinematic_chain`
|
|
95
|
-
* = [[0,2,5,8,11], [0,1,4,7,10], [0,3,6,9,12,15], [9,14,17,19,21],
|
|
96
|
-
* [9,13,16,18,20]]. That fixes the TOPOLOGY and the left/right ordering
|
|
97
|
-
* exactly: index 1 is the LEFT hip (it heads the chain that also contains
|
|
98
|
-
* 4,7,10) and index 2 the right.
|
|
99
|
-
* 2. The names are SMPL's, because HumanML3D's 22 joints are SMPL body joints
|
|
100
|
-
* 0..21 verbatim: github.com/vchoutas/smplx `smplx/joint_names.py`.
|
|
101
|
-
* The two agree on every entry: joint 9 (`spine3`) is the parent of 12/13/14,
|
|
102
|
-
* which is precisely the "carries the neck and both clavicles" role.
|
|
103
|
-
*/
|
|
104
|
-
export const HML22_JOINTS = [
|
|
105
|
-
"pelvis", // 0
|
|
106
|
-
"left_hip", // 1
|
|
107
|
-
"right_hip", // 2
|
|
108
|
-
"spine1", // 3
|
|
109
|
-
"left_knee", // 4
|
|
110
|
-
"right_knee", // 5
|
|
111
|
-
"spine2", // 6
|
|
112
|
-
"left_ankle", // 7
|
|
113
|
-
"right_ankle", // 8
|
|
114
|
-
"spine3", // 9
|
|
115
|
-
"left_foot", // 10 ← TOE BASE, not the ankle
|
|
116
|
-
"right_foot", // 11 ← TOE BASE, not the ankle
|
|
117
|
-
"neck", // 12
|
|
118
|
-
"left_collar", // 13 ← CLAVICLE (cskel27 "LeftShoulder")
|
|
119
|
-
"right_collar", // 14
|
|
120
|
-
"head", // 15
|
|
121
|
-
"left_shoulder", // 16 ← UPPER ARM (cskel27 "LeftArm")
|
|
122
|
-
"right_shoulder", // 17
|
|
123
|
-
"left_elbow", // 18
|
|
124
|
-
"right_elbow", // 19
|
|
125
|
-
"left_wrist", // 20
|
|
126
|
-
"right_wrist", // 21
|
|
127
|
-
];
|
|
128
|
-
|
|
129
|
-
/** Parent index per hml22 joint, expanded from t2m_kinematic_chain. */
|
|
130
|
-
export const HML22_PARENTS = [
|
|
131
|
-
null, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 12, 13, 14, 16, 17, 18, 19,
|
|
132
|
-
];
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* cskel27 joint name → the hml22 joint that drives it, or null for "this joint
|
|
136
|
-
* is not authored by the source".
|
|
137
|
-
*
|
|
138
|
-
* TORSO ANCHORING is structural, not cosmetic, exactly as in the soma77 case.
|
|
139
|
-
* Hips↔pelvis is the root in both. cskel27's Spine3 carries the neck AND both
|
|
140
|
-
* clavicles; hml22's `spine3` (9) carries `neck` (12) and both collars (13,14).
|
|
141
|
-
* So Spine3↔spine3 is FORCED. That leaves three cskel27 links (Spine, Spine1,
|
|
142
|
-
* Spine2) spanning the two hml22 links (spine1, spine2) between the anchors, so
|
|
143
|
-
* exactly one cskel27 spine joint must go unauthored. Spine2 is the one chosen,
|
|
144
|
-
* for the same reason soma77-to-cskel27 chose it: mapping bottom-up keeps the
|
|
145
|
-
* lower spine — where the large trunk rotation lives — driven by real data and
|
|
146
|
-
* pushes the identity link to the top of the chain, where the residual
|
|
147
|
-
* Spine2→Spine3 rotation is absorbed into Spine3's local anyway.
|
|
148
|
-
*
|
|
149
|
-
* The hand ends and thumbs stay unauthored: hml22 stops at the wrist. Borrowing
|
|
150
|
-
* anything would invent finger articulation the model never generated.
|
|
151
|
-
*/
|
|
152
|
-
export const CSKEL27_FROM_HML22 = {
|
|
153
|
-
Hips: "pelvis",
|
|
154
|
-
Spine: "spine1",
|
|
155
|
-
Spine1: "spine2",
|
|
156
|
-
Spine2: null,
|
|
157
|
-
Spine3: "spine3",
|
|
158
|
-
Neck: "neck",
|
|
159
|
-
Head: "head",
|
|
160
|
-
|
|
161
|
-
RightShoulder: "right_collar",
|
|
162
|
-
RightArm: "right_shoulder",
|
|
163
|
-
RightForeArm: "right_elbow",
|
|
164
|
-
RightHand: "right_wrist",
|
|
165
|
-
RightHandEnd: null,
|
|
166
|
-
RightHandThumb1: null,
|
|
167
|
-
|
|
168
|
-
LeftShoulder: "left_collar",
|
|
169
|
-
LeftArm: "left_shoulder",
|
|
170
|
-
LeftForeArm: "left_elbow",
|
|
171
|
-
LeftHand: "left_wrist",
|
|
172
|
-
LeftHandEnd: null,
|
|
173
|
-
LeftHandThumb1: null,
|
|
174
|
-
|
|
175
|
-
RightUpLeg: "right_hip",
|
|
176
|
-
RightLeg: "right_knee",
|
|
177
|
-
RightFoot: "right_ankle",
|
|
178
|
-
RightToeBase: "right_foot",
|
|
179
|
-
|
|
180
|
-
LeftUpLeg: "left_hip",
|
|
181
|
-
LeftLeg: "left_knee",
|
|
182
|
-
LeftFoot: "left_ankle",
|
|
183
|
-
LeftToeBase: "left_foot",
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Per mapped cskel27 joint, the DESCENDANT whose bone direction determines this
|
|
188
|
-
* joint's global rotation, plus (where a second independent direction exists)
|
|
189
|
-
* the pair whose difference pins the twist.
|
|
190
|
-
*
|
|
191
|
-
* `aim` names a cskel27 joint, but the direction is measured between the two
|
|
192
|
-
* joints' HML22 SOURCES — which is why Spine1 aims at Spine3 and not Spine2:
|
|
193
|
-
* Spine2 is unauthored, so in the reconstructed skeleton G_Spine2 === G_Spine1
|
|
194
|
-
* and the EFFECTIVE bone leaving Spine1 is Spine1→Spine3.
|
|
195
|
-
*
|
|
196
|
-
* A null entry is a joint whose own orientation positions cannot observe (no
|
|
197
|
-
* mapped child below it): Head, both Hands, both ToeBases. They inherit their
|
|
198
|
-
* parent's global, so their local is identity — and their POSITION is still
|
|
199
|
-
* exact, because it is produced by the parent's aim.
|
|
200
|
-
*/
|
|
201
|
-
const AIM = {
|
|
202
|
-
Hips: { aim: "Spine", twistRef: ["LeftUpLeg", "RightUpLeg"] },
|
|
203
|
-
Spine: { aim: "Spine1" },
|
|
204
|
-
Spine1: { aim: "Spine3" },
|
|
205
|
-
Spine2: null,
|
|
206
|
-
Spine3: { aim: "Neck", twistRef: ["LeftShoulder", "RightShoulder"] },
|
|
207
|
-
Neck: { aim: "Head" },
|
|
208
|
-
Head: null,
|
|
209
|
-
|
|
210
|
-
LeftShoulder: { aim: "LeftArm" },
|
|
211
|
-
LeftArm: { aim: "LeftForeArm" },
|
|
212
|
-
LeftForeArm: { aim: "LeftHand" },
|
|
213
|
-
LeftHand: null,
|
|
214
|
-
LeftHandEnd: null,
|
|
215
|
-
LeftHandThumb1: null,
|
|
216
|
-
|
|
217
|
-
RightShoulder: { aim: "RightArm" },
|
|
218
|
-
RightArm: { aim: "RightForeArm" },
|
|
219
|
-
RightForeArm: { aim: "RightHand" },
|
|
220
|
-
RightHand: null,
|
|
221
|
-
RightHandEnd: null,
|
|
222
|
-
RightHandThumb1: null,
|
|
223
|
-
|
|
224
|
-
LeftUpLeg: { aim: "LeftLeg" },
|
|
225
|
-
LeftLeg: { aim: "LeftFoot" },
|
|
226
|
-
LeftFoot: { aim: "LeftToeBase" },
|
|
227
|
-
LeftToeBase: null,
|
|
228
|
-
|
|
229
|
-
RightUpLeg: { aim: "RightLeg" },
|
|
230
|
-
RightLeg: { aim: "RightFoot" },
|
|
231
|
-
RightFoot: { aim: "RightToeBase" },
|
|
232
|
-
RightToeBase: null,
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
const IDENTITY = [
|
|
236
|
-
[1, 0, 0],
|
|
237
|
-
[0, 1, 0],
|
|
238
|
-
[0, 0, 1],
|
|
239
|
-
];
|
|
240
|
-
|
|
241
|
-
/** cskel27 joints whose Y decides where the floor is (same set as soma77). */
|
|
242
|
-
const GROUND_JOINTS = ["LeftFoot", "LeftToeBase", "RightFoot", "RightToeBase"];
|
|
243
|
-
|
|
244
|
-
const CSKEL_INDEX = new Map(CSKEL27_JOINTS.map((name, index) => [name, index]));
|
|
245
|
-
const HML_INDEX = new Map(HML22_JOINTS.map((name, index) => [name, index]));
|
|
246
|
-
|
|
247
|
-
/** cskel27 index → hml22 index, or -1 when unauthored. */
|
|
248
|
-
export const CSKEL27_TO_HML22_INDEX = CSKEL27_JOINTS.map((name) => {
|
|
249
|
-
const source = CSKEL27_FROM_HML22[name];
|
|
250
|
-
return source === null || source === undefined ? -1 : HML_INDEX.get(source);
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
/** hml22 index → cskel27 index. Total: every hml22 joint has exactly one target. */
|
|
254
|
-
export const HML22_TO_CSKEL27_INDEX = HML22_JOINTS.map((name) => {
|
|
255
|
-
const target = CSKEL27_JOINTS.findIndex((joint) => CSKEL27_FROM_HML22[joint] === name);
|
|
256
|
-
if (target < 0) {
|
|
257
|
-
throw new Error(`hml22-to-cskel27: hml22 joint ${name} has no cskel27 target`);
|
|
258
|
-
}
|
|
259
|
-
return target;
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Per cskel27 joint, the hml22 index whose orientation it takes. An unmapped
|
|
264
|
-
* joint walks up the cskel27 parent chain to the nearest mapped ancestor, which
|
|
265
|
-
* is what makes its local come out identity. Verbatim the soma77 rule.
|
|
266
|
-
*/
|
|
267
|
-
export function resolveSourceIndices() {
|
|
268
|
-
const resolved = new Array(CSKEL27_JOINTS.length);
|
|
269
|
-
for (let index = 0; index < CSKEL27_JOINTS.length; index += 1) {
|
|
270
|
-
let cursor = index;
|
|
271
|
-
let source = CSKEL27_FROM_HML22[CSKEL27_JOINTS[cursor]];
|
|
272
|
-
while (source == null) {
|
|
273
|
-
cursor = CSKEL27_PARENTS[cursor];
|
|
274
|
-
if (cursor === null) {
|
|
275
|
-
throw new Error(
|
|
276
|
-
`hml22ToCskel27Motion: ${CSKEL27_JOINTS[index]} has no mapped ancestor`
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
|
-
source = CSKEL27_FROM_HML22[CSKEL27_JOINTS[cursor]];
|
|
280
|
-
}
|
|
281
|
-
resolved[index] = HML_INDEX.get(source);
|
|
282
|
-
}
|
|
283
|
-
return resolved;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// ---------------------------------------------------------------------------
|
|
287
|
-
// small vector helpers (convert.js exports matrix maths but no matVec)
|
|
288
|
-
// ---------------------------------------------------------------------------
|
|
289
|
-
|
|
290
|
-
const sub = (a, b) => [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
|
291
|
-
const dot = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
292
|
-
const cross = (a, b) => [
|
|
293
|
-
a[1] * b[2] - a[2] * b[1],
|
|
294
|
-
a[2] * b[0] - a[0] * b[2],
|
|
295
|
-
a[0] * b[1] - a[1] * b[0],
|
|
296
|
-
];
|
|
297
|
-
const norm = (a) => Math.hypot(a[0], a[1], a[2]);
|
|
298
|
-
|
|
299
|
-
function unit(a, label) {
|
|
300
|
-
const length = norm(a);
|
|
301
|
-
if (!(length > 1e-9)) {
|
|
302
|
-
throw new Error(`hml22ToCskel27Motion: degenerate direction for ${label}`);
|
|
303
|
-
}
|
|
304
|
-
return [a[0] / length, a[1] / length, a[2] / length];
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
function matVec(m, v) {
|
|
308
|
-
return [
|
|
309
|
-
m[0][0] * v[0] + m[0][1] * v[1] + m[0][2] * v[2],
|
|
310
|
-
m[1][0] * v[0] + m[1][1] * v[1] + m[1][2] * v[2],
|
|
311
|
-
m[2][0] * v[0] + m[2][1] * v[1] + m[2][2] * v[2],
|
|
312
|
-
];
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/** Component of `v` perpendicular to unit vector `axis`, or null if it vanishes. */
|
|
316
|
-
function rejectFrom(v, axis) {
|
|
317
|
-
if (!v) return null;
|
|
318
|
-
const k = dot(v, axis);
|
|
319
|
-
const out = [v[0] - axis[0] * k, v[1] - axis[1] * k, v[2] - axis[2] * k];
|
|
320
|
-
return norm(out) > 1e-6 ? out : null;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* Minimal ("swing") rotation taking unit vector `from` onto unit vector `to`,
|
|
325
|
-
* returned together with the axis it turned about.
|
|
326
|
-
*
|
|
327
|
-
* THE BRANCH POINT. When `from` and `to` are antiparallel every axis
|
|
328
|
-
* perpendicular to `from` is a valid 180° rotation, and picking a fixed seed
|
|
329
|
-
* (say +X) makes the choice jump the moment the bone crosses that
|
|
330
|
-
* configuration — a visible 180° limb flip between two adjacent frames. `hint`
|
|
331
|
-
* is the axis this same joint turned about on the PREVIOUS frame; continuing it
|
|
332
|
-
* is the branch nearest the previous frame, which is exactly the continuity
|
|
333
|
-
* rule this converter promises. The fixed seed survives only as the
|
|
334
|
-
* first-frame fallback, where there is no previous frame to be near.
|
|
335
|
-
*/
|
|
336
|
-
function swingWithAxis(from, to, hint, label) {
|
|
337
|
-
const cosine = Math.max(-1, Math.min(1, dot(from, to)));
|
|
338
|
-
if (cosine > 1 - 1e-12) {
|
|
339
|
-
// Already aligned: identity, and the previous axis stays the live hint so
|
|
340
|
-
// a momentary alignment does not erase the branch we have been following.
|
|
341
|
-
return { matrix: IDENTITY.map((row) => row.slice()), axis: hint ?? null };
|
|
342
|
-
}
|
|
343
|
-
let axis;
|
|
344
|
-
if (cosine < -1 + 1e-9) {
|
|
345
|
-
const continued = rejectFrom(hint, from);
|
|
346
|
-
const seed = continued ?? (Math.abs(from[0]) < 0.9 ? [1, 0, 0] : [0, 1, 0]);
|
|
347
|
-
axis = continued
|
|
348
|
-
? unit(continued, `${label} antiparallel continuation`)
|
|
349
|
-
: unit(cross(from, seed), `${label} antiparallel swing`);
|
|
350
|
-
} else {
|
|
351
|
-
axis = unit(cross(from, to), `${label} swing`);
|
|
352
|
-
}
|
|
353
|
-
const sine = Math.sqrt(Math.max(0, 1 - cosine * cosine));
|
|
354
|
-
const t = 1 - cosine;
|
|
355
|
-
const [x, y, z] = axis;
|
|
356
|
-
return {
|
|
357
|
-
matrix: [
|
|
358
|
-
[t * x * x + cosine, t * x * y - sine * z, t * x * z + sine * y],
|
|
359
|
-
[t * x * y + sine * z, t * y * y + cosine, t * y * z - sine * x],
|
|
360
|
-
[t * x * z - sine * y, t * y * z + sine * x, t * z * z + cosine],
|
|
361
|
-
],
|
|
362
|
-
axis,
|
|
363
|
-
};
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* Orthonormal frame (as a rotation matrix, columns e1/e2/e3) from two vectors.
|
|
368
|
-
* `hint` is the previous frame's e2 for this joint: it only comes into play when
|
|
369
|
-
* the secondary collapses onto the primary (a body folded so the hip axis lies
|
|
370
|
-
* along the spine), which is the other branch point, resolved the same way.
|
|
371
|
-
*/
|
|
372
|
-
function frameOf(primary, secondary, label, hint) {
|
|
373
|
-
const e1 = unit(primary, `${label} primary`);
|
|
374
|
-
let projected = rejectFrom(secondary, e1);
|
|
375
|
-
if (!projected) projected = rejectFrom(hint, e1);
|
|
376
|
-
if (!projected) {
|
|
377
|
-
throw new Error(`hml22ToCskel27Motion: ${label} secondary is parallel to its primary`);
|
|
378
|
-
}
|
|
379
|
-
const e2 = unit(projected, `${label} secondary`);
|
|
380
|
-
const e3 = cross(e1, e2);
|
|
381
|
-
return [
|
|
382
|
-
[e1[0], e2[0], e3[0]],
|
|
383
|
-
[e1[1], e2[1], e3[1]],
|
|
384
|
-
[e1[2], e2[2], e3[2]],
|
|
385
|
-
];
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
// ---------------------------------------------------------------------------
|
|
389
|
-
// 27 → 22 (downsample). Positions only.
|
|
390
|
-
//
|
|
391
|
-
// This is the direction the BOX is fed: ProjFlow's preservation rows are the
|
|
392
|
-
// source take's own frames expressed in hml22 space, so the driver needs the
|
|
393
|
-
// inverse of this file. It is also what the verify harness uses to manufacture
|
|
394
|
-
// an hml22 fixture out of a real cclay take without a HumanML3D download.
|
|
395
|
-
// Pure index gather — lossless by construction, since every hml22 joint has a
|
|
396
|
-
// cskel27 counterpart.
|
|
397
|
-
// ---------------------------------------------------------------------------
|
|
398
|
-
|
|
399
|
-
/**
|
|
400
|
-
* @param {{frames?:number, posedJoints:ArrayLike<number>}} input cskel27 [T,27,3]
|
|
401
|
-
* @returns {Float32Array} hml22 positions, [T,22,3]
|
|
402
|
-
*/
|
|
403
|
-
export function cskel27ToHml22Positions({ frames, posedJoints }) {
|
|
404
|
-
if (!(ArrayBuffer.isView(posedJoints) || Array.isArray(posedJoints))) {
|
|
405
|
-
throw new Error("cskel27ToHml22Positions: posedJoints must be an array of numbers");
|
|
406
|
-
}
|
|
407
|
-
const count = frames ?? posedJoints.length / (27 * 3);
|
|
408
|
-
if (!Number.isInteger(count) || count < 1) {
|
|
409
|
-
throw new Error(
|
|
410
|
-
`cskel27ToHml22Positions: posedJoints length ${posedJoints.length} is not a whole number of 27-joint frames`
|
|
411
|
-
);
|
|
412
|
-
}
|
|
413
|
-
if (posedJoints.length !== count * 27 * 3) {
|
|
414
|
-
throw new Error(
|
|
415
|
-
`cskel27ToHml22Positions: posedJoints must be ${count * 27 * 3} long, got ${posedJoints.length}`
|
|
416
|
-
);
|
|
417
|
-
}
|
|
418
|
-
const out = new Float32Array(count * 22 * 3);
|
|
419
|
-
for (let frame = 0; frame < count; frame += 1) {
|
|
420
|
-
const src = frame * 27 * 3;
|
|
421
|
-
const dst = frame * 22 * 3;
|
|
422
|
-
for (let joint = 0; joint < 22; joint += 1) {
|
|
423
|
-
const source = HML22_TO_CSKEL27_INDEX[joint];
|
|
424
|
-
out[dst + joint * 3] = posedJoints[src + source * 3];
|
|
425
|
-
out[dst + joint * 3 + 1] = posedJoints[src + source * 3 + 1];
|
|
426
|
-
out[dst + joint * 3 + 2] = posedJoints[src + source * 3 + 2];
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
return out;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
// ---------------------------------------------------------------------------
|
|
433
|
-
// 22 → 27, pose level. These are the building blocks the motion loop composes;
|
|
434
|
-
// they are exported so a test can pin one frame's behaviour (and one frame's
|
|
435
|
-
// continuity carry) without going through the whole clip.
|
|
436
|
-
// ---------------------------------------------------------------------------
|
|
437
|
-
|
|
438
|
-
/**
|
|
439
|
-
* The canonical cskel27 rest body re-expressed at hml22 indices — the rest
|
|
440
|
-
* directions every aim is measured against.
|
|
441
|
-
* @param {{posed_joints:number[][]}} reference
|
|
442
|
-
* @returns {number[][]} 22 rest positions
|
|
443
|
-
*/
|
|
444
|
-
export function restPositionsHml22(reference) {
|
|
445
|
-
const rest = new Array(22);
|
|
446
|
-
for (let joint = 0; joint < 22; joint += 1) {
|
|
447
|
-
rest[joint] = reference.posed_joints[HML22_TO_CSKEL27_INDEX[joint]].slice();
|
|
448
|
-
}
|
|
449
|
-
return rest;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
/**
|
|
453
|
-
* One frame of hml22 positions → 27 GLOBAL rotation matrices.
|
|
454
|
-
*
|
|
455
|
-
* @param {number[][]} observed 22 world positions for this frame
|
|
456
|
-
* @param {object} options
|
|
457
|
-
* @param {number[][]} options.rest 22 rest positions (restPositionsHml22)
|
|
458
|
-
* @param {{swingAxis:Array, frameSecondary:Array}} [options.carry]
|
|
459
|
-
* the previous frame's branch state; pass the returned `carry` straight
|
|
460
|
-
* back in on the next frame to keep the clip continuous.
|
|
461
|
-
* @param {number} [options.frame] frame index, for error messages only
|
|
462
|
-
* @returns {{globals:number[][][], carry:{swingAxis:Array, frameSecondary:Array}}}
|
|
463
|
-
*/
|
|
464
|
-
export function liftHml22PoseGlobals(observed, { rest, carry = null, frame = 0 } = {}) {
|
|
465
|
-
if (!Array.isArray(observed) || observed.length !== 22) {
|
|
466
|
-
throw new Error("liftHml22PoseGlobals: observed must be 22 [x,y,z] positions");
|
|
467
|
-
}
|
|
468
|
-
if (!Array.isArray(rest) || rest.length !== 22) {
|
|
469
|
-
throw new Error("liftHml22PoseGlobals: rest must be 22 [x,y,z] positions");
|
|
470
|
-
}
|
|
471
|
-
const globals = new Array(27);
|
|
472
|
-
const swingAxis = new Array(27).fill(null);
|
|
473
|
-
const frameSecondary = new Array(27).fill(null);
|
|
474
|
-
|
|
475
|
-
// cskel27 indices are topologically ordered (parent < child) — asserted in
|
|
476
|
-
// the verify file — so one ascending pass suffices.
|
|
477
|
-
for (let joint = 0; joint < 27; joint += 1) {
|
|
478
|
-
const name = CSKEL27_JOINTS[joint];
|
|
479
|
-
const parent = CSKEL27_PARENTS[joint];
|
|
480
|
-
const spec = AIM[name];
|
|
481
|
-
const parentGlobal = parent === null ? IDENTITY : globals[parent];
|
|
482
|
-
|
|
483
|
-
if (!spec) {
|
|
484
|
-
// Unauthored joint, or a mapped joint with no mapped child: borrow the
|
|
485
|
-
// nearest mapped ancestor's global ⇒ identity local.
|
|
486
|
-
globals[joint] = parentGlobal.map((row) => row.slice());
|
|
487
|
-
continue;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
const label = `${name} (frame ${frame})`;
|
|
491
|
-
const self = CSKEL27_TO_HML22_INDEX[joint];
|
|
492
|
-
const aim = CSKEL27_TO_HML22_INDEX[CSKEL_INDEX.get(spec.aim)];
|
|
493
|
-
const restDir = unit(sub(rest[aim], rest[self]), `${label} rest bone`);
|
|
494
|
-
const observedDir = unit(sub(observed[aim], observed[self]), `${label} observed bone`);
|
|
495
|
-
|
|
496
|
-
if (spec.twistRef) {
|
|
497
|
-
// Two independent directions ⇒ the full orientation, twist and all.
|
|
498
|
-
const [first, second] = spec.twistRef;
|
|
499
|
-
const a = CSKEL27_TO_HML22_INDEX[CSKEL_INDEX.get(first)];
|
|
500
|
-
const b = CSKEL27_TO_HML22_INDEX[CSKEL_INDEX.get(second)];
|
|
501
|
-
const restFrame = frameOf(restDir, sub(rest[a], rest[b]), `${label} rest frame`);
|
|
502
|
-
const observedFrame = frameOf(
|
|
503
|
-
observedDir,
|
|
504
|
-
sub(observed[a], observed[b]),
|
|
505
|
-
`${label} observed frame`,
|
|
506
|
-
carry?.frameSecondary?.[joint]
|
|
507
|
-
);
|
|
508
|
-
frameSecondary[joint] = [observedFrame[0][1], observedFrame[1][1], observedFrame[2][1]];
|
|
509
|
-
globals[joint] = matMul(observedFrame, matTranspose(restFrame));
|
|
510
|
-
} else {
|
|
511
|
-
// Aim + inherited twist.
|
|
512
|
-
const aimed = unit(matVec(parentGlobal, restDir), `${label} aimed rest bone`);
|
|
513
|
-
const swung = swingWithAxis(aimed, observedDir, carry?.swingAxis?.[joint], label);
|
|
514
|
-
swingAxis[joint] = swung.axis;
|
|
515
|
-
globals[joint] = matMul(swung.matrix, parentGlobal);
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
return { globals, carry: { swingAxis, frameSecondary } };
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
/**
|
|
522
|
-
* 27 global rotations → 27 LOCAL rotations. The soma77 rule, unchanged: whatever
|
|
523
|
-
* sits between two mapped joints composes into the child's local, and an
|
|
524
|
-
* unauthored joint (which borrowed its parent's global) comes out identity.
|
|
525
|
-
* @param {number[][][]} globals
|
|
526
|
-
* @returns {number[][][]}
|
|
527
|
-
*/
|
|
528
|
-
export function globalsToCskel27Locals(globals) {
|
|
529
|
-
const locals = new Array(27);
|
|
530
|
-
for (let joint = 0; joint < 27; joint += 1) {
|
|
531
|
-
const parent = CSKEL27_PARENTS[joint];
|
|
532
|
-
locals[joint] =
|
|
533
|
-
parent === null
|
|
534
|
-
? globals[joint].map((row) => row.slice())
|
|
535
|
-
: matMul(matTranspose(globals[parent]), globals[joint]);
|
|
536
|
-
}
|
|
537
|
-
return locals;
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
/**
|
|
541
|
-
* Full single-frame convert: hml22 positions → cskel27 rotations + posed joints.
|
|
542
|
-
* The motion path calls exactly this per frame; a test can call it on one pose.
|
|
543
|
-
*
|
|
544
|
-
* @param {number[][]} observed 22 world positions
|
|
545
|
-
* @param {object} options
|
|
546
|
-
* @param {number[][]} options.rest
|
|
547
|
-
* @param {number[][]} options.boneOffsets canonical cskel27 bone offsets
|
|
548
|
-
* @param {number} [options.rootScale=1]
|
|
549
|
-
* @param {object} [options.carry]
|
|
550
|
-
* @param {number} [options.frame]
|
|
551
|
-
* @returns {{globals, locals, root:number[], posed:number[][], carry:object}}
|
|
552
|
-
*/
|
|
553
|
-
export function hml22PoseToCskel27(observed, { rest, boneOffsets, rootScale = 1, carry = null, frame = 0 }) {
|
|
554
|
-
const lifted = liftHml22PoseGlobals(observed, { rest, carry, frame });
|
|
555
|
-
const locals = globalsToCskel27Locals(lifted.globals);
|
|
556
|
-
const pelvis = observed[HML_INDEX.get("pelvis")];
|
|
557
|
-
const root = [pelvis[0] * rootScale, pelvis[1] * rootScale, pelvis[2] * rootScale];
|
|
558
|
-
// FK on OUR body, never the source's bone lengths: the take has to move
|
|
559
|
-
// CozyClay's figure, not drag HumanML3D's average proportions into the scene.
|
|
560
|
-
const posed = forwardKinematics(locals, boneOffsets, root);
|
|
561
|
-
return { globals: lifted.globals, locals, root, posed, carry: lifted.carry };
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
// ---------------------------------------------------------------------------
|
|
565
|
-
// 22 → 27, motion level. THE C8 ENTRY POINT.
|
|
566
|
-
// ---------------------------------------------------------------------------
|
|
567
|
-
|
|
568
|
-
function chainLengthHml(positions, names) {
|
|
569
|
-
let total = 0;
|
|
570
|
-
for (let step = 1; step < names.length; step += 1) {
|
|
571
|
-
total += norm(sub(positions[HML_INDEX.get(names[step])], positions[HML_INDEX.get(names[step - 1])]));
|
|
572
|
-
}
|
|
573
|
-
return total;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
function chainLengthCskel(positions, names) {
|
|
577
|
-
let total = 0;
|
|
578
|
-
for (let step = 1; step < names.length; step += 1) {
|
|
579
|
-
total += norm(sub(positions[CSKEL_INDEX.get(names[step])], positions[CSKEL_INDEX.get(names[step - 1])]));
|
|
580
|
-
}
|
|
581
|
-
return total;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
/**
|
|
585
|
-
* ProjFlow motion → the cclay motion arrays.
|
|
586
|
-
*
|
|
587
|
-
* @param {object} raw
|
|
588
|
-
* @param {Float32Array|number[]} raw.positions hml22 joint positions, [T,22,3]
|
|
589
|
-
* flattened, world metres, Y-up. ACMDM's own output tensor.
|
|
590
|
-
* @param {number} [raw.fps=20] source rate. NOT retimed here (see header).
|
|
591
|
-
* @param {number} [raw.frames] optional; derived from `positions` when absent,
|
|
592
|
-
* cross-checked against it when present.
|
|
593
|
-
* @param {object} [raw.reference] {local_rot_mats, posed_joints} cskel27 rest
|
|
594
|
-
* (default: canonicalCskel27Reference())
|
|
595
|
-
* @param {boolean} [raw.scaleRoot=true] scale the root trajectory by the
|
|
596
|
-
* leg-length ratio so stride survives reproportioning (soma77's rule)
|
|
597
|
-
* @param {boolean} [raw.ground=true] one rigid floor shift for the clip
|
|
598
|
-
* (soma77's rule; see the note at the bottom of this function)
|
|
599
|
-
* @returns {{frames:number, fps:number, rotMats:Float32Array, rootPos:Float32Array, posedJoints:Float32Array}}
|
|
600
|
-
* rotMats are LOCAL rotations, [T,27,3,3] flattened row-major — the exact
|
|
601
|
-
* five fields soma77ToCskel27Motion returns, nothing more.
|
|
602
|
-
*/
|
|
603
|
-
export function hml22ToCskel27Motion(raw) {
|
|
604
|
-
if (!raw || typeof raw !== "object") {
|
|
605
|
-
throw new Error("hml22ToCskel27Motion: expected an object { positions, fps }");
|
|
606
|
-
}
|
|
607
|
-
const {
|
|
608
|
-
positions,
|
|
609
|
-
fps = 20,
|
|
610
|
-
frames: declaredFrames,
|
|
611
|
-
reference = canonicalCskel27Reference(),
|
|
612
|
-
scaleRoot = true,
|
|
613
|
-
ground = true,
|
|
614
|
-
} = raw;
|
|
615
|
-
|
|
616
|
-
// `ArrayBuffer.isView || Array.isArray` and not a duck-typed `.length` check:
|
|
617
|
-
// a string has a length too, and would sail past the shape check to die
|
|
618
|
-
// later as a NaN with a misleading joint name.
|
|
619
|
-
if (!(ArrayBuffer.isView(positions) || Array.isArray(positions))) {
|
|
620
|
-
throw new Error(
|
|
621
|
-
"hml22ToCskel27Motion: positions must be a Float32Array or number[] of [T,22,3] world metres"
|
|
622
|
-
);
|
|
623
|
-
}
|
|
624
|
-
if (positions.length === 0 || positions.length % (22 * 3) !== 0) {
|
|
625
|
-
throw new Error(
|
|
626
|
-
`hml22ToCskel27Motion: positions length ${positions.length} is not a whole number of 22-joint frames (multiple of 66)`
|
|
627
|
-
);
|
|
628
|
-
}
|
|
629
|
-
const frames = positions.length / (22 * 3);
|
|
630
|
-
if (declaredFrames !== undefined && declaredFrames !== frames) {
|
|
631
|
-
throw new Error(
|
|
632
|
-
`hml22ToCskel27Motion: frames says ${declaredFrames} but positions holds ${frames} frames`
|
|
633
|
-
);
|
|
634
|
-
}
|
|
635
|
-
if (!Number.isInteger(fps) || fps < 1) {
|
|
636
|
-
throw new Error(`hml22ToCskel27Motion: fps must be a positive integer, got ${fps}`);
|
|
637
|
-
}
|
|
638
|
-
// Scan once up front rather than letting a NaN propagate into a rotation
|
|
639
|
-
// matrix, where it would surface a thousand lines downstream as an empty
|
|
640
|
-
// viewport instead of as "frame 61 left_wrist z is NaN".
|
|
641
|
-
for (let index = 0; index < positions.length; index += 1) {
|
|
642
|
-
if (!Number.isFinite(positions[index])) {
|
|
643
|
-
const frame = Math.floor(index / (22 * 3));
|
|
644
|
-
const joint = Math.floor((index % (22 * 3)) / 3);
|
|
645
|
-
throw new Error(
|
|
646
|
-
`hml22ToCskel27Motion: positions[${index}] is ${positions[index]} ` +
|
|
647
|
-
`(frame ${frame}, joint ${joint} ${HML22_JOINTS[joint]}, axis ${"xyz"[index % 3]})`
|
|
648
|
-
);
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
const boneOffsets = deriveBoneOffsets(reference.posed_joints, reference.local_rot_mats);
|
|
653
|
-
const rest = restPositionsHml22(reference);
|
|
654
|
-
|
|
655
|
-
// Leg-length ratio, measured on the source's own first frame against the
|
|
656
|
-
// reference body, so a stride generated for HumanML3D's average body covers
|
|
657
|
-
// the same fraction of a step on ours. Same rule as soma77ToCskel27Motion.
|
|
658
|
-
const firstFrame = [];
|
|
659
|
-
for (let joint = 0; joint < 22; joint += 1) {
|
|
660
|
-
firstFrame.push([positions[joint * 3], positions[joint * 3 + 1], positions[joint * 3 + 2]]);
|
|
661
|
-
}
|
|
662
|
-
const sourceLeg = chainLengthHml(firstFrame, ["left_hip", "left_knee", "left_ankle"]);
|
|
663
|
-
const referenceLeg = chainLengthCskel(reference.posed_joints, ["LeftUpLeg", "LeftLeg", "LeftFoot"]);
|
|
664
|
-
if (!(sourceLeg > 0)) {
|
|
665
|
-
throw new Error("hml22ToCskel27Motion: source leg length measured as zero");
|
|
666
|
-
}
|
|
667
|
-
const rootScale = scaleRoot ? referenceLeg / sourceLeg : 1;
|
|
668
|
-
|
|
669
|
-
const rotMats = new Float32Array(frames * 27 * 9);
|
|
670
|
-
const rootPos = new Float32Array(frames * 3);
|
|
671
|
-
const outPosed = new Float32Array(frames * 27 * 3);
|
|
672
|
-
let floorY = Infinity;
|
|
673
|
-
let carry = null;
|
|
674
|
-
|
|
675
|
-
for (let frame = 0; frame < frames; frame += 1) {
|
|
676
|
-
const base = frame * 22 * 3;
|
|
677
|
-
const observed = new Array(22);
|
|
678
|
-
for (let joint = 0; joint < 22; joint += 1) {
|
|
679
|
-
observed[joint] = [
|
|
680
|
-
positions[base + joint * 3],
|
|
681
|
-
positions[base + joint * 3 + 1],
|
|
682
|
-
positions[base + joint * 3 + 2],
|
|
683
|
-
];
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
// `carry` threads the previous frame's branch choices in and this frame's
|
|
687
|
-
// out — the whole of the temporal-continuity guarantee lives on this line.
|
|
688
|
-
const pose = hml22PoseToCskel27(observed, { rest, boneOffsets, rootScale, carry, frame });
|
|
689
|
-
carry = pose.carry;
|
|
690
|
-
|
|
691
|
-
for (const name of GROUND_JOINTS) {
|
|
692
|
-
const y = pose.posed[CSKEL_INDEX.get(name)][1];
|
|
693
|
-
if (y < floorY) floorY = y;
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
const rotBase = frame * 27 * 9;
|
|
697
|
-
for (let joint = 0; joint < 27; joint += 1) {
|
|
698
|
-
const m = pose.locals[joint];
|
|
699
|
-
for (let row = 0; row < 3; row += 1) {
|
|
700
|
-
for (let col = 0; col < 3; col += 1) {
|
|
701
|
-
rotMats[rotBase + joint * 9 + row * 3 + col] = m[row][col];
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
rootPos[frame * 3] = pose.root[0];
|
|
706
|
-
rootPos[frame * 3 + 1] = pose.root[1];
|
|
707
|
-
rootPos[frame * 3 + 2] = pose.root[2];
|
|
708
|
-
const posBase = frame * 27 * 3;
|
|
709
|
-
for (let joint = 0; joint < 27; joint += 1) {
|
|
710
|
-
outPosed[posBase + joint * 3] = pose.posed[joint][0];
|
|
711
|
-
outPosed[posBase + joint * 3 + 1] = pose.posed[joint][1];
|
|
712
|
-
outPosed[posBase + joint * 3 + 2] = pose.posed[joint][2];
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
// ONE RIGID FLOOR SHIFT for the whole clip, so the lowest foot sample of the
|
|
717
|
-
// clip touches Y=0. This is soma77ToCskel27Motion's convention copied
|
|
718
|
-
// deliberately and it is a SPLICING requirement, not a cosmetic one: a
|
|
719
|
-
// ProjFlow line edit gets spliced back into a Kimodo/ARDY take, and if the two
|
|
720
|
-
// converters grounded differently the figure would step up or down at the
|
|
721
|
-
// seam. Per-frame grounding is the wrong answer for the same reason it is
|
|
722
|
-
// there: it would flatten the arc of a jump, which is exactly the motion this
|
|
723
|
-
// pipeline exists to stage.
|
|
724
|
-
if (ground && Number.isFinite(floorY) && floorY !== 0) {
|
|
725
|
-
for (let frame = 0; frame < frames; frame += 1) {
|
|
726
|
-
rootPos[frame * 3 + 1] -= floorY;
|
|
727
|
-
const posBase = frame * 27 * 3;
|
|
728
|
-
for (let joint = 0; joint < 27; joint += 1) outPosed[posBase + joint * 3 + 1] -= floorY;
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
return { frames, fps, rotMats, rootPos, posedJoints: outPosed };
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
export { IDENTITY as CSKEL27_IDENTITY };
|