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