cozyclay 2.1.1 → 3.0.1

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