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
package/src/props.jsx CHANGED
@@ -5,204 +5,28 @@
5
5
  * world" (a car to lean on, to walk past, to frame behind glass) has nothing
6
6
  * to work with. These props are built from primitives in the same clay style
7
7
  * as the Room and the character tint, so the whole frame stays one
8
- * consistent maquette. Dimensions are metres against real vehicle sizes so
9
- * the 1.8 m figure keeps honest scale.
8
+ * consistent maquette. The hand-built set pieces live in set-pieces.jsx;
9
+ * practical lights and glow in scene-lights.jsx and object-glow.js.
10
10
  */
11
11
 
12
12
  import { useEffect, useMemo, useRef, useState } from "react";
13
13
  import { useFrame } from "@react-three/fiber";
14
14
  import { objectTransformAt } from "./object-path.js";
15
- import { sceneObjectCarryMatrixAt, sceneObjectTravelMatrixAt, travelPose } from "./object-travel.js";
15
+ import { sceneObjectCarryMatrixAt, sceneObjectTravelMatrixAt, trackShown, travelPose } from "./object-travel.js";
16
16
  import * as THREE from "three";
17
17
  import { GIZMO_LAYER } from "./dualview.jsx";
18
18
  import { CUTOUT_KIND, MESH_KIND, EMPTY_KIND, EMPTY_MARKER_SIZE, SCULPT_KIND } from "./scene-objects.js";
19
19
  import { buildSculptGroup, disposeSculptGroup } from "./sculpt-geometry.js";
20
20
  import { subscribeToAssetTexture } from "./scene-asset-cache.js";
21
- import { subscribeToMeshScene } from "./scene-mesh-cache.js";
22
- import { cloneMeshGraph } from "./mesh-graph-clone.js";
21
+ import { ImportedMesh } from "./imported-mesh.jsx";
22
+ import { EffectMarker, EffectsLayer } from "./effects/EffectsLayer.jsx";
23
+ import { EFFECT_KIND } from "./effects/effect-record.js";
23
24
  import { applyPoseFade } from "./pose-fade.js";
24
25
  import { coplanarDepthRanks } from "./coplanar-depth.js";
