framefields 2.14.0 → 2.16.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 (333) hide show
  1. package/NOTICE.md +5 -1
  2. package/dist/audio/index.d.mts +3 -3
  3. package/dist/audio/index.mjs +10 -10
  4. package/dist/{audio-DFwRbID_.mjs → audio-6TfPlfl4.mjs} +3 -3
  5. package/dist/{audio-DFwRbID_.mjs.map → audio-6TfPlfl4.mjs.map} +1 -1
  6. package/dist/{audio-CdCuGK5d.d.mts → audio-7oqTUi4G.d.mts} +2 -2
  7. package/dist/{audio-CdCuGK5d.d.mts.map → audio-7oqTUi4G.d.mts.map} +1 -1
  8. package/dist/cli/check-worker.mjs +44 -44
  9. package/dist/cli.mjs +1 -1
  10. package/dist/cloud/index.mjs +1 -1
  11. package/dist/{cloud-Cj1TIme8.mjs → cloud-jnk1hPYf.mjs} +2 -2
  12. package/dist/{cloud-Cj1TIme8.mjs.map → cloud-jnk1hPYf.mjs.map} +1 -1
  13. package/dist/components/index.d.mts +11 -11
  14. package/dist/components/index.mjs +43 -43
  15. package/dist/{dist-Kbf-JTry.mjs → dist-BtI6ukB5.mjs} +2 -2
  16. package/dist/{dist-Kbf-JTry.mjs.map → dist-BtI6ukB5.mjs.map} +1 -1
  17. package/dist/{dist-B8Nby2CU.mjs → dist-COOpgG9G.mjs} +4 -3
  18. package/dist/{dist-B8Nby2CU.mjs.map → dist-COOpgG9G.mjs.map} +1 -1
  19. package/dist/{dist--Uu5bQ_6.mjs → dist-XqlFM2a8.mjs} +13733 -5323
  20. package/dist/dist-XqlFM2a8.mjs.map +1 -0
  21. package/dist/{dist-CiTgmfUw.mjs → dist-f1zkl69f.mjs} +4 -4
  22. package/dist/dist-f1zkl69f.mjs.map +1 -0
  23. package/dist/{dist-B_rSWeOz.mjs → dist-umQOWz0a.mjs} +1 -1
  24. package/dist/effects/index.d.mts +4 -4
  25. package/dist/effects/index.mjs +35 -35
  26. package/dist/{effects-WMgv8ZaD.mjs → effects-DlRMovU5.mjs} +2 -2
  27. package/dist/{effects-WMgv8ZaD.mjs.map → effects-DlRMovU5.mjs.map} +1 -1
  28. package/dist/fonts/index.d.mts +1 -1
  29. package/dist/fonts/index.mjs +2 -2
  30. package/dist/{fonts-OW3yAfO5.mjs → fonts-BWYcXjyK.mjs} +3 -3
  31. package/dist/fonts-BWYcXjyK.mjs.map +1 -0
  32. package/dist/{gpu-R4T2Tndu.mjs → gpu-CH1PdM4T.mjs} +2 -2
  33. package/dist/{gpu-R4T2Tndu.mjs.map → gpu-CH1PdM4T.mjs.map} +1 -1
  34. package/dist/{index-CEKxvGY-.d.mts → index-BHTycml5.d.mts} +2 -2
  35. package/dist/{index-CEKxvGY-.d.mts.map → index-BHTycml5.d.mts.map} +1 -1
  36. package/dist/{index-DoP6EiHj.d.mts → index-BcxxxwhV.d.mts} +4 -4
  37. package/dist/{index-DoP6EiHj.d.mts.map → index-BcxxxwhV.d.mts.map} +1 -1
  38. package/dist/index-C-V6CAhP.d.mts.map +1 -1
  39. package/dist/{index-D1FqV1eu.d.mts → index-CMhkkc56.d.mts} +43 -11
  40. package/dist/index-CMhkkc56.d.mts.map +1 -0
  41. package/dist/{index-B0Vq8Bwf.d.mts → index-CYVpYGM-.d.mts} +2 -2
  42. package/dist/{index-B0Vq8Bwf.d.mts.map → index-CYVpYGM-.d.mts.map} +1 -1
  43. package/dist/{index-Bv7gxGK1.d.mts → index-Cq8jgdNY.d.mts} +90 -3
  44. package/dist/index-Cq8jgdNY.d.mts.map +1 -0
  45. package/dist/{index-9xte4JgL.d.mts → index-Dg5BAtDq.d.mts} +1446 -541
  46. package/dist/index-Dg5BAtDq.d.mts.map +1 -0
  47. package/dist/{index-C7-p9zwU.d.mts → index-DhL7ckvs.d.mts} +5 -5
  48. package/dist/index-DhL7ckvs.d.mts.map +1 -0
  49. package/dist/{index-GA9_Z8Fn.d.mts → index-RILu3ULT.d.mts} +2 -2
  50. package/dist/{index-GA9_Z8Fn.d.mts.map → index-RILu3ULT.d.mts.map} +1 -1
  51. package/dist/{index-C5sgRLlG.d.mts → index-TCtdSXd7.d.mts} +3 -1
  52. package/dist/index-TCtdSXd7.d.mts.map +1 -0
  53. package/dist/{index-CRaydvLi.d.mts → index-s2qQ8ifZ.d.mts} +886 -51
  54. package/dist/index-s2qQ8ifZ.d.mts.map +1 -0
  55. package/dist/index.d.mts +13 -13
  56. package/dist/index.mjs +52 -52
  57. package/dist/preview/index.d.mts +4 -4
  58. package/dist/preview/index.mjs +11 -11
  59. package/dist/preview-Dm912wly.mjs +1037 -0
  60. package/dist/preview-Dm912wly.mjs.map +1 -0
  61. package/dist/preview-engine/audio.mjs +1 -1
  62. package/dist/preview-engine/{chunk-64L4GUOF.mjs → chunk-4F3DG6YB.mjs} +2 -2
  63. package/dist/preview-engine/chunk-A2OTOW3B.mjs +4694 -0
  64. package/dist/preview-engine/{chunk-GNDE3OXQ.mjs → chunk-BR56TQBP.mjs} +1 -1
  65. package/dist/preview-engine/{chunk-ADLIF544.mjs → chunk-H2XKXXAU.mjs} +1 -1
  66. package/dist/preview-engine/chunk-NDUODAY2.mjs +3 -0
  67. package/dist/preview-engine/{chunk-FZ4RD7HV.mjs → chunk-QAQJSWUT.mjs} +1 -1
  68. package/dist/preview-engine/chunk-SWRGQTVC.mjs +138 -0
  69. package/dist/preview-engine/{chunk-52X7SURQ.mjs → chunk-TEA3GVBJ.mjs} +1 -1
  70. package/dist/preview-engine/chunk-VZUVB3BP.mjs +644 -0
  71. package/dist/preview-engine/{chunk-UBY2UYEL.mjs → chunk-XSHNQJ7L.mjs} +1 -1
  72. package/dist/preview-engine/components.mjs +1 -1
  73. package/dist/preview-engine/effects.mjs +1 -1
  74. package/dist/preview-engine/fonts.mjs +1 -1
  75. package/dist/preview-engine/framefields.mjs +1 -1
  76. package/dist/preview-engine/player.mjs +3 -3
  77. package/dist/preview-engine/{renderers-DU7KPFVV.mjs → renderers-6C7NWWDX.mjs} +1 -1
  78. package/dist/preview-engine/renderers-HHVGNMSJ.mjs +206 -0
  79. package/dist/preview-engine/{src-YXNX4MLK.mjs → src-RCNEKIRP.mjs} +1 -1
  80. package/dist/preview-page/index.html +19 -0
  81. package/dist/preview-page/main.css +1 -0
  82. package/dist/preview-page/main.js +326 -0
  83. package/dist/{program-tFKCWU2j.mjs → program-CMigyfso.mjs} +829 -52
  84. package/dist/program-CMigyfso.mjs.map +1 -0
  85. package/dist/renderer/index.d.mts +2 -2
  86. package/dist/renderer/index.mjs +2 -2
  87. package/dist/{renderer-DkOuQi84.mjs → renderer-0GMEL_Zi.mjs} +3 -3
  88. package/dist/{renderer-DkOuQi84.mjs.map → renderer-0GMEL_Zi.mjs.map} +1 -1
  89. package/dist/{renderer-BGp0An5A.mjs → renderer-3Wv2ZBJG.mjs} +3 -3
  90. package/dist/{renderer-BGp0An5A.mjs.map → renderer-3Wv2ZBJG.mjs.map} +1 -1
  91. package/dist/{renderer-BVIXKfbC.mjs → renderer-4nXkaAWu.mjs} +1178 -199
  92. package/dist/renderer-4nXkaAWu.mjs.map +1 -0
  93. package/dist/{renderer-Cw6_NL2i.mjs → renderer-6SnfLXeg.mjs} +3 -3
  94. package/dist/{renderer-Cw6_NL2i.mjs.map → renderer-6SnfLXeg.mjs.map} +1 -1
  95. package/dist/{renderer-BVU22lBS.mjs → renderer-8gHw3O0P.mjs} +2 -2
  96. package/dist/{renderer-BVU22lBS.mjs.map → renderer-8gHw3O0P.mjs.map} +1 -1
  97. package/dist/{renderer-BjDddtw9.mjs → renderer-AOBglZHC.mjs} +2 -2
  98. package/dist/{renderer-D-KHHWQD.mjs → renderer-B0bSARK3.mjs} +3 -3
  99. package/dist/{renderer-D-KHHWQD.mjs.map → renderer-B0bSARK3.mjs.map} +1 -1
  100. package/dist/{renderer-CCxk-RJR.mjs → renderer-B2_tuRfl.mjs} +2 -2
  101. package/dist/{renderer-CCxk-RJR.mjs.map → renderer-B2_tuRfl.mjs.map} +1 -1
  102. package/dist/{renderer-k5d7JSbs.mjs → renderer-B82FhkEr.mjs} +3 -3
  103. package/dist/{renderer-k5d7JSbs.mjs.map → renderer-B82FhkEr.mjs.map} +1 -1
  104. package/dist/{renderer-I0Hh9cLK.mjs → renderer-B9lzA0PY.mjs} +3 -3
  105. package/dist/{renderer-I0Hh9cLK.mjs.map → renderer-B9lzA0PY.mjs.map} +1 -1
  106. package/dist/{renderer-Dwwnx7Dk.mjs → renderer-BBSIWSry.mjs} +2 -2
  107. package/dist/{renderer-Dwwnx7Dk.mjs.map → renderer-BBSIWSry.mjs.map} +1 -1
  108. package/dist/{renderer-_pC-jB9O.mjs → renderer-BEKDMxbi.mjs} +2 -2
  109. package/dist/{renderer-_pC-jB9O.mjs.map → renderer-BEKDMxbi.mjs.map} +1 -1
  110. package/dist/renderer-BERGAxqA.mjs +5 -0
  111. package/dist/{renderer-raxwsiGI.mjs → renderer-BF960HY8.mjs} +3 -3
  112. package/dist/{renderer-raxwsiGI.mjs.map → renderer-BF960HY8.mjs.map} +1 -1
  113. package/dist/{renderer-Dr0tXA4k.mjs → renderer-BFwyVxCu.mjs} +3 -3
  114. package/dist/{renderer-Dr0tXA4k.mjs.map → renderer-BFwyVxCu.mjs.map} +1 -1
  115. package/dist/{renderer-DEjo6dw_.mjs → renderer-BNiwcI3-.mjs} +2 -2
  116. package/dist/{renderer-DEjo6dw_.mjs.map → renderer-BNiwcI3-.mjs.map} +1 -1
  117. package/dist/{renderer-DIEeHiQA.mjs → renderer-BPbVb2Q4.mjs} +2 -2
  118. package/dist/{renderer-DIEeHiQA.mjs.map → renderer-BPbVb2Q4.mjs.map} +1 -1
  119. package/dist/renderer-BQ_YkBMt.mjs +6 -0
  120. package/dist/{renderer-BBMtmxks.mjs → renderer-BUWxlA8T.mjs} +3 -3
  121. package/dist/{renderer-BBMtmxks.mjs.map → renderer-BUWxlA8T.mjs.map} +1 -1
  122. package/dist/renderer-BW0Tjsv9.mjs +5 -0
  123. package/dist/{renderer-Dgj46uEU.mjs → renderer-BX8liu-e.mjs} +2 -2
  124. package/dist/{renderer-ZnrEfkYh.mjs → renderer-BXIwDAr_.mjs} +2 -2
  125. package/dist/{renderer-ZnrEfkYh.mjs.map → renderer-BXIwDAr_.mjs.map} +1 -1
  126. package/dist/{renderer-DA8YEo_d.mjs → renderer-B_Hy1W-g.mjs} +3 -3
  127. package/dist/{renderer-DA8YEo_d.mjs.map → renderer-B_Hy1W-g.mjs.map} +1 -1
  128. package/dist/{renderer-iMiYStcW.mjs → renderer-Bb6cvSgh.mjs} +2 -2
  129. package/dist/{renderer-iMiYStcW.mjs.map → renderer-Bb6cvSgh.mjs.map} +1 -1
  130. package/dist/{renderer-BacY4nh8.mjs → renderer-BdppwWC9.mjs} +2 -2
  131. package/dist/{renderer-BacY4nh8.mjs.map → renderer-BdppwWC9.mjs.map} +1 -1
  132. package/dist/{renderer-CrxwW6VQ.mjs → renderer-Bhc24HVe.mjs} +3 -3
  133. package/dist/{renderer-CrxwW6VQ.mjs.map → renderer-Bhc24HVe.mjs.map} +1 -1
  134. package/dist/{renderer-BH7UCMPQ.mjs → renderer-BiNA2wF0.mjs} +2 -2
  135. package/dist/{renderer-BH7UCMPQ.mjs.map → renderer-BiNA2wF0.mjs.map} +1 -1
  136. package/dist/{renderer-DVPgWHZw.mjs → renderer-BmoBgTp5.mjs} +3 -3
  137. package/dist/{renderer-DVPgWHZw.mjs.map → renderer-BmoBgTp5.mjs.map} +1 -1
  138. package/dist/{renderer-C20efMI0.mjs → renderer-BmqFmCWS.mjs} +3 -3
  139. package/dist/{renderer-C20efMI0.mjs.map → renderer-BmqFmCWS.mjs.map} +1 -1
  140. package/dist/{renderer-B0yVUOPP.mjs → renderer-BnVwAObV.mjs} +2 -2
  141. package/dist/{renderer-B0yVUOPP.mjs.map → renderer-BnVwAObV.mjs.map} +1 -1
  142. package/dist/{renderer-CP8wLzTU.mjs → renderer-BxttZHme.mjs} +2 -2
  143. package/dist/{renderer-CP8wLzTU.mjs.map → renderer-BxttZHme.mjs.map} +1 -1
  144. package/dist/renderer-BzyPdUD1.mjs +5 -0
  145. package/dist/renderer-C0hM-mtY.mjs +5 -0
  146. package/dist/renderer-C6cyUTvk.mjs +5 -0
  147. package/dist/{renderer-BOSZAdgF.mjs → renderer-C8E4XsSg.mjs} +2 -2
  148. package/dist/{renderer-BOSZAdgF.mjs.map → renderer-C8E4XsSg.mjs.map} +1 -1
  149. package/dist/renderer-C8zY3pzk.mjs +5 -0
  150. package/dist/{renderer-DWq9gYRw.mjs → renderer-C9U-LY50.mjs} +3 -3
  151. package/dist/{renderer-DWq9gYRw.mjs.map → renderer-C9U-LY50.mjs.map} +1 -1
  152. package/dist/{renderer-Dj2H5JPR.mjs → renderer-CB2JVu5x.mjs} +3 -3
  153. package/dist/{renderer-Dj2H5JPR.mjs.map → renderer-CB2JVu5x.mjs.map} +1 -1
  154. package/dist/{renderer-Bcu3UrI-.mjs → renderer-CC4YBJ8Z.mjs} +3 -3
  155. package/dist/{renderer-Bcu3UrI-.mjs.map → renderer-CC4YBJ8Z.mjs.map} +1 -1
  156. package/dist/renderer-CEkSOyKu.mjs +5 -0
  157. package/dist/{renderer-DzLrvCB2.mjs → renderer-CIycc5O-.mjs} +2 -2
  158. package/dist/{renderer-DzLrvCB2.mjs.map → renderer-CIycc5O-.mjs.map} +1 -1
  159. package/dist/renderer-CJYtfa4x.mjs +5 -0
  160. package/dist/{renderer-BeNl_6EG.mjs → renderer-CJraQlV-.mjs} +2 -2
  161. package/dist/{renderer-BeNl_6EG.mjs.map → renderer-CJraQlV-.mjs.map} +1 -1
  162. package/dist/renderer-CKMtHGZh.mjs +5 -0
  163. package/dist/renderer-CLzicoiy.mjs +5 -0
  164. package/dist/renderer-CM2A4BHd.mjs +5 -0
  165. package/dist/{renderer-DtMVitEF.mjs → renderer-COWGWtob.mjs} +2 -2
  166. package/dist/{renderer-DtMVitEF.mjs.map → renderer-COWGWtob.mjs.map} +1 -1
  167. package/dist/{renderer-CT3zkfbf.mjs → renderer-COxG2XGf.mjs} +2 -2
  168. package/dist/{renderer-CT3zkfbf.mjs.map → renderer-COxG2XGf.mjs.map} +1 -1
  169. package/dist/{renderer-DNxWOz8Q.mjs → renderer-CQDCIsQm.mjs} +2 -2
  170. package/dist/{renderer-DNxWOz8Q.mjs.map → renderer-CQDCIsQm.mjs.map} +1 -1
  171. package/dist/{renderer-C5AoaymD.mjs → renderer-CRUeag8i.mjs} +3 -3
  172. package/dist/{renderer-C5AoaymD.mjs.map → renderer-CRUeag8i.mjs.map} +1 -1
  173. package/dist/{renderer-ngmYIIob.mjs → renderer-CWVput06.mjs} +3 -3
  174. package/dist/{renderer-ngmYIIob.mjs.map → renderer-CWVput06.mjs.map} +1 -1
  175. package/dist/renderer-CZlHjcxr.mjs +5 -0
  176. package/dist/{renderer-0rveCDm-.mjs → renderer-CbbnBLOB.mjs} +3 -3
  177. package/dist/{renderer-0rveCDm-.mjs.map → renderer-CbbnBLOB.mjs.map} +1 -1
  178. package/dist/renderer-CbimvSU8.mjs +5 -0
  179. package/dist/renderer-Ccvc63yd.mjs +5 -0
  180. package/dist/renderer-Cdp-SGfq.mjs +5 -0
  181. package/dist/renderer-CeTcSs3e.mjs +5 -0
  182. package/dist/{renderer-Brmbl12Y.mjs → renderer-CfLZein2.mjs} +3 -3
  183. package/dist/{renderer-Brmbl12Y.mjs.map → renderer-CfLZein2.mjs.map} +1 -1
  184. package/dist/{renderer-BJ8ea92W.mjs → renderer-Ch-q1O-q.mjs} +3 -3
  185. package/dist/{renderer-BJ8ea92W.mjs.map → renderer-Ch-q1O-q.mjs.map} +1 -1
  186. package/dist/{renderer-CP108ABB.mjs → renderer-Ch5JvJWl.mjs} +2 -2
  187. package/dist/{renderer-CP108ABB.mjs.map → renderer-Ch5JvJWl.mjs.map} +1 -1
  188. package/dist/{renderer-47y12Z37.mjs → renderer-ChtEzq-R.mjs} +2 -2
  189. package/dist/{renderer-47y12Z37.mjs.map → renderer-ChtEzq-R.mjs.map} +1 -1
  190. package/dist/{renderer-DhyMfG1v.mjs → renderer-Cj9nTC5l.mjs} +3 -3
  191. package/dist/{renderer-DhyMfG1v.mjs.map → renderer-Cj9nTC5l.mjs.map} +1 -1
  192. package/dist/{renderer-BDzn1AKF.mjs → renderer-CqT8HsSS.mjs} +2 -2
  193. package/dist/{renderer-BDzn1AKF.mjs.map → renderer-CqT8HsSS.mjs.map} +1 -1
  194. package/dist/{renderer-B7G1KohJ.mjs → renderer-CuQjMhdV.mjs} +2 -2
  195. package/dist/renderer-CvNuu52U.mjs +5 -0
  196. package/dist/renderer-CwciC0oI.mjs +5 -0
  197. package/dist/renderer-CysLUbIr.mjs +5 -0
  198. package/dist/{renderer-Cm0tsbQH.mjs → renderer-Cz30EVws.mjs} +2 -2
  199. package/dist/{renderer-Cm0tsbQH.mjs.map → renderer-Cz30EVws.mjs.map} +1 -1
  200. package/dist/renderer-D3A22Vxb.mjs +5 -0
  201. package/dist/renderer-D5SY9EmA.mjs +5 -0
  202. package/dist/renderer-D8h3ZvaE.mjs +5 -0
  203. package/dist/renderer-DE-B6N9n.mjs +5 -0
  204. package/dist/renderer-DETZTI7t.mjs +5 -0
  205. package/dist/{renderer-nHS3Sius.mjs → renderer-DHoX-5U_.mjs} +3 -3
  206. package/dist/{renderer-nHS3Sius.mjs.map → renderer-DHoX-5U_.mjs.map} +1 -1
  207. package/dist/{renderer-BWSRryIA.mjs → renderer-DOkm0Ycd.mjs} +2 -2
  208. package/dist/{renderer-BWSRryIA.mjs.map → renderer-DOkm0Ycd.mjs.map} +1 -1
  209. package/dist/{renderer-BHYJlK6H.d.mts → renderer-DU3f6ZrS.d.mts} +2 -2
  210. package/dist/{renderer-BHYJlK6H.d.mts.map → renderer-DU3f6ZrS.d.mts.map} +1 -1
  211. package/dist/{renderer-BpVOk9ux.mjs → renderer-DUzv-shq.mjs} +2 -2
  212. package/dist/{renderer-BpVOk9ux.mjs.map → renderer-DUzv-shq.mjs.map} +1 -1
  213. package/dist/renderer-DWnDj0IM.mjs +5 -0
  214. package/dist/renderer-DcGUIbQF.mjs +5 -0
  215. package/dist/renderer-Dfq6FoNO.mjs +5 -0
  216. package/dist/renderer-DhasXW6S.mjs +3 -0
  217. package/dist/{renderer-NRMlYkX6.mjs → renderer-DhnOWnEA.mjs} +3 -3
  218. package/dist/{renderer-NRMlYkX6.mjs.map → renderer-DhnOWnEA.mjs.map} +1 -1
  219. package/dist/{renderer-COgwYqev.mjs → renderer-Dhy1XHzz.mjs} +3 -3
  220. package/dist/{renderer-COgwYqev.mjs.map → renderer-Dhy1XHzz.mjs.map} +1 -1
  221. package/dist/renderer-DjCvpsV_.mjs +5 -0
  222. package/dist/{renderer-BfZNIiSv.mjs → renderer-DjJshz9Y.mjs} +2 -2
  223. package/dist/{renderer-BfZNIiSv.mjs.map → renderer-DjJshz9Y.mjs.map} +1 -1
  224. package/dist/{renderer-CKo7VR8x.mjs → renderer-DmTUIdus.mjs} +3 -3
  225. package/dist/{renderer-CKo7VR8x.mjs.map → renderer-DmTUIdus.mjs.map} +1 -1
  226. package/dist/{renderer-Baw6Utxi.mjs → renderer-Dq7AioJa.mjs} +2 -2
  227. package/dist/{renderer-Baw6Utxi.mjs.map → renderer-Dq7AioJa.mjs.map} +1 -1
  228. package/dist/renderer-DrAaM6JD.mjs +5 -0
  229. package/dist/{renderer-D-GWBoVT.mjs → renderer-Dt4sqNEu.mjs} +2 -2
  230. package/dist/{renderer-D-GWBoVT.mjs.map → renderer-Dt4sqNEu.mjs.map} +1 -1
  231. package/dist/{renderer-FYOL5Za6.mjs → renderer-Dtcs4idV.mjs} +3 -3
  232. package/dist/{renderer-FYOL5Za6.mjs.map → renderer-Dtcs4idV.mjs.map} +1 -1
  233. package/dist/renderer-Dxlv8LYm.mjs +5 -0
  234. package/dist/{renderer-D-RhYvVl.mjs → renderer-Gj8SrE2W.mjs} +2 -2
  235. package/dist/{renderer-D-RhYvVl.mjs.map → renderer-Gj8SrE2W.mjs.map} +1 -1
  236. package/dist/{renderer-D_j1WKMD.mjs → renderer-KfnXDJS0.mjs} +3 -3
  237. package/dist/{renderer-D_j1WKMD.mjs.map → renderer-KfnXDJS0.mjs.map} +1 -1
  238. package/dist/{renderer-Bpm145BW.mjs → renderer-MXVlg4p5.mjs} +3 -3
  239. package/dist/{renderer-Bpm145BW.mjs.map → renderer-MXVlg4p5.mjs.map} +1 -1
  240. package/dist/{renderer-Nnmi8Gt6.mjs → renderer-N2b6QZ1E.mjs} +3 -3
  241. package/dist/{renderer-Nnmi8Gt6.mjs.map → renderer-N2b6QZ1E.mjs.map} +1 -1
  242. package/dist/renderer-Pb43OO2t.mjs +5 -0
  243. package/dist/{renderer-Ca1KjNCO.mjs → renderer-Q7gDzm8c.mjs} +2 -2
  244. package/dist/{renderer-Ca1KjNCO.mjs.map → renderer-Q7gDzm8c.mjs.map} +1 -1
  245. package/dist/{renderer-D2wNFNRf.mjs → renderer-RCiANtRV.mjs} +2 -2
  246. package/dist/{renderer-D2wNFNRf.mjs.map → renderer-RCiANtRV.mjs.map} +1 -1
  247. package/dist/{renderer-Cqb7PGrI.mjs → renderer-Sig9nZcI.mjs} +2 -2
  248. package/dist/renderer-VQbm-cGr.mjs +5 -0
  249. package/dist/{renderer-W1bVNVR6.mjs → renderer-fabBgwQX.mjs} +3 -3
  250. package/dist/{renderer-W1bVNVR6.mjs.map → renderer-fabBgwQX.mjs.map} +1 -1
  251. package/dist/{renderer-zRq0j6Xy.mjs → renderer-g-0PCNoo.mjs} +3 -3
  252. package/dist/{renderer-zRq0j6Xy.mjs.map → renderer-g-0PCNoo.mjs.map} +1 -1
  253. package/dist/{renderer-2PjR3w-q.mjs → renderer-gmHKIbKI.mjs} +3 -3
  254. package/dist/{renderer-2PjR3w-q.mjs.map → renderer-gmHKIbKI.mjs.map} +1 -1
  255. package/dist/{renderer-03mYJLgX.mjs → renderer-kVZwiRoK.mjs} +3 -3
  256. package/dist/{renderer-03mYJLgX.mjs.map → renderer-kVZwiRoK.mjs.map} +1 -1
  257. package/dist/renderer-oPMypDM1.mjs +5 -0
  258. package/dist/{renderer-DcAKDAnS.mjs → renderer-p7tgxP-A.mjs} +3 -3
  259. package/dist/{renderer-DcAKDAnS.mjs.map → renderer-p7tgxP-A.mjs.map} +1 -1
  260. package/dist/{renderer-jUKdkmG5.mjs → renderer-tvYj306A.mjs} +81 -78
  261. package/dist/renderer-tvYj306A.mjs.map +1 -0
  262. package/dist/renderer-xvWGpVyp.mjs +5 -0
  263. package/dist/renderer-zafZbD6Q.mjs +5 -0
  264. package/dist/{renderers-CP8CFWDs-CshljORp.mjs → renderers-CP8CFWDs-DLjtjrwx.mjs} +2 -2
  265. package/dist/{renderers-CP8CFWDs-CshljORp.mjs.map → renderers-CP8CFWDs-DLjtjrwx.mjs.map} +1 -1
  266. package/dist/sam/index.d.mts +2 -2
  267. package/dist/sam/index.mjs +1 -1
  268. package/dist/signals/index.d.mts +4 -4
  269. package/dist/signals/index.mjs +4 -4
  270. package/dist/{src-BU6vm9sP-DdTHmCr4.mjs → src-B23p_eh7-Cj6-lvV6.mjs} +293 -6
  271. package/dist/src-B23p_eh7-Cj6-lvV6.mjs.map +1 -0
  272. package/dist/{src-Dy4gnc6w.mjs → src-CoHAjviq.mjs} +1349 -177
  273. package/dist/src-CoHAjviq.mjs.map +1 -0
  274. package/dist/storage/index.mjs +1 -1
  275. package/package.json +63 -60
  276. package/dist/dist--Uu5bQ_6.mjs.map +0 -1
  277. package/dist/dist-CiTgmfUw.mjs.map +0 -1
  278. package/dist/fonts-OW3yAfO5.mjs.map +0 -1
  279. package/dist/index-9xte4JgL.d.mts.map +0 -1
  280. package/dist/index-Bv7gxGK1.d.mts.map +0 -1
  281. package/dist/index-C5sgRLlG.d.mts.map +0 -1
  282. package/dist/index-C7-p9zwU.d.mts.map +0 -1
  283. package/dist/index-CRaydvLi.d.mts.map +0 -1
  284. package/dist/index-D1FqV1eu.d.mts.map +0 -1
  285. package/dist/preview-D7BGmE1E.mjs +0 -2446
  286. package/dist/preview-D7BGmE1E.mjs.map +0 -1
  287. package/dist/preview-engine/chunk-5DZTPUMH.mjs +0 -2568
  288. package/dist/preview-engine/chunk-NCQHOGZK.mjs +0 -3
  289. package/dist/preview-engine/chunk-PH3UUX6A.mjs +0 -138
  290. package/dist/preview-engine/chunk-UPZURPJY.mjs +0 -2227
  291. package/dist/preview-engine/renderers-DLJOA3BM.mjs +0 -228
  292. package/dist/program-tFKCWU2j.mjs.map +0 -1
  293. package/dist/renderer-6_bzbbXt.mjs +0 -5
  294. package/dist/renderer-8M-9G6F_.mjs +0 -5
  295. package/dist/renderer-B5OzKvF8.mjs +0 -5
  296. package/dist/renderer-BBCZ2qbE.mjs +0 -5
  297. package/dist/renderer-BChbxCux.mjs +0 -5
  298. package/dist/renderer-BGeMPr8C.mjs +0 -5
  299. package/dist/renderer-BPQ8Pkjb.mjs +0 -5
  300. package/dist/renderer-BTRpA3HD.mjs +0 -5
  301. package/dist/renderer-BVIXKfbC.mjs.map +0 -1
  302. package/dist/renderer-BVsECSn-.mjs +0 -5
  303. package/dist/renderer-Bgos68gc.mjs +0 -6
  304. package/dist/renderer-BjHOvoqO.mjs +0 -5
  305. package/dist/renderer-BjXMPPlV.mjs +0 -5
  306. package/dist/renderer-Byobxqkv.mjs +0 -5
  307. package/dist/renderer-CKrsg3EG.mjs +0 -5
  308. package/dist/renderer-Cj875NuH.mjs +0 -5
  309. package/dist/renderer-CnmzJoMv.mjs +0 -5
  310. package/dist/renderer-CtklDaMg.mjs +0 -5
  311. package/dist/renderer-D1tK3in_.mjs +0 -5
  312. package/dist/renderer-D2yc9P61.mjs +0 -5
  313. package/dist/renderer-D4v-JvSr.mjs +0 -5
  314. package/dist/renderer-D5yp74NL.mjs +0 -3
  315. package/dist/renderer-D7WRWDwB.mjs +0 -5
  316. package/dist/renderer-DGvA0tdB.mjs +0 -5
  317. package/dist/renderer-DO7v5Sdm.mjs +0 -5
  318. package/dist/renderer-DRVvt7-V.mjs +0 -5
  319. package/dist/renderer-DYMCd3jL.mjs +0 -5
  320. package/dist/renderer-DmHKw-MM.mjs +0 -5
  321. package/dist/renderer-DoDsuZ6G.mjs +0 -5
  322. package/dist/renderer-EpgclS70.mjs +0 -5
  323. package/dist/renderer-GroKiYyK.mjs +0 -5
  324. package/dist/renderer-MQ5K_xIS.mjs +0 -5
  325. package/dist/renderer-U43SflML.mjs +0 -5
  326. package/dist/renderer-UqOmBgCv.mjs +0 -5
  327. package/dist/renderer-XLULgJM0.mjs +0 -5
  328. package/dist/renderer-jUKdkmG5.mjs.map +0 -1
  329. package/dist/renderer-lNjxY1GG.mjs +0 -5
  330. package/dist/renderer-ukufL7pl.mjs +0 -5
  331. package/dist/renderer-wb0NCKdF.mjs +0 -5
  332. package/dist/src-BU6vm9sP-DdTHmCr4.mjs.map +0 -1
  333. package/dist/src-Dy4gnc6w.mjs.map +0 -1
