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.
Files changed (583) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +12 -30
  3. package/bin/agent/agent-routes.mjs +40 -113
  4. package/bin/agent/agent-runner.mjs +9 -10
  5. package/bin/agent/cliproxy-provider.mjs +135 -0
  6. package/bin/agent/credential-store.mjs +30 -7
  7. package/bin/agent/pi-tools.mjs +4 -1
  8. package/bin/agent/provider-keys.mjs +32 -0
  9. package/bin/agent/provider-login.mjs +110 -0
  10. package/bin/agent/provider-routes.mjs +99 -0
  11. package/bin/agent/providers.mjs +19 -97
  12. package/bin/agent/script-tool.mjs +17 -4
  13. package/bin/agent/studio-motion-reference.mjs +23 -0
  14. package/bin/agent/studio-prompt.mjs +1 -1
  15. package/bin/agent/studio-rebase-text.mjs +32 -0
  16. package/bin/agent/studio-rebase.mjs +26 -39
  17. package/bin/agent/studio-tools.mjs +9 -28
  18. package/bin/agent/tool-arguments.mjs +49 -0
  19. package/bin/agent/turn-frames.mjs +66 -0
  20. package/bin/codex-auth.mjs +9 -2
  21. package/bin/cozyclay.mjs +10 -116
  22. package/bin/live/cli.mjs +22 -6
  23. package/bin/mcp-runtime.mjs +2 -12
  24. package/bin/package-signature.mjs +0 -4
  25. package/dist/app/index.html +7 -6
  26. package/dist/assets/EXRLoader-CvVIvGVE.js +1 -0
  27. package/dist/assets/analytics-C9sUnndd.js +1 -0
  28. package/dist/assets/app-CCh--1Hc.js +5087 -0
  29. package/dist/assets/app-f-fkV5uP.css +1 -0
  30. package/dist/assets/compile-worker-CkxiD6lH.js +5 -0
  31. package/dist/assets/fflate.module-KbGj5-H6.js +5 -0
  32. package/dist/assets/{first-shot-handoff-BlT33CGf.js → first-shot-handoff-CC4xe4HM.js} +1 -1
  33. package/dist/assets/{landing-BSesrCpL.js → landing-DjHD8u7k.js} +1 -1
  34. package/dist/assets/scene-asset-sync-Hx_FzdOH.js +20 -0
  35. package/dist/assets/scene-asset-sync-_S8NQl5B.css +1 -0
  36. package/dist/assets/workflow-C4yKH1p4.js +179 -0
  37. package/dist/cozyclay-package.json +1 -1
  38. package/dist/index.html +3 -3
  39. package/dist/llms.txt +14 -0
  40. package/dist/sw.js +3 -6
  41. package/dist/workflow/index.html +5 -4
  42. package/mcp/LIVE-PROTOCOL.md +7 -47
  43. package/mcp/README.md +16 -26
  44. package/mcp/live-hub.mjs +6 -10
  45. package/mcp/package.json +1 -3
  46. package/mcp/tool-handlers.mjs +4 -168
  47. package/package.json +3 -22
  48. package/src/App.jsx +174 -2639
  49. package/src/agent-progress.js +79 -0
  50. package/src/agent-turn-mark.js +30 -0
  51. package/src/analytics.js +1 -19
  52. package/src/app-context.js +5 -0
  53. package/src/app-stage.jsx +2 -34
  54. package/src/asset-pane.jsx +6 -5
  55. package/src/camera-block.js +21 -0
  56. package/src/camera-direction.js +120 -0
  57. package/src/character-kind.js +3 -9
  58. package/src/command-bus.js +8 -6
  59. package/src/commands/cast.js +5 -34
  60. package/src/commands/elements/character.js +1 -1
  61. package/src/commands/elements/motion.js +1 -6
  62. package/src/commands/elements/object.js +6 -2
  63. package/src/commands/elements/shot.js +4 -1
  64. package/src/commands/elements.js +38 -14
  65. package/src/commands/index.js +3 -1
  66. package/src/commands/kits.js +37 -0
  67. package/src/commands/motion-timing.js +57 -0
  68. package/src/commands/motion.js +9 -152
  69. package/src/commands/motion3-compile.js +65 -0
  70. package/src/commands/motion3.js +207 -0
  71. package/src/commands/objects.js +6 -4
  72. package/src/commands/shot.js +49 -11
  73. package/src/commands/stage.js +14 -1
  74. package/src/commands/view.js +4 -1
  75. package/src/document-mirrors.js +42 -0
  76. package/src/document-store.js +4 -0
  77. package/src/domains/cast.js +19 -194
  78. package/src/domains/motion/motion-range-pin-rules.js +1 -1
  79. package/src/domains/motion/take-source.js +51 -0
  80. package/src/domains/motion.js +60 -1838
  81. package/src/domains/scenes.js +12 -55
  82. package/src/domains/shots.js +2 -2
  83. package/src/domains/stage.js +4 -2
  84. package/src/draft-ghost.js +46 -0
  85. package/src/draft-ghost.jsx +129 -0
  86. package/src/dualview.jsx +4 -3
  87. package/src/effects/EffectsLayer.jsx +112 -0
  88. package/src/effects/effect-meshes.js +232 -0
  89. package/src/effects/effect-record.js +103 -0
  90. package/src/effects/emitter.js +59 -0
  91. package/src/effects/simulate.js +261 -0
  92. package/src/effects/state.js +59 -0
  93. package/src/execution-telemetry.js +2 -3
  94. package/src/first-success-guide.jsx +5 -5
  95. package/src/ghost-set.js +257 -0
  96. package/src/held-object-edit.jsx +113 -0
  97. package/src/held-object-pose.js +175 -0
  98. package/src/hierarchy-model.js +1 -1
  99. package/src/hierarchy-panel.jsx +1 -1
  100. package/src/imported-mesh.jsx +103 -0
  101. package/src/kits/held.js +84 -0
  102. package/src/kits/index.js +70 -0
  103. package/src/kits/smoking.js +57 -0
  104. package/src/light-blockers.js +93 -0
  105. package/src/motion/beat-board-model.js +84 -0
  106. package/src/motion/beat-board.css +124 -0
  107. package/src/motion/beat-board.jsx +59 -0
  108. package/src/motion/center-of-mass.js +66 -0
  109. package/src/motion/fix-collisions.js +850 -0
  110. package/src/motion/physics-review.js +118 -0
  111. package/src/{ardy → motion}/physics-support.js +1 -76
  112. package/src/motion/rig-snapshot.js +22 -0
  113. package/src/motion/take-segments.jsx +140 -0
  114. package/src/{ardy → motion}/timeline-coordinates.js +0 -21
  115. package/src/{ardy → motion}/timeline.css +1 -2
  116. package/src/{ardy → motion}/timeline.jsx +48 -419
  117. package/src/motion/waypoints.js +76 -0
  118. package/src/motion3/beats.js +83 -0
  119. package/src/motion3/body.js +158 -0
  120. package/src/motion3/bones.js +47 -0
  121. package/src/motion3/carried.js +47 -0
  122. package/src/motion3/compile-job.js +75 -0
  123. package/src/motion3/compile-worker.js +10 -0
  124. package/src/motion3/compiler/anchors.js +105 -0
  125. package/src/motion3/compiler/arm-orient.js +128 -0
  126. package/src/motion3/compiler/channels.js +125 -0
  127. package/src/motion3/compiler/compile.js +205 -0
  128. package/src/motion3/compiler/gait-rhythm.js +97 -0
  129. package/src/motion3/compiler/grip.js +171 -0
  130. package/src/motion3/compiler/head-steady.js +48 -0
  131. package/src/motion3/compiler/hold.js +134 -0
  132. package/src/motion3/compiler/idle.js +113 -0
  133. package/src/motion3/compiler/limbs.js +170 -0
  134. package/src/motion3/compiler/math.js +72 -0
  135. package/src/motion3/compiler/pose.js +162 -0
  136. package/src/motion3/compiler/quat.js +49 -0
  137. package/src/motion3/compiler/skeleton.js +74 -0
  138. package/src/motion3/compiler/validate-common.js +32 -0
  139. package/src/motion3/compiler/validate-goals.js +118 -0
  140. package/src/motion3/compiler/validate-objects.js +143 -0
  141. package/src/motion3/compiler/validate-parts.js +116 -0
  142. package/src/motion3/compiler/validate.js +122 -0
  143. package/src/motion3/compiler/vocab.js +73 -0
  144. package/src/motion3/compiler/walk-pelvis.js +275 -0
  145. package/src/motion3/compiler/walk.js +207 -0
  146. package/src/motion3/compiler/wrist-clamp.js +32 -0
  147. package/src/motion3/delta.js +47 -0
  148. package/src/motion3/describe.js +174 -0
  149. package/src/motion3/docs.js +27 -0
  150. package/src/motion3/edit-carry.js +21 -0
  151. package/src/motion3/fix/ease.js +16 -0
  152. package/src/motion3/fix/ground.js +37 -0
  153. package/src/motion3/fix/index.js +52 -0
  154. package/src/motion3/fix/push.js +71 -0
  155. package/src/motion3/fix/reach.js +46 -0
  156. package/src/motion3/fix/retime.js +79 -0
  157. package/src/motion3/fix/self.js +211 -0
  158. package/src/motion3/fix/slide.js +70 -0
  159. package/src/motion3/fix/turn.js +51 -0
  160. package/src/motion3/follow.js +39 -0
  161. package/src/motion3/free-motion.js +173 -0
  162. package/src/motion3/grip-choice.js +130 -0
  163. package/src/motion3/hold-plan.js +157 -0
  164. package/src/motion3/json-patch.js +77 -0
  165. package/src/motion3/key-aim.js +105 -0
  166. package/src/motion3/key-edit.js +82 -0
  167. package/src/motion3/lenient.js +127 -0
  168. package/src/motion3/object-keys.js +236 -0
  169. package/src/motion3/object-pose.js +182 -0
  170. package/src/motion3/object-tracks.js +12 -0
  171. package/src/motion3/pipeline.js +102 -0
  172. package/src/motion3/playback.js +51 -0
  173. package/src/motion3/probes.js +91 -0
  174. package/src/motion3/qa/body-checks.js +34 -0
  175. package/src/motion3/qa/check-names.js +14 -0
  176. package/src/motion3/qa/checks.js +121 -0
  177. package/src/motion3/qa/contact-checks.js +51 -0
  178. package/src/motion3/qa/explain.js +80 -0
  179. package/src/motion3/qa/fmt.js +5 -0
  180. package/src/motion3/qa/foot-checks.js +72 -0
  181. package/src/motion3/qa/geometry.js +77 -0
  182. package/src/motion3/qa/goals.js +191 -0
  183. package/src/motion3/qa/ground.js +60 -0
  184. package/src/motion3/qa/hints-contact.js +179 -0
  185. package/src/motion3/qa/hints-goal.js +160 -0
  186. package/src/motion3/qa/hints-knee.js +24 -0
  187. package/src/motion3/qa/hints-motion.js +209 -0
  188. package/src/motion3/qa/hints-object.js +91 -0
  189. package/src/motion3/qa/index.js +42 -0
  190. package/src/motion3/qa/key-checks.js +82 -0
  191. package/src/motion3/qa/near.js +96 -0
  192. package/src/motion3/qa/object-checks.js +135 -0
  193. package/src/motion3/qa/patch.js +161 -0
  194. package/src/motion3/qa/severity.js +38 -0
  195. package/src/motion3/qa/spec-checks.js +65 -0
  196. package/src/motion3/qa/style.js +218 -0
  197. package/src/motion3/qa/wrist-checks.js +69 -0
  198. package/src/motion3/qa/wrist-obs.js +60 -0
  199. package/src/motion3/refs.js +120 -0
  200. package/src/motion3/repair-tune.js +121 -0
  201. package/src/motion3/repair.js +83 -0
  202. package/src/motion3/report.js +67 -0
  203. package/src/motion3/rigs/index.js +14 -0
  204. package/src/motion3/rigs/x-bot-tpose-body.json +1 -0
  205. package/src/motion3/rigs/x-bot-tpose.json +1 -0
  206. package/src/motion3/rigs/y-bot-tpose-body.json +1 -0
  207. package/src/motion3/rigs/y-bot-tpose.json +1 -0
  208. package/src/motion3/scene.js +60 -0
  209. package/src/motion3/schema/brief.js +89 -0
  210. package/src/motion3/schema/fields.js +44 -0
  211. package/src/motion3/schema/spec-examples.js +46 -0
  212. package/src/motion3/schema/topics.js +111 -0
  213. package/src/motion3/schema/types.js +147 -0
  214. package/src/motion3/search.js +117 -0
  215. package/src/motion3/second-hand.js +112 -0
  216. package/src/motion3/skills/basic.js +82 -0
  217. package/src/motion3/skills/book.js +76 -0
  218. package/src/motion3/skills/car.js +145 -0
  219. package/src/motion3/skills/frames-auto.js +29 -0
  220. package/src/motion3/skills/index.js +83 -0
  221. package/src/motion3/skills/info.js +65 -0
  222. package/src/motion3/skills/smoke-kit.js +109 -0
  223. package/src/motion3/skills/smoke.js +160 -0
  224. package/src/motion3/spec-store.js +59 -0
  225. package/src/motion3/take-edit.js +155 -0
  226. package/src/motion3/take.js +63 -0
  227. package/src/motion3/timeline.js +127 -0
  228. package/src/motion3/views.js +121 -0
  229. package/src/motion3/walk-plan.js +119 -0
  230. package/src/object-catalog.jsx +7 -1
  231. package/src/object-colors.js +98 -0
  232. package/src/object-gizmo-parts.js +115 -0
  233. package/src/object-gizmo.jsx +37 -122
  234. package/src/object-glow.js +139 -0
  235. package/src/object-hidden.js +33 -0
  236. package/src/object-look.js +196 -0
  237. package/src/object-size.js +10 -0
  238. package/src/object-travel.js +71 -6
  239. package/src/panels/CameraPanel.jsx +7 -7
  240. package/src/panels/HdrEnvironmentControls.jsx +59 -0
  241. package/src/panels/LightPanel.jsx +25 -4
  242. package/src/panels/ObjectLookPanel.jsx +88 -0
  243. package/src/panels/PoseImagePanel.jsx +1 -1
  244. package/src/panels/PosePanel.jsx +2 -17
  245. package/src/panels/RigControlPanel.jsx +5 -53
  246. package/src/panels/SubjectBox.jsx +4 -1
  247. package/src/panels/details.css +14 -0
  248. package/src/posestudio.jsx +4 -27
  249. package/src/project-browser.jsx +3 -4
  250. package/src/project-resources.js +18 -31
  251. package/src/project.js +13 -104
  252. package/src/props.jsx +25 -280
  253. package/src/rail-dot-drag.js +23 -0
  254. package/src/render-passes.js +10 -0
  255. package/src/resource-status.jsx +1 -0
  256. package/src/result-modal.jsx +4 -19
  257. package/src/room.jsx +20 -12
  258. package/src/scene-assets.js +8 -6
  259. package/src/scene-light-place.js +72 -0
  260. package/src/scene-lights.jsx +148 -0
  261. package/src/scene-objects.js +31 -139
  262. package/src/scenes.js +15 -105
  263. package/src/semantic-edit.js +2 -5
  264. package/src/set-pieces.jsx +183 -0
  265. package/src/settings-menu.jsx +3 -11
  266. package/src/shell/BottomDock.jsx +11 -53
  267. package/src/shell/CompositionGuideButton.jsx +20 -0
  268. package/src/shell/DetailsSlot.jsx +16 -200
  269. package/src/shell/GhostsMenuGroup.jsx +74 -0
  270. package/src/shell/LibrarySlot.jsx +1 -3
  271. package/src/shell/MenuBar.jsx +3 -3
  272. package/src/shell/PreferencesDialog.jsx +15 -264
  273. package/src/shell/PreferencesSlot.jsx +2 -8
  274. package/src/shell/ShotPreviewHeader.jsx +17 -0
  275. package/src/shell/TopBar.jsx +5 -83
  276. package/src/shell/ViewportToolbar.jsx +23 -93
  277. package/src/shell/agent-glass.css +19 -6
  278. package/src/shell/details-selection.js +42 -0
  279. package/src/shell/ghosts-menu.css +22 -0
  280. package/src/shell/glass-regions.css +29 -3
  281. package/src/shell/preferences.css +6 -1
  282. package/src/shell/viewport.css +12 -0
  283. package/src/shot-authoring.js +4 -7
  284. package/src/soft-shadows.js +37 -0
  285. package/src/stage-attach-frames.js +12 -12
  286. package/src/stage-canvas-rigs.jsx +3 -3
  287. package/src/stage-capture.jsx +42 -8
  288. package/src/stage-characters.jsx +22 -216
  289. package/src/stage-editor-gizmos.jsx +6 -6
  290. package/src/stage-environment.js +93 -0
  291. package/src/stage-hdr-builtins.js +107 -0
  292. package/src/stage-hdr-image.js +116 -0
  293. package/src/stage-hdr-library.js +70 -0
  294. package/src/stage-hdr-map.js +94 -0
  295. package/src/stage-hdr.js +130 -0
  296. package/src/stage-lighting.js +149 -0
  297. package/src/stage-occlusion.js +210 -0
  298. package/src/stage-sky.jsx +159 -0
  299. package/src/stage-startup-defaults.js +5 -9
  300. package/src/studio-actions.js +12 -14
  301. package/src/studio-agent-commands.js +46 -16
  302. package/src/studio-agent-motion.js +9 -9
  303. package/src/studio-agent-protocol.js +46 -73
  304. package/src/studio-app-binding.js +29 -45
  305. package/src/studio-asset-list.js +17 -0
  306. package/src/studio-contact-sheet.js +37 -5
  307. package/src/studio-elements.js +27 -7
  308. package/src/studio-entity-bounds.js +18 -0
  309. package/src/studio-error-codes.js +8 -0
  310. package/src/studio-export-framing.js +152 -0
  311. package/src/studio-lighting-check.js +103 -0
  312. package/src/studio-motion-visual.js +150 -0
  313. package/src/studio-schema-errors.js +80 -0
  314. package/src/studio-support-gap.js +74 -0
  315. package/src/styles.css +1 -19
  316. package/src/timeline-defaults.js +5 -0
  317. package/src/timeline-extent.js +8 -20
  318. package/src/tutorial-walk.js +17 -0
  319. package/src/ui-scrub.js +1 -1
  320. package/src/use-shot-camera-tracks.js +12 -0
  321. package/src/view-ghosts.js +97 -0
  322. package/src/view-ghosts.jsx +49 -0
  323. package/src/workflow/AgentPanel.jsx +47 -166
  324. package/src/workflow/AgentProgressCard.jsx +107 -0
  325. package/src/workflow/AgentReplyText.jsx +37 -0
  326. package/src/workflow/AgentTurnSteps.jsx +84 -0
  327. package/src/workflow/LoginPanel.jsx +140 -0
  328. package/src/workflow/WorkflowBuilder.jsx +7 -23
  329. package/src/workflow/agent-client.js +54 -51
  330. package/src/workflow/agent-panel.css +71 -1
  331. package/src/workflow/agent-progress-card.css +167 -0
  332. package/src/workflow/agent-progress-view.js +47 -0
  333. package/src/workflow/agent-turn-steps.css +113 -0
  334. package/src/workflow/agent-turn.js +191 -0
  335. package/src/workflow/login-client.js +34 -0
  336. package/src/workflow/motion-input.js +18 -31
  337. package/src/workflow/node-schema.js +1 -1
  338. package/src/workflow/reply-markdown.js +69 -0
  339. package/src/workflow/scene-asset-sync.js +8 -8
  340. package/tools/agent-session-stats.mjs +61 -0
  341. package/tools/dev-full.mjs +5 -56
  342. package/tools/eslint-baseline.json +12 -13
  343. package/tools/headless-studio.mjs +85 -0
  344. package/tools/knip-baseline.json +0 -179
  345. package/tools/loc-baseline.json +33 -83
  346. package/tools/motion3/extract-body.mjs +103 -0
  347. package/tools/motion3/extract-rig.mjs +85 -0
  348. package/tools/process-supervisor.mjs +0 -88
  349. package/tools/run-tests.mjs +9 -124
  350. package/bin/agent/motion-runtime.mjs +0 -76
  351. package/dist/ardy/cskel27-rest.json +0 -557
  352. package/dist/assets/analytics-B41FmPYI.js +0 -1
  353. package/dist/assets/app-Do-Og5Pu.css +0 -1
  354. package/dist/assets/app-DwP-tl2x.js +0 -4920
  355. package/dist/assets/config-Bx7pYsD9.js +0 -1
  356. package/dist/assets/config-DeoFTYzb.css +0 -1
  357. package/dist/assets/demo-Dt0qZfS7.js +0 -1
  358. package/dist/assets/scene-asset-sync-BCWDJB1d.js +0 -19
  359. package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
  360. package/dist/assets/ticket-jqMhqL88.js +0 -1
  361. package/dist/assets/workflow-Br-K7Srs.js +0 -179
  362. package/dist/demo/walk-then-stop.npz +0 -0
  363. package/mcp/ardy-prompts.mjs +0 -218
  364. package/src/ardy/auto-fix-panel.css +0 -108
  365. package/src/ardy/auto-physics.js +0 -1194
  366. package/src/ardy/client.js +0 -153
  367. package/src/ardy/convert.js +0 -313
  368. package/src/ardy/cskel27-neutral.js +0 -39
  369. package/src/ardy/cskel27.js +0 -77
  370. package/src/ardy/export.js +0 -165
  371. package/src/ardy/fix-collisions.js +0 -2257
  372. package/src/ardy/foot-lock.js +0 -230
  373. package/src/ardy/motion-calibration.js +0 -118
  374. package/src/ardy/motion-edit.js +0 -213
  375. package/src/ardy/motion-url.js +0 -55
  376. package/src/ardy/mp4-duration.js +0 -102
  377. package/src/ardy/npz.js +0 -595
  378. package/src/ardy/physics-panel.css +0 -21
  379. package/src/ardy/physics-panel.jsx +0 -89
  380. package/src/ardy/physics-refine.js +0 -111
  381. package/src/ardy/physics-review.js +0 -567
  382. package/src/ardy/physics-temporal.js +0 -54
  383. package/src/ardy/platform-fit-panel.css +0 -64
  384. package/src/ardy/platform-fit-panel.jsx +0 -41
  385. package/src/ardy/platform-fit.js +0 -326
  386. package/src/ardy/playback.js +0 -711
  387. package/src/ardy/pose-pin.js +0 -97
  388. package/src/ardy/prompt-clips.js +0 -30
  389. package/src/ardy/retime.js +0 -211
  390. package/src/ardy/root-drop.js +0 -369
  391. package/src/ardy/timeline-resize.js +0 -13
  392. package/src/ardy/to-cskel27.js +0 -225
  393. package/src/ardy/trim.js +0 -33
  394. package/src/ardy/waypoints.js +0 -369
  395. package/src/ardy-bridge-contract.js +0 -281
  396. package/src/domains/motion/motion-video-capture.js +0 -664
  397. package/src/i2v-motion-client.js +0 -77
  398. package/src/i2v-motion-studio.jsx +0 -180
  399. package/src/line-edit.js +0 -1348
  400. package/src/motion/generation.js +0 -105
  401. package/src/motion-readiness-ui.jsx +0 -67
  402. package/src/motion-readiness.js +0 -40
  403. package/src/motion-resources.js +0 -177
  404. package/src/motion-store.js +0 -65
  405. package/src/motion-trail.js +0 -601
  406. package/src/multimodel-ingest.js +0 -394
  407. package/src/panels/PromptBlocksPanel.jsx +0 -444
  408. package/src/panels/VideoCapturePanel.jsx +0 -181
  409. package/src/pose-extract/browser.js +0 -42
  410. package/src/pose-extract/detector.js +0 -100
  411. package/src/pose-extract/fit.js +0 -227
  412. package/src/pose-extract/image-frame.js +0 -51
  413. package/src/pose-extract/index.js +0 -58
  414. package/src/pose-extract/keyframes.js +0 -74
  415. package/src/pose-extract/landmarks.js +0 -162
  416. package/src/pose-extract/mirror.js +0 -153
  417. package/src/pose-extract/take.js +0 -131
  418. package/src/pose-extract/video-frames.js +0 -91
  419. package/src/take-recipe.js +0 -165
  420. package/src/trail-key-conflicts.js +0 -39
  421. package/src/trail-pick.js +0 -59
  422. package/src/use-i2v-capture-modal.js +0 -15
  423. package/tools/ardy/BRIDGE.md +0 -37
  424. package/tools/ardy/GVHMR-NPZ.md +0 -68
  425. package/tools/ardy/README.md +0 -124
  426. package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
  427. package/tools/ardy/artifacts.mjs +0 -34
  428. package/tools/ardy/bridge.mjs +0 -1732
  429. package/tools/ardy/bvh-cskel27.mjs +0 -1265
  430. package/tools/ardy/cclay_constrained_generate.py +0 -561
  431. package/tools/ardy/cclay_gvhmr_worker.py +0 -264
  432. package/tools/ardy/cclay_motion_edit.py +0 -510
  433. package/tools/ardy/cclay_sequence_generate.py +0 -705
  434. package/tools/ardy/cclay_worker.py +0 -304
  435. package/tools/ardy/dump-npz.py +0 -205
  436. package/tools/ardy/extract-rest.mjs +0 -299
  437. package/tools/ardy/extract.mjs +0 -507
  438. package/tools/ardy/footage.mjs +0 -467
  439. package/tools/ardy/gvhmr-floor.mjs +0 -102
  440. package/tools/ardy/gvhmr-to-cskel27.mjs +0 -21
  441. package/tools/ardy/gvhmr_fastpath.py +0 -168
  442. package/tools/ardy/gvhmr_trajectory.py +0 -240
  443. package/tools/ardy/merge-text-encoder.py +0 -160
  444. package/tools/ardy/mocap-metrics.mjs +0 -200
  445. package/tools/ardy/mocap-quality-gate.mjs +0 -110
  446. package/tools/ardy/motion-stabilize.mjs +0 -348
  447. package/tools/ardy/npz.mjs +0 -414
  448. package/tools/ardy/pose-to-npz.mjs +0 -106
  449. package/tools/ardy/prompt-limits.mjs +0 -5
  450. package/tools/ardy/run-edit-on-box.sh +0 -73
  451. package/tools/ardy/run-local.mjs +0 -233
  452. package/tools/ardy/run-on-box.sh +0 -593
  453. package/tools/ardy/run-sequence-on-box.sh +0 -177
  454. package/tools/ardy/runners/gvhmr-worker.mjs +0 -206
  455. package/tools/ardy/runners/index.mjs +0 -18
  456. package/tools/ardy/runners/local.mjs +0 -455
  457. package/tools/ardy/runners/proc.mjs +0 -125
  458. package/tools/ardy/runners/remote.mjs +0 -252
  459. package/tools/ardy/setup-local.mjs +0 -189
  460. package/tools/ardy/setup-text-encoder-on-box.sh +0 -93
  461. package/tools/ardy/setup-text-encoder.py +0 -306
  462. package/tools/ardy/smpl-cskel27.mjs +0 -178
  463. package/tools/ardy/visual-qa.mjs +0 -196
  464. package/tools/ardy/vq-car.mjs +0 -52
  465. package/tools/bench/EXP3.md +0 -61
  466. package/tools/bench/cclay_bench_extract_incam.py +0 -125
  467. package/tools/bench/cclay_bench_extract_obs.py +0 -204
  468. package/tools/bench/cclay_bench_runner.py +0 -42
  469. package/tools/bench/cube-contact.mjs +0 -162
  470. package/tools/bench/exp3.mjs +0 -152
  471. package/tools/bench/extract-bench-lib.mjs +0 -216
  472. package/tools/bench/extract-bench.mjs +0 -302
  473. package/tools/bench/fal-generate.mjs +0 -88
  474. package/tools/bench/fit/README.md +0 -189
  475. package/tools/bench/fit/camera.mjs +0 -59
  476. package/tools/bench/fit/contact.mjs +0 -419
  477. package/tools/bench/fit/footlock.mjs +0 -234
  478. package/tools/bench/fit/motion.mjs +0 -85
  479. package/tools/bench/fit/pin.mjs +0 -27
  480. package/tools/bench/fit/remote.mjs +0 -105
  481. package/tools/bench/fit-bench.mjs +0 -126
  482. package/tools/bench/fit-sanity.mjs +0 -45
  483. package/tools/bench/metrics.mjs +0 -313
  484. package/tools/bench/obs/depth.mjs +0 -258
  485. package/tools/bench/obs/extrinsics.mjs +0 -175
  486. package/tools/bench/obs/fit_mannequin_betas.py +0 -340
  487. package/tools/bench/obs/ground.mjs +0 -294
  488. package/tools/bench/obs/heading.mjs +0 -151
  489. package/tools/bench/obs/ladder.mjs +0 -555
  490. package/tools/bench/obs/mannequin-betas.json +0 -124
  491. package/tools/bench/obs/remote.mjs +0 -83
  492. package/tools/bench/obs/rest_joints.py +0 -52
  493. package/tools/bench/obs/ybot-targets.mjs +0 -49
  494. package/tools/bench/obs-bench.mjs +0 -520
  495. package/tools/bench/score.mjs +0 -343
  496. package/tools/bench/summarize.mjs +0 -86
  497. package/tools/demo-worker/README.md +0 -137
  498. package/tools/demo-worker/api-client.mjs +0 -233
  499. package/tools/demo-worker/generate.mjs +0 -301
  500. package/tools/demo-worker/index.mjs +0 -466
  501. package/tools/demo-worker/sign-core.mjs +0 -112
  502. package/tools/demo-worker/sign.mjs +0 -161
  503. package/tools/gt-render/browser.mjs +0 -159
  504. package/tools/gt-render/camera-math.mjs +0 -276
  505. package/tools/gt-render/page.mjs +0 -267
  506. package/tools/gt-render/render.mjs +0 -514
  507. package/tools/gt-render/scene-box.mjs +0 -31
  508. package/tools/gt-render/take-transform.mjs +0 -87
  509. package/tools/kimodo/compare-seam.mjs +0 -108
  510. package/tools/kimodo/constraints.mjs +0 -244
  511. package/tools/kimodo/edit.mjs +0 -259
  512. package/tools/kimodo/effector-constraints.mjs +0 -200
  513. package/tools/kimodo/generate.mjs +0 -646
  514. package/tools/kimodo/local-output.mjs +0 -265
  515. package/tools/kimodo/measure-pose.mjs +0 -65
  516. package/tools/kimodo/measure-preserve.mjs +0 -899
  517. package/tools/kimodo/measure-waypoints.mjs +0 -55
  518. package/tools/kimodo/mlx-generate.py +0 -111
  519. package/tools/kimodo/pose-constraints.mjs +0 -248
  520. package/tools/kimodo/preserve-mask.mjs +0 -543
  521. package/tools/kimodo/read-npz.mjs +0 -189
  522. package/tools/kimodo/run-edit-on-box.mjs +0 -155
  523. package/tools/kimodo/run-sequence-on-box.mjs +0 -178
  524. package/tools/kimodo/runner.mjs +0 -284
  525. package/tools/kimodo/seam-batch.mjs +0 -77
  526. package/tools/kimodo/setup-local.mjs +0 -30
  527. package/tools/kimodo/setup-on-box.sh +0 -339
  528. package/tools/kimodo/soma77-to-cskel27.mjs +0 -305
  529. package/tools/morphgs/assets/gen_truth.py +0 -39
  530. package/tools/morphgs/assets/mesh_ori_rig.txt +0 -27
  531. package/tools/morphgs/assets/playback-check.mjs +0 -30
  532. package/tools/morphgs/demo-gate.sh +0 -26
  533. package/tools/morphgs/fbx2morphgs.mjs +0 -68
  534. package/tools/morphgs/morphgs-to-cskel27.mjs +0 -105
  535. package/tools/morphgs/patches/preprocess_src-none-mode.patch +0 -76
  536. package/tools/morphgs/setup-on-cluster.sh +0 -126
  537. package/tools/projflow/driver.py +0 -1069
  538. package/tools/projflow/generate.mjs +0 -805
  539. package/tools/projflow/hml22-to-cskel27.mjs +0 -735
  540. package/tools/projflow/line-edit-job.mjs +0 -547
  541. package/tools/projflow/replay.mjs +0 -452
  542. package/tools/projflow/runner.mjs +0 -269
  543. package/tools/projflow/service.mjs +0 -742
  544. package/tools/track/DESIGN.md +0 -513
  545. package/tools/track/backfill-provenance.mjs +0 -116
  546. package/tools/track/budget.mjs +0 -8
  547. package/tools/track/check-rig.mjs +0 -209
  548. package/tools/track/diagnostics.schema.json +0 -60
  549. package/tools/track/export-rig.mjs +0 -256
  550. package/tools/track/fallback.mjs +0 -7
  551. package/tools/track/fk-parity-fixture.mjs +0 -161
  552. package/tools/track/gate.mjs +0 -349
  553. package/tools/track/masks.mjs +0 -318
  554. package/tools/track/metrics.mjs +0 -201
  555. package/tools/track/publish-obs.mjs +0 -108
  556. package/tools/track/py/check_env.py +0 -61
  557. package/tools/track/py/eval_lr.py +0 -229
  558. package/tools/track/py/lr_viterbi.py +0 -294
  559. package/tools/track/py/masks.py +0 -405
  560. package/tools/track/py/objective.py +0 -382
  561. package/tools/track/py/rig.py +0 -508
  562. package/tools/track/py/scene.py +0 -326
  563. package/tools/track/py/test_joint_indices.py +0 -123
  564. package/tools/track/py/test_lr_viterbi.py +0 -195
  565. package/tools/track/py/test_masks.py +0 -229
  566. package/tools/track/py/test_rig.py +0 -288
  567. package/tools/track/py/test_scene.py +0 -354
  568. package/tools/track/py/test_track.py +0 -328
  569. package/tools/track/py/track.py +0 -411
  570. package/tools/track/remote.mjs +0 -140
  571. package/tools/track/rig-dump.mjs +0 -227
  572. package/tools/track/run-box-tests.mjs +0 -14
  573. package/tools/track/run-box.mjs +0 -168
  574. package/tools/track/setup-box.sh +0 -59
  575. package/tools/track/study-2d.mjs +0 -728
  576. /package/src/{ardy → motion}/collision-blockers.js +0 -0
  577. /package/src/{ardy → motion}/ground.js +0 -0
  578. /package/src/{ardy → motion}/ik-drag.js +0 -0
  579. /package/src/{ardy → motion}/ik-key-json.js +0 -0
  580. /package/src/{ardy → motion}/ik.js +0 -0
  581. /package/src/{ardy → motion}/physics-surface.js +0 -0
  582. /package/src/{ardy → motion}/playback-clock.js +0 -0
  583. /package/src/{ardy → motion}/range-pin.js +0 -0
@@ -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
- }