25
-
26
- const CLAY_CAR = "#d98770";
27
- const CLAY_CAR_TOP = "#e49a84";
28
- const CLAY_TIRE = "#41484c";
29
- const CLAY_RIM = "#7c8588";
30
- const CLAY_GLASS = "#55697a";
31
- const CLAY_PLANE = "#7896a4";
32
- const CLAY_PLANE_TRIM = "#e1a849";
33
- const CLAY_CHAIR = "#b9855d";
34
- const CLAY_CHAIR_LIGHT = "#cf9d72";
35
-
36
- function Wheel({ position }) {
37
- return (
38
- <group position={position}>
39
- <mesh rotation={[0, 0, Math.PI / 2]}>
40
- <cylinderGeometry args={[0.33, 0.33, 0.24, 20]} />
41
- <meshStandardMaterial color={CLAY_TIRE} roughness={0.95} />
42
- </mesh>
43
- <mesh rotation={[0, 0, Math.PI / 2]}>
44
- <cylinderGeometry args={[0.17, 0.17, 0.26, 16]} />
45
- <meshStandardMaterial color={CLAY_RIM} roughness={0.5} metalness={0.35} />
46
- </mesh>
47
- </group>
48
- );
49
- }
50
- /**
51
- * A generic 5-door-ish sedan silhouette, ~4.5 m long. Origin at the centre
52
- * of the footprint, +Z forward.
53
- */
54
- export function Car({ position = [0, 0, 0], rotY = 0, color = CLAY_CAR, topColor = CLAY_CAR_TOP, autoColor = undefined }) {
55
- const bodyMaterial = autoColor
56
- ? { ...autoFlat(autoColor) }
57
- : { color, roughness: 0.55, metalness: 0.25 };
58
- const topMaterial = autoColor
59
- ? { ...autoFlat(autoColor) }
60
- : { color: topColor, roughness: 0.5, metalness: 0.2 };
61
- return (
62
- <group position={position} rotation={[0, rotY, 0]}>
63
- <mesh position={[0, 0.62, 0]}>
64
- <boxGeometry args={[1.78, 0.62, 4.45]} />
65
- <meshStandardMaterial {...bodyMaterial} />
66
- </mesh>
67
- <mesh position={[0, 1.12, -0.15]}>
68
- <boxGeometry args={[1.58, 0.5, 2.2]} />
69
- <meshStandardMaterial {...topMaterial} />
70
- </mesh>
71
- <mesh position={[0, 1.14, -0.15]}>
72
- <boxGeometry args={[1.62, 0.3, 1.9]} />
73
- <meshStandardMaterial color={CLAY_GLASS} roughness={0.15} metalness={0.6} />
74
- </mesh>
75
- <mesh position={[0, 1.05, 0.95]} rotation={[0.5, 0, 0]}>
76
- <boxGeometry args={[1.56, 0.42, 0.08]} />
77
- <meshStandardMaterial color={CLAY_GLASS} roughness={0.15} metalness={0.6} />
78
- </mesh>
79
- <mesh position={[0, 1.05, -1.25]} rotation={[-0.55, 0, 0]}>
80
- <boxGeometry args={[1.56, 0.42, 0.08]} />
81
- <meshStandardMaterial color={CLAY_GLASS} roughness={0.15} metalness={0.6} />
82
- </mesh>
83
- <Wheel position={[0.82, 0.33, 1.45]} />
84
- <Wheel position={[-0.82, 0.33, 1.45]} />
85
- <Wheel position={[0.82, 0.33, -1.45]} />
86
- <Wheel position={[-0.82, 0.33, -1.45]} />
87
- </group>
88
- );
89
- }
90
-
91
- /** Compact single-engine propeller plane, ~3.4 m wingspan and +Z forward. */
92
- export function SmallPlane({ position = [0, 0, 0], rotY = 0, autoColor = undefined }) {
93
- // Auto-color mode tints the PRIMARY surfaces only (fuselage, wings, trim);
94
- // glass, tires and rims keep their materials so the silhouette still reads.
95
- // Each mesh keeps its own hand-tuned roughness when the mode is off.
96
- const mat = (color, roughness, metalness) =>
97
- autoColor ? autoFlat(autoColor) : { color, roughness, metalness };
98
- return (
99
- <group position={position} rotation={[0, rotY, 0]}>
100
- <mesh position={[0, 0.76, 0]} rotation={[Math.PI / 2, 0, 0]}>
101
- <cylinderGeometry args={[0.23, 0.31, 2.75, 16]} />
102
- <meshStandardMaterial {...mat(CLAY_PLANE, 0.58, 0.18)} />
103
- </mesh>
104
- <mesh position={[0, 0.76, 1.55]} rotation={[Math.PI / 2, 0, 0]}>
105
- <coneGeometry args={[0.23, 0.55, 16]} />
106
- <meshStandardMaterial {...mat(CLAY_PLANE_TRIM, 0.52, 0.2)} />
107
- </mesh>
108
-
109
- <mesh position={[0, 0.73, 0.15]}>
110
- <boxGeometry args={[3.4, 0.09, 0.58]} />
111
- <meshStandardMaterial {...mat(CLAY_PLANE, 0.62, 0.14)} />
112
- </mesh>
113
- <mesh position={[0, 0.84, -1.18]}>
114
- <boxGeometry args={[1.45, 0.07, 0.38]} />
115
- <meshStandardMaterial {...mat(CLAY_PLANE_TRIM, 0.62, 0.12)} />
116
- </mesh>
117
- <mesh position={[0, 1.08, -1.2]} rotation={[0.22, 0, 0]}>
118
- <boxGeometry args={[0.08, 0.62, 0.48]} />
119
- <meshStandardMaterial {...mat(CLAY_PLANE, 0.62, 0.12)} />
120
- </mesh>
121
-
122
- <mesh position={[0, 1.02, 0.42]} scale={[0.62, 0.46, 0.9]}>
123
- <sphereGeometry args={[0.42, 16, 10, 0, Math.PI * 2, 0, Math.PI / 2]} />
124
- <meshStandardMaterial color={CLAY_GLASS} roughness={0.16} metalness={0.55} />
125
- </mesh>
126
-
127
- <group position={[0, 0.76, 1.86]}>
128
- <mesh rotation={[Math.PI / 2, 0, 0]}>
129
- <cylinderGeometry args={[0.11, 0.11, 0.2, 12]} />
130
- <meshStandardMaterial color={CLAY_RIM} roughness={0.45} metalness={0.4} />
131
- </mesh>
132
- <mesh position={[0, 0, 0.12]} rotation={[0, 0, 0.28]}>
133
- <boxGeometry args={[1.25, 0.08, 0.06]} />
134
- <meshStandardMaterial color={CLAY_TIRE} roughness={0.8} />
135
- </mesh>
136
- </group>
137
-
138
- {[-0.55, 0.55].map((x) => (
139
- <group key={x} position={[x, 0.2, 0.15]}>
140
- <mesh position={[0, 0.24, 0]} rotation={[0, 0, x < 0 ? -0.35 : 0.35]}>
141
- <boxGeometry args={[0.045, 0.55, 0.045]} />
142
- <meshStandardMaterial color={CLAY_RIM} roughness={0.65} metalness={0.3} />
143
- </mesh>
144
- <mesh rotation={[0, 0, Math.PI / 2]}>
145
- <cylinderGeometry args={[0.16, 0.16, 0.09, 14]} />
146
- <meshStandardMaterial color={CLAY_TIRE} roughness={0.95} />
147
- </mesh>
148
- </group>
149
- ))}
150
- </group>
151
- );
152
- }
153
-
154
- export function Chair({ position = [0, 0, 0], rotY = 0, autoColor = undefined }) {
155
- // Auto-color mode tints seat/back and frame together; undefined keeps the
156
- // hand-picked clay pair exactly as it ships.
157
- const seat = autoColor ? autoFlat(autoColor) : { color: CLAY_CHAIR_LIGHT, roughness: 0.86 };
158
- const frame = autoColor ? autoFlat(autoColor) : { color: CLAY_CHAIR, roughness: 0.9 };
159
- const legPositions = [
160
- [-0.24, 0.23, -0.22],
161
- [0.24, 0.23, -0.22],
162
- [-0.24, 0.23, 0.22],
163
- [0.24, 0.23, 0.22],
164
- ];
165
- return (
166
- <group position={position} rotation={[0, rotY, 0]} scale={0.9}>
167
- <mesh position={[0, 0.49, 0]} castShadow receiveShadow>
168
- <boxGeometry args={[0.6, 0.12, 0.58]} />
169
- <meshStandardMaterial {...seat} />
170
- </mesh>
171
- {legPositions.map((leg, index) => (
172
- <mesh key={index} position={leg} castShadow receiveShadow>
173
- <boxGeometry args={[0.09, 0.46, 0.09]} />
174
- <meshStandardMaterial {...frame} />
175
- </mesh>
176
- ))}
177
- <mesh position={[-0.24, 0.92, -0.245]} castShadow receiveShadow>
178
- <boxGeometry args={[0.09, 0.86, 0.09]} />
179
- <meshStandardMaterial {...frame} />
180
- </mesh>
181
- <mesh position={[0.24, 0.92, -0.245]} castShadow receiveShadow>
182
- <boxGeometry args={[0.09, 0.86, 0.09]} />
183
- <meshStandardMaterial {...frame} />
184
- </mesh>
185
- <mesh position={[0, 1.08, -0.245]} castShadow receiveShadow>
186
- <boxGeometry args={[0.52, 0.38, 0.1]} />
187
- <meshStandardMaterial {...seat} />
188
- </mesh>
189
- </group>
190
- );
191
- }
192
-
193
- /**
194
- * The creatable primitives, Unity's 3D Object menu in clay. Each one is built
195
- * with its base on the local floor (y = 0), so an object's `y` reads as height
196
- * above the deck rather than "half of me is underground".
197
- */
198
- // Blender's solid-view response for auto-colored surfaces. Workbench shades
199
- // with a neutral studio rig, so the derived hue reaches the eye unmultiplied;
200
- // under our warm key light a plain standard material turns every hue olive.
201
- // Full roughness kills the specular sheen and a self-light of the same hue
202
- // lifts the shadow side the way workbench's studio lighting does.
203
- function autoFlat(hex) {
204
- return { color: hex, roughness: 1, metalness: 0, emissive: hex, emissiveIntensity: 0.4 };
205
- }
26
+ import { autoFlat, Car, Chair, SmallPlane } from "./set-pieces.jsx";
27
+ import { LIGHT_KIND, capPracticalShadows } from "./object-look.js";
28
+ import { SceneLight, useLookRefresh } from "./scene-lights.jsx";
29
+ import { placeObjectLook } from "./scene-light-place.js";
206
30
 
