framefields 2.3.1 → 2.4.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 +24 -1
- package/dist/audio/index.d.mts +4 -4
- package/dist/audio/index.mjs +10 -10
- package/dist/{audio-BNnEclHU.d.mts → audio-C6toT7F3.d.mts} +3 -3
- package/dist/{audio-BNnEclHU.d.mts.map → audio-C6toT7F3.d.mts.map} +1 -1
- package/dist/{audio-CS104aUj.mjs → audio-DXwNhi1z.mjs} +3 -3
- package/dist/{audio-CS104aUj.mjs.map → audio-DXwNhi1z.mjs.map} +1 -1
- package/dist/{dist-BOdxAhOT.mjs → dist-Cl0mvShy.mjs} +2 -2
- package/dist/{dist-BOdxAhOT.mjs.map → dist-Cl0mvShy.mjs.map} +1 -1
- package/dist/{dist-jU289ehm.mjs → dist-DRB6Vg5u.mjs} +1395 -878
- package/dist/dist-DRB6Vg5u.mjs.map +1 -0
- package/dist/effects/index.d.mts +4 -4
- package/dist/effects/index.mjs +34 -34
- package/dist/{effects-BzOlVD-w.mjs → effects-lBqG-vd1.mjs} +2 -2
- package/dist/{effects-BzOlVD-w.mjs.map → effects-lBqG-vd1.mjs.map} +1 -1
- package/dist/fonts/index.mjs +2 -2
- package/dist/{fonts-BhlV_3Qt.mjs → fonts-DlRJD6NS.mjs} +2 -2
- package/dist/{fonts-BhlV_3Qt.mjs.map → fonts-DlRJD6NS.mjs.map} +1 -1
- package/dist/{gpu-BA1Wz_mS.mjs → gpu-DOQX1W2q.mjs} +2 -2
- package/dist/{gpu-BA1Wz_mS.mjs.map → gpu-DOQX1W2q.mjs.map} +1 -1
- package/dist/{index-QmkcUL3W.d.mts → index-BHqdtJbN.d.mts} +2 -2
- package/dist/{index-QmkcUL3W.d.mts.map → index-BHqdtJbN.d.mts.map} +1 -1
- package/dist/{index-CeRv1KC0.d.mts → index-BONyHjZX.d.mts} +3 -3
- package/dist/{index-CeRv1KC0.d.mts.map → index-BONyHjZX.d.mts.map} +1 -1
- package/dist/{index-Cg2ptXcs.d.mts → index-CaiZ5p6q.d.mts} +2 -2
- package/dist/{index-Cg2ptXcs.d.mts.map → index-CaiZ5p6q.d.mts.map} +1 -1
- package/dist/{index-BU51pxZO.d.mts → index-Cf1pVxn0.d.mts} +4 -4
- package/dist/{index-BU51pxZO.d.mts.map → index-Cf1pVxn0.d.mts.map} +1 -1
- package/dist/{index-AdrZIWT0.d.mts → index-DEODKaL6.d.mts} +13 -1
- package/dist/index-DEODKaL6.d.mts.map +1 -0
- package/dist/{index-B3BwLHa6.d.mts → index-DN9oXO-X.d.mts} +3 -3
- package/dist/{index-B3BwLHa6.d.mts.map → index-DN9oXO-X.d.mts.map} +1 -1
- package/dist/index.d.mts +177 -9
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +485 -90
- package/dist/index.mjs.map +1 -1
- package/dist/preview/index.d.mts +2 -2
- package/dist/preview/index.mjs +10 -10
- package/dist/{preview-BVyG76gO.mjs → preview-Ca4tF5Ro.mjs} +2 -2
- package/dist/{preview-BVyG76gO.mjs.map → preview-Ca4tF5Ro.mjs.map} +1 -1
- package/dist/preview-engine/audio.mjs +1 -1
- package/dist/preview-engine/{chunk-VO2R3DWV.mjs → chunk-44CQSOYE.mjs} +1 -1
- package/dist/preview-engine/{chunk-FNE22WGA.mjs → chunk-6AEU4DC7.mjs} +4 -4
- package/dist/preview-engine/{chunk-PR2BUYCL.mjs → chunk-7G3XIOP7.mjs} +1 -1
- package/dist/preview-engine/chunk-7QJUC6YY.mjs +1525 -0
- package/dist/preview-engine/{chunk-AXQZX3TC.mjs → chunk-AY7UX37L.mjs} +1 -1
- package/dist/preview-engine/{chunk-FPRXK2WD.mjs → chunk-DIGIQ5MJ.mjs} +1 -1
- package/dist/preview-engine/{chunk-K773AOF3.mjs → chunk-EVXWDOCL.mjs} +1 -1
- package/dist/preview-engine/{chunk-ZNL4W4DK.mjs → chunk-EYWPOCZW.mjs} +1 -1
- package/dist/preview-engine/{chunk-WSIURGP2.mjs → chunk-HE4R7EZM.mjs} +1 -1
- package/dist/preview-engine/{chunk-L3CHEZMZ.mjs → chunk-IR5DNNWR.mjs} +1 -1
- package/dist/preview-engine/{chunk-2MZXSWVT.mjs → chunk-JXNG2ACR.mjs} +1 -1
- package/dist/preview-engine/{chunk-O2RNIQXT.mjs → chunk-L6BH5J4P.mjs} +1 -1
- package/dist/preview-engine/{chunk-BVA5NGSH.mjs → chunk-M5HZPZHQ.mjs} +1 -1
- package/dist/preview-engine/{chunk-PFA427ZC.mjs → chunk-MLY5M7Y5.mjs} +1 -1
- package/dist/preview-engine/{chunk-ZH5MZQTZ.mjs → chunk-OG62XGCT.mjs} +1 -1
- package/dist/preview-engine/{chunk-LBRVQ4GE.mjs → chunk-PSRQA3QW.mjs} +1 -1
- package/dist/preview-engine/{chunk-3UPI4EPV.mjs → chunk-Q22PJXFJ.mjs} +1 -1
- package/dist/preview-engine/{chunk-E3HC4GRQ.mjs → chunk-T2FE5VOW.mjs} +1 -1
- package/dist/preview-engine/{chunk-Q2DPNVIA.mjs → chunk-TS3JI3BH.mjs} +1 -1
- package/dist/preview-engine/{chunk-NEE3KQTX.mjs → chunk-U4JIFQRR.mjs} +1 -1
- package/dist/preview-engine/{chunk-BQY4BUMN.mjs → chunk-VGZZSPDN.mjs} +1 -1
- package/dist/preview-engine/{chunk-TPAEN6VV.mjs → chunk-VK2CVMWJ.mjs} +1 -1
- package/dist/preview-engine/effects.mjs +1 -1
- package/dist/preview-engine/framefields.mjs +32 -25
- package/dist/preview-engine/player.mjs +5 -5
- package/dist/preview-engine/renderers-2H7YEUTD.mjs +1 -1
- package/dist/preview-engine/renderers-2ILG23ED.mjs +1 -0
- package/dist/preview-engine/renderers-2P6KXWEZ.mjs +1 -1
- package/dist/preview-engine/{renderers-P4S7GL64.mjs → renderers-2VN3AH45.mjs} +1 -1
- package/dist/preview-engine/{renderers-LLUEOW5T.mjs → renderers-3GHEWWVX.mjs} +1 -1
- package/dist/preview-engine/renderers-3SRG77SC.mjs +1 -1
- package/dist/preview-engine/{renderers-YNFKFM7D.mjs → renderers-3YCJOINH.mjs} +1 -1
- package/dist/preview-engine/{renderers-WP67KK5K.mjs → renderers-4GRVAW2W.mjs} +1 -1
- package/dist/preview-engine/{renderers-BGGRPAVV.mjs → renderers-5FQA2DP4.mjs} +1 -1
- package/dist/preview-engine/{renderers-FGHOLJJK.mjs → renderers-5YEIUTVN.mjs} +1 -1
- package/dist/preview-engine/{renderers-JLFGBYN4.mjs → renderers-6CG7DBTB.mjs} +1 -1
- package/dist/preview-engine/{renderers-O2DZPBOJ.mjs → renderers-6OGFUSH6.mjs} +1 -1
- package/dist/preview-engine/renderers-6WH7NZCC.mjs +1 -1
- package/dist/preview-engine/renderers-7VJN5L3Z.mjs +1 -1
- package/dist/preview-engine/{renderers-7JTNJE7M.mjs → renderers-A6HM64UZ.mjs} +1 -1
- package/dist/preview-engine/renderers-B7DSGUMI.mjs +1 -0
- package/dist/preview-engine/renderers-BFNJ7VCI.mjs +1 -0
- package/dist/preview-engine/{renderers-XCMVZ2XG.mjs → renderers-BSKZXITT.mjs} +1 -1
- package/dist/preview-engine/renderers-CGYIDAVV.mjs +1 -0
- package/dist/preview-engine/{renderers-LVDUZZ75.mjs → renderers-CRRLV7IO.mjs} +1 -1
- package/dist/preview-engine/{renderers-TFZST6WW.mjs → renderers-D6T6IPER.mjs} +1 -1
- package/dist/preview-engine/renderers-DXHJAMZR.mjs +1 -1
- package/dist/preview-engine/{renderers-6ODPWZ2R.mjs → renderers-E76OKK66.mjs} +1 -1
- package/dist/preview-engine/renderers-ECA3P4ON.mjs +1 -0
- package/dist/preview-engine/{renderers-7NCG6BYS.mjs → renderers-EJWPVVQR.mjs} +1 -1
- package/dist/preview-engine/renderers-FWGMNPYB.mjs +1 -1
- package/dist/preview-engine/{renderers-3F5AIDEL.mjs → renderers-HQEKSUNR.mjs} +3 -3
- package/dist/preview-engine/renderers-HUW2ZMS3.mjs +1 -1
- package/dist/preview-engine/renderers-HXFRTIJD.mjs +1 -1
- package/dist/preview-engine/{renderers-ZSENYGIY.mjs → renderers-I5E2RSV4.mjs} +1 -1
- package/dist/preview-engine/renderers-IWQ3V6AJ.mjs +1 -1
- package/dist/preview-engine/renderers-JHFT27YU.mjs +1 -0
- package/dist/preview-engine/renderers-KZWGSKRF.mjs +1 -1
- package/dist/preview-engine/{renderers-FQWWAETX.mjs → renderers-MCECHPCL.mjs} +1 -1
- package/dist/preview-engine/renderers-MON4QFRF.mjs +1 -0
- package/dist/preview-engine/{renderers-UJ4YVJZT.mjs → renderers-N7FR54V2.mjs} +1 -1
- package/dist/preview-engine/{renderers-PVYP2ONH.mjs → renderers-N7TS5562.mjs} +1 -1
- package/dist/preview-engine/renderers-NQXMYNSY.mjs +1 -0
- package/dist/preview-engine/renderers-NWHS7N2L.mjs +1 -1
- package/dist/preview-engine/renderers-ORAIXUL3.mjs +1 -0
- package/dist/preview-engine/renderers-OT7D3BUA.mjs +1 -1
- package/dist/preview-engine/{renderers-WHBDHTRI.mjs → renderers-PYWXELLG.mjs} +1 -1
- package/dist/preview-engine/renderers-Q4OFCMXI.mjs +1 -1
- package/dist/preview-engine/renderers-QVZVEYP5.mjs +1 -0
- package/dist/preview-engine/renderers-RJO6H4FC.mjs +1 -1
- package/dist/preview-engine/renderers-RKDPX5JU.mjs +1 -0
- package/dist/preview-engine/{renderers-D2EJULOJ.mjs → renderers-RLHTNLRE.mjs} +1 -1
- package/dist/preview-engine/renderers-SBFF7JP5.mjs +1 -0
- package/dist/preview-engine/renderers-TAWT3ZSS.mjs +1 -1
- package/dist/preview-engine/{renderers-HMR2NAD7.mjs → renderers-TR2V2HZ2.mjs} +1 -1
- package/dist/preview-engine/{renderers-UPKB5OLS.mjs → renderers-U6KY34TP.mjs} +1 -1
- package/dist/preview-engine/renderers-UHTO6BQA.mjs +1 -1
- package/dist/preview-engine/renderers-UNK6I4JJ.mjs +1 -0
- package/dist/preview-engine/renderers-W773SBPV.mjs +1 -0
- package/dist/preview-engine/renderers-XHTMRXC5.mjs +1 -1
- package/dist/preview-engine/renderers-YECY2YFT.mjs +1 -1
- package/dist/preview-engine/renderers-YMN6QXK7.mjs +1 -1
- package/dist/preview-engine/{renderers-DN2GF3I7.mjs → renderers-YMPXGYRU.mjs} +1 -1
- package/dist/preview-engine/renderers-ZANPE3OK.mjs +1 -1
- package/dist/preview-engine/renderers-ZHTX73PR.mjs +1 -0
- package/dist/preview-engine/{renderers-TPBPNXWH.mjs → renderers-ZHZMGMLN.mjs} +1 -1
- package/dist/preview-engine/renderers-ZJTRANIX.mjs +1 -1
- package/dist/preview-engine/signals.mjs +1 -1
- package/dist/{program-CCSfHMfh.mjs → program-CJEg2wMa.mjs} +2 -2
- package/dist/{program-CCSfHMfh.mjs.map → program-CJEg2wMa.mjs.map} +1 -1
- package/dist/renderer/index.d.mts +2 -2
- package/dist/renderer/index.mjs +2 -2
- package/dist/renderer--IHe-yqK.mjs +5 -0
- package/dist/{renderer-DukD7S3w.mjs → renderer-445i0uxs.mjs} +2 -2
- package/dist/{renderer-DukD7S3w.mjs.map → renderer-445i0uxs.mjs.map} +1 -1
- package/dist/{renderer-CNYfB5cC.mjs → renderer-5tQ7ImPk.mjs} +2 -2
- package/dist/{renderer-CNYfB5cC.mjs.map → renderer-5tQ7ImPk.mjs.map} +1 -1
- package/dist/{renderer-g-ui08Lh.mjs → renderer-Ai5nZsof.mjs} +3 -3
- package/dist/{renderer-g-ui08Lh.mjs.map → renderer-Ai5nZsof.mjs.map} +1 -1
- package/dist/renderer-B16iwR0_.mjs +5 -0
- package/dist/renderer-B3EEYpx6.mjs +5 -0
- package/dist/renderer-B3Si5IB4.mjs +5 -0
- package/dist/{renderer-BetA_iVd.mjs → renderer-B4JmNa_T.mjs} +3 -3
- package/dist/{renderer-BetA_iVd.mjs.map → renderer-B4JmNa_T.mjs.map} +1 -1
- package/dist/{renderer-BvvuGfZk.mjs → renderer-B6UqWjtH.mjs} +3 -3
- package/dist/{renderer-BvvuGfZk.mjs.map → renderer-B6UqWjtH.mjs.map} +1 -1
- package/dist/{renderer-D1wd0FhZ.mjs → renderer-B8v8iEkB.mjs} +7 -6
- package/dist/{renderer-D1wd0FhZ.mjs.map → renderer-B8v8iEkB.mjs.map} +1 -1
- package/dist/{renderer-FXUCKps6.mjs → renderer-BFWRUTov.mjs} +3 -3
- package/dist/{renderer-FXUCKps6.mjs.map → renderer-BFWRUTov.mjs.map} +1 -1
- package/dist/renderer-BH8vZKmX.mjs +5 -0
- package/dist/renderer-BHZie6Pa.mjs +5 -0
- package/dist/renderer-BHpJ1e15.mjs +5 -0
- package/dist/{renderer-CMdtUn5o.mjs → renderer-BL36f2IQ.mjs} +2 -2
- package/dist/{renderer-CMdtUn5o.mjs.map → renderer-BL36f2IQ.mjs.map} +1 -1
- package/dist/{renderer-BonWt8jF.mjs → renderer-BOvCstdU.mjs} +3 -3
- package/dist/{renderer-BonWt8jF.mjs.map → renderer-BOvCstdU.mjs.map} +1 -1
- package/dist/renderer-BPJ79b47.mjs +5 -0
- package/dist/renderer-BPbM_6WR.mjs +5 -0
- package/dist/{renderer-BmDpyiII.mjs → renderer-BPsi7Q7Z.mjs} +3 -3
- package/dist/{renderer-BmDpyiII.mjs.map → renderer-BPsi7Q7Z.mjs.map} +1 -1
- package/dist/{renderer-CuphUktB.mjs → renderer-BTJyO6ov.mjs} +2 -2
- package/dist/{renderer-CuphUktB.mjs.map → renderer-BTJyO6ov.mjs.map} +1 -1
- package/dist/{renderer-Celrh689.mjs → renderer-BZCLdb8F.mjs} +3 -3
- package/dist/{renderer-Celrh689.mjs.map → renderer-BZCLdb8F.mjs.map} +1 -1
- package/dist/{renderer-D7ss6fLB.mjs → renderer-BZF48o1T.mjs} +3 -3
- package/dist/{renderer-D7ss6fLB.mjs.map → renderer-BZF48o1T.mjs.map} +1 -1
- package/dist/{renderer-DOWekunR.mjs → renderer-BZWiY6Jw.mjs} +3 -3
- package/dist/{renderer-DOWekunR.mjs.map → renderer-BZWiY6Jw.mjs.map} +1 -1
- package/dist/{renderer-dUkOBfGB.mjs → renderer-BZvWlGsa.mjs} +3 -3
- package/dist/{renderer-dUkOBfGB.mjs.map → renderer-BZvWlGsa.mjs.map} +1 -1
- package/dist/{renderer-D26S5hUh.mjs → renderer-BaBluD-r.mjs} +2 -2
- package/dist/{renderer-D26S5hUh.mjs.map → renderer-BaBluD-r.mjs.map} +1 -1
- package/dist/{renderer-BD7Lmb8h.mjs → renderer-BaHEN_Kv.mjs} +3 -3
- package/dist/{renderer-BD7Lmb8h.mjs.map → renderer-BaHEN_Kv.mjs.map} +1 -1
- package/dist/{renderer-Cn6VlWHZ.mjs → renderer-BhZutM8K.mjs} +3 -3
- package/dist/{renderer-Cn6VlWHZ.mjs.map → renderer-BhZutM8K.mjs.map} +1 -1
- package/dist/renderer-Bn-WnRh9.mjs +5 -0
- package/dist/renderer-BqHvDAUe.mjs +5 -0
- package/dist/{renderer-PknbdJyL.mjs → renderer-BtJ6XYN7.mjs} +3 -3
- package/dist/{renderer-PknbdJyL.mjs.map → renderer-BtJ6XYN7.mjs.map} +1 -1
- package/dist/{renderer-C39GD6jP.mjs → renderer-BzgnTEvf.mjs} +3 -3
- package/dist/{renderer-C39GD6jP.mjs.map → renderer-BzgnTEvf.mjs.map} +1 -1
- package/dist/{renderer-c-bwxMde.mjs → renderer-C-La-IAt.mjs} +2 -2
- package/dist/{renderer-c-bwxMde.mjs.map → renderer-C-La-IAt.mjs.map} +1 -1
- package/dist/{renderer-pPiQwFYh.mjs → renderer-C2ZIAhdJ.mjs} +2 -2
- package/dist/{renderer-pPiQwFYh.mjs.map → renderer-C2ZIAhdJ.mjs.map} +1 -1
- package/dist/{renderer-C6cF5seJ.mjs → renderer-C9pl21Zb.mjs} +2 -2
- package/dist/{renderer-C6cF5seJ.mjs.map → renderer-C9pl21Zb.mjs.map} +1 -1
- package/dist/renderer-CAtXDNsh.mjs +5 -0
- package/dist/{renderer-CLdwOhST.mjs → renderer-CCCitH4J.mjs} +2 -2
- package/dist/{renderer-CLdwOhST.mjs.map → renderer-CCCitH4J.mjs.map} +1 -1
- package/dist/{renderer-Dq4aO7az.mjs → renderer-CCu1Q8ib.mjs} +2 -2
- package/dist/{renderer-Dq4aO7az.mjs.map → renderer-CCu1Q8ib.mjs.map} +1 -1
- package/dist/{renderer-Bel1UHA5.mjs → renderer-CG4TmDWH.mjs} +2 -2
- package/dist/{renderer-CiMpdh0s.mjs → renderer-CJmZURPQ.mjs} +2 -2
- package/dist/{renderer-CiMpdh0s.mjs.map → renderer-CJmZURPQ.mjs.map} +1 -1
- package/dist/{renderer-B93zd3sx.mjs → renderer-CN3iCli4.mjs} +2 -2
- package/dist/{renderer-B93zd3sx.mjs.map → renderer-CN3iCli4.mjs.map} +1 -1
- package/dist/{renderer-BFRI5alp.mjs → renderer-CNS8LA2_.mjs} +3 -3
- package/dist/{renderer-BFRI5alp.mjs.map → renderer-CNS8LA2_.mjs.map} +1 -1
- package/dist/renderer-C_5nfVce.mjs +5 -0
- package/dist/{renderer-kSs8VtY7.mjs → renderer-CduDa1qf.mjs} +3 -3
- package/dist/{renderer-kSs8VtY7.mjs.map → renderer-CduDa1qf.mjs.map} +1 -1
- package/dist/{renderer-CFe9pZFN.mjs → renderer-CduL1AOK.mjs} +2 -2
- package/dist/{renderer-CFe9pZFN.mjs.map → renderer-CduL1AOK.mjs.map} +1 -1
- package/dist/{renderer-BRwp1kV-.mjs → renderer-Cfto0m0-.mjs} +3 -3
- package/dist/{renderer-BRwp1kV-.mjs.map → renderer-Cfto0m0-.mjs.map} +1 -1
- package/dist/{renderer-DCYabwlC.mjs → renderer-CgZ1G37H.mjs} +2 -2
- package/dist/{renderer-DCYabwlC.mjs.map → renderer-CgZ1G37H.mjs.map} +1 -1
- package/dist/renderer-Cie7KKeQ.mjs +5 -0
- package/dist/{renderer-C8GZ0pCV.mjs → renderer-CjTxu89O.mjs} +3 -3
- package/dist/{renderer-C8GZ0pCV.mjs.map → renderer-CjTxu89O.mjs.map} +1 -1
- package/dist/{renderer-4VpA7LfU.mjs → renderer-CoN2JgLO.mjs} +2 -2
- package/dist/{renderer-4VpA7LfU.mjs.map → renderer-CoN2JgLO.mjs.map} +1 -1
- package/dist/{renderer-YJ3Yukou.mjs → renderer-CoO2j8AC.mjs} +3 -3
- package/dist/{renderer-YJ3Yukou.mjs.map → renderer-CoO2j8AC.mjs.map} +1 -1
- package/dist/{renderer-DpwlN4LH.mjs → renderer-CoX23zo_.mjs} +3 -3
- package/dist/{renderer-DpwlN4LH.mjs.map → renderer-CoX23zo_.mjs.map} +1 -1
- package/dist/renderer-CuoxvPsX.mjs +5 -0
- package/dist/renderer-Cvd3oIJQ.mjs +5 -0
- package/dist/renderer-CxAgpBrJ.mjs +5 -0
- package/dist/{renderer-BvBIHi6N.mjs → renderer-CxSH3F_n.mjs} +2 -2
- package/dist/{renderer-BvBIHi6N.mjs.map → renderer-CxSH3F_n.mjs.map} +1 -1
- package/dist/{renderer-DUBb7D17.mjs → renderer-Cz9fzS11.mjs} +3 -3
- package/dist/{renderer-DUBb7D17.mjs.map → renderer-Cz9fzS11.mjs.map} +1 -1
- package/dist/{renderer-C3lPwajT.mjs → renderer-D-IcBLT0.mjs} +3 -3
- package/dist/{renderer-C3lPwajT.mjs.map → renderer-D-IcBLT0.mjs.map} +1 -1
- package/dist/renderer-D-cWBX0r.mjs +5 -0
- package/dist/{renderer-HMl2V4iM.mjs → renderer-D-e3vDnO.mjs} +3 -3
- package/dist/{renderer-HMl2V4iM.mjs.map → renderer-D-e3vDnO.mjs.map} +1 -1
- package/dist/renderer-D0q2VyJR.mjs +5 -0
- package/dist/{renderer-AtLlZMQ9.mjs → renderer-D4E8Yf61.mjs} +2 -2
- package/dist/{renderer-AtLlZMQ9.mjs.map → renderer-D4E8Yf61.mjs.map} +1 -1
- package/dist/renderer-D6NXn3Ky.mjs +5 -0
- package/dist/renderer-D9bp2jor.mjs +5 -0
- package/dist/{renderer-BpEsZG3t.mjs → renderer-DA1brg35.mjs} +3 -3
- package/dist/{renderer-BpEsZG3t.mjs.map → renderer-DA1brg35.mjs.map} +1 -1
- package/dist/{renderer-D1Jt-c3F.mjs → renderer-DAFi5TC_.mjs} +2 -2
- package/dist/{renderer-D1Jt-c3F.mjs.map → renderer-DAFi5TC_.mjs.map} +1 -1
- package/dist/renderer-DEzIOIB7.mjs +5 -0
- package/dist/{renderer-Cu5k5D9N.mjs → renderer-DFX2V9Dp.mjs} +2 -2
- package/dist/{renderer-Cu5k5D9N.mjs.map → renderer-DFX2V9Dp.mjs.map} +1 -1
- package/dist/{renderer-8BM6sJUR.mjs → renderer-DH0wkQkR.mjs} +2 -2
- package/dist/{renderer-8BM6sJUR.mjs.map → renderer-DH0wkQkR.mjs.map} +1 -1
- package/dist/{renderer-DerDxKK3.mjs → renderer-DOZJeWM4.mjs} +3 -3
- package/dist/{renderer-DerDxKK3.mjs.map → renderer-DOZJeWM4.mjs.map} +1 -1
- package/dist/{renderer-BVUTOGHs.mjs → renderer-DOkEalxy.mjs} +2 -2
- package/dist/{renderer-BVUTOGHs.mjs.map → renderer-DOkEalxy.mjs.map} +1 -1
- package/dist/renderer-DSSTVulw.mjs +5 -0
- package/dist/renderer-DUmTURPu.mjs +5 -0
- package/dist/{renderer-LM_XIKl1.mjs → renderer-DVaVVz-D.mjs} +3 -3
- package/dist/{renderer-LM_XIKl1.mjs.map → renderer-DVaVVz-D.mjs.map} +1 -1
- package/dist/renderer-DXCAgI9m.mjs +5 -0
- package/dist/{renderer-O1b-oYJE.mjs → renderer-DY8fPeH3.mjs} +2 -2
- package/dist/renderer-DZ9j3fQj.mjs +5 -0
- package/dist/{renderer-CWx3Kg79.mjs → renderer-DZndEvZN.mjs} +3 -3
- package/dist/{renderer-CWx3Kg79.mjs.map → renderer-DZndEvZN.mjs.map} +1 -1
- package/dist/{renderer-DPWeKe_x.mjs → renderer-DaV95CAd.mjs} +3 -3
- package/dist/{renderer-DPWeKe_x.mjs.map → renderer-DaV95CAd.mjs.map} +1 -1
- package/dist/{renderer-Dh-OF6aH.mjs → renderer-DautVsW4.mjs} +2 -2
- package/dist/{renderer-Dh-OF6aH.mjs.map → renderer-DautVsW4.mjs.map} +1 -1
- package/dist/renderer-Dd1G_VKM.mjs +5 -0
- package/dist/renderer-Dee8079X.mjs +5 -0
- package/dist/{renderer-BG5sbTXt.mjs → renderer-Dg5yLAM9.mjs} +3 -3
- package/dist/{renderer-BG5sbTXt.mjs.map → renderer-Dg5yLAM9.mjs.map} +1 -1
- package/dist/renderer-DgWh5noB.mjs +5 -0
- package/dist/{renderer-DlESyfg7.d.mts → renderer-Dim2ZlfK.d.mts} +2 -2
- package/dist/{renderer-DlESyfg7.d.mts.map → renderer-Dim2ZlfK.d.mts.map} +1 -1
- package/dist/{renderer-DMZ5aLU1.mjs → renderer-Dk0dnuHr.mjs} +2 -2
- package/dist/{renderer-DMZ5aLU1.mjs.map → renderer-Dk0dnuHr.mjs.map} +1 -1
- package/dist/{renderer-Bzl0s07U.mjs → renderer-DkM_wN8j.mjs} +2 -2
- package/dist/{renderer-Bzl0s07U.mjs.map → renderer-DkM_wN8j.mjs.map} +1 -1
- package/dist/{renderer-CqewgazK.mjs → renderer-DkYiOWPJ.mjs} +3 -3
- package/dist/{renderer-CqewgazK.mjs.map → renderer-DkYiOWPJ.mjs.map} +1 -1
- package/dist/{renderer-Dk-THZHp.mjs → renderer-DqgduGiO.mjs} +3 -3
- package/dist/{renderer-Dk-THZHp.mjs.map → renderer-DqgduGiO.mjs.map} +1 -1
- package/dist/renderer-DuD9QRix.mjs +5 -0
- package/dist/{renderer-DCGLXnYW.mjs → renderer-Edn9xltT.mjs} +2 -2
- package/dist/renderer-KtG5p44R.mjs +5 -0
- package/dist/{renderer-DcAfp1Er.mjs → renderer-Tf1AS1aO.mjs} +2 -2
- package/dist/{renderer-DcAfp1Er.mjs.map → renderer-Tf1AS1aO.mjs.map} +1 -1
- package/dist/{renderer-1uLtqb74.mjs → renderer-TyZu7nQ2.mjs} +91 -73
- package/dist/renderer-TyZu7nQ2.mjs.map +1 -0
- package/dist/{renderer-DdlaBsk_.mjs → renderer-eWBBsCv2.mjs} +2 -2
- package/dist/{renderer-DdlaBsk_.mjs.map → renderer-eWBBsCv2.mjs.map} +1 -1
- package/dist/{renderer-YdLL2o6d.mjs → renderer-fXRwAdO2.mjs} +2 -2
- package/dist/{renderer-YdLL2o6d.mjs.map → renderer-fXRwAdO2.mjs.map} +1 -1
- package/dist/renderer-fdVdNIhL.mjs +5 -0
- package/dist/renderer-gBKWL0_E.mjs +5 -0
- package/dist/{renderer-DfkWYUTX.mjs → renderer-gbBgGPNL.mjs} +2 -2
- package/dist/{renderer-DaCsT-XU.mjs → renderer-h9sJnoxw.mjs} +3 -3
- package/dist/{renderer-DaCsT-XU.mjs.map → renderer-h9sJnoxw.mjs.map} +1 -1
- package/dist/renderer-hmWhsR6C.mjs +5 -0
- package/dist/{renderer-CdOy3EBJ.mjs → renderer-i4RJ-_sx.mjs} +2 -2
- package/dist/{renderer-CdOy3EBJ.mjs.map → renderer-i4RJ-_sx.mjs.map} +1 -1
- package/dist/renderer-jkL9FU2W.mjs +6 -0
- package/dist/{renderer-wL-ly4fT.mjs → renderer-nMnE_T2y.mjs} +3 -3
- package/dist/{renderer-wL-ly4fT.mjs.map → renderer-nMnE_T2y.mjs.map} +1 -1
- package/dist/{renderer-DpWRj6zF.mjs → renderer-q971j1BW.mjs} +3 -3
- package/dist/{renderer-DpWRj6zF.mjs.map → renderer-q971j1BW.mjs.map} +1 -1
- package/dist/{renderer-ChloaI6P.mjs → renderer-wFyrVuuj.mjs} +3 -3
- package/dist/{renderer-ChloaI6P.mjs.map → renderer-wFyrVuuj.mjs.map} +1 -1
- package/dist/renderer-yAWv-daq.mjs +5 -0
- package/dist/{renderer-BI4OMlEx.mjs → renderer-yGARXwV0.mjs} +2 -2
- package/dist/{renderer-BI4OMlEx.mjs.map → renderer-yGARXwV0.mjs.map} +1 -1
- package/dist/{renderers-CP8CFWDs-CVHwMTiz.mjs → renderers-CP8CFWDs-DoVn7dbW.mjs} +2 -2
- package/dist/{renderers-CP8CFWDs-CVHwMTiz.mjs.map → renderers-CP8CFWDs-DoVn7dbW.mjs.map} +1 -1
- package/dist/signals/index.d.mts +5 -5
- package/dist/signals/index.mjs +4 -4
- package/package.json +57 -56
- package/dist/dist-jU289ehm.mjs.map +0 -1
- package/dist/index-AdrZIWT0.d.mts.map +0 -1
- package/dist/preview-engine/chunk-GCTVRM7J.mjs +0 -1522
- package/dist/preview-engine/chunk-OCX5AD23.mjs +0 -1
- package/dist/preview-engine/renderers-3T7Q4SPP.mjs +0 -1
- package/dist/preview-engine/renderers-4DV4REGM.mjs +0 -1
- package/dist/preview-engine/renderers-CQXQHOW2.mjs +0 -1
- package/dist/preview-engine/renderers-IXGM55XK.mjs +0 -1
- package/dist/preview-engine/renderers-KVHUVWSN.mjs +0 -1
- package/dist/preview-engine/renderers-M7R2ZJUP.mjs +0 -1
- package/dist/preview-engine/renderers-PMOO7DLX.mjs +0 -1
- package/dist/preview-engine/renderers-QCCPCH73.mjs +0 -1
- package/dist/preview-engine/renderers-RRXFJRGI.mjs +0 -1
- package/dist/preview-engine/renderers-VLMQR3DW.mjs +0 -1
- package/dist/preview-engine/renderers-WVTTRCVI.mjs +0 -1
- package/dist/preview-engine/renderers-X5PUIPU5.mjs +0 -1
- package/dist/preview-engine/renderers-YDCDY3BR.mjs +0 -1
- package/dist/preview-engine/renderers-YJNQ4B7B.mjs +0 -1
- package/dist/preview-engine/renderers-Z7YQUFAY.mjs +0 -1
- package/dist/preview-engine/skia-canvas-QBXAOGPB.mjs +0 -1
- package/dist/renderer-1Kv7oA86.mjs +0 -5
- package/dist/renderer-1uLtqb74.mjs.map +0 -1
- package/dist/renderer-AVHJ33nv.mjs +0 -5
- package/dist/renderer-B8dJGf8R.mjs +0 -6
- package/dist/renderer-BEA6nknY.mjs +0 -5
- package/dist/renderer-BOfi06L2.mjs +0 -5
- package/dist/renderer-BOhvtAGL.mjs +0 -5
- package/dist/renderer-BQT3GI9g.mjs +0 -5
- package/dist/renderer-BXmStgeT.mjs +0 -5
- package/dist/renderer-Bajxo7Nt.mjs +0 -5
- package/dist/renderer-BzRR0KMm.mjs +0 -5
- package/dist/renderer-C3Gjy2iE.mjs +0 -5
- package/dist/renderer-C5hQgkz-.mjs +0 -5
- package/dist/renderer-CLF-MiOA.mjs +0 -5
- package/dist/renderer-CLa43fCd.mjs +0 -5
- package/dist/renderer-CMK1d6w_.mjs +0 -5
- package/dist/renderer-CPEQOe3u.mjs +0 -5
- package/dist/renderer-CemfjOM9.mjs +0 -5
- package/dist/renderer-CkcrScs9.mjs +0 -5
- package/dist/renderer-CwrLihY6.mjs +0 -5
- package/dist/renderer-D4S6i2MC.mjs +0 -5
- package/dist/renderer-DB8F0XoP.mjs +0 -5
- package/dist/renderer-DGpVLJOn.mjs +0 -5
- package/dist/renderer-DGxibeSa.mjs +0 -5
- package/dist/renderer-DJQm3bAW.mjs +0 -5
- package/dist/renderer-DMftVSfX.mjs +0 -5
- package/dist/renderer-DNpovOd8.mjs +0 -5
- package/dist/renderer-DSHlvKQk.mjs +0 -5
- package/dist/renderer-DYtrdR2p.mjs +0 -5
- package/dist/renderer-DuSImLUz.mjs +0 -5
- package/dist/renderer-FbyMYKXe.mjs +0 -5
- package/dist/renderer-NMFv7Auz.mjs +0 -5
- package/dist/renderer-P3vpdUnb.mjs +0 -5
- package/dist/renderer-QTOm26Lg.mjs +0 -5
- package/dist/renderer-ahzhMa5q.mjs +0 -5
- package/dist/renderer-dThfTNge.mjs +0 -5
- package/dist/renderer-dgAS_k7B.mjs +0 -5
|
@@ -12675,6 +12675,63 @@ var InputStore = class {
|
|
|
12675
12675
|
}
|
|
12676
12676
|
};
|
|
12677
12677
|
const inputStore = new InputStore();
|
|
12678
|
+
/** The texture formats the renderer decodes (sharp); others (TGA, BMP, PSD, DDS, …) are skipped. */
|
|
12679
|
+
const MIME_BY_EXT = {
|
|
12680
|
+
".png": "image/png",
|
|
12681
|
+
".jpg": "image/jpeg",
|
|
12682
|
+
".jpeg": "image/jpeg",
|
|
12683
|
+
".webp": "image/webp",
|
|
12684
|
+
".gif": "image/gif",
|
|
12685
|
+
".tif": "image/tiff",
|
|
12686
|
+
".tiff": "image/tiff",
|
|
12687
|
+
".avif": "image/avif"
|
|
12688
|
+
};
|
|
12689
|
+
/** MIME type of an image file name, by extension (PNG when unknown). */
|
|
12690
|
+
function textureMime(name) {
|
|
12691
|
+
return MIME_BY_EXT[path.extname(name).toLowerCase()] ?? "image/png";
|
|
12692
|
+
}
|
|
12693
|
+
/** Whether the renderer can decode a texture with this file name (no extension: assume yes). */
|
|
12694
|
+
function isSupportedTexture(name) {
|
|
12695
|
+
const ext = path.extname(name.replace(/\\/g, "/")).toLowerCase();
|
|
12696
|
+
return !ext || ext in MIME_BY_EXT;
|
|
12697
|
+
}
|
|
12698
|
+
/**
|
|
12699
|
+
* Reads a texture a model file refers to. Exporters often write absolute
|
|
12700
|
+
* paths from the author's machine (`C:\Users\…\wood.png`), so after the
|
|
12701
|
+
* path as written (relative to `basePath`), the file name is also tried in
|
|
12702
|
+
* `basePath`, its `textures/` folder and any `*.fbm` folder next to it.
|
|
12703
|
+
*/
|
|
12704
|
+
function readTextureFile(ref, basePath) {
|
|
12705
|
+
if (!ref || typeof fs === "undefined" || !fs.existsSync) return void 0;
|
|
12706
|
+
if (!isSupportedTexture(ref)) return void 0;
|
|
12707
|
+
let cleaned = ref.trim().replace(/^file:\/\//, "");
|
|
12708
|
+
try {
|
|
12709
|
+
cleaned = decodeURIComponent(cleaned);
|
|
12710
|
+
} catch (_) {}
|
|
12711
|
+
const normalized = cleaned.replace(/\\/g, "/");
|
|
12712
|
+
const fileName = normalized.split("/").pop() ?? normalized;
|
|
12713
|
+
const candidates = [];
|
|
12714
|
+
if (path.isAbsolute(normalized)) candidates.push(normalized);
|
|
12715
|
+
if (basePath) {
|
|
12716
|
+
candidates.push(path.resolve(basePath, normalized));
|
|
12717
|
+
candidates.push(path.join(basePath, fileName));
|
|
12718
|
+
candidates.push(path.join(basePath, "textures", fileName));
|
|
12719
|
+
try {
|
|
12720
|
+
for (const entry of fs.readdirSync(basePath)) if (entry.toLowerCase().endsWith(".fbm")) candidates.push(path.join(basePath, entry, fileName));
|
|
12721
|
+
} catch (_) {}
|
|
12722
|
+
}
|
|
12723
|
+
for (const file of candidates) try {
|
|
12724
|
+
if (fs.existsSync(file) && fs.statSync(file).isFile()) return {
|
|
12725
|
+
bytes: new Uint8Array(fs.readFileSync(file)),
|
|
12726
|
+
mime: textureMime(file)
|
|
12727
|
+
};
|
|
12728
|
+
} catch (_) {}
|
|
12729
|
+
}
|
|
12730
|
+
/** Warns that a model's texture is missing or in an unsupported format (the mesh renders untextured). */
|
|
12731
|
+
function warnMissingTexture(loader, material, ref) {
|
|
12732
|
+
const reason = isSupportedTexture(ref) ? "was not found" : `is in an unsupported format (use ${Object.keys(MIME_BY_EXT).join(", ")})`;
|
|
12733
|
+
console.warn(`[${loader}] Texture "${ref}" for material "${material}" ${reason}; the material renders without it.`);
|
|
12734
|
+
}
|
|
12678
12735
|
var ObjLoader = class ObjLoader$1 {
|
|
12679
12736
|
/**
|
|
12680
12737
|
* Parses Wavefront .obj text content (and optional .mtl text) into Model3DData.
|
|
@@ -12692,7 +12749,7 @@ var ObjLoader = class ObjLoader$1 {
|
|
|
12692
12749
|
};
|
|
12693
12750
|
meshBuilders.push(currentBuilder);
|
|
12694
12751
|
const materials = {};
|
|
12695
|
-
if (mtlText) Object.assign(materials, ObjLoader$1.parseMtl(mtlText));
|
|
12752
|
+
if (mtlText) Object.assign(materials, ObjLoader$1.loadTextures(ObjLoader$1.parseMtl(mtlText), options.basePath));
|
|
12696
12753
|
const lines = objText.split(/\r?\n/);
|
|
12697
12754
|
for (let i = 0; i < lines.length; i++) {
|
|
12698
12755
|
const line = lines[i]?.trim();
|
|
@@ -12705,7 +12762,7 @@ var ObjLoader = class ObjLoader$1 {
|
|
|
12705
12762
|
const resolvedMtlPath = path.resolve(options.basePath, mtlFileName);
|
|
12706
12763
|
if (fs.existsSync(resolvedMtlPath)) {
|
|
12707
12764
|
const content = fs.readFileSync(resolvedMtlPath, "utf-8");
|
|
12708
|
-
Object.assign(materials, ObjLoader$1.parseMtl(content));
|
|
12765
|
+
Object.assign(materials, ObjLoader$1.loadTextures(ObjLoader$1.parseMtl(content), path.dirname(resolvedMtlPath)));
|
|
12709
12766
|
}
|
|
12710
12767
|
} catch (_) {}
|
|
12711
12768
|
} else if (cmd === "v") {
|
|
@@ -12793,12 +12850,12 @@ var ObjLoader = class ObjLoader$1 {
|
|
|
12793
12850
|
maxY = 1;
|
|
12794
12851
|
maxZ = 1;
|
|
12795
12852
|
}
|
|
12796
|
-
|
|
12797
|
-
|
|
12798
|
-
|
|
12799
|
-
|
|
12800
|
-
|
|
12801
|
-
|
|
12853
|
+
const centerX = (minX + maxX) * .5;
|
|
12854
|
+
const centerY = (minY + maxY) * .5;
|
|
12855
|
+
const centerZ = (minZ + maxZ) * .5;
|
|
12856
|
+
const sizeX = Math.max(1e-4, maxX - minX);
|
|
12857
|
+
const sizeY = Math.max(1e-4, maxY - minY);
|
|
12858
|
+
const sizeZ = Math.max(1e-4, maxZ - minZ);
|
|
12802
12859
|
const maxDim = Math.max(sizeX, sizeY, sizeZ);
|
|
12803
12860
|
let scaleMultiplier = 1;
|
|
12804
12861
|
if (options.normalizeSize && options.normalizeSize > 0) scaleMultiplier = options.normalizeSize / maxDim;
|
|
@@ -13012,8 +13069,57 @@ var ObjLoader = class ObjLoader$1 {
|
|
|
13012
13069
|
const val = Number.parseFloat(parts[1] ?? "1");
|
|
13013
13070
|
currentMat.opacity = cmd === "d" ? val : 1 - val;
|
|
13014
13071
|
if (currentMat.diffuseColor) currentMat.diffuseColor[3] = currentMat.opacity;
|
|
13015
|
-
} else if (cmd === "map_kd")
|
|
13016
|
-
|
|
13072
|
+
} else if (cmd === "map_kd") {
|
|
13073
|
+
currentMat.diffuseTexture = ObjLoader$1.mapFileName(parts);
|
|
13074
|
+
const clamp = parts.findIndex((p) => p.toLowerCase() === "-clamp");
|
|
13075
|
+
currentMat.textureWrap = clamp > 0 && parts[clamp + 1]?.toLowerCase() === "on" ? "clamp" : "repeat";
|
|
13076
|
+
} else if (cmd === "norm" || cmd === "map_kn") currentMat.normalTexture = ObjLoader$1.mapFileName(parts);
|
|
13077
|
+
}
|
|
13078
|
+
}
|
|
13079
|
+
return materials;
|
|
13080
|
+
}
|
|
13081
|
+
/** Option arities of MTL map statements (`-o`, `-s`, `-t` take 1–3 numbers). */
|
|
13082
|
+
static MAP_OPTION_ARGS = {
|
|
13083
|
+
"-blendu": 1,
|
|
13084
|
+
"-blendv": 1,
|
|
13085
|
+
"-cc": 1,
|
|
13086
|
+
"-clamp": 1,
|
|
13087
|
+
"-texres": 1,
|
|
13088
|
+
"-bm": 1,
|
|
13089
|
+
"-boost": 1,
|
|
13090
|
+
"-imfchan": 1,
|
|
13091
|
+
"-type": 1,
|
|
13092
|
+
"-mm": 2
|
|
13093
|
+
};
|
|
13094
|
+
/** The file name of a map statement (`map_Kd -s 1 1 1 wood grain.png`), past its options. */
|
|
13095
|
+
static mapFileName(parts) {
|
|
13096
|
+
let i = 1;
|
|
13097
|
+
while (i < parts.length && parts[i]?.startsWith("-")) {
|
|
13098
|
+
const option = parts[i].toLowerCase();
|
|
13099
|
+
i++;
|
|
13100
|
+
if (option === "-o" || option === "-s" || option === "-t") for (let n = 0; n < 3 && i < parts.length; n++) {
|
|
13101
|
+
if (Number.isNaN(Number(parts[i]))) break;
|
|
13102
|
+
i++;
|
|
13103
|
+
}
|
|
13104
|
+
else i += ObjLoader$1.MAP_OPTION_ARGS[option] ?? 0;
|
|
13105
|
+
}
|
|
13106
|
+
return parts.slice(i).join(" ");
|
|
13107
|
+
}
|
|
13108
|
+
/** Reads the diffuse and normal maps the materials name, relative to `dir`. */
|
|
13109
|
+
static loadTextures(materials, dir) {
|
|
13110
|
+
if (!dir) return materials;
|
|
13111
|
+
for (const mat of Object.values(materials)) {
|
|
13112
|
+
if (mat.diffuseTexture && !mat.diffuseTextureBuffer) {
|
|
13113
|
+
const file = readTextureFile(mat.diffuseTexture, dir);
|
|
13114
|
+
if (file) {
|
|
13115
|
+
mat.diffuseTextureBuffer = file.bytes;
|
|
13116
|
+
mat.diffuseTextureMime = file.mime;
|
|
13117
|
+
} else warnMissingTexture("ObjLoader", mat.name, mat.diffuseTexture);
|
|
13118
|
+
}
|
|
13119
|
+
if (mat.normalTexture && !mat.normalTextureBuffer) {
|
|
13120
|
+
const file = readTextureFile(mat.normalTexture, dir);
|
|
13121
|
+
if (file) mat.normalTextureBuffer = file.bytes;
|
|
13122
|
+
else warnMissingTexture("ObjLoader", mat.name, mat.normalTexture);
|
|
13017
13123
|
}
|
|
13018
13124
|
}
|
|
13019
13125
|
return materials;
|
|
@@ -13049,192 +13155,501 @@ function loadOBJ(srcOrData, mtlText, options = {}) {
|
|
|
13049
13155
|
}
|
|
13050
13156
|
return ObjLoader.parse(text, mtlText, options);
|
|
13051
13157
|
}
|
|
13052
|
-
|
|
13053
|
-
|
|
13054
|
-
|
|
13055
|
-
|
|
13056
|
-
|
|
13057
|
-
|
|
13058
|
-
|
|
13059
|
-
|
|
13158
|
+
/**
|
|
13159
|
+
* Adds each triangle's (area-weighted) face normal to its three vertices, then
|
|
13160
|
+
* normalizes every vertex normal in place.
|
|
13161
|
+
*/
|
|
13162
|
+
function accumulateVertexNormals(positions, indices, normals) {
|
|
13163
|
+
const vertex = (i) => [
|
|
13164
|
+
positions[i * 3],
|
|
13165
|
+
positions[i * 3 + 1],
|
|
13166
|
+
positions[i * 3 + 2]
|
|
13167
|
+
];
|
|
13168
|
+
for (let i = 0; i < indices.length; i += 3) {
|
|
13169
|
+
const i0 = indices[i];
|
|
13170
|
+
const i1 = indices[i + 1];
|
|
13171
|
+
const i2 = indices[i + 2];
|
|
13172
|
+
const p0 = vertex(i0);
|
|
13173
|
+
const fn = Vector3Math.cross(Vector3Math.subtract(vertex(i1), p0), Vector3Math.subtract(vertex(i2), p0));
|
|
13174
|
+
for (const v of [
|
|
13175
|
+
i0,
|
|
13176
|
+
i1,
|
|
13177
|
+
i2
|
|
13178
|
+
]) {
|
|
13179
|
+
normals[v * 3] += fn[0];
|
|
13180
|
+
normals[v * 3 + 1] += fn[1];
|
|
13181
|
+
normals[v * 3 + 2] += fn[2];
|
|
13182
|
+
}
|
|
13060
13183
|
}
|
|
13061
|
-
|
|
13062
|
-
const
|
|
13063
|
-
|
|
13064
|
-
const
|
|
13065
|
-
|
|
13066
|
-
|
|
13184
|
+
for (let i = 0; i < normals.length; i += 3) {
|
|
13185
|
+
const nx = normals[i];
|
|
13186
|
+
const ny = normals[i + 1];
|
|
13187
|
+
const nz = normals[i + 2];
|
|
13188
|
+
const len = Math.hypot(nx, ny, nz) || 1;
|
|
13189
|
+
normals[i] = nx / len;
|
|
13190
|
+
normals[i + 1] = ny / len;
|
|
13191
|
+
normals[i + 2] = nz / len;
|
|
13067
13192
|
}
|
|
13068
|
-
|
|
13069
|
-
|
|
13070
|
-
|
|
13071
|
-
|
|
13072
|
-
|
|
13073
|
-
|
|
13074
|
-
const
|
|
13075
|
-
|
|
13076
|
-
const
|
|
13077
|
-
|
|
13078
|
-
|
|
13079
|
-
|
|
13080
|
-
|
|
13081
|
-
|
|
13082
|
-
|
|
13083
|
-
let endOffset;
|
|
13084
|
-
let numProperties;
|
|
13085
|
-
let nameLen;
|
|
13086
|
-
if (is64Bit) {
|
|
13087
|
-
if (offset + 25 > u8.byteLength) return null;
|
|
13088
|
-
endOffset = Number(view.getBigUint64(offset, true));
|
|
13089
|
-
numProperties = Number(view.getBigUint64(offset + 8, true));
|
|
13090
|
-
nameLen = view.getUint8(offset + 24);
|
|
13091
|
-
offset += 25;
|
|
13092
|
-
} else {
|
|
13093
|
-
if (offset + 13 > u8.byteLength) return null;
|
|
13094
|
-
endOffset = view.getUint32(offset, true);
|
|
13095
|
-
numProperties = view.getUint32(offset + 4, true);
|
|
13096
|
-
nameLen = view.getUint8(offset + 12);
|
|
13097
|
-
offset += 13;
|
|
13098
|
-
}
|
|
13099
|
-
if (endOffset === 0) return null;
|
|
13100
|
-
const name = new TextDecoder().decode(u8.subarray(offset, offset + nameLen));
|
|
13101
|
-
offset += nameLen;
|
|
13102
|
-
const props = [];
|
|
13103
|
-
for (let p = 0; p < numProperties; p++) {
|
|
13104
|
-
const typeCode = String.fromCharCode(u8[offset]);
|
|
13105
|
-
offset += 1;
|
|
13106
|
-
switch (typeCode) {
|
|
13107
|
-
case "Y":
|
|
13108
|
-
props.push(view.getInt16(offset, true));
|
|
13109
|
-
offset += 2;
|
|
13110
|
-
break;
|
|
13111
|
-
case "C":
|
|
13112
|
-
case "B":
|
|
13113
|
-
props.push(view.getUint8(offset) !== 0);
|
|
13114
|
-
offset += 1;
|
|
13115
|
-
break;
|
|
13116
|
-
case "I":
|
|
13117
|
-
props.push(view.getInt32(offset, true));
|
|
13118
|
-
offset += 4;
|
|
13119
|
-
break;
|
|
13120
|
-
case "F":
|
|
13121
|
-
props.push(view.getFloat32(offset, true));
|
|
13122
|
-
offset += 4;
|
|
13123
|
-
break;
|
|
13124
|
-
case "D":
|
|
13125
|
-
props.push(view.getFloat64(offset, true));
|
|
13126
|
-
offset += 8;
|
|
13127
|
-
break;
|
|
13128
|
-
case "L":
|
|
13129
|
-
props.push(Number(view.getBigInt64(offset, true)));
|
|
13130
|
-
offset += 8;
|
|
13131
|
-
break;
|
|
13132
|
-
case "S": {
|
|
13133
|
-
const len = view.getUint32(offset, true);
|
|
13134
|
-
offset += 4;
|
|
13135
|
-
const str = new TextDecoder().decode(u8.subarray(offset, offset + len));
|
|
13136
|
-
props.push(str);
|
|
13137
|
-
offset += len;
|
|
13138
|
-
break;
|
|
13139
|
-
}
|
|
13140
|
-
case "R": {
|
|
13141
|
-
const len = view.getUint32(offset, true);
|
|
13142
|
-
offset += 4;
|
|
13143
|
-
const raw = u8.subarray(offset, offset + len);
|
|
13144
|
-
props.push(raw);
|
|
13145
|
-
offset += len;
|
|
13146
|
-
break;
|
|
13147
|
-
}
|
|
13148
|
-
case "f":
|
|
13149
|
-
case "d":
|
|
13150
|
-
case "i":
|
|
13151
|
-
case "l":
|
|
13152
|
-
case "b":
|
|
13153
|
-
case "c": {
|
|
13154
|
-
const arrayLen = view.getUint32(offset, true);
|
|
13155
|
-
const encoding = view.getUint32(offset + 4, true);
|
|
13156
|
-
const compressedLen = view.getUint32(offset + 8, true);
|
|
13157
|
-
offset += 12;
|
|
13158
|
-
let arrayBuffer;
|
|
13159
|
-
if (encoding === 1) {
|
|
13160
|
-
arrayBuffer = inflateSync(u8.subarray(offset, offset + compressedLen));
|
|
13161
|
-
offset += compressedLen;
|
|
13162
|
-
} else {
|
|
13163
|
-
const totalBytes = arrayLen * (typeCode === "d" || typeCode === "l" ? 8 : typeCode === "f" || typeCode === "i" ? 4 : 1);
|
|
13164
|
-
arrayBuffer = u8.subarray(offset, offset + totalBytes);
|
|
13165
|
-
offset += totalBytes;
|
|
13166
|
-
}
|
|
13167
|
-
const arrayView = new DataView(arrayBuffer.buffer, arrayBuffer.byteOffset, arrayBuffer.byteLength);
|
|
13168
|
-
if (typeCode === "f") {
|
|
13169
|
-
const arr = new Float32Array(arrayLen);
|
|
13170
|
-
for (let k = 0; k < arrayLen; k++) arr[k] = arrayView.getFloat32(k * 4, true);
|
|
13171
|
-
props.push(arr);
|
|
13172
|
-
} else if (typeCode === "d") {
|
|
13173
|
-
const arr = new Float64Array(arrayLen);
|
|
13174
|
-
for (let k = 0; k < arrayLen; k++) arr[k] = arrayView.getFloat64(k * 8, true);
|
|
13175
|
-
props.push(arr);
|
|
13176
|
-
} else if (typeCode === "i") {
|
|
13177
|
-
const arr = new Int32Array(arrayLen);
|
|
13178
|
-
for (let k = 0; k < arrayLen; k++) arr[k] = arrayView.getInt32(k * 4, true);
|
|
13179
|
-
props.push(arr);
|
|
13180
|
-
} else if (typeCode === "l") {
|
|
13181
|
-
const arr = new Float64Array(arrayLen);
|
|
13182
|
-
for (let k = 0; k < arrayLen; k++) arr[k] = Number(arrayView.getBigInt64(k * 8, true));
|
|
13183
|
-
props.push(arr);
|
|
13184
|
-
} else if (typeCode === "b") {
|
|
13185
|
-
const arr = new Uint8Array(arrayLen);
|
|
13186
|
-
for (let k = 0; k < arrayLen; k++) arr[k] = arrayView.getUint8(k);
|
|
13187
|
-
props.push(arr);
|
|
13188
|
-
} else props.push(arrayBuffer);
|
|
13189
|
-
break;
|
|
13190
|
-
}
|
|
13191
|
-
default: break;
|
|
13192
|
-
}
|
|
13193
|
-
}
|
|
13194
|
-
const children = [];
|
|
13195
|
-
while (offset < endOffset) {
|
|
13196
|
-
const child = readNode();
|
|
13197
|
-
if (child) children.push(child);
|
|
13198
|
-
else break;
|
|
13199
|
-
}
|
|
13200
|
-
offset = endOffset;
|
|
13201
|
-
return {
|
|
13202
|
-
name,
|
|
13203
|
-
props,
|
|
13204
|
-
children
|
|
13205
|
-
};
|
|
13206
|
-
};
|
|
13207
|
-
while (offset < u8.byteLength - (is64Bit ? 25 : 13)) {
|
|
13208
|
-
const node = readNode();
|
|
13209
|
-
if (node) root.children.push(node);
|
|
13210
|
-
else break;
|
|
13211
|
-
}
|
|
13212
|
-
return root;
|
|
13193
|
+
}
|
|
13194
|
+
/** Axis-aligned bounds of xyz position arrays (all zero when empty). */
|
|
13195
|
+
function computeBounds(...positionArrays) {
|
|
13196
|
+
let minX = Infinity, minY = Infinity, minZ = Infinity;
|
|
13197
|
+
let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
|
|
13198
|
+
for (const positions of positionArrays) for (let i = 0; i < positions.length; i += 3) {
|
|
13199
|
+
const x = positions[i];
|
|
13200
|
+
const y = positions[i + 1];
|
|
13201
|
+
const z = positions[i + 2];
|
|
13202
|
+
if (x < minX) minX = x;
|
|
13203
|
+
if (x > maxX) maxX = x;
|
|
13204
|
+
if (y < minY) minY = y;
|
|
13205
|
+
if (y > maxY) maxY = y;
|
|
13206
|
+
if (z < minZ) minZ = z;
|
|
13207
|
+
if (z > maxZ) maxZ = z;
|
|
13213
13208
|
}
|
|
13214
|
-
|
|
13215
|
-
|
|
13216
|
-
|
|
13217
|
-
|
|
13218
|
-
|
|
13219
|
-
|
|
13220
|
-
|
|
13221
|
-
|
|
13222
|
-
|
|
13223
|
-
|
|
13224
|
-
|
|
13225
|
-
|
|
13226
|
-
|
|
13227
|
-
|
|
13228
|
-
|
|
13229
|
-
|
|
13230
|
-
|
|
13231
|
-
|
|
13232
|
-
|
|
13233
|
-
|
|
13234
|
-
|
|
13235
|
-
|
|
13236
|
-
|
|
13237
|
-
|
|
13209
|
+
if (!Number.isFinite(minX)) {
|
|
13210
|
+
minX = minY = minZ = 0;
|
|
13211
|
+
maxX = maxY = maxZ = 0;
|
|
13212
|
+
}
|
|
13213
|
+
return {
|
|
13214
|
+
min: [
|
|
13215
|
+
minX,
|
|
13216
|
+
minY,
|
|
13217
|
+
minZ
|
|
13218
|
+
],
|
|
13219
|
+
max: [
|
|
13220
|
+
maxX,
|
|
13221
|
+
maxY,
|
|
13222
|
+
maxZ
|
|
13223
|
+
],
|
|
13224
|
+
center: [
|
|
13225
|
+
(minX + maxX) * .5,
|
|
13226
|
+
(minY + maxY) * .5,
|
|
13227
|
+
(minZ + maxZ) * .5
|
|
13228
|
+
],
|
|
13229
|
+
size: [
|
|
13230
|
+
maxX - minX,
|
|
13231
|
+
maxY - minY,
|
|
13232
|
+
maxZ - minZ
|
|
13233
|
+
]
|
|
13234
|
+
};
|
|
13235
|
+
}
|
|
13236
|
+
/**
|
|
13237
|
+
* The placement `LoadModelOptions` asks for: recentring on the bounds
|
|
13238
|
+
* (`center`), fitting the largest side to `normalizeSize`, then `scale`.
|
|
13239
|
+
*/
|
|
13240
|
+
function resolveModelTransform(bounds, options) {
|
|
13241
|
+
const maxDimension = Math.max(...bounds.size, 1e-4);
|
|
13242
|
+
let scaleMultiplier = 1;
|
|
13243
|
+
if (options.normalizeSize && options.normalizeSize > 0) scaleMultiplier = options.normalizeSize / maxDimension;
|
|
13244
|
+
const scale = [
|
|
13245
|
+
scaleMultiplier,
|
|
13246
|
+
scaleMultiplier,
|
|
13247
|
+
scaleMultiplier
|
|
13248
|
+
];
|
|
13249
|
+
if (typeof options.scale === "number") {
|
|
13250
|
+
scale[0] *= options.scale;
|
|
13251
|
+
scale[1] *= options.scale;
|
|
13252
|
+
scale[2] *= options.scale;
|
|
13253
|
+
} else if (Array.isArray(options.scale)) {
|
|
13254
|
+
scale[0] *= options.scale[0];
|
|
13255
|
+
scale[1] *= options.scale[1];
|
|
13256
|
+
scale[2] *= options.scale[2];
|
|
13257
|
+
}
|
|
13258
|
+
return {
|
|
13259
|
+
offset: options.center ? bounds.center : [
|
|
13260
|
+
0,
|
|
13261
|
+
0,
|
|
13262
|
+
0
|
|
13263
|
+
],
|
|
13264
|
+
scale
|
|
13265
|
+
};
|
|
13266
|
+
}
|
|
13267
|
+
/** `(p - offset) * scale` for every xyz position. */
|
|
13268
|
+
function transformPositions(positions, { offset, scale }) {
|
|
13269
|
+
const out = new Float32Array(positions.length);
|
|
13270
|
+
for (let i = 0; i < positions.length; i += 3) {
|
|
13271
|
+
out[i] = (positions[i] - offset[0]) * scale[0];
|
|
13272
|
+
out[i + 1] = (positions[i + 1] - offset[1]) * scale[1];
|
|
13273
|
+
out[i + 2] = (positions[i + 2] - offset[2]) * scale[2];
|
|
13274
|
+
}
|
|
13275
|
+
return out;
|
|
13276
|
+
}
|
|
13277
|
+
/** Uint16 indices while every vertex fits, Uint32 beyond 65535 vertices. */
|
|
13278
|
+
function createIndexArray(indices, vertexCount) {
|
|
13279
|
+
return vertexCount > 65535 ? new Uint32Array(indices) : new Uint16Array(indices);
|
|
13280
|
+
}
|
|
13281
|
+
/** The model's bounds after `transform`, as `Model3DData.bounds` reports them. */
|
|
13282
|
+
function transformModelBounds(bounds, { offset, scale }) {
|
|
13283
|
+
const min = [
|
|
13284
|
+
(bounds.min[0] - offset[0]) * scale[0],
|
|
13285
|
+
(bounds.min[1] - offset[1]) * scale[1],
|
|
13286
|
+
(bounds.min[2] - offset[2]) * scale[2]
|
|
13287
|
+
];
|
|
13288
|
+
const max = [
|
|
13289
|
+
(bounds.max[0] - offset[0]) * scale[0],
|
|
13290
|
+
(bounds.max[1] - offset[1]) * scale[1],
|
|
13291
|
+
(bounds.max[2] - offset[2]) * scale[2]
|
|
13292
|
+
];
|
|
13293
|
+
return {
|
|
13294
|
+
min,
|
|
13295
|
+
max,
|
|
13296
|
+
center: [
|
|
13297
|
+
(min[0] + max[0]) * .5,
|
|
13298
|
+
(min[1] + max[1]) * .5,
|
|
13299
|
+
(min[2] + max[2]) * .5
|
|
13300
|
+
],
|
|
13301
|
+
size: [
|
|
13302
|
+
max[0] - min[0],
|
|
13303
|
+
max[1] - min[1],
|
|
13304
|
+
max[2] - min[2]
|
|
13305
|
+
],
|
|
13306
|
+
boundingSphereRadius: Math.hypot(max[0] - min[0], max[1] - min[1], max[2] - min[2]) * .5
|
|
13307
|
+
};
|
|
13308
|
+
}
|
|
13309
|
+
/**
|
|
13310
|
+
* Poses a model's scene graph (glTF semantics): samples an animation clip's
|
|
13311
|
+
* translation / rotation / scale tracks at a time, composes the node
|
|
13312
|
+
* hierarchy into model-space matrices, and turns a skin's joints into the
|
|
13313
|
+
* joint matrices the mesh shader blends (joint world × inverse bind).
|
|
13314
|
+
*/
|
|
13315
|
+
/** Joints per skin the mesh shader holds (mesh3d.wgsl jointMatrices). */
|
|
13316
|
+
const MAX_JOINTS = 128;
|
|
13317
|
+
/** Column-major translate · rotate(quaternion) · scale. */
|
|
13318
|
+
function trsMatrix(t, q, s) {
|
|
13319
|
+
const [x, y, z, w] = q;
|
|
13320
|
+
const xx = x * x;
|
|
13321
|
+
const yy = y * y;
|
|
13322
|
+
const zz = z * z;
|
|
13323
|
+
const xy = x * y;
|
|
13324
|
+
const xz = x * z;
|
|
13325
|
+
const yz = y * z;
|
|
13326
|
+
const wx = w * x;
|
|
13327
|
+
const wy = w * y;
|
|
13328
|
+
const wz = w * z;
|
|
13329
|
+
return [
|
|
13330
|
+
(1 - 2 * (yy + zz)) * s[0],
|
|
13331
|
+
2 * (xy + wz) * s[0],
|
|
13332
|
+
2 * (xz - wy) * s[0],
|
|
13333
|
+
0,
|
|
13334
|
+
2 * (xy - wz) * s[1],
|
|
13335
|
+
(1 - 2 * (xx + zz)) * s[1],
|
|
13336
|
+
2 * (yz + wx) * s[1],
|
|
13337
|
+
0,
|
|
13338
|
+
2 * (xz + wy) * s[2],
|
|
13339
|
+
2 * (yz - wx) * s[2],
|
|
13340
|
+
(1 - 2 * (xx + yy)) * s[2],
|
|
13341
|
+
0,
|
|
13342
|
+
t[0],
|
|
13343
|
+
t[1],
|
|
13344
|
+
t[2],
|
|
13345
|
+
1
|
|
13346
|
+
];
|
|
13347
|
+
}
|
|
13348
|
+
function normalizeQuat(q) {
|
|
13349
|
+
const len = Math.hypot(q[0], q[1], q[2], q[3]) || 1;
|
|
13350
|
+
return [
|
|
13351
|
+
q[0] / len,
|
|
13352
|
+
q[1] / len,
|
|
13353
|
+
q[2] / len,
|
|
13354
|
+
q[3] / len
|
|
13355
|
+
];
|
|
13356
|
+
}
|
|
13357
|
+
function slerp(a, b, t) {
|
|
13358
|
+
let cos = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
|
|
13359
|
+
const sign = cos < 0 ? -1 : 1;
|
|
13360
|
+
cos *= sign;
|
|
13361
|
+
if (cos > .9995) return normalizeQuat([
|
|
13362
|
+
a[0] + (sign * b[0] - a[0]) * t,
|
|
13363
|
+
a[1] + (sign * b[1] - a[1]) * t,
|
|
13364
|
+
a[2] + (sign * b[2] - a[2]) * t,
|
|
13365
|
+
a[3] + (sign * b[3] - a[3]) * t
|
|
13366
|
+
]);
|
|
13367
|
+
const theta = Math.acos(cos);
|
|
13368
|
+
const sin = Math.sin(theta);
|
|
13369
|
+
const wa = Math.sin((1 - t) * theta) / sin;
|
|
13370
|
+
const wb = sign * Math.sin(t * theta) / sin;
|
|
13371
|
+
return [
|
|
13372
|
+
a[0] * wa + b[0] * wb,
|
|
13373
|
+
a[1] * wa + b[1] * wb,
|
|
13374
|
+
a[2] * wa + b[2] * wb,
|
|
13375
|
+
a[3] * wa + b[3] * wb
|
|
13376
|
+
];
|
|
13377
|
+
}
|
|
13378
|
+
/** Index of the last keyframe at or before `time` (binary search). */
|
|
13379
|
+
function keyBefore(times, time$2) {
|
|
13380
|
+
let lo = 0;
|
|
13381
|
+
let hi = times.length - 1;
|
|
13382
|
+
while (lo < hi) {
|
|
13383
|
+
const mid = lo + hi + 1 >> 1;
|
|
13384
|
+
if ((times[mid] ?? 0) <= time$2) lo = mid;
|
|
13385
|
+
else hi = mid - 1;
|
|
13386
|
+
}
|
|
13387
|
+
return lo;
|
|
13388
|
+
}
|
|
13389
|
+
/** Element `k` (of `size` components) of a track's values; cubic tracks store [in, value, out] triples. */
|
|
13390
|
+
function valueAt(values, k, size, cubic, part = 1) {
|
|
13391
|
+
const at = cubic ? (k * 3 + part) * size : k * size;
|
|
13392
|
+
return Array.from(values.subarray(at, at + size));
|
|
13393
|
+
}
|
|
13394
|
+
function sampleChannel(track, values, size, time$2) {
|
|
13395
|
+
const { times } = track;
|
|
13396
|
+
const cubic = track.interpolation === "cubic";
|
|
13397
|
+
const last = times.length - 1;
|
|
13398
|
+
if (time$2 <= (times[0] ?? 0)) return valueAt(values, 0, size, cubic);
|
|
13399
|
+
if (time$2 >= (times[last] ?? 0)) return valueAt(values, last, size, cubic);
|
|
13400
|
+
const k = keyBefore(times, time$2);
|
|
13401
|
+
if (track.interpolation === "step") return valueAt(values, k, size, cubic);
|
|
13402
|
+
const t0 = times[k] ?? 0;
|
|
13403
|
+
const dt = (times[k + 1] ?? t0) - t0;
|
|
13404
|
+
const u = dt > 0 ? (time$2 - t0) / dt : 0;
|
|
13405
|
+
const a = valueAt(values, k, size, cubic);
|
|
13406
|
+
const b = valueAt(values, k + 1, size, cubic);
|
|
13407
|
+
if (cubic) {
|
|
13408
|
+
const outA = valueAt(values, k, size, true, 2);
|
|
13409
|
+
const inB = valueAt(values, k + 1, size, true, 0);
|
|
13410
|
+
const u2 = u * u;
|
|
13411
|
+
const u3 = u2 * u;
|
|
13412
|
+
const out = a.map((p0, i) => (2 * u3 - 3 * u2 + 1) * p0 + (u3 - 2 * u2 + u) * dt * (outA[i] ?? 0) + (-2 * u3 + 3 * u2) * (b[i] ?? 0) + (u3 - u2) * dt * (inB[i] ?? 0));
|
|
13413
|
+
return size === 4 ? normalizeQuat(out) : out;
|
|
13414
|
+
}
|
|
13415
|
+
if (size === 4) return slerp(a, b, u);
|
|
13416
|
+
return a.map((v, i) => v + ((b[i] ?? v) - v) * u);
|
|
13417
|
+
}
|
|
13418
|
+
/** Each node's local matrix with the clip's tracks applied at `time` (seconds). */
|
|
13419
|
+
function localMatrices(nodes, clip, time$2) {
|
|
13420
|
+
const t = nodes.map((n) => [...n.translation]);
|
|
13421
|
+
const r = nodes.map((n) => [...n.rotation]);
|
|
13422
|
+
const s = nodes.map((n) => [...n.scale]);
|
|
13423
|
+
const byName = new Map(nodes.map((n, i) => [n.name, i]));
|
|
13424
|
+
for (const track of clip?.tracks ?? []) {
|
|
13425
|
+
const i = track.node ?? byName.get(track.nodeName);
|
|
13426
|
+
if (i === void 0 || !track.times.length) continue;
|
|
13427
|
+
if (track.translations) t[i] = sampleChannel(track, track.translations, 3, time$2);
|
|
13428
|
+
if (track.rotations) r[i] = sampleChannel(track, track.rotations, 4, time$2);
|
|
13429
|
+
if (track.scales) s[i] = sampleChannel(track, track.scales, 3, time$2);
|
|
13430
|
+
}
|
|
13431
|
+
return nodes.map((n, i) => n.matrix ?? trsMatrix(t[i], r[i], s[i]));
|
|
13432
|
+
}
|
|
13433
|
+
/**
|
|
13434
|
+
* Model-space matrix of every node, posed by `clip` at `time` seconds (the
|
|
13435
|
+
* rest pose without a clip). Parents may follow their children in the list.
|
|
13436
|
+
*/
|
|
13437
|
+
function poseNodes(model, clip, time$2) {
|
|
13438
|
+
const nodes = model.nodes ?? [];
|
|
13439
|
+
const local = localMatrices(nodes, clip, time$2);
|
|
13440
|
+
const world = new Array(nodes.length);
|
|
13441
|
+
const resolve$1$1 = (i) => {
|
|
13442
|
+
const done = world[i];
|
|
13443
|
+
if (done) return done;
|
|
13444
|
+
const parent = nodes[i].parent;
|
|
13445
|
+
const m = parent < 0 ? local[i] : Matrix4Math.multiply(resolve$1$1(parent), local[i]);
|
|
13446
|
+
world[i] = m;
|
|
13447
|
+
return m;
|
|
13448
|
+
};
|
|
13449
|
+
return nodes.map((_, i) => resolve$1$1(i));
|
|
13450
|
+
}
|
|
13451
|
+
/** The joint matrices of one skin for a posed scene graph, padded with identity to MAX_JOINTS. */
|
|
13452
|
+
function jointMatrices(model, skinIndex, world) {
|
|
13453
|
+
const skin = model.skins?.[skinIndex];
|
|
13454
|
+
if (!skin) throw new Error(`model has no skin ${skinIndex}`);
|
|
13455
|
+
const out = new Float32Array(MAX_JOINTS * 16);
|
|
13456
|
+
for (let j = 0; j < MAX_JOINTS; j++) {
|
|
13457
|
+
const node = skin.joints[j];
|
|
13458
|
+
if (node === void 0) {
|
|
13459
|
+
out.set(Matrix4Math.identity(), j * 16);
|
|
13460
|
+
continue;
|
|
13461
|
+
}
|
|
13462
|
+
const ibm = Array.from(skin.inverseBindMatrices.subarray(j * 16, j * 16 + 16));
|
|
13463
|
+
out.set(Matrix4Math.multiply(world[node], ibm), j * 16);
|
|
13464
|
+
}
|
|
13465
|
+
return out;
|
|
13466
|
+
}
|
|
13467
|
+
var FbxLoader = class FbxLoader$1 {
|
|
13468
|
+
static FBX_KTIME = 46186158e3;
|
|
13469
|
+
/**
|
|
13470
|
+
* Parses binary or text FBX buffer/string into Model3DData.
|
|
13471
|
+
*/
|
|
13472
|
+
static parse(bufferOrString, options = {}) {
|
|
13473
|
+
const rootNode = typeof bufferOrString === "string" ? FbxLoader$1.parseAscii(bufferOrString) : FbxLoader$1.isBinary(bufferOrString) ? FbxLoader$1.parseBinary(bufferOrString) : FbxLoader$1.parseAscii(new TextDecoder().decode(bufferOrString));
|
|
13474
|
+
return FbxLoader$1.buildModelFromTree(rootNode, options);
|
|
13475
|
+
}
|
|
13476
|
+
static isBinary(buffer) {
|
|
13477
|
+
const view = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
|
|
13478
|
+
if (view.length < 27) return false;
|
|
13479
|
+
const header = "Kaydara FBX Binary \0\0";
|
|
13480
|
+
for (let i = 0; i < 23; i++) if (view[i] !== header.charCodeAt(i)) return false;
|
|
13481
|
+
return true;
|
|
13482
|
+
}
|
|
13483
|
+
/**
|
|
13484
|
+
* Parses Binary FBX 7.x/6.x format.
|
|
13485
|
+
*/
|
|
13486
|
+
static parseBinary(buffer) {
|
|
13487
|
+
const u8 = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
|
|
13488
|
+
const view = new DataView(u8.buffer, u8.byteOffset, u8.byteLength);
|
|
13489
|
+
const is64Bit = view.getUint32(23, true) >= 7500;
|
|
13490
|
+
let offset = 27;
|
|
13491
|
+
const root = {
|
|
13492
|
+
name: "Root",
|
|
13493
|
+
props: [],
|
|
13494
|
+
children: []
|
|
13495
|
+
};
|
|
13496
|
+
const readNode = () => {
|
|
13497
|
+
if (offset >= u8.byteLength) return null;
|
|
13498
|
+
let endOffset;
|
|
13499
|
+
let numProperties;
|
|
13500
|
+
let nameLen;
|
|
13501
|
+
if (is64Bit) {
|
|
13502
|
+
if (offset + 25 > u8.byteLength) return null;
|
|
13503
|
+
endOffset = Number(view.getBigUint64(offset, true));
|
|
13504
|
+
numProperties = Number(view.getBigUint64(offset + 8, true));
|
|
13505
|
+
nameLen = view.getUint8(offset + 24);
|
|
13506
|
+
offset += 25;
|
|
13507
|
+
} else {
|
|
13508
|
+
if (offset + 13 > u8.byteLength) return null;
|
|
13509
|
+
endOffset = view.getUint32(offset, true);
|
|
13510
|
+
numProperties = view.getUint32(offset + 4, true);
|
|
13511
|
+
nameLen = view.getUint8(offset + 12);
|
|
13512
|
+
offset += 13;
|
|
13513
|
+
}
|
|
13514
|
+
if (endOffset === 0) return null;
|
|
13515
|
+
const name = new TextDecoder().decode(u8.subarray(offset, offset + nameLen));
|
|
13516
|
+
offset += nameLen;
|
|
13517
|
+
const props = [];
|
|
13518
|
+
for (let p = 0; p < numProperties; p++) {
|
|
13519
|
+
const typeCode = String.fromCharCode(u8[offset]);
|
|
13520
|
+
offset += 1;
|
|
13521
|
+
switch (typeCode) {
|
|
13522
|
+
case "Y":
|
|
13523
|
+
props.push(view.getInt16(offset, true));
|
|
13524
|
+
offset += 2;
|
|
13525
|
+
break;
|
|
13526
|
+
case "C":
|
|
13527
|
+
case "B":
|
|
13528
|
+
props.push(view.getUint8(offset) !== 0);
|
|
13529
|
+
offset += 1;
|
|
13530
|
+
break;
|
|
13531
|
+
case "I":
|
|
13532
|
+
props.push(view.getInt32(offset, true));
|
|
13533
|
+
offset += 4;
|
|
13534
|
+
break;
|
|
13535
|
+
case "F":
|
|
13536
|
+
props.push(view.getFloat32(offset, true));
|
|
13537
|
+
offset += 4;
|
|
13538
|
+
break;
|
|
13539
|
+
case "D":
|
|
13540
|
+
props.push(view.getFloat64(offset, true));
|
|
13541
|
+
offset += 8;
|
|
13542
|
+
break;
|
|
13543
|
+
case "L":
|
|
13544
|
+
props.push(Number(view.getBigInt64(offset, true)));
|
|
13545
|
+
offset += 8;
|
|
13546
|
+
break;
|
|
13547
|
+
case "S": {
|
|
13548
|
+
const len = view.getUint32(offset, true);
|
|
13549
|
+
offset += 4;
|
|
13550
|
+
const str = new TextDecoder().decode(u8.subarray(offset, offset + len));
|
|
13551
|
+
props.push(str);
|
|
13552
|
+
offset += len;
|
|
13553
|
+
break;
|
|
13554
|
+
}
|
|
13555
|
+
case "R": {
|
|
13556
|
+
const len = view.getUint32(offset, true);
|
|
13557
|
+
offset += 4;
|
|
13558
|
+
const raw = u8.subarray(offset, offset + len);
|
|
13559
|
+
props.push(raw);
|
|
13560
|
+
offset += len;
|
|
13561
|
+
break;
|
|
13562
|
+
}
|
|
13563
|
+
case "f":
|
|
13564
|
+
case "d":
|
|
13565
|
+
case "i":
|
|
13566
|
+
case "l":
|
|
13567
|
+
case "b":
|
|
13568
|
+
case "c": {
|
|
13569
|
+
const arrayLen = view.getUint32(offset, true);
|
|
13570
|
+
const encoding = view.getUint32(offset + 4, true);
|
|
13571
|
+
const compressedLen = view.getUint32(offset + 8, true);
|
|
13572
|
+
offset += 12;
|
|
13573
|
+
let arrayBuffer;
|
|
13574
|
+
if (encoding === 1) {
|
|
13575
|
+
arrayBuffer = inflateSync(u8.subarray(offset, offset + compressedLen));
|
|
13576
|
+
offset += compressedLen;
|
|
13577
|
+
} else {
|
|
13578
|
+
const totalBytes = arrayLen * (typeCode === "d" || typeCode === "l" ? 8 : typeCode === "f" || typeCode === "i" ? 4 : 1);
|
|
13579
|
+
arrayBuffer = u8.subarray(offset, offset + totalBytes);
|
|
13580
|
+
offset += totalBytes;
|
|
13581
|
+
}
|
|
13582
|
+
const arrayView = new DataView(arrayBuffer.buffer, arrayBuffer.byteOffset, arrayBuffer.byteLength);
|
|
13583
|
+
if (typeCode === "f") {
|
|
13584
|
+
const arr = new Float32Array(arrayLen);
|
|
13585
|
+
for (let k = 0; k < arrayLen; k++) arr[k] = arrayView.getFloat32(k * 4, true);
|
|
13586
|
+
props.push(arr);
|
|
13587
|
+
} else if (typeCode === "d") {
|
|
13588
|
+
const arr = new Float64Array(arrayLen);
|
|
13589
|
+
for (let k = 0; k < arrayLen; k++) arr[k] = arrayView.getFloat64(k * 8, true);
|
|
13590
|
+
props.push(arr);
|
|
13591
|
+
} else if (typeCode === "i") {
|
|
13592
|
+
const arr = new Int32Array(arrayLen);
|
|
13593
|
+
for (let k = 0; k < arrayLen; k++) arr[k] = arrayView.getInt32(k * 4, true);
|
|
13594
|
+
props.push(arr);
|
|
13595
|
+
} else if (typeCode === "l") {
|
|
13596
|
+
const arr = new Float64Array(arrayLen);
|
|
13597
|
+
for (let k = 0; k < arrayLen; k++) arr[k] = Number(arrayView.getBigInt64(k * 8, true));
|
|
13598
|
+
props.push(arr);
|
|
13599
|
+
} else if (typeCode === "b") {
|
|
13600
|
+
const arr = new Uint8Array(arrayLen);
|
|
13601
|
+
for (let k = 0; k < arrayLen; k++) arr[k] = arrayView.getUint8(k);
|
|
13602
|
+
props.push(arr);
|
|
13603
|
+
} else props.push(arrayBuffer);
|
|
13604
|
+
break;
|
|
13605
|
+
}
|
|
13606
|
+
default: break;
|
|
13607
|
+
}
|
|
13608
|
+
}
|
|
13609
|
+
const children = [];
|
|
13610
|
+
while (offset < endOffset) {
|
|
13611
|
+
const child = readNode();
|
|
13612
|
+
if (child) children.push(child);
|
|
13613
|
+
else break;
|
|
13614
|
+
}
|
|
13615
|
+
offset = endOffset;
|
|
13616
|
+
return {
|
|
13617
|
+
name,
|
|
13618
|
+
props,
|
|
13619
|
+
children
|
|
13620
|
+
};
|
|
13621
|
+
};
|
|
13622
|
+
while (offset < u8.byteLength - (is64Bit ? 25 : 13)) {
|
|
13623
|
+
const node = readNode();
|
|
13624
|
+
if (node) root.children.push(node);
|
|
13625
|
+
else break;
|
|
13626
|
+
}
|
|
13627
|
+
return root;
|
|
13628
|
+
}
|
|
13629
|
+
/**
|
|
13630
|
+
* Parses ASCII FBX format.
|
|
13631
|
+
*/
|
|
13632
|
+
static parseAscii(text) {
|
|
13633
|
+
const root = {
|
|
13634
|
+
name: "Root",
|
|
13635
|
+
props: [],
|
|
13636
|
+
children: []
|
|
13637
|
+
};
|
|
13638
|
+
const stack = [root];
|
|
13639
|
+
const lines = text.split(/\r?\n/);
|
|
13640
|
+
for (let i = 0; i < lines.length; i++) {
|
|
13641
|
+
const line = lines[i]?.trim();
|
|
13642
|
+
if (!line || line.startsWith(";")) continue;
|
|
13643
|
+
if (line === "}") {
|
|
13644
|
+
if (stack.length > 1) stack.pop();
|
|
13645
|
+
continue;
|
|
13646
|
+
}
|
|
13647
|
+
const colonIdx = line.indexOf(":");
|
|
13648
|
+
if (colonIdx === -1) continue;
|
|
13649
|
+
const name = line.substring(0, colonIdx).trim();
|
|
13650
|
+
const rest = line.substring(colonIdx + 1).trim();
|
|
13651
|
+
const hasBrace = rest.endsWith("{");
|
|
13652
|
+
const propStr = hasBrace ? rest.slice(0, -1).trim() : rest;
|
|
13238
13653
|
const props = [];
|
|
13239
13654
|
if (propStr) {
|
|
13240
13655
|
const rawProps = propStr.split(",").map((p) => p.trim());
|
|
@@ -13253,6 +13668,53 @@ var FbxLoader = class FbxLoader$1 {
|
|
|
13253
13668
|
}
|
|
13254
13669
|
return root;
|
|
13255
13670
|
}
|
|
13671
|
+
/** Material properties a texture can feed, by the FBX (or Maya / 3ds Max) property name. */
|
|
13672
|
+
static textureSlot(prop) {
|
|
13673
|
+
const p = prop.toLowerCase();
|
|
13674
|
+
if (p === "diffusecolor" || p === "diffuse" || p.endsWith("|basecolor") || p.endsWith("|base_color") || p === "basecolor") return "diffuse";
|
|
13675
|
+
if (p === "normalmap" || p.endsWith("|normalcamera") || p.endsWith("|bump_map")) return "normal";
|
|
13676
|
+
}
|
|
13677
|
+
/**
|
|
13678
|
+
* Fills a material's diffuse / normal map from its connected Texture
|
|
13679
|
+
* objects: the bytes embedded in the Video's `Content`, else the file
|
|
13680
|
+
* named by `RelativeFilename` / `FileName`, looked up next to the model.
|
|
13681
|
+
*/
|
|
13682
|
+
static attachTextures(material, materialId, objects, connections, basePath) {
|
|
13683
|
+
const stringChild = (node, name) => {
|
|
13684
|
+
const value = node?.children.find((c) => c.name === name)?.props[0];
|
|
13685
|
+
return typeof value === "string" && value ? value : void 0;
|
|
13686
|
+
};
|
|
13687
|
+
for (const c of connections) {
|
|
13688
|
+
if (c.parentId !== materialId || !c.propName) continue;
|
|
13689
|
+
const slot = FbxLoader$1.textureSlot(c.propName);
|
|
13690
|
+
const texture = objects.get(c.childId);
|
|
13691
|
+
if (!slot || texture?.node.name !== "Texture") continue;
|
|
13692
|
+
if (slot === "diffuse" ? material.diffuseTextureBuffer : material.normalTextureBuffer) continue;
|
|
13693
|
+
const video = connections.filter((v) => v.parentId === c.childId && !v.propName).map((v) => objects.get(v.childId)?.node).find((n) => n?.name === "Video");
|
|
13694
|
+
const ref = stringChild(texture.node, "RelativeFilename") ?? stringChild(texture.node, "FileName") ?? stringChild(video, "RelativeFilename") ?? stringChild(video, "Filename");
|
|
13695
|
+
if (ref && !isSupportedTexture(ref)) {
|
|
13696
|
+
warnMissingTexture("FbxLoader", material.name, ref);
|
|
13697
|
+
continue;
|
|
13698
|
+
}
|
|
13699
|
+
let bytes;
|
|
13700
|
+
const content = video?.children.find((n) => n.name === "Content")?.props[0];
|
|
13701
|
+
if (content instanceof Uint8Array && content.length > 0) bytes = content;
|
|
13702
|
+
else if (ref) bytes = readTextureFile(ref, basePath)?.bytes;
|
|
13703
|
+
if (!bytes) {
|
|
13704
|
+
if (ref) warnMissingTexture("FbxLoader", material.name, ref);
|
|
13705
|
+
continue;
|
|
13706
|
+
}
|
|
13707
|
+
if (slot === "diffuse") {
|
|
13708
|
+
material.diffuseTextureBuffer = bytes;
|
|
13709
|
+
material.diffuseTextureMime = textureMime(ref ?? "");
|
|
13710
|
+
if (ref) material.diffuseTexture = ref;
|
|
13711
|
+
material.textureWrap = "repeat";
|
|
13712
|
+
} else {
|
|
13713
|
+
material.normalTextureBuffer = bytes;
|
|
13714
|
+
if (ref) material.normalTexture = ref;
|
|
13715
|
+
}
|
|
13716
|
+
}
|
|
13717
|
+
}
|
|
13256
13718
|
/**
|
|
13257
13719
|
* Builds ready-to-render Model3DData from parsed FBX node tree.
|
|
13258
13720
|
*/
|
|
@@ -13286,6 +13748,20 @@ var FbxLoader = class FbxLoader$1 {
|
|
|
13286
13748
|
});
|
|
13287
13749
|
}
|
|
13288
13750
|
}
|
|
13751
|
+
const materialNameForGeometry = (geometryId) => {
|
|
13752
|
+
for (const c of connections) {
|
|
13753
|
+
const mat = c.parentId === geometryId ? objects.get(c.childId) : void 0;
|
|
13754
|
+
if (mat?.node.name === "Material" && !c.propName) return mat.name;
|
|
13755
|
+
}
|
|
13756
|
+
for (const g$1 of connections) {
|
|
13757
|
+
if (g$1.childId !== geometryId || g$1.propName) continue;
|
|
13758
|
+
for (const m of connections) {
|
|
13759
|
+
if (m.parentId !== g$1.parentId || m.propName) continue;
|
|
13760
|
+
const mat = objects.get(m.childId);
|
|
13761
|
+
if (mat?.node.name === "Material") return mat.name;
|
|
13762
|
+
}
|
|
13763
|
+
}
|
|
13764
|
+
};
|
|
13289
13765
|
const meshes = [];
|
|
13290
13766
|
const materials = {};
|
|
13291
13767
|
function extractArrayProp(node) {
|
|
@@ -13296,149 +13772,142 @@ var FbxLoader = class FbxLoader$1 {
|
|
|
13296
13772
|
if (Array.isArray(node.props) && node.props.length > 0 && typeof node.props[0] === "number") return node.props;
|
|
13297
13773
|
return [];
|
|
13298
13774
|
}
|
|
13775
|
+
const modelIds = [...objects.values()].filter((o) => o.node.name === "Model").map((o) => o.id);
|
|
13776
|
+
const nodeIndexById = new Map(modelIds.map((id, i) => [id, i]));
|
|
13777
|
+
const nodes = modelIds.map((id) => {
|
|
13778
|
+
const model$1 = objects.get(id);
|
|
13779
|
+
const parentConn = connections.find((c) => c.childId === id && !c.propName && nodeIndexById.has(c.parentId));
|
|
13780
|
+
return {
|
|
13781
|
+
name: model$1.name,
|
|
13782
|
+
parent: parentConn ? nodeIndexById.get(parentConn.parentId) : -1,
|
|
13783
|
+
translation: readVec3(model$1.node, "Lcl Translation", 0),
|
|
13784
|
+
rotation: modelRotation(model$1.node, fbxEulerToQuat(readVec3(model$1.node, "Lcl Rotation", 0), rotationOrder(model$1.node))),
|
|
13785
|
+
scale: readVec3(model$1.node, "Lcl Scaling", 1)
|
|
13786
|
+
};
|
|
13787
|
+
});
|
|
13788
|
+
const modelOfGeometry = (geometryId) => connections.find((c) => c.childId === geometryId && !c.propName && nodeIndexById.has(c.parentId))?.parentId;
|
|
13789
|
+
const skins = [];
|
|
13299
13790
|
for (const [id, entry] of objects) if (entry.node.name === "Geometry" && (entry.type === "Mesh" || entry.node.props[2] === "Mesh" || entry.type === "")) {
|
|
13300
13791
|
const geomNode = entry.node;
|
|
13301
13792
|
const verticesNode = geomNode.children.find((c) => c.name === "Vertices");
|
|
13302
13793
|
const indicesNode = geomNode.children.find((c) => c.name === "PolygonVertexIndex");
|
|
13303
|
-
const normalLayer = geomNode.children.find((c) => c.name === "LayerElementNormal");
|
|
13304
|
-
const uvLayer = geomNode.children.find((c) => c.name === "LayerElementUV");
|
|
13305
13794
|
if (!verticesNode || !indicesNode) continue;
|
|
13306
13795
|
const rawVerts = extractArrayProp(verticesNode);
|
|
13307
13796
|
const rawPolyIndices = extractArrayProp(indicesNode);
|
|
13308
|
-
const
|
|
13309
|
-
|
|
13310
|
-
|
|
13311
|
-
|
|
13312
|
-
|
|
13313
|
-
const
|
|
13314
|
-
const
|
|
13315
|
-
const
|
|
13316
|
-
let polygon = [];
|
|
13317
|
-
|
|
13318
|
-
|
|
13319
|
-
|
|
13320
|
-
|
|
13321
|
-
|
|
13322
|
-
|
|
13323
|
-
|
|
13324
|
-
|
|
13325
|
-
|
|
13326
|
-
]
|
|
13327
|
-
polygon
|
|
13328
|
-
|
|
13329
|
-
|
|
13330
|
-
|
|
13331
|
-
|
|
13332
|
-
|
|
13333
|
-
|
|
13334
|
-
|
|
13335
|
-
|
|
13336
|
-
const
|
|
13337
|
-
|
|
13338
|
-
|
|
13339
|
-
|
|
13340
|
-
|
|
13341
|
-
|
|
13342
|
-
|
|
13343
|
-
|
|
13344
|
-
|
|
13345
|
-
|
|
13346
|
-
|
|
13347
|
-
|
|
13348
|
-
|
|
13349
|
-
|
|
13350
|
-
|
|
13351
|
-
|
|
13352
|
-
|
|
13353
|
-
|
|
13354
|
-
|
|
13355
|
-
|
|
13356
|
-
|
|
13357
|
-
|
|
13358
|
-
|
|
13359
|
-
outIndices.push(outIdx);
|
|
13360
|
-
}
|
|
13361
|
-
}
|
|
13362
|
-
if (rawNormals.length === 0 || options.smoothNormals) {
|
|
13363
|
-
const computedNormals = new Float32Array(outPositions.length);
|
|
13364
|
-
for (let i = 0; i < outIndices.length; i += 3) {
|
|
13365
|
-
const i0 = outIndices[i];
|
|
13366
|
-
const i1 = outIndices[i + 1];
|
|
13367
|
-
const i2 = outIndices[i + 2];
|
|
13368
|
-
const p0 = [
|
|
13369
|
-
outPositions[i0 * 3],
|
|
13370
|
-
outPositions[i0 * 3 + 1],
|
|
13371
|
-
outPositions[i0 * 3 + 2]
|
|
13372
|
-
];
|
|
13373
|
-
const p1 = [
|
|
13374
|
-
outPositions[i1 * 3],
|
|
13375
|
-
outPositions[i1 * 3 + 1],
|
|
13376
|
-
outPositions[i1 * 3 + 2]
|
|
13377
|
-
];
|
|
13378
|
-
const p2 = [
|
|
13379
|
-
outPositions[i2 * 3],
|
|
13380
|
-
outPositions[i2 * 3 + 1],
|
|
13381
|
-
outPositions[i2 * 3 + 2]
|
|
13382
|
-
];
|
|
13383
|
-
const edge1 = Vector3Math.subtract(p1, p0);
|
|
13384
|
-
const edge2 = Vector3Math.subtract(p2, p0);
|
|
13385
|
-
const faceNormal = Vector3Math.cross(edge1, edge2);
|
|
13386
|
-
computedNormals[i0 * 3] += faceNormal[0];
|
|
13387
|
-
computedNormals[i0 * 3 + 1] += faceNormal[1];
|
|
13388
|
-
computedNormals[i0 * 3 + 2] += faceNormal[2];
|
|
13389
|
-
computedNormals[i1 * 3] += faceNormal[0];
|
|
13390
|
-
computedNormals[i1 * 3 + 1] += faceNormal[1];
|
|
13391
|
-
computedNormals[i1 * 3 + 2] += faceNormal[2];
|
|
13392
|
-
computedNormals[i2 * 3] += faceNormal[0];
|
|
13393
|
-
computedNormals[i2 * 3 + 1] += faceNormal[1];
|
|
13394
|
-
computedNormals[i2 * 3 + 2] += faceNormal[2];
|
|
13395
|
-
}
|
|
13396
|
-
for (let v = 0; v < outPositions.length / 3; v++) {
|
|
13397
|
-
const nx = computedNormals[v * 3];
|
|
13398
|
-
const ny = computedNormals[v * 3 + 1];
|
|
13399
|
-
const nz = computedNormals[v * 3 + 2];
|
|
13400
|
-
const len = Math.hypot(nx, ny, nz) || 1;
|
|
13401
|
-
outNormals[v * 3] = nx / len;
|
|
13402
|
-
outNormals[v * 3 + 1] = ny / len;
|
|
13403
|
-
outNormals[v * 3 + 2] = nz / len;
|
|
13404
|
-
}
|
|
13797
|
+
const layer = (name, data, index, size) => {
|
|
13798
|
+
const node = geomNode.children.find((c) => c.name === name);
|
|
13799
|
+
if (!node) return void 0;
|
|
13800
|
+
return layerElement(node, extractArrayProp(node.children.find((c) => c.name === data)), extractArrayProp(node.children.find((c) => c.name === index)), size);
|
|
13801
|
+
};
|
|
13802
|
+
const normalAt = layer("LayerElementNormal", "Normals", "NormalsIndex", 3);
|
|
13803
|
+
const uvAt = layer("LayerElementUV", "UV", "UVIndex", 2);
|
|
13804
|
+
const corners = [];
|
|
13805
|
+
let polygon = [];
|
|
13806
|
+
let polyIndex = 0;
|
|
13807
|
+
for (let i = 0; i < rawPolyIndices.length; i++) {
|
|
13808
|
+
const idx = rawPolyIndices[i];
|
|
13809
|
+
polygon.push({
|
|
13810
|
+
cp: idx < 0 ? -(idx + 1) : idx,
|
|
13811
|
+
pv: i
|
|
13812
|
+
});
|
|
13813
|
+
if (idx >= 0) continue;
|
|
13814
|
+
for (let k = 1; k < polygon.length - 1; k++) for (const c of [
|
|
13815
|
+
polygon[0],
|
|
13816
|
+
polygon[k],
|
|
13817
|
+
polygon[k + 1]
|
|
13818
|
+
]) corners.push({
|
|
13819
|
+
...c,
|
|
13820
|
+
poly: polyIndex
|
|
13821
|
+
});
|
|
13822
|
+
polygon = [];
|
|
13823
|
+
polyIndex++;
|
|
13824
|
+
}
|
|
13825
|
+
const numVerts = corners.length;
|
|
13826
|
+
const outPositions = new Float32Array(numVerts * 3);
|
|
13827
|
+
const outNormals = new Float32Array(numVerts * 3);
|
|
13828
|
+
const outUvs = new Float32Array(numVerts * 2);
|
|
13829
|
+
const outIndices = new Array(numVerts);
|
|
13830
|
+
corners.forEach(({ cp, pv, poly }, v) => {
|
|
13831
|
+
outPositions[v * 3] = rawVerts[cp * 3] ?? 0;
|
|
13832
|
+
outPositions[v * 3 + 1] = rawVerts[cp * 3 + 1] ?? 0;
|
|
13833
|
+
outPositions[v * 3 + 2] = rawVerts[cp * 3 + 2] ?? 0;
|
|
13834
|
+
const n = normalAt?.(cp, pv, poly);
|
|
13835
|
+
outNormals.set(n ?? [
|
|
13836
|
+
0,
|
|
13837
|
+
0,
|
|
13838
|
+
1
|
|
13839
|
+
], v * 3);
|
|
13840
|
+
const uv = uvAt?.(cp, pv, poly);
|
|
13841
|
+
outUvs[v * 2] = uv?.[0] ?? 0;
|
|
13842
|
+
outUvs[v * 2 + 1] = 1 - (uv?.[1] ?? 0);
|
|
13843
|
+
outIndices[v] = v;
|
|
13844
|
+
});
|
|
13845
|
+
if (!normalAt || options.smoothNormals) {
|
|
13846
|
+
outNormals.fill(0);
|
|
13847
|
+
accumulateVertexNormals(outPositions, outIndices, outNormals);
|
|
13405
13848
|
}
|
|
13406
|
-
const skinConn = connections.find((c) => c.parentId === id && objects.get(c.childId)?.
|
|
13849
|
+
const skinConn = connections.find((c) => c.parentId === id && objects.get(c.childId)?.node.name === "Deformer" && objects.get(c.childId)?.node.props[2] === "Skin");
|
|
13407
13850
|
let jointIndices;
|
|
13408
13851
|
let jointWeights;
|
|
13852
|
+
let skinIndex;
|
|
13409
13853
|
if (skinConn) {
|
|
13410
|
-
const
|
|
13411
|
-
|
|
13412
|
-
|
|
13413
|
-
|
|
13414
|
-
const
|
|
13415
|
-
|
|
13416
|
-
const
|
|
13417
|
-
if (
|
|
13418
|
-
|
|
13419
|
-
|
|
13420
|
-
|
|
13421
|
-
|
|
13422
|
-
|
|
13423
|
-
|
|
13424
|
-
|
|
13425
|
-
|
|
13426
|
-
|
|
13427
|
-
|
|
13854
|
+
const joints = [];
|
|
13855
|
+
const inverseBinds = [];
|
|
13856
|
+
const influences = /* @__PURE__ */ new Map();
|
|
13857
|
+
for (const cc of connections) {
|
|
13858
|
+
const cluster = objects.get(cc.childId);
|
|
13859
|
+
if (cc.parentId !== skinConn.childId || cluster?.node.name !== "Deformer" || cluster.node.props[2] !== "Cluster") continue;
|
|
13860
|
+
const boneId = connections.find((b) => b.parentId === cluster.id && nodeIndexById.has(b.childId))?.childId;
|
|
13861
|
+
if (boneId === void 0) continue;
|
|
13862
|
+
const joint = joints.length;
|
|
13863
|
+
joints.push(nodeIndexById.get(boneId));
|
|
13864
|
+
const transform$1 = matrixChild(cluster.node, "Transform");
|
|
13865
|
+
const link = matrixChild(cluster.node, "TransformLink");
|
|
13866
|
+
inverseBinds.push(...Matrix4Math.multiply(Matrix4Math.invert(link) ?? Matrix4Math.identity(), transform$1));
|
|
13867
|
+
const cpIndexes = extractArrayProp(cluster.node.children.find((c) => c.name === "Indexes"));
|
|
13868
|
+
const cpWeights = extractArrayProp(cluster.node.children.find((c) => c.name === "Weights"));
|
|
13869
|
+
for (let k = 0; k < cpIndexes.length; k++) {
|
|
13870
|
+
const weight = Number(cpWeights[k] ?? 0);
|
|
13871
|
+
if (!(weight > 0)) continue;
|
|
13872
|
+
const cp = Number(cpIndexes[k]);
|
|
13873
|
+
const list = influences.get(cp) ?? [];
|
|
13874
|
+
list.push([joint, weight]);
|
|
13875
|
+
influences.set(cp, list);
|
|
13428
13876
|
}
|
|
13429
|
-
}
|
|
13877
|
+
}
|
|
13878
|
+
if (joints.length > MAX_JOINTS) throw new Error(`FBX skin has ${joints.length} bones; the mesh shader holds ${MAX_JOINTS}`);
|
|
13879
|
+
if (joints.length > 0) {
|
|
13880
|
+
skinIndex = skins.length;
|
|
13881
|
+
skins.push({
|
|
13882
|
+
joints,
|
|
13883
|
+
inverseBindMatrices: new Float32Array(inverseBinds)
|
|
13884
|
+
});
|
|
13885
|
+
jointIndices = new Float32Array(numVerts * 4);
|
|
13886
|
+
jointWeights = new Float32Array(numVerts * 4);
|
|
13887
|
+
corners.forEach(({ cp }, v) => {
|
|
13888
|
+
const top = (influences.get(cp) ?? []).sort((a, b) => b[1] - a[1]).slice(0, 4);
|
|
13889
|
+
const total = top.reduce((sum, [, w]) => sum + w, 0);
|
|
13890
|
+
top.forEach(([joint, weight], slot) => {
|
|
13891
|
+
jointIndices[v * 4 + slot] = joint;
|
|
13892
|
+
jointWeights[v * 4 + slot] = total > 0 ? weight / total : 0;
|
|
13893
|
+
});
|
|
13894
|
+
});
|
|
13895
|
+
}
|
|
13430
13896
|
}
|
|
13431
|
-
const
|
|
13897
|
+
const modelId = modelOfGeometry(id);
|
|
13898
|
+
const isLarge = numVerts > 65535;
|
|
13432
13899
|
meshes.push({
|
|
13433
13900
|
id: `fbx_mesh_${id}`,
|
|
13434
13901
|
name: entry.name,
|
|
13435
|
-
positions:
|
|
13436
|
-
normals:
|
|
13437
|
-
uvs:
|
|
13902
|
+
positions: outPositions,
|
|
13903
|
+
normals: outNormals,
|
|
13904
|
+
uvs: outUvs,
|
|
13438
13905
|
indices: isLarge ? new Uint32Array(outIndices) : new Uint16Array(outIndices),
|
|
13439
13906
|
jointIndices,
|
|
13440
13907
|
jointWeights,
|
|
13441
|
-
materialName: entry.name
|
|
13908
|
+
materialName: materialNameForGeometry(id) ?? entry.name,
|
|
13909
|
+
node: modelId !== void 0 ? nodeIndexById.get(modelId) : void 0,
|
|
13910
|
+
skin: skinIndex
|
|
13442
13911
|
});
|
|
13443
13912
|
}
|
|
13444
13913
|
for (const [, entry] of objects) if (entry.node.name === "Material") {
|
|
@@ -13467,12 +13936,14 @@ var FbxLoader = class FbxLoader$1 {
|
|
|
13467
13936
|
}
|
|
13468
13937
|
}
|
|
13469
13938
|
}
|
|
13470
|
-
|
|
13939
|
+
const material = {
|
|
13471
13940
|
name: entry.name,
|
|
13472
13941
|
diffuseColor,
|
|
13473
13942
|
shininess,
|
|
13474
13943
|
opacity
|
|
13475
13944
|
};
|
|
13945
|
+
FbxLoader$1.attachTextures(material, entry.id, objects, connections, options.basePath);
|
|
13946
|
+
materials[entry.name] = material;
|
|
13476
13947
|
}
|
|
13477
13948
|
const animations = [];
|
|
13478
13949
|
for (const [id, entry] of objects) if (entry.node.name === "AnimationStack") {
|
|
@@ -13492,12 +13963,13 @@ var FbxLoader = class FbxLoader$1 {
|
|
|
13492
13963
|
const key = `${targetModel?.id ?? curveNodeObj.id}|${kind}`;
|
|
13493
13964
|
let group = groups.get(key);
|
|
13494
13965
|
if (!group) {
|
|
13495
|
-
const model = targetModel?.node;
|
|
13966
|
+
const model$1 = targetModel?.node;
|
|
13496
13967
|
group = {
|
|
13497
13968
|
nodeName: targetModel?.name ?? curveNodeObj.name,
|
|
13498
|
-
|
|
13969
|
+
modelId: targetModel?.id,
|
|
13970
|
+
model: model$1,
|
|
13499
13971
|
kind,
|
|
13500
|
-
defaults: staticTransform(model, curveNodeObj.node, kind),
|
|
13972
|
+
defaults: staticTransform(model$1, curveNodeObj.node, kind),
|
|
13501
13973
|
channels: [
|
|
13502
13974
|
void 0,
|
|
13503
13975
|
void 0,
|
|
@@ -13522,7 +13994,13 @@ var FbxLoader = class FbxLoader$1 {
|
|
|
13522
13994
|
}
|
|
13523
13995
|
for (const group of groups.values()) {
|
|
13524
13996
|
const track = buildTransformTrack(group);
|
|
13525
|
-
if (track)
|
|
13997
|
+
if (!track) continue;
|
|
13998
|
+
if (group.modelId !== void 0) track.node = nodeIndexById.get(group.modelId);
|
|
13999
|
+
if (track.rotations && group.model) for (let k = 0; k < track.rotations.length; k += 4) {
|
|
14000
|
+
const q = Array.from(track.rotations.subarray(k, k + 4));
|
|
14001
|
+
track.rotations.set(modelRotation(group.model, q), k);
|
|
14002
|
+
}
|
|
14003
|
+
tracks.push(track);
|
|
13526
14004
|
}
|
|
13527
14005
|
}
|
|
13528
14006
|
let maxDuration = 1;
|
|
@@ -13537,68 +14015,172 @@ var FbxLoader = class FbxLoader$1 {
|
|
|
13537
14015
|
tracks
|
|
13538
14016
|
});
|
|
13539
14017
|
}
|
|
13540
|
-
|
|
13541
|
-
let minY = Infinity;
|
|
13542
|
-
let minZ = Infinity;
|
|
13543
|
-
let maxX = -Infinity;
|
|
13544
|
-
let maxY = -Infinity;
|
|
13545
|
-
let maxZ = -Infinity;
|
|
13546
|
-
for (const mesh of meshes) for (let i = 0; i < mesh.positions.length; i += 3) {
|
|
13547
|
-
const x = mesh.positions[i];
|
|
13548
|
-
const y = mesh.positions[i + 1];
|
|
13549
|
-
const z = mesh.positions[i + 2];
|
|
13550
|
-
if (x < minX) minX = x;
|
|
13551
|
-
if (y < minY) minY = y;
|
|
13552
|
-
if (z < minZ) minZ = z;
|
|
13553
|
-
if (x > maxX) maxX = x;
|
|
13554
|
-
if (y > maxY) maxY = y;
|
|
13555
|
-
if (z > maxZ) maxZ = z;
|
|
13556
|
-
}
|
|
13557
|
-
if (!Number.isFinite(minX)) {
|
|
13558
|
-
minX = -1;
|
|
13559
|
-
minY = -1;
|
|
13560
|
-
minZ = -1;
|
|
13561
|
-
maxX = 1;
|
|
13562
|
-
maxY = 1;
|
|
13563
|
-
maxZ = 1;
|
|
13564
|
-
}
|
|
13565
|
-
const sizeX = maxX - minX;
|
|
13566
|
-
const sizeY = maxY - minY;
|
|
13567
|
-
const sizeZ = maxZ - minZ;
|
|
13568
|
-
const centerX = (minX + maxX) * .5;
|
|
13569
|
-
const centerY = (minY + maxY) * .5;
|
|
13570
|
-
const centerZ = (minZ + maxZ) * .5;
|
|
13571
|
-
const sphereRadius = Math.hypot(sizeX, sizeY, sizeZ) * .5;
|
|
13572
|
-
return {
|
|
14018
|
+
const model = {
|
|
13573
14019
|
meshes,
|
|
13574
14020
|
materials,
|
|
13575
14021
|
animations,
|
|
14022
|
+
nodes,
|
|
14023
|
+
skins,
|
|
13576
14024
|
bounds: {
|
|
13577
14025
|
min: [
|
|
13578
|
-
|
|
13579
|
-
|
|
13580
|
-
|
|
14026
|
+
0,
|
|
14027
|
+
0,
|
|
14028
|
+
0
|
|
13581
14029
|
],
|
|
13582
14030
|
max: [
|
|
13583
|
-
|
|
13584
|
-
|
|
13585
|
-
|
|
14031
|
+
0,
|
|
14032
|
+
0,
|
|
14033
|
+
0
|
|
13586
14034
|
],
|
|
13587
14035
|
center: [
|
|
13588
|
-
|
|
13589
|
-
|
|
13590
|
-
|
|
14036
|
+
0,
|
|
14037
|
+
0,
|
|
14038
|
+
0
|
|
13591
14039
|
],
|
|
13592
14040
|
size: [
|
|
13593
|
-
|
|
13594
|
-
|
|
13595
|
-
|
|
14041
|
+
0,
|
|
14042
|
+
0,
|
|
14043
|
+
0
|
|
13596
14044
|
],
|
|
13597
|
-
boundingSphereRadius:
|
|
14045
|
+
boundingSphereRadius: 0
|
|
14046
|
+
}
|
|
14047
|
+
};
|
|
14048
|
+
const rest = poseNodes(model, void 0, 0);
|
|
14049
|
+
const lo = [
|
|
14050
|
+
Infinity,
|
|
14051
|
+
Infinity,
|
|
14052
|
+
Infinity
|
|
14053
|
+
];
|
|
14054
|
+
const hi = [
|
|
14055
|
+
-Infinity,
|
|
14056
|
+
-Infinity,
|
|
14057
|
+
-Infinity
|
|
14058
|
+
];
|
|
14059
|
+
for (const mesh of meshes) {
|
|
14060
|
+
const place = mesh.node !== void 0 ? rest[mesh.node] : void 0;
|
|
14061
|
+
for (let i = 0; i < mesh.positions.length; i += 3) {
|
|
14062
|
+
const p = [
|
|
14063
|
+
mesh.positions[i],
|
|
14064
|
+
mesh.positions[i + 1],
|
|
14065
|
+
mesh.positions[i + 2]
|
|
14066
|
+
];
|
|
14067
|
+
const q = place ? Matrix4Math.projectPoint(place, p) : p;
|
|
14068
|
+
for (let a = 0; a < 3; a++) {
|
|
14069
|
+
if (q[a] < lo[a]) lo[a] = q[a];
|
|
14070
|
+
if (q[a] > hi[a]) hi[a] = q[a];
|
|
14071
|
+
}
|
|
13598
14072
|
}
|
|
14073
|
+
}
|
|
14074
|
+
if (!Number.isFinite(lo[0])) {
|
|
14075
|
+
lo.fill(-1);
|
|
14076
|
+
hi.fill(1);
|
|
14077
|
+
}
|
|
14078
|
+
const { offset, scale } = resolveModelTransform({
|
|
14079
|
+
min: lo,
|
|
14080
|
+
max: hi,
|
|
14081
|
+
center: [
|
|
14082
|
+
(lo[0] + hi[0]) * .5,
|
|
14083
|
+
(lo[1] + hi[1]) * .5,
|
|
14084
|
+
(lo[2] + hi[2]) * .5
|
|
14085
|
+
],
|
|
14086
|
+
size: [
|
|
14087
|
+
hi[0] - lo[0],
|
|
14088
|
+
hi[1] - lo[1],
|
|
14089
|
+
hi[2] - lo[2]
|
|
14090
|
+
]
|
|
14091
|
+
}, options);
|
|
14092
|
+
model.rootTransform = Matrix4Math.multiply(Matrix4Math.scale(scale[0], scale[1], scale[2]), Matrix4Math.translate(-offset[0], -offset[1], -offset[2]));
|
|
14093
|
+
const min = [
|
|
14094
|
+
0,
|
|
14095
|
+
1,
|
|
14096
|
+
2
|
|
14097
|
+
].map((a) => (lo[a] - offset[a]) * scale[a]);
|
|
14098
|
+
const max = [
|
|
14099
|
+
0,
|
|
14100
|
+
1,
|
|
14101
|
+
2
|
|
14102
|
+
].map((a) => (hi[a] - offset[a]) * scale[a]);
|
|
14103
|
+
model.bounds = {
|
|
14104
|
+
min,
|
|
14105
|
+
max,
|
|
14106
|
+
center: [
|
|
14107
|
+
(min[0] + max[0]) * .5,
|
|
14108
|
+
(min[1] + max[1]) * .5,
|
|
14109
|
+
(min[2] + max[2]) * .5
|
|
14110
|
+
],
|
|
14111
|
+
size: [
|
|
14112
|
+
max[0] - min[0],
|
|
14113
|
+
max[1] - min[1],
|
|
14114
|
+
max[2] - min[2]
|
|
14115
|
+
],
|
|
14116
|
+
boundingSphereRadius: Math.hypot(max[0] - min[0], max[1] - min[1], max[2] - min[2]) * .5
|
|
13599
14117
|
};
|
|
14118
|
+
return model;
|
|
13600
14119
|
}
|
|
13601
14120
|
};
|
|
14121
|
+
/** A Properties70 vector (Lcl Translation / Rotation / Scaling), `fallback` where absent. */
|
|
14122
|
+
function readVec3(node, name, fallback) {
|
|
14123
|
+
const v = readProperty70(node, name);
|
|
14124
|
+
return [
|
|
14125
|
+
0,
|
|
14126
|
+
1,
|
|
14127
|
+
2
|
|
14128
|
+
].map((a) => {
|
|
14129
|
+
const x = v?.[a];
|
|
14130
|
+
return x !== void 0 && Number.isFinite(x) ? x : fallback;
|
|
14131
|
+
});
|
|
14132
|
+
}
|
|
14133
|
+
function multiplyQuat(a, b) {
|
|
14134
|
+
return [
|
|
14135
|
+
a[3] * b[0] + a[0] * b[3] + a[1] * b[2] - a[2] * b[1],
|
|
14136
|
+
a[3] * b[1] - a[0] * b[2] + a[1] * b[3] + a[2] * b[0],
|
|
14137
|
+
a[3] * b[2] + a[0] * b[1] - a[1] * b[0] + a[2] * b[3],
|
|
14138
|
+
a[3] * b[3] - a[0] * b[0] - a[1] * b[1] - a[2] * b[2]
|
|
14139
|
+
];
|
|
14140
|
+
}
|
|
14141
|
+
/**
|
|
14142
|
+
* A Model's full local rotation for an Lcl Rotation `q`:
|
|
14143
|
+
* PreRotation · q · PostRotation⁻¹ (FBX always applies pre/post in XYZ order).
|
|
14144
|
+
*/
|
|
14145
|
+
function modelRotation(model, q) {
|
|
14146
|
+
const pre = readProperty70(model, "PreRotation");
|
|
14147
|
+
const post = readProperty70(model, "PostRotation");
|
|
14148
|
+
let out = q;
|
|
14149
|
+
if (pre) out = multiplyQuat(fbxEulerToQuat(readVec3(model, "PreRotation", 0)), out);
|
|
14150
|
+
if (post) {
|
|
14151
|
+
const p = fbxEulerToQuat(readVec3(model, "PostRotation", 0));
|
|
14152
|
+
out = multiplyQuat(out, [
|
|
14153
|
+
-p[0],
|
|
14154
|
+
-p[1],
|
|
14155
|
+
-p[2],
|
|
14156
|
+
p[3]
|
|
14157
|
+
]);
|
|
14158
|
+
}
|
|
14159
|
+
return out;
|
|
14160
|
+
}
|
|
14161
|
+
/** A 4×4 matrix child (`Transform: *16 { a: … }`), identity when absent. */
|
|
14162
|
+
function matrixChild(node, name) {
|
|
14163
|
+
const child = node.children.find((c) => c.name === name);
|
|
14164
|
+
const raw = child?.children.find((c) => c.name === "a")?.props ?? child?.props;
|
|
14165
|
+
const values = raw?.[0] instanceof Float64Array || raw?.[0] instanceof Float32Array ? Array.from(raw[0]) : (raw ?? []).map(Number);
|
|
14166
|
+
return values.length === 16 ? values : Matrix4Math.identity();
|
|
14167
|
+
}
|
|
14168
|
+
/**
|
|
14169
|
+
* A geometry layer (normals, UVs) as a lookup by corner: its mapping says
|
|
14170
|
+
* whether values are per control point, per polygon vertex, per polygon or
|
|
14171
|
+
* one for all; IndexToDirect layers go through their index array.
|
|
14172
|
+
*/
|
|
14173
|
+
function layerElement(layer, data, index, size) {
|
|
14174
|
+
if (data.length === 0) return void 0;
|
|
14175
|
+
const text = (name) => String(layer.children.find((c) => c.name === name)?.props[0] ?? "");
|
|
14176
|
+
const mapping = text("MappingInformationType");
|
|
14177
|
+
const indexed = /^Index/.test(text("ReferenceInformationType")) && index.length > 0;
|
|
14178
|
+
return (cp, pv, poly) => {
|
|
14179
|
+
let i = mapping === "ByPolygonVertex" ? pv : mapping === "ByPolygon" ? poly : mapping === "AllSame" ? 0 : cp;
|
|
14180
|
+
if (indexed) i = Number(index[i] ?? i);
|
|
14181
|
+
return Array.from({ length: size }, (_, c) => Number(data[i * size + c] ?? 0));
|
|
14182
|
+
};
|
|
14183
|
+
}
|
|
13602
14184
|
/**
|
|
13603
14185
|
* Classifies a curve node from its OP connection property ("Lcl Translation",
|
|
13604
14186
|
* "Lcl Rotation", "Lcl Scaling") or, failing that, its short name ("T", "R", "S").
|
|
@@ -13739,181 +14321,34 @@ function buildTransformTrack(group) {
|
|
|
13739
14321
|
return {
|
|
13740
14322
|
nodeName: group.nodeName,
|
|
13741
14323
|
times,
|
|
13742
|
-
translations: group.kind === "T" ? values : void 0,
|
|
13743
|
-
rotations: isRotation ? values : void 0,
|
|
13744
|
-
scales: group.kind === "S" ? values : void 0
|
|
13745
|
-
};
|
|
13746
|
-
}
|
|
13747
|
-
function parseFBX(bufferOrText, options = {}) {
|
|
13748
|
-
return FbxLoader.parse(bufferOrText, options);
|
|
13749
|
-
}
|
|
13750
|
-
function loadFBX(srcOrData, options = {}) {
|
|
13751
|
-
let data = srcOrData;
|
|
13752
|
-
if (typeof srcOrData === "string") {
|
|
13753
|
-
const trimmed = srcOrData.trim();
|
|
13754
|
-
if (!(trimmed.includes("FBXHeaderExtension") || trimmed.includes("Kaydara FBX")) && typeof fs !== "undefined" && fs.existsSync) try {
|
|
13755
|
-
let filePath = srcOrData.startsWith("file://") ? srcOrData.slice(7) : srcOrData;
|
|
13756
|
-
try {
|
|
13757
|
-
filePath = decodeURIComponent(filePath);
|
|
13758
|
-
} catch (_) {}
|
|
13759
|
-
if (fs.existsSync(filePath)) {
|
|
13760
|
-
const buf = fs.readFileSync(filePath);
|
|
13761
|
-
data = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
13762
|
-
}
|
|
13763
|
-
} catch (_) {}
|
|
13764
|
-
}
|
|
13765
|
-
return FbxLoader.parse(data, options);
|
|
13766
|
-
}
|
|
13767
|
-
/**
|
|
13768
|
-
* Adds each triangle's (area-weighted) face normal to its three vertices, then
|
|
13769
|
-
* normalizes every vertex normal in place.
|
|
13770
|
-
*/
|
|
13771
|
-
function accumulateVertexNormals(positions, indices, normals) {
|
|
13772
|
-
const vertex = (i) => [
|
|
13773
|
-
positions[i * 3],
|
|
13774
|
-
positions[i * 3 + 1],
|
|
13775
|
-
positions[i * 3 + 2]
|
|
13776
|
-
];
|
|
13777
|
-
for (let i = 0; i < indices.length; i += 3) {
|
|
13778
|
-
const i0 = indices[i];
|
|
13779
|
-
const i1 = indices[i + 1];
|
|
13780
|
-
const i2 = indices[i + 2];
|
|
13781
|
-
const p0 = vertex(i0);
|
|
13782
|
-
const fn = Vector3Math.cross(Vector3Math.subtract(vertex(i1), p0), Vector3Math.subtract(vertex(i2), p0));
|
|
13783
|
-
for (const v of [
|
|
13784
|
-
i0,
|
|
13785
|
-
i1,
|
|
13786
|
-
i2
|
|
13787
|
-
]) {
|
|
13788
|
-
normals[v * 3] += fn[0];
|
|
13789
|
-
normals[v * 3 + 1] += fn[1];
|
|
13790
|
-
normals[v * 3 + 2] += fn[2];
|
|
13791
|
-
}
|
|
13792
|
-
}
|
|
13793
|
-
for (let i = 0; i < normals.length; i += 3) {
|
|
13794
|
-
const nx = normals[i];
|
|
13795
|
-
const ny = normals[i + 1];
|
|
13796
|
-
const nz = normals[i + 2];
|
|
13797
|
-
const len = Math.hypot(nx, ny, nz) || 1;
|
|
13798
|
-
normals[i] = nx / len;
|
|
13799
|
-
normals[i + 1] = ny / len;
|
|
13800
|
-
normals[i + 2] = nz / len;
|
|
13801
|
-
}
|
|
13802
|
-
}
|
|
13803
|
-
/** Axis-aligned bounds of xyz position arrays (all zero when empty). */
|
|
13804
|
-
function computeBounds(...positionArrays) {
|
|
13805
|
-
let minX = Infinity, minY = Infinity, minZ = Infinity;
|
|
13806
|
-
let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
|
|
13807
|
-
for (const positions of positionArrays) for (let i = 0; i < positions.length; i += 3) {
|
|
13808
|
-
const x = positions[i];
|
|
13809
|
-
const y = positions[i + 1];
|
|
13810
|
-
const z = positions[i + 2];
|
|
13811
|
-
if (x < minX) minX = x;
|
|
13812
|
-
if (x > maxX) maxX = x;
|
|
13813
|
-
if (y < minY) minY = y;
|
|
13814
|
-
if (y > maxY) maxY = y;
|
|
13815
|
-
if (z < minZ) minZ = z;
|
|
13816
|
-
if (z > maxZ) maxZ = z;
|
|
13817
|
-
}
|
|
13818
|
-
if (!Number.isFinite(minX)) {
|
|
13819
|
-
minX = minY = minZ = 0;
|
|
13820
|
-
maxX = maxY = maxZ = 0;
|
|
13821
|
-
}
|
|
13822
|
-
return {
|
|
13823
|
-
min: [
|
|
13824
|
-
minX,
|
|
13825
|
-
minY,
|
|
13826
|
-
minZ
|
|
13827
|
-
],
|
|
13828
|
-
max: [
|
|
13829
|
-
maxX,
|
|
13830
|
-
maxY,
|
|
13831
|
-
maxZ
|
|
13832
|
-
],
|
|
13833
|
-
center: [
|
|
13834
|
-
(minX + maxX) * .5,
|
|
13835
|
-
(minY + maxY) * .5,
|
|
13836
|
-
(minZ + maxZ) * .5
|
|
13837
|
-
],
|
|
13838
|
-
size: [
|
|
13839
|
-
maxX - minX,
|
|
13840
|
-
maxY - minY,
|
|
13841
|
-
maxZ - minZ
|
|
13842
|
-
]
|
|
13843
|
-
};
|
|
13844
|
-
}
|
|
13845
|
-
/**
|
|
13846
|
-
* The placement `LoadModelOptions` asks for: recentring on the bounds
|
|
13847
|
-
* (`center`), fitting the largest side to `normalizeSize`, then `scale`.
|
|
13848
|
-
*/
|
|
13849
|
-
function resolveModelTransform(bounds, options) {
|
|
13850
|
-
const maxDimension = Math.max(...bounds.size, 1e-4);
|
|
13851
|
-
let scaleMultiplier = 1;
|
|
13852
|
-
if (options.normalizeSize && options.normalizeSize > 0) scaleMultiplier = options.normalizeSize / maxDimension;
|
|
13853
|
-
const scale = [
|
|
13854
|
-
scaleMultiplier,
|
|
13855
|
-
scaleMultiplier,
|
|
13856
|
-
scaleMultiplier
|
|
13857
|
-
];
|
|
13858
|
-
if (typeof options.scale === "number") {
|
|
13859
|
-
scale[0] *= options.scale;
|
|
13860
|
-
scale[1] *= options.scale;
|
|
13861
|
-
scale[2] *= options.scale;
|
|
13862
|
-
} else if (Array.isArray(options.scale)) {
|
|
13863
|
-
scale[0] *= options.scale[0];
|
|
13864
|
-
scale[1] *= options.scale[1];
|
|
13865
|
-
scale[2] *= options.scale[2];
|
|
13866
|
-
}
|
|
13867
|
-
return {
|
|
13868
|
-
offset: options.center ? bounds.center : [
|
|
13869
|
-
0,
|
|
13870
|
-
0,
|
|
13871
|
-
0
|
|
13872
|
-
],
|
|
13873
|
-
scale
|
|
14324
|
+
translations: group.kind === "T" ? values : void 0,
|
|
14325
|
+
rotations: isRotation ? values : void 0,
|
|
14326
|
+
scales: group.kind === "S" ? values : void 0
|
|
13874
14327
|
};
|
|
13875
14328
|
}
|
|
13876
|
-
|
|
13877
|
-
|
|
13878
|
-
const out = new Float32Array(positions.length);
|
|
13879
|
-
for (let i = 0; i < positions.length; i += 3) {
|
|
13880
|
-
out[i] = (positions[i] - offset[0]) * scale[0];
|
|
13881
|
-
out[i + 1] = (positions[i + 1] - offset[1]) * scale[1];
|
|
13882
|
-
out[i + 2] = (positions[i + 2] - offset[2]) * scale[2];
|
|
13883
|
-
}
|
|
13884
|
-
return out;
|
|
13885
|
-
}
|
|
13886
|
-
/** Uint16 indices while every vertex fits, Uint32 beyond 65535 vertices. */
|
|
13887
|
-
function createIndexArray(indices, vertexCount) {
|
|
13888
|
-
return vertexCount > 65535 ? new Uint32Array(indices) : new Uint16Array(indices);
|
|
14329
|
+
function parseFBX(bufferOrText, options = {}) {
|
|
14330
|
+
return FbxLoader.parse(bufferOrText, options);
|
|
13889
14331
|
}
|
|
13890
|
-
|
|
13891
|
-
|
|
13892
|
-
|
|
13893
|
-
|
|
13894
|
-
(
|
|
13895
|
-
|
|
13896
|
-
|
|
13897
|
-
|
|
13898
|
-
|
|
13899
|
-
|
|
13900
|
-
|
|
13901
|
-
|
|
13902
|
-
|
|
13903
|
-
|
|
13904
|
-
|
|
13905
|
-
|
|
13906
|
-
|
|
13907
|
-
|
|
13908
|
-
|
|
13909
|
-
|
|
13910
|
-
size: [
|
|
13911
|
-
max[0] - min[0],
|
|
13912
|
-
max[1] - min[1],
|
|
13913
|
-
max[2] - min[2]
|
|
13914
|
-
],
|
|
13915
|
-
boundingSphereRadius: Math.hypot(max[0] - min[0], max[1] - min[1], max[2] - min[2]) * .5
|
|
13916
|
-
};
|
|
14332
|
+
function loadFBX(srcOrData, options = {}) {
|
|
14333
|
+
let data = srcOrData;
|
|
14334
|
+
if (typeof srcOrData === "string") {
|
|
14335
|
+
const trimmed = srcOrData.trim();
|
|
14336
|
+
if (!(trimmed.includes("FBXHeaderExtension") || trimmed.includes("Kaydara FBX")) && typeof fs !== "undefined" && fs.existsSync) try {
|
|
14337
|
+
let filePath = srcOrData.startsWith("file://") ? srcOrData.slice(7) : srcOrData;
|
|
14338
|
+
try {
|
|
14339
|
+
filePath = decodeURIComponent(filePath);
|
|
14340
|
+
} catch (_) {}
|
|
14341
|
+
if (fs.existsSync(filePath)) {
|
|
14342
|
+
const buf = fs.readFileSync(filePath);
|
|
14343
|
+
data = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
14344
|
+
options = {
|
|
14345
|
+
basePath: path.dirname(filePath),
|
|
14346
|
+
...options
|
|
14347
|
+
};
|
|
14348
|
+
}
|
|
14349
|
+
} catch (_) {}
|
|
14350
|
+
}
|
|
14351
|
+
return FbxLoader.parse(data, options);
|
|
13917
14352
|
}
|
|
13918
14353
|
var StlLoader = class StlLoader$1 {
|
|
13919
14354
|
/**
|
|
@@ -14340,250 +14775,97 @@ var PlyLoader = class PlyLoader$1 {
|
|
|
14340
14775
|
let nx = 0, ny = 0, nz = 0;
|
|
14341
14776
|
let u = 0, vCoord = 0;
|
|
14342
14777
|
let r = 1, g$1 = 1, b = 1, a = 1;
|
|
14343
|
-
for (const prop of vProps) {
|
|
14344
|
-
const val = readType(prop.type);
|
|
14345
|
-
const pName = prop.name.toLowerCase();
|
|
14346
|
-
if (pName === "x") x = val;
|
|
14347
|
-
else if (pName === "y") y = val;
|
|
14348
|
-
else if (pName === "z") z = val;
|
|
14349
|
-
else if (pName === "nx") nx = val;
|
|
14350
|
-
else if (pName === "ny") ny = val;
|
|
14351
|
-
else if (pName === "nz") nz = val;
|
|
14352
|
-
else if (pName === "u" || pName === "s") u = val;
|
|
14353
|
-
else if (pName === "v" || pName === "t") vCoord = val;
|
|
14354
|
-
else if (pName === "red" || pName === "r") r = prop.type.includes("int") ? val / 255 : val;
|
|
14355
|
-
else if (pName === "green" || pName === "g") g$1 = prop.type.includes("int") ? val / 255 : val;
|
|
14356
|
-
else if (pName === "blue" || pName === "b") b = prop.type.includes("int") ? val / 255 : val;
|
|
14357
|
-
else if (pName === "alpha" || pName === "a") a = prop.type.includes("int") ? val / 255 : val;
|
|
14358
|
-
}
|
|
14359
|
-
rawPositions.push(x, y, z);
|
|
14360
|
-
rawNormals.push(nx, ny, nz);
|
|
14361
|
-
rawUvs.push(u, 1 - vCoord);
|
|
14362
|
-
if (hasColors) rawColors.push(r, g$1, b, a);
|
|
14363
|
-
}
|
|
14364
|
-
const listProp = (faceElement?.properties ?? []).find((p) => p.isList);
|
|
14365
|
-
for (let f = 0; f < faceCount; f++) {
|
|
14366
|
-
if (offset >= bodyBytes.byteLength) break;
|
|
14367
|
-
if (listProp) {
|
|
14368
|
-
const count = readType(listProp.countType ?? "uchar");
|
|
14369
|
-
const verts = [];
|
|
14370
|
-
for (let k = 0; k < count; k++) verts.push(readType(listProp.itemType ?? "int"));
|
|
14371
|
-
if (verts.length >= 3) {
|
|
14372
|
-
const v0 = verts[0];
|
|
14373
|
-
for (let i = 1; i < verts.length - 1; i++) rawIndices.push(v0, verts[i], verts[i + 1]);
|
|
14374
|
-
}
|
|
14375
|
-
}
|
|
14376
|
-
}
|
|
14377
|
-
return PlyLoader$1.finalizeModel(rawPositions, rawNormals, rawUvs, hasColors ? rawColors : void 0, rawIndices, hasNormals, options);
|
|
14378
|
-
}
|
|
14379
|
-
static finalizeModel(rawPositions, rawNormals, rawUvs, rawColors, rawIndices, hasNormals, options) {
|
|
14380
|
-
if (!hasNormals || options.smoothNormals) accumulateVertexNormals(rawPositions, rawIndices, rawNormals);
|
|
14381
|
-
const bounds = computeBounds(rawPositions);
|
|
14382
|
-
const transform$1 = resolveModelTransform(bounds, options);
|
|
14383
|
-
const positions = transformPositions(rawPositions, transform$1);
|
|
14384
|
-
const indexArray = createIndexArray(rawIndices, positions.length / 3);
|
|
14385
|
-
const defaultMaterial = {
|
|
14386
|
-
name: "ply_default",
|
|
14387
|
-
diffuseColor: [
|
|
14388
|
-
.8,
|
|
14389
|
-
.8,
|
|
14390
|
-
.85,
|
|
14391
|
-
1
|
|
14392
|
-
],
|
|
14393
|
-
shininess: 32,
|
|
14394
|
-
specularIntensity: .5,
|
|
14395
|
-
...options.defaultMaterial
|
|
14396
|
-
};
|
|
14397
|
-
return {
|
|
14398
|
-
name: "PLY_Model",
|
|
14399
|
-
meshes: [{
|
|
14400
|
-
id: "ply_mesh_0",
|
|
14401
|
-
name: "ply_geometry",
|
|
14402
|
-
positions,
|
|
14403
|
-
normals: new Float32Array(rawNormals),
|
|
14404
|
-
uvs: new Float32Array(rawUvs),
|
|
14405
|
-
colors: rawColors ? new Float32Array(rawColors) : void 0,
|
|
14406
|
-
indices: indexArray,
|
|
14407
|
-
materialName: "ply_default"
|
|
14408
|
-
}],
|
|
14409
|
-
materials: { ply_default: defaultMaterial },
|
|
14410
|
-
animations: [],
|
|
14411
|
-
bounds: transformModelBounds(bounds, transform$1)
|
|
14412
|
-
};
|
|
14413
|
-
}
|
|
14414
|
-
/**
|
|
14415
|
-
* Loads a PLY file synchronously or asynchronously from a path or URL.
|
|
14416
|
-
*/
|
|
14417
|
-
static load(srcOrData, options = {}) {
|
|
14418
|
-
if (typeof srcOrData !== "string") return PlyLoader$1.parse(srcOrData, options);
|
|
14419
|
-
if (srcOrData.startsWith("/") || srcOrData.startsWith("./") || srcOrData.startsWith("../") || srcOrData.startsWith("file://")) {
|
|
14420
|
-
const cleanPath = srcOrData.startsWith("file://") ? srcOrData.replace("file://", "") : srcOrData;
|
|
14421
|
-
const buf = fs.readFileSync(cleanPath);
|
|
14422
|
-
return PlyLoader$1.parse(buf, options);
|
|
14423
|
-
}
|
|
14424
|
-
return PlyLoader$1.parse(srcOrData, options);
|
|
14425
|
-
}
|
|
14426
|
-
};
|
|
14427
|
-
function loadPLY(srcOrData, options = {}) {
|
|
14428
|
-
return PlyLoader.load(srcOrData, options);
|
|
14429
|
-
}
|
|
14430
|
-
/**
|
|
14431
|
-
* Poses a model's scene graph (glTF semantics): samples an animation clip's
|
|
14432
|
-
* translation / rotation / scale tracks at a time, composes the node
|
|
14433
|
-
* hierarchy into model-space matrices, and turns a skin's joints into the
|
|
14434
|
-
* joint matrices the mesh shader blends (joint world × inverse bind).
|
|
14435
|
-
*/
|
|
14436
|
-
/** Joints per skin the mesh shader holds (mesh3d.wgsl jointMatrices). */
|
|
14437
|
-
const MAX_JOINTS = 128;
|
|
14438
|
-
/** Column-major translate · rotate(quaternion) · scale. */
|
|
14439
|
-
function trsMatrix(t, q, s) {
|
|
14440
|
-
const [x, y, z, w] = q;
|
|
14441
|
-
const xx = x * x;
|
|
14442
|
-
const yy = y * y;
|
|
14443
|
-
const zz = z * z;
|
|
14444
|
-
const xy = x * y;
|
|
14445
|
-
const xz = x * z;
|
|
14446
|
-
const yz = y * z;
|
|
14447
|
-
const wx = w * x;
|
|
14448
|
-
const wy = w * y;
|
|
14449
|
-
const wz = w * z;
|
|
14450
|
-
return [
|
|
14451
|
-
(1 - 2 * (yy + zz)) * s[0],
|
|
14452
|
-
2 * (xy + wz) * s[0],
|
|
14453
|
-
2 * (xz - wy) * s[0],
|
|
14454
|
-
0,
|
|
14455
|
-
2 * (xy - wz) * s[1],
|
|
14456
|
-
(1 - 2 * (xx + zz)) * s[1],
|
|
14457
|
-
2 * (yz + wx) * s[1],
|
|
14458
|
-
0,
|
|
14459
|
-
2 * (xz + wy) * s[2],
|
|
14460
|
-
2 * (yz - wx) * s[2],
|
|
14461
|
-
(1 - 2 * (xx + yy)) * s[2],
|
|
14462
|
-
0,
|
|
14463
|
-
t[0],
|
|
14464
|
-
t[1],
|
|
14465
|
-
t[2],
|
|
14466
|
-
1
|
|
14467
|
-
];
|
|
14468
|
-
}
|
|
14469
|
-
function normalizeQuat(q) {
|
|
14470
|
-
const len = Math.hypot(q[0], q[1], q[2], q[3]) || 1;
|
|
14471
|
-
return [
|
|
14472
|
-
q[0] / len,
|
|
14473
|
-
q[1] / len,
|
|
14474
|
-
q[2] / len,
|
|
14475
|
-
q[3] / len
|
|
14476
|
-
];
|
|
14477
|
-
}
|
|
14478
|
-
function slerp(a, b, t) {
|
|
14479
|
-
let cos = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
|
|
14480
|
-
const sign = cos < 0 ? -1 : 1;
|
|
14481
|
-
cos *= sign;
|
|
14482
|
-
if (cos > .9995) return normalizeQuat([
|
|
14483
|
-
a[0] + (sign * b[0] - a[0]) * t,
|
|
14484
|
-
a[1] + (sign * b[1] - a[1]) * t,
|
|
14485
|
-
a[2] + (sign * b[2] - a[2]) * t,
|
|
14486
|
-
a[3] + (sign * b[3] - a[3]) * t
|
|
14487
|
-
]);
|
|
14488
|
-
const theta = Math.acos(cos);
|
|
14489
|
-
const sin = Math.sin(theta);
|
|
14490
|
-
const wa = Math.sin((1 - t) * theta) / sin;
|
|
14491
|
-
const wb = sign * Math.sin(t * theta) / sin;
|
|
14492
|
-
return [
|
|
14493
|
-
a[0] * wa + b[0] * wb,
|
|
14494
|
-
a[1] * wa + b[1] * wb,
|
|
14495
|
-
a[2] * wa + b[2] * wb,
|
|
14496
|
-
a[3] * wa + b[3] * wb
|
|
14497
|
-
];
|
|
14498
|
-
}
|
|
14499
|
-
/** Index of the last keyframe at or before `time` (binary search). */
|
|
14500
|
-
function keyBefore(times, time$2) {
|
|
14501
|
-
let lo = 0;
|
|
14502
|
-
let hi = times.length - 1;
|
|
14503
|
-
while (lo < hi) {
|
|
14504
|
-
const mid = lo + hi + 1 >> 1;
|
|
14505
|
-
if ((times[mid] ?? 0) <= time$2) lo = mid;
|
|
14506
|
-
else hi = mid - 1;
|
|
14507
|
-
}
|
|
14508
|
-
return lo;
|
|
14509
|
-
}
|
|
14510
|
-
/** Element `k` (of `size` components) of a track's values; cubic tracks store [in, value, out] triples. */
|
|
14511
|
-
function valueAt(values, k, size, cubic, part = 1) {
|
|
14512
|
-
const at = cubic ? (k * 3 + part) * size : k * size;
|
|
14513
|
-
return Array.from(values.subarray(at, at + size));
|
|
14514
|
-
}
|
|
14515
|
-
function sampleChannel(track, values, size, time$2) {
|
|
14516
|
-
const { times } = track;
|
|
14517
|
-
const cubic = track.interpolation === "cubic";
|
|
14518
|
-
const last = times.length - 1;
|
|
14519
|
-
if (time$2 <= (times[0] ?? 0)) return valueAt(values, 0, size, cubic);
|
|
14520
|
-
if (time$2 >= (times[last] ?? 0)) return valueAt(values, last, size, cubic);
|
|
14521
|
-
const k = keyBefore(times, time$2);
|
|
14522
|
-
if (track.interpolation === "step") return valueAt(values, k, size, cubic);
|
|
14523
|
-
const t0 = times[k] ?? 0;
|
|
14524
|
-
const dt = (times[k + 1] ?? t0) - t0;
|
|
14525
|
-
const u = dt > 0 ? (time$2 - t0) / dt : 0;
|
|
14526
|
-
const a = valueAt(values, k, size, cubic);
|
|
14527
|
-
const b = valueAt(values, k + 1, size, cubic);
|
|
14528
|
-
if (cubic) {
|
|
14529
|
-
const outA = valueAt(values, k, size, true, 2);
|
|
14530
|
-
const inB = valueAt(values, k + 1, size, true, 0);
|
|
14531
|
-
const u2 = u * u;
|
|
14532
|
-
const u3 = u2 * u;
|
|
14533
|
-
const out = a.map((p0, i) => (2 * u3 - 3 * u2 + 1) * p0 + (u3 - 2 * u2 + u) * dt * (outA[i] ?? 0) + (-2 * u3 + 3 * u2) * (b[i] ?? 0) + (u3 - u2) * dt * (inB[i] ?? 0));
|
|
14534
|
-
return size === 4 ? normalizeQuat(out) : out;
|
|
14778
|
+
for (const prop of vProps) {
|
|
14779
|
+
const val = readType(prop.type);
|
|
14780
|
+
const pName = prop.name.toLowerCase();
|
|
14781
|
+
if (pName === "x") x = val;
|
|
14782
|
+
else if (pName === "y") y = val;
|
|
14783
|
+
else if (pName === "z") z = val;
|
|
14784
|
+
else if (pName === "nx") nx = val;
|
|
14785
|
+
else if (pName === "ny") ny = val;
|
|
14786
|
+
else if (pName === "nz") nz = val;
|
|
14787
|
+
else if (pName === "u" || pName === "s") u = val;
|
|
14788
|
+
else if (pName === "v" || pName === "t") vCoord = val;
|
|
14789
|
+
else if (pName === "red" || pName === "r") r = prop.type.includes("int") ? val / 255 : val;
|
|
14790
|
+
else if (pName === "green" || pName === "g") g$1 = prop.type.includes("int") ? val / 255 : val;
|
|
14791
|
+
else if (pName === "blue" || pName === "b") b = prop.type.includes("int") ? val / 255 : val;
|
|
14792
|
+
else if (pName === "alpha" || pName === "a") a = prop.type.includes("int") ? val / 255 : val;
|
|
14793
|
+
}
|
|
14794
|
+
rawPositions.push(x, y, z);
|
|
14795
|
+
rawNormals.push(nx, ny, nz);
|
|
14796
|
+
rawUvs.push(u, 1 - vCoord);
|
|
14797
|
+
if (hasColors) rawColors.push(r, g$1, b, a);
|
|
14798
|
+
}
|
|
14799
|
+
const listProp = (faceElement?.properties ?? []).find((p) => p.isList);
|
|
14800
|
+
for (let f = 0; f < faceCount; f++) {
|
|
14801
|
+
if (offset >= bodyBytes.byteLength) break;
|
|
14802
|
+
if (listProp) {
|
|
14803
|
+
const count = readType(listProp.countType ?? "uchar");
|
|
14804
|
+
const verts = [];
|
|
14805
|
+
for (let k = 0; k < count; k++) verts.push(readType(listProp.itemType ?? "int"));
|
|
14806
|
+
if (verts.length >= 3) {
|
|
14807
|
+
const v0 = verts[0];
|
|
14808
|
+
for (let i = 1; i < verts.length - 1; i++) rawIndices.push(v0, verts[i], verts[i + 1]);
|
|
14809
|
+
}
|
|
14810
|
+
}
|
|
14811
|
+
}
|
|
14812
|
+
return PlyLoader$1.finalizeModel(rawPositions, rawNormals, rawUvs, hasColors ? rawColors : void 0, rawIndices, hasNormals, options);
|
|
14535
14813
|
}
|
|
14536
|
-
|
|
14537
|
-
|
|
14538
|
-
|
|
14539
|
-
|
|
14540
|
-
|
|
14541
|
-
|
|
14542
|
-
|
|
14543
|
-
|
|
14544
|
-
|
|
14545
|
-
|
|
14546
|
-
|
|
14547
|
-
|
|
14548
|
-
|
|
14549
|
-
|
|
14550
|
-
|
|
14814
|
+
static finalizeModel(rawPositions, rawNormals, rawUvs, rawColors, rawIndices, hasNormals, options) {
|
|
14815
|
+
if (!hasNormals || options.smoothNormals) accumulateVertexNormals(rawPositions, rawIndices, rawNormals);
|
|
14816
|
+
const bounds = computeBounds(rawPositions);
|
|
14817
|
+
const transform$1 = resolveModelTransform(bounds, options);
|
|
14818
|
+
const positions = transformPositions(rawPositions, transform$1);
|
|
14819
|
+
const indexArray = createIndexArray(rawIndices, positions.length / 3);
|
|
14820
|
+
const defaultMaterial = {
|
|
14821
|
+
name: "ply_default",
|
|
14822
|
+
diffuseColor: rawColors ? [
|
|
14823
|
+
1,
|
|
14824
|
+
1,
|
|
14825
|
+
1,
|
|
14826
|
+
1
|
|
14827
|
+
] : [
|
|
14828
|
+
.8,
|
|
14829
|
+
.8,
|
|
14830
|
+
.85,
|
|
14831
|
+
1
|
|
14832
|
+
],
|
|
14833
|
+
shininess: 32,
|
|
14834
|
+
specularIntensity: .5,
|
|
14835
|
+
...options.defaultMaterial
|
|
14836
|
+
};
|
|
14837
|
+
return {
|
|
14838
|
+
name: "PLY_Model",
|
|
14839
|
+
meshes: [{
|
|
14840
|
+
id: "ply_mesh_0",
|
|
14841
|
+
name: "ply_geometry",
|
|
14842
|
+
positions,
|
|
14843
|
+
normals: new Float32Array(rawNormals),
|
|
14844
|
+
uvs: new Float32Array(rawUvs),
|
|
14845
|
+
colors: rawColors ? new Float32Array(rawColors) : void 0,
|
|
14846
|
+
indices: indexArray,
|
|
14847
|
+
materialName: "ply_default"
|
|
14848
|
+
}],
|
|
14849
|
+
materials: { ply_default: defaultMaterial },
|
|
14850
|
+
animations: [],
|
|
14851
|
+
bounds: transformModelBounds(bounds, transform$1)
|
|
14852
|
+
};
|
|
14551
14853
|
}
|
|
14552
|
-
|
|
14553
|
-
|
|
14554
|
-
|
|
14555
|
-
|
|
14556
|
-
|
|
14557
|
-
|
|
14558
|
-
|
|
14559
|
-
|
|
14560
|
-
|
|
14561
|
-
const world = new Array(nodes.length);
|
|
14562
|
-
const resolve$1$1 = (i) => {
|
|
14563
|
-
const done = world[i];
|
|
14564
|
-
if (done) return done;
|
|
14565
|
-
const parent = nodes[i].parent;
|
|
14566
|
-
const m = parent < 0 ? local[i] : Matrix4Math.multiply(resolve$1$1(parent), local[i]);
|
|
14567
|
-
world[i] = m;
|
|
14568
|
-
return m;
|
|
14569
|
-
};
|
|
14570
|
-
return nodes.map((_, i) => resolve$1$1(i));
|
|
14571
|
-
}
|
|
14572
|
-
/** The joint matrices of one skin for a posed scene graph, padded with identity to MAX_JOINTS. */
|
|
14573
|
-
function jointMatrices(model, skinIndex, world) {
|
|
14574
|
-
const skin = model.skins?.[skinIndex];
|
|
14575
|
-
if (!skin) throw new Error(`model has no skin ${skinIndex}`);
|
|
14576
|
-
const out = new Float32Array(MAX_JOINTS * 16);
|
|
14577
|
-
for (let j = 0; j < MAX_JOINTS; j++) {
|
|
14578
|
-
const node = skin.joints[j];
|
|
14579
|
-
if (node === void 0) {
|
|
14580
|
-
out.set(Matrix4Math.identity(), j * 16);
|
|
14581
|
-
continue;
|
|
14854
|
+
/**
|
|
14855
|
+
* Loads a PLY file synchronously or asynchronously from a path or URL.
|
|
14856
|
+
*/
|
|
14857
|
+
static load(srcOrData, options = {}) {
|
|
14858
|
+
if (typeof srcOrData !== "string") return PlyLoader$1.parse(srcOrData, options);
|
|
14859
|
+
if (srcOrData.startsWith("/") || srcOrData.startsWith("./") || srcOrData.startsWith("../") || srcOrData.startsWith("file://")) {
|
|
14860
|
+
const cleanPath = srcOrData.startsWith("file://") ? srcOrData.replace("file://", "") : srcOrData;
|
|
14861
|
+
const buf = fs.readFileSync(cleanPath);
|
|
14862
|
+
return PlyLoader$1.parse(buf, options);
|
|
14582
14863
|
}
|
|
14583
|
-
|
|
14584
|
-
out.set(Matrix4Math.multiply(world[node], ibm), j * 16);
|
|
14864
|
+
return PlyLoader$1.parse(srcOrData, options);
|
|
14585
14865
|
}
|
|
14586
|
-
|
|
14866
|
+
};
|
|
14867
|
+
function loadPLY(srcOrData, options = {}) {
|
|
14868
|
+
return PlyLoader.load(srcOrData, options);
|
|
14587
14869
|
}
|
|
14588
14870
|
const COMPONENTS = {
|
|
14589
14871
|
SCALAR: 1,
|
|
@@ -14613,6 +14895,11 @@ function componentReader(componentType, normalized = false) {
|
|
|
14613
14895
|
default: return (dv, at) => dv.getFloat32(at, true);
|
|
14614
14896
|
}
|
|
14615
14897
|
}
|
|
14898
|
+
/** Mesh compression the loader cannot decode: a file that requires it fails with a clear error. */
|
|
14899
|
+
const UNSUPPORTED_REQUIRED_EXTENSIONS = {
|
|
14900
|
+
KHR_draco_mesh_compression: "Draco mesh compression",
|
|
14901
|
+
EXT_meshopt_compression: "meshopt compression"
|
|
14902
|
+
};
|
|
14616
14903
|
const GLB_MAGIC = 1179937895;
|
|
14617
14904
|
const CHUNK_TYPE_JSON = 1313821514;
|
|
14618
14905
|
const CHUNK_TYPE_BIN = 5130562;
|
|
@@ -14667,6 +14954,10 @@ var GltfLoader = class GltfLoader$1 {
|
|
|
14667
14954
|
}
|
|
14668
14955
|
}
|
|
14669
14956
|
}
|
|
14957
|
+
for (const ext of doc.extensionsRequired ?? []) {
|
|
14958
|
+
const what = UNSUPPORTED_REQUIRED_EXTENSIONS[ext];
|
|
14959
|
+
if (what) throw new Error(`glTF file requires ${ext} (${what}), which framefields cannot decode. Re-export the model without mesh compression (in Blender's glTF exporter, turn off Compression).`);
|
|
14960
|
+
}
|
|
14670
14961
|
return GltfLoader$1.buildModel(doc, binaryBuffers, options, basePath);
|
|
14671
14962
|
}
|
|
14672
14963
|
static unpackGlb(bytes) {
|
|
@@ -14754,11 +15045,59 @@ var GltfLoader = class GltfLoader$1 {
|
|
|
14754
15045
|
}
|
|
14755
15046
|
if (!basePath) return void 0;
|
|
14756
15047
|
const file = path.isAbsolute(image.uri) ? image.uri : path.join(basePath, decodeURIComponent(image.uri));
|
|
14757
|
-
if (!fs.existsSync(file))
|
|
14758
|
-
|
|
15048
|
+
if (!fs.existsSync(file)) {
|
|
15049
|
+
GltfLoader$1.warn(`Image "${image.uri}" was not found next to the model; textures using it are skipped.`);
|
|
15050
|
+
return;
|
|
15051
|
+
}
|
|
14759
15052
|
return {
|
|
14760
15053
|
bytes: new Uint8Array(fs.readFileSync(file)),
|
|
14761
|
-
mime: image.mimeType ?? (
|
|
15054
|
+
mime: image.mimeType ?? textureMime(file)
|
|
15055
|
+
};
|
|
15056
|
+
}
|
|
15057
|
+
static warn(message) {
|
|
15058
|
+
console.warn(`[GltfLoader] ${message}`);
|
|
15059
|
+
}
|
|
15060
|
+
/** The UV set of a texture reference (KHR_texture_transform may override it). */
|
|
15061
|
+
static texCoordOf(info) {
|
|
15062
|
+
return info.extensions?.KHR_texture_transform?.texCoord ?? info.texCoord ?? 0;
|
|
15063
|
+
}
|
|
15064
|
+
/** The UV set and transform of the material's base color map (else its first map). */
|
|
15065
|
+
static uvPlan(mat) {
|
|
15066
|
+
const first = [
|
|
15067
|
+
mat?.pbrMetallicRoughness?.baseColorTexture,
|
|
15068
|
+
mat?.normalTexture,
|
|
15069
|
+
mat?.pbrMetallicRoughness?.metallicRoughnessTexture,
|
|
15070
|
+
mat?.occlusionTexture,
|
|
15071
|
+
mat?.emissiveTexture
|
|
15072
|
+
].find((t) => t !== void 0);
|
|
15073
|
+
return first ? {
|
|
15074
|
+
texCoord: GltfLoader$1.texCoordOf(first),
|
|
15075
|
+
transform: first.extensions?.KHR_texture_transform
|
|
15076
|
+
} : { texCoord: 0 };
|
|
15077
|
+
}
|
|
15078
|
+
/**
|
|
15079
|
+
* A texture's decodable image: EXT_texture_webp's source when present.
|
|
15080
|
+
* KTX2 / Basis (KHR_texture_basisu) and other formats the renderer
|
|
15081
|
+
* cannot decode are skipped with a warning.
|
|
15082
|
+
*/
|
|
15083
|
+
static textureImage(doc, binaryBuffers, textureIndex, basePath, label) {
|
|
15084
|
+
const texture = doc.textures?.[textureIndex];
|
|
15085
|
+
if (!texture) return void 0;
|
|
15086
|
+
const source = texture.extensions?.EXT_texture_webp?.source ?? texture.source;
|
|
15087
|
+
if (source === void 0) {
|
|
15088
|
+
if (texture.extensions?.KHR_texture_basisu) GltfLoader$1.warn(`${label} is a KTX2 / Basis texture, which framefields cannot decode; it is skipped (use PNG, JPEG or WebP).`);
|
|
15089
|
+
return;
|
|
15090
|
+
}
|
|
15091
|
+
const image = GltfLoader$1.imageBytes(doc, binaryBuffers, source, basePath);
|
|
15092
|
+
if (!image) return void 0;
|
|
15093
|
+
if (!SUPPORTED_IMAGE_MIMES.has(image.mime)) {
|
|
15094
|
+
GltfLoader$1.warn(`${label} is ${image.mime}, which framefields cannot decode; it is skipped (use PNG, JPEG or WebP).`);
|
|
15095
|
+
return;
|
|
15096
|
+
}
|
|
15097
|
+
const sampler = texture.sampler !== void 0 ? doc.samplers?.[texture.sampler] : void 0;
|
|
15098
|
+
return {
|
|
15099
|
+
...image,
|
|
15100
|
+
sampler
|
|
14762
15101
|
};
|
|
14763
15102
|
}
|
|
14764
15103
|
static buildMaterial(doc, binaryBuffers, mat, name, basePath) {
|
|
@@ -14798,38 +15137,45 @@ var GltfLoader = class GltfLoader$1 {
|
|
|
14798
15137
|
1
|
|
14799
15138
|
];
|
|
14800
15139
|
} else if (mat.extensions?.KHR_materials_unlit) material.shading = "unlit";
|
|
14801
|
-
const
|
|
14802
|
-
const
|
|
14803
|
-
|
|
14804
|
-
const
|
|
14805
|
-
|
|
14806
|
-
|
|
14807
|
-
|
|
14808
|
-
|
|
14809
|
-
material.textureWrap = wrap === 33071 ? "clamp" : wrap === 33648 ? "mirror" : "repeat";
|
|
15140
|
+
const plan = GltfLoader$1.uvPlan(mat);
|
|
15141
|
+
const image = (info, slot) => {
|
|
15142
|
+
if (!info) return void 0;
|
|
15143
|
+
const label = `The ${slot} map of material "${name}"`;
|
|
15144
|
+
const set = GltfLoader$1.texCoordOf(info);
|
|
15145
|
+
if (set !== plan.texCoord) {
|
|
15146
|
+
GltfLoader$1.warn(`${label} uses UV set ${set}, but the mesh draws with UV set ${plan.texCoord}; the map is skipped.`);
|
|
15147
|
+
return;
|
|
14810
15148
|
}
|
|
14811
|
-
|
|
14812
|
-
|
|
14813
|
-
|
|
14814
|
-
return tex?.source !== void 0 ? GltfLoader$1.imageBytes(doc, binaryBuffers, tex.source, basePath)?.bytes : void 0;
|
|
15149
|
+
const t = info.extensions?.KHR_texture_transform;
|
|
15150
|
+
if (JSON.stringify(t ?? null) !== JSON.stringify(plan.transform ?? null)) GltfLoader$1.warn(`${label} has a different KHR_texture_transform than the mesh's first map; it is drawn with that one's.`);
|
|
15151
|
+
return GltfLoader$1.textureImage(doc, binaryBuffers, info.index, basePath, label);
|
|
14815
15152
|
};
|
|
14816
|
-
const
|
|
15153
|
+
const base = image(pbr?.baseColorTexture, "base color");
|
|
15154
|
+
if (base) {
|
|
15155
|
+
material.diffuseTextureBuffer = base.bytes;
|
|
15156
|
+
material.diffuseTextureMime = base.mime;
|
|
15157
|
+
const wrap = base.sampler?.wrapS;
|
|
15158
|
+
material.textureWrap = wrap === 33071 ? "clamp" : wrap === 33648 ? "mirror" : "repeat";
|
|
15159
|
+
if (base.sampler?.magFilter === 9728) material.textureFilter = "nearest";
|
|
15160
|
+
}
|
|
15161
|
+
const mapBytes = (info, slot) => image(info, slot)?.bytes;
|
|
15162
|
+
const normal = mapBytes(mat.normalTexture, "normal");
|
|
14817
15163
|
if (normal) {
|
|
14818
15164
|
material.normalTextureBuffer = normal;
|
|
14819
15165
|
material.normalScale = mat.normalTexture?.scale ?? 1;
|
|
14820
15166
|
}
|
|
14821
|
-
const mr = mapBytes(pbr?.metallicRoughnessTexture);
|
|
15167
|
+
const mr = mapBytes(pbr?.metallicRoughnessTexture, "metallic-roughness");
|
|
14822
15168
|
if (mr) {
|
|
14823
15169
|
material.metallicRoughnessTextureBuffer = mr;
|
|
14824
15170
|
material.roughness = pbr?.roughnessFactor ?? 1;
|
|
14825
15171
|
material.metallic = pbr?.metallicFactor ?? 1;
|
|
14826
15172
|
}
|
|
14827
|
-
const occlusion = mapBytes(mat.occlusionTexture);
|
|
15173
|
+
const occlusion = mapBytes(mat.occlusionTexture, "occlusion");
|
|
14828
15174
|
if (occlusion) {
|
|
14829
15175
|
material.occlusionTextureBuffer = occlusion;
|
|
14830
15176
|
material.occlusionStrength = mat.occlusionTexture?.strength ?? 1;
|
|
14831
15177
|
}
|
|
14832
|
-
const emissiveMap = material.emissive && mapBytes(mat.emissiveTexture);
|
|
15178
|
+
const emissiveMap = material.emissive && mapBytes(mat.emissiveTexture, "emissive");
|
|
14833
15179
|
if (emissiveMap) material.emissiveTextureBuffer = emissiveMap;
|
|
14834
15180
|
return material;
|
|
14835
15181
|
}
|
|
@@ -14903,6 +15249,7 @@ var GltfLoader = class GltfLoader$1 {
|
|
|
14903
15249
|
const key = materialKeys[idx];
|
|
14904
15250
|
materials[key] = GltfLoader$1.buildMaterial(doc, binaryBuffers, mat, key, basePath);
|
|
14905
15251
|
});
|
|
15252
|
+
const uvPlans = (doc.materials ?? []).map((mat) => GltfLoader$1.uvPlan(mat));
|
|
14906
15253
|
const nodes = GltfLoader$1.buildNodes(doc);
|
|
14907
15254
|
const skins = (doc.skins ?? []).map((skin) => ({
|
|
14908
15255
|
joints: skin.joints,
|
|
@@ -14966,11 +15313,18 @@ var GltfLoader = class GltfLoader$1 {
|
|
|
14966
15313
|
const read = (acc) => acc === void 0 ? void 0 : GltfLoader$1.readFloats(doc, binaryBuffers, acc);
|
|
14967
15314
|
const pos = read(prim.attributes.POSITION);
|
|
14968
15315
|
const numVerts = pos.count;
|
|
15316
|
+
const plan = prim.material !== void 0 ? uvPlans[prim.material] : void 0;
|
|
15317
|
+
const attributes = prim.attributes;
|
|
15318
|
+
const uvs = read(attributes[`TEXCOORD_${plan?.texCoord ?? 0}`])?.data ?? read(prim.attributes.TEXCOORD_0)?.data ?? new Float32Array(numVerts * 2);
|
|
15319
|
+
if (plan?.transform) applyTextureTransform(uvs, plan.transform);
|
|
14969
15320
|
const colors = read(prim.attributes.COLOR_0);
|
|
14970
15321
|
let rgba;
|
|
14971
15322
|
if (colors) {
|
|
14972
15323
|
rgba = new Float32Array(numVerts * 4).fill(1);
|
|
14973
|
-
for (let v = 0; v < numVerts; v++) for (let c = 0; c < colors.itemSize; c++)
|
|
15324
|
+
for (let v = 0; v < numVerts; v++) for (let c = 0; c < colors.itemSize; c++) {
|
|
15325
|
+
const value = colors.data[v * colors.itemSize + c] ?? 1;
|
|
15326
|
+
rgba[v * 4 + c] = c < 3 ? linearToSrgb(value) : value;
|
|
15327
|
+
}
|
|
14974
15328
|
}
|
|
14975
15329
|
let indices;
|
|
14976
15330
|
if (prim.indices !== void 0) indices = GltfLoader$1.readIndices(doc, binaryBuffers, prim.indices);
|
|
@@ -14998,7 +15352,7 @@ var GltfLoader = class GltfLoader$1 {
|
|
|
14998
15352
|
name: meshDef.name ?? `Mesh_${inst.mesh}`,
|
|
14999
15353
|
positions: pos.data,
|
|
15000
15354
|
normals: read(prim.attributes.NORMAL)?.data ?? new Float32Array(numVerts * 3),
|
|
15001
|
-
uvs
|
|
15355
|
+
uvs,
|
|
15002
15356
|
colors: rgba,
|
|
15003
15357
|
jointIndices: skinned ? read(prim.attributes.JOINTS_0)?.data : void 0,
|
|
15004
15358
|
jointWeights: skinned ? read(prim.attributes.WEIGHTS_0)?.data : void 0,
|
|
@@ -15072,8 +15426,8 @@ var GltfLoader = class GltfLoader$1 {
|
|
|
15072
15426
|
*/
|
|
15073
15427
|
static load(srcOrData, options = {}) {
|
|
15074
15428
|
if (typeof srcOrData !== "string") return GltfLoader$1.parse(srcOrData, options);
|
|
15075
|
-
|
|
15076
|
-
|
|
15429
|
+
const cleanPath = srcOrData.startsWith("file://") ? srcOrData.replace("file://", "") : srcOrData;
|
|
15430
|
+
if (srcOrData.startsWith("/") || srcOrData.startsWith("./") || srcOrData.startsWith("../") || srcOrData.startsWith("file://") || !srcOrData.trimStart().startsWith("{") && fs.existsSync(cleanPath)) {
|
|
15077
15431
|
const buf = fs.readFileSync(cleanPath);
|
|
15078
15432
|
const basePath = path.dirname(cleanPath);
|
|
15079
15433
|
return GltfLoader$1.parse(buf, options, basePath);
|
|
@@ -15087,6 +15441,33 @@ function loadGLTF(srcOrData, options = {}) {
|
|
|
15087
15441
|
function loadGLB(srcOrData, options = {}) {
|
|
15088
15442
|
return GltfLoader.load(srcOrData, options);
|
|
15089
15443
|
}
|
|
15444
|
+
function linearToSrgb(c) {
|
|
15445
|
+
const x = Math.min(Math.max(c, 0), 1);
|
|
15446
|
+
return x <= .0031308 ? x * 12.92 : 1.055 * x ** (1 / 2.4) - .055;
|
|
15447
|
+
}
|
|
15448
|
+
/** The image types the renderer decodes (sharp). */
|
|
15449
|
+
const SUPPORTED_IMAGE_MIMES = new Set([
|
|
15450
|
+
"image/png",
|
|
15451
|
+
"image/jpeg",
|
|
15452
|
+
"image/webp",
|
|
15453
|
+
"image/gif",
|
|
15454
|
+
"image/tiff",
|
|
15455
|
+
"image/avif"
|
|
15456
|
+
]);
|
|
15457
|
+
/**
|
|
15458
|
+
* Bakes KHR_texture_transform into UVs in place: uv' = T * R * S * uv
|
|
15459
|
+
* (offset, then rotation counter-clockwise in UV space, then scale).
|
|
15460
|
+
*/
|
|
15461
|
+
function applyTextureTransform(uvs, { offset = [0, 0], rotation = 0, scale = [1, 1] }) {
|
|
15462
|
+
const c = Math.cos(rotation);
|
|
15463
|
+
const s = Math.sin(rotation);
|
|
15464
|
+
for (let i = 0; i < uvs.length; i += 2) {
|
|
15465
|
+
const u = uvs[i] * scale[0];
|
|
15466
|
+
const v = uvs[i + 1] * scale[1];
|
|
15467
|
+
uvs[i] = c * u + s * v + offset[0];
|
|
15468
|
+
uvs[i + 1] = -s * u + c * v + offset[1];
|
|
15469
|
+
}
|
|
15470
|
+
}
|
|
15090
15471
|
const DEFAULT_PALETTE = [[
|
|
15091
15472
|
0,
|
|
15092
15473
|
0,
|
|
@@ -15660,7 +16041,12 @@ var OffLoader = class OffLoader$1 {
|
|
|
15660
16041
|
const indexArray = createIndexArray(rawIndices, positions.length / 3);
|
|
15661
16042
|
const defaultMaterial = {
|
|
15662
16043
|
name: "off_default",
|
|
15663
|
-
diffuseColor: [
|
|
16044
|
+
diffuseColor: hasColors ? [
|
|
16045
|
+
1,
|
|
16046
|
+
1,
|
|
16047
|
+
1,
|
|
16048
|
+
1
|
|
16049
|
+
] : [
|
|
15664
16050
|
.8,
|
|
15665
16051
|
.8,
|
|
15666
16052
|
.85,
|
|
@@ -20139,7 +20525,7 @@ function decodeBase64(base64$1) {
|
|
|
20139
20525
|
}
|
|
20140
20526
|
function extractFontFamiliesFromLottie(lottieJson) {
|
|
20141
20527
|
const fonts = /* @__PURE__ */ new Set();
|
|
20142
|
-
const fontsList = lottieJson?.fonts?.list;
|
|
20528
|
+
const fontsList = (lottieJson?.fonts)?.list;
|
|
20143
20529
|
if (Array.isArray(fontsList)) for (const entry of fontsList) {
|
|
20144
20530
|
if (entry?.fName && typeof entry.fName === "string") fonts.add(entry.fName);
|
|
20145
20531
|
if (entry?.fFamily && typeof entry.fFamily === "string") fonts.add(entry.fFamily);
|
|
@@ -20147,19 +20533,24 @@ function extractFontFamiliesFromLottie(lottieJson) {
|
|
|
20147
20533
|
const layers = lottieJson?.layers;
|
|
20148
20534
|
if (!Array.isArray(layers)) return [...fonts];
|
|
20149
20535
|
for (const layer of layers) {
|
|
20150
|
-
|
|
20151
|
-
|
|
20536
|
+
const l = layer;
|
|
20537
|
+
if (l?.ty !== 5) continue;
|
|
20538
|
+
const k = l?.t?.d?.k ?? l?.t?.k;
|
|
20152
20539
|
if (!k) continue;
|
|
20153
20540
|
const kArr = Array.isArray(k) ? k : [k];
|
|
20154
20541
|
for (const kf of kArr) {
|
|
20542
|
+
const kfObj = kf;
|
|
20155
20543
|
const possibleDocs = [
|
|
20156
|
-
|
|
20157
|
-
|
|
20544
|
+
kfObj?.s,
|
|
20545
|
+
kfObj?.e,
|
|
20158
20546
|
kf
|
|
20159
20547
|
].filter(Boolean);
|
|
20160
20548
|
for (const doc of possibleDocs) {
|
|
20161
20549
|
const docArr = Array.isArray(doc) ? doc : [doc];
|
|
20162
|
-
for (const d of docArr)
|
|
20550
|
+
for (const d of docArr) {
|
|
20551
|
+
const docItem = d;
|
|
20552
|
+
if (docItem?.f && typeof docItem.f === "string") fonts.add(docItem.f);
|
|
20553
|
+
}
|
|
20163
20554
|
}
|
|
20164
20555
|
}
|
|
20165
20556
|
}
|
|
@@ -20187,7 +20578,7 @@ async function registerLottieFont(family) {
|
|
|
20187
20578
|
if (typeof window !== "undefined" && typeof document !== "undefined" && typeof document.createElement === "function" && typeof document.getElementById === "function") {
|
|
20188
20579
|
try {
|
|
20189
20580
|
await localFontManager.loadFont(family, fontUrl);
|
|
20190
|
-
} catch (
|
|
20581
|
+
} catch (_err) {
|
|
20191
20582
|
fontUrl = GetFontAssetUrl(family);
|
|
20192
20583
|
try {
|
|
20193
20584
|
await localFontManager.loadFont(family, fontUrl);
|
|
@@ -20229,16 +20620,58 @@ async function registerLottieFont(family) {
|
|
|
20229
20620
|
if (registered) registeredLottieFonts.add(family);
|
|
20230
20621
|
else console.warn(`[LottieRenderer] All font registration attempts failed for "${family}". Text using this font will not render.`);
|
|
20231
20622
|
}
|
|
20623
|
+
var HeadlessLottieCanvas = class {
|
|
20624
|
+
width;
|
|
20625
|
+
height;
|
|
20626
|
+
style = {};
|
|
20627
|
+
constructor(width, height) {
|
|
20628
|
+
this.width = width;
|
|
20629
|
+
this.height = height;
|
|
20630
|
+
}
|
|
20631
|
+
addEventListener() {}
|
|
20632
|
+
removeEventListener() {}
|
|
20633
|
+
getContext(contextId) {
|
|
20634
|
+
if (contextId === "2d") return {
|
|
20635
|
+
fillRect() {},
|
|
20636
|
+
clearRect() {},
|
|
20637
|
+
putImageData() {},
|
|
20638
|
+
createImageData(width, height) {
|
|
20639
|
+
return {
|
|
20640
|
+
width,
|
|
20641
|
+
height,
|
|
20642
|
+
data: new Uint8ClampedArray(width * height * 4)
|
|
20643
|
+
};
|
|
20644
|
+
},
|
|
20645
|
+
getImageData(_sx, _sy, sw, sh) {
|
|
20646
|
+
return {
|
|
20647
|
+
width: sw,
|
|
20648
|
+
height: sh,
|
|
20649
|
+
data: new Uint8ClampedArray(sw * sh * 4)
|
|
20650
|
+
};
|
|
20651
|
+
}
|
|
20652
|
+
};
|
|
20653
|
+
return null;
|
|
20654
|
+
}
|
|
20655
|
+
getBoundingClientRect() {
|
|
20656
|
+
return {
|
|
20657
|
+
left: 0,
|
|
20658
|
+
top: 0,
|
|
20659
|
+
width: this.width,
|
|
20660
|
+
height: this.height
|
|
20661
|
+
};
|
|
20662
|
+
}
|
|
20663
|
+
};
|
|
20232
20664
|
var LottieInstance = class {
|
|
20233
20665
|
dotLottie;
|
|
20234
20666
|
canvas;
|
|
20235
20667
|
loadPromise;
|
|
20236
20668
|
_width;
|
|
20237
20669
|
_height;
|
|
20670
|
+
uploadBuffer = null;
|
|
20238
20671
|
constructor(src, width, height, data) {
|
|
20239
20672
|
this._width = width;
|
|
20240
20673
|
this._height = height;
|
|
20241
|
-
this.canvas = new OffscreenCanvas(width, height);
|
|
20674
|
+
this.canvas = typeof window !== "undefined" && typeof document !== "undefined" && typeof document.createElement === "function" && typeof document.getElementById === "function" ? new OffscreenCanvas(width, height) : new HeadlessLottieCanvas(width, height);
|
|
20242
20675
|
this.dotLottie = new DotLottie({
|
|
20243
20676
|
canvas: this.canvas,
|
|
20244
20677
|
...data ? { data } : { src },
|
|
@@ -20281,6 +20714,29 @@ var LottieInstance = class {
|
|
|
20281
20714
|
async ready() {
|
|
20282
20715
|
await this.loadPromise;
|
|
20283
20716
|
}
|
|
20717
|
+
renderPixels(frame, fit) {
|
|
20718
|
+
const loopedFrame = Math.floor(frame) % Math.max(1, this.totalFrames);
|
|
20719
|
+
this.dotLottie.setLayout({
|
|
20720
|
+
align: [.5, .5],
|
|
20721
|
+
fit: fit || "contain"
|
|
20722
|
+
});
|
|
20723
|
+
this.dotLottie.resize();
|
|
20724
|
+
this.dotLottie.setFrame(loopedFrame);
|
|
20725
|
+
try {
|
|
20726
|
+
this.dotLottie.render?.();
|
|
20727
|
+
} catch (_) {}
|
|
20728
|
+
const core = this.dotLottie._dotLottieCore;
|
|
20729
|
+
if (core && typeof core.get_pixel_buffer === "function") {
|
|
20730
|
+
const buf = core.get_pixel_buffer();
|
|
20731
|
+
const expectedLength = this._width * this._height * 4;
|
|
20732
|
+
if (buf && buf.byteLength === expectedLength) {
|
|
20733
|
+
if (!this.uploadBuffer || this.uploadBuffer.byteLength !== expectedLength) this.uploadBuffer = new Uint8Array(expectedLength);
|
|
20734
|
+
this.uploadBuffer.set(buf);
|
|
20735
|
+
return premultiplyPixels(this.uploadBuffer);
|
|
20736
|
+
}
|
|
20737
|
+
}
|
|
20738
|
+
return null;
|
|
20739
|
+
}
|
|
20284
20740
|
renderFrame(frame, fit) {
|
|
20285
20741
|
const loopedFrame = Math.floor(frame) % Math.max(1, this.totalFrames);
|
|
20286
20742
|
this.dotLottie.setLayout({
|
|
@@ -20298,6 +20754,7 @@ var LottieInstance = class {
|
|
|
20298
20754
|
try {
|
|
20299
20755
|
this.dotLottie.destroy();
|
|
20300
20756
|
} catch (_) {}
|
|
20757
|
+
this.uploadBuffer = null;
|
|
20301
20758
|
}
|
|
20302
20759
|
};
|
|
20303
20760
|
let wasmConfigured = false;
|
|
@@ -20329,13 +20786,7 @@ function polyfillHeadlessDOM() {
|
|
|
20329
20786
|
async function ensureWasmConfigured() {
|
|
20330
20787
|
if (wasmConfigured) return;
|
|
20331
20788
|
if (!(typeof window !== "undefined" && typeof document !== "undefined" && typeof document.createElement === "function" && typeof document.getElementById === "function")) {
|
|
20332
|
-
if (typeof globalThis.OffscreenCanvas === "undefined")
|
|
20333
|
-
const { Canvas: Canvas$1 } = await import(
|
|
20334
|
-
/* webpackIgnore: true */
|
|
20335
|
-
"skia-canvas"
|
|
20336
|
-
);
|
|
20337
|
-
globalThis.OffscreenCanvas = Canvas$1;
|
|
20338
|
-
} catch {}
|
|
20789
|
+
if (typeof globalThis.OffscreenCanvas === "undefined") globalThis.OffscreenCanvas = HeadlessLottieCanvas;
|
|
20339
20790
|
polyfillHeadlessDOM();
|
|
20340
20791
|
if (typeof globalThis.Response === "function") try {
|
|
20341
20792
|
Object.defineProperty(globalThis.Response, Symbol.hasInstance, {
|
|
@@ -20418,7 +20869,7 @@ var LottieLoader = class {
|
|
|
20418
20869
|
try {
|
|
20419
20870
|
const fs$1 = await import(
|
|
20420
20871
|
/* webpackIgnore: true */
|
|
20421
|
-
"fs/promises"
|
|
20872
|
+
"node:fs/promises"
|
|
20422
20873
|
);
|
|
20423
20874
|
if (filePath.endsWith(".json")) {
|
|
20424
20875
|
const jsonContent = await fs$1.readFile(filePath, "utf-8");
|
|
@@ -20467,37 +20918,27 @@ async function drawLottieNode(ctx, pass, props) {
|
|
|
20467
20918
|
const cacheKey = `${props.src}@@${loopedFrame}@@${resolvedWidth}x${resolvedHeight}@@${fitMode}`;
|
|
20468
20919
|
let tex = textureCache.acquire(cacheKey);
|
|
20469
20920
|
if (!tex) {
|
|
20470
|
-
const canvas = instance.renderFrame(loopedFrame, fitMode);
|
|
20471
20921
|
tex = ctx.device.createTexture({
|
|
20472
20922
|
size: [instance.width, instance.height],
|
|
20473
20923
|
format: "rgba8unorm",
|
|
20474
20924
|
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT
|
|
20475
20925
|
});
|
|
20476
|
-
|
|
20477
|
-
|
|
20478
|
-
|
|
20479
|
-
|
|
20480
|
-
|
|
20481
|
-
|
|
20482
|
-
|
|
20483
|
-
|
|
20484
|
-
|
|
20485
|
-
|
|
20486
|
-
|
|
20487
|
-
|
|
20488
|
-
|
|
20489
|
-
|
|
20490
|
-
|
|
20491
|
-
|
|
20492
|
-
const flipped = new Uint8Array(pixels.length);
|
|
20493
|
-
const bytesPerRow = instance.width * 4;
|
|
20494
|
-
for (let y = 0; y < instance.height; y++) {
|
|
20495
|
-
const srcOffset = y * bytesPerRow;
|
|
20496
|
-
const dstOffset = (instance.height - 1 - y) * bytesPerRow;
|
|
20497
|
-
flipped.set(pixels.subarray(srcOffset, srcOffset + bytesPerRow), dstOffset);
|
|
20498
|
-
}
|
|
20499
|
-
ctx.device.queue.writeTexture({ texture: tex }, flipped, { bytesPerRow }, [instance.width, instance.height]);
|
|
20500
|
-
} else console.error("[LottieRenderer] Could not acquire 2D or WebGL context to read pixels.");
|
|
20926
|
+
const pixelBuffer = instance.renderPixels(loopedFrame, fitMode);
|
|
20927
|
+
if (pixelBuffer) ctx.device.queue.writeTexture({ texture: tex }, pixelBuffer, { bytesPerRow: instance.width * 4 }, [instance.width, instance.height]);
|
|
20928
|
+
else {
|
|
20929
|
+
const canvas = instance.renderFrame(loopedFrame, fitMode);
|
|
20930
|
+
try {
|
|
20931
|
+
ctx.device.queue.copyExternalImageToTexture({ source: canvas }, {
|
|
20932
|
+
texture: tex,
|
|
20933
|
+
premultipliedAlpha: true
|
|
20934
|
+
}, [instance.width, instance.height]);
|
|
20935
|
+
} catch {
|
|
20936
|
+
const c2d = canvas.getContext("2d");
|
|
20937
|
+
if (c2d) {
|
|
20938
|
+
const imgData = c2d.getImageData(0, 0, instance.width, instance.height);
|
|
20939
|
+
const fallbackBuffer = premultiplyPixels(new Uint8Array(imgData.data.buffer, imgData.data.byteOffset, imgData.data.byteLength));
|
|
20940
|
+
ctx.device.queue.writeTexture({ texture: tex }, fallbackBuffer, { bytesPerRow: instance.width * 4 }, [instance.width, instance.height]);
|
|
20941
|
+
}
|
|
20501
20942
|
}
|
|
20502
20943
|
}
|
|
20503
20944
|
textureCache.set(cacheKey, tex, ctx.device);
|
|
@@ -20823,8 +21264,40 @@ var MediaDecoderCache = class {
|
|
|
20823
21264
|
videoDecoders = /* @__PURE__ */ new Map();
|
|
20824
21265
|
audioDecoders = /* @__PURE__ */ new Map();
|
|
20825
21266
|
lastAccessed = /* @__PURE__ */ new Map();
|
|
21267
|
+
/** Rendered frames so far (see `tick`), and the frame each decoder was last used on. */
|
|
21268
|
+
ticks = 0;
|
|
21269
|
+
lastTick = /* @__PURE__ */ new Map();
|
|
20826
21270
|
touchKey(key) {
|
|
20827
21271
|
this.lastAccessed.set(key, Date.now());
|
|
21272
|
+
this.lastTick.set(key, this.ticks);
|
|
21273
|
+
}
|
|
21274
|
+
dropKey(key) {
|
|
21275
|
+
const videoDecoder = this.videoDecoders.get(key);
|
|
21276
|
+
if (videoDecoder) {
|
|
21277
|
+
try {
|
|
21278
|
+
videoDecoder.destroy();
|
|
21279
|
+
} catch (_) {}
|
|
21280
|
+
this.videoDecoders.delete(key);
|
|
21281
|
+
}
|
|
21282
|
+
const audioDecoder = this.audioDecoders.get(key);
|
|
21283
|
+
if (audioDecoder) {
|
|
21284
|
+
try {
|
|
21285
|
+
audioDecoder.destroy();
|
|
21286
|
+
} catch (_) {}
|
|
21287
|
+
this.audioDecoders.delete(key);
|
|
21288
|
+
}
|
|
21289
|
+
this.lastAccessed.delete(key);
|
|
21290
|
+
this.lastTick.delete(key);
|
|
21291
|
+
}
|
|
21292
|
+
/**
|
|
21293
|
+
* Called once per rendered frame by headless renders. A decoder holds a
|
|
21294
|
+
* full-frame RGBA buffer and native decoder state, so one that no layer has
|
|
21295
|
+
* drawn for `idleFrames` frames (its clip ended, or a still render used a
|
|
21296
|
+
* one-off id) is released instead of waiting for the wall-clock prune.
|
|
21297
|
+
*/
|
|
21298
|
+
tick(idleFrames = 120) {
|
|
21299
|
+
this.ticks++;
|
|
21300
|
+
for (const [key, last] of this.lastTick) if (this.ticks - last > idleFrames) this.dropKey(key);
|
|
20828
21301
|
}
|
|
20829
21302
|
destroy() {
|
|
20830
21303
|
for (const decoder of this.videoDecoders.values()) try {
|
|
@@ -20836,22 +21309,15 @@ var MediaDecoderCache = class {
|
|
|
20836
21309
|
} catch (_) {}
|
|
20837
21310
|
this.audioDecoders.clear();
|
|
20838
21311
|
this.lastAccessed.clear();
|
|
21312
|
+
this.lastTick.clear();
|
|
21313
|
+
}
|
|
21314
|
+
/** Decoders currently open (for tests and diagnostics). */
|
|
21315
|
+
get size() {
|
|
21316
|
+
return this.videoDecoders.size + this.audioDecoders.size;
|
|
20839
21317
|
}
|
|
20840
21318
|
prune(maxAgeMs = 3e5) {
|
|
20841
21319
|
const now = Date.now();
|
|
20842
|
-
for (const [key, lastTime] of this.lastAccessed.entries()) if (now - lastTime > maxAgeMs)
|
|
20843
|
-
const videoDecoder = this.videoDecoders.get(key);
|
|
20844
|
-
if (videoDecoder) {
|
|
20845
|
-
videoDecoder.destroy();
|
|
20846
|
-
this.videoDecoders.delete(key);
|
|
20847
|
-
}
|
|
20848
|
-
const audioDecoder = this.audioDecoders.get(key);
|
|
20849
|
-
if (audioDecoder) {
|
|
20850
|
-
audioDecoder.destroy();
|
|
20851
|
-
this.audioDecoders.delete(key);
|
|
20852
|
-
}
|
|
20853
|
-
this.lastAccessed.delete(key);
|
|
20854
|
-
}
|
|
21320
|
+
for (const [key, lastTime] of this.lastAccessed.entries()) if (now - lastTime > maxAgeMs) this.dropKey(key);
|
|
20855
21321
|
}
|
|
20856
21322
|
getVideo(url, isHeadless, nodeId) {
|
|
20857
21323
|
const key = `${url}-${isHeadless}-${nodeId ?? "default"}`;
|
|
@@ -20876,16 +21342,8 @@ var MediaDecoderCache = class {
|
|
|
20876
21342
|
return decoder;
|
|
20877
21343
|
}
|
|
20878
21344
|
clearNode(nodeId) {
|
|
20879
|
-
|
|
20880
|
-
|
|
20881
|
-
this.videoDecoders.delete(key);
|
|
20882
|
-
this.lastAccessed.delete(key);
|
|
20883
|
-
}
|
|
20884
|
-
for (const [key, decoder] of this.audioDecoders) if (key.includes(nodeId)) {
|
|
20885
|
-
decoder.destroy();
|
|
20886
|
-
this.audioDecoders.delete(key);
|
|
20887
|
-
this.lastAccessed.delete(key);
|
|
20888
|
-
}
|
|
21345
|
+
const keys = [...this.videoDecoders.keys(), ...this.audioDecoders.keys()];
|
|
21346
|
+
for (const key of keys) if (key.includes(nodeId)) this.dropKey(key);
|
|
20889
21347
|
}
|
|
20890
21348
|
};
|
|
20891
21349
|
const mediaDecoderCache = new MediaDecoderCache();
|
|
@@ -26523,6 +26981,7 @@ struct VertexInput {
|
|
|
26523
26981
|
@location(2) normal : vec3<f32>,
|
|
26524
26982
|
@location(3) joints : vec4<f32>,
|
|
26525
26983
|
@location(4) weights : vec4<f32>,
|
|
26984
|
+
@location(5) color : vec4<f32>, // vertex color (display / sRGB), white when absent
|
|
26526
26985
|
};
|
|
26527
26986
|
|
|
26528
26987
|
struct VertexOutput {
|
|
@@ -26531,6 +26990,7 @@ struct VertexOutput {
|
|
|
26531
26990
|
@location(1) worldPos : vec3<f32>,
|
|
26532
26991
|
@location(2) worldNormal : vec3<f32>,
|
|
26533
26992
|
@location(3) linearDepth : f32,
|
|
26993
|
+
@location(4) color : vec4<f32>,
|
|
26534
26994
|
};
|
|
26535
26995
|
|
|
26536
26996
|
@vertex
|
|
@@ -26575,6 +27035,7 @@ fn vs_main(in: VertexInput) -> VertexOutput {
|
|
|
26575
27035
|
|
|
26576
27036
|
out.clipPos = cam.viewProjMatrix * worldPos4;
|
|
26577
27037
|
out.uv = in.uv;
|
|
27038
|
+
out.color = in.color;
|
|
26578
27039
|
|
|
26579
27040
|
out.linearDepth = dot(worldPos4.xyz - cam.cameraPos.xyz, cam.cameraDir.xyz);
|
|
26580
27041
|
return out;
|
|
@@ -26719,12 +27180,12 @@ fn shadeMesh(in: VertexOutput, isFront: bool) -> MeshFragment {
|
|
|
26719
27180
|
if (texColor.a < model.shade.a) {
|
|
26720
27181
|
discard;
|
|
26721
27182
|
}
|
|
26722
|
-
let alpha = texColor.a * model.params.x * model.colorTint.a * matteCoverage(in.clipPos.xy);
|
|
27183
|
+
let alpha = texColor.a * in.color.a * model.params.x * model.colorTint.a * matteCoverage(in.clipPos.xy);
|
|
26723
27184
|
if (alpha <= 0.001) {
|
|
26724
27185
|
discard;
|
|
26725
27186
|
}
|
|
26726
27187
|
|
|
26727
|
-
let baseRgb = texColor.rgb * model.colorTint.rgb;
|
|
27188
|
+
let baseRgb = texColor.rgb * in.color.rgb * model.colorTint.rgb;
|
|
26728
27189
|
var out: MeshFragment;
|
|
26729
27190
|
if (model.materialParams.w > 1.5) {
|
|
26730
27191
|
var toon = baseRgb;
|
|
@@ -27416,6 +27877,11 @@ var Mesh3DPipeline = class {
|
|
|
27416
27877
|
/** (0.5, 0.5, 1): an unperturbed tangent-space normal. */
|
|
27417
27878
|
flatNormalTexture;
|
|
27418
27879
|
defaultSkinningBuffer;
|
|
27880
|
+
/** White vertex colors for meshes without any, grown to the largest mesh. */
|
|
27881
|
+
whiteColorBuffer = null;
|
|
27882
|
+
whiteColorCapacity = 0;
|
|
27883
|
+
/** Outgrown white buffers: earlier draws this frame may still read them. */
|
|
27884
|
+
retiredColorBuffers = [];
|
|
27419
27885
|
modelData = new Float32Array(52 + SURFACE_PARAMS_FLOATS);
|
|
27420
27886
|
constructor(device, format, depthFormat = "depth24plus", cameraLayout, lightsLayout, sampleCount = 1) {
|
|
27421
27887
|
this.device = device;
|
|
@@ -27507,6 +27973,13 @@ var Mesh3DPipeline = class {
|
|
|
27507
27973
|
format: "float32x4"
|
|
27508
27974
|
}
|
|
27509
27975
|
]
|
|
27976
|
+
}, {
|
|
27977
|
+
arrayStride: 16,
|
|
27978
|
+
attributes: [{
|
|
27979
|
+
shaderLocation: 5,
|
|
27980
|
+
offset: 0,
|
|
27981
|
+
format: "float32x4"
|
|
27982
|
+
}]
|
|
27510
27983
|
}]
|
|
27511
27984
|
};
|
|
27512
27985
|
const base = {
|
|
@@ -27701,6 +28174,7 @@ var Mesh3DPipeline = class {
|
|
|
27701
28174
|
pass.setBindGroup(2, textureBindGroup);
|
|
27702
28175
|
pass.setBindGroup(3, lightsBindGroup);
|
|
27703
28176
|
pass.setVertexBuffer(0, meshBuffers.vertexBuffer);
|
|
28177
|
+
pass.setVertexBuffer(1, this.colorsFor(meshBuffers));
|
|
27704
28178
|
pass.setIndexBuffer(meshBuffers.indexBuffer, meshBuffers.indexFormat);
|
|
27705
28179
|
pass.drawIndexed(meshBuffers.indexCount);
|
|
27706
28180
|
}
|
|
@@ -27710,10 +28184,31 @@ var Mesh3DPipeline = class {
|
|
|
27710
28184
|
pass.setBindGroup(0, lightCameraBindGroup);
|
|
27711
28185
|
pass.setBindGroup(1, this.packModel(data));
|
|
27712
28186
|
pass.setVertexBuffer(0, meshBuffers.vertexBuffer);
|
|
28187
|
+
pass.setVertexBuffer(1, this.colorsFor(meshBuffers));
|
|
27713
28188
|
pass.setIndexBuffer(meshBuffers.indexBuffer, meshBuffers.indexFormat);
|
|
27714
28189
|
pass.drawIndexed(meshBuffers.indexCount);
|
|
27715
28190
|
}
|
|
28191
|
+
colorsFor(meshBuffers) {
|
|
28192
|
+
if (meshBuffers.colorBuffer) return meshBuffers.colorBuffer;
|
|
28193
|
+
const needed = Math.max(1, Math.ceil(meshBuffers.vertexBuffer.size / 64));
|
|
28194
|
+
if (!this.whiteColorBuffer || this.whiteColorCapacity < needed) {
|
|
28195
|
+
if (this.whiteColorBuffer) this.retiredColorBuffers.push(this.whiteColorBuffer);
|
|
28196
|
+
const capacity = 2 ** Math.ceil(Math.log2(needed));
|
|
28197
|
+
this.whiteColorBuffer = this.device.createBuffer({
|
|
28198
|
+
label: "Mesh3DWhiteVertexColors",
|
|
28199
|
+
size: capacity * 16,
|
|
28200
|
+
usage: GPUBufferUsage.VERTEX,
|
|
28201
|
+
mappedAtCreation: true
|
|
28202
|
+
});
|
|
28203
|
+
new Float32Array(this.whiteColorBuffer.getMappedRange()).fill(1);
|
|
28204
|
+
this.whiteColorBuffer.unmap();
|
|
28205
|
+
this.whiteColorCapacity = capacity;
|
|
28206
|
+
}
|
|
28207
|
+
return this.whiteColorBuffer;
|
|
28208
|
+
}
|
|
27716
28209
|
destroy() {
|
|
28210
|
+
this.whiteColorBuffer?.destroy();
|
|
28211
|
+
for (const buffer of this.retiredColorBuffers) buffer.destroy();
|
|
27717
28212
|
this.modelPool.destroy();
|
|
27718
28213
|
this.skinningPool.destroy();
|
|
27719
28214
|
this.defaultWhiteTexture.destroy();
|
|
@@ -30260,11 +30755,23 @@ var Renderer3D = class {
|
|
|
30260
30755
|
if (is32Bit) new Uint32Array(indexBuffer.getMappedRange()).set(mesh.indices);
|
|
30261
30756
|
else new Uint16Array(indexBuffer.getMappedRange()).set(mesh.indices);
|
|
30262
30757
|
indexBuffer.unmap();
|
|
30758
|
+
let colorBuffer;
|
|
30759
|
+
if (mesh.colors && mesh.colors.length >= numVerts * 4) {
|
|
30760
|
+
colorBuffer = this.device.createBuffer({
|
|
30761
|
+
label: "Mesh3DVertexColors",
|
|
30762
|
+
size: Math.max(16, numVerts * 16),
|
|
30763
|
+
usage: GPUBufferUsage.VERTEX,
|
|
30764
|
+
mappedAtCreation: true
|
|
30765
|
+
});
|
|
30766
|
+
new Float32Array(colorBuffer.getMappedRange()).set(mesh.colors.subarray(0, numVerts * 4));
|
|
30767
|
+
colorBuffer.unmap();
|
|
30768
|
+
}
|
|
30263
30769
|
cached$1 = {
|
|
30264
30770
|
vertexBuffer,
|
|
30265
30771
|
indexBuffer,
|
|
30266
30772
|
indexCount: mesh.indices.length,
|
|
30267
|
-
indexFormat: is32Bit ? "uint32" : "uint16"
|
|
30773
|
+
indexFormat: is32Bit ? "uint32" : "uint16",
|
|
30774
|
+
colorBuffer
|
|
30268
30775
|
};
|
|
30269
30776
|
this.meshBuffersCache.set(mesh, cached$1);
|
|
30270
30777
|
return cached$1;
|
|
@@ -30282,7 +30789,8 @@ var Renderer3D = class {
|
|
|
30282
30789
|
}),
|
|
30283
30790
|
indexBuffer: meshBuffers.indexBuffer,
|
|
30284
30791
|
indexCount: meshBuffers.indexCount,
|
|
30285
|
-
indexFormat: meshBuffers.indexFormat
|
|
30792
|
+
indexFormat: meshBuffers.indexFormat,
|
|
30793
|
+
colorBuffer: meshBuffers.colorBuffer
|
|
30286
30794
|
};
|
|
30287
30795
|
}
|
|
30288
30796
|
drawMesh3D(pass, cameraBindGroup, lightsBindGroup, meshOrBuffers, texture, opts = {}, useMrt = false) {
|
|
@@ -30292,13 +30800,15 @@ var Renderer3D = class {
|
|
|
30292
30800
|
1,
|
|
30293
30801
|
1,
|
|
30294
30802
|
1
|
|
30295
|
-
], twoSided = true, wireframe = false, isSkinned = false, skinningMatrices, material = "lit", shadeColor, alphaCutoff, textureWrap = "clamp", shininess = 32, roughness, specularIntensity = .5, ambientIntensity = 1, metallic } = opts;
|
|
30803
|
+
], twoSided = true, wireframe = false, isSkinned = false, skinningMatrices, material = "lit", shadeColor, alphaCutoff, textureWrap = "clamp", textureFilter = "linear", shininess = 32, roughness, specularIntensity = .5, ambientIntensity = 1, metallic } = opts;
|
|
30296
30804
|
const m = this.meshModelMatrix(opts);
|
|
30297
30805
|
const normalMatrix = opts.normalMatrix ?? Light3D.computeNormalMatrix(m);
|
|
30298
30806
|
const address = ADDRESS_MODES[textureWrap];
|
|
30299
30807
|
const sampler = this.samplerCache.getSampler(this.device, {
|
|
30300
30808
|
addressModeU: address,
|
|
30301
|
-
addressModeV: address
|
|
30809
|
+
addressModeV: address,
|
|
30810
|
+
magFilter: textureFilter,
|
|
30811
|
+
...textureFilter === "nearest" && { maxAnisotropy: 1 }
|
|
30302
30812
|
});
|
|
30303
30813
|
const glassy = material === "glass" || material === "acrylic" || material === "frosted";
|
|
30304
30814
|
const glass = glassy ? {
|
|
@@ -30397,7 +30907,14 @@ var Renderer3D = class {
|
|
|
30397
30907
|
"sharp"
|
|
30398
30908
|
)).default;
|
|
30399
30909
|
const source = Buffer.from(encoded instanceof Uint8Array ? encoded : new Uint8Array(encoded));
|
|
30400
|
-
|
|
30910
|
+
let width;
|
|
30911
|
+
let height;
|
|
30912
|
+
try {
|
|
30913
|
+
({width, height} = await sharp(source).metadata());
|
|
30914
|
+
} catch (err) {
|
|
30915
|
+
console.warn(`[Renderer3D] Could not decode texture "${label}"; drawing without it:`, err instanceof Error ? err.message : err);
|
|
30916
|
+
return;
|
|
30917
|
+
}
|
|
30401
30918
|
if (!width || !height) return void 0;
|
|
30402
30919
|
const mipLevelCount = Math.floor(Math.log2(Math.max(width, height))) + 1;
|
|
30403
30920
|
const texture = this.device.createTexture({
|
|
@@ -31135,4 +31652,4 @@ if (typeof globalThis !== "undefined") globalThis.__GATEWAI_DELAYS__ = globalThi
|
|
|
31135
31652
|
|
|
31136
31653
|
//#endregion
|
|
31137
31654
|
export { resolveTextSpans as $, TextSelectorSchema as $t, ensureDevice as A, sampleAudioSpectrumScale as An, GlyphAnchorGroupingSchema as At, loadModel3D as B, SignalRegistry as Bt, drawGifNode as C, evaluateTypewriterState as Cn, AudioSpectrumTypographySchema as Ct, drawSignalNode as D, getScramblePool as Dn, EnhancedTextNodeSchema as Dt, drawParagraphNode as E, generatePathSegments as En, DynamicStrokePropertiesSchema as Et, getRenderer2D as F, ParticleDissolveConfigSchema as Ft, parseColor as G, TextAnimatorBuilder as Gt, lutStore as H, Spatial3DPropertiesSchema as Ht, initHeadlessWebGPU as I, SelectorBasedOnSchema as It, parseOBJ as J, TextPathBuilder as Jt, parseFBX as K, TextCursorConfigSchema as Kt, inputStore as L, SelectorModeSchema as Lt, generateExtrudedTextGeometry as M, samplePathChainGeometry as Mn, KineticMarqueeConfigSchema as Mt, getHeadlessFontPath as N, signalRegistry as Nn, LineSpacingDynamicsSchema as Nt, drawSvgNode as O, getShuffledOrder as On, ExpressionSelectorSchema as Ot, getPrimitiveGeometry as P, waveToCubicBeziers as Pn, LongShadowConfigSchema as Pt, registerHeadlessFont as Q, TextScrambleConfigSchema as Qt, jointMatrices as R, SelectorShapeSchema as Rt, drawCaptionNode as S, evaluateSpringProgress as Sn, AudioSignalComputePipeline as St, drawLottieNode as T, generateArcLengthLUT as Tn, CubicBezierSegmentSchema as Tt, measureMaxCaptionHeight as U, SpringPhysicsSchema as Ut, loadOBJ as V, SkewPropertiesSchema as Vt, mediaDecoderCache as W, TextAnchorConfigSchema as Wt, poseNodes as X, TextPathSourceSchema as Xt, parseSvgPathToSegments as Y, TextPathOptionsSchema as Yt, probeVideoSize as Z, TextRangeSelectorSchema as Zt, buildShapeSvgPath as _, evaluateCubicBezierDerivative as _n, AdvancedTextAnimatorBuilder as _t, GlassPipeline as a, Vec2Schema as an, resolveMediaSourceUrl as at, colorMatrixWgsl as b, evaluateSelectorCombination as bn, AnchorPresetSchema as bt, Renderer2D as c, buildPathLUT as cn, LIGHT_UNIFORM_SIZE_BYTES as ct, ScreenSpaceRelightPipeline as d, computeLineLeadingDisplacement as dn, Matrix4Math as dt, TextSignalSelectorSchema as en, shaderStore as et, ShadowPipeline as f, computeRangeSelectorWeight as fn, Path3D as ft, WebGPUAudioProcessor as g, evaluateCubicBezier as gn, AETextAnimatorSchema as gt, SlugPipeline as h, ellipseToCubicBeziers as hn, resolveEnvironment as ht, EffectPipeline as i, VariableFontAxesSchema as in, GetFontAssetUrl as it, fitSpansToText as j, sampleCurveGeometry as jn, GlyphAnchorPointSchema as jt, drawVideoNode as k, parseSvgPathToCubicBeziers as kn, ExtendedGlyphPropertiesSchema as kt, Renderer3D as l, buildWGSLSignalFn as ln, Light3D as lt, SlugGeometry as m, distanceToCurveParameter as mn, parseColorToRgb as mt, AudioLatentTrackCache as n, TypewriterAnimatorSchema as nn, srtLoader as nt, MotionBlurPipeline as o, VolumetricFormationConfigSchema as on, Camera3D as ot, SlugFontCache as p, deterministicWigglyNoise as pn, Vector3Math as pt, parseMTL as q, TextCursorStyleSchema as qt, AudioMeshDeformPipeline as r, TypingCadenceSchema as rn, textureCache as rt, NodeSurfaceProvider as s, VolumetricFormationModeSchema as sn, ENVIRONMENT_PRESETS as st, AudioLatentEngine as t, TextWigglySelectorSchema as tn, spansText as tt, SSAOPipeline as u, computeAnchorPivotOffset as un, MAX_LIGHTS as ut, clearAllVideoCache as v, evaluateExpressionSelector as vn, AdvancedTextAnimatorSchema as vt, drawImageNode as w, evaluateVolumetricFormation as wn, ChromaticGlitchConfigSchema as wt, describeGpu as x, evaluateSkewMatrix as xn, AnimatableGlyphPropsSchema as xt, clearHeadlessFonts as y, evaluateMarqueeOffset as yn, AnchorGroupingSchema as yt, loadFBX as z, SelectorUnitsSchema as zt };
|
|
31138
|
-
//# sourceMappingURL=dist-
|
|
31655
|
+
//# sourceMappingURL=dist-DRB6Vg5u.mjs.map
|