cozyclay 2.1.0 → 3.0.0

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