@@ -1,83 +1,29 @@
1
1
  import "./chunk-DOL2i2sR.mjs";
2
2
  import { It as getActiveMediaMetadata, _n as reportRenderDiagnostic, p as DEFAULT_DURATION_MS, rn as mediaSourceTime, vn as resolveBoolean, xn as resolveNumber } from "./dist-C8XSqfTx.mjs";
3
3
  import { i as effectReach, n as defineRenderer, r as effectMaxPixelScale, s as resolveFrameNumber, t as audioRegistry, u as solveHomography } from "./renderer-BuvKGwk3.mjs";
4
- import { $ as poseNodes, B as jointMatrices, F as getPrimitiveGeometry, G as measureMaxCaptionHeight, J as parseDashArray, M as fitSpansToText, N as generateExtrudedTextGeometry, _t as Light3D, ct as srtLoader, d as ScreenSpaceRelightPipeline, dt as transformScale, it as sampleMorphWeights, l as Renderer3D, mt as Camera3D, n as AudioLatentTrackCache, p as SlugFontCache, pt as resolveMediaSourceUrl, q as parseColor, rt as resolveTextSpans, st as spansText, xt as Vector3Math, yt as Matrix4Math } from "./dist--Uu5bQ_6.mjs";
5
- import { a as createFullscreenPipeline, l as fragmentBindGroupLayout, n as PREMULTIPLIED_BLEND, t as FULLSCREEN_QUAD_WGSL } from "./gpu-R4T2Tndu.mjs";
6
- import { i as keyedGain, m as measureText, n as decodeAudioSource, s as resolveLayerDuration } from "./dist-B8Nby2CU.mjs";
4
+ import { $ as measureMaxCaptionHeight, B as fractureModel, Dt as resolveMediaSourceUrl, Ft as Light3D, J as jointMatrices, Kt as defaultCubeFaceSize, Lt as Matrix4Math, S as blendClipLayers, Tt as trsMatrix, U as getPrimitiveGeometry, Ut as createRandom, V as generateExtrudedTextGeometry, Vt as cameraBasis, Wt as cubeFaceCameras, Zt as lensCenterDensity, _ as SpringBones, a as FRACTURE_INTERIOR_MATERIAL, b as applyConstraints, bt as srtLoader, f as ScreenSpaceRelightPipeline, ft as resolveTextSpans, gt as seatOnSurface, ht as scatterOnSurface, kt as Camera3D, m as SlugFontCache, mt as sampleMorphWeights, n as AudioLatentTrackCache, nt as parseDashArray, pt as rotateByQuat, st as poseNodes, tt as parseColor, u as Renderer3D, ut as resolveClips, wt as transformScale, x as bakeCrowdPalette, yt as spansText, z as fitSpansToText, zt as Vector3Math } from "./dist-XqlFM2a8.mjs";
5
+ import { a as createFullscreenPipeline, l as fragmentBindGroupLayout, n as PREMULTIPLIED_BLEND, t as FULLSCREEN_QUAD_WGSL } from "./gpu-CH1PdM4T.mjs";
6
+ import { i as keyedGain, m as measureText, n as decodeAudioSource, s as resolveLayerDuration } from "./dist-COOpgG9G.mjs";
7
7
  import { o as TemporalDeflickerPipeline } from "./dist-DgZpN6IK.mjs";
8
- import { F as collectNodeOps, G as initLayout, H as evaluateKeyframes, R as computeLayout, U as evaluateTrackAtFrame } from "./program-tFKCWU2j.mjs";
8
+ import { B as collectNodeOps, U as computeLayout, Y as initLayout, Z as mappedEase, et as numberAt, it as vec3At, q as evaluateTrackAtFrame, rt as sampleTrack } from "./program-CMigyfso.mjs";
9
9
  import gsapModule from "gsap";
10
10
 
