framefields 2.13.1 → 2.15.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 (339) hide show
  1. package/NOTICE.md +5 -1
  2. package/dist/audio/index.d.mts +5 -5
  3. package/dist/audio/index.mjs +10 -10
  4. package/dist/{audio-DIvi6hTD.mjs → audio-6TfPlfl4.mjs} +3 -3
  5. package/dist/{audio-DIvi6hTD.mjs.map → audio-6TfPlfl4.mjs.map} +1 -1
  6. package/dist/{audio-C4B9p_yq.d.mts → audio-7oqTUi4G.d.mts} +3 -3
  7. package/dist/{audio-C4B9p_yq.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.d.mts.map +1 -1
  11. package/dist/cloud/index.mjs +1 -1
  12. package/dist/{cloud-Cj1TIme8.mjs → cloud-jnk1hPYf.mjs} +2 -2
  13. package/dist/{cloud-Cj1TIme8.mjs.map → cloud-jnk1hPYf.mjs.map} +1 -1
  14. package/dist/components/index.d.mts +13 -13
  15. package/dist/components/index.mjs +43 -43
  16. package/dist/{dist-Kbf-JTry.mjs → dist-BtI6ukB5.mjs} +2 -2
  17. package/dist/{dist-Kbf-JTry.mjs.map → dist-BtI6ukB5.mjs.map} +1 -1
  18. package/dist/{dist-CCrYOkcc.mjs → dist-COOpgG9G.mjs} +4 -3
  19. package/dist/{dist-CCrYOkcc.mjs.map → dist-COOpgG9G.mjs.map} +1 -1
  20. package/dist/{dist-DtPPPsZV.mjs → dist-XqlFM2a8.mjs} +14063 -5308
  21. package/dist/dist-XqlFM2a8.mjs.map +1 -0
  22. package/dist/{dist-CiTgmfUw.mjs → dist-f1zkl69f.mjs} +4 -4
  23. package/dist/dist-f1zkl69f.mjs.map +1 -0
  24. package/dist/{dist-B_rSWeOz.mjs → dist-umQOWz0a.mjs} +1 -1
  25. package/dist/effects/index.d.mts +5 -5
  26. package/dist/effects/index.mjs +35 -35
  27. package/dist/{effects-B-L3rLs6.mjs → effects-DlRMovU5.mjs} +2 -2
  28. package/dist/{effects-B-L3rLs6.mjs.map → effects-DlRMovU5.mjs.map} +1 -1
  29. package/dist/fonts/index.d.mts +1 -1
  30. package/dist/fonts/index.mjs +2 -2
  31. package/dist/{fonts-k5ll8NTE.mjs → fonts-BWYcXjyK.mjs} +3 -3
  32. package/dist/fonts-BWYcXjyK.mjs.map +1 -0
  33. package/dist/{gpu-D2HT3KcE.mjs → gpu-CH1PdM4T.mjs} +2 -2
  34. package/dist/{gpu-D2HT3KcE.mjs.map → gpu-CH1PdM4T.mjs.map} +1 -1
  35. package/dist/{index-rtCXNxGO.d.mts → index-BHTycml5.d.mts} +2 -2
  36. package/dist/{index-rtCXNxGO.d.mts.map → index-BHTycml5.d.mts.map} +1 -1
  37. package/dist/{index-BFTtXosj.d.mts → index-BVd6nXd5.d.mts} +2 -2
  38. package/dist/{index-BFTtXosj.d.mts.map → index-BVd6nXd5.d.mts.map} +1 -1
  39. package/dist/{index-ByhlTwxh.d.mts → index-BcxxxwhV.d.mts} +5 -5
  40. package/dist/{index-ByhlTwxh.d.mts.map → index-BcxxxwhV.d.mts.map} +1 -1
  41. package/dist/{index-CnZhBlob.d.mts → index-BnB_Ou2y.d.mts} +2 -2
  42. package/dist/index-BnB_Ou2y.d.mts.map +1 -0
  43. package/dist/{index-a6JrbAK0.d.mts → index-C-V6CAhP.d.mts} +32 -2
  44. package/dist/index-C-V6CAhP.d.mts.map +1 -0
  45. package/dist/{index-DBh3WphX.d.mts → index-CMhkkc56.d.mts} +44 -12
  46. package/dist/index-CMhkkc56.d.mts.map +1 -0
  47. package/dist/{index-BGlaws7-.d.mts → index-CYVpYGM-.d.mts} +3 -3
  48. package/dist/{index-BGlaws7-.d.mts.map → index-CYVpYGM-.d.mts.map} +1 -1
  49. package/dist/{index-DQ1U3HC3.d.mts → index-DhL7ckvs.d.mts} +7 -7
  50. package/dist/index-DhL7ckvs.d.mts.map +1 -0
  51. package/dist/{index-CtKUQ2VG.d.mts → index-DkAjKnhW.d.mts} +1487 -533
  52. package/dist/index-DkAjKnhW.d.mts.map +1 -0
  53. package/dist/{index-B08eSpaI.d.mts → index-RILu3ULT.d.mts} +2 -2
  54. package/dist/{index-B08eSpaI.d.mts.map → index-RILu3ULT.d.mts.map} +1 -1
  55. package/dist/{index-C5sgRLlG.d.mts → index-TCtdSXd7.d.mts} +3 -1
  56. package/dist/index-TCtdSXd7.d.mts.map +1 -0
  57. package/dist/{index-C2yaponf.d.mts → index-s2qQ8ifZ.d.mts} +911 -57
  58. package/dist/index-s2qQ8ifZ.d.mts.map +1 -0
  59. package/dist/index.d.mts +15 -15
  60. package/dist/index.mjs +52 -52
  61. package/dist/preview/index.d.mts +5 -5
  62. package/dist/preview/index.mjs +10 -10
  63. package/dist/preview-BeGF1Bb1.mjs +1002 -0
  64. package/dist/preview-BeGF1Bb1.mjs.map +1 -0
  65. package/dist/preview-engine/audio.mjs +1 -1
  66. package/dist/preview-engine/{chunk-64L4GUOF.mjs → chunk-4F3DG6YB.mjs} +2 -2
  67. package/dist/preview-engine/chunk-A2OTOW3B.mjs +4694 -0
  68. package/dist/preview-engine/chunk-BR56TQBP.mjs +5 -0
  69. package/dist/preview-engine/{chunk-ADLIF544.mjs → chunk-H2XKXXAU.mjs} +1 -1
  70. package/dist/preview-engine/chunk-NDUODAY2.mjs +3 -0
  71. package/dist/preview-engine/{chunk-FZ4RD7HV.mjs → chunk-QAQJSWUT.mjs} +1 -1
  72. package/dist/preview-engine/chunk-SWRGQTVC.mjs +138 -0
  73. package/dist/preview-engine/{chunk-52X7SURQ.mjs → chunk-TEA3GVBJ.mjs} +1 -1
  74. package/dist/preview-engine/chunk-VM2ZBPLM.mjs +644 -0
  75. package/dist/preview-engine/{chunk-UBY2UYEL.mjs → chunk-XSHNQJ7L.mjs} +1 -1
  76. package/dist/preview-engine/components.mjs +1 -1
  77. package/dist/preview-engine/effects.mjs +1 -1
  78. package/dist/preview-engine/fonts.mjs +1 -1
  79. package/dist/preview-engine/framefields.mjs +1 -1
  80. package/dist/preview-engine/player.mjs +2 -2
  81. package/dist/preview-engine/{renderers-DU7KPFVV.mjs → renderers-6C7NWWDX.mjs} +1 -1
  82. package/dist/preview-engine/renderers-HHVGNMSJ.mjs +206 -0
  83. package/dist/preview-engine/{src-YXNX4MLK.mjs → src-RCNEKIRP.mjs} +1 -1
  84. package/dist/preview-page/index.html +19 -0
  85. package/dist/preview-page/main.css +1 -0
  86. package/dist/preview-page/main.js +327 -0
  87. package/dist/{program-BfiB9ttP.mjs → program-CMigyfso.mjs} +919 -58
  88. package/dist/program-CMigyfso.mjs.map +1 -0
  89. package/dist/renderer/index.d.mts +4 -4
  90. package/dist/renderer/index.mjs +2 -2
  91. package/dist/{renderer-CoC64eam.mjs → renderer-0GMEL_Zi.mjs} +3 -3
  92. package/dist/{renderer-CoC64eam.mjs.map → renderer-0GMEL_Zi.mjs.map} +1 -1
  93. package/dist/{renderer-CSu_vYPn.mjs → renderer-3Wv2ZBJG.mjs} +3 -3
  94. package/dist/{renderer-CSu_vYPn.mjs.map → renderer-3Wv2ZBJG.mjs.map} +1 -1
  95. package/dist/{renderer-Dv_iRIyx.mjs → renderer-4nXkaAWu.mjs} +1280 -285
  96. package/dist/renderer-4nXkaAWu.mjs.map +1 -0
  97. package/dist/{renderer-TfeohPxt.mjs → renderer-6SnfLXeg.mjs} +3 -3
  98. package/dist/{renderer-TfeohPxt.mjs.map → renderer-6SnfLXeg.mjs.map} +1 -1
  99. package/dist/{renderer-D0xM67cK.mjs → renderer-8gHw3O0P.mjs} +2 -2
  100. package/dist/{renderer-D0xM67cK.mjs.map → renderer-8gHw3O0P.mjs.map} +1 -1
  101. package/dist/{renderer-C64_B8It.mjs → renderer-AOBglZHC.mjs} +2 -2
  102. package/dist/{renderer-B10HDC13.mjs → renderer-B0bSARK3.mjs} +3 -3
  103. package/dist/{renderer-B10HDC13.mjs.map → renderer-B0bSARK3.mjs.map} +1 -1
  104. package/dist/{renderer-DWYmJSrl.mjs → renderer-B2_tuRfl.mjs} +2 -2
  105. package/dist/{renderer-DWYmJSrl.mjs.map → renderer-B2_tuRfl.mjs.map} +1 -1
  106. package/dist/{renderer-mDBoz0zT.mjs → renderer-B82FhkEr.mjs} +3 -3
  107. package/dist/{renderer-mDBoz0zT.mjs.map → renderer-B82FhkEr.mjs.map} +1 -1
  108. package/dist/{renderer-DYeNNc_G.mjs → renderer-B9lzA0PY.mjs} +3 -3
  109. package/dist/{renderer-DYeNNc_G.mjs.map → renderer-B9lzA0PY.mjs.map} +1 -1
  110. package/dist/{renderer-BGchUBW_.mjs → renderer-BBSIWSry.mjs} +2 -2
  111. package/dist/{renderer-BGchUBW_.mjs.map → renderer-BBSIWSry.mjs.map} +1 -1
  112. package/dist/{renderer-GIlQ9lZq.mjs → renderer-BEKDMxbi.mjs} +2 -2
  113. package/dist/{renderer-GIlQ9lZq.mjs.map → renderer-BEKDMxbi.mjs.map} +1 -1
  114. package/dist/renderer-BERGAxqA.mjs +5 -0
  115. package/dist/{renderer-Bw0O--gW.mjs → renderer-BF960HY8.mjs} +3 -3
  116. package/dist/{renderer-Bw0O--gW.mjs.map → renderer-BF960HY8.mjs.map} +1 -1
  117. package/dist/{renderer-BhcTYhaP.mjs → renderer-BFwyVxCu.mjs} +3 -3
  118. package/dist/{renderer-BhcTYhaP.mjs.map → renderer-BFwyVxCu.mjs.map} +1 -1
  119. package/dist/{renderer-DYlx7VHo.mjs → renderer-BNiwcI3-.mjs} +2 -2
  120. package/dist/{renderer-DYlx7VHo.mjs.map → renderer-BNiwcI3-.mjs.map} +1 -1
  121. package/dist/{renderer-B3_GddgJ.mjs → renderer-BPbVb2Q4.mjs} +2 -2
  122. package/dist/{renderer-B3_GddgJ.mjs.map → renderer-BPbVb2Q4.mjs.map} +1 -1
  123. package/dist/renderer-BQ_YkBMt.mjs +6 -0
  124. package/dist/{renderer-Y1kjtOto.mjs → renderer-BUWxlA8T.mjs} +3 -3
  125. package/dist/{renderer-Y1kjtOto.mjs.map → renderer-BUWxlA8T.mjs.map} +1 -1
  126. package/dist/renderer-BW0Tjsv9.mjs +5 -0
  127. package/dist/{renderer-DtFZbE1j.mjs → renderer-BX8liu-e.mjs} +2 -2
  128. package/dist/{renderer-BOlHW3Mi.mjs → renderer-BXIwDAr_.mjs} +2 -2
  129. package/dist/{renderer-BOlHW3Mi.mjs.map → renderer-BXIwDAr_.mjs.map} +1 -1
  130. package/dist/{renderer-DzON3wDZ.mjs → renderer-B_Hy1W-g.mjs} +3 -3
  131. package/dist/{renderer-DzON3wDZ.mjs.map → renderer-B_Hy1W-g.mjs.map} +1 -1
  132. package/dist/{renderer-BM19iK8v.mjs → renderer-Bb6cvSgh.mjs} +2 -2
  133. package/dist/{renderer-BM19iK8v.mjs.map → renderer-Bb6cvSgh.mjs.map} +1 -1
  134. package/dist/{renderer-D0h1hTOo.mjs → renderer-BdppwWC9.mjs} +2 -2
  135. package/dist/{renderer-D0h1hTOo.mjs.map → renderer-BdppwWC9.mjs.map} +1 -1
  136. package/dist/{renderer-BTwuM9u-.mjs → renderer-Bhc24HVe.mjs} +3 -3
  137. package/dist/{renderer-BTwuM9u-.mjs.map → renderer-Bhc24HVe.mjs.map} +1 -1
  138. package/dist/{renderer-BZX932Lj.mjs → renderer-BiNA2wF0.mjs} +2 -2
  139. package/dist/{renderer-BZX932Lj.mjs.map → renderer-BiNA2wF0.mjs.map} +1 -1
  140. package/dist/{renderer-CkTehyRX.mjs → renderer-BmoBgTp5.mjs} +3 -3
  141. package/dist/{renderer-CkTehyRX.mjs.map → renderer-BmoBgTp5.mjs.map} +1 -1
  142. package/dist/{renderer-B3un4Pdi.mjs → renderer-BmqFmCWS.mjs} +3 -3
  143. package/dist/{renderer-B3un4Pdi.mjs.map → renderer-BmqFmCWS.mjs.map} +1 -1
  144. package/dist/{renderer-CHCRcCKw.mjs → renderer-BnVwAObV.mjs} +2 -2
  145. package/dist/{renderer-CHCRcCKw.mjs.map → renderer-BnVwAObV.mjs.map} +1 -1
  146. package/dist/{renderer-Cv9CKKWG.mjs → renderer-BxttZHme.mjs} +2 -2
  147. package/dist/{renderer-Cv9CKKWG.mjs.map → renderer-BxttZHme.mjs.map} +1 -1
  148. package/dist/renderer-BzyPdUD1.mjs +5 -0
  149. package/dist/renderer-C0hM-mtY.mjs +5 -0
  150. package/dist/renderer-C6cyUTvk.mjs +5 -0
  151. package/dist/{renderer-B6bK9rXm.mjs → renderer-C8E4XsSg.mjs} +2 -2
  152. package/dist/{renderer-B6bK9rXm.mjs.map → renderer-C8E4XsSg.mjs.map} +1 -1
  153. package/dist/renderer-C8zY3pzk.mjs +5 -0
  154. package/dist/{renderer-4nsuReW2.mjs → renderer-C9U-LY50.mjs} +3 -3
  155. package/dist/{renderer-4nsuReW2.mjs.map → renderer-C9U-LY50.mjs.map} +1 -1
  156. package/dist/{renderer-BvBqTAtk.mjs → renderer-CB2JVu5x.mjs} +3 -3
  157. package/dist/{renderer-BvBqTAtk.mjs.map → renderer-CB2JVu5x.mjs.map} +1 -1
  158. package/dist/{renderer-CQ4h8n7p.mjs → renderer-CC4YBJ8Z.mjs} +3 -3
  159. package/dist/{renderer-CQ4h8n7p.mjs.map → renderer-CC4YBJ8Z.mjs.map} +1 -1
  160. package/dist/renderer-CEkSOyKu.mjs +5 -0
  161. package/dist/{renderer-zLi2qFGT.mjs → renderer-CIycc5O-.mjs} +2 -2
  162. package/dist/{renderer-zLi2qFGT.mjs.map → renderer-CIycc5O-.mjs.map} +1 -1
  163. package/dist/renderer-CJYtfa4x.mjs +5 -0
  164. package/dist/{renderer-yNWYqvvq.mjs → renderer-CJraQlV-.mjs} +2 -2
  165. package/dist/{renderer-yNWYqvvq.mjs.map → renderer-CJraQlV-.mjs.map} +1 -1
  166. package/dist/renderer-CKMtHGZh.mjs +5 -0
  167. package/dist/renderer-CLzicoiy.mjs +5 -0
  168. package/dist/renderer-CM2A4BHd.mjs +5 -0
  169. package/dist/{renderer-BrEeUw7T.mjs → renderer-COWGWtob.mjs} +2 -2
  170. package/dist/{renderer-BrEeUw7T.mjs.map → renderer-COWGWtob.mjs.map} +1 -1
  171. package/dist/{renderer-uER8kn7f.mjs → renderer-COxG2XGf.mjs} +2 -2
  172. package/dist/{renderer-uER8kn7f.mjs.map → renderer-COxG2XGf.mjs.map} +1 -1
  173. package/dist/{renderer-C4hd6Cqi.mjs → renderer-CQDCIsQm.mjs} +2 -2
  174. package/dist/{renderer-C4hd6Cqi.mjs.map → renderer-CQDCIsQm.mjs.map} +1 -1
  175. package/dist/{renderer-Cfx_FCSf.mjs → renderer-CRUeag8i.mjs} +3 -3
  176. package/dist/{renderer-Cfx_FCSf.mjs.map → renderer-CRUeag8i.mjs.map} +1 -1
  177. package/dist/{renderer-CXrnzihz.mjs → renderer-CWVput06.mjs} +3 -3
  178. package/dist/{renderer-CXrnzihz.mjs.map → renderer-CWVput06.mjs.map} +1 -1
  179. package/dist/renderer-CZlHjcxr.mjs +5 -0
  180. package/dist/{renderer-BmyFfvVA.mjs → renderer-CbbnBLOB.mjs} +3 -3
  181. package/dist/{renderer-BmyFfvVA.mjs.map → renderer-CbbnBLOB.mjs.map} +1 -1
  182. package/dist/renderer-CbimvSU8.mjs +5 -0
  183. package/dist/renderer-Ccvc63yd.mjs +5 -0
  184. package/dist/renderer-Cdp-SGfq.mjs +5 -0
  185. package/dist/renderer-CeTcSs3e.mjs +5 -0
  186. package/dist/{renderer-_J9JcCMR.mjs → renderer-CfLZein2.mjs} +3 -3
  187. package/dist/{renderer-_J9JcCMR.mjs.map → renderer-CfLZein2.mjs.map} +1 -1
  188. package/dist/{renderer-D-mX1EVA.mjs → renderer-Ch-q1O-q.mjs} +3 -3
  189. package/dist/{renderer-D-mX1EVA.mjs.map → renderer-Ch-q1O-q.mjs.map} +1 -1
  190. package/dist/{renderer-uzU6dsdR.mjs → renderer-Ch5JvJWl.mjs} +2 -2
  191. package/dist/{renderer-uzU6dsdR.mjs.map → renderer-Ch5JvJWl.mjs.map} +1 -1
  192. package/dist/{renderer-BZMh2io2.mjs → renderer-ChtEzq-R.mjs} +2 -2
  193. package/dist/{renderer-BZMh2io2.mjs.map → renderer-ChtEzq-R.mjs.map} +1 -1
  194. package/dist/{renderer-B2IUPjhw.mjs → renderer-Cj9nTC5l.mjs} +3 -3
  195. package/dist/{renderer-B2IUPjhw.mjs.map → renderer-Cj9nTC5l.mjs.map} +1 -1
  196. package/dist/{renderer-CdXBRdFf.mjs → renderer-CqT8HsSS.mjs} +2 -2
  197. package/dist/{renderer-CdXBRdFf.mjs.map → renderer-CqT8HsSS.mjs.map} +1 -1
  198. package/dist/{renderer-CldjJmRR.mjs → renderer-CuQjMhdV.mjs} +2 -2
  199. package/dist/renderer-CvNuu52U.mjs +5 -0
  200. package/dist/renderer-CwciC0oI.mjs +5 -0
  201. package/dist/renderer-CysLUbIr.mjs +5 -0
  202. package/dist/{renderer-BsrKj6Td.mjs → renderer-Cz30EVws.mjs} +2 -2
  203. package/dist/{renderer-BsrKj6Td.mjs.map → renderer-Cz30EVws.mjs.map} +1 -1
  204. package/dist/renderer-D3A22Vxb.mjs +5 -0
  205. package/dist/renderer-D5SY9EmA.mjs +5 -0
  206. package/dist/renderer-D8h3ZvaE.mjs +5 -0
  207. package/dist/renderer-DE-B6N9n.mjs +5 -0
  208. package/dist/renderer-DETZTI7t.mjs +5 -0
  209. package/dist/{renderer-CC22Djgi.mjs → renderer-DHoX-5U_.mjs} +3 -3
  210. package/dist/{renderer-CC22Djgi.mjs.map → renderer-DHoX-5U_.mjs.map} +1 -1
  211. package/dist/{renderer-HKv5cBdp.mjs → renderer-DOkm0Ycd.mjs} +2 -2
  212. package/dist/{renderer-HKv5cBdp.mjs.map → renderer-DOkm0Ycd.mjs.map} +1 -1
  213. package/dist/{renderer-fG8Jy8Rx.d.mts → renderer-DU3f6ZrS.d.mts} +2 -2
  214. package/dist/{renderer-fG8Jy8Rx.d.mts.map → renderer-DU3f6ZrS.d.mts.map} +1 -1
  215. package/dist/{renderer-C98XwEIr.mjs → renderer-DUzv-shq.mjs} +2 -2
  216. package/dist/{renderer-C98XwEIr.mjs.map → renderer-DUzv-shq.mjs.map} +1 -1
  217. package/dist/renderer-DWnDj0IM.mjs +5 -0
  218. package/dist/renderer-DcGUIbQF.mjs +5 -0
  219. package/dist/renderer-Dfq6FoNO.mjs +5 -0
  220. package/dist/renderer-DhasXW6S.mjs +3 -0
  221. package/dist/{renderer-BEuXJY-C.mjs → renderer-DhnOWnEA.mjs} +3 -3
  222. package/dist/{renderer-BEuXJY-C.mjs.map → renderer-DhnOWnEA.mjs.map} +1 -1
  223. package/dist/{renderer-BIKkxw_Z.mjs → renderer-Dhy1XHzz.mjs} +3 -3
  224. package/dist/{renderer-BIKkxw_Z.mjs.map → renderer-Dhy1XHzz.mjs.map} +1 -1
  225. package/dist/renderer-DjCvpsV_.mjs +5 -0
  226. package/dist/{renderer-pn52dzZz.mjs → renderer-DjJshz9Y.mjs} +2 -2
  227. package/dist/{renderer-pn52dzZz.mjs.map → renderer-DjJshz9Y.mjs.map} +1 -1
  228. package/dist/{renderer-DhGZ41hQ.mjs → renderer-DmTUIdus.mjs} +3 -3
  229. package/dist/{renderer-DhGZ41hQ.mjs.map → renderer-DmTUIdus.mjs.map} +1 -1
  230. package/dist/{renderer-D_m6Qbsi.mjs → renderer-Dq7AioJa.mjs} +2 -2
  231. package/dist/{renderer-D_m6Qbsi.mjs.map → renderer-Dq7AioJa.mjs.map} +1 -1
  232. package/dist/renderer-DrAaM6JD.mjs +5 -0
  233. package/dist/{renderer-D-GWBoVT.mjs → renderer-Dt4sqNEu.mjs} +2 -2
  234. package/dist/{renderer-D-GWBoVT.mjs.map → renderer-Dt4sqNEu.mjs.map} +1 -1
  235. package/dist/{renderer-DCrKEVjb.mjs → renderer-Dtcs4idV.mjs} +3 -3
  236. package/dist/{renderer-DCrKEVjb.mjs.map → renderer-Dtcs4idV.mjs.map} +1 -1
  237. package/dist/renderer-Dxlv8LYm.mjs +5 -0
  238. package/dist/{renderer-CO9vE3sd.mjs → renderer-Gj8SrE2W.mjs} +2 -2
  239. package/dist/{renderer-CO9vE3sd.mjs.map → renderer-Gj8SrE2W.mjs.map} +1 -1
  240. package/dist/{renderer-DvHi_wXE.mjs → renderer-KfnXDJS0.mjs} +3 -3
  241. package/dist/{renderer-DvHi_wXE.mjs.map → renderer-KfnXDJS0.mjs.map} +1 -1
  242. package/dist/{renderer-7xQmXXMH.mjs → renderer-MXVlg4p5.mjs} +3 -3
  243. package/dist/{renderer-7xQmXXMH.mjs.map → renderer-MXVlg4p5.mjs.map} +1 -1
  244. package/dist/{renderer-YONlI6W-.mjs → renderer-N2b6QZ1E.mjs} +3 -3
  245. package/dist/{renderer-YONlI6W-.mjs.map → renderer-N2b6QZ1E.mjs.map} +1 -1
  246. package/dist/renderer-Pb43OO2t.mjs +5 -0
  247. package/dist/{renderer-DNynPM6X.mjs → renderer-Q7gDzm8c.mjs} +2 -2
  248. package/dist/{renderer-DNynPM6X.mjs.map → renderer-Q7gDzm8c.mjs.map} +1 -1
  249. package/dist/{renderer-BEhZiOT4.mjs → renderer-RCiANtRV.mjs} +2 -2
  250. package/dist/{renderer-BEhZiOT4.mjs.map → renderer-RCiANtRV.mjs.map} +1 -1
  251. package/dist/{renderer-Dm57W73p.mjs → renderer-Sig9nZcI.mjs} +2 -2
  252. package/dist/renderer-VQbm-cGr.mjs +5 -0
  253. package/dist/{renderer-DiZkZ0Pr.mjs → renderer-fabBgwQX.mjs} +3 -3
  254. package/dist/{renderer-DiZkZ0Pr.mjs.map → renderer-fabBgwQX.mjs.map} +1 -1
  255. package/dist/{renderer-Bw-_GZVw.mjs → renderer-g-0PCNoo.mjs} +3 -3
  256. package/dist/{renderer-Bw-_GZVw.mjs.map → renderer-g-0PCNoo.mjs.map} +1 -1
  257. package/dist/{renderer-QbSrnPZU.mjs → renderer-gmHKIbKI.mjs} +3 -3
  258. package/dist/{renderer-QbSrnPZU.mjs.map → renderer-gmHKIbKI.mjs.map} +1 -1
  259. package/dist/{renderer-CU6WT5jW.mjs → renderer-kVZwiRoK.mjs} +3 -3
  260. package/dist/{renderer-CU6WT5jW.mjs.map → renderer-kVZwiRoK.mjs.map} +1 -1
  261. package/dist/renderer-oPMypDM1.mjs +5 -0
  262. package/dist/{renderer-CRhEGqW4.mjs → renderer-p7tgxP-A.mjs} +3 -3
  263. package/dist/{renderer-CRhEGqW4.mjs.map → renderer-p7tgxP-A.mjs.map} +1 -1
  264. package/dist/{renderer-CO4NGvAh.mjs → renderer-tvYj306A.mjs} +81 -78
  265. package/dist/renderer-tvYj306A.mjs.map +1 -0
  266. package/dist/renderer-xvWGpVyp.mjs +5 -0
  267. package/dist/renderer-zafZbD6Q.mjs +5 -0
  268. package/dist/{renderers-CP8CFWDs-BufiCyn7.mjs → renderers-CP8CFWDs-DLjtjrwx.mjs} +2 -2
  269. package/dist/{renderers-CP8CFWDs-BufiCyn7.mjs.map → renderers-CP8CFWDs-DLjtjrwx.mjs.map} +1 -1
  270. package/dist/sam/index.d.mts +3 -3
  271. package/dist/sam/index.mjs +1 -1
  272. package/dist/signals/index.d.mts +5 -5
  273. package/dist/signals/index.mjs +4 -4
  274. package/dist/{src-BU6vm9sP-DdTHmCr4.mjs → src-B23p_eh7-Cj6-lvV6.mjs} +293 -6
  275. package/dist/src-B23p_eh7-Cj6-lvV6.mjs.map +1 -0
  276. package/dist/{src-DrP7fGJX.mjs → src-CoHAjviq.mjs} +1355 -178
  277. package/dist/src-CoHAjviq.mjs.map +1 -0
  278. package/dist/storage/index.mjs +1 -1
  279. package/package.json +63 -60
  280. package/dist/dist-CiTgmfUw.mjs.map +0 -1
  281. package/dist/dist-DtPPPsZV.mjs.map +0 -1
  282. package/dist/fonts-k5ll8NTE.mjs.map +0 -1
  283. package/dist/index-C2yaponf.d.mts.map +0 -1
  284. package/dist/index-C5sgRLlG.d.mts.map +0 -1
  285. package/dist/index-CnZhBlob.d.mts.map +0 -1
  286. package/dist/index-CtKUQ2VG.d.mts.map +0 -1
  287. package/dist/index-DBh3WphX.d.mts.map +0 -1
  288. package/dist/index-DQ1U3HC3.d.mts.map +0 -1
  289. package/dist/index-a6JrbAK0.d.mts.map +0 -1
  290. package/dist/preview-engine/chunk-5ZS46EFB.mjs +0 -5
  291. package/dist/preview-engine/chunk-N3XTDAL6.mjs +0 -2227
  292. package/dist/preview-engine/chunk-NCQHOGZK.mjs +0 -3
  293. package/dist/preview-engine/chunk-PH3UUX6A.mjs +0 -138
  294. package/dist/preview-engine/chunk-XCPRRMMW.mjs +0 -2462
  295. package/dist/preview-engine/renderers-ZMMQ7S2A.mjs +0 -228
  296. package/dist/preview-lHH8Tv4j.mjs +0 -2446
  297. package/dist/preview-lHH8Tv4j.mjs.map +0 -1
  298. package/dist/program-BfiB9ttP.mjs.map +0 -1
  299. package/dist/renderer-0rLmztAv.mjs +0 -5
  300. package/dist/renderer-B0pjOXFY.mjs +0 -5
  301. package/dist/renderer-B7J13reL.mjs +0 -5
  302. package/dist/renderer-BPGIKGgl.mjs +0 -5
  303. package/dist/renderer-Bv-Artj1.mjs +0 -5
  304. package/dist/renderer-BvLjbhcL.mjs +0 -5
  305. package/dist/renderer-By6awWaf.mjs +0 -5
  306. package/dist/renderer-C0rvsz7m.mjs +0 -5
  307. package/dist/renderer-CDgud_HB.mjs +0 -5
  308. package/dist/renderer-CDiRF3bG.mjs +0 -6
  309. package/dist/renderer-CIIEQd4B.mjs +0 -5
  310. package/dist/renderer-CJblwOA9.mjs +0 -5
  311. package/dist/renderer-CNMOXtXE.mjs +0 -5
  312. package/dist/renderer-CO4NGvAh.mjs.map +0 -1
  313. package/dist/renderer-COeSeNGg.mjs +0 -5
  314. package/dist/renderer-CUfElmtZ.mjs +0 -5
  315. package/dist/renderer-CYh2lFoJ.mjs +0 -5
  316. package/dist/renderer-CYtVZYMH.mjs +0 -5
  317. package/dist/renderer-CjxZlkjb.mjs +0 -5
  318. package/dist/renderer-Cpd6TvTC.mjs +0 -5
  319. package/dist/renderer-D3ziOPhX.mjs +0 -5
  320. package/dist/renderer-D5yp74NL.mjs +0 -3
  321. package/dist/renderer-DL-phtL5.mjs +0 -5
  322. package/dist/renderer-DSnmYnHg.mjs +0 -5
  323. package/dist/renderer-DUGfsT-E.mjs +0 -5
  324. package/dist/renderer-DW5af9x1.mjs +0 -5
  325. package/dist/renderer-DYMrol1t.mjs +0 -5
  326. package/dist/renderer-DekeUHaz.mjs +0 -5
  327. package/dist/renderer-Dt5uyJdZ.mjs +0 -5
  328. package/dist/renderer-Dv_iRIyx.mjs.map +0 -1
  329. package/dist/renderer-DwLQAnaK.mjs +0 -5
  330. package/dist/renderer-IXQP2CWM.mjs +0 -5
  331. package/dist/renderer-K-Pkw63O.mjs +0 -5
  332. package/dist/renderer-MPw2KH9P.mjs +0 -5
  333. package/dist/renderer-RPu8RMav.mjs +0 -5
  334. package/dist/renderer-dCqxd5N2.mjs +0 -5
  335. package/dist/renderer-lhjmE-Fp.mjs +0 -5
  336. package/dist/renderer-u9cmBuvM.mjs +0 -5
  337. package/dist/renderer-vH12jcpz.mjs +0 -5
  338. package/dist/src-BU6vm9sP-DdTHmCr4.mjs.map +0 -1
  339. package/dist/src-DrP7fGJX.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-DtPPPsZV.mjs";
5
- import { a as createFullscreenPipeline, l as fragmentBindGroupLayout, n as PREMULTIPLIED_BLEND, t as FULLSCREEN_QUAD_WGSL } from "./gpu-D2HT3KcE.mjs";
6
- import { i as keyedGain, m as measureText, n as decodeAudioSource, s as resolveLayerDuration } from "./dist-CCrYOkcc.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 { B as evaluateKeyframes, I as computeLayout, N as collectNodeOps, U as initLayout, V as evaluateTrackAtFrame } from "./program-BfiB9ttP.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-C0dsKt0u.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
  }
@@ -1084,6 +1636,25 @@ function has3DTransform(target, lop) {
1084
1636
  return false;
1085
1637
  }
1086
1638
  const meshHasAlpha = /* @__PURE__ */ new WeakMap();
1639
+ /**
1640
+ * How far a text node's ink can reach past its layout box, in px: an outside
1641
+ * stroke reaches its full width past the outline (any other alignment, half),
1642
+ * a shadow its offset plus blur. One px of slack covers antialiasing.
1643
+ */
1644
+ function textInkOutset(target, lop) {
1645
+ const num = (v) => typeof v === "number" && v > 0 ? v : 0;
1646
+ const width = num(target?.strokeWidth ?? lop.strokeWidth);
1647
+ const stroke = Boolean(target?.stroke ?? target?.strokeColor ?? lop.stroke) ? lop.strokeAlign === "outside" ? width : width / 2 : 0;
1648
+ let shadow = 0;
1649
+ const shadows = lop.shadows;
1650
+ if (Array.isArray(shadows)) for (const s of shadows) shadow = Math.max(shadow, Math.max(Math.abs(s.offset?.x ?? 0), Math.abs(s.offset?.y ?? 0)) + 2 * num(s.blurRadius));
1651
+ const reach = stroke + shadow;
1652
+ return reach > 0 ? Math.ceil(reach) + 1 : 0;
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
+ }
1087
1658
  function hasVertexAlpha(mesh) {
1088
1659
  const cached = meshHasAlpha.get(mesh);
1089
1660
  if (cached !== void 0) return cached;
@@ -1177,6 +1748,8 @@ const audioTrackLatentCache = /* @__PURE__ */ new Map();
1177
1748
  const swarmStateMap = /* @__PURE__ */ new WeakMap();
1178
1749
  const deviceRenderer3DCache = /* @__PURE__ */ new WeakMap();
1179
1750
  /** One Renderer3D per device, target format and MSAA sample count. */
1751
+ /** The command encoder the 3D pools were last recycled for, per device. */
1752
+ const poolEncoder = /* @__PURE__ */ new WeakMap();
1180
1753
  function getRenderer3D(device, format, sampleCount) {
1181
1754
  let byConfig = deviceRenderer3DCache.get(device);
1182
1755
  if (!byConfig) {
@@ -1329,8 +1902,37 @@ function surfaceProps(target, lop, mat) {
1329
1902
  out.emissiveIntensity = target?.emissiveIntensity ?? lop.emissiveIntensity ?? mat?.emissiveStrength ?? 1;
1330
1903
  }
1331
1904
  if (lop.castShadow !== void 0) out.castShadow = lop.castShadow;
1905
+ out.toon = target?.toon ?? lop.toon;
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;
1332
1929
  return out;
1333
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
+ }
1334
1936
  function computeContainerScissor(matrix, width, height) {
1335
1937
  const corners = [
1336
1938
  matrix.transformPoint(new DOMPoint(0, 0)),
@@ -1642,6 +2244,7 @@ const CompositorWebGPURenderer = async (args) => {
1642
2244
  const inheritedSeekOffset = props.inheritedSeekOffset ?? 0;
1643
2245
  const shiftedFrame = Math.max(0, frame != null && fps != null ? frame + Math.round(inheritedSeekOffset * fps) : frame ?? 0);
1644
2246
  const compositionFrame = props.compositionFrame ?? shiftedFrame;
2247
+ const historyOnly = props.motionHistoryOnly === true;
1645
2248
  const compTex = ctx.renderer.getTemporaryTexture(nativeWidth, nativeHeight, [...props.excludeTextures || [], targetTexture]);
1646
2249
  const compView = compTex.createView();
1647
2250
  const composeDuration = virtualMedia?.metadata?.durationMs ?? DEFAULT_DURATION_MS;
@@ -1651,9 +2254,15 @@ const CompositorWebGPURenderer = async (args) => {
1651
2254
  durationSec
1652
2255
  });
1653
2256
  const { targetsById } = compiled;
2257
+ if (!historyOnly && frame != null && sceneMotionBlur(virtualMedia) && motionHistory(ctx.device, compiled.key).needsPrevious(shiftedFrame)) await renderMotionHistory(args, frame - 1);
1654
2258
  seekTimeline(compiled, shiftedFrame, fps ?? 24);
1655
- getRenderer3D(ctx.device, ctx.renderer.format ?? "rgba8unorm", sampleCount3d).resetPools();
1656
- getScreenSpaceRelightPipeline(ctx.device, ctx.renderer.format ?? "rgba8unorm").resetPools();
2259
+ const r3d = getRenderer3D(ctx.device, ctx.renderer.format ?? "rgba8unorm", sampleCount3d);
2260
+ const relightPipe = getScreenSpaceRelightPipeline(ctx.device, ctx.renderer.format ?? "rgba8unorm");
2261
+ if (poolEncoder.get(ctx.device) !== encoder) {
2262
+ poolEncoder.set(ctx.device, encoder);
2263
+ r3d.resetPools();
2264
+ relightPipe.resetPools();
2265
+ }
1657
2266
  const clearColor = parseColor(vop.backgroundColor);
1658
2267
  ctx.renderer.beginFrame(encoder, compView, clearColor, nativeWidth, nativeHeight, "clear").end();
1659
2268
  let currentCompTex = compTex;
@@ -1724,6 +2333,7 @@ const CompositorWebGPURenderer = async (args) => {
1724
2333
  const textStyle = {
1725
2334
  fontSize: node.fontSize ?? 48,
1726
2335
  fontWeight: node.fontWeight,
2336
+ fontVariations: node.fontVariations,
1727
2337
  fontFamily: node.fontFamily,
1728
2338
  letterSpacing: node.letterSpacing,
1729
2339
  lineHeight: node.lineHeight,
@@ -1787,6 +2397,11 @@ const CompositorWebGPURenderer = async (args) => {
1787
2397
  0
1788
2398
  ];
1789
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;
1790
2405
  const sceneLights = [];
1791
2406
  let sceneEnvironment = null;
1792
2407
  let sceneBloom = null;
@@ -1849,6 +2464,13 @@ const CompositorWebGPURenderer = async (args) => {
1849
2464
  camera.fStop = camStub?.fStop ?? camOp?.dof?.fStop ?? 2.8;
1850
2465
  camera.maxBlurRadius = camStub?.maxBlurRadius ?? camOp?.dof?.maxBlurRadius ?? 24;
1851
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.`);
1852
2474
  for (const lvm of lightVMs) {
1853
2475
  const lop = lvm.operation;
1854
2476
  const targetId = lop.id || lop.inputHandleId;
@@ -1863,8 +2485,23 @@ const CompositorWebGPURenderer = async (args) => {
1863
2485
  const lightType = target?.lightType ?? lop.lightType ?? "point";
1864
2486
  if (lightType === "environment") {
1865
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;
1866
2502
  sceneEnvironment = {
1867
2503
  preset: lop.preset,
2504
+ hdri: lop.hdri,
1868
2505
  top: lop.top,
1869
2506
  horizon: lop.horizon,
1870
2507
  bottom: lop.bottom,
@@ -1874,6 +2511,10 @@ const CompositorWebGPURenderer = async (args) => {
1874
2511
  toneMapping: lop.toneMapping,
1875
2512
  fog: lop.fog,
1876
2513
  floor: lop.floor,
2514
+ outlines: lop.outlines,
2515
+ ssr: lop.ssr,
2516
+ gi: lop.gi,
2517
+ ...volumetricFog && { volumetricFog },
1877
2518
  ...intensity$1 !== void 0 && { intensity: intensity$1 }
1878
2519
  };
1879
2520
  sceneBloom = lop.bloom === true ? {} : lop.bloom && typeof lop.bloom === "object" ? { ...lop.bloom } : null;
@@ -1891,6 +2532,10 @@ const CompositorWebGPURenderer = async (args) => {
1891
2532
  const angle = target?.angle ?? lop.angle ?? 45;
1892
2533
  const penumbra = target?.penumbra ?? lop.penumbra ?? .2;
1893
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;
1894
2539
  sceneLights.push({
1895
2540
  type: lightType,
1896
2541
  color,
@@ -1905,10 +2550,15 @@ const CompositorWebGPURenderer = async (args) => {
1905
2550
  targetY,
1906
2551
  targetZ
1907
2552
  ],
2553
+ ...Array.isArray(lop.direction) && { direction: lop.direction },
1908
2554
  radius,
1909
2555
  angle,
1910
2556
  penumbra,
1911
2557
  decay,
2558
+ ...width !== void 0 && { width },
2559
+ ...height !== void 0 && { height },
2560
+ twoSided,
2561
+ rotation,
1912
2562
  castShadow: Boolean(lop.castShadow),
1913
2563
  shadowSoftness: target?.shadowSoftness ?? lop.shadowSoftness,
1914
2564
  shadowDarkness: target?.shadowDarkness ?? lop.shadowDarkness
@@ -1942,16 +2592,79 @@ const CompositorWebGPURenderer = async (args) => {
1942
2592
  eye: reliefEye
1943
2593
  } };
1944
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
+ };
1945
2650
  const flush3DPass = (curTex, curView, curExcludes) => {
1946
2651
  if (pending3DItems.length === 0 || !camera) return {
1947
2652
  finalTex: curTex,
1948
2653
  finalView: curView
1949
2654
  };
1950
- const r3d = getRenderer3D(ctx.device, ctx.renderer.format ?? "rgba8unorm", sampleCount3d);
2655
+ const r3d$1 = getRenderer3D(ctx.device, ctx.renderer.format ?? "rgba8unorm", sampleCount3d);
1951
2656
  for (const item of pending3DItems) {
1952
2657
  if (item.kind !== "mesh") continue;
1953
- if (item.opts.morphWeights?.length) item.meshBuffers = r3d.morphMesh(encoder, item.mesh, item.opts.morphWeights, item.opts.wireframe);
1954
- if (item.opts.audioDeform) item.meshBuffers = r3d.deformMesh(encoder, item.mesh, item.opts.audioDeform, item.opts.wireframe, item.meshBuffers);
2658
+ if (item.opts.morphWeights?.length) item.meshBuffers = r3d$1.morphMesh(encoder, item.mesh, item.opts.morphWeights, item.opts.wireframe);
2659
+ if (item.opts.audioDeform) item.meshBuffers = r3d$1.deformMesh(encoder, item.mesh, item.opts.audioDeform, item.opts.wireframe, item.meshBuffers);
2660
+ }
2661
+ if (historyOnly) {
2662
+ if (history) recordMotion(history, camera);
2663
+ pending3DItems.length = 0;
2664
+ return {
2665
+ finalTex: curTex,
2666
+ finalView: curView
2667
+ };
1955
2668
  }
1956
2669
  const itemTextures = pendingTextures();
1957
2670
  const scene3dTex = ctx.renderer.getTemporaryTexture(nativeWidth, nativeHeight, [
@@ -1961,17 +2674,25 @@ const CompositorWebGPURenderer = async (args) => {
1961
2674
  ...itemTextures
1962
2675
  ]);
1963
2676
  const scene3dView = scene3dTex.createView();
2677
+ const outlineSetting = sceneEnvironment?.outlines;
2678
+ const outlines = outlineSetting === true ? {} : outlineSetting || null;
2679
+ const volumetricFogEnabled = Boolean(sceneEnvironment?.volumetricFog);
2680
+ const screenIndirectEnabled = Boolean(sceneEnvironment?.ssr || sceneEnvironment?.gi);
2681
+ const geometryEnabled = dofEnabled || Boolean(outlines) || volumetricFogEnabled || screenIndirectEnabled;
1964
2682
  let linearDepthTex;
1965
2683
  let linearDepthView;
1966
- if (dofEnabled) {
1967
- linearDepthTex = r3d.getOrCreateLinearDepthTexture(nativeWidth, nativeHeight);
2684
+ if (geometryEnabled) {
2685
+ linearDepthTex = r3d$1.getOrCreateLinearDepthTexture(nativeWidth, nativeHeight);
1968
2686
  linearDepthView = linearDepthTex.createView();
1969
2687
  }
1970
2688
  const shadow = [];
1971
2689
  const shadowLights = sceneLights.filter((light) => (light.lightType ?? light.type) !== "ambient").map((light, index) => ({
1972
2690
  light,
1973
2691
  index
1974
- })).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
+ });
1975
2696
  if (shadowLights.length > 0) {
1976
2697
  const casters = [];
1977
2698
  const receivers = [];
@@ -1982,7 +2703,7 @@ const CompositorWebGPURenderer = async (args) => {
1982
2703
  buffers: item.meshBuffers,
1983
2704
  opts: item.opts
1984
2705
  };
1985
- receivers.push(r3d.worldBounds(caster));
2706
+ receivers.push(r3d$1.worldBounds(caster));
1986
2707
  if (item.opts.castShadow !== false && item.opts.material !== "unlit") casters.push(caster);
1987
2708
  } else if (item.kind === "quad") {
1988
2709
  const caster = {
@@ -1990,16 +2711,26 @@ const CompositorWebGPURenderer = async (args) => {
1990
2711
  texture: item.texture,
1991
2712
  opts: item.opts
1992
2713
  };
1993
- receivers.push(r3d.worldBounds(caster));
2714
+ receivers.push(r3d$1.worldBounds(caster));
1994
2715
  if (item.opts.castShadow === true) casters.push(caster);
1995
2716
  }
1996
- if (casters.length > 0) for (const { light, index } of shadowLights) {
1997
- const result = r3d.renderShadowMap(encoder, light, index, casters, receivers, {
1998
- mapSize: shadowMapSize,
1999
- layer: shadow.length,
2000
- layerCount: shadowLights.length
2001
- });
2002
- 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
+ }
2003
2734
  }
2004
2735
  }
2005
2736
  const isGlass = (item) => item.kind === "mesh" && (item.opts.material === "glass" || item.opts.material === "acrylic" || item.opts.material === "frosted");
@@ -2009,7 +2740,7 @@ const CompositorWebGPURenderer = async (args) => {
2009
2740
  const forward = Vector3Math.subtract(camera.target, camera.eye);
2010
2741
  for (const item of pending3DItems) if (isGlass(item)) glass.push(item);
2011
2742
  else if (item.kind === "mesh" && item.opts.transparent) {
2012
- const bounds = r3d.worldBounds({
2743
+ const bounds = r3d$1.worldBounds({
2013
2744
  kind: "mesh",
2014
2745
  mesh: item.mesh,
2015
2746
  opts: item.opts
@@ -2043,7 +2774,7 @@ const CompositorWebGPURenderer = async (args) => {
2043
2774
  for (let j = i; j < end; j++) {
2044
2775
  const item = opaque[j];
2045
2776
  if (item.kind !== "mesh") throw new Error("Invalid mesh batch");
2046
- const matrix = r3d.meshModelMatrix(item.opts);
2777
+ const matrix = r3d$1.meshModelMatrix(item.opts);
2047
2778
  data.set(matrix, (j - i) * 32);
2048
2779
  data.set(item.opts.normalMatrix ?? Light3D.computeNormalMatrix(matrix), (j - i) * 32 + 16);
2049
2780
  }
@@ -2053,7 +2784,7 @@ const CompositorWebGPURenderer = async (args) => {
2053
2784
  ...first.opts,
2054
2785
  modelMatrix: Matrix4Math.identity(),
2055
2786
  normalMatrix: Matrix4Math.identity(),
2056
- instanceBuffer: r3d.uploadInstanceData(data),
2787
+ instanceBuffer: r3d$1.uploadInstanceData(data),
2057
2788
  instanceCount: end - i,
2058
2789
  matrixInstances: true
2059
2790
  }
@@ -2066,65 +2797,192 @@ const CompositorWebGPURenderer = async (args) => {
2066
2797
  ...transparent.map(({ item }) => item),
2067
2798
  ...glass
2068
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
+ }
2069
2804
  const floorSpec = sceneEnvironment?.floor;
2070
- let blurredReflectionTex = null;
2071
- 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;
2072
2809
  const reflectedCamera = {
2073
- ...camera,
2810
+ ...view,
2074
2811
  eye: [
2075
- camera.eye[0],
2076
- 2 * floorSpec.y - camera.eye[1],
2077
- camera.eye[2]
2812
+ view.eye[0],
2813
+ 2 * floorSpec.y - view.eye[1],
2814
+ view.eye[2]
2078
2815
  ],
2079
2816
  target: [
2080
- camera.target[0],
2081
- 2 * floorSpec.y - camera.target[1],
2082
- camera.target[2]
2817
+ view.target[0],
2818
+ 2 * floorSpec.y - view.target[1],
2819
+ view.target[2]
2083
2820
  ],
2084
2821
  up: [
2085
- camera.up[0],
2086
- camera.up[1],
2087
- camera.up[2]
2822
+ view.up[0],
2823
+ view.up[1],
2824
+ view.up[2]
2088
2825
  ]
2089
2826
  };
2090
- const { pass: reflPass, cameraBindGroup: reflCamBind, lightsBindGroup: reflLightsBind, reflectionTexture: reflTex } = r3d.beginReflectionPass(encoder, nativeWidth, nativeHeight, reflectedCamera, sceneLights, {
2827
+ const { pass: reflPass, cameraBindGroup: reflCamBind, lightsBindGroup: reflLightsBind, reflectionTexture: reflTex } = r3d$1.beginReflectionPass(encoder, width, height, reflectedCamera, sceneLights, {
2091
2828
  environment: sceneEnvironment,
2092
2829
  shadow: null
2093
2830
  });
2094
- for (const item of ordered) if (item.kind === "quad") r3d.drawTextureQuad(reflPass, reflCamBind, reflLightsBind, item.texture, item.opts, false);
2095
- else if (item.kind === "slug") r3d.drawSlugText(reflPass, reflCamBind, reflLightsBind, item.batches, item.opts, item.stackBase, false);
2096
- else if (item.kind === "mesh") r3d.drawMesh3D(reflPass, reflCamBind, reflLightsBind, item.meshBuffers ?? item.mesh, item.texture, item.opts, false);
2831
+ for (const item of ordered) if (item.kind === "quad") r3d$1.drawTextureQuad(reflPass, reflCamBind, reflLightsBind, item.texture, item.opts, false);
2832
+ else if (item.kind === "slug") r3d$1.drawSlugText(reflPass, reflCamBind, reflLightsBind, item.batches, item.opts, item.stackBase, false);
2833
+ else if (item.kind === "mesh") r3d$1.drawMesh3D(reflPass, reflCamBind, reflLightsBind, item.meshBuffers ?? item.mesh, item.texture, item.opts, false);
2097
2834
  reflPass.end();
2098
- blurredReflectionTex = r3d.blurReflection(encoder, reflTex, floorSpec.roughness ?? .35);
2099
- }
2100
- const { pass: pass3d, cameraBindGroup, cameraBuffer, lightsBindGroup } = r3d.beginPass(encoder, scene3dView, nativeWidth, nativeHeight, camera, "clear", {
2101
- r: 0,
2102
- g: 0,
2103
- b: 0,
2104
- a: 0
2105
- }, linearDepthView, sceneLights, {
2106
- environment: sceneEnvironment,
2107
- shadow: shadow.length ? shadow : null
2108
- }, Boolean(sceneBloom));
2109
- if (floorSpec && blurredReflectionTex) r3d.drawFloor(pass3d, cameraBindGroup, lightsBindGroup, blurredReflectionTex, floorSpec, dofEnabled, Boolean(sceneBloom));
2110
- for (const item of ordered) if (item.kind === "quad") r3d.drawTextureQuad(pass3d, cameraBindGroup, lightsBindGroup, item.texture, item.opts, dofEnabled);
2111
- else if (item.kind === "slug") r3d.drawSlugText(pass3d, cameraBindGroup, lightsBindGroup, item.batches, item.opts, item.stackBase, dofEnabled);
2112
- else if (item.kind === "mesh") r3d.drawMesh3D(pass3d, cameraBindGroup, lightsBindGroup, item.meshBuffers ?? item.mesh, item.texture, isGlass(item) ? {
2113
- ...item.opts,
2114
- backdrop: curTex
2115
- } : item.opts, dofEnabled);
2116
- 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);
2117
2869
  let final3dTex = scene3dTex;
2118
- if (dofEnabled && 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);
2941
+ const outlinedTex = ctx.renderer.getTemporaryTexture(nativeWidth, nativeHeight, [
2942
+ curTex,
2943
+ targetTexture,
2944
+ scene3dTex,
2945
+ linearDepthTex,
2946
+ ...curExcludes
2947
+ ]);
2948
+ r3d$1.applyOutlines(encoder, scene3dTex, linearDepthTex, outlinedTex.createView(), camera, outlines, sceneEnvironment?.fog);
2949
+ final3dTex = outlinedTex;
2950
+ }
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) {
2119
2964
  const dofResultTex = ctx.renderer.getTemporaryTexture(nativeWidth, nativeHeight, [
2120
2965
  curTex,
2121
2966
  targetTexture,
2122
2967
  scene3dTex,
2968
+ final3dTex,
2123
2969
  ...curExcludes
2124
2970
  ]);
2125
- r3d.applyDepthOfField(encoder, scene3dTex, linearDepthTex, dofResultTex.createView(), cameraBuffer);
2971
+ r3d$1.applyDepthOfField(encoder, final3dTex, linearDepthTex, dofResultTex.createView(), mainPass.cameraBuffer);
2126
2972
  final3dTex = dofResultTex;
2127
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);
2128
2986
  if (sceneBloom) {
2129
2987
  const bloomTex = ctx.renderer.getTemporaryTexture(nativeWidth, nativeHeight, [
2130
2988
  curTex,
@@ -2133,7 +2991,7 @@ const CompositorWebGPURenderer = async (args) => {
2133
2991
  scene3dTex,
2134
2992
  ...curExcludes
2135
2993
  ]);
2136
- r3d.applyBloom(encoder, final3dTex, bloomTex, sceneBloom, r3d.hdrSource());
2994
+ r3d$1.applyBloom(encoder, final3dTex, bloomTex, sceneBloom, r3d$1.hdrSource());
2137
2995
  final3dTex = bloomTex;
2138
2996
  }
2139
2997
  const nextTex = ctx.renderer.composite(encoder, curTex, final3dTex, "normal", [targetTexture, ...curExcludes]);
@@ -2185,15 +3043,17 @@ const CompositorWebGPURenderer = async (args) => {
2185
3043
  let activeExcludes = excludes;
2186
3044
  const mattesByOwner = /* @__PURE__ */ new Map();
2187
3045
  const reliefsByOwner = /* @__PURE__ */ new Map();
3046
+ const texturesByOwner = /* @__PURE__ */ new Map();
2188
3047
  for (const vm of vms) {
2189
3048
  const op$1 = vm.operation;
2190
3049
  if (op$1?.matteOf) mattesByOwner.set(op$1.matteOf, vm);
2191
3050
  if (op$1?.reliefOf) reliefsByOwner.set(op$1.reliefOf, vm);
3051
+ if (op$1?.textureOf) texturesByOwner.set(op$1.textureOf, vm);
2192
3052
  }
2193
3053
  for (const child of sorted) {
2194
3054
  const lop = child.operation;
2195
3055
  if (lop.kind === "camera" || lop.kind === "light") continue;
2196
- if (lop.matteOf || lop.reliefOf) continue;
3056
+ if (lop.matteOf || lop.reliefOf || lop.textureOf) continue;
2197
3057
  const targetId = lop.id || lop.inputHandleId;
2198
3058
  const target = targetId ? targetsById[targetId] : void 0;
2199
3059
  const isChild3D = Boolean(target?.is3D ?? lop.is3D) || Boolean(camera && ((target?.z ?? lop.z ?? 0) !== 0 || lop.is3D)) || Boolean(parent3DMatrix);
@@ -2252,10 +3112,37 @@ const CompositorWebGPURenderer = async (args) => {
2252
3112
  amount: lop.reliefAmount ?? 0
2253
3113
  });
2254
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
+ }
2255
3142
  const matteVM = lop.matteId ? mattesByOwner.get(lop.id) : void 0;
2256
3143
  if (matteVM) {
2257
3144
  const mode = matteVM.operation.matteMode ?? "alpha";
2258
- 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";
2259
3146
  if (isChild3D) {
2260
3147
  let coverage = await isolate(matteVM, nodeIndex);
2261
3148
  activeExcludes = [...activeExcludes, coverage];
@@ -2273,7 +3160,7 @@ const CompositorWebGPURenderer = async (args) => {
2273
3160
  b: 1,
2274
3161
  a: 1
2275
3162
  }, targetW, targetH, "clear").end();
2276
- coverage = ctx.renderer.composite(encoder, white, coverage, blend, [
3163
+ coverage = ctx.renderer.composite(encoder, white, coverage, blend$1, [
2277
3164
  targetTexture,
2278
3165
  activeTex,
2279
3166
  ...activeExcludes,
@@ -2285,6 +3172,7 @@ const CompositorWebGPURenderer = async (args) => {
2285
3172
  const before = pending3DItems.length;
2286
3173
  const res$1 = await renderLayerVM(child, nodeIndex, activeTex, activeView, activeExcludes, accumulatedOpacity, parentMatrix, parentRect, targetW, targetH, parent3DMatrix, isRootScene);
2287
3174
  for (const item of pending3DItems.slice(before)) item.opts.matte = coverage;
3175
+ wrapTexture?.();
2288
3176
  if (res$1) {
2289
3177
  if (res$1.finalTex !== activeTex) activeExcludes = [...activeExcludes, res$1.finalTex];
2290
3178
  activeTex = res$1.finalTex;
@@ -2296,7 +3184,7 @@ const CompositorWebGPURenderer = async (args) => {
2296
3184
  activeExcludes = [...activeExcludes, contentTex];
2297
3185
  nodeIndex += 1;
2298
3186
  const matteTex = await isolate(matteVM, nodeIndex);
2299
- const masked = ctx.renderer.composite(encoder, contentTex, matteTex, blend, [
3187
+ const masked = ctx.renderer.composite(encoder, contentTex, matteTex, blend$1, [
2300
3188
  targetTexture,
2301
3189
  activeTex,
2302
3190
  ...activeExcludes,
@@ -2313,6 +3201,7 @@ const CompositorWebGPURenderer = async (args) => {
2313
3201
  continue;
2314
3202
  }
2315
3203
  const res = await renderLayerVM(child, nodeIndex, activeTex, activeView, activeExcludes, accumulatedOpacity, parentMatrix, parentRect, targetW, targetH, parent3DMatrix, isRootScene);
3204
+ wrapTexture?.();
2316
3205
  if (res) {
2317
3206
  if (res.finalTex !== activeTex) activeExcludes = [...activeExcludes, res.finalTex];
2318
3207
  activeTex = res.finalTex;
@@ -2345,8 +3234,10 @@ const CompositorWebGPURenderer = async (args) => {
2345
3234
  if (shiftedFrame < startFrame) return;
2346
3235
  if (durationFrames !== void 0 && shiftedFrame >= startFrame + durationFrames) return;
2347
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;
2348
3239
  if (kind === "model3d") {
2349
- const r3d = getRenderer3D(ctx.device, ctx.renderer.format ?? "rgba8unorm", sampleCount3d);
3240
+ const r3d$1 = getRenderer3D(ctx.device, ctx.renderer.format ?? "rgba8unorm", sampleCount3d);
2350
3241
  let modelSrc = lop.modelData ?? lop.src;
2351
3242
  if (!modelSrc && lop.text3dOptions) {
2352
3243
  const tOpts = lop.text3dOptions;
@@ -2366,13 +3257,14 @@ const CompositorWebGPURenderer = async (args) => {
2366
3257
  if (!modelSrc) return;
2367
3258
  let model;
2368
3259
  try {
2369
- model = typeof modelSrc === "object" && modelSrc !== null && "meshes" in modelSrc ? modelSrc : await r3d.loadModelAsync(modelSrc, lop.modelFormat ?? "auto", {
3260
+ model = typeof modelSrc === "object" && modelSrc !== null && "meshes" in modelSrc ? modelSrc : await r3d$1.loadModelAsync(modelSrc, lop.modelFormat ?? "auto", {
2370
3261
  center: lop.center,
2371
3262
  normalizeSize: lop.normalizeSize
2372
3263
  });
2373
3264
  } catch (err) {
2374
3265
  throw new Error(`Failed to load 3D model ${lop.id ?? "(unnamed)"}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
2375
3266
  }
3267
+ if (model && lop.fracture3d) model = fracturedModel(model, lop.fracture3d);
2376
3268
  if (!model || model.meshes.length === 0) return;
2377
3269
  const baseX$1 = lop.x ?? 0;
2378
3270
  const baseY$1 = lop.y ?? 0;
@@ -2428,6 +3320,7 @@ const CompositorWebGPURenderer = async (args) => {
2428
3320
  const instOpt = lop.instances3d;
2429
3321
  const rawMeshes = Array.isArray(instOpt.mesh) ? instOpt.mesh : instOpt.mesh ? [instOpt.mesh] : lop.modelData ? [lop.modelData] : lop.primitive3d ? [getPrimitiveGeometry(lop.primitive3d)] : [];
2430
3322
  const variants = [];
3323
+ const variantPres = [];
2431
3324
  for (const m of rawMeshes) {
2432
3325
  let vModel;
2433
3326
  if (typeof m === "object" && m !== null) {
@@ -2435,25 +3328,56 @@ const CompositorWebGPURenderer = async (args) => {
2435
3328
  else if (m.modelData && "meshes" in m.modelData) vModel = m.modelData;
2436
3329
  else if (m.primitive3d) vModel = getPrimitiveGeometry(m.primitive3d);
2437
3330
  else if (m.src) try {
2438
- vModel = r3d.loadModel(m.src, m.modelFormat ?? "auto", {
3331
+ vModel = r3d$1.loadModel(m.src, m.modelFormat ?? "auto", {
2439
3332
  center: m.center,
2440
3333
  normalizeSize: m.normalizeSize
2441
3334
  });
2442
3335
  } catch {}
2443
3336
  }
2444
- 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;
2445
3358
  }
2446
- if (variants.length === 0 && model) variants.push(model);
2447
3359
  if (variants.length > 0) {
2448
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);
2449
3361
  if (count > 0) {
2450
3362
  const numVariants = variants.length;
2451
3363
  const bucketCounts = new Uint32Array(numVariants);
2452
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
+ };
2453
3377
  const bucketItems = [];
2454
3378
  const bucketBounds = [];
2455
3379
  for (let k = 0; k < numVariants; k++) {
2456
- bucketItems.push(new Float32Array(bucketCounts[k] * 16));
3380
+ bucketItems.push(new Float32Array(bucketCounts[k] * strides[k]));
2457
3381
  bucketBounds.push({
2458
3382
  min: [
2459
3383
  Infinity,
@@ -2482,7 +3406,8 @@ const CompositorWebGPURenderer = async (args) => {
2482
3406
  const cursor = bucketCursors[k];
2483
3407
  const bData = bucketItems[k];
2484
3408
  const bBox = bucketBounds[k];
2485
- const off = cursor * 16;
3409
+ const off = cursor * strides[k];
3410
+ let ianim = {};
2486
3411
  let ix = 0;
2487
3412
  let iy = 0;
2488
3413
  let iz = 0;
@@ -2539,6 +3464,7 @@ const CompositorWebGPURenderer = async (args) => {
2539
3464
  } else if (typeof instOpt.instanceAt === "function") {
2540
3465
  const t = instOpt.instanceAt(i$1, shiftedFrame ?? 0, fps || 30);
2541
3466
  if (!t) continue;
3467
+ ianim = t;
2542
3468
  ix = t.x ?? 0;
2543
3469
  iy = t.y ?? 0;
2544
3470
  iz = t.z ?? 0;
@@ -2585,6 +3511,7 @@ const CompositorWebGPURenderer = async (args) => {
2585
3511
  }
2586
3512
  } else if (Array.isArray(instOpt.transforms) && instOpt.transforms[i$1]) {
2587
3513
  const t = instOpt.transforms[i$1];
3514
+ ianim = t;
2588
3515
  ix = t.x ?? 0;
2589
3516
  iy = t.y ?? 0;
2590
3517
  iz = t.z ?? 0;
@@ -2631,6 +3558,16 @@ const CompositorWebGPURenderer = async (args) => {
2631
3558
  }
2632
3559
  } else continue;
2633
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
+ }
2634
3571
  bData[off + 0] = ix;
2635
3572
  bData[off + 1] = iy;
2636
3573
  bData[off + 2] = iz;
@@ -2647,7 +3584,10 @@ const CompositorWebGPURenderer = async (args) => {
2647
3584
  bData[off + 13] = ieg;
2648
3585
  bData[off + 14] = ieb;
2649
3586
  bData[off + 15] = iew;
2650
- 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;
2651
3591
  bBox.min[0] = Math.min(bBox.min[0], ix - rad);
2652
3592
  bBox.min[1] = Math.min(bBox.min[1], iy - rad);
2653
3593
  bBox.min[2] = Math.min(bBox.min[2], iz - rad);
@@ -2659,15 +3599,26 @@ const CompositorWebGPURenderer = async (args) => {
2659
3599
  const bCount = bucketCursors[k];
2660
3600
  if (bCount === 0) continue;
2661
3601
  const v = variants[k];
2662
- const activeSlice = bCount === bucketCounts[k] ? bucketItems[k] : bucketItems[k].subarray(0, bCount * 16);
2663
- const instanceBuffer = r3d.uploadInstanceData(activeSlice);
3602
+ const activeSlice = bCount === bucketCounts[k] ? bucketItems[k] : bucketItems[k].subarray(0, bCount * strides[k]);
3603
+ const instanceBuffer = r3d$1.uploadInstanceData(activeSlice);
2664
3604
  const bounds = Number.isFinite(bucketBounds[k].min[0]) ? bucketBounds[k] : void 0;
2665
3605
  for (const mesh of v.meshes) {
2666
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
+ ];
2667
3613
  const meshOpts = {
2668
3614
  modelMatrix: finalModelMatrix,
2669
3615
  opacity: modelOpacity * (mat?.opacity ?? 1),
2670
- 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
+ ],
2671
3622
  transparent: false,
2672
3623
  twoSided: target?.twoSided ?? lop.twoSided ?? true,
2673
3624
  wireframe: Boolean(target?.wireframe ?? lop.wireframe),
@@ -2679,12 +3630,15 @@ const CompositorWebGPURenderer = async (args) => {
2679
3630
  ...surfaceProps(target, lop, mat),
2680
3631
  instanceBuffer,
2681
3632
  instanceCount: bCount,
2682
- bounds
3633
+ bounds,
3634
+ wind: meshWind(lop.wind, v, (shiftedFrame ?? 0) / (fps || 30)),
3635
+ crowd: crowdOpts(crowdBakes[k], mesh)
2683
3636
  };
2684
3637
  pending3DItems.push({
3638
+ owner: lop.id,
2685
3639
  kind: "mesh",
2686
3640
  mesh,
2687
- texture: mat ? await r3d.materialTexture(mat) : void 0,
3641
+ texture: mat ? await r3d$1.materialTexture(mat) : void 0,
2688
3642
  opts: meshOpts
2689
3643
  });
2690
3644
  }
@@ -2800,7 +3754,7 @@ const CompositorWebGPURenderer = async (args) => {
2800
3754
  else if (m.modelData && "meshes" in m.modelData) vModel = m.modelData;
2801
3755
  else if (m.primitive3d) vModel = getPrimitiveGeometry(m.primitive3d);
2802
3756
  else if (m.src) try {
2803
- vModel = r3d.loadModel(m.src, m.modelFormat ?? "auto", {
3757
+ vModel = r3d$1.loadModel(m.src, m.modelFormat ?? "auto", {
2804
3758
  center: m.center,
2805
3759
  normalizeSize: m.normalizeSize
2806
3760
  });
@@ -2905,7 +3859,7 @@ const CompositorWebGPURenderer = async (args) => {
2905
3859
  };
2906
3860
  let swarmState = swarmStateMap.get(lop);
2907
3861
  if (!swarmState || swarmState.device !== ctx.device) {
2908
- swarmState = r3d.swarm3dPipeline.createState(staticConfig);
3862
+ swarmState = r3d$1.swarm3dPipeline.createState(staticConfig);
2909
3863
  swarmStateMap.set(lop, swarmState);
2910
3864
  }
2911
3865
  const targetF = shiftedFrame ?? 0;
@@ -2995,7 +3949,7 @@ const CompositorWebGPURenderer = async (args) => {
2995
3949
  emitterPos
2996
3950
  };
2997
3951
  };
2998
- r3d.swarm3dPipeline.advance(swarmState, staticConfig, targetF, fps || 30, frameSampler);
3952
+ r3d$1.swarm3dPipeline.advance(swarmState, staticConfig, targetF, fps || 30, frameSampler);
2999
3953
  if (variants.length > 0) {
3000
3954
  const sliceCount = swarmState.sliceCount;
3001
3955
  for (let vIdx = 0; vIdx < variants.length; vIdx++) {
@@ -3021,9 +3975,10 @@ const CompositorWebGPURenderer = async (args) => {
3021
3975
  firstInstance
3022
3976
  };
3023
3977
  pending3DItems.push({
3978
+ owner: lop.id,
3024
3979
  kind: "mesh",
3025
3980
  mesh,
3026
- texture: mat ? await r3d.materialTexture(mat) : void 0,
3981
+ texture: mat ? await r3d$1.materialTexture(mat) : void 0,
3027
3982
  opts: meshOpts
3028
3983
  });
3029
3984
  }
@@ -3051,9 +4006,10 @@ const CompositorWebGPURenderer = async (args) => {
3051
4006
  firstInstance: sparkFirst
3052
4007
  };
3053
4008
  pending3DItems.push({
4009
+ owner: lop.id,
3054
4010
  kind: "mesh",
3055
4011
  mesh,
3056
- texture: mat ? await r3d.materialTexture(mat) : void 0,
4012
+ texture: mat ? await r3d$1.materialTexture(mat) : void 0,
3057
4013
  opts: sparkOpts
3058
4014
  });
3059
4015
  }
@@ -3075,7 +4031,22 @@ const CompositorWebGPURenderer = async (args) => {
3075
4031
  if (lop.loop ?? true) clipSec %= clip.duration;
3076
4032
  }
3077
4033
  const sceneRoot = Matrix4Math.multiply(finalModelMatrix, model.rootTransform ?? Matrix4Math.identity());
3078
- 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);
3079
4050
  const skinCache = /* @__PURE__ */ new Map();
3080
4051
  const placeMesh = (mesh) => {
3081
4052
  if (!posed) return { matrix: sceneRoot };
@@ -3136,6 +4107,7 @@ const CompositorWebGPURenderer = async (args) => {
3136
4107
  }
3137
4108
  }
3138
4109
  for (const mesh of model.meshes) {
4110
+ if (wholeFracture && mesh.materialName === FRACTURE_INTERIOR_MATERIAL) continue;
3139
4111
  const mat = mesh.materialName ? model.materials[mesh.materialName] : void 0;
3140
4112
  const placed = placeMesh(mesh);
3141
4113
  const meshSkin = placed.joints ?? skinningMatrices;
@@ -3238,7 +4210,10 @@ const CompositorWebGPURenderer = async (args) => {
3238
4210
  }
3239
4211
  const meshOpts = {
3240
4212
  modelMatrix: placed.matrix,
3241
- 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
+ }),
3242
4217
  opacity: modelOpacity * (mat?.opacity ?? 1),
3243
4218
  colorTint: meshColorTint,
3244
4219
  transparent: mat?.alphaMode === "blend" || modelOpacity * (mat?.opacity ?? 1) < 1 || meshColorTint[3] < 1 || hasVertexAlpha(mesh),
@@ -3257,16 +4232,19 @@ const CompositorWebGPURenderer = async (args) => {
3257
4232
  ambientIntensity: target?.ambientIntensity ?? lop.ambientIntensity ?? 1,
3258
4233
  metallic: target?.metallic ?? lop.metallic ?? mat?.metallic,
3259
4234
  ...surfaceProps(target, lop, mat),
3260
- ior: target?.ior ?? lop.ior,
4235
+ ior: target?.ior ?? lop.ior ?? mat?.ior,
3261
4236
  dispersion: target?.dispersion ?? lop.dispersion,
3262
4237
  thickness: target?.thickness ?? lop.thickness,
3263
- 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))
3264
4241
  };
3265
- if (mat) meshOpts.maps = await r3d.materialMaps(mat);
4242
+ if (mat) meshOpts.maps = await r3d$1.materialMaps(mat);
3266
4243
  pending3DItems.push({
4244
+ owner: lop.id,
3267
4245
  kind: "mesh",
3268
4246
  mesh,
3269
- texture: mat ? await r3d.materialTexture(mat) : void 0,
4247
+ texture: mat ? await r3d$1.materialTexture(mat) : void 0,
3270
4248
  opts: meshOpts
3271
4249
  });
3272
4250
  }
@@ -3326,14 +4304,17 @@ const CompositorWebGPURenderer = async (args) => {
3326
4304
  padX = Math.max(80, Math.ceil(rect.width * .2));
3327
4305
  padY = Math.max(50, Math.ceil(rect.height * .5));
3328
4306
  }
4307
+ if (kind === "text" && !isPathText) {
4308
+ const ink = textInkOutset(target, lop);
4309
+ padX = Math.max(padX, ink);
4310
+ padY = Math.max(padY, ink);
4311
+ }
3329
4312
  const layerW = isPathText ? targetW : rect.width + padX * 2;
3330
4313
  const layerH = isPathText ? targetH : rect.height + padY * 2;
3331
- if (!isPathText && hasKineticAnimators) {
3332
- localX -= padX;
3333
- localY -= padY;
3334
- }
3335
- const anchorX = lop.anchorX ?? .5;
3336
- const anchorY = lop.anchorY ?? .5;
4314
+ localX -= padX;
4315
+ localY -= padY;
4316
+ const anchorX = isPathText ? (lop.anchorX ?? .5) * rect.width / layerW : (padX + (lop.anchorX ?? .5) * rect.width) / layerW;
4317
+ const anchorY = isPathText ? (lop.anchorY ?? .5) * rect.height / layerH : (padY + (lop.anchorY ?? .5) * rect.height) / layerH;
3337
4318
  const localMatrix = toDOMMatrix(buildLayerMatrix({
3338
4319
  x: localX,
3339
4320
  y: localY,
@@ -3343,8 +4324,8 @@ const CompositorWebGPURenderer = async (args) => {
3343
4324
  scale: scale$1,
3344
4325
  scaleX,
3345
4326
  scaleY,
3346
- anchorX: isPathText ? anchorX * rect.width / layerW : anchorX,
3347
- anchorY: isPathText ? anchorY * rect.height / layerH : anchorY
4327
+ anchorX,
4328
+ anchorY
3348
4329
  }));
3349
4330
  const layerMatrix = parentMatrix.multiply(localMatrix);
3350
4331
  const blendMode = lop.blendMode || "normal";
@@ -3356,8 +4337,8 @@ const CompositorWebGPURenderer = async (args) => {
3356
4337
  const rotateZ = target?.rotateZ ?? lop.rotateZ ?? 0;
3357
4338
  const translateZ = target?.translateZ ?? lop.translateZ ?? 0;
3358
4339
  const perspective = target?.perspective ?? lop.perspective ?? 0;
3359
- const perspectiveOriginX = target?.perspectiveOriginX ?? lop.perspectiveOriginX ?? lop.anchorX ?? .5;
3360
- const perspectiveOriginY = target?.perspectiveOriginY ?? lop.perspectiveOriginY ?? lop.anchorY ?? .5;
4340
+ const perspectiveOriginX = target?.perspectiveOriginX ?? lop.perspectiveOriginX ?? anchorX;
4341
+ const perspectiveOriginY = target?.perspectiveOriginY ?? lop.perspectiveOriginY ?? anchorY;
3361
4342
  const backfaceVisibility = target?.backfaceVisibility ?? lop.backfaceVisibility ?? "visible";
3362
4343
  let quad3D = null;
3363
4344
  if (is3D) {
@@ -3386,7 +4367,6 @@ const CompositorWebGPURenderer = async (args) => {
3386
4367
  if (backfaceVisibility === "hidden" && Transform3DMath.computeSignedArea(q) < 0) return;
3387
4368
  quad3D = q;
3388
4369
  }
3389
- const isTrue3D = Boolean(target?.is3D ?? lop.is3D) || Boolean(camera && ((target?.z ?? lop.z ?? 0) !== 0 || lop.is3D));
3390
4370
  if (isContainer) {
3391
4371
  if (lop.transformStyle === "preserve-3d" && isTrue3D) {
3392
4372
  let current3DMatrix = Matrix4Math.identity();
@@ -3471,30 +4451,6 @@ const CompositorWebGPURenderer = async (args) => {
3471
4451
  }, { opacity: 1 });
3472
4452
  borderPass.end();
3473
4453
  }
3474
- let modelMatrix;
3475
- if (parent3DMatrix) {
3476
- let localM = Matrix4Math.identity();
3477
- const childX = target?.x ?? lop.x ?? 0;
3478
- const childY = target?.y ?? lop.y ?? 0;
3479
- const childZ = target?.z ?? lop.z ?? 0;
3480
- localM = Matrix4Math.multiply(localM, Matrix4Math.translate(childX, childY, childZ));
3481
- const rxRad = (target?.rotateX ?? lop.rotateX ?? 0) * Math.PI / 180;
3482
- const ryRad = (target?.rotateY ?? lop.rotateY ?? 0) * Math.PI / 180;
3483
- const rzRad = ((target?.rotateZ ?? lop.rotateZ ?? 0) !== 0 ? target?.rotateZ ?? lop.rotateZ ?? 0 : rotation) * (Math.PI / 180);
3484
- if (rzRad !== 0) localM = Matrix4Math.multiply(localM, Matrix4Math.rotateZ(rzRad));
3485
- if (ryRad !== 0) localM = Matrix4Math.multiply(localM, Matrix4Math.rotateY(ryRad));
3486
- if (rxRad !== 0) localM = Matrix4Math.multiply(localM, Matrix4Math.rotateX(rxRad));
3487
- const sx = layerW * scale$1 * scaleX;
3488
- const sy = layerH * scale$1 * scaleY;
3489
- const sz = target?.scaleZ ?? lop.scaleZ ?? 1;
3490
- localM = Matrix4Math.multiply(localM, Matrix4Math.scale(sx, sy, sz));
3491
- const ax = lop.anchorX ?? .5;
3492
- const ay = lop.anchorY ?? .5;
3493
- const pivotX = .5 - ax;
3494
- const pivotY = .5 - ay;
3495
- if (pivotX !== 0 || pivotY !== 0) localM = Matrix4Math.multiply(localM, Matrix4Math.translate(pivotX, pivotY, 0));
3496
- modelMatrix = Matrix4Math.multiply(parent3DMatrix, localM);
3497
- }
3498
4454
  const quadOpts = {
3499
4455
  position: [
3500
4456
  localX + layerW * .5,
@@ -3527,10 +4483,21 @@ const CompositorWebGPURenderer = async (args) => {
3527
4483
  ambientIntensity: target?.ambientIntensity ?? lop.ambientIntensity,
3528
4484
  metallic: target?.metallic ?? lop.metallic,
3529
4485
  ...surfaceProps(target, lop),
3530
- ...modelMatrix && { modelMatrix },
3531
4486
  ...reliefOpts(lop.id, localX, localY, layerW, layerH)
3532
4487
  };
4488
+ if (parent3DMatrix) {
4489
+ const r3d$1 = getRenderer3D(ctx.device, ctx.renderer.format ?? "rgba8unorm", sampleCount3d);
4490
+ quadOpts.modelMatrix = Matrix4Math.multiply(parent3DMatrix, r3d$1.quadModelMatrix({
4491
+ ...quadOpts,
4492
+ position: [
4493
+ target?.x ?? lop.x ?? 0,
4494
+ target?.y ?? lop.y ?? 0,
4495
+ target?.z ?? lop.z ?? 0
4496
+ ]
4497
+ }));
4498
+ }
3533
4499
  pending3DItems.push({
4500
+ owner: lop.id,
3534
4501
  kind: "quad",
3535
4502
  texture: activeGroupTex$1,
3536
4503
  opts: quadOpts
@@ -3639,7 +4606,7 @@ const CompositorWebGPURenderer = async (args) => {
3639
4606
  finalView: activeView
3640
4607
  };
3641
4608
  }
3642
- const shouldClip = lop.overflow !== "visible";
4609
+ const shouldClip = (target?.overflow ?? lop.overflow) === "hidden";
3643
4610
  const scissorRect = shouldClip ? computeContainerScissor(layerMatrix, layerW, layerH) : null;
3644
4611
  const clipRadius = shouldClip ? Math.min(target?.borderRadius ?? lop.borderRadius ?? 0, layerW / 2, layerH / 2) : 0;
3645
4612
  if ((kind === "flex" || kind === "block") && (!Array.isArray(lop.effects) || lop.effects.length === 0) && clipRadius <= 0 && !shouldPaintContainerBackground(kind, lop.background) && layerOpacity === 1 && blendMode === "normal" && !lop.borderWidth && !lop.borderColor && (typeof lop.relighting !== "object" || !lop.relighting) && (typeof lop.relight !== "object" || !lop.relight) && (typeof target?.relighting !== "object" || !target?.relighting) && (typeof target?.relight !== "object" || !target?.relight) && (typeof lop.deflicker !== "object" || !lop.deflicker) && (typeof target?.deflicker !== "object" || !target?.deflicker)) {
@@ -4034,6 +5001,7 @@ const CompositorWebGPURenderer = async (args) => {
4034
5001
  fontFamily: lop.fontFamily,
4035
5002
  fontStyle: lop.fontStyle,
4036
5003
  fontWeight: lop.fontWeight,
5004
+ fontVariations: lop.fontVariations,
4037
5005
  fill: target?.fill ?? target?.color ?? lop.fill,
4038
5006
  align: lop.align,
4039
5007
  direction: lop.direction,
@@ -4284,8 +5252,9 @@ const CompositorWebGPURenderer = async (args) => {
4284
5252
  if (Math.hypot(vx, vy) > .5) {
4285
5253
  if (!isTrue3D && !(is3D && quad3D)) {
4286
5254
  const reach = shutterAngle / 360;
4287
- const padX$1 = Math.ceil(Math.abs(vx) * rasterScale * reach);
4288
- 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)));
4289
5258
  blurPadX = padX$1 / rasterScale;
4290
5259
  blurPadY = padY$1 / rasterScale;
4291
5260
  const paddedTex = ctx.renderer.getTemporaryTexture(texW + padX$1 * 2, texH + padY$1 * 2, [
@@ -4350,30 +5319,6 @@ const CompositorWebGPURenderer = async (args) => {
4350
5319
  }, { opacity: 1 });
4351
5320
  borderPass.end();
4352
5321
  }
4353
- let modelMatrix;
4354
- if (parent3DMatrix) {
4355
- let localM = Matrix4Math.identity();
4356
- const childX = target?.x ?? lop.x ?? 0;
4357
- const childY = target?.y ?? lop.y ?? 0;
4358
- const childZ = target?.z ?? lop.z ?? 0;
4359
- localM = Matrix4Math.multiply(localM, Matrix4Math.translate(childX, childY, childZ));
4360
- const rxRad = (target?.rotateX ?? lop.rotateX ?? 0) * Math.PI / 180;
4361
- const ryRad = (target?.rotateY ?? lop.rotateY ?? 0) * Math.PI / 180;
4362
- const rzRad = ((target?.rotateZ ?? lop.rotateZ ?? 0) !== 0 ? target?.rotateZ ?? lop.rotateZ ?? 0 : rotation) * (Math.PI / 180);
4363
- if (rzRad !== 0) localM = Matrix4Math.multiply(localM, Matrix4Math.rotateZ(rzRad));
4364
- if (ryRad !== 0) localM = Matrix4Math.multiply(localM, Matrix4Math.rotateY(ryRad));
4365
- if (rxRad !== 0) localM = Matrix4Math.multiply(localM, Matrix4Math.rotateX(rxRad));
4366
- const sx = layerW * scale$1 * scaleX;
4367
- const sy = layerH * scale$1 * scaleY;
4368
- const sz = target?.scaleZ ?? lop.scaleZ ?? 1;
4369
- localM = Matrix4Math.multiply(localM, Matrix4Math.scale(sx, sy, sz));
4370
- const ax = lop.anchorX ?? .5;
4371
- const ay = lop.anchorY ?? .5;
4372
- const pivotX = .5 - ax;
4373
- const pivotY = .5 - ay;
4374
- if (pivotX !== 0 || pivotY !== 0) localM = Matrix4Math.multiply(localM, Matrix4Math.translate(pivotX, pivotY, 0));
4375
- modelMatrix = Matrix4Math.multiply(parent3DMatrix, localM);
4376
- }
4377
5322
  const quadOpts = {
4378
5323
  position: [
4379
5324
  localX + layerW * .5,
@@ -4406,16 +5351,28 @@ const CompositorWebGPURenderer = async (args) => {
4406
5351
  ambientIntensity: target?.ambientIntensity ?? lop.ambientIntensity,
4407
5352
  metallic: target?.metallic ?? lop.metallic,
4408
5353
  ...surfaceProps(target, lop),
4409
- ...modelMatrix && { modelMatrix },
4410
5354
  ...reliefOpts(lop.id, localX, localY, layerW, layerH)
4411
5355
  };
5356
+ if (parent3DMatrix) {
5357
+ const r3d$1 = getRenderer3D(ctx.device, ctx.renderer.format ?? "rgba8unorm", sampleCount3d);
5358
+ quadOpts.modelMatrix = Matrix4Math.multiply(parent3DMatrix, r3d$1.quadModelMatrix({
5359
+ ...quadOpts,
5360
+ position: [
5361
+ target?.x ?? lop.x ?? 0,
5362
+ target?.y ?? lop.y ?? 0,
5363
+ target?.z ?? lop.z ?? 0
5364
+ ]
5365
+ }));
5366
+ }
4412
5367
  const quadHasContent = layerHasRaster || Boolean(quadOpts.borderWidth && quadOpts.borderWidth > 0 && quadOpts.borderColor);
4413
5368
  if (!glyphBatches || quadHasContent) pending3DItems.push({
5369
+ owner: lop.id,
4414
5370
  kind: "quad",
4415
5371
  texture: renderedTex,
4416
5372
  opts: quadOpts
4417
5373
  });
4418
5374
  if (glyphBatches && glyphBatches.length > 0) pending3DItems.push({
5375
+ owner: lop.id,
4419
5376
  kind: "slug",
4420
5377
  batches: glyphBatches,
4421
5378
  opts: quadOpts,
@@ -4542,6 +5499,7 @@ const CompositorWebGPURenderer = async (args) => {
4542
5499
  ...props.excludeTextures || []
4543
5500
  ];
4544
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);
4545
5503
  ctx.renderer.popTransform();
4546
5504
  ctx.renderer.popScissor();
4547
5505
  const finalPass = ctx.renderer.beginFrame(encoder, targetView, {
@@ -4559,6 +5517,43 @@ const CompositorWebGPURenderer = async (args) => {
4559
5517
  args.pass = finalPass;
4560
5518
  }
4561
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
+ }
4562
5557
  /** Pixels of room around a border texture for its anti-aliased edge. */
4563
5558
  const BORDER_AA_MARGIN = 2;
4564
5559
  /**
@@ -4732,4 +5727,4 @@ var renderers_default = defineRenderer({ WebGPURenderer: CompositorWebGPURendere
4732
5727
 
4733
5728
  //#endregion
4734
5729
  export { Transform3DMath, renderers_default as default, has3DTransform };
4735
- //# sourceMappingURL=renderer-Dv_iRIyx.mjs.map
5730
+ //# sourceMappingURL=renderer-4nXkaAWu.mjs.map