@holoscript/core 6.0.3 → 6.1.1
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 +40 -40
- package/bin/holoscript.cjs +3 -0
- package/dist/GLTFPipeline-SFIORFJU.js +16 -0
- package/dist/GLTFPipeline-VNNDPONV.cjs +37 -0
- package/dist/{HoloScriptPlusParser-BZR5DELQ.js → HoloScriptPlusParser-7FBIFUFC.js} +6 -5
- package/dist/{HoloScriptPlusParser-SBMYDNXO.cjs → HoloScriptPlusParser-A2W7OCN2.cjs} +17 -16
- package/dist/HoloScriptPlusParser-F2Z56FJR.js +10 -0
- package/dist/HoloScriptPlusParser-WKHRIH6T.cjs +27 -0
- package/dist/{USDZExporter-MHEHXZB4.cjs → USDZExporter-3FWJ25JI.cjs} +344 -16
- package/dist/{USDZExporter-YA55ZS35.js → USDZExporter-D2HQLQYK.js} +344 -16
- package/dist/agents/index.cjs +19 -0
- package/dist/agents/index.d.ts +100 -0
- package/dist/agents/index.js +17 -0
- package/dist/chunk-22W6NGYN.js +725 -0
- package/dist/{chunk-XSUZMPVQ.cjs → chunk-27MPVJVZ.cjs} +64 -28
- package/dist/{chunk-W76ETJTI.js → chunk-2A7FURVO.js} +44 -8
- package/dist/{chunk-C7BNX4XJ.js → chunk-2B7WVIDZ.js} +11 -8
- package/dist/chunk-2BSMSUCE.js +1941 -0
- package/dist/chunk-2HLIGOKU.cjs +5546 -0
- package/dist/{chunk-GC3YU46J.js → chunk-2M2DPHKB.js} +4 -4
- package/dist/{chunk-A6GO3DPZ.cjs → chunk-2YSNA2F2.cjs} +321 -49
- package/dist/{chunk-ZL6VJ6SN.js → chunk-33XCOVE3.js} +5 -5
- package/dist/chunk-3FOKASKO.cjs +348 -0
- package/dist/{chunk-XSF76QRU.js → chunk-4C57PR42.js} +3 -3
- package/dist/chunk-4N4SXXWF.js +769 -0
- package/dist/chunk-4PDEPEQW.cjs +41 -0
- package/dist/chunk-4R6SBXDB.js +45 -0
- package/dist/chunk-4SY77NA4.cjs +413 -0
- package/dist/{chunk-74YCHHTE.js → chunk-4UUKPCY4.js} +670 -116
- package/dist/{chunk-VZNKJZTT.cjs → chunk-52ULD552.cjs} +178 -113
- package/dist/{chunk-65RFOWZI.js → chunk-5CKA5SXU.js} +441 -23
- package/dist/chunk-5FSKKMR7.cjs +2092 -0
- package/dist/{chunk-FF3Q7ADR.js → chunk-5JEU52DW.js} +5 -3
- package/dist/chunk-5N54COB6.cjs +327 -0
- package/dist/chunk-5S7RKFGR.cjs +446 -0
- package/dist/{chunk-EJA7G2C4.cjs → chunk-5WUNN6ZN.cjs} +2 -2
- package/dist/{chunk-ELLQPFAF.js → chunk-6ASWLXXN.js} +24 -10
- package/dist/chunk-6CTRH7HT.cjs +1985 -0
- package/dist/{chunk-ARNKA274.cjs → chunk-6EX3I5P4.cjs} +6 -6
- package/dist/chunk-6FGQNDR4.js +5731 -0
- package/dist/{chunk-X4YVN7H3.cjs → chunk-6KMK2UXT.cjs} +152 -56
- package/dist/{chunk-KUJRR4FJ.js → chunk-6L4VE27M.js} +36 -24
- package/dist/{chunk-RT7LJRSF.cjs → chunk-6S6TKNEA.cjs} +7357 -8312
- package/dist/chunk-77V4GTCA.js +596 -0
- package/dist/{chunk-XH7SE4HH.cjs → chunk-7AD52FRS.cjs} +19 -19
- package/dist/chunk-7OPW2KO7.cjs +639 -0
- package/dist/chunk-A6KINLZJ.js +236 -0
- package/dist/chunk-AIA52I7T.cjs +543 -0
- package/dist/chunk-AIX25INK.cjs +602 -0
- package/dist/{chunk-WN3YF33G.cjs → chunk-AO4I2Z7M.cjs} +15 -17
- package/dist/{chunk-TSWTWZ42.cjs → chunk-BPICAGBV.cjs} +376 -209
- package/dist/chunk-BRQJKJGT.cjs +20 -0
- package/dist/{chunk-3FXV5S25.cjs → chunk-C37BHB7N.cjs} +6 -3
- package/dist/{chunk-XNMEH2BI.js → chunk-CBJY23VL.js} +3 -3
- package/dist/chunk-CIWWXYHZ.cjs +50 -0
- package/dist/{chunk-NTMZSDXM.cjs → chunk-DIL6DDDN.cjs} +101 -45
- package/dist/chunk-DMXC24WV.js +404 -0
- package/dist/{chunk-SPDELRRV.cjs → chunk-DTGBOSEM.cjs} +12 -12
- package/dist/chunk-E7JP3BS3.cjs +61 -0
- package/dist/{chunk-HJJLD366.js → chunk-EKBKLK7P.js} +3 -3
- package/dist/chunk-EKYL4PE3.cjs +404 -0
- package/dist/{chunk-7N3JIJMT.js → chunk-EMMC5CSQ.js} +12 -8
- package/dist/{chunk-EUFLX2PI.js → chunk-EOBB6A4B.js} +7263 -8214
- package/dist/chunk-EOFNVCM6.js +325 -0
- package/dist/{chunk-NKRKT6V2.js → chunk-ER4GATIU.js} +11 -10
- package/dist/{chunk-N5FTS757.cjs → chunk-EXKSFGSX.cjs} +7 -148
- package/dist/{chunk-LK5IAVH6.cjs → chunk-EXUP2BUM.cjs} +27 -27
- package/dist/chunk-FDSGHPAK.js +130 -0
- package/dist/chunk-FEWUHQNM.cjs +144 -0
- package/dist/{chunk-5TOFIR6L.js → chunk-FGGJQRLZ.js} +5 -142
- package/dist/chunk-FHP6LXJS.js +415 -0
- package/dist/{chunk-7LCYLPW2.cjs → chunk-FJPADH77.cjs} +7 -4
- package/dist/{chunk-HBZYCASG.js → chunk-FXKTYFYA.js} +203 -36
- package/dist/chunk-GCMWDSBZ.cjs +729 -0
- package/dist/chunk-GJNCFRJU.cjs +125 -0
- package/dist/{chunk-I3XGTIHM.cjs → chunk-GMP623FG.cjs} +16 -13
- package/dist/{chunk-CNVM7J3M.js → chunk-GN3KDX4W.js} +149 -53
- package/dist/{chunk-WFJIDI2N.cjs → chunk-GQYKQSTC.cjs} +10 -10
- package/dist/chunk-GTU2Z2WW.cjs +262 -0
- package/dist/chunk-GZG4KA4E.js +529 -0
- package/dist/{chunk-DIEDKX5B.cjs → chunk-H3MGK7TX.cjs} +45 -33
- package/dist/{chunk-DN7UFU63.cjs → chunk-H5DZIHJP.cjs} +14 -17
- package/dist/{chunk-DH5G2JUA.cjs → chunk-HANL7K2Q.cjs} +66 -53
- package/dist/{chunk-EGIZHYJP.cjs → chunk-HCCQLAA4.cjs} +2 -13
- package/dist/{chunk-HHS6FMOU.cjs → chunk-HGVXZDSK.cjs} +178 -29
- package/dist/{chunk-LBPEZQAF.js → chunk-HRODTEEO.js} +30 -10
- package/dist/{chunk-ZGTGVSTZ.js → chunk-HYNTS5CP.js} +2 -2
- package/dist/{chunk-RE3OKSYF.cjs → chunk-HZCQO4JM.cjs} +6 -6
- package/dist/{chunk-NCUHGRTZ.js → chunk-I2O7TW6B.js} +12 -8
- package/dist/chunk-IHESJZSW.js +4490 -0
- package/dist/{chunk-7H5UNJZD.cjs → chunk-IRWWS72C.cjs} +64 -22
- package/dist/{chunk-EMO7HAKJ.cjs → chunk-IVIA7K75.cjs} +691 -133
- package/dist/chunk-IVY74O6C.js +247 -0
- package/dist/chunk-J32ZJZJH.js +554 -0
- package/dist/chunk-JD7CIZ33.js +1949 -0
- package/dist/{chunk-7X2IEJIE.cjs → chunk-JIYN45TV.cjs} +41 -42
- package/dist/{chunk-XDXZM3ZP.cjs → chunk-KHPCJRI3.cjs} +24 -21
- package/dist/chunk-KKFZ3B6J.cjs +557 -0
- package/dist/chunk-KPJL34Y7.cjs +186 -0
- package/dist/{chunk-7KPI4EKH.cjs → chunk-KQTPCYTG.cjs} +130 -50
- package/dist/{chunk-F5LVGHNT.js → chunk-KX76JUQY.js} +3 -3
- package/dist/chunk-L4LFINPO.js +623 -0
- package/dist/{chunk-2UX5LRAP.cjs → chunk-L63H3SQ4.cjs} +46 -32
- package/dist/{chunk-NLPSZT4C.js → chunk-LAOCQJ2M.js} +64 -8
- package/dist/chunk-LM2VU56C.cjs +37 -0
- package/dist/chunk-M56JSP4A.js +400 -0
- package/dist/chunk-MBOOR7X4.js +18 -0
- package/dist/{chunk-PQLGZKMC.cjs → chunk-MJVIRCUV.cjs} +31 -27
- package/dist/{chunk-AC4BSHFV.js → chunk-MSMIFMBL.js} +135 -18
- package/dist/{chunk-56IF4NTA.js → chunk-MXFUTKCO.js} +18 -18
- package/dist/{chunk-NRUB55IT.cjs → chunk-MXJ5SS53.cjs} +450 -32
- package/dist/chunk-MXWNGCRS.js +2176 -0
- package/dist/chunk-MYY4PMWX.cjs +264 -0
- package/dist/chunk-N75IH2HY.js +258 -0
- package/dist/chunk-NG77ATEW.cjs +778 -0
- package/dist/chunk-NW5G5FBQ.js +215 -0
- package/dist/{chunk-4FCZDTD5.js → chunk-OEFY723R.js} +29 -16
- package/dist/chunk-OEV5RMZO.cjs +3042 -0
- package/dist/{chunk-VSXOIUCF.cjs → chunk-OIQYIIO3.cjs} +7 -7
- package/dist/{chunk-32TWR3HE.js → chunk-OL46QLBJ.js} +3 -12
- package/dist/chunk-OPZ7XDDI.js +487 -0
- package/dist/chunk-OSYNRVOV.cjs +243 -0
- package/dist/{chunk-HUFNKFA6.js → chunk-P7YIA5LN.js} +93 -13
- package/dist/chunk-PGANYCAG.cjs +220 -0
- package/dist/{chunk-CO2VM2DK.js → chunk-QAVXL2IH.js} +62 -20
- package/dist/{chunk-BO2OKHIY.js → chunk-QCCBXSCE.js} +1640 -1311
- package/dist/{chunk-WYH4GVZ5.js → chunk-QPOQ7VPI.js} +16 -13
- package/dist/{chunk-V42NTCFH.js → chunk-QTPMJRGS.js} +2331 -17
- package/dist/{chunk-FEFHPUEM.cjs → chunk-QWNQ523V.cjs} +1705 -1382
- package/dist/{chunk-VK5AXKO3.js → chunk-R3IVNY6P.js} +166 -101
- package/dist/chunk-R75BSDH7.cjs +50 -0
- package/dist/{chunk-AIUXRS74.cjs → chunk-RMHC2VG7.cjs} +2358 -41
- package/dist/chunk-RUXGBKPS.cjs +4517 -0
- package/dist/{chunk-PLMYCCA4.js → chunk-S5IFXW6E.js} +113 -9
- package/dist/{chunk-27LWW5FU.cjs → chunk-S6YWHCKV.cjs} +5 -5
- package/dist/{chunk-SUCBB66F.js → chunk-SIEXCILW.js} +3 -3
- package/dist/{chunk-CLYEIWA5.cjs → chunk-SLU6UPJI.cjs} +5 -3
- package/dist/{chunk-H6WMMLQK.cjs → chunk-T5J7ZKP3.cjs} +454 -61
- package/dist/chunk-TDCNJHWO.js +341 -0
- package/dist/{chunk-IHOUYQIA.js → chunk-TG7O4WBC.js} +5 -5
- package/dist/chunk-THCK6ECW.js +2085 -0
- package/dist/chunk-TJHVSBP2.js +439 -0
- package/dist/chunk-TK74TR4H.js +5541 -0
- package/dist/{chunk-BU5ZAFMC.js → chunk-U4NBQJBY.js} +11 -13
- package/dist/{chunk-Y5MUAYTO.js → chunk-UD3DSHZM.js} +10 -13
- package/dist/chunk-UDMYRFDZ.js +3038 -0
- package/dist/{chunk-PPULB4GG.cjs → chunk-UOIGFQXC.cjs} +90 -68
- package/dist/{chunk-EPWRXL6S.js → chunk-URG2SJLK.js} +41 -42
- package/dist/{chunk-VJVCD5T5.cjs → chunk-V73NPPTM.cjs} +152 -35
- package/dist/chunk-VECQYRXL.js +474 -0
- package/dist/chunk-VEXPG4WN.js +1989 -0
- package/dist/{chunk-4SMUJFHL.js → chunk-VMLKSV26.js} +9 -6
- package/dist/{chunk-HKCVM6OK.cjs → chunk-VSK7W3O5.cjs} +47 -27
- package/dist/chunk-VVKVQZI6.js +55 -0
- package/dist/{chunk-KDB6BUMB.js → chunk-W55Z7QIX.js} +172 -25
- package/dist/{chunk-BY3B7ZYV.cjs → chunk-W6P2GBHH.cjs} +3 -3
- package/dist/{chunk-PBA6NXCT.cjs → chunk-W6RW64BB.cjs} +11 -8
- package/dist/chunk-WDT3JTGM.cjs +483 -0
- package/dist/{chunk-26TLYBFD.js → chunk-WKX5AMOY.js} +3 -3
- package/dist/chunk-WNNE3PO3.js +34 -0
- package/dist/chunk-WRLUBRKS.cjs +1943 -0
- package/dist/chunk-WRNEH5MO.js +3169 -0
- package/dist/{chunk-54FHG2MX.js → chunk-WRWEX5ZF.js} +7 -4
- package/dist/chunk-X3GJPRVK.cjs +3246 -0
- package/dist/{chunk-4KJ2R7VP.cjs → chunk-XFXGA5IW.cjs} +126 -22
- package/dist/{chunk-L3Z2HIWJ.cjs → chunk-XHIV6326.cjs} +4 -4
- package/dist/chunk-XJAN7CZG.cjs +432 -0
- package/dist/chunk-XL6INWKT.js +121 -0
- package/dist/{chunk-KYM4XRFG.js → chunk-XMWZG36H.js} +4 -4
- package/dist/{chunk-BKIGPPVM.js → chunk-XSFNHP2E.js} +6 -3
- package/dist/{chunk-ODGMVILH.js → chunk-XT6JY5W5.js} +25 -3
- package/dist/chunk-XXZJEFKW.cjs +490 -0
- package/dist/chunk-Y3MWXQSB.cjs +5737 -0
- package/dist/chunk-YF2I3MV3.js +46 -0
- package/dist/{chunk-CMYAWUX3.js → chunk-YIMN575O.js} +445 -53
- package/dist/chunk-YIVFJNYV.js +176 -0
- package/dist/{chunk-V2ILLPHK.cjs → chunk-YKGPCZEE.cjs} +10 -10
- package/dist/chunk-YQELFNJV.js +39 -0
- package/dist/chunk-YSCKYJOB.cjs +2196 -0
- package/dist/chunk-Z5XMPRNC.cjs +5390 -0
- package/dist/{chunk-3FYZOR7S.cjs → chunk-ZDSYWBIE.cjs} +18 -18
- package/dist/{chunk-CQDOF3G7.cjs → chunk-ZKSS47EV.cjs} +24 -19
- package/dist/chunk-ZUL5AVTX.cjs +1993 -0
- package/dist/chunk-ZXBZVXDW.js +5386 -0
- package/dist/{chunk-AW7WAELW.js → chunk-ZZ7RO2UQ.js} +314 -46
- package/dist/cli/holoscript-runner.cjs +203 -206
- package/dist/cli/holoscript-runner.js +147 -150
- package/dist/codebase/index.cjs +1 -1
- package/dist/codebase/index.d.ts +25 -0
- package/dist/codebase/index.js +1 -1
- package/dist/compiler/agent-inference.cjs +10 -8
- package/dist/compiler/agent-inference.js +6 -4
- package/dist/compiler/android-xr.cjs +12 -8
- package/dist/compiler/android-xr.js +10 -6
- package/dist/compiler/android.cjs +12 -9
- package/dist/compiler/android.js +10 -7
- package/dist/compiler/ar.cjs +9 -7
- package/dist/compiler/ar.js +7 -5
- package/dist/compiler/babylon.cjs +12 -9
- package/dist/compiler/babylon.js +11 -8
- package/dist/compiler/business-quest.cjs +43 -0
- package/dist/compiler/business-quest.js +4 -0
- package/dist/compiler/coco.cjs +1 -1
- package/dist/compiler/coco.js +1 -1
- package/dist/compiler/context.cjs +25 -0
- package/dist/compiler/context.d.ts +28 -0
- package/dist/compiler/context.js +8 -0
- package/dist/compiler/domain-block-utils.cjs +161 -151
- package/dist/compiler/domain-block-utils.js +7 -5
- package/dist/compiler/dtdl.cjs +10 -8
- package/dist/compiler/dtdl.js +7 -5
- package/dist/compiler/gltf-pipeline.cjs +9 -9
- package/dist/compiler/gltf-pipeline.js +2 -2
- package/dist/compiler/godot.cjs +9 -7
- package/dist/compiler/godot.js +8 -6
- package/dist/compiler/incremental.cjs +12 -8
- package/dist/compiler/incremental.js +7 -3
- package/dist/compiler/index.cjs +5152 -814
- package/dist/compiler/index.d.ts +404 -21
- package/dist/compiler/index.js +5063 -859
- package/dist/compiler/ios.cjs +11 -9
- package/dist/compiler/ios.js +9 -7
- package/dist/compiler/llm-provider-capabilities.cjs +25 -0
- package/dist/compiler/llm-provider-capabilities.d.ts +43 -0
- package/dist/compiler/llm-provider-capabilities.js +8 -0
- package/dist/compiler/multi-layer.cjs +14 -17
- package/dist/compiler/multi-layer.js +13 -10
- package/dist/compiler/nodetoy.cjs +5 -5
- package/dist/compiler/nodetoy.js +2 -2
- package/dist/compiler/openxr.cjs +10 -7
- package/dist/compiler/openxr.js +9 -6
- package/dist/compiler/playcanvas.cjs +10 -8
- package/dist/compiler/playcanvas.js +9 -7
- package/dist/compiler/r3f.cjs +17 -14
- package/dist/compiler/r3f.d.ts +22 -0
- package/dist/compiler/r3f.js +12 -9
- package/dist/compiler/remotion.cjs +1 -1
- package/dist/compiler/remotion.js +1 -1
- package/dist/compiler/reproducibility.cjs +1 -1
- package/dist/compiler/reproducibility.js +1 -1
- package/dist/compiler/sdf.cjs +10 -8
- package/dist/compiler/sdf.js +8 -6
- package/dist/compiler/semantic-scene.cjs +7 -7
- package/dist/compiler/semantic-scene.js +2 -2
- package/dist/compiler/state.cjs +9 -6
- package/dist/compiler/state.js +7 -4
- package/dist/compiler/trait-composition.cjs +11 -8
- package/dist/compiler/trait-composition.js +7 -4
- package/dist/compiler/unity.cjs +10 -7
- package/dist/compiler/unity.js +9 -6
- package/dist/compiler/unreal.cjs +11 -8
- package/dist/compiler/unreal.js +9 -6
- package/dist/compiler/urdf.cjs +20 -14
- package/dist/compiler/urdf.js +8 -6
- package/dist/compiler/usd-physics.cjs +10 -8
- package/dist/compiler/usd-physics.js +7 -5
- package/dist/compiler/visionos.cjs +10 -7
- package/dist/compiler/visionos.js +9 -6
- package/dist/compiler/vrchat.cjs +10 -8
- package/dist/compiler/vrchat.js +8 -6
- package/dist/compiler/vrr.cjs +10 -8
- package/dist/compiler/vrr.js +8 -6
- package/dist/compiler/wasm.cjs +13 -11
- package/dist/compiler/wasm.js +8 -6
- package/dist/compiler/webgpu.cjs +9 -7
- package/dist/compiler/webgpu.js +8 -6
- package/dist/constants.d.ts +4 -0
- package/dist/coordinators/index.cjs +1266 -0
- package/dist/coordinators/index.d.ts +260 -0
- package/dist/coordinators/index.js +1259 -0
- package/dist/debugger.cjs +9 -7
- package/dist/debugger.js +7 -5
- package/dist/entries/interop.cjs +23 -21
- package/dist/entries/interop.d.ts +9 -0
- package/dist/entries/interop.js +5 -3
- package/dist/entries/scripting.cjs +18 -17
- package/dist/entries/scripting.d.ts +7 -0
- package/dist/entries/scripting.js +8 -7
- package/dist/hololand/index.cjs +146 -0
- package/dist/hololand/index.d.ts +70 -0
- package/dist/hololand/index.js +5 -0
- package/dist/index.cjs +21396 -39967
- package/dist/index.d.ts +1496 -40
- package/dist/index.js +21066 -40142
- package/dist/math/tropical-spmv.cjs +45 -0
- package/dist/math/tropical-spmv.js +4 -0
- package/dist/math/vec3.cjs +14 -14
- package/dist/math/vec3.js +2 -2
- package/dist/ml-dsa-FSKBTE3W.cjs +1265 -0
- package/dist/ml-dsa-Z7JDKW4L.js +1265 -0
- package/dist/paper-0c-spike/index.cjs +150 -0
- package/dist/paper-0c-spike/index.d.ts +29 -0
- package/dist/paper-0c-spike/index.js +141 -0
- package/dist/parser.cjs +45 -10
- package/dist/parser.d.ts +11 -0
- package/dist/parser.js +10 -7
- package/dist/{playwright-7DTEQCBD.cjs → playwright-3AT3AKTV.cjs} +1985 -1985
- package/dist/{playwright-BIZXMLD2.js → playwright-VZPDVUEQ.js} +3 -3
- package/dist/reconstruction/index.cjs +153 -0
- package/dist/reconstruction/index.d.ts +105 -0
- package/dist/reconstruction/index.js +8 -0
- package/dist/runtime.cjs +7 -5
- package/dist/runtime.js +6 -4
- package/dist/self-improvement/index.cjs +563 -0
- package/dist/self-improvement/index.d.ts +0 -48
- package/dist/self-improvement/index.js +545 -0
- package/dist/storage/index.cjs +10 -9
- package/dist/storage/index.d.ts +24 -0
- package/dist/storage/index.js +3 -2
- package/dist/testing.cjs +25 -0
- package/dist/testing.d.ts +70 -0
- package/dist/testing.js +4 -0
- package/dist/tools/index.d.ts +33 -0
- package/dist/traitDocs/traitDocs.cjs +33 -0
- package/dist/traitDocs/traitDocs.d.ts +69 -0
- package/dist/traitDocs/traitDocs.js +7 -0
- package/dist/traits/botanical-lotus.cjs +41 -0
- package/dist/traits/botanical-lotus.d.ts +172 -0
- package/dist/traits/botanical-lotus.js +4 -0
- package/dist/traits/engines/index.cjs +520 -0
- package/dist/traits/engines/index.d.ts +111 -0
- package/dist/traits/engines/index.js +380 -0
- package/dist/traits/index.cjs +14387 -4320
- package/dist/traits/index.d.ts +197 -0
- package/dist/traits/index.js +16455 -6958
- package/dist/traits/simulation-solver-factory.cjs +13 -0
- package/dist/traits/simulation-solver-factory.d.ts +23 -0
- package/dist/traits/simulation-solver-factory.js +4 -0
- package/dist/traits/webcam-gaze.cjs +35 -0
- package/dist/traits/webcam-gaze.d.ts +17 -0
- package/dist/traits/webcam-gaze.js +4 -0
- package/dist/type-checker.cjs +6 -5
- package/dist/type-checker.js +4 -3
- package/dist/world/index.cjs +947 -0
- package/dist/world/index.d.ts +147 -0
- package/dist/world/index.js +939 -0
- package/dist/world-model/index.cjs +1446 -0
- package/dist/world-model/index.d.ts +119 -0
- package/dist/world-model/index.js +1414 -0
- package/dist/wot/index.cjs +7 -7
- package/dist/wot/index.js +2 -2
- package/package.json +150 -23
- package/dist/GLTFPipeline-3KLWWUQO.cjs +0 -34
- package/dist/GLTFPipeline-LYII2ZVQ.js +0 -13
- package/dist/chunk-3KEU5QYY.cjs +0 -175
- package/dist/chunk-6OQBLABR.js +0 -5240
- package/dist/chunk-6WNCRE6F.js +0 -126
- package/dist/chunk-C2QHVHZF.js +0 -4066
- package/dist/chunk-CN4NOESF.cjs +0 -416
- package/dist/chunk-DXVCEFZB.js +0 -2027
- package/dist/chunk-ENV7K6XA.js +0 -282
- package/dist/chunk-H7XMORZI.js +0 -2731
- package/dist/chunk-HAN4V3PF.cjs +0 -2037
- package/dist/chunk-HF4OFY25.cjs +0 -4097
- package/dist/chunk-LER4WXW5.cjs +0 -286
- package/dist/chunk-M6FU6S22.js +0 -2911
- package/dist/chunk-MWEFR6YQ.js +0 -1857
- package/dist/chunk-PJMOXFPR.cjs +0 -1861
- package/dist/chunk-QHVVVN47.cjs +0 -2033
- package/dist/chunk-QWKUKVRE.js +0 -2026
- package/dist/chunk-UITWA6DV.cjs +0 -2829
- package/dist/chunk-UQW6SLM5.js +0 -172
- package/dist/chunk-WUXIRGZP.cjs +0 -2915
- package/dist/chunk-X67XRI2T.js +0 -410
- package/dist/chunk-Y7VK5TH3.cjs +0 -129
- package/dist/chunk-YCMQQQ5U.cjs +0 -5244
- package/dist/post-quantum-JTTAAGO3.cjs +0 -6
- package/dist/post-quantum-RVPVDEPI.js +0 -4
package/dist/chunk-PJMOXFPR.cjs
DELETED
|
@@ -1,1861 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var chunkHHS6FMOU_cjs = require('./chunk-HHS6FMOU.cjs');
|
|
4
|
-
var chunkQHVVVN47_cjs = require('./chunk-QHVVVN47.cjs');
|
|
5
|
-
var chunkEGIZHYJP_cjs = require('./chunk-EGIZHYJP.cjs');
|
|
6
|
-
|
|
7
|
-
// src/compiler/AndroidXRCompiler.ts
|
|
8
|
-
var AndroidXRCompiler_exports = {};
|
|
9
|
-
chunkEGIZHYJP_cjs.__export(AndroidXRCompiler_exports, {
|
|
10
|
-
AndroidXRCompiler: () => exports.AndroidXRCompiler,
|
|
11
|
-
compileToAndroidXR: () => compileToAndroidXR
|
|
12
|
-
});
|
|
13
|
-
function compileToAndroidXR(composition, options) {
|
|
14
|
-
const compiler = new exports.AndroidXRCompiler(options);
|
|
15
|
-
return compiler.compile(composition, "");
|
|
16
|
-
}
|
|
17
|
-
exports.AndroidXRCompiler = void 0;
|
|
18
|
-
var init_AndroidXRCompiler = chunkEGIZHYJP_cjs.__esm({
|
|
19
|
-
"src/compiler/AndroidXRCompiler.ts"() {
|
|
20
|
-
chunkQHVVVN47_cjs.init_CompilerBase();
|
|
21
|
-
chunkQHVVVN47_cjs.init_ANSNamespace();
|
|
22
|
-
chunkHHS6FMOU_cjs.init_DomainBlockCompilerMixin();
|
|
23
|
-
exports.AndroidXRCompiler = class extends chunkQHVVVN47_cjs.CompilerBase {
|
|
24
|
-
constructor(options = {}) {
|
|
25
|
-
super();
|
|
26
|
-
this.compilerName = "AndroidXRCompiler";
|
|
27
|
-
this.lines = [];
|
|
28
|
-
this.indentLevel = 0;
|
|
29
|
-
this.options = {
|
|
30
|
-
packageName: options.packageName || "com.holoscript.generated",
|
|
31
|
-
activityName: options.activityName || "GeneratedXRActivity",
|
|
32
|
-
useFilament: options.useFilament ?? true,
|
|
33
|
-
useARCore: options.useARCore ?? true,
|
|
34
|
-
indent: options.indent || " ",
|
|
35
|
-
minSdk: options.minSdk || 30,
|
|
36
|
-
targetSdk: options.targetSdk || 35,
|
|
37
|
-
formFactor: options.formFactor || "headset"
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
getRequiredCapability() {
|
|
41
|
-
return chunkQHVVVN47_cjs.ANSCapabilityPath.ANDROID_XR;
|
|
42
|
-
}
|
|
43
|
-
/** Returns true when targeting AI glasses form factor. */
|
|
44
|
-
get isGlassesMode() {
|
|
45
|
-
return this.options.formFactor === "glasses";
|
|
46
|
-
}
|
|
47
|
-
compile(composition, agentToken, outputPath) {
|
|
48
|
-
this.validateCompilerAccess(agentToken, outputPath);
|
|
49
|
-
if (this.isGlassesMode) {
|
|
50
|
-
return {
|
|
51
|
-
activityFile: this.generateGlassesActivityFile(composition),
|
|
52
|
-
stateFile: this.generateStateFile(composition),
|
|
53
|
-
nodeFactoryFile: this.generateNodeFactoryFile(composition),
|
|
54
|
-
manifestFile: this.generateGlassesManifestFile(composition),
|
|
55
|
-
buildGradle: this.generateGlassesBuildGradle(composition),
|
|
56
|
-
glimmerComponentsFile: this.generateGlimmerComponentsFile(composition)
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
return {
|
|
60
|
-
activityFile: this.generateActivityFile(composition),
|
|
61
|
-
stateFile: this.generateStateFile(composition),
|
|
62
|
-
nodeFactoryFile: this.generateNodeFactoryFile(composition),
|
|
63
|
-
manifestFile: this.generateManifestFile(composition),
|
|
64
|
-
buildGradle: this.generateBuildGradle(composition)
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
generateActivityFile(composition) {
|
|
68
|
-
this.lines = [];
|
|
69
|
-
this.indentLevel = 0;
|
|
70
|
-
this.emit("// Auto-generated by HoloScript AndroidXRCompiler");
|
|
71
|
-
this.emit(
|
|
72
|
-
`// Source: composition "${this.escapeStringValue(composition.name, "Kotlin")}"`
|
|
73
|
-
);
|
|
74
|
-
this.emit("// Do not edit manually -- regenerate from .holo source");
|
|
75
|
-
this.emit("");
|
|
76
|
-
this.emit(`package ${this.options.packageName}`);
|
|
77
|
-
this.emit("");
|
|
78
|
-
this.emitImports(composition);
|
|
79
|
-
this.emit("");
|
|
80
|
-
this.emitActivityClass(composition);
|
|
81
|
-
this.compileAndroidXRDomainBlocks(composition);
|
|
82
|
-
return this.lines.join("\n");
|
|
83
|
-
}
|
|
84
|
-
// ─── State File ─────────────────────────────────────────────────────
|
|
85
|
-
generateStateFile(composition) {
|
|
86
|
-
this.lines = [];
|
|
87
|
-
this.indentLevel = 0;
|
|
88
|
-
const pkg = this.options.packageName;
|
|
89
|
-
this.emit("// Auto-generated by HoloScript AndroidXRCompiler");
|
|
90
|
-
this.emit(`// State: ${this.escapeStringValue(composition.name, "Kotlin")}`);
|
|
91
|
-
this.emit("");
|
|
92
|
-
this.emit(`package ${pkg}`);
|
|
93
|
-
this.emit("");
|
|
94
|
-
this.emit("import androidx.compose.runtime.mutableStateOf");
|
|
95
|
-
this.emit("import androidx.compose.runtime.getValue");
|
|
96
|
-
this.emit("import androidx.compose.runtime.setValue");
|
|
97
|
-
this.emit("import androidx.lifecycle.ViewModel");
|
|
98
|
-
this.emit("import androidx.lifecycle.MutableLiveData");
|
|
99
|
-
this.emit("import androidx.lifecycle.LiveData");
|
|
100
|
-
this.emit("import androidx.xr.runtime.math.Vector3");
|
|
101
|
-
this.emit("");
|
|
102
|
-
this.emit("class XRSceneState : ViewModel() {");
|
|
103
|
-
this.indent();
|
|
104
|
-
if (composition.state) {
|
|
105
|
-
this.emit("// === State Properties ===");
|
|
106
|
-
for (const prop of composition.state.properties) {
|
|
107
|
-
const kotlinType = this.toKotlinType(prop.value);
|
|
108
|
-
const kotlinValue = this.toKotlinValue(prop.value);
|
|
109
|
-
this.emit(
|
|
110
|
-
`private val _${this.escapeStringValue(prop.key, "Kotlin")} = MutableLiveData(${kotlinValue})`
|
|
111
|
-
);
|
|
112
|
-
this.emit(
|
|
113
|
-
`val ${this.escapeStringValue(prop.key, "Kotlin")}: LiveData<${kotlinType}> get() = _${this.escapeStringValue(prop.key, "Kotlin")}`
|
|
114
|
-
);
|
|
115
|
-
this.emit("");
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
this.emit("// === XR State ===");
|
|
119
|
-
this.emit("private val _spatialCapabilities = MutableLiveData<Set<String>>(emptySet())");
|
|
120
|
-
this.emit("val spatialCapabilities: LiveData<Set<String>> get() = _spatialCapabilities");
|
|
121
|
-
this.emit("");
|
|
122
|
-
this.emit(
|
|
123
|
-
"private val _activeEntities = MutableLiveData<MutableMap<String, Any>>(mutableMapOf())"
|
|
124
|
-
);
|
|
125
|
-
this.emit("val activeEntities: LiveData<MutableMap<String, Any>> get() = _activeEntities");
|
|
126
|
-
this.emit("");
|
|
127
|
-
this.emit("private val _selectedEntity = MutableLiveData<String?>()");
|
|
128
|
-
this.emit("val selectedEntity: LiveData<String?> get() = _selectedEntity");
|
|
129
|
-
this.emit("");
|
|
130
|
-
this.emit("fun onEntitySelected(entityId: String) {");
|
|
131
|
-
this.indent();
|
|
132
|
-
this.emit("_selectedEntity.value = entityId");
|
|
133
|
-
this.emit('android.util.Log.d("HoloScript", "Entity selected: $entityId")');
|
|
134
|
-
this.dedent();
|
|
135
|
-
this.emit("}");
|
|
136
|
-
this.emit("");
|
|
137
|
-
this.emit("fun updateSpatialCapabilities(capabilities: Set<String>) {");
|
|
138
|
-
this.indent();
|
|
139
|
-
this.emit("_spatialCapabilities.value = capabilities");
|
|
140
|
-
this.dedent();
|
|
141
|
-
this.emit("}");
|
|
142
|
-
this.emit("");
|
|
143
|
-
this.emit("fun reset() {");
|
|
144
|
-
this.indent();
|
|
145
|
-
this.emit("_spatialCapabilities.value = emptySet()");
|
|
146
|
-
this.emit("_activeEntities.value = mutableMapOf()");
|
|
147
|
-
this.emit("_selectedEntity.value = null");
|
|
148
|
-
if (composition.state) {
|
|
149
|
-
for (const prop of composition.state.properties) {
|
|
150
|
-
const kotlinValue = this.toKotlinValue(prop.value);
|
|
151
|
-
this.emit(
|
|
152
|
-
`_${this.escapeStringValue(prop.key, "Kotlin")}.value = ${kotlinValue}`
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
this.emit('android.util.Log.d("HoloScript", "XR State reset")');
|
|
157
|
-
this.dedent();
|
|
158
|
-
this.emit("}");
|
|
159
|
-
if (composition.logic?.actions) {
|
|
160
|
-
this.emit("");
|
|
161
|
-
this.emit("// === Actions ===");
|
|
162
|
-
for (const action of composition.logic.actions) {
|
|
163
|
-
this.compileAction(action);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
this.dedent();
|
|
167
|
-
this.emit("}");
|
|
168
|
-
return this.lines.join("\n");
|
|
169
|
-
}
|
|
170
|
-
compileAction(action) {
|
|
171
|
-
const rawName = this.sanitizeName(action.name);
|
|
172
|
-
const name = rawName.charAt(0).toLowerCase() + rawName.slice(1);
|
|
173
|
-
this.emit(`fun ${name}() {`);
|
|
174
|
-
this.indent();
|
|
175
|
-
this.emit(
|
|
176
|
-
`android.util.Log.d("HoloScript", "Action: ${this.escapeStringValue(action.name, "Kotlin")}")`
|
|
177
|
-
);
|
|
178
|
-
this.emit("// Action implementation");
|
|
179
|
-
this.dedent();
|
|
180
|
-
this.emit("}");
|
|
181
|
-
this.emit("");
|
|
182
|
-
}
|
|
183
|
-
// ─── Node Factory File ──────────────────────────────────────────────
|
|
184
|
-
generateNodeFactoryFile(composition) {
|
|
185
|
-
this.lines = [];
|
|
186
|
-
this.indentLevel = 0;
|
|
187
|
-
const pkg = this.options.packageName;
|
|
188
|
-
this.emit("// Auto-generated by HoloScript AndroidXRCompiler");
|
|
189
|
-
this.emit(
|
|
190
|
-
`// Node Factory: ${this.escapeStringValue(composition.name, "Kotlin")}`
|
|
191
|
-
);
|
|
192
|
-
this.emit("");
|
|
193
|
-
this.emit(`package ${pkg}`);
|
|
194
|
-
this.emit("");
|
|
195
|
-
this.emit("import android.net.Uri");
|
|
196
|
-
this.emit("import androidx.xr.scenecore.Session as XRSession");
|
|
197
|
-
this.emit("import androidx.xr.scenecore.Entity");
|
|
198
|
-
this.emit("import androidx.xr.scenecore.GltfModel");
|
|
199
|
-
this.emit("import androidx.xr.scenecore.GltfModelEntity");
|
|
200
|
-
this.emit("import androidx.xr.runtime.math.Pose");
|
|
201
|
-
this.emit("import androidx.xr.runtime.math.Vector3");
|
|
202
|
-
this.emit("import androidx.xr.runtime.math.Quaternion");
|
|
203
|
-
this.emit("");
|
|
204
|
-
this.emit("object XRNodeFactory {");
|
|
205
|
-
this.indent();
|
|
206
|
-
this.emit("/**");
|
|
207
|
-
this.emit(" * Create a default entity in the XR scene.");
|
|
208
|
-
this.emit(" */");
|
|
209
|
-
this.emit("fun createDefaultEntity(session: XRSession): Entity {");
|
|
210
|
-
this.indent();
|
|
211
|
-
if (composition.objects?.length) {
|
|
212
|
-
const firstObj = composition.objects[0];
|
|
213
|
-
const modelSrc = this.findProp(firstObj, "model") ?? this.findProp(firstObj, "src");
|
|
214
|
-
if (modelSrc) {
|
|
215
|
-
this.emit(`val gltfModel = GltfModel.create(session, Uri.parse("${modelSrc}"))`);
|
|
216
|
-
this.emit("return GltfModelEntity.create(session, gltfModel).apply {");
|
|
217
|
-
this.indent();
|
|
218
|
-
this.emit("parent = session.scene.activitySpace");
|
|
219
|
-
const pos = this.findProp(firstObj, "position");
|
|
220
|
-
if (pos && Array.isArray(pos)) {
|
|
221
|
-
this.emit(
|
|
222
|
-
`setPose(Pose(${this.toKotlinFloat3(pos)}, Quaternion.identity()))`
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
this.dedent();
|
|
226
|
-
this.emit("}");
|
|
227
|
-
} else {
|
|
228
|
-
this.emit(
|
|
229
|
-
`val entity = session.scene.createEntity("${this.escapeStringValue(firstObj.name, "Kotlin")}")`
|
|
230
|
-
);
|
|
231
|
-
this.emit("entity.parent = session.scene.activitySpace");
|
|
232
|
-
const pos = this.findProp(firstObj, "position");
|
|
233
|
-
if (pos && Array.isArray(pos)) {
|
|
234
|
-
this.emit(
|
|
235
|
-
`entity.setPose(Pose(${this.toKotlinFloat3(pos)}, Quaternion.identity()))`
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
this.emit("return entity");
|
|
239
|
-
}
|
|
240
|
-
} else {
|
|
241
|
-
this.emit('val entity = session.scene.createEntity("default")');
|
|
242
|
-
this.emit("entity.parent = session.scene.activitySpace");
|
|
243
|
-
this.emit("return entity");
|
|
244
|
-
}
|
|
245
|
-
this.dedent();
|
|
246
|
-
this.emit("}");
|
|
247
|
-
this.emit("");
|
|
248
|
-
this.emit("/**");
|
|
249
|
-
this.emit(" * Load a glTF model and create a GltfModelEntity.");
|
|
250
|
-
this.emit(" */");
|
|
251
|
-
this.emit("fun loadGltfModel(");
|
|
252
|
-
this.indent();
|
|
253
|
-
this.emit("session: XRSession,");
|
|
254
|
-
this.emit("modelUri: String,");
|
|
255
|
-
this.emit("position: Vector3 = Vector3(0f, 0f, 0f),");
|
|
256
|
-
this.emit("scale: Float = 1f");
|
|
257
|
-
this.dedent();
|
|
258
|
-
this.emit("): GltfModelEntity {");
|
|
259
|
-
this.indent();
|
|
260
|
-
this.emit("val gltfModel = GltfModel.create(session, Uri.parse(modelUri))");
|
|
261
|
-
this.emit("return GltfModelEntity.create(session, gltfModel).apply {");
|
|
262
|
-
this.indent();
|
|
263
|
-
this.emit("parent = session.scene.activitySpace");
|
|
264
|
-
this.emit("setPose(Pose(position, Quaternion.identity()))");
|
|
265
|
-
this.emit("setScale(com.google.android.filament.utils.Float3(scale, scale, scale))");
|
|
266
|
-
this.dedent();
|
|
267
|
-
this.emit("}");
|
|
268
|
-
this.dedent();
|
|
269
|
-
this.emit("}");
|
|
270
|
-
this.emit("");
|
|
271
|
-
for (const obj of composition.objects || []) {
|
|
272
|
-
this.compileObjectFactory(obj);
|
|
273
|
-
}
|
|
274
|
-
this.dedent();
|
|
275
|
-
this.emit("}");
|
|
276
|
-
return this.lines.join("\n");
|
|
277
|
-
}
|
|
278
|
-
compileObjectFactory(obj) {
|
|
279
|
-
const methodName = `create${this.sanitizeName(obj.name).charAt(0).toUpperCase() + this.sanitizeName(obj.name).slice(1)}`;
|
|
280
|
-
const modelSrc = this.findProp(obj, "model") ?? this.findProp(obj, "src");
|
|
281
|
-
this.emit(`fun ${methodName}(session: XRSession): Entity {`);
|
|
282
|
-
this.indent();
|
|
283
|
-
if (modelSrc) {
|
|
284
|
-
this.emit(`val gltfModel = GltfModel.create(session, Uri.parse("${modelSrc}"))`);
|
|
285
|
-
this.emit("return GltfModelEntity.create(session, gltfModel).apply {");
|
|
286
|
-
this.indent();
|
|
287
|
-
this.emit("parent = session.scene.activitySpace");
|
|
288
|
-
const pos = this.findProp(obj, "position");
|
|
289
|
-
if (pos && Array.isArray(pos)) {
|
|
290
|
-
this.emit(`setPose(Pose(${this.toKotlinFloat3(pos)}, Quaternion.identity()))`);
|
|
291
|
-
}
|
|
292
|
-
const scale = this.findProp(obj, "scale");
|
|
293
|
-
if (scale && Array.isArray(scale)) {
|
|
294
|
-
this.emit(`setScale(${this.toKotlinFloat3(scale)})`);
|
|
295
|
-
} else if (typeof scale === "number") {
|
|
296
|
-
this.emit(
|
|
297
|
-
`setScale(com.google.android.filament.utils.Float3(${scale}f, ${scale}f, ${scale}f))`
|
|
298
|
-
);
|
|
299
|
-
}
|
|
300
|
-
this.dedent();
|
|
301
|
-
this.emit("}");
|
|
302
|
-
} else {
|
|
303
|
-
this.emit(
|
|
304
|
-
`val entity = session.scene.createEntity("${this.escapeStringValue(obj.name, "Kotlin")}")`
|
|
305
|
-
);
|
|
306
|
-
this.emit("entity.parent = session.scene.activitySpace");
|
|
307
|
-
const pos = this.findProp(obj, "position");
|
|
308
|
-
if (pos && Array.isArray(pos)) {
|
|
309
|
-
this.emit(
|
|
310
|
-
`entity.setPose(Pose(${this.toKotlinFloat3(pos)}, Quaternion.identity()))`
|
|
311
|
-
);
|
|
312
|
-
}
|
|
313
|
-
const scale = this.findProp(obj, "scale");
|
|
314
|
-
if (scale && Array.isArray(scale)) {
|
|
315
|
-
this.emit(`entity.setScale(${this.toKotlinFloat3(scale)})`);
|
|
316
|
-
} else if (typeof scale === "number") {
|
|
317
|
-
this.emit(
|
|
318
|
-
`entity.setScale(com.google.android.filament.utils.Float3(${scale}f, ${scale}f, ${scale}f))`
|
|
319
|
-
);
|
|
320
|
-
}
|
|
321
|
-
this.emit("return entity");
|
|
322
|
-
}
|
|
323
|
-
this.dedent();
|
|
324
|
-
this.emit("}");
|
|
325
|
-
this.emit("");
|
|
326
|
-
}
|
|
327
|
-
// ─── Manifest File ──────────────────────────────────────────────────
|
|
328
|
-
generateManifestFile(composition) {
|
|
329
|
-
const pkg = this.options.packageName;
|
|
330
|
-
const activityName = this.options.activityName;
|
|
331
|
-
const hasHandTracking = composition.objects?.some(
|
|
332
|
-
(o) => o.traits?.some((t) => t.name === "hand_tracking")
|
|
333
|
-
);
|
|
334
|
-
const hasFaceTracking = composition.objects?.some(
|
|
335
|
-
(o) => o.traits?.some((t) => t.name === "face_tracking")
|
|
336
|
-
);
|
|
337
|
-
const hasHeadFollow = composition.objects?.some(
|
|
338
|
-
(o) => o.traits?.some((t) => t.name === "follows_head" || t.name === "head_follow")
|
|
339
|
-
);
|
|
340
|
-
const hasEyeTracking = composition.objects?.some(
|
|
341
|
-
(o) => o.traits?.some((t) => t.name === "eye_tracking" || t.name === "eye_tracked")
|
|
342
|
-
);
|
|
343
|
-
const hasPlaneDetection = composition.objects?.some(
|
|
344
|
-
(o) => o.traits?.some((t) => t.name === "plane_detection" || t.name === "anchor")
|
|
345
|
-
);
|
|
346
|
-
const xrPermissions = [];
|
|
347
|
-
xrPermissions.push(' <uses-permission android:name="android.permission.CAMERA" />');
|
|
348
|
-
if (hasHandTracking) {
|
|
349
|
-
xrPermissions.push(' <uses-permission android:name="android.permission.HAND_TRACKING" />');
|
|
350
|
-
}
|
|
351
|
-
if (hasFaceTracking) {
|
|
352
|
-
xrPermissions.push(' <uses-permission android:name="android.permission.FACE_TRACKING" />');
|
|
353
|
-
}
|
|
354
|
-
if (hasHeadFollow) {
|
|
355
|
-
xrPermissions.push(' <uses-permission android:name="android.permission.HEAD_TRACKING" />');
|
|
356
|
-
}
|
|
357
|
-
if (hasEyeTracking) {
|
|
358
|
-
xrPermissions.push(' <uses-permission android:name="android.permission.EYE_TRACKING" />');
|
|
359
|
-
}
|
|
360
|
-
if (hasPlaneDetection) {
|
|
361
|
-
xrPermissions.push(
|
|
362
|
-
' <uses-permission android:name="android.permission.SCENE_UNDERSTANDING_COARSE" />'
|
|
363
|
-
);
|
|
364
|
-
}
|
|
365
|
-
return `<?xml version="1.0" encoding="utf-8"?>
|
|
366
|
-
<!-- Auto-generated by HoloScript AndroidXRCompiler -->
|
|
367
|
-
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
368
|
-
package="${pkg}">
|
|
369
|
-
|
|
370
|
-
<!-- XR Permissions -->
|
|
371
|
-
${xrPermissions.join("\n")}
|
|
372
|
-
|
|
373
|
-
<!-- XR Required Features -->
|
|
374
|
-
<uses-feature android:name="android.hardware.xr.headtracking" android:required="true" />
|
|
375
|
-
<uses-feature android:glEsVersion="0x00030002" android:required="true" />
|
|
376
|
-
|
|
377
|
-
<application
|
|
378
|
-
android:allowBackup="true"
|
|
379
|
-
android:label="${this.escapeStringValue(composition.name, "XML")}"
|
|
380
|
-
android:supportsRtl="true"
|
|
381
|
-
android:theme="@style/Theme.MaterialComponents.DayNight.NoActionBar">
|
|
382
|
-
|
|
383
|
-
<!-- Android XR metadata -->
|
|
384
|
-
<meta-data android:name="com.google.ar.core" android:value="required" />
|
|
385
|
-
|
|
386
|
-
<activity
|
|
387
|
-
android:name=".${activityName}"
|
|
388
|
-
android:exported="true"
|
|
389
|
-
android:configChanges="orientation|screenSize|keyboardHidden|screenLayout|uiMode"
|
|
390
|
-
android:screenOrientation="landscape"
|
|
391
|
-
android:enableOnBackInvokedCallback="true">
|
|
392
|
-
<intent-filter>
|
|
393
|
-
<action android:name="android.intent.action.MAIN" />
|
|
394
|
-
<category android:name="android.intent.category.LAUNCHER" />
|
|
395
|
-
<category android:name="com.google.intent.category.XR" />
|
|
396
|
-
</intent-filter>
|
|
397
|
-
</activity>
|
|
398
|
-
</application>
|
|
399
|
-
</manifest>`;
|
|
400
|
-
}
|
|
401
|
-
// ─── Build Gradle File ──────────────────────────────────────────────
|
|
402
|
-
generateBuildGradle(composition) {
|
|
403
|
-
const hasDrmVideo = composition.objects?.some(
|
|
404
|
-
(o) => o.traits?.some((t) => t.name === "drm_video" || t.name === "protected_video")
|
|
405
|
-
);
|
|
406
|
-
return `// Auto-generated by HoloScript AndroidXRCompiler
|
|
407
|
-
// Source: ${this.escapeStringValue(composition.name, "Kotlin")}
|
|
408
|
-
|
|
409
|
-
plugins {
|
|
410
|
-
id("com.android.application")
|
|
411
|
-
id("org.jetbrains.kotlin.android")
|
|
412
|
-
id("org.jetbrains.kotlin.plugin.compose")
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
android {
|
|
416
|
-
namespace = "${this.options.packageName}"
|
|
417
|
-
compileSdk = ${this.options.targetSdk}
|
|
418
|
-
|
|
419
|
-
defaultConfig {
|
|
420
|
-
applicationId = "${this.options.packageName}"
|
|
421
|
-
minSdk = ${this.options.minSdk}
|
|
422
|
-
targetSdk = ${this.options.targetSdk}
|
|
423
|
-
versionCode = 1
|
|
424
|
-
versionName = "1.0"
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
buildTypes {
|
|
428
|
-
release {
|
|
429
|
-
isMinifyEnabled = false
|
|
430
|
-
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
compileOptions {
|
|
435
|
-
sourceCompatibility = JavaVersion.VERSION_17
|
|
436
|
-
targetCompatibility = JavaVersion.VERSION_17
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
kotlinOptions {
|
|
440
|
-
jvmTarget = "17"
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
buildFeatures {
|
|
444
|
-
compose = true
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
dependencies {
|
|
449
|
-
// Core
|
|
450
|
-
implementation("androidx.core:core-ktx:1.13.1")
|
|
451
|
-
implementation("androidx.appcompat:appcompat:1.7.0")
|
|
452
|
-
implementation("com.google.android.material:material:1.12.0")
|
|
453
|
-
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.0")
|
|
454
|
-
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.8.0")
|
|
455
|
-
implementation("androidx.activity:activity-compose:1.9.0")
|
|
456
|
-
|
|
457
|
-
// Jetpack Compose
|
|
458
|
-
implementation(platform("androidx.compose:compose-bom:2024.06.00"))
|
|
459
|
-
implementation("androidx.compose.ui:ui")
|
|
460
|
-
implementation("androidx.compose.material3:material3")
|
|
461
|
-
implementation("androidx.compose.runtime:runtime")
|
|
462
|
-
|
|
463
|
-
// Android XR - Jetpack XR SceneCore
|
|
464
|
-
implementation("androidx.xr.scenecore:scenecore:1.0.0-alpha01")
|
|
465
|
-
implementation("androidx.xr.compose:compose:1.0.0-alpha01")
|
|
466
|
-
|
|
467
|
-
// Android XR - ARCore for Jetpack XR
|
|
468
|
-
implementation("androidx.xr.arcore:arcore:1.0.0-alpha01")
|
|
469
|
-
|
|
470
|
-
// Android XR - Runtime
|
|
471
|
-
implementation("androidx.xr:xr:1.0.0-alpha01")
|
|
472
|
-
|
|
473
|
-
// Filament (PBR Rendering)
|
|
474
|
-
implementation("com.google.android.filament:filament-android:1.51.0")
|
|
475
|
-
implementation("com.google.android.filament:filament-utils-android:1.51.0")
|
|
476
|
-
implementation("com.google.android.filament:gltfio-android:1.51.0")
|
|
477
|
-
${hasDrmVideo ? `
|
|
478
|
-
// Media3 ExoPlayer (DRM Video Playback)
|
|
479
|
-
implementation("androidx.media3:media3-exoplayer:1.3.1")
|
|
480
|
-
implementation("androidx.media3:media3-common:1.3.1")
|
|
481
|
-
implementation("androidx.media3:media3-exoplayer-dash:1.3.1")
|
|
482
|
-
` : ""}
|
|
483
|
-
// ARCore
|
|
484
|
-
implementation("com.google.ar:core:1.43.0")
|
|
485
|
-
}`;
|
|
486
|
-
}
|
|
487
|
-
// ─── Imports ─────────────────────────────────────────────────────────
|
|
488
|
-
emitImports(composition) {
|
|
489
|
-
this.emit("import android.os.Bundle");
|
|
490
|
-
this.emit("import android.net.Uri");
|
|
491
|
-
this.emit("import android.animation.ObjectAnimator");
|
|
492
|
-
this.emit("import android.animation.AnimatorSet");
|
|
493
|
-
this.emit("");
|
|
494
|
-
this.emit("import androidx.activity.ComponentActivity");
|
|
495
|
-
this.emit("import androidx.activity.compose.setContent");
|
|
496
|
-
this.emit("import androidx.compose.runtime.*");
|
|
497
|
-
this.emit("import androidx.compose.foundation.layout.*");
|
|
498
|
-
this.emit("import androidx.compose.material3.*");
|
|
499
|
-
this.emit("import androidx.compose.ui.Modifier");
|
|
500
|
-
this.emit("import androidx.compose.ui.graphics.Color");
|
|
501
|
-
this.emit("");
|
|
502
|
-
this.emit("import androidx.xr.compose.spatial.*");
|
|
503
|
-
this.emit("import androidx.xr.compose.subspace.layout.*");
|
|
504
|
-
this.emit("");
|
|
505
|
-
this.emit("import androidx.xr.scenecore.Entity");
|
|
506
|
-
this.emit("import androidx.xr.scenecore.Session as XRSession");
|
|
507
|
-
this.emit("import androidx.xr.scenecore.GltfModel");
|
|
508
|
-
this.emit("import androidx.xr.scenecore.GltfModelEntity");
|
|
509
|
-
this.emit("import androidx.xr.scenecore.SurfaceEntity");
|
|
510
|
-
this.emit("import androidx.xr.scenecore.SpatialCapability");
|
|
511
|
-
this.emit("import androidx.xr.runtime.math.Pose");
|
|
512
|
-
this.emit("import androidx.xr.runtime.math.Vector3");
|
|
513
|
-
this.emit("import androidx.xr.runtime.math.Quaternion");
|
|
514
|
-
this.emit("");
|
|
515
|
-
if (this.options.useARCore) {
|
|
516
|
-
this.emit("import com.google.ar.core.Config");
|
|
517
|
-
this.emit("import com.google.ar.core.Plane");
|
|
518
|
-
this.emit("import com.google.ar.core.Anchor");
|
|
519
|
-
}
|
|
520
|
-
if (this.options.useFilament) {
|
|
521
|
-
this.emit("import com.google.android.filament.LightManager");
|
|
522
|
-
this.emit("import com.google.android.filament.utils.Float3");
|
|
523
|
-
}
|
|
524
|
-
const hasHandTracking = composition.objects?.some(
|
|
525
|
-
(o) => o.traits?.some((t) => t.name === "hand_tracking")
|
|
526
|
-
);
|
|
527
|
-
if (hasHandTracking) {
|
|
528
|
-
this.emit("import androidx.xr.scenecore.InputEvent");
|
|
529
|
-
}
|
|
530
|
-
const hasFaceTracking = composition.objects?.some(
|
|
531
|
-
(o) => o.traits?.some((t) => t.name === "face_tracking")
|
|
532
|
-
);
|
|
533
|
-
if (hasFaceTracking) {
|
|
534
|
-
this.emit("import androidx.xr.arcore.Face");
|
|
535
|
-
this.emit("import androidx.xr.arcore.FaceBlendShapeType");
|
|
536
|
-
this.emit("import androidx.xr.arcore.FaceConfidenceRegion");
|
|
537
|
-
this.emit("import androidx.xr.runtime.FaceTrackingMode");
|
|
538
|
-
}
|
|
539
|
-
const hasHeadFollow = composition.objects?.some(
|
|
540
|
-
(o) => o.traits?.some((t) => t.name === "follows_head" || t.name === "head_follow")
|
|
541
|
-
);
|
|
542
|
-
if (hasHeadFollow) {
|
|
543
|
-
this.emit("import androidx.xr.compose.spatial.UserSubspace");
|
|
544
|
-
}
|
|
545
|
-
const hasDrmVideo = composition.objects?.some(
|
|
546
|
-
(o) => o.traits?.some((t) => t.name === "drm_video" || t.name === "protected_video")
|
|
547
|
-
);
|
|
548
|
-
if (hasDrmVideo) {
|
|
549
|
-
this.emit("import androidx.media3.exoplayer.ExoPlayer");
|
|
550
|
-
this.emit("import androidx.media3.common.MediaItem");
|
|
551
|
-
this.emit("import androidx.media3.common.C");
|
|
552
|
-
}
|
|
553
|
-
if (composition.audio?.length) {
|
|
554
|
-
this.emit("import android.media.AudioAttributes");
|
|
555
|
-
this.emit("import android.media.SoundPool");
|
|
556
|
-
this.emit("import androidx.xr.scenecore.SpatialAudioTrack");
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
// ─── Activity Class ──────────────────────────────────────────────────
|
|
560
|
-
emitActivityClass(composition) {
|
|
561
|
-
this.emit(`class ${this.options.activityName} : ComponentActivity() {`);
|
|
562
|
-
this.indent();
|
|
563
|
-
this.emit("private lateinit var xrSession: XRSession");
|
|
564
|
-
if (composition.audio?.length) this.emit("private lateinit var soundPool: SoundPool");
|
|
565
|
-
this.emit("");
|
|
566
|
-
this.emit("override fun onCreate(savedInstanceState: Bundle?) {");
|
|
567
|
-
this.indent();
|
|
568
|
-
this.emit("super.onCreate(savedInstanceState)");
|
|
569
|
-
this.emit("xrSession = XRSession.create(this)");
|
|
570
|
-
if (this.options.useARCore) {
|
|
571
|
-
this.emit("");
|
|
572
|
-
this.emitARSession();
|
|
573
|
-
}
|
|
574
|
-
if (composition.audio?.length) {
|
|
575
|
-
this.emit("");
|
|
576
|
-
this.emit("val audioAttrs = AudioAttributes.Builder()");
|
|
577
|
-
this.indent();
|
|
578
|
-
this.emit(".setUsage(AudioAttributes.USAGE_GAME)");
|
|
579
|
-
this.emit(".setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).build()");
|
|
580
|
-
this.dedent();
|
|
581
|
-
this.emit(
|
|
582
|
-
"soundPool = SoundPool.Builder().setMaxStreams(8).setAudioAttributes(audioAttrs).build()"
|
|
583
|
-
);
|
|
584
|
-
}
|
|
585
|
-
this.emit("");
|
|
586
|
-
this.emit("setContent {");
|
|
587
|
-
this.indent();
|
|
588
|
-
this.emit(`${this.sanitizeName(composition.name)}Scene()`);
|
|
589
|
-
this.dedent();
|
|
590
|
-
this.emit("}");
|
|
591
|
-
this.dedent();
|
|
592
|
-
this.emit("}");
|
|
593
|
-
this.emit("");
|
|
594
|
-
this.emitSceneComposable(composition);
|
|
595
|
-
for (const tl of composition.timelines ?? []) this.emitTimeline(tl);
|
|
596
|
-
for (const tr of composition.transitions ?? []) this.emitTransition(tr);
|
|
597
|
-
this.dedent();
|
|
598
|
-
this.emit("}");
|
|
599
|
-
}
|
|
600
|
-
// ─── AR Session ──────────────────────────────────────────────────────
|
|
601
|
-
emitARSession() {
|
|
602
|
-
this.emit("xrSession.scene.configure { config ->");
|
|
603
|
-
this.indent();
|
|
604
|
-
this.emit("config.planeFindingMode = Config.PlaneFindingMode.HORIZONTAL_AND_VERTICAL");
|
|
605
|
-
this.emit("config.lightEstimationMode = Config.LightEstimationMode.ENVIRONMENTAL_HDR");
|
|
606
|
-
this.emit("config.depthMode = Config.DepthMode.AUTOMATIC");
|
|
607
|
-
this.emit("config.updateMode = Config.UpdateMode.LATEST_CAMERA_IMAGE");
|
|
608
|
-
this.dedent();
|
|
609
|
-
this.emit("}");
|
|
610
|
-
}
|
|
611
|
-
// ─── Scene Composable ────────────────────────────────────────────────
|
|
612
|
-
emitSceneComposable(composition) {
|
|
613
|
-
const name = this.sanitizeName(composition.name);
|
|
614
|
-
this.emit("@Composable");
|
|
615
|
-
this.emit(`private fun ${name}Scene() {`);
|
|
616
|
-
this.indent();
|
|
617
|
-
if (composition.state) {
|
|
618
|
-
for (const p of composition.state.properties) {
|
|
619
|
-
this.emit(
|
|
620
|
-
`var ${this.escapeStringValue(p.key, "Kotlin")} by remember { mutableStateOf(${this.toKotlinValue(p.value)}) }`
|
|
621
|
-
);
|
|
622
|
-
}
|
|
623
|
-
this.emit("");
|
|
624
|
-
}
|
|
625
|
-
this.emit("Subspace {");
|
|
626
|
-
this.indent();
|
|
627
|
-
if (composition.environment) this.emitEnvironment(composition.environment);
|
|
628
|
-
if (composition.camera) this.emitCamera(composition.camera);
|
|
629
|
-
for (const l of composition.lights ?? []) this.emitLight(l);
|
|
630
|
-
for (const o of composition.objects ?? []) {
|
|
631
|
-
const hasHeadFollow = o.traits?.some(
|
|
632
|
-
(t) => t.name === "follows_head" || t.name === "head_follow"
|
|
633
|
-
);
|
|
634
|
-
if (hasHeadFollow) {
|
|
635
|
-
this.emit("");
|
|
636
|
-
this.emit("// DP3: UserSubspace \u2014 head-following content");
|
|
637
|
-
this.emit("UserSubspace {");
|
|
638
|
-
this.indent();
|
|
639
|
-
this.emitObject(o);
|
|
640
|
-
this.dedent();
|
|
641
|
-
this.emit("}");
|
|
642
|
-
} else {
|
|
643
|
-
this.emitObject(o);
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
for (const g of composition.spatialGroups ?? []) this.emitGroup(g);
|
|
647
|
-
for (const a of composition.audio ?? []) this.emitAudio(a);
|
|
648
|
-
for (const z of composition.zones ?? []) this.emitZone(z);
|
|
649
|
-
if (composition.ui) this.emitUI(composition.ui);
|
|
650
|
-
if (composition.effects) this.emitEffects(composition.effects);
|
|
651
|
-
this.dedent();
|
|
652
|
-
this.emit("}");
|
|
653
|
-
this.dedent();
|
|
654
|
-
this.emit("}");
|
|
655
|
-
this.emit("");
|
|
656
|
-
}
|
|
657
|
-
// ─── Environment ─────────────────────────────────────────────────────
|
|
658
|
-
emitEnvironment(env) {
|
|
659
|
-
this.emit("// Environment");
|
|
660
|
-
for (const prop of env.properties) {
|
|
661
|
-
if (prop.key === "preset" || prop.key === "skybox") {
|
|
662
|
-
this.emit(`// Preset: "${prop.value}"`);
|
|
663
|
-
this.emit("xrSession.scene.configure { config ->");
|
|
664
|
-
this.indent();
|
|
665
|
-
this.emit("config.lightEstimationMode = Config.LightEstimationMode.ENVIRONMENTAL_HDR");
|
|
666
|
-
this.dedent();
|
|
667
|
-
this.emit("}");
|
|
668
|
-
} else if (prop.key === "passthrough") {
|
|
669
|
-
if (prop.value === true || prop.value === "true") {
|
|
670
|
-
this.emit("xrSession.scene.configure { it.focusMode = Config.FocusMode.AUTO }");
|
|
671
|
-
}
|
|
672
|
-
} else if (prop.key === "fog" && typeof prop.value === "object") {
|
|
673
|
-
this.emit(`// Fog: ${JSON.stringify(prop.value)} -- apply via Filament renderer`);
|
|
674
|
-
} else if (prop.key === "ambient_light") {
|
|
675
|
-
this.emit(`// Ambient light: ${prop.value}`);
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
// ─── Object ──────────────────────────────────────────────────────────
|
|
680
|
-
emitObject(obj) {
|
|
681
|
-
const v = this.sanitizeName(obj.name);
|
|
682
|
-
const meshType = this.findProp(obj, "mesh") ?? this.findProp(obj, "type") ?? "cube";
|
|
683
|
-
const modelSrc = this.findProp(obj, "model") ?? this.findProp(obj, "src");
|
|
684
|
-
this.emit("");
|
|
685
|
-
this.emit(`// Object: ${this.escapeStringValue(obj.name, "Kotlin")}`);
|
|
686
|
-
if (obj.traits?.some((t) => t.name === "plane_detection")) {
|
|
687
|
-
this.emit(
|
|
688
|
-
`val ${v}Plane = xrSession.scene.createEntity("${this.escapeStringValue(obj.name, "Kotlin")}_plane")`
|
|
689
|
-
);
|
|
690
|
-
this.emit(`${v}Plane.setPose(Pose(Vector3(0f, 0f, 0f), Quaternion.identity()))`);
|
|
691
|
-
}
|
|
692
|
-
const anchor = obj.traits?.find((t) => t.name === "anchor");
|
|
693
|
-
if (anchor) {
|
|
694
|
-
this.emit(
|
|
695
|
-
`val ${v}Anchor = xrSession.scene.createAnchorEntity() // type: ${anchor.config?.type ?? "plane"}`
|
|
696
|
-
);
|
|
697
|
-
}
|
|
698
|
-
if (obj.traits?.some((t) => t.name === "hand_tracking")) {
|
|
699
|
-
this.emit(`val ${v}Hand = HandNode(xrSession)`);
|
|
700
|
-
this.emit(`${v}Hand.setOnInputEventListener { event: InputEvent -> /* hand input */ }`);
|
|
701
|
-
}
|
|
702
|
-
if (modelSrc) {
|
|
703
|
-
this.emit(`val ${v}GltfModel = GltfModel.create(xrSession, Uri.parse("${modelSrc}"))`);
|
|
704
|
-
this.emit(`val ${v} = GltfModelEntity.create(xrSession, ${v}GltfModel).apply {`);
|
|
705
|
-
this.indent();
|
|
706
|
-
this.emit(`parent = xrSession.scene.activitySpace`);
|
|
707
|
-
const modelPos = this.findProp(obj, "position");
|
|
708
|
-
if (modelPos && Array.isArray(modelPos)) {
|
|
709
|
-
this.emit(
|
|
710
|
-
`setPose(Pose(${this.toKotlinFloat3(modelPos)}, Quaternion.identity()))`
|
|
711
|
-
);
|
|
712
|
-
}
|
|
713
|
-
this.dedent();
|
|
714
|
-
this.emit("}");
|
|
715
|
-
} else if (meshType === "text") {
|
|
716
|
-
const text = this.findProp(obj, "text") ?? obj.name;
|
|
717
|
-
const color = this.findProp(obj, "color");
|
|
718
|
-
this.emit(`SpatialPanel(SubspaceModifier.width(200f).height(60f)) {`);
|
|
719
|
-
this.indent();
|
|
720
|
-
this.emit(
|
|
721
|
-
`Text("${text}", color = ${color ? this.toKotlinColor(color) : "Color.White"})`
|
|
722
|
-
);
|
|
723
|
-
this.dedent();
|
|
724
|
-
this.emit("}");
|
|
725
|
-
} else {
|
|
726
|
-
this.emit(
|
|
727
|
-
`val ${v} = xrSession.scene.createEntity("${this.escapeStringValue(obj.name, "Kotlin")}")`
|
|
728
|
-
);
|
|
729
|
-
const pos = this.findProp(obj, "position");
|
|
730
|
-
if (pos && Array.isArray(pos)) {
|
|
731
|
-
this.emit(
|
|
732
|
-
`${v}.setPose(Pose(${this.toKotlinFloat3(pos)}, Quaternion.identity()))`
|
|
733
|
-
);
|
|
734
|
-
}
|
|
735
|
-
const scale = this.findProp(obj, "scale");
|
|
736
|
-
if (scale && Array.isArray(scale)) {
|
|
737
|
-
this.emit(`${v}.setScale(${this.toKotlinFloat3(scale)})`);
|
|
738
|
-
} else if (typeof scale === "number") {
|
|
739
|
-
this.emit(`${v}.setScale(Float3(${scale}f, ${scale}f, ${scale}f))`);
|
|
740
|
-
}
|
|
741
|
-
const mat = this.findProp(obj, "material");
|
|
742
|
-
if (mat && typeof mat === "object") {
|
|
743
|
-
const m = mat;
|
|
744
|
-
if (m.color) this.emit(`val ${v}Color = ${this.toKotlinColor(m.color)}`);
|
|
745
|
-
if (m.roughness !== void 0) this.emit(`// roughness = ${m.roughness}f`);
|
|
746
|
-
if (m.metalness !== void 0) this.emit(`// metalness = ${m.metalness}f`);
|
|
747
|
-
}
|
|
748
|
-
this.emit(`// Geometry: ${this.mapShapeToFilament(meshType)}`);
|
|
749
|
-
}
|
|
750
|
-
const rot = this.findProp(obj, "rotation");
|
|
751
|
-
if (rot && Array.isArray(rot) && meshType !== "text") {
|
|
752
|
-
this.emit(`// Rotation: ${rot.join(", ")} deg`);
|
|
753
|
-
}
|
|
754
|
-
for (const t of obj.traits ?? []) {
|
|
755
|
-
if (t.name === "collidable") this.emit(`// Collision enabled for ${v}`);
|
|
756
|
-
else if (t.name === "physics") this.emit(`// Physics: mass=${t.config?.mass ?? 1}`);
|
|
757
|
-
else if (t.name === "grabbable") {
|
|
758
|
-
this.emit(`${v}.setOnInputEventListener { _: InputEvent -> /* grab */ }`);
|
|
759
|
-
} else if (t.name === "face_tracking") {
|
|
760
|
-
this.emitFaceTracking(v, t.config ?? {});
|
|
761
|
-
} else if (t.name === "follows_head" || t.name === "head_follow") {
|
|
762
|
-
this.emitHeadFollowUI(v, obj, t.config ?? {});
|
|
763
|
-
} else if (t.name === "drm_video" || t.name === "protected_video") {
|
|
764
|
-
this.emitDrmVideo(v, t.config ?? {});
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
for (const child of obj.children ?? []) this.emitObject(child);
|
|
768
|
-
}
|
|
769
|
-
// ─── Spatial Group ───────────────────────────────────────────────────
|
|
770
|
-
emitGroup(group) {
|
|
771
|
-
const v = this.sanitizeName(group.name);
|
|
772
|
-
this.emit("");
|
|
773
|
-
this.emit(`// Group: ${this.escapeStringValue(group.name, "Kotlin")}`);
|
|
774
|
-
this.emit(
|
|
775
|
-
`val ${v} = xrSession.scene.createEntity("${this.escapeStringValue(group.name, "Kotlin")}")`
|
|
776
|
-
);
|
|
777
|
-
for (const p of group.properties) {
|
|
778
|
-
if (p.key === "position" && Array.isArray(p.value))
|
|
779
|
-
this.emit(
|
|
780
|
-
`${v}.setPose(Pose(${this.toKotlinFloat3(p.value)}, Quaternion.identity()))`
|
|
781
|
-
);
|
|
782
|
-
else if (p.key === "scale" && Array.isArray(p.value))
|
|
783
|
-
this.emit(`${v}.setScale(${this.toKotlinFloat3(p.value)})`);
|
|
784
|
-
else if (p.key === "scale" && typeof p.value === "number")
|
|
785
|
-
this.emit(`${v}.setScale(Float3(${p.value}f, ${p.value}f, ${p.value}f))`);
|
|
786
|
-
}
|
|
787
|
-
for (const o of group.objects) {
|
|
788
|
-
this.emitObject(o);
|
|
789
|
-
this.emit(`${v}.addChild(${this.sanitizeName(o.name)})`);
|
|
790
|
-
}
|
|
791
|
-
for (const sub of group.groups ?? []) {
|
|
792
|
-
this.emitGroup(sub);
|
|
793
|
-
this.emit(`${v}.addChild(${this.sanitizeName(sub.name)})`);
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
// ─── Light ───────────────────────────────────────────────────────────
|
|
797
|
-
emitLight(light) {
|
|
798
|
-
const v = this.sanitizeName(light.name);
|
|
799
|
-
const typeMap = {
|
|
800
|
-
directional: "DIRECTIONAL",
|
|
801
|
-
point: "POINT",
|
|
802
|
-
spot: "SPOT",
|
|
803
|
-
ambient: "DIRECTIONAL",
|
|
804
|
-
hemisphere: "DIRECTIONAL",
|
|
805
|
-
area: "POINT"
|
|
806
|
-
};
|
|
807
|
-
this.emit("");
|
|
808
|
-
this.emit(
|
|
809
|
-
`val ${v} = xrSession.scene.createEntity("${this.escapeStringValue(light.name, "Kotlin")}")`
|
|
810
|
-
);
|
|
811
|
-
this.emit(`// Filament type: LightManager.Type.${typeMap[light.lightType] ?? "POINT"}`);
|
|
812
|
-
const color = light.properties.find((p) => p.key === "color");
|
|
813
|
-
const intensity = light.properties.find((p) => p.key === "intensity");
|
|
814
|
-
const pos = light.properties.find((p) => p.key === "position");
|
|
815
|
-
if (color) this.emit(`// Color: ${color.value}`);
|
|
816
|
-
if (intensity) this.emit(`// Intensity: ${intensity.value * 1e5}f lux`);
|
|
817
|
-
if (pos && Array.isArray(pos.value))
|
|
818
|
-
this.emit(
|
|
819
|
-
`${v}.setPose(Pose(${this.toKotlinFloat3(pos.value)}, Quaternion.identity()))`
|
|
820
|
-
);
|
|
821
|
-
}
|
|
822
|
-
// ─── Camera ──────────────────────────────────────────────────────────
|
|
823
|
-
emitCamera(cam) {
|
|
824
|
-
this.emit(`// Camera: ${cam.cameraType}`);
|
|
825
|
-
for (const p of cam.properties) {
|
|
826
|
-
if (p.key === "passthrough" && (p.value === true || p.value === "true"))
|
|
827
|
-
this.emit("xrSession.scene.configure { it.focusMode = Config.FocusMode.AUTO }");
|
|
828
|
-
else if (p.key === "fov") this.emit(`// FOV: ${p.value} -- managed by XR headset`);
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
// ─── Timeline ────────────────────────────────────────────────────────
|
|
832
|
-
emitTimeline(tl) {
|
|
833
|
-
const fn = this.sanitizeName(tl.name);
|
|
834
|
-
this.emit("");
|
|
835
|
-
this.emit(`private fun playTimeline_${fn}() {`);
|
|
836
|
-
this.indent();
|
|
837
|
-
this.emit("val set = AnimatorSet()");
|
|
838
|
-
this.emit("val anims = mutableListOf<ObjectAnimator>()");
|
|
839
|
-
for (const e of tl.entries) {
|
|
840
|
-
const ms = Math.round(e.time * 1e3);
|
|
841
|
-
if (e.action.kind === "animate") {
|
|
842
|
-
for (const [k, val] of Object.entries(e.action.properties)) {
|
|
843
|
-
if (typeof val === "number") {
|
|
844
|
-
this.emit(
|
|
845
|
-
`anims.add(ObjectAnimator.ofFloat(null, "${k}", ${val}f).apply { startDelay = ${ms}L; duration = 300L })`
|
|
846
|
-
);
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
} else if (e.action.kind === "emit") {
|
|
850
|
-
this.emit(`// @${e.time}s emit "${e.action.event}"`);
|
|
851
|
-
} else if (e.action.kind === "call") {
|
|
852
|
-
this.emit(`// @${e.time}s call ${e.action.method}()`);
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
this.emit("set.playTogether(anims as List<android.animation.Animator>)");
|
|
856
|
-
if (tl.loop) {
|
|
857
|
-
this.emit("set.addListener(object : android.animation.AnimatorListenerAdapter() {");
|
|
858
|
-
this.indent();
|
|
859
|
-
this.emit(
|
|
860
|
-
`override fun onAnimationEnd(a: android.animation.Animator) { playTimeline_${fn}() }`
|
|
861
|
-
);
|
|
862
|
-
this.dedent();
|
|
863
|
-
this.emit("})");
|
|
864
|
-
}
|
|
865
|
-
this.emit("set.start()");
|
|
866
|
-
this.dedent();
|
|
867
|
-
this.emit("}");
|
|
868
|
-
}
|
|
869
|
-
// ─── Audio ───────────────────────────────────────────────────────────
|
|
870
|
-
emitAudio(audio) {
|
|
871
|
-
const v = this.sanitizeName(audio.name);
|
|
872
|
-
const src = audio.properties.find((p) => p.key === "src" || p.key === "source")?.value;
|
|
873
|
-
const spatial = audio.properties.find((p) => p.key === "spatial")?.value;
|
|
874
|
-
const volume = audio.properties.find((p) => p.key === "volume")?.value ?? 1;
|
|
875
|
-
const loop = audio.properties.find((p) => p.key === "loop")?.value;
|
|
876
|
-
const pos = audio.properties.find((p) => p.key === "position")?.value;
|
|
877
|
-
this.emit("");
|
|
878
|
-
this.emit(`// Audio: ${this.escapeStringValue(audio.name, "Kotlin")}`);
|
|
879
|
-
if (src) {
|
|
880
|
-
this.emit(`val ${v}Id = soundPool.load(this, R.raw.${this.sanitizeName(src)}, 1)`);
|
|
881
|
-
}
|
|
882
|
-
if (spatial && pos && Array.isArray(pos)) {
|
|
883
|
-
this.emit(
|
|
884
|
-
`val ${v}Entity = xrSession.scene.createEntity("${this.escapeStringValue(audio.name, "Kotlin")}")`
|
|
885
|
-
);
|
|
886
|
-
this.emit(
|
|
887
|
-
`${v}Entity.setPose(Pose(${this.toKotlinFloat3(pos)}, Quaternion.identity()))`
|
|
888
|
-
);
|
|
889
|
-
this.emit(`val ${v}Track = SpatialAudioTrack(xrSession, ${v}Entity)`);
|
|
890
|
-
}
|
|
891
|
-
if (src) {
|
|
892
|
-
this.emit(`soundPool.setOnLoadCompleteListener { pool, id, _ ->`);
|
|
893
|
-
this.indent();
|
|
894
|
-
this.emit(
|
|
895
|
-
`if (id == ${v}Id) pool.play(id, ${volume}f, ${volume}f, 1, ${loop ? 1 : 0}, 1.0f)`
|
|
896
|
-
);
|
|
897
|
-
this.dedent();
|
|
898
|
-
this.emit("}");
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
// ─── UI ──────────────────────────────────────────────────────────────
|
|
902
|
-
emitUI(ui) {
|
|
903
|
-
this.emit("");
|
|
904
|
-
this.emit("// UI Layer");
|
|
905
|
-
for (const el of ui.elements) {
|
|
906
|
-
const vn = this.sanitizeName(el.name);
|
|
907
|
-
const w = el.properties.find((p) => p.key === "width")?.value ?? 400;
|
|
908
|
-
const h = el.properties.find((p) => p.key === "height")?.value ?? 300;
|
|
909
|
-
const text = el.properties.find((p) => p.key === "text")?.value;
|
|
910
|
-
const label = el.properties.find((p) => p.key === "label")?.value;
|
|
911
|
-
const elType = el.properties.find((p) => p.key === "type")?.value;
|
|
912
|
-
this.emit(`SpatialPanel(SubspaceModifier.width(${w}f).height(${h}f)) {`);
|
|
913
|
-
this.indent();
|
|
914
|
-
if (elType === "button" && label) {
|
|
915
|
-
this.emit(`Button(onClick = { /* ${vn} */ }) { Text("${label}") }`);
|
|
916
|
-
} else if (text) {
|
|
917
|
-
const color = el.properties.find((p) => p.key === "color")?.value;
|
|
918
|
-
this.emit(
|
|
919
|
-
`Text("${text}", color = ${color ? this.toKotlinColor(color) : "Color.White"})`
|
|
920
|
-
);
|
|
921
|
-
} else {
|
|
922
|
-
this.emit(
|
|
923
|
-
`Column(modifier = Modifier.fillMaxSize()) { Text("${this.escapeStringValue(el.name, "Kotlin")}") }`
|
|
924
|
-
);
|
|
925
|
-
}
|
|
926
|
-
this.dedent();
|
|
927
|
-
this.emit("}");
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
// ─── Zones ───────────────────────────────────────────────────────────
|
|
931
|
-
emitZone(zone) {
|
|
932
|
-
const v = this.sanitizeName(zone.name);
|
|
933
|
-
this.emit(
|
|
934
|
-
`val ${v} = xrSession.scene.createEntity("${this.escapeStringValue(zone.name, "Kotlin")}")`
|
|
935
|
-
);
|
|
936
|
-
const pos = zone.properties.find((p) => p.key === "position")?.value;
|
|
937
|
-
if (pos && Array.isArray(pos))
|
|
938
|
-
this.emit(
|
|
939
|
-
`${v}.setPose(Pose(${this.toKotlinFloat3(pos)}, Quaternion.identity()))`
|
|
940
|
-
);
|
|
941
|
-
const shape = zone.properties.find((p) => p.key === "shape")?.value;
|
|
942
|
-
const size = zone.properties.find((p) => p.key === "size")?.value;
|
|
943
|
-
const radius = zone.properties.find((p) => p.key === "radius")?.value;
|
|
944
|
-
if (shape === "box" && size && Array.isArray(size))
|
|
945
|
-
this.emit(`// Trigger volume: box ${size.join("x")}`);
|
|
946
|
-
else if (shape === "sphere" && radius) this.emit(`// Trigger volume: sphere r=${radius}`);
|
|
947
|
-
if (zone.handlers?.length)
|
|
948
|
-
this.emit(`// Handlers: ${zone.handlers.map((h) => h.event).join(", ")}`);
|
|
949
|
-
}
|
|
950
|
-
// ─── Effects ─────────────────────────────────────────────────────────
|
|
951
|
-
emitEffects(effects) {
|
|
952
|
-
this.emit("// Post-processing (Filament Renderer)");
|
|
953
|
-
for (const fx of effects.effects)
|
|
954
|
-
this.emit(`// ${fx.effectType}: ${JSON.stringify(fx.properties)}`);
|
|
955
|
-
}
|
|
956
|
-
// ─── DP3: Face Tracking with 68 Blendshapes ─────────────────────────
|
|
957
|
-
emitFaceTracking(varName, config) {
|
|
958
|
-
const blendshapes = config.blendshapes ?? [];
|
|
959
|
-
this.emit("");
|
|
960
|
-
this.emit(`// DP3: Face tracking \u2014 68 blendshapes via ARCore for Jetpack XR`);
|
|
961
|
-
this.emit(`// android.permission.FACE_TRACKING required`);
|
|
962
|
-
this.emit(`val ${varName}FaceConfig = xrSession.config.copy(`);
|
|
963
|
-
this.indent();
|
|
964
|
-
this.emit("faceTracking = FaceTrackingMode.BLEND_SHAPES");
|
|
965
|
-
this.dedent();
|
|
966
|
-
this.emit(")");
|
|
967
|
-
this.emit(`xrSession.configure(${varName}FaceConfig)`);
|
|
968
|
-
this.emit("");
|
|
969
|
-
this.emit(`val ${varName}Face = Face.getUserFace(xrSession)`);
|
|
970
|
-
this.emit(`${varName}Face?.state?.collect { faceState ->`);
|
|
971
|
-
this.indent();
|
|
972
|
-
this.emit(`if (faceState.trackingState != TrackingState.TRACKING) return@collect`);
|
|
973
|
-
if (blendshapes.length > 0) {
|
|
974
|
-
for (const bs of blendshapes) {
|
|
975
|
-
const bsConst = `FaceBlendShapeType.FACE_BLEND_SHAPE_TYPE_${bs.toUpperCase()}`;
|
|
976
|
-
this.emit(`val ${varName}_${this.sanitizeName(bs)} = faceState.blendShapes[${bsConst}]`);
|
|
977
|
-
}
|
|
978
|
-
} else {
|
|
979
|
-
this.emit(
|
|
980
|
-
"// Access blendshapes: faceState.blendShapes[FaceBlendShapeType.FACE_BLEND_SHAPE_TYPE_*]"
|
|
981
|
-
);
|
|
982
|
-
this.emit(
|
|
983
|
-
"// 68 blendshapes available: BROW_LOWERER_L/R, EYES_CLOSED_L/R, JAW_DROP, LIPS_TOWARD, etc."
|
|
984
|
-
);
|
|
985
|
-
}
|
|
986
|
-
this.emit(
|
|
987
|
-
`val ${varName}Confidence = faceState.getConfidence(FaceConfidenceRegion.FACE_CONFIDENCE_REGION_LOWER)`
|
|
988
|
-
);
|
|
989
|
-
this.dedent();
|
|
990
|
-
this.emit("}");
|
|
991
|
-
}
|
|
992
|
-
// ─── DP3: Head-Following UI via UserSubspace ──────────────────────
|
|
993
|
-
emitHeadFollowUI(varName, obj, config) {
|
|
994
|
-
const distance = config.distance ?? config.follow_distance ?? 1.5;
|
|
995
|
-
const w = this.findProp(obj, "width") ?? config.width ?? 400;
|
|
996
|
-
const h = this.findProp(obj, "height") ?? config.height ?? 300;
|
|
997
|
-
this.emit("");
|
|
998
|
-
this.emit(`// DP3: Head-following UI panel via UserSubspace`);
|
|
999
|
-
this.emit(`// android.permission.HEAD_TRACKING required`);
|
|
1000
|
-
this.emit(`// Content follows user's head at ${distance}m distance (soft-locking)`);
|
|
1001
|
-
this.emit(`SpatialPanel(SubspaceModifier.width(${w}f).height(${h}f)) {`);
|
|
1002
|
-
this.indent();
|
|
1003
|
-
const text = this.findProp(obj, "text");
|
|
1004
|
-
const label = this.findProp(obj, "label");
|
|
1005
|
-
if (text) {
|
|
1006
|
-
this.emit(`Text("${text}")`);
|
|
1007
|
-
} else if (label) {
|
|
1008
|
-
this.emit(`Text("${label}")`);
|
|
1009
|
-
} else {
|
|
1010
|
-
this.emit(
|
|
1011
|
-
`Column(modifier = Modifier.fillMaxSize()) { Text("${this.escapeStringValue(obj.name, "Kotlin")}") }`
|
|
1012
|
-
);
|
|
1013
|
-
}
|
|
1014
|
-
this.dedent();
|
|
1015
|
-
this.emit("}");
|
|
1016
|
-
}
|
|
1017
|
-
// ─── DP3: DRM-Protected Video via SurfaceEntity ───────────────────
|
|
1018
|
-
emitDrmVideo(varName, config) {
|
|
1019
|
-
const videoUri = config.uri ?? config.src ?? config.source ?? "";
|
|
1020
|
-
const licenseUri = config.license_uri ?? config.drm_license ?? "";
|
|
1021
|
-
const stereoMode = config.stereo_mode ?? "SIDE_BY_SIDE";
|
|
1022
|
-
const shape = config.shape ?? "quad";
|
|
1023
|
-
const width = config.width ?? 1;
|
|
1024
|
-
const height = config.height ?? 1;
|
|
1025
|
-
const radius = config.radius ?? 5;
|
|
1026
|
-
this.emit("");
|
|
1027
|
-
this.emit(`// DP3: DRM-protected video via SurfaceEntity + Widevine`);
|
|
1028
|
-
let shapeCode;
|
|
1029
|
-
if (shape === "sphere") {
|
|
1030
|
-
shapeCode = `SurfaceEntity.Shape.Sphere(${radius}f)`;
|
|
1031
|
-
} else if (shape === "hemisphere") {
|
|
1032
|
-
shapeCode = `SurfaceEntity.Shape.Hemisphere(${radius}f)`;
|
|
1033
|
-
} else {
|
|
1034
|
-
shapeCode = `SurfaceEntity.Shape.Quad(FloatSize2d(${width}f, ${height}f))`;
|
|
1035
|
-
}
|
|
1036
|
-
const stereoModeMap = {
|
|
1037
|
-
SIDE_BY_SIDE: "SurfaceEntity.StereoMode.SIDE_BY_SIDE",
|
|
1038
|
-
TOP_BOTTOM: "SurfaceEntity.StereoMode.TOP_BOTTOM",
|
|
1039
|
-
MONO: "SurfaceEntity.StereoMode.MONO",
|
|
1040
|
-
MULTIVIEW_LEFT: "SurfaceEntity.StereoMode.MULTIVIEW_LEFT_PRIMARY",
|
|
1041
|
-
MULTIVIEW_RIGHT: "SurfaceEntity.StereoMode.MULTIVIEW_RIGHT_PRIMARY"
|
|
1042
|
-
};
|
|
1043
|
-
const stereoModeKotlin = stereoModeMap[stereoMode] ?? "SurfaceEntity.StereoMode.SIDE_BY_SIDE";
|
|
1044
|
-
this.emit(`val ${varName}Surface = SurfaceEntity.create(`);
|
|
1045
|
-
this.indent();
|
|
1046
|
-
this.emit("session = xrSession,");
|
|
1047
|
-
this.emit(`stereoMode = ${stereoModeKotlin},`);
|
|
1048
|
-
this.emit("pose = Pose(Vector3(0f, 0f, -1.5f), Quaternion.identity()),");
|
|
1049
|
-
this.emit(`shape = ${shapeCode},`);
|
|
1050
|
-
this.emit("surfaceProtection = SurfaceEntity.SurfaceProtection.PROTECTED");
|
|
1051
|
-
this.dedent();
|
|
1052
|
-
this.emit(")");
|
|
1053
|
-
if (videoUri) {
|
|
1054
|
-
this.emit("");
|
|
1055
|
-
this.emit(`val ${varName}MediaItem = MediaItem.Builder()`);
|
|
1056
|
-
this.indent();
|
|
1057
|
-
this.emit(`.setUri("${videoUri}")`);
|
|
1058
|
-
if (licenseUri) {
|
|
1059
|
-
this.emit(".setDrmConfiguration(");
|
|
1060
|
-
this.indent();
|
|
1061
|
-
this.emit("MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID)");
|
|
1062
|
-
this.indent();
|
|
1063
|
-
this.emit(`.setLicenseUri("${licenseUri}")`);
|
|
1064
|
-
this.emit(".build()");
|
|
1065
|
-
this.dedent();
|
|
1066
|
-
this.dedent();
|
|
1067
|
-
this.emit(")");
|
|
1068
|
-
}
|
|
1069
|
-
this.emit(".build()");
|
|
1070
|
-
this.dedent();
|
|
1071
|
-
this.emit("");
|
|
1072
|
-
this.emit(`val ${varName}Player = ExoPlayer.Builder(this).build()`);
|
|
1073
|
-
this.emit(`${varName}Player.setVideoSurface(${varName}Surface.getSurface())`);
|
|
1074
|
-
this.emit(`${varName}Player.setMediaItem(${varName}MediaItem)`);
|
|
1075
|
-
this.emit(`${varName}Player.prepare()`);
|
|
1076
|
-
this.emit(`${varName}Player.play()`);
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
// ─── Domain Blocks (v4.2) ──────────────────────────────────────────
|
|
1080
|
-
compileAndroidXRDomainBlocks(composition) {
|
|
1081
|
-
const domainBlocks = composition.domainBlocks ?? [];
|
|
1082
|
-
if (domainBlocks.length === 0) return;
|
|
1083
|
-
this.emit("");
|
|
1084
|
-
this.emit("// === v4.2 Domain Blocks ===");
|
|
1085
|
-
let blockIdx = 0;
|
|
1086
|
-
const compiled = chunkHHS6FMOU_cjs.compileDomainBlocks(
|
|
1087
|
-
// @ts-expect-error During migration
|
|
1088
|
-
domainBlocks,
|
|
1089
|
-
{
|
|
1090
|
-
material: (block) => {
|
|
1091
|
-
const mat = chunkHHS6FMOU_cjs.compileMaterialBlock(block);
|
|
1092
|
-
return chunkHHS6FMOU_cjs.materialToAndroidXR(mat, `db${blockIdx++}`);
|
|
1093
|
-
},
|
|
1094
|
-
physics: (block) => {
|
|
1095
|
-
const phys = chunkHHS6FMOU_cjs.compilePhysicsBlock(block);
|
|
1096
|
-
return chunkHHS6FMOU_cjs.physicsToAndroidXR(phys, `db${blockIdx++}`);
|
|
1097
|
-
},
|
|
1098
|
-
vfx: (block) => {
|
|
1099
|
-
const ps = chunkHHS6FMOU_cjs.compileParticleBlock(block);
|
|
1100
|
-
return chunkHHS6FMOU_cjs.particlesToAndroidXR(ps, `db${blockIdx++}`);
|
|
1101
|
-
},
|
|
1102
|
-
postfx: (block) => {
|
|
1103
|
-
const pp = chunkHHS6FMOU_cjs.compilePostProcessingBlock(block);
|
|
1104
|
-
const postfx = pp;
|
|
1105
|
-
const effects = postfx.effects.map((e) => `// Effect: ${e.type} \u2014 ${JSON.stringify(e.properties)}`).join("\n");
|
|
1106
|
-
return `// Post-Processing: ${postfx.keyword} \u2014 configure via Filament Renderer post-processing
|
|
1107
|
-
${effects}`;
|
|
1108
|
-
},
|
|
1109
|
-
audio: (block) => {
|
|
1110
|
-
const audio = chunkHHS6FMOU_cjs.compileAudioSourceBlock(block);
|
|
1111
|
-
return chunkHHS6FMOU_cjs.audioSourceToAndroidXR(audio, `db${blockIdx++}`);
|
|
1112
|
-
},
|
|
1113
|
-
weather: (block) => {
|
|
1114
|
-
const weather = chunkHHS6FMOU_cjs.compileWeatherBlock(block);
|
|
1115
|
-
return chunkHHS6FMOU_cjs.weatherToAndroidXR(weather);
|
|
1116
|
-
}
|
|
1117
|
-
},
|
|
1118
|
-
(block) => `// Domain block: ${block.domain}/${block.keyword} "${this.escapeStringValue(block.name, "Kotlin")}"`
|
|
1119
|
-
);
|
|
1120
|
-
for (const line of compiled) {
|
|
1121
|
-
for (const l of line.split("\n")) {
|
|
1122
|
-
this.emit(l);
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
// ─── Transitions ─────────────────────────────────────────────────────
|
|
1127
|
-
emitTransition(tr) {
|
|
1128
|
-
const fn = this.sanitizeName(tr.name);
|
|
1129
|
-
const target = tr.properties.find((p) => p.key === "target")?.value;
|
|
1130
|
-
const dur = tr.properties.find((p) => p.key === "duration")?.value ?? 0.5;
|
|
1131
|
-
this.emit("");
|
|
1132
|
-
this.emit(`private fun transition_${fn}() {`);
|
|
1133
|
-
this.indent();
|
|
1134
|
-
this.emit(`// -> "${target}"`);
|
|
1135
|
-
this.emit(
|
|
1136
|
-
`ObjectAnimator.ofFloat(null, "alpha", 1f, 0f).apply { duration = ${Math.round(dur * 1e3)}L }.start()`
|
|
1137
|
-
);
|
|
1138
|
-
this.dedent();
|
|
1139
|
-
this.emit("}");
|
|
1140
|
-
}
|
|
1141
|
-
// ─── Value Conversion Helpers ────────────────────────────────────────
|
|
1142
|
-
/** Converts [x, y, z] to `Float3(xf, yf, zf)`. */
|
|
1143
|
-
toKotlinFloat3(arr) {
|
|
1144
|
-
if (Array.isArray(arr) && arr.length >= 3) return `Float3(${arr[0]}f, ${arr[1]}f, ${arr[2]}f)`;
|
|
1145
|
-
if (Array.isArray(arr) && arr.length >= 1) return `Float3(${arr[0]}f, ${arr[0]}f, ${arr[0]}f)`;
|
|
1146
|
-
const v = typeof arr === "number" ? arr : 0;
|
|
1147
|
-
return `Float3(${v}f, ${v}f, ${v}f)`;
|
|
1148
|
-
}
|
|
1149
|
-
/** Converts `#RRGGBB` or `#RRGGBBAA` to `Color(0xAARRGGBB)`. */
|
|
1150
|
-
toKotlinColor(hex) {
|
|
1151
|
-
if (typeof hex === "string" && hex.startsWith("#")) {
|
|
1152
|
-
const raw = hex.slice(1);
|
|
1153
|
-
if (raw.length === 6) return `Color(0xFF${raw.toUpperCase()})`;
|
|
1154
|
-
if (raw.length === 8) {
|
|
1155
|
-
const [rr, gg, bb, aa] = [
|
|
1156
|
-
raw.substring(0, 2),
|
|
1157
|
-
raw.substring(2, 4),
|
|
1158
|
-
raw.substring(4, 6),
|
|
1159
|
-
raw.substring(6, 8)
|
|
1160
|
-
];
|
|
1161
|
-
return `Color(0x${aa.toUpperCase()}${rr.toUpperCase()}${gg.toUpperCase()}${bb.toUpperCase()})`;
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
return "Color.White";
|
|
1165
|
-
}
|
|
1166
|
-
/** Maps HoloScript shape names to Filament geometric primitive descriptions. */
|
|
1167
|
-
mapShapeToFilament(type) {
|
|
1168
|
-
const map = {
|
|
1169
|
-
cube: "BoxShape",
|
|
1170
|
-
box: "BoxShape",
|
|
1171
|
-
sphere: "IcoSphereShape",
|
|
1172
|
-
cylinder: "CylinderShape",
|
|
1173
|
-
cone: "ConeShape",
|
|
1174
|
-
plane: "QuadShape",
|
|
1175
|
-
torus: "CustomRingGeometry",
|
|
1176
|
-
capsule: "CapsuleShape"
|
|
1177
|
-
};
|
|
1178
|
-
return map[type] ?? `Unknown("${type}")`;
|
|
1179
|
-
}
|
|
1180
|
-
// ─── Generic Helpers ─────────────────────────────────────────────────
|
|
1181
|
-
toKotlinType(value) {
|
|
1182
|
-
if (value === null) return "Any?";
|
|
1183
|
-
if (typeof value === "boolean") return "Boolean";
|
|
1184
|
-
if (typeof value === "number") return Number.isInteger(value) ? "Int" : "Float";
|
|
1185
|
-
if (typeof value === "string") return "String";
|
|
1186
|
-
if (Array.isArray(value)) {
|
|
1187
|
-
if (value.length === 3 && value.every((v) => typeof v === "number")) return "Vector3";
|
|
1188
|
-
return "List<Any>";
|
|
1189
|
-
}
|
|
1190
|
-
return "Any";
|
|
1191
|
-
}
|
|
1192
|
-
toKotlinValue(value) {
|
|
1193
|
-
if (typeof value === "number") return Number.isInteger(value) ? `${value}` : `${value}f`;
|
|
1194
|
-
if (typeof value === "boolean") return value ? "true" : "false";
|
|
1195
|
-
if (typeof value === "string") return `"${this.escapeStringValue(value, "Kotlin")}"`;
|
|
1196
|
-
if (value === null) return "null";
|
|
1197
|
-
if (Array.isArray(value))
|
|
1198
|
-
return `listOf(${value.map((v) => this.toKotlinValue(v)).join(", ")})`;
|
|
1199
|
-
return "null";
|
|
1200
|
-
}
|
|
1201
|
-
findProp(obj, key) {
|
|
1202
|
-
return obj.properties?.find((p) => p.key === key)?.value;
|
|
1203
|
-
}
|
|
1204
|
-
sanitizeName(name) {
|
|
1205
|
-
return name.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
1206
|
-
}
|
|
1207
|
-
emit(line) {
|
|
1208
|
-
this.lines.push(this.options.indent.repeat(this.indentLevel) + line);
|
|
1209
|
-
}
|
|
1210
|
-
indent() {
|
|
1211
|
-
this.indentLevel++;
|
|
1212
|
-
}
|
|
1213
|
-
dedent() {
|
|
1214
|
-
if (this.indentLevel > 0) this.indentLevel--;
|
|
1215
|
-
}
|
|
1216
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
1217
|
-
// AI GLASSES MODE — Jetpack Compose Glimmer + Jetpack Projected
|
|
1218
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
1219
|
-
/**
|
|
1220
|
-
* Generates the main Activity file for AI glasses form factor.
|
|
1221
|
-
*
|
|
1222
|
-
* Uses Jetpack Compose Glimmer (GlimmerTheme, Card, Button, Text) instead of
|
|
1223
|
-
* Material3 + SceneCore, and integrates ProjectedContext /
|
|
1224
|
-
* ProjectedDeviceController for hardware access.
|
|
1225
|
-
*/
|
|
1226
|
-
generateGlassesActivityFile(composition) {
|
|
1227
|
-
this.lines = [];
|
|
1228
|
-
this.indentLevel = 0;
|
|
1229
|
-
this.emit("// Auto-generated by HoloScript AndroidXRCompiler (AI Glasses mode)");
|
|
1230
|
-
this.emit(
|
|
1231
|
-
`// Source: composition "${this.escapeStringValue(composition.name, "Kotlin")}"`
|
|
1232
|
-
);
|
|
1233
|
-
this.emit("// Form factor: AI Glasses (Jetpack Compose Glimmer + Jetpack Projected)");
|
|
1234
|
-
this.emit("// Do not edit manually -- regenerate from .holo source");
|
|
1235
|
-
this.emit("");
|
|
1236
|
-
this.emit(`package ${this.options.packageName}`);
|
|
1237
|
-
this.emit("");
|
|
1238
|
-
this.emitGlassesImports(composition);
|
|
1239
|
-
this.emit("");
|
|
1240
|
-
this.emitGlassesActivityClass(composition);
|
|
1241
|
-
return this.lines.join("\n");
|
|
1242
|
-
}
|
|
1243
|
-
/**
|
|
1244
|
-
* Emits Glimmer-specific imports for glasses mode.
|
|
1245
|
-
*/
|
|
1246
|
-
emitGlassesImports(composition) {
|
|
1247
|
-
this.emit("import android.os.Bundle");
|
|
1248
|
-
this.emit("");
|
|
1249
|
-
this.emit("import androidx.activity.ComponentActivity");
|
|
1250
|
-
this.emit("import androidx.activity.compose.setContent");
|
|
1251
|
-
this.emit("import androidx.compose.runtime.*");
|
|
1252
|
-
this.emit("import androidx.compose.foundation.layout.*");
|
|
1253
|
-
this.emit("import androidx.compose.ui.Alignment");
|
|
1254
|
-
this.emit("import androidx.compose.ui.Modifier");
|
|
1255
|
-
this.emit("import androidx.compose.ui.graphics.Color");
|
|
1256
|
-
this.emit("import androidx.lifecycle.lifecycleScope");
|
|
1257
|
-
this.emit("");
|
|
1258
|
-
this.emit("import androidx.xr.glimmer.GlimmerTheme");
|
|
1259
|
-
this.emit("import androidx.xr.glimmer.Card");
|
|
1260
|
-
this.emit("import androidx.xr.glimmer.Button");
|
|
1261
|
-
this.emit("import androidx.xr.glimmer.Text");
|
|
1262
|
-
this.emit("import androidx.xr.glimmer.ListItem");
|
|
1263
|
-
this.emit("import androidx.xr.glimmer.TitleChip");
|
|
1264
|
-
this.emit("import androidx.xr.glimmer.surface");
|
|
1265
|
-
this.emit("");
|
|
1266
|
-
this.emit("import androidx.xr.projected.ProjectedContext");
|
|
1267
|
-
this.emit("import androidx.xr.projected.ProjectedDeviceController");
|
|
1268
|
-
this.emit(
|
|
1269
|
-
"import androidx.xr.projected.ProjectedDeviceController.Companion.CAPABILITY_VISUAL_UI"
|
|
1270
|
-
);
|
|
1271
|
-
this.emit("import androidx.xr.projected.ProjectedDisplayController");
|
|
1272
|
-
this.emit("");
|
|
1273
|
-
this.emit("import kotlinx.coroutines.launch");
|
|
1274
|
-
this.emit("");
|
|
1275
|
-
if (this.options.useARCore) {
|
|
1276
|
-
this.emit("// ARCore for Jetpack XR (motion tracking + geospatial on glasses)");
|
|
1277
|
-
this.emit("import com.google.ar.core.Config");
|
|
1278
|
-
}
|
|
1279
|
-
if (composition.audio?.length) {
|
|
1280
|
-
this.emit("import android.media.AudioAttributes");
|
|
1281
|
-
this.emit("import android.media.SoundPool");
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1284
|
-
/**
|
|
1285
|
-
* Emits the AI glasses Activity class using GlimmerTheme and Projected API.
|
|
1286
|
-
*/
|
|
1287
|
-
emitGlassesActivityClass(composition) {
|
|
1288
|
-
this.emit("@OptIn(ExperimentalProjectedApi::class)");
|
|
1289
|
-
this.emit(`class ${this.options.activityName} : ComponentActivity() {`);
|
|
1290
|
-
this.indent();
|
|
1291
|
-
this.emit("private var displayController: ProjectedDisplayController? = null");
|
|
1292
|
-
this.emit("private var isVisualUiSupported by mutableStateOf(false)");
|
|
1293
|
-
this.emit("private var areVisualsOn by mutableStateOf(true)");
|
|
1294
|
-
if (composition.audio?.length) {
|
|
1295
|
-
this.emit("private lateinit var soundPool: SoundPool");
|
|
1296
|
-
}
|
|
1297
|
-
this.emit("");
|
|
1298
|
-
this.emit("override fun onCreate(savedInstanceState: Bundle?) {");
|
|
1299
|
-
this.indent();
|
|
1300
|
-
this.emit("super.onCreate(savedInstanceState)");
|
|
1301
|
-
this.emit("");
|
|
1302
|
-
this.emit("// Detect glasses display capability via Projected API");
|
|
1303
|
-
this.emit("lifecycleScope.launch {");
|
|
1304
|
-
this.indent();
|
|
1305
|
-
this.emit(
|
|
1306
|
-
"val projectedDeviceController = ProjectedDeviceController.create(this@" + this.options.activityName + ")"
|
|
1307
|
-
);
|
|
1308
|
-
this.emit(
|
|
1309
|
-
"isVisualUiSupported = projectedDeviceController.capabilities.contains(CAPABILITY_VISUAL_UI)"
|
|
1310
|
-
);
|
|
1311
|
-
this.dedent();
|
|
1312
|
-
this.emit("}");
|
|
1313
|
-
this.emit("");
|
|
1314
|
-
if (composition.audio?.length) {
|
|
1315
|
-
this.emit("val audioAttrs = AudioAttributes.Builder()");
|
|
1316
|
-
this.indent();
|
|
1317
|
-
this.emit(".setUsage(AudioAttributes.USAGE_GAME)");
|
|
1318
|
-
this.emit(".setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).build()");
|
|
1319
|
-
this.dedent();
|
|
1320
|
-
this.emit(
|
|
1321
|
-
"soundPool = SoundPool.Builder().setMaxStreams(8).setAudioAttributes(audioAttrs).build()"
|
|
1322
|
-
);
|
|
1323
|
-
this.emit("");
|
|
1324
|
-
}
|
|
1325
|
-
this.emit("setContent {");
|
|
1326
|
-
this.indent();
|
|
1327
|
-
this.emit("GlimmerTheme {");
|
|
1328
|
-
this.indent();
|
|
1329
|
-
this.emit(`${this.sanitizeName(composition.name)}GlassesScreen(`);
|
|
1330
|
-
this.indent();
|
|
1331
|
-
this.emit("areVisualsOn = areVisualsOn,");
|
|
1332
|
-
this.emit("isVisualUiSupported = isVisualUiSupported,");
|
|
1333
|
-
this.emit(`onClose = { finish() }`);
|
|
1334
|
-
this.dedent();
|
|
1335
|
-
this.emit(")");
|
|
1336
|
-
this.dedent();
|
|
1337
|
-
this.emit("}");
|
|
1338
|
-
this.dedent();
|
|
1339
|
-
this.emit("}");
|
|
1340
|
-
this.dedent();
|
|
1341
|
-
this.emit("}");
|
|
1342
|
-
this.emit("");
|
|
1343
|
-
this.emitGlassesSceneComposable(composition);
|
|
1344
|
-
this.dedent();
|
|
1345
|
-
this.emit("}");
|
|
1346
|
-
}
|
|
1347
|
-
/**
|
|
1348
|
-
* Emits the main Glimmer composable screen for AI glasses.
|
|
1349
|
-
*
|
|
1350
|
-
* Generates lightweight AR overlay UI using Glimmer components (Card, Text, Button,
|
|
1351
|
-
* ListItem, TitleChip) instead of the immersive SceneCore entity hierarchy used
|
|
1352
|
-
* in headset mode.
|
|
1353
|
-
*/
|
|
1354
|
-
emitGlassesSceneComposable(composition) {
|
|
1355
|
-
const name = this.sanitizeName(composition.name);
|
|
1356
|
-
this.emit("@Composable");
|
|
1357
|
-
this.emit(`private fun ${name}GlassesScreen(`);
|
|
1358
|
-
this.indent();
|
|
1359
|
-
this.emit("areVisualsOn: Boolean,");
|
|
1360
|
-
this.emit("isVisualUiSupported: Boolean,");
|
|
1361
|
-
this.emit("onClose: () -> Unit,");
|
|
1362
|
-
this.emit("modifier: Modifier = Modifier");
|
|
1363
|
-
this.dedent();
|
|
1364
|
-
this.emit(") {");
|
|
1365
|
-
this.indent();
|
|
1366
|
-
if (composition.state) {
|
|
1367
|
-
for (const p of composition.state.properties) {
|
|
1368
|
-
this.emit(
|
|
1369
|
-
`var ${this.escapeStringValue(p.key, "Kotlin")} by remember { mutableStateOf(${this.toKotlinValue(p.value)}) }`
|
|
1370
|
-
);
|
|
1371
|
-
}
|
|
1372
|
-
this.emit("");
|
|
1373
|
-
}
|
|
1374
|
-
this.emit("Box(");
|
|
1375
|
-
this.indent();
|
|
1376
|
-
this.emit("modifier = modifier");
|
|
1377
|
-
this.indent();
|
|
1378
|
-
this.emit(".surface(focusable = false)");
|
|
1379
|
-
this.emit(".fillMaxSize(),");
|
|
1380
|
-
this.dedent();
|
|
1381
|
-
this.emit("contentAlignment = Alignment.Center");
|
|
1382
|
-
this.dedent();
|
|
1383
|
-
this.emit(") {");
|
|
1384
|
-
this.indent();
|
|
1385
|
-
this.emit("if (isVisualUiSupported && areVisualsOn) {");
|
|
1386
|
-
this.indent();
|
|
1387
|
-
this.emitGlimmerContentTree(composition);
|
|
1388
|
-
this.dedent();
|
|
1389
|
-
this.emit("} else {");
|
|
1390
|
-
this.indent();
|
|
1391
|
-
this.emit("// Audio-only fallback for glasses without display");
|
|
1392
|
-
this.emit('Text("Audio Guidance Mode Active")');
|
|
1393
|
-
this.dedent();
|
|
1394
|
-
this.emit("}");
|
|
1395
|
-
this.dedent();
|
|
1396
|
-
this.emit("}");
|
|
1397
|
-
this.dedent();
|
|
1398
|
-
this.emit("}");
|
|
1399
|
-
this.emit("");
|
|
1400
|
-
}
|
|
1401
|
-
/**
|
|
1402
|
-
* Emits Glimmer-based content tree: converts HoloScript objects/UI into
|
|
1403
|
-
* Card, TitleChip, ListItem, Button, and Text composables.
|
|
1404
|
-
*/
|
|
1405
|
-
emitGlimmerContentTree(composition) {
|
|
1406
|
-
this.emit("Column(");
|
|
1407
|
-
this.indent();
|
|
1408
|
-
this.emit("modifier = Modifier.fillMaxSize(),");
|
|
1409
|
-
this.emit("verticalArrangement = Arrangement.spacedBy(8.dp)");
|
|
1410
|
-
this.dedent();
|
|
1411
|
-
this.emit(") {");
|
|
1412
|
-
this.indent();
|
|
1413
|
-
this.emit(
|
|
1414
|
-
`TitleChip(title = "${this.escapeStringValue(composition.name, "Kotlin")}")`
|
|
1415
|
-
);
|
|
1416
|
-
this.emit("");
|
|
1417
|
-
for (const obj of composition.objects ?? []) {
|
|
1418
|
-
this.emitGlimmerObject(obj);
|
|
1419
|
-
}
|
|
1420
|
-
if (composition.ui) {
|
|
1421
|
-
this.emit("");
|
|
1422
|
-
this.emit("// UI Elements");
|
|
1423
|
-
for (const el of composition.ui.elements) {
|
|
1424
|
-
this.emitGlimmerUIElement(el);
|
|
1425
|
-
}
|
|
1426
|
-
}
|
|
1427
|
-
this.emit("");
|
|
1428
|
-
this.emit("Button(onClick = onClose) {");
|
|
1429
|
-
this.indent();
|
|
1430
|
-
this.emit('Text("Close")');
|
|
1431
|
-
this.dedent();
|
|
1432
|
-
this.emit("}");
|
|
1433
|
-
this.dedent();
|
|
1434
|
-
this.emit("}");
|
|
1435
|
-
}
|
|
1436
|
-
/**
|
|
1437
|
-
* Converts a HoloObject into a Glimmer Card composable.
|
|
1438
|
-
*
|
|
1439
|
-
* For glasses mode, 3D objects are represented as information cards showing
|
|
1440
|
-
* the object's name, type, and properties — the glasses display provides
|
|
1441
|
-
* AR overlay information rather than full 3D rendering.
|
|
1442
|
-
*/
|
|
1443
|
-
emitGlimmerObject(obj) {
|
|
1444
|
-
const v = this.sanitizeName(obj.name);
|
|
1445
|
-
const meshType = this.findProp(obj, "mesh") ?? this.findProp(obj, "type") ?? "object";
|
|
1446
|
-
const modelSrc = this.findProp(obj, "model") ?? this.findProp(obj, "src");
|
|
1447
|
-
const text = this.findProp(obj, "text");
|
|
1448
|
-
this.emit(
|
|
1449
|
-
`// Object: ${this.escapeStringValue(obj.name, "Kotlin")} (glasses overlay)`
|
|
1450
|
-
);
|
|
1451
|
-
if (text) {
|
|
1452
|
-
const color = this.findProp(obj, "color");
|
|
1453
|
-
this.emit(
|
|
1454
|
-
`Text("${text}"${color ? `, color = ${this.toKotlinColor(color)}` : ""})`
|
|
1455
|
-
);
|
|
1456
|
-
} else if (modelSrc) {
|
|
1457
|
-
this.emit(`Card(`);
|
|
1458
|
-
this.indent();
|
|
1459
|
-
this.emit(`title = { Text("${this.escapeStringValue(obj.name, "Kotlin")}") },`);
|
|
1460
|
-
this.emit(`action = {`);
|
|
1461
|
-
this.indent();
|
|
1462
|
-
this.emit(`Button(onClick = { /* interact with ${v} */ }) {`);
|
|
1463
|
-
this.indent();
|
|
1464
|
-
this.emit('Text("View")');
|
|
1465
|
-
this.dedent();
|
|
1466
|
-
this.emit("}");
|
|
1467
|
-
this.dedent();
|
|
1468
|
-
this.emit("}");
|
|
1469
|
-
this.dedent();
|
|
1470
|
-
this.emit(") {");
|
|
1471
|
-
this.indent();
|
|
1472
|
-
this.emit(`Text("3D Model: ${modelSrc}")`);
|
|
1473
|
-
const pos = this.findProp(obj, "position");
|
|
1474
|
-
if (pos && Array.isArray(pos)) {
|
|
1475
|
-
this.emit(`Text("Position: ${pos.join(", ")}")`);
|
|
1476
|
-
}
|
|
1477
|
-
this.dedent();
|
|
1478
|
-
this.emit("}");
|
|
1479
|
-
} else {
|
|
1480
|
-
this.emit(`ListItem(`);
|
|
1481
|
-
this.indent();
|
|
1482
|
-
this.emit(
|
|
1483
|
-
`headlineContent = { Text("${this.escapeStringValue(obj.name, "Kotlin")}") },`
|
|
1484
|
-
);
|
|
1485
|
-
this.emit(`supportingContent = { Text("Type: ${meshType}") }`);
|
|
1486
|
-
this.dedent();
|
|
1487
|
-
this.emit(")");
|
|
1488
|
-
}
|
|
1489
|
-
this.emit("");
|
|
1490
|
-
for (const child of obj.children ?? []) {
|
|
1491
|
-
this.emitGlimmerObject(child);
|
|
1492
|
-
}
|
|
1493
|
-
}
|
|
1494
|
-
/**
|
|
1495
|
-
* Converts a HoloUI element into appropriate Glimmer composable.
|
|
1496
|
-
*/
|
|
1497
|
-
emitGlimmerUIElement(el) {
|
|
1498
|
-
const vn = this.sanitizeName(el.name);
|
|
1499
|
-
const text = el.properties.find((p) => p.key === "text")?.value;
|
|
1500
|
-
const label = el.properties.find((p) => p.key === "label")?.value;
|
|
1501
|
-
const elType = el.properties.find((p) => p.key === "type")?.value;
|
|
1502
|
-
if (elType === "button" && label) {
|
|
1503
|
-
this.emit(`Button(onClick = { /* ${vn} */ }) { Text("${label}") }`);
|
|
1504
|
-
} else if (text) {
|
|
1505
|
-
const color = el.properties.find((p) => p.key === "color")?.value;
|
|
1506
|
-
this.emit(
|
|
1507
|
-
`Text("${text}"${color ? `, color = ${this.toKotlinColor(color)}` : ""})`
|
|
1508
|
-
);
|
|
1509
|
-
} else {
|
|
1510
|
-
this.emit(
|
|
1511
|
-
`Card(title = { Text("${this.escapeStringValue(el.name, "Kotlin")}") }) {`
|
|
1512
|
-
);
|
|
1513
|
-
this.indent();
|
|
1514
|
-
this.emit(`Text("${this.escapeStringValue(el.name, "Kotlin")}")`);
|
|
1515
|
-
this.dedent();
|
|
1516
|
-
this.emit("}");
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
|
-
// ─── Glasses Manifest ──────────────────────────────────────────────
|
|
1520
|
-
/**
|
|
1521
|
-
* Generates AndroidManifest.xml for AI glasses form factor.
|
|
1522
|
-
*
|
|
1523
|
-
* Key differences from headset:
|
|
1524
|
-
* - requiredDisplayCategory="xr_projected" on the activity
|
|
1525
|
-
* - No XR headtracking hardware requirement
|
|
1526
|
-
* - Camera permission for glasses camera via ProjectedContext
|
|
1527
|
-
*/
|
|
1528
|
-
generateGlassesManifestFile(composition) {
|
|
1529
|
-
const pkg = this.options.packageName;
|
|
1530
|
-
const activityName = this.options.activityName;
|
|
1531
|
-
const permissions = [];
|
|
1532
|
-
permissions.push(' <uses-permission android:name="android.permission.CAMERA" />');
|
|
1533
|
-
permissions.push(' <uses-permission android:name="android.permission.RECORD_AUDIO" />');
|
|
1534
|
-
permissions.push(' <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />');
|
|
1535
|
-
const hasHandTracking = composition.objects?.some(
|
|
1536
|
-
(o) => o.traits?.some((t) => t.name === "hand_tracking")
|
|
1537
|
-
);
|
|
1538
|
-
if (hasHandTracking) {
|
|
1539
|
-
permissions.push(' <uses-permission android:name="android.permission.HAND_TRACKING" />');
|
|
1540
|
-
}
|
|
1541
|
-
return `<?xml version="1.0" encoding="utf-8"?>
|
|
1542
|
-
<!-- Auto-generated by HoloScript AndroidXRCompiler (AI Glasses mode) -->
|
|
1543
|
-
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
1544
|
-
package="${pkg}">
|
|
1545
|
-
|
|
1546
|
-
<!-- Glasses Permissions -->
|
|
1547
|
-
${permissions.join("\n")}
|
|
1548
|
-
|
|
1549
|
-
<application
|
|
1550
|
-
android:allowBackup="true"
|
|
1551
|
-
android:label="${this.escapeStringValue(composition.name, "XML")}"
|
|
1552
|
-
android:supportsRtl="true"
|
|
1553
|
-
android:theme="@style/Theme.MaterialComponents.DayNight.NoActionBar">
|
|
1554
|
-
|
|
1555
|
-
<!-- Android XR Glasses metadata -->
|
|
1556
|
-
<meta-data android:name="com.google.ar.core" android:value="optional" />
|
|
1557
|
-
|
|
1558
|
-
<activity
|
|
1559
|
-
android:name=".${activityName}"
|
|
1560
|
-
android:exported="true"
|
|
1561
|
-
android:requiredDisplayCategory="xr_projected"
|
|
1562
|
-
android:label="${this.escapeStringValue(composition.name, "XML")} AI Glasses">
|
|
1563
|
-
<intent-filter>
|
|
1564
|
-
<action android:name="android.intent.action.MAIN" />
|
|
1565
|
-
</intent-filter>
|
|
1566
|
-
</activity>
|
|
1567
|
-
</application>
|
|
1568
|
-
</manifest>`;
|
|
1569
|
-
}
|
|
1570
|
-
// ─── Glasses Build Gradle ──────────────────────────────────────────
|
|
1571
|
-
/**
|
|
1572
|
-
* Generates build.gradle.kts for AI glasses form factor.
|
|
1573
|
-
*
|
|
1574
|
-
* Includes Jetpack Compose Glimmer and Jetpack Projected dependencies
|
|
1575
|
-
* instead of the full SceneCore/Filament stack used in headset mode.
|
|
1576
|
-
*/
|
|
1577
|
-
generateGlassesBuildGradle(composition) {
|
|
1578
|
-
return `// Auto-generated by HoloScript AndroidXRCompiler (AI Glasses mode)
|
|
1579
|
-
// Source: ${this.escapeStringValue(composition.name, "Kotlin")}
|
|
1580
|
-
|
|
1581
|
-
plugins {
|
|
1582
|
-
id("com.android.application")
|
|
1583
|
-
id("org.jetbrains.kotlin.android")
|
|
1584
|
-
id("org.jetbrains.kotlin.plugin.compose")
|
|
1585
|
-
}
|
|
1586
|
-
|
|
1587
|
-
android {
|
|
1588
|
-
namespace = "${this.options.packageName}"
|
|
1589
|
-
compileSdk = ${this.options.targetSdk}
|
|
1590
|
-
|
|
1591
|
-
defaultConfig {
|
|
1592
|
-
applicationId = "${this.options.packageName}"
|
|
1593
|
-
minSdk = ${this.options.minSdk}
|
|
1594
|
-
targetSdk = ${this.options.targetSdk}
|
|
1595
|
-
versionCode = 1
|
|
1596
|
-
versionName = "1.0"
|
|
1597
|
-
}
|
|
1598
|
-
|
|
1599
|
-
buildTypes {
|
|
1600
|
-
release {
|
|
1601
|
-
isMinifyEnabled = false
|
|
1602
|
-
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
|
1603
|
-
}
|
|
1604
|
-
}
|
|
1605
|
-
|
|
1606
|
-
compileOptions {
|
|
1607
|
-
sourceCompatibility = JavaVersion.VERSION_17
|
|
1608
|
-
targetCompatibility = JavaVersion.VERSION_17
|
|
1609
|
-
}
|
|
1610
|
-
|
|
1611
|
-
kotlinOptions {
|
|
1612
|
-
jvmTarget = "17"
|
|
1613
|
-
}
|
|
1614
|
-
|
|
1615
|
-
buildFeatures {
|
|
1616
|
-
compose = true
|
|
1617
|
-
}
|
|
1618
|
-
}
|
|
1619
|
-
|
|
1620
|
-
dependencies {
|
|
1621
|
-
// Core
|
|
1622
|
-
implementation("androidx.core:core-ktx:1.13.1")
|
|
1623
|
-
implementation("androidx.appcompat:appcompat:1.7.0")
|
|
1624
|
-
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.0")
|
|
1625
|
-
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.8.0")
|
|
1626
|
-
implementation("androidx.activity:activity-compose:1.9.0")
|
|
1627
|
-
|
|
1628
|
-
// Jetpack Compose
|
|
1629
|
-
implementation(platform("androidx.compose:compose-bom:2024.06.00"))
|
|
1630
|
-
implementation("androidx.compose.ui:ui")
|
|
1631
|
-
implementation("androidx.compose.runtime:runtime")
|
|
1632
|
-
|
|
1633
|
-
// Jetpack Compose Glimmer \u2014 AI Glasses UI toolkit
|
|
1634
|
-
implementation("androidx.xr.glimmer:glimmer:1.0.0-alpha06")
|
|
1635
|
-
|
|
1636
|
-
// Jetpack Projected \u2014 AI Glasses hardware access
|
|
1637
|
-
implementation("androidx.xr.projected:projected:1.0.0-alpha04")
|
|
1638
|
-
|
|
1639
|
-
// Android XR Runtime
|
|
1640
|
-
implementation("androidx.xr.runtime:runtime:1.0.0-alpha11")
|
|
1641
|
-
implementation("androidx.xr:xr:1.0.0-alpha01")
|
|
1642
|
-
|
|
1643
|
-
// ARCore for Jetpack XR (motion tracking + geospatial on glasses)
|
|
1644
|
-
implementation("androidx.xr.arcore:arcore:1.0.0-alpha10")
|
|
1645
|
-
|
|
1646
|
-
// CameraX (for glasses camera via ProjectedContext)
|
|
1647
|
-
implementation("androidx.camera:camera-core:1.4.0")
|
|
1648
|
-
implementation("androidx.camera:camera-camera2:1.4.0")
|
|
1649
|
-
implementation("androidx.camera:camera-lifecycle:1.4.0")
|
|
1650
|
-
|
|
1651
|
-
// Coroutines
|
|
1652
|
-
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0")
|
|
1653
|
-
}`;
|
|
1654
|
-
}
|
|
1655
|
-
// ─── Glimmer Components File ──────────────────────────────────────
|
|
1656
|
-
/**
|
|
1657
|
-
* Generates a dedicated Glimmer components file with reusable composables
|
|
1658
|
-
* for the AI glasses experience.
|
|
1659
|
-
*
|
|
1660
|
-
* This file contains:
|
|
1661
|
-
* - GlimmerOverlay: Root overlay composable for AR content
|
|
1662
|
-
* - GlimmerInfoCard: Object information card
|
|
1663
|
-
* - GlimmerActionButton: Touchpad-friendly action button
|
|
1664
|
-
* - ProjectedCameraPreview: Camera integration via ProjectedContext
|
|
1665
|
-
*/
|
|
1666
|
-
generateGlimmerComponentsFile(composition) {
|
|
1667
|
-
this.lines = [];
|
|
1668
|
-
this.indentLevel = 0;
|
|
1669
|
-
const pkg = this.options.packageName;
|
|
1670
|
-
const name = this.sanitizeName(composition.name);
|
|
1671
|
-
this.emit("// Auto-generated by HoloScript AndroidXRCompiler (AI Glasses mode)");
|
|
1672
|
-
this.emit(
|
|
1673
|
-
`// Glimmer Components: ${this.escapeStringValue(composition.name, "Kotlin")}`
|
|
1674
|
-
);
|
|
1675
|
-
this.emit("");
|
|
1676
|
-
this.emit(`package ${pkg}`);
|
|
1677
|
-
this.emit("");
|
|
1678
|
-
this.emit("import androidx.compose.runtime.*");
|
|
1679
|
-
this.emit("import androidx.compose.foundation.layout.*");
|
|
1680
|
-
this.emit("import androidx.compose.ui.Alignment");
|
|
1681
|
-
this.emit("import androidx.compose.ui.Modifier");
|
|
1682
|
-
this.emit("import androidx.compose.ui.unit.dp");
|
|
1683
|
-
this.emit("");
|
|
1684
|
-
this.emit("import androidx.xr.glimmer.GlimmerTheme");
|
|
1685
|
-
this.emit("import androidx.xr.glimmer.Card");
|
|
1686
|
-
this.emit("import androidx.xr.glimmer.Button");
|
|
1687
|
-
this.emit("import androidx.xr.glimmer.Text");
|
|
1688
|
-
this.emit("import androidx.xr.glimmer.ListItem");
|
|
1689
|
-
this.emit("import androidx.xr.glimmer.TitleChip");
|
|
1690
|
-
this.emit("import androidx.xr.glimmer.surface");
|
|
1691
|
-
this.emit("");
|
|
1692
|
-
this.emit("import androidx.xr.projected.ProjectedContext");
|
|
1693
|
-
this.emit("");
|
|
1694
|
-
this.emit("/**");
|
|
1695
|
-
this.emit(" * Root overlay composable for the AI glasses AR experience.");
|
|
1696
|
-
this.emit(" * Wraps content in GlimmerTheme with glasses-optimized styling.");
|
|
1697
|
-
this.emit(" */");
|
|
1698
|
-
this.emit("@Composable");
|
|
1699
|
-
this.emit(`fun ${name}GlimmerOverlay(`);
|
|
1700
|
-
this.indent();
|
|
1701
|
-
this.emit("isVisualUiSupported: Boolean,");
|
|
1702
|
-
this.emit("content: @Composable () -> Unit");
|
|
1703
|
-
this.dedent();
|
|
1704
|
-
this.emit(") {");
|
|
1705
|
-
this.indent();
|
|
1706
|
-
this.emit("GlimmerTheme {");
|
|
1707
|
-
this.indent();
|
|
1708
|
-
this.emit("Box(");
|
|
1709
|
-
this.indent();
|
|
1710
|
-
this.emit("modifier = Modifier");
|
|
1711
|
-
this.indent();
|
|
1712
|
-
this.emit(".surface(focusable = false)");
|
|
1713
|
-
this.emit(".fillMaxSize(),");
|
|
1714
|
-
this.dedent();
|
|
1715
|
-
this.emit("contentAlignment = Alignment.Center");
|
|
1716
|
-
this.dedent();
|
|
1717
|
-
this.emit(") {");
|
|
1718
|
-
this.indent();
|
|
1719
|
-
this.emit("if (isVisualUiSupported) {");
|
|
1720
|
-
this.indent();
|
|
1721
|
-
this.emit("content()");
|
|
1722
|
-
this.dedent();
|
|
1723
|
-
this.emit("} else {");
|
|
1724
|
-
this.indent();
|
|
1725
|
-
this.emit('Text("Audio Guidance Mode Active")');
|
|
1726
|
-
this.dedent();
|
|
1727
|
-
this.emit("}");
|
|
1728
|
-
this.dedent();
|
|
1729
|
-
this.emit("}");
|
|
1730
|
-
this.dedent();
|
|
1731
|
-
this.emit("}");
|
|
1732
|
-
this.dedent();
|
|
1733
|
-
this.emit("}");
|
|
1734
|
-
this.emit("");
|
|
1735
|
-
this.emit("/**");
|
|
1736
|
-
this.emit(" * Glimmer info card for displaying object details in AR overlay.");
|
|
1737
|
-
this.emit(" * Optimized for optical see-through display with high contrast.");
|
|
1738
|
-
this.emit(" */");
|
|
1739
|
-
this.emit("@Composable");
|
|
1740
|
-
this.emit("fun GlimmerInfoCard(");
|
|
1741
|
-
this.indent();
|
|
1742
|
-
this.emit("title: String,");
|
|
1743
|
-
this.emit('subtitle: String = "",');
|
|
1744
|
-
this.emit("onAction: (() -> Unit)? = null,");
|
|
1745
|
-
this.emit('actionLabel: String = "View"');
|
|
1746
|
-
this.dedent();
|
|
1747
|
-
this.emit(") {");
|
|
1748
|
-
this.indent();
|
|
1749
|
-
this.emit("Card(");
|
|
1750
|
-
this.indent();
|
|
1751
|
-
this.emit("title = { Text(title) },");
|
|
1752
|
-
this.emit("action = onAction?.let { action ->");
|
|
1753
|
-
this.indent();
|
|
1754
|
-
this.emit("{");
|
|
1755
|
-
this.indent();
|
|
1756
|
-
this.emit("Button(onClick = action) {");
|
|
1757
|
-
this.indent();
|
|
1758
|
-
this.emit("Text(actionLabel)");
|
|
1759
|
-
this.dedent();
|
|
1760
|
-
this.emit("}");
|
|
1761
|
-
this.dedent();
|
|
1762
|
-
this.emit("}");
|
|
1763
|
-
this.dedent();
|
|
1764
|
-
this.emit("}");
|
|
1765
|
-
this.dedent();
|
|
1766
|
-
this.emit(") {");
|
|
1767
|
-
this.indent();
|
|
1768
|
-
this.emit("if (subtitle.isNotEmpty()) {");
|
|
1769
|
-
this.indent();
|
|
1770
|
-
this.emit("Text(subtitle)");
|
|
1771
|
-
this.dedent();
|
|
1772
|
-
this.emit("}");
|
|
1773
|
-
this.dedent();
|
|
1774
|
-
this.emit("}");
|
|
1775
|
-
this.dedent();
|
|
1776
|
-
this.emit("}");
|
|
1777
|
-
this.emit("");
|
|
1778
|
-
this.emit("/**");
|
|
1779
|
-
this.emit(" * Touchpad-friendly action button for AI glasses input.");
|
|
1780
|
-
this.emit(" * Pre-sized for comfortable touchpad tap targeting.");
|
|
1781
|
-
this.emit(" */");
|
|
1782
|
-
this.emit("@Composable");
|
|
1783
|
-
this.emit("fun GlimmerActionButton(");
|
|
1784
|
-
this.indent();
|
|
1785
|
-
this.emit("label: String,");
|
|
1786
|
-
this.emit("onClick: () -> Unit,");
|
|
1787
|
-
this.emit("modifier: Modifier = Modifier");
|
|
1788
|
-
this.dedent();
|
|
1789
|
-
this.emit(") {");
|
|
1790
|
-
this.indent();
|
|
1791
|
-
this.emit("Button(");
|
|
1792
|
-
this.indent();
|
|
1793
|
-
this.emit("onClick = onClick,");
|
|
1794
|
-
this.emit("modifier = modifier.fillMaxWidth()");
|
|
1795
|
-
this.dedent();
|
|
1796
|
-
this.emit(") {");
|
|
1797
|
-
this.indent();
|
|
1798
|
-
this.emit("Text(label)");
|
|
1799
|
-
this.dedent();
|
|
1800
|
-
this.emit("}");
|
|
1801
|
-
this.dedent();
|
|
1802
|
-
this.emit("}");
|
|
1803
|
-
this.emit("");
|
|
1804
|
-
this.emit("/**");
|
|
1805
|
-
this.emit(" * Accesses the AI glasses camera via ProjectedContext.");
|
|
1806
|
-
this.emit(
|
|
1807
|
-
" * Uses CameraX with projected device context for the glasses' outward-facing camera."
|
|
1808
|
-
);
|
|
1809
|
-
this.emit(" */");
|
|
1810
|
-
this.emit(
|
|
1811
|
-
"fun getGlassesCameraContext(hostContext: android.content.Context): android.content.Context? {"
|
|
1812
|
-
);
|
|
1813
|
-
this.indent();
|
|
1814
|
-
this.emit("return try {");
|
|
1815
|
-
this.indent();
|
|
1816
|
-
this.emit("ProjectedContext.createProjectedDeviceContext(hostContext)");
|
|
1817
|
-
this.dedent();
|
|
1818
|
-
this.emit("} catch (e: IllegalStateException) {");
|
|
1819
|
-
this.indent();
|
|
1820
|
-
this.emit('android.util.Log.w("HoloScript", "AI Glasses not connected: ${e.message}")');
|
|
1821
|
-
this.emit("null");
|
|
1822
|
-
this.dedent();
|
|
1823
|
-
this.emit("}");
|
|
1824
|
-
this.dedent();
|
|
1825
|
-
this.emit("}");
|
|
1826
|
-
this.emit("");
|
|
1827
|
-
this.emit("/**");
|
|
1828
|
-
this.emit(" * Monitors AI glasses connection state via Jetpack Projected.");
|
|
1829
|
-
this.emit(" * Returns a Flow<Boolean> tracking whether glasses are connected.");
|
|
1830
|
-
this.emit(" */");
|
|
1831
|
-
this.emit("@Composable");
|
|
1832
|
-
this.emit(
|
|
1833
|
-
"fun rememberGlassesConnectionState(context: android.content.Context): State<Boolean> {"
|
|
1834
|
-
);
|
|
1835
|
-
this.indent();
|
|
1836
|
-
this.emit("val isConnected = remember { mutableStateOf(false) }");
|
|
1837
|
-
this.emit("LaunchedEffect(Unit) {");
|
|
1838
|
-
this.indent();
|
|
1839
|
-
this.emit(
|
|
1840
|
-
"ProjectedContext.isProjectedDeviceConnected(context, coroutineContext).collect { connected ->"
|
|
1841
|
-
);
|
|
1842
|
-
this.indent();
|
|
1843
|
-
this.emit("isConnected.value = connected");
|
|
1844
|
-
this.dedent();
|
|
1845
|
-
this.emit("}");
|
|
1846
|
-
this.dedent();
|
|
1847
|
-
this.emit("}");
|
|
1848
|
-
this.emit("return isConnected");
|
|
1849
|
-
this.dedent();
|
|
1850
|
-
this.emit("}");
|
|
1851
|
-
return this.lines.join("\n");
|
|
1852
|
-
}
|
|
1853
|
-
};
|
|
1854
|
-
}
|
|
1855
|
-
});
|
|
1856
|
-
|
|
1857
|
-
exports.AndroidXRCompiler_exports = AndroidXRCompiler_exports;
|
|
1858
|
-
exports.compileToAndroidXR = compileToAndroidXR;
|
|
1859
|
-
exports.init_AndroidXRCompiler = init_AndroidXRCompiler;
|
|
1860
|
-
//# sourceMappingURL=chunk-PJMOXFPR.cjs.map
|
|
1861
|
-
//# sourceMappingURL=chunk-PJMOXFPR.cjs.map
|