@holoscript/core 7.0.0 → 8.0.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 +4 -4
- package/bin/holoscript.cjs +3 -0
- package/dist/GLTFPipeline-N6B6PJ6G.cjs +38 -0
- package/dist/GLTFPipeline-P3WMNYCN.js +17 -0
- package/dist/HoloScriptPlusParser-7FBIFUFC.js +693 -0
- package/dist/HoloScriptPlusParser-A2W7OCN2.cjs +698 -0
- package/dist/HoloScriptPlusParser-F2Z56FJR.js +10 -0
- package/dist/HoloScriptPlusParser-WKHRIH6T.cjs +27 -0
- package/dist/HoloScriptRuntime-CBYJX3LZ.js +10 -0
- package/dist/HoloScriptRuntime-VJ374RER.cjs +19 -0
- package/dist/USDZExporter-3FWJ25JI.cjs +1037 -0
- package/dist/USDZExporter-D2HQLQYK.js +1035 -0
- 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-2BSMSUCE.js +1941 -0
- package/dist/chunk-2IWC4FP7.cjs +1189 -0
- package/dist/chunk-2M2DPHKB.js +762 -0
- package/dist/chunk-2X7NGX3M.js +2426 -0
- package/dist/chunk-2YSNA2F2.cjs +1274 -0
- package/dist/chunk-3FOKASKO.cjs +348 -0
- package/dist/chunk-3WZEJHUQ.cjs +116 -0
- package/dist/chunk-4C57PR42.js +468 -0
- package/dist/chunk-4GU5GCIY.js +1384 -0
- package/dist/chunk-4N4SXXWF.js +769 -0
- package/dist/chunk-4PDEPEQW.cjs +41 -0
- package/dist/chunk-4R6SBXDB.js +45 -0
- package/dist/chunk-4SDKXQ4C.js +811 -0
- package/dist/chunk-4V2E7U7Q.cjs +2015 -0
- package/dist/chunk-5FSKKMR7.cjs +2092 -0
- package/dist/chunk-5GLCTO44.js +161 -0
- package/dist/chunk-5H2G27PK.cjs +627 -0
- package/dist/chunk-5JEU52DW.js +382 -0
- package/dist/chunk-5NWINHR5.cjs +689 -0
- package/dist/chunk-5S7RKFGR.cjs +446 -0
- package/dist/chunk-5WUNN6ZN.cjs +428 -0
- package/dist/chunk-6CTRH7HT.cjs +1985 -0
- package/dist/chunk-6EX3I5P4.cjs +130 -0
- package/dist/chunk-6Q7RC2MK.cjs +1418 -0
- package/dist/chunk-7H33U5SM.cjs +413 -0
- package/dist/chunk-7M3PNU2M.cjs +672 -0
- package/dist/chunk-7T25IEZ2.js +449 -0
- package/dist/chunk-7V4JI5TB.cjs +6947 -0
- package/dist/chunk-A6KINLZJ.js +236 -0
- package/dist/chunk-ADADVMXY.js +404 -0
- package/dist/chunk-AIA52I7T.cjs +543 -0
- package/dist/chunk-ALC6K7GL.cjs +4402 -0
- package/dist/chunk-ANXZFVY2.js +113 -0
- package/dist/chunk-AOVGZ25K.cjs +56583 -0
- package/dist/chunk-AVJHHUC5.cjs +718 -0
- package/dist/chunk-BGY3SDG3.js +4396 -0
- package/dist/chunk-BOVPWH4H.js +3857 -0
- package/dist/chunk-BRQJKJGT.cjs +20 -0
- package/dist/chunk-C37BHB7N.cjs +636 -0
- package/dist/chunk-CBJY23VL.js +126 -0
- package/dist/chunk-CCATWPXM.js +778 -0
- package/dist/chunk-CIWWXYHZ.cjs +50 -0
- package/dist/chunk-CMT5NMMS.js +669 -0
- package/dist/chunk-CR4FMIWL.cjs +1049 -0
- package/dist/chunk-CR7XRXRN.cjs +639 -0
- package/dist/chunk-CSD27UNQ.js +2176 -0
- package/dist/chunk-D3STJ56J.js +714 -0
- package/dist/chunk-DAJXW4KX.js +725 -0
- package/dist/chunk-DHO2YRIM.js +756 -0
- package/dist/chunk-DTGBOSEM.cjs +319 -0
- package/dist/chunk-E6XXE66V.js +2252 -0
- package/dist/chunk-E7JP3BS3.cjs +61 -0
- package/dist/chunk-EKBKLK7P.js +1169 -0
- package/dist/chunk-EKYL4PE3.cjs +404 -0
- package/dist/chunk-EXKSFGSX.cjs +274 -0
- package/dist/chunk-EXUP2BUM.cjs +1173 -0
- package/dist/chunk-FDSGHPAK.js +130 -0
- package/dist/chunk-FEWUHQNM.cjs +144 -0
- package/dist/chunk-FFAOS5HQ.js +70 -0
- package/dist/chunk-FGGJQRLZ.js +270 -0
- package/dist/chunk-FHP6LXJS.js +415 -0
- package/dist/chunk-FJPADH77.cjs +666 -0
- package/dist/chunk-FL33V6U7.js +421 -0
- package/dist/chunk-FYSPZT4V.cjs +169 -0
- package/dist/chunk-GIOYPNTH.js +5575 -0
- package/dist/chunk-GJNCFRJU.cjs +125 -0
- package/dist/chunk-GMP623FG.cjs +1508 -0
- package/dist/chunk-GQHQOPXZ.cjs +5580 -0
- package/dist/chunk-GTU2Z2WW.cjs +262 -0
- package/dist/chunk-GYZE55MO.js +2007 -0
- package/dist/chunk-GZG4KA4E.js +529 -0
- package/dist/chunk-H652NHX5.js +600 -0
- package/dist/chunk-HCCQLAA4.cjs +74 -0
- package/dist/chunk-HKEXJUWS.cjs +4007 -0
- package/dist/chunk-HNDK4IQU.cjs +5737 -0
- package/dist/chunk-HR2OSYVQ.js +56165 -0
- package/dist/chunk-HYNTS5CP.js +422 -0
- package/dist/chunk-I6XZ5DZF.cjs +488 -0
- package/dist/chunk-ILRBGFVD.cjs +72 -0
- package/dist/chunk-IUDGEUDO.cjs +1409 -0
- package/dist/chunk-IUTY5WQ2.cjs +424 -0
- package/dist/chunk-IVY74O6C.js +247 -0
- package/dist/chunk-IWC5DM2M.cjs +611 -0
- package/dist/chunk-J32ZJZJH.js +554 -0
- package/dist/chunk-J52VSAQ7.js +6884 -0
- package/dist/chunk-J6M4FPK5.js +421 -0
- package/dist/chunk-J7XZV333.js +844 -0
- package/dist/chunk-JBNDTXXV.cjs +2433 -0
- package/dist/chunk-JC5YBPSF.cjs +984 -0
- package/dist/chunk-JD7CIZ33.js +1949 -0
- package/dist/chunk-JIBL5LRK.js +4287 -0
- package/dist/chunk-JIYN45TV.cjs +2525 -0
- package/dist/chunk-K574J76X.cjs +457 -0
- package/dist/chunk-KHDZJE5D.cjs +2196 -0
- package/dist/chunk-KKFZ3B6J.cjs +557 -0
- package/dist/chunk-KKP7JKN4.cjs +6002 -0
- package/dist/chunk-KPJL34Y7.cjs +186 -0
- package/dist/chunk-KQCN6AKJ.cjs +3042 -0
- package/dist/chunk-L5LKKENW.cjs +424 -0
- package/dist/chunk-LHAXPADA.js +882 -0
- package/dist/chunk-LM2VU56C.cjs +37 -0
- package/dist/chunk-M56JSP4A.js +400 -0
- package/dist/chunk-MBOOR7X4.js +18 -0
- package/dist/chunk-MQWAT42R.cjs +4310 -0
- package/dist/chunk-MXFUTKCO.js +56 -0
- package/dist/chunk-MYY4PMWX.cjs +264 -0
- package/dist/chunk-N75IH2HY.js +258 -0
- package/dist/chunk-NCWCUZNS.cjs +72 -0
- package/dist/chunk-NG77ATEW.cjs +778 -0
- package/dist/chunk-NW5G5FBQ.js +215 -0
- package/dist/chunk-OBIYPYYS.cjs +1993 -0
- package/dist/chunk-OH2FAIVU.js +608 -0
- package/dist/chunk-OIQYIIO3.cjs +473 -0
- package/dist/chunk-OL46QLBJ.js +61 -0
- package/dist/chunk-OMXII44L.js +5386 -0
- package/dist/chunk-OSYNRVOV.cjs +243 -0
- package/dist/chunk-PGANYCAG.cjs +220 -0
- package/dist/chunk-PHENW4MQ.js +65 -0
- package/dist/chunk-PY37LNDP.js +686 -0
- package/dist/chunk-Q56TT7IL.js +483 -0
- package/dist/chunk-QD32ADK6.js +1046 -0
- package/dist/chunk-QPOQ7VPI.js +1491 -0
- package/dist/chunk-QPSKHBJP.js +5963 -0
- package/dist/chunk-QXH7QDSZ.js +1989 -0
- package/dist/chunk-R24NNAEG.js +1399 -0
- package/dist/chunk-R75BSDH7.cjs +50 -0
- package/dist/chunk-RARB3MBR.js +207 -0
- package/dist/chunk-ROHLEP2W.cjs +847 -0
- package/dist/chunk-S3MDHNKB.cjs +490 -0
- package/dist/chunk-S6YWHCKV.cjs +410 -0
- package/dist/chunk-SIEXCILW.js +229 -0
- package/dist/chunk-SLU6UPJI.cjs +410 -0
- package/dist/chunk-SMIWS6DE.cjs +212 -0
- package/dist/chunk-T5J7ZKP3.cjs +4885 -0
- package/dist/chunk-TDCNJHWO.js +341 -0
- package/dist/chunk-TG7O4WBC.js +406 -0
- package/dist/chunk-THCK6ECW.js +2085 -0
- package/dist/chunk-TJHVSBP2.js +439 -0
- package/dist/chunk-TVHSPJ4N.cjs +606 -0
- package/dist/chunk-TWUXUCEW.cjs +781 -0
- package/dist/chunk-U44IKGG7.cjs +5390 -0
- package/dist/chunk-UFZZ7TSN.cjs +886 -0
- package/dist/chunk-UJXISJBN.js +487 -0
- package/dist/chunk-UOIGFQXC.cjs +9750 -0
- package/dist/chunk-URG2SJLK.js +2521 -0
- package/dist/chunk-UWJHCOH2.js +623 -0
- package/dist/chunk-V6MKC2FZ.js +979 -0
- package/dist/chunk-VJ3L7CHT.cjs +169 -0
- package/dist/chunk-VMLKSV26.js +1216 -0
- package/dist/chunk-VVKVQZI6.js +55 -0
- package/dist/chunk-W6RW64BB.cjs +1219 -0
- package/dist/chunk-WKX5AMOY.js +309 -0
- package/dist/chunk-WNNE3PO3.js +34 -0
- package/dist/chunk-WRKUDAZJ.js +3038 -0
- package/dist/chunk-WRLUBRKS.cjs +1943 -0
- package/dist/chunk-WRNEH5MO.js +3169 -0
- package/dist/chunk-WRWEX5ZF.js +658 -0
- package/dist/chunk-X3GJPRVK.cjs +3246 -0
- package/dist/chunk-X3UVCWNL.cjs +2257 -0
- package/dist/chunk-XB274MW5.cjs +814 -0
- package/dist/chunk-XHIV6326.cjs +793 -0
- package/dist/chunk-XIVIE43A.cjs +729 -0
- package/dist/chunk-XJAN7CZG.cjs +432 -0
- package/dist/chunk-XKTPFXGA.js +164 -0
- package/dist/chunk-XL6INWKT.js +121 -0
- package/dist/chunk-XSFNHP2E.js +632 -0
- package/dist/chunk-XT6JY5W5.js +9684 -0
- package/dist/chunk-XUILI4KB.cjs +430 -0
- package/dist/chunk-YF2I3MV3.js +46 -0
- package/dist/chunk-YIMN575O.js +4847 -0
- package/dist/chunk-YIVFJNYV.js +176 -0
- package/dist/chunk-YKGPCZEE.cjs +237 -0
- package/dist/chunk-YN2YWTXW.js +5731 -0
- package/dist/chunk-YN6V7R3L.cjs +327 -0
- package/dist/chunk-YQELFNJV.js +39 -0
- package/dist/chunk-YVD3CSO3.js +1183 -0
- package/dist/chunk-YVOAF4ML.js +623 -0
- package/dist/chunk-YWOBW2PV.cjs +759 -0
- package/dist/chunk-YYXH45E2.js +426 -0
- package/dist/chunk-ZDSYWBIE.cjs +69 -0
- package/dist/chunk-ZU7NIRT6.js +325 -0
- package/dist/chunk-ZZ7RO2UQ.js +1243 -0
- package/dist/cli/holoscript-runner.cjs +2255 -0
- package/dist/cli/holoscript-runner.js +2230 -0
- package/dist/codebase/index.cjs +336 -0
- package/dist/codebase/index.js +331 -0
- package/dist/compiler/agent-inference.cjs +31 -0
- package/dist/compiler/agent-inference.js +8 -0
- package/dist/compiler/android-xr.cjs +24 -0
- package/dist/compiler/android-xr.js +14 -0
- package/dist/compiler/android.cjs +24 -0
- package/dist/compiler/android.js +14 -0
- package/dist/compiler/ar.cjs +23 -0
- package/dist/compiler/ar.js +11 -0
- package/dist/compiler/babylon.cjs +21 -0
- package/dist/compiler/babylon.js +15 -0
- package/dist/compiler/business-quest.cjs +43 -0
- package/dist/compiler/business-quest.js +4 -0
- package/dist/compiler/coco.cjs +27 -0
- package/dist/compiler/coco.js +4 -0
- 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 +609 -0
- package/dist/compiler/domain-block-utils.js +11 -0
- package/dist/compiler/dtdl.cjs +27 -0
- package/dist/compiler/dtdl.js +11 -0
- package/dist/compiler/gltf-pipeline.cjs +39 -0
- package/dist/compiler/gltf-pipeline.js +4 -0
- package/dist/compiler/godot.cjs +18 -0
- package/dist/compiler/godot.js +12 -0
- package/dist/compiler/incremental.cjs +34 -0
- package/dist/compiler/incremental.js +9 -0
- package/dist/compiler/index.cjs +5408 -0
- package/dist/compiler/index.d.ts +334 -21
- package/dist/compiler/index.js +5069 -0
- package/dist/compiler/ios.cjs +23 -0
- package/dist/compiler/ios.js +13 -0
- 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 +24 -0
- package/dist/compiler/multi-layer.js +15 -0
- package/dist/compiler/nodetoy.cjs +23 -0
- package/dist/compiler/nodetoy.d.ts +69 -0
- package/dist/compiler/nodetoy.js +4 -0
- package/dist/compiler/openxr.cjs +19 -0
- package/dist/compiler/openxr.js +13 -0
- package/dist/compiler/playcanvas.cjs +19 -0
- package/dist/compiler/playcanvas.js +13 -0
- package/dist/compiler/r3f.cjs +39 -0
- package/dist/compiler/r3f.d.ts +22 -0
- package/dist/compiler/r3f.js +17 -0
- package/dist/compiler/remotion.cjs +23 -0
- package/dist/compiler/remotion.js +4 -0
- package/dist/compiler/reproducibility.cjs +39 -0
- package/dist/compiler/reproducibility.js +4 -0
- package/dist/compiler/sdf.cjs +24 -0
- package/dist/compiler/sdf.js +12 -0
- package/dist/compiler/semantic-scene.cjs +31 -0
- package/dist/compiler/semantic-scene.js +4 -0
- package/dist/compiler/state.cjs +21 -0
- package/dist/compiler/state.js +11 -0
- package/dist/compiler/trait-composition.cjs +30 -0
- package/dist/compiler/trait-composition.js +9 -0
- package/dist/compiler/unity.cjs +19 -0
- package/dist/compiler/unity.js +13 -0
- package/dist/compiler/unreal.cjs +23 -0
- package/dist/compiler/unreal.js +13 -0
- package/dist/compiler/urdf.cjs +52 -0
- package/dist/compiler/urdf.js +12 -0
- package/dist/compiler/usd-physics.cjs +26 -0
- package/dist/compiler/usd-physics.js +9 -0
- package/dist/compiler/visionos.cjs +19 -0
- package/dist/compiler/visionos.js +13 -0
- package/dist/compiler/vrchat.cjs +22 -0
- package/dist/compiler/vrchat.js +12 -0
- package/dist/compiler/vrr.cjs +24 -0
- package/dist/compiler/vrr.js +12 -0
- package/dist/compiler/wasm.cjs +36 -0
- package/dist/compiler/wasm.js +12 -0
- package/dist/compiler/webgpu.cjs +18 -0
- package/dist/compiler/webgpu.js +12 -0
- package/dist/coordinators/index.cjs +1266 -0
- package/dist/coordinators/index.d.ts +72 -1
- package/dist/coordinators/index.js +1259 -0
- package/dist/debugger.cjs +24 -0
- package/dist/debugger.js +11 -0
- package/dist/dist-ACXG6IH5.cjs +3113 -0
- package/dist/dist-VD4L6V7Q.js +3076 -0
- package/dist/entries/interop.cjs +64 -0
- package/dist/entries/interop.js +7 -0
- package/dist/entries/scripting.cjs +55 -0
- package/dist/entries/scripting.js +10 -0
- 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 +44660 -0
- package/dist/index.d.ts +978 -21
- package/dist/index.js +41422 -0
- package/dist/math/tropical-spmv.cjs +45 -0
- package/dist/math/tropical-spmv.js +4 -0
- package/dist/math/vec3.cjs +57 -0
- package/dist/math/vec3.js +4 -0
- 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.js +141 -0
- package/dist/parser.cjs +61 -0
- package/dist/parser.d.ts +11 -0
- package/dist/parser.js +12 -0
- package/dist/playwright-3AT3AKTV.cjs +98673 -0
- package/dist/playwright-VZPDVUEQ.js +98660 -0
- package/dist/reconstruction/index.cjs +153 -0
- package/dist/reconstruction/index.d.ts +29 -2
- package/dist/reconstruction/index.js +8 -0
- package/dist/runtime.cjs +316 -0
- package/dist/runtime.d.ts +61 -0
- package/dist/runtime.js +31 -0
- package/dist/self-improvement/index.cjs +563 -0
- package/dist/self-improvement/index.js +545 -0
- package/dist/src-4ROC227P.cjs +2094 -0
- package/dist/src-HCCCSYP7.js +2073 -0
- package/dist/storage/index.cjs +38 -0
- package/dist/storage/index.js +5 -0
- package/dist/testing.cjs +25 -0
- package/dist/testing.d.ts +70 -0
- package/dist/testing.js +4 -0
- package/dist/traitDocs/traitDocs.cjs +33 -0
- package/dist/traitDocs/traitDocs.js +7 -0
- package/dist/traits/botanical-lotus.cjs +177 -0
- package/dist/traits/botanical-lotus.d.ts +502 -0
- package/dist/traits/botanical-lotus.js +7 -0
- package/dist/traits/engines/index.cjs +520 -0
- package/dist/traits/engines/index.js +380 -0
- package/dist/traits/index.cjs +24548 -0
- package/dist/traits/index.d.ts +171 -35
- package/dist/traits/index.js +21638 -0
- 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 +19 -0
- package/dist/type-checker.js +6 -0
- 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 +29 -0
- package/dist/wot/index.js +4 -0
- package/package.json +93 -49
|
@@ -0,0 +1,3042 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkFEWUHQNM_cjs = require('./chunk-FEWUHQNM.cjs');
|
|
4
|
+
var chunkKKP7JKN4_cjs = require('./chunk-KKP7JKN4.cjs');
|
|
5
|
+
var chunkDTGBOSEM_cjs = require('./chunk-DTGBOSEM.cjs');
|
|
6
|
+
var chunk6EX3I5P4_cjs = require('./chunk-6EX3I5P4.cjs');
|
|
7
|
+
var chunkHCCQLAA4_cjs = require('./chunk-HCCQLAA4.cjs');
|
|
8
|
+
var ans = require('@holoscript/core-types/ans');
|
|
9
|
+
|
|
10
|
+
// src/compiler/AndroidFeatureGenerators.ts
|
|
11
|
+
function hasGeoTraits(composition) {
|
|
12
|
+
const geoTraitNames = [
|
|
13
|
+
"geo_anchor",
|
|
14
|
+
"geo_persist",
|
|
15
|
+
"geo_radius",
|
|
16
|
+
"geo_compass_heading",
|
|
17
|
+
"geo_altitude",
|
|
18
|
+
"geo_cloud_anchor",
|
|
19
|
+
"geo_terrain_snap",
|
|
20
|
+
"geo_session_continuity",
|
|
21
|
+
"geo_proximity_trigger",
|
|
22
|
+
"geo_discoverable",
|
|
23
|
+
"geo_arcore_geospatial",
|
|
24
|
+
"geo_arkit_geo_anchor",
|
|
25
|
+
// ARCore Geospatial API traits (M.010.15)
|
|
26
|
+
"geospatial_vps",
|
|
27
|
+
"geospatial_anchor",
|
|
28
|
+
"geospatial_terrain_anchor",
|
|
29
|
+
"geospatial_rooftop_anchor",
|
|
30
|
+
"geospatial_streetscape",
|
|
31
|
+
"geospatial_heading"
|
|
32
|
+
];
|
|
33
|
+
for (const obj of composition.objects || []) {
|
|
34
|
+
for (const trait of obj.traits || []) {
|
|
35
|
+
const name = typeof trait === "string" ? trait : trait.name;
|
|
36
|
+
if (geoTraitNames.includes(name)) return true;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
function hasGeospatialVPSTraits(composition) {
|
|
42
|
+
const geospatialTraitNames = [
|
|
43
|
+
"geospatial_vps",
|
|
44
|
+
"geospatial_anchor",
|
|
45
|
+
"geospatial_terrain_anchor",
|
|
46
|
+
"geospatial_rooftop_anchor",
|
|
47
|
+
"geospatial_streetscape",
|
|
48
|
+
"geospatial_heading"
|
|
49
|
+
];
|
|
50
|
+
for (const obj of composition.objects || []) {
|
|
51
|
+
for (const trait of obj.traits || []) {
|
|
52
|
+
const name = typeof trait === "string" ? trait : trait.name;
|
|
53
|
+
if (geospatialTraitNames.includes(name)) return true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
function emitGeoAnchorSetup(compiler, composition) {
|
|
59
|
+
compiler.emit("");
|
|
60
|
+
compiler.emit("// === Geo-Anchor: GPS-pinned persistent holograms ===");
|
|
61
|
+
compiler.emit(
|
|
62
|
+
"private var fusedLocationClient: com.google.android.gms.location.FusedLocationProviderClient? = null"
|
|
63
|
+
);
|
|
64
|
+
compiler.emit("private val geoAnchors = mutableMapOf<String, Anchor>()");
|
|
65
|
+
compiler.emit("");
|
|
66
|
+
compiler.emit("private fun setupGeoAnchors() {");
|
|
67
|
+
compiler.indentLevel++;
|
|
68
|
+
compiler.emit(
|
|
69
|
+
"fusedLocationClient = com.google.android.gms.location.LocationServices.getFusedLocationProviderClient(this)"
|
|
70
|
+
);
|
|
71
|
+
compiler.emit("");
|
|
72
|
+
const usesGeospatial = compiler.compositionHasTrait(composition, "geo_arcore_geospatial");
|
|
73
|
+
if (usesGeospatial) {
|
|
74
|
+
compiler.emit("// Enable ARCore Geospatial API (VPS)");
|
|
75
|
+
compiler.emit("val session = arFragment.arSceneView.session ?: return");
|
|
76
|
+
compiler.emit("val config = session.config");
|
|
77
|
+
compiler.emit("config.geospatialMode = Config.GeospatialMode.ENABLED");
|
|
78
|
+
compiler.emit("session.configure(config)");
|
|
79
|
+
compiler.emit("");
|
|
80
|
+
}
|
|
81
|
+
for (const obj of composition.objects || []) {
|
|
82
|
+
const traits = obj.traits || [];
|
|
83
|
+
const hasGeoAnchor = traits.some((t) => {
|
|
84
|
+
const name = typeof t === "string" ? t : t.name;
|
|
85
|
+
return name === "geo_anchor";
|
|
86
|
+
});
|
|
87
|
+
if (!hasGeoAnchor) continue;
|
|
88
|
+
const geoAnchorTrait = traits.find((t) => {
|
|
89
|
+
const name = typeof t === "string" ? t : t.name;
|
|
90
|
+
return name === "geo_anchor";
|
|
91
|
+
});
|
|
92
|
+
const config = typeof geoAnchorTrait === "string" ? {} : geoAnchorTrait?.config || {};
|
|
93
|
+
const lat = config.latitude ?? 0;
|
|
94
|
+
const lng = config.longitude ?? 0;
|
|
95
|
+
const altTrait = traits.find((t) => {
|
|
96
|
+
const name = typeof t === "string" ? t : t.name;
|
|
97
|
+
return name === "geo_altitude";
|
|
98
|
+
});
|
|
99
|
+
const altConfig = typeof altTrait === "string" ? {} : altTrait?.config || {};
|
|
100
|
+
const alt = altConfig.meters ?? 0;
|
|
101
|
+
const headingTrait = traits.find((t) => {
|
|
102
|
+
const name = typeof t === "string" ? t : t.name;
|
|
103
|
+
return name === "geo_compass_heading";
|
|
104
|
+
});
|
|
105
|
+
const headingConfig = typeof headingTrait === "string" ? {} : headingTrait?.config || {};
|
|
106
|
+
const heading = headingConfig.degrees ?? 0;
|
|
107
|
+
compiler.emit(`// Geo-anchor: ${compiler.escapeStringValue(obj.name, "Kotlin")}`);
|
|
108
|
+
compiler.emit(
|
|
109
|
+
`createGeoAnchor("${compiler.escapeStringValue(obj.name, "Kotlin")}", ${lat}, ${lng}, ${alt}, ${heading}f)`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
const usesPersist = compiler.compositionHasTrait(composition, "geo_persist");
|
|
113
|
+
const usesCloud = compiler.compositionHasTrait(composition, "geo_cloud_anchor");
|
|
114
|
+
if (usesPersist || usesCloud) {
|
|
115
|
+
compiler.emit("");
|
|
116
|
+
compiler.emit("// Restore persisted geo anchors");
|
|
117
|
+
compiler.emit("restoreGeoAnchors()");
|
|
118
|
+
}
|
|
119
|
+
compiler.indentLevel--;
|
|
120
|
+
compiler.emit("}");
|
|
121
|
+
compiler.emit("");
|
|
122
|
+
compiler.emit(
|
|
123
|
+
"private fun createGeoAnchor(name: String, lat: Double, lng: Double, alt: Double, heading: Float) {"
|
|
124
|
+
);
|
|
125
|
+
compiler.indentLevel++;
|
|
126
|
+
compiler.emit("val session = arFragment.arSceneView.session ?: return");
|
|
127
|
+
compiler.emit("val earth = session.earth ?: return");
|
|
128
|
+
compiler.emit("if (earth.trackingState != TrackingState.TRACKING) return");
|
|
129
|
+
compiler.emit("");
|
|
130
|
+
compiler.emit(
|
|
131
|
+
"val anchor = earth.createAnchor(lat, lng, alt, 0f, 0f, Math.sin(Math.toRadians(heading.toDouble() / 2)).toFloat(), Math.cos(Math.toRadians(heading.toDouble() / 2)).toFloat())"
|
|
132
|
+
);
|
|
133
|
+
compiler.emit("geoAnchors[name] = anchor");
|
|
134
|
+
compiler.emit("");
|
|
135
|
+
compiler.emit("val anchorNode = AnchorNode(anchor)");
|
|
136
|
+
compiler.emit("anchorNode.setParent(arFragment.arSceneView.scene)");
|
|
137
|
+
compiler.emit("");
|
|
138
|
+
compiler.emit("NodeFactory.createDefaultNode(this) { renderable ->");
|
|
139
|
+
compiler.indentLevel++;
|
|
140
|
+
compiler.emit("val node = TransformableNode(arFragment.transformationSystem)");
|
|
141
|
+
compiler.emit("node.setParent(anchorNode)");
|
|
142
|
+
compiler.emit("node.renderable = renderable");
|
|
143
|
+
compiler.emit("placedNodes[name] = node");
|
|
144
|
+
compiler.emit('android.util.Log.d("HoloScript", "Geo-anchored: $name at ($lat, $lng, $alt)")');
|
|
145
|
+
compiler.indentLevel--;
|
|
146
|
+
compiler.emit("}");
|
|
147
|
+
compiler.indentLevel--;
|
|
148
|
+
compiler.emit("}");
|
|
149
|
+
compiler.emit("");
|
|
150
|
+
if (usesPersist || usesCloud) {
|
|
151
|
+
compiler.emit("private fun saveGeoAnchorToCloud(name: String, anchor: Anchor) {");
|
|
152
|
+
compiler.indentLevel++;
|
|
153
|
+
compiler.emit("val session = arFragment.arSceneView.session ?: return");
|
|
154
|
+
compiler.emit("session.hostCloudAnchorAsync(anchor, 365) { cloudId, state ->");
|
|
155
|
+
compiler.indentLevel++;
|
|
156
|
+
compiler.emit("if (state == Anchor.CloudAnchorState.SUCCESS && cloudId != null) {");
|
|
157
|
+
compiler.indentLevel++;
|
|
158
|
+
compiler.emit('val prefs = getSharedPreferences("geo_anchors", MODE_PRIVATE)');
|
|
159
|
+
compiler.emit("prefs.edit().putString(name, cloudId).apply()");
|
|
160
|
+
compiler.emit('android.util.Log.d("HoloScript", "Cloud anchor saved: $name -> $cloudId")');
|
|
161
|
+
compiler.indentLevel--;
|
|
162
|
+
compiler.emit("}");
|
|
163
|
+
compiler.indentLevel--;
|
|
164
|
+
compiler.emit("}");
|
|
165
|
+
compiler.indentLevel--;
|
|
166
|
+
compiler.emit("}");
|
|
167
|
+
compiler.emit("");
|
|
168
|
+
compiler.emit("private fun restoreGeoAnchors() {");
|
|
169
|
+
compiler.indentLevel++;
|
|
170
|
+
compiler.emit('val prefs = getSharedPreferences("geo_anchors", MODE_PRIVATE)');
|
|
171
|
+
compiler.emit("val session = arFragment.arSceneView.session ?: return");
|
|
172
|
+
compiler.emit("for ((name, cloudId) in prefs.all) {");
|
|
173
|
+
compiler.indentLevel++;
|
|
174
|
+
compiler.emit("val id = cloudId as? String ?: continue");
|
|
175
|
+
compiler.emit("session.resolveCloudAnchorAsync(id) { anchor, state ->");
|
|
176
|
+
compiler.indentLevel++;
|
|
177
|
+
compiler.emit("if (state == Anchor.CloudAnchorState.SUCCESS && anchor != null) {");
|
|
178
|
+
compiler.indentLevel++;
|
|
179
|
+
compiler.emit("geoAnchors[name] = anchor");
|
|
180
|
+
compiler.emit("val anchorNode = AnchorNode(anchor)");
|
|
181
|
+
compiler.emit("anchorNode.setParent(arFragment.arSceneView.scene)");
|
|
182
|
+
compiler.emit('android.util.Log.d("HoloScript", "Restored cloud anchor: $name")');
|
|
183
|
+
compiler.indentLevel--;
|
|
184
|
+
compiler.emit("}");
|
|
185
|
+
compiler.indentLevel--;
|
|
186
|
+
compiler.emit("}");
|
|
187
|
+
compiler.indentLevel--;
|
|
188
|
+
compiler.emit("}");
|
|
189
|
+
compiler.indentLevel--;
|
|
190
|
+
compiler.emit("}");
|
|
191
|
+
compiler.emit("");
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function emitGeospatialVPSSetup(compiler, composition) {
|
|
195
|
+
compiler.emit("");
|
|
196
|
+
compiler.emit("// === ARCore Geospatial API: VPS street-level geo-anchoring ===");
|
|
197
|
+
compiler.emit("private var earth: Earth? = null");
|
|
198
|
+
compiler.emit("private val geospatialAnchors = mutableMapOf<String, Anchor>()");
|
|
199
|
+
compiler.emit("");
|
|
200
|
+
compiler.emit("private fun setupGeospatialVPS() {");
|
|
201
|
+
compiler.indentLevel++;
|
|
202
|
+
compiler.emit("val session = arFragment.arSceneView.session ?: return");
|
|
203
|
+
compiler.emit("val config = session.config");
|
|
204
|
+
compiler.emit("config.geospatialMode = Config.GeospatialMode.ENABLED");
|
|
205
|
+
const usesStreetscape = compiler.compositionHasTrait(composition, "geospatial_streetscape");
|
|
206
|
+
if (usesStreetscape) {
|
|
207
|
+
compiler.emit("config.streetscapeGeometryMode = Config.StreetscapeGeometryMode.ENABLED");
|
|
208
|
+
}
|
|
209
|
+
compiler.emit("session.configure(config)");
|
|
210
|
+
compiler.emit("");
|
|
211
|
+
compiler.emit("// Monitor geospatial tracking state");
|
|
212
|
+
compiler.emit("arFragment.arSceneView.scene.addOnUpdateListener {");
|
|
213
|
+
compiler.indentLevel++;
|
|
214
|
+
compiler.emit("val sessionRef = arFragment.arSceneView.session ?: return@addOnUpdateListener");
|
|
215
|
+
compiler.emit("earth = sessionRef.earth");
|
|
216
|
+
compiler.emit("val trackingState = earth?.trackingState ?: return@addOnUpdateListener");
|
|
217
|
+
compiler.emit("if (trackingState != TrackingState.TRACKING) return@addOnUpdateListener");
|
|
218
|
+
compiler.emit("");
|
|
219
|
+
compiler.emit("val geospatialState = earth?.cameraGeospatialPose ?: return@addOnUpdateListener");
|
|
220
|
+
compiler.emit("val horizontalAccuracy = geospatialState.horizontalAccuracy");
|
|
221
|
+
compiler.emit("val headingAccuracy = geospatialState.headingAccuracy");
|
|
222
|
+
compiler.emit("");
|
|
223
|
+
compiler.emit(
|
|
224
|
+
`if (horizontalAccuracy > ${chunkDTGBOSEM_cjs.GEOSPATIAL_DEFAULTS.vps.accuracyThreshold} || headingAccuracy > ${chunkDTGBOSEM_cjs.GEOSPATIAL_DEFAULTS.vps.headingAccuracyThreshold}) {`
|
|
225
|
+
);
|
|
226
|
+
compiler.indentLevel++;
|
|
227
|
+
compiler.emit("// Accuracy not yet sufficient for anchoring");
|
|
228
|
+
compiler.emit("return@addOnUpdateListener");
|
|
229
|
+
compiler.indentLevel--;
|
|
230
|
+
compiler.emit("}");
|
|
231
|
+
compiler.emit("");
|
|
232
|
+
compiler.emit(
|
|
233
|
+
'android.util.Log.d("HoloScript", "Geospatial tracking: accuracy=$horizontalAccuracy m, heading=$headingAccuracy deg")'
|
|
234
|
+
);
|
|
235
|
+
if (usesStreetscape) {
|
|
236
|
+
compiler.emit("");
|
|
237
|
+
compiler.emit("// Process Streetscape Geometry meshes");
|
|
238
|
+
compiler.emit("processStreetscapeGeometry(sessionRef)");
|
|
239
|
+
}
|
|
240
|
+
compiler.indentLevel--;
|
|
241
|
+
compiler.emit("}");
|
|
242
|
+
compiler.indentLevel--;
|
|
243
|
+
compiler.emit("}");
|
|
244
|
+
compiler.emit("");
|
|
245
|
+
compiler.emit(
|
|
246
|
+
"private fun createGeospatialAnchor(name: String, lat: Double, lng: Double, alt: Double, heading: Float) {"
|
|
247
|
+
);
|
|
248
|
+
compiler.indentLevel++;
|
|
249
|
+
compiler.emit("val earthRef = earth ?: return");
|
|
250
|
+
compiler.emit("if (earthRef.trackingState != TrackingState.TRACKING) return");
|
|
251
|
+
compiler.emit("");
|
|
252
|
+
compiler.emit(
|
|
253
|
+
"val anchor = earthRef.createAnchor(lat, lng, alt, 0f, 0f, Math.sin(Math.toRadians(heading.toDouble() / 2)).toFloat(), Math.cos(Math.toRadians(heading.toDouble() / 2)).toFloat())"
|
|
254
|
+
);
|
|
255
|
+
compiler.emit("geospatialAnchors[name] = anchor");
|
|
256
|
+
compiler.emit("");
|
|
257
|
+
compiler.emit("val anchorNode = AnchorNode(anchor)");
|
|
258
|
+
compiler.emit("anchorNode.setParent(arFragment.arSceneView.scene)");
|
|
259
|
+
compiler.emit("");
|
|
260
|
+
compiler.emit("NodeFactory.createDefaultNode(this) { renderable ->");
|
|
261
|
+
compiler.indentLevel++;
|
|
262
|
+
compiler.emit("val node = TransformableNode(arFragment.transformationSystem)");
|
|
263
|
+
compiler.emit("node.setParent(anchorNode)");
|
|
264
|
+
compiler.emit("node.renderable = renderable");
|
|
265
|
+
compiler.emit("placedNodes[name] = node");
|
|
266
|
+
compiler.emit('android.util.Log.d("HoloScript", "Geospatial anchor: $name at ($lat, $lng, $alt)")');
|
|
267
|
+
compiler.indentLevel--;
|
|
268
|
+
compiler.emit("}");
|
|
269
|
+
compiler.indentLevel--;
|
|
270
|
+
compiler.emit("}");
|
|
271
|
+
compiler.emit("");
|
|
272
|
+
if (compiler.compositionHasTrait(composition, "geospatial_terrain_anchor")) {
|
|
273
|
+
compiler.emit(
|
|
274
|
+
"private fun resolveTerrainAnchor(name: String, lat: Double, lng: Double, altOffset: Double, heading: Float) {"
|
|
275
|
+
);
|
|
276
|
+
compiler.indentLevel++;
|
|
277
|
+
compiler.emit("val earthRef = earth ?: return");
|
|
278
|
+
compiler.emit("if (earthRef.trackingState != TrackingState.TRACKING) return");
|
|
279
|
+
compiler.emit("");
|
|
280
|
+
compiler.emit("val qx = 0f");
|
|
281
|
+
compiler.emit(
|
|
282
|
+
"val qy = Math.sin(Math.toRadians(heading.toDouble() / 2)).toFloat()"
|
|
283
|
+
);
|
|
284
|
+
compiler.emit("val qz = 0f");
|
|
285
|
+
compiler.emit(
|
|
286
|
+
"val qw = Math.cos(Math.toRadians(heading.toDouble() / 2)).toFloat()"
|
|
287
|
+
);
|
|
288
|
+
compiler.emit("");
|
|
289
|
+
compiler.emit(
|
|
290
|
+
"earthRef.resolveAnchorOnTerrainAsync(lat, lng, altOffset, qx, qy, qz, qw) { anchor, state ->"
|
|
291
|
+
);
|
|
292
|
+
compiler.indentLevel++;
|
|
293
|
+
compiler.emit("if (state == Anchor.TerrainAnchorState.SUCCESS && anchor != null) {");
|
|
294
|
+
compiler.indentLevel++;
|
|
295
|
+
compiler.emit("geospatialAnchors[name] = anchor");
|
|
296
|
+
compiler.emit("val anchorNode = AnchorNode(anchor)");
|
|
297
|
+
compiler.emit("anchorNode.setParent(arFragment.arSceneView.scene)");
|
|
298
|
+
compiler.emit("");
|
|
299
|
+
compiler.emit(
|
|
300
|
+
"NodeFactory.createDefaultNode(this@" + compiler.options.className + "Activity) { renderable ->"
|
|
301
|
+
);
|
|
302
|
+
compiler.indentLevel++;
|
|
303
|
+
compiler.emit("val node = TransformableNode(arFragment.transformationSystem)");
|
|
304
|
+
compiler.emit("node.setParent(anchorNode)");
|
|
305
|
+
compiler.emit("node.renderable = renderable");
|
|
306
|
+
compiler.emit("placedNodes[name] = node");
|
|
307
|
+
compiler.emit('android.util.Log.d("HoloScript", "Terrain anchor resolved: $name")');
|
|
308
|
+
compiler.indentLevel--;
|
|
309
|
+
compiler.emit("}");
|
|
310
|
+
compiler.indentLevel--;
|
|
311
|
+
compiler.emit("}");
|
|
312
|
+
compiler.indentLevel--;
|
|
313
|
+
compiler.emit("}");
|
|
314
|
+
compiler.indentLevel--;
|
|
315
|
+
compiler.emit("}");
|
|
316
|
+
compiler.emit("");
|
|
317
|
+
}
|
|
318
|
+
if (compiler.compositionHasTrait(composition, "geospatial_rooftop_anchor")) {
|
|
319
|
+
compiler.emit(
|
|
320
|
+
"private fun resolveRooftopAnchor(name: String, lat: Double, lng: Double, altOffset: Double, heading: Float) {"
|
|
321
|
+
);
|
|
322
|
+
compiler.indentLevel++;
|
|
323
|
+
compiler.emit("val earthRef = earth ?: return");
|
|
324
|
+
compiler.emit("if (earthRef.trackingState != TrackingState.TRACKING) return");
|
|
325
|
+
compiler.emit("");
|
|
326
|
+
compiler.emit("val qx = 0f");
|
|
327
|
+
compiler.emit(
|
|
328
|
+
"val qy = Math.sin(Math.toRadians(heading.toDouble() / 2)).toFloat()"
|
|
329
|
+
);
|
|
330
|
+
compiler.emit("val qz = 0f");
|
|
331
|
+
compiler.emit(
|
|
332
|
+
"val qw = Math.cos(Math.toRadians(heading.toDouble() / 2)).toFloat()"
|
|
333
|
+
);
|
|
334
|
+
compiler.emit("");
|
|
335
|
+
compiler.emit(
|
|
336
|
+
"earthRef.resolveAnchorOnRooftopAsync(lat, lng, altOffset, qx, qy, qz, qw) { anchor, state ->"
|
|
337
|
+
);
|
|
338
|
+
compiler.indentLevel++;
|
|
339
|
+
compiler.emit("if (state == Anchor.RooftopAnchorState.SUCCESS && anchor != null) {");
|
|
340
|
+
compiler.indentLevel++;
|
|
341
|
+
compiler.emit("geospatialAnchors[name] = anchor");
|
|
342
|
+
compiler.emit("val anchorNode = AnchorNode(anchor)");
|
|
343
|
+
compiler.emit("anchorNode.setParent(arFragment.arSceneView.scene)");
|
|
344
|
+
compiler.emit("");
|
|
345
|
+
compiler.emit(
|
|
346
|
+
"NodeFactory.createDefaultNode(this@" + compiler.options.className + "Activity) { renderable ->"
|
|
347
|
+
);
|
|
348
|
+
compiler.indentLevel++;
|
|
349
|
+
compiler.emit("val node = TransformableNode(arFragment.transformationSystem)");
|
|
350
|
+
compiler.emit("node.setParent(anchorNode)");
|
|
351
|
+
compiler.emit("node.renderable = renderable");
|
|
352
|
+
compiler.emit("placedNodes[name] = node");
|
|
353
|
+
compiler.emit('android.util.Log.d("HoloScript", "Rooftop anchor resolved: $name")');
|
|
354
|
+
compiler.indentLevel--;
|
|
355
|
+
compiler.emit("}");
|
|
356
|
+
compiler.indentLevel--;
|
|
357
|
+
compiler.emit("}");
|
|
358
|
+
compiler.indentLevel--;
|
|
359
|
+
compiler.emit("}");
|
|
360
|
+
compiler.indentLevel--;
|
|
361
|
+
compiler.emit("}");
|
|
362
|
+
compiler.emit("");
|
|
363
|
+
}
|
|
364
|
+
if (usesStreetscape) {
|
|
365
|
+
compiler.emit("private fun processStreetscapeGeometry(session: Session) {");
|
|
366
|
+
compiler.indentLevel++;
|
|
367
|
+
compiler.emit(
|
|
368
|
+
"val streetscapeGeometries = session.getAllTrackables(StreetscapeGeometry::class.java)"
|
|
369
|
+
);
|
|
370
|
+
compiler.emit("for (geometry in streetscapeGeometries) {");
|
|
371
|
+
compiler.indentLevel++;
|
|
372
|
+
compiler.emit("if (geometry.trackingState != TrackingState.TRACKING) continue");
|
|
373
|
+
compiler.emit("");
|
|
374
|
+
compiler.emit("val mesh = geometry.meshes.firstOrNull() ?: continue");
|
|
375
|
+
compiler.emit("val type = geometry.type");
|
|
376
|
+
compiler.emit(
|
|
377
|
+
'android.util.Log.d("HoloScript", "Streetscape geometry: type=$type, vertices=${mesh.vertexList.size}")'
|
|
378
|
+
);
|
|
379
|
+
compiler.emit("");
|
|
380
|
+
compiler.emit("// Streetscape mesh available for occlusion, raycasting, or rendering");
|
|
381
|
+
compiler.emit("// Type is TERRAIN or BUILDING");
|
|
382
|
+
compiler.indentLevel--;
|
|
383
|
+
compiler.emit("}");
|
|
384
|
+
compiler.indentLevel--;
|
|
385
|
+
compiler.emit("}");
|
|
386
|
+
compiler.emit("");
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
function hasDepthScanTraits(composition) {
|
|
390
|
+
for (const obj of composition.objects || []) {
|
|
391
|
+
for (const trait of obj.traits || []) {
|
|
392
|
+
const name = typeof trait === "string" ? trait : trait.name;
|
|
393
|
+
if (chunkDTGBOSEM_cjs.DEPTH_SCANNER_TRAITS.includes(name)) return true;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
398
|
+
function emitDepthScanSetup(compiler, composition) {
|
|
399
|
+
const hasMlArcore = compiler.compositionHasTrait(composition, "depth_ml_arcore");
|
|
400
|
+
const hasConfidence = compiler.compositionHasTrait(composition, "depth_confidence_map");
|
|
401
|
+
const hasAutoSelect = compiler.compositionHasTrait(composition, "depth_auto_select");
|
|
402
|
+
const hasMeshGenerate = compiler.compositionHasTrait(composition, "depth_mesh_generate");
|
|
403
|
+
const hasMeshToHolo = compiler.compositionHasTrait(composition, "depth_mesh_to_holo");
|
|
404
|
+
const hasRealtime = compiler.compositionHasTrait(composition, "depth_realtime");
|
|
405
|
+
const hasExport = compiler.compositionHasTrait(composition, "depth_export");
|
|
406
|
+
compiler.emit("");
|
|
407
|
+
compiler.emit("// === Depth Scanner: ARCore depth, ToF, stereo (M.010.02b) ===");
|
|
408
|
+
compiler.emit(
|
|
409
|
+
`private val depthConfidenceThreshold = ${chunkDTGBOSEM_cjs.DEPTH_SCANNER_DEFAULTS.scan.confidenceThreshold}`
|
|
410
|
+
);
|
|
411
|
+
compiler.emit(`private val depthMaxMeters = ${chunkDTGBOSEM_cjs.DEPTH_SCANNER_DEFAULTS.scan.maxDepthMeters}f`);
|
|
412
|
+
compiler.emit(`private val depthMeshDecimation = ${chunkDTGBOSEM_cjs.DEPTH_SCANNER_DEFAULTS.scan.meshDecimation}f`);
|
|
413
|
+
compiler.emit("private var depthImage: android.media.Image? = null");
|
|
414
|
+
if (hasConfidence) {
|
|
415
|
+
compiler.emit("private var depthConfidenceImage: android.media.Image? = null");
|
|
416
|
+
}
|
|
417
|
+
compiler.emit("");
|
|
418
|
+
compiler.emit("private fun setupDepthScanner() {");
|
|
419
|
+
compiler.indentLevel++;
|
|
420
|
+
compiler.emit("val session = arFragment.arSceneView.session ?: return");
|
|
421
|
+
compiler.emit("val config = session.config");
|
|
422
|
+
compiler.emit("if (!session.isDepthModeSupported(Config.DepthMode.AUTOMATIC)) {");
|
|
423
|
+
compiler.indentLevel++;
|
|
424
|
+
compiler.emit(
|
|
425
|
+
'android.util.Log.w("HoloScript", "Depth scanner unavailable: ARCore DepthMode.AUTOMATIC is not supported on this device")'
|
|
426
|
+
);
|
|
427
|
+
compiler.emit("return");
|
|
428
|
+
compiler.indentLevel--;
|
|
429
|
+
compiler.emit("}");
|
|
430
|
+
compiler.emit("config.depthMode = Config.DepthMode.AUTOMATIC");
|
|
431
|
+
compiler.emit("session.configure(config)");
|
|
432
|
+
compiler.emit('android.util.Log.d("HoloScript", "Depth scanner: DepthMode.AUTOMATIC enabled")');
|
|
433
|
+
compiler.emit("");
|
|
434
|
+
if (hasAutoSelect) {
|
|
435
|
+
compiler.emit("// Auto-select best depth source: ToF > ARCore ML > Stereo");
|
|
436
|
+
compiler.emit("detectDepthSource()");
|
|
437
|
+
compiler.emit("");
|
|
438
|
+
}
|
|
439
|
+
if (hasRealtime) {
|
|
440
|
+
compiler.emit("// Continuous depth frame processing in render loop");
|
|
441
|
+
compiler.emit("arFragment.arSceneView.scene.addOnUpdateListener { _ ->");
|
|
442
|
+
compiler.indentLevel++;
|
|
443
|
+
compiler.emit("val frame = arFragment.arSceneView.arFrame ?: return@addOnUpdateListener");
|
|
444
|
+
compiler.emit("processDepthFrame(frame)");
|
|
445
|
+
compiler.indentLevel--;
|
|
446
|
+
compiler.emit("}");
|
|
447
|
+
}
|
|
448
|
+
compiler.indentLevel--;
|
|
449
|
+
compiler.emit("}");
|
|
450
|
+
compiler.emit("");
|
|
451
|
+
compiler.emit("private fun processDepthFrame(frame: Frame) {");
|
|
452
|
+
compiler.indentLevel++;
|
|
453
|
+
compiler.emit("try {");
|
|
454
|
+
compiler.indentLevel++;
|
|
455
|
+
if (hasMlArcore) {
|
|
456
|
+
compiler.emit("// ARCore ML depth: acquire 16-bit depth image");
|
|
457
|
+
compiler.emit("depthImage?.close()");
|
|
458
|
+
compiler.emit("depthImage = frame.acquireDepthImage16Bits()");
|
|
459
|
+
} else {
|
|
460
|
+
compiler.emit("depthImage?.close()");
|
|
461
|
+
compiler.emit("depthImage = frame.acquireDepthImage16Bits()");
|
|
462
|
+
}
|
|
463
|
+
if (hasConfidence) {
|
|
464
|
+
compiler.emit("");
|
|
465
|
+
compiler.emit("// Confidence map: per-pixel confidence (0-255)");
|
|
466
|
+
compiler.emit("depthConfidenceImage?.close()");
|
|
467
|
+
compiler.emit("depthConfidenceImage = frame.acquireRawDepthConfidenceImage()");
|
|
468
|
+
}
|
|
469
|
+
if (hasMeshGenerate) {
|
|
470
|
+
compiler.emit("");
|
|
471
|
+
compiler.emit("val depthImg = depthImage ?: return");
|
|
472
|
+
compiler.emit("generateMeshFromDepth(depthImg)");
|
|
473
|
+
}
|
|
474
|
+
compiler.indentLevel--;
|
|
475
|
+
compiler.emit("} catch (e: Exception) {");
|
|
476
|
+
compiler.indentLevel++;
|
|
477
|
+
compiler.emit('android.util.Log[3]("HoloScript", "Depth frame unavailable: ${e.message}")');
|
|
478
|
+
compiler.indentLevel--;
|
|
479
|
+
compiler.emit("}");
|
|
480
|
+
compiler.indentLevel--;
|
|
481
|
+
compiler.emit("}");
|
|
482
|
+
compiler.emit("");
|
|
483
|
+
if (hasAutoSelect) {
|
|
484
|
+
compiler.emit("private fun detectDepthSource() {");
|
|
485
|
+
compiler.indentLevel++;
|
|
486
|
+
compiler.emit("val session = arFragment.arSceneView.session ?: return");
|
|
487
|
+
compiler.emit("");
|
|
488
|
+
compiler.emit("// Check ToF sensor availability");
|
|
489
|
+
compiler.emit(
|
|
490
|
+
'val hasToF = packageManager.hasSystemFeature("android.hardware.sensor.proximity")'
|
|
491
|
+
);
|
|
492
|
+
compiler.emit("");
|
|
493
|
+
compiler.emit("// Check ARCore depth support");
|
|
494
|
+
compiler.emit("val hasARCoreDepth = session.isDepthModeSupported(Config.DepthMode.AUTOMATIC)");
|
|
495
|
+
compiler.emit("");
|
|
496
|
+
compiler.emit("// Check dual camera for stereo depth");
|
|
497
|
+
compiler.emit(
|
|
498
|
+
"val cameraManager = getSystemService(android.hardware.camera2.CameraManager::class.java)"
|
|
499
|
+
);
|
|
500
|
+
compiler.emit("val hasStereo = (cameraManager?.cameraIdList?.size ?: 0) >= 2");
|
|
501
|
+
compiler.emit("");
|
|
502
|
+
compiler.emit("// Priority: ToF > ARCore ML > Stereo");
|
|
503
|
+
compiler.emit("val depthSource = when {");
|
|
504
|
+
compiler.indentLevel++;
|
|
505
|
+
compiler.emit('hasToF -> "ToF"');
|
|
506
|
+
compiler.emit('hasARCoreDepth -> "ARCore_ML"');
|
|
507
|
+
compiler.emit('hasStereo -> "Stereo"');
|
|
508
|
+
compiler.emit('else -> "None"');
|
|
509
|
+
compiler.indentLevel--;
|
|
510
|
+
compiler.emit("}");
|
|
511
|
+
compiler.emit(
|
|
512
|
+
'android.util.Log.d("HoloScript", "Depth source selected: $depthSource (ToF=$hasToF, ARCore=$hasARCoreDepth, Stereo=$hasStereo)")'
|
|
513
|
+
);
|
|
514
|
+
compiler.indentLevel--;
|
|
515
|
+
compiler.emit("}");
|
|
516
|
+
compiler.emit("");
|
|
517
|
+
}
|
|
518
|
+
if (hasMeshGenerate) {
|
|
519
|
+
compiler.emit("private fun generateMeshFromDepth(depthImage: android.media.Image) {");
|
|
520
|
+
compiler.indentLevel++;
|
|
521
|
+
compiler.emit("val width = depthImage.width");
|
|
522
|
+
compiler.emit("val height = depthImage.height");
|
|
523
|
+
compiler.emit("val buffer = depthImage.planes[0].buffer");
|
|
524
|
+
compiler.emit("val vertices = mutableListOf<Vector3>()");
|
|
525
|
+
compiler.emit("val indices = mutableListOf<Int>()");
|
|
526
|
+
compiler.emit("");
|
|
527
|
+
compiler.emit("// Convert depth pixels to 3D points with decimation");
|
|
528
|
+
compiler.emit("val step = (1.0f / depthMeshDecimation).toInt().coerceAtLeast(1)");
|
|
529
|
+
compiler.emit("for (y in 0 until height step step) {");
|
|
530
|
+
compiler.indentLevel++;
|
|
531
|
+
compiler.emit("for (x in 0 until width step step) {");
|
|
532
|
+
compiler.indentLevel++;
|
|
533
|
+
compiler.emit("val depthMm = buffer.getShort((y * width + x) * 2).toInt() and 0xFFFF");
|
|
534
|
+
compiler.emit("val depthM = depthMm / 1000.0f");
|
|
535
|
+
compiler.emit("if (depthM <= 0f || depthM > depthMaxMeters) continue");
|
|
536
|
+
compiler.emit("");
|
|
537
|
+
compiler.emit("// Unproject pixel to 3D point (simplified pinhole model)");
|
|
538
|
+
compiler.emit("val fx = width / 2.0f // focal length approximation");
|
|
539
|
+
compiler.emit("val fy = height / 2.0f");
|
|
540
|
+
compiler.emit("val cx = width / 2.0f");
|
|
541
|
+
compiler.emit("val cy = height / 2.0f");
|
|
542
|
+
compiler.emit("val px = (x - cx) / fx * depthM");
|
|
543
|
+
compiler.emit("val py = (y - cy) / fy * depthM");
|
|
544
|
+
compiler.emit("vertices.add(Vector3(px, py, depthM))");
|
|
545
|
+
compiler.indentLevel--;
|
|
546
|
+
compiler.emit("}");
|
|
547
|
+
compiler.indentLevel--;
|
|
548
|
+
compiler.emit("}");
|
|
549
|
+
compiler.emit("");
|
|
550
|
+
compiler.emit("// Triangulate adjacent points into mesh");
|
|
551
|
+
compiler.emit("val cols = width / step");
|
|
552
|
+
compiler.emit("for (row in 0 until (vertices.size / cols) - 1) {");
|
|
553
|
+
compiler.indentLevel++;
|
|
554
|
+
compiler.emit("for (col in 0 until cols - 1) {");
|
|
555
|
+
compiler.indentLevel++;
|
|
556
|
+
compiler.emit("val i = row * cols + col");
|
|
557
|
+
compiler.emit("indices.add(i)");
|
|
558
|
+
compiler.emit("indices.add(i + 1)");
|
|
559
|
+
compiler.emit("indices.add(i + cols)");
|
|
560
|
+
compiler.emit("indices.add(i + 1)");
|
|
561
|
+
compiler.emit("indices.add(i + cols + 1)");
|
|
562
|
+
compiler.emit("indices.add(i + cols)");
|
|
563
|
+
compiler.indentLevel--;
|
|
564
|
+
compiler.emit("}");
|
|
565
|
+
compiler.indentLevel--;
|
|
566
|
+
compiler.emit("}");
|
|
567
|
+
compiler.emit("");
|
|
568
|
+
compiler.emit(
|
|
569
|
+
'android.util.Log.d("HoloScript", "Depth mesh: ${vertices.size} vertices, ${indices.size / 3} triangles")'
|
|
570
|
+
);
|
|
571
|
+
if (hasMeshToHolo) {
|
|
572
|
+
compiler.emit("convertMeshToHolo(vertices, indices)");
|
|
573
|
+
}
|
|
574
|
+
compiler.indentLevel--;
|
|
575
|
+
compiler.emit("}");
|
|
576
|
+
compiler.emit("");
|
|
577
|
+
}
|
|
578
|
+
if (hasMeshToHolo) {
|
|
579
|
+
compiler.emit("private fun convertMeshToHolo(vertices: List<Vector3>, indices: List<Int>) {");
|
|
580
|
+
compiler.indentLevel++;
|
|
581
|
+
compiler.emit("// Convert mesh vertices + classification to HoloScript entities");
|
|
582
|
+
compiler.emit("val holoEntities = mutableListOf<Map<String, Any>>()");
|
|
583
|
+
compiler.emit("for ((i, vertex) in vertices.withIndex()) {");
|
|
584
|
+
compiler.indentLevel++;
|
|
585
|
+
compiler.emit("holoEntities.add(mapOf(");
|
|
586
|
+
compiler.indentLevel++;
|
|
587
|
+
compiler.emit('"type" to "DepthPoint",');
|
|
588
|
+
compiler.emit('"id" to "dp_$i",');
|
|
589
|
+
compiler.emit('"position" to listOf(vertex[0], vertex[1], vertex[2]),');
|
|
590
|
+
compiler.emit('"classification" to "unknown"');
|
|
591
|
+
compiler.indentLevel--;
|
|
592
|
+
compiler.emit("))");
|
|
593
|
+
compiler.indentLevel--;
|
|
594
|
+
compiler.emit("}");
|
|
595
|
+
compiler.emit(
|
|
596
|
+
'android.util.Log.d("HoloScript", "Converted ${holoEntities.size} depth points to .holo entities")'
|
|
597
|
+
);
|
|
598
|
+
compiler.indentLevel--;
|
|
599
|
+
compiler.emit("}");
|
|
600
|
+
compiler.emit("");
|
|
601
|
+
}
|
|
602
|
+
if (hasExport) {
|
|
603
|
+
const format = chunkDTGBOSEM_cjs.DEPTH_SCANNER_DEFAULTS.export.format;
|
|
604
|
+
compiler.emit("private fun exportDepthMesh(vertices: List<Vector3>, indices: List<Int>) {");
|
|
605
|
+
compiler.indentLevel++;
|
|
606
|
+
compiler.emit(`val format = "${format}"`);
|
|
607
|
+
compiler.emit('val filename = "depth_scan_${System.currentTimeMillis()}.$format"');
|
|
608
|
+
compiler.emit("val file = java.io.File(getExternalFilesDir(null), filename)");
|
|
609
|
+
compiler.emit("");
|
|
610
|
+
compiler.emit('if (format == "obj") {');
|
|
611
|
+
compiler.indentLevel++;
|
|
612
|
+
compiler.emit("val sb = StringBuilder()");
|
|
613
|
+
compiler.emit('sb.appendLine("# HoloScript Depth Scan Export")');
|
|
614
|
+
compiler.emit("for (v in vertices) {");
|
|
615
|
+
compiler.indentLevel++;
|
|
616
|
+
compiler.emit('sb.appendLine("v ${v[0]} ${v[1]} ${v[2]}")');
|
|
617
|
+
compiler.indentLevel--;
|
|
618
|
+
compiler.emit("}");
|
|
619
|
+
compiler.emit("for (i in indices.indices step 3) {");
|
|
620
|
+
compiler.indentLevel++;
|
|
621
|
+
compiler.emit('sb.appendLine("f ${indices[i]+1} ${indices[i+1]+1} ${indices[i+2]+1}")');
|
|
622
|
+
compiler.indentLevel--;
|
|
623
|
+
compiler.emit("}");
|
|
624
|
+
compiler.emit("file.writeText(sb.toString())");
|
|
625
|
+
compiler.indentLevel--;
|
|
626
|
+
compiler.emit("} else {");
|
|
627
|
+
compiler.indentLevel++;
|
|
628
|
+
compiler.emit("// GLB export: binary glTF format");
|
|
629
|
+
compiler.emit('android.util.Log.d("HoloScript", "GLB export requires glTF serializer library")');
|
|
630
|
+
compiler.indentLevel--;
|
|
631
|
+
compiler.emit("}");
|
|
632
|
+
compiler.emit("");
|
|
633
|
+
compiler.emit('android.util.Log.d("HoloScript", "Depth mesh exported: ${file.absolutePath}")');
|
|
634
|
+
compiler.indentLevel--;
|
|
635
|
+
compiler.emit("}");
|
|
636
|
+
compiler.emit("");
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
function hasPortalARTraits(composition) {
|
|
640
|
+
const portalNames = chunk6EX3I5P4_cjs.PORTAL_AR_TRAITS;
|
|
641
|
+
for (const obj of composition.objects || []) {
|
|
642
|
+
for (const trait of obj.traits || []) {
|
|
643
|
+
const name = typeof trait === "string" ? trait : trait.name;
|
|
644
|
+
if (portalNames.includes(name)) return true;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
return false;
|
|
648
|
+
}
|
|
649
|
+
function emitPortalARSetup(compiler, composition) {
|
|
650
|
+
compiler.emit("");
|
|
651
|
+
compiler.emit("// === Portal AR: full-scene holographic layer behind reality ===");
|
|
652
|
+
compiler.emit("private var portalSession: Session? = null");
|
|
653
|
+
compiler.emit("private var portalRenderer: HolographicRenderer? = null");
|
|
654
|
+
compiler.emit("private var depthTexture: Image? = null");
|
|
655
|
+
compiler.emit("private var sceneMesh: Mesh? = null");
|
|
656
|
+
compiler.emit("");
|
|
657
|
+
compiler.emit("private fun setupPortalAR() {");
|
|
658
|
+
compiler.indentLevel++;
|
|
659
|
+
compiler.emit("val session = arFragment.arSceneView.session ?: return");
|
|
660
|
+
compiler.emit("");
|
|
661
|
+
compiler.emit("// Enable depth mode for portal occlusion");
|
|
662
|
+
compiler.emit("val config = Config(session)");
|
|
663
|
+
compiler.emit("config.depthMode = Config.DepthMode.AUTOMATIC");
|
|
664
|
+
compiler.emit("config.lightEstimationMode = Config.LightEstimationMode.ENVIRONMENTAL_HDR");
|
|
665
|
+
if (compiler.compositionHasTrait(composition, "portal_world_mesh") || compiler.compositionHasTrait(composition, "portal_mesh_occlusion")) {
|
|
666
|
+
compiler.emit("config.planeFindingMode = Config.PlaneFindingMode.HORIZONTAL_AND_VERTICAL");
|
|
667
|
+
compiler.emit("// Enable scene mesh for portal world mesh integration");
|
|
668
|
+
compiler.emit("if (session.isDepthModeSupported(Config.DepthMode.AUTOMATIC)) {");
|
|
669
|
+
compiler.indentLevel++;
|
|
670
|
+
compiler.emit("config.depthMode = Config.DepthMode.AUTOMATIC");
|
|
671
|
+
compiler.indentLevel--;
|
|
672
|
+
compiler.emit("}");
|
|
673
|
+
}
|
|
674
|
+
compiler.emit("session.configure(config)");
|
|
675
|
+
compiler.emit("portalSession = session");
|
|
676
|
+
compiler.emit("");
|
|
677
|
+
compiler.emit("// Create holographic render layer behind reality");
|
|
678
|
+
compiler.emit("portalRenderer = HolographicRenderer(this)");
|
|
679
|
+
compiler.emit("portalRenderer?.initialize(session)");
|
|
680
|
+
compiler.emit("");
|
|
681
|
+
if (compiler.compositionHasTrait(composition, "portal_occlusion")) {
|
|
682
|
+
compiler.emit("// Portal occlusion: real objects block holograms via depth buffer");
|
|
683
|
+
compiler.emit("arFragment.arSceneView.scene.addOnUpdateListener { _ ->");
|
|
684
|
+
compiler.indentLevel++;
|
|
685
|
+
compiler.emit("val frame = arFragment.arSceneView.arFrame ?: return@addOnUpdateListener");
|
|
686
|
+
compiler.emit("try {");
|
|
687
|
+
compiler.indentLevel++;
|
|
688
|
+
compiler.emit("depthTexture = frame.acquireDepthImage16Bits()");
|
|
689
|
+
compiler.emit("portalRenderer?.updateDepthOcclusion(depthTexture!!)");
|
|
690
|
+
compiler.indentLevel--;
|
|
691
|
+
compiler.emit("} catch (e: Exception) {");
|
|
692
|
+
compiler.indentLevel++;
|
|
693
|
+
compiler.emit("// Depth not available on this frame");
|
|
694
|
+
compiler.indentLevel--;
|
|
695
|
+
compiler.emit("} finally {");
|
|
696
|
+
compiler.indentLevel++;
|
|
697
|
+
compiler.emit("depthTexture?.close()");
|
|
698
|
+
compiler.indentLevel--;
|
|
699
|
+
compiler.emit("}");
|
|
700
|
+
compiler.indentLevel--;
|
|
701
|
+
compiler.emit("}");
|
|
702
|
+
compiler.emit("");
|
|
703
|
+
}
|
|
704
|
+
if (compiler.compositionHasTrait(composition, "portal_parallax")) {
|
|
705
|
+
compiler.emit("// Portal parallax: depth-correct parallax from device pose");
|
|
706
|
+
compiler.emit("arFragment.arSceneView.scene.addOnUpdateListener { _ ->");
|
|
707
|
+
compiler.indentLevel++;
|
|
708
|
+
compiler.emit("val frame = arFragment.arSceneView.arFrame ?: return@addOnUpdateListener");
|
|
709
|
+
compiler.emit("val cameraPose = frame.camera.pose");
|
|
710
|
+
compiler.emit("portalRenderer?.applyParallaxCorrection(cameraPose)");
|
|
711
|
+
compiler.indentLevel--;
|
|
712
|
+
compiler.emit("}");
|
|
713
|
+
compiler.emit("");
|
|
714
|
+
}
|
|
715
|
+
if (compiler.compositionHasTrait(composition, "portal_depth_fade")) {
|
|
716
|
+
compiler.emit("// Portal depth fade: holograms fade at distance");
|
|
717
|
+
compiler.emit("portalRenderer?.enableDepthFade(nearPlane = 0.1f, farPlane = 10.0f)");
|
|
718
|
+
compiler.emit("");
|
|
719
|
+
}
|
|
720
|
+
if (compiler.compositionHasTrait(composition, "portal_world_mesh") || compiler.compositionHasTrait(composition, "portal_mesh_occlusion")) {
|
|
721
|
+
compiler.emit("// Portal world mesh: scene mesh for realistic occlusion");
|
|
722
|
+
compiler.emit("arFragment.arSceneView.scene.addOnUpdateListener { _ ->");
|
|
723
|
+
compiler.indentLevel++;
|
|
724
|
+
compiler.emit("val frame = arFragment.arSceneView.arFrame ?: return@addOnUpdateListener");
|
|
725
|
+
compiler.emit("try {");
|
|
726
|
+
compiler.indentLevel++;
|
|
727
|
+
compiler.emit("val rawDepth = frame.acquireRawDepthImage16Bits()");
|
|
728
|
+
compiler.emit("sceneMesh = portalRenderer?.reconstructMesh(rawDepth)");
|
|
729
|
+
compiler.emit("portalRenderer?.updateMeshOcclusion(sceneMesh!!)");
|
|
730
|
+
compiler.emit("rawDepth.close()");
|
|
731
|
+
compiler.indentLevel--;
|
|
732
|
+
compiler.emit("} catch (e: Exception) { /* depth not available */ }");
|
|
733
|
+
compiler.indentLevel--;
|
|
734
|
+
compiler.emit("}");
|
|
735
|
+
compiler.emit("");
|
|
736
|
+
}
|
|
737
|
+
if (compiler.compositionHasTrait(composition, "portal_peek_through")) {
|
|
738
|
+
compiler.emit("// Portal peek through: tilt phone to reveal holographic layer");
|
|
739
|
+
compiler.emit("val sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager");
|
|
740
|
+
compiler.emit("val accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)");
|
|
741
|
+
compiler.emit("val portalTiltThreshold = 30.0f // degrees");
|
|
742
|
+
compiler.emit("sensorManager.registerListener(object : SensorEventListener {");
|
|
743
|
+
compiler.indentLevel++;
|
|
744
|
+
compiler.emit("override fun onSensorChanged(event: SensorEvent) {");
|
|
745
|
+
compiler.indentLevel++;
|
|
746
|
+
compiler.emit(
|
|
747
|
+
"val tiltAngle = Math.toDegrees(Math.atan2(event.values[1].toDouble(), event.values[2].toDouble())).toFloat()"
|
|
748
|
+
);
|
|
749
|
+
compiler.emit("portalRenderer?.setPortalVisibility(Math.abs(tiltAngle) > portalTiltThreshold)");
|
|
750
|
+
compiler.indentLevel--;
|
|
751
|
+
compiler.emit("}");
|
|
752
|
+
compiler.emit("override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}");
|
|
753
|
+
compiler.indentLevel--;
|
|
754
|
+
compiler.emit("}, accelerometer, SensorManager.SENSOR_DELAY_UI)");
|
|
755
|
+
compiler.emit("");
|
|
756
|
+
}
|
|
757
|
+
if (compiler.compositionHasTrait(composition, "portal_boundary")) {
|
|
758
|
+
compiler.emit("// Portal boundary: configurable shape (circle/rect)");
|
|
759
|
+
compiler.emit("val portalBoundary = PortalBoundary()");
|
|
760
|
+
compiler.emit("portalBoundary.shape = PortalBoundary.Shape.CIRCLE // configurable");
|
|
761
|
+
compiler.emit("portalBoundary.radius = 1.5f");
|
|
762
|
+
compiler.emit("portalRenderer?.setBoundary(portalBoundary)");
|
|
763
|
+
compiler.emit("");
|
|
764
|
+
}
|
|
765
|
+
if (compiler.compositionHasTrait(composition, "portal_lighting_match")) {
|
|
766
|
+
compiler.emit("// Portal lighting match: use ARCore light estimation");
|
|
767
|
+
compiler.emit("arFragment.arSceneView.scene.addOnUpdateListener { _ ->");
|
|
768
|
+
compiler.indentLevel++;
|
|
769
|
+
compiler.emit("val frame = arFragment.arSceneView.arFrame ?: return@addOnUpdateListener");
|
|
770
|
+
compiler.emit("val lightEstimate = frame.lightEstimate");
|
|
771
|
+
compiler.emit("if (lightEstimate != null && lightEstimate.state == LightEstimate.State.VALID) {");
|
|
772
|
+
compiler.indentLevel++;
|
|
773
|
+
compiler.emit("val intensity = lightEstimate.pixelIntensity");
|
|
774
|
+
compiler.emit("val colorCorrection = FloatArray(4)");
|
|
775
|
+
compiler.emit("lightEstimate.getColorCorrection(colorCorrection, 0)");
|
|
776
|
+
compiler.emit("portalRenderer?.updateLighting(intensity, colorCorrection)");
|
|
777
|
+
compiler.indentLevel--;
|
|
778
|
+
compiler.emit("}");
|
|
779
|
+
compiler.indentLevel--;
|
|
780
|
+
compiler.emit("}");
|
|
781
|
+
compiler.emit("");
|
|
782
|
+
}
|
|
783
|
+
compiler.emit('print("[HoloScript] Portal AR initialized")');
|
|
784
|
+
compiler.indentLevel--;
|
|
785
|
+
compiler.emit("}");
|
|
786
|
+
}
|
|
787
|
+
function hasHandTrackingTraits(composition) {
|
|
788
|
+
const handTraitNames = chunk6EX3I5P4_cjs.CAMERA_HAND_TRACKING_TRAITS;
|
|
789
|
+
for (const obj of composition.objects || []) {
|
|
790
|
+
for (const trait of obj.traits || []) {
|
|
791
|
+
const name = typeof trait === "string" ? trait : trait.name;
|
|
792
|
+
if (handTraitNames.includes(name)) return true;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
return false;
|
|
796
|
+
}
|
|
797
|
+
function emitHandTrackingSetup(compiler, composition) {
|
|
798
|
+
compiler.emit("");
|
|
799
|
+
compiler.emit("// === Camera Hand Tracking: MediaPipe Hands (M.010.04) ===");
|
|
800
|
+
compiler.emit("private var handsolution: com.google.mediapipe.solutions.hands.Hands? = null");
|
|
801
|
+
compiler.emit("");
|
|
802
|
+
const twoHands = compiler.compositionHasTrait(composition, "camera_hand_two_hands");
|
|
803
|
+
const maxHands = twoHands ? 2 : 1;
|
|
804
|
+
const hasPinch = compiler.compositionHasTrait(composition, "camera_hand_gesture_pinch");
|
|
805
|
+
const hasPoint = compiler.compositionHasTrait(composition, "camera_hand_gesture_point");
|
|
806
|
+
const hasPalm = compiler.compositionHasTrait(composition, "camera_hand_gesture_palm");
|
|
807
|
+
const hasFist = compiler.compositionHasTrait(composition, "camera_hand_gesture_fist");
|
|
808
|
+
const hasConfidence = compiler.compositionHasTrait(composition, "camera_hand_confidence");
|
|
809
|
+
const hasSkeleton = compiler.compositionHasTrait(composition, "camera_hand_skeleton");
|
|
810
|
+
const hasToSpatial = compiler.compositionHasTrait(composition, "camera_hand_to_spatial");
|
|
811
|
+
compiler.emit("private fun setupHandTracking() {");
|
|
812
|
+
compiler.indentLevel++;
|
|
813
|
+
compiler.emit("val options = com.google.mediapipe.solutions.hands.HandsOptions.builder()");
|
|
814
|
+
compiler.emit(` .setMaxNumHands(${maxHands})`);
|
|
815
|
+
compiler.emit(" .setStaticImageMode(false)");
|
|
816
|
+
compiler.emit(
|
|
817
|
+
" .setRunningMode(com.google.mediapipe.solutions.hands.Hands.RUNNING_MODE_LIVE_STREAM)"
|
|
818
|
+
);
|
|
819
|
+
if (hasConfidence) {
|
|
820
|
+
compiler.emit(" .setMinHandDetectionConfidence(0.7f)");
|
|
821
|
+
} else {
|
|
822
|
+
compiler.emit(" .setMinHandDetectionConfidence(0.5f)");
|
|
823
|
+
}
|
|
824
|
+
compiler.emit(" .setMinTrackingConfidence(0.5f)");
|
|
825
|
+
compiler.emit(" .build()");
|
|
826
|
+
compiler.emit("");
|
|
827
|
+
compiler.emit("handsolution = com.google.mediapipe.solutions.hands.Hands(this, options)");
|
|
828
|
+
compiler.emit("handsolution?.setResultListener { result ->");
|
|
829
|
+
compiler.indentLevel++;
|
|
830
|
+
compiler.emit("processHandResults(result)");
|
|
831
|
+
compiler.indentLevel--;
|
|
832
|
+
compiler.emit("}");
|
|
833
|
+
compiler.emit("");
|
|
834
|
+
compiler.emit("// Start CameraX front camera feed");
|
|
835
|
+
compiler.emit("startCameraForHandTracking()");
|
|
836
|
+
compiler.indentLevel--;
|
|
837
|
+
compiler.emit("}");
|
|
838
|
+
compiler.emit("");
|
|
839
|
+
compiler.emit("private fun startCameraForHandTracking() {");
|
|
840
|
+
compiler.indentLevel++;
|
|
841
|
+
compiler.emit(
|
|
842
|
+
"val cameraProviderFuture = androidx.camera.lifecycle.ProcessCameraProvider.getInstance(this)"
|
|
843
|
+
);
|
|
844
|
+
compiler.emit("cameraProviderFuture.addListener({");
|
|
845
|
+
compiler.indentLevel++;
|
|
846
|
+
compiler.emit("val cameraProvider = cameraProviderFuture.get()");
|
|
847
|
+
compiler.emit("val preview = androidx.camera.core.Preview.Builder().build()");
|
|
848
|
+
compiler.emit("val imageAnalysis = androidx.camera.core.ImageAnalysis.Builder()");
|
|
849
|
+
compiler.emit(
|
|
850
|
+
" .setBackpressureStrategy(androidx.camera.core.ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)"
|
|
851
|
+
);
|
|
852
|
+
compiler.emit(" .build()");
|
|
853
|
+
compiler.emit(
|
|
854
|
+
"imageAnalysis.setAnalyzer(java.util.concurrent.Executors.newSingleThreadExecutor()) { imageProxy ->"
|
|
855
|
+
);
|
|
856
|
+
compiler.indentLevel++;
|
|
857
|
+
compiler.emit("handsolution?.send(imageProxy)");
|
|
858
|
+
compiler.indentLevel--;
|
|
859
|
+
compiler.emit("}");
|
|
860
|
+
compiler.emit("val cameraSelector = androidx.camera.core.CameraSelector.DEFAULT_FRONT_CAMERA");
|
|
861
|
+
compiler.emit("cameraProvider.unbindAll()");
|
|
862
|
+
compiler.emit("cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalysis)");
|
|
863
|
+
compiler.indentLevel--;
|
|
864
|
+
compiler.emit("}, androidx.core.content.ContextCompat.getMainExecutor(this))");
|
|
865
|
+
compiler.indentLevel--;
|
|
866
|
+
compiler.emit("}");
|
|
867
|
+
compiler.emit("");
|
|
868
|
+
compiler.emit(
|
|
869
|
+
"private fun processHandResults(result: com.google.mediapipe.solutions.hands.HandsResult) {"
|
|
870
|
+
);
|
|
871
|
+
compiler.indentLevel++;
|
|
872
|
+
compiler.emit("if (result.multiHandLandmarks().isEmpty()) return");
|
|
873
|
+
compiler.emit("");
|
|
874
|
+
compiler.emit("for ((handIndex, landmarks) in result.multiHandLandmarks().withIndex()) {");
|
|
875
|
+
compiler.indentLevel++;
|
|
876
|
+
if (hasSkeleton) {
|
|
877
|
+
compiler.emit("// 21-joint skeleton data");
|
|
878
|
+
compiler.emit("val wrist = landmarks.landmarkList[0]");
|
|
879
|
+
compiler.emit("val thumbCmc = landmarks.landmarkList[1]");
|
|
880
|
+
compiler.emit("val thumbMcp = landmarks.landmarkList[2]");
|
|
881
|
+
compiler.emit("val thumbIp = landmarks.landmarkList[3]");
|
|
882
|
+
compiler.emit("val thumbTip = landmarks.landmarkList[4]");
|
|
883
|
+
compiler.emit("val indexMcp = landmarks.landmarkList[5]");
|
|
884
|
+
compiler.emit("val indexPip = landmarks.landmarkList[6]");
|
|
885
|
+
compiler.emit("val indexDip = landmarks.landmarkList[7]");
|
|
886
|
+
compiler.emit("val indexTip = landmarks.landmarkList[8]");
|
|
887
|
+
compiler.emit("val middleMcp = landmarks.landmarkList[9]");
|
|
888
|
+
compiler.emit("val middlePip = landmarks.landmarkList[10]");
|
|
889
|
+
compiler.emit("val middleDip = landmarks.landmarkList[11]");
|
|
890
|
+
compiler.emit("val middleTip = landmarks.landmarkList[12]");
|
|
891
|
+
compiler.emit("val ringMcp = landmarks.landmarkList[13]");
|
|
892
|
+
compiler.emit("val ringPip = landmarks.landmarkList[14]");
|
|
893
|
+
compiler.emit("val ringDip = landmarks.landmarkList[15]");
|
|
894
|
+
compiler.emit("val ringTip = landmarks.landmarkList[16]");
|
|
895
|
+
compiler.emit("val pinkyMcp = landmarks.landmarkList[17]");
|
|
896
|
+
compiler.emit("val pinkyPip = landmarks.landmarkList[18]");
|
|
897
|
+
compiler.emit("val pinkyDip = landmarks.landmarkList[19]");
|
|
898
|
+
compiler.emit("val pinkyTip = landmarks.landmarkList[20]");
|
|
899
|
+
compiler.emit("");
|
|
900
|
+
} else {
|
|
901
|
+
compiler.emit("// Key landmarks for gesture recognition");
|
|
902
|
+
compiler.emit("val thumbTip = landmarks.landmarkList[4]");
|
|
903
|
+
compiler.emit("val indexMcp = landmarks.landmarkList[5]");
|
|
904
|
+
compiler.emit("val indexPip = landmarks.landmarkList[6]");
|
|
905
|
+
compiler.emit("val indexTip = landmarks.landmarkList[8]");
|
|
906
|
+
compiler.emit("val middleMcp = landmarks.landmarkList[9]");
|
|
907
|
+
compiler.emit("val middlePip = landmarks.landmarkList[10]");
|
|
908
|
+
compiler.emit("val middleTip = landmarks.landmarkList[12]");
|
|
909
|
+
compiler.emit("val ringMcp = landmarks.landmarkList[13]");
|
|
910
|
+
compiler.emit("val ringPip = landmarks.landmarkList[14]");
|
|
911
|
+
compiler.emit("val ringTip = landmarks.landmarkList[16]");
|
|
912
|
+
compiler.emit("val pinkyMcp = landmarks.landmarkList[17]");
|
|
913
|
+
compiler.emit("val pinkyPip = landmarks.landmarkList[18]");
|
|
914
|
+
compiler.emit("val pinkyTip = landmarks.landmarkList[20]");
|
|
915
|
+
compiler.emit("");
|
|
916
|
+
}
|
|
917
|
+
if (hasConfidence) {
|
|
918
|
+
compiler.emit("// Filter low-confidence landmarks");
|
|
919
|
+
compiler.emit("val minConfidence = 0.7f");
|
|
920
|
+
compiler.emit(
|
|
921
|
+
"if (thumbTip.visibility < minConfidence || indexTip.visibility < minConfidence) {"
|
|
922
|
+
);
|
|
923
|
+
compiler.indentLevel++;
|
|
924
|
+
compiler.emit("continue");
|
|
925
|
+
compiler.indentLevel--;
|
|
926
|
+
compiler.emit("}");
|
|
927
|
+
compiler.emit("");
|
|
928
|
+
}
|
|
929
|
+
if (hasPinch) {
|
|
930
|
+
compiler.emit("// Pinch gesture: thumb tip close to index tip");
|
|
931
|
+
compiler.emit("val pinchDist = Math.sqrt(");
|
|
932
|
+
compiler.emit(
|
|
933
|
+
" Math.pow((thumbTip[0] - indexTip[0]).toDouble(), 2.0) +"
|
|
934
|
+
);
|
|
935
|
+
compiler.emit(" Math.pow((thumbTip[1] - indexTip[1]).toDouble(), 2.0)");
|
|
936
|
+
compiler.emit(").toFloat()");
|
|
937
|
+
compiler.emit("if (pinchDist < 0.05f) {");
|
|
938
|
+
compiler.indentLevel++;
|
|
939
|
+
compiler.emit('android.util.Log.d("HoloScript", "Hand $handIndex: PINCH detected")');
|
|
940
|
+
if (hasToSpatial) {
|
|
941
|
+
compiler.emit('onSpatialInput("pinch", handIndex, thumbTip[0], thumbTip[1], thumbTip[2])');
|
|
942
|
+
}
|
|
943
|
+
compiler.indentLevel--;
|
|
944
|
+
compiler.emit("}");
|
|
945
|
+
compiler.emit("");
|
|
946
|
+
}
|
|
947
|
+
if (hasPoint) {
|
|
948
|
+
compiler.emit("// Point gesture: index extended, others curled");
|
|
949
|
+
compiler.emit("val indexExtended = indexTip[1] < indexMcp[1]");
|
|
950
|
+
compiler.emit("val middleCurled = middleTip[1] > middlePip[1]");
|
|
951
|
+
compiler.emit("val ringCurled = ringTip[1] > ringPip[1]");
|
|
952
|
+
compiler.emit("val pinkyCurled = pinkyTip[1] > pinkyPip[1]");
|
|
953
|
+
compiler.emit("if (indexExtended && middleCurled && ringCurled && pinkyCurled) {");
|
|
954
|
+
compiler.indentLevel++;
|
|
955
|
+
compiler.emit('android.util.Log.d("HoloScript", "Hand $handIndex: POINT detected")');
|
|
956
|
+
if (hasToSpatial) {
|
|
957
|
+
compiler.emit('onSpatialInput("point", handIndex, indexTip[0], indexTip[1], indexTip[2])');
|
|
958
|
+
}
|
|
959
|
+
compiler.indentLevel--;
|
|
960
|
+
compiler.emit("}");
|
|
961
|
+
compiler.emit("");
|
|
962
|
+
}
|
|
963
|
+
if (hasPalm) {
|
|
964
|
+
compiler.emit("// Palm gesture: all fingertips above MCPs (open hand)");
|
|
965
|
+
compiler.emit("val allExtended = indexTip[1] < indexMcp[1] && middleTip[1] < middleMcp[1] &&");
|
|
966
|
+
compiler.emit(" ringTip[1] < ringMcp[1] && pinkyTip[1] < pinkyMcp[1]");
|
|
967
|
+
compiler.emit("if (allExtended) {");
|
|
968
|
+
compiler.indentLevel++;
|
|
969
|
+
compiler.emit('android.util.Log.d("HoloScript", "Hand $handIndex: PALM detected")');
|
|
970
|
+
if (hasToSpatial) {
|
|
971
|
+
compiler.emit('onSpatialInput("palm", handIndex, indexTip[0], indexTip[1], indexTip[2])');
|
|
972
|
+
}
|
|
973
|
+
compiler.indentLevel--;
|
|
974
|
+
compiler.emit("}");
|
|
975
|
+
compiler.emit("");
|
|
976
|
+
}
|
|
977
|
+
if (hasFist) {
|
|
978
|
+
compiler.emit("// Fist gesture: all fingertips below PIPs (closed hand)");
|
|
979
|
+
compiler.emit("val allCurled = indexTip[1] > indexPip[1] && middleTip[1] > middlePip[1] &&");
|
|
980
|
+
compiler.emit(" ringTip[1] > ringPip[1] && pinkyTip[1] > pinkyPip[1]");
|
|
981
|
+
compiler.emit("if (allCurled) {");
|
|
982
|
+
compiler.indentLevel++;
|
|
983
|
+
compiler.emit('android.util.Log.d("HoloScript", "Hand $handIndex: FIST detected")');
|
|
984
|
+
if (hasToSpatial) {
|
|
985
|
+
compiler.emit('onSpatialInput("fist", handIndex, indexTip[0], indexTip[1], indexTip[2])');
|
|
986
|
+
}
|
|
987
|
+
compiler.indentLevel--;
|
|
988
|
+
compiler.emit("}");
|
|
989
|
+
compiler.emit("");
|
|
990
|
+
}
|
|
991
|
+
compiler.indentLevel--;
|
|
992
|
+
compiler.emit("}");
|
|
993
|
+
compiler.indentLevel--;
|
|
994
|
+
compiler.emit("}");
|
|
995
|
+
if (hasToSpatial) {
|
|
996
|
+
compiler.emit("");
|
|
997
|
+
compiler.emit(
|
|
998
|
+
"private fun onSpatialInput(gesture: String, handIndex: Int, x: Float, y: Float, z: Float) {"
|
|
999
|
+
);
|
|
1000
|
+
compiler.indentLevel++;
|
|
1001
|
+
compiler.emit("// Bridge to HoloScript spatial_input event system");
|
|
1002
|
+
compiler.emit("val event = mapOf(");
|
|
1003
|
+
compiler.indentLevel++;
|
|
1004
|
+
compiler.emit('"type" to "hand_gesture",');
|
|
1005
|
+
compiler.emit('"gesture" to gesture,');
|
|
1006
|
+
compiler.emit('"handIndex" to handIndex,');
|
|
1007
|
+
compiler.emit('"x" to x, "y" to y, "z" to z');
|
|
1008
|
+
compiler.indentLevel--;
|
|
1009
|
+
compiler.emit(")");
|
|
1010
|
+
compiler.emit('android.util.Log.d("HoloScript", "SpatialInput: $event")');
|
|
1011
|
+
compiler.indentLevel--;
|
|
1012
|
+
compiler.emit("}");
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
function hasAuthoringTraits(composition) {
|
|
1016
|
+
const authoringNames = chunkDTGBOSEM_cjs.SPATIAL_AUTHORING_TRAITS;
|
|
1017
|
+
for (const obj of composition.objects || []) {
|
|
1018
|
+
for (const trait of obj.traits || []) {
|
|
1019
|
+
const name = typeof trait === "string" ? trait : trait.name;
|
|
1020
|
+
if (authoringNames.includes(name)) return true;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
return false;
|
|
1024
|
+
}
|
|
1025
|
+
function emitAuthoringSetup(compiler, composition) {
|
|
1026
|
+
compiler.lines = [];
|
|
1027
|
+
compiler.indentLevel = 0;
|
|
1028
|
+
compiler.emit("// === Spatial Authoring Setup (M.010.08) ===");
|
|
1029
|
+
compiler.emit("");
|
|
1030
|
+
const hasGyro = compiler.compositionHasTrait(composition, "author_gyro_place");
|
|
1031
|
+
const hasPinch = compiler.compositionHasTrait(composition, "author_pinch_scale");
|
|
1032
|
+
const hasSwipe = compiler.compositionHasTrait(composition, "author_swipe_browse");
|
|
1033
|
+
const hasVoice = compiler.compositionHasTrait(composition, "author_voice_cmd");
|
|
1034
|
+
const hasShake = compiler.compositionHasTrait(composition, "author_shake_undo");
|
|
1035
|
+
if (hasGyro) {
|
|
1036
|
+
compiler.emit(`private val gyroFilterAlpha = ${chunkDTGBOSEM_cjs.SPATIAL_AUTHORING_DEFAULTS.gyroFilterAlpha}f`);
|
|
1037
|
+
compiler.emit("private var sensorManager: android.hardware.SensorManager? = null");
|
|
1038
|
+
compiler.emit("private var rotationSensor: android.hardware.Sensor? = null");
|
|
1039
|
+
}
|
|
1040
|
+
if (hasPinch) {
|
|
1041
|
+
compiler.emit(`private val pinchScaleMin = ${chunkDTGBOSEM_cjs.SPATIAL_AUTHORING_DEFAULTS.pinchScaleMin}f`);
|
|
1042
|
+
compiler.emit(`private val pinchScaleMax = ${chunkDTGBOSEM_cjs.SPATIAL_AUTHORING_DEFAULTS.pinchScaleMax}f`);
|
|
1043
|
+
compiler.emit("private var scaleGestureDetector: android.view.ScaleGestureDetector? = null");
|
|
1044
|
+
}
|
|
1045
|
+
if (hasSwipe) {
|
|
1046
|
+
compiler.emit("private var gestureDetector: android.view.GestureDetector? = null");
|
|
1047
|
+
}
|
|
1048
|
+
if (hasVoice) {
|
|
1049
|
+
compiler.emit(`private val speechLocale = "${chunkDTGBOSEM_cjs.SPATIAL_AUTHORING_DEFAULTS.speechLocale}"`);
|
|
1050
|
+
compiler.emit("private var speechRecognizer: android.speech.SpeechRecognizer? = null");
|
|
1051
|
+
}
|
|
1052
|
+
if (hasShake) {
|
|
1053
|
+
compiler.emit(`private val shakeThreshold = ${chunkDTGBOSEM_cjs.SPATIAL_AUTHORING_DEFAULTS.shakeThreshold}f`);
|
|
1054
|
+
compiler.emit(`private val undoStackDepth = ${chunkDTGBOSEM_cjs.SPATIAL_AUTHORING_DEFAULTS.undoStackDepth}`);
|
|
1055
|
+
compiler.emit("private val undoStack = ArrayDeque<() -> Unit>()");
|
|
1056
|
+
}
|
|
1057
|
+
return compiler.lines.join("\n");
|
|
1058
|
+
}
|
|
1059
|
+
function emitAuthoringInlineSetup(compiler, composition) {
|
|
1060
|
+
compiler.emit("");
|
|
1061
|
+
compiler.emit("// --- Spatial Authoring inline setup (M.010.08) ---");
|
|
1062
|
+
if (compiler.compositionHasTrait(composition, "author_gyro_place")) {
|
|
1063
|
+
compiler.emit("private fun setupGyroPlacement() {");
|
|
1064
|
+
compiler.indentLevel++;
|
|
1065
|
+
compiler.emit(
|
|
1066
|
+
"sensorManager = getSystemService(SENSOR_SERVICE) as android.hardware.SensorManager"
|
|
1067
|
+
);
|
|
1068
|
+
compiler.emit(
|
|
1069
|
+
"rotationSensor = sensorManager?.getDefaultSensor(android.hardware.Sensor.TYPE_ROTATION_VECTOR)"
|
|
1070
|
+
);
|
|
1071
|
+
compiler.indentLevel--;
|
|
1072
|
+
compiler.emit("}");
|
|
1073
|
+
}
|
|
1074
|
+
if (compiler.compositionHasTrait(composition, "author_pinch_scale")) {
|
|
1075
|
+
compiler.emit("private fun setupPinchScale() {");
|
|
1076
|
+
compiler.indentLevel++;
|
|
1077
|
+
compiler.emit(
|
|
1078
|
+
"scaleGestureDetector = android.view.ScaleGestureDetector(this, object : android.view.ScaleGestureDetector.SimpleOnScaleGestureListener() {"
|
|
1079
|
+
);
|
|
1080
|
+
compiler.indentLevel++;
|
|
1081
|
+
compiler.emit("override fun onScale(detector: android.view.ScaleGestureDetector): Boolean {");
|
|
1082
|
+
compiler.indentLevel++;
|
|
1083
|
+
compiler.emit("val factor = detector.scaleFactor.coerceIn(pinchScaleMin, pinchScaleMax)");
|
|
1084
|
+
compiler.emit("selectedNode?.localScale = selectedNode?.localScale?.let { it.scaled(factor) }");
|
|
1085
|
+
compiler.emit("return true");
|
|
1086
|
+
compiler.indentLevel--;
|
|
1087
|
+
compiler.emit("}");
|
|
1088
|
+
compiler.indentLevel--;
|
|
1089
|
+
compiler.emit("})");
|
|
1090
|
+
compiler.indentLevel--;
|
|
1091
|
+
compiler.emit("}");
|
|
1092
|
+
}
|
|
1093
|
+
if (compiler.compositionHasTrait(composition, "author_shake_undo")) {
|
|
1094
|
+
compiler.emit("private fun setupShakeUndo() {");
|
|
1095
|
+
compiler.indentLevel++;
|
|
1096
|
+
compiler.emit(
|
|
1097
|
+
"val accelerometer = sensorManager?.getDefaultSensor(android.hardware.Sensor.TYPE_ACCELEROMETER)"
|
|
1098
|
+
);
|
|
1099
|
+
compiler.emit("// Shake detection triggers undoStack.removeLastOrNull()?.invoke()");
|
|
1100
|
+
compiler.indentLevel--;
|
|
1101
|
+
compiler.emit("}");
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
function hasNPUSceneTraits(composition) {
|
|
1105
|
+
const npuNames = chunk6EX3I5P4_cjs.NPU_SCENE_TRAITS;
|
|
1106
|
+
for (const obj of composition.objects || []) {
|
|
1107
|
+
for (const trait of obj.traits || []) {
|
|
1108
|
+
const name = typeof trait === "string" ? trait : trait.name;
|
|
1109
|
+
if (npuNames.includes(name)) return true;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
return false;
|
|
1113
|
+
}
|
|
1114
|
+
function emitNPUSceneSetup(compiler, composition) {
|
|
1115
|
+
compiler.lines = [];
|
|
1116
|
+
compiler.indentLevel = 0;
|
|
1117
|
+
const pkg = compiler.options.packageName;
|
|
1118
|
+
const cls = compiler.options.className;
|
|
1119
|
+
const defaults = chunk6EX3I5P4_cjs.NPU_SCENE_DEFAULTS;
|
|
1120
|
+
const usedTraits = /* @__PURE__ */ new Set();
|
|
1121
|
+
for (const obj of composition.objects || []) {
|
|
1122
|
+
for (const trait of obj.traits || []) {
|
|
1123
|
+
const name = typeof trait === "string" ? trait : trait.name;
|
|
1124
|
+
if (chunk6EX3I5P4_cjs.NPU_SCENE_TRAITS.includes(name)) {
|
|
1125
|
+
usedTraits.add(name);
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
compiler.emit("// Auto-generated by HoloScript AndroidCompiler \u2014 NPU Scene Understanding");
|
|
1130
|
+
compiler.emit(
|
|
1131
|
+
`// Source: composition "${compiler.escapeStringValue(composition.name, "Kotlin")}"`
|
|
1132
|
+
);
|
|
1133
|
+
compiler.emit("// Requires ML Kit, CameraX, NNAPI delegate");
|
|
1134
|
+
compiler.emit("// Do not edit manually \u2014 regenerate from .holo source");
|
|
1135
|
+
compiler.emit("");
|
|
1136
|
+
compiler.emit(`package ${pkg}`);
|
|
1137
|
+
compiler.emit("");
|
|
1138
|
+
compiler.emit("import android.graphics.Bitmap");
|
|
1139
|
+
compiler.emit("import android.graphics.Rect");
|
|
1140
|
+
compiler.emit("import android.util.Log");
|
|
1141
|
+
compiler.emit("import android.util.Size");
|
|
1142
|
+
compiler.emit("import androidx.camera.core.ImageAnalysis");
|
|
1143
|
+
compiler.emit("import androidx.camera.core.ImageProxy");
|
|
1144
|
+
compiler.emit("import androidx.lifecycle.ViewModel");
|
|
1145
|
+
compiler.emit("import androidx.lifecycle.viewModelScope");
|
|
1146
|
+
compiler.emit("import kotlinx.coroutines.flow.MutableStateFlow");
|
|
1147
|
+
compiler.emit("import kotlinx.coroutines.flow.StateFlow");
|
|
1148
|
+
compiler.emit("import kotlinx.coroutines.launch");
|
|
1149
|
+
if (usedTraits.has("npu_detect")) {
|
|
1150
|
+
compiler.emit("import com.google.mlkit.vision.common.InputImage");
|
|
1151
|
+
compiler.emit("import com.google.mlkit.vision.objects.ObjectDetection");
|
|
1152
|
+
compiler.emit("import com.google.mlkit.vision.objects.ObjectDetector");
|
|
1153
|
+
compiler.emit("import com.google.mlkit.vision.objects.defaults.ObjectDetectorOptions");
|
|
1154
|
+
}
|
|
1155
|
+
if (usedTraits.has("npu_classify")) {
|
|
1156
|
+
compiler.emit("import com.google.mlkit.vision.common.InputImage");
|
|
1157
|
+
compiler.emit("import com.google.mlkit.vision.label.ImageLabeling");
|
|
1158
|
+
compiler.emit("import com.google.mlkit.vision.label.ImageLabeler");
|
|
1159
|
+
compiler.emit("import com.google.mlkit.vision.label.defaults.ImageLabelerOptions");
|
|
1160
|
+
}
|
|
1161
|
+
if (usedTraits.has("npu_segment")) {
|
|
1162
|
+
compiler.emit("import com.google.mlkit.vision.segmentation.Segmentation");
|
|
1163
|
+
compiler.emit("import com.google.mlkit.vision.segmentation.selfie.SelfieSegmenterOptions");
|
|
1164
|
+
}
|
|
1165
|
+
if (usedTraits.has("npu_model_custom")) {
|
|
1166
|
+
compiler.emit("import org.tensorflow.lite.Interpreter");
|
|
1167
|
+
compiler.emit("import org.tensorflow.lite.nnapi.NnApiDelegate");
|
|
1168
|
+
compiler.emit("import com.google.mlkit.common.model.LocalModel");
|
|
1169
|
+
}
|
|
1170
|
+
compiler.emit("");
|
|
1171
|
+
compiler.emit("/** A single detection result from NPU scene understanding. */");
|
|
1172
|
+
compiler.emit("data class NPUDetection(");
|
|
1173
|
+
compiler.indentLevel++;
|
|
1174
|
+
compiler.emit("val label: String,");
|
|
1175
|
+
compiler.emit("val confidence: Float,");
|
|
1176
|
+
compiler.emit("val boundingBox: Rect? = null,");
|
|
1177
|
+
compiler.emit("val position: FloatArray? = null,");
|
|
1178
|
+
compiler.emit("val segmentationMask: Bitmap? = null,");
|
|
1179
|
+
compiler.emit("val depthValue: Float? = null");
|
|
1180
|
+
compiler.indentLevel--;
|
|
1181
|
+
compiler.emit(")");
|
|
1182
|
+
compiler.emit("");
|
|
1183
|
+
compiler.emit(`class ${cls}NPUSceneManager : ViewModel() {`);
|
|
1184
|
+
compiler.indentLevel++;
|
|
1185
|
+
compiler.emit("");
|
|
1186
|
+
compiler.emit("private val _detections = MutableStateFlow<List<NPUDetection>>(emptyList())");
|
|
1187
|
+
compiler.emit("val detections: StateFlow<List<NPUDetection>> = _detections");
|
|
1188
|
+
compiler.emit("");
|
|
1189
|
+
compiler.emit("private val _isProcessing = MutableStateFlow(false)");
|
|
1190
|
+
compiler.emit("val isProcessing: StateFlow<Boolean> = _isProcessing");
|
|
1191
|
+
compiler.emit("");
|
|
1192
|
+
compiler.emit("private var inferenceCount: Int = 0");
|
|
1193
|
+
compiler.emit(`private val confidenceThreshold: Float = ${defaults.confidenceThreshold}f`);
|
|
1194
|
+
compiler.emit(`private val maxDetections: Int = ${defaults.maxDetections}`);
|
|
1195
|
+
compiler.emit(`private val targetFPS: Int = ${defaults.targetFPS}`);
|
|
1196
|
+
compiler.emit(`private val entityScale: Float = ${defaults.entityScale}f`);
|
|
1197
|
+
compiler.emit(`private val labelOffsetY: Float = ${defaults.labelOffsetY}f`);
|
|
1198
|
+
compiler.emit("private var frameCounter: Int = 0");
|
|
1199
|
+
compiler.emit("private var lastProcessTimeMs: Long = 0");
|
|
1200
|
+
compiler.emit("");
|
|
1201
|
+
if (usedTraits.has("npu_detect")) {
|
|
1202
|
+
compiler.emit("// MARK: Object Detection (npu_detect)");
|
|
1203
|
+
compiler.emit("");
|
|
1204
|
+
compiler.emit("private val objectDetector: ObjectDetector by lazy {");
|
|
1205
|
+
compiler.indentLevel++;
|
|
1206
|
+
compiler.emit("val options = ObjectDetectorOptions.Builder()");
|
|
1207
|
+
compiler.emit(" .setDetectorMode(ObjectDetectorOptions.STREAM_MODE)");
|
|
1208
|
+
compiler.emit(" .enableMultipleObjects()");
|
|
1209
|
+
compiler.emit(" .enableClassification()");
|
|
1210
|
+
compiler.emit(" .build()");
|
|
1211
|
+
compiler.emit("ObjectDetection.getClient(options)");
|
|
1212
|
+
compiler.indentLevel--;
|
|
1213
|
+
compiler.emit("}");
|
|
1214
|
+
compiler.emit("");
|
|
1215
|
+
compiler.emit("fun detectObjects(image: InputImage) {");
|
|
1216
|
+
compiler.indentLevel++;
|
|
1217
|
+
compiler.emit("objectDetector.process(image)");
|
|
1218
|
+
compiler.emit(" .addOnSuccessListener { objects ->");
|
|
1219
|
+
compiler.indentLevel++;
|
|
1220
|
+
compiler.emit("val results = objects");
|
|
1221
|
+
compiler.emit(
|
|
1222
|
+
" .filter { it.labels.isNotEmpty() && (it.labels.firstOrNull()?.confidence ?: 0f) >= confidenceThreshold }"
|
|
1223
|
+
);
|
|
1224
|
+
compiler.emit(" .take(maxDetections)");
|
|
1225
|
+
compiler.emit(" .map { obj ->");
|
|
1226
|
+
compiler.indentLevel++;
|
|
1227
|
+
compiler.emit("NPUDetection(");
|
|
1228
|
+
compiler.emit(' label = obj.labels.firstOrNull()?.text ?: "unknown",');
|
|
1229
|
+
compiler.emit(" confidence = obj.labels.firstOrNull()?.confidence ?: 0f,");
|
|
1230
|
+
compiler.emit(" boundingBox = obj.boundingBox");
|
|
1231
|
+
compiler.emit(")");
|
|
1232
|
+
compiler.indentLevel--;
|
|
1233
|
+
compiler.emit(" }");
|
|
1234
|
+
compiler.emit("_detections.value = results");
|
|
1235
|
+
compiler.emit("inferenceCount++");
|
|
1236
|
+
compiler.emit('Log.d("HoloScript", "Detected ${results.size} objects")');
|
|
1237
|
+
compiler.indentLevel--;
|
|
1238
|
+
compiler.emit(" }");
|
|
1239
|
+
compiler.emit(" .addOnFailureListener { e ->");
|
|
1240
|
+
compiler.indentLevel++;
|
|
1241
|
+
compiler.emit('Log.e("HoloScript", "Object detection failed", e)');
|
|
1242
|
+
compiler.indentLevel--;
|
|
1243
|
+
compiler.emit(" }");
|
|
1244
|
+
compiler.indentLevel--;
|
|
1245
|
+
compiler.emit("}");
|
|
1246
|
+
compiler.emit("");
|
|
1247
|
+
}
|
|
1248
|
+
if (usedTraits.has("npu_classify")) {
|
|
1249
|
+
compiler.emit("// MARK: Classification (npu_classify)");
|
|
1250
|
+
compiler.emit("");
|
|
1251
|
+
compiler.emit("private val imageLabeler: ImageLabeler by lazy {");
|
|
1252
|
+
compiler.indentLevel++;
|
|
1253
|
+
compiler.emit("val options = ImageLabelerOptions.Builder()");
|
|
1254
|
+
compiler.emit(` .setConfidenceThreshold(${defaults.confidenceThreshold}f)`);
|
|
1255
|
+
compiler.emit(" .build()");
|
|
1256
|
+
compiler.emit("ImageLabeling.getClient(options)");
|
|
1257
|
+
compiler.indentLevel--;
|
|
1258
|
+
compiler.emit("}");
|
|
1259
|
+
compiler.emit("");
|
|
1260
|
+
compiler.emit("fun classifyImage(image: InputImage) {");
|
|
1261
|
+
compiler.indentLevel++;
|
|
1262
|
+
compiler.emit("imageLabeler.process(image)");
|
|
1263
|
+
compiler.emit(" .addOnSuccessListener { labels ->");
|
|
1264
|
+
compiler.indentLevel++;
|
|
1265
|
+
compiler.emit("val results = labels");
|
|
1266
|
+
compiler.emit(" .filter { it.confidence >= confidenceThreshold }");
|
|
1267
|
+
compiler.emit(" .take(maxDetections)");
|
|
1268
|
+
compiler.emit(" .map { label ->");
|
|
1269
|
+
compiler.indentLevel++;
|
|
1270
|
+
compiler.emit("NPUDetection(label = label.text, confidence = label.confidence)");
|
|
1271
|
+
compiler.indentLevel--;
|
|
1272
|
+
compiler.emit(" }");
|
|
1273
|
+
compiler.emit("_detections.value = results");
|
|
1274
|
+
compiler.emit("inferenceCount++");
|
|
1275
|
+
compiler.indentLevel--;
|
|
1276
|
+
compiler.emit(" }");
|
|
1277
|
+
compiler.emit(" .addOnFailureListener { e ->");
|
|
1278
|
+
compiler.indentLevel++;
|
|
1279
|
+
compiler.emit('Log.e("HoloScript", "Image classification failed", e)');
|
|
1280
|
+
compiler.indentLevel--;
|
|
1281
|
+
compiler.emit(" }");
|
|
1282
|
+
compiler.indentLevel--;
|
|
1283
|
+
compiler.emit("}");
|
|
1284
|
+
compiler.emit("");
|
|
1285
|
+
}
|
|
1286
|
+
if (usedTraits.has("npu_segment")) {
|
|
1287
|
+
compiler.emit("// MARK: Segmentation (npu_segment)");
|
|
1288
|
+
compiler.emit("");
|
|
1289
|
+
compiler.emit("private val segmenter by lazy {");
|
|
1290
|
+
compiler.indentLevel++;
|
|
1291
|
+
compiler.emit("val options = SelfieSegmenterOptions.Builder()");
|
|
1292
|
+
compiler.emit(" .setDetectorMode(SelfieSegmenterOptions.STREAM_MODE)");
|
|
1293
|
+
compiler.emit(" .enableRawSizeMask()");
|
|
1294
|
+
compiler.emit(" .build()");
|
|
1295
|
+
compiler.emit("Segmentation.getClient(options)");
|
|
1296
|
+
compiler.indentLevel--;
|
|
1297
|
+
compiler.emit("}");
|
|
1298
|
+
compiler.emit("");
|
|
1299
|
+
compiler.emit("fun segmentScene(image: InputImage) {");
|
|
1300
|
+
compiler.indentLevel++;
|
|
1301
|
+
compiler.emit("segmenter.process(image)");
|
|
1302
|
+
compiler.emit(" .addOnSuccessListener { mask ->");
|
|
1303
|
+
compiler.indentLevel++;
|
|
1304
|
+
compiler.emit("val buffer = mask.buffer");
|
|
1305
|
+
compiler.emit("val width = mask.width");
|
|
1306
|
+
compiler.emit("val height = mask.height");
|
|
1307
|
+
compiler.emit("val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)");
|
|
1308
|
+
compiler.emit("_detections.value = listOf(NPUDetection(");
|
|
1309
|
+
compiler.emit(' label = "person_segmentation", confidence = 1.0f,');
|
|
1310
|
+
compiler.emit(" segmentationMask = bitmap");
|
|
1311
|
+
compiler.emit("))");
|
|
1312
|
+
compiler.emit("inferenceCount++");
|
|
1313
|
+
compiler.indentLevel--;
|
|
1314
|
+
compiler.emit(" }");
|
|
1315
|
+
compiler.emit(" .addOnFailureListener { e ->");
|
|
1316
|
+
compiler.indentLevel++;
|
|
1317
|
+
compiler.emit('Log.e("HoloScript", "Segmentation failed", e)');
|
|
1318
|
+
compiler.indentLevel--;
|
|
1319
|
+
compiler.emit(" }");
|
|
1320
|
+
compiler.indentLevel--;
|
|
1321
|
+
compiler.emit("}");
|
|
1322
|
+
compiler.emit("");
|
|
1323
|
+
}
|
|
1324
|
+
if (usedTraits.has("npu_entity_pipe")) {
|
|
1325
|
+
compiler.emit("// MARK: Entity Pipeline (npu_entity_pipe)");
|
|
1326
|
+
compiler.emit("");
|
|
1327
|
+
compiler.emit(
|
|
1328
|
+
"fun mapDetectionsToEntities(detections: List<NPUDetection>, arFragment: com.google.ar.sceneform.ux.ArFragment) {"
|
|
1329
|
+
);
|
|
1330
|
+
compiler.indentLevel++;
|
|
1331
|
+
compiler.emit("val scene = arFragment.arSceneView.scene");
|
|
1332
|
+
compiler.emit("for (detection in detections) {");
|
|
1333
|
+
compiler.indentLevel++;
|
|
1334
|
+
compiler.emit("val bbox = detection.boundingBox ?: continue");
|
|
1335
|
+
compiler.emit("val centerX = (bbox.left + bbox.right) / 2f");
|
|
1336
|
+
compiler.emit("val centerY = (bbox.top + bbox.bottom) / 2f");
|
|
1337
|
+
compiler.emit("val frame = arFragment.arSceneView.arFrame ?: continue");
|
|
1338
|
+
compiler.emit("val hits = frame.hitTest(centerX, centerY)");
|
|
1339
|
+
compiler.emit("val hit = hits.firstOrNull() ?: continue");
|
|
1340
|
+
compiler.emit("val anchor = hit.createAnchor()");
|
|
1341
|
+
compiler.emit("val anchorNode = com.google.ar.sceneform.AnchorNode(anchor)");
|
|
1342
|
+
compiler.emit("anchorNode.setParent(scene)");
|
|
1343
|
+
compiler.emit("com.google.ar.sceneform.rendering.MaterialFactory");
|
|
1344
|
+
compiler.emit(" .makeOpaqueWithColor(arFragment.requireContext(),");
|
|
1345
|
+
compiler.emit(" com.google.ar.sceneform.rendering.Color(0.2f, 0.6f, 1.0f, 0.6f))");
|
|
1346
|
+
compiler.emit(" .thenAccept { material ->");
|
|
1347
|
+
compiler.indentLevel++;
|
|
1348
|
+
compiler.emit(
|
|
1349
|
+
`val renderable = com.google.ar.sceneform.rendering.ShapeFactory.makeSphere(${defaults.entityScale}f, com.google.ar.sceneform.math.Vector3.zero(), material)`
|
|
1350
|
+
);
|
|
1351
|
+
compiler.emit(
|
|
1352
|
+
"val node = com.google.ar.sceneform.ux.TransformableNode(arFragment.transformationSystem)"
|
|
1353
|
+
);
|
|
1354
|
+
compiler.emit("node.setParent(anchorNode)");
|
|
1355
|
+
compiler.emit("node.renderable = renderable");
|
|
1356
|
+
compiler.emit("node.name = detection.label");
|
|
1357
|
+
compiler.emit('Log.d("HoloScript", "Entity created: ${detection.label}")');
|
|
1358
|
+
compiler.indentLevel--;
|
|
1359
|
+
compiler.emit(" }");
|
|
1360
|
+
compiler.indentLevel--;
|
|
1361
|
+
compiler.emit("}");
|
|
1362
|
+
compiler.indentLevel--;
|
|
1363
|
+
compiler.emit("}");
|
|
1364
|
+
compiler.emit("");
|
|
1365
|
+
}
|
|
1366
|
+
if (usedTraits.has("npu_realtime")) {
|
|
1367
|
+
compiler.emit("// MARK: Realtime Processing (npu_realtime)");
|
|
1368
|
+
compiler.emit("");
|
|
1369
|
+
compiler.emit("fun createImageAnalyzer(): ImageAnalysis.Analyzer {");
|
|
1370
|
+
compiler.indentLevel++;
|
|
1371
|
+
compiler.emit("return ImageAnalysis.Analyzer { imageProxy ->");
|
|
1372
|
+
compiler.indentLevel++;
|
|
1373
|
+
compiler.emit("frameCounter++");
|
|
1374
|
+
compiler.emit("val now = System.currentTimeMillis()");
|
|
1375
|
+
compiler.emit("val intervalMs = 1000L / targetFPS");
|
|
1376
|
+
compiler.emit("if (now - lastProcessTimeMs < intervalMs) {");
|
|
1377
|
+
compiler.indentLevel++;
|
|
1378
|
+
compiler.emit("imageProxy.close()");
|
|
1379
|
+
compiler.emit("return@Analyzer");
|
|
1380
|
+
compiler.indentLevel--;
|
|
1381
|
+
compiler.emit("}");
|
|
1382
|
+
compiler.emit("lastProcessTimeMs = now");
|
|
1383
|
+
compiler.emit("_isProcessing.value = true");
|
|
1384
|
+
compiler.emit("");
|
|
1385
|
+
compiler.emit("@androidx.camera.core.ExperimentalGetImage");
|
|
1386
|
+
compiler.emit("val mediaImage = imageProxy.image");
|
|
1387
|
+
compiler.emit("if (mediaImage != null) {");
|
|
1388
|
+
compiler.indentLevel++;
|
|
1389
|
+
compiler.emit(
|
|
1390
|
+
"val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)"
|
|
1391
|
+
);
|
|
1392
|
+
if (usedTraits.has("npu_classify")) {
|
|
1393
|
+
compiler.emit("classifyImage(image)");
|
|
1394
|
+
}
|
|
1395
|
+
if (usedTraits.has("npu_detect")) {
|
|
1396
|
+
compiler.emit("detectObjects(image)");
|
|
1397
|
+
}
|
|
1398
|
+
if (usedTraits.has("npu_segment")) {
|
|
1399
|
+
compiler.emit("segmentScene(image)");
|
|
1400
|
+
}
|
|
1401
|
+
compiler.indentLevel--;
|
|
1402
|
+
compiler.emit("}");
|
|
1403
|
+
compiler.emit("_isProcessing.value = false");
|
|
1404
|
+
compiler.emit("imageProxy.close()");
|
|
1405
|
+
compiler.indentLevel--;
|
|
1406
|
+
compiler.emit("}");
|
|
1407
|
+
compiler.indentLevel--;
|
|
1408
|
+
compiler.emit("}");
|
|
1409
|
+
compiler.emit("");
|
|
1410
|
+
}
|
|
1411
|
+
if (usedTraits.has("npu_model_custom")) {
|
|
1412
|
+
compiler.emit("// MARK: Custom Model (npu_model_custom)");
|
|
1413
|
+
compiler.emit("");
|
|
1414
|
+
compiler.emit("private var customInterpreter: Interpreter? = null");
|
|
1415
|
+
compiler.emit("");
|
|
1416
|
+
compiler.emit("fun loadCustomModel(context: android.content.Context, assetPath: String) {");
|
|
1417
|
+
compiler.indentLevel++;
|
|
1418
|
+
compiler.emit("val localModel = LocalModel.Builder()");
|
|
1419
|
+
compiler.emit(" .setAssetFilePath(assetPath)");
|
|
1420
|
+
compiler.emit(" .build()");
|
|
1421
|
+
compiler.emit("");
|
|
1422
|
+
compiler.emit("// NNAPI delegate for hardware acceleration");
|
|
1423
|
+
compiler.emit("val nnApiDelegate = NnApiDelegate()");
|
|
1424
|
+
compiler.emit("val options = Interpreter.Options().addDelegate(nnApiDelegate)");
|
|
1425
|
+
compiler.emit("");
|
|
1426
|
+
compiler.emit("val modelFile = context.assets.open(assetPath)");
|
|
1427
|
+
compiler.emit("val buffer = modelFile.readBytes()");
|
|
1428
|
+
compiler.emit("val byteBuffer = java.nio.ByteBuffer.allocateDirect(buffer.size)");
|
|
1429
|
+
compiler.emit("byteBuffer.put(buffer)");
|
|
1430
|
+
compiler.emit("customInterpreter = Interpreter(byteBuffer, options)");
|
|
1431
|
+
compiler.emit('Log.d("HoloScript", "Custom model loaded with NNAPI: $assetPath")');
|
|
1432
|
+
compiler.indentLevel--;
|
|
1433
|
+
compiler.emit("}");
|
|
1434
|
+
compiler.emit("");
|
|
1435
|
+
}
|
|
1436
|
+
if (usedTraits.has("npu_label_overlay")) {
|
|
1437
|
+
compiler.emit("// MARK: Label Overlay (npu_label_overlay)");
|
|
1438
|
+
compiler.emit("");
|
|
1439
|
+
compiler.emit("fun createLabelOverlay(");
|
|
1440
|
+
compiler.emit(" detection: NPUDetection,");
|
|
1441
|
+
compiler.emit(" arFragment: com.google.ar.sceneform.ux.ArFragment,");
|
|
1442
|
+
compiler.emit(" anchorNode: com.google.ar.sceneform.AnchorNode");
|
|
1443
|
+
compiler.emit(") {");
|
|
1444
|
+
compiler.indentLevel++;
|
|
1445
|
+
compiler.emit("com.google.ar.sceneform.rendering.ViewRenderable.builder()");
|
|
1446
|
+
compiler.emit(
|
|
1447
|
+
" .setView(arFragment.requireContext(), android.widget.TextView(arFragment.requireContext()).apply {"
|
|
1448
|
+
);
|
|
1449
|
+
compiler.indentLevel++;
|
|
1450
|
+
compiler.emit('text = "${detection.label} (${(detection.confidence * 100).toInt()}%)"');
|
|
1451
|
+
compiler.emit("setTextColor(android.graphics.Color.WHITE)");
|
|
1452
|
+
compiler.emit("setBackgroundColor(android.graphics.Color.argb(180, 0, 0, 0))");
|
|
1453
|
+
compiler.emit("setPadding(12, 6, 12, 6)");
|
|
1454
|
+
compiler.emit("textSize = 12f");
|
|
1455
|
+
compiler.indentLevel--;
|
|
1456
|
+
compiler.emit(" })");
|
|
1457
|
+
compiler.emit(" .build()");
|
|
1458
|
+
compiler.emit(" .thenAccept { renderable ->");
|
|
1459
|
+
compiler.indentLevel++;
|
|
1460
|
+
compiler.emit("val labelNode = com.google.ar.sceneform.Node()");
|
|
1461
|
+
compiler.emit("labelNode.setParent(anchorNode)");
|
|
1462
|
+
compiler.emit(
|
|
1463
|
+
`labelNode.localPosition = com.google.ar.sceneform.math.Vector3(0f, ${defaults.labelOffsetY}f, 0f)`
|
|
1464
|
+
);
|
|
1465
|
+
compiler.emit("labelNode.renderable = renderable");
|
|
1466
|
+
compiler.emit('Log.d("HoloScript", "Label overlay: ${detection.label}")');
|
|
1467
|
+
compiler.indentLevel--;
|
|
1468
|
+
compiler.emit(" }");
|
|
1469
|
+
compiler.indentLevel--;
|
|
1470
|
+
compiler.emit("}");
|
|
1471
|
+
compiler.emit("");
|
|
1472
|
+
}
|
|
1473
|
+
compiler.indentLevel--;
|
|
1474
|
+
compiler.emit("}");
|
|
1475
|
+
return compiler.lines.join("\n");
|
|
1476
|
+
}
|
|
1477
|
+
var init_AndroidFeatureGenerators = chunkHCCQLAA4_cjs.__esm({
|
|
1478
|
+
"src/compiler/AndroidFeatureGenerators.ts"() {
|
|
1479
|
+
chunkDTGBOSEM_cjs.init_geospatial();
|
|
1480
|
+
chunkDTGBOSEM_cjs.init_depth_scanner();
|
|
1481
|
+
chunk6EX3I5P4_cjs.init_portal_ar();
|
|
1482
|
+
chunk6EX3I5P4_cjs.init_camera_hand_tracking();
|
|
1483
|
+
chunk6EX3I5P4_cjs.init_npu_scene();
|
|
1484
|
+
chunkDTGBOSEM_cjs.init_spatial_authoring();
|
|
1485
|
+
}
|
|
1486
|
+
});
|
|
1487
|
+
|
|
1488
|
+
// src/compiler/AndroidARGenerators.ts
|
|
1489
|
+
function toKotlinFloatLiteral(value, fallback) {
|
|
1490
|
+
const numeric = typeof value === "number" ? value : Number(value);
|
|
1491
|
+
const resolved = Number.isFinite(numeric) ? numeric : fallback;
|
|
1492
|
+
return `${resolved}f`;
|
|
1493
|
+
}
|
|
1494
|
+
function toKotlinVector3(value, fallback) {
|
|
1495
|
+
const components = Array.isArray(value) && value.length >= 3 ? [value[0], value[1], value[2]] : typeof value === "number" ? [value, value, value] : fallback;
|
|
1496
|
+
return `Vector3(${components.map((component, index) => toKotlinFloatLiteral(component, fallback[index])).join(", ")})`;
|
|
1497
|
+
}
|
|
1498
|
+
function generateActivityFile(compiler, composition) {
|
|
1499
|
+
compiler.lines = [];
|
|
1500
|
+
compiler.indentLevel = 0;
|
|
1501
|
+
const pkg = compiler.options.packageName;
|
|
1502
|
+
const cls = compiler.options.className;
|
|
1503
|
+
compiler.emit("// Auto-generated by HoloScript AndroidCompiler");
|
|
1504
|
+
compiler.emit(
|
|
1505
|
+
`// Source: composition "${compiler.escapeStringValue(composition.name, "Kotlin")}"`
|
|
1506
|
+
);
|
|
1507
|
+
compiler.emit("// Do not edit manually \u2014 regenerate from .holo source");
|
|
1508
|
+
compiler.emit("");
|
|
1509
|
+
compiler.emit(`package ${pkg}`);
|
|
1510
|
+
compiler.emit("");
|
|
1511
|
+
compiler.emit("import android.os.Bundle");
|
|
1512
|
+
compiler.emit("import android.view.MotionEvent");
|
|
1513
|
+
compiler.emit("import android.view.View");
|
|
1514
|
+
compiler.emit("import android.widget.Toast");
|
|
1515
|
+
compiler.emit("import androidx.appcompat.app.AppCompatActivity");
|
|
1516
|
+
compiler.emit("import androidx.lifecycle.ViewModelProvider");
|
|
1517
|
+
compiler.emit("import com.google.ar.core.*");
|
|
1518
|
+
compiler.emit("import com.google.ar.core.exceptions.*");
|
|
1519
|
+
compiler.emit("import com.google.ar.sceneform.*");
|
|
1520
|
+
compiler.emit("import com.google.ar.sceneform.math.Vector3");
|
|
1521
|
+
compiler.emit("import com.google.ar.sceneform.rendering.*");
|
|
1522
|
+
compiler.emit("import com.google.ar.sceneform.ux.*");
|
|
1523
|
+
compiler.emit("import java.util.concurrent.CompletableFuture");
|
|
1524
|
+
compiler.emit("");
|
|
1525
|
+
compiler.emit(`class ${cls}Activity : AppCompatActivity() {`);
|
|
1526
|
+
compiler.indentLevel++;
|
|
1527
|
+
compiler.emit("private lateinit var arFragment: ArFragment");
|
|
1528
|
+
compiler.emit("private lateinit var sceneState: SceneState");
|
|
1529
|
+
compiler.emit("private val placedNodes = mutableMapOf<String, TransformableNode>()");
|
|
1530
|
+
compiler.emit("");
|
|
1531
|
+
compiler.emit("override fun onCreate(savedInstanceState: Bundle?) {");
|
|
1532
|
+
compiler.indentLevel++;
|
|
1533
|
+
compiler.emit("super.onCreate(savedInstanceState)");
|
|
1534
|
+
compiler.emit(`setContentView(R.layout.activity_ar_scene)`);
|
|
1535
|
+
compiler.emit("");
|
|
1536
|
+
compiler.emit("sceneState = ViewModelProvider(this)[SceneState::class.java]");
|
|
1537
|
+
compiler.emit(
|
|
1538
|
+
"arFragment = supportFragmentManager.findFragmentById(R.id.ar_fragment) as ArFragment"
|
|
1539
|
+
);
|
|
1540
|
+
compiler.emit("");
|
|
1541
|
+
compiler.emit("setupARSession()");
|
|
1542
|
+
compiler.emit("setupTapListener()");
|
|
1543
|
+
compiler.emit("setupUI()");
|
|
1544
|
+
if (hasGeoTraits(composition)) {
|
|
1545
|
+
compiler.emit("setupGeoAnchors()");
|
|
1546
|
+
}
|
|
1547
|
+
if (hasGeospatialVPSTraits(composition)) {
|
|
1548
|
+
compiler.emit("setupGeospatialVPS()");
|
|
1549
|
+
}
|
|
1550
|
+
if (hasDepthScanTraits(composition)) {
|
|
1551
|
+
compiler.emit("setupDepthScanner()");
|
|
1552
|
+
}
|
|
1553
|
+
if (hasPortalARTraits(composition)) {
|
|
1554
|
+
compiler.emit("setupPortalAR()");
|
|
1555
|
+
}
|
|
1556
|
+
if (hasHandTrackingTraits(composition)) {
|
|
1557
|
+
compiler.emit("setupHandTracking()");
|
|
1558
|
+
}
|
|
1559
|
+
if (hasAuthoringTraits(composition)) {
|
|
1560
|
+
compiler.emit("setupSpatialAuthoring()");
|
|
1561
|
+
}
|
|
1562
|
+
compiler.indentLevel--;
|
|
1563
|
+
compiler.emit("}");
|
|
1564
|
+
compiler.emit("");
|
|
1565
|
+
compiler.emit("private fun setupARSession() {");
|
|
1566
|
+
compiler.indentLevel++;
|
|
1567
|
+
compiler.emit("arFragment.arSceneView.scene.addOnUpdateListener { frameTime ->");
|
|
1568
|
+
compiler.indentLevel++;
|
|
1569
|
+
compiler.emit("val frame = arFragment.arSceneView.arFrame ?: return@addOnUpdateListener");
|
|
1570
|
+
compiler.emit("");
|
|
1571
|
+
compiler.emit("// Track planes");
|
|
1572
|
+
compiler.emit("for (plane in frame.getUpdatedTrackables(Plane::class.java)) {");
|
|
1573
|
+
compiler.indentLevel++;
|
|
1574
|
+
compiler.emit("if (plane.trackingState == TrackingState.TRACKING) {");
|
|
1575
|
+
compiler.indentLevel++;
|
|
1576
|
+
compiler.emit("sceneState.onPlaneDetected(plane)");
|
|
1577
|
+
compiler.indentLevel--;
|
|
1578
|
+
compiler.emit("}");
|
|
1579
|
+
compiler.indentLevel--;
|
|
1580
|
+
compiler.emit("}");
|
|
1581
|
+
compiler.indentLevel--;
|
|
1582
|
+
compiler.emit("}");
|
|
1583
|
+
compiler.indentLevel--;
|
|
1584
|
+
compiler.emit("}");
|
|
1585
|
+
compiler.emit("");
|
|
1586
|
+
compiler.emit("private fun setupTapListener() {");
|
|
1587
|
+
compiler.indentLevel++;
|
|
1588
|
+
compiler.emit("arFragment.setOnTapArPlaneListener { hitResult, plane, motionEvent ->");
|
|
1589
|
+
compiler.indentLevel++;
|
|
1590
|
+
compiler.emit(
|
|
1591
|
+
"if (plane.type != Plane.Type.HORIZONTAL_UPWARD_FACING) return@setOnTapArPlaneListener"
|
|
1592
|
+
);
|
|
1593
|
+
compiler.emit("");
|
|
1594
|
+
compiler.emit("placeObject(hitResult)");
|
|
1595
|
+
compiler.indentLevel--;
|
|
1596
|
+
compiler.emit("}");
|
|
1597
|
+
compiler.indentLevel--;
|
|
1598
|
+
compiler.emit("}");
|
|
1599
|
+
compiler.emit("");
|
|
1600
|
+
compiler.emit("private fun placeObject(hitResult: HitResult) {");
|
|
1601
|
+
compiler.indentLevel++;
|
|
1602
|
+
compiler.emit("val anchor = hitResult.createAnchor()");
|
|
1603
|
+
compiler.emit("val anchorNode = AnchorNode(anchor)");
|
|
1604
|
+
compiler.emit("anchorNode.setParent(arFragment.arSceneView.scene)");
|
|
1605
|
+
compiler.emit("");
|
|
1606
|
+
if (composition.objects?.length) {
|
|
1607
|
+
compiler.emit("// Create one renderable node per HoloScript object");
|
|
1608
|
+
for (const obj of composition.objects) {
|
|
1609
|
+
const methodName = `create${compiler.sanitizeName(obj.name)}`;
|
|
1610
|
+
const objectName = compiler.escapeStringValue(obj.name, "Kotlin");
|
|
1611
|
+
const position = toKotlinVector3(compiler.findObjProp(obj, "position"), [0, 0, 0]);
|
|
1612
|
+
const scale = toKotlinVector3(compiler.findObjProp(obj, "scale"), [1, 1, 1]);
|
|
1613
|
+
compiler.emit(`NodeFactory.${methodName}(this) { renderable ->`);
|
|
1614
|
+
compiler.indentLevel++;
|
|
1615
|
+
compiler.emit(
|
|
1616
|
+
`attachRenderableToAnchor(anchorNode, "${objectName}", renderable, ${position}, ${scale})`
|
|
1617
|
+
);
|
|
1618
|
+
compiler.indentLevel--;
|
|
1619
|
+
compiler.emit("}");
|
|
1620
|
+
}
|
|
1621
|
+
} else {
|
|
1622
|
+
compiler.emit("// Create fallback node for empty scenes");
|
|
1623
|
+
compiler.emit("NodeFactory.createDefaultNode(this) { renderable ->");
|
|
1624
|
+
compiler.indentLevel++;
|
|
1625
|
+
compiler.emit(
|
|
1626
|
+
'attachRenderableToAnchor(anchorNode, "default", renderable, Vector3(0f, 0f, 0f), Vector3(1f, 1f, 1f))'
|
|
1627
|
+
);
|
|
1628
|
+
compiler.indentLevel--;
|
|
1629
|
+
compiler.emit("}");
|
|
1630
|
+
}
|
|
1631
|
+
compiler.indentLevel--;
|
|
1632
|
+
compiler.emit("}");
|
|
1633
|
+
compiler.emit("");
|
|
1634
|
+
compiler.emit("private fun attachRenderableToAnchor(");
|
|
1635
|
+
compiler.indentLevel++;
|
|
1636
|
+
compiler.emit("anchorNode: AnchorNode,");
|
|
1637
|
+
compiler.emit("objectName: String,");
|
|
1638
|
+
compiler.emit("renderable: Renderable,");
|
|
1639
|
+
compiler.emit("localPosition: Vector3,");
|
|
1640
|
+
compiler.emit("localScale: Vector3");
|
|
1641
|
+
compiler.indentLevel--;
|
|
1642
|
+
compiler.emit(") {");
|
|
1643
|
+
compiler.indentLevel++;
|
|
1644
|
+
compiler.emit("val transformableNode = TransformableNode(arFragment.transformationSystem)");
|
|
1645
|
+
compiler.emit("transformableNode.setParent(anchorNode)");
|
|
1646
|
+
compiler.emit("transformableNode.renderable = renderable");
|
|
1647
|
+
compiler.emit("transformableNode.localPosition = localPosition");
|
|
1648
|
+
compiler.emit("transformableNode.localScale = localScale");
|
|
1649
|
+
compiler.emit("transformableNode.select()");
|
|
1650
|
+
compiler.emit("");
|
|
1651
|
+
compiler.emit('val id = "$objectName-${java.util.UUID.randomUUID()}"');
|
|
1652
|
+
compiler.emit("placedNodes[id] = transformableNode");
|
|
1653
|
+
compiler.emit("");
|
|
1654
|
+
compiler.emit("// Setup interaction");
|
|
1655
|
+
compiler.emit("transformableNode.setOnTapListener { _, _ ->");
|
|
1656
|
+
compiler.indentLevel++;
|
|
1657
|
+
compiler.emit("sceneState.onNodeTapped(id)");
|
|
1658
|
+
compiler.emit("animateNodeTap(transformableNode)");
|
|
1659
|
+
compiler.indentLevel--;
|
|
1660
|
+
compiler.emit("}");
|
|
1661
|
+
compiler.emit("");
|
|
1662
|
+
compiler.emit('android.util.Log.d("HoloScript", "Placed object: $objectName as $id")');
|
|
1663
|
+
compiler.indentLevel--;
|
|
1664
|
+
compiler.emit("}");
|
|
1665
|
+
compiler.emit("");
|
|
1666
|
+
compiler.emit("private fun animateNodeTap(node: TransformableNode) {");
|
|
1667
|
+
compiler.indentLevel++;
|
|
1668
|
+
compiler.emit("val originalScale = node.localScale");
|
|
1669
|
+
compiler.emit(
|
|
1670
|
+
"val scaledUp = Vector3(originalScale[0] * 1.2f, originalScale[1] * 1.2f, originalScale[2] * 1.2f)"
|
|
1671
|
+
);
|
|
1672
|
+
compiler.emit("");
|
|
1673
|
+
compiler.emit("android.animation.ObjectAnimator.ofObject(");
|
|
1674
|
+
compiler.indentLevel++;
|
|
1675
|
+
compiler.emit('node, "localScale",');
|
|
1676
|
+
compiler.emit("com.google.ar.sceneform.math.Vector3Evaluator(),");
|
|
1677
|
+
compiler.emit("originalScale, scaledUp, originalScale");
|
|
1678
|
+
compiler.indentLevel--;
|
|
1679
|
+
compiler.emit(").apply {");
|
|
1680
|
+
compiler.indentLevel++;
|
|
1681
|
+
compiler.emit("duration = 200");
|
|
1682
|
+
compiler.emit("start()");
|
|
1683
|
+
compiler.indentLevel--;
|
|
1684
|
+
compiler.emit("}");
|
|
1685
|
+
compiler.indentLevel--;
|
|
1686
|
+
compiler.emit("}");
|
|
1687
|
+
compiler.emit("");
|
|
1688
|
+
compiler.emit("private fun setupUI() {");
|
|
1689
|
+
compiler.indentLevel++;
|
|
1690
|
+
compiler.emit("findViewById<View>(R.id.reset_button)?.setOnClickListener {");
|
|
1691
|
+
compiler.indentLevel++;
|
|
1692
|
+
compiler.emit("resetScene()");
|
|
1693
|
+
compiler.indentLevel--;
|
|
1694
|
+
compiler.emit("}");
|
|
1695
|
+
compiler.indentLevel--;
|
|
1696
|
+
compiler.emit("}");
|
|
1697
|
+
compiler.emit("");
|
|
1698
|
+
compiler.emit("private fun resetScene() {");
|
|
1699
|
+
compiler.indentLevel++;
|
|
1700
|
+
compiler.emit("for (node in placedNodes.values) {");
|
|
1701
|
+
compiler.indentLevel++;
|
|
1702
|
+
compiler.emit("node.anchor?.detach()");
|
|
1703
|
+
compiler.emit("node.setParent(null)");
|
|
1704
|
+
compiler.indentLevel--;
|
|
1705
|
+
compiler.emit("}");
|
|
1706
|
+
compiler.emit("placedNodes.clear()");
|
|
1707
|
+
compiler.emit("");
|
|
1708
|
+
compiler.emit("sceneState.reset()");
|
|
1709
|
+
compiler.emit('Toast.makeText(this, "Scene reset", Toast.LENGTH_SHORT).show()');
|
|
1710
|
+
compiler.indentLevel--;
|
|
1711
|
+
compiler.emit("}");
|
|
1712
|
+
compiler.emit("");
|
|
1713
|
+
compiler.emit("override fun onResume() {");
|
|
1714
|
+
compiler.indentLevel++;
|
|
1715
|
+
compiler.emit("super.onResume()");
|
|
1716
|
+
compiler.emit("checkARCoreAvailability()");
|
|
1717
|
+
compiler.indentLevel--;
|
|
1718
|
+
compiler.emit("}");
|
|
1719
|
+
compiler.emit("");
|
|
1720
|
+
compiler.emit("private fun checkARCoreAvailability() {");
|
|
1721
|
+
compiler.indentLevel++;
|
|
1722
|
+
compiler.emit("val availability = ArCoreApk.getInstance().checkAvailability(this)");
|
|
1723
|
+
compiler.emit("if (availability.isTransient) {");
|
|
1724
|
+
compiler.indentLevel++;
|
|
1725
|
+
compiler.emit("android.os.Handler(mainLooper).postDelayed({ checkARCoreAvailability() }, 200)");
|
|
1726
|
+
compiler.emit("return");
|
|
1727
|
+
compiler.indentLevel--;
|
|
1728
|
+
compiler.emit("}");
|
|
1729
|
+
compiler.emit("if (!availability.isSupported) {");
|
|
1730
|
+
compiler.indentLevel++;
|
|
1731
|
+
compiler.emit(
|
|
1732
|
+
'Toast.makeText(this, "ARCore is not supported on this device", Toast.LENGTH_LONG).show()'
|
|
1733
|
+
);
|
|
1734
|
+
compiler.emit("finish()");
|
|
1735
|
+
compiler.indentLevel--;
|
|
1736
|
+
compiler.emit("}");
|
|
1737
|
+
compiler.indentLevel--;
|
|
1738
|
+
compiler.emit("}");
|
|
1739
|
+
if (hasGeoTraits(composition)) {
|
|
1740
|
+
emitGeoAnchorSetup(compiler, composition);
|
|
1741
|
+
}
|
|
1742
|
+
if (hasGeospatialVPSTraits(composition)) {
|
|
1743
|
+
emitGeospatialVPSSetup(compiler, composition);
|
|
1744
|
+
}
|
|
1745
|
+
if (hasDepthScanTraits(composition)) {
|
|
1746
|
+
emitDepthScanSetup(compiler, composition);
|
|
1747
|
+
}
|
|
1748
|
+
if (hasPortalARTraits(composition)) {
|
|
1749
|
+
emitPortalARSetup(compiler, composition);
|
|
1750
|
+
}
|
|
1751
|
+
if (hasHandTrackingTraits(composition)) {
|
|
1752
|
+
emitHandTrackingSetup(compiler, composition);
|
|
1753
|
+
}
|
|
1754
|
+
if (hasAuthoringTraits(composition)) {
|
|
1755
|
+
emitAuthoringInlineSetup(compiler, composition);
|
|
1756
|
+
}
|
|
1757
|
+
compiler.indentLevel--;
|
|
1758
|
+
compiler.emit("}");
|
|
1759
|
+
return compiler.lines.join("\n");
|
|
1760
|
+
}
|
|
1761
|
+
function generateStateFile(compiler, composition) {
|
|
1762
|
+
compiler.lines = [];
|
|
1763
|
+
compiler.indentLevel = 0;
|
|
1764
|
+
const pkg = compiler.options.packageName;
|
|
1765
|
+
compiler.emit("// Auto-generated by HoloScript AndroidCompiler");
|
|
1766
|
+
compiler.emit(`// State: ${compiler.escapeStringValue(composition.name, "Kotlin")}`);
|
|
1767
|
+
compiler.emit("");
|
|
1768
|
+
compiler.emit(`package ${pkg}`);
|
|
1769
|
+
compiler.emit("");
|
|
1770
|
+
compiler.emit("import androidx.lifecycle.ViewModel");
|
|
1771
|
+
compiler.emit("import androidx.lifecycle.MutableLiveData");
|
|
1772
|
+
compiler.emit("import androidx.lifecycle.LiveData");
|
|
1773
|
+
compiler.emit("import com.google.ar.core.Plane");
|
|
1774
|
+
compiler.emit("");
|
|
1775
|
+
compiler.emit("class SceneState : ViewModel() {");
|
|
1776
|
+
compiler.indentLevel++;
|
|
1777
|
+
if (composition.state) {
|
|
1778
|
+
compiler.emit("// === State Properties ===");
|
|
1779
|
+
for (const prop of composition.state.properties) {
|
|
1780
|
+
const kotlinType = compiler.toKotlinType(prop.value);
|
|
1781
|
+
const kotlinValue = compiler.toKotlinValue(prop.value);
|
|
1782
|
+
compiler.emit(
|
|
1783
|
+
`private val _${compiler.escapeStringValue(prop.key, "Kotlin")} = MutableLiveData(${kotlinValue})`
|
|
1784
|
+
);
|
|
1785
|
+
compiler.emit(
|
|
1786
|
+
`val ${compiler.escapeStringValue(prop.key, "Kotlin")}: LiveData<${kotlinType}> get() = _${compiler.escapeStringValue(prop.key, "Kotlin")}`
|
|
1787
|
+
);
|
|
1788
|
+
compiler.emit("");
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
compiler.emit("// === AR State ===");
|
|
1792
|
+
compiler.emit("private val _detectedPlanes = MutableLiveData<List<Plane>>(emptyList())");
|
|
1793
|
+
compiler.emit("val detectedPlanes: LiveData<List<Plane>> get() = _detectedPlanes");
|
|
1794
|
+
compiler.emit("");
|
|
1795
|
+
compiler.emit("private val _tappedNodes = MutableLiveData<String?>()");
|
|
1796
|
+
compiler.emit("val tappedNodes: LiveData<String?> get() = _tappedNodes");
|
|
1797
|
+
compiler.emit("");
|
|
1798
|
+
compiler.emit("fun onPlaneDetected(plane: Plane) {");
|
|
1799
|
+
compiler.indentLevel++;
|
|
1800
|
+
compiler.emit("val current = _detectedPlanes.value.orEmpty().toMutableList()");
|
|
1801
|
+
compiler.emit("if (!current.contains(plane)) {");
|
|
1802
|
+
compiler.indentLevel++;
|
|
1803
|
+
compiler.emit("current.add(plane)");
|
|
1804
|
+
compiler.emit("_detectedPlanes.value = current");
|
|
1805
|
+
compiler.emit('android.util.Log.d("HoloScript", "Plane detected: ${plane.type}")');
|
|
1806
|
+
compiler.indentLevel--;
|
|
1807
|
+
compiler.emit("}");
|
|
1808
|
+
compiler.indentLevel--;
|
|
1809
|
+
compiler.emit("}");
|
|
1810
|
+
compiler.emit("");
|
|
1811
|
+
compiler.emit("fun onNodeTapped(nodeId: String) {");
|
|
1812
|
+
compiler.indentLevel++;
|
|
1813
|
+
compiler.emit("_tappedNodes.value = nodeId");
|
|
1814
|
+
compiler.emit('android.util.Log.d("HoloScript", "Node tapped: $nodeId")');
|
|
1815
|
+
compiler.indentLevel--;
|
|
1816
|
+
compiler.emit("}");
|
|
1817
|
+
compiler.emit("");
|
|
1818
|
+
compiler.emit("fun reset() {");
|
|
1819
|
+
compiler.indentLevel++;
|
|
1820
|
+
compiler.emit("_detectedPlanes.value = emptyList()");
|
|
1821
|
+
compiler.emit("_tappedNodes.value = null");
|
|
1822
|
+
if (composition.state) {
|
|
1823
|
+
for (const prop of composition.state.properties) {
|
|
1824
|
+
const kotlinValue = compiler.toKotlinValue(prop.value);
|
|
1825
|
+
compiler.emit(
|
|
1826
|
+
`_${compiler.escapeStringValue(prop.key, "Kotlin")}.value = ${kotlinValue}`
|
|
1827
|
+
);
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
compiler.emit('android.util.Log.d("HoloScript", "State reset")');
|
|
1831
|
+
compiler.indentLevel--;
|
|
1832
|
+
compiler.emit("}");
|
|
1833
|
+
if (composition.logic?.actions) {
|
|
1834
|
+
compiler.emit("");
|
|
1835
|
+
compiler.emit("// === Actions ===");
|
|
1836
|
+
for (const action of composition.logic.actions) {
|
|
1837
|
+
compileAction(compiler, action);
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
compiler.indentLevel--;
|
|
1841
|
+
compiler.emit("}");
|
|
1842
|
+
return compiler.lines.join("\n");
|
|
1843
|
+
}
|
|
1844
|
+
function generateNodeFactoryFile(compiler, composition) {
|
|
1845
|
+
compiler.lines = [];
|
|
1846
|
+
compiler.indentLevel = 0;
|
|
1847
|
+
const pkg = compiler.options.packageName;
|
|
1848
|
+
compiler.emit("// Auto-generated by HoloScript AndroidCompiler");
|
|
1849
|
+
compiler.emit(`// Node Factory: ${compiler.escapeStringValue(composition.name, "Kotlin")}`);
|
|
1850
|
+
compiler.emit("");
|
|
1851
|
+
compiler.emit(`package ${pkg}`);
|
|
1852
|
+
compiler.emit("");
|
|
1853
|
+
compiler.emit("import android.content.Context");
|
|
1854
|
+
compiler.emit("import com.google.ar.sceneform.math.Vector3");
|
|
1855
|
+
compiler.emit("import com.google.ar.sceneform.rendering.*");
|
|
1856
|
+
compiler.emit("import com.google.android.filament.utils.Float3");
|
|
1857
|
+
compiler.emit("");
|
|
1858
|
+
compiler.emit("object NodeFactory {");
|
|
1859
|
+
compiler.indentLevel++;
|
|
1860
|
+
compiler.emit("fun createDefaultNode(context: Context, callback: (Renderable) -> Unit) {");
|
|
1861
|
+
compiler.indentLevel++;
|
|
1862
|
+
if (composition.objects?.length) {
|
|
1863
|
+
const firstObj = composition.objects[0];
|
|
1864
|
+
const color = compiler.findObjProp(firstObj, "color");
|
|
1865
|
+
compiler.emit("MaterialFactory.makeOpaqueWithColor(");
|
|
1866
|
+
compiler.indentLevel++;
|
|
1867
|
+
compiler.emit("context,");
|
|
1868
|
+
compiler.emit(`Color(${compiler.toAndroidColor(color)})`);
|
|
1869
|
+
compiler.indentLevel--;
|
|
1870
|
+
compiler.emit(").thenAccept { material ->");
|
|
1871
|
+
compiler.indentLevel++;
|
|
1872
|
+
const meshType = compiler.findObjProp(firstObj, "mesh") || compiler.findObjProp(firstObj, "type") || "cube";
|
|
1873
|
+
const geometry = compiler.getSceneformGeometry(meshType);
|
|
1874
|
+
compiler.emit(`${geometry}.thenAccept { renderable ->`);
|
|
1875
|
+
compiler.indentLevel++;
|
|
1876
|
+
compiler.emit("renderable.material = material");
|
|
1877
|
+
compiler.emit("callback(renderable)");
|
|
1878
|
+
compiler.indentLevel--;
|
|
1879
|
+
compiler.emit("}");
|
|
1880
|
+
compiler.indentLevel--;
|
|
1881
|
+
compiler.emit("}");
|
|
1882
|
+
} else {
|
|
1883
|
+
compiler.emit("MaterialFactory.makeOpaqueWithColor(context, Color(android.graphics.Color.BLUE))");
|
|
1884
|
+
compiler.emit(" .thenCompose { material ->");
|
|
1885
|
+
compiler.emit(
|
|
1886
|
+
" ShapeFactory.makeCube(Vector3(0.1f, 0.1f, 0.1f), Vector3.zero(), material)"
|
|
1887
|
+
);
|
|
1888
|
+
compiler.emit(" }");
|
|
1889
|
+
compiler.emit(" .thenAccept { renderable -> callback(renderable) }");
|
|
1890
|
+
}
|
|
1891
|
+
compiler.indentLevel--;
|
|
1892
|
+
compiler.emit("}");
|
|
1893
|
+
compiler.emit("");
|
|
1894
|
+
for (const obj of composition.objects || []) {
|
|
1895
|
+
compileObjectFactory(compiler, obj);
|
|
1896
|
+
}
|
|
1897
|
+
compiler.indentLevel--;
|
|
1898
|
+
compiler.emit("}");
|
|
1899
|
+
return compiler.lines.join("\n");
|
|
1900
|
+
}
|
|
1901
|
+
function compileObjectFactory(compiler, obj) {
|
|
1902
|
+
const methodName = `create${compiler.sanitizeName(obj.name)}`;
|
|
1903
|
+
compiler.emit(`fun ${methodName}(context: Context, callback: (Renderable) -> Unit) {`);
|
|
1904
|
+
compiler.indentLevel++;
|
|
1905
|
+
const color = compiler.findObjProp(obj, "color");
|
|
1906
|
+
const meshType = compiler.findObjProp(obj, "mesh") || compiler.findObjProp(obj, "type") || "cube";
|
|
1907
|
+
compiler.emit("MaterialFactory.makeOpaqueWithColor(");
|
|
1908
|
+
compiler.indentLevel++;
|
|
1909
|
+
compiler.emit("context,");
|
|
1910
|
+
compiler.emit(`Color(${compiler.toAndroidColor(color)})`);
|
|
1911
|
+
compiler.indentLevel--;
|
|
1912
|
+
compiler.emit(").thenAccept { material ->");
|
|
1913
|
+
compiler.indentLevel++;
|
|
1914
|
+
const geometry = compiler.getSceneformGeometry(meshType);
|
|
1915
|
+
compiler.emit(`${geometry}.thenAccept { renderable ->`);
|
|
1916
|
+
compiler.indentLevel++;
|
|
1917
|
+
compiler.emit("renderable.material = material");
|
|
1918
|
+
compiler.emit("callback(renderable)");
|
|
1919
|
+
compiler.indentLevel--;
|
|
1920
|
+
compiler.emit("}");
|
|
1921
|
+
compiler.indentLevel--;
|
|
1922
|
+
compiler.emit("}");
|
|
1923
|
+
compiler.indentLevel--;
|
|
1924
|
+
compiler.emit("}");
|
|
1925
|
+
compiler.emit("");
|
|
1926
|
+
}
|
|
1927
|
+
function generateManifestFile(compiler, composition) {
|
|
1928
|
+
const pkg = compiler.options.packageName;
|
|
1929
|
+
const cls = compiler.options.className;
|
|
1930
|
+
const hasGeo = hasGeoTraits(composition);
|
|
1931
|
+
const hasGeospatialVPS = hasGeospatialVPSTraits(composition);
|
|
1932
|
+
const geoPermissions = hasGeo || hasGeospatialVPS ? `
|
|
1933
|
+
<!-- Geo-Anchor: Location permissions -->
|
|
1934
|
+
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
|
1935
|
+
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />` : "";
|
|
1936
|
+
const geospatialApiKeyMeta = hasGeospatialVPS ? `
|
|
1937
|
+
<!-- ARCore Geospatial API key (replace with your key from Google Cloud Console) -->
|
|
1938
|
+
<meta-data android:name="com.google.android.ar.API_KEY" android:value="\${GEOSPATIAL_API_KEY}" />` : "";
|
|
1939
|
+
return `<?xml version="1.0" encoding="utf-8"?>
|
|
1940
|
+
<!-- Auto-generated by HoloScript AndroidCompiler -->
|
|
1941
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
1942
|
+
package="${pkg}">
|
|
1943
|
+
|
|
1944
|
+
<!-- AR Required -->
|
|
1945
|
+
<uses-permission android:name="android.permission.CAMERA" />
|
|
1946
|
+
<uses-feature android:name="android.hardware.camera.ar" android:required="true" />
|
|
1947
|
+
|
|
1948
|
+
<!-- ARCore Required -->
|
|
1949
|
+
<uses-feature android:glEsVersion="0x00030000" android:required="true" />${geoPermissions}
|
|
1950
|
+
|
|
1951
|
+
<application
|
|
1952
|
+
android:allowBackup="true"
|
|
1953
|
+
android:label="${compiler.escapeStringValue(composition.name, "XML")}"
|
|
1954
|
+
android:supportsRtl="true"
|
|
1955
|
+
android:theme="@style/Theme.AppCompat.NoActionBar">
|
|
1956
|
+
|
|
1957
|
+
<!-- ARCore metadata -->
|
|
1958
|
+
<meta-data android:name="com.google.ar.core" android:value="required" />${geospatialApiKeyMeta}
|
|
1959
|
+
|
|
1960
|
+
<activity
|
|
1961
|
+
android:name=".${cls}Activity"
|
|
1962
|
+
android:exported="true"
|
|
1963
|
+
android:configChanges="orientation|screenSize"
|
|
1964
|
+
android:screenOrientation="locked">
|
|
1965
|
+
<intent-filter>
|
|
1966
|
+
<action android:name="android.intent.action.MAIN" />
|
|
1967
|
+
<category android:name="android.intent.category.LAUNCHER" />
|
|
1968
|
+
</intent-filter>
|
|
1969
|
+
</activity>
|
|
1970
|
+
</application>
|
|
1971
|
+
</manifest>`;
|
|
1972
|
+
}
|
|
1973
|
+
function generateBuildGradle(compiler, composition) {
|
|
1974
|
+
const hasGeospatialVPS = hasGeospatialVPSTraits(composition);
|
|
1975
|
+
const hasDepthScan = hasDepthScanTraits(composition);
|
|
1976
|
+
const hasHandTracking = hasHandTrackingTraits(composition);
|
|
1977
|
+
const geospatialDeps = hasGeospatialVPS ? `
|
|
1978
|
+
// ARCore Geospatial API (included in com.google.ar:core >= 1.31.0)
|
|
1979
|
+
// Location services for VPS
|
|
1980
|
+
implementation 'com.google.android.gms:play-services-location:21.1.0'` : "";
|
|
1981
|
+
const depthScanDeps = hasDepthScan ? `
|
|
1982
|
+
// ARCore Depth API (M.010.02b)
|
|
1983
|
+
implementation 'com.google.ar:core:1.40.0'` : "";
|
|
1984
|
+
const handTrackingDeps = hasHandTracking ? `
|
|
1985
|
+
// MediaPipe Hands \u2014 camera-based hand tracking (M.010.04)
|
|
1986
|
+
implementation 'com.google.mediapipe:solution-hands:0.10.14'
|
|
1987
|
+
// CameraX for front camera feed
|
|
1988
|
+
implementation 'androidx.camera:camera-core:1.3.1'
|
|
1989
|
+
implementation 'androidx.camera:camera-camera2:1.3.1'
|
|
1990
|
+
implementation 'androidx.camera:camera-lifecycle:1.3.1'` : "";
|
|
1991
|
+
const hasAuthoring = hasAuthoringTraits(composition);
|
|
1992
|
+
const authoringDeps = hasAuthoring ? `
|
|
1993
|
+
// Spatial Authoring \u2014 speech recognition (M.010.08)
|
|
1994
|
+
implementation 'androidx.recyclerview:recyclerview:1.3.2'` : "";
|
|
1995
|
+
return `// Auto-generated by HoloScript AndroidCompiler
|
|
1996
|
+
// Source: ${compiler.escapeStringValue(composition.name, "Kotlin")}
|
|
1997
|
+
|
|
1998
|
+
plugins {
|
|
1999
|
+
id 'com.android.application'
|
|
2000
|
+
id 'org.jetbrains.kotlin.android'
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
android {
|
|
2004
|
+
namespace '${compiler.options.packageName}'
|
|
2005
|
+
compileSdk ${compiler.options.targetSdk}
|
|
2006
|
+
|
|
2007
|
+
defaultConfig {
|
|
2008
|
+
applicationId "${compiler.options.packageName}"
|
|
2009
|
+
minSdk ${compiler.options.minSdk}
|
|
2010
|
+
targetSdk ${compiler.options.targetSdk}
|
|
2011
|
+
versionCode 1
|
|
2012
|
+
versionName "1.0"
|
|
2013
|
+
}
|
|
2014
|
+
|
|
2015
|
+
buildTypes {
|
|
2016
|
+
release {
|
|
2017
|
+
minifyEnabled false
|
|
2018
|
+
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
compileOptions {
|
|
2023
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
2024
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
kotlinOptions {
|
|
2028
|
+
jvmTarget = '17'
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
buildFeatures {
|
|
2032
|
+
viewBinding true
|
|
2033
|
+
compose ${compiler.options.useJetpackCompose}
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
dependencies {
|
|
2038
|
+
implementation 'androidx.core:core-ktx:1.12.0'
|
|
2039
|
+
implementation 'androidx.appcompat:appcompat:1.6.1'
|
|
2040
|
+
implementation 'com.google.android.material:material:1.11.0'
|
|
2041
|
+
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0'
|
|
2042
|
+
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.7.0'
|
|
2043
|
+
|
|
2044
|
+
// ARCore (>= 1.31.0 includes Geospatial API)
|
|
2045
|
+
implementation 'com.google.ar:core:1.41.0'
|
|
2046
|
+
|
|
2047
|
+
// Sceneform (maintained fork)
|
|
2048
|
+
implementation 'com.gorisse.thomas:sceneform:1.22.0'${geospatialDeps}${depthScanDeps}${handTrackingDeps}${authoringDeps}
|
|
2049
|
+
|
|
2050
|
+
// Optional: Jetpack Compose
|
|
2051
|
+
${compiler.options.useJetpackCompose ? `implementation platform('androidx.compose:compose-bom:2024.01.00')
|
|
2052
|
+
implementation 'androidx.compose.ui:ui'
|
|
2053
|
+
implementation 'androidx.compose.material3:material3'
|
|
2054
|
+
implementation 'androidx.activity:activity-compose:1.8.2'` : "// Compose disabled"}
|
|
2055
|
+
}`;
|
|
2056
|
+
}
|
|
2057
|
+
function compileAction(compiler, action) {
|
|
2058
|
+
const rawName = compiler.sanitizeName(action.name);
|
|
2059
|
+
const name = rawName.charAt(0).toLowerCase() + rawName.slice(1);
|
|
2060
|
+
compiler.emit(`fun ${name}() {`);
|
|
2061
|
+
compiler.indentLevel++;
|
|
2062
|
+
compiler.emit(
|
|
2063
|
+
`android.util.Log.d("HoloScript", "Action: ${compiler.escapeStringValue(action.name, "Kotlin")}")`
|
|
2064
|
+
);
|
|
2065
|
+
compiler.emit("// Action implementation");
|
|
2066
|
+
compiler.indentLevel--;
|
|
2067
|
+
compiler.emit("}");
|
|
2068
|
+
compiler.emit("");
|
|
2069
|
+
}
|
|
2070
|
+
var init_AndroidARGenerators = chunkHCCQLAA4_cjs.__esm({
|
|
2071
|
+
"src/compiler/AndroidARGenerators.ts"() {
|
|
2072
|
+
init_AndroidFeatureGenerators();
|
|
2073
|
+
}
|
|
2074
|
+
});
|
|
2075
|
+
|
|
2076
|
+
// src/compiler/AndroidPeripheralGenerators.ts
|
|
2077
|
+
function hasHapticTraits(composition) {
|
|
2078
|
+
const names = chunkDTGBOSEM_cjs.HAPTIC_FEEDBACK_TRAITS;
|
|
2079
|
+
for (const obj of composition.objects || []) {
|
|
2080
|
+
for (const trait of obj.traits || []) {
|
|
2081
|
+
const name = typeof trait === "string" ? trait : trait.name;
|
|
2082
|
+
if (names.includes(name)) return true;
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
return false;
|
|
2086
|
+
}
|
|
2087
|
+
function emitHapticSetup(compiler, composition) {
|
|
2088
|
+
compiler.lines = [];
|
|
2089
|
+
compiler.indentLevel = 0;
|
|
2090
|
+
const pkg = compiler.options.packageName;
|
|
2091
|
+
const cls = compiler.options.className;
|
|
2092
|
+
compiler.emit("// Auto-generated by HoloScript AndroidCompiler \u2014 Haptic Feedback");
|
|
2093
|
+
compiler.emit(
|
|
2094
|
+
`// Source: composition "${compiler.escapeStringValue(composition.name, "Kotlin")}"`
|
|
2095
|
+
);
|
|
2096
|
+
compiler.emit("// Requires Android API 26+ (VibrationEffect)");
|
|
2097
|
+
compiler.emit("");
|
|
2098
|
+
compiler.emit(`package ${pkg}`);
|
|
2099
|
+
compiler.emit("");
|
|
2100
|
+
compiler.emit("import android.content.Context");
|
|
2101
|
+
compiler.emit("import android.os.Build");
|
|
2102
|
+
compiler.emit("import android.os.VibrationEffect");
|
|
2103
|
+
compiler.emit("import android.os.Vibrator");
|
|
2104
|
+
compiler.emit("import android.os.VibratorManager");
|
|
2105
|
+
compiler.emit("import android.view.HapticFeedbackConstants");
|
|
2106
|
+
compiler.emit("");
|
|
2107
|
+
compiler.emit(`class ${cls}HapticManager(private val context: Context) {`);
|
|
2108
|
+
compiler.indentLevel++;
|
|
2109
|
+
compiler.emit("");
|
|
2110
|
+
compiler.emit("private val vibrator: Vibrator = if (Build.VERSION.SDK_INT >= 31) {");
|
|
2111
|
+
compiler.indentLevel++;
|
|
2112
|
+
compiler.emit(
|
|
2113
|
+
"(context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager).defaultVibrator"
|
|
2114
|
+
);
|
|
2115
|
+
compiler.indentLevel--;
|
|
2116
|
+
compiler.emit("} else {");
|
|
2117
|
+
compiler.indentLevel++;
|
|
2118
|
+
compiler.emit(
|
|
2119
|
+
'@Suppress("DEPRECATION") context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator'
|
|
2120
|
+
);
|
|
2121
|
+
compiler.indentLevel--;
|
|
2122
|
+
compiler.emit("}");
|
|
2123
|
+
compiler.emit("");
|
|
2124
|
+
compiler.emit("private var intensityMultiplier: Float = 1.0f");
|
|
2125
|
+
compiler.emit("private var enabled: Boolean = true");
|
|
2126
|
+
compiler.emit("");
|
|
2127
|
+
if (compiler.compositionHasTrait(composition, "haptic_enable")) {
|
|
2128
|
+
compiler.emit("fun setEnabled(on: Boolean) { enabled = on }");
|
|
2129
|
+
compiler.emit("");
|
|
2130
|
+
}
|
|
2131
|
+
if (compiler.compositionHasTrait(composition, "haptic_intensity")) {
|
|
2132
|
+
compiler.emit("fun setIntensity(value: Float) { intensityMultiplier = value.coerceIn(0f, 1f) }");
|
|
2133
|
+
compiler.emit("");
|
|
2134
|
+
}
|
|
2135
|
+
compiler.emit("fun onTouch() { vibrate(VibrationEffect.EFFECT_CLICK) }");
|
|
2136
|
+
compiler.emit("fun onGrab() { vibrate(VibrationEffect.EFFECT_HEAVY_CLICK) }");
|
|
2137
|
+
compiler.emit("fun onThrow() { vibrate(VibrationEffect.EFFECT_DOUBLE_CLICK) }");
|
|
2138
|
+
compiler.emit("fun onImpact() { vibrate(VibrationEffect.EFFECT_TICK) }");
|
|
2139
|
+
compiler.emit("fun onSelect() { vibrate(VibrationEffect.EFFECT_CLICK) }");
|
|
2140
|
+
compiler.emit("");
|
|
2141
|
+
if (compiler.compositionHasTrait(composition, "haptic_proximity")) {
|
|
2142
|
+
compiler.emit("fun onProximity(distance: Float) {");
|
|
2143
|
+
compiler.indentLevel++;
|
|
2144
|
+
compiler.emit("if (!enabled) return");
|
|
2145
|
+
compiler.emit(
|
|
2146
|
+
"val amplitude = ((1.0f - distance.coerceIn(0f, 1f)) * 255 * intensityMultiplier).toInt()"
|
|
2147
|
+
);
|
|
2148
|
+
compiler.emit("vibrator.vibrate(VibrationEffect.createOneShot(50, amplitude.coerceIn(1, 255)))");
|
|
2149
|
+
compiler.indentLevel--;
|
|
2150
|
+
compiler.emit("}");
|
|
2151
|
+
compiler.emit("");
|
|
2152
|
+
}
|
|
2153
|
+
if (compiler.compositionHasTrait(composition, "haptic_pattern_library")) {
|
|
2154
|
+
compiler.emit("fun playPattern(timings: LongArray, amplitudes: IntArray) {");
|
|
2155
|
+
compiler.indentLevel++;
|
|
2156
|
+
compiler.emit("if (!enabled) return");
|
|
2157
|
+
compiler.emit("vibrator.vibrate(VibrationEffect.createWaveform(timings, amplitudes, -1))");
|
|
2158
|
+
compiler.indentLevel--;
|
|
2159
|
+
compiler.emit("}");
|
|
2160
|
+
compiler.emit("");
|
|
2161
|
+
}
|
|
2162
|
+
compiler.emit("private fun vibrate(effectId: Int) {");
|
|
2163
|
+
compiler.indentLevel++;
|
|
2164
|
+
compiler.emit("if (!enabled) return");
|
|
2165
|
+
compiler.emit("vibrator.vibrate(VibrationEffect.createPredefined(effectId))");
|
|
2166
|
+
compiler.indentLevel--;
|
|
2167
|
+
compiler.emit("}");
|
|
2168
|
+
compiler.indentLevel--;
|
|
2169
|
+
compiler.emit("}");
|
|
2170
|
+
return compiler.lines.join("\n");
|
|
2171
|
+
}
|
|
2172
|
+
function hasNearbyTraits(composition) {
|
|
2173
|
+
const names = chunkDTGBOSEM_cjs.NEARBY_CONNECTIONS_TRAITS;
|
|
2174
|
+
for (const obj of composition.objects || []) {
|
|
2175
|
+
for (const trait of obj.traits || []) {
|
|
2176
|
+
const name = typeof trait === "string" ? trait : trait.name;
|
|
2177
|
+
if (names.includes(name)) return true;
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
return false;
|
|
2181
|
+
}
|
|
2182
|
+
function emitNearbySetup(compiler, composition) {
|
|
2183
|
+
compiler.lines = [];
|
|
2184
|
+
compiler.indentLevel = 0;
|
|
2185
|
+
const pkg = compiler.options.packageName;
|
|
2186
|
+
const cls = compiler.options.className;
|
|
2187
|
+
compiler.emit("// Auto-generated by HoloScript AndroidCompiler \u2014 Nearby Connections");
|
|
2188
|
+
compiler.emit(
|
|
2189
|
+
`// Source: composition "${compiler.escapeStringValue(composition.name, "Kotlin")}"`
|
|
2190
|
+
);
|
|
2191
|
+
compiler.emit("// Requires Google Play Services Nearby");
|
|
2192
|
+
compiler.emit("");
|
|
2193
|
+
compiler.emit(`package ${pkg}`);
|
|
2194
|
+
compiler.emit("");
|
|
2195
|
+
compiler.emit("import android.content.Context");
|
|
2196
|
+
compiler.emit("import android.util.Log");
|
|
2197
|
+
compiler.emit("import com.google.android.gms.nearby.Nearby");
|
|
2198
|
+
compiler.emit("import com.google.android.gms.nearby.connection.*");
|
|
2199
|
+
compiler.emit("");
|
|
2200
|
+
compiler.emit(`class ${cls}NearbyManager(private val context: Context) {`);
|
|
2201
|
+
compiler.indentLevel++;
|
|
2202
|
+
compiler.emit("");
|
|
2203
|
+
compiler.emit('private val TAG = "HoloNearby"');
|
|
2204
|
+
compiler.emit('private val SERVICE_ID = "com.holoscript.nearby"');
|
|
2205
|
+
compiler.emit("private val connectedEndpoints = mutableSetOf<String>()");
|
|
2206
|
+
compiler.emit("");
|
|
2207
|
+
compiler.emit("private val connectionCallback = object : ConnectionLifecycleCallback() {");
|
|
2208
|
+
compiler.indentLevel++;
|
|
2209
|
+
compiler.emit("override fun onConnectionInitiated(endpointId: String, info: ConnectionInfo) {");
|
|
2210
|
+
compiler.indentLevel++;
|
|
2211
|
+
compiler.emit("Nearby.getConnectionsClient(context).acceptConnection(endpointId, payloadCallback)");
|
|
2212
|
+
compiler.indentLevel--;
|
|
2213
|
+
compiler.emit("}");
|
|
2214
|
+
compiler.emit(
|
|
2215
|
+
"override fun onConnectionResult(endpointId: String, result: ConnectionResolution) {"
|
|
2216
|
+
);
|
|
2217
|
+
compiler.indentLevel++;
|
|
2218
|
+
compiler.emit("if (result.status.isSuccess) { connectedEndpoints.add(endpointId) }");
|
|
2219
|
+
compiler.indentLevel--;
|
|
2220
|
+
compiler.emit("}");
|
|
2221
|
+
compiler.emit(
|
|
2222
|
+
"override fun onDisconnected(endpointId: String) { connectedEndpoints.remove(endpointId) }"
|
|
2223
|
+
);
|
|
2224
|
+
compiler.indentLevel--;
|
|
2225
|
+
compiler.emit("}");
|
|
2226
|
+
compiler.emit("");
|
|
2227
|
+
compiler.emit("private val payloadCallback = object : PayloadCallback() {");
|
|
2228
|
+
compiler.indentLevel++;
|
|
2229
|
+
compiler.emit("override fun onPayloadReceived(endpointId: String, payload: Payload) {");
|
|
2230
|
+
compiler.indentLevel++;
|
|
2231
|
+
compiler.emit('Log.d(TAG, "Payload received from $endpointId")');
|
|
2232
|
+
compiler.indentLevel--;
|
|
2233
|
+
compiler.emit("}");
|
|
2234
|
+
compiler.emit(
|
|
2235
|
+
"override fun onPayloadTransferUpdate(endpointId: String, update: PayloadTransferUpdate) {}"
|
|
2236
|
+
);
|
|
2237
|
+
compiler.indentLevel--;
|
|
2238
|
+
compiler.emit("}");
|
|
2239
|
+
compiler.emit("");
|
|
2240
|
+
if (compiler.compositionHasTrait(composition, "nearby_advertise")) {
|
|
2241
|
+
compiler.emit("fun startAdvertising() {");
|
|
2242
|
+
compiler.indentLevel++;
|
|
2243
|
+
compiler.emit(
|
|
2244
|
+
"val options = AdvertisingOptions.Builder().setStrategy(Strategy.P2P_CLUSTER).build()"
|
|
2245
|
+
);
|
|
2246
|
+
compiler.emit(
|
|
2247
|
+
'Nearby.getConnectionsClient(context).startAdvertising("HoloDevice", SERVICE_ID, connectionCallback, options)'
|
|
2248
|
+
);
|
|
2249
|
+
compiler.indentLevel--;
|
|
2250
|
+
compiler.emit("}");
|
|
2251
|
+
compiler.emit("");
|
|
2252
|
+
}
|
|
2253
|
+
if (compiler.compositionHasTrait(composition, "nearby_discover")) {
|
|
2254
|
+
compiler.emit("fun startDiscovery() {");
|
|
2255
|
+
compiler.indentLevel++;
|
|
2256
|
+
compiler.emit(
|
|
2257
|
+
"val options = DiscoveryOptions.Builder().setStrategy(Strategy.P2P_CLUSTER).build()"
|
|
2258
|
+
);
|
|
2259
|
+
compiler.emit(
|
|
2260
|
+
"Nearby.getConnectionsClient(context).startDiscovery(SERVICE_ID, object : EndpointDiscoveryCallback() {"
|
|
2261
|
+
);
|
|
2262
|
+
compiler.indentLevel++;
|
|
2263
|
+
compiler.emit("override fun onEndpointFound(endpointId: String, info: DiscoveredEndpointInfo) {");
|
|
2264
|
+
compiler.indentLevel++;
|
|
2265
|
+
compiler.emit(
|
|
2266
|
+
'Nearby.getConnectionsClient(context).requestConnection("HoloDevice", endpointId, connectionCallback)'
|
|
2267
|
+
);
|
|
2268
|
+
compiler.indentLevel--;
|
|
2269
|
+
compiler.emit("}");
|
|
2270
|
+
compiler.emit(
|
|
2271
|
+
'override fun onEndpointLost(endpointId: String) { Log.d(TAG, "Lost $endpointId") }'
|
|
2272
|
+
);
|
|
2273
|
+
compiler.indentLevel--;
|
|
2274
|
+
compiler.emit("}, options)");
|
|
2275
|
+
compiler.indentLevel--;
|
|
2276
|
+
compiler.emit("}");
|
|
2277
|
+
compiler.emit("");
|
|
2278
|
+
}
|
|
2279
|
+
if (compiler.compositionHasTrait(composition, "nearby_broadcast")) {
|
|
2280
|
+
compiler.emit("fun broadcast(data: ByteArray) {");
|
|
2281
|
+
compiler.indentLevel++;
|
|
2282
|
+
compiler.emit("val payload = Payload.fromBytes(data)");
|
|
2283
|
+
compiler.emit(
|
|
2284
|
+
"connectedEndpoints.forEach { Nearby.getConnectionsClient(context).sendPayload(it, payload) }"
|
|
2285
|
+
);
|
|
2286
|
+
compiler.indentLevel--;
|
|
2287
|
+
compiler.emit("}");
|
|
2288
|
+
compiler.emit("");
|
|
2289
|
+
}
|
|
2290
|
+
compiler.emit("fun disconnect() {");
|
|
2291
|
+
compiler.indentLevel++;
|
|
2292
|
+
compiler.emit("Nearby.getConnectionsClient(context).stopAllEndpoints()");
|
|
2293
|
+
compiler.emit("connectedEndpoints.clear()");
|
|
2294
|
+
compiler.indentLevel--;
|
|
2295
|
+
compiler.emit("}");
|
|
2296
|
+
compiler.indentLevel--;
|
|
2297
|
+
compiler.emit("}");
|
|
2298
|
+
return compiler.lines.join("\n");
|
|
2299
|
+
}
|
|
2300
|
+
function hasFoldableTraits(composition) {
|
|
2301
|
+
const names = chunkDTGBOSEM_cjs.FOLDABLE_DISPLAY_TRAITS;
|
|
2302
|
+
for (const obj of composition.objects || []) {
|
|
2303
|
+
for (const trait of obj.traits || []) {
|
|
2304
|
+
const name = typeof trait === "string" ? trait : trait.name;
|
|
2305
|
+
if (names.includes(name)) return true;
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
return false;
|
|
2309
|
+
}
|
|
2310
|
+
function emitFoldableSetup(compiler, composition) {
|
|
2311
|
+
compiler.lines = [];
|
|
2312
|
+
compiler.indentLevel = 0;
|
|
2313
|
+
const pkg = compiler.options.packageName;
|
|
2314
|
+
const cls = compiler.options.className;
|
|
2315
|
+
compiler.emit("// Auto-generated by HoloScript AndroidCompiler \u2014 Foldable Display");
|
|
2316
|
+
compiler.emit(
|
|
2317
|
+
`// Source: composition "${compiler.escapeStringValue(composition.name, "Kotlin")}"`
|
|
2318
|
+
);
|
|
2319
|
+
compiler.emit("// Requires Jetpack WindowManager");
|
|
2320
|
+
compiler.emit("");
|
|
2321
|
+
compiler.emit(`package ${pkg}`);
|
|
2322
|
+
compiler.emit("");
|
|
2323
|
+
compiler.emit("import android.app.Activity");
|
|
2324
|
+
compiler.emit("import android.util.Log");
|
|
2325
|
+
compiler.emit("import androidx.lifecycle.ViewModel");
|
|
2326
|
+
compiler.emit("import androidx.lifecycle.viewModelScope");
|
|
2327
|
+
compiler.emit("import androidx.window.layout.FoldingFeature");
|
|
2328
|
+
compiler.emit("import androidx.window.layout.WindowInfoTracker");
|
|
2329
|
+
compiler.emit("import kotlinx.coroutines.flow.MutableStateFlow");
|
|
2330
|
+
compiler.emit("import kotlinx.coroutines.flow.StateFlow");
|
|
2331
|
+
compiler.emit("import kotlinx.coroutines.launch");
|
|
2332
|
+
compiler.emit("");
|
|
2333
|
+
compiler.emit(`class ${cls}FoldableManager : ViewModel() {`);
|
|
2334
|
+
compiler.indentLevel++;
|
|
2335
|
+
compiler.emit("");
|
|
2336
|
+
compiler.emit("private val _foldState = MutableStateFlow<FoldState>(FoldState.FLAT)");
|
|
2337
|
+
compiler.emit("val foldState: StateFlow<FoldState> = _foldState");
|
|
2338
|
+
compiler.emit("");
|
|
2339
|
+
compiler.emit("private val _hingeAngle = MutableStateFlow(180f)");
|
|
2340
|
+
compiler.emit("val hingeAngle: StateFlow<Float> = _hingeAngle");
|
|
2341
|
+
compiler.emit("");
|
|
2342
|
+
compiler.emit("enum class FoldState { FLAT, HALF_OPENED, FOLDED }");
|
|
2343
|
+
compiler.emit("");
|
|
2344
|
+
compiler.emit("fun observeWindowLayout(activity: Activity) {");
|
|
2345
|
+
compiler.indentLevel++;
|
|
2346
|
+
compiler.emit("viewModelScope.launch {");
|
|
2347
|
+
compiler.indentLevel++;
|
|
2348
|
+
compiler.emit(
|
|
2349
|
+
"WindowInfoTracker.getOrCreate(activity).windowLayoutInfo(activity).collect { layoutInfo ->"
|
|
2350
|
+
);
|
|
2351
|
+
compiler.indentLevel++;
|
|
2352
|
+
compiler.emit(
|
|
2353
|
+
"val foldingFeature = layoutInfo.displayFeatures.filterIsInstance<FoldingFeature>().firstOrNull()"
|
|
2354
|
+
);
|
|
2355
|
+
compiler.emit("if (foldingFeature != null) {");
|
|
2356
|
+
compiler.indentLevel++;
|
|
2357
|
+
compiler.emit("_foldState.value = when {");
|
|
2358
|
+
compiler.indentLevel++;
|
|
2359
|
+
compiler.emit("FoldingFeature.State.HALF_OPENED -> FoldState.HALF_OPENED");
|
|
2360
|
+
compiler.emit("FoldingFeature.State.FLAT -> FoldState.FLAT");
|
|
2361
|
+
compiler.emit("else -> FoldState.FOLDED");
|
|
2362
|
+
compiler.indentLevel--;
|
|
2363
|
+
compiler.emit("}");
|
|
2364
|
+
if (compiler.compositionHasTrait(composition, "foldable_hinge_angle")) {
|
|
2365
|
+
compiler.emit("// Hinge angle exposed via foldingFeature orientation");
|
|
2366
|
+
compiler.emit(
|
|
2367
|
+
'Log.d("HoloFoldable", "Fold state: ${foldingFeature.state}, orientation: ${foldingFeature.orientation}")'
|
|
2368
|
+
);
|
|
2369
|
+
}
|
|
2370
|
+
compiler.indentLevel--;
|
|
2371
|
+
compiler.emit("}");
|
|
2372
|
+
compiler.indentLevel--;
|
|
2373
|
+
compiler.emit("}");
|
|
2374
|
+
compiler.indentLevel--;
|
|
2375
|
+
compiler.emit("}");
|
|
2376
|
+
compiler.indentLevel--;
|
|
2377
|
+
compiler.emit("}");
|
|
2378
|
+
compiler.emit("");
|
|
2379
|
+
if (compiler.compositionHasTrait(composition, "foldable_split_view")) {
|
|
2380
|
+
compiler.emit(
|
|
2381
|
+
"fun isSplitViewRecommended(): Boolean = _foldState.value == FoldState.HALF_OPENED"
|
|
2382
|
+
);
|
|
2383
|
+
compiler.emit("");
|
|
2384
|
+
}
|
|
2385
|
+
if (compiler.compositionHasTrait(composition, "foldable_tabletop")) {
|
|
2386
|
+
compiler.emit("fun isTabletopMode(): Boolean = _foldState.value == FoldState.HALF_OPENED");
|
|
2387
|
+
compiler.emit("");
|
|
2388
|
+
}
|
|
2389
|
+
compiler.indentLevel--;
|
|
2390
|
+
compiler.emit("}");
|
|
2391
|
+
return compiler.lines.join("\n");
|
|
2392
|
+
}
|
|
2393
|
+
function hasDexTraits(composition) {
|
|
2394
|
+
const names = chunkDTGBOSEM_cjs.SAMSUNG_DEX_TRAITS;
|
|
2395
|
+
for (const obj of composition.objects || []) {
|
|
2396
|
+
for (const trait of obj.traits || []) {
|
|
2397
|
+
const name = typeof trait === "string" ? trait : trait.name;
|
|
2398
|
+
if (names.includes(name)) return true;
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
return false;
|
|
2402
|
+
}
|
|
2403
|
+
function emitDexSetup(compiler, composition) {
|
|
2404
|
+
compiler.lines = [];
|
|
2405
|
+
compiler.indentLevel = 0;
|
|
2406
|
+
const pkg = compiler.options.packageName;
|
|
2407
|
+
const cls = compiler.options.className;
|
|
2408
|
+
compiler.emit("// Auto-generated by HoloScript AndroidCompiler \u2014 Samsung DeX");
|
|
2409
|
+
compiler.emit(
|
|
2410
|
+
`// Source: composition "${compiler.escapeStringValue(composition.name, "Kotlin")}"`
|
|
2411
|
+
);
|
|
2412
|
+
compiler.emit("// Samsung DeX mode detection and desktop UI transition");
|
|
2413
|
+
compiler.emit("");
|
|
2414
|
+
compiler.emit(`package ${pkg}`);
|
|
2415
|
+
compiler.emit("");
|
|
2416
|
+
compiler.emit("import android.app.Activity");
|
|
2417
|
+
compiler.emit("import android.content.res.Configuration");
|
|
2418
|
+
compiler.emit("import android.util.Log");
|
|
2419
|
+
compiler.emit("");
|
|
2420
|
+
compiler.emit(`class ${cls}DexManager(private val activity: Activity) {`);
|
|
2421
|
+
compiler.indentLevel++;
|
|
2422
|
+
compiler.emit("");
|
|
2423
|
+
compiler.emit('private val TAG = "HoloDex"');
|
|
2424
|
+
compiler.emit("");
|
|
2425
|
+
compiler.emit("fun isDeXMode(): Boolean {");
|
|
2426
|
+
compiler.indentLevel++;
|
|
2427
|
+
compiler.emit("val config = activity.resources.configuration");
|
|
2428
|
+
compiler.emit("return try {");
|
|
2429
|
+
compiler.indentLevel++;
|
|
2430
|
+
compiler.emit("val configClass = config.javaClass");
|
|
2431
|
+
compiler.emit(
|
|
2432
|
+
'val semDesktopModeEnabled = configClass.getField("SEM_DESKTOP_MODE_ENABLED").getInt(configClass)'
|
|
2433
|
+
);
|
|
2434
|
+
compiler.emit('val enabledField = configClass.getField("semDesktopModeEnabled").getInt(config)');
|
|
2435
|
+
compiler.emit("enabledField == semDesktopModeEnabled");
|
|
2436
|
+
compiler.indentLevel--;
|
|
2437
|
+
compiler.emit("} catch (e: Exception) { false }");
|
|
2438
|
+
compiler.indentLevel--;
|
|
2439
|
+
compiler.emit("}");
|
|
2440
|
+
compiler.emit("");
|
|
2441
|
+
if (compiler.compositionHasTrait(composition, "dex_handoff")) {
|
|
2442
|
+
compiler.emit("fun onDexModeChanged(isDex: Boolean) {");
|
|
2443
|
+
compiler.indentLevel++;
|
|
2444
|
+
compiler.emit("if (isDex) {");
|
|
2445
|
+
compiler.indentLevel++;
|
|
2446
|
+
compiler.emit('Log.d(TAG, "DeX connected \u2014 switching to desktop 3D editor")');
|
|
2447
|
+
compiler.emit("enableDesktopLayout()");
|
|
2448
|
+
compiler.indentLevel--;
|
|
2449
|
+
compiler.emit("} else {");
|
|
2450
|
+
compiler.indentLevel++;
|
|
2451
|
+
compiler.emit('Log.d(TAG, "DeX disconnected \u2014 switching to mobile AR")');
|
|
2452
|
+
compiler.emit("enableMobileLayout()");
|
|
2453
|
+
compiler.indentLevel--;
|
|
2454
|
+
compiler.emit("}");
|
|
2455
|
+
compiler.indentLevel--;
|
|
2456
|
+
compiler.emit("}");
|
|
2457
|
+
compiler.emit("");
|
|
2458
|
+
}
|
|
2459
|
+
if (compiler.compositionHasTrait(composition, "dex_desktop_controls")) {
|
|
2460
|
+
compiler.emit("fun enableDesktopLayout() {");
|
|
2461
|
+
compiler.indentLevel++;
|
|
2462
|
+
compiler.emit("// Enable mouse/keyboard controls, enlarge UI targets");
|
|
2463
|
+
compiler.emit('Log.d(TAG, "Desktop layout enabled with mouse/keyboard support")');
|
|
2464
|
+
compiler.indentLevel--;
|
|
2465
|
+
compiler.emit("}");
|
|
2466
|
+
compiler.emit("");
|
|
2467
|
+
}
|
|
2468
|
+
if (compiler.compositionHasTrait(composition, "dex_resolution_adapt")) {
|
|
2469
|
+
compiler.emit("fun adaptResolution() {");
|
|
2470
|
+
compiler.indentLevel++;
|
|
2471
|
+
compiler.emit("val display = activity.windowManager.defaultDisplay");
|
|
2472
|
+
compiler.emit("val metrics = android.util.DisplayMetrics()");
|
|
2473
|
+
compiler.emit('@Suppress("DEPRECATION") display.getRealMetrics(metrics)');
|
|
2474
|
+
compiler.emit('Log.d(TAG, "External display: ${metrics.widthPixels}x${metrics.heightPixels}")');
|
|
2475
|
+
compiler.indentLevel--;
|
|
2476
|
+
compiler.emit("}");
|
|
2477
|
+
compiler.emit("");
|
|
2478
|
+
}
|
|
2479
|
+
compiler.emit("private fun enableMobileLayout() {");
|
|
2480
|
+
compiler.indentLevel++;
|
|
2481
|
+
compiler.emit('Log.d(TAG, "Mobile AR layout restored")');
|
|
2482
|
+
compiler.indentLevel--;
|
|
2483
|
+
compiler.emit("}");
|
|
2484
|
+
compiler.indentLevel--;
|
|
2485
|
+
compiler.emit("}");
|
|
2486
|
+
return compiler.lines.join("\n");
|
|
2487
|
+
}
|
|
2488
|
+
function hasLensTraits(composition) {
|
|
2489
|
+
const names = chunkDTGBOSEM_cjs.GOOGLE_LENS_TRAITS;
|
|
2490
|
+
for (const obj of composition.objects || []) {
|
|
2491
|
+
for (const trait of obj.traits || []) {
|
|
2492
|
+
const name = typeof trait === "string" ? trait : trait.name;
|
|
2493
|
+
if (names.includes(name)) return true;
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
return false;
|
|
2497
|
+
}
|
|
2498
|
+
function emitLensSetup(compiler, composition) {
|
|
2499
|
+
compiler.lines = [];
|
|
2500
|
+
compiler.indentLevel = 0;
|
|
2501
|
+
const pkg = compiler.options.packageName;
|
|
2502
|
+
const cls = compiler.options.className;
|
|
2503
|
+
const usedTraits = /* @__PURE__ */ new Set();
|
|
2504
|
+
for (const obj of composition.objects || []) {
|
|
2505
|
+
for (const trait of obj.traits || []) {
|
|
2506
|
+
const name = typeof trait === "string" ? trait : trait.name;
|
|
2507
|
+
if (chunkDTGBOSEM_cjs.GOOGLE_LENS_TRAITS.includes(name)) {
|
|
2508
|
+
usedTraits.add(name);
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
compiler.emit("// Auto-generated by HoloScript AndroidCompiler \u2014 Google Lens Integration");
|
|
2513
|
+
compiler.emit(
|
|
2514
|
+
`// Source: composition "${compiler.escapeStringValue(composition.name, "Kotlin")}"`
|
|
2515
|
+
);
|
|
2516
|
+
compiler.emit("// Requires ML Kit (vision, text, translate)");
|
|
2517
|
+
compiler.emit("");
|
|
2518
|
+
compiler.emit(`package ${pkg}`);
|
|
2519
|
+
compiler.emit("");
|
|
2520
|
+
compiler.emit("import android.graphics.Bitmap");
|
|
2521
|
+
compiler.emit("import android.util.Log");
|
|
2522
|
+
compiler.emit("import androidx.lifecycle.ViewModel");
|
|
2523
|
+
compiler.emit("import androidx.lifecycle.viewModelScope");
|
|
2524
|
+
compiler.emit("import kotlinx.coroutines.flow.MutableStateFlow");
|
|
2525
|
+
compiler.emit("import kotlinx.coroutines.flow.StateFlow");
|
|
2526
|
+
compiler.emit("import kotlinx.coroutines.launch");
|
|
2527
|
+
if (usedTraits.has("lens_recognize") || usedTraits.has("lens_product_id") || usedTraits.has("lens_plant_animal_id") || usedTraits.has("lens_landmark_info")) {
|
|
2528
|
+
compiler.emit("import com.google.mlkit.vision.common.InputImage");
|
|
2529
|
+
compiler.emit("import com.google.mlkit.vision.objects.ObjectDetection");
|
|
2530
|
+
compiler.emit("import com.google.mlkit.vision.objects.defaults.ObjectDetectorOptions");
|
|
2531
|
+
compiler.emit("import com.google.mlkit.vision.label.ImageLabeling");
|
|
2532
|
+
compiler.emit("import com.google.mlkit.vision.label.defaults.ImageLabelerOptions");
|
|
2533
|
+
}
|
|
2534
|
+
if (usedTraits.has("lens_text_overlay") || usedTraits.has("lens_translate")) {
|
|
2535
|
+
compiler.emit("import com.google.mlkit.vision.text.TextRecognition");
|
|
2536
|
+
compiler.emit("import com.google.mlkit.vision.text.latin.TextRecognizerOptions");
|
|
2537
|
+
}
|
|
2538
|
+
if (usedTraits.has("lens_translate")) {
|
|
2539
|
+
compiler.emit("import com.google.mlkit.nl.translate.TranslateLanguage");
|
|
2540
|
+
compiler.emit("import com.google.mlkit.nl.translate.Translation");
|
|
2541
|
+
compiler.emit("import com.google.mlkit.nl.translate.TranslatorOptions");
|
|
2542
|
+
}
|
|
2543
|
+
compiler.emit("");
|
|
2544
|
+
compiler.emit(
|
|
2545
|
+
"data class LensDetection(val label: String, val confidence: Float, val text: String? = null)"
|
|
2546
|
+
);
|
|
2547
|
+
compiler.emit("");
|
|
2548
|
+
compiler.emit(`class ${cls}LensManager : ViewModel() {`);
|
|
2549
|
+
compiler.indentLevel++;
|
|
2550
|
+
compiler.emit("");
|
|
2551
|
+
compiler.emit('private val TAG = "HoloLens"');
|
|
2552
|
+
compiler.emit("private val _detections = MutableStateFlow<List<LensDetection>>(emptyList())");
|
|
2553
|
+
compiler.emit("val detections: StateFlow<List<LensDetection>> = _detections");
|
|
2554
|
+
compiler.emit("");
|
|
2555
|
+
if (usedTraits.has("lens_recognize")) {
|
|
2556
|
+
compiler.emit("private val objectDetector by lazy {");
|
|
2557
|
+
compiler.indentLevel++;
|
|
2558
|
+
compiler.emit("ObjectDetection.getClient(ObjectDetectorOptions.Builder()");
|
|
2559
|
+
compiler.emit(" .setDetectorMode(ObjectDetectorOptions.STREAM_MODE)");
|
|
2560
|
+
compiler.emit(" .enableMultipleObjects().enableClassification().build())");
|
|
2561
|
+
compiler.indentLevel--;
|
|
2562
|
+
compiler.emit("}");
|
|
2563
|
+
compiler.emit("");
|
|
2564
|
+
compiler.emit("fun recognizeObjects(image: InputImage) {");
|
|
2565
|
+
compiler.indentLevel++;
|
|
2566
|
+
compiler.emit("objectDetector.process(image).addOnSuccessListener { objects ->");
|
|
2567
|
+
compiler.indentLevel++;
|
|
2568
|
+
compiler.emit(
|
|
2569
|
+
"_detections.value = objects.flatMap { it.labels }.map { LensDetection(it.text, it.confidence) }"
|
|
2570
|
+
);
|
|
2571
|
+
compiler.indentLevel--;
|
|
2572
|
+
compiler.emit("}");
|
|
2573
|
+
compiler.indentLevel--;
|
|
2574
|
+
compiler.emit("}");
|
|
2575
|
+
compiler.emit("");
|
|
2576
|
+
}
|
|
2577
|
+
if (usedTraits.has("lens_text_overlay")) {
|
|
2578
|
+
compiler.emit(
|
|
2579
|
+
"private val textRecognizer by lazy { TextRecognition.getClient(TextRecognizerOptions.Builder().build()) }"
|
|
2580
|
+
);
|
|
2581
|
+
compiler.emit("");
|
|
2582
|
+
compiler.emit("fun recognizeText(image: InputImage) {");
|
|
2583
|
+
compiler.indentLevel++;
|
|
2584
|
+
compiler.emit("textRecognizer.process(image).addOnSuccessListener { visionText ->");
|
|
2585
|
+
compiler.indentLevel++;
|
|
2586
|
+
compiler.emit('Log.d(TAG, "Recognized: ${visionText.text}")');
|
|
2587
|
+
compiler.indentLevel--;
|
|
2588
|
+
compiler.emit("}");
|
|
2589
|
+
compiler.indentLevel--;
|
|
2590
|
+
compiler.emit("}");
|
|
2591
|
+
compiler.emit("");
|
|
2592
|
+
}
|
|
2593
|
+
if (usedTraits.has("lens_translate")) {
|
|
2594
|
+
compiler.emit(
|
|
2595
|
+
'fun translateText(text: String, targetLang: String = "es", callback: (String) -> Unit) {'
|
|
2596
|
+
);
|
|
2597
|
+
compiler.indentLevel++;
|
|
2598
|
+
compiler.emit("val options = TranslatorOptions.Builder()");
|
|
2599
|
+
compiler.emit(" .setSourceLanguage(TranslateLanguage.ENGLISH)");
|
|
2600
|
+
compiler.emit(" .setTargetLanguage(targetLang).build()");
|
|
2601
|
+
compiler.emit("val translator = Translation.getClient(options)");
|
|
2602
|
+
compiler.emit("translator.downloadModelIfNeeded().addOnSuccessListener {");
|
|
2603
|
+
compiler.indentLevel++;
|
|
2604
|
+
compiler.emit("translator.translate(text).addOnSuccessListener { callback(it) }");
|
|
2605
|
+
compiler.indentLevel--;
|
|
2606
|
+
compiler.emit("}");
|
|
2607
|
+
compiler.indentLevel--;
|
|
2608
|
+
compiler.emit("}");
|
|
2609
|
+
compiler.emit("");
|
|
2610
|
+
}
|
|
2611
|
+
compiler.indentLevel--;
|
|
2612
|
+
compiler.emit("}");
|
|
2613
|
+
return compiler.lines.join("\n");
|
|
2614
|
+
}
|
|
2615
|
+
function hasWebXRTraits(composition) {
|
|
2616
|
+
const names = chunkDTGBOSEM_cjs.WEBXR_TRAITS;
|
|
2617
|
+
for (const obj of composition.objects || []) {
|
|
2618
|
+
for (const trait of obj.traits || []) {
|
|
2619
|
+
const name = typeof trait === "string" ? trait : trait.name;
|
|
2620
|
+
if (names.includes(name)) return true;
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
return false;
|
|
2624
|
+
}
|
|
2625
|
+
function collectWebXRTraits(composition) {
|
|
2626
|
+
const usedTraits = /* @__PURE__ */ new Set();
|
|
2627
|
+
for (const obj of composition.objects || []) {
|
|
2628
|
+
for (const trait of obj.traits || []) {
|
|
2629
|
+
const name = typeof trait === "string" ? trait : trait.name;
|
|
2630
|
+
if (chunkDTGBOSEM_cjs.WEBXR_TRAITS.includes(name)) {
|
|
2631
|
+
usedTraits.add(name);
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2635
|
+
return usedTraits;
|
|
2636
|
+
}
|
|
2637
|
+
function emitWebXRSetup(compiler, composition) {
|
|
2638
|
+
compiler.lines = [];
|
|
2639
|
+
compiler.indentLevel = 0;
|
|
2640
|
+
const usedTraits = collectWebXRTraits(composition);
|
|
2641
|
+
const compositionName = compiler.escapeStringValue(composition.name, "TypeScript");
|
|
2642
|
+
const sessionType = usedTraits.has("webxr_session") ? "immersive-ar" : usedTraits.has("webxr_inline") ? "inline" : "immersive-ar";
|
|
2643
|
+
const refSpace = usedTraits.has("webxr_reference_space") ? "local-floor" : "local";
|
|
2644
|
+
const requiredFeatures = [];
|
|
2645
|
+
if (usedTraits.has("webxr_hit_test")) requiredFeatures.push("hit-test");
|
|
2646
|
+
if (usedTraits.has("webxr_anchors")) requiredFeatures.push("anchors");
|
|
2647
|
+
if (usedTraits.has("webxr_light_estimation")) requiredFeatures.push("light-estimation");
|
|
2648
|
+
if (usedTraits.has("webxr_dom_overlay")) requiredFeatures.push("dom-overlay");
|
|
2649
|
+
if (usedTraits.has("webxr_depth_sensing")) requiredFeatures.push("depth-sensing");
|
|
2650
|
+
if (usedTraits.has("webxr_hand_tracking")) requiredFeatures.push("hand-tracking");
|
|
2651
|
+
if (usedTraits.has("webxr_layers")) requiredFeatures.push("layers");
|
|
2652
|
+
compiler.emit("<!DOCTYPE html>");
|
|
2653
|
+
compiler.emit(`<html lang="en">`);
|
|
2654
|
+
compiler.emit("<head>");
|
|
2655
|
+
compiler.indentLevel++;
|
|
2656
|
+
compiler.emit('<meta charset="utf-8">');
|
|
2657
|
+
compiler.emit('<meta name="viewport" content="width=device-width, initial-scale=1.0">');
|
|
2658
|
+
compiler.emit(`<title>${compositionName} \u2014 WebXR</title>`);
|
|
2659
|
+
compiler.emit("<style>");
|
|
2660
|
+
compiler.indentLevel++;
|
|
2661
|
+
compiler.emit("body { margin: 0; overflow: hidden; }");
|
|
2662
|
+
compiler.emit("canvas { display: block; }");
|
|
2663
|
+
if (usedTraits.has("webxr_dom_overlay")) {
|
|
2664
|
+
compiler.emit(
|
|
2665
|
+
"#overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 1; }"
|
|
2666
|
+
);
|
|
2667
|
+
compiler.emit(
|
|
2668
|
+
"#overlay .ui-panel { pointer-events: auto; background: rgba(0,0,0,0.6); color: #fff; padding: 12px; border-radius: 8px; margin: 16px; font-family: sans-serif; }"
|
|
2669
|
+
);
|
|
2670
|
+
}
|
|
2671
|
+
compiler.emit(
|
|
2672
|
+
"#no-xr { display: none; padding: 20px; text-align: center; font-family: sans-serif; }"
|
|
2673
|
+
);
|
|
2674
|
+
compiler.indentLevel--;
|
|
2675
|
+
compiler.emit("</style>");
|
|
2676
|
+
compiler.emit('<script type="importmap">');
|
|
2677
|
+
compiler.emit(
|
|
2678
|
+
'{ "imports": { "three": "https://unpkg.com/three@0.160.0/build/three.module.js", "three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/" } }'
|
|
2679
|
+
);
|
|
2680
|
+
compiler.emit("</script>");
|
|
2681
|
+
compiler.indentLevel--;
|
|
2682
|
+
compiler.emit("</head>");
|
|
2683
|
+
compiler.emit("<body>");
|
|
2684
|
+
compiler.indentLevel++;
|
|
2685
|
+
if (usedTraits.has("webxr_dom_overlay")) {
|
|
2686
|
+
compiler.emit('<div id="overlay">');
|
|
2687
|
+
compiler.indentLevel++;
|
|
2688
|
+
compiler.emit(`<div class="ui-panel">${compositionName}</div>`);
|
|
2689
|
+
compiler.indentLevel--;
|
|
2690
|
+
compiler.emit("</div>");
|
|
2691
|
+
}
|
|
2692
|
+
compiler.emit('<div id="no-xr">WebXR not supported on this browser/device.</div>');
|
|
2693
|
+
compiler.emit('<script type="module">');
|
|
2694
|
+
compiler.indentLevel++;
|
|
2695
|
+
compiler.emit("import * as THREE from 'three';");
|
|
2696
|
+
compiler.emit("");
|
|
2697
|
+
compiler.emit("if (!navigator.xr) {");
|
|
2698
|
+
compiler.indentLevel++;
|
|
2699
|
+
compiler.emit("document.getElementById('no-xr').style.display = 'block';");
|
|
2700
|
+
compiler.emit("throw new Error('WebXR not supported');");
|
|
2701
|
+
compiler.indentLevel--;
|
|
2702
|
+
compiler.emit("}");
|
|
2703
|
+
compiler.emit("");
|
|
2704
|
+
compiler.emit("const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });");
|
|
2705
|
+
compiler.emit("renderer.setPixelRatio(window.devicePixelRatio);");
|
|
2706
|
+
compiler.emit("renderer.setSize(window.innerWidth, window.innerHeight);");
|
|
2707
|
+
compiler.emit("renderer.xr.enabled = true;");
|
|
2708
|
+
compiler.emit("document.body.appendChild(renderer.domElement);");
|
|
2709
|
+
compiler.emit("");
|
|
2710
|
+
compiler.emit("const scene = new THREE.Scene();");
|
|
2711
|
+
compiler.emit(
|
|
2712
|
+
"const camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 100);"
|
|
2713
|
+
);
|
|
2714
|
+
compiler.emit("");
|
|
2715
|
+
compiler.emit("scene.add(new THREE.AmbientLight(0xffffff, 0.6));");
|
|
2716
|
+
if (usedTraits.has("webxr_light_estimation")) {
|
|
2717
|
+
compiler.emit("");
|
|
2718
|
+
compiler.emit("// WebXR Light Estimation");
|
|
2719
|
+
compiler.emit("const estimatedLight = new THREE.DirectionalLight(0xffffff, 1.0);");
|
|
2720
|
+
compiler.emit("estimatedLight.position.set(0.5, 1, 0.5);");
|
|
2721
|
+
compiler.emit("scene.add(estimatedLight);");
|
|
2722
|
+
compiler.emit("let xrLightProbe = null;");
|
|
2723
|
+
}
|
|
2724
|
+
compiler.emit("");
|
|
2725
|
+
compiler.emit("// Scene objects from HoloScript composition");
|
|
2726
|
+
for (const obj of composition.objects || []) {
|
|
2727
|
+
const objName = obj.name || "Object";
|
|
2728
|
+
const shape = compiler.findObjProp(obj, "shape");
|
|
2729
|
+
const color = compiler.findObjProp(obj, "color");
|
|
2730
|
+
const position = compiler.findObjProp(obj, "position");
|
|
2731
|
+
let geometryClass = "BoxGeometry(0.1, 0.1, 0.1)";
|
|
2732
|
+
if (shape === "sphere") geometryClass = "SphereGeometry(0.05, 32, 32)";
|
|
2733
|
+
else if (shape === "cylinder") geometryClass = "CylinderGeometry(0.05, 0.05, 0.1, 32)";
|
|
2734
|
+
else if (shape === "plane") geometryClass = "PlaneGeometry(0.2, 0.2)";
|
|
2735
|
+
const colorHex = typeof color === "string" ? color : "#4488ff";
|
|
2736
|
+
const pos = Array.isArray(position) && position.length >= 3 ? position : [0, 0, -1];
|
|
2737
|
+
compiler.emit("{");
|
|
2738
|
+
compiler.indentLevel++;
|
|
2739
|
+
compiler.emit(`const geometry = new THREE.${geometryClass};`);
|
|
2740
|
+
compiler.emit(`const material = new THREE.MeshStandardMaterial({ color: '${colorHex}' });`);
|
|
2741
|
+
compiler.emit("const mesh = new THREE.Mesh(geometry, material);");
|
|
2742
|
+
compiler.emit(`mesh.name = '${compiler.escapeStringValue(objName, "TypeScript")}';`);
|
|
2743
|
+
compiler.emit(`mesh.position.set(${pos[0]}, ${pos[1]}, ${pos[2]});`);
|
|
2744
|
+
compiler.emit("scene.add(mesh);");
|
|
2745
|
+
compiler.indentLevel--;
|
|
2746
|
+
compiler.emit("}");
|
|
2747
|
+
}
|
|
2748
|
+
compiler.emit("");
|
|
2749
|
+
if (usedTraits.has("webxr_hit_test")) {
|
|
2750
|
+
compiler.emit("// Hit test reticle");
|
|
2751
|
+
compiler.emit("const reticle = new THREE.Mesh(");
|
|
2752
|
+
compiler.emit(" new THREE.RingGeometry(0.05, 0.06, 32).rotateX(-Math.PI / 2),");
|
|
2753
|
+
compiler.emit(" new THREE.MeshBasicMaterial({ color: 0x00ff00 })");
|
|
2754
|
+
compiler.emit(");");
|
|
2755
|
+
compiler.emit("reticle.matrixAutoUpdate = false;");
|
|
2756
|
+
compiler.emit("reticle.visible = false;");
|
|
2757
|
+
compiler.emit("scene.add(reticle);");
|
|
2758
|
+
compiler.emit("let hitTestSource = null;");
|
|
2759
|
+
compiler.emit("");
|
|
2760
|
+
}
|
|
2761
|
+
if (usedTraits.has("webxr_framebuffer")) {
|
|
2762
|
+
compiler.emit("let xrFramebuffer = null;");
|
|
2763
|
+
compiler.emit("");
|
|
2764
|
+
}
|
|
2765
|
+
if (usedTraits.has("webxr_anchors")) {
|
|
2766
|
+
compiler.emit("const anchors = new Map();");
|
|
2767
|
+
compiler.emit("");
|
|
2768
|
+
compiler.emit("async function createAnchor(frame, refSpace, position) {");
|
|
2769
|
+
compiler.indentLevel++;
|
|
2770
|
+
compiler.emit("const anchorPose = new XRRigidTransform(position);");
|
|
2771
|
+
compiler.emit("const anchor = await frame.createAnchor(anchorPose, refSpace);");
|
|
2772
|
+
compiler.emit("anchors.set(anchor, position);");
|
|
2773
|
+
compiler.emit("return anchor;");
|
|
2774
|
+
compiler.indentLevel--;
|
|
2775
|
+
compiler.emit("}");
|
|
2776
|
+
compiler.emit("");
|
|
2777
|
+
}
|
|
2778
|
+
if (usedTraits.has("webxr_hand_tracking")) {
|
|
2779
|
+
compiler.emit("function updateHands(frame, refSpace) {");
|
|
2780
|
+
compiler.indentLevel++;
|
|
2781
|
+
compiler.emit("for (const source of frame.session.inputSources) {");
|
|
2782
|
+
compiler.indentLevel++;
|
|
2783
|
+
compiler.emit("if (!source.hand) continue;");
|
|
2784
|
+
compiler.emit("for (const [name, jointSpace] of source.hand) {");
|
|
2785
|
+
compiler.indentLevel++;
|
|
2786
|
+
compiler.emit("const jointPose = frame.getJointPose(jointSpace, refSpace);");
|
|
2787
|
+
compiler.emit("if (jointPose) { /* joint position: jointPose.transform.position */ }");
|
|
2788
|
+
compiler.indentLevel--;
|
|
2789
|
+
compiler.emit("}");
|
|
2790
|
+
compiler.indentLevel--;
|
|
2791
|
+
compiler.emit("}");
|
|
2792
|
+
compiler.indentLevel--;
|
|
2793
|
+
compiler.emit("}");
|
|
2794
|
+
compiler.emit("");
|
|
2795
|
+
}
|
|
2796
|
+
compiler.emit("// Session configuration");
|
|
2797
|
+
compiler.emit("const sessionOptions = {");
|
|
2798
|
+
compiler.indentLevel++;
|
|
2799
|
+
if (requiredFeatures.length > 0) {
|
|
2800
|
+
const featStr = requiredFeatures.map((f) => `'${f}'`).join(", ");
|
|
2801
|
+
compiler.emit(`requiredFeatures: [${featStr}],`);
|
|
2802
|
+
}
|
|
2803
|
+
if (usedTraits.has("webxr_dom_overlay")) {
|
|
2804
|
+
compiler.emit("domOverlay: { root: document.getElementById('overlay') },");
|
|
2805
|
+
}
|
|
2806
|
+
if (usedTraits.has("webxr_depth_sensing")) {
|
|
2807
|
+
compiler.emit(
|
|
2808
|
+
'depthSensing: { usagePreference: ["cpu-optimized"], dataFormatPreference: ["luminance-alpha"] },'
|
|
2809
|
+
);
|
|
2810
|
+
}
|
|
2811
|
+
compiler.indentLevel--;
|
|
2812
|
+
compiler.emit("};");
|
|
2813
|
+
compiler.emit("");
|
|
2814
|
+
compiler.emit(
|
|
2815
|
+
`navigator.xr.requestSession('${sessionType}', sessionOptions).then(async (session) => {`
|
|
2816
|
+
);
|
|
2817
|
+
compiler.indentLevel++;
|
|
2818
|
+
compiler.emit(`const refSpace = await session.requestReferenceSpace('${refSpace}');`);
|
|
2819
|
+
compiler.emit("renderer.xr.setReferenceSpace(refSpace);");
|
|
2820
|
+
compiler.emit("renderer.xr.setSession(session);");
|
|
2821
|
+
compiler.emit("");
|
|
2822
|
+
if (usedTraits.has("webxr_hit_test")) {
|
|
2823
|
+
compiler.emit("// Initialize hit test source");
|
|
2824
|
+
compiler.emit("const viewerSpace = await session.requestReferenceSpace('viewer');");
|
|
2825
|
+
compiler.emit("hitTestSource = await session.requestHitTestSource({ space: viewerSpace });");
|
|
2826
|
+
compiler.emit("");
|
|
2827
|
+
}
|
|
2828
|
+
if (usedTraits.has("webxr_light_estimation")) {
|
|
2829
|
+
compiler.emit("// Initialize light probe");
|
|
2830
|
+
compiler.emit("xrLightProbe = await session.requestLightProbe();");
|
|
2831
|
+
compiler.emit("");
|
|
2832
|
+
}
|
|
2833
|
+
if (usedTraits.has("webxr_layers")) {
|
|
2834
|
+
compiler.emit("// XR Layers");
|
|
2835
|
+
compiler.emit("const xrProjectionLayer = renderer.xr.getBaseLayer();");
|
|
2836
|
+
compiler.emit("");
|
|
2837
|
+
}
|
|
2838
|
+
compiler.emit("renderer.setAnimationLoop((timestamp, frame) => {");
|
|
2839
|
+
compiler.indentLevel++;
|
|
2840
|
+
compiler.emit("if (!frame) return;");
|
|
2841
|
+
compiler.emit("");
|
|
2842
|
+
if (usedTraits.has("webxr_hit_test")) {
|
|
2843
|
+
compiler.emit("if (hitTestSource) {");
|
|
2844
|
+
compiler.indentLevel++;
|
|
2845
|
+
compiler.emit("const hitTestResults = frame.getHitTestResults(hitTestSource);");
|
|
2846
|
+
compiler.emit("if (hitTestResults.length > 0) {");
|
|
2847
|
+
compiler.indentLevel++;
|
|
2848
|
+
compiler.emit("const hit = hitTestResults[0];");
|
|
2849
|
+
compiler.emit("reticle.visible = true;");
|
|
2850
|
+
compiler.emit("reticle.matrix.fromArray(hit.getPose(refSpace).transform.matrix);");
|
|
2851
|
+
compiler.indentLevel--;
|
|
2852
|
+
compiler.emit("} else { reticle.visible = false; }");
|
|
2853
|
+
compiler.indentLevel--;
|
|
2854
|
+
compiler.emit("}");
|
|
2855
|
+
compiler.emit("");
|
|
2856
|
+
}
|
|
2857
|
+
if (usedTraits.has("webxr_light_estimation")) {
|
|
2858
|
+
compiler.emit("if (xrLightProbe) {");
|
|
2859
|
+
compiler.indentLevel++;
|
|
2860
|
+
compiler.emit("const estimate = frame.getLightEstimate(xrLightProbe);");
|
|
2861
|
+
compiler.emit("if (estimate) {");
|
|
2862
|
+
compiler.indentLevel++;
|
|
2863
|
+
compiler.emit("const dir = estimate.primaryLightDirection;");
|
|
2864
|
+
compiler.emit("estimatedLight.position.set(dir[0], dir[1], dir[2]);");
|
|
2865
|
+
compiler.emit("estimatedLight.intensity = estimate.primaryLightIntensity?.y || 1.0;");
|
|
2866
|
+
compiler.indentLevel--;
|
|
2867
|
+
compiler.emit("}");
|
|
2868
|
+
compiler.indentLevel--;
|
|
2869
|
+
compiler.emit("}");
|
|
2870
|
+
compiler.emit("");
|
|
2871
|
+
}
|
|
2872
|
+
if (usedTraits.has("webxr_depth_sensing")) {
|
|
2873
|
+
compiler.emit("// Depth sensing");
|
|
2874
|
+
compiler.emit("const viewerPose = frame.getViewerPose(refSpace);");
|
|
2875
|
+
compiler.emit("if (viewerPose) {");
|
|
2876
|
+
compiler.indentLevel++;
|
|
2877
|
+
compiler.emit("for (const view of viewerPose.views) {");
|
|
2878
|
+
compiler.indentLevel++;
|
|
2879
|
+
compiler.emit("const depthInfo = frame.getDepthInformation(view);");
|
|
2880
|
+
compiler.emit("if (depthInfo) { /* depthInfo.data contains depth buffer */ }");
|
|
2881
|
+
compiler.indentLevel--;
|
|
2882
|
+
compiler.emit("}");
|
|
2883
|
+
compiler.indentLevel--;
|
|
2884
|
+
compiler.emit("}");
|
|
2885
|
+
compiler.emit("");
|
|
2886
|
+
}
|
|
2887
|
+
if (usedTraits.has("webxr_hand_tracking")) {
|
|
2888
|
+
compiler.emit("updateHands(frame, refSpace);");
|
|
2889
|
+
compiler.emit("");
|
|
2890
|
+
}
|
|
2891
|
+
if (usedTraits.has("webxr_framebuffer")) {
|
|
2892
|
+
compiler.emit("xrFramebuffer = renderer.xr.getBaseLayer()?.framebuffer || null;");
|
|
2893
|
+
compiler.emit("");
|
|
2894
|
+
}
|
|
2895
|
+
compiler.emit("renderer.render(scene, camera);");
|
|
2896
|
+
compiler.indentLevel--;
|
|
2897
|
+
compiler.emit("});");
|
|
2898
|
+
compiler.emit("");
|
|
2899
|
+
compiler.emit("session.addEventListener('end', () => {");
|
|
2900
|
+
compiler.indentLevel++;
|
|
2901
|
+
compiler.emit("renderer.setAnimationLoop(null);");
|
|
2902
|
+
if (usedTraits.has("webxr_hit_test")) {
|
|
2903
|
+
compiler.emit("hitTestSource = null;");
|
|
2904
|
+
}
|
|
2905
|
+
compiler.indentLevel--;
|
|
2906
|
+
compiler.emit("});");
|
|
2907
|
+
compiler.indentLevel--;
|
|
2908
|
+
compiler.emit(
|
|
2909
|
+
"}).catch(err => { console.error('WebXR session failed:', err); document.getElementById('no-xr').style.display = 'block'; });"
|
|
2910
|
+
);
|
|
2911
|
+
compiler.indentLevel--;
|
|
2912
|
+
compiler.emit("</script>");
|
|
2913
|
+
compiler.indentLevel--;
|
|
2914
|
+
compiler.emit("</body>");
|
|
2915
|
+
compiler.emit("</html>");
|
|
2916
|
+
return compiler.lines.join("\n");
|
|
2917
|
+
}
|
|
2918
|
+
var init_AndroidPeripheralGenerators = chunkHCCQLAA4_cjs.__esm({
|
|
2919
|
+
"src/compiler/AndroidPeripheralGenerators.ts"() {
|
|
2920
|
+
chunkDTGBOSEM_cjs.init_haptic_feedback();
|
|
2921
|
+
chunkDTGBOSEM_cjs.init_nearby_connections();
|
|
2922
|
+
chunkDTGBOSEM_cjs.init_foldable_display();
|
|
2923
|
+
chunkDTGBOSEM_cjs.init_samsung_dex();
|
|
2924
|
+
chunkDTGBOSEM_cjs.init_google_lens();
|
|
2925
|
+
chunkDTGBOSEM_cjs.init_webxr();
|
|
2926
|
+
}
|
|
2927
|
+
});
|
|
2928
|
+
|
|
2929
|
+
// src/compiler/AndroidCompiler.ts
|
|
2930
|
+
var AndroidCompiler_exports = {};
|
|
2931
|
+
chunkHCCQLAA4_cjs.__export(AndroidCompiler_exports, {
|
|
2932
|
+
AndroidCompiler: () => exports.AndroidCompiler,
|
|
2933
|
+
compileToAndroid: () => compileToAndroid
|
|
2934
|
+
});
|
|
2935
|
+
function compileToAndroid(composition, options) {
|
|
2936
|
+
const compiler = new exports.AndroidCompiler(options);
|
|
2937
|
+
return Promise.resolve(compiler.compile(composition, "test-token", void 0));
|
|
2938
|
+
}
|
|
2939
|
+
exports.AndroidCompiler = void 0;
|
|
2940
|
+
var init_AndroidCompiler = chunkHCCQLAA4_cjs.__esm({
|
|
2941
|
+
"src/compiler/AndroidCompiler.ts"() {
|
|
2942
|
+
chunkKKP7JKN4_cjs.init_CompilerBase();
|
|
2943
|
+
init_AndroidARGenerators();
|
|
2944
|
+
init_AndroidFeatureGenerators();
|
|
2945
|
+
init_AndroidPeripheralGenerators();
|
|
2946
|
+
chunkFEWUHQNM_cjs.init_AndroidKotlinHelpers();
|
|
2947
|
+
exports.AndroidCompiler = class extends chunkKKP7JKN4_cjs.CompilerBase {
|
|
2948
|
+
constructor(options = {}) {
|
|
2949
|
+
super();
|
|
2950
|
+
this.compilerName = "AndroidCompiler";
|
|
2951
|
+
this.lines = [];
|
|
2952
|
+
this.indentLevel = 0;
|
|
2953
|
+
this.options = {
|
|
2954
|
+
packageName: options.packageName || "com.holoscript.generated",
|
|
2955
|
+
className: options.className || "GeneratedARScene",
|
|
2956
|
+
indent: options.indent || " ",
|
|
2957
|
+
minSdk: options.minSdk || 26,
|
|
2958
|
+
targetSdk: options.targetSdk || 34,
|
|
2959
|
+
useJetpackCompose: options.useJetpackCompose ?? true,
|
|
2960
|
+
useSceneform: options.useSceneform ?? true,
|
|
2961
|
+
useFilament: options.useFilament ?? false
|
|
2962
|
+
};
|
|
2963
|
+
}
|
|
2964
|
+
getRequiredCapability() {
|
|
2965
|
+
return ans.ANSCapabilityPath.ANDROID;
|
|
2966
|
+
}
|
|
2967
|
+
compile(composition, agentToken, outputPath) {
|
|
2968
|
+
this.validateCompilerAccess(agentToken, outputPath);
|
|
2969
|
+
const result = {
|
|
2970
|
+
activityFile: generateActivityFile(this, composition),
|
|
2971
|
+
stateFile: generateStateFile(this, composition),
|
|
2972
|
+
nodeFactoryFile: generateNodeFactoryFile(this, composition),
|
|
2973
|
+
manifestFile: generateManifestFile(this, composition),
|
|
2974
|
+
buildGradle: generateBuildGradle(this, composition)
|
|
2975
|
+
};
|
|
2976
|
+
if (hasNPUSceneTraits(composition)) {
|
|
2977
|
+
result.npuSceneSetup = emitNPUSceneSetup(this, composition);
|
|
2978
|
+
}
|
|
2979
|
+
if (hasAuthoringTraits(composition)) {
|
|
2980
|
+
result.authoringSetup = emitAuthoringSetup(this, composition);
|
|
2981
|
+
}
|
|
2982
|
+
if (hasHapticTraits(composition)) {
|
|
2983
|
+
result.hapticSetup = emitHapticSetup(this, composition);
|
|
2984
|
+
}
|
|
2985
|
+
if (hasNearbyTraits(composition)) {
|
|
2986
|
+
result.nearbySetup = emitNearbySetup(this, composition);
|
|
2987
|
+
}
|
|
2988
|
+
if (hasFoldableTraits(composition)) {
|
|
2989
|
+
result.foldableSetup = emitFoldableSetup(this, composition);
|
|
2990
|
+
}
|
|
2991
|
+
if (hasDexTraits(composition)) {
|
|
2992
|
+
result.dexSetup = emitDexSetup(this, composition);
|
|
2993
|
+
}
|
|
2994
|
+
if (hasLensTraits(composition)) {
|
|
2995
|
+
result.lensSetup = emitLensSetup(this, composition);
|
|
2996
|
+
}
|
|
2997
|
+
if (hasWebXRTraits(composition)) {
|
|
2998
|
+
result.webxrSetup = emitWebXRSetup(this, composition);
|
|
2999
|
+
}
|
|
3000
|
+
return result;
|
|
3001
|
+
}
|
|
3002
|
+
emit(line) {
|
|
3003
|
+
const indent = this.options.indent.repeat(this.indentLevel);
|
|
3004
|
+
this.lines.push(indent + line);
|
|
3005
|
+
}
|
|
3006
|
+
/** @deprecated Use sanitizeNameCapitalized from AndroidKotlinHelpers */
|
|
3007
|
+
sanitizeName(name) {
|
|
3008
|
+
return chunkFEWUHQNM_cjs.sanitizeNameCapitalized(name);
|
|
3009
|
+
}
|
|
3010
|
+
/** @deprecated Use getSceneformGeometry from AndroidKotlinHelpers */
|
|
3011
|
+
getSceneformGeometry(meshType) {
|
|
3012
|
+
return chunkFEWUHQNM_cjs.getSceneformGeometry(meshType);
|
|
3013
|
+
}
|
|
3014
|
+
/** @deprecated Use findObjProp from AndroidKotlinHelpers */
|
|
3015
|
+
findObjProp(obj, key) {
|
|
3016
|
+
return chunkFEWUHQNM_cjs.findObjProp(obj, key);
|
|
3017
|
+
}
|
|
3018
|
+
/** @deprecated Use toKotlinType from AndroidKotlinHelpers */
|
|
3019
|
+
toKotlinType(value) {
|
|
3020
|
+
return chunkFEWUHQNM_cjs.toKotlinType(value);
|
|
3021
|
+
}
|
|
3022
|
+
/** @deprecated Use toKotlinValue from AndroidKotlinHelpers */
|
|
3023
|
+
toKotlinValue(value) {
|
|
3024
|
+
return chunkFEWUHQNM_cjs.toKotlinValue(value, (s, t) => this.escapeStringValue(s, t));
|
|
3025
|
+
}
|
|
3026
|
+
/** @deprecated Use toAndroidColor from AndroidKotlinHelpers */
|
|
3027
|
+
toAndroidColor(value) {
|
|
3028
|
+
return chunkFEWUHQNM_cjs.toAndroidColor(value);
|
|
3029
|
+
}
|
|
3030
|
+
/** @deprecated Use compositionHasTrait from AndroidKotlinHelpers */
|
|
3031
|
+
compositionHasTrait(composition, traitName) {
|
|
3032
|
+
return chunkFEWUHQNM_cjs.compositionHasTrait(composition, traitName);
|
|
3033
|
+
}
|
|
3034
|
+
};
|
|
3035
|
+
}
|
|
3036
|
+
});
|
|
3037
|
+
|
|
3038
|
+
exports.AndroidCompiler_exports = AndroidCompiler_exports;
|
|
3039
|
+
exports.compileToAndroid = compileToAndroid;
|
|
3040
|
+
exports.init_AndroidCompiler = init_AndroidCompiler;
|
|
3041
|
+
//# sourceMappingURL=chunk-KQCN6AKJ.cjs.map
|
|
3042
|
+
//# sourceMappingURL=chunk-KQCN6AKJ.cjs.map
|