framefields 2.8.0 → 2.9.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 (460) hide show
  1. package/README.md +30 -1
  2. package/dist/audio/index.d.mts +4 -4
  3. package/dist/audio/index.mjs +10 -10
  4. package/dist/{audio-CGVJlZb9.mjs → audio-BfQg37MW.mjs} +3 -3
  5. package/dist/{audio-CGVJlZb9.mjs.map → audio-BfQg37MW.mjs.map} +1 -1
  6. package/dist/{audio-CxAUxVyA.d.mts → audio-Ccpl9mjV.d.mts} +3 -3
  7. package/dist/{audio-CxAUxVyA.d.mts.map → audio-Ccpl9mjV.d.mts.map} +1 -1
  8. package/dist/cli/check-worker.mjs +44 -44
  9. package/dist/components/index.d.mts +10 -10
  10. package/dist/components/index.mjs +43 -43
  11. package/dist/{dist-CnFcSau0.mjs → dist-C1cCW7Bt.mjs} +2297 -213
  12. package/dist/dist-C1cCW7Bt.mjs.map +1 -0
  13. package/dist/{dist-C5eZw8ms.mjs → dist-C8Xq3zgx.mjs} +2 -2
  14. package/dist/{dist-C5eZw8ms.mjs.map → dist-C8Xq3zgx.mjs.map} +1 -1
  15. package/dist/effects/index.d.mts +4 -4
  16. package/dist/effects/index.mjs +35 -35
  17. package/dist/{effects-DdvkiI3u.mjs → effects-CemgqEPv.mjs} +2 -2
  18. package/dist/{effects-DdvkiI3u.mjs.map → effects-CemgqEPv.mjs.map} +1 -1
  19. package/dist/fonts/index.mjs +2 -2
  20. package/dist/{fonts-k_89s1HO.mjs → fonts-BW0pPcvJ.mjs} +2 -2
  21. package/dist/{fonts-k_89s1HO.mjs.map → fonts-BW0pPcvJ.mjs.map} +1 -1
  22. package/dist/{gpu-BoyykRdD.mjs → gpu-CMaa63nz.mjs} +2 -2
  23. package/dist/{gpu-BoyykRdD.mjs.map → gpu-CMaa63nz.mjs.map} +1 -1
  24. package/dist/{index-CPFKis94.d.mts → index-7Z9_a_iB.d.mts} +3 -3
  25. package/dist/{index-CPFKis94.d.mts.map → index-7Z9_a_iB.d.mts.map} +1 -1
  26. package/dist/{index-CFKMZDv1.d.mts → index-BIjMAW1h.d.mts} +2 -2
  27. package/dist/{index-CFKMZDv1.d.mts.map → index-BIjMAW1h.d.mts.map} +1 -1
  28. package/dist/{index-BukqOGDa.d.mts → index-CFoAmh7q.d.mts} +3 -3
  29. package/dist/{index-BukqOGDa.d.mts.map → index-CFoAmh7q.d.mts.map} +1 -1
  30. package/dist/{index-CPeaSZoz.d.mts → index-CJL0JAU8.d.mts} +2 -2
  31. package/dist/{index-CPeaSZoz.d.mts.map → index-CJL0JAU8.d.mts.map} +1 -1
  32. package/dist/{index-Ds9_v60R.d.mts → index-CZjrnylh.d.mts} +544 -336
  33. package/dist/index-CZjrnylh.d.mts.map +1 -0
  34. package/dist/{index-Ced1Ry4S.d.mts → index-D5UDyVMb.d.mts} +211 -9
  35. package/dist/index-D5UDyVMb.d.mts.map +1 -0
  36. package/dist/{index-B_nooAhS.d.mts → index-Du5YgdzM.d.mts} +53 -15
  37. package/dist/index-Du5YgdzM.d.mts.map +1 -0
  38. package/dist/index.d.mts +11 -11
  39. package/dist/index.mjs +50 -50
  40. package/dist/preview/index.d.mts +3 -3
  41. package/dist/preview/index.mjs +10 -10
  42. package/dist/{preview-DHqCrFhG.mjs → preview-BQVgn6Ho.mjs} +2 -2
  43. package/dist/{preview-DHqCrFhG.mjs.map → preview-BQVgn6Ho.mjs.map} +1 -1
  44. package/dist/preview-engine/audio.mjs +1 -1
  45. package/dist/preview-engine/{chunk-JGDZQCNU.mjs → chunk-2B722GXM.mjs} +1 -1
  46. package/dist/preview-engine/{chunk-GFW3D464.mjs → chunk-2DWY7UBV.mjs} +1 -1
  47. package/dist/preview-engine/{chunk-72DYOIMP.mjs → chunk-2PYTDN2C.mjs} +1 -1
  48. package/dist/preview-engine/{chunk-PF3SXQNV.mjs → chunk-34DL53D5.mjs} +1 -1
  49. package/dist/preview-engine/{chunk-GLXJOXBE.mjs → chunk-34RJ7GAS.mjs} +1 -1
  50. package/dist/preview-engine/{chunk-6OY4YLTC.mjs → chunk-35HMRW4I.mjs} +1 -1
  51. package/dist/preview-engine/chunk-374QY63R.mjs +1974 -0
  52. package/dist/preview-engine/{chunk-W26U6BP2.mjs → chunk-4WYM72ZN.mjs} +1 -1
  53. package/dist/preview-engine/{chunk-EQ4VBUWM.mjs → chunk-5AWAOMBR.mjs} +1 -1
  54. package/dist/preview-engine/{chunk-J4SHMCTG.mjs → chunk-6R3UJBL5.mjs} +1 -1
  55. package/dist/preview-engine/{chunk-VHGC72XP.mjs → chunk-73CNXGAK.mjs} +1 -1
  56. package/dist/preview-engine/{chunk-K5F4ZW6T.mjs → chunk-7U34VKNO.mjs} +1 -1
  57. package/dist/preview-engine/{chunk-KYOPOCO5.mjs → chunk-AHQCDTD6.mjs} +1 -1
  58. package/dist/preview-engine/{chunk-ADV2TIKA.mjs → chunk-BEYA25UZ.mjs} +1 -1
  59. package/dist/preview-engine/{chunk-RBDIRBEI.mjs → chunk-BJV2LBII.mjs} +1 -1
  60. package/dist/preview-engine/{chunk-LBJBBEKP.mjs → chunk-BVDWS3D2.mjs} +1 -1
  61. package/dist/preview-engine/chunk-BXPM5SJ3.mjs +1 -0
  62. package/dist/preview-engine/{chunk-NIPWL6Z5.mjs → chunk-C4M3B2Q2.mjs} +1 -1
  63. package/dist/preview-engine/{chunk-LLUFOFF4.mjs → chunk-CC4NNNIY.mjs} +1 -1
  64. package/dist/preview-engine/chunk-CCB3VTY2.mjs +39 -0
  65. package/dist/preview-engine/{chunk-NX66555S.mjs → chunk-CN2VLYLB.mjs} +1 -1
  66. package/dist/preview-engine/{chunk-SK7PUBTC.mjs → chunk-DMSZXG56.mjs} +1 -1
  67. package/dist/preview-engine/{chunk-WC257OBY.mjs → chunk-DVZITDQS.mjs} +1 -1
  68. package/dist/preview-engine/chunk-FKG27DVO.mjs +4 -0
  69. package/dist/preview-engine/{chunk-COZG35ZR.mjs → chunk-FTCEG3SF.mjs} +1 -1
  70. package/dist/preview-engine/{chunk-57LES7TL.mjs → chunk-GA4HNR44.mjs} +1 -1
  71. package/dist/preview-engine/chunk-GEUUXTDL.mjs +138 -0
  72. package/dist/preview-engine/{chunk-S4WNCEMO.mjs → chunk-GIICZZQI.mjs} +1 -1
  73. package/dist/preview-engine/{chunk-RTHU7REC.mjs → chunk-GRJJVSOV.mjs} +1 -1
  74. package/dist/preview-engine/{chunk-RGDM6PQ5.mjs → chunk-GZWA5QJS.mjs} +1 -1
  75. package/dist/preview-engine/{chunk-LZNNM5OB.mjs → chunk-H7BSN7V7.mjs} +1 -1
  76. package/dist/preview-engine/{chunk-JXUENEQO.mjs → chunk-HOV6DDBZ.mjs} +1 -1
  77. package/dist/preview-engine/{chunk-C6XLAJLL.mjs → chunk-JSYG6OWZ.mjs} +1 -1
  78. package/dist/preview-engine/{chunk-GZVPVOHB.mjs → chunk-K3C6GUQD.mjs} +1 -1
  79. package/dist/preview-engine/chunk-KOVN277Z.mjs +1 -0
  80. package/dist/preview-engine/{chunk-Y6TWMUJC.mjs → chunk-LVYVXVZO.mjs} +1 -1
  81. package/dist/preview-engine/chunk-MIEH74UH.mjs +480 -0
  82. package/dist/preview-engine/{chunk-AVDHZNIZ.mjs → chunk-OH5W5EL6.mjs} +1 -1
  83. package/dist/preview-engine/{chunk-22F33PPP.mjs → chunk-P5FKRQFU.mjs} +1 -1
  84. package/dist/preview-engine/{chunk-7X2AUKTU.mjs → chunk-PM2W4VLT.mjs} +1 -1
  85. package/dist/preview-engine/chunk-QCSVT73T.mjs +2403 -0
  86. package/dist/preview-engine/{chunk-523CTDOC.mjs → chunk-RJIAES3X.mjs} +1 -1
  87. package/dist/preview-engine/{chunk-DPQN7WYQ.mjs → chunk-S2ABWY5G.mjs} +1 -1
  88. package/dist/preview-engine/{chunk-G7JJ6HVL.mjs → chunk-SF37HXAH.mjs} +1 -1
  89. package/dist/preview-engine/{chunk-OKLYIQ5F.mjs → chunk-SPJPM43P.mjs} +1 -1
  90. package/dist/preview-engine/{chunk-UOTMBWBJ.mjs → chunk-TNJEZKXM.mjs} +1 -1
  91. package/dist/preview-engine/chunk-ULOFDFHJ.mjs +1 -0
  92. package/dist/preview-engine/{chunk-LLB3AGBQ.mjs → chunk-UQKPDGEH.mjs} +1 -1
  93. package/dist/preview-engine/{chunk-DISKQ5MH.mjs → chunk-UXYM2G6J.mjs} +1 -1
  94. package/dist/preview-engine/{chunk-U5YNECNG.mjs → chunk-VN36PRVD.mjs} +1 -1
  95. package/dist/preview-engine/{chunk-PMOKPVSD.mjs → chunk-VVE2QHVG.mjs} +1 -1
  96. package/dist/preview-engine/{chunk-PEUQRSIM.mjs → chunk-Y5K5LVOR.mjs} +1 -1
  97. package/dist/preview-engine/chunk-YGKO4NIU.mjs +1 -0
  98. package/dist/preview-engine/{chunk-PSEMCJBH.mjs → chunk-ZJ5SWTCW.mjs} +1 -1
  99. package/dist/preview-engine/chunk-ZY47XIRK.mjs +5 -0
  100. package/dist/preview-engine/components.mjs +1 -1
  101. package/dist/preview-engine/effects.mjs +1 -1
  102. package/dist/preview-engine/framefields.mjs +1 -1
  103. package/dist/preview-engine/player.mjs +1 -1
  104. package/dist/preview-engine/renderers-3BOQOUEU.mjs +1 -0
  105. package/dist/preview-engine/renderers-3M2FESR2.mjs +1 -0
  106. package/dist/preview-engine/{renderers-FEEIDQF3.mjs → renderers-4F7WLQD5.mjs} +1 -1
  107. package/dist/preview-engine/{renderers-OP6MKYRM.mjs → renderers-5KANN7XF.mjs} +1 -1
  108. package/dist/preview-engine/{renderers-TVRR7PGN.mjs → renderers-5TO2SXFR.mjs} +1 -1
  109. package/dist/preview-engine/{renderers-DR7UZ3QR.mjs → renderers-5UZ3YFBD.mjs} +1 -1
  110. package/dist/preview-engine/renderers-6LHIWKMZ.mjs +1 -0
  111. package/dist/preview-engine/{renderers-7F2KF5RE.mjs → renderers-6Z7DWAPH.mjs} +1 -1
  112. package/dist/preview-engine/renderers-77PDUMYI.mjs +1 -0
  113. package/dist/preview-engine/{renderers-3J3NCOAI.mjs → renderers-7C5RA3R5.mjs} +1 -1
  114. package/dist/preview-engine/renderers-7XHO6M3B.mjs +228 -0
  115. package/dist/preview-engine/{renderers-KJAOPYAU.mjs → renderers-A24KZK2Q.mjs} +1 -1
  116. package/dist/preview-engine/renderers-A43VCJ7X.mjs +1 -0
  117. package/dist/preview-engine/renderers-AKY464RY.mjs +1 -0
  118. package/dist/preview-engine/renderers-C5UO6PL5.mjs +1 -0
  119. package/dist/preview-engine/renderers-C64QSZO4.mjs +1 -0
  120. package/dist/preview-engine/renderers-CIRCR6NG.mjs +1 -0
  121. package/dist/preview-engine/renderers-CIWOPETY.mjs +1 -0
  122. package/dist/preview-engine/renderers-CUMVIVHI.mjs +1 -0
  123. package/dist/preview-engine/renderers-CZEZCELP.mjs +1 -0
  124. package/dist/preview-engine/{renderers-R3YVLWKV.mjs → renderers-ENMY4DKF.mjs} +1 -1
  125. package/dist/preview-engine/{renderers-AAENCA7X.mjs → renderers-FSI5KNGZ.mjs} +1 -1
  126. package/dist/preview-engine/renderers-FSQMWHQM.mjs +1 -0
  127. package/dist/preview-engine/renderers-FZEW5C23.mjs +1 -0
  128. package/dist/preview-engine/renderers-HCET2IML.mjs +1 -0
  129. package/dist/preview-engine/{renderers-LBX7GPDU.mjs → renderers-IHGAWLUF.mjs} +1 -1
  130. package/dist/preview-engine/renderers-IUOOC6K7.mjs +1 -0
  131. package/dist/preview-engine/{renderers-VXMR47WZ.mjs → renderers-KC6ZSKCW.mjs} +1 -1
  132. package/dist/preview-engine/renderers-KYYJLVIJ.mjs +1 -0
  133. package/dist/preview-engine/renderers-L4GE7WV3.mjs +1 -0
  134. package/dist/preview-engine/renderers-L6YDX6GS.mjs +1 -0
  135. package/dist/preview-engine/renderers-LRAFWKCO.mjs +1 -0
  136. package/dist/preview-engine/renderers-LU3XZWEB.mjs +1 -0
  137. package/dist/preview-engine/{renderers-4YYGKLDK.mjs → renderers-MD3PRFZL.mjs} +1 -1
  138. package/dist/preview-engine/renderers-MOEC2XZZ.mjs +1 -0
  139. package/dist/preview-engine/{renderers-RGDTRVZM.mjs → renderers-NCGH2WWJ.mjs} +1 -1
  140. package/dist/preview-engine/renderers-ND2WV4K3.mjs +1 -0
  141. package/dist/preview-engine/renderers-NJ5CWVVH.mjs +1 -0
  142. package/dist/preview-engine/renderers-NKXYPR3V.mjs +1 -0
  143. package/dist/preview-engine/{renderers-WVQQZQ6Y.mjs → renderers-NUAI4TO5.mjs} +1 -1
  144. package/dist/preview-engine/renderers-NWR66CGK.mjs +1 -0
  145. package/dist/preview-engine/renderers-O22WTBRM.mjs +1 -0
  146. package/dist/preview-engine/renderers-O55BEDQZ.mjs +1 -0
  147. package/dist/preview-engine/renderers-PBBA7LQB.mjs +1 -0
  148. package/dist/preview-engine/{renderers-BL7Z3FTF.mjs → renderers-PIIEUKFE.mjs} +1 -1
  149. package/dist/preview-engine/renderers-PPZK6VEW.mjs +1 -0
  150. package/dist/preview-engine/renderers-Q6LJRZ7R.mjs +1 -0
  151. package/dist/preview-engine/{renderers-7TNEKJZS.mjs → renderers-QA2HKUJJ.mjs} +1 -1
  152. package/dist/preview-engine/renderers-RMEACSRL.mjs +1 -0
  153. package/dist/preview-engine/{renderers-AWRIELVX.mjs → renderers-RN2MHIFK.mjs} +1 -1
  154. package/dist/preview-engine/renderers-SEIAUKJF.mjs +1 -0
  155. package/dist/preview-engine/renderers-SP42LCSI.mjs +1 -0
  156. package/dist/preview-engine/{renderers-OE55O27K.mjs → renderers-T74CQCEN.mjs} +1 -1
  157. package/dist/preview-engine/renderers-T7SZA3BX.mjs +1 -0
  158. package/dist/preview-engine/{renderers-ZUQ2ZBYY.mjs → renderers-TGJSOKDY.mjs} +1 -1
  159. package/dist/preview-engine/renderers-TSBV4KPE.mjs +1 -0
  160. package/dist/preview-engine/{renderers-2SE5BLLB.mjs → renderers-U4M7ZCV4.mjs} +1 -1
  161. package/dist/preview-engine/{renderers-YEI4K4W4.mjs → renderers-V3AQO7AR.mjs} +1 -1
  162. package/dist/preview-engine/renderers-VADTDAHD.mjs +1 -0
  163. package/dist/preview-engine/renderers-VNNWI5C3.mjs +1 -0
  164. package/dist/preview-engine/{renderers-YYMRKHAA.mjs → renderers-WEYAHOBK.mjs} +1 -1
  165. package/dist/preview-engine/renderers-WZ75MLP3.mjs +1 -0
  166. package/dist/preview-engine/renderers-XB5R4UQA.mjs +1 -0
  167. package/dist/preview-engine/{renderers-YIVPMPQS.mjs → renderers-Y22VSOXF.mjs} +1 -1
  168. package/dist/preview-engine/{renderers-Y53D5CQ7.mjs → renderers-YBIUZDGQ.mjs} +1 -1
  169. package/dist/preview-engine/renderers-YESJR4O7.mjs +1 -0
  170. package/dist/preview-engine/renderers-YKSRZRRT.mjs +1 -0
  171. package/dist/preview-engine/{renderers-V4VML5IG.mjs → renderers-ZACSHGNZ.mjs} +1 -1
  172. package/dist/preview-engine/signals.mjs +1 -1
  173. package/dist/preview-engine/src-NGVAJYHC.mjs +8 -0
  174. package/dist/{program-CqQDWfSV.mjs → program-CKpKkllV.mjs} +5 -6
  175. package/dist/program-CKpKkllV.mjs.map +1 -0
  176. package/dist/renderer/index.d.mts +2 -2
  177. package/dist/renderer/index.mjs +2 -2
  178. package/dist/renderer--Ac-dg4A.mjs +5 -0
  179. package/dist/renderer--N7TMqzU.mjs +5 -0
  180. package/dist/{renderer-DYfNgEhK.mjs → renderer--fkmiVjl.mjs} +2 -2
  181. package/dist/{renderer-DYfNgEhK.mjs.map → renderer--fkmiVjl.mjs.map} +1 -1
  182. package/dist/{renderer-JDLYPnRh.mjs → renderer-1r-Uq-Ae.mjs} +2 -2
  183. package/dist/{renderer-JDLYPnRh.mjs.map → renderer-1r-Uq-Ae.mjs.map} +1 -1
  184. package/dist/renderer-2ecmdOae.mjs +5 -0
  185. package/dist/{renderer-DZKZup7n.mjs → renderer-8QIwXh7O.mjs} +3 -3
  186. package/dist/{renderer-DZKZup7n.mjs.map → renderer-8QIwXh7O.mjs.map} +1 -1
  187. package/dist/{renderer-DCyp-jzQ.mjs → renderer-B-ur0Ej8.mjs} +3 -3
  188. package/dist/{renderer-DCyp-jzQ.mjs.map → renderer-B-ur0Ej8.mjs.map} +1 -1
  189. package/dist/{renderer-BD25uND8.mjs → renderer-B0piZg9r.mjs} +383 -171
  190. package/dist/renderer-B0piZg9r.mjs.map +1 -0
  191. package/dist/{renderer-Cn34qZC4.mjs → renderer-B2jjxsjO.mjs} +2 -2
  192. package/dist/{renderer-Cn34qZC4.mjs.map → renderer-B2jjxsjO.mjs.map} +1 -1
  193. package/dist/{renderer-DbO4xZBb.mjs → renderer-B39I6CQw.mjs} +3 -3
  194. package/dist/{renderer-DbO4xZBb.mjs.map → renderer-B39I6CQw.mjs.map} +1 -1
  195. package/dist/{renderer-CTr_Zi08.mjs → renderer-B522Dkdk.mjs} +3 -3
  196. package/dist/{renderer-CTr_Zi08.mjs.map → renderer-B522Dkdk.mjs.map} +1 -1
  197. package/dist/renderer-B5RJp5g-.mjs +5 -0
  198. package/dist/renderer-B7xyT1Vb.mjs +5 -0
  199. package/dist/{renderer-4a4koLCl.mjs → renderer-BAYTI6jj.mjs} +2 -2
  200. package/dist/{renderer-4a4koLCl.mjs.map → renderer-BAYTI6jj.mjs.map} +1 -1
  201. package/dist/{renderer-CCnrG0Pd.mjs → renderer-BAusZ6Vu.mjs} +2 -2
  202. package/dist/{renderer-CCnrG0Pd.mjs.map → renderer-BAusZ6Vu.mjs.map} +1 -1
  203. package/dist/renderer-BBeArN_x.mjs +5 -0
  204. package/dist/renderer-BCAZDu-r.mjs +5 -0
  205. package/dist/{renderer-bE6yZ_Ef.mjs → renderer-BF0hx_iJ.mjs} +3 -3
  206. package/dist/{renderer-bE6yZ_Ef.mjs.map → renderer-BF0hx_iJ.mjs.map} +1 -1
  207. package/dist/renderer-BJnhik7Y.mjs +5 -0
  208. package/dist/{renderer-kg8-ngnB.mjs → renderer-BPWDsufX.mjs} +2 -2
  209. package/dist/{renderer-kg8-ngnB.mjs.map → renderer-BPWDsufX.mjs.map} +1 -1
  210. package/dist/{renderer-DXu0nrzo.d.mts → renderer-BX7jS14r.d.mts} +2 -2
  211. package/dist/{renderer-DXu0nrzo.d.mts.map → renderer-BX7jS14r.d.mts.map} +1 -1
  212. package/dist/{renderer-DBN6SV8L.mjs → renderer-BXJXziIt.mjs} +2 -2
  213. package/dist/{renderer-DBN6SV8L.mjs.map → renderer-BXJXziIt.mjs.map} +1 -1
  214. package/dist/{renderer-4F4FypvN.mjs → renderer-BdyopiLC.mjs} +2 -2
  215. package/dist/{renderer-4F4FypvN.mjs.map → renderer-BdyopiLC.mjs.map} +1 -1
  216. package/dist/{renderer-wH_l-jMH.mjs → renderer-Bfk6A7UO.mjs} +3 -3
  217. package/dist/{renderer-wH_l-jMH.mjs.map → renderer-Bfk6A7UO.mjs.map} +1 -1
  218. package/dist/{renderer-DYT4iPi-.mjs → renderer-BgO6BA_z.mjs} +2 -2
  219. package/dist/{renderer-DYT4iPi-.mjs.map → renderer-BgO6BA_z.mjs.map} +1 -1
  220. package/dist/{renderer-Dv5yE4LE.mjs → renderer-BlPqTEii.mjs} +2 -2
  221. package/dist/{renderer-Dv5yE4LE.mjs.map → renderer-BlPqTEii.mjs.map} +1 -1
  222. package/dist/{renderer-CuK1lPnA.mjs → renderer-BlvGcyAN.mjs} +2 -2
  223. package/dist/{renderer-CuK1lPnA.mjs.map → renderer-BlvGcyAN.mjs.map} +1 -1
  224. package/dist/{renderer-DX5ZpEBB.mjs → renderer-BpIF1QIE.mjs} +3 -3
  225. package/dist/{renderer-DX5ZpEBB.mjs.map → renderer-BpIF1QIE.mjs.map} +1 -1
  226. package/dist/renderer-BxRW8MZO.mjs +5 -0
  227. package/dist/renderer-C-lfsEAN.mjs +5 -0
  228. package/dist/{renderer-DRLtfxdW.mjs → renderer-C41U1tK6.mjs} +95 -16
  229. package/dist/renderer-C41U1tK6.mjs.map +1 -0
  230. package/dist/renderer-C6nOFN4-.mjs +5 -0
  231. package/dist/{renderer-CpEU-TRR.mjs → renderer-C7L8Ceqr.mjs} +2 -2
  232. package/dist/{renderer-CpEU-TRR.mjs.map → renderer-C7L8Ceqr.mjs.map} +1 -1
  233. package/dist/{renderer-BMnX_-PZ.mjs → renderer-C7eFJf0_.mjs} +3 -3
  234. package/dist/{renderer-BMnX_-PZ.mjs.map → renderer-C7eFJf0_.mjs.map} +1 -1
  235. package/dist/renderer-C84TSQmS.mjs +5 -0
  236. package/dist/renderer-C8_6YofT.mjs +5 -0
  237. package/dist/{renderer-CeIXVu9F.mjs → renderer-CCycwCEB.mjs} +2 -2
  238. package/dist/{renderer-CeIXVu9F.mjs.map → renderer-CCycwCEB.mjs.map} +1 -1
  239. package/dist/{renderer-CNVzJIBz.mjs → renderer-CHr7MiYW.mjs} +3 -3
  240. package/dist/{renderer-CNVzJIBz.mjs.map → renderer-CHr7MiYW.mjs.map} +1 -1
  241. package/dist/renderer-COR66Sby.mjs +3 -0
  242. package/dist/renderer-COb3tOPG.mjs +5 -0
  243. package/dist/{renderer-Crl8q_vD.mjs → renderer-CQvmbRQ5.mjs} +3 -3
  244. package/dist/{renderer-Crl8q_vD.mjs.map → renderer-CQvmbRQ5.mjs.map} +1 -1
  245. package/dist/renderer-CSCIzaLy.mjs +5 -0
  246. package/dist/{renderer-DO15Nj-w.mjs → renderer-CYC32bDW.mjs} +3 -3
  247. package/dist/{renderer-DO15Nj-w.mjs.map → renderer-CYC32bDW.mjs.map} +1 -1
  248. package/dist/{renderer-NvGahExL.mjs → renderer-CcvvoKiZ.mjs} +3 -3
  249. package/dist/{renderer-NvGahExL.mjs.map → renderer-CcvvoKiZ.mjs.map} +1 -1
  250. package/dist/{renderer-vVKNp3ri.mjs → renderer-CiQhD8Gg.mjs} +3 -3
  251. package/dist/{renderer-vVKNp3ri.mjs.map → renderer-CiQhD8Gg.mjs.map} +1 -1
  252. package/dist/renderer-CmI4c8MS.mjs +5 -0
  253. package/dist/{renderer-Sj11W1xq.mjs → renderer-CpUJBOfb.mjs} +2 -2
  254. package/dist/renderer-CqVltdX9.mjs +5 -0
  255. package/dist/{renderer-j-3XB8YI.mjs → renderer-CwFqqpWY.mjs} +3 -3
  256. package/dist/{renderer-j-3XB8YI.mjs.map → renderer-CwFqqpWY.mjs.map} +1 -1
  257. package/dist/renderer-D-FpMzLY.mjs +5 -0
  258. package/dist/renderer-D-Oyn9mG.mjs +5 -0
  259. package/dist/renderer-D2QrkA8_.mjs +5 -0
  260. package/dist/{renderer-ClxmV_id.mjs → renderer-D47AEJE_.mjs} +2 -2
  261. package/dist/{renderer-ClxmV_id.mjs.map → renderer-D47AEJE_.mjs.map} +1 -1
  262. package/dist/{renderer-BrXkb-9N.mjs → renderer-D61B9bXF.mjs} +2 -2
  263. package/dist/{renderer-BrXkb-9N.mjs.map → renderer-D61B9bXF.mjs.map} +1 -1
  264. package/dist/renderer-D64r4iN-.mjs +5 -0
  265. package/dist/{renderer-DXsV3mZD.mjs → renderer-D7Al3_-5.mjs} +3 -3
  266. package/dist/{renderer-DXsV3mZD.mjs.map → renderer-D7Al3_-5.mjs.map} +1 -1
  267. package/dist/{renderer-Bw3tlr9I.mjs → renderer-D7r6onRT.mjs} +2 -2
  268. package/dist/{renderer-Bw3tlr9I.mjs.map → renderer-D7r6onRT.mjs.map} +1 -1
  269. package/dist/{renderer-Dypn26aP.mjs → renderer-D8bBHrWC.mjs} +3 -3
  270. package/dist/{renderer-Dypn26aP.mjs.map → renderer-D8bBHrWC.mjs.map} +1 -1
  271. package/dist/renderer-DCq6YkgP.mjs +5 -0
  272. package/dist/{renderer-BHwbnT6S.mjs → renderer-DDEkFbVI.mjs} +3 -3
  273. package/dist/{renderer-BHwbnT6S.mjs.map → renderer-DDEkFbVI.mjs.map} +1 -1
  274. package/dist/renderer-DDHG3GdR.mjs +5 -0
  275. package/dist/{renderer-DqMZiy5N.mjs → renderer-DEmYyaqj.mjs} +2 -2
  276. package/dist/{renderer-DqMZiy5N.mjs.map → renderer-DEmYyaqj.mjs.map} +1 -1
  277. package/dist/{renderer-CyELq6P6.mjs → renderer-DF-yvIrV.mjs} +2 -2
  278. package/dist/{renderer-CyELq6P6.mjs.map → renderer-DF-yvIrV.mjs.map} +1 -1
  279. package/dist/{renderer-XnWe1hSL.mjs → renderer-DFzI3yI_.mjs} +2 -2
  280. package/dist/{renderer-XnWe1hSL.mjs.map → renderer-DFzI3yI_.mjs.map} +1 -1
  281. package/dist/{renderer-Cx2Sw9Ib.mjs → renderer-DIw3UtEU.mjs} +3 -3
  282. package/dist/{renderer-Cx2Sw9Ib.mjs.map → renderer-DIw3UtEU.mjs.map} +1 -1
  283. package/dist/{renderer-D_Mf-Zft.d.mts → renderer-DKgPVOLL.d.mts} +2 -2
  284. package/dist/{renderer-D_Mf-Zft.d.mts.map → renderer-DKgPVOLL.d.mts.map} +1 -1
  285. package/dist/renderer-DMdNfcud.mjs +6 -0
  286. package/dist/{renderer-CaRPsrbX.mjs → renderer-DMvmf3T2.mjs} +2 -2
  287. package/dist/{renderer-jvgqy4Td.mjs → renderer-DNQvOrkY.mjs} +3 -3
  288. package/dist/{renderer-jvgqy4Td.mjs.map → renderer-DNQvOrkY.mjs.map} +1 -1
  289. package/dist/{renderer-DB7PM7nZ.mjs → renderer-DO-KbpYZ.mjs} +3 -3
  290. package/dist/{renderer-DB7PM7nZ.mjs.map → renderer-DO-KbpYZ.mjs.map} +1 -1
  291. package/dist/{renderer-CQkZUsvc.mjs → renderer-DTqDy2m9.mjs} +3 -3
  292. package/dist/{renderer-CQkZUsvc.mjs.map → renderer-DTqDy2m9.mjs.map} +1 -1
  293. package/dist/{renderer-DPUULdjh.mjs → renderer-DVCJjVn7.mjs} +2 -2
  294. package/dist/{renderer-DPUULdjh.mjs.map → renderer-DVCJjVn7.mjs.map} +1 -1
  295. package/dist/{renderer-C7SYbHgB.mjs → renderer-DX41niRU.mjs} +2 -2
  296. package/dist/{renderer-C7SYbHgB.mjs.map → renderer-DX41niRU.mjs.map} +1 -1
  297. package/dist/{renderer-BYIa_hnS.mjs → renderer-DXEWbnRF.mjs} +3 -3
  298. package/dist/{renderer-BYIa_hnS.mjs.map → renderer-DXEWbnRF.mjs.map} +1 -1
  299. package/dist/renderer-DZQXZ4lK.mjs +5 -0
  300. package/dist/{renderer-iA7rxZmR.mjs → renderer-DbRZe0Re.mjs} +3 -3
  301. package/dist/{renderer-iA7rxZmR.mjs.map → renderer-DbRZe0Re.mjs.map} +1 -1
  302. package/dist/renderer-DczJJLA1.mjs +5 -0
  303. package/dist/renderer-DfqJj9oe.mjs +5 -0
  304. package/dist/renderer-DgO9ihVJ.mjs +5 -0
  305. package/dist/renderer-DhgnQZYb.mjs +5 -0
  306. package/dist/{renderer-l-K6iZLC.mjs → renderer-DiY70S8S.mjs} +3 -3
  307. package/dist/{renderer-l-K6iZLC.mjs.map → renderer-DiY70S8S.mjs.map} +1 -1
  308. package/dist/renderer-DjxKjHMf.mjs +5 -0
  309. package/dist/{renderer-Qd-HCwKj.mjs → renderer-DkRSZg5g.mjs} +3 -3
  310. package/dist/{renderer-Qd-HCwKj.mjs.map → renderer-DkRSZg5g.mjs.map} +1 -1
  311. package/dist/{renderer-CaE0rp9j.mjs → renderer-DmkWrWAk.mjs} +2 -2
  312. package/dist/{renderer-CaE0rp9j.mjs.map → renderer-DmkWrWAk.mjs.map} +1 -1
  313. package/dist/{renderer-BUV6IH0o.mjs → renderer-DpruWmZU.mjs} +2 -2
  314. package/dist/{renderer-BUV6IH0o.mjs.map → renderer-DpruWmZU.mjs.map} +1 -1
  315. package/dist/renderer-DqLA5cdR.mjs +5 -0
  316. package/dist/{renderer-DfTnsfy9.mjs → renderer-DqvoMLca.mjs} +3 -3
  317. package/dist/{renderer-DfTnsfy9.mjs.map → renderer-DqvoMLca.mjs.map} +1 -1
  318. package/dist/{renderer-Dhi0Q1cI.mjs → renderer-DsEyjwfT.mjs} +70 -70
  319. package/dist/{renderer-Dhi0Q1cI.mjs.map → renderer-DsEyjwfT.mjs.map} +1 -1
  320. package/dist/renderer-Dudp6ZlH.mjs +5 -0
  321. package/dist/{renderer-Bw5jGUo5.mjs → renderer-EaLUCobW.mjs} +2 -2
  322. package/dist/{renderer-Bw5jGUo5.mjs.map → renderer-EaLUCobW.mjs.map} +1 -1
  323. package/dist/{renderer-Ca-LoeJh.mjs → renderer-GBmWjBvy.mjs} +2 -2
  324. package/dist/{renderer-Ca-LoeJh.mjs.map → renderer-GBmWjBvy.mjs.map} +1 -1
  325. package/dist/{renderer-DS-Gv-3w.mjs → renderer-Lth37j17.mjs} +2 -2
  326. package/dist/{renderer-BlUfAZyK.mjs → renderer-SSBL4M1o.mjs} +3 -3
  327. package/dist/{renderer-BlUfAZyK.mjs.map → renderer-SSBL4M1o.mjs.map} +1 -1
  328. package/dist/{renderer-DSM5c7UA.mjs → renderer-SWr3vbrV.mjs} +2 -2
  329. package/dist/{renderer-DSM5c7UA.mjs.map → renderer-SWr3vbrV.mjs.map} +1 -1
  330. package/dist/{renderer-BLrhcSex.mjs → renderer-WCZu2Rf5.mjs} +3 -3
  331. package/dist/{renderer-BLrhcSex.mjs.map → renderer-WCZu2Rf5.mjs.map} +1 -1
  332. package/dist/{renderer-D7FuHXiz.mjs → renderer-Xv6GfNUI.mjs} +2 -2
  333. package/dist/{renderer-D7FuHXiz.mjs.map → renderer-Xv6GfNUI.mjs.map} +1 -1
  334. package/dist/renderer-_jC5KIT1.mjs +5 -0
  335. package/dist/{renderer-8pS82_mX.mjs → renderer-bAKUeHF3.mjs} +3 -3
  336. package/dist/{renderer-8pS82_mX.mjs.map → renderer-bAKUeHF3.mjs.map} +1 -1
  337. package/dist/renderer-cTgAuVmJ.mjs +5 -0
  338. package/dist/{renderer-DjWBBISJ.mjs → renderer-cod5Ona9.mjs} +2 -2
  339. package/dist/{renderer-C55eLbl1.mjs → renderer-g_vrrAQU.mjs} +2 -2
  340. package/dist/{renderer-C55eLbl1.mjs.map → renderer-g_vrrAQU.mjs.map} +1 -1
  341. package/dist/{renderer-CT_aJBk2.mjs → renderer-hSCfnvaK.mjs} +3 -3
  342. package/dist/{renderer-CT_aJBk2.mjs.map → renderer-hSCfnvaK.mjs.map} +1 -1
  343. package/dist/{renderer-BuYRrBRf.mjs → renderer-ktWXVYka.mjs} +3 -3
  344. package/dist/{renderer-BuYRrBRf.mjs.map → renderer-ktWXVYka.mjs.map} +1 -1
  345. package/dist/renderer-nVKDvDLO.mjs +5 -0
  346. package/dist/{renderer-7Tx-L9Vf.mjs → renderer-oKRF4A0M.mjs} +2 -2
  347. package/dist/{renderer-7Tx-L9Vf.mjs.map → renderer-oKRF4A0M.mjs.map} +1 -1
  348. package/dist/{renderer-fv9WvK0d.mjs → renderer-pHWEHDnM.mjs} +3 -3
  349. package/dist/{renderer-fv9WvK0d.mjs.map → renderer-pHWEHDnM.mjs.map} +1 -1
  350. package/dist/renderer-pZ6_pUo1.mjs +5 -0
  351. package/dist/{renderer-DsGE9qtz.mjs → renderer-sCovEubT.mjs} +3 -3
  352. package/dist/{renderer-DsGE9qtz.mjs.map → renderer-sCovEubT.mjs.map} +1 -1
  353. package/dist/{renderer-BcHpcLq3.mjs → renderer-sYSkIUfo.mjs} +3 -3
  354. package/dist/{renderer-BcHpcLq3.mjs.map → renderer-sYSkIUfo.mjs.map} +1 -1
  355. package/dist/{renderer-4GwADfB0.mjs → renderer-zXPVLRlD.mjs} +3 -3
  356. package/dist/{renderer-4GwADfB0.mjs.map → renderer-zXPVLRlD.mjs.map} +1 -1
  357. package/dist/{renderers-CP8CFWDs-fYqxoIKK.mjs → renderers-CP8CFWDs-DH2oktaC.mjs} +2 -2
  358. package/dist/{renderers-CP8CFWDs-fYqxoIKK.mjs.map → renderers-CP8CFWDs-DH2oktaC.mjs.map} +1 -1
  359. package/dist/signals/index.d.mts +5 -5
  360. package/dist/signals/index.mjs +4 -4
  361. package/dist/{src-DggYlKaX.mjs → src-CK5rGVbh.mjs} +664 -72
  362. package/dist/src-CK5rGVbh.mjs.map +1 -0
  363. package/package.json +67 -58
  364. package/dist/dist-CnFcSau0.mjs.map +0 -1
  365. package/dist/index-B_nooAhS.d.mts.map +0 -1
  366. package/dist/index-Ced1Ry4S.d.mts.map +0 -1
  367. package/dist/index-Ds9_v60R.d.mts.map +0 -1
  368. package/dist/preview-engine/chunk-3OXRGTQ2.mjs +0 -2390
  369. package/dist/preview-engine/chunk-3U2A3RW4.mjs +0 -5
  370. package/dist/preview-engine/chunk-6E3JSPJN.mjs +0 -1
  371. package/dist/preview-engine/chunk-7EYVBRN3.mjs +0 -480
  372. package/dist/preview-engine/chunk-7WBDEZ2A.mjs +0 -1974
  373. package/dist/preview-engine/chunk-F4F2UDRX.mjs +0 -1
  374. package/dist/preview-engine/chunk-HPP52AQI.mjs +0 -4
  375. package/dist/preview-engine/chunk-PYSBWKIJ.mjs +0 -1
  376. package/dist/preview-engine/chunk-U2IFQMS3.mjs +0 -174
  377. package/dist/preview-engine/renderers-2GMY2ZNK.mjs +0 -1
  378. package/dist/preview-engine/renderers-2QJNKL7Q.mjs +0 -1
  379. package/dist/preview-engine/renderers-3OYU6O6F.mjs +0 -1
  380. package/dist/preview-engine/renderers-433Y2PCK.mjs +0 -1
  381. package/dist/preview-engine/renderers-4AGPGEL4.mjs +0 -1
  382. package/dist/preview-engine/renderers-54DBHGUW.mjs +0 -1
  383. package/dist/preview-engine/renderers-5M2O55U3.mjs +0 -1
  384. package/dist/preview-engine/renderers-6FVMDEAY.mjs +0 -1
  385. package/dist/preview-engine/renderers-6R7ZJ7DU.mjs +0 -1
  386. package/dist/preview-engine/renderers-75GCMFOD.mjs +0 -1
  387. package/dist/preview-engine/renderers-7LWLUCVQ.mjs +0 -1
  388. package/dist/preview-engine/renderers-BLDENIEV.mjs +0 -1
  389. package/dist/preview-engine/renderers-CCOEGTZJ.mjs +0 -228
  390. package/dist/preview-engine/renderers-CDB3YK3R.mjs +0 -1
  391. package/dist/preview-engine/renderers-D6D4XDFY.mjs +0 -1
  392. package/dist/preview-engine/renderers-DJVFQZYU.mjs +0 -1
  393. package/dist/preview-engine/renderers-DMXSV7VO.mjs +0 -1
  394. package/dist/preview-engine/renderers-EBMASIOE.mjs +0 -1
  395. package/dist/preview-engine/renderers-F262HDFS.mjs +0 -1
  396. package/dist/preview-engine/renderers-FMXKIF22.mjs +0 -1
  397. package/dist/preview-engine/renderers-GJKSZ6DR.mjs +0 -1
  398. package/dist/preview-engine/renderers-GR3RLYDZ.mjs +0 -1
  399. package/dist/preview-engine/renderers-GT3W2QIA.mjs +0 -1
  400. package/dist/preview-engine/renderers-IHC7HUK3.mjs +0 -1
  401. package/dist/preview-engine/renderers-J56ZGEVM.mjs +0 -1
  402. package/dist/preview-engine/renderers-KYM6352U.mjs +0 -1
  403. package/dist/preview-engine/renderers-LFXX6TWI.mjs +0 -1
  404. package/dist/preview-engine/renderers-NAHUPFPU.mjs +0 -1
  405. package/dist/preview-engine/renderers-NM3FZLHH.mjs +0 -1
  406. package/dist/preview-engine/renderers-NNB4BTJA.mjs +0 -1
  407. package/dist/preview-engine/renderers-OYVQW6QI.mjs +0 -1
  408. package/dist/preview-engine/renderers-P5RIFU3C.mjs +0 -1
  409. package/dist/preview-engine/renderers-QB7HJUTS.mjs +0 -1
  410. package/dist/preview-engine/renderers-QRQVOCDC.mjs +0 -1
  411. package/dist/preview-engine/renderers-QVFHJTZA.mjs +0 -1
  412. package/dist/preview-engine/renderers-R3WQIE4L.mjs +0 -1
  413. package/dist/preview-engine/renderers-SQBVOLG7.mjs +0 -1
  414. package/dist/preview-engine/renderers-TLXMYEDI.mjs +0 -1
  415. package/dist/preview-engine/renderers-X4S3WELJ.mjs +0 -1
  416. package/dist/preview-engine/renderers-XT7U25I7.mjs +0 -1
  417. package/dist/preview-engine/renderers-XVGLMBB3.mjs +0 -1
  418. package/dist/preview-engine/renderers-YUWU3TQZ.mjs +0 -1
  419. package/dist/preview-engine/renderers-ZNUD5OVO.mjs +0 -1
  420. package/dist/program-CqQDWfSV.mjs.map +0 -1
  421. package/dist/renderer-68XHoE2b.mjs +0 -5
  422. package/dist/renderer-AqUForA-.mjs +0 -5
  423. package/dist/renderer-B0J-uGBG.mjs +0 -5
  424. package/dist/renderer-BD25uND8.mjs.map +0 -1
  425. package/dist/renderer-BF2R0rwb.mjs +0 -5
  426. package/dist/renderer-BLrO9lqK.mjs +0 -5
  427. package/dist/renderer-BMy8jXhL.mjs +0 -5
  428. package/dist/renderer-BUxENp9L.mjs +0 -5
  429. package/dist/renderer-C-ebryrM.mjs +0 -5
  430. package/dist/renderer-C8Gbs3kr.mjs +0 -5
  431. package/dist/renderer-CFZvMv2-.mjs +0 -5
  432. package/dist/renderer-CGXW7mOs.mjs +0 -5
  433. package/dist/renderer-CNq6OGbb.mjs +0 -5
  434. package/dist/renderer-CO-Ddz6-.mjs +0 -5
  435. package/dist/renderer-CRdxAsP7.mjs +0 -5
  436. package/dist/renderer-CZLo0ALi.mjs +0 -5
  437. package/dist/renderer-CdvVUkhR.mjs +0 -5
  438. package/dist/renderer-Cjg7kmTq.mjs +0 -5
  439. package/dist/renderer-Cvz22ZSh.mjs +0 -3
  440. package/dist/renderer-CxKymhVa.mjs +0 -5
  441. package/dist/renderer-Cxlpu3pN.mjs +0 -5
  442. package/dist/renderer-Cz6qZ2Ii.mjs +0 -5
  443. package/dist/renderer-DDGvF2YA.mjs +0 -5
  444. package/dist/renderer-DFxtOKpi.mjs +0 -5
  445. package/dist/renderer-DGBNjTsB.mjs +0 -6
  446. package/dist/renderer-DRLtfxdW.mjs.map +0 -1
  447. package/dist/renderer-DUFcdZmw.mjs +0 -5
  448. package/dist/renderer-DUMu6EOH.mjs +0 -5
  449. package/dist/renderer-DdULrvJo.mjs +0 -5
  450. package/dist/renderer-DoaOOP6_.mjs +0 -5
  451. package/dist/renderer-DqmZqV-a.mjs +0 -5
  452. package/dist/renderer-LxTQ2E-G.mjs +0 -5
  453. package/dist/renderer-SpU-RddZ.mjs +0 -5
  454. package/dist/renderer-W6ahCM9z.mjs +0 -5
  455. package/dist/renderer-gGqtzw-S.mjs +0 -5
  456. package/dist/renderer-l0Wn1AoV.mjs +0 -5
  457. package/dist/renderer-rpXHvMZG.mjs +0 -5
  458. package/dist/renderer-twdsKOiz.mjs +0 -5
  459. package/dist/renderer-uJz-eIqO.mjs +0 -5
  460. package/dist/src-DggYlKaX.mjs.map +0 -1
@@ -10052,6 +10052,7 @@ function resolveEnvironment(opts) {
10052
10052
  * normal matrix computation for non-uniform surface scaling,
10053
10053
  * and binary uniform buffer packing for WGSL shaders.
10054
10054
  */
10055
+ /** Slots retained in the legacy uniform prefix; actual direct lights use storage. */
10055
10056
  const MAX_LIGHTS = 8;
10056
10057
  /**
10057
10058
  * 16 (ambient) + 16 (params) + 8 * 64 (lights), then the physical-shading
@@ -10255,7 +10256,7 @@ const Light3D = {
10255
10256
  ambientR += col[0] * int$1;
10256
10257
  ambientG += col[1] * int$1;
10257
10258
  ambientB += col[2] * int$1;
10258
- } else if (directLights.length < MAX_LIGHTS) directLights.push(Light3D.processLight(l));
10259
+ } else directLights.push(Light3D.processLight(l));
10259
10260
  buffer[0] = ambientR;
10260
10261
  buffer[1] = ambientG;
10261
10262
  buffer[2] = ambientB;
@@ -10287,12 +10288,42 @@ const Light3D = {
10287
10288
  }
10288
10289
  return buffer;
10289
10290
  },
10291
+ packDirectLights(lights, shadows = []) {
10292
+ const direct = lights.filter((light) => (light.lightType ?? light.type) !== "ambient");
10293
+ const data = new Float32Array(Math.max(1, direct.length) * 40);
10294
+ const shadowLayers = new Map(shadows.map((shadow, layer) => [shadow.lightIndex, layer]));
10295
+ for (const [i, light] of direct.entries()) {
10296
+ const packed = Light3D.processLight(light);
10297
+ const offset = i * 40;
10298
+ data.set(packed.posType, offset);
10299
+ data.set(packed.dirRadius, offset + 4);
10300
+ data.set(packed.colorIntensity, offset + 8);
10301
+ data.set(packed.spotParams, offset + 12);
10302
+ const layer = shadowLayers.get(i);
10303
+ const shadow = layer === void 0 ? void 0 : shadows[layer];
10304
+ if (shadow && layer !== void 0) {
10305
+ for (let j = 0; j < 16; j++) data[offset + 16 + j] = shadow.matrix[j] ?? 0;
10306
+ data.set([
10307
+ shadow.softness,
10308
+ shadow.mapSize,
10309
+ layer,
10310
+ 1
10311
+ ], offset + 32);
10312
+ data.set([
10313
+ shadow.bias,
10314
+ shadow.normalOffset,
10315
+ shadow.darkness,
10316
+ 0
10317
+ ], offset + 36);
10318
+ }
10319
+ }
10320
+ return data;
10321
+ },
10290
10322
  shadowCasterIndex(lights) {
10291
10323
  let index = 0;
10292
10324
  for (const l of lights) {
10293
10325
  const kind = l.lightType ?? l.type;
10294
10326
  if (kind === "ambient") continue;
10295
- if (index >= MAX_LIGHTS) break;
10296
10327
  if (l.castShadow && (kind === "directional" || kind === "spot")) return index;
10297
10328
  index++;
10298
10329
  }
@@ -10400,7 +10431,7 @@ var Path3D = class Path3D$1 {
10400
10431
  0,
10401
10432
  0
10402
10433
  ];
10403
- let b0 = Vector3Math.normalize(Vector3Math.cross(t0, n0));
10434
+ const b0 = Vector3Math.normalize(Vector3Math.cross(t0, n0));
10404
10435
  this.frames[0] = {
10405
10436
  normal: n0,
10406
10437
  binormal: b0,
@@ -10443,6 +10474,21 @@ var Path3D = class Path3D$1 {
10443
10474
  ];
10444
10475
  }
10445
10476
  /**
10477
+ * Returns a copy of the discrete samples `[position, tangent, distance]` along this path.
10478
+ */
10479
+ getSamples() {
10480
+ return this.samples;
10481
+ }
10482
+ /**
10483
+ * Evaluates `count` evenly arc-length-spaced 3D positions along the path.
10484
+ */
10485
+ getPoints(count = 64) {
10486
+ const n = Math.max(2, Math.round(count));
10487
+ const pts = [];
10488
+ for (let i = 0; i < n; i++) pts.push(this.getPointAt(i / (n - 1)).position);
10489
+ return pts;
10490
+ }
10491
+ /**
10446
10492
  * Evaluates point, tangent, normal, binormal, and rotation at normalized progress u in [0, 1].
10447
10493
  * Progression moves with constant velocity along the arc length of the path.
10448
10494
  */
@@ -17784,8 +17830,6 @@ function transformModelBounds(bounds, { offset, scale }) {
17784
17830
  * hierarchy into model-space matrices, and turns a skin's joints into the
17785
17831
  * joint matrices the mesh shader blends (joint world × inverse bind).
17786
17832
  */
17787
- /** Joints per skin the mesh shader holds (mesh3d.wgsl jointMatrices). */
17788
- const MAX_JOINTS = 128;
17789
17833
  /** Column-major translate · rotate(quaternion) · scale. */
17790
17834
  function trsMatrix(t, q, s) {
17791
17835
  const [x, y, z, w] = q;
@@ -17927,12 +17971,12 @@ function poseNodes(model, clip, time$2) {
17927
17971
  };
17928
17972
  return nodes.map((_, i) => resolve$1$1(i));
17929
17973
  }
17930
- /** The joint matrices of one skin for a posed scene graph, padded with identity to MAX_JOINTS. */
17974
+ /** The joint matrices of one skin for a posed scene graph. */
17931
17975
  function jointMatrices(model, skinIndex, world) {
17932
17976
  const skin = model.skins?.[skinIndex];
17933
17977
  if (!skin) throw new Error(`model has no skin ${skinIndex}`);
17934
- const out = new Float32Array(MAX_JOINTS * 16);
17935
- for (let j = 0; j < MAX_JOINTS; j++) {
17978
+ const out = new Float32Array(Math.max(1, skin.joints.length) * 16);
17979
+ for (let j = 0; j < Math.max(1, skin.joints.length); j++) {
17936
17980
  const node = skin.joints[j];
17937
17981
  if (node === void 0) {
17938
17982
  out.set(Matrix4Math.identity(), j * 16);
@@ -18354,7 +18398,6 @@ var FbxLoader = class FbxLoader$1 {
18354
18398
  influences.set(cp, list);
18355
18399
  }
18356
18400
  }
18357
- if (joints.length > MAX_JOINTS) throw new Error(`FBX skin has ${joints.length} bones; the mesh shader holds ${MAX_JOINTS}`);
18358
18401
  if (joints.length > 0) {
18359
18402
  skinIndex = skins.length;
18360
18403
  skins.push({
@@ -20054,7 +20097,6 @@ var GltfLoader = class GltfLoader$1 {
20054
20097
  });
20055
20098
  });
20056
20099
  }
20057
- for (const skin of skins) if (skin.joints.length > MAX_JOINTS) throw new Error(`glTF skin has ${skin.joints.length} joints; the mesh shader holds ${MAX_JOINTS}`);
20058
20100
  if (!Number.isFinite(minX)) {
20059
20101
  minX = minY = minZ = 0;
20060
20102
  maxX = maxY = maxZ = 0;
@@ -25798,6 +25840,13 @@ var VideoDecoder = class extends BaseMediaDecoder {
25798
25840
  */
25799
25841
  decodeBuffer = null;
25800
25842
  nextRequest = null;
25843
+ /** Precise video-track duration, independent of a potentially longer audio track. */
25844
+ async durationSec() {
25845
+ await this.init();
25846
+ const track = await this.input?.getPrimaryVideoTrack();
25847
+ if (!track) throw new Error(`No video track in ${this.url}`);
25848
+ return track.computeDuration();
25849
+ }
25801
25850
  pushHeadlessQueue(sample) {
25802
25851
  const index = this.headlessQueue.findIndex((s) => s.timestamp > sample.timestamp);
25803
25852
  if (index === -1) this.headlessQueue.push(sample);
@@ -30950,6 +30999,16 @@ struct GPULight {
30950
30999
  spotParams : vec4<f32>, // x = cos(outer), y = cos(inner), z = decay, w = unused
30951
31000
  };
30952
31001
 
31002
+ struct DirectLight {
31003
+ posType : vec4<f32>,
31004
+ dirRadius : vec4<f32>,
31005
+ colorIntensity : vec4<f32>,
31006
+ spotParams : vec4<f32>,
31007
+ shadowMatrix : mat4x4<f32>,
31008
+ shadowParams : vec4<f32>,
31009
+ shadowBias : vec4<f32>,
31010
+ };
31011
+
30953
31012
  struct LightsUniforms {
30954
31013
  ambientColor : vec4<f32>, // rgb = ambient color * intensity (sRGB), w = unused
30955
31014
  params : vec4<f32>, // x = numLights, y = hasLights, z = physical (1), w = tone mapping (0 none, 1 ACES, 2 AgX, 3 PBR Neutral)
@@ -31132,25 +31191,26 @@ var<private> POISSON : array<vec2<f32>, 12> = array<vec2<f32>, 12>(
31132
31191
 
31133
31192
  /// 1 = fully lit, 0 = fully shadowed, for light index i.
31134
31193
  fn shadowFactor(worldPos: vec3<f32>, N: vec3<f32>, i: i32) -> f32 {
31135
- if (lights.shadowParams.x < 0.5 || i32(lights.shadowParams.y) != i) {
31194
+ let shadow = directLights[i];
31195
+ if (shadow.shadowParams.w < 0.5) {
31136
31196
  return 1.0;
31137
31197
  }
31138
- let p = worldPos + N * lights.shadowBias.y;
31139
- let lp = lights.shadowMatrix * vec4<f32>(p, 1.0);
31198
+ let p = worldPos + N * shadow.shadowBias.y;
31199
+ let lp = shadow.shadowMatrix * vec4<f32>(p, 1.0);
31140
31200
  let ndc = lp.xyz / lp.w;
31141
31201
  let uv = vec2<f32>(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);
31142
31202
  if (lp.w <= 0.0 || uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0 || ndc.z > 1.0) {
31143
31203
  return 1.0;
31144
31204
  }
31145
- let texel = 1.0 / max(lights.shadowParams.w, 1.0);
31146
- let radius = lights.shadowParams.z * texel;
31147
- let depthRef = ndc.z - lights.shadowBias.x;
31205
+ let texel = 1.0 / max(shadow.shadowParams.y, 1.0);
31206
+ let radius = shadow.shadowParams.x * texel;
31207
+ let depthRef = ndc.z - shadow.shadowBias.x;
31148
31208
  var lit = 0.0;
31149
31209
  for (var k = 0; k < 12; k++) {
31150
- lit += textureSampleCompareLevel(shadowMap, shadowSampler, uv + POISSON[k] * radius, depthRef);
31210
+ lit += textureSampleCompareLevel(shadowMap, shadowSampler, uv + POISSON[k] * radius, i32(shadow.shadowParams.z), depthRef);
31151
31211
  }
31152
31212
  lit = lit / 12.0;
31153
- return mix(1.0, lit, lights.shadowBias.z);
31213
+ return mix(1.0, lit, shadow.shadowBias.z);
31154
31214
  }
31155
31215
 
31156
31216
  // ── Lights ────────────────────────────────────────────────────────────
@@ -31163,7 +31223,7 @@ struct LightSample {
31163
31223
 
31164
31224
  fn sampleLight(i: i32, worldPos: vec3<f32>) -> LightSample {
31165
31225
  var out = LightSample(vec3<f32>(0.0, 0.0, 1.0), 1.0, false);
31166
- let light = lights.lights[i];
31226
+ let light = directLights[i];
31167
31227
  let lightType = light.posType.w;
31168
31228
  if (lightType > 0.5 && lightType < 1.5) {
31169
31229
  out.L = normalize(-light.dirRadius.xyz);
@@ -31212,15 +31272,12 @@ fn applyLighting3D(worldPos: vec3<f32>, worldNormal: vec3<f32>, baseColor: vec3<
31212
31272
 
31213
31273
  var totalIllumination = baseColor * (lights.ambientColor.rgb * ambientInt);
31214
31274
  let numLights = i32(lights.params.x);
31215
- for (var i = 0; i < 8; i++) {
31216
- if (i >= numLights) {
31217
- break;
31218
- }
31275
+ for (var i = 0; i < numLights; i++) {
31219
31276
  let ls = sampleLight(i, worldPos);
31220
31277
  if (!ls.valid) {
31221
31278
  continue;
31222
31279
  }
31223
- let light = lights.lights[i];
31280
+ let light = directLights[i];
31224
31281
  let lightColor = light.colorIntensity.rgb;
31225
31282
  let intensity = light.colorIntensity.w;
31226
31283
  let NdotL = max(dot(N, ls.L), 0.0);
@@ -31253,15 +31310,12 @@ fn shadePhysical(worldPos: vec3<f32>, worldNormal: vec3<f32>, albedo: vec3<f32>,
31253
31310
 
31254
31311
  var lo = vec3<f32>(0.0);
31255
31312
  let numLights = i32(lights.params.x);
31256
- for (var i = 0; i < 8; i++) {
31257
- if (i >= numLights) {
31258
- break;
31259
- }
31313
+ for (var i = 0; i < numLights; i++) {
31260
31314
  let ls = sampleLight(i, worldPos);
31261
31315
  if (!ls.valid) {
31262
31316
  continue;
31263
31317
  }
31264
- let light = lights.lights[i];
31318
+ let light = directLights[i];
31265
31319
  let nDotL = max(dot(N, ls.L), 0.0);
31266
31320
  if (nDotL <= 0.0) {
31267
31321
  continue;
@@ -31358,8 +31412,9 @@ fn shadeSurface(worldPos: vec3<f32>, worldNormal: vec3<f32>, baseColor: vec3<f32
31358
31412
  /** Group-3 bindings every lit 3D shader declares. */
31359
31413
  const lightsBindingsWgsl = `
31360
31414
  @group(3) @binding(0) var<uniform> lights : LightsUniforms;
31361
- @group(3) @binding(1) var shadowMap : texture_depth_2d;
31415
+ @group(3) @binding(1) var shadowMap : texture_depth_2d_array;
31362
31416
  @group(3) @binding(2) var shadowSampler : sampler_comparison;
31417
+ @group(3) @binding(3) var<storage, read> directLights : array<DirectLight>;
31363
31418
  `;
31364
31419
  /**
31365
31420
  * The 3D pipelines draw into one of four target sets: color; color + linear
@@ -32385,7 +32440,7 @@ fn matteCoverage(fragPos : vec2<f32>) -> f32 {
32385
32440
  }
32386
32441
  /**
32387
32442
  * WebGPU WGSL shaders for 3D static and skinned meshes.
32388
- * Supports GPU Linear Blend Skinning (up to 128 bones),
32443
+ * Supports GPU Linear Blend Skinning,
32389
32444
  * the shared 3D surface shading (Blinn-Phong, or physical GGX + image-based light
32390
32445
  * in scenes with an environment) or toon shading, shadow maps, emissive,
32391
32446
  * alpha-cutoff (glTF MASK) materials,
@@ -32398,7 +32453,7 @@ struct ModelUniforms {
32398
32453
  modelMatrix : mat4x4<f32>,
32399
32454
  normalMatrix : mat4x4<f32>,
32400
32455
  colorTint : vec4<f32>,
32401
- params : vec4<f32>, // x = opacity, y = twoSided, z = isSkinned, w = flags (bit 0 instanced, bit 1 map UVs)
32456
+ params : vec4<f32>, // x = opacity, y = twoSided, z = isSkinned, w = flags (bit 0 instanced, bit 1 map UVs, bit 2 matrix instances)
32402
32457
  materialParams : vec4<f32>, // x = shininess, y = specularIntensity, z = ambientIntensity, w = materialMode (0=unlit, 1=lit, 2=toon)
32403
32458
  shade : vec4<f32>, // rgb = toon shadow-side color multiplier, a = alpha cutoff (0 = none)
32404
32459
  pbr : vec4<f32>, // x = roughness (<0 = default), y = metallic
@@ -32407,7 +32462,7 @@ struct ModelUniforms {
32407
32462
  };
32408
32463
 
32409
32464
  struct SkinningUniforms {
32410
- jointMatrices : array<mat4x4<f32>, 128>,
32465
+ jointMatrices : array<mat4x4<f32>>,
32411
32466
  };
32412
32467
 
32413
32468
  struct InstanceData {
@@ -32419,7 +32474,7 @@ struct InstanceData {
32419
32474
 
32420
32475
  @group(0) @binding(0) var<uniform> cam : CameraUniforms;
32421
32476
  @group(1) @binding(0) var<uniform> model : ModelUniforms;
32422
- @group(1) @binding(1) var<uniform> skinning : SkinningUniforms;
32477
+ @group(1) @binding(1) var<storage, read> skinning : SkinningUniforms;
32423
32478
  ${matte3dWgsl(1, 2)}@group(1) @binding(3) var backdropTex : texture_2d<f32>;
32424
32479
  @group(1) @binding(4) var<storage, read> instances : array<InstanceData>;
32425
32480
  @group(2) @binding(0) var texSampler : sampler;
@@ -32505,7 +32560,14 @@ fn vs_main(
32505
32560
  var instColor = vec4<f32>(1.0);
32506
32561
  var instEmissive = vec4<f32>(0.0);
32507
32562
 
32508
- if ((u32(model.params.w) & 1u) != 0u) {
32563
+ if ((u32(model.params.w) & 4u) != 0u) {
32564
+ let a = instances[instanceIndex * 2u];
32565
+ let b = instances[instanceIndex * 2u + 1u];
32566
+ let instanceModel = mat4x4<f32>(a.posScale, a.quat, a.color, a.emissive);
32567
+ let instanceNormal = mat4x4<f32>(b.posScale, b.quat, b.color, b.emissive);
32568
+ localPos = (instanceModel * vec4<f32>(localPos, 1.0)).xyz;
32569
+ localNorm = (instanceNormal * vec4<f32>(localNorm, 0.0)).xyz;
32570
+ } else if ((u32(model.params.w) & 1u) != 0u) {
32509
32571
  let inst = instances[instanceIndex];
32510
32572
  let scaledPos = localPos * inst.posScale.w;
32511
32573
  localPos = rotateVecByQuat(scaledPos, inst.quat) + inst.posScale.xyz;
@@ -33057,12 +33119,20 @@ function createLightsLayout(device, label = "Lights3DLayout") {
33057
33119
  {
33058
33120
  binding: 1,
33059
33121
  visibility: GPUShaderStage.FRAGMENT,
33060
- texture: { sampleType: "depth" }
33122
+ texture: {
33123
+ sampleType: "depth",
33124
+ viewDimension: "2d-array"
33125
+ }
33061
33126
  },
33062
33127
  {
33063
33128
  binding: 2,
33064
33129
  visibility: GPUShaderStage.FRAGMENT,
33065
33130
  sampler: { type: "comparison" }
33131
+ },
33132
+ {
33133
+ binding: 3,
33134
+ visibility: GPUShaderStage.FRAGMENT,
33135
+ buffer: { type: "read-only-storage" }
33066
33136
  }
33067
33137
  ]
33068
33138
  });
@@ -33367,7 +33437,7 @@ var Mesh3DPipeline = class {
33367
33437
  constructor(device, format, depthFormat = "depth24plus", cameraLayout, lightsLayout, sampleCount = 1) {
33368
33438
  this.device = device;
33369
33439
  this.modelPool = new BufferPool((52 + SURFACE_PARAMS_FLOATS) * 4, GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST);
33370
- this.skinningPool = new BufferPool(8192, GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST);
33440
+ this.skinningPool = new BufferPool(64, GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST);
33371
33441
  const module$1 = device.createShaderModule({
33372
33442
  label: "mesh3d.wgsl",
33373
33443
  code: mesh3dWgsl
@@ -33391,7 +33461,7 @@ var Mesh3DPipeline = class {
33391
33461
  {
33392
33462
  binding: 1,
33393
33463
  visibility: GPUShaderStage.VERTEX,
33394
- buffer: { type: "uniform" }
33464
+ buffer: { type: "read-only-storage" }
33395
33465
  },
33396
33466
  matteLayoutEntry(2),
33397
33467
  matteLayoutEntry(3),
@@ -33642,16 +33712,11 @@ var Mesh3DPipeline = class {
33642
33712
  width: 1,
33643
33713
  height: 1
33644
33714
  });
33645
- const idMatrices = new Float32Array(2048);
33646
- for (let b = 0; b < 128; b++) {
33647
- idMatrices[b * 16] = 1;
33648
- idMatrices[b * 16 + 5] = 1;
33649
- idMatrices[b * 16 + 10] = 1;
33650
- idMatrices[b * 16 + 15] = 1;
33651
- }
33715
+ const idMatrices = new Float32Array(16);
33716
+ idMatrices[0] = idMatrices[5] = idMatrices[10] = idMatrices[15] = 1;
33652
33717
  this.defaultSkinningBuffer = device.createBuffer({
33653
- size: 8192,
33654
- usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
33718
+ size: 64,
33719
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
33655
33720
  mappedAtCreation: true
33656
33721
  });
33657
33722
  new Float32Array(this.defaultSkinningBuffer.getMappedRange()).set(idMatrices);
@@ -33690,7 +33755,7 @@ var Mesh3DPipeline = class {
33690
33755
  this.modelData[36] = data.opacity ?? 1;
33691
33756
  this.modelData[37] = data.twoSided ? 1 : 0;
33692
33757
  this.modelData[38] = data.isSkinned ? 1 : 0;
33693
- this.modelData[39] = (data.isInstanced || data.instanceBuffer ? 1 : 0) + (data.hasMapUvs ? 2 : 0);
33758
+ this.modelData[39] = (data.isInstanced || data.instanceBuffer ? 1 : 0) + (data.hasMapUvs ? 2 : 0) + (data.matrixInstances ? 4 : 0);
33694
33759
  const shininess = data.roughness !== void 0 ? Math.max(1, (1 - data.roughness) * 128) : data.shininess ?? 32;
33695
33760
  const specInt = data.metallic !== void 0 ? data.metallic : data.specularIntensity ?? .5;
33696
33761
  const ambientInt = data.ambientIntensity ?? 1;
@@ -33723,6 +33788,7 @@ var Mesh3DPipeline = class {
33723
33788
  this.modelData[51] = glass.ior;
33724
33789
  }
33725
33790
  const modelBuffer = this.modelPool.getBuffer(this.device, this.modelData);
33791
+ if (data.skinningMatrices && data.skinningMatrices.byteLength > this.device.limits.maxStorageBufferBindingSize) throw new Error(`Skinning data requires ${data.skinningMatrices.byteLength} bytes, exceeding the device storage binding limit of ${this.device.limits.maxStorageBufferBindingSize} bytes`);
33726
33792
  const skinningBuffer = data.isSkinned && data.skinningMatrices ? this.skinningPool.getBuffer(this.device, data.skinningMatrices) : this.defaultSkinningBuffer;
33727
33793
  return this.device.createBindGroup({
33728
33794
  layout: this.modelLayout,
@@ -34119,37 +34185,13 @@ var MotionBlurPipeline = class {
34119
34185
  }
34120
34186
  };
34121
34187
  function parseHexOrRgbColor(colorStr) {
34122
- const c = colorStr.trim();
34123
- if (c.startsWith("#")) {
34124
- const hex = c.slice(1);
34125
- if (hex.length === 3) return [
34126
- Number.parseInt(hex[0] + hex[0], 16) / 255,
34127
- Number.parseInt(hex[1] + hex[1], 16) / 255,
34128
- Number.parseInt(hex[2] + hex[2], 16) / 255,
34129
- 1
34130
- ];
34131
- if (hex.length === 6) return [
34132
- Number.parseInt(hex.slice(0, 2), 16) / 255,
34133
- Number.parseInt(hex.slice(2, 4), 16) / 255,
34134
- Number.parseInt(hex.slice(4, 6), 16) / 255,
34135
- 1
34136
- ];
34137
- if (hex.length === 8) return [
34138
- Number.parseInt(hex.slice(0, 2), 16) / 255,
34139
- Number.parseInt(hex.slice(2, 4), 16) / 255,
34140
- Number.parseInt(hex.slice(4, 6), 16) / 255,
34141
- Number.parseInt(hex.slice(6, 8), 16) / 255
34142
- ];
34143
- }
34144
- if (c.startsWith("rgb")) {
34145
- const parts = c.replace(/rgba?\(/, "").replace(/\)/, "").split(",").map((p) => Number.parseFloat(p.trim()));
34146
- return [
34147
- (parts[0] ?? 0) / 255,
34148
- (parts[1] ?? 0) / 255,
34149
- (parts[2] ?? 0) / 255,
34150
- parts[3] !== void 0 ? parts[3] : 1
34151
- ];
34152
- }
34188
+ const c = parseColor(colorStr);
34189
+ if (c.a > 0 || colorStr.trim().toLowerCase() === "transparent") return [
34190
+ c.r,
34191
+ c.g,
34192
+ c.b,
34193
+ c.a
34194
+ ];
34153
34195
  return [
34154
34196
  1,
34155
34197
  1,
@@ -34298,7 +34340,7 @@ function extractGlyphContours(glyph, glyphX, glyphY, scale, flattening) {
34298
34340
  };
34299
34341
  }
34300
34342
  /** Side-wall segments meeting at more than this angle (30°) keep a hard edge. */
34301
- const SIDE_CREASE_COS = Math.cos(30 * Math.PI / 180);
34343
+ const SIDE_CREASE_COS$1 = Math.cos(30 * Math.PI / 180);
34302
34344
  /**
34303
34345
  * Group a glyph's contours into filled outlines and their holes by nesting
34304
34346
  * depth: a contour inside an even number of others is an outline (the ring
@@ -34597,7 +34639,7 @@ function generateExtrudedTextGeometry(options) {
34597
34639
  const u = along / perimeter;
34598
34640
  const sx = incoming.x + outgoing.x;
34599
34641
  const sy = incoming.y + outgoing.y;
34600
- const averaged = incoming.x * outgoing.x + incoming.y * outgoing.y >= SIDE_CREASE_COS ? {
34642
+ const averaged = incoming.x * outgoing.x + incoming.y * outgoing.y >= SIDE_CREASE_COS$1 ? {
34601
34643
  x: sx / Math.hypot(sx, sy),
34602
34644
  y: sy / Math.hypot(sx, sy)
34603
34645
  } : null;
@@ -34736,11 +34778,14 @@ var MeshBuilder = class {
34736
34778
  positions = [];
34737
34779
  normals = [];
34738
34780
  uvs = [];
34781
+ colors = [];
34739
34782
  indices = [];
34740
- vertex(p, n, uv) {
34783
+ topology;
34784
+ vertex(p, n, uv, color) {
34741
34785
  this.positions.push(p[0], p[1], p[2]);
34742
34786
  this.normals.push(n[0], n[1], n[2]);
34743
34787
  this.uvs.push(uv[0], uv[1]);
34788
+ if (color) this.colors.push(color[0], color[1], color[2], color[3]);
34744
34789
  return this.positions.length / 3 - 1;
34745
34790
  }
34746
34791
  /**
@@ -34760,7 +34805,7 @@ var MeshBuilder = class {
34760
34805
  }
34761
34806
  build(name) {
34762
34807
  const count = this.positions.length / 3;
34763
- return {
34808
+ const mesh = {
34764
34809
  id: `${name}-mesh`,
34765
34810
  name,
34766
34811
  positions: new Float32Array(this.positions),
@@ -34769,6 +34814,9 @@ var MeshBuilder = class {
34769
34814
  indices: count > 65535 ? new Uint32Array(this.indices) : new Uint16Array(this.indices),
34770
34815
  materialName: `${name}-material`
34771
34816
  };
34817
+ if (this.topology) mesh.topology = this.topology;
34818
+ if (this.colors.length === count * 4 && this.colors.length > 0) mesh.colors = new Float32Array(this.colors);
34819
+ return mesh;
34772
34820
  }
34773
34821
  };
34774
34822
  function normalize(v) {
@@ -34783,6 +34831,23 @@ function normalize(v) {
34783
34831
  0
34784
34832
  ];
34785
34833
  }
34834
+ function sub3(a, b) {
34835
+ return [
34836
+ a[0] - b[0],
34837
+ a[1] - b[1],
34838
+ a[2] - b[2]
34839
+ ];
34840
+ }
34841
+ function dot3(a, b) {
34842
+ return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
34843
+ }
34844
+ function cross3(a, b) {
34845
+ return [
34846
+ a[1] * b[2] - a[2] * b[1],
34847
+ a[2] * b[0] - a[0] * b[2],
34848
+ a[0] * b[1] - a[1] * b[0]
34849
+ ];
34850
+ }
34786
34851
  /**
34787
34852
  * 1D samples across a side of half-extent `e` whose ends round off with
34788
34853
  * radius `r`: arc-spaced through each rounded zone, nothing in between, so
@@ -34943,29 +35008,87 @@ function sphere(o, mb) {
34943
35008
  }
34944
35009
  function cylinder(o, mb) {
34945
35010
  const seg = Math.max(3, Math.round(o.segments ?? 32));
34946
- const rTop = o.radiusTop ?? o.radius ?? 50;
34947
- const rBottom = o.radiusBottom ?? o.radius ?? 50;
35011
+ const rTop = Math.max(0, o.radiusTop ?? o.radius ?? 50);
35012
+ const rBottom = Math.max(0, o.radiusBottom ?? o.radius ?? 50);
34948
35013
  const hy = o.height / 2;
34949
- const lean = o.height > 0 ? (rBottom - rTop) / o.height : 0;
34950
- for (let j = 0; j <= 1; j++) {
34951
- const rr = j === 0 ? rTop : rBottom;
34952
- const y = j === 0 ? -hy : hy;
34953
- for (let i = 0; i <= seg; i++) {
34954
- const theta = i / seg * Math.PI * 2;
34955
- const c = Math.cos(theta);
34956
- const s = Math.sin(theta);
34957
- mb.vertex([
34958
- c * rr,
34959
- y,
34960
- s * rr
34961
- ], normalize([
34962
- c,
34963
- -lean,
34964
- s
34965
- ]), [i / seg, j]);
35014
+ if (o.flatShaded) for (let i = 0; i < seg; i++) {
35015
+ const theta0 = i / seg * Math.PI * 2;
35016
+ const theta1 = (i + 1) / seg * Math.PI * 2;
35017
+ const c0 = Math.cos(theta0);
35018
+ const s0 = Math.sin(theta0);
35019
+ const c1 = Math.cos(theta1);
35020
+ const s1 = Math.sin(theta1);
35021
+ const t0 = [
35022
+ c0 * rTop,
35023
+ -hy,
35024
+ s0 * rTop
35025
+ ];
35026
+ const t1 = [
35027
+ c1 * rTop,
35028
+ -hy,
35029
+ s1 * rTop
35030
+ ];
35031
+ const b0 = [
35032
+ c0 * rBottom,
35033
+ hy,
35034
+ s0 * rBottom
35035
+ ];
35036
+ const b1 = [
35037
+ c1 * rBottom,
35038
+ hy,
35039
+ s1 * rBottom
35040
+ ];
35041
+ const fn = normalize(cross3(sub3(b0, t0), rBottom >= rTop ? sub3(b1, b0) : sub3(t1, t0)));
35042
+ const u0 = i / seg;
35043
+ const u1 = (i + 1) / seg;
35044
+ if (rTop <= 1e-6) {
35045
+ const apex = mb.vertex([
35046
+ 0,
35047
+ -hy,
35048
+ 0
35049
+ ], fn, [(u0 + u1) / 2, 0]);
35050
+ const vB0 = mb.vertex(b0, fn, [u0, 1]);
35051
+ const vB1 = mb.vertex(b1, fn, [u1, 1]);
35052
+ mb.indices.push(apex, vB0, vB1);
35053
+ } else if (rBottom <= 1e-6) {
35054
+ const vT0 = mb.vertex(t0, fn, [u0, 0]);
35055
+ const vT1 = mb.vertex(t1, fn, [u1, 0]);
35056
+ const apex = mb.vertex([
35057
+ 0,
35058
+ hy,
35059
+ 0
35060
+ ], fn, [(u0 + u1) / 2, 1]);
35061
+ mb.indices.push(vT0, apex, vT1);
35062
+ } else {
35063
+ const base = mb.vertex(t0, fn, [u0, 0]);
35064
+ mb.vertex(t1, fn, [u1, 0]);
35065
+ mb.vertex(b0, fn, [u0, 1]);
35066
+ mb.vertex(b1, fn, [u1, 1]);
35067
+ mb.indices.push(base, base + 2, base + 1, base + 1, base + 2, base + 3);
35068
+ }
35069
+ }
35070
+ else {
35071
+ const lean = o.height > 0 ? (rBottom - rTop) / o.height : 0;
35072
+ for (let j = 0; j <= 1; j++) {
35073
+ const rr = j === 0 ? rTop : rBottom;
35074
+ const y = j === 0 ? -hy : hy;
35075
+ for (let i = 0; i <= seg; i++) {
35076
+ const theta = i / seg * Math.PI * 2;
35077
+ const c = Math.cos(theta);
35078
+ const s = Math.sin(theta);
35079
+ mb.vertex([
35080
+ c * rr,
35081
+ y,
35082
+ s * rr
35083
+ ], normalize([
35084
+ c,
35085
+ -lean,
35086
+ s
35087
+ ]), [i / seg, j]);
35088
+ }
34966
35089
  }
35090
+ mb.grid(0, seg, 1);
34967
35091
  }
34968
- mb.grid(0, seg, 1);
34969
35092
  if (o.openEnded) return;
34970
35093
  for (const [y, rr, ny] of [[
34971
35094
  -hy,
@@ -34976,7 +35099,7 @@ function cylinder(o, mb) {
34976
35099
  rBottom,
34977
35100
  1
34978
35101
  ]]) {
34979
- if (rr <= 0) continue;
35102
+ if (rr <= 1e-6) continue;
34980
35103
  const center = mb.vertex([
34981
35104
  0,
34982
35105
  y,
@@ -34986,7 +35109,7 @@ function cylinder(o, mb) {
34986
35109
  ny,
34987
35110
  0
34988
35111
  ], [.5, .5]);
34989
- const ring = mb.positions.length / 3;
35112
+ const ring$1 = mb.positions.length / 3;
34990
35113
  for (let i = 0; i <= seg; i++) {
34991
35114
  const theta = i / seg * Math.PI * 2;
34992
35115
  const c = Math.cos(theta);
@@ -35001,98 +35124,2015 @@ function cylinder(o, mb) {
35001
35124
  0
35002
35125
  ], [(c + 1) / 2, (s + 1) / 2]);
35003
35126
  }
35004
- for (let i = 0; i < seg; i++) if (ny < 0) mb.indices.push(center, ring + i, ring + i + 1);
35005
- else mb.indices.push(center, ring + i + 1, ring + i);
35127
+ for (let i = 0; i < seg; i++) if (ny < 0) mb.indices.push(center, ring$1 + i, ring$1 + i + 1);
35128
+ else mb.indices.push(center, ring$1 + i + 1, ring$1 + i);
35006
35129
  }
35007
35130
  }
35008
- function shard(o, mb) {
35009
- const r = o.radius ?? 50;
35010
- const hx = (o.size ? o.size[0] : r) || 50;
35011
- const hy = (o.size ? o.size[1] : r) || 50;
35012
- const hz = (o.size ? o.size[2] : r) || 50;
35013
- const maxR = Math.max(hx, hy, hz);
35014
- const facets = Math.max(4, Math.min(64, Math.round(o.facets ?? 14)));
35015
- const seed = Math.round(o.seed ?? 1) >>> 0;
35016
- const irregularity = Math.max(0, Math.min(.85, o.irregularity ?? .35));
35017
- let state = BigInt(seed) * 6364136223846793005n + 1442695040888963407n;
35018
- const rnd = () => {
35019
- state = state * 6364136223846793005n + 1442695040888963407n;
35020
- const xorshifted = Number((state >> 18n ^ state) >> 27n) >>> 0;
35021
- const rot = Number(state >> 59n) >>> 0;
35022
- return ((xorshifted >>> rot | xorshifted << (32 - rot & 31)) >>> 0) / 4294967296;
35023
- };
35024
- const sub3 = (a, b) => [
35025
- a[0] - b[0],
35026
- a[1] - b[1],
35027
- a[2] - b[2]
35028
- ];
35029
- const dot3 = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
35030
- const cross3 = (a, b) => [
35031
- a[1] * b[2] - a[2] * b[1],
35032
- a[2] * b[0] - a[0] * b[2],
35033
- a[0] * b[1] - a[1] * b[0]
35034
- ];
35035
- let faces = [
35036
- [
35037
- [
35038
- -hx,
35039
- -hy,
35131
+ /**
35132
+ * Torus centered at origin lying in the XY plane (so its hole faces a default
35133
+ * camera looking along +Z), with major radius `radius` and tube radius `tube`.
35134
+ */
35135
+ function torus(o, mb) {
35136
+ const R = Math.max(.001, o.radius ?? 60);
35137
+ const r = Math.max(.001, o.tube ?? 20);
35138
+ const radialSegments = Math.max(3, Math.round(o.radialSegments ?? 24));
35139
+ const tubularSegments = Math.max(3, Math.round(o.tubularSegments ?? 48));
35140
+ const arcRad = (o.arc ?? 360) * Math.PI / 180;
35141
+ const base = mb.positions.length / 3;
35142
+ for (let j = 0; j <= radialSegments; j++) {
35143
+ const v = j / radialSegments * Math.PI * 2;
35144
+ const cv = Math.cos(v);
35145
+ const sv = Math.sin(v);
35146
+ for (let i = 0; i <= tubularSegments; i++) {
35147
+ const u = i / tubularSegments * arcRad;
35148
+ const cu = Math.cos(u);
35149
+ const su = Math.sin(u);
35150
+ const x = (R + r * cv) * cu;
35151
+ const y = (R + r * cv) * su;
35152
+ const z = r * sv;
35153
+ const nx = cv * cu;
35154
+ const ny = cv * su;
35155
+ const nz = sv;
35156
+ mb.vertex([
35157
+ x,
35158
+ y,
35159
+ z
35160
+ ], normalize([
35161
+ nx,
35162
+ ny,
35163
+ nz
35164
+ ]), [i / tubularSegments, j / radialSegments]);
35165
+ }
35166
+ }
35167
+ mb.grid(base, tubularSegments, radialSegments, true);
35168
+ if (Math.abs(arcRad) < Math.PI * 2 - 1e-4) for (const [u, sign] of [[0, -1], [arcRad, 1]]) {
35169
+ const cu = Math.cos(u);
35170
+ const su = Math.sin(u);
35171
+ const capN = [
35172
+ -su * sign,
35173
+ cu * sign,
35174
+ 0
35175
+ ];
35176
+ const center = mb.vertex([
35177
+ R * cu,
35178
+ R * su,
35179
+ 0
35180
+ ], capN, [.5, .5]);
35181
+ const ring$1 = mb.positions.length / 3;
35182
+ for (let j = 0; j <= radialSegments; j++) {
35183
+ const v = j / radialSegments * Math.PI * 2;
35184
+ const cv = Math.cos(v);
35185
+ const sv = Math.sin(v);
35186
+ mb.vertex([
35187
+ (R + r * cv) * cu,
35188
+ (R + r * cv) * su,
35189
+ r * sv
35190
+ ], capN, [(cv + 1) / 2, (sv + 1) / 2]);
35191
+ }
35192
+ for (let j = 0; j < radialSegments; j++) if (sign < 0) mb.indices.push(center, ring$1 + j, ring$1 + j + 1);
35193
+ else mb.indices.push(center, ring$1 + j + 1, ring$1 + j);
35194
+ }
35195
+ }
35196
+ /**
35197
+ * Capsule aligned along the Y axis: a cylinder of height `length` and radius `radius`
35198
+ * capped by top (-y) and bottom (+y) hemispheres of radius `radius`.
35199
+ */
35200
+ function capsule(o, mb) {
35201
+ const r = Math.max(.001, o.radius ?? 30);
35202
+ const length = Math.max(0, o.length ?? 60);
35203
+ const hy = length / 2;
35204
+ const capSeg = Math.max(2, Math.round(o.capSegments ?? 12));
35205
+ const radSeg = Math.max(3, Math.round(o.radialSegments ?? 32));
35206
+ const rings = [];
35207
+ const totalArc = Math.PI * r + length;
35208
+ for (let j = 0; j <= capSeg; j++) {
35209
+ const phi = j / capSeg * Math.PI / 2;
35210
+ const s = Math.sin(phi);
35211
+ const c = Math.cos(phi);
35212
+ rings.push({
35213
+ y: -hy - r * c,
35214
+ rRing: r * s,
35215
+ ny: -c,
35216
+ nr: s,
35217
+ v: phi * r / (totalArc || 1)
35218
+ });
35219
+ }
35220
+ for (let j = 0; j <= capSeg; j++) {
35221
+ if (j === 0 && length <= 1e-6) continue;
35222
+ const phi = Math.PI / 2 + j / capSeg * Math.PI / 2;
35223
+ const s = Math.sin(phi);
35224
+ const c = Math.cos(phi);
35225
+ rings.push({
35226
+ y: hy - r * c,
35227
+ rRing: r * s,
35228
+ ny: -c,
35229
+ nr: s,
35230
+ v: (Math.PI / 2 * r + length + (phi - Math.PI / 2) * r) / (totalArc || 1)
35231
+ });
35232
+ }
35233
+ const base = mb.positions.length / 3;
35234
+ for (const ring$1 of rings) for (let i = 0; i <= radSeg; i++) {
35235
+ const theta = i / radSeg * Math.PI * 2;
35236
+ const ct = Math.cos(theta);
35237
+ const st = Math.sin(theta);
35238
+ const n = normalize([
35239
+ ring$1.nr * ct,
35240
+ ring$1.ny,
35241
+ ring$1.nr * st
35242
+ ]);
35243
+ mb.vertex([
35244
+ ring$1.rRing * ct,
35245
+ ring$1.y,
35246
+ ring$1.rRing * st
35247
+ ], n, [i / radSeg, ring$1.v]);
35248
+ }
35249
+ mb.grid(base, radSeg, rings.length - 1, false);
35250
+ }
35251
+ /**
35252
+ * Flat or thick-walled annulus (ring) in the XY plane facing -Z (front) and +Z (back).
35253
+ * Always builds a closed two-sided or solid manifold so it works with single-sided culling.
35254
+ */
35255
+ function ring(o, mb) {
35256
+ const rInner = Math.max(0, o.innerRadius ?? 30);
35257
+ const rOuter = Math.max(rInner + .001, o.outerRadius ?? 60);
35258
+ const hz = Math.max(0, o.depth ?? 0) / 2;
35259
+ const thetaSeg = Math.max(3, Math.round(o.thetaSegments ?? 48));
35260
+ const phiSeg = Math.max(1, Math.round(o.phiSegments ?? 1));
35261
+ const thetaStart = (o.thetaStart ?? 0) * Math.PI / 180;
35262
+ const thetaLength = (o.thetaLength ?? 360) * Math.PI / 180;
35263
+ for (const [z, nz, flip] of [[
35264
+ -hz,
35265
+ -1,
35266
+ false
35267
+ ], [
35268
+ hz,
35269
+ 1,
35270
+ true
35271
+ ]]) {
35272
+ const base = mb.positions.length / 3;
35273
+ for (let j = 0; j <= phiSeg; j++) {
35274
+ const radius = rInner + (rOuter - rInner) * j / phiSeg;
35275
+ for (let i = 0; i <= thetaSeg; i++) {
35276
+ const theta = thetaStart + i / thetaSeg * thetaLength;
35277
+ const c = Math.cos(theta);
35278
+ const s = Math.sin(theta);
35279
+ const x = radius * c;
35280
+ const y = radius * s;
35281
+ mb.vertex([
35282
+ x,
35283
+ y,
35284
+ z
35285
+ ], [
35286
+ 0,
35287
+ 0,
35288
+ nz
35289
+ ], [(x / rOuter + 1) / 2, (y / rOuter + 1) / 2]);
35290
+ }
35291
+ }
35292
+ mb.grid(base, thetaSeg, phiSeg, !flip);
35293
+ }
35294
+ if (hz > 0) {
35295
+ const outerBase = mb.positions.length / 3;
35296
+ for (const [z, v] of [[-hz, 0], [hz, 1]]) for (let i = 0; i <= thetaSeg; i++) {
35297
+ const theta = thetaStart + i / thetaSeg * thetaLength;
35298
+ const c = Math.cos(theta);
35299
+ const s = Math.sin(theta);
35300
+ mb.vertex([
35301
+ rOuter * c,
35302
+ rOuter * s,
35303
+ z
35304
+ ], [
35305
+ c,
35306
+ s,
35307
+ 0
35308
+ ], [i / thetaSeg, v]);
35309
+ }
35310
+ mb.grid(outerBase, thetaSeg, 1, true);
35311
+ if (rInner > 1e-6) {
35312
+ const innerBase = mb.positions.length / 3;
35313
+ for (const [z, v] of [[-hz, 0], [hz, 1]]) for (let i = 0; i <= thetaSeg; i++) {
35314
+ const theta = thetaStart + i / thetaSeg * thetaLength;
35315
+ const c = Math.cos(theta);
35316
+ const s = Math.sin(theta);
35317
+ mb.vertex([
35318
+ rInner * c,
35319
+ rInner * s,
35320
+ z
35321
+ ], [
35322
+ -c,
35323
+ -s,
35324
+ 0
35325
+ ], [i / thetaSeg, v]);
35326
+ }
35327
+ mb.grid(innerBase, thetaSeg, 1, false);
35328
+ }
35329
+ if (Math.abs(thetaLength) < Math.PI * 2 - 1e-4) for (const [theta, sign] of [[thetaStart, -1], [thetaStart + thetaLength, 1]]) {
35330
+ const c = Math.cos(theta);
35331
+ const s = Math.sin(theta);
35332
+ const capN = [
35333
+ -s * sign,
35334
+ c * sign,
35335
+ 0
35336
+ ];
35337
+ const p0 = mb.vertex([
35338
+ rInner * c,
35339
+ rInner * s,
35340
+ -hz
35341
+ ], capN, [0, 0]);
35342
+ const p1 = mb.vertex([
35343
+ rOuter * c,
35344
+ rOuter * s,
35040
35345
  -hz
35346
+ ], capN, [1, 0]);
35347
+ const p2 = mb.vertex([
35348
+ rInner * c,
35349
+ rInner * s,
35350
+ hz
35351
+ ], capN, [0, 1]);
35352
+ const p3 = mb.vertex([
35353
+ rOuter * c,
35354
+ rOuter * s,
35355
+ hz
35356
+ ], capN, [1, 1]);
35357
+ if (sign > 0) mb.indices.push(p0, p2, p1, p1, p2, p3);
35358
+ else mb.indices.push(p0, p1, p2, p1, p3, p2);
35359
+ }
35360
+ }
35361
+ }
35362
+ /**
35363
+ * Platonic solids (tetrahedron, octahedron, dodecahedron, icosahedron) and
35364
+ * geodesic icosphere with configurable subdivision `detail`.
35365
+ */
35366
+ function polyhedron(o, mb) {
35367
+ const shape = o.shape ?? "icosahedron";
35368
+ const radius = Math.max(.001, o.radius ?? 50);
35369
+ const detail = Math.max(0, Math.min(5, Math.round(o.detail ?? (shape === "icosphere" ? 2 : 0))));
35370
+ const flatShaded = o.flatShaded ?? shape !== "icosphere";
35371
+ let baseVerts = [];
35372
+ let baseFaces = [];
35373
+ if (shape === "tetrahedron") {
35374
+ baseVerts = [
35375
+ [
35376
+ 1,
35377
+ 1,
35378
+ 1
35041
35379
  ],
35042
35380
  [
35043
- hx,
35044
- -hy,
35045
- -hz
35381
+ -1,
35382
+ -1,
35383
+ 1
35046
35384
  ],
35047
35385
  [
35048
- hx,
35049
- hy,
35050
- -hz
35386
+ -1,
35387
+ 1,
35388
+ -1
35051
35389
  ],
35052
35390
  [
35053
- -hx,
35054
- hy,
35055
- -hz
35391
+ 1,
35392
+ -1,
35393
+ -1
35056
35394
  ]
35057
- ],
35058
- [
35395
+ ];
35396
+ baseFaces = [
35059
35397
  [
35060
- hx,
35061
- -hy,
35062
- hz
35398
+ 2,
35399
+ 1,
35400
+ 0
35063
35401
  ],
35064
35402
  [
35065
- -hx,
35066
- -hy,
35067
- hz
35403
+ 0,
35404
+ 3,
35405
+ 2
35068
35406
  ],
35069
35407
  [
35070
- -hx,
35071
- hy,
35072
- hz
35408
+ 1,
35409
+ 3,
35410
+ 0
35073
35411
  ],
35074
35412
  [
35075
- hx,
35076
- hy,
35077
- hz
35413
+ 2,
35414
+ 3,
35415
+ 1
35078
35416
  ]
35079
- ],
35080
- [
35417
+ ];
35418
+ } else if (shape === "octahedron") {
35419
+ baseVerts = [
35081
35420
  [
35082
- -hx,
35083
- -hy,
35084
- hz
35421
+ 1,
35422
+ 0,
35423
+ 0
35085
35424
  ],
35086
35425
  [
35087
- -hx,
35088
- -hy,
35089
- -hz
35426
+ -1,
35427
+ 0,
35428
+ 0
35090
35429
  ],
35091
35430
  [
35092
- -hx,
35093
- hy,
35094
- -hz
35095
- ],
35431
+ 0,
35432
+ 1,
35433
+ 0
35434
+ ],
35435
+ [
35436
+ 0,
35437
+ -1,
35438
+ 0
35439
+ ],
35440
+ [
35441
+ 0,
35442
+ 0,
35443
+ 1
35444
+ ],
35445
+ [
35446
+ 0,
35447
+ 0,
35448
+ -1
35449
+ ]
35450
+ ];
35451
+ baseFaces = [
35452
+ [
35453
+ 0,
35454
+ 2,
35455
+ 4
35456
+ ],
35457
+ [
35458
+ 0,
35459
+ 4,
35460
+ 3
35461
+ ],
35462
+ [
35463
+ 0,
35464
+ 3,
35465
+ 5
35466
+ ],
35467
+ [
35468
+ 0,
35469
+ 5,
35470
+ 2
35471
+ ],
35472
+ [
35473
+ 1,
35474
+ 2,
35475
+ 5
35476
+ ],
35477
+ [
35478
+ 1,
35479
+ 5,
35480
+ 3
35481
+ ],
35482
+ [
35483
+ 1,
35484
+ 3,
35485
+ 4
35486
+ ],
35487
+ [
35488
+ 1,
35489
+ 4,
35490
+ 2
35491
+ ]
35492
+ ];
35493
+ } else if (shape === "dodecahedron") {
35494
+ const t = (1 + Math.sqrt(5)) / 2;
35495
+ const r = 1 / t;
35496
+ baseVerts = [
35497
+ [
35498
+ -1,
35499
+ -1,
35500
+ -1
35501
+ ],
35502
+ [
35503
+ -1,
35504
+ -1,
35505
+ 1
35506
+ ],
35507
+ [
35508
+ -1,
35509
+ 1,
35510
+ -1
35511
+ ],
35512
+ [
35513
+ -1,
35514
+ 1,
35515
+ 1
35516
+ ],
35517
+ [
35518
+ 1,
35519
+ -1,
35520
+ -1
35521
+ ],
35522
+ [
35523
+ 1,
35524
+ -1,
35525
+ 1
35526
+ ],
35527
+ [
35528
+ 1,
35529
+ 1,
35530
+ -1
35531
+ ],
35532
+ [
35533
+ 1,
35534
+ 1,
35535
+ 1
35536
+ ],
35537
+ [
35538
+ 0,
35539
+ -r,
35540
+ -t
35541
+ ],
35542
+ [
35543
+ 0,
35544
+ -r,
35545
+ t
35546
+ ],
35547
+ [
35548
+ 0,
35549
+ r,
35550
+ -t
35551
+ ],
35552
+ [
35553
+ 0,
35554
+ r,
35555
+ t
35556
+ ],
35557
+ [
35558
+ -r,
35559
+ -t,
35560
+ 0
35561
+ ],
35562
+ [
35563
+ -r,
35564
+ t,
35565
+ 0
35566
+ ],
35567
+ [
35568
+ r,
35569
+ -t,
35570
+ 0
35571
+ ],
35572
+ [
35573
+ r,
35574
+ t,
35575
+ 0
35576
+ ],
35577
+ [
35578
+ -t,
35579
+ 0,
35580
+ -r
35581
+ ],
35582
+ [
35583
+ t,
35584
+ 0,
35585
+ -r
35586
+ ],
35587
+ [
35588
+ -t,
35589
+ 0,
35590
+ r
35591
+ ],
35592
+ [
35593
+ t,
35594
+ 0,
35595
+ r
35596
+ ]
35597
+ ];
35598
+ const pentagons = [
35599
+ [
35600
+ 3,
35601
+ 11,
35602
+ 7,
35603
+ 15,
35604
+ 13
35605
+ ],
35606
+ [
35607
+ 7,
35608
+ 19,
35609
+ 17,
35610
+ 6,
35611
+ 15
35612
+ ],
35613
+ [
35614
+ 17,
35615
+ 4,
35616
+ 8,
35617
+ 10,
35618
+ 6
35619
+ ],
35620
+ [
35621
+ 8,
35622
+ 0,
35623
+ 16,
35624
+ 2,
35625
+ 10
35626
+ ],
35627
+ [
35628
+ 0,
35629
+ 12,
35630
+ 1,
35631
+ 18,
35632
+ 16
35633
+ ],
35634
+ [
35635
+ 6,
35636
+ 10,
35637
+ 2,
35638
+ 13,
35639
+ 15
35640
+ ],
35641
+ [
35642
+ 2,
35643
+ 16,
35644
+ 18,
35645
+ 3,
35646
+ 13
35647
+ ],
35648
+ [
35649
+ 18,
35650
+ 1,
35651
+ 9,
35652
+ 11,
35653
+ 3
35654
+ ],
35655
+ [
35656
+ 4,
35657
+ 14,
35658
+ 12,
35659
+ 0,
35660
+ 8
35661
+ ],
35662
+ [
35663
+ 11,
35664
+ 9,
35665
+ 5,
35666
+ 19,
35667
+ 7
35668
+ ],
35669
+ [
35670
+ 19,
35671
+ 5,
35672
+ 14,
35673
+ 4,
35674
+ 17
35675
+ ],
35676
+ [
35677
+ 1,
35678
+ 12,
35679
+ 14,
35680
+ 5,
35681
+ 9
35682
+ ]
35683
+ ];
35684
+ if (detail === 0 && flatShaded) {
35685
+ const unitVerts = baseVerts.map((v) => normalize(v));
35686
+ for (const pent of pentagons) {
35687
+ const pts = pent.map((idx) => unitVerts[idx]);
35688
+ const center = [
35689
+ pts.reduce((s, p) => s + p[0], 0) / 5,
35690
+ pts.reduce((s, p) => s + p[1], 0) / 5,
35691
+ pts.reduce((s, p) => s + p[2], 0) / 5
35692
+ ];
35693
+ let fn = normalize(cross3(sub3(pts[1], pts[0]), sub3(pts[2], pts[0])));
35694
+ if (dot3(fn, center) < 0) {
35695
+ pts.reverse();
35696
+ fn = [
35697
+ -fn[0],
35698
+ -fn[1],
35699
+ -fn[2]
35700
+ ];
35701
+ }
35702
+ const base = mb.positions.length / 3;
35703
+ for (const p of pts) {
35704
+ const u = .5 + Math.atan2(p[2], p[0]) / (2 * Math.PI);
35705
+ const v = Math.acos(Math.max(-1, Math.min(1, -p[1]))) / Math.PI;
35706
+ mb.vertex([
35707
+ p[0] * radius,
35708
+ p[1] * radius,
35709
+ p[2] * radius
35710
+ ], fn, [u, v]);
35711
+ }
35712
+ for (let k = 1; k < pts.length - 1; k++) mb.indices.push(base, base + k, base + k + 1);
35713
+ }
35714
+ return;
35715
+ }
35716
+ for (const p of pentagons) baseFaces.push([
35717
+ p[0],
35718
+ p[1],
35719
+ p[2]
35720
+ ], [
35721
+ p[0],
35722
+ p[2],
35723
+ p[3]
35724
+ ], [
35725
+ p[0],
35726
+ p[3],
35727
+ p[4]
35728
+ ]);
35729
+ } else {
35730
+ const t = (1 + Math.sqrt(5)) / 2;
35731
+ baseVerts = [
35732
+ [
35733
+ -1,
35734
+ t,
35735
+ 0
35736
+ ],
35737
+ [
35738
+ 1,
35739
+ t,
35740
+ 0
35741
+ ],
35742
+ [
35743
+ -1,
35744
+ -t,
35745
+ 0
35746
+ ],
35747
+ [
35748
+ 1,
35749
+ -t,
35750
+ 0
35751
+ ],
35752
+ [
35753
+ 0,
35754
+ -1,
35755
+ t
35756
+ ],
35757
+ [
35758
+ 0,
35759
+ 1,
35760
+ t
35761
+ ],
35762
+ [
35763
+ 0,
35764
+ -1,
35765
+ -t
35766
+ ],
35767
+ [
35768
+ 0,
35769
+ 1,
35770
+ -t
35771
+ ],
35772
+ [
35773
+ t,
35774
+ 0,
35775
+ -1
35776
+ ],
35777
+ [
35778
+ t,
35779
+ 0,
35780
+ 1
35781
+ ],
35782
+ [
35783
+ -t,
35784
+ 0,
35785
+ -1
35786
+ ],
35787
+ [
35788
+ -t,
35789
+ 0,
35790
+ 1
35791
+ ]
35792
+ ];
35793
+ baseFaces = [
35794
+ [
35795
+ 0,
35796
+ 11,
35797
+ 5
35798
+ ],
35799
+ [
35800
+ 0,
35801
+ 5,
35802
+ 1
35803
+ ],
35804
+ [
35805
+ 0,
35806
+ 1,
35807
+ 7
35808
+ ],
35809
+ [
35810
+ 0,
35811
+ 7,
35812
+ 10
35813
+ ],
35814
+ [
35815
+ 0,
35816
+ 10,
35817
+ 11
35818
+ ],
35819
+ [
35820
+ 1,
35821
+ 5,
35822
+ 9
35823
+ ],
35824
+ [
35825
+ 5,
35826
+ 11,
35827
+ 4
35828
+ ],
35829
+ [
35830
+ 11,
35831
+ 10,
35832
+ 2
35833
+ ],
35834
+ [
35835
+ 10,
35836
+ 7,
35837
+ 6
35838
+ ],
35839
+ [
35840
+ 7,
35841
+ 1,
35842
+ 8
35843
+ ],
35844
+ [
35845
+ 3,
35846
+ 9,
35847
+ 4
35848
+ ],
35849
+ [
35850
+ 3,
35851
+ 4,
35852
+ 2
35853
+ ],
35854
+ [
35855
+ 3,
35856
+ 2,
35857
+ 6
35858
+ ],
35859
+ [
35860
+ 3,
35861
+ 6,
35862
+ 8
35863
+ ],
35864
+ [
35865
+ 3,
35866
+ 8,
35867
+ 9
35868
+ ],
35869
+ [
35870
+ 4,
35871
+ 9,
35872
+ 5
35873
+ ],
35874
+ [
35875
+ 2,
35876
+ 4,
35877
+ 11
35878
+ ],
35879
+ [
35880
+ 6,
35881
+ 2,
35882
+ 10
35883
+ ],
35884
+ [
35885
+ 8,
35886
+ 6,
35887
+ 7
35888
+ ],
35889
+ [
35890
+ 9,
35891
+ 8,
35892
+ 1
35893
+ ]
35894
+ ];
35895
+ }
35896
+ const verts = baseVerts.map((v) => normalize(v));
35897
+ let tris = baseFaces.map((f) => [
35898
+ f[0],
35899
+ f[1],
35900
+ f[2]
35901
+ ]);
35902
+ for (let d = 0; d < detail; d++) {
35903
+ const midCache = /* @__PURE__ */ new Map();
35904
+ const midpoint = (a, b) => {
35905
+ const key = a < b ? `${a}_${b}` : `${b}_${a}`;
35906
+ const existing = midCache.get(key);
35907
+ if (existing !== void 0) return existing;
35908
+ const va = verts[a];
35909
+ const vb = verts[b];
35910
+ const idx = verts.length;
35911
+ verts.push(normalize([
35912
+ va[0] + vb[0],
35913
+ va[1] + vb[1],
35914
+ va[2] + vb[2]
35915
+ ]));
35916
+ midCache.set(key, idx);
35917
+ return idx;
35918
+ };
35919
+ const nextTris = [];
35920
+ for (const [i0, i1, i2] of tris) {
35921
+ const m01 = midpoint(i0, i1);
35922
+ const m12 = midpoint(i1, i2);
35923
+ const m20 = midpoint(i2, i0);
35924
+ nextTris.push([
35925
+ i0,
35926
+ m01,
35927
+ m20
35928
+ ], [
35929
+ i1,
35930
+ m12,
35931
+ m01
35932
+ ], [
35933
+ i2,
35934
+ m20,
35935
+ m12
35936
+ ], [
35937
+ m01,
35938
+ m12,
35939
+ m20
35940
+ ]);
35941
+ }
35942
+ tris = nextTris;
35943
+ }
35944
+ const uvOf = (n) => [.5 + Math.atan2(n[2], n[0]) / (2 * Math.PI), Math.acos(Math.max(-1, Math.min(1, -n[1]))) / Math.PI];
35945
+ if (flatShaded) for (const [i0, i1, i2] of tris) {
35946
+ const a = verts[i0];
35947
+ let b = verts[i1];
35948
+ let c = verts[i2];
35949
+ const centroid = [
35950
+ (a[0] + b[0] + c[0]) / 3,
35951
+ (a[1] + b[1] + c[1]) / 3,
35952
+ (a[2] + b[2] + c[2]) / 3
35953
+ ];
35954
+ let fn = normalize(cross3(sub3(b, a), sub3(c, a)));
35955
+ if (dot3(fn, centroid) < 0) {
35956
+ const tmp = b;
35957
+ b = c;
35958
+ c = tmp;
35959
+ fn = [
35960
+ -fn[0],
35961
+ -fn[1],
35962
+ -fn[2]
35963
+ ];
35964
+ }
35965
+ const base = mb.positions.length / 3;
35966
+ mb.vertex([
35967
+ a[0] * radius,
35968
+ a[1] * radius,
35969
+ a[2] * radius
35970
+ ], fn, uvOf(a));
35971
+ mb.vertex([
35972
+ b[0] * radius,
35973
+ b[1] * radius,
35974
+ b[2] * radius
35975
+ ], fn, uvOf(b));
35976
+ mb.vertex([
35977
+ c[0] * radius,
35978
+ c[1] * radius,
35979
+ c[2] * radius
35980
+ ], fn, uvOf(c));
35981
+ mb.indices.push(base, base + 1, base + 2);
35982
+ }
35983
+ else {
35984
+ const base = mb.positions.length / 3;
35985
+ for (const n of verts) mb.vertex([
35986
+ n[0] * radius,
35987
+ n[1] * radius,
35988
+ n[2] * radius
35989
+ ], n, uvOf(n));
35990
+ for (const [i0, i1, i2] of tris) {
35991
+ const a = verts[i0];
35992
+ const b = verts[i1];
35993
+ const c = verts[i2];
35994
+ const centroid = [
35995
+ (a[0] + b[0] + c[0]) / 3,
35996
+ (a[1] + b[1] + c[1]) / 3,
35997
+ (a[2] + b[2] + c[2]) / 3
35998
+ ];
35999
+ if (dot3(cross3(sub3(b, a), sub3(c, a)), centroid) < 0) mb.indices.push(base + i0, base + i2, base + i1);
36000
+ else mb.indices.push(base + i0, base + i1, base + i2);
36001
+ }
36002
+ }
36003
+ }
36004
+ /**
36005
+ * Parses an SVG path string (supporting M, L, H, V, C, S, Q, T, A, Z and multiple subpaths)
36006
+ * into closed 2D contours with adaptive or fixed curve flattening.
36007
+ */
36008
+ function parseSvgPathToContours(d, flattening) {
36009
+ const contours = [];
36010
+ if (!d || typeof d !== "string") return contours;
36011
+ const tokens = [];
36012
+ const regex = /([a-df-z])|([-+]?(?:\d*\.\d+|\d+)(?:[eE][-+]?\d+)?)/gi;
36013
+ let match = regex.exec(d);
36014
+ while (match !== null) {
36015
+ if (match[1]) tokens.push(match[1]);
36016
+ else if (match[2]) tokens.push(Number.parseFloat(match[2]));
36017
+ match = regex.exec(d);
36018
+ }
36019
+ let cursor = 0;
36020
+ let current = {
36021
+ x: 0,
36022
+ y: 0
36023
+ };
36024
+ let subpathStart = {
36025
+ x: 0,
36026
+ y: 0
36027
+ };
36028
+ let lastCubicControl = null;
36029
+ let lastQuadControl = null;
36030
+ let contour = [];
36031
+ const finishContour = () => {
36032
+ if (contour.length >= 2) {
36033
+ const first = contour[0];
36034
+ const last = contour[contour.length - 1];
36035
+ if (Math.hypot(last.x - first.x, last.y - first.y) < 1e-4) contour.pop();
36036
+ if (contour.length >= 2) contours.push(contour);
36037
+ }
36038
+ contour = [];
36039
+ };
36040
+ const segCount = (maxSecondDeriv) => {
36041
+ if (flattening.segments !== void 0) return Math.max(1, flattening.segments);
36042
+ const n = Math.ceil(Math.sqrt(maxSecondDeriv / (8 * Math.max(.001, flattening.tolerance))));
36043
+ return Math.min(64, Math.max(1, n));
36044
+ };
36045
+ const addQuad = (p0, cp, end) => {
36046
+ const n = segCount(2 * Math.hypot(p0.x - 2 * cp.x + end.x, p0.y - 2 * cp.y + end.y));
36047
+ for (let s = 1; s <= n; s++) {
36048
+ const t = s / n;
36049
+ const mt = 1 - t;
36050
+ contour.push({
36051
+ x: mt * mt * p0.x + 2 * mt * t * cp.x + t * t * end.x,
36052
+ y: mt * mt * p0.y + 2 * mt * t * cp.y + t * t * end.y
36053
+ });
36054
+ }
36055
+ };
36056
+ const addCubic = (p0, cp1, cp2, end) => {
36057
+ const n = segCount(6 * Math.max(Math.hypot(p0.x - 2 * cp1.x + cp2.x, p0.y - 2 * cp1.y + cp2.y), Math.hypot(cp1.x - 2 * cp2.x + end.x, cp1.y - 2 * cp2.y + end.y)));
36058
+ for (let s = 1; s <= n; s++) {
36059
+ const t = s / n;
36060
+ const mt = 1 - t;
36061
+ contour.push({
36062
+ x: mt * mt * mt * p0.x + 3 * mt * mt * t * cp1.x + 3 * mt * t * t * cp2.x + t * t * t * end.x,
36063
+ y: mt * mt * mt * p0.y + 3 * mt * mt * t * cp1.y + 3 * mt * t * t * cp2.y + t * t * t * end.y
36064
+ });
36065
+ }
36066
+ };
36067
+ const getNum = () => cursor < tokens.length && typeof tokens[cursor] === "number" ? tokens[cursor++] : 0;
36068
+ while (cursor < tokens.length) {
36069
+ const cmdToken = tokens[cursor++];
36070
+ if (typeof cmdToken !== "string") continue;
36071
+ const isRel = cmdToken === cmdToken.toLowerCase();
36072
+ switch (cmdToken.toUpperCase()) {
36073
+ case "M": {
36074
+ finishContour();
36075
+ let first = true;
36076
+ while (cursor < tokens.length && typeof tokens[cursor] === "number") {
36077
+ let x = getNum();
36078
+ let y = getNum();
36079
+ if (isRel) {
36080
+ x += current.x;
36081
+ y += current.y;
36082
+ }
36083
+ current = {
36084
+ x,
36085
+ y
36086
+ };
36087
+ if (first) {
36088
+ subpathStart = current;
36089
+ contour = [current];
36090
+ first = false;
36091
+ } else contour.push(current);
36092
+ }
36093
+ lastCubicControl = null;
36094
+ lastQuadControl = null;
36095
+ break;
36096
+ }
36097
+ case "L":
36098
+ while (cursor < tokens.length && typeof tokens[cursor] === "number") {
36099
+ let x = getNum();
36100
+ let y = getNum();
36101
+ if (isRel) {
36102
+ x += current.x;
36103
+ y += current.y;
36104
+ }
36105
+ current = {
36106
+ x,
36107
+ y
36108
+ };
36109
+ contour.push(current);
36110
+ }
36111
+ lastCubicControl = null;
36112
+ lastQuadControl = null;
36113
+ break;
36114
+ case "H":
36115
+ while (cursor < tokens.length && typeof tokens[cursor] === "number") {
36116
+ let x = getNum();
36117
+ if (isRel) x += current.x;
36118
+ current = {
36119
+ x,
36120
+ y: current.y
36121
+ };
36122
+ contour.push(current);
36123
+ }
36124
+ lastCubicControl = null;
36125
+ lastQuadControl = null;
36126
+ break;
36127
+ case "V":
36128
+ while (cursor < tokens.length && typeof tokens[cursor] === "number") {
36129
+ let y = getNum();
36130
+ if (isRel) y += current.y;
36131
+ current = {
36132
+ x: current.x,
36133
+ y
36134
+ };
36135
+ contour.push(current);
36136
+ }
36137
+ lastCubicControl = null;
36138
+ lastQuadControl = null;
36139
+ break;
36140
+ case "C":
36141
+ while (cursor < tokens.length && typeof tokens[cursor] === "number") {
36142
+ let x1 = getNum();
36143
+ let y1 = getNum();
36144
+ let x2 = getNum();
36145
+ let y2 = getNum();
36146
+ let x = getNum();
36147
+ let y = getNum();
36148
+ if (isRel) {
36149
+ x1 += current.x;
36150
+ y1 += current.y;
36151
+ x2 += current.x;
36152
+ y2 += current.y;
36153
+ x += current.x;
36154
+ y += current.y;
36155
+ }
36156
+ const cp1 = {
36157
+ x: x1,
36158
+ y: y1
36159
+ };
36160
+ const cp2 = {
36161
+ x: x2,
36162
+ y: y2
36163
+ };
36164
+ const end = {
36165
+ x,
36166
+ y
36167
+ };
36168
+ addCubic(current, cp1, cp2, end);
36169
+ current = end;
36170
+ lastCubicControl = cp2;
36171
+ }
36172
+ lastQuadControl = null;
36173
+ break;
36174
+ case "S":
36175
+ while (cursor < tokens.length && typeof tokens[cursor] === "number") {
36176
+ let x2 = getNum();
36177
+ let y2 = getNum();
36178
+ let x = getNum();
36179
+ let y = getNum();
36180
+ if (isRel) {
36181
+ x2 += current.x;
36182
+ y2 += current.y;
36183
+ x += current.x;
36184
+ y += current.y;
36185
+ }
36186
+ const cp1 = lastCubicControl ? {
36187
+ x: 2 * current.x - lastCubicControl.x,
36188
+ y: 2 * current.y - lastCubicControl.y
36189
+ } : current;
36190
+ const cp2 = {
36191
+ x: x2,
36192
+ y: y2
36193
+ };
36194
+ const end = {
36195
+ x,
36196
+ y
36197
+ };
36198
+ addCubic(current, cp1, cp2, end);
36199
+ current = end;
36200
+ lastCubicControl = cp2;
36201
+ }
36202
+ lastQuadControl = null;
36203
+ break;
36204
+ case "Q":
36205
+ while (cursor < tokens.length && typeof tokens[cursor] === "number") {
36206
+ let x1 = getNum();
36207
+ let y1 = getNum();
36208
+ let x = getNum();
36209
+ let y = getNum();
36210
+ if (isRel) {
36211
+ x1 += current.x;
36212
+ y1 += current.y;
36213
+ x += current.x;
36214
+ y += current.y;
36215
+ }
36216
+ const cp = {
36217
+ x: x1,
36218
+ y: y1
36219
+ };
36220
+ const end = {
36221
+ x,
36222
+ y
36223
+ };
36224
+ addQuad(current, cp, end);
36225
+ current = end;
36226
+ lastQuadControl = cp;
36227
+ }
36228
+ lastCubicControl = null;
36229
+ break;
36230
+ case "T":
36231
+ while (cursor < tokens.length && typeof tokens[cursor] === "number") {
36232
+ let x = getNum();
36233
+ let y = getNum();
36234
+ if (isRel) {
36235
+ x += current.x;
36236
+ y += current.y;
36237
+ }
36238
+ const cp = lastQuadControl ? {
36239
+ x: 2 * current.x - lastQuadControl.x,
36240
+ y: 2 * current.y - lastQuadControl.y
36241
+ } : current;
36242
+ const end = {
36243
+ x,
36244
+ y
36245
+ };
36246
+ addQuad(current, cp, end);
36247
+ current = end;
36248
+ lastQuadControl = cp;
36249
+ }
36250
+ lastCubicControl = null;
36251
+ break;
36252
+ case "A":
36253
+ while (cursor < tokens.length && typeof tokens[cursor] === "number") {
36254
+ const rx = getNum();
36255
+ const ry = getNum();
36256
+ const rot = getNum();
36257
+ const largeArc = getNum() !== 0;
36258
+ const sweep = getNum() !== 0;
36259
+ let x = getNum();
36260
+ let y = getNum();
36261
+ if (isRel) {
36262
+ x += current.x;
36263
+ y += current.y;
36264
+ }
36265
+ const end = {
36266
+ x,
36267
+ y
36268
+ };
36269
+ const segs = sampleSvgArc(current, rx, ry, rot, largeArc, sweep, end);
36270
+ for (const s of segs) contour.push(s.p1);
36271
+ current = end;
36272
+ }
36273
+ lastCubicControl = null;
36274
+ lastQuadControl = null;
36275
+ break;
36276
+ case "Z":
36277
+ finishContour();
36278
+ current = subpathStart;
36279
+ lastCubicControl = null;
36280
+ lastQuadControl = null;
36281
+ break;
36282
+ }
36283
+ }
36284
+ finishContour();
36285
+ return contours;
36286
+ }
36287
+ function toPoint2D(p) {
36288
+ return Array.isArray(p) ? {
36289
+ x: p[0],
36290
+ y: p[1]
36291
+ } : {
36292
+ x: p.x,
36293
+ y: p.y
36294
+ };
36295
+ }
36296
+ function normalizeContoursInput(o, flattening) {
36297
+ const raw = o.shape ?? o.d ?? o.contour;
36298
+ let contours = [];
36299
+ if (typeof raw === "string") contours = parseSvgPathToContours(raw, flattening).filter((c) => c.length >= 3);
36300
+ else if (Array.isArray(raw) && raw.length > 0) {
36301
+ const first = raw[0];
36302
+ if (Array.isArray(first) && first.length > 0 && (Array.isArray(first[0]) || typeof first[0] === "object" && first[0] !== null && "x" in first[0])) contours = raw.map((c) => c.map(toPoint2D)).filter((c) => c.length >= 3);
36303
+ else {
36304
+ const single = raw.map(toPoint2D);
36305
+ if (single.length >= 3) contours = [single];
36306
+ }
36307
+ }
36308
+ if (Array.isArray(o.holes)) for (const hole of o.holes) {
36309
+ const mapped = hole.map(toPoint2D);
36310
+ if (mapped.length >= 3) contours.push(mapped);
36311
+ }
36312
+ return contours;
36313
+ }
36314
+ /** Side-wall segments meeting at more than 30° keep a crisp hard edge. */
36315
+ const SIDE_CREASE_COS = Math.cos(30 * Math.PI / 180);
36316
+ /**
36317
+ * Extrudes any 2D outline (SVG path string with subpaths/holes, or polygon contour arrays)
36318
+ * into a 3D solid mesh with optional front/back bevels and crease-aware smooth/hard side normals.
36319
+ */
36320
+ function buildExtrudedShapeModel(o) {
36321
+ const halfDepth = Math.max(.001, o.depth ?? 20) / 2;
36322
+ const bevelThickness = Math.max(0, Math.min(halfDepth * .49, o.bevelThickness ?? 0));
36323
+ const bevelSize = Math.max(0, o.bevelSize ?? bevelThickness);
36324
+ const bevelSegments = bevelThickness > 0 || bevelSize > 0 ? Math.max(1, Math.round(o.bevelSegments ?? 2)) : 0;
36325
+ const polygons = organizeGlyphPolygons({
36326
+ contours: normalizeContoursInput(o, {
36327
+ segments: o.curveSegments,
36328
+ tolerance: Math.max(.001, o.curveTolerance ?? .25)
36329
+ }),
36330
+ simplified: false
36331
+ });
36332
+ let minX = Number.POSITIVE_INFINITY;
36333
+ let maxX = Number.NEGATIVE_INFINITY;
36334
+ let minY = Number.POSITIVE_INFINITY;
36335
+ let maxY = Number.NEGATIVE_INFINITY;
36336
+ for (const poly of polygons) {
36337
+ for (const p of poly.outer) {
36338
+ if (p.x < minX) minX = p.x;
36339
+ if (p.x > maxX) maxX = p.x;
36340
+ if (p.y < minY) minY = p.y;
36341
+ if (p.y > maxY) maxY = p.y;
36342
+ }
36343
+ for (const hole of poly.holes) for (const p of hole) {
36344
+ if (p.x < minX) minX = p.x;
36345
+ if (p.x > maxX) maxX = p.x;
36346
+ if (p.y < minY) minY = p.y;
36347
+ if (p.y > maxY) maxY = p.y;
36348
+ }
36349
+ }
36350
+ if (!Number.isFinite(minX)) minX = maxX = minY = maxY = 0;
36351
+ if (o.center ?? true) {
36352
+ const cx = (minX + maxX) / 2;
36353
+ const cy = (minY + maxY) / 2;
36354
+ for (const poly of polygons) {
36355
+ for (const p of poly.outer) {
36356
+ p.x -= cx;
36357
+ p.y -= cy;
36358
+ }
36359
+ for (const hole of poly.holes) for (const p of hole) {
36360
+ p.x -= cx;
36361
+ p.y -= cy;
36362
+ }
36363
+ }
36364
+ minX -= cx;
36365
+ maxX -= cx;
36366
+ minY -= cy;
36367
+ maxY -= cy;
36368
+ }
36369
+ const w = maxX - minX;
36370
+ const h = maxY - minY;
36371
+ const frontMb = new MeshBuilder();
36372
+ const sideMb = new MeshBuilder();
36373
+ const useSeparateSideMaterial = o.bevelColor !== void 0 && o.bevelColor !== o.color;
36374
+ const wallMb = useSeparateSideMaterial ? sideMb : frontMb;
36375
+ const cleanContour = (contour) => {
36376
+ const pts = contour.filter((p, k) => {
36377
+ const next = contour[(k + 1) % contour.length];
36378
+ return Math.hypot(next.x - p.x, next.y - p.y) >= 1e-5;
36379
+ });
36380
+ const count = pts.length;
36381
+ if (count < 3) return null;
36382
+ const edgeLengths = [];
36383
+ const edgeNormals = pts.map((p, k) => {
36384
+ const next = pts[(k + 1) % count];
36385
+ const len = Math.hypot(next.x - p.x, next.y - p.y);
36386
+ edgeLengths.push(len);
36387
+ return {
36388
+ x: (next.y - p.y) / len,
36389
+ y: -(next.x - p.x) / len
36390
+ };
36391
+ });
36392
+ return {
36393
+ pts,
36394
+ edgeLengths,
36395
+ edgeNormals,
36396
+ miterNormals: pts.map((_, k) => {
36397
+ const nPrev = edgeNormals[(k - 1 + count) % count];
36398
+ const nNext = edgeNormals[k];
36399
+ const sx = nPrev.x + nNext.x;
36400
+ const sy = nPrev.y + nNext.y;
36401
+ const len = Math.hypot(sx, sy);
36402
+ if (len < 1e-6) return nNext;
36403
+ const dot = sx / len * nNext.x + sy / len * nNext.y;
36404
+ const scale = Math.min(2, 1 / Math.max(.35, dot));
36405
+ return {
36406
+ x: sx / len * scale,
36407
+ y: sy / len * scale
36408
+ };
36409
+ })
36410
+ };
36411
+ };
36412
+ const addSideWallWithProfile = (contour) => {
36413
+ const info = cleanContour(contour);
36414
+ if (!info) return;
36415
+ const { pts, edgeLengths, edgeNormals, miterNormals } = info;
36416
+ const count = pts.length;
36417
+ const perimeter = edgeLengths.reduce((a, b) => a + b, 0) || 1;
36418
+ const slices = [];
36419
+ if (bevelSegments <= 0) slices.push({
36420
+ z: -halfDepth,
36421
+ inset: 0,
36422
+ nr: 1,
36423
+ nz: 0,
36424
+ v: 0
36425
+ }, {
36426
+ z: halfDepth,
36427
+ inset: 0,
36428
+ nr: 1,
36429
+ nz: 0,
36430
+ v: 1
36431
+ });
36432
+ else {
36433
+ const totalRows = bevelSegments * 2 + 1;
36434
+ let row = 0;
36435
+ for (let b = 0; b <= bevelSegments; b++) {
36436
+ const a = b / bevelSegments * Math.PI / 2;
36437
+ slices.push({
36438
+ z: -halfDepth + bevelThickness * (1 - Math.cos(a)),
36439
+ inset: -bevelSize * (1 - Math.sin(a)),
36440
+ nr: Math.sin(a),
36441
+ nz: -Math.cos(a),
36442
+ v: row++ / totalRows
36443
+ });
36444
+ }
36445
+ for (let b = 0; b <= bevelSegments; b++) {
36446
+ const a = b / bevelSegments * Math.PI / 2;
36447
+ slices.push({
36448
+ z: halfDepth - bevelThickness * (1 - Math.sin(a)),
36449
+ inset: -bevelSize * (1 - Math.cos(a)),
36450
+ nr: Math.cos(a),
36451
+ nz: Math.sin(a),
36452
+ v: row++ / totalRows
36453
+ });
36454
+ }
36455
+ }
36456
+ const numSlices = slices.length;
36457
+ const addColumn = (p, miter, n2d, u) => {
36458
+ const base = wallMb.positions.length / 3;
36459
+ for (const s of slices) {
36460
+ const px = p.x + miter.x * s.inset;
36461
+ const py = p.y + miter.y * s.inset;
36462
+ const n3 = normalize([
36463
+ n2d.x * s.nr,
36464
+ n2d.y * s.nr,
36465
+ s.nz
36466
+ ]);
36467
+ wallMb.vertex([
36468
+ px,
36469
+ py,
36470
+ s.z
36471
+ ], n3, [u, s.v]);
36472
+ }
36473
+ return base;
36474
+ };
36475
+ const startColumn = [];
36476
+ const endColumn = [];
36477
+ let along = 0;
36478
+ for (let k = 0; k < count; k++) {
36479
+ const incoming = edgeNormals[(k - 1 + count) % count];
36480
+ const outgoing = edgeNormals[k];
36481
+ const miter = miterNormals[k];
36482
+ const u = along / perimeter;
36483
+ const sx = incoming.x + outgoing.x;
36484
+ const sy = incoming.y + outgoing.y;
36485
+ const averaged = incoming.x * outgoing.x + incoming.y * outgoing.y >= SIDE_CREASE_COS ? {
36486
+ x: sx / Math.hypot(sx, sy),
36487
+ y: sy / Math.hypot(sx, sy)
36488
+ } : null;
36489
+ if (averaged && k > 0) startColumn[k] = endColumn[k] = addColumn(pts[k], miter, averaged, u);
36490
+ else {
36491
+ endColumn[k] = addColumn(pts[k], miter, averaged ?? incoming, k === 0 ? 1 : u);
36492
+ startColumn[k] = addColumn(pts[k], miter, averaged ?? outgoing, u);
36493
+ }
36494
+ along += edgeLengths[k];
36495
+ }
36496
+ for (let k = 0; k < count; k++) {
36497
+ const colA = startColumn[k];
36498
+ const colB = endColumn[(k + 1) % count];
36499
+ for (let s = 0; s < numSlices - 1; s++) {
36500
+ const a0 = colA + s;
36501
+ const a1 = colA + s + 1;
36502
+ const b0 = colB + s;
36503
+ const b1 = colB + s + 1;
36504
+ wallMb.indices.push(a0, b0, a1, b0, b1, a1);
36505
+ }
36506
+ }
36507
+ };
36508
+ for (const poly of polygons) {
36509
+ const outer = poly.outer;
36510
+ if (computeContourSignedArea(outer) < 0) outer.reverse();
36511
+ for (const hole of poly.holes) if (computeContourSignedArea(hole) > 0) hole.reverse();
36512
+ const outerInfo = cleanContour(outer);
36513
+ if (!outerInfo) continue;
36514
+ const holeInfos = poly.holes.map(cleanContour).filter((x) => x !== null);
36515
+ const capInset = -bevelSize;
36516
+ const earcutCoords = [];
36517
+ const holeIndices = [];
36518
+ for (let k = 0; k < outerInfo.pts.length; k++) {
36519
+ const p = outerInfo.pts[k];
36520
+ const m = outerInfo.miterNormals[k];
36521
+ earcutCoords.push(p.x + m.x * capInset, p.y + m.y * capInset);
36522
+ }
36523
+ for (const hInfo of holeInfos) {
36524
+ holeIndices.push(earcutCoords.length / 2);
36525
+ for (let k = 0; k < hInfo.pts.length; k++) {
36526
+ const p = hInfo.pts[k];
36527
+ const m = hInfo.miterNormals[k];
36528
+ earcutCoords.push(p.x + m.x * capInset, p.y + m.y * capInset);
36529
+ }
36530
+ }
36531
+ const triangles = earcut(earcutCoords, holeIndices, 2);
36532
+ const vertCount = earcutCoords.length / 2;
36533
+ const frontBase = frontMb.positions.length / 3;
36534
+ for (let v = 0; v < vertCount; v++) {
36535
+ const vx = earcutCoords[v * 2];
36536
+ const vy = earcutCoords[v * 2 + 1];
36537
+ frontMb.vertex([
36538
+ vx,
36539
+ vy,
36540
+ -halfDepth
36541
+ ], [
36542
+ 0,
36543
+ 0,
36544
+ -1
36545
+ ], [(vx - minX) / (w || 1), (vy - minY) / (h || 1)]);
36546
+ }
36547
+ for (let t = 0; t < triangles.length; t += 3) frontMb.indices.push(frontBase + triangles[t], frontBase + triangles[t + 2], frontBase + triangles[t + 1]);
36548
+ const backBase = frontMb.positions.length / 3;
36549
+ for (let v = 0; v < vertCount; v++) {
36550
+ const vx = earcutCoords[v * 2];
36551
+ const vy = earcutCoords[v * 2 + 1];
36552
+ frontMb.vertex([
36553
+ vx,
36554
+ vy,
36555
+ halfDepth
36556
+ ], [
36557
+ 0,
36558
+ 0,
36559
+ 1
36560
+ ], [(vx - minX) / (w || 1), (vy - minY) / (h || 1)]);
36561
+ }
36562
+ for (let t = 0; t < triangles.length; t += 3) frontMb.indices.push(backBase + triangles[t], backBase + triangles[t + 1], backBase + triangles[t + 2]);
36563
+ addSideWallWithProfile(outer);
36564
+ for (const hole of poly.holes) addSideWallWithProfile(hole);
36565
+ }
36566
+ const meshes = [];
36567
+ const materials = {};
36568
+ const frontMesh = frontMb.build("extrude-front");
36569
+ frontMesh.materialName = "extrude-front-material";
36570
+ meshes.push(frontMesh);
36571
+ materials["extrude-front-material"] = {
36572
+ name: "extrude-front-material",
36573
+ diffuseColor: parseHexOrRgbColor(o.color ?? "#ffffff"),
36574
+ shading: o.material ?? "lit",
36575
+ shininess: o.shininess ?? 32,
36576
+ roughness: o.roughness,
36577
+ metallic: o.metallic
36578
+ };
36579
+ if (useSeparateSideMaterial && sideMb.indices.length > 0) {
36580
+ const sideMesh = sideMb.build("extrude-side");
36581
+ sideMesh.materialName = "extrude-side-material";
36582
+ meshes.push(sideMesh);
36583
+ materials["extrude-side-material"] = {
36584
+ name: "extrude-side-material",
36585
+ diffuseColor: parseHexOrRgbColor(o.bevelColor ?? o.color ?? "#ffffff"),
36586
+ shading: o.material ?? "lit",
36587
+ shininess: o.shininess ?? 32,
36588
+ roughness: o.roughness,
36589
+ metallic: o.metallic
36590
+ };
36591
+ }
36592
+ return finalizeModel3DData("extrude", meshes, materials);
36593
+ }
36594
+ /**
36595
+ * Revolves a 2D profile `(r, y)` around the Y axis (`x = r cos φ, y = y, z = r sin φ`).
36596
+ */
36597
+ function lathe(o, mb) {
36598
+ const rawPoints = typeof o.points === "string" ? parseSvgPathToContours(o.points, { tolerance: .25 })[0] ?? [] : o.points.map(toPoint2D);
36599
+ if (rawPoints.length < 2) return;
36600
+ const pts = rawPoints.map((p) => ({
36601
+ x: Math.max(0, p.x),
36602
+ y: p.y
36603
+ }));
36604
+ const isClosedLoop = pts.length >= 3 && Math.hypot(pts[pts.length - 1].x - pts[0].x, pts[pts.length - 1].y - pts[0].y) < 1e-4;
36605
+ if (isClosedLoop) {
36606
+ if (computeContourSignedArea(pts) < 0) pts.reverse();
36607
+ } else if (pts[0].y > pts[pts.length - 1].y) pts.reverse();
36608
+ const seg = Math.max(3, Math.round(o.segments ?? 32));
36609
+ const phiStart = (o.phiStart ?? 0) * Math.PI / 180;
36610
+ const phiLength = (o.phiLength ?? 360) * Math.PI / 180;
36611
+ const count = pts.length;
36612
+ const segLens = [];
36613
+ const segNormals = [];
36614
+ let totalLen = 0;
36615
+ for (let j = 0; j < count - 1; j++) {
36616
+ const a = pts[j];
36617
+ const b = pts[j + 1];
36618
+ const dr = b.x - a.x;
36619
+ const dy = b.y - a.y;
36620
+ const len = Math.hypot(dr, dy);
36621
+ segLens.push(len);
36622
+ totalLen += len;
36623
+ segNormals.push(len > 1e-8 ? {
36624
+ x: dy / len,
36625
+ y: -dr / len
36626
+ } : {
36627
+ x: 1,
36628
+ y: 0
36629
+ });
36630
+ }
36631
+ const vCoords = [0];
36632
+ let acc = 0;
36633
+ for (const len of segLens) {
36634
+ acc += len;
36635
+ vCoords.push(totalLen > 0 ? acc / totalLen : 0);
36636
+ }
36637
+ if (o.flatShaded) for (let j = 0; j < count - 1; j++) {
36638
+ const pA = pts[j];
36639
+ const pB = pts[j + 1];
36640
+ const n2 = segNormals[j];
36641
+ for (let i = 0; i < seg; i++) {
36642
+ const phi0 = phiStart + i / seg * phiLength;
36643
+ const phi1 = phiStart + (i + 1) / seg * phiLength;
36644
+ const phiMid = (phi0 + phi1) / 2;
36645
+ const fn = normalize([
36646
+ n2.x * Math.cos(phiMid),
36647
+ n2.y,
36648
+ n2.x * Math.sin(phiMid)
36649
+ ]);
36650
+ const c0 = Math.cos(phi0);
36651
+ const s0 = Math.sin(phi0);
36652
+ const c1 = Math.cos(phi1);
36653
+ const s1 = Math.sin(phi1);
36654
+ const base = mb.positions.length / 3;
36655
+ mb.vertex([
36656
+ pA.x * c0,
36657
+ pA.y,
36658
+ pA.x * s0
36659
+ ], fn, [i / seg, vCoords[j]]);
36660
+ mb.vertex([
36661
+ pA.x * c1,
36662
+ pA.y,
36663
+ pA.x * s1
36664
+ ], fn, [(i + 1) / seg, vCoords[j]]);
36665
+ mb.vertex([
36666
+ pB.x * c0,
36667
+ pB.y,
36668
+ pB.x * s0
36669
+ ], fn, [i / seg, vCoords[j + 1]]);
36670
+ mb.vertex([
36671
+ pB.x * c1,
36672
+ pB.y,
36673
+ pB.x * s1
36674
+ ], fn, [(i + 1) / seg, vCoords[j + 1]]);
36675
+ mb.indices.push(base, base + 2, base + 1, base + 1, base + 2, base + 3);
36676
+ }
36677
+ }
36678
+ else {
36679
+ const smoothOrCrease = (nSeg, nAdj) => {
36680
+ if (nSeg.x * nAdj.x + nSeg.y * nAdj.y < SIDE_CREASE_COS) return nSeg;
36681
+ const nx = nSeg.x + nAdj.x;
36682
+ const ny = nSeg.y + nAdj.y;
36683
+ const nLen = Math.hypot(nx, ny);
36684
+ return nLen > 1e-8 ? {
36685
+ x: nx / nLen,
36686
+ y: ny / nLen
36687
+ } : nSeg;
36688
+ };
36689
+ const emitRing = (p, n2, v) => {
36690
+ for (let i = 0; i <= seg; i++) {
36691
+ const phi = phiStart + i / seg * phiLength;
36692
+ const c = Math.cos(phi);
36693
+ const s = Math.sin(phi);
36694
+ const vNorm = p.x <= 1e-5 && Math.abs(n2.y) > 1e-5 ? [
36695
+ 0,
36696
+ n2.y < 0 ? -1 : 1,
36697
+ 0
36698
+ ] : normalize([
36699
+ n2.x * c,
36700
+ n2.y,
36701
+ n2.x * s
36702
+ ]);
36703
+ mb.vertex([
36704
+ p.x * c,
36705
+ p.y,
36706
+ p.x * s
36707
+ ], vNorm, [i / seg, v]);
36708
+ }
36709
+ };
36710
+ for (let j = 0; j < count - 1; j++) {
36711
+ const pA = pts[j];
36712
+ const pB = pts[j + 1];
36713
+ const nSeg = segNormals[j];
36714
+ const nPrevA = j > 0 ? segNormals[j - 1] : isClosedLoop ? segNormals[count - 2] : nSeg;
36715
+ const nNextB = j + 1 < count - 1 ? segNormals[j + 1] : isClosedLoop ? segNormals[0] : nSeg;
36716
+ const nA = smoothOrCrease(nSeg, nPrevA);
36717
+ const nB = smoothOrCrease(nSeg, nNextB);
36718
+ const base = mb.positions.length / 3;
36719
+ emitRing(pA, nA, vCoords[j]);
36720
+ emitRing(pB, nB, vCoords[j + 1]);
36721
+ mb.grid(base, seg, 1, false);
36722
+ }
36723
+ }
36724
+ if ((o.capped ?? o.closed) && !isClosedLoop) {
36725
+ const first = pts[0];
36726
+ const last = pts[count - 1];
36727
+ for (const [pt, ny] of [[first, -1], [last, 1]]) {
36728
+ if (pt.x <= 1e-5) continue;
36729
+ const center = mb.vertex([
36730
+ 0,
36731
+ pt.y,
36732
+ 0
36733
+ ], [
36734
+ 0,
36735
+ ny,
36736
+ 0
36737
+ ], [.5, .5]);
36738
+ const ring$1 = mb.positions.length / 3;
36739
+ for (let i = 0; i <= seg; i++) {
36740
+ const phi = phiStart + i / seg * phiLength;
36741
+ const c = Math.cos(phi);
36742
+ const s = Math.sin(phi);
36743
+ mb.vertex([
36744
+ pt.x * c,
36745
+ pt.y,
36746
+ pt.x * s
36747
+ ], [
36748
+ 0,
36749
+ ny,
36750
+ 0
36751
+ ], [(c + 1) / 2, (s + 1) / 2]);
36752
+ }
36753
+ for (let i = 0; i < seg; i++) if (ny < 0) mb.indices.push(center, ring$1 + i, ring$1 + i + 1);
36754
+ else mb.indices.push(center, ring$1 + i + 1, ring$1 + i);
36755
+ }
36756
+ }
36757
+ }
36758
+ function buildPath3DFromSpec(spec, closedDefault = false) {
36759
+ if (Array.isArray(spec)) {
36760
+ if (spec.length < 2) throw new Error("3D path requires at least 2 points");
36761
+ return Path3D.catmullRom(spec, { closed: closedDefault });
36762
+ }
36763
+ if (spec.kind === "catmullRom") return Path3D.catmullRom(spec.points, {
36764
+ tension: spec.tension,
36765
+ closed: spec.closed ?? closedDefault,
36766
+ samplesPerSegment: spec.samplesPerSegment
36767
+ });
36768
+ if (spec.kind === "bezier") return Path3D.bezier(spec.segments, spec.samplesPerSegment);
36769
+ if (spec.kind === "helix") return Path3D.helix({
36770
+ center: spec.center ?? [
36771
+ 0,
36772
+ 0,
36773
+ 0
36774
+ ],
36775
+ radius: spec.radius,
36776
+ pitch: spec.pitch,
36777
+ turns: spec.turns,
36778
+ axis: spec.axis,
36779
+ samplesPerTurn: spec.samplesPerTurn
36780
+ });
36781
+ return Path3D.circle({
36782
+ center: spec.center ?? [
36783
+ 0,
36784
+ 0,
36785
+ 0
36786
+ ],
36787
+ radius: spec.radius,
36788
+ startAngleDeg: spec.startAngleDeg,
36789
+ endAngleDeg: spec.endAngleDeg,
36790
+ samples: spec.samples
36791
+ });
36792
+ }
36793
+ /**
36794
+ * Sweeps a circular (or thick-walled hollow pipe) cross-section along a 3D path
36795
+ * using Rotation-Minimizing Frames (RMF) so the tube never kinks or gimbal-flips.
36796
+ */
36797
+ function tube(o, mb) {
36798
+ const closed = o.closed ?? (!Array.isArray(o.path) && o.path.kind === "catmullRom" && Boolean(o.path.closed));
36799
+ const path$1 = buildPath3DFromSpec(o.path, closed);
36800
+ const tubSeg = Math.max(2, Math.round(o.tubularSegments ?? 64));
36801
+ const radSeg = Math.max(3, Math.round(o.radialSegments ?? 16));
36802
+ const rDefault = Math.max(.001, o.radius ?? 12);
36803
+ const rStart = o.radiusTaper ? Math.max(0, o.radiusTaper[0]) : rDefault;
36804
+ const rEnd = o.radiusTaper ? Math.max(0, o.radiusTaper[1]) : rDefault;
36805
+ const rInnerBase = Math.max(0, Math.min(rDefault - .001, o.innerRadius ?? 0));
36806
+ const capped = o.capped ?? true;
36807
+ const frames = [];
36808
+ for (let j = 0; j <= tubSeg; j++) frames.push(path$1.getPointAt(j / tubSeg));
36809
+ const outerBase = mb.positions.length / 3;
36810
+ for (let j = 0; j <= tubSeg; j++) {
36811
+ const u = j / tubSeg;
36812
+ const r = rStart + (rEnd - rStart) * u;
36813
+ const f = frames[j];
36814
+ const [px, py, pz] = f.position;
36815
+ const [nx, ny, nz] = f.normal;
36816
+ const [bx, by, bz] = f.binormal;
36817
+ for (let i = 0; i <= radSeg; i++) {
36818
+ const v = i / radSeg;
36819
+ const theta = v * Math.PI * 2;
36820
+ const c = Math.cos(theta);
36821
+ const s = Math.sin(theta);
36822
+ const dirX = c * nx + s * bx;
36823
+ const dirY = c * ny + s * by;
36824
+ const dirZ = c * nz + s * bz;
36825
+ mb.vertex([
36826
+ px + r * dirX,
36827
+ py + r * dirY,
36828
+ pz + r * dirZ
36829
+ ], normalize([
36830
+ dirX,
36831
+ dirY,
36832
+ dirZ
36833
+ ]), [u, v]);
36834
+ }
36835
+ }
36836
+ mb.grid(outerBase, radSeg, tubSeg, true);
36837
+ if (rInnerBase > 1e-6) {
36838
+ const innerRatio = rInnerBase / rDefault;
36839
+ const innerBase = mb.positions.length / 3;
36840
+ for (let j = 0; j <= tubSeg; j++) {
36841
+ const u = j / tubSeg;
36842
+ const r = (rStart + (rEnd - rStart) * u) * innerRatio;
36843
+ const f = frames[j];
36844
+ const [px, py, pz] = f.position;
36845
+ const [nx, ny, nz] = f.normal;
36846
+ const [bx, by, bz] = f.binormal;
36847
+ for (let i = 0; i <= radSeg; i++) {
36848
+ const v = i / radSeg;
36849
+ const theta = v * Math.PI * 2;
36850
+ const c = Math.cos(theta);
36851
+ const s = Math.sin(theta);
36852
+ const dirX = c * nx + s * bx;
36853
+ const dirY = c * ny + s * by;
36854
+ const dirZ = c * nz + s * bz;
36855
+ mb.vertex([
36856
+ px + r * dirX,
36857
+ py + r * dirY,
36858
+ pz + r * dirZ
36859
+ ], normalize([
36860
+ -dirX,
36861
+ -dirY,
36862
+ -dirZ
36863
+ ]), [u, v]);
36864
+ }
36865
+ }
36866
+ mb.grid(innerBase, radSeg, tubSeg, false);
36867
+ if (!closed && capped) for (const [j, sign] of [[0, -1], [tubSeg, 1]]) {
36868
+ const u = j / tubSeg;
36869
+ const rOut = rStart + (rEnd - rStart) * u;
36870
+ const rIn = rOut * innerRatio;
36871
+ const f = frames[j];
36872
+ const [px, py, pz] = f.position;
36873
+ const [nx, ny, nz] = f.normal;
36874
+ const [bx, by, bz] = f.binormal;
36875
+ const capN = normalize([
36876
+ f.tangent[0] * sign,
36877
+ f.tangent[1] * sign,
36878
+ f.tangent[2] * sign
36879
+ ]);
36880
+ const base = mb.positions.length / 3;
36881
+ for (const rr of [rIn, rOut]) for (let i = 0; i <= radSeg; i++) {
36882
+ const theta = i / radSeg * Math.PI * 2;
36883
+ const c = Math.cos(theta);
36884
+ const s = Math.sin(theta);
36885
+ const dirX = c * nx + s * bx;
36886
+ const dirY = c * ny + s * by;
36887
+ const dirZ = c * nz + s * bz;
36888
+ mb.vertex([
36889
+ px + rr * dirX,
36890
+ py + rr * dirY,
36891
+ pz + rr * dirZ
36892
+ ], capN, [(c * (rr / rOut) + 1) / 2, (s * (rr / rOut) + 1) / 2]);
36893
+ }
36894
+ mb.grid(base, radSeg, 1, sign < 0);
36895
+ }
36896
+ } else if (!closed && capped) for (const [j, sign] of [[0, -1], [tubSeg, 1]]) {
36897
+ const u = j / tubSeg;
36898
+ const r = rStart + (rEnd - rStart) * u;
36899
+ if (r <= 1e-6) continue;
36900
+ const f = frames[j];
36901
+ const [px, py, pz] = f.position;
36902
+ const [nx, ny, nz] = f.normal;
36903
+ const [bx, by, bz] = f.binormal;
36904
+ const capN = normalize([
36905
+ f.tangent[0] * sign,
36906
+ f.tangent[1] * sign,
36907
+ f.tangent[2] * sign
36908
+ ]);
36909
+ const center = mb.vertex([
36910
+ px,
36911
+ py,
36912
+ pz
36913
+ ], capN, [.5, .5]);
36914
+ const ring$1 = mb.positions.length / 3;
36915
+ for (let i = 0; i <= radSeg; i++) {
36916
+ const theta = i / radSeg * Math.PI * 2;
36917
+ const c = Math.cos(theta);
36918
+ const s = Math.sin(theta);
36919
+ const dirX = c * nx + s * bx;
36920
+ const dirY = c * ny + s * by;
36921
+ const dirZ = c * nz + s * bz;
36922
+ mb.vertex([
36923
+ px + r * dirX,
36924
+ py + r * dirY,
36925
+ pz + r * dirZ
36926
+ ], capN, [(c + 1) / 2, (s + 1) / 2]);
36927
+ }
36928
+ for (let i = 0; i < radSeg; i++) if (sign > 0) mb.indices.push(center, ring$1 + i, ring$1 + i + 1);
36929
+ else mb.indices.push(center, ring$1 + i + 1, ring$1 + i);
36930
+ }
36931
+ }
36932
+ function resolvePointArray(spec) {
36933
+ if (Array.isArray(spec)) return spec;
36934
+ const path$1 = buildPath3DFromSpec(spec);
36935
+ const count = 96;
36936
+ const out = [];
36937
+ for (let i = 0; i <= count; i++) out.push(path$1.getPointAt(i / count).position);
36938
+ return out;
36939
+ }
36940
+ /**
36941
+ * Native 3D line geometry (`line-list` topology) with optional dashing and per-vertex colors.
36942
+ */
36943
+ function line3d(o, mb) {
36944
+ mb.topology = "line-list";
36945
+ const pts = resolvePointArray(o.points);
36946
+ if (pts.length < 2) return;
36947
+ const parsedColors = o.colors?.map((c) => parseHexOrRgbColor(c));
36948
+ const mode = o.mode ?? "strip";
36949
+ if (o.dashSize && o.dashSize > 0 && mode === "strip") {
36950
+ const dash = o.dashSize;
36951
+ const cycle = dash + (o.gapSize ?? dash);
36952
+ const workPts = o.closed ? [...pts, pts[0]] : pts;
36953
+ let dist = 0;
36954
+ for (let i = 0; i < workPts.length - 1; i++) {
36955
+ const a = workPts[i];
36956
+ const b = workPts[i + 1];
36957
+ const segLen = Math.hypot(b[0] - a[0], b[1] - a[1], b[2] - a[2]);
36958
+ if (segLen < 1e-6) continue;
36959
+ const dir = [
36960
+ (b[0] - a[0]) / segLen,
36961
+ (b[1] - a[1]) / segLen,
36962
+ (b[2] - a[2]) / segLen
36963
+ ];
36964
+ const colA = parsedColors?.[i % parsedColors.length];
36965
+ let local = 0;
36966
+ while (local < segLen) {
36967
+ const phase = (dist + local) % cycle;
36968
+ if (phase < dash) {
36969
+ const step = Math.min(dash - phase, segLen - local);
36970
+ const p0 = [
36971
+ a[0] + dir[0] * local,
36972
+ a[1] + dir[1] * local,
36973
+ a[2] + dir[2] * local
36974
+ ];
36975
+ const p1 = [
36976
+ a[0] + dir[0] * (local + step),
36977
+ a[1] + dir[1] * (local + step),
36978
+ a[2] + dir[2] * (local + step)
36979
+ ];
36980
+ const idx0 = mb.vertex(p0, [
36981
+ 0,
36982
+ -1,
36983
+ 0
36984
+ ], [0, 0], colA);
36985
+ const idx1 = mb.vertex(p1, [
36986
+ 0,
36987
+ -1,
36988
+ 0
36989
+ ], [1, 0], colA);
36990
+ mb.indices.push(idx0, idx1);
36991
+ local += step;
36992
+ } else local += cycle - phase;
36993
+ }
36994
+ dist += segLen;
36995
+ }
36996
+ return;
36997
+ }
36998
+ for (let i = 0; i < pts.length; i++) {
36999
+ const u = pts.length > 1 ? i / (pts.length - 1) : 0;
37000
+ mb.vertex(pts[i], [
37001
+ 0,
37002
+ -1,
37003
+ 0
37004
+ ], [u, 0], parsedColors ? parsedColors[i % parsedColors.length] : void 0);
37005
+ }
37006
+ if (mode === "segments") for (let i = 0; i + 1 < pts.length; i += 2) mb.indices.push(i, i + 1);
37007
+ else {
37008
+ for (let i = 0; i < pts.length - 1; i++) mb.indices.push(i, i + 1);
37009
+ if (o.closed && pts.length >= 3) mb.indices.push(pts.length - 1, 0);
37010
+ }
37011
+ }
37012
+ /**
37013
+ * 3D point cloud geometry: either native GPU `point-list` topology (`pointSize <= 0` or omitted)
37014
+ * or icosahedral 3D dots of radius `pointSize` for diagram vertices that scale with perspective.
37015
+ */
37016
+ function points3d(o, mb) {
37017
+ const pts = resolvePointArray(o.points);
37018
+ if (pts.length === 0) return;
37019
+ const parsedColors = o.colors?.map((c) => parseHexOrRgbColor(c));
37020
+ const pointSize = o.pointSize ?? 0;
37021
+ if (pointSize > 0) {
37022
+ const dotMb = new MeshBuilder();
37023
+ polyhedron({
37024
+ type: "polyhedron",
37025
+ shape: "icosphere",
37026
+ radius: pointSize,
37027
+ detail: 1,
37028
+ flatShaded: false
37029
+ }, dotMb);
37030
+ const dotVerts = dotMb.positions.length / 3;
37031
+ for (let pIdx = 0; pIdx < pts.length; pIdx++) {
37032
+ const [px, py, pz] = pts[pIdx];
37033
+ const col = parsedColors ? parsedColors[pIdx % parsedColors.length] : void 0;
37034
+ const base = mb.positions.length / 3;
37035
+ for (let v = 0; v < dotVerts; v++) mb.vertex([
37036
+ px + dotMb.positions[v * 3],
37037
+ py + dotMb.positions[v * 3 + 1],
37038
+ pz + dotMb.positions[v * 3 + 2]
37039
+ ], [
37040
+ dotMb.normals[v * 3],
37041
+ dotMb.normals[v * 3 + 1],
37042
+ dotMb.normals[v * 3 + 2]
37043
+ ], [dotMb.uvs[v * 2], dotMb.uvs[v * 2 + 1]], col);
37044
+ for (const idx of dotMb.indices) mb.indices.push(base + idx);
37045
+ }
37046
+ return;
37047
+ }
37048
+ mb.topology = "point-list";
37049
+ for (let i = 0; i < pts.length; i++) {
37050
+ const u = pts.length > 1 ? i / (pts.length - 1) : 0;
37051
+ const idx = mb.vertex(pts[i], [
37052
+ 0,
37053
+ -1,
37054
+ 0
37055
+ ], [u, 0], parsedColors ? parsedColors[i % parsedColors.length] : void 0);
37056
+ mb.indices.push(idx);
37057
+ }
37058
+ }
37059
+ function shard(o, mb) {
37060
+ const r = o.radius ?? 50;
37061
+ const hx = (o.size ? o.size[0] : r) || 50;
37062
+ const hy = (o.size ? o.size[1] : r) || 50;
37063
+ const hz = (o.size ? o.size[2] : r) || 50;
37064
+ const maxR = Math.max(hx, hy, hz);
37065
+ const facets = Math.max(4, Math.min(64, Math.round(o.facets ?? 14)));
37066
+ const seed = Math.round(o.seed ?? 1) >>> 0;
37067
+ const irregularity = Math.max(0, Math.min(.85, o.irregularity ?? .35));
37068
+ let state = BigInt(seed) * 6364136223846793005n + 1442695040888963407n;
37069
+ const rnd = () => {
37070
+ state = state * 6364136223846793005n + 1442695040888963407n;
37071
+ const xorshifted = Number((state >> 18n ^ state) >> 27n) >>> 0;
37072
+ const rot = Number(state >> 59n) >>> 0;
37073
+ return ((xorshifted >>> rot | xorshifted << (32 - rot & 31)) >>> 0) / 4294967296;
37074
+ };
37075
+ let faces = [
37076
+ [
37077
+ [
37078
+ -hx,
37079
+ -hy,
37080
+ -hz
37081
+ ],
37082
+ [
37083
+ hx,
37084
+ -hy,
37085
+ -hz
37086
+ ],
37087
+ [
37088
+ hx,
37089
+ hy,
37090
+ -hz
37091
+ ],
37092
+ [
37093
+ -hx,
37094
+ hy,
37095
+ -hz
37096
+ ]
37097
+ ],
37098
+ [
37099
+ [
37100
+ hx,
37101
+ -hy,
37102
+ hz
37103
+ ],
37104
+ [
37105
+ -hx,
37106
+ -hy,
37107
+ hz
37108
+ ],
37109
+ [
37110
+ -hx,
37111
+ hy,
37112
+ hz
37113
+ ],
37114
+ [
37115
+ hx,
37116
+ hy,
37117
+ hz
37118
+ ]
37119
+ ],
37120
+ [
37121
+ [
37122
+ -hx,
37123
+ -hy,
37124
+ hz
37125
+ ],
37126
+ [
37127
+ -hx,
37128
+ -hy,
37129
+ -hz
37130
+ ],
37131
+ [
37132
+ -hx,
37133
+ hy,
37134
+ -hz
37135
+ ],
35096
37136
  [
35097
37137
  -hx,
35098
37138
  hy,
@@ -35330,24 +37370,7 @@ function shard(o, mb) {
35330
37370
  for (let j = 1; j < face.length - 1; j++) mb.indices.push(baseIdx, baseIdx + j, baseIdx + j + 1);
35331
37371
  }
35332
37372
  }
35333
- /** Builds a procedural solid as a single-mesh Model3DData. */
35334
- function generatePrimitiveGeometry(options) {
35335
- const mb = new MeshBuilder();
35336
- if (options.type === "roundedBox") roundedBox(options, mb);
35337
- else if (options.type === "sphere") sphere(options, mb);
35338
- else if (options.type === "cylinder") cylinder(options, mb);
35339
- else if (options.type === "shard") shard(options, mb);
35340
- else throw new Error(`Unknown 3D primitive: ${options.type}`);
35341
- const name = options.type;
35342
- const mesh = mb.build(name);
35343
- const material = {
35344
- name: `${name}-material`,
35345
- diffuseColor: parseHexOrRgbColor(options.color ?? "#ffffff"),
35346
- shading: options.material ?? "lit",
35347
- shininess: options.shininess ?? 32,
35348
- roughness: options.roughness,
35349
- metallic: options.metallic
35350
- };
37373
+ function finalizeModel3DData(name, meshes, materials) {
35351
37374
  const min = [
35352
37375
  Number.POSITIVE_INFINITY,
35353
37376
  Number.POSITIVE_INFINITY,
@@ -35358,11 +37381,17 @@ function generatePrimitiveGeometry(options) {
35358
37381
  Number.NEGATIVE_INFINITY,
35359
37382
  Number.NEGATIVE_INFINITY
35360
37383
  ];
35361
- const pos = mesh.positions;
35362
- for (let i = 0; i < pos.length; i += 3) for (let k = 0; k < 3; k++) {
35363
- const v = pos[i + k];
35364
- if (v < min[k]) min[k] = v;
35365
- if (v > max[k]) max[k] = v;
37384
+ for (const mesh of meshes) {
37385
+ const pos = mesh.positions;
37386
+ for (let i = 0; i < pos.length; i += 3) for (let k = 0; k < 3; k++) {
37387
+ const v = pos[i + k];
37388
+ if (v < min[k]) min[k] = v;
37389
+ if (v > max[k]) max[k] = v;
37390
+ }
37391
+ }
37392
+ if (!Number.isFinite(min[0])) {
37393
+ min[0] = min[1] = min[2] = 0;
37394
+ max[0] = max[1] = max[2] = 0;
35366
37395
  }
35367
37396
  const size = [
35368
37397
  max[0] - min[0],
@@ -35371,8 +37400,8 @@ function generatePrimitiveGeometry(options) {
35371
37400
  ];
35372
37401
  return {
35373
37402
  name: `primitive-${name}`,
35374
- meshes: [mesh],
35375
- materials: { [material.name]: material },
37403
+ meshes,
37404
+ materials,
35376
37405
  animations: [],
35377
37406
  bounds: {
35378
37407
  min,
@@ -35387,6 +37416,36 @@ function generatePrimitiveGeometry(options) {
35387
37416
  }
35388
37417
  };
35389
37418
  }
37419
+ /** Builds a procedural solid as a Model3DData. */
37420
+ function generatePrimitiveGeometry(options) {
37421
+ if (options.type === "extrude") return buildExtrudedShapeModel(options);
37422
+ const mb = new MeshBuilder();
37423
+ if (options.type === "roundedBox") roundedBox(options, mb);
37424
+ else if (options.type === "sphere") sphere(options, mb);
37425
+ else if (options.type === "cylinder") cylinder(options, mb);
37426
+ else if (options.type === "torus") torus(options, mb);
37427
+ else if (options.type === "capsule") capsule(options, mb);
37428
+ else if (options.type === "ring") ring(options, mb);
37429
+ else if (options.type === "polyhedron") polyhedron(options, mb);
37430
+ else if (options.type === "lathe") lathe(options, mb);
37431
+ else if (options.type === "tube") tube(options, mb);
37432
+ else if (options.type === "line3d") line3d(options, mb);
37433
+ else if (options.type === "points3d") points3d(options, mb);
37434
+ else if (options.type === "shard") shard(options, mb);
37435
+ else throw new Error(`Unknown 3D primitive: ${options.type}`);
37436
+ const name = options.type;
37437
+ const mesh = mb.build(name);
37438
+ const defaultShading = options.type === "line3d" || options.type === "points3d" && (options.pointSize ?? 0) <= 0 ? "unlit" : "lit";
37439
+ const material = {
37440
+ name: `${name}-material`,
37441
+ diffuseColor: parseHexOrRgbColor(options.color ?? "#ffffff"),
37442
+ shading: options.material ?? defaultShading,
37443
+ shininess: options.shininess ?? 32,
37444
+ roughness: options.roughness,
37445
+ metallic: options.metallic
37446
+ };
37447
+ return finalizeModel3DData(name, [mesh], { [material.name]: material });
37448
+ }
35390
37449
  const primitiveCache = /* @__PURE__ */ new Map();
35391
37450
  /**
35392
37451
  * Like generatePrimitiveGeometry, but identical options return the same
@@ -37084,6 +39143,7 @@ var Renderer3D = class {
37084
39143
  dofBindGroupLayout;
37085
39144
  cameraPool;
37086
39145
  lightsPool;
39146
+ directLightsPool;
37087
39147
  instancePool;
37088
39148
  cameraUniformBindGroupCache = new UniformBindGroupCache();
37089
39149
  samplerCache = new SamplerCache();
@@ -37108,6 +39168,7 @@ var Renderer3D = class {
37108
39168
  modelCache = /* @__PURE__ */ new Map();
37109
39169
  lastCameraPose = null;
37110
39170
  shadowTexture = null;
39171
+ shadowTextures = /* @__PURE__ */ new Map();
37111
39172
  placeholderShadowTexture = null;
37112
39173
  shadowSampler = null;
37113
39174
  meshBounds = /* @__PURE__ */ new WeakMap();
@@ -37132,6 +39193,7 @@ var Renderer3D = class {
37132
39193
  this.floorReflectionPipeline = new FloorReflectionPipeline(device, format, sampleCount, this.quad3dPipeline.cameraLayout, this.quad3dPipeline.lightsLayout, "depth24plus");
37133
39194
  this.cameraPool = new BufferPool(320, GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST);
37134
39195
  this.lightsPool = new BufferPool(LIGHT_UNIFORM_SIZE_BYTES, GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST);
39196
+ this.directLightsPool = new BufferPool(64, GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST);
37135
39197
  this.instancePool = new BufferPool(65536, GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST);
37136
39198
  const dofModule = device.createShaderModule({
37137
39199
  label: "dof-bokeh.wgsl",
@@ -37183,6 +39245,7 @@ var Renderer3D = class {
37183
39245
  this.lastCameraPose = null;
37184
39246
  this.lastCameraBuffer = null;
37185
39247
  this.lightsPool.reset();
39248
+ this.directLightsPool.reset();
37186
39249
  this.instancePool.reset();
37187
39250
  this.quad3dPipeline.resetPools();
37188
39251
  this.slug3dPipeline.resetPools();
@@ -37367,11 +39430,14 @@ var Renderer3D = class {
37367
39430
  return this.cameraUniformBindGroupCache.getBindGroup(this.device, this.quad3dPipeline.cameraLayout, cameraBuffer);
37368
39431
  }
37369
39432
  getOrCreateLightsBuffer(lights, world) {
37370
- const lightsData = Light3D.packLightsUniforms(lights ?? [], world?.environment, world?.shadow);
39433
+ const lightsData = Light3D.packLightsUniforms(lights ?? [], world?.environment, Array.isArray(world?.shadow) ? world.shadow[0] : world?.shadow);
37371
39434
  return this.lightsPool.getBuffer(this.device, lightsData);
37372
39435
  }
37373
39436
  /** Lights uniform + shadow map + comparison sampler (group 3 of every lit shader). */
37374
- createLightsBindGroup(lightsBuffer, withShadow) {
39437
+ createLightsBindGroup(lightsBuffer, withShadow, lights, shadows) {
39438
+ const data = Light3D.packDirectLights(lights, shadows);
39439
+ if (data.byteLength > this.device.limits.maxStorageBufferBindingSize) throw new Error(`Direct lights require ${data.byteLength} bytes, exceeding the device storage binding limit of ${this.device.limits.maxStorageBufferBindingSize} bytes`);
39440
+ const directBuffer = this.directLightsPool.getBuffer(this.device, data);
37375
39441
  this.shadowSampler ??= this.device.createSampler({
37376
39442
  label: "ShadowComparisonSampler",
37377
39443
  compare: "less-equal",
@@ -37400,11 +39466,15 @@ var Renderer3D = class {
37400
39466
  },
37401
39467
  {
37402
39468
  binding: 1,
37403
- resource: map.createView()
39469
+ resource: map.createView({ dimension: "2d-array" })
37404
39470
  },
37405
39471
  {
37406
39472
  binding: 2,
37407
39473
  resource: this.shadowSampler
39474
+ },
39475
+ {
39476
+ binding: 3,
39477
+ resource: { buffer: directBuffer }
37408
39478
  }
37409
39479
  ]
37410
39480
  });
@@ -37487,6 +39557,9 @@ var Renderer3D = class {
37487
39557
  processed.dirRadius[2]
37488
39558
  ];
37489
39559
  const mapSize = options.mapSize ?? 2048;
39560
+ const layer = options.layer ?? 0;
39561
+ const layerCount = options.layerCount ?? 1;
39562
+ if (layerCount > this.device.limits.maxTextureArrayLayers) throw new Error(`Shadow maps require ${layerCount} texture layers, exceeding the device limit of ${this.device.limits.maxTextureArrayLayers}`);
37490
39563
  const casterBounds = casters.map((c) => this.worldBounds(c));
37491
39564
  const frustum = fitShadowFrustum({
37492
39565
  type: kind,
@@ -37495,18 +39568,22 @@ var Renderer3D = class {
37495
39568
  angle: light.angle
37496
39569
  }, casterBounds, receivers, mapSize);
37497
39570
  if (!frustum) return null;
37498
- if (!this.shadowTexture || this.shadowTexture.width !== mapSize) {
37499
- this.shadowTexture?.destroy();
37500
- this.shadowTexture = this.device.createTexture({
39571
+ const key = `${mapSize}:${layerCount}`;
39572
+ let texture = this.shadowTextures.get(key);
39573
+ if (!texture) {
39574
+ texture = this.device.createTexture({
37501
39575
  label: "ShadowMap",
37502
39576
  size: {
37503
39577
  width: mapSize,
37504
- height: mapSize
39578
+ height: mapSize,
39579
+ depthOrArrayLayers: layerCount
37505
39580
  },
37506
39581
  format: SHADOW_DEPTH_FORMAT,
37507
39582
  usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
37508
39583
  });
39584
+ this.shadowTextures.set(key, texture);
37509
39585
  }
39586
+ this.shadowTexture = texture;
37510
39587
  const lightCam = new Float32Array(80);
37511
39588
  lightCam.set(frustum.viewProj, 32);
37512
39589
  lightCam.set(frustum.viewProj, 0);
@@ -37525,7 +39602,11 @@ var Renderer3D = class {
37525
39602
  label: "ShadowMapPass",
37526
39603
  colorAttachments: [],
37527
39604
  depthStencilAttachment: {
37528
- view: this.shadowTexture.createView(),
39605
+ view: this.shadowTexture.createView({
39606
+ dimension: "2d",
39607
+ baseArrayLayer: layer,
39608
+ arrayLayerCount: 1
39609
+ }),
37529
39610
  depthClearValue: 1,
37530
39611
  depthLoadOp: "clear",
37531
39612
  depthStoreOp: "store"
@@ -37576,7 +39657,7 @@ var Renderer3D = class {
37576
39657
  const cameraBuffer = this.packCameraUniforms(camera, surfaceWidth, surfaceHeight);
37577
39658
  const cameraBindGroup = this.createCameraBindGroup(cameraBuffer);
37578
39659
  const lightsBuffer = this.getOrCreateLightsBuffer(lights, world);
37579
- const lightsBindGroup = this.createLightsBindGroup(lightsBuffer, Boolean(world?.shadow));
39660
+ const lightsBindGroup = this.createLightsBindGroup(lightsBuffer, Boolean(world?.shadow), lights ?? [], world?.shadow ? Array.isArray(world.shadow) ? world.shadow : [world.shadow] : []);
37580
39661
  const msaa = this.sampleCount > 1;
37581
39662
  if (msaa && loadOp === "load") throw new Error("Renderer3D.beginPass: loadOp 'load' needs a Renderer3D built with sampleCount 1");
37582
39663
  const attach = (view, msaaTex, clearValue, attachmentLoadOp) => msaaTex ? {
@@ -37636,9 +39717,9 @@ var Renderer3D = class {
37636
39717
  const depthTex = this.getOrCreateReflectionDepthTexture(reflWidth, reflHeight);
37637
39718
  const cameraBuffer = this.packCameraUniforms(camera, reflWidth, reflHeight);
37638
39719
  const cameraBindGroup = this.createCameraBindGroup(cameraBuffer);
37639
- const lightsData = Light3D.packLightsUniforms(lights ?? [], world?.environment, world?.shadow, true);
39720
+ const lightsData = Light3D.packLightsUniforms(lights ?? [], world?.environment, Array.isArray(world?.shadow) ? world.shadow[0] : world?.shadow, true);
37640
39721
  const lightsBuffer = this.lightsPool.getBuffer(this.device, lightsData);
37641
- const lightsBindGroup = this.createLightsBindGroup(lightsBuffer, Boolean(world?.shadow));
39722
+ const lightsBindGroup = this.createLightsBindGroup(lightsBuffer, Boolean(world?.shadow), lights ?? [], world?.shadow ? Array.isArray(world.shadow) ? world.shadow : [world.shadow] : []);
37642
39723
  const msaa = this.sampleCount > 1;
37643
39724
  const msaaTex = msaa ? this.getOrCreateReflectionMsaaTexture(reflWidth, reflHeight) : null;
37644
39725
  const colorAttachments = [msaaTex ? {
@@ -38061,7 +40142,8 @@ var Renderer3D = class {
38061
40142
  matte: opts.matte,
38062
40143
  maps: opts.maps,
38063
40144
  isInstanced: Boolean(opts.instanceBuffer || opts.instanceCount && opts.instanceCount > 1),
38064
- instanceBuffer: opts.instanceBuffer
40145
+ instanceBuffer: opts.instanceBuffer,
40146
+ matrixInstances: opts.matrixInstances
38065
40147
  }, useMrt, this.passWritesHdr, opts.instanceCount ?? 1, opts.firstInstance ?? 0);
38066
40148
  }
38067
40149
  loadModel(srcOrData, format = "auto", options = {}) {
@@ -38265,7 +40347,8 @@ var Renderer3D = class {
38265
40347
  destroy() {
38266
40348
  this.bloomPipeline?.destroy();
38267
40349
  this.depthTexture?.destroy();
38268
- this.shadowTexture?.destroy();
40350
+ for (const texture of this.shadowTextures.values()) texture.destroy();
40351
+ this.shadowTextures.clear();
38269
40352
  this.placeholderShadowTexture?.destroy();
38270
40353
  this.linearDepthTexture?.destroy();
38271
40354
  this.msaaColorTexture?.destroy();
@@ -38284,6 +40367,7 @@ var Renderer3D = class {
38284
40367
  this.reflectionBlurredTexture?.destroy();
38285
40368
  this.cameraPool.destroy();
38286
40369
  this.lightsPool.destroy();
40370
+ this.directLightsPool.destroy();
38287
40371
  this.instancePool.destroy();
38288
40372
  this.cameraUniformBindGroupCache.destroy();
38289
40373
  }
@@ -38934,4 +41018,4 @@ if (typeof globalThis !== "undefined") globalThis.__GATEWAI_DELAYS__ = globalThi
38934
41018
 
38935
41019
  //#endregion
38936
41020
  export { probeVideoSize as $, TextPathBuilder as $t, emojiGraphemes as A, generateArcLengthLUT as An, CubicBezierSegmentSchema as At, jointMatrices as B, ParticleDissolveConfigSchema as Bt, drawGifNode as C, evaluateExpressionSelector as Cn, AdvancedTextAnimatorSchema as Ct, drawSignalNode as D, evaluateSpringProgress as Dn, AudioSignalComputePipeline as Dt, drawParagraphNode as E, evaluateSkewMatrix as En, AnimatableGlyphPropsSchema as Et, getPrimitiveGeometry as F, sampleAudioSpectrumScale as Fn, GlyphAnchorGroupingSchema as Ft, measureMaxCaptionHeight as G, SignalRegistry as Gt, loadModel3D as H, SelectorModeSchema as Ht, getRegisteredFontSource as I, sampleCurveGeometry as In, GlyphAnchorPointSchema as It, parseFBX as J, SpringPhysicsSchema as Jt, mediaDecoderCache as K, SkewPropertiesSchema as Kt, getRenderer2D as L, samplePathChainGeometry as Ln, KineticMarqueeConfigSchema as Lt, fitSpansToText as M, getScramblePool as Mn, EnhancedTextNodeSchema as Mt, generateExtrudedTextGeometry as N, getShuffledOrder as Nn, ExpressionSelectorSchema as Nt, drawSvgNode as O, evaluateTypewriterState as On, AudioSpectrumTypographySchema as Ot, getHeadlessFontPath as P, parseSvgPathToCubicBeziers as Pn, ExtendedGlyphPropertiesSchema as Pt, poseNodes as Q, TextCursorStyleSchema as Qt, initHeadlessWebGPU as R, signalRegistry as Rn, LineSpacingDynamicsSchema as Rt, drawCaptionNode as S, evaluateCubicBezierDerivative as Sn, AdvancedTextAnimatorBuilder as St, drawLottieNode as T, evaluateSelectorCombination as Tn, AnchorPresetSchema as Tt, loadOBJ as U, SelectorShapeSchema as Ut, loadFBX as V, SelectorBasedOnSchema as Vt, lutStore as W, SelectorUnitsSchema as Wt, parseOBJ as X, TextAnimatorBuilder as Xt, parseMTL as Y, TextAnchorConfigSchema as Yt, parseSvgPathToSegments as Z, TextCursorConfigSchema as Zt, buildShapeSvgPath as _, computeRangeSelectorWeight as _n, Vector3Math as _t, GlassPipeline as a, TextSignalSelectorSchema as an, spansText as at, colorMatrixWgsl as b, ellipseToCubicBeziers as bn, resolveEnvironment as bt, Renderer2D as c, TypingCadenceSchema as cn, GetFontAssetUrl as ct, ScreenSpaceRelightPipeline as d, VolumetricFormationConfigSchema as dn, ENVIRONMENT_PRESETS as dt, TextPathOptionsSchema as en, registerHeadlessFont as et, ShadowPipeline as f, VolumetricFormationModeSchema as fn, LIGHT_UNIFORM_SIZE_BYTES as ft, WebGPUAudioProcessor as g, computeLineLeadingDisplacement as gn, Path3D as gt, SlugPipeline as h, computeAnchorPivotOffset as hn, Matrix4Math as ht, EffectPipeline as i, TextSelectorSchema as in, shaderStore as it, ensureDevice as j, generatePathSegments as jn, DynamicStrokePropertiesSchema as jt, drawVideoNode as k, evaluateVolumetricFormation as kn, ChromaticGlitchConfigSchema as kt, Renderer3D as l, VariableFontAxesSchema as ln, resolveMediaSourceUrl as lt, SlugGeometry as m, buildWGSLSignalFn as mn, MAX_LIGHTS as mt, AudioLatentTrackCache as n, TextRangeSelectorSchema as nn, sampleMorphWeights as nt, MotionBlurPipeline as o, TextWigglySelectorSchema as on, srtLoader as ot, SlugFontCache as p, buildPathLUT as pn, Light3D as pt, parseColor as q, Spatial3DPropertiesSchema as qt, AudioMeshDeformPipeline as r, TextScrambleConfigSchema as rn, setRegisteredFontSource as rt, NodeSurfaceProvider as s, TypewriterAnimatorSchema as sn, textureCache as st, AudioLatentEngine as t, TextPathSourceSchema as tn, resolveTextSpans as tt, SSAOPipeline as u, Vec2Schema as un, Camera3D as ut, clearAllVideoCache as v, deterministicWigglyNoise as vn, linearRgb as vt, drawImageNode as w, evaluateMarqueeOffset as wn, AnchorGroupingSchema as wt, describeGpu as x, evaluateCubicBezier as xn, AETextAnimatorSchema as xt, clearHeadlessFonts as y, distanceToCurveParameter as yn, parseColorToRgb as yt, inputStore as z, waveToCubicBeziers as zn, LongShadowConfigSchema as zt };
38937
- //# sourceMappingURL=dist-CnFcSau0.mjs.map
41021
+ //# sourceMappingURL=dist-C1cCW7Bt.mjs.map