207
31
  // Parts of an assembly can share a plane with an earlier part (hood top and
208
32
  // grille top). The depth test can't order them and the winner flips as the
@@ -212,6 +36,11 @@ function depthRankProps(rank) {
212
36
  return rank > 0 ? { polygonOffset: true, polygonOffsetFactor: -rank, polygonOffsetUnits: -rank } : null;
213
37
  }
214
38
 
39
+ /**
40
+ * The creatable primitives, Unity's 3D Object menu in clay. Each one is built
41
+ * with its base on the local floor (y = 0), so an object's `y` reads as height
42
+ * above the deck rather than "half of me is underground".
43
+ */
215
44
  function Primitive({ kind, color, autoColor, depthRank = 0 }) {
216
45
  const side = kind === "plane" ? THREE.DoubleSide : THREE.FrontSide;
217
46
  const material = autoColor ? (
@@ -284,97 +113,6 @@ function useAssetTexture(assetId) {
284
113
  * missing): blockout grey, because that is exactly what it is again. */
285
114
  const MISSING_CUTOUT = "#c2c6c8";
286
115
 
287
- /** The parsed GLB for an `assetId`, or null while it loads (or forever, if
288
- * the blob is gone). The cached graph is the FILE's own pivot and materials;
289
- * each instance clones, then fits to the stored height. */
290
- function useMeshScene(assetId) {
291
- const [scene, setScene] = useState(null);
292
- useEffect(() => {
293
- setScene(null);
294
- if (!assetId) return undefined;
295
- return subscribeToMeshScene(assetId, setScene);
296
- }, [assetId]);
297
- return scene;
298
- }
299
-
300
- function disposeOwnedMaterials(root) {
301
- if (!root) return;
302
- root.traverse((node) => {
303
- if (!node.isMesh || !(node.userData?.clayOwned || node.userData?.instanceOwned)) return;
304
- const materials = Array.isArray(node.material) ? node.material : [node.material];
305
- for (const material of materials) material?.dispose?.();
306
- });
307
- }
308
-
309
- /**
310
- * Clone the cached graph, scale so its bbox height equals the stored
311
- * `object.height`, and sit the underside on y = 0. The import heuristic
312
- * already wrote that height — this pass must not re-guess it.
313
- *
314
- * Skinned graphs go through `cloneMeshGraph` so a Mixamo-as-statue keeps
315
- * its bind pose. Clay replaces materials on THIS clone only, so a second
316
- * instance of the same file can keep the textures from the disk. Every clone
317
- * owns its materials, so auto-color tints and opacity fades never leak.
318
- */
319
- function instantiateMesh(source, object) {
320
- const root = cloneMeshGraph(source);
321
- root.updateMatrixWorld(true);
322
- const box = new THREE.Box3().setFromObject(root);
323
- const size = box.getSize(new THREE.Vector3());
324
- const targetHeight = Number(object.height);
325
- if (Number.isFinite(targetHeight) && targetHeight > 0 && size.y > 1e-8) {
326
- root.scale.multiplyScalar(targetHeight / size.y);
327
- root.updateMatrixWorld(true);
328
- box.setFromObject(root);
329
- }
330
- if (Number.isFinite(box.min.y)) root.position.y -= box.min.y;
331
- const clay = object.clay === true;
332
- const clayColor = object.autoColor ?? object.color ?? "#c4b8a8";
333
- root.traverse((node) => {
334
- if (node.isLight || node.isCamera) {
335
- node.visible = false;
336
- return;
337
- }
338
- if (!node.isMesh) return;
339
- node.castShadow = true;
340
- node.receiveShadow = true;
341
- if (clay) {
342
- const make = () => new THREE.MeshStandardMaterial({ color: clayColor, roughness: 0.9, metalness: 0 });
343
- for (const material of Array.isArray(node.material) ? node.material : [node.material]) material?.dispose?.();
344
- node.material = Array.isArray(node.material) ? node.material.map(() => make()) : make();
345
- node.userData.clayOwned = true;
346
- return;
347
- }
348
- if (!object.autoColor || !node.material) return;
349
- // cloneMeshGraph already gave this instance its own materials.
350
- for (const material of Array.isArray(node.material) ? node.material : [node.material]) {
351
- if (material.color) material.color.set(object.autoColor);
352
- }
353
- });
354
- return root;
355
- }
356
-
357
- function ImportedMesh({ object }) {
358
- const source = useMeshScene(object.assetId);
359
- const root = useMemo(
360
- () => (source ? instantiateMesh(source, object) : null),
361
- [source, object.height, object.clay, object.autoColor, object.color],
362
- );
363
- useEffect(() => () => disposeOwnedMaterials(root), [root]);
364
- if (!root) {
365
- const width = object.footprint?.width ?? 1;
366
- const depth = object.footprint?.depth ?? 1;
367
- const height = object.height ?? 1;
368
- return (
369
- <mesh position={[0, height / 2, 0]} castShadow receiveShadow>
370
- <boxGeometry args={[width, height, depth]} />
371
- <meshStandardMaterial color={MISSING_CUTOUT} roughness={0.92} metalness={0} />
372
- </mesh>
373
- );
374
- }
375
- return <primitive object={root} />;
376
- }
377
-
378
116
  /**
379
117
  * A cutout: an imported picture standing on a card, the standee a blockout
380
118
  * gets instead of a modelled prop.
@@ -499,6 +237,8 @@ function SceneObjectContent({ object, depthRank = 0 }) {
499
237
  // override as a cube — file materials are cloned and tinted per instance.
500
238
  const { renderer, color, autoColor } = object;
501
239
  if (renderer === EMPTY_KIND) return <EmptyMarker />;
240
+ if (renderer === LIGHT_KIND) return <SceneLight object={object} />;
241
+ if (renderer === EFFECT_KIND) return <EffectMarker object={object} />;
502
242
  if (renderer === CUTOUT_KIND) return <Cutout object={object} />;
503
243
  if (renderer === MESH_KIND) return <ImportedMesh object={object} />;
504
244
  if (renderer === SCULPT_KIND) return <Sculpt object={object} depthRank={depthRank} />;
@@ -517,7 +257,7 @@ function SceneObjectContent({ object, depthRank = 0 }) {
517
257
  function SelectionBox({ object }) {
518
258
  // An Empty has no extent, so its cage is the marker's own cube, centred on
519
259
  // the origin (a prop's cage stands on it).
520
- const empty = object.renderer === EMPTY_KIND;
260
+ const empty = object.renderer === EMPTY_KIND || object.renderer === LIGHT_KIND || object.renderer === EFFECT_KIND;
521
261
  const height = empty ? EMPTY_MARKER_SIZE : Math.max(object.height ?? 1, 0.08);
522
262
  const width = empty ? EMPTY_MARKER_SIZE : object.footprint?.width ?? 1;
523
263
  const depth = empty ? EMPTY_MARKER_SIZE : object.footprint?.depth ?? 1;
@@ -575,7 +315,7 @@ function SceneObject({ object, selected, frameRef = null, take = null, attachFra
575
315
  const travelled = frameRef && !attach && travelLookupRef?.current
576
316
  ? sceneObjectTravelMatrixAt(travelLookupRef.current, object.id, frame, take ?? {}, placeWorld)
577
317
  : null;
578
- if (travelled) {
318
+ if ((group.visible = trackShown(object.id, frame, take, travelLookupRef?.current)) && travelled) {
579
319
  travelled.decompose(group.position, group.quaternion, group.scale);
580
320
  } else if (attach || object.path) {
581
321
  // The authored numbers first. While attached they are the prop's LOCAL
@@ -617,8 +357,11 @@ function SceneObject({ object, selected, frameRef = null, take = null, attachFra
617
357
  (window.__cclayPropWorld ??= {})[object.id] = { x: group.position.x, y: group.position.y, z: group.position.z, quat: { x: group.quaternion.x, y: group.quaternion.y, z: group.quaternion.z, w: group.quaternion.w }, frame };
618
358
  }
619
359
  applyPoseFade(group, fadeOpacity, object.opacity ?? 1);
360
+ // A light's level, flicker and aim, and a glow, for this frame (scene-light-place.js).
361
+ placeObjectLook(group, object, frame, take);
620
362
  };
621
363
  useFrame(place);
364
+ useLookRefresh(object);
622
365
  // Two things the App needs to reach imperatively, registered per prop: a
623
366
  // placement pass for the recorder (which renders through gl.render() and so
624
367
  // never runs the frame loop — see SetProps' syncRef), and the prop's live
@@ -684,7 +427,8 @@ export function SetProps({ objects = [], authoredObjects = null, selectedId = nu
684
427
  if (worldRef) worldRef.current = (id, out) => registryRef.current.get(id)?.world(out) ?? null;
685
428
  return (
686
429
  <group>
687
- {objects.map((object) => (
430
+ {/* Lights past the practical shadow cap draw without one (object-look.js). */}
431
+ {capPracticalShadows(objects).map((object) => (
688
432
  <SceneObject
689
433
  key={object.id}
690
434
  object={object}
@@ -698,6 +442,7 @@ export function SetProps({ objects = [], authoredObjects = null, selectedId = nu
698
442
  depthRank={depthRanks.get(object.id) ?? 0}
699
443
  />
700
444
  ))}
445
+ <EffectsLayer objects={authoredObjects ?? objects} frameRef={frameRef} take={take} registryRef={registryRef} fadeOpacity={fadeOpacity} />
701
446
  </group>
702
447
  );
703
448
  }
@@ -0,0 +1,25 @@
1
+ // How a QA result reads to a person. QA records one issue per frame and groups them by kind and subject (qa/index.js),
2
+ // so one goal missed on 321 frames is 321 issues but one problem: the user saw "QA 549" for two long groups. People read
3
+ // problems (the groups); qa.total stays the frame-level count the agent, repair and search work with.
4
+ import { ko } from "./locale.js";
5
+
6
+ /** Problems in a QA result: its groups (qa.problems from runMotionQa; an older result counts its groups). A failed result
7
+ * with neither, or with no groups, falls back to its frame-level count, so it never reads as clean. */
8
+ export function qaProblems(qa) {
9
+ const n = qa?.problems ?? (Array.isArray(qa?.groups) ? qa.groups.length : 0);
10
+ return n || (qa?.ok ? 0 : (qa?.total ?? 0));
11
+ }
12
+
13
+ /** "QA clean" | "2 problems" ("QA 통과" | "문제 2개"). */
14
+ export function qaLabel(qa) {
15
+ const n = qaProblems(qa);
16
+ if (qa?.ok || !n) return ko("QA clean", "QA 통과");
17
+ return ko(`${n} problem${n === 1 ? "" : "s"}`, `문제 ${n}개`);
18
+ }
19
+
20
+ /** A receipt's QA phrase: "clean" | "2 problems (321 frame issues)". */
21
+ export function qaSay(qa) {
22
+ if (qa?.ok) return "clean";
23
+ const n = qaProblems(qa), total = qa?.total ?? 0;
24
+ return `${n} problem${n === 1 ? "" : "s"} (${total} frame issue${total === 1 ? "" : "s"})`;
25
+ }
@@ -0,0 +1,23 @@
1
+ // A free drag on a camera rail's dot in the 3D view (CraneHandles): the dot moves over the floor and the rail
2
+ // bends under it, with the weighted bump the X and Z arrows use (prepareRailBend), on both floor axes at once.
3
+ // The drag reads the floor plane, not a plane level with the dot: that one goes edge-on when the editor's eye
4
+ // is at crane height. The dot's height stays on the Y arrow.
5
+ import * as THREE from "three";
6
+ import { prepareRailBend, railPoint } from "./camera-follow.js";
7
+
8
+ const FLOOR = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
9
+
10
+ /**
11
+ * The drag a free press on crane mark `index` starts, shaped like an arrow drag (axis "floor"), or null when it
12
+ * cannot bend the rail: Shift (a slide along the arc), no rail callback or control points, or a ray off the floor.
13
+ * s: CraneHandles' live state { rail, crane, controlPoints, onChangeRail }; ray: the press's ray.
14
+ */
15
+ export function floorDragStart(s, index, ray, shiftKey) {
16
+ const point = s.crane?.points[index];
17
+ if (shiftKey || !point || !s.rail || !s.onChangeRail) return null;
18
+ const bend = prepareRailBend(s.controlPoints, railPoint(s.rail, point.t * s.rail.length), s.crane.points, index);
19
+ const hitStart = new THREE.Vector3();
20
+ if (!bend || !ray.intersectPlane(FLOOR, hitStart)) return null;
21
+ return { axis: "floor", index, dir: new THREE.Vector3(), plane: FLOOR, hitStart, startControls: bend.startControls, bendWeights: bend.weights,
22
+ t0: bend.t0, radius: bend.radius, recorded: false };
23
+ }
@@ -105,6 +105,15 @@ export function renderPass(capture, scene, camera, kind, toDataUrl = null, optio
105
105
  const previousMaterial = scene.overrideMaterial;
106
106
  const previousBackground = scene.background;
107
107
  let buffer = null;
108
+ // A glow halo (userData.beautyOnly) is light, not a surface: drawn with the
109
+ // pass material it would be a solid quad in the depth or normal plate.
110
+ const hidden = [];
111
+ scene.traverse?.((node) => {
112
+ if (node.userData?.beautyOnly && node.visible) {
113
+ hidden.push(node);
114
+ node.visible = false;
115
+ }
116
+ });
108
117
  try {
109
118
  scene.overrideMaterial = material;
110
119
  // Empty sky is infinitely far away, so on a depth plate it is black. The
@@ -118,6 +127,7 @@ export function renderPass(capture, scene, camera, kind, toDataUrl = null, optio
118
127
  // threw.
119
128
  scene.overrideMaterial = previousMaterial;
120
129
  scene.background = previousBackground;
130
+ for (const node of hidden) node.visible = true;
121
131
  material.dispose();
122
132
  }
123
133
  if (!buffer) return null;
@@ -15,6 +15,7 @@ const KIND_LABELS = {
15
15
  image: () => ko("Image", "이미지"),
16
16
  motion: () => ko("Motion", "모션"),
17
17
  pose: () => ko("Pose", "포즈"),
18
+ environment: () => ko("HDR environment", "HDR 환경"),
18
19
  "workflow-output": () => ko("Workflow output", "워크플로 출력"),
19
20
  };
20
21
 
@@ -2,8 +2,6 @@ import { ko, isKo } from "./locale.js";
2
2
 
3
3
  export default function ResultModal({ result, copied, recordedVideoName, onClose, onCopy, onDownload, downloadDisabled = false, exportFeedback = null }) {
4
4
  const isVideo = result.mode === "video";
5
- const motionVideo = result.motion?.videoUrl ?? null;
6
- const isI2vMotion = Boolean(motionVideo);
7
5
  const modelLabel = result.modelLabel ?? (isVideo ? ko("your AI video tool", "AI 영상 도구") : ko("your selected image model", "선택한 이미지 모델"));
8
6
  const hasFrame = Boolean(result.frame);
9
7
 
@@ -42,14 +40,12 @@ export default function ResultModal({ result, copied, recordedVideoName, onClose
42
40
  <div className="modal-overlay" onClick={onClose}>
43
41
  <div className="modal result-modal" role="dialog" aria-modal="true" aria-labelledby="result-title" onClick={(event) => event.stopPropagation()}>
44
42
  <div className="modal-head">
45
- <h3 id="result-title">{isI2vMotion ? ko("Your motion is ready", "모션이 준비됐어요") : ko("Your shot is ready", "장면이 준비됐어요")}</h3>
43
+ <h3 id="result-title">{ko("Your shot is ready", "장면이 준비됐어요")}</h3>
46
44
  <button type="button" className="x" onClick={onClose} aria-label={ko("Close the result", "결과 닫기")}>
47
45
  ✕
48
46
  </button>
49
47
  </div>
50
- {isI2vMotion ? (
51
- <video className="preview result-motion-video" src={motionVideo} controls playsInline preload="metadata" />
52
- ) : result.frameB ? (
48
+ {result.frameB ? (
53
49
  <div className="move-frames">
54
50
  <figure>
55
51
  <img className="preview" src={result.frame} alt={ko("Camera move start frame", "카메라 움직임 시작 프레임")} />
@@ -69,13 +65,6 @@ export default function ResultModal({ result, copied, recordedVideoName, onClose
69
65
  <small>{ko("Camera move made from timeline keyframes", "타임라인 키프레임으로 만든 카메라 움직임")}</small>
70
66
  </div>
71
67
  )}
72
- {isI2vMotion && result.motion && <div className="result-motion-meta">
73
- <span>{result.motion.resolution || "480P"}</span>
74
- {result.motion.width && result.motion.height && <span>{result.motion.width}×{result.motion.height}</span>}
75
- {result.motion.fps && <span>{result.motion.fps} fps</span>}
76
- {result.motion.duration && <span>{result.motion.duration}s</span>}
77
- {result.motion.cost != null && <span>${Number(result.motion.cost).toFixed(4)}</span>}
78
- </div>}
79
68
  <label className="modal-label">{ko("Prompt", "프롬프트")} {copied && <em>· {ko("copied", "복사됨")}</em>}</label>
80
69
  <div className="promptbox">{result.prompt}</div>
81
70
  <div className="modal-actions">
@@ -90,11 +79,7 @@ export default function ResultModal({ result, copied, recordedVideoName, onClose
90
79
  </div>
91
80
 
92
81
  {exportFeedback}
93
- {isI2vMotion ? <section className="result-next" aria-labelledby="result-next-title">
94
- <span className="result-next-kicker">{ko("Next · CozyClay motion source", "다음 · CozyClay 모션 소스")}</span>
95
- <h4 id="result-next-title">{ko("Use this video for mocap extraction", "이 영상을 모캡 추출에 사용하세요")}</h4>
96
- <div className="result-next-intro"><p>{ko("The fixed-camera H3 Max Turbo video is attached to the extraction panel as a motion source. Run GVHMR there, then review the resulting take in the timeline.", "고정 카메라 H3 Max Turbo 영상이 추출 패널의 모션 소스로 연결됐어요. 거기서 GVHMR을 실행한 뒤 타임라인의 테이크를 확인하세요.")}</p></div>
97
- </section> : <section className="result-next" aria-labelledby="result-next-title">
82
+ <section className="result-next" aria-labelledby="result-next-title">
98
83
  <span className="result-next-kicker">{ko(`Next · ${modelLabel}`, `다음 · ${modelLabel}`)}</span>
99
84
  <h4 id="result-next-title">{ko("Handing off to your AI", "AI에 넣는 순서")}</h4>
100
85
  <div className="result-next-intro">{nextStep}</div>
@@ -121,7 +106,7 @@ export default function ResultModal({ result, copied, recordedVideoName, onClose
121
106
  {ko("A video recorded with the recording feature: ", "녹화 기능으로 만든 ")}<code>{recordedVideoName}</code>{ko(" file. Use it separately from the reference frame in the prompt above.", " 파일이에요. 위의 프롬프트 참고 프레임과는 별도로 활용하세요.")}
122
107
  </p>
123
108
  )}
124
- </section>}
109
+ </section>
125
110
  </div>
126
111
  </div>
127
112
  );
package/src/room.jsx CHANGED
@@ -1,5 +1,9 @@
1
1
  import { useEffect, useMemo } from "react";
2
2
  import * as THREE from "three";
3
+ import { stageLook } from "./stage-lighting.js";
4
+ // Patches three's PCF filter before any material compiles (16 taps past radius 1).
5
+ import "./soft-shadows.js";
6
+ export { StageAtmosphere } from "./stage-sky.jsx";
3
7
 
4
8
  /**
5
9
  * The blocking set: an open stage floor.
@@ -128,25 +132,24 @@ export function keyLightColor(warmth = 0.5) {
128
132
  * One neutral rig (Blender's workbench studio light idea): a white sky over
129
133
  * a grey ground bounce. `neutral` is the grid view, where the deck is gone
130
134
  * and the ambient is lifted so authored colours stay saturated without any
131
- * pretend floor.
135
+ * pretend floor. `lighting` (stage.lighting) scales and tints the rig into a
136
+ * look — night, golden hour, a black stage — through stageLook(); its studio
137
+ * default renders the tuned rig unchanged.
132
138
  */
133
- export function StageLights({ keyLight = { x: 6, y: 9, z: 4, intensity: 1.12, warmth: 0.5 }, neutral = false, light = false }) {
134
- const dim = keyLight.intensity / 1.12;
139
+ export function StageLights({ keyLight = { x: 6, y: 9, z: 4, intensity: 1.12, warmth: 0.5 }, lighting = null, neutral = false, light = false }) {
140
+ const look = stageLook({ lighting, keyLight, theme: light ? "light" : "dark", grid: neutral });
135
141
  return (
136
142
  <>
137
- <hemisphereLight
138
- args={neutral ? ["#ffffff", "#3a3d42", 0.9] : ["#ffffff", light ? "#d6d8de" : "#5a5d64", 0.9]}
139
- intensity={0.9 * Math.min(1, 0.35 + 0.65 * dim)}
140
- />
143
+ <hemisphereLight args={[look.hemi.sky, look.hemi.ground, 0.9]} intensity={look.hemi.intensity} />
141
144
  {/* The light stage is a bright room: more ambient, so the clay's shaded
142
145
  side stays soft grey instead of going charcoal against white. */}
143
- <ambientLight intensity={(neutral ? 0.34 : light ? 0.32 : 0.18) * Math.min(1, 0.35 + 0.65 * dim)} />
146
+ <ambientLight intensity={look.ambient} />
144
147
  {/* Only the key casts: one soft, unambiguous contact shadow reads as
145
148
  ground contact, while three overlapping shadows read as noise. The
146
149
  map covers the blocking area rather than the whole 500 m deck — a
147
150
  stage-wide frustum would spend its resolution on empty floor. */}
148
151
  <directionalLight
149
- color={keyLightColor(keyLight.warmth)}
152
+ color={keyLight.color ?? keyLightColor(keyLight.warmth)}
150
153
  position={[keyLight.x, keyLight.y, keyLight.z]}
151
154
  intensity={keyLight.intensity}
152
155
  castShadow
@@ -162,10 +165,15 @@ export function StageLights({ keyLight = { x: 6, y: 9, z: 4, intensity: 1.12, wa
162
165
  // shadow there. 60 covers the clamp envelope with headroom.
163
166
  shadow-camera-far={60}
164
167
  shadow-bias={-0.0006}
165
- shadow-normalBias={0.02}
168
+ // PCF spreads its noise-rotated taps over shadow.radius texels, so a
169
+ // larger radius is a wider penumbra in the same single pass
170
+ // (stage.lighting.shadowSoftness; soft-shadows.js says why not VSM).
171
+ // The normal offset grows with the radius.
172
+ shadow-normalBias={look.shadowNormalBias}
173
+ shadow-radius={look.shadowRadius}
166
174
  />
167
- <directionalLight color="#eef2f4" position={[-6, 4, -4]} intensity={0.36 * dim} />
168
- <directionalLight color="#ffffff" position={[2, 3, 9]} intensity={0.22 * dim} />
175
+ <directionalLight color="#eef2f4" position={[-6, 4, -4]} intensity={look.fill} />
176
+ <directionalLight color="#ffffff" position={[2, 3, 9]} intensity={look.rim} />
169
177
  </>
170
178
  );
171
179
  }
@@ -18,6 +18,7 @@
18
18
  * Everything above the storage adapter is pure and testable in node; the
19
19
  * adapter is the only part that needs a browser.
20
20
  */
21
+ import { isHdrAssetId, normalizeHdrAsset } from "./stage-hdr.js";
21
22
 
22
23
  /** Content-addressed, so the same bytes always land on the same id. */
23
24
  export const ASSET_ID_PREFIX = "img-";
@@ -85,7 +86,7 @@ export function isMeshAssetId(value) {
85
86
  }
86
87
 
87
88
  export function isAssetId(value) {
88
- return isImageAssetId(value) || isMeshAssetId(value);
89
+ return isImageAssetId(value) || isMeshAssetId(value) || isHdrAssetId(value);
89
90
  }
90
91
 
91
92
  async function sha256Hex(bytes, subtle) {
@@ -149,6 +150,8 @@ export function normalizeAsset(record) {
149
150
  if (!record || typeof record !== "object" || Array.isArray(record)) return null;
150
151
  const bytes = record.bytes instanceof ArrayBuffer ? record.bytes : null;
151
152
  if (!bytes || !bytes.byteLength) return null;
153
+ // An `hdr-` id is an HDR environment file (stage-hdr.js), stored verbatim.
154
+ if (isHdrAssetId(record.id)) return normalizeHdrAsset(record, bytes);
152
155
  // Prefix and MIME must agree: a mesh id with an image type (or the reverse)
153
156
  // is a cross-wired record and cannot be drawn as either kind.
154
157
  if (isMeshAssetId(record.id)) {
@@ -309,12 +312,11 @@ export async function importImageFile(file, {
309
312
  */
310
313
  export function referencedAssetIds(scenes) {
311
314
  const ids = new Set(assetUsageCounts(scenes).keys());
312
- // A storyboard panel's stylized picture is held by its shot, not by an object.
315
+ // A storyboard panel's stylized picture is held by its shot, not by an object;
316
+ // a scene's HDR environment file by its stage look (stage.lighting.hdr).
313
317
  for (const scene of Array.isArray(scenes) ? scenes : []) {
314
- const shots = scene?.shotDocument?.shots;
315
- for (const shot of Array.isArray(shots) ? shots : []) {
316
- if (isImageAssetId(shot?.stylizedAssetId)) ids.add(shot.stylizedAssetId);
317
- }
318
+ for (const shot of Array.isArray(scene?.shotDocument?.shots) ? scene.shotDocument.shots : []) if (isImageAssetId(shot?.stylizedAssetId)) ids.add(shot.stylizedAssetId);
319
+ if (isHdrAssetId(scene?.stage?.lighting?.hdr)) ids.add(scene.stage.lighting.hdr);
318
320
  }
319
321
  return ids;
320
322
  }