11
- //#region ../../nodes/node-compositor/dist/renderers-IzbDoLXh.mjs
12
- /**
13
- * Solves a cubic bezier curve defined by control points (x1, y1) and (x2, y2)
14
- * for a given progress parameter t in [0, 1].
15
- */
16
- function solveCubicBezier(x1, y1, x2, y2, t) {
17
- if (t <= 0) return 0;
18
- if (t >= 1) return 1;
19
- if (x1 === y1 && x2 === y2) return t;
20
- let u = t;
21
- for (let i = 0; i < 8; i++) {
22
- const oneMinusU$1 = 1 - u;
23
- const oneMinusU2 = oneMinusU$1 * oneMinusU$1;
24
- const u2 = u * u;
25
- const diff = 3 * oneMinusU2 * u * x1 + 3 * oneMinusU$1 * u2 * x2 + u2 * u - t;
26
- if (Math.abs(diff) < 1e-6) break;
27
- const dx = 3 * oneMinusU2 * x1 + 6 * oneMinusU$1 * u * (x2 - x1) + 3 * u2 * (1 - x2);
28
- if (Math.abs(dx) < 1e-6) break;
29
- u -= diff / dx;
30
- u = Math.max(0, Math.min(1, u));
31
- }
32
- const oneMinusU = 1 - u;
33
- const checkX = 3 * oneMinusU * oneMinusU * u * x1 + 3 * oneMinusU * u * u * x2 + u * u * u;
34
- if (Math.abs(checkX - t) > .001) {
35
- let low = 0;
36
- let high = 1;
37
- u = t;
38
- for (let i = 0; i < 16; i++) {
39
- const mid = (low + high) * .5;
40
- const oMid = 1 - mid;
41
- const xMid = 3 * oMid * oMid * mid * x1 + 3 * oMid * mid * mid * x2 + mid * mid * mid;
42
- if (Math.abs(xMid - t) < 1e-5) {
43
- u = mid;
44
- break;
45
- }
46
- if (xMid < t) low = mid;
47
- else high = mid;
48
- u = mid;
49
- }
50
- }
51
- const oU = 1 - u;
52
- return 3 * oU * oU * u * y1 + 3 * oU * u * u * y2 + u * u * u;
53
- }
11
+ //#region ../../nodes/node-compositor/dist/renderers-CrRYcARA.mjs
54
12
  const gsap = gsapModule.timeline ? gsapModule : gsapModule.default || gsapModule;
55
- /** GSAP's shaping for a keyframe segment, so draw-time tracks ease like baked ones. */
56
- function gsapEase(fps) {
57
- return (ease, deltaFrames, t) => {
58
- const e = mappedEase(ease, deltaFrames, fps);
59
- const fn = typeof e === "function" ? e : gsap.parseEase(e);
60
- return fn ? fn(t) : t;
61
- };
62
- }
63
- function evaluateLive(live, frame, fps, signals) {
64
- if ((live.track.source ?? { type: "keyframe" }).type === "keyframe") return evaluateKeyframes(live.track, frame - live.startFrame, gsapEase(fps));
65
- return evaluateTrackAtFrame(live.track, frame, fps, {
66
- startFrame: live.startFrame,
67
- baseValues: live.stub,
68
- signals
69
- });
70
- }
71
13
  /** Seeks the compiled timeline to a frame and applies its live signal tracks. */
72
14
  function seekTimeline(result, frame, fps) {
73
15
  result.tl.seek(frame / fps);
74
16
  for (const live of result.live) {
75
17
  let f = Math.max(live.startFrame, Math.min(live.endFrame, frame));
76
18
  if (live.step && live.step > 1) f = live.startFrame + Math.floor((f - live.startFrame) / live.step) * live.step;
77
- live.stub[live.track.prop] = evaluateLive(live, f, fps, result.signals);
19
+ live.stub[live.track.prop] = sampleTrack(live.track, f, fps, {
20
+ startFrame: live.startFrame,
21
+ base: live.stub,
22
+ signals: result.signals
23
+ });
78
24
  }
79
25
  }
80
- const cache = /* @__PURE__ */ new Map();
26
+ const cache$1 = /* @__PURE__ */ new Map();
81
27
  const nodesHashMemo = /* @__PURE__ */ new WeakMap();
82
28
  /** Entries untouched for this long are dropped (and their timeline killed). */
83
29
  const CACHE_TTL_MS = 1e4;
@@ -121,17 +67,17 @@ function release(value) {
121
67
  * the least-recently-used entry if the cache is at capacity. Every dropped
122
68
  * timeline MUST be killed — see `release`.
123
69
  */
124
- function evictForInsert(cache$1, maxSize) {
70
+ function evictForInsert(cache$2, maxSize) {
125
71
  const now = Date.now();
126
- for (const [key, entry] of cache$1.entries()) if (now - entry.cacheTime > CACHE_TTL_MS) {
127
- cache$1.delete(key);
72
+ for (const [key, entry] of cache$2.entries()) if (now - entry.cacheTime > CACHE_TTL_MS) {
73
+ cache$2.delete(key);
128
74
  release(entry.value);
129
75
  }
130
- if (cache$1.size >= maxSize) {
131
- const oldestKey = cache$1.keys().next().value;
76
+ if (cache$2.size >= maxSize) {
77
+ const oldestKey = cache$2.keys().next().value;
132
78
  if (oldestKey !== void 0) {
133
- const oldest = cache$1.get(oldestKey);
134
- cache$1.delete(oldestKey);
79
+ const oldest = cache$2.get(oldestKey);
80
+ cache$2.delete(oldestKey);
135
81
  if (oldest) release(oldest.value);
136
82
  }
137
83
  }
@@ -147,76 +93,26 @@ function stableStringify(obj) {
147
93
  }).join(",") + "}";
148
94
  }
149
95
  function getCachedTimeline(cacheKey) {
150
- const entry = cache.get(cacheKey);
96
+ const entry = cache$1.get(cacheKey);
151
97
  if (entry) {
152
98
  if (Date.now() - entry.cacheTime > CACHE_TTL_MS) {
153
- cache.delete(cacheKey);
99
+ cache$1.delete(cacheKey);
154
100
  release(entry.value);
155
101
  return;
156
102
  }
157
103
  entry.cacheTime = Date.now();
158
- cache.delete(cacheKey);
159
- cache.set(cacheKey, entry);
104
+ cache$1.delete(cacheKey);
105
+ cache$1.set(cacheKey, entry);
160
106
  return entry.value;
161
107
  }
162
108
  }
163
109
  function setCachedTimeline(cacheKey, value) {
164
- evictForInsert(cache, TIMELINE_CACHE_MAX);
165
- cache.set(cacheKey, {
110
+ evictForInsert(cache$1, TIMELINE_CACHE_MAX);
111
+ cache$1.set(cacheKey, {
166
112
  cacheTime: Date.now(),
167
113
  value
168
114
  });
169
115
  }
170
- function mappedEase(ease, durationFrames, fps = 24) {
171
- if (!ease) return "none";
172
- const { name, dir, params } = ease;
173
- if (name === "none") return "none";
174
- if (name === "spring") {
175
- const damping = Math.max(.001, params?.[0] ?? 10);
176
- const stiffness = Math.max(.001, params?.[1] ?? 100);
177
- const mass = Math.max(.001, params?.[2] ?? 1);
178
- return (p) => {
179
- const t = p * (durationFrames && durationFrames > 0 ? durationFrames / (fps > 0 ? fps : 24) : 1);
180
- const m = mass;
181
- const k = stiffness;
182
- const c = damping;
183
- const w0 = Math.sqrt(k / m);
184
- const zeta = c / (2 * Math.sqrt(k * m));
185
- let x = 0;
186
- if (zeta < 1) {
187
- const wd = w0 * Math.sqrt(1 - zeta * zeta);
188
- const A = -1;
189
- const B = -zeta * w0 / wd;
190
- x = Math.exp(-zeta * w0 * t) * (A * Math.cos(wd * t) + B * Math.sin(wd * t));
191
- } else if (zeta === 1) {
192
- const A = -1;
193
- const B = -w0;
194
- x = Math.exp(-w0 * t) * (A + B * t);
195
- } else {
196
- const r1 = -w0 * (zeta - Math.sqrt(zeta * zeta - 1));
197
- const r2 = -w0 * (zeta + Math.sqrt(zeta * zeta - 1));
198
- const C1 = -1 / (1 - r1 / r2);
199
- const C2 = -1 - C1;
200
- x = C1 * Math.exp(r1 * t) + C2 * Math.exp(r2 * t);
201
- }
202
- return 1 + x;
203
- };
204
- }
205
- if (name === "cubic") {
206
- const x1 = params?.[0] ?? .25;
207
- const y1 = params?.[1] ?? .1;
208
- const x2 = params?.[2] ?? .25;
209
- const y2 = params?.[3] ?? 1;
210
- return (p) => solveCubicBezier(x1, y1, x2, y2, p);
211
- }
212
- if (name === "hold") return (p) => p >= .9999 ? 1 : 0;
213
- const easeName = name;
214
- let suffix = "";
215
- if (params && params.length > 0) suffix = `(${params.join(", ")})`;
216
- else if (name === "back") suffix = "(1.7)";
217
- else if (name === "elastic") suffix = "(1, 0.3)";
218
- return `${easeName}.${dir}${suffix}`;
219
- }
220
116
  /** Props that switch rather than tween: each key is a set from its frame on. */
221
117
  const DISCRETE_PROPS = new Set(["hidden", "muted"]);
222
118
  function compileTrackToTimeline(tl, stub, track, startFrame, fps, durationFrames, signals, live, step) {
@@ -231,7 +127,11 @@ function compileTrackToTimeline(tl, stub, track, startFrame, fps, durationFrames
231
127
  endFrame: startFrame + (Number.isFinite(durationFrames) ? durationFrames : Math.round(fps * 5)),
232
128
  ...stepped && { step }
233
129
  };
234
- stub[prop] = evaluateLive(entry, startFrame, fps, signals);
130
+ stub[prop] = sampleTrack(track, startFrame, fps, {
131
+ startFrame,
132
+ base: stub,
133
+ signals
134
+ });
235
135
  live.push(entry);
236
136
  return {
237
137
  dropped: 0,
@@ -366,6 +266,28 @@ function compileTrackToTimeline(tl, stub, track, startFrame, fps, durationFrames
366
266
  maxAuthoredFrame
367
267
  };
368
268
  }
269
+ /** A camera's `x` / `y` / `z` are its eye, the same as `cameraX` / `cameraY` / `cameraZ`. */
270
+ const CAMERA_EYE = {
271
+ x: "cameraX",
272
+ y: "cameraY",
273
+ z: "cameraZ"
274
+ };
275
+ /**
276
+ * A layer's tracks, with a camera's `x` / `y` / `z` tracks driving its eye:
277
+ * the eye is read from `cameraX` / `cameraY` / `cameraZ`, which the static
278
+ * `x` / `y` / `z` seed, so a track left on `x` would never move it.
279
+ */
280
+ function layerTracks(lop) {
281
+ const tracks = lop.animation?.tracks ?? [];
282
+ if (lop.kind !== "camera") return tracks;
283
+ return tracks.map((t) => {
284
+ const eye = CAMERA_EYE[t.prop];
285
+ return eye ? {
286
+ ...t,
287
+ prop: eye
288
+ } : t;
289
+ });
290
+ }
369
291
  /**
370
292
  * Dedupe per compiled cache key — a fresh render of the same broken doc SHOULD
371
293
  * re-warn. Single-frame renders are the exception: each `renderFrame` call has
@@ -434,6 +356,7 @@ function hashNodes(nodeOps, width, height, fps, durationSec) {
434
356
  maxBlurRadius: lop.maxBlurRadius,
435
357
  shakeTranslation: lop.shakeTranslation,
436
358
  shakeRotation: lop.shakeRotation,
359
+ relativitySpeed: lop.relativitySpeed,
437
360
  orbitRadius: lop.orbitRadius,
438
361
  orbitAzimuth: lop.orbitAzimuth,
439
362
  orbitElevation: lop.orbitElevation,
@@ -443,11 +366,35 @@ function hashNodes(nodeOps, width, height, fps, durationSec) {
443
366
  specularIntensity: lop.specularIntensity,
444
367
  ambientIntensity: lop.ambientIntensity,
445
368
  metallic: lop.metallic,
369
+ emissive: lop.emissive,
370
+ emissiveIntensity: lop.emissiveIntensity,
371
+ clearcoat: lop.clearcoat,
372
+ clearcoatRoughness: lop.clearcoatRoughness,
373
+ anisotropy: lop.anisotropy,
374
+ anisotropyRotation: lop.anisotropyRotation,
375
+ subsurface: lop.subsurface,
376
+ subsurfaceColor: lop.subsurfaceColor,
377
+ sheen: lop.sheen,
378
+ sheenColor: lop.sheenColor,
379
+ sheenRoughness: lop.sheenRoughness,
380
+ iridescence: lop.iridescence,
381
+ iridescenceThickness: lop.iridescenceThickness,
382
+ iridescenceIor: lop.iridescenceIor,
383
+ triplanar: lop.triplanar,
384
+ triplanarScale: lop.triplanarScale,
385
+ triplanarSharpness: lop.triplanarSharpness,
446
386
  lightType: lop.lightType,
387
+ color: lop.color,
447
388
  intensity: lop.intensity,
448
389
  radius: lop.radius,
449
390
  angle: lop.angle,
450
391
  penumbra: lop.penumbra,
392
+ exposure: lop.exposure,
393
+ hdri: typeof lop.hdri === "string" ? lop.hdri : Boolean(lop.hdri),
394
+ volumetricFog: lop.volumetricFog,
395
+ volumetricFogDensity: lop.volumetricFogDensity,
396
+ volumetricFogAnisotropy: lop.volumetricFogAnisotropy,
397
+ volumetricFogNoiseStrength: lop.volumetricFogNoiseStrength,
451
398
  animationProgress: lop.animationProgress,
452
399
  animationTime: lop.animationTime,
453
400
  animationSpeed: lop.animationSpeed,
@@ -540,13 +487,30 @@ function compileTimeline(renderId, virtualMedia, options) {
540
487
  is3D: lop.is3D ?? (lop.kind === "camera" || lop.kind === "light" || lop.kind === "model3d" ? true : false),
541
488
  z: lop.z ?? (lop.kind === "camera" ? lop.z ?? -1500 : 0),
542
489
  scaleZ: lop.scaleZ ?? 1,
543
- twoSided: lop.twoSided ?? true,
490
+ twoSided: lop.twoSided ?? lop.kind !== "light",
544
491
  material: lop.material,
545
492
  shininess: lop.shininess,
546
493
  roughness: lop.roughness,
547
494
  specularIntensity: lop.specularIntensity,
548
495
  ambientIntensity: lop.ambientIntensity,
549
496
  metallic: lop.metallic,
497
+ emissive: lop.emissive,
498
+ emissiveIntensity: lop.emissiveIntensity,
499
+ clearcoat: lop.clearcoat,
500
+ clearcoatRoughness: lop.clearcoatRoughness,
501
+ anisotropy: lop.anisotropy,
502
+ anisotropyRotation: lop.anisotropyRotation,
503
+ subsurface: lop.subsurface,
504
+ subsurfaceColor: lop.subsurfaceColor,
505
+ sheen: lop.sheen,
506
+ sheenColor: lop.sheenColor,
507
+ sheenRoughness: lop.sheenRoughness,
508
+ iridescence: lop.iridescence,
509
+ iridescenceThickness: lop.iridescenceThickness,
510
+ iridescenceIor: lop.iridescenceIor,
511
+ triplanar: lop.triplanar,
512
+ triplanarScale: lop.triplanarScale,
513
+ triplanarSharpness: lop.triplanarSharpness,
550
514
  animationProgress: lop.animationProgress ?? 0,
551
515
  animationTime: lop.animationTime ?? 0,
552
516
  animationSpeed: lop.animationSpeed ?? 1,
@@ -570,17 +534,23 @@ function compileTimeline(renderId, virtualMedia, options) {
570
534
  maxBlurRadius: lop.maxBlurRadius ?? lop.dof?.maxBlurRadius ?? 24,
571
535
  shakeTranslation: lop.shakeTranslation ?? (lop.shake?.enabled ? lop.shake?.translationAmplitude ?? 0 : 0),
572
536
  shakeRotation: lop.shakeRotation ?? (lop.shake?.enabled ? lop.shake?.rotationAmplitude ?? 0 : 0),
537
+ relativitySpeed: lop.relativitySpeed ?? lop.relativity?.speed ?? 0,
573
538
  orbitRadius: lop.orbitRadius,
574
539
  orbitAzimuth: lop.orbitAzimuth,
575
540
  orbitElevation: lop.orbitElevation,
576
541
  lightType: lop.lightType,
542
+ color: lop.color,
577
543
  intensity: lop.intensity ?? (lop.kind === "light" ? 1 : void 0),
578
544
  radius: lop.radius,
579
545
  angle: lop.angle,
580
546
  penumbra: lop.penumbra,
581
- decay: lop.decay
547
+ decay: lop.decay,
548
+ exposure: lop.exposure,
549
+ volumetricFogDensity: lop.volumetricFogDensity,
550
+ volumetricFogAnisotropy: lop.volumetricFogAnisotropy,
551
+ volumetricFogNoiseStrength: lop.volumetricFogNoiseStrength
582
552
  };
583
- for (const track of lop.animation?.tracks ?? []) if ((track.source ?? { type: "keyframe" }).type !== "keyframe") stub[track.prop] = evaluateTrackAtFrame(track, lop.startFrame ?? 0, fps, {
553
+ for (const track of layerTracks(lop)) if ((track.source ?? { type: "keyframe" }).type !== "keyframe") stub[track.prop] = evaluateTrackAtFrame(track, lop.startFrame ?? 0, fps, {
584
554
  startFrame: lop.startFrame ?? 0,
585
555
  baseValues: stub,
586
556
  signals
@@ -610,7 +580,7 @@ function compileTimeline(renderId, virtualMedia, options) {
610
580
  const durationMs = resolveLayerDuration(void 0, childMeta?.durationMs ?? void 0, composeDuration, layerType);
611
581
  durationFrames = Math.max(1, Math.round(durationMs / 1e3 * fps));
612
582
  }
613
- for (const track of lop.animation?.tracks ?? []) {
583
+ for (const track of layerTracks(lop)) {
614
584
  const { dropped, maxAuthoredFrame } = compileTrackToTimeline(tl, stub, track, startFrame, fps, durationFrames, signals, live, lop.animation?.frameStep);
615
585
  if (dropped > 0) truncations.push({
616
586
  layerId,
@@ -633,6 +603,7 @@ function compileTimeline(renderId, virtualMedia, options) {
633
603
  tl.seek(1e-6);
634
604
  tl.seek(0);
635
605
  const compileResult = {
606
+ key: cacheKey,
636
607
  tl,
637
608
  targetsById,
638
609
  nodes: nodeOps,
@@ -687,6 +658,232 @@ function compileLayerTimeline(layerId, animation, baseVolume, baseMuted, fps) {
687
658
  return result;
688
659
  }
689
660
  /**
661
+ * Character animation for the compositor: poses a model from its clip
662
+ * layers, look-at and IK constraints (targets in scene coordinates) and
663
+ * spring bones (replayed from frame 0 and cached, so frames render the
664
+ * same in any order); and the per-instance records of skinned crowds.
665
+ */
666
+ const transformPoint$1 = (m, p) => [
667
+ m[0] * p[0] + m[4] * p[1] + m[8] * p[2] + m[12],
668
+ m[1] * p[0] + m[5] * p[1] + m[9] * p[2] + m[13],
669
+ m[2] * p[0] + m[6] * p[1] + m[10] * p[2] + m[14]
670
+ ];
671
+ const transformDir = (m, v) => [
672
+ m[0] * v[0] + m[4] * v[1] + m[8] * v[2],
673
+ m[1] * v[0] + m[5] * v[1] + m[9] * v[2],
674
+ m[2] * v[0] + m[6] * v[1] + m[10] * v[2]
675
+ ];
676
+ /**
677
+ * A mesh's morph weights: the clip's (or the file's defaults), with any
678
+ * target the layer names in `morphs` set from its track. Names match
679
+ * case-insensitively against the mesh's `morphTargetNames`.
680
+ */
681
+ function namedMorphWeights(names, base, morphs, clock) {
682
+ if (!morphs || !names?.length) return base;
683
+ const out = [...base];
684
+ const index = new Map(names.map((n, i) => [n.toLowerCase(), i]));
685
+ for (const [name, track] of Object.entries(morphs)) {
686
+ const i = index.get(name.toLowerCase());
687
+ if (i !== void 0) out[i] = numberAt(track, clock, out[i] ?? 0);
688
+ }
689
+ return out;
690
+ }
691
+ /** Whether a node needs the rig evaluator (anything beyond one plain clip). */
692
+ function hasRig(n) {
693
+ return Boolean(n.animationLayers?.length || n.ik?.length || n.lookAt?.length || n.springBones?.length || n.pose && Object.keys(n.pose).length || n.aim?.length || n.drive?.length);
694
+ }
695
+ /** Local rotation for Euler degrees, applied Z, then Y, then X (R = Rz · Ry · Rx), like layers. */
696
+ function eulerQuat(rx, ry, rz) {
697
+ const h = (d) => d * Math.PI / 360;
698
+ const [sx, cx] = [Math.sin(h(rx)), Math.cos(h(rx))];
699
+ const [sy, cy] = [Math.sin(h(ry)), Math.cos(h(ry))];
700
+ const [sz, cz] = [Math.sin(h(rz)), Math.cos(h(rz))];
701
+ return [
702
+ cz * cy * sx - sz * sy * cx,
703
+ cz * sy * cx + sz * cy * sx,
704
+ sz * cy * cx - cz * sy * sx,
705
+ cz * cy * cx + sz * sy * sx
706
+ ];
707
+ }
708
+ function jointPoses(p, clock) {
709
+ return Object.entries(p.node.pose ?? {}).map(([joint, j]) => ({
710
+ joint,
711
+ rotation: eulerQuat(numberAt(j.rotateX, clock, 0), numberAt(j.rotateY, clock, 0), numberAt(j.rotateZ, clock, 0)),
712
+ offset: [
713
+ numberAt(j.x, clock, 0),
714
+ numberAt(j.y, clock, 0),
715
+ numberAt(j.z, clock, 0)
716
+ ],
717
+ weight: numberAt(j.weight, clock, 1),
718
+ mode: j.mode ?? "additive"
719
+ }));
720
+ }
721
+ function clipLayers(p, frame) {
722
+ const layers = p.node.animationLayers;
723
+ if (!layers?.length) return p.clip ? [{
724
+ clip: p.clip,
725
+ time: p.clipSec,
726
+ weight: 1
727
+ }] : [];
728
+ const out = [];
729
+ for (const l of layers) {
730
+ const [clip] = resolveClips(p.model, [l.clip]);
731
+ if (!clip) continue;
732
+ let time = (l.timeOffset ?? 0) + frame / p.fps * (l.speed ?? 1);
733
+ if (clip.duration > 0) time = l.loop ?? true ? (time % clip.duration + clip.duration) % clip.duration : Math.min(Math.max(time, 0), clip.duration);
734
+ out.push({
735
+ clip,
736
+ time,
737
+ weight: numberAt(l.weight, {
738
+ frame,
739
+ fps: p.fps
740
+ }, 1),
741
+ mask: l.mask
742
+ });
743
+ }
744
+ return out;
745
+ }
746
+ function constraintsAt(p, frame, toModel) {
747
+ const clock = {
748
+ frame,
749
+ fps: p.fps
750
+ };
751
+ const point = (t) => transformPoint$1(toModel, vec3At(t, clock));
752
+ const direction = (t) => transformDir(toModel, vec3At(t, clock));
753
+ return {
754
+ pose: jointPoses(p, clock),
755
+ aim: (p.node.aim ?? []).map((a) => ({
756
+ joint: a.joint,
757
+ direction: direction(a.direction),
758
+ weight: numberAt(a.weight, clock, 1),
759
+ ...a.axis && { axis: a.axis }
760
+ })),
761
+ ik: (p.node.ik ?? []).map((c) => ({
762
+ chain: c.chain,
763
+ target: point(c.target),
764
+ pole: c.poleTarget ? point(c.poleTarget) : void 0,
765
+ weight: numberAt(c.weight, clock, 1)
766
+ })),
767
+ drive: [...p.model.drivers ?? [], ...p.node.drive ?? []],
768
+ lookAt: (p.node.lookAt ?? []).map((c) => ({
769
+ joint: c.joint,
770
+ target: point(c.target),
771
+ weight: numberAt(c.weight, clock, 1),
772
+ maxAngleDeg: c.maxAngleDeg ?? 70,
773
+ axis: c.axis ?? [
774
+ 0,
775
+ 0,
776
+ 1
777
+ ]
778
+ }))
779
+ };
780
+ }
781
+ /** The pose before springs: blended clips, then constraints. */
782
+ function basePose(p, frame, toModel) {
783
+ const rig = blendClipLayers(p.model, clipLayers(p, frame));
784
+ applyConstraints(rig, constraintsAt(p, frame, toModel));
785
+ return rig;
786
+ }
787
+ const springStates = /* @__PURE__ */ new Map();
788
+ function springsAt(p, toModel, rig) {
789
+ const chains = p.node.springBones;
790
+ if (!chains?.length) return void 0;
791
+ const config = JSON.stringify([
792
+ chains,
793
+ p.node.animationLayers,
794
+ p.node.pose,
795
+ p.node.aim,
796
+ p.fps
797
+ ]);
798
+ let s = springStates.get(p.key);
799
+ const frame = Math.max(0, Math.floor(p.frame));
800
+ if (!s || s.model !== p.model || s.config !== config || s.frame > frame) {
801
+ const gravityModel = (g) => transformDir(toModel, g ?? [
802
+ 0,
803
+ 0,
804
+ 0
805
+ ]);
806
+ s = {
807
+ model: p.model,
808
+ config,
809
+ springs: new SpringBones(rig, chains.map((c) => ({
810
+ ...c,
811
+ gravity: gravityModel(c.gravity)
812
+ }))),
813
+ frame: -1
814
+ };
815
+ if (springStates.size > 256) springStates.clear();
816
+ springStates.set(p.key, s);
817
+ }
818
+ for (let f = s.frame + 1; f <= frame; f++) {
819
+ s.springs.step(basePose(p, f, toModel), 1 / p.fps);
820
+ s.frame = f;
821
+ }
822
+ return s.springs;
823
+ }
824
+ /** Model-space node matrices for this frame. */
825
+ function poseCharacter(p) {
826
+ if (!hasRig(p.node) && !p.model.drivers?.length) return poseNodes(p.model, p.clip, p.clipSec);
827
+ const toModel = Matrix4Math.invert(p.sceneRoot) ?? Matrix4Math.identity();
828
+ const rig = basePose(p, p.frame, toModel);
829
+ springsAt(p, toModel, rig)?.apply(rig);
830
+ return rig.world();
831
+ }
832
+ const bakes = /* @__PURE__ */ new WeakMap();
833
+ const palettes = /* @__PURE__ */ new WeakMap();
834
+ /** A baked palette on the GPU, uploaded once. */
835
+ function paletteBuffer(device, palette) {
836
+ let buffer = palettes.get(palette);
837
+ if (!buffer) {
838
+ buffer = device.createBuffer({
839
+ label: "CrowdPalette",
840
+ size: Math.max(64, palette.byteLength),
841
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
842
+ });
843
+ device.queue.writeBuffer(buffer, 0, palette);
844
+ palettes.set(palette, buffer);
845
+ }
846
+ return buffer;
847
+ }
848
+ /** The model's crowd palette for these clips, baked once. */
849
+ function crowdBake(model, crowd) {
850
+ const key = JSON.stringify([crowd.clips, crowd.fps ?? 30]);
851
+ let byKey = bakes.get(model);
852
+ if (!byKey) {
853
+ byKey = /* @__PURE__ */ new Map();
854
+ bakes.set(model, byKey);
855
+ }
856
+ let bake = byKey.get(key);
857
+ if (!bake) {
858
+ const clips = resolveClips(model, crowd.clips);
859
+ if (!clips.length) return void 0;
860
+ bake = bakeCrowdPalette(model, clips, crowd.fps ?? 30);
861
+ byKey.set(key, bake);
862
+ }
863
+ return bake;
864
+ }
865
+ /**
866
+ * Instance `i`'s second record: (first baked frame, frames per loop, phase
867
+ * in frames, frames per second of playback). Unset fields come from the seed: clip i % n, a random
868
+ * phase, a speed within `speedRange`.
869
+ */
870
+ function crowdRecord(bake, crowd, i, anim) {
871
+ const rand = createRandom((crowd.seed ?? 1) * 7919 + i * 104729);
872
+ const pick = rand();
873
+ const speedDraw = rand();
874
+ const clipIndex = (anim.clip ?? i) % bake.clips.length;
875
+ const range = bake.clips[Math.max(0, clipIndex)];
876
+ const [s0, s1] = crowd.speedRange ?? [1, 1];
877
+ const offset = anim.animOffset ?? ((crowd.phase ?? "random") === "random" ? pick * range.duration : 0);
878
+ const rate = range.duration > 0 ? range.count / range.duration : bake.fps;
879
+ return [
880
+ range.base,
881
+ range.count,
882
+ offset * rate,
883
+ (anim.animSpeed ?? s0 + (s1 - s0) * speedDraw) * rate
884
+ ];
885
+ }
886
+ /**
690
887
  * Which container kinds paint their own background.
691
888
  *
692
889
  * flex/block/box all accept an optional `background` in the program schema.
@@ -698,6 +895,110 @@ function shouldPaintContainerBackground(kind, background) {
698
895
  if (!background) return false;
699
896
  return kind === "flex" || kind === "block" || kind === "box";
700
897
  }
898
+ /** Largest face a cube camera picks on its own; `lens.cubeSize` may go higher. */
899
+ const DEFAULT_MAX_FACE = 4096;
900
+ /**
901
+ * The cube camera an authored camera asks for, or null for an ordinary
902
+ * perspective view. A camera with `relativity` is always a cube camera, even
903
+ * at speed 0, so a shot that accelerates from rest never switches pipelines.
904
+ */
905
+ function resolveCubeCamera(authored, camera, speed, width, height, maxTextureSize) {
906
+ const projection = authored?.lens?.projection ?? "perspective";
907
+ const relativity = authored?.relativity;
908
+ if (projection === "perspective" && !relativity) return null;
909
+ const lens = {
910
+ projection,
911
+ fov: camera.fov,
912
+ zoom: camera.zoom
913
+ };
914
+ const requested = authored?.lens?.cubeSize;
915
+ const faceSize = Math.min(maxTextureSize, requested ?? defaultCubeFaceSize(lens, width, height, Math.min(DEFAULT_MAX_FACE, maxTextureSize)));
916
+ const basis = cameraBasis(camera);
917
+ const observer = relativity ? {
918
+ speed,
919
+ direction: relativity.direction ? Vector3Math.normalize(relativity.direction) : basis.forward,
920
+ doppler: relativity.doppler ?? true,
921
+ beaming: relativity.beaming ?? true,
922
+ exposure: relativity.exposure ?? "auto"
923
+ } : null;
924
+ return {
925
+ faceSize,
926
+ remap: {
927
+ ...lens,
928
+ basis,
929
+ observer
930
+ },
931
+ centerDensity: lensCenterDensity(lens, width, height)
932
+ };
933
+ }
934
+ /**
935
+ * Whether the pieces are all still where they started at `frame` (or there
936
+ * is no track): the layer looks whole, and its cut faces, hidden inside,
937
+ * need not draw (where pieces meet, a cut face can tie the surface's depth).
938
+ */
939
+ function fractureWhole(model, spec, frame) {
940
+ const track = spec.track;
941
+ const stride = (model.nodes?.length ?? 0) * 7;
942
+ if (!track?.length || !stride) return true;
943
+ const frames = Math.floor(track.length / stride);
944
+ const at = Math.min(Math.max(Math.ceil(frame), 0), frames - 1) * stride;
945
+ for (let i = 0; i < stride; i++) if (track[at + i] !== track[i]) return false;
946
+ return true;
947
+ }
948
+ /** The fractured model for a node's source model (cached per model and spec). */
949
+ function fracturedModel(model, spec) {
950
+ return fractureModel(model, spec).model;
951
+ }
952
+ function poseAt(track, at) {
953
+ return {
954
+ p: [
955
+ track[at],
956
+ track[at + 1],
957
+ track[at + 2]
958
+ ],
959
+ q: [
960
+ track[at + 3],
961
+ track[at + 4],
962
+ track[at + 5],
963
+ track[at + 6]
964
+ ]
965
+ };
966
+ }
967
+ function blend(a, b, t) {
968
+ if (t <= 0) return a;
969
+ const sign = a.q.reduce((s, v, i) => s + v * b.q[i], 0) < 0 ? -1 : 1;
970
+ const q = a.q.map((v, i) => v + (b.q[i] * sign - v) * t);
971
+ const len = Math.hypot(...q) || 1;
972
+ return {
973
+ p: a.p.map((v, i) => v + (b.p[i] - v) * t),
974
+ q: q.map((v) => v / len)
975
+ };
976
+ }
977
+ /**
978
+ * Model-space matrices of the pieces' nodes at `frame`. The track is in the
979
+ * layer's parent space (physics reads the layer's own x/y/z, as for any
980
+ * body), and `layerProps` is the layer's matrix in that space, L. A piece
981
+ * at pose (p, q) has node matrix L⁻¹ · T(p) · R(q) · S, so with its inverse
982
+ * bind T(−centroid) a vertex lands at parent · (p + q · S · (v − centroid)):
983
+ * pieces fly in their group, like other bodies. Undefined without a track.
984
+ */
985
+ function fracturePose(model, spec, frame, layerProps, scale$1) {
986
+ const track = spec.track;
987
+ const pieces = model.nodes?.length ?? 0;
988
+ const stride = pieces * 7;
989
+ if (!track?.length || !stride || track.length % stride !== 0) return void 0;
990
+ const frames = track.length / stride;
991
+ const f = Math.min(Math.max(frame, 0), frames - 1);
992
+ const f0 = Math.floor(f);
993
+ const f1 = Math.min(frames - 1, f0 + 1);
994
+ const toModel = Matrix4Math.invert(layerProps) ?? Matrix4Math.identity();
995
+ const out = [];
996
+ for (let i = 0; i < pieces; i++) {
997
+ const { p, q } = blend(poseAt(track, f0 * stride + i * 7), poseAt(track, f1 * stride + i * 7), f - f0);
998
+ out.push(Matrix4Math.multiply(toModel, trsMatrix(p, q, scale$1)));
999
+ }
1000
+ return out;
1001
+ }
701
1002
  const TRANSFORMS = new Set([
702
1003
  "position",
703
1004
  "rotation",
@@ -729,6 +1030,253 @@ function compatibleMeshDraws(a, b) {
729
1030
  for (const key of keys) if (!TRANSFORMS.has(key) && !sameValue(left[key], right[key])) return false;
730
1031
  return true;
731
1032
  }
1033
+ /** Two frames of one value: the latest and the one before it. */
1034
+ var Slots = class {
1035
+ frame = NaN;
1036
+ value;
1037
+ prevFrame = NaN;
1038
+ prevValue;
1039
+ write(frame, value) {
1040
+ if (frame !== this.frame) {
1041
+ this.prevFrame = this.frame;
1042
+ this.prevValue = this.value;
1043
+ }
1044
+ this.frame = frame;
1045
+ this.value = value;
1046
+ }
1047
+ /** The value at `frame`, if it is one of the two held. */
1048
+ at(frame) {
1049
+ if (frame === this.frame) return this.value;
1050
+ if (frame === this.prevFrame) return this.prevValue;
1051
+ }
1052
+ };
1053
+ var MotionHistory = class {
1054
+ states = /* @__PURE__ */ new Map();
1055
+ instances;
1056
+ vertices;
1057
+ camera = new Slots();
1058
+ /** Frames recorded, latest last (two kept). */
1059
+ frames = new Slots();
1060
+ constructor(device) {
1061
+ this.instances = new GpuPairs(device, "Instances");
1062
+ this.vertices = new GpuPairs(device, "Vertices");
1063
+ }
1064
+ /** Whether rendering `frame` must render `frame - 1` first to know its motion. */
1065
+ needsPrevious(frame) {
1066
+ return frame > 0 && this.frames.at(frame - 1) === void 0;
1067
+ }
1068
+ previousCamera(frame) {
1069
+ return this.camera.at(frame - 1);
1070
+ }
1071
+ /** A mesh's state at `frame - 1`, shaped for the velocity pass. */
1072
+ previousMesh(key, frame, now) {
1073
+ const prev = this.states.get(key)?.at(frame - 1);
1074
+ if (prev?.kind !== "mesh") return void 0;
1075
+ const out = { modelMatrix: prev.modelMatrix };
1076
+ if (now.skinning && prev.skinning?.length === now.skinning.length) out.skinningMatrices = prev.skinning;
1077
+ const copy = this.instances.at(key, frame - 1);
1078
+ if (now.instanceBuffer && copy && prev.instanceBytes === now.instanceBuffer.size) out.instanceBuffer = copy;
1079
+ const vertices = this.vertices.at(key, frame - 1);
1080
+ if (now.vertexBytes && vertices && prev.vertexBytes === now.vertexBytes) out.vertexBuffer = vertices;
1081
+ if (now.wind && prev.windTime !== void 0) out.wind = {
1082
+ ...now.wind,
1083
+ time: prev.windTime
1084
+ };
1085
+ if (now.crowd && prev.crowdTime !== void 0) out.crowd = {
1086
+ ...now.crowd,
1087
+ time: prev.crowdTime
1088
+ };
1089
+ return out;
1090
+ }
1091
+ previousQuad(key, frame) {
1092
+ const prev = this.states.get(key)?.at(frame - 1);
1093
+ return prev?.kind === "quad" ? prev.modelMatrix : void 0;
1094
+ }
1095
+ /**
1096
+ * Records a mesh at `frame`; its instance records and deformed vertices
1097
+ * (when the GPU deforms it) are copied in `encoder`.
1098
+ */
1099
+ recordMesh(encoder, key, frame, state, buffers) {
1100
+ const { instanceBuffer, vertices } = buffers;
1101
+ if (instanceBuffer) this.instances.copy(encoder, key, frame, instanceBuffer, instanceBuffer.size, GPUBufferUsage.STORAGE);
1102
+ if (vertices) this.vertices.copy(encoder, key, frame, vertices.buffer, vertices.bytes, GPUBufferUsage.VERTEX);
1103
+ this.slots(key).write(frame, {
1104
+ kind: "mesh",
1105
+ ...state,
1106
+ ...state.skinning && { skinning: state.skinning.slice() },
1107
+ instanceBytes: instanceBuffer?.size,
1108
+ vertexBytes: vertices?.bytes
1109
+ });
1110
+ }
1111
+ previousSlug(key, frame) {
1112
+ const prev = this.states.get(key)?.at(frame - 1);
1113
+ return prev?.kind === "slug" ? prev : void 0;
1114
+ }
1115
+ /** Records a text plane at `frame`; glyph records are copied (batches are rebuilt each frame). */
1116
+ recordSlug(key, frame, modelMatrix, batches) {
1117
+ this.slots(key).write(frame, {
1118
+ kind: "slug",
1119
+ modelMatrix,
1120
+ batches: batches.map((b) => ({
1121
+ ...b,
1122
+ instances: b.instances.slice(0, b.instanceCount * 24)
1123
+ }))
1124
+ });
1125
+ }
1126
+ recordQuad(key, frame, modelMatrix) {
1127
+ this.slots(key).write(frame, {
1128
+ kind: "quad",
1129
+ modelMatrix
1130
+ });
1131
+ }
1132
+ recordFrame(frame, camera) {
1133
+ this.camera.write(frame, camera);
1134
+ this.frames.write(frame, true);
1135
+ }
1136
+ slots(key) {
1137
+ let s = this.states.get(key);
1138
+ if (!s) {
1139
+ s = new Slots();
1140
+ this.states.set(key, s);
1141
+ }
1142
+ return s;
1143
+ }
1144
+ };
1145
+ /** GPU copies of a buffer per key, one per frame parity. */
1146
+ var GpuPairs = class {
1147
+ pairs = /* @__PURE__ */ new Map();
1148
+ constructor(device, label) {
1149
+ this.device = device;
1150
+ this.label = label;
1151
+ }
1152
+ at(key, frame) {
1153
+ return this.pairs.get(key)?.[frame & 1];
1154
+ }
1155
+ copy(encoder, key, frame, source, bytes, usage) {
1156
+ const pair = this.pairs.get(key) ?? [];
1157
+ const slot = frame & 1;
1158
+ let copy = pair[slot];
1159
+ if (!copy || copy.size !== bytes) {
1160
+ copy?.destroy();
1161
+ copy = this.device.createBuffer({
1162
+ label: `MotionHistory${this.label}:${key}`,
1163
+ size: bytes,
1164
+ usage: usage | GPUBufferUsage.COPY_DST
1165
+ });
1166
+ pair[slot] = copy;
1167
+ this.pairs.set(key, pair);
1168
+ }
1169
+ encoder.copyBufferToBuffer(source, 0, copy, 0, bytes);
1170
+ }
1171
+ };
1172
+ const histories = /* @__PURE__ */ new Map();
1173
+ /** The motion history of one composition render (by render id). */
1174
+ function motionHistory(device, renderId) {
1175
+ let h = histories.get(renderId);
1176
+ if (!h) {
1177
+ if (histories.size > 64) histories.clear();
1178
+ h = new MotionHistory(device);
1179
+ histories.set(renderId, h);
1180
+ }
1181
+ return h;
1182
+ }
1183
+ /** T · Rz · Ry · Rx · S: the transform the compositor draws a model with. */
1184
+ function nodeMatrix(n) {
1185
+ const rad = Math.PI / 180;
1186
+ const s = n.scale ?? 1;
1187
+ let m = Matrix4Math.translate(n.x ?? 0, n.y ?? 0, n.z ?? 0);
1188
+ if (n.rotateZ) m = Matrix4Math.multiply(m, Matrix4Math.rotateZ(n.rotateZ * rad));
1189
+ if (n.rotateY) m = Matrix4Math.multiply(m, Matrix4Math.rotateY(n.rotateY * rad));
1190
+ if (n.rotateX) m = Matrix4Math.multiply(m, Matrix4Math.rotateX(n.rotateX * rad));
1191
+ return Matrix4Math.multiply(m, Matrix4Math.scale(s * (n.scaleX ?? 1), s * (n.scaleY ?? 1), s * (n.scaleZ ?? 1)));
1192
+ }
1193
+ const isModel = (v) => typeof v === "object" && v !== null && "meshes" in v;
1194
+ /** The surface's mesh: in memory, rebuilt from its primitive spec, or loaded. */
1195
+ async function surfaceModel(n, load) {
1196
+ if (isModel(n.modelData)) return n.modelData;
1197
+ if (n.primitive3d) return getPrimitiveGeometry(n.primitive3d);
1198
+ if (n.src) return load(n.src, n);
1199
+ }
1200
+ const NO_PRE = {
1201
+ q: [
1202
+ 0,
1203
+ 0,
1204
+ 0,
1205
+ 1
1206
+ ],
1207
+ s: 1
1208
+ };
1209
+ function composeQuat(a, b) {
1210
+ return [
1211
+ a[3] * b[0] + a[0] * b[3] + a[1] * b[2] - a[2] * b[1],
1212
+ a[3] * b[1] - a[0] * b[2] + a[1] * b[3] + a[2] * b[0],
1213
+ a[3] * b[2] + a[0] * b[1] - a[1] * b[0] + a[2] * b[3],
1214
+ a[3] * b[3] - a[0] * b[0] - a[1] * b[1] - a[2] * b[2]
1215
+ ];
1216
+ }
1217
+ /** The variant's lowest point after its own rotation and scale: the largest y (up is -y). */
1218
+ function variantBottom(model, pre) {
1219
+ const b = model.bounds;
1220
+ if (!b) return 0;
1221
+ let bottom = Number.NEGATIVE_INFINITY;
1222
+ for (let k = 0; k < 8; k++) {
1223
+ const corner = [
1224
+ k & 1 ? b.max[0] : b.min[0],
1225
+ k & 2 ? b.max[1] : b.min[1],
1226
+ k & 4 ? b.max[2] : b.min[2]
1227
+ ];
1228
+ bottom = Math.max(bottom, rotateByQuat(pre.q, corner)[1] * pre.s);
1229
+ }
1230
+ return bottom;
1231
+ }
1232
+ const cache = /* @__PURE__ */ new WeakMap();
1233
+ /**
1234
+ * Instance transforms for a scatter: points on the surface, each seated on
1235
+ * its variant's base (instance i draws variant i % bottoms.length; bottoms
1236
+ * already include the variant's own rotation and scale).
1237
+ */
1238
+ function scatterTransforms(scatter, surface, bottoms) {
1239
+ const surfaceNode = scatter.surface;
1240
+ const key = JSON.stringify([bottoms, surface.bounds?.size]);
1241
+ const hit = cache.get(scatter);
1242
+ if (hit && hit.key === key) return hit.out;
1243
+ const { points } = scatterOnSurface({
1244
+ model: surface,
1245
+ matrix: nodeMatrix(surfaceNode)
1246
+ }, scatter);
1247
+ const n = Math.max(1, bottoms.length);
1248
+ const out = points.map((p, i) => {
1249
+ const [x, y, z] = seatOnSurface(p, bottoms[i % n] ?? 0, scatter.sink ?? 0);
1250
+ return {
1251
+ x,
1252
+ y,
1253
+ z,
1254
+ rotation: p.rotation,
1255
+ scale: p.scale
1256
+ };
1257
+ });
1258
+ cache.set(scatter, {
1259
+ key,
1260
+ out
1261
+ });
1262
+ return out;
1263
+ }
1264
+ /** The wind a mesh of `model` sways in at `seconds`, or undefined when calm. */
1265
+ function meshWind(wind, model, seconds) {
1266
+ if (!wind) return void 0;
1267
+ const b = model.bounds;
1268
+ const height = b ? b.size[1] : 0;
1269
+ if (!b || height <= 0) return void 0;
1270
+ return {
1271
+ amplitudeDeg: wind.amplitudeDeg ?? 8,
1272
+ frequency: wind.frequency ?? .4,
1273
+ direction: wind.direction ?? [1, 0],
1274
+ waveScale: wind.waveScale ?? 600,
1275
+ time: seconds,
1276
+ baseY: b.max[1],
1277
+ height
1278
+ };
1279
+ }
732
1280
  function translate(tx, ty) {
733
1281
  return {
734
1282
  a: 1,
@@ -855,16 +1403,20 @@ const MAX_RASTER_SCALE = 4;
855
1403
  * Pixels per layout px to rasterize a 2D layer at, so it stays sharp when its
856
1404
  * transform onto the canvas scales it up by `axisScale` (the transform's
857
1405
  * larger axis scale). Rounded up to a quarter, so an animated zoom reuses a
858
- * few texture sizes instead of allocating new ones every frame; never below 1
1406
+ * few texture sizes instead of allocating new ones every frame; not below 1
859
1407
  * (a shrunk layer keeps its layout raster). Capped, rounded down to a quarter,
860
1408
  * by MAX_RASTER_SCALE, by `limits.maxDimension` (the GPU's texture side), by
861
1409
  * `limits.maxPixels` (a pixel budget for one texture) and by
862
1410
  * `limits.effects`, the highest scale the layer's effects keep their look at.
1411
+ * The texture side is a hard limit: a layer longer than it at layout scale
1412
+ * (a tall reel strip, a long scroll) rasterizes just under it, below 1.
863
1413
  */
864
1414
  function layerRasterScale(axisScale, size, limits) {
865
1415
  const w = Math.max(1, size.width);
866
1416
  const h = Math.max(1, size.height);
867
1417
  const cap = Math.min(MAX_RASTER_SCALE, limits.maxDimension / Math.max(w, h), Math.sqrt(limits.maxPixels / (w * h)), limits.effects);
1418
+ const fits = limits.maxDimension / Math.max(w, h);
1419
+ if (fits < 1) return Math.floor(fits * 1024) / 1024;
868
1420
  const wanted = Math.ceil(axisScale * 4 - 1e-6) / 4;
869
1421
  return Math.max(1, Math.min(wanted, Math.floor(cap * 4 + 1e-6) / 4));
870
1422
  }
@@ -1099,6 +1651,10 @@ function textInkOutset(target, lop) {
1099
1651
  const reach = stroke + shadow;
1100
1652
  return reach > 0 ? Math.ceil(reach) + 1 : 0;
1101
1653
  }
1654
+ /** Bytes of a mesh's vertex buffer: 16 floats per vertex. */
1655
+ function deformedBytes(mesh) {
1656
+ return mesh.positions.length / 3 * 64;
1657
+ }
1102
1658
  function hasVertexAlpha(mesh) {
1103
1659
  const cached = meshHasAlpha.get(mesh);
1104
1660
  if (cached !== void 0) return cached;
@@ -1348,8 +1904,35 @@ function surfaceProps(target, lop, mat) {
1348
1904
  if (lop.castShadow !== void 0) out.castShadow = lop.castShadow;
1349
1905
  out.toon = target?.toon ?? lop.toon;
1350
1906
  out.outline = target?.outline ?? lop.outline;
1907
+ const file = mat?.surface;
1908
+ const lobes = {
1909
+ clearcoat: target?.clearcoat ?? lop.clearcoat ?? file?.clearcoat,
1910
+ clearcoatRoughness: target?.clearcoatRoughness ?? lop.clearcoatRoughness ?? file?.clearcoatRoughness,
1911
+ anisotropy: target?.anisotropy ?? lop.anisotropy ?? file?.anisotropy,
1912
+ anisotropyRotation: target?.anisotropyRotation ?? lop.anisotropyRotation ?? file?.anisotropyRotation,
1913
+ subsurface: target?.subsurface ?? lop.subsurface ?? file?.subsurface,
1914
+ subsurfaceColor: target?.subsurfaceColor ?? lop.subsurfaceColor ?? file?.subsurfaceColor,
1915
+ sheen: target?.sheen ?? lop.sheen ?? file?.sheen,
1916
+ sheenColor: target?.sheenColor ?? lop.sheenColor ?? file?.sheenColor,
1917
+ sheenRoughness: target?.sheenRoughness ?? lop.sheenRoughness ?? file?.sheenRoughness,
1918
+ iridescence: target?.iridescence ?? lop.iridescence ?? file?.iridescence,
1919
+ iridescenceThickness: target?.iridescenceThickness ?? lop.iridescenceThickness ?? file?.iridescenceThickness,
1920
+ iridescenceIor: target?.iridescenceIor ?? lop.iridescenceIor ?? file?.iridescenceIor
1921
+ };
1922
+ for (const [key, value] of Object.entries(lobes)) if (value !== void 0) out[key] = value;
1923
+ const triplanar = target?.triplanar ?? lop.triplanar;
1924
+ if (triplanar !== void 0) out.triplanar = triplanar;
1925
+ const triplanarScale = target?.triplanarScale ?? lop.triplanarScale;
1926
+ if (triplanarScale !== void 0) out.triplanarScale = triplanarScale;
1927
+ const triplanarSharpness = target?.triplanarSharpness ?? lop.triplanarSharpness;
1928
+ if (triplanarSharpness !== void 0) out.triplanarSharpness = triplanarSharpness;
1351
1929
  return out;
1352
1930
  }
1931
+ /** A transform's mean scale: the average length of its basis columns. */
1932
+ function matrixScale(m) {
1933
+ const c = (i) => Math.hypot(m[i], m[i + 1], m[i + 2]);
1934
+ return (c(0) + c(4) + c(8)) / 3;
1935
+ }
1353
1936
  function computeContainerScissor(matrix, width, height) {
1354
1937
  const corners = [
1355
1938
  matrix.transformPoint(new DOMPoint(0, 0)),
@@ -1661,6 +2244,7 @@ const CompositorWebGPURenderer = async (args) => {
1661
2244
  const inheritedSeekOffset = props.inheritedSeekOffset ?? 0;
1662
2245
  const shiftedFrame = Math.max(0, frame != null && fps != null ? frame + Math.round(inheritedSeekOffset * fps) : frame ?? 0);
1663
2246
  const compositionFrame = props.compositionFrame ?? shiftedFrame;
2247
+ const historyOnly = props.motionHistoryOnly === true;
1664
2248
  const compTex = ctx.renderer.getTemporaryTexture(nativeWidth, nativeHeight, [...props.excludeTextures || [], targetTexture]);
1665
2249
  const compView = compTex.createView();
1666
2250
  const composeDuration = virtualMedia?.metadata?.durationMs ?? DEFAULT_DURATION_MS;
@@ -1670,6 +2254,7 @@ const CompositorWebGPURenderer = async (args) => {
1670
2254
  durationSec
1671
2255
  });
1672
2256
  const { targetsById } = compiled;
2257
+ if (!historyOnly && frame != null && sceneMotionBlur(virtualMedia) && motionHistory(ctx.device, compiled.key).needsPrevious(shiftedFrame)) await renderMotionHistory(args, frame - 1);
1673
2258
  seekTimeline(compiled, shiftedFrame, fps ?? 24);
1674
2259
  const r3d = getRenderer3D(ctx.device, ctx.renderer.format ?? "rgba8unorm", sampleCount3d);
1675
2260
  const relightPipe = getScreenSpaceRelightPipeline(ctx.device, ctx.renderer.format ?? "rgba8unorm");
@@ -1748,6 +2333,7 @@ const CompositorWebGPURenderer = async (args) => {
1748
2333
  const textStyle = {
1749
2334
  fontSize: node.fontSize ?? 48,
1750
2335
  fontWeight: node.fontWeight,
2336
+ fontVariations: node.fontVariations,
1751
2337
  fontFamily: node.fontFamily,
1752
2338
  letterSpacing: node.letterSpacing,
1753
2339
  lineHeight: node.lineHeight,
@@ -1811,6 +2397,11 @@ const CompositorWebGPURenderer = async (args) => {
1811
2397
  0
1812
2398
  ];
1813
2399
  let dofEnabled = false;
2400
+ /** Shutter motion blur of the 3D scene, and what its draws looked like a frame ago. */
2401
+ let motionBlur = null;
2402
+ let history = null;
2403
+ /** Set when the camera draws a cube map and remaps it through its lens. */
2404
+ let cubeCamera = null;
1814
2405
  const sceneLights = [];
1815
2406
  let sceneEnvironment = null;
1816
2407
  let sceneBloom = null;
@@ -1873,6 +2464,13 @@ const CompositorWebGPURenderer = async (args) => {
1873
2464
  camera.fStop = camStub?.fStop ?? camOp?.dof?.fStop ?? 2.8;
1874
2465
  camera.maxBlurRadius = camStub?.maxBlurRadius ?? camOp?.dof?.maxBlurRadius ?? 24;
1875
2466
  }
2467
+ const blur = camOp?.motionBlur;
2468
+ if (blur && fps) {
2469
+ motionBlur = blur === true ? {} : blur;
2470
+ history = motionHistory(ctx.device, compiled.key);
2471
+ }
2472
+ cubeCamera = resolveCubeCamera(camOp, camera, camStub?.relativitySpeed ?? camOp?.relativity?.speed ?? 0, nativeWidth, nativeHeight, ctx.device.limits.maxTextureDimension2D);
2473
+ if (cubeCamera && dofEnabled) throw new Error(`Camera '${camOp?.id}' is a cube camera (${camOp?.relativity ? "relativity" : camOp?.lens?.projection}) with depth of field: depth of field needs a single perspective view.`);
1876
2474
  for (const lvm of lightVMs) {
1877
2475
  const lop = lvm.operation;
1878
2476
  const targetId = lop.id || lop.inputHandleId;
@@ -1887,8 +2485,23 @@ const CompositorWebGPURenderer = async (args) => {
1887
2485
  const lightType = target?.lightType ?? lop.lightType ?? "point";
1888
2486
  if (lightType === "environment") {
1889
2487
  const intensity$1 = target?.intensity ?? lop.intensity;
2488
+ const vfDensity = target?.volumetricFogDensity ?? lop.volumetricFogDensity;
2489
+ const vfAnisotropy = target?.volumetricFogAnisotropy ?? lop.volumetricFogAnisotropy;
2490
+ const vfNoiseStrength = target?.volumetricFogNoiseStrength ?? lop.volumetricFogNoiseStrength;
2491
+ const rawVf = lop.volumetricFog;
2492
+ const baseVf = rawVf === true ? {} : rawVf && typeof rawVf === "object" ? { ...rawVf } : void 0;
2493
+ const volumetricFog = baseVf ? {
2494
+ ...baseVf,
2495
+ ...vfDensity !== void 0 && { density: vfDensity },
2496
+ ...vfAnisotropy !== void 0 && { anisotropy: vfAnisotropy },
2497
+ ...vfNoiseStrength !== void 0 && { noise: {
2498
+ ...typeof baseVf.noise === "object" ? baseVf.noise : {},
2499
+ strength: vfNoiseStrength
2500
+ } }
2501
+ } : void 0;
1890
2502
  sceneEnvironment = {
1891
2503
  preset: lop.preset,
2504
+ hdri: lop.hdri,
1892
2505
  top: lop.top,
1893
2506
  horizon: lop.horizon,
1894
2507
  bottom: lop.bottom,
@@ -1899,6 +2512,9 @@ const CompositorWebGPURenderer = async (args) => {
1899
2512
  fog: lop.fog,
1900
2513
  floor: lop.floor,
1901
2514
  outlines: lop.outlines,
2515
+ ssr: lop.ssr,
2516
+ gi: lop.gi,
2517
+ ...volumetricFog && { volumetricFog },
1902
2518
  ...intensity$1 !== void 0 && { intensity: intensity$1 }
1903
2519
  };
1904
2520
  sceneBloom = lop.bloom === true ? {} : lop.bloom && typeof lop.bloom === "object" ? { ...lop.bloom } : null;
@@ -1916,6 +2532,10 @@ const CompositorWebGPURenderer = async (args) => {
1916
2532
  const angle = target?.angle ?? lop.angle ?? 45;
1917
2533
  const penumbra = target?.penumbra ?? lop.penumbra ?? .2;
1918
2534
  const decay = target?.decay ?? lop.decay ?? 1;
2535
+ const width = typeof (target?.width ?? lop.width) === "number" ? target?.width ?? lop.width : void 0;
2536
+ const height = typeof (target?.height ?? lop.height) === "number" ? target?.height ?? lop.height : void 0;
2537
+ const twoSided = Boolean(target?.twoSided ?? lop.twoSided);
2538
+ const rotation = target?.rotation ?? lop.rotation ?? 0;
1919
2539
  sceneLights.push({
1920
2540
  type: lightType,
1921
2541
  color,
@@ -1930,10 +2550,15 @@ const CompositorWebGPURenderer = async (args) => {
1930
2550
  targetY,
1931
2551
  targetZ
1932
2552
  ],
2553
+ ...Array.isArray(lop.direction) && { direction: lop.direction },
1933
2554
  radius,
1934
2555
  angle,
1935
2556
  penumbra,
1936
2557
  decay,
2558
+ ...width !== void 0 && { width },
2559
+ ...height !== void 0 && { height },
2560
+ twoSided,
2561
+ rotation,
1937
2562
  castShadow: Boolean(lop.castShadow),
1938
2563
  shadowSoftness: target?.shadowSoftness ?? lop.shadowSoftness,
1939
2564
  shadowDarkness: target?.shadowDarkness ?? lop.shadowDarkness
@@ -1967,6 +2592,61 @@ const CompositorWebGPURenderer = async (args) => {
1967
2592
  eye: reliefEye
1968
2593
  } };
1969
2594
  };
2595
+ /** Each queued item with its motion key: owner id and its index among the owner's items. */
2596
+ const motionItems = () => {
2597
+ const seen = /* @__PURE__ */ new Map();
2598
+ return pending3DItems.map((item) => {
2599
+ if (item.owner === void 0) return [item, void 0];
2600
+ const n = seen.get(item.owner) ?? 0;
2601
+ seen.set(item.owner, n + 1);
2602
+ return [item, `${item.owner}#${n}`];
2603
+ });
2604
+ };
2605
+ /** Writes this frame's draws (and camera) into the motion history. */
2606
+ const recordMotion = (h, cam) => {
2607
+ const r3d$1 = getRenderer3D(ctx.device, ctx.renderer.format ?? "rgba8unorm", sampleCount3d);
2608
+ for (const [item, key] of motionItems()) {
2609
+ if (!key) continue;
2610
+ if (item.kind === "mesh") h.recordMesh(encoder, key, shiftedFrame, {
2611
+ modelMatrix: r3d$1.meshModelMatrix(item.opts),
2612
+ skinning: item.opts.skinningMatrices,
2613
+ windTime: item.opts.wind?.time,
2614
+ crowdTime: item.opts.crowd?.time
2615
+ }, {
2616
+ instanceBuffer: item.opts.instanceBuffer,
2617
+ vertices: item.meshBuffers && {
2618
+ buffer: item.meshBuffers.vertexBuffer,
2619
+ bytes: deformedBytes(item.mesh)
2620
+ }
2621
+ });
2622
+ else if (item.kind === "quad") h.recordQuad(key, shiftedFrame, r3d$1.quadModelMatrix(item.opts));
2623
+ else h.recordSlug(key, shiftedFrame, r3d$1.quadModelMatrix(item.opts), item.batches);
2624
+ }
2625
+ h.recordFrame(shiftedFrame, cam);
2626
+ };
2627
+ /** Draws the queued items' screen motion since the last frame, then blurs `color` by it into `into`. */
2628
+ const blurScene = (h, cam, color, into, blur, floor) => {
2629
+ const r3d$1 = getRenderer3D(ctx.device, ctx.renderer.format ?? "rgba8unorm", sampleCount3d);
2630
+ const vel = r3d$1.beginVelocityPass(encoder, nativeWidth, nativeHeight, cam, h.previousCamera(shiftedFrame) ?? cam);
2631
+ if (floor) r3d$1.drawFloorVelocity(vel.pass, vel.cameraBindGroup, floor);
2632
+ for (const [item, key] of motionItems()) if (item.kind === "mesh") {
2633
+ const prev = key ? h.previousMesh(key, shiftedFrame, {
2634
+ skinning: item.opts.skinningMatrices,
2635
+ instanceBuffer: item.opts.instanceBuffer,
2636
+ vertexBytes: item.meshBuffers && deformedBytes(item.mesh),
2637
+ wind: item.opts.wind,
2638
+ crowd: item.opts.crowd
2639
+ }) : void 0;
2640
+ r3d$1.drawMeshVelocity(vel.pass, vel.cameraBindGroup, item.meshBuffers ?? item.mesh, item.texture, item.opts, prev);
2641
+ } else if (item.kind === "quad") r3d$1.drawQuadVelocity(vel.pass, vel.cameraBindGroup, item.texture, item.opts, key ? h.previousQuad(key, shiftedFrame) : void 0);
2642
+ else r3d$1.drawSlugVelocity(vel.pass, vel.cameraBindGroup, item.batches, item.opts, item.stackBase, key ? h.previousSlug(key, shiftedFrame) : void 0);
2643
+ vel.pass.end();
2644
+ r3d$1.applyMotionBlur(encoder, color, vel.velocity, into.createView(), {
2645
+ shutterAngle: blur.shutter,
2646
+ sampleCount: blur.samples,
2647
+ maxBlur: blur.maxBlur
2648
+ });
2649
+ };
1970
2650
  const flush3DPass = (curTex, curView, curExcludes) => {
1971
2651
  if (pending3DItems.length === 0 || !camera) return {
1972
2652
  finalTex: curTex,
@@ -1978,6 +2658,14 @@ const CompositorWebGPURenderer = async (args) => {
1978
2658
  if (item.opts.morphWeights?.length) item.meshBuffers = r3d$1.morphMesh(encoder, item.mesh, item.opts.morphWeights, item.opts.wireframe);
1979
2659
  if (item.opts.audioDeform) item.meshBuffers = r3d$1.deformMesh(encoder, item.mesh, item.opts.audioDeform, item.opts.wireframe, item.meshBuffers);
1980
2660
  }
2661
+ if (historyOnly) {
2662
+ if (history) recordMotion(history, camera);
2663
+ pending3DItems.length = 0;
2664
+ return {
2665
+ finalTex: curTex,
2666
+ finalView: curView
2667
+ };
2668
+ }
1981
2669
  const itemTextures = pendingTextures();
1982
2670
  const scene3dTex = ctx.renderer.getTemporaryTexture(nativeWidth, nativeHeight, [
1983
2671
  curTex,
@@ -1988,7 +2676,9 @@ const CompositorWebGPURenderer = async (args) => {
1988
2676
  const scene3dView = scene3dTex.createView();
1989
2677
  const outlineSetting = sceneEnvironment?.outlines;
1990
2678
  const outlines = outlineSetting === true ? {} : outlineSetting || null;
1991
- const geometryEnabled = dofEnabled || Boolean(outlines);
2679
+ const volumetricFogEnabled = Boolean(sceneEnvironment?.volumetricFog);
2680
+ const screenIndirectEnabled = Boolean(sceneEnvironment?.ssr || sceneEnvironment?.gi);
2681
+ const geometryEnabled = dofEnabled || Boolean(outlines) || volumetricFogEnabled || screenIndirectEnabled;
1992
2682
  let linearDepthTex;
1993
2683
  let linearDepthView;
1994
2684
  if (geometryEnabled) {
@@ -1999,7 +2689,10 @@ const CompositorWebGPURenderer = async (args) => {
1999
2689
  const shadowLights = sceneLights.filter((light) => (light.lightType ?? light.type) !== "ambient").map((light, index) => ({
2000
2690
  light,
2001
2691
  index
2002
- })).filter(({ light }) => light.castShadow && ((light.lightType ?? light.type) === "directional" || (light.lightType ?? light.type) === "spot"));
2692
+ })).filter(({ light }) => {
2693
+ const k = light.lightType ?? light.type;
2694
+ return light.castShadow && (k === "directional" || k === "spot" || k === "rect" || k === "point");
2695
+ });
2003
2696
  if (shadowLights.length > 0) {
2004
2697
  const casters = [];
2005
2698
  const receivers = [];
@@ -2021,13 +2714,23 @@ const CompositorWebGPURenderer = async (args) => {
2021
2714
  receivers.push(r3d$1.worldBounds(caster));
2022
2715
  if (item.opts.castShadow === true) casters.push(caster);
2023
2716
  }
2024
- if (casters.length > 0) for (const { light, index } of shadowLights) {
2025
- const result = r3d$1.renderShadowMap(encoder, light, index, casters, receivers, {
2026
- mapSize: shadowMapSize,
2027
- layer: shadow.length,
2028
- layerCount: shadowLights.length
2029
- });
2030
- if (result) shadow.push(result);
2717
+ if (casters.length > 0) {
2718
+ const totalShadowLayers = shadowLights.reduce((sum, { light }) => sum + ((light.lightType ?? light.type) === "point" ? 6 : 1), 0);
2719
+ const allPointShadows = shadowLights.every(({ light }) => (light.lightType ?? light.type) === "point");
2720
+ const resolvedShadowMapSize = shadowMapSize ?? (allPointShadows ? 1024 : 2048);
2721
+ let currentLayer = 0;
2722
+ for (const { light, index } of shadowLights) {
2723
+ const needed = (light.lightType ?? light.type) === "point" ? 6 : 1;
2724
+ const result = r3d$1.renderShadowMap(encoder, light, index, casters, receivers, {
2725
+ mapSize: resolvedShadowMapSize,
2726
+ layer: currentLayer,
2727
+ layerCount: totalShadowLayers
2728
+ });
2729
+ if (result) {
2730
+ shadow.push(result);
2731
+ currentLayer += result.layerCount ?? needed;
2732
+ }
2733
+ }
2031
2734
  }
2032
2735
  }
2033
2736
  const isGlass = (item) => item.kind === "mesh" && (item.opts.material === "glass" || item.opts.material === "acrylic" || item.opts.material === "frosted");
@@ -2094,28 +2797,34 @@ const CompositorWebGPURenderer = async (args) => {
2094
2797
  ...transparent.map(({ item }) => item),
2095
2798
  ...glass
2096
2799
  ];
2800
+ if (cubeCamera) {
2801
+ const single = [screenIndirectEnabled && "screen-space reflections and GI (ssr, gi)", motionBlur && "motion blur"].filter(Boolean);
2802
+ if (single.length) throw new Error(`A cube camera (${cubeCamera.remap.projection ?? "lens"}) cannot draw ${single.join(" or ")}: they need a single perspective view.`);
2803
+ }
2097
2804
  const floorSpec = sceneEnvironment?.floor;
2098
- let blurredReflectionTex = null;
2099
- if (floorSpec && typeof floorSpec.y === "number" && camera.eye[1] < floorSpec.y) {
2805
+ const sceneShadow = shadow.length ? shadow : null;
2806
+ /** The floor's mirror image from `view`, blurred by its roughness, when the eye is above it. */
2807
+ const floorReflection = (view, width, height) => {
2808
+ if (!floorSpec || typeof floorSpec.y !== "number" || view.eye[1] >= floorSpec.y) return null;
2100
2809
  const reflectedCamera = {
2101
- ...camera,
2810
+ ...view,
2102
2811
  eye: [
2103
- camera.eye[0],
2104
- 2 * floorSpec.y - camera.eye[1],
2105
- camera.eye[2]
2812
+ view.eye[0],
2813
+ 2 * floorSpec.y - view.eye[1],
2814
+ view.eye[2]
2106
2815
  ],
2107
2816
  target: [
2108
- camera.target[0],
2109
- 2 * floorSpec.y - camera.target[1],
2110
- camera.target[2]
2817
+ view.target[0],
2818
+ 2 * floorSpec.y - view.target[1],
2819
+ view.target[2]
2111
2820
  ],
2112
2821
  up: [
2113
- camera.up[0],
2114
- camera.up[1],
2115
- camera.up[2]
2822
+ view.up[0],
2823
+ view.up[1],
2824
+ view.up[2]
2116
2825
  ]
2117
2826
  };
2118
- const { pass: reflPass, cameraBindGroup: reflCamBind, lightsBindGroup: reflLightsBind, reflectionTexture: reflTex } = r3d$1.beginReflectionPass(encoder, nativeWidth, nativeHeight, reflectedCamera, sceneLights, {
2827
+ const { pass: reflPass, cameraBindGroup: reflCamBind, lightsBindGroup: reflLightsBind, reflectionTexture: reflTex } = r3d$1.beginReflectionPass(encoder, width, height, reflectedCamera, sceneLights, {
2119
2828
  environment: sceneEnvironment,
2120
2829
  shadow: null
2121
2830
  });
@@ -2123,28 +2832,112 @@ const CompositorWebGPURenderer = async (args) => {
2123
2832
  else if (item.kind === "slug") r3d$1.drawSlugText(reflPass, reflCamBind, reflLightsBind, item.batches, item.opts, item.stackBase, false);
2124
2833
  else if (item.kind === "mesh") r3d$1.drawMesh3D(reflPass, reflCamBind, reflLightsBind, item.meshBuffers ?? item.mesh, item.texture, item.opts, false);
2125
2834
  reflPass.end();
2126
- blurredReflectionTex = r3d$1.blurReflection(encoder, reflTex, floorSpec.roughness ?? .35);
2127
- }
2128
- const { pass: pass3d, cameraBindGroup, cameraBuffer, lightsBindGroup } = r3d$1.beginPass(encoder, scene3dView, nativeWidth, nativeHeight, camera, "clear", {
2129
- r: 0,
2130
- g: 0,
2131
- b: 0,
2132
- a: 0
2133
- }, linearDepthView, sceneLights, {
2134
- environment: sceneEnvironment,
2135
- shadow: shadow.length ? shadow : null
2136
- }, Boolean(sceneBloom));
2137
- if (floorSpec && blurredReflectionTex) r3d$1.drawFloor(pass3d, cameraBindGroup, lightsBindGroup, blurredReflectionTex, floorSpec, geometryEnabled, Boolean(sceneBloom));
2138
- for (const item of ordered) if (item.kind === "quad") r3d$1.drawTextureQuad(pass3d, cameraBindGroup, lightsBindGroup, item.texture, item.opts, geometryEnabled);
2139
- else if (item.kind === "slug") r3d$1.drawSlugText(pass3d, cameraBindGroup, lightsBindGroup, item.batches, item.opts, item.stackBase, geometryEnabled);
2140
- else if (item.kind === "mesh") r3d$1.drawMesh3D(pass3d, cameraBindGroup, lightsBindGroup, item.meshBuffers ?? item.mesh, item.texture, isGlass(item) ? {
2141
- ...item.opts,
2142
- backdrop: curTex
2143
- } : item.opts, geometryEnabled);
2144
- pass3d.end();
2835
+ return r3d$1.blurReflection(encoder, reflTex, floorSpec.roughness ?? .35);
2836
+ };
2837
+ /**
2838
+ * One forward pass over the batch: floor, then the sorted draws.
2839
+ * `glassBackdrop` is the 2D picture glass refracts: undefined draws
2840
+ * glass over the 3D scene alone (a cube face), null leaves the glass
2841
+ * out (the pre-pass that makes that picture).
2842
+ */
2843
+ const drawView = (target, screenIndirect, writeHdr, glassBackdrop) => {
2844
+ const begun = r3d$1.beginPass(encoder, target.color, target.width, target.height, target.view, "clear", {
2845
+ r: 0,
2846
+ g: 0,
2847
+ b: 0,
2848
+ a: 0
2849
+ }, target.geometry, sceneLights, {
2850
+ environment: sceneEnvironment,
2851
+ shadow: sceneShadow,
2852
+ screenIndirect
2853
+ }, writeHdr, target.hdr);
2854
+ const { pass: pass$1, cameraBindGroup, lightsBindGroup } = begun;
2855
+ if (floorSpec && target.reflection) r3d$1.drawFloor(pass$1, cameraBindGroup, lightsBindGroup, target.reflection, floorSpec, geometryEnabled, writeHdr);
2856
+ for (const item of ordered) {
2857
+ if (glassBackdrop === null && isGlass(item)) continue;
2858
+ if (item.kind === "quad") r3d$1.drawTextureQuad(pass$1, cameraBindGroup, lightsBindGroup, item.texture, item.opts, geometryEnabled);
2859
+ else if (item.kind === "slug") r3d$1.drawSlugText(pass$1, cameraBindGroup, lightsBindGroup, item.batches, item.opts, item.stackBase, geometryEnabled);
2860
+ else if (item.kind === "mesh") r3d$1.drawMesh3D(pass$1, cameraBindGroup, lightsBindGroup, item.meshBuffers ?? item.mesh, item.texture, isGlass(item) ? {
2861
+ ...item.opts,
2862
+ backdrop: glassBackdrop ?? void 0
2863
+ } : item.opts, geometryEnabled);
2864
+ }
2865
+ pass$1.end();
2866
+ return begun;
2867
+ };
2868
+ const sceneSeconds = shiftedFrame / Math.max(1, fps ?? 24);
2145
2869
  let final3dTex = scene3dTex;
2146
- if (outlines && linearDepthTex) {
2147
- if (dofEnabled) r3d$1.resolveGeometry(encoder, linearDepthTex);
2870
+ let mainReflection = null;
2871
+ let mainPass = null;
2872
+ if (cubeCamera) {
2873
+ const size = cubeCamera.faceSize;
2874
+ const cube = r3d$1.getOrCreateCubeTexture(size, false);
2875
+ const hdrCube = sceneBloom ? r3d$1.getOrCreateCubeTexture(size, true) : null;
2876
+ const faceOutlines = outlines && {
2877
+ ...outlines,
2878
+ referenceHeight: (outlines.referenceHeight ?? 1080) * 2 * cubeCamera.centerDensity / nativeHeight
2879
+ };
2880
+ const post = Boolean(faceOutlines) || volumetricFogEnabled;
2881
+ const faceExcludes = [
2882
+ curTex,
2883
+ targetTexture,
2884
+ scene3dTex,
2885
+ ...curExcludes,
2886
+ ...itemTextures
2887
+ ];
2888
+ const faceTex = post ? ctx.renderer.getTemporaryTexture(size, size, faceExcludes) : null;
2889
+ const fogTex = faceOutlines && volumetricFogEnabled ? ctx.renderer.getTemporaryTexture(size, size, [...faceExcludes, ...faceTex ? [faceTex] : []]) : null;
2890
+ const faceGeometry = geometryEnabled ? r3d$1.getOrCreateLinearDepthTexture(size, size) : null;
2891
+ cubeFaceCameras(camera.eye, camera.near, camera.far).forEach((face, i) => {
2892
+ const faceView = r3d$1.cubeFaceView(cube, i);
2893
+ const { cameraBindGroup, lightsBindGroup } = drawView({
2894
+ view: face,
2895
+ width: size,
2896
+ height: size,
2897
+ color: faceTex ? faceTex.createView() : faceView,
2898
+ geometry: faceGeometry?.createView(),
2899
+ hdr: hdrCube ? r3d$1.cubeFaceView(hdrCube, i) : void 0,
2900
+ reflection: floorReflection(face, size, size)
2901
+ }, void 0, Boolean(sceneBloom), void 0);
2902
+ if (!faceTex || !faceGeometry) return;
2903
+ if (volumetricFogEnabled) r3d$1.resolveGeometry(encoder, faceGeometry);
2904
+ let source = faceTex;
2905
+ if (faceOutlines) {
2906
+ const inked = fogTex ?? null;
2907
+ r3d$1.applyOutlines(encoder, faceTex, faceGeometry, inked ? inked.createView() : faceView, face, faceOutlines, sceneEnvironment?.fog);
2908
+ if (!inked) return;
2909
+ source = inked;
2910
+ }
2911
+ if (volumetricFogEnabled && sceneEnvironment) r3d$1.applyVolumetricFog(encoder, source, faceGeometry, faceView, cameraBindGroup, lightsBindGroup, sceneEnvironment, sceneSeconds, shiftedFrame);
2912
+ });
2913
+ r3d$1.remapLens(encoder, cube, hdrCube, scene3dView, nativeWidth, nativeHeight, cubeCamera.remap);
2914
+ } else {
2915
+ mainReflection = floorReflection(camera, nativeWidth, nativeHeight);
2916
+ const target = {
2917
+ view: camera,
2918
+ width: nativeWidth,
2919
+ height: nativeHeight,
2920
+ color: scene3dView,
2921
+ geometry: linearDepthView,
2922
+ reflection: mainReflection
2923
+ };
2924
+ if (screenIndirectEnabled && sceneEnvironment) {
2925
+ const { cameraBuffer: radianceCamera } = drawView(target, "radiance", true, curTex);
2926
+ r3d$1.traceScreenIndirect(encoder, radianceCamera, sceneEnvironment);
2927
+ }
2928
+ let glassBackdrop = curTex;
2929
+ if (glass.length) {
2930
+ drawView(target, screenIndirectEnabled ? "apply" : void 0, false, null);
2931
+ glassBackdrop = ctx.renderer.composite(encoder, curTex, scene3dTex, "normal", [
2932
+ targetTexture,
2933
+ scene3dTex,
2934
+ ...curExcludes
2935
+ ]);
2936
+ }
2937
+ mainPass = drawView(target, screenIndirectEnabled ? "apply" : void 0, Boolean(sceneBloom), glassBackdrop);
2938
+ }
2939
+ if (mainPass && outlines && linearDepthTex) {
2940
+ if (dofEnabled || volumetricFogEnabled) r3d$1.resolveGeometry(encoder, linearDepthTex);
2148
2941
  const outlinedTex = ctx.renderer.getTemporaryTexture(nativeWidth, nativeHeight, [
2149
2942
  curTex,
2150
2943
  targetTexture,
@@ -2155,7 +2948,19 @@ const CompositorWebGPURenderer = async (args) => {
2155
2948
  r3d$1.applyOutlines(encoder, scene3dTex, linearDepthTex, outlinedTex.createView(), camera, outlines, sceneEnvironment?.fog);
2156
2949
  final3dTex = outlinedTex;
2157
2950
  }
2158
- if (dofEnabled && linearDepthTex) {
2951
+ if (mainPass && volumetricFogEnabled && sceneEnvironment && linearDepthTex) {
2952
+ const vfogTex = ctx.renderer.getTemporaryTexture(nativeWidth, nativeHeight, [
2953
+ curTex,
2954
+ targetTexture,
2955
+ scene3dTex,
2956
+ final3dTex,
2957
+ linearDepthTex,
2958
+ ...curExcludes
2959
+ ]);
2960
+ r3d$1.applyVolumetricFog(encoder, final3dTex, linearDepthTex, vfogTex.createView(), mainPass.cameraBindGroup, mainPass.lightsBindGroup, sceneEnvironment, sceneSeconds, shiftedFrame);
2961
+ final3dTex = vfogTex;
2962
+ }
2963
+ if (mainPass && dofEnabled && linearDepthTex) {
2159
2964
  const dofResultTex = ctx.renderer.getTemporaryTexture(nativeWidth, nativeHeight, [
2160
2965
  curTex,
2161
2966
  targetTexture,
@@ -2163,9 +2968,21 @@ const CompositorWebGPURenderer = async (args) => {
2163
2968
  final3dTex,
2164
2969
  ...curExcludes
2165
2970
  ]);
2166
- r3d$1.applyDepthOfField(encoder, final3dTex, linearDepthTex, dofResultTex.createView(), cameraBuffer);
2971
+ r3d$1.applyDepthOfField(encoder, final3dTex, linearDepthTex, dofResultTex.createView(), mainPass.cameraBuffer);
2167
2972
  final3dTex = dofResultTex;
2168
2973
  }
2974
+ if (motionBlur && history) {
2975
+ const blurredTex = ctx.renderer.getTemporaryTexture(nativeWidth, nativeHeight, [
2976
+ curTex,
2977
+ targetTexture,
2978
+ scene3dTex,
2979
+ final3dTex,
2980
+ ...curExcludes
2981
+ ]);
2982
+ blurScene(history, camera, final3dTex, blurredTex, motionBlur, floorSpec && mainReflection ? floorSpec : void 0);
2983
+ final3dTex = blurredTex;
2984
+ }
2985
+ if (history) recordMotion(history, camera);
2169
2986
  if (sceneBloom) {
2170
2987
  const bloomTex = ctx.renderer.getTemporaryTexture(nativeWidth, nativeHeight, [
2171
2988
  curTex,
@@ -2226,15 +3043,17 @@ const CompositorWebGPURenderer = async (args) => {
2226
3043
  let activeExcludes = excludes;
2227
3044
  const mattesByOwner = /* @__PURE__ */ new Map();
2228
3045
  const reliefsByOwner = /* @__PURE__ */ new Map();
3046
+ const texturesByOwner = /* @__PURE__ */ new Map();
2229
3047
  for (const vm of vms) {
2230
3048
  const op$1 = vm.operation;
2231
3049
  if (op$1?.matteOf) mattesByOwner.set(op$1.matteOf, vm);
2232
3050
  if (op$1?.reliefOf) reliefsByOwner.set(op$1.reliefOf, vm);
3051
+ if (op$1?.textureOf) texturesByOwner.set(op$1.textureOf, vm);
2233
3052
  }
2234
3053
  for (const child of sorted) {
2235
3054
  const lop = child.operation;
2236
3055
  if (lop.kind === "camera" || lop.kind === "light") continue;
2237
- if (lop.matteOf || lop.reliefOf) continue;
3056
+ if (lop.matteOf || lop.reliefOf || lop.textureOf) continue;
2238
3057
  const targetId = lop.id || lop.inputHandleId;
2239
3058
  const target = targetId ? targetsById[targetId] : void 0;
2240
3059
  const isChild3D = Boolean(target?.is3D ?? lop.is3D) || Boolean(camera && ((target?.z ?? lop.z ?? 0) !== 0 || lop.is3D)) || Boolean(parent3DMatrix);
@@ -2293,10 +3112,37 @@ const CompositorWebGPURenderer = async (args) => {
2293
3112
  amount: lop.reliefAmount ?? 0
2294
3113
  });
2295
3114
  }
3115
+ const textureVM = lop.textureLayerId ? texturesByOwner.get(lop.id) : void 0;
3116
+ let wrapTexture;
3117
+ if (textureVM) {
3118
+ const texture = await isolate(textureVM, nodeIndex);
3119
+ activeExcludes = [...activeExcludes, texture];
3120
+ nodeIndex += 1;
3121
+ const r = rects[lop.textureLayerId];
3122
+ const uvRect = r ? [
3123
+ r.x / targetW,
3124
+ r.y / targetH,
3125
+ r.width / targetW,
3126
+ r.height / targetH
3127
+ ] : [
3128
+ 0,
3129
+ 0,
3130
+ 1,
3131
+ 1
3132
+ ];
3133
+ const from = pending3DItems.length;
3134
+ wrapTexture = () => {
3135
+ for (const item of pending3DItems.slice(from)) {
3136
+ if (item.kind !== "mesh") continue;
3137
+ item.texture = texture;
3138
+ item.opts.uvRect = uvRect;
3139
+ }
3140
+ };
3141
+ }
2296
3142
  const matteVM = lop.matteId ? mattesByOwner.get(lop.id) : void 0;
2297
3143
  if (matteVM) {
2298
3144
  const mode = matteVM.operation.matteMode ?? "alpha";
2299
- const blend = mode === "luma" ? "luma-in" : mode === "luma-inverted" ? "luma-out" : mode === "alpha-inverted" ? "destination-out" : "destination-in";
3145
+ const blend$1 = mode === "luma" ? "luma-in" : mode === "luma-inverted" ? "luma-out" : mode === "alpha-inverted" ? "destination-out" : "destination-in";
2300
3146
  if (isChild3D) {
2301
3147
  let coverage = await isolate(matteVM, nodeIndex);
2302
3148
  activeExcludes = [...activeExcludes, coverage];
@@ -2314,7 +3160,7 @@ const CompositorWebGPURenderer = async (args) => {
2314
3160
  b: 1,
2315
3161
  a: 1
2316
3162
  }, targetW, targetH, "clear").end();
2317
- coverage = ctx.renderer.composite(encoder, white, coverage, blend, [
3163
+ coverage = ctx.renderer.composite(encoder, white, coverage, blend$1, [
2318
3164
  targetTexture,
2319
3165
  activeTex,
2320
3166
  ...activeExcludes,
@@ -2326,6 +3172,7 @@ const CompositorWebGPURenderer = async (args) => {
2326
3172
  const before = pending3DItems.length;
2327
3173
  const res$1 = await renderLayerVM(child, nodeIndex, activeTex, activeView, activeExcludes, accumulatedOpacity, parentMatrix, parentRect, targetW, targetH, parent3DMatrix, isRootScene);
2328
3174
  for (const item of pending3DItems.slice(before)) item.opts.matte = coverage;
3175
+ wrapTexture?.();
2329
3176
  if (res$1) {
2330
3177
  if (res$1.finalTex !== activeTex) activeExcludes = [...activeExcludes, res$1.finalTex];
2331
3178
  activeTex = res$1.finalTex;
@@ -2337,7 +3184,7 @@ const CompositorWebGPURenderer = async (args) => {
2337
3184
  activeExcludes = [...activeExcludes, contentTex];
2338
3185
  nodeIndex += 1;
2339
3186
  const matteTex = await isolate(matteVM, nodeIndex);
2340
- const masked = ctx.renderer.composite(encoder, contentTex, matteTex, blend, [
3187
+ const masked = ctx.renderer.composite(encoder, contentTex, matteTex, blend$1, [
2341
3188
  targetTexture,
2342
3189
  activeTex,
2343
3190
  ...activeExcludes,
@@ -2354,6 +3201,7 @@ const CompositorWebGPURenderer = async (args) => {
2354
3201
  continue;
2355
3202
  }
2356
3203
  const res = await renderLayerVM(child, nodeIndex, activeTex, activeView, activeExcludes, accumulatedOpacity, parentMatrix, parentRect, targetW, targetH, parent3DMatrix, isRootScene);
3204
+ wrapTexture?.();
2357
3205
  if (res) {
2358
3206
  if (res.finalTex !== activeTex) activeExcludes = [...activeExcludes, res.finalTex];
2359
3207
  activeTex = res.finalTex;
@@ -2386,6 +3234,8 @@ const CompositorWebGPURenderer = async (args) => {
2386
3234
  if (shiftedFrame < startFrame) return;
2387
3235
  if (durationFrames !== void 0 && shiftedFrame >= startFrame + durationFrames) return;
2388
3236
  }
3237
+ const isTrue3D = Boolean(target?.is3D ?? lop.is3D) || Boolean(camera && ((target?.z ?? lop.z ?? 0) !== 0 || lop.is3D));
3238
+ if (historyOnly && !isContainer && kind !== "model3d" && !isTrue3D && !has3DTransform(target, lop)) return;
2389
3239
  if (kind === "model3d") {
2390
3240
  const r3d$1 = getRenderer3D(ctx.device, ctx.renderer.format ?? "rgba8unorm", sampleCount3d);
2391
3241
  let modelSrc = lop.modelData ?? lop.src;
@@ -2414,6 +3264,7 @@ const CompositorWebGPURenderer = async (args) => {
2414
3264
  } catch (err) {
2415
3265
  throw new Error(`Failed to load 3D model ${lop.id ?? "(unnamed)"}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
2416
3266
  }
3267
+ if (model && lop.fracture3d) model = fracturedModel(model, lop.fracture3d);
2417
3268
  if (!model || model.meshes.length === 0) return;
2418
3269
  const baseX$1 = lop.x ?? 0;
2419
3270
  const baseY$1 = lop.y ?? 0;
@@ -2469,6 +3320,7 @@ const CompositorWebGPURenderer = async (args) => {
2469
3320
  const instOpt = lop.instances3d;
2470
3321
  const rawMeshes = Array.isArray(instOpt.mesh) ? instOpt.mesh : instOpt.mesh ? [instOpt.mesh] : lop.modelData ? [lop.modelData] : lop.primitive3d ? [getPrimitiveGeometry(lop.primitive3d)] : [];
2471
3322
  const variants = [];
3323
+ const variantPres = [];
2472
3324
  for (const m of rawMeshes) {
2473
3325
  let vModel;
2474
3326
  if (typeof m === "object" && m !== null) {
@@ -2482,19 +3334,50 @@ const CompositorWebGPURenderer = async (args) => {
2482
3334
  });
2483
3335
  } catch {}
2484
3336
  }
2485
- if (vModel && vModel.meshes && vModel.meshes.length > 0) variants.push(vModel);
3337
+ if (vModel && vModel.meshes && vModel.meshes.length > 0) {
3338
+ variants.push(vModel);
3339
+ const isNode = !("meshes" in m);
3340
+ variantPres.push(isNode ? {
3341
+ q: quatFromEulerAngles(m.rotateX ?? 0, m.rotateY ?? 0, m.rotateZ ?? 0),
3342
+ s: m.scale ?? 1
3343
+ } : NO_PRE);
3344
+ }
3345
+ }
3346
+ if (variants.length === 0 && model) {
3347
+ variants.push(model);
3348
+ variantPres.push(NO_PRE);
3349
+ }
3350
+ if (lop.scatter3d && variants.length > 0) {
3351
+ const scatter = lop.scatter3d;
3352
+ const surface = await surfaceModel(scatter.surface, (src, n) => r3d$1.loadModelAsync(src, n.modelFormat ?? "auto", {
3353
+ center: n.center,
3354
+ normalizeSize: n.normalizeSize
3355
+ }));
3356
+ instOpt.transforms = surface ? scatterTransforms(scatter, surface, variants.map((v, k) => variantBottom(v, variantPres[k]))) : [];
3357
+ instOpt.count = instOpt.transforms.length;
2486
3358
  }
2487
- if (variants.length === 0 && model) variants.push(model);
2488
3359
  if (variants.length > 0) {
2489
3360
  const count = instOpt.bodies && Array.isArray(instOpt.bodies) && instOpt.bodies.length > 0 ? instOpt.bodies.length : instOpt.count ?? (Array.isArray(instOpt.transforms) ? instOpt.transforms.length : 0);
2490
3361
  if (count > 0) {
2491
3362
  const numVariants = variants.length;
2492
3363
  const bucketCounts = new Uint32Array(numVariants);
2493
3364
  for (let i$1 = 0; i$1 < count; i$1++) bucketCounts[i$1 % numVariants]++;
3365
+ const crowdCfg = lop.crowd;
3366
+ const crowdBakes = variants.map((v) => crowdCfg ? crowdBake(v, crowdCfg) : void 0);
3367
+ const strides = crowdBakes.map((b) => b ? 32 : 16);
3368
+ const crowdOpts = (bake, mesh) => {
3369
+ const baked = bake?.meshes.get(mesh);
3370
+ if (!bake || !baked) return void 0;
3371
+ return {
3372
+ palette: paletteBuffer(ctx.device, baked.palette),
3373
+ jointCount: baked.jointCount,
3374
+ time: (shiftedFrame ?? 0) / (fps || 30)
3375
+ };
3376
+ };
2494
3377
  const bucketItems = [];
2495
3378
  const bucketBounds = [];
2496
3379
  for (let k = 0; k < numVariants; k++) {
2497
- bucketItems.push(new Float32Array(bucketCounts[k] * 16));
3380
+ bucketItems.push(new Float32Array(bucketCounts[k] * strides[k]));
2498
3381
  bucketBounds.push({
2499
3382
  min: [
2500
3383
  Infinity,
@@ -2523,7 +3406,8 @@ const CompositorWebGPURenderer = async (args) => {
2523
3406
  const cursor = bucketCursors[k];
2524
3407
  const bData = bucketItems[k];
2525
3408
  const bBox = bucketBounds[k];
2526
- const off = cursor * 16;
3409
+ const off = cursor * strides[k];
3410
+ let ianim = {};
2527
3411
  let ix = 0;
2528
3412
  let iy = 0;
2529
3413
  let iz = 0;
@@ -2580,6 +3464,7 @@ const CompositorWebGPURenderer = async (args) => {
2580
3464
  } else if (typeof instOpt.instanceAt === "function") {
2581
3465
  const t = instOpt.instanceAt(i$1, shiftedFrame ?? 0, fps || 30);
2582
3466
  if (!t) continue;
3467
+ ianim = t;
2583
3468
  ix = t.x ?? 0;
2584
3469
  iy = t.y ?? 0;
2585
3470
  iz = t.z ?? 0;
@@ -2626,6 +3511,7 @@ const CompositorWebGPURenderer = async (args) => {
2626
3511
  }
2627
3512
  } else if (Array.isArray(instOpt.transforms) && instOpt.transforms[i$1]) {
2628
3513
  const t = instOpt.transforms[i$1];
3514
+ ianim = t;
2629
3515
  ix = t.x ?? 0;
2630
3516
  iy = t.y ?? 0;
2631
3517
  iz = t.z ?? 0;
@@ -2672,6 +3558,16 @@ const CompositorWebGPURenderer = async (args) => {
2672
3558
  }
2673
3559
  } else continue;
2674
3560
  bucketCursors[k]++;
3561
+ const pre = variantPres[k];
3562
+ if (pre !== NO_PRE) {
3563
+ [iqx, iqy, iqz, iqw] = composeQuat([
3564
+ iqx,
3565
+ iqy,
3566
+ iqz,
3567
+ iqw
3568
+ ], pre.q);
3569
+ iscale *= pre.s;
3570
+ }
2675
3571
  bData[off + 0] = ix;
2676
3572
  bData[off + 1] = iy;
2677
3573
  bData[off + 2] = iz;
@@ -2688,7 +3584,10 @@ const CompositorWebGPURenderer = async (args) => {
2688
3584
  bData[off + 13] = ieg;
2689
3585
  bData[off + 14] = ieb;
2690
3586
  bData[off + 15] = iew;
2691
- const rad = 50 * iscale;
3587
+ const bake = crowdBakes[k];
3588
+ if (bake && crowdCfg) bData.set(crowdRecord(bake, crowdCfg, i$1, ianim), off + 16);
3589
+ const vb = variants[k].bounds;
3590
+ const rad = (vb ? vb.boundingSphereRadius + Math.hypot(...vb.center) : 50) * iscale;
2692
3591
  bBox.min[0] = Math.min(bBox.min[0], ix - rad);
2693
3592
  bBox.min[1] = Math.min(bBox.min[1], iy - rad);
2694
3593
  bBox.min[2] = Math.min(bBox.min[2], iz - rad);
@@ -2700,15 +3599,26 @@ const CompositorWebGPURenderer = async (args) => {
2700
3599
  const bCount = bucketCursors[k];
2701
3600
  if (bCount === 0) continue;
2702
3601
  const v = variants[k];
2703
- const activeSlice = bCount === bucketCounts[k] ? bucketItems[k] : bucketItems[k].subarray(0, bCount * 16);
3602
+ const activeSlice = bCount === bucketCounts[k] ? bucketItems[k] : bucketItems[k].subarray(0, bCount * strides[k]);
2704
3603
  const instanceBuffer = r3d$1.uploadInstanceData(activeSlice);
2705
3604
  const bounds = Number.isFinite(bucketBounds[k].min[0]) ? bucketBounds[k] : void 0;
2706
3605
  for (const mesh of v.meshes) {
2707
3606
  const mat = mesh.materialName ? v.materials[mesh.materialName] : void 0;
3607
+ const diffuse = mat?.diffuseColor ?? [
3608
+ 1,
3609
+ 1,
3610
+ 1,
3611
+ 1
3612
+ ];
2708
3613
  const meshOpts = {
2709
3614
  modelMatrix: finalModelMatrix,
2710
3615
  opacity: modelOpacity * (mat?.opacity ?? 1),
2711
- colorTint,
3616
+ colorTint: [
3617
+ colorTint[0] * diffuse[0],
3618
+ colorTint[1] * diffuse[1],
3619
+ colorTint[2] * diffuse[2],
3620
+ colorTint[3] * (diffuse[3] ?? 1)
3621
+ ],
2712
3622
  transparent: false,
2713
3623
  twoSided: target?.twoSided ?? lop.twoSided ?? true,
2714
3624
  wireframe: Boolean(target?.wireframe ?? lop.wireframe),
@@ -2720,9 +3630,12 @@ const CompositorWebGPURenderer = async (args) => {
2720
3630
  ...surfaceProps(target, lop, mat),
2721
3631
  instanceBuffer,
2722
3632
  instanceCount: bCount,
2723
- bounds
3633
+ bounds,
3634
+ wind: meshWind(lop.wind, v, (shiftedFrame ?? 0) / (fps || 30)),
3635
+ crowd: crowdOpts(crowdBakes[k], mesh)
2724
3636
  };
2725
3637
  pending3DItems.push({
3638
+ owner: lop.id,
2726
3639
  kind: "mesh",
2727
3640
  mesh,
2728
3641
  texture: mat ? await r3d$1.materialTexture(mat) : void 0,
@@ -3062,6 +3975,7 @@ const CompositorWebGPURenderer = async (args) => {
3062
3975
  firstInstance
3063
3976
  };
3064
3977
  pending3DItems.push({
3978
+ owner: lop.id,
3065
3979
  kind: "mesh",
3066
3980
  mesh,
3067
3981
  texture: mat ? await r3d$1.materialTexture(mat) : void 0,
@@ -3092,6 +4006,7 @@ const CompositorWebGPURenderer = async (args) => {
3092
4006
  firstInstance: sparkFirst
3093
4007
  };
3094
4008
  pending3DItems.push({
4009
+ owner: lop.id,
3095
4010
  kind: "mesh",
3096
4011
  mesh,
3097
4012
  texture: mat ? await r3d$1.materialTexture(mat) : void 0,
@@ -3116,7 +4031,22 @@ const CompositorWebGPURenderer = async (args) => {
3116
4031
  if (lop.loop ?? true) clipSec %= clip.duration;
3117
4032
  }
3118
4033
  const sceneRoot = Matrix4Math.multiply(finalModelMatrix, model.rootTransform ?? Matrix4Math.identity());
3119
- const posed = model.nodes ? poseNodes(model, clip, clipSec) : void 0;
4034
+ const layerProps = Matrix4Math.multiply(Matrix4Math.translate(sampledX$1 - posX, sampledY$1 - posY, 0), localM);
4035
+ const wholeFracture = Boolean(lop.fracture3d && fractureWhole(model, lop.fracture3d, shiftedFrame ?? 0));
4036
+ const posed = lop.fracture3d && !wholeFracture && fracturePose(model, lop.fracture3d, shiftedFrame ?? 0, layerProps, [
4037
+ scX,
4038
+ scY,
4039
+ scZ
4040
+ ]) || (model.nodes ? poseCharacter({
4041
+ model,
4042
+ node: lop,
4043
+ frame: shiftedFrame ?? 0,
4044
+ fps: fps || 30,
4045
+ clip,
4046
+ clipSec,
4047
+ sceneRoot,
4048
+ key: `${compiled.key}:${lop.id}`
4049
+ }) : void 0);
3120
4050
  const skinCache = /* @__PURE__ */ new Map();
3121
4051
  const placeMesh = (mesh) => {
3122
4052
  if (!posed) return { matrix: sceneRoot };
@@ -3177,6 +4107,7 @@ const CompositorWebGPURenderer = async (args) => {
3177
4107
  }
3178
4108
  }
3179
4109
  for (const mesh of model.meshes) {
4110
+ if (wholeFracture && mesh.materialName === FRACTURE_INTERIOR_MATERIAL) continue;
3180
4111
  const mat = mesh.materialName ? model.materials[mesh.materialName] : void 0;
3181
4112
  const placed = placeMesh(mesh);
3182
4113
  const meshSkin = placed.joints ?? skinningMatrices;
@@ -3279,7 +4210,10 @@ const CompositorWebGPURenderer = async (args) => {
3279
4210
  }
3280
4211
  const meshOpts = {
3281
4212
  modelMatrix: placed.matrix,
3282
- morphWeights: sampleMorphWeights(mesh, clip, clipSec),
4213
+ morphWeights: namedMorphWeights(mesh.morphTargetNames, sampleMorphWeights(mesh, clip, clipSec), target?.morphs ?? lop.morphs, {
4214
+ frame: shiftedFrame ?? 0,
4215
+ fps: fps || 30
4216
+ }),
3283
4217
  opacity: modelOpacity * (mat?.opacity ?? 1),
3284
4218
  colorTint: meshColorTint,
3285
4219
  transparent: mat?.alphaMode === "blend" || modelOpacity * (mat?.opacity ?? 1) < 1 || meshColorTint[3] < 1 || hasVertexAlpha(mesh),
@@ -3298,13 +4232,16 @@ const CompositorWebGPURenderer = async (args) => {
3298
4232
  ambientIntensity: target?.ambientIntensity ?? lop.ambientIntensity ?? 1,
3299
4233
  metallic: target?.metallic ?? lop.metallic ?? mat?.metallic,
3300
4234
  ...surfaceProps(target, lop, mat),
3301
- ior: target?.ior ?? lop.ior,
4235
+ ior: target?.ior ?? lop.ior ?? mat?.ior,
3302
4236
  dispersion: target?.dispersion ?? lop.dispersion,
3303
4237
  thickness: target?.thickness ?? lop.thickness,
3304
- audioDeform: meshAudioDeform
4238
+ ...mat?.thickness !== void 0 ? { worldThickness: mat.thickness * matrixScale(placed.matrix) } : {},
4239
+ audioDeform: meshAudioDeform,
4240
+ wind: meshWind(lop.wind, model, (shiftedFrame ?? 0) / (fps || 30))
3305
4241
  };
3306
4242
  if (mat) meshOpts.maps = await r3d$1.materialMaps(mat);
3307
4243
  pending3DItems.push({
4244
+ owner: lop.id,
3308
4245
  kind: "mesh",
3309
4246
  mesh,
3310
4247
  texture: mat ? await r3d$1.materialTexture(mat) : void 0,
@@ -3430,7 +4367,6 @@ const CompositorWebGPURenderer = async (args) => {
3430
4367
  if (backfaceVisibility === "hidden" && Transform3DMath.computeSignedArea(q) < 0) return;
3431
4368
  quad3D = q;
3432
4369
  }
3433
- const isTrue3D = Boolean(target?.is3D ?? lop.is3D) || Boolean(camera && ((target?.z ?? lop.z ?? 0) !== 0 || lop.is3D));
3434
4370
  if (isContainer) {
3435
4371
  if (lop.transformStyle === "preserve-3d" && isTrue3D) {
3436
4372
  let current3DMatrix = Matrix4Math.identity();
@@ -3561,6 +4497,7 @@ const CompositorWebGPURenderer = async (args) => {
3561
4497
  }));
3562
4498
  }
3563
4499
  pending3DItems.push({
4500
+ owner: lop.id,
3564
4501
  kind: "quad",
3565
4502
  texture: activeGroupTex$1,
3566
4503
  opts: quadOpts
@@ -4064,6 +5001,7 @@ const CompositorWebGPURenderer = async (args) => {
4064
5001
  fontFamily: lop.fontFamily,
4065
5002
  fontStyle: lop.fontStyle,
4066
5003
  fontWeight: lop.fontWeight,
5004
+ fontVariations: lop.fontVariations,
4067
5005
  fill: target?.fill ?? target?.color ?? lop.fill,
4068
5006
  align: lop.align,
4069
5007
  direction: lop.direction,
@@ -4314,8 +5252,9 @@ const CompositorWebGPURenderer = async (args) => {
4314
5252
  if (Math.hypot(vx, vy) > .5) {
4315
5253
  if (!isTrue3D && !(is3D && quad3D)) {
4316
5254
  const reach = shutterAngle / 360;
4317
- const padX$1 = Math.ceil(Math.abs(vx) * rasterScale * reach);
4318
- const padY$1 = Math.ceil(Math.abs(vy) * rasterScale * reach);
5255
+ const side = ctx.device.limits.maxTextureDimension2D;
5256
+ const padX$1 = Math.min(Math.ceil(Math.abs(vx) * rasterScale * reach), Math.max(0, Math.floor((side - texW) / 2)));
5257
+ const padY$1 = Math.min(Math.ceil(Math.abs(vy) * rasterScale * reach), Math.max(0, Math.floor((side - texH) / 2)));
4319
5258
  blurPadX = padX$1 / rasterScale;
4320
5259
  blurPadY = padY$1 / rasterScale;
4321
5260
  const paddedTex = ctx.renderer.getTemporaryTexture(texW + padX$1 * 2, texH + padY$1 * 2, [
@@ -4427,11 +5366,13 @@ const CompositorWebGPURenderer = async (args) => {
4427
5366
  }
4428
5367
  const quadHasContent = layerHasRaster || Boolean(quadOpts.borderWidth && quadOpts.borderWidth > 0 && quadOpts.borderColor);
4429
5368
  if (!glyphBatches || quadHasContent) pending3DItems.push({
5369
+ owner: lop.id,
4430
5370
  kind: "quad",
4431
5371
  texture: renderedTex,
4432
5372
  opts: quadOpts
4433
5373
  });
4434
5374
  if (glyphBatches && glyphBatches.length > 0) pending3DItems.push({
5375
+ owner: lop.id,
4435
5376
  kind: "slug",
4436
5377
  batches: glyphBatches,
4437
5378
  opts: quadOpts,
@@ -4558,6 +5499,7 @@ const CompositorWebGPURenderer = async (args) => {
4558
5499
  ...props.excludeTextures || []
4559
5500
  ];
4560
5501
  currentCompTex = (await renderNodeTree(virtualMedia.children ?? [], compTex, compView, initialExcludes, 1, new DOMMatrix(), null, nativeWidth, nativeHeight, null, true)).finalTex;
5502
+ if (history && camera) history.recordFrame(shiftedFrame, camera);
4561
5503
  ctx.renderer.popTransform();
4562
5504
  ctx.renderer.popScissor();
4563
5505
  const finalPass = ctx.renderer.beginFrame(encoder, targetView, {
@@ -4575,6 +5517,43 @@ const CompositorWebGPURenderer = async (args) => {
4575
5517
  args.pass = finalPass;
4576
5518
  }
4577
5519
  };
5520
+ /** Whether a composition's camera asks for shutter motion blur. */
5521
+ function sceneMotionBlur(virtualMedia) {
5522
+ return collectNodeOps(virtualMedia.children).some((vm) => {
5523
+ const op = vm.operation;
5524
+ return op?.kind === "camera" && Boolean(op.motionBlur);
5525
+ });
5526
+ }
5527
+ /**
5528
+ * Renders `frame` of the composition into a scratch target only to record
5529
+ * its 3D motion history (the frame after it reads it).
5530
+ */
5531
+ async function renderMotionHistory(args, frame) {
5532
+ const { ctx, encoder, props, targetTexture, targetWidth, targetHeight } = args;
5533
+ const excludeTextures = [...props.excludeTextures ?? [], targetTexture];
5534
+ const scratch = ctx.renderer.getTemporaryTexture(targetWidth, targetHeight, excludeTextures);
5535
+ const view = scratch.createView();
5536
+ const sub = {
5537
+ ...args,
5538
+ targetView: view,
5539
+ targetTexture: scratch,
5540
+ pass: ctx.renderer.beginFrame(encoder, view, {
5541
+ r: 0,
5542
+ g: 0,
5543
+ b: 0,
5544
+ a: 0
5545
+ }, targetWidth, targetHeight, "clear"),
5546
+ props: {
5547
+ ...props,
5548
+ frame,
5549
+ ...props.compositionFrame !== void 0 && { compositionFrame: props.compositionFrame - 1 },
5550
+ motionHistoryOnly: true,
5551
+ excludeTextures
5552
+ }
5553
+ };
5554
+ await CompositorWebGPURenderer(sub);
5555
+ sub.pass.end();
5556
+ }
4578
5557
  /** Pixels of room around a border texture for its anti-aliased edge. */
4579
5558
  const BORDER_AA_MARGIN = 2;
4580
5559
  /**
@@ -4748,4 +5727,4 @@ var renderers_default = defineRenderer({ WebGPURenderer: CompositorWebGPURendere
4748
5727
 
4749
5728
  //#endregion
4750
5729
  export { Transform3DMath, renderers_default as default, has3DTransform };
4751
- //# sourceMappingURL=renderer-BVIXKfbC.mjs.map
5730
+ //# sourceMappingURL=renderer-4nXkaAWu.mjs.map