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.
- package/README.md +30 -1
- package/dist/audio/index.d.mts +4 -4
- package/dist/audio/index.mjs +10 -10
- package/dist/{audio-CGVJlZb9.mjs → audio-BfQg37MW.mjs} +3 -3
- package/dist/{audio-CGVJlZb9.mjs.map → audio-BfQg37MW.mjs.map} +1 -1
- package/dist/{audio-CxAUxVyA.d.mts → audio-Ccpl9mjV.d.mts} +3 -3
- package/dist/{audio-CxAUxVyA.d.mts.map → audio-Ccpl9mjV.d.mts.map} +1 -1
- package/dist/cli/check-worker.mjs +44 -44
- package/dist/components/index.d.mts +10 -10
- package/dist/components/index.mjs +43 -43
- package/dist/{dist-CnFcSau0.mjs → dist-C1cCW7Bt.mjs} +2297 -213
- package/dist/dist-C1cCW7Bt.mjs.map +1 -0
- package/dist/{dist-C5eZw8ms.mjs → dist-C8Xq3zgx.mjs} +2 -2
- package/dist/{dist-C5eZw8ms.mjs.map → dist-C8Xq3zgx.mjs.map} +1 -1
- package/dist/effects/index.d.mts +4 -4
- package/dist/effects/index.mjs +35 -35
- package/dist/{effects-DdvkiI3u.mjs → effects-CemgqEPv.mjs} +2 -2
- package/dist/{effects-DdvkiI3u.mjs.map → effects-CemgqEPv.mjs.map} +1 -1
- package/dist/fonts/index.mjs +2 -2
- package/dist/{fonts-k_89s1HO.mjs → fonts-BW0pPcvJ.mjs} +2 -2
- package/dist/{fonts-k_89s1HO.mjs.map → fonts-BW0pPcvJ.mjs.map} +1 -1
- package/dist/{gpu-BoyykRdD.mjs → gpu-CMaa63nz.mjs} +2 -2
- package/dist/{gpu-BoyykRdD.mjs.map → gpu-CMaa63nz.mjs.map} +1 -1
- package/dist/{index-CPFKis94.d.mts → index-7Z9_a_iB.d.mts} +3 -3
- package/dist/{index-CPFKis94.d.mts.map → index-7Z9_a_iB.d.mts.map} +1 -1
- package/dist/{index-CFKMZDv1.d.mts → index-BIjMAW1h.d.mts} +2 -2
- package/dist/{index-CFKMZDv1.d.mts.map → index-BIjMAW1h.d.mts.map} +1 -1
- package/dist/{index-BukqOGDa.d.mts → index-CFoAmh7q.d.mts} +3 -3
- package/dist/{index-BukqOGDa.d.mts.map → index-CFoAmh7q.d.mts.map} +1 -1
- package/dist/{index-CPeaSZoz.d.mts → index-CJL0JAU8.d.mts} +2 -2
- package/dist/{index-CPeaSZoz.d.mts.map → index-CJL0JAU8.d.mts.map} +1 -1
- package/dist/{index-Ds9_v60R.d.mts → index-CZjrnylh.d.mts} +544 -336
- package/dist/index-CZjrnylh.d.mts.map +1 -0
- package/dist/{index-Ced1Ry4S.d.mts → index-D5UDyVMb.d.mts} +211 -9
- package/dist/index-D5UDyVMb.d.mts.map +1 -0
- package/dist/{index-B_nooAhS.d.mts → index-Du5YgdzM.d.mts} +53 -15
- package/dist/index-Du5YgdzM.d.mts.map +1 -0
- package/dist/index.d.mts +11 -11
- package/dist/index.mjs +50 -50
- package/dist/preview/index.d.mts +3 -3
- package/dist/preview/index.mjs +10 -10
- package/dist/{preview-DHqCrFhG.mjs → preview-BQVgn6Ho.mjs} +2 -2
- package/dist/{preview-DHqCrFhG.mjs.map → preview-BQVgn6Ho.mjs.map} +1 -1
- package/dist/preview-engine/audio.mjs +1 -1
- package/dist/preview-engine/{chunk-JGDZQCNU.mjs → chunk-2B722GXM.mjs} +1 -1
- package/dist/preview-engine/{chunk-GFW3D464.mjs → chunk-2DWY7UBV.mjs} +1 -1
- package/dist/preview-engine/{chunk-72DYOIMP.mjs → chunk-2PYTDN2C.mjs} +1 -1
- package/dist/preview-engine/{chunk-PF3SXQNV.mjs → chunk-34DL53D5.mjs} +1 -1
- package/dist/preview-engine/{chunk-GLXJOXBE.mjs → chunk-34RJ7GAS.mjs} +1 -1
- package/dist/preview-engine/{chunk-6OY4YLTC.mjs → chunk-35HMRW4I.mjs} +1 -1
- package/dist/preview-engine/chunk-374QY63R.mjs +1974 -0
- package/dist/preview-engine/{chunk-W26U6BP2.mjs → chunk-4WYM72ZN.mjs} +1 -1
- package/dist/preview-engine/{chunk-EQ4VBUWM.mjs → chunk-5AWAOMBR.mjs} +1 -1
- package/dist/preview-engine/{chunk-J4SHMCTG.mjs → chunk-6R3UJBL5.mjs} +1 -1
- package/dist/preview-engine/{chunk-VHGC72XP.mjs → chunk-73CNXGAK.mjs} +1 -1
- package/dist/preview-engine/{chunk-K5F4ZW6T.mjs → chunk-7U34VKNO.mjs} +1 -1
- package/dist/preview-engine/{chunk-KYOPOCO5.mjs → chunk-AHQCDTD6.mjs} +1 -1
- package/dist/preview-engine/{chunk-ADV2TIKA.mjs → chunk-BEYA25UZ.mjs} +1 -1
- package/dist/preview-engine/{chunk-RBDIRBEI.mjs → chunk-BJV2LBII.mjs} +1 -1
- package/dist/preview-engine/{chunk-LBJBBEKP.mjs → chunk-BVDWS3D2.mjs} +1 -1
- package/dist/preview-engine/chunk-BXPM5SJ3.mjs +1 -0
- package/dist/preview-engine/{chunk-NIPWL6Z5.mjs → chunk-C4M3B2Q2.mjs} +1 -1
- package/dist/preview-engine/{chunk-LLUFOFF4.mjs → chunk-CC4NNNIY.mjs} +1 -1
- package/dist/preview-engine/chunk-CCB3VTY2.mjs +39 -0
- package/dist/preview-engine/{chunk-NX66555S.mjs → chunk-CN2VLYLB.mjs} +1 -1
- package/dist/preview-engine/{chunk-SK7PUBTC.mjs → chunk-DMSZXG56.mjs} +1 -1
- package/dist/preview-engine/{chunk-WC257OBY.mjs → chunk-DVZITDQS.mjs} +1 -1
- package/dist/preview-engine/chunk-FKG27DVO.mjs +4 -0
- package/dist/preview-engine/{chunk-COZG35ZR.mjs → chunk-FTCEG3SF.mjs} +1 -1
- package/dist/preview-engine/{chunk-57LES7TL.mjs → chunk-GA4HNR44.mjs} +1 -1
- package/dist/preview-engine/chunk-GEUUXTDL.mjs +138 -0
- package/dist/preview-engine/{chunk-S4WNCEMO.mjs → chunk-GIICZZQI.mjs} +1 -1
- package/dist/preview-engine/{chunk-RTHU7REC.mjs → chunk-GRJJVSOV.mjs} +1 -1
- package/dist/preview-engine/{chunk-RGDM6PQ5.mjs → chunk-GZWA5QJS.mjs} +1 -1
- package/dist/preview-engine/{chunk-LZNNM5OB.mjs → chunk-H7BSN7V7.mjs} +1 -1
- package/dist/preview-engine/{chunk-JXUENEQO.mjs → chunk-HOV6DDBZ.mjs} +1 -1
- package/dist/preview-engine/{chunk-C6XLAJLL.mjs → chunk-JSYG6OWZ.mjs} +1 -1
- package/dist/preview-engine/{chunk-GZVPVOHB.mjs → chunk-K3C6GUQD.mjs} +1 -1
- package/dist/preview-engine/chunk-KOVN277Z.mjs +1 -0
- package/dist/preview-engine/{chunk-Y6TWMUJC.mjs → chunk-LVYVXVZO.mjs} +1 -1
- package/dist/preview-engine/chunk-MIEH74UH.mjs +480 -0
- package/dist/preview-engine/{chunk-AVDHZNIZ.mjs → chunk-OH5W5EL6.mjs} +1 -1
- package/dist/preview-engine/{chunk-22F33PPP.mjs → chunk-P5FKRQFU.mjs} +1 -1
- package/dist/preview-engine/{chunk-7X2AUKTU.mjs → chunk-PM2W4VLT.mjs} +1 -1
- package/dist/preview-engine/chunk-QCSVT73T.mjs +2403 -0
- package/dist/preview-engine/{chunk-523CTDOC.mjs → chunk-RJIAES3X.mjs} +1 -1
- package/dist/preview-engine/{chunk-DPQN7WYQ.mjs → chunk-S2ABWY5G.mjs} +1 -1
- package/dist/preview-engine/{chunk-G7JJ6HVL.mjs → chunk-SF37HXAH.mjs} +1 -1
- package/dist/preview-engine/{chunk-OKLYIQ5F.mjs → chunk-SPJPM43P.mjs} +1 -1
- package/dist/preview-engine/{chunk-UOTMBWBJ.mjs → chunk-TNJEZKXM.mjs} +1 -1
- package/dist/preview-engine/chunk-ULOFDFHJ.mjs +1 -0
- package/dist/preview-engine/{chunk-LLB3AGBQ.mjs → chunk-UQKPDGEH.mjs} +1 -1
- package/dist/preview-engine/{chunk-DISKQ5MH.mjs → chunk-UXYM2G6J.mjs} +1 -1
- package/dist/preview-engine/{chunk-U5YNECNG.mjs → chunk-VN36PRVD.mjs} +1 -1
- package/dist/preview-engine/{chunk-PMOKPVSD.mjs → chunk-VVE2QHVG.mjs} +1 -1
- package/dist/preview-engine/{chunk-PEUQRSIM.mjs → chunk-Y5K5LVOR.mjs} +1 -1
- package/dist/preview-engine/chunk-YGKO4NIU.mjs +1 -0
- package/dist/preview-engine/{chunk-PSEMCJBH.mjs → chunk-ZJ5SWTCW.mjs} +1 -1
- package/dist/preview-engine/chunk-ZY47XIRK.mjs +5 -0
- package/dist/preview-engine/components.mjs +1 -1
- package/dist/preview-engine/effects.mjs +1 -1
- package/dist/preview-engine/framefields.mjs +1 -1
- package/dist/preview-engine/player.mjs +1 -1
- package/dist/preview-engine/renderers-3BOQOUEU.mjs +1 -0
- package/dist/preview-engine/renderers-3M2FESR2.mjs +1 -0
- package/dist/preview-engine/{renderers-FEEIDQF3.mjs → renderers-4F7WLQD5.mjs} +1 -1
- package/dist/preview-engine/{renderers-OP6MKYRM.mjs → renderers-5KANN7XF.mjs} +1 -1
- package/dist/preview-engine/{renderers-TVRR7PGN.mjs → renderers-5TO2SXFR.mjs} +1 -1
- package/dist/preview-engine/{renderers-DR7UZ3QR.mjs → renderers-5UZ3YFBD.mjs} +1 -1
- package/dist/preview-engine/renderers-6LHIWKMZ.mjs +1 -0
- package/dist/preview-engine/{renderers-7F2KF5RE.mjs → renderers-6Z7DWAPH.mjs} +1 -1
- package/dist/preview-engine/renderers-77PDUMYI.mjs +1 -0
- package/dist/preview-engine/{renderers-3J3NCOAI.mjs → renderers-7C5RA3R5.mjs} +1 -1
- package/dist/preview-engine/renderers-7XHO6M3B.mjs +228 -0
- package/dist/preview-engine/{renderers-KJAOPYAU.mjs → renderers-A24KZK2Q.mjs} +1 -1
- package/dist/preview-engine/renderers-A43VCJ7X.mjs +1 -0
- package/dist/preview-engine/renderers-AKY464RY.mjs +1 -0
- package/dist/preview-engine/renderers-C5UO6PL5.mjs +1 -0
- package/dist/preview-engine/renderers-C64QSZO4.mjs +1 -0
- package/dist/preview-engine/renderers-CIRCR6NG.mjs +1 -0
- package/dist/preview-engine/renderers-CIWOPETY.mjs +1 -0
- package/dist/preview-engine/renderers-CUMVIVHI.mjs +1 -0
- package/dist/preview-engine/renderers-CZEZCELP.mjs +1 -0
- package/dist/preview-engine/{renderers-R3YVLWKV.mjs → renderers-ENMY4DKF.mjs} +1 -1
- package/dist/preview-engine/{renderers-AAENCA7X.mjs → renderers-FSI5KNGZ.mjs} +1 -1
- package/dist/preview-engine/renderers-FSQMWHQM.mjs +1 -0
- package/dist/preview-engine/renderers-FZEW5C23.mjs +1 -0
- package/dist/preview-engine/renderers-HCET2IML.mjs +1 -0
- package/dist/preview-engine/{renderers-LBX7GPDU.mjs → renderers-IHGAWLUF.mjs} +1 -1
- package/dist/preview-engine/renderers-IUOOC6K7.mjs +1 -0
- package/dist/preview-engine/{renderers-VXMR47WZ.mjs → renderers-KC6ZSKCW.mjs} +1 -1
- package/dist/preview-engine/renderers-KYYJLVIJ.mjs +1 -0
- package/dist/preview-engine/renderers-L4GE7WV3.mjs +1 -0
- package/dist/preview-engine/renderers-L6YDX6GS.mjs +1 -0
- package/dist/preview-engine/renderers-LRAFWKCO.mjs +1 -0
- package/dist/preview-engine/renderers-LU3XZWEB.mjs +1 -0
- package/dist/preview-engine/{renderers-4YYGKLDK.mjs → renderers-MD3PRFZL.mjs} +1 -1
- package/dist/preview-engine/renderers-MOEC2XZZ.mjs +1 -0
- package/dist/preview-engine/{renderers-RGDTRVZM.mjs → renderers-NCGH2WWJ.mjs} +1 -1
- package/dist/preview-engine/renderers-ND2WV4K3.mjs +1 -0
- package/dist/preview-engine/renderers-NJ5CWVVH.mjs +1 -0
- package/dist/preview-engine/renderers-NKXYPR3V.mjs +1 -0
- package/dist/preview-engine/{renderers-WVQQZQ6Y.mjs → renderers-NUAI4TO5.mjs} +1 -1
- package/dist/preview-engine/renderers-NWR66CGK.mjs +1 -0
- package/dist/preview-engine/renderers-O22WTBRM.mjs +1 -0
- package/dist/preview-engine/renderers-O55BEDQZ.mjs +1 -0
- package/dist/preview-engine/renderers-PBBA7LQB.mjs +1 -0
- package/dist/preview-engine/{renderers-BL7Z3FTF.mjs → renderers-PIIEUKFE.mjs} +1 -1
- package/dist/preview-engine/renderers-PPZK6VEW.mjs +1 -0
- package/dist/preview-engine/renderers-Q6LJRZ7R.mjs +1 -0
- package/dist/preview-engine/{renderers-7TNEKJZS.mjs → renderers-QA2HKUJJ.mjs} +1 -1
- package/dist/preview-engine/renderers-RMEACSRL.mjs +1 -0
- package/dist/preview-engine/{renderers-AWRIELVX.mjs → renderers-RN2MHIFK.mjs} +1 -1
- package/dist/preview-engine/renderers-SEIAUKJF.mjs +1 -0
- package/dist/preview-engine/renderers-SP42LCSI.mjs +1 -0
- package/dist/preview-engine/{renderers-OE55O27K.mjs → renderers-T74CQCEN.mjs} +1 -1
- package/dist/preview-engine/renderers-T7SZA3BX.mjs +1 -0
- package/dist/preview-engine/{renderers-ZUQ2ZBYY.mjs → renderers-TGJSOKDY.mjs} +1 -1
- package/dist/preview-engine/renderers-TSBV4KPE.mjs +1 -0
- package/dist/preview-engine/{renderers-2SE5BLLB.mjs → renderers-U4M7ZCV4.mjs} +1 -1
- package/dist/preview-engine/{renderers-YEI4K4W4.mjs → renderers-V3AQO7AR.mjs} +1 -1
- package/dist/preview-engine/renderers-VADTDAHD.mjs +1 -0
- package/dist/preview-engine/renderers-VNNWI5C3.mjs +1 -0
- package/dist/preview-engine/{renderers-YYMRKHAA.mjs → renderers-WEYAHOBK.mjs} +1 -1
- package/dist/preview-engine/renderers-WZ75MLP3.mjs +1 -0
- package/dist/preview-engine/renderers-XB5R4UQA.mjs +1 -0
- package/dist/preview-engine/{renderers-YIVPMPQS.mjs → renderers-Y22VSOXF.mjs} +1 -1
- package/dist/preview-engine/{renderers-Y53D5CQ7.mjs → renderers-YBIUZDGQ.mjs} +1 -1
- package/dist/preview-engine/renderers-YESJR4O7.mjs +1 -0
- package/dist/preview-engine/renderers-YKSRZRRT.mjs +1 -0
- package/dist/preview-engine/{renderers-V4VML5IG.mjs → renderers-ZACSHGNZ.mjs} +1 -1
- package/dist/preview-engine/signals.mjs +1 -1
- package/dist/preview-engine/src-NGVAJYHC.mjs +8 -0
- package/dist/{program-CqQDWfSV.mjs → program-CKpKkllV.mjs} +5 -6
- package/dist/program-CKpKkllV.mjs.map +1 -0
- package/dist/renderer/index.d.mts +2 -2
- package/dist/renderer/index.mjs +2 -2
- package/dist/renderer--Ac-dg4A.mjs +5 -0
- package/dist/renderer--N7TMqzU.mjs +5 -0
- package/dist/{renderer-DYfNgEhK.mjs → renderer--fkmiVjl.mjs} +2 -2
- package/dist/{renderer-DYfNgEhK.mjs.map → renderer--fkmiVjl.mjs.map} +1 -1
- package/dist/{renderer-JDLYPnRh.mjs → renderer-1r-Uq-Ae.mjs} +2 -2
- package/dist/{renderer-JDLYPnRh.mjs.map → renderer-1r-Uq-Ae.mjs.map} +1 -1
- package/dist/renderer-2ecmdOae.mjs +5 -0
- package/dist/{renderer-DZKZup7n.mjs → renderer-8QIwXh7O.mjs} +3 -3
- package/dist/{renderer-DZKZup7n.mjs.map → renderer-8QIwXh7O.mjs.map} +1 -1
- package/dist/{renderer-DCyp-jzQ.mjs → renderer-B-ur0Ej8.mjs} +3 -3
- package/dist/{renderer-DCyp-jzQ.mjs.map → renderer-B-ur0Ej8.mjs.map} +1 -1
- package/dist/{renderer-BD25uND8.mjs → renderer-B0piZg9r.mjs} +383 -171
- package/dist/renderer-B0piZg9r.mjs.map +1 -0
- package/dist/{renderer-Cn34qZC4.mjs → renderer-B2jjxsjO.mjs} +2 -2
- package/dist/{renderer-Cn34qZC4.mjs.map → renderer-B2jjxsjO.mjs.map} +1 -1
- package/dist/{renderer-DbO4xZBb.mjs → renderer-B39I6CQw.mjs} +3 -3
- package/dist/{renderer-DbO4xZBb.mjs.map → renderer-B39I6CQw.mjs.map} +1 -1
- package/dist/{renderer-CTr_Zi08.mjs → renderer-B522Dkdk.mjs} +3 -3
- package/dist/{renderer-CTr_Zi08.mjs.map → renderer-B522Dkdk.mjs.map} +1 -1
- package/dist/renderer-B5RJp5g-.mjs +5 -0
- package/dist/renderer-B7xyT1Vb.mjs +5 -0
- package/dist/{renderer-4a4koLCl.mjs → renderer-BAYTI6jj.mjs} +2 -2
- package/dist/{renderer-4a4koLCl.mjs.map → renderer-BAYTI6jj.mjs.map} +1 -1
- package/dist/{renderer-CCnrG0Pd.mjs → renderer-BAusZ6Vu.mjs} +2 -2
- package/dist/{renderer-CCnrG0Pd.mjs.map → renderer-BAusZ6Vu.mjs.map} +1 -1
- package/dist/renderer-BBeArN_x.mjs +5 -0
- package/dist/renderer-BCAZDu-r.mjs +5 -0
- package/dist/{renderer-bE6yZ_Ef.mjs → renderer-BF0hx_iJ.mjs} +3 -3
- package/dist/{renderer-bE6yZ_Ef.mjs.map → renderer-BF0hx_iJ.mjs.map} +1 -1
- package/dist/renderer-BJnhik7Y.mjs +5 -0
- package/dist/{renderer-kg8-ngnB.mjs → renderer-BPWDsufX.mjs} +2 -2
- package/dist/{renderer-kg8-ngnB.mjs.map → renderer-BPWDsufX.mjs.map} +1 -1
- package/dist/{renderer-DXu0nrzo.d.mts → renderer-BX7jS14r.d.mts} +2 -2
- package/dist/{renderer-DXu0nrzo.d.mts.map → renderer-BX7jS14r.d.mts.map} +1 -1
- package/dist/{renderer-DBN6SV8L.mjs → renderer-BXJXziIt.mjs} +2 -2
- package/dist/{renderer-DBN6SV8L.mjs.map → renderer-BXJXziIt.mjs.map} +1 -1
- package/dist/{renderer-4F4FypvN.mjs → renderer-BdyopiLC.mjs} +2 -2
- package/dist/{renderer-4F4FypvN.mjs.map → renderer-BdyopiLC.mjs.map} +1 -1
- package/dist/{renderer-wH_l-jMH.mjs → renderer-Bfk6A7UO.mjs} +3 -3
- package/dist/{renderer-wH_l-jMH.mjs.map → renderer-Bfk6A7UO.mjs.map} +1 -1
- package/dist/{renderer-DYT4iPi-.mjs → renderer-BgO6BA_z.mjs} +2 -2
- package/dist/{renderer-DYT4iPi-.mjs.map → renderer-BgO6BA_z.mjs.map} +1 -1
- package/dist/{renderer-Dv5yE4LE.mjs → renderer-BlPqTEii.mjs} +2 -2
- package/dist/{renderer-Dv5yE4LE.mjs.map → renderer-BlPqTEii.mjs.map} +1 -1
- package/dist/{renderer-CuK1lPnA.mjs → renderer-BlvGcyAN.mjs} +2 -2
- package/dist/{renderer-CuK1lPnA.mjs.map → renderer-BlvGcyAN.mjs.map} +1 -1
- package/dist/{renderer-DX5ZpEBB.mjs → renderer-BpIF1QIE.mjs} +3 -3
- package/dist/{renderer-DX5ZpEBB.mjs.map → renderer-BpIF1QIE.mjs.map} +1 -1
- package/dist/renderer-BxRW8MZO.mjs +5 -0
- package/dist/renderer-C-lfsEAN.mjs +5 -0
- package/dist/{renderer-DRLtfxdW.mjs → renderer-C41U1tK6.mjs} +95 -16
- package/dist/renderer-C41U1tK6.mjs.map +1 -0
- package/dist/renderer-C6nOFN4-.mjs +5 -0
- package/dist/{renderer-CpEU-TRR.mjs → renderer-C7L8Ceqr.mjs} +2 -2
- package/dist/{renderer-CpEU-TRR.mjs.map → renderer-C7L8Ceqr.mjs.map} +1 -1
- package/dist/{renderer-BMnX_-PZ.mjs → renderer-C7eFJf0_.mjs} +3 -3
- package/dist/{renderer-BMnX_-PZ.mjs.map → renderer-C7eFJf0_.mjs.map} +1 -1
- package/dist/renderer-C84TSQmS.mjs +5 -0
- package/dist/renderer-C8_6YofT.mjs +5 -0
- package/dist/{renderer-CeIXVu9F.mjs → renderer-CCycwCEB.mjs} +2 -2
- package/dist/{renderer-CeIXVu9F.mjs.map → renderer-CCycwCEB.mjs.map} +1 -1
- package/dist/{renderer-CNVzJIBz.mjs → renderer-CHr7MiYW.mjs} +3 -3
- package/dist/{renderer-CNVzJIBz.mjs.map → renderer-CHr7MiYW.mjs.map} +1 -1
- package/dist/renderer-COR66Sby.mjs +3 -0
- package/dist/renderer-COb3tOPG.mjs +5 -0
- package/dist/{renderer-Crl8q_vD.mjs → renderer-CQvmbRQ5.mjs} +3 -3
- package/dist/{renderer-Crl8q_vD.mjs.map → renderer-CQvmbRQ5.mjs.map} +1 -1
- package/dist/renderer-CSCIzaLy.mjs +5 -0
- package/dist/{renderer-DO15Nj-w.mjs → renderer-CYC32bDW.mjs} +3 -3
- package/dist/{renderer-DO15Nj-w.mjs.map → renderer-CYC32bDW.mjs.map} +1 -1
- package/dist/{renderer-NvGahExL.mjs → renderer-CcvvoKiZ.mjs} +3 -3
- package/dist/{renderer-NvGahExL.mjs.map → renderer-CcvvoKiZ.mjs.map} +1 -1
- package/dist/{renderer-vVKNp3ri.mjs → renderer-CiQhD8Gg.mjs} +3 -3
- package/dist/{renderer-vVKNp3ri.mjs.map → renderer-CiQhD8Gg.mjs.map} +1 -1
- package/dist/renderer-CmI4c8MS.mjs +5 -0
- package/dist/{renderer-Sj11W1xq.mjs → renderer-CpUJBOfb.mjs} +2 -2
- package/dist/renderer-CqVltdX9.mjs +5 -0
- package/dist/{renderer-j-3XB8YI.mjs → renderer-CwFqqpWY.mjs} +3 -3
- package/dist/{renderer-j-3XB8YI.mjs.map → renderer-CwFqqpWY.mjs.map} +1 -1
- package/dist/renderer-D-FpMzLY.mjs +5 -0
- package/dist/renderer-D-Oyn9mG.mjs +5 -0
- package/dist/renderer-D2QrkA8_.mjs +5 -0
- package/dist/{renderer-ClxmV_id.mjs → renderer-D47AEJE_.mjs} +2 -2
- package/dist/{renderer-ClxmV_id.mjs.map → renderer-D47AEJE_.mjs.map} +1 -1
- package/dist/{renderer-BrXkb-9N.mjs → renderer-D61B9bXF.mjs} +2 -2
- package/dist/{renderer-BrXkb-9N.mjs.map → renderer-D61B9bXF.mjs.map} +1 -1
- package/dist/renderer-D64r4iN-.mjs +5 -0
- package/dist/{renderer-DXsV3mZD.mjs → renderer-D7Al3_-5.mjs} +3 -3
- package/dist/{renderer-DXsV3mZD.mjs.map → renderer-D7Al3_-5.mjs.map} +1 -1
- package/dist/{renderer-Bw3tlr9I.mjs → renderer-D7r6onRT.mjs} +2 -2
- package/dist/{renderer-Bw3tlr9I.mjs.map → renderer-D7r6onRT.mjs.map} +1 -1
- package/dist/{renderer-Dypn26aP.mjs → renderer-D8bBHrWC.mjs} +3 -3
- package/dist/{renderer-Dypn26aP.mjs.map → renderer-D8bBHrWC.mjs.map} +1 -1
- package/dist/renderer-DCq6YkgP.mjs +5 -0
- package/dist/{renderer-BHwbnT6S.mjs → renderer-DDEkFbVI.mjs} +3 -3
- package/dist/{renderer-BHwbnT6S.mjs.map → renderer-DDEkFbVI.mjs.map} +1 -1
- package/dist/renderer-DDHG3GdR.mjs +5 -0
- package/dist/{renderer-DqMZiy5N.mjs → renderer-DEmYyaqj.mjs} +2 -2
- package/dist/{renderer-DqMZiy5N.mjs.map → renderer-DEmYyaqj.mjs.map} +1 -1
- package/dist/{renderer-CyELq6P6.mjs → renderer-DF-yvIrV.mjs} +2 -2
- package/dist/{renderer-CyELq6P6.mjs.map → renderer-DF-yvIrV.mjs.map} +1 -1
- package/dist/{renderer-XnWe1hSL.mjs → renderer-DFzI3yI_.mjs} +2 -2
- package/dist/{renderer-XnWe1hSL.mjs.map → renderer-DFzI3yI_.mjs.map} +1 -1
- package/dist/{renderer-Cx2Sw9Ib.mjs → renderer-DIw3UtEU.mjs} +3 -3
- package/dist/{renderer-Cx2Sw9Ib.mjs.map → renderer-DIw3UtEU.mjs.map} +1 -1
- package/dist/{renderer-D_Mf-Zft.d.mts → renderer-DKgPVOLL.d.mts} +2 -2
- package/dist/{renderer-D_Mf-Zft.d.mts.map → renderer-DKgPVOLL.d.mts.map} +1 -1
- package/dist/renderer-DMdNfcud.mjs +6 -0
- package/dist/{renderer-CaRPsrbX.mjs → renderer-DMvmf3T2.mjs} +2 -2
- package/dist/{renderer-jvgqy4Td.mjs → renderer-DNQvOrkY.mjs} +3 -3
- package/dist/{renderer-jvgqy4Td.mjs.map → renderer-DNQvOrkY.mjs.map} +1 -1
- package/dist/{renderer-DB7PM7nZ.mjs → renderer-DO-KbpYZ.mjs} +3 -3
- package/dist/{renderer-DB7PM7nZ.mjs.map → renderer-DO-KbpYZ.mjs.map} +1 -1
- package/dist/{renderer-CQkZUsvc.mjs → renderer-DTqDy2m9.mjs} +3 -3
- package/dist/{renderer-CQkZUsvc.mjs.map → renderer-DTqDy2m9.mjs.map} +1 -1
- package/dist/{renderer-DPUULdjh.mjs → renderer-DVCJjVn7.mjs} +2 -2
- package/dist/{renderer-DPUULdjh.mjs.map → renderer-DVCJjVn7.mjs.map} +1 -1
- package/dist/{renderer-C7SYbHgB.mjs → renderer-DX41niRU.mjs} +2 -2
- package/dist/{renderer-C7SYbHgB.mjs.map → renderer-DX41niRU.mjs.map} +1 -1
- package/dist/{renderer-BYIa_hnS.mjs → renderer-DXEWbnRF.mjs} +3 -3
- package/dist/{renderer-BYIa_hnS.mjs.map → renderer-DXEWbnRF.mjs.map} +1 -1
- package/dist/renderer-DZQXZ4lK.mjs +5 -0
- package/dist/{renderer-iA7rxZmR.mjs → renderer-DbRZe0Re.mjs} +3 -3
- package/dist/{renderer-iA7rxZmR.mjs.map → renderer-DbRZe0Re.mjs.map} +1 -1
- package/dist/renderer-DczJJLA1.mjs +5 -0
- package/dist/renderer-DfqJj9oe.mjs +5 -0
- package/dist/renderer-DgO9ihVJ.mjs +5 -0
- package/dist/renderer-DhgnQZYb.mjs +5 -0
- package/dist/{renderer-l-K6iZLC.mjs → renderer-DiY70S8S.mjs} +3 -3
- package/dist/{renderer-l-K6iZLC.mjs.map → renderer-DiY70S8S.mjs.map} +1 -1
- package/dist/renderer-DjxKjHMf.mjs +5 -0
- package/dist/{renderer-Qd-HCwKj.mjs → renderer-DkRSZg5g.mjs} +3 -3
- package/dist/{renderer-Qd-HCwKj.mjs.map → renderer-DkRSZg5g.mjs.map} +1 -1
- package/dist/{renderer-CaE0rp9j.mjs → renderer-DmkWrWAk.mjs} +2 -2
- package/dist/{renderer-CaE0rp9j.mjs.map → renderer-DmkWrWAk.mjs.map} +1 -1
- package/dist/{renderer-BUV6IH0o.mjs → renderer-DpruWmZU.mjs} +2 -2
- package/dist/{renderer-BUV6IH0o.mjs.map → renderer-DpruWmZU.mjs.map} +1 -1
- package/dist/renderer-DqLA5cdR.mjs +5 -0
- package/dist/{renderer-DfTnsfy9.mjs → renderer-DqvoMLca.mjs} +3 -3
- package/dist/{renderer-DfTnsfy9.mjs.map → renderer-DqvoMLca.mjs.map} +1 -1
- package/dist/{renderer-Dhi0Q1cI.mjs → renderer-DsEyjwfT.mjs} +70 -70
- package/dist/{renderer-Dhi0Q1cI.mjs.map → renderer-DsEyjwfT.mjs.map} +1 -1
- package/dist/renderer-Dudp6ZlH.mjs +5 -0
- package/dist/{renderer-Bw5jGUo5.mjs → renderer-EaLUCobW.mjs} +2 -2
- package/dist/{renderer-Bw5jGUo5.mjs.map → renderer-EaLUCobW.mjs.map} +1 -1
- package/dist/{renderer-Ca-LoeJh.mjs → renderer-GBmWjBvy.mjs} +2 -2
- package/dist/{renderer-Ca-LoeJh.mjs.map → renderer-GBmWjBvy.mjs.map} +1 -1
- package/dist/{renderer-DS-Gv-3w.mjs → renderer-Lth37j17.mjs} +2 -2
- package/dist/{renderer-BlUfAZyK.mjs → renderer-SSBL4M1o.mjs} +3 -3
- package/dist/{renderer-BlUfAZyK.mjs.map → renderer-SSBL4M1o.mjs.map} +1 -1
- package/dist/{renderer-DSM5c7UA.mjs → renderer-SWr3vbrV.mjs} +2 -2
- package/dist/{renderer-DSM5c7UA.mjs.map → renderer-SWr3vbrV.mjs.map} +1 -1
- package/dist/{renderer-BLrhcSex.mjs → renderer-WCZu2Rf5.mjs} +3 -3
- package/dist/{renderer-BLrhcSex.mjs.map → renderer-WCZu2Rf5.mjs.map} +1 -1
- package/dist/{renderer-D7FuHXiz.mjs → renderer-Xv6GfNUI.mjs} +2 -2
- package/dist/{renderer-D7FuHXiz.mjs.map → renderer-Xv6GfNUI.mjs.map} +1 -1
- package/dist/renderer-_jC5KIT1.mjs +5 -0
- package/dist/{renderer-8pS82_mX.mjs → renderer-bAKUeHF3.mjs} +3 -3
- package/dist/{renderer-8pS82_mX.mjs.map → renderer-bAKUeHF3.mjs.map} +1 -1
- package/dist/renderer-cTgAuVmJ.mjs +5 -0
- package/dist/{renderer-DjWBBISJ.mjs → renderer-cod5Ona9.mjs} +2 -2
- package/dist/{renderer-C55eLbl1.mjs → renderer-g_vrrAQU.mjs} +2 -2
- package/dist/{renderer-C55eLbl1.mjs.map → renderer-g_vrrAQU.mjs.map} +1 -1
- package/dist/{renderer-CT_aJBk2.mjs → renderer-hSCfnvaK.mjs} +3 -3
- package/dist/{renderer-CT_aJBk2.mjs.map → renderer-hSCfnvaK.mjs.map} +1 -1
- package/dist/{renderer-BuYRrBRf.mjs → renderer-ktWXVYka.mjs} +3 -3
- package/dist/{renderer-BuYRrBRf.mjs.map → renderer-ktWXVYka.mjs.map} +1 -1
- package/dist/renderer-nVKDvDLO.mjs +5 -0
- package/dist/{renderer-7Tx-L9Vf.mjs → renderer-oKRF4A0M.mjs} +2 -2
- package/dist/{renderer-7Tx-L9Vf.mjs.map → renderer-oKRF4A0M.mjs.map} +1 -1
- package/dist/{renderer-fv9WvK0d.mjs → renderer-pHWEHDnM.mjs} +3 -3
- package/dist/{renderer-fv9WvK0d.mjs.map → renderer-pHWEHDnM.mjs.map} +1 -1
- package/dist/renderer-pZ6_pUo1.mjs +5 -0
- package/dist/{renderer-DsGE9qtz.mjs → renderer-sCovEubT.mjs} +3 -3
- package/dist/{renderer-DsGE9qtz.mjs.map → renderer-sCovEubT.mjs.map} +1 -1
- package/dist/{renderer-BcHpcLq3.mjs → renderer-sYSkIUfo.mjs} +3 -3
- package/dist/{renderer-BcHpcLq3.mjs.map → renderer-sYSkIUfo.mjs.map} +1 -1
- package/dist/{renderer-4GwADfB0.mjs → renderer-zXPVLRlD.mjs} +3 -3
- package/dist/{renderer-4GwADfB0.mjs.map → renderer-zXPVLRlD.mjs.map} +1 -1
- package/dist/{renderers-CP8CFWDs-fYqxoIKK.mjs → renderers-CP8CFWDs-DH2oktaC.mjs} +2 -2
- package/dist/{renderers-CP8CFWDs-fYqxoIKK.mjs.map → renderers-CP8CFWDs-DH2oktaC.mjs.map} +1 -1
- package/dist/signals/index.d.mts +5 -5
- package/dist/signals/index.mjs +4 -4
- package/dist/{src-DggYlKaX.mjs → src-CK5rGVbh.mjs} +664 -72
- package/dist/src-CK5rGVbh.mjs.map +1 -0
- package/package.json +67 -58
- package/dist/dist-CnFcSau0.mjs.map +0 -1
- package/dist/index-B_nooAhS.d.mts.map +0 -1
- package/dist/index-Ced1Ry4S.d.mts.map +0 -1
- package/dist/index-Ds9_v60R.d.mts.map +0 -1
- package/dist/preview-engine/chunk-3OXRGTQ2.mjs +0 -2390
- package/dist/preview-engine/chunk-3U2A3RW4.mjs +0 -5
- package/dist/preview-engine/chunk-6E3JSPJN.mjs +0 -1
- package/dist/preview-engine/chunk-7EYVBRN3.mjs +0 -480
- package/dist/preview-engine/chunk-7WBDEZ2A.mjs +0 -1974
- package/dist/preview-engine/chunk-F4F2UDRX.mjs +0 -1
- package/dist/preview-engine/chunk-HPP52AQI.mjs +0 -4
- package/dist/preview-engine/chunk-PYSBWKIJ.mjs +0 -1
- package/dist/preview-engine/chunk-U2IFQMS3.mjs +0 -174
- package/dist/preview-engine/renderers-2GMY2ZNK.mjs +0 -1
- package/dist/preview-engine/renderers-2QJNKL7Q.mjs +0 -1
- package/dist/preview-engine/renderers-3OYU6O6F.mjs +0 -1
- package/dist/preview-engine/renderers-433Y2PCK.mjs +0 -1
- package/dist/preview-engine/renderers-4AGPGEL4.mjs +0 -1
- package/dist/preview-engine/renderers-54DBHGUW.mjs +0 -1
- package/dist/preview-engine/renderers-5M2O55U3.mjs +0 -1
- package/dist/preview-engine/renderers-6FVMDEAY.mjs +0 -1
- package/dist/preview-engine/renderers-6R7ZJ7DU.mjs +0 -1
- package/dist/preview-engine/renderers-75GCMFOD.mjs +0 -1
- package/dist/preview-engine/renderers-7LWLUCVQ.mjs +0 -1
- package/dist/preview-engine/renderers-BLDENIEV.mjs +0 -1
- package/dist/preview-engine/renderers-CCOEGTZJ.mjs +0 -228
- package/dist/preview-engine/renderers-CDB3YK3R.mjs +0 -1
- package/dist/preview-engine/renderers-D6D4XDFY.mjs +0 -1
- package/dist/preview-engine/renderers-DJVFQZYU.mjs +0 -1
- package/dist/preview-engine/renderers-DMXSV7VO.mjs +0 -1
- package/dist/preview-engine/renderers-EBMASIOE.mjs +0 -1
- package/dist/preview-engine/renderers-F262HDFS.mjs +0 -1
- package/dist/preview-engine/renderers-FMXKIF22.mjs +0 -1
- package/dist/preview-engine/renderers-GJKSZ6DR.mjs +0 -1
- package/dist/preview-engine/renderers-GR3RLYDZ.mjs +0 -1
- package/dist/preview-engine/renderers-GT3W2QIA.mjs +0 -1
- package/dist/preview-engine/renderers-IHC7HUK3.mjs +0 -1
- package/dist/preview-engine/renderers-J56ZGEVM.mjs +0 -1
- package/dist/preview-engine/renderers-KYM6352U.mjs +0 -1
- package/dist/preview-engine/renderers-LFXX6TWI.mjs +0 -1
- package/dist/preview-engine/renderers-NAHUPFPU.mjs +0 -1
- package/dist/preview-engine/renderers-NM3FZLHH.mjs +0 -1
- package/dist/preview-engine/renderers-NNB4BTJA.mjs +0 -1
- package/dist/preview-engine/renderers-OYVQW6QI.mjs +0 -1
- package/dist/preview-engine/renderers-P5RIFU3C.mjs +0 -1
- package/dist/preview-engine/renderers-QB7HJUTS.mjs +0 -1
- package/dist/preview-engine/renderers-QRQVOCDC.mjs +0 -1
- package/dist/preview-engine/renderers-QVFHJTZA.mjs +0 -1
- package/dist/preview-engine/renderers-R3WQIE4L.mjs +0 -1
- package/dist/preview-engine/renderers-SQBVOLG7.mjs +0 -1
- package/dist/preview-engine/renderers-TLXMYEDI.mjs +0 -1
- package/dist/preview-engine/renderers-X4S3WELJ.mjs +0 -1
- package/dist/preview-engine/renderers-XT7U25I7.mjs +0 -1
- package/dist/preview-engine/renderers-XVGLMBB3.mjs +0 -1
- package/dist/preview-engine/renderers-YUWU3TQZ.mjs +0 -1
- package/dist/preview-engine/renderers-ZNUD5OVO.mjs +0 -1
- package/dist/program-CqQDWfSV.mjs.map +0 -1
- package/dist/renderer-68XHoE2b.mjs +0 -5
- package/dist/renderer-AqUForA-.mjs +0 -5
- package/dist/renderer-B0J-uGBG.mjs +0 -5
- package/dist/renderer-BD25uND8.mjs.map +0 -1
- package/dist/renderer-BF2R0rwb.mjs +0 -5
- package/dist/renderer-BLrO9lqK.mjs +0 -5
- package/dist/renderer-BMy8jXhL.mjs +0 -5
- package/dist/renderer-BUxENp9L.mjs +0 -5
- package/dist/renderer-C-ebryrM.mjs +0 -5
- package/dist/renderer-C8Gbs3kr.mjs +0 -5
- package/dist/renderer-CFZvMv2-.mjs +0 -5
- package/dist/renderer-CGXW7mOs.mjs +0 -5
- package/dist/renderer-CNq6OGbb.mjs +0 -5
- package/dist/renderer-CO-Ddz6-.mjs +0 -5
- package/dist/renderer-CRdxAsP7.mjs +0 -5
- package/dist/renderer-CZLo0ALi.mjs +0 -5
- package/dist/renderer-CdvVUkhR.mjs +0 -5
- package/dist/renderer-Cjg7kmTq.mjs +0 -5
- package/dist/renderer-Cvz22ZSh.mjs +0 -3
- package/dist/renderer-CxKymhVa.mjs +0 -5
- package/dist/renderer-Cxlpu3pN.mjs +0 -5
- package/dist/renderer-Cz6qZ2Ii.mjs +0 -5
- package/dist/renderer-DDGvF2YA.mjs +0 -5
- package/dist/renderer-DFxtOKpi.mjs +0 -5
- package/dist/renderer-DGBNjTsB.mjs +0 -6
- package/dist/renderer-DRLtfxdW.mjs.map +0 -1
- package/dist/renderer-DUFcdZmw.mjs +0 -5
- package/dist/renderer-DUMu6EOH.mjs +0 -5
- package/dist/renderer-DdULrvJo.mjs +0 -5
- package/dist/renderer-DoaOOP6_.mjs +0 -5
- package/dist/renderer-DqmZqV-a.mjs +0 -5
- package/dist/renderer-LxTQ2E-G.mjs +0 -5
- package/dist/renderer-SpU-RddZ.mjs +0 -5
- package/dist/renderer-W6ahCM9z.mjs +0 -5
- package/dist/renderer-gGqtzw-S.mjs +0 -5
- package/dist/renderer-l0Wn1AoV.mjs +0 -5
- package/dist/renderer-rpXHvMZG.mjs +0 -5
- package/dist/renderer-twdsKOiz.mjs +0 -5
- package/dist/renderer-uJz-eIqO.mjs +0 -5
- 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
|
|
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
|
-
|
|
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
|
|
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(
|
|
17935
|
-
for (let j = 0; 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
|
-
|
|
31194
|
+
let shadow = directLights[i];
|
|
31195
|
+
if (shadow.shadowParams.w < 0.5) {
|
|
31136
31196
|
return 1.0;
|
|
31137
31197
|
}
|
|
31138
|
-
let p = worldPos + N *
|
|
31139
|
-
let lp =
|
|
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(
|
|
31146
|
-
let radius =
|
|
31147
|
-
let depthRef = ndc.z -
|
|
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,
|
|
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 =
|
|
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 <
|
|
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 =
|
|
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 <
|
|
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 =
|
|
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 :
|
|
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
|
|
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
|
|
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<
|
|
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) &
|
|
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: {
|
|
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(
|
|
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: "
|
|
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(
|
|
33646
|
-
|
|
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:
|
|
33654
|
-
usage: GPUBufferUsage.
|
|
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
|
|
34123
|
-
if (c.
|
|
34124
|
-
|
|
34125
|
-
|
|
34126
|
-
|
|
34127
|
-
|
|
34128
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
34950
|
-
|
|
34951
|
-
const
|
|
34952
|
-
const
|
|
34953
|
-
|
|
34954
|
-
|
|
34955
|
-
|
|
34956
|
-
|
|
34957
|
-
|
|
34958
|
-
|
|
34959
|
-
|
|
34960
|
-
|
|
34961
|
-
|
|
34962
|
-
|
|
34963
|
-
|
|
34964
|
-
|
|
34965
|
-
|
|
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 <=
|
|
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
|
-
|
|
35009
|
-
|
|
35010
|
-
|
|
35011
|
-
|
|
35012
|
-
|
|
35013
|
-
const
|
|
35014
|
-
const
|
|
35015
|
-
const
|
|
35016
|
-
const
|
|
35017
|
-
|
|
35018
|
-
const
|
|
35019
|
-
|
|
35020
|
-
const
|
|
35021
|
-
const
|
|
35022
|
-
|
|
35023
|
-
|
|
35024
|
-
|
|
35025
|
-
|
|
35026
|
-
|
|
35027
|
-
|
|
35028
|
-
|
|
35029
|
-
|
|
35030
|
-
|
|
35031
|
-
|
|
35032
|
-
|
|
35033
|
-
|
|
35034
|
-
|
|
35035
|
-
|
|
35036
|
-
|
|
35037
|
-
[
|
|
35038
|
-
|
|
35039
|
-
|
|
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
|
-
|
|
35044
|
-
-
|
|
35045
|
-
|
|
35381
|
+
-1,
|
|
35382
|
+
-1,
|
|
35383
|
+
1
|
|
35046
35384
|
],
|
|
35047
35385
|
[
|
|
35048
|
-
|
|
35049
|
-
|
|
35050
|
-
-
|
|
35386
|
+
-1,
|
|
35387
|
+
1,
|
|
35388
|
+
-1
|
|
35051
35389
|
],
|
|
35052
35390
|
[
|
|
35053
|
-
|
|
35054
|
-
|
|
35055
|
-
-
|
|
35391
|
+
1,
|
|
35392
|
+
-1,
|
|
35393
|
+
-1
|
|
35056
35394
|
]
|
|
35057
|
-
]
|
|
35058
|
-
[
|
|
35395
|
+
];
|
|
35396
|
+
baseFaces = [
|
|
35059
35397
|
[
|
|
35060
|
-
|
|
35061
|
-
|
|
35062
|
-
|
|
35398
|
+
2,
|
|
35399
|
+
1,
|
|
35400
|
+
0
|
|
35063
35401
|
],
|
|
35064
35402
|
[
|
|
35065
|
-
|
|
35066
|
-
|
|
35067
|
-
|
|
35403
|
+
0,
|
|
35404
|
+
3,
|
|
35405
|
+
2
|
|
35068
35406
|
],
|
|
35069
35407
|
[
|
|
35070
|
-
|
|
35071
|
-
|
|
35072
|
-
|
|
35408
|
+
1,
|
|
35409
|
+
3,
|
|
35410
|
+
0
|
|
35073
35411
|
],
|
|
35074
35412
|
[
|
|
35075
|
-
|
|
35076
|
-
|
|
35077
|
-
|
|
35413
|
+
2,
|
|
35414
|
+
3,
|
|
35415
|
+
1
|
|
35078
35416
|
]
|
|
35079
|
-
]
|
|
35080
|
-
|
|
35417
|
+
];
|
|
35418
|
+
} else if (shape === "octahedron") {
|
|
35419
|
+
baseVerts = [
|
|
35081
35420
|
[
|
|
35082
|
-
|
|
35083
|
-
|
|
35084
|
-
|
|
35421
|
+
1,
|
|
35422
|
+
0,
|
|
35423
|
+
0
|
|
35085
35424
|
],
|
|
35086
35425
|
[
|
|
35087
|
-
-
|
|
35088
|
-
|
|
35089
|
-
|
|
35426
|
+
-1,
|
|
35427
|
+
0,
|
|
35428
|
+
0
|
|
35090
35429
|
],
|
|
35091
35430
|
[
|
|
35092
|
-
|
|
35093
|
-
|
|
35094
|
-
|
|
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
|
-
|
|
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
|
|
35362
|
-
|
|
35363
|
-
|
|
35364
|
-
|
|
35365
|
-
|
|
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
|
|
35375
|
-
materials
|
|
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
|
-
|
|
37499
|
-
|
|
37500
|
-
|
|
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.
|
|
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-
|
|
41021
|
+
//# sourceMappingURL=dist-C1cCW7Bt.mjs.map
|