@oeave/bakery3 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +562 -2
- package/dist/animation-B1h0Ryvj.d.ts +97 -0
- package/dist/bake/index.d.ts +369 -0
- package/dist/bake/index.js +9 -0
- package/dist/bake/index.js.map +1 -0
- package/dist/bake-DaOlenyx.d.ts +1357 -0
- package/dist/catalog/index.d.ts +100 -0
- package/dist/catalog/index.js +154 -0
- package/dist/catalog/index.js.map +1 -0
- package/dist/catalog.gen-BM-aNf7n.d.ts +733 -0
- package/dist/chunk-25ZXSVQ3.js +1107 -0
- package/dist/chunk-25ZXSVQ3.js.map +1 -0
- package/dist/chunk-3G5QL4F4.js +145 -0
- package/dist/chunk-3G5QL4F4.js.map +1 -0
- package/dist/chunk-4E5VV4QY.js +12 -0
- package/dist/chunk-4E5VV4QY.js.map +1 -0
- package/dist/chunk-54WJDN5R.js +3286 -0
- package/dist/chunk-54WJDN5R.js.map +1 -0
- package/dist/chunk-6NYR73Y7.js +832 -0
- package/dist/chunk-6NYR73Y7.js.map +1 -0
- package/dist/chunk-6YVQANMW.js +1463 -0
- package/dist/chunk-6YVQANMW.js.map +1 -0
- package/dist/chunk-AF5BIELI.js +70 -0
- package/dist/chunk-AF5BIELI.js.map +1 -0
- package/dist/chunk-APBOWWSV.js +92 -0
- package/dist/chunk-APBOWWSV.js.map +1 -0
- package/dist/chunk-ARMTTZXZ.js +1157 -0
- package/dist/chunk-ARMTTZXZ.js.map +1 -0
- package/dist/chunk-CX4WTVGH.js +4492 -0
- package/dist/chunk-CX4WTVGH.js.map +1 -0
- package/dist/chunk-HQTUFVMM.js +1412 -0
- package/dist/chunk-HQTUFVMM.js.map +1 -0
- package/dist/chunk-MD2U6S4N.js +1952 -0
- package/dist/chunk-MD2U6S4N.js.map +1 -0
- package/dist/chunk-S4AJTLLN.js +23 -0
- package/dist/chunk-S4AJTLLN.js.map +1 -0
- package/dist/chunk-TGFNQBKC.js +551 -0
- package/dist/chunk-TGFNQBKC.js.map +1 -0
- package/dist/chunk-V3QINEEG.js +87 -0
- package/dist/chunk-V3QINEEG.js.map +1 -0
- package/dist/chunk-W6WQ5UP7.js +91 -0
- package/dist/chunk-W6WQ5UP7.js.map +1 -0
- package/dist/chunk-WBIAFIHV.js +346 -0
- package/dist/chunk-WBIAFIHV.js.map +1 -0
- package/dist/chunk-ZEBVAIJJ.js +156 -0
- package/dist/chunk-ZEBVAIJJ.js.map +1 -0
- package/dist/devtools/index.d.ts +536 -0
- package/dist/devtools/index.js +15 -0
- package/dist/devtools/index.js.map +1 -0
- package/dist/environments/index.d.ts +93 -0
- package/dist/environments/index.js +382 -0
- package/dist/environments/index.js.map +1 -0
- package/dist/hotspots/index.d.ts +111 -0
- package/dist/hotspots/index.js +285 -0
- package/dist/hotspots/index.js.map +1 -0
- package/dist/index.d.ts +944 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/node/index.cjs +5115 -0
- package/dist/node/index.cjs.map +1 -0
- package/dist/node/index.d.cts +4862 -0
- package/dist/node/index.d.ts +576 -0
- package/dist/node/index.js +1279 -0
- package/dist/node/index.js.map +1 -0
- package/dist/prepare-C5qVvaw4.d.ts +112 -0
- package/dist/presets/index.d.ts +526 -0
- package/dist/presets/index.js +14 -0
- package/dist/presets/index.js.map +1 -0
- package/dist/r3f/index.d.ts +159 -0
- package/dist/r3f/index.js +592 -0
- package/dist/r3f/index.js.map +1 -0
- package/dist/room-S5TLCK4F.js +9 -0
- package/dist/room-S5TLCK4F.js.map +1 -0
- package/dist/rooms.gen-vhj6sFW5.d.ts +1434 -0
- package/dist/session-YCQS4MRH.js +9 -0
- package/dist/session-YCQS4MRH.js.map +1 -0
- package/dist/shapes/index.d.ts +222 -0
- package/dist/shapes/index.js +836 -0
- package/dist/shapes/index.js.map +1 -0
- package/dist/testRun-BIPXaVPb.d.ts +1130 -0
- package/dist/timeline-ChwgD7bT.d.ts +470 -0
- package/dist/tsl/index.d.ts +165 -0
- package/dist/tsl/index.js +310 -0
- package/dist/tsl/index.js.map +1 -0
- package/package.json +170 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/bake/textures.ts","../src/bake/session.ts"],"names":["byUrl"],"mappings":";;;;;;;;AAoEO,SAAS,iBAAA,CACd,SACA,MAAA,EACS;AACT,EAAA,OAAA,CAAQ,OAAA,GAAU,CAAA;AAClB,EAAA,OAAA,CAAQ,KAAA,GAAQ,KAAA;AAIhB,EAAA,OAAA,CAAQ,UAAA,GAAa,MAAA,KAAW,KAAA,GAAQ,oBAAA,GAAuB,cAAA;AAC/D,EAAA,OAAA,CAAQ,eAAA,GAAkB,KAAA;AAC1B,EAAA,OAAA,CAAQ,SAAA,GAAY,YAAA;AACpB,EAAA,OAAA,CAAQ,SAAA,GAAY,YAAA;AACpB,EAAA,OAAA,CAAQ,KAAA,GAAQ,mBAAA;AAChB,EAAA,OAAA,CAAQ,KAAA,GAAQ,mBAAA;AAChB,EAAA,OAAA,CAAQ,WAAA,GAAc,IAAA;AACtB,EAAA,OAAO,OAAA;AACT;AASO,SAAS,eAAe,OAAA,EAA2B;AACxD,EAAA,OAAA,CAAQ,OAAA,GAAU,gCAAA;AAClB,EAAA,OAAA,CAAQ,UAAA,GAAa,oBAAA;AACrB,EAAA,OAAA,CAAQ,WAAA,GAAc,IAAA;AACtB,EAAA,OAAO,OAAA;AACT;AAYO,SAAS,mBAAA,CACd,SACA,IAAA,EACS;AACT,EAAA,OAAA,CAAQ,OAAA,GAAU,CAAA;AAClB,EAAA,IAAI,SAAS,WAAA,EAAa;AACxB,IAAA,OAAA,CAAQ,UAAA,GAAa,oBAAA;AACrB,IAAA,OAAA,CAAQ,eAAA,GAAkB,KAAA;AAC1B,IAAA,OAAA,CAAQ,SAAA,GAAY,YAAA;AACpB,IAAA,OAAA,CAAQ,SAAA,GAAY,YAAA;AAAA,EACtB,CAAA,MAAO;AACL,IAAA,OAAA,CAAQ,KAAA,GAAQ,IAAA;AAChB,IAAA,OAAA,CAAQ,UAAA,GAAa,IAAA,KAAS,OAAA,GAAU,cAAA,GAAiB,YAAA;AAAA,EAC3D;AACA,EAAA,OAAA,CAAQ,KAAA,GAAQ,mBAAA;AAChB,EAAA,OAAA,CAAQ,KAAA,GAAQ,mBAAA;AAChB,EAAA,OAAA,CAAQ,WAAA,GAAc,IAAA;AACtB,EAAA,OAAO,OAAA;AACT;AAUO,SAAS,qBAAA,GAA4C;AAC1D,EAAA,MAAM,GAAA,GAAM,IAAI,SAAA,EAAU;AAC1B,EAAA,MAAM,GAAA,GAAM,IAAI,aAAA,EAAc;AAG9B,EAAA,GAAA,CAAI,eAAe,WAAW,CAAA;AAC9B,EAAA,GAAA,CAAI,eAAe,WAAW,CAAA;AAE9B,EAAA,OAAO,OAAO,MAAA,KAAW;AACvB,IAAA,MAAM,MAAA,GAAS,MAAA,CAAO,MAAA,KAAW,KAAA,GAAQ,GAAA,GAAM,GAAA;AAC/C,IAAA,MAAM,UAAU,MAAM,IAAI,OAAA,CAAiB,CAAC,SAAS,MAAA,KAAW;AAC9D,MAAA,MAAA,CAAO,IAAA;AAAA,QACL,MAAA,CAAO,GAAA;AAAA,QACP,CAAC,MAAA,KAAW,OAAA,CAAQ,MAAiB,CAAA;AAAA,QACrC,MAAA;AAAA,QACA,MACE,MAAA;AAAA,UACE,IAAI,YAAA,CAAa;AAAA,YACf,IAAA,EAAM,gBAAA;AAAA,YACN,OAAA,EACE,OAAO,IAAA,KAAS,OAAA,GACZ,yCACA,MAAA,CAAO,IAAA,KAAS,OAAA,IACd,MAAA,CAAO,IAAA,KAAS,QAAA,IAChB,OAAO,IAAA,KAAS,WAAA,GAChB,CAAA,mBAAA,EAAsB,MAAA,CAAO,IAAI,CAAA,SAAA,EAAY,OAAO,QAAQ,CAAA,EAAA,CAAA,GAC5D,CAAA,iCAAA,EAAoC,MAAA,CAAO,QAAQ,CAAA,EAAA,CAAA;AAAA,YAC3D,GAAA,EAAK,CAAA,EAAG,MAAA,CAAO,GAAG,CAAA,mIAAA,CAAA;AAAA,YAClB,GAAA,EAAK;AAAA,WACN;AAAA;AACH,OACJ;AAAA,IACF,CAAC,CAAA;AACD,IAAA,IAAI,MAAA,CAAO,IAAA,KAAS,OAAA,EAAS,OAAO,eAAe,OAAO,CAAA;AAC1D,IAAA,IAAI,MAAA,CAAO,IAAA,KAAS,QAAA,EAAU,OAAO,gBAAgB,OAAO,CAAA;AAC5D,IAAA,IACE,MAAA,CAAO,SAAS,OAAA,IAChB,MAAA,CAAO,SAAS,QAAA,IAChB,MAAA,CAAO,SAAS,WAAA,EAChB;AACA,MAAA,OAAO,mBAAA,CAAoB,OAAA,EAAS,MAAA,CAAO,IAAI,CAAA;AAAA,IACjD;AACA,IAAA,OAAO,iBAAA,CAAkB,OAAA,EAAS,MAAA,CAAO,MAAM,CAAA;AAAA,EACjD,CAAA;AACF;AASO,SAAS,gBAAA,CAAiB,KAAa,OAAA,EAA0B;AACtE,EAAA,IAAI,sBAAA,CAAuB,KAAK,GAAG,CAAA,IAAK,IAAI,UAAA,CAAW,IAAI,GAAG,OAAO,GAAA;AACrE,EAAA,IAAI,CAAC,SAAS,OAAO,GAAA;AACrB,EAAA,IAAI;AACF,IAAA,OAAO,IAAI,GAAA,CAAI,GAAA,EAAK,OAAO,EAAE,QAAA,EAAS;AAAA,EACxC,CAAA,CAAA,MAAQ;AAGN,IAAA,OAAO,GAAA;AAAA,EACT;AACF;;;AChKO,IAAM,IAAA,GAAO;AAwKb,SAAS,kBAAkB,OAAA,EAA0C;AAC1E,EAAA,MAAM,QAAQ,OAAA,CAAQ,KAAA;AACtB,EAAA,MAAM,SAAA,GAAY,QAAQ,SAAA,IAAa,CAAA;AACvC,EAAA,MAAM,OAAA,GAAuB,QAAQ,OAAA,IAAW,SAAA;AAChD,EAAA,MAAM,WAAW,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,OAAA,CAAQ,uBAAuB,CAAC,CAAA;AAC7D,EAAA,IAAI,QAAA,GAAW,QAAQ,KAAA,IAAS,IAAA;AAChC,EAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,cAAA,IAAkB,qBAAA,EAAsB;AAEpE,EAAA,MAAM,cAAgC,EAAC;AACvC,EAAA,MAAM,MAAA,uBAAa,GAAA,EAA8B;AACjD,EAAA,MAAM,SAAA,uBAAgB,GAAA,EAAoC;AAE1D,EAAA,IAAI,OAAA,GAAU,IAAA;AACd,EAAA,IAAI,KAAA,GAA0B,IAAA;AAC9B,EAAA,IAAI,UAAA,GAAqC,IAAA;AACzC,EAAA,IAAI,SAAA,GAAY,KAAA;AAChB,EAAA,IAAI,KAAA,GAA2D,IAAA;AAC/D,EAAA,IAAI,QAAA,GAAW,KAAA;AAGf,EAAA,IAAI,UAAA,GAAa,CAAA;AACjB,EAAA,IAAI,KAAA,GAAQ,CAAA;AAGZ,EAAA,IAAI,iBAA4C,EAAC;AAEjD,EAAA,SAAS,IAAA,GAAO;AACd,IAAA,KAAA,MAAW,QAAA,IAAY,SAAA,EAAW,QAAA,CAAS,OAAO,CAAA;AAAA,EACpD;AAEA,EAAA,SAAS,YAAA,GAAe;AACtB,IAAA,KAAA,EAAO,OAAA,EAAQ;AACf,IAAA,KAAA,GAAQ,IAAA;AACR,IAAA,SAAA,GAAY,KAAA;AAGZ,IAAA,KAAA,MAAW,IAAA,IAAQ,cAAA,EAAgB,IAAA,CAAK,MAAA,EAAO;AAC/C,IAAA,cAAA,GAAiB,EAAC;AAAA,EACpB;AAEA,EAAA,SAAS,kBAAkB,KAAA,EAAe;AACxC,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,GAAA,CAAI,KAAK,CAAA;AAC9B,IAAA,IAAI,CAAC,KAAA,EAAO;AAEZ,IAAA,KAAA,MAAW,OAAA,IAAW,IAAI,GAAA,CAAI,KAAA,CAAM,SAAS,MAAA,EAAQ,CAAA,EAAG,OAAA,CAAQ,OAAA,EAAQ;AACxE,IAAA,KAAA,MAAW,OAAA,IAAW,IAAI,GAAA,CAAI,KAAA,CAAM,MAAM,QAAA,CAAS,MAAA,EAAO,IAAK,EAAE,CAAA;AAC/D,MAAA,OAAA,CAAQ,OAAA,EAAQ;AAClB,IAAA,KAAA,MAAW,IAAA,IAAQ,KAAA,CAAM,OAAA,CAAQ,MAAA,EAAO,EAAG;AACzC,MAAA,IAAI,IAAA,CAAK,IAAA,KAAS,WAAA,EAAa,IAAA,CAAK,IAAI,OAAA,EAAQ;AAAA,WAAA,IACvC,IAAA,CAAK,IAAA,KAAS,WAAA,EAAa,IAAA,CAAK,MAAM,OAAA,EAAQ;AAAA;AAErD,QAAA,KAAA,MAAW,WAAW,CAAC,IAAA,CAAK,OAAO,IAAA,CAAK,MAAA,EAAQ,KAAK,KAAK,CAAA;AACxD,UAAA,OAAA,CAAQ,OAAA,EAAQ;AAAA,IACtB;AACA,IAAA,KAAA,CAAM,OAAO,OAAA,EAAQ;AACrB,IAAA,KAAA,CAAM,QAAQ,OAAA,EAAQ;AACtB,IAAA,MAAA,CAAO,OAAO,KAAK,CAAA;AACnB,IAAA,MAAM,UAAA,GAAa,YAAY,KAAK,CAAA;AACpC,IAAA,IAAI,UAAA,aAAuB,MAAA,GAAS,KAAA;AAAA,EACtC;AAEA,EAAA,SAAS,MAAM,IAAA,EAAc;AAC3B,IAAA,IAAI,MAAA,CAAO,QAAQ,QAAA,EAAU;AAC7B,IAAA,MAAM,MAAA,GAAS,CAAC,GAAG,MAAA,CAAO,OAAA,EAAS,CAAA,CAChC,MAAA,CAAO,CAAC,CAAC,KAAK,CAAA,KAAM,KAAA,KAAU,IAAI,CAAA,CAClC,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,CAAC,CAAA,CAAE,MAAA,GAAS,CAAA,CAAE,CAAC,CAAA,CAAE,MAAM,CAAA;AAC3C,IAAA,OAAO,MAAA,CAAO,IAAA,GAAO,QAAA,IAAY,MAAA,CAAO,MAAA,EAAQ;AAC9C,MAAA,iBAAA,CAAkB,MAAA,CAAO,KAAA,EAAM,CAAG,CAAC,CAAC,CAAA;AAAA,IACtC;AAAA,EACF;AAEA,EAAA,eAAe,aAAa,KAAA,EAA0C;AACpE,IAAA,MAAM,QAAA,GAAW,MAAA,CAAO,GAAA,CAAI,KAAK,CAAA;AACjC,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,QAAA,CAAS,SAAS,EAAE,KAAA;AACpB,MAAA,OAAO,QAAA;AAAA,IACT;AACA,IAAA,MAAM,UAAA,GAAa,YAAY,KAAK,CAAA;AACpC,IAAA,MAAM,QAAA,uBAAe,GAAA,EAAqB;AAC1C,IAAA,MAAM,QAAA,uBAAe,GAAA,EAAoB;AAIzC,IAAA,MAAM,KAAA,uBAAY,GAAA,EAAqB;AAIvC,IAAA,KAAA,MAAW,KAAA,IAAS,UAAA,CAAW,MAAA,CAAO,OAAA,EAAS;AAC7C,MAAA,IAAI;AACF,QAAA,MAAM,MAAM,gBAAA,CAAiB,KAAA,CAAM,QAAA,CAAS,GAAA,EAAK,WAAW,OAAO,CAAA;AACnE,QAAA,IAAI,OAAA,GAAU,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AAC3B,QAAA,IAAI,CAAC,OAAA,EAAS;AACZ,UAAA,OAAA,GAAU,MAAM,WAAA,CAAY;AAAA,YAC1B,GAAA;AAAA,YACA,MAAA,EAAQ,MAAM,QAAA,CAAS,MAAA;AAAA,YACvB,UAAU,KAAA,CAAM,QAAA;AAAA,YAChB,IAAA,EAAM,MAAM,QAAA,CAAS;AAAA,WACtB,CAAA;AACD,UAAA,KAAA,CAAM,GAAA,CAAI,KAAK,OAAO,CAAA;AAAA,QACxB;AACA,QAAA,QAAA,CAAS,GAAA,CAAI,KAAA,CAAM,QAAA,EAAU,OAAO,CAAA;AAAA,MACtC,SAAS,KAAA,EAAO;AAId,QAAA,QAAA,CAAS,GAAA,CAAI,KAAA,CAAM,QAAA,EAAU,QAAA,CAAS,KAAK,CAAC,CAAA;AAAA,MAC9C;AAAA,IACF;AACA,IAAA,MAAM,OAAA,uBAAc,GAAA,EAA2B;AAC/C,IAAA,KAAA,MAAW,KAAA,IAAS,UAAA,CAAW,MAAA,CAAO,OAAA,IAAW,EAAC,EAAG;AACnD,MAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,IAAA,IAAQ,CAAA,SAAA,EAAY,MAAM,KAAK,CAAA,CAAA;AACnD,MAAA,MAAM,OAAO,CACX,GAAA,EACA,MAAA,EACA,IAAA,EACA,SAEA,WAAA,CAAY;AAAA,QACV,GAAA,EAAK,gBAAA,CAAiB,GAAA,EAAK,UAAA,CAAW,OAAO,CAAA;AAAA,QAC7C,MAAA;AAAA,QACA,QAAA,EAAU,KAAA;AAAA,QACV,IAAA;AAAA,QACA;AAAA,OACD,CAAA;AACH,MAAA,IAAI;AACF,QAAA,IAAI,KAAA,CAAM,SAAS,WAAA,EAAa;AAC9B,UAAA,OAAA,CAAQ,GAAA,CAAI,MAAM,KAAA,EAAO;AAAA,YACvB,IAAA,EAAM,WAAA;AAAA,YACN,GAAA,EAAK,MAAM,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,KAAK,KAAA,EAAO,WAAA,EAAa,KAAA,CAAM,GAAA,CAAI,KAAK;AAAA,WACnE,CAAA;AAAA,QACH,CAAA,MAAA,IAAW,KAAA,CAAM,IAAA,KAAS,WAAA,EAAa;AACrC,UAAA,OAAA,CAAQ,GAAA,CAAI,MAAM,KAAA,EAAO;AAAA,YACvB,IAAA,EAAM,WAAA;AAAA,YACN,OAAO,MAAM,IAAA;AAAA,cACX,MAAM,QAAA,CAAS,GAAA;AAAA,cACf,KAAA;AAAA,cACA,UAAA;AAAA,cACA,MAAM,QAAA,CAAS;AAAA;AACjB,WACD,CAAA;AAAA,QACH,CAAA,MAAO;AACL,UAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA;AAC5B,UAAA,OAAA,CAAQ,GAAA,CAAI,MAAM,KAAA,EAAO;AAAA,YACvB,IAAA,EAAM,MAAA;AAAA,YACN,KAAA,EAAO,MAAM,IAAA,CAAK,KAAA,CAAM,MAAM,GAAA,EAAK,KAAA,EAAO,SAAS,IAAI,CAAA;AAAA,YACvD,MAAA,EAAQ,MAAM,IAAA,CAAK,KAAA,CAAM,OAAO,GAAA,EAAK,KAAA,EAAO,UAAU,IAAI,CAAA;AAAA,YAC1D,KAAA,EAAO,MAAM,IAAA,CAAK,KAAA,CAAM,SAAS,GAAA,EAAK,KAAA,EAAO,YAAY,IAAI;AAAA,WAC9D,CAAA;AAAA,QACH;AAAA,MACF,SAAS,KAAA,EAAO;AACd,QAAA,QAAA,CAAS,IAAI,CAAA,SAAA,EAAY,KAAA,CAAM,KAAK,CAAA,CAAA,CAAA,EAAK,QAAA,CAAS,KAAK,CAAC,CAAA;AAAA,MAC1D;AAAA,IACF;AACA,IAAA,IAAI,MAAA;AACJ,IAAA,IAAI,UAAA,CAAW,OAAO,MAAA,EAAQ;AAC5B,MAAA,MAAM,WAAA,GAAc,WAAW,MAAA,CAAO,MAAA;AACtC,MAAA,IAAI;AACF,QAAA,MAAA,GAAS,eAAA;AAAA,UACP,MAAM,WAAA,CAAY;AAAA,YAChB,GAAA,EAAK,gBAAA,CAAiB,WAAA,CAAY,GAAA,EAAK,WAAW,OAAO,CAAA;AAAA,YACzD,QAAQ,WAAA,CAAY,MAAA;AAAA,YACpB,QAAA,EAAU,QAAA;AAAA,YACV,MAAM,WAAA,CAAY,IAAA;AAAA,YAClB,IAAA,EAAM;AAAA,WACP;AAAA,SACH;AAAA,MACF,SAAS,KAAA,EAAO;AAGd,QAAA,QAAA,CAAS,GAAA,CAAI,QAAA,EAAU,QAAA,CAAS,KAAK,CAAC,CAAA;AAAA,MACxC;AAAA,IACF;AAIA,IAAA,IAAI,IAAA;AACJ,IAAA,IAAI,UAAA;AACJ,IAAA,MAAM,SAAA,GAAY,WAAW,MAAA,CAAO,IAAA;AACpC,IAAA,IAAI,SAAA,EAAW;AACb,MAAA,MAAM,IAAA,GAAO,CAAA,EAAA,CAAI,OAAA,CAAQ,aAAA,IAAiB,aAAA,EAAc,EAAG,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAC,CAAA,EAAA,EAAK,eAAe,CAAA,CAAA,CAAA;AACjG,MAAA,MAAM,MAAM,IAAI,GAAA,CAAI,UAAU,GAAA,EAAK,IAAI,EAAE,QAAA,EAAS;AAClD,MAAA,IAAI;AACF,QAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAG,CAAA;AAChC,QAAA,IAAI,CAAC,QAAA,CAAS,EAAA;AACZ,UAAA,MAAM,IAAI,MAAM,CAAA,EAAG,QAAA,CAAS,MAAM,CAAA,CAAA,EAAI,QAAA,CAAS,UAAU,CAAA,CAAE,CAAA;AAC7D,QAAA,MAAM,MAAA,GAAU,MAAM,QAAA,CAAS,IAAA,EAAK;AACpC,QAAA,MAAM,YAAA,uBAAmB,GAAA,EAAqB;AAC9C,QAAA,MAAMA,MAAAA,uBAAY,GAAA,EAAqB;AACvC,QAAA,KAAA,MAAW,KAAA,IAAS,OAAO,OAAA,EAAS;AAClC,UAAA,MAAM,UAAA,GAAa,gBAAA,CAAiB,KAAA,CAAM,QAAA,CAAS,KAAK,GAAG,CAAA;AAC3D,UAAA,IAAI,OAAA,GAAUA,MAAAA,CAAM,GAAA,CAAI,UAAU,CAAA;AAClC,UAAA,IAAI,CAAC,OAAA,EAAS;AACZ,YAAA,OAAA,GAAU,MAAM,WAAA,CAAY;AAAA,cAC1B,GAAA,EAAK,UAAA;AAAA,cACL,MAAA,EAAQ,MAAM,QAAA,CAAS,MAAA;AAAA,cACvB,UAAU,KAAA,CAAM,QAAA;AAAA,cAChB,IAAA,EAAM,MAAM,QAAA,CAAS;AAAA,aACtB,CAAA;AACD,YAAAA,MAAAA,CAAM,GAAA,CAAI,UAAA,EAAY,OAAO,CAAA;AAAA,UAC/B;AACA,UAAA,YAAA,CAAa,GAAA,CAAI,KAAA,CAAM,QAAA,EAAU,OAAO,CAAA;AAAA,QAC1C;AACA,QAAA,IAAA,GAAO,EAAE,MAAA,EAAQ,OAAA,EAAS,GAAA,EAAK,UAAU,YAAA,EAAa;AAAA,MACxD,SAAS,KAAA,EAAO;AAGd,QAAA,UAAA,GAAa,SAAS,KAAK,CAAA;AAAA,MAC7B;AAAA,IACF;AAEA,IAAA,MAAM,KAAA,GAA0B;AAAA,MAC9B,QAAA;AAAA,MACA,QAAA;AAAA,MACA,OAAA;AAAA,MACA,QAAQ,EAAE,KAAA;AAAA,MACV,GAAI,MAAA,GAAS,EAAE,MAAA,KAAW,EAAC;AAAA,MAC3B,GAAI,IAAA,GAAO,EAAE,IAAA,KAAS,EAAC;AAAA,MACvB,GAAI,UAAA,GAAa,EAAE,UAAA,KAAe;AAAC,KACrC;AACA,IAAA,MAAA,CAAO,GAAA,CAAI,OAAO,KAAK,CAAA;AACvB,IAAA,UAAA,CAAW,MAAA,GAAS,IAAA;AACpB,IAAA,OAAO,KAAA;AAAA,EACT;AASA,EAAA,eAAe,WAAA,CACb,OACA,KAAA,EAC8B;AAC9B,IAAA,IAAI,KAAA,CAAM,KAAA,IAAS,KAAA,CAAM,WAAA,SAAoB,KAAA,CAAM,KAAA;AACnD,IAAA,MAAM,UAAA,GAAa,YAAY,KAAK,CAAA;AACpC,IAAA,MAAM,UAAA,GAAa,WAAW,MAAA,CAAO,KAAA;AACrC,IAAA,IAAI,CAAC,YAAY,OAAO,MAAA;AACxB,IAAA,IAAI;AACF,MAAA,KAAA,CAAM,KAAA,GAAQ,cAAA;AAAA,QACZ,MAAM,WAAA,CAAY;AAAA,UAChB,GAAA,EAAK,gBAAA,CAAiB,UAAA,CAAW,GAAA,EAAK,WAAW,OAAO,CAAA;AAAA,UACxD,QAAQ,UAAA,CAAW,MAAA;AAAA,UACnB,QAAA,EAAU,OAAA;AAAA,UACV,MAAM,UAAA,CAAW,KAAA;AAAA,UACjB,IAAA,EAAM;AAAA,SACP;AAAA,OACH;AAAA,IACF,SAAS,KAAA,EAAO;AACd,MAAA,KAAA,CAAM,WAAA,GAAc,SAAS,KAAK,CAAA;AAAA,IACpC;AACA,IAAA,OAAO,KAAA,CAAM,KAAA;AAAA,EACf;AAEA,EAAA,MAAM,OAAA,GAAuB;AAAA,IAC3B,IAAI,WAAA,GAAc;AAChB,MAAA,OAAO,WAAA;AAAA,IACT,CAAA;AAAA,IACA,IAAI,OAAA,GAAU;AACZ,MAAA,OAAO,OAAA;AAAA,IACT,CAAA;AAAA,IACA,IAAI,UAAA,GAAa;AACf,MAAA,OAAO,UAAA;AAAA,IACT,CAAA;AAAA,IACA,IAAI,SAAA,GAAY;AACd,MAAA,OAAO,SAAA;AAAA,IACT,CAAA;AAAA,IACA,IAAI,WAAA,GAAc;AAChB,MAAA,OAAO,KAAA,KAAU,IAAA;AAAA,IACnB,CAAA;AAAA,IACA,IAAI,KAAA,GAAQ;AACV,MAAA,OAAO,QAAA;AAAA,IACT,CAAA;AAAA,IAEA,UAAA,GAAa;AACX,MAAA,OAAO,cAAA,CAAe,EAAE,KAAA,EAAO,CAAA;AAAA,IACjC,CAAA;AAAA,IAEA,aAAA,CAAc,MAAA,EAAQ,UAAA,GAAa,EAAC,EAAG;AACrC,MAAA,IAAI,QAAA,EAAU,MAAM,aAAA,CAAc,eAAe,CAAA;AACjD,MAAA,MAAM,QAAQ,WAAA,CAAY,MAAA;AAC1B,MAAA,WAAA,CAAY,IAAA,CAAK;AAAA,QACf,KAAA;AAAA,QACA,KAAA,EACE,UAAA,CAAW,KAAA,IACX,CAAA,EAAG,MAAA,CAAO,SAAS,WAAW,CAAA,MAAA,EAAM,MAAA,CAAO,QAAA,CAAS,OAAO,CAAA,CAAA;AAAA,QAC7D,MAAA;AAAA,QACA,GAAI,WAAW,OAAA,KAAY,MAAA,GACvB,EAAE,OAAA,EAAS,UAAA,CAAW,OAAA,EAAQ,GAC9B,EAAC;AAAA,QACL,SAAA,EAAW,KAAK,GAAA,EAAI;AAAA,QACpB,MAAA,EAAQ;AAAA,OACT,CAAA;AACD,MAAA,IAAA,EAAK;AACL,MAAA,OAAO,KAAA;AAAA,IACT,CAAA;AAAA,IAEA,MAAM,MAAM,KAAA,EAAO;AACjB,MAAA,IAAI,QAAA,EAAU,MAAM,aAAA,CAAc,OAAO,CAAA;AACzC,MAAA,IAAI,UAAU,IAAA,EAAM;AAClB,QAAA,OAAA,CAAQ,MAAA,EAAO;AACf,QAAA,OAAO,UAAA;AAAA,MACT;AACA,MAAA,MAAM,UAAA,GAAa,YAAY,KAAK,CAAA;AACpC,MAAA,IAAI,CAAC,UAAA,EAAY;AACf,QAAA,MAAM,IAAI,YAAA,CAAa;AAAA,UACrB,IAAA,EAAM,gBAAA;AAAA,UACN,OAAA,EAAS,+BAA+B,KAAK,CAAA,CAAA,CAAA;AAAA,UAC7C,GAAA,EAAK,CAAA,iBAAA,EAAoB,WAAA,CAAY,MAAM,CAAA,eAAA,CAAA;AAAA,UAC3C,GAAA,EAAK;AAAA,SACN,CAAA;AAAA,MACH;AAEA,MAAA,MAAM,QAAQ,EAAE,UAAA;AAChB,MAAA,MAAM,KAAA,GAAQ,MAAM,YAAA,CAAa,KAAK,CAAA;AACtC,MAAA,MAAM,QAAQ,QAAA,GAAW,MAAM,WAAA,CAAY,KAAA,EAAO,KAAK,CAAA,GAAI,MAAA;AAC3D,MAAA,IAAI,UAAU,UAAA,IAAc,QAAA;AAC1B,QAAA,OAAO,UAAA,IAAc,YAAY,KAAK,CAAA;AAGxC,MAAA,YAAA,EAAa;AAEb,MAAA,MAAM,UAGD,EAAC;AACN,MAAA,MAAM,kBAAmC,EAAC;AAC1C,MAAA,KAAA,MAAW,KAAA,IAAS,UAAA,CAAW,MAAA,CAAO,OAAA,EAAS;AAC7C,QAAA,MAAM,OAAA,GAAU,KAAA,CAAM,QAAA,CAAS,GAAA,CAAI,MAAM,QAAQ,CAAA;AACjD,QAAA,IAAI,CAAC,OAAA,EAAS;AACZ,UAAA,eAAA,CAAgB,IAAA,CAAK;AAAA,YACnB,UAAU,KAAA,CAAM,QAAA;AAAA,YAChB,GAAI,MAAM,IAAA,GAAO,EAAE,MAAM,KAAA,CAAM,IAAA,KAAS,EAAC;AAAA,YACzC,IAAA,EAAM,yBAAA;AAAA,YACN,OAAA,EAAS,CAAA,kBAAA,EAAqB,KAAA,CAAM,IAAA,IAAQ,MAAM,QAAQ,CAAA,eAAA,CAAA;AAAA,YAC1D,KACE,KAAA,CAAM,QAAA,CAAS,GAAA,CAAI,KAAA,CAAM,QAAQ,CAAA,IACjC,4EAAA;AAAA,YACF,GAAA,EAAK;AAAA,WACN,CAAA;AACD,UAAA;AAAA,QACF;AACA,QAAA,OAAA,CAAQ,IAAA,CAAK,EAAE,KAAA,EAAO,OAAA,EAAS,CAAA;AAAA,MACjC;AAGA,MAAA,MAAM,eAAe,KAAA,CAAM,IAAA,EAAM,MAAA,CAAO,OAAA,IAAW,EAAC,EAAG,OAAA;AAAA,QACrD,CAAC,KAAA,KAAU;AACT,UAAA,MAAM,UAAU,KAAA,CAAM,IAAA,CAAM,QAAA,CAAS,GAAA,CAAI,MAAM,QAAQ,CAAA;AACvD,UAAA,OAAO,UAAU,CAAC,EAAE,OAAO,OAAA,EAAS,IAAI,EAAC;AAAA,QAC3C;AAAA,OACF;AACA,MAAA,MAAM,SAAS,IAAI,GAAA;AAAA,QACjB,WAAW,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAC,MAAA,KAAW,OAAO,QAAQ;AAAA,OAC3D;AACA,MAAA,KAAA,MAAW,EAAE,KAAA,EAAM,IAAK,aAAa,MAAA,CAAO,GAAA,CAAI,MAAM,QAAQ,CAAA;AAC9D,MAAA,MAAM,QAAA,GAAA,CAAY,WAAW,MAAA,CAAO,OAAA,IAAW,EAAC,EAAG,OAAA,CAAQ,CAAC,KAAA,KAAU;AACpE,QAAA,MAAM,QAAA,GAAW,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,MAAM,KAAK,CAAA;AAC9C,QAAA,IAAI,UAAU,OAAO,CAAC,EAAE,KAAA,EAAO,UAAU,CAAA;AACzC,QAAA,eAAA,CAAgB,IAAA,CAAK;AAAA,UACnB,QAAA,EAAU,CAAA,SAAA,EAAY,KAAA,CAAM,KAAK,CAAA,CAAA,CAAA;AAAA,UACjC,GAAI,MAAM,IAAA,GAAO,EAAE,MAAM,KAAA,CAAM,IAAA,KAAS,EAAC;AAAA,UACzC,IAAA,EAAM,yBAAA;AAAA,UACN,OAAA,EAAS,CAAA,2BAAA,EAA8B,KAAA,CAAM,IAAA,IAAQ,MAAM,KAAK,CAAA,eAAA,CAAA;AAAA,UAChE,GAAA,EACE,MAAM,QAAA,CAAS,GAAA,CAAI,YAAY,KAAA,CAAM,KAAK,GAAG,CAAA,IAC7C,sDAAA;AAAA,UACF,GAAA,EAAK;AAAA,SACN,CAAA;AACD,QAAA,OAAO,EAAC;AAAA,MACV,CAAC,CAAA;AAGD,MAAA,KAAA,MAAW,EAAE,OAAM,IAAK,QAAA;AACtB,QAAA,KAAA,MAAW,EAAA,IAAM,KAAA,CAAM,QAAA,EAAU,MAAA,CAAO,IAAI,EAAE,CAAA;AAMhD,MAAA,MAAM,UAAA,uBAAiB,GAAA,EAGrB;AACF,MAAA,KAAA,MAAW,MAAA,IAAU,CAAC,GAAG,WAAA,EAAa,GAAG,OAAO,CAAA;AAC9C,QAAA,UAAA,CAAW,GAAA,CAAI,MAAA,CAAO,KAAA,CAAM,QAAA,EAAU,MAAM,CAAA;AAC9C,MAAA,MAAM,OAAA,GAAU,CAAC,GAAG,UAAA,CAAW,QAAQ,CAAA;AAOvC,MAAA,MAAM,OAAA,uBAAc,GAAA,CAAI;AAAA,QACtB,GAAG,WAAW,IAAA,EAAK;AAAA,QACnB,GAAG,SAAS,OAAA,CAAQ,CAAC,EAAE,KAAA,EAAM,KAAM,MAAM,QAAQ;AAAA,OAClD,CAAA;AACD,MAAA,KAAA,MAAW,EAAE,KAAA,EAAO,MAAA,EAAO,IAAK,kBAAA,CAAmB,KAAK,CAAA,EAAG;AACzD,QAAA,IAAI,UAAU,KAAA,EAAO;AACrB,QAAA,IAAI,QAAA,GAAW,KAAA;AACf,QAAA,KAAA,CAAM,QAAA,CAAS,CAAC,IAAA,KAAS;AACvB,UAAA,IAAI,QAAQ,GAAA,CAAI,IAAA,CAAK,QAAA,CAAS,SAAmB,GAAG,QAAA,GAAW,IAAA;AAAA,QACjE,CAAC,CAAA;AACD,QAAA,IAAI,CAAC,QAAA,EAAU;AACf,QAAA,MAAA,CAAO,OAAA,EAAQ;AACf,QAAA,cAAA,CAAe,KAAK,MAAM,CAAA;AAAA,MAC5B;AAEA,MAAA,MAAM,QAAQ,UAAA,CAAW;AAAA,QACvB,KAAA;AAAA,QACA,OAAA,EAAS,OAAA;AAAA,QACT,SAAA;AAAA,QACA,OAAA;AAAA,QACA,GAAI,KAAA,GACA;AAAA,UACE,KAAA,EAAO;AAAA,YACL,OAAA,EAAS,KAAA;AAAA,YACT,MAAA,EAAQ,YAAA,CAAa,OAAA,CAAQ,UAAA,IAAc,MAAM;AAAA;AACnD,YAEF,EAAC;AAAA,QACL,GAAI,QAAA,CAAS,MAAA,GAAS,EAAE,OAAA,EAAS,QAAA,KAAa,EAAC;AAAA,QAC/C,GAAI,UAAA,CAAW,MAAA,CAAO,MAAA,IAAU,MAAM,MAAA,GAClC;AAAA,UACE,MAAA,EAAQ;AAAA,YACN,MAAA,EAAQ,WAAW,MAAA,CAAO,MAAA;AAAA,YAC1B,SAAS,KAAA,CAAM;AAAA;AACjB,YAEF;AAAC,OACN,CAAA;AACD,MAAA,KAAA,CAAM,MAAM,EAAA,EAAG;AACf,MAAA,KAAA,GAAQ,KAAA,CAAM,KAAA;AACd,MAAA,OAAA,GAAU,KAAA;AAEV,MAAA,MAAM,QAAA,GAAW,IAAI,GAAA,CAAI,MAAM,CAAA;AAC/B,MAAA,KAAA,MAAW,QAAQ,UAAA,CAAW,MAAA,CAAO,SAAS,QAAA,CAAS,GAAA,CAAI,KAAK,QAAQ,CAAA;AACxE,MAAA,MAAM,OAAA,GAAU,OAAA,CACb,UAAA,EAAW,CACX,MAAA;AAAA,QACC,CAAC,SAAA,KACC,SAAA,CAAU,QAAA,IACV,SAAA,CAAU,kBACV,CAAC,QAAA,CAAS,GAAA,CAAI,SAAA,CAAU,QAAQ;AAAA,OACpC,CACC,GAAA,CAAI,CAAC,SAAA,KAAc,UAAU,QAAQ,CAAA;AAExC,MAAA,UAAA,GAAa;AAAA,QACX,UAAA,EAAY,KAAA;AAAA,QACZ,SAAS,KAAA,CAAM,OAAA;AAAA,QACf,WAAW,KAAA,CAAM,SAAA;AAAA,QACjB,OAAA;AAAA,QACA,SAAS,CAAC,GAAG,eAAA,EAAiB,GAAG,MAAM,OAAO,CAAA;AAAA,QAC9C,QAAQ,KAAA,CAAM,MAAA;AAAA,QACd,UAAU,KAAA,CAAM,QAAA;AAAA,QAChB,UAAU,KAAA,CAAM,QAAA;AAAA,QAChB,QAAQ,KAAA,CAAM,MAAA;AAAA,QACd,GAAI,MAAM,IAAA,GACN;AAAA,UACE,IAAA,EAAM;AAAA,YACJ,IAAA,EAAM,UAAA,CAAW,MAAA,CAAO,IAAA,CAAM,IAAA;AAAA,YAC9B,QAAA,EAAU,UAAA,CAAW,MAAA,CAAO,IAAA,CAAM,QAAA;AAAA,YAClC,SAAS,WAAA,CAAY,MAAA;AAAA,cAAO,CAAC,EAAE,KAAA,EAAM,KACnC,MAAM,OAAA,CAAQ,QAAA,CAAS,MAAM,QAAQ;AAAA,aACvC,CAAE,MAAA;AAAA,YACF,WAAW,WAAA,CAAY,MAAA;AAAA,cAAO,CAAC,EAAE,KAAA,EAAM,KACrC,MAAM,SAAA,CAAU,QAAA,CAAS,MAAM,QAAQ;AAAA,aACzC,CAAE;AAAA;AACJ,YAEF,EAAC;AAAA,QACL,GAAI,MAAM,UAAA,GAAa,EAAE,WAAW,KAAA,CAAM,UAAA,KAAe,EAAC;AAAA,QAC1D,GAAI,YAAY,KAAA,CAAM,WAAA,GAClB,EAAE,UAAA,EAAY,KAAA,CAAM,WAAA,EAAY,GAChC;AAAC,OACP;AACA,MAAA,KAAA,CAAM,KAAK,CAAA;AACX,MAAA,IAAA,EAAK;AACL,MAAA,OAAO,UAAA;AAAA,IACT,CAAA;AAAA,IAEA,MAAA,GAAS;AACP,MAAA,YAAA,EAAa;AACb,MAAA,OAAA,GAAU,IAAA;AACV,MAAA,UAAA,GAAa,YAAY,IAAI,CAAA;AAC7B,MAAA,IAAA,EAAK;AAAA,IACP,CAAA;AAAA,IAEA,QAAQ,QAAA,EAAU;AAChB,MAAA,IAAI,CAAC,KAAA,EAAO;AACZ,MAAA,IAAI,QAAA,QAAgB,GAAA,EAAI;AAAA,iBACb,EAAA,EAAG;AACd,MAAA,SAAA,GAAY,QAAA;AACZ,MAAA,IAAA,EAAK;AAAA,IACP,CAAA;AAAA,IAEA,MAAM,SAAS,EAAA,EAAI;AACjB,MAAA,IAAI,QAAA,EAAU,MAAM,aAAA,CAAc,UAAU,CAAA;AAC5C,MAAA,IAAI,EAAA,KAAO,QAAA,EAAU,OAAO,UAAA,IAAc,YAAY,OAAO,CAAA;AAC7D,MAAA,QAAA,GAAW,EAAA;AAGX,MAAA,IAAI,YAAY,IAAA,EAAM;AACpB,QAAA,IAAA,EAAK;AACL,QAAA,OAAO,UAAA,IAAc,YAAY,IAAI,CAAA;AAAA,MACvC;AACA,MAAA,OAAO,OAAA,CAAQ,MAAM,OAAO,CAAA;AAAA,IAC9B,CAAA;AAAA,IAEA,WAAW,EAAA,EAAI;AACb,MAAA,IAAI,EAAA,MAAQ,UAAU,IAAA,CAAA,EAAO;AAC7B,MAAA,IAAI,EAAA,EAAI;AACN,QAAA,MAAM,UAAsD,EAAC;AAC7D,QAAA,KAAA,CAAM,QAAA,CAAS,CAAC,MAAA,KAAW;AACzB,UAAA,MAAM,KAAA,GAAQ,MAAA;AACd,UAAA,IAAI,CAAC,MAAM,OAAA,EAAS;AACpB,UAAA,OAAA,CAAQ,KAAK,EAAE,KAAA,EAAO,SAAA,EAAW,KAAA,CAAM,WAAW,CAAA;AAClD,UAAA,KAAA,CAAM,SAAA,GAAY,CAAA;AAAA,QACpB,CAAC,CAAA;AACD,QAAA,KAAA,GAAQ,OAAA;AAAA,MACV,CAAA,MAAO;AACL,QAAA,KAAA,MAAW,KAAA,IAAS,KAAA,EAAQ,KAAA,CAAM,KAAA,CAAM,YAAY,KAAA,CAAM,SAAA;AAC1D,QAAA,KAAA,GAAQ,IAAA;AAAA,MACV;AACA,MAAA,IAAA,EAAK;AAAA,IACP,CAAA;AAAA,IAEA,EAAA,CAAG,QAAQ,QAAA,EAAU;AACnB,MAAA,SAAA,CAAU,IAAI,QAAQ,CAAA;AACtB,MAAA,OAAO,MAAM,SAAA,CAAU,MAAA,CAAO,QAAQ,CAAA;AAAA,IACxC,CAAA;AAAA,IAEA,OAAA,GAAU;AACR,MAAA,IAAI,QAAA,EAAU;AACd,MAAA,QAAA,GAAW,IAAA;AACX,MAAA,YAAA,EAAa;AACb,MAAA,IAAI,KAAA,EAAO;AACT,QAAA,KAAA,MAAW,KAAA,IAAS,KAAA,EAAO,KAAA,CAAM,KAAA,CAAM,YAAY,KAAA,CAAM,SAAA;AACzD,QAAA,KAAA,GAAQ,IAAA;AAAA,MACV;AACA,MAAA,KAAA,MAAW,KAAA,IAAS,CAAC,GAAG,MAAA,CAAO,MAAM,CAAA,oBAAqB,KAAK,CAAA;AAC/D,MAAA,OAAA,GAAU,IAAA;AACV,MAAA,SAAA,CAAU,KAAA,EAAM;AAAA,IAClB;AAAA,GACF;AAEA,EAAA,OAAO,OAAA;AACT;AAEA,SAAS,YAAY,UAAA,EAAqC;AACxD,EAAA,OAAO;AAAA,IACL,UAAA;AAAA,IACA,SAAS,EAAC;AAAA,IACV,WAAW,EAAC;AAAA,IACZ,SAAS,EAAC;AAAA,IACV,SAAS,EAAC;AAAA,IACV,QAAQ,EAAC;AAAA,IACT,UAAU,EAAC;AAAA,IACX,UAAU,EAAC;AAAA,IACX,MAAA,EAAQ;AAAA,GACV;AACF;AAGA,SAAS,SAAS,KAAA,EAAwB;AACxC,EAAA,IAAI,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,IAAY,qBAAqB,KAAA,EAAO;AACpE,IAAA,OAAQ,MAAwC,eAAA,EAAgB;AAAA,EAClE;AACA,EAAA,OAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK,CAAA;AAC9D;AAEA,SAAS,cAAc,IAAA,EAA4B;AACjD,EAAA,OAAO,IAAI,YAAA,CAAa;AAAA,IACtB,IAAA,EAAM,gBAAA;AAAA,IACN,OAAA,EAAS,GAAG,IAAI,CAAA,yCAAA,CAAA;AAAA,IAChB,GAAA,EAAK,yFAAA;AAAA,IACL,GAAA,EAAK;AAAA,GACN,CAAA;AACH","file":"chunk-TGFNQBKC.js","sourcesContent":["/**\n * A lightmap URI becomes a `THREE.Texture`, with the settings that make it\n * land the right way up on the right UV set.\n *\n * Orientation is a contract, not a guess: the render worker encodes the flip\n * at bake time, so the pixels and the UV values it ships are already correct\n * for `EXRLoader`'s defaults. This module changes nothing: `flipY = false` on\n * both the EXR and the PNG path, so an LDR fallback lands identically to the\n * HDR one.\n *\n * The bake's unwrap is a second UV set (your own `uv` still maps your\n * albedo). In three the second set is the `uv1` attribute, and\n * `texture.channel = 1` is how a map selects it, under WebGLRenderer and\n * WebGPURenderer alike.\n *\n * No mipmaps: a lightmap atlas is islands separated by a dilation margin, and\n * mipping it averages across island boundaries, which shows as light leaking\n * round the silhouette of every object at distance. Linear filtering with no\n * mips is the right trade for a map that is already low-frequency.\n */\n\nimport {\n ClampToEdgeWrapping,\n EquirectangularReflectionMapping,\n LinearFilter,\n LinearSRGBColorSpace,\n NoColorSpace,\n SRGBColorSpace,\n TextureLoader,\n type Texture,\n} from 'three';\nimport { EXRLoader } from 'three/examples/jsm/loaders/EXRLoader.js';\nimport { configureShadow } from './shadow.js';\nimport { Bakery3Error } from '../errors.js';\n\nexport type BakeTextureSource = {\n /** Already resolved against the bundle's own location. */\n url: string;\n format: 'exr' | 'png';\n /** For error messages: which object's lightmap this is. */\n objectId: string;\n size: number;\n /** What the texture is for. A lightmap (the default) is configured by\n * `configureLightmap`; the reflection probe by `configureProbe`. A factory\n * of your own may ignore this: the session applies the probe's mapping\n * itself either way. */\n role?: 'lightmap' | 'probe' | 'color' | 'normal' | 'billboard' | 'shadow';\n};\n\n/**\n * How a lightmap URI becomes a texture.\n *\n * Injectable for two reasons: a test wants a `DataTexture` and no\n * network, and an application that already has a loading manager, a CDN\n * rewrite or an auth header wants its own fetch in the loop.\n */\nexport type BakeTextureFactory = (\n source: BakeTextureSource,\n) => Promise<Texture>;\n\n/**\n * Apply the settings above to a texture from any source.\n *\n * Exported because a caller supplying their own `BakeTextureFactory` still\n * wants the channel, the color space and the filtering to be the ones the\n * bake was authored against: those are part of the format, not of the\n * loader.\n */\nexport function configureLightmap(\n texture: Texture,\n format: 'exr' | 'png',\n): Texture {\n texture.channel = 1;\n texture.flipY = false;\n // EXR is linear HDR irradiance and must not be decoded. A PNG lightmap is\n // the LDR fallback and is sRGB-encoded; three then decodes it to linear\n // before multiplying, which is what keeps the two formats interchangeable.\n texture.colorSpace = format === 'exr' ? LinearSRGBColorSpace : SRGBColorSpace;\n texture.generateMipmaps = false;\n texture.minFilter = LinearFilter;\n texture.magFilter = LinearFilter;\n texture.wrapS = ClampToEdgeWrapping;\n texture.wrapT = ClampToEdgeWrapping;\n texture.needsUpdate = true;\n return texture;\n}\n\n/**\n * The reflection probe as an environment map: three's equirect mapping, in\n * linear light, as the render worker wrote it (three's own equirect frame,\n * so no rotation). Everything else (the loader's flip, filtering, mips) is\n * left as `EXRLoader` sets it, which is what an HDRI loaded for\n * `scene.environment` gets.\n */\nexport function configureProbe(texture: Texture): Texture {\n texture.mapping = EquirectangularReflectionMapping;\n texture.colorSpace = LinearSRGBColorSpace;\n texture.needsUpdate = true;\n return texture;\n}\n\n/**\n * A stand-in's own maps (see proxies.ts and `BakedProxy`).\n *\n * `color` and `normal` are ordinary PNGs, the right way up for an ordinary\n * image loader, on the stand-in's first uv set; the color is sRGB and the\n * normals are data. They keep their mipmaps: a bookcase of spines shimmers\n * without them, and the unwrap's margin is what keeps a mip from reaching\n * across islands. `billboard` is linear radiance with alpha, left as\n * `EXRLoader` hands it over apart from the clamp.\n */\nexport function configureStandInMap(\n texture: Texture,\n role: 'color' | 'normal' | 'billboard',\n): Texture {\n texture.channel = 0;\n if (role === 'billboard') {\n texture.colorSpace = LinearSRGBColorSpace;\n texture.generateMipmaps = false;\n texture.minFilter = LinearFilter;\n texture.magFilter = LinearFilter;\n } else {\n texture.flipY = true;\n texture.colorSpace = role === 'color' ? SRGBColorSpace : NoColorSpace;\n }\n texture.wrapS = ClampToEdgeWrapping;\n texture.wrapT = ClampToEdgeWrapping;\n texture.needsUpdate = true;\n return texture;\n}\n\n/**\n * The default loader: `EXRLoader` for HDR, `TextureLoader` for the PNG\n * fallback.\n *\n * A failure here is a designed error naming the object and the URL, because\n * \"the bake applied to 11 of 12 objects\" is a question the developer must be\n * able to answer without opening the network tab.\n */\nexport function defaultTextureFactory(): BakeTextureFactory {\n const exr = new EXRLoader();\n const png = new TextureLoader();\n // Both loaders are asked for cross-origin data by default; a bundle served\n // from a CDN on another origin is the normal case, not the exception.\n exr.setCrossOrigin('anonymous');\n png.setCrossOrigin('anonymous');\n\n return async (source) => {\n const loader = source.format === 'exr' ? exr : png;\n const texture = await new Promise<Texture>((resolve, reject) => {\n loader.load(\n source.url,\n (loaded) => resolve(loaded as Texture),\n undefined,\n () =>\n reject(\n new Bakery3Error({\n code: 'InvalidRequest',\n message:\n source.role === 'probe'\n ? 'Could not load the reflection probe.'\n : source.role === 'color' ||\n source.role === 'normal' ||\n source.role === 'billboard'\n ? `Could not load the ${source.role} map of \"${source.objectId}\".`\n : `Could not load the lightmap for \"${source.objectId}\".`,\n why: `${source.url} did not load. A bundle's uris are relative to where the bundle came from, and the artifacts have to be reachable from the browser.`,\n fix: 'Pass { baseUrl } to addGeneration() if the bundle moved, and check the artifact server allows cross-origin reads.',\n }),\n ),\n );\n });\n if (source.role === 'probe') return configureProbe(texture);\n if (source.role === 'shadow') return configureShadow(texture);\n if (\n source.role === 'color' ||\n source.role === 'normal' ||\n source.role === 'billboard'\n ) {\n return configureStandInMap(texture, source.role);\n }\n return configureLightmap(texture, source.format);\n };\n}\n\n/**\n * A bundle uri, resolved against where the bundle came from.\n *\n * A uri is left alone when it already names its own origin (`https:`,\n * `data:`, `blob:`, `//host`): a hosted bundle carries signed URLs. `baseUrl`\n * is for a bundle saved to disk and served from somewhere else later.\n */\nexport function resolveBundleUri(uri: string, baseUrl?: string): string {\n if (/^[a-z][a-z0-9+.-]*:/i.test(uri) || uri.startsWith('//')) return uri;\n if (!baseUrl) return uri;\n try {\n return new URL(uri, baseUrl).toString();\n } catch {\n // A baseUrl that is not a URL is a caller mistake, but failing the whole\n // apply over it would be worse than trying the raw uri.\n return uri;\n }\n}\n","/**\n * A bake session: generations of lighting you can step through, over one\n * live scene.\n *\n * The session is the thing a panel binds to. It owns three pieces of state\n * and nothing else: which bundles it knows about, which one is currently\n * applied (`-1` means \"your scene, untouched\"), and whether the realtime\n * lights are muted. Everything visible follows from those.\n *\n * Generations do not stack: `apply(i)` reverts to live first, every time.\n * Applying generation 2 over generation 1 would leave generation 1's cloned\n * materials and rebuilt geometries in the scene under generation 2's\n * textures, and the second revert would restore the first bake rather than\n * your scene.\n *\n * Generations are evicted: twelve objects at 2048² of half-float RGBA is\n * around 400 MB of VRAM per generation, so only the applied generation and\n * the most recent few keep their textures. The rest are disposed and\n * reloaded on demand.\n */\n\nimport type { Light, Object3D, Scene, Texture } from 'three';\nimport type { BakeBundle, BakedObject } from '../protocol/index.js';\nimport { Bakery3Error } from '../errors.js';\nimport { bakeCandidates, type BakeCandidate } from './candidates.js';\nimport { buildPatch, type BakeApplySkip, type BakePatch } from './apply.js';\nimport { probeTargets } from './probe.js';\nimport type { ProxyTextures } from './proxies.js';\nimport { configureShadow } from './shadow.js';\nimport { presetBaseUrl } from '../presets/baseUrl.js';\nimport { prebakedRoomsUnder } from '../presets/roomPrebake.js';\nimport { PRESETS_VERSION } from '../presets/catalog.gen.js';\nimport type { BakeShading } from './shading.js';\nimport {\n configureProbe,\n defaultTextureFactory,\n resolveBundleUri,\n type BakeTextureFactory,\n} from './textures.js';\n\n/** Your own scene, untouched: always a generation you can go back to. */\n/** No generation applied: your scene, untouched. */\nexport const LIVE = -1;\n\nexport type BakeGeneration = {\n /** Stable index into `session.generations`; generations are append-only. */\n index: number;\n /** What the stepper shows: `1024 · standard` unless the host names it. */\n label: string;\n bundle: BakeBundle;\n baseUrl?: string;\n /** When `addGeneration` was called, for \"2m ago\". */\n createdAt: number;\n /** True while this generation's textures are resident. */\n loaded: boolean;\n};\n\n/**\n * What one `apply()` actually managed to do.\n *\n * Reported rather than thrown, because a generation that no longer perfectly\n * matches the scene is the normal case the moment you edit anything, and\n * \"applied 10 of 12, here are the other two\" is more useful than either a\n * failure or a lie.\n */\nexport type BakeApplyReport = {\n /** The generation this report is about, or -1 for your scene untouched. */\n generation: number;\n /** objectIds whose lightmap landed. */\n applied: string[];\n /** Baked objects with nothing in the scene answering to their id. */\n unmatched: string[];\n /** Bakeable meshes in the scene this generation has no lightmap for. */\n unbaked: string[];\n /** Objects the apply refused, each with a reason and a fix. */\n skipped: BakeApplySkip[];\n /** objectIds that received the bake's reflection probe as `envMap`: the\n * meshes the bake did not light (the product, the glass beside it). */\n probed: string[];\n /** Stand-ins that went into the scene, by their place in the request. */\n standIns: number[];\n /** objectIds out of sight because a stand-in took their place. */\n replaced: string[];\n /** Whether the bake's shadow plane is in the scene. */\n shadow: boolean;\n /**\n * The prebaked preset room applied under this generation, and how many of\n * its surfaces found a mesh. A bake in `room: 'preset'` mode covers only\n * the product; this is where the rest of the light came from.\n */\n room?: { name: string; lighting: string; applied: number; unmatched: number };\n /** Why the prebaked room is not on: it was wanted and did not load. The\n * product's own lightmaps applied regardless. */\n roomError?: string;\n /** Why the bundle's probe is on nothing: it was wanted and did not load.\n * The lightmaps applied regardless: a probe costs the product its\n * reflections of the room and nothing else. */\n probeError?: string;\n};\n\nexport type BakeSessionOptions = {\n scene: Scene | Object3D;\n /**\n * Where the preset library lives, for a bundle that stands its objects in\n * a prebaked room (`BakeBundle.room`). Defaults to the CDN the SDK ships\n * with; a test points it somewhere else.\n */\n presetBaseUrl?: string;\n /**\n * How a lightmap uri becomes a texture. Defaults to EXRLoader/TextureLoader\n * with the channel, color space and filtering the format requires.\n * Injected by tests, and by applications that need their own fetch.\n */\n textureFactory?: BakeTextureFactory;\n /** `lightMapIntensity` to write. Default 1: the bake is already in the\n * scene's own light units. */\n intensity?: number;\n /**\n * How the lightmap meets the lights that keep running. `'replace'` (the\n * default) makes it the only diffuse lighting while lamps and the\n * environment still supply specular, the combination that matches the\n * traced still. `'add'` is three's stock behavior, for a bake that\n * deliberately holds only part of the light. See shading.ts.\n */\n shading?: BakeShading;\n /**\n * Put the bake's reflection probe on the objects it did not light, as\n * `envMap`, so a product kept live in a baked room reflects that room\n * rather than the scene's own environment. Default `true`; `false` leaves\n * every material's environment as you set it. See probe.ts.\n */\n probe?: boolean;\n /** How many generations keep their textures. Default 3. */\n residentGenerations?: number;\n};\n\nexport type BakeSession = {\n readonly generations: readonly BakeGeneration[];\n /** Index of the applied generation, or -1 when none is (see `revert()`). */\n readonly current: number;\n /** The last apply's report, for the panel's \"what didn't match\" line. */\n readonly lastReport: BakeApplyReport | null;\n /** True while `compare(true)` is showing the live scene under an applied\n * generation: the applied state is still there, just switched off. */\n readonly comparing: boolean;\n readonly lightsMuted: boolean;\n /** Whether an applied bake puts its reflection probe on the objects it did\n * not light. The `probe` option, until `setProbe` changes it. */\n readonly probe: boolean;\n\n /** Which meshes could be baked, and why the others could not. */\n candidates(): BakeCandidate[];\n\n /** Remember a bundle. Textures are not fetched until it is applied. */\n addGeneration(\n bundle: BakeBundle,\n options?: { baseUrl?: string; label?: string },\n ): number;\n\n /** Revert to live, then apply generation `index`. `revert()` goes back to live. */\n apply(index: number): Promise<BakeApplyReport>;\n /** Back to your scene, exactly. */\n revert(): void;\n /** Hold-to-compare: `true` shows live, `false` puts the bake back. Neither\n * loses the applied patch, so both are instant and lossless. */\n compare(showLive: boolean): void;\n /**\n * The reflection probe on or off. With a generation applied this re-applies\n * it, so the change shows at once and the report says which objects carry\n * the probe; turning it on for the first time fetches the file.\n */\n setProbe(on: boolean): Promise<BakeApplyReport>;\n /**\n * Realtime lights to zero and back. Under the default `'replace'` shading\n * the lamps only add specular, so this shows the bake's diffuse on its own;\n * under `'add'` it is what stops the lamps being counted twice.\n */\n muteLights(on: boolean): void;\n\n on(event: 'change', listener: (session: BakeSession) => void): () => void;\n /** Revert, drop every texture, restore the lights. */\n dispose(): void;\n};\n\ntype LoadedGeneration = {\n textures: Map<string, Texture>;\n /** objectId → why its lightmap did not load. One unreachable artifact\n * costs one object, not the whole apply. */\n failures: Map<string, string>;\n /** Stand-in index → its maps. One that did not load is in `failures`\n * under `simplify[n]` and simply does not go in. */\n proxies: Map<number, ProxyTextures>;\n /** The shadow plane's ratio map. */\n shadow?: Texture;\n /** The prebaked room's own bundle and textures, fetched once. */\n room?: {\n bundle: BakeBundle;\n baseUrl: string;\n textures: Map<string, Texture>;\n };\n roomFailed?: string;\n /** For LRU eviction. */\n usedAt: number;\n /** The bundle's reflection probe, as an equirect environment map. */\n probe?: Texture;\n /** It was asked for and did not load; not asked for again. The reason,\n * for the report. */\n probeFailed?: string;\n};\n\nexport function createBakeSession(options: BakeSessionOptions): BakeSession {\n const scene = options.scene;\n const intensity = options.intensity ?? 1;\n const shading: BakeShading = options.shading ?? 'replace';\n const resident = Math.max(1, options.residentGenerations ?? 3);\n let useProbe = options.probe ?? true;\n const loadTexture = options.textureFactory ?? defaultTextureFactory();\n\n const generations: BakeGeneration[] = [];\n const loaded = new Map<number, LoadedGeneration>();\n const listeners = new Set<(session: BakeSession) => void>();\n\n let current = LIVE;\n let patch: BakePatch | null = null;\n let lastReport: BakeApplyReport | null = null;\n let comparing = false;\n let muted: Array<{ light: Light; intensity: number }> | null = null;\n let disposed = false;\n /** Guards against two applies racing over one scene: the later one wins and\n * the earlier one drops its result rather than half-applying under it. */\n let applyToken = 0;\n let clock = 0;\n /** Preset rooms lit by their own library lightmaps that the applied\n * generation lights over, switched off until it is released. */\n let suspendedRooms: Array<{ resume(): void }> = [];\n\n function emit() {\n for (const listener of listeners) listener(session);\n }\n\n function releasePatch() {\n patch?.dispose();\n patch = null;\n comparing = false;\n // After the patch is gone, so the room's own lightmaps go back on over\n // the materials they were first put on (see presets/roomPrebake.ts).\n for (const room of suspendedRooms) room.resume();\n suspendedRooms = [];\n }\n\n function disposeGeneration(index: number) {\n const entry = loaded.get(index);\n if (!entry) return;\n // Unique textures only: an atlas is one Texture shared by every object.\n for (const texture of new Set(entry.textures.values())) texture.dispose();\n for (const texture of new Set(entry.room?.textures.values() ?? []))\n texture.dispose();\n for (const maps of entry.proxies.values()) {\n if (maps.kind === 'billboard') maps.map.dispose();\n else if (maps.kind === 'decimated') maps.light.dispose();\n else\n for (const texture of [maps.color, maps.normal, maps.light])\n texture.dispose();\n }\n entry.probe?.dispose();\n entry.shadow?.dispose();\n loaded.delete(index);\n const generation = generations[index];\n if (generation) generation.loaded = false;\n }\n\n function evict(keep: number) {\n if (loaded.size <= resident) return;\n const ranked = [...loaded.entries()]\n .filter(([index]) => index !== keep)\n .sort((a, b) => a[1].usedAt - b[1].usedAt);\n while (loaded.size > resident && ranked.length) {\n disposeGeneration(ranked.shift()![0]);\n }\n }\n\n async function ensureLoaded(index: number): Promise<LoadedGeneration> {\n const existing = loaded.get(index);\n if (existing) {\n existing.usedAt = ++clock;\n return existing;\n }\n const generation = generations[index]!;\n const textures = new Map<string, Texture>();\n const failures = new Map<string, string>();\n // One texture per file: an atlas bundle names the same lightmap on every\n // object, and it must be fetched and decoded once and bound once, which\n // is the reason an atlas exists.\n const byUrl = new Map<string, Texture>();\n // Sequential rather than parallel: a twelve-object bundle at 2048² is a\n // lot of megabytes, and a browser that opens twelve HDR decodes at once\n // janks the frame you are watching.\n for (const baked of generation.bundle.objects) {\n try {\n const url = resolveBundleUri(baked.lightmap.uri, generation.baseUrl);\n let texture = byUrl.get(url);\n if (!texture) {\n texture = await loadTexture({\n url,\n format: baked.lightmap.format,\n objectId: baked.objectId,\n size: baked.lightmap.size,\n });\n byUrl.set(url, texture);\n }\n textures.set(baked.objectId, texture);\n } catch (error) {\n // One unreachable artifact must not cost the other eleven objects\n // their lighting: it becomes a per-object skip with the reason\n // instead.\n failures.set(baked.objectId, describe(error));\n }\n }\n const proxies = new Map<number, ProxyTextures>();\n for (const proxy of generation.bundle.proxies ?? []) {\n const label = proxy.name ?? `stand-in ${proxy.index}`;\n const load = (\n uri: string,\n format: 'exr' | 'png',\n role: 'color' | 'normal' | 'billboard' | 'lightmap',\n size: number,\n ) =>\n loadTexture({\n url: resolveBundleUri(uri, generation.baseUrl),\n format,\n objectId: label,\n size,\n role,\n });\n try {\n if (proxy.kind === 'billboard') {\n proxies.set(proxy.index, {\n kind: 'billboard',\n map: await load(proxy.map.uri, 'exr', 'billboard', proxy.map.width),\n });\n } else if (proxy.kind === 'decimated') {\n proxies.set(proxy.index, {\n kind: 'decimated',\n light: await load(\n proxy.lightmap.uri,\n 'exr',\n 'lightmap',\n proxy.lightmap.size,\n ),\n });\n } else {\n const size = proxy.lightmap.size;\n proxies.set(proxy.index, {\n kind: 'mesh',\n color: await load(proxy.color.uri, 'png', 'color', size),\n normal: await load(proxy.normal.uri, 'png', 'normal', size),\n light: await load(proxy.lightmap.uri, 'exr', 'lightmap', size),\n });\n }\n } catch (error) {\n failures.set(`simplify[${proxy.index}]`, describe(error));\n }\n }\n let shadow: Texture | undefined;\n if (generation.bundle.shadow) {\n const shadowEntry = generation.bundle.shadow;\n try {\n shadow = configureShadow(\n await loadTexture({\n url: resolveBundleUri(shadowEntry.uri, generation.baseUrl),\n format: shadowEntry.format,\n objectId: 'shadow',\n size: shadowEntry.size,\n role: 'shadow',\n }),\n );\n } catch (error) {\n // A missing shadow costs the product its contact with the floor and\n // nothing else, so it is a note on the report, not a failed apply.\n failures.set('shadow', describe(error));\n }\n }\n // The prebaked room, when the bundle stands its objects in one. One\n // fetch for a library asset every page that shows this room shares, and\n // the reason a bundle of three objects can light a whole room.\n let room: LoadedGeneration['room'];\n let roomFailed: string | undefined;\n const wantsRoom = generation.bundle.room;\n if (wantsRoom) {\n const base = `${(options.presetBaseUrl ?? presetBaseUrl()).replace(/\\/$/, '')}/v${PRESETS_VERSION}/`;\n const url = new URL(wantsRoom.uri, base).toString();\n try {\n const response = await fetch(url);\n if (!response.ok)\n throw new Error(`${response.status} ${response.statusText}`);\n const bundle = (await response.json()) as BakeBundle;\n const roomTextures = new Map<string, Texture>();\n const byUrl = new Map<string, Texture>();\n for (const baked of bundle.objects) {\n const textureUrl = resolveBundleUri(baked.lightmap.uri, url);\n let texture = byUrl.get(textureUrl);\n if (!texture) {\n texture = await loadTexture({\n url: textureUrl,\n format: baked.lightmap.format,\n objectId: baked.objectId,\n size: baked.lightmap.size,\n });\n byUrl.set(textureUrl, texture);\n }\n roomTextures.set(baked.objectId, texture);\n }\n room = { bundle, baseUrl: url, textures: roomTextures };\n } catch (error) {\n // The product's own lightmaps still apply. What is lost is the room\n // around them, which is worth saying and not worth failing over.\n roomFailed = describe(error);\n }\n }\n\n const entry: LoadedGeneration = {\n textures,\n failures,\n proxies,\n usedAt: ++clock,\n ...(shadow ? { shadow } : {}),\n ...(room ? { room } : {}),\n ...(roomFailed ? { roomFailed } : {}),\n };\n loaded.set(index, entry);\n generation.loaded = true;\n return entry;\n }\n\n /**\n * The reflection probe, fetched the first time it is wanted, so a session\n * created with `probe: false` never downloads it, and one that turns it on\n * later (`setProbe`) gets it then. A probe that does not load costs the\n * product its reflections of the room and nothing else, so the failure is\n * remembered and the apply goes on without it.\n */\n async function ensureProbe(\n index: number,\n entry: LoadedGeneration,\n ): Promise<Texture | undefined> {\n if (entry.probe || entry.probeFailed) return entry.probe;\n const generation = generations[index]!;\n const probeEntry = generation.bundle.probe;\n if (!probeEntry) return undefined;\n try {\n entry.probe = configureProbe(\n await loadTexture({\n url: resolveBundleUri(probeEntry.uri, generation.baseUrl),\n format: probeEntry.format,\n objectId: 'probe',\n size: probeEntry.width,\n role: 'probe',\n }),\n );\n } catch (error) {\n entry.probeFailed = describe(error);\n }\n return entry.probe;\n }\n\n const session: BakeSession = {\n get generations() {\n return generations;\n },\n get current() {\n return current;\n },\n get lastReport() {\n return lastReport;\n },\n get comparing() {\n return comparing;\n },\n get lightsMuted() {\n return muted !== null;\n },\n get probe() {\n return useProbe;\n },\n\n candidates() {\n return bakeCandidates({ scene });\n },\n\n addGeneration(bundle, addOptions = {}) {\n if (disposed) throw disposedError('addGeneration');\n const index = generations.length;\n generations.push({\n index,\n label:\n addOptions.label ??\n `${bundle.settings.textureSize} · ${bundle.settings.quality}`,\n bundle,\n ...(addOptions.baseUrl !== undefined\n ? { baseUrl: addOptions.baseUrl }\n : {}),\n createdAt: Date.now(),\n loaded: false,\n });\n emit();\n return index;\n },\n\n async apply(index) {\n if (disposed) throw disposedError('apply');\n if (index === LIVE) {\n session.revert();\n return lastReport!;\n }\n const generation = generations[index];\n if (!generation) {\n throw new Bakery3Error({\n code: 'InvalidRequest',\n message: `There is no bake generation ${index}.`,\n why: `This session has ${generations.length} generation(s).`,\n fix: 'Call addGeneration(bundle) with a BakeBundle first, or revert() to go back to live.',\n });\n }\n\n const token = ++applyToken;\n const entry = await ensureLoaded(index);\n const probe = useProbe ? await ensureProbe(index, entry) : undefined;\n if (token !== applyToken || disposed)\n return lastReport ?? emptyReport(index);\n\n // Always revert first. See the file header.\n releasePatch();\n\n const objects: Array<{\n baked: (typeof generation.bundle.objects)[number];\n texture: Texture;\n }> = [];\n const missingTextures: BakeApplySkip[] = [];\n for (const baked of generation.bundle.objects) {\n const texture = entry.textures.get(baked.objectId);\n if (!texture) {\n missingTextures.push({\n objectId: baked.objectId,\n ...(baked.name ? { name: baked.name } : {}),\n code: 'BakeLightmapUnavailable',\n message: `The lightmap for \"${baked.name ?? baked.objectId}\" did not load.`,\n why:\n entry.failures.get(baked.objectId) ??\n 'Its texture is missing from this generation, so there is nothing to apply.',\n fix: 'Check the bundle uris are reachable from the browser, then apply again.',\n });\n continue;\n }\n objects.push({ baked, texture });\n }\n\n // The prebaked room's objects are baked too, just not by this bake.\n const roomObjects = (entry.room?.bundle.objects ?? []).flatMap(\n (baked) => {\n const texture = entry.room!.textures.get(baked.objectId);\n return texture ? [{ baked, texture }] : [];\n },\n );\n const litIds = new Set(\n generation.bundle.objects.map((object) => object.objectId),\n );\n for (const { baked } of roomObjects) litIds.add(baked.objectId);\n const standIns = (generation.bundle.proxies ?? []).flatMap((proxy) => {\n const textures = entry.proxies.get(proxy.index);\n if (textures) return [{ proxy, textures }];\n missingTextures.push({\n objectId: `simplify[${proxy.index}]`,\n ...(proxy.name ? { name: proxy.name } : {}),\n code: 'BakeLightmapUnavailable',\n message: `The maps for the stand-in \"${proxy.name ?? proxy.index}\" did not load.`,\n why:\n entry.failures.get(`simplify[${proxy.index}]`) ??\n 'One of its textures is missing from this generation.',\n fix: 'Check the bundle uris are reachable from the browser, then apply again. The objects it replaces are left as they were.',\n });\n return [];\n });\n // What a stand-in replaces is out of sight: it is neither unbaked nor\n // something to put the reflection probe on.\n for (const { proxy } of standIns)\n for (const id of proxy.replaces) litIds.add(id);\n // One entry per object. The room's lightmaps go in first and this\n // bake's overwrite them, because a stand the product sits on is in\n // both and only one of the two was traced with the product on it.\n // Applying an id twice would also make revert restore the wrong state:\n // the second touch records the first's edit as the value to put back.\n const byObjectId = new Map<\n string,\n { baked: BakedObject; texture: Texture }\n >();\n for (const entry_ of [...roomObjects, ...objects])\n byObjectId.set(entry_.baked.objectId, entry_);\n const winners = [...byObjectId.values()];\n\n // A preset room already lit by its own library lightmaps: when this\n // generation lights any of its objects, that patch comes off first, so\n // this one records the room's own materials as the thing to restore.\n // The room's session is scoped to its group, which is how it tells\n // itself apart from a session over the whole scene.\n const touched = new Set([\n ...byObjectId.keys(),\n ...standIns.flatMap(({ proxy }) => proxy.replaces),\n ]);\n for (const { group, handle } of prebakedRoomsUnder(scene)) {\n if (group === scene) continue;\n let lightsIt = false;\n group.traverse((node) => {\n if (touched.has(node.userData.bakery3Id as string)) lightsIt = true;\n });\n if (!lightsIt) continue;\n handle.suspend();\n suspendedRooms.push(handle);\n }\n\n const built = buildPatch({\n scene,\n objects: winners,\n intensity,\n shading,\n ...(probe\n ? {\n probe: {\n texture: probe,\n meshes: probeTargets(session.candidates(), litIds),\n },\n }\n : {}),\n ...(standIns.length ? { proxies: standIns } : {}),\n ...(generation.bundle.shadow && entry.shadow\n ? {\n shadow: {\n shadow: generation.bundle.shadow,\n texture: entry.shadow,\n },\n }\n : {}),\n });\n built.patch.on();\n patch = built.patch;\n current = index;\n\n const bakedIds = new Set(litIds);\n for (const skip of generation.bundle.skipped) bakedIds.add(skip.objectId);\n const unbaked = session\n .candidates()\n .filter(\n (candidate) =>\n candidate.bakeable &&\n candidate.defaultInclude &&\n !bakedIds.has(candidate.objectId),\n )\n .map((candidate) => candidate.objectId);\n\n lastReport = {\n generation: index,\n applied: built.applied,\n unmatched: built.unmatched,\n unbaked,\n skipped: [...missingTextures, ...built.skipped],\n probed: built.probed,\n standIns: built.standIns,\n replaced: built.replaced,\n shadow: built.shadow,\n ...(entry.room\n ? {\n room: {\n name: generation.bundle.room!.name,\n lighting: generation.bundle.room!.lighting,\n applied: roomObjects.filter(({ baked }) =>\n built.applied.includes(baked.objectId),\n ).length,\n unmatched: roomObjects.filter(({ baked }) =>\n built.unmatched.includes(baked.objectId),\n ).length,\n },\n }\n : {}),\n ...(entry.roomFailed ? { roomError: entry.roomFailed } : {}),\n ...(useProbe && entry.probeFailed\n ? { probeError: entry.probeFailed }\n : {}),\n };\n evict(index);\n emit();\n return lastReport;\n },\n\n revert() {\n releasePatch();\n current = LIVE;\n lastReport = emptyReport(LIVE);\n emit();\n },\n\n compare(showLive) {\n if (!patch) return;\n if (showLive) patch.off();\n else patch.on();\n comparing = showLive;\n emit();\n },\n\n async setProbe(on) {\n if (disposed) throw disposedError('setProbe');\n if (on === useProbe) return lastReport ?? emptyReport(current);\n useProbe = on;\n // The probe is part of the applied patch, so changing it is an apply:\n // revert, then the same generation with or without it.\n if (current === LIVE) {\n emit();\n return lastReport ?? emptyReport(LIVE);\n }\n return session.apply(current);\n },\n\n muteLights(on) {\n if (on === (muted !== null)) return;\n if (on) {\n const touched: Array<{ light: Light; intensity: number }> = [];\n scene.traverse((object) => {\n const light = object as Light;\n if (!light.isLight) return;\n touched.push({ light, intensity: light.intensity });\n light.intensity = 0;\n });\n muted = touched;\n } else {\n for (const entry of muted!) entry.light.intensity = entry.intensity;\n muted = null;\n }\n emit();\n },\n\n on(_event, listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n\n dispose() {\n if (disposed) return;\n disposed = true;\n releasePatch();\n if (muted) {\n for (const entry of muted) entry.light.intensity = entry.intensity;\n muted = null;\n }\n for (const index of [...loaded.keys()]) disposeGeneration(index);\n current = LIVE;\n listeners.clear();\n },\n };\n\n return session;\n}\n\nfunction emptyReport(generation: number): BakeApplyReport {\n return {\n generation,\n applied: [],\n unmatched: [],\n unbaked: [],\n skipped: [],\n probed: [],\n standIns: [],\n replaced: [],\n shadow: false,\n };\n}\n\n/** The designed failure shape when there is one, the message otherwise. */\nfunction describe(error: unknown): string {\n if (error && typeof error === 'object' && 'toDisplayString' in error) {\n return (error as { toDisplayString(): string }).toDisplayString();\n }\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction disposedError(what: string): Bakery3Error {\n return new Bakery3Error({\n code: 'InvalidRequest',\n message: `${what}() was called on a disposed bake session.`,\n why: 'dispose() reverts the scene and drops every texture, so there is nothing left to apply.',\n fix: 'Create a new session with createBakeSession({ scene }).',\n });\n}\n"]}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Bakery3Error } from './chunk-3G5QL4F4.js';
|
|
2
|
+
|
|
3
|
+
// src/capture/camera.ts
|
|
4
|
+
function captureCamera(camera) {
|
|
5
|
+
camera.updateMatrixWorld();
|
|
6
|
+
const position = [0, 0, 0];
|
|
7
|
+
const quaternion = [0, 0, 0, 1];
|
|
8
|
+
const elements = camera.matrixWorld.elements;
|
|
9
|
+
position[0] = elements[12];
|
|
10
|
+
position[1] = elements[13];
|
|
11
|
+
position[2] = elements[14];
|
|
12
|
+
const q = worldQuaternion(camera);
|
|
13
|
+
quaternion[0] = q[0];
|
|
14
|
+
quaternion[1] = q[1];
|
|
15
|
+
quaternion[2] = q[2];
|
|
16
|
+
quaternion[3] = q[3];
|
|
17
|
+
const anyCamera = camera;
|
|
18
|
+
if (anyCamera.isPerspectiveCamera) {
|
|
19
|
+
return {
|
|
20
|
+
type: "perspective",
|
|
21
|
+
fov: anyCamera.fov,
|
|
22
|
+
aspect: anyCamera.aspect,
|
|
23
|
+
near: anyCamera.near,
|
|
24
|
+
far: anyCamera.far,
|
|
25
|
+
zoom: anyCamera.zoom,
|
|
26
|
+
position,
|
|
27
|
+
quaternion,
|
|
28
|
+
...camera.name ? { name: camera.name } : {}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (anyCamera.isOrthographicCamera) {
|
|
32
|
+
return {
|
|
33
|
+
type: "orthographic",
|
|
34
|
+
left: anyCamera.left,
|
|
35
|
+
right: anyCamera.right,
|
|
36
|
+
top: anyCamera.top,
|
|
37
|
+
bottom: anyCamera.bottom,
|
|
38
|
+
near: anyCamera.near,
|
|
39
|
+
far: anyCamera.far,
|
|
40
|
+
zoom: anyCamera.zoom,
|
|
41
|
+
position,
|
|
42
|
+
quaternion,
|
|
43
|
+
...camera.name ? { name: camera.name } : {}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
throw new Bakery3Error({
|
|
47
|
+
code: "InvalidRequest",
|
|
48
|
+
message: `Unsupported camera type: ${camera.type}`,
|
|
49
|
+
why: "The renderer draws a pinhole or an orthographic projection. An array, cube or stereo camera has no single frustum to reproduce.",
|
|
50
|
+
fix: "Pass one PerspectiveCamera or OrthographicCamera to render().",
|
|
51
|
+
docs: "https://bakery3.com/docs/cameras"
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function worldQuaternion(object) {
|
|
55
|
+
const e = object.matrixWorld.elements;
|
|
56
|
+
const sx = Math.hypot(e[0], e[1], e[2]) || 1;
|
|
57
|
+
const sy = Math.hypot(e[4], e[5], e[6]) || 1;
|
|
58
|
+
const sz = Math.hypot(e[8], e[9], e[10]) || 1;
|
|
59
|
+
const m00 = e[0] / sx;
|
|
60
|
+
const m01 = e[4] / sy;
|
|
61
|
+
const m02 = e[8] / sz;
|
|
62
|
+
const m10 = e[1] / sx;
|
|
63
|
+
const m11 = e[5] / sy;
|
|
64
|
+
const m12 = e[9] / sz;
|
|
65
|
+
const m20 = e[2] / sx;
|
|
66
|
+
const m21 = e[6] / sy;
|
|
67
|
+
const m22 = e[10] / sz;
|
|
68
|
+
const trace = m00 + m11 + m22;
|
|
69
|
+
if (trace > 0) {
|
|
70
|
+
const s2 = 0.5 / Math.sqrt(trace + 1);
|
|
71
|
+
return [(m21 - m12) * s2, (m02 - m20) * s2, (m10 - m01) * s2, 0.25 / s2];
|
|
72
|
+
}
|
|
73
|
+
if (m00 > m11 && m00 > m22) {
|
|
74
|
+
const s2 = 2 * Math.sqrt(1 + m00 - m11 - m22);
|
|
75
|
+
return [0.25 * s2, (m01 + m10) / s2, (m02 + m20) / s2, (m21 - m12) / s2];
|
|
76
|
+
}
|
|
77
|
+
if (m11 > m22) {
|
|
78
|
+
const s2 = 2 * Math.sqrt(1 + m11 - m00 - m22);
|
|
79
|
+
return [(m01 + m10) / s2, 0.25 * s2, (m12 + m21) / s2, (m02 - m20) / s2];
|
|
80
|
+
}
|
|
81
|
+
const s = 2 * Math.sqrt(1 + m22 - m00 - m11);
|
|
82
|
+
return [(m02 + m20) / s, (m12 + m21) / s, 0.25 * s, (m10 - m01) / s];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export { captureCamera };
|
|
86
|
+
//# sourceMappingURL=chunk-V3QINEEG.js.map
|
|
87
|
+
//# sourceMappingURL=chunk-V3QINEEG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/capture/camera.ts"],"names":["s"],"mappings":"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","file":"chunk-V3QINEEG.js","sourcesContent":["/**\n * The active camera, in full.\n *\n * It is written out as data rather than referenced into the GLB by name\n * because the live camera is often not in the graph the exporter walks: R3F\n * holds the default camera outside the scene, drei's controls swap it, and a\n * configurator often animates a camera that was never added as a child of\n * anything.\n */\n\nimport type { Camera, OrthographicCamera, PerspectiveCamera } from 'three';\nimport type { CameraSpec, Quat, Vec3 } from '../protocol/index.js';\nimport { Bakery3Error } from '../errors.js';\n\nexport function captureCamera(camera: Camera): CameraSpec {\n // World, not local. A camera parented to a rig (an orbit pivot, a\n // turntable, a vehicle) has a local transform that means nothing on its\n // own, and that is exactly the case a configurator is in.\n camera.updateMatrixWorld();\n const position: Vec3 = [0, 0, 0];\n const quaternion: Quat = [0, 0, 0, 1];\n const elements = camera.matrixWorld.elements;\n position[0] = elements[12]!;\n position[1] = elements[13]!;\n position[2] = elements[14]!;\n const q = worldQuaternion(camera);\n quaternion[0] = q[0];\n quaternion[1] = q[1];\n quaternion[2] = q[2];\n quaternion[3] = q[3];\n\n const anyCamera = camera as PerspectiveCamera & OrthographicCamera;\n\n if (anyCamera.isPerspectiveCamera) {\n return {\n type: 'perspective',\n fov: anyCamera.fov,\n aspect: anyCamera.aspect,\n near: anyCamera.near,\n far: anyCamera.far,\n zoom: anyCamera.zoom,\n position,\n quaternion,\n ...(camera.name ? { name: camera.name } : {}),\n };\n }\n\n if (anyCamera.isOrthographicCamera) {\n return {\n type: 'orthographic',\n left: anyCamera.left,\n right: anyCamera.right,\n top: anyCamera.top,\n bottom: anyCamera.bottom,\n near: anyCamera.near,\n far: anyCamera.far,\n zoom: anyCamera.zoom,\n position,\n quaternion,\n ...(camera.name ? { name: camera.name } : {}),\n };\n }\n\n throw new Bakery3Error({\n code: 'InvalidRequest',\n message: `Unsupported camera type: ${camera.type}`,\n why: 'The renderer draws a pinhole or an orthographic projection. An array, cube or stereo camera has no single frustum to reproduce.',\n fix: 'Pass one PerspectiveCamera or OrthographicCamera to render().',\n docs: 'https://bakery3.com/docs/cameras',\n });\n}\n\n/**\n * The world-space rotation, decomposed by hand.\n *\n * `getWorldQuaternion` would allocate a `Quaternion` and pull three's class\n * into this module's import graph for one value; the matrix is already\n * up to date and the extraction is nine multiplies.\n */\nfunction worldQuaternion(object: Camera): Quat {\n const e = object.matrixWorld.elements;\n // Strip scale, or a camera under a scaled rig reads as a rotation it is not.\n const sx = Math.hypot(e[0]!, e[1]!, e[2]!) || 1;\n const sy = Math.hypot(e[4]!, e[5]!, e[6]!) || 1;\n const sz = Math.hypot(e[8]!, e[9]!, e[10]!) || 1;\n const m00 = e[0]! / sx;\n const m01 = e[4]! / sy;\n const m02 = e[8]! / sz;\n const m10 = e[1]! / sx;\n const m11 = e[5]! / sy;\n const m12 = e[9]! / sz;\n const m20 = e[2]! / sx;\n const m21 = e[6]! / sy;\n const m22 = e[10]! / sz;\n\n const trace = m00 + m11 + m22;\n if (trace > 0) {\n const s = 0.5 / Math.sqrt(trace + 1);\n return [(m21 - m12) * s, (m02 - m20) * s, (m10 - m01) * s, 0.25 / s];\n }\n if (m00 > m11 && m00 > m22) {\n const s = 2 * Math.sqrt(1 + m00 - m11 - m22);\n return [0.25 * s, (m01 + m10) / s, (m02 + m20) / s, (m21 - m12) / s];\n }\n if (m11 > m22) {\n const s = 2 * Math.sqrt(1 + m11 - m00 - m22);\n return [(m01 + m10) / s, 0.25 * s, (m12 + m21) / s, (m02 - m20) / s];\n }\n const s = 2 * Math.sqrt(1 + m22 - m00 - m11);\n return [(m02 + m20) / s, (m12 + m21) / s, 0.25 * s, (m10 - m01) / s];\n}\n"]}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { ROOM_PRESETS } from './chunk-MD2U6S4N.js';
|
|
2
|
+
|
|
3
|
+
// src/protocol/bake.ts
|
|
4
|
+
var MAX_BAKE_TEXELS = 32 * 1024 * 1024;
|
|
5
|
+
var MAX_BAKE_OBJECTS = 256;
|
|
6
|
+
var BAKE_TEXTURE_SIZES = [256, 512, 1024, 2048];
|
|
7
|
+
var BAKE_ATLAS_SIZES = [1024, 2048, 4096];
|
|
8
|
+
var MAX_BAKE_SIMPLIFY_TRIANGLES = 5e4;
|
|
9
|
+
var MAX_BAKE_SIMPLIFY = 32;
|
|
10
|
+
function presetRoomBakePath(name, lighting) {
|
|
11
|
+
return `rooms/${name}/bake/${lighting}/bundle.json`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// src/presets/roomPrebake.ts
|
|
15
|
+
function roomPrebakeChanges(room) {
|
|
16
|
+
const changed = [];
|
|
17
|
+
if (room.surfaces) changed.push("its materials");
|
|
18
|
+
if (room.hdri !== void 0) changed.push("what is outside its windows");
|
|
19
|
+
if (room.pedestal !== void 0) changed.push("its stand");
|
|
20
|
+
return changed;
|
|
21
|
+
}
|
|
22
|
+
function roomIsPrebaked(name, lighting) {
|
|
23
|
+
const published = ROOM_PRESETS[name]?.prebaked ?? [];
|
|
24
|
+
return published.includes(lighting);
|
|
25
|
+
}
|
|
26
|
+
var handles = /* @__PURE__ */ new WeakMap();
|
|
27
|
+
function prebakedRoomsUnder(root) {
|
|
28
|
+
const found = [];
|
|
29
|
+
root.traverse((node) => {
|
|
30
|
+
const handle = handles.get(node);
|
|
31
|
+
if (handle) found.push({ group: node, handle });
|
|
32
|
+
});
|
|
33
|
+
return found;
|
|
34
|
+
}
|
|
35
|
+
function setPresetRoomPrebaked(room, on) {
|
|
36
|
+
handles.get(room)?.set(on);
|
|
37
|
+
}
|
|
38
|
+
async function applyRoomPrebake(group, spec, base, presetsVersion, sessionOptions = {}) {
|
|
39
|
+
if (!roomIsPrebaked(spec.name, spec.lighting) || roomPrebakeChanges(spec).length)
|
|
40
|
+
return null;
|
|
41
|
+
const url = `${base.replace(/\/$/, "")}/v${presetsVersion}/${presetRoomBakePath(spec.name, spec.lighting)}`;
|
|
42
|
+
try {
|
|
43
|
+
const response = await fetch(url);
|
|
44
|
+
if (!response.ok)
|
|
45
|
+
throw new Error(`${response.status} ${response.statusText}`);
|
|
46
|
+
const { room: _self, ...bundle } = await response.json();
|
|
47
|
+
const { createBakeSession } = await import('./session-YCQS4MRH.js');
|
|
48
|
+
const session = createBakeSession({
|
|
49
|
+
...sessionOptions,
|
|
50
|
+
scene: group,
|
|
51
|
+
probe: false,
|
|
52
|
+
presetBaseUrl: base
|
|
53
|
+
});
|
|
54
|
+
const generation = session.addGeneration(bundle, {
|
|
55
|
+
baseUrl: url,
|
|
56
|
+
label: "prebaked"
|
|
57
|
+
});
|
|
58
|
+
let wanted = true;
|
|
59
|
+
let suspended = 0;
|
|
60
|
+
const show = () => session.compare(!(wanted && suspended === 0));
|
|
61
|
+
handles.set(group, {
|
|
62
|
+
suspend() {
|
|
63
|
+
suspended++;
|
|
64
|
+
show();
|
|
65
|
+
},
|
|
66
|
+
resume() {
|
|
67
|
+
suspended = Math.max(0, suspended - 1);
|
|
68
|
+
show();
|
|
69
|
+
},
|
|
70
|
+
set(on) {
|
|
71
|
+
wanted = on;
|
|
72
|
+
show();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
const report = await session.apply(generation);
|
|
76
|
+
show();
|
|
77
|
+
return {
|
|
78
|
+
applied: report.applied.length,
|
|
79
|
+
unmatched: report.unmatched.length
|
|
80
|
+
};
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.warn(
|
|
83
|
+
`[bakery3] The prebaked light for room "${spec.name}" (${spec.lighting}) did not load, so it is lit by its preview lights. ${error instanceof Error ? error.message : String(error)}`
|
|
84
|
+
);
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export { BAKE_ATLAS_SIZES, BAKE_TEXTURE_SIZES, MAX_BAKE_OBJECTS, MAX_BAKE_SIMPLIFY, MAX_BAKE_SIMPLIFY_TRIANGLES, MAX_BAKE_TEXELS, applyRoomPrebake, prebakedRoomsUnder, roomIsPrebaked, roomPrebakeChanges, setPresetRoomPrebaked };
|
|
90
|
+
//# sourceMappingURL=chunk-W6WQ5UP7.js.map
|
|
91
|
+
//# sourceMappingURL=chunk-W6WQ5UP7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/protocol/bake.ts","../src/presets/roomPrebake.ts"],"names":[],"mappings":"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","file":"chunk-W6WQ5UP7.js","sourcesContent":["/**\n * Scene baking: path-traced lighting, returned to the live scene.\n *\n * A bake does not hand back a new scene. It returns one lightmap per object,\n * keyed by the same `objectId` the manifest uses everywhere, plus the second\n * UV set that lightmap needs. The SDK lays them on the meshes that are\n * already there and can take them off again. A portable GLB is available as\n * a download for taking the look elsewhere, never as the application format.\n *\n * A lightmap carries light only: diffuse irradiance (direct and indirect)\n * with the surface's own albedo left out. three.js multiplies `lightMap`\n * with the material's color and maps at shade time, so textures, colors and\n * roughness keep working and view-dependent effects (reflections, clearcoat,\n * custom shaders) keep running live on top. The portable GLB is the one\n * exception: a download has to stand on its own, so it bakes the finished\n * surface onto an unlit material.\n *\n * UV data comes back in the live geometry's own vertex numbering: usually as\n * two floats per existing vertex, applied in place; where the unwrap had to\n * split a vertex, as an explicit, reversible `topology` rebuild.\n */\n\n// ---------------------------------------------------------------------------\n// Versions and ceilings\n// ---------------------------------------------------------------------------\n\n/**\n * The bake pipeline's version, part of every bake cache key. Bumping it\n * re-addresses the bake cache, so a change to texel values or UV layouts is\n * never served from a cached bundle made before it.\n */\nexport const BAKE_VERSION = 14;\n\n/**\n * The reflection probe's size in pixels: width, height. Small on purpose: a\n * reflection of a room needs its light and its layout, not its texture\n * detail, and the file has to arrive before the product is looked at.\n */\nexport const BAKE_PROBE_SIZE = [512, 256] as const;\n\n/**\n * The portable GLB's own version, in the bake cache key only when\n * `deliverGlb` is set, so a change to the download alone does not throw away\n * cached lightmaps that did not change.\n */\nexport const BAKE_GLB_VERSION = 2;\n\n/**\n * The texel ceiling for one bake, enforced at estimate time before any GPU\n * time is bought. Texels drive a bake's cost the way frames drive a video's;\n * 32M texels is a full room at sensible resolutions.\n */\nexport const MAX_BAKE_TEXELS = 32 * 1024 * 1024;\n\n/**\n * The object-count ceiling for one bake job. A request past it is refused,\n * never sampled down.\n */\nexport const MAX_BAKE_OBJECTS = 256;\n\n// ---------------------------------------------------------------------------\n// The request\n// ---------------------------------------------------------------------------\n\n/**\n * How good a bake should be. The worker chooses sample counts and margins\n * from this, the way `QualityPreset` maps to `QualitySettings`.\n */\nexport type BakeQuality = 'preview' | 'standard' | 'high';\n\n/**\n * Samples per texel at each bake quality. The worker traces exactly this\n * many (adaptive sampling is off for a bake, so the cost is a straight\n * multiplication) and the estimate prices exactly this many, which is what\n * makes the quote match the charge. Lower than a still's pixel samples\n * because a diffuse-only bake converges faster: there is no specular lobe and\n * no caustic path, only irradiance.\n */\nexport const BAKE_SAMPLES: Record<BakeQuality, number> = {\n preview: 64,\n standard: 256,\n high: 1024,\n};\n\n/** The sides a lightmap or a stand-in texture may have, in texels. */\nexport const BAKE_TEXTURE_SIZES = [256, 512, 1024, 2048] as const;\nexport type BakeTextureSize = (typeof BAKE_TEXTURE_SIZES)[number];\n\n/** Atlas sides. 4096² is 16.8M texels, inside the texel ceiling on its own. */\nexport const BAKE_ATLAS_SIZES = [1024, 2048, 4096] as const;\nexport type BakeAtlasSize = (typeof BAKE_ATLAS_SIZES)[number];\n\n/**\n * One lightmap for every baked object, rather than one each.\n *\n * A room of sixty objects lit from sixty textures is sixty texture binds and\n * sixty downloads; lit from one atlas it is one of each, which is what an\n * interactive scene wants. The atlas holds the same texels the separate maps\n * would, and is priced as they are.\n *\n * Every object is baked into a square of its own: the side its world-space\n * surface earns, capped by `textureSize` and `sizes` as usual, times the\n * square root of its `weight`, rounded to a power of two. The squares are\n * laid into the map with no waste, and the map is filled: when the squares\n * would cover under a quarter of it every side doubles, and when they would\n * not fit the largest are halved until they do. So `textureSize`, `sizes`\n * and `weights` set relative shares, kept to within a power of two, and\n * `size` sets the texels there are to share.\n *\n * A hero product is `weights: { [heroId]: 4 }`: twice the side and four times\n * the area its surface alone would earn. Weights round to the nearest power\n * of two in side: 1.5 to 2.8 doubles it, 2.9 to 5.6 quadruples it.\n */\nexport type BakeAtlas = {\n size: BakeAtlasSize;\n /** Per-object density multipliers, keyed by objectId. Default 1. */\n weights?: Record<string, number>;\n};\n\n// ---------------------------------------------------------------------------\n// Simplifying: a stand-in for geometry the viewer does not need\n// ---------------------------------------------------------------------------\n\n/**\n * What a group of objects is replaced with in the baked scene.\n *\n * A lightmap keeps the developer's geometry and changes its light. That is\n * right for a product and wrong for a bookcase of forty books at the back of\n * the room: forty draw calls, forty lightmaps and forty unwraps to light\n * something the viewer reads as one lit shape. A stand-in is that one shape,\n * with the detail it lost baked on: the color of the surface it replaces, a\n * normal map for the relief it no longer has, and the irradiance traced at\n * the real surface. The real objects stay in the traced scene, so they still\n * cast their shadows and bounce their light onto everything else; they are\n * only absent from what is delivered.\n *\n * `'lowpoly'` the objects, shrink-wrapped into one skin and reduced to\n * `triangles`. The default.\n * `'box'` the box round them, turned about the vertical to fit.\n * Right for anything that reads as a box from where it is\n * seen: a bookcase, a cabinet, a radiator.\n * `'billboard'` one upright picture of them that turns to face the\n * camera. For a potted plant: half a million triangles of\n * leaves that no lightmap suits and no box resembles.\n * `{ objectId }` a mesh of the developer's own, in the captured scene and\n * hidden (so nothing traces it), used as the stand-in's\n * shape exactly as authored.\n * `'decimate'` one object, the same object with fewer triangles: its own\n * parts, silhouette, texture uvs and materials kept, reduced\n * to `triangles` by collapsing edges. For the product itself\n * (a 146k-triangle shoe), where a skin would melt laces and\n * eyelets together and a baked color would turn metal and\n * gloss into paint. Only its light is baked, in one pass at\n * the real surface; the stand-in wears the replaced object's\n * own materials at their own resolution. One entry per\n * object.\n */\nexport type BakeSimplifyTo =\n 'lowpoly' | 'box' | 'billboard' | 'decimate' | { objectId: string };\n\nexport type BakeSimplify = {\n /**\n * objectIds the stand-in replaces. They are not in `include`: a replaced\n * object gets no lightmap of its own, which is where the saving is.\n */\n objects: string[];\n to: BakeSimplifyTo;\n /** `'lowpoly'` only: the triangle budget. Default `BAKE_SIMPLIFY_TRIANGLES`. */\n triangles?: number;\n /** The stand-in's texture side. Default `textureSize`. */\n textureSize?: BakeTextureSize;\n};\n\nexport const BAKE_SIMPLIFY_TRIANGLES = 1500;\n/**\n * `'decimate'`'s default budget: close-up quality for a product, a tenth to\n * a fifth of what a scanned or vendor model usually carries.\n */\nexport const BAKE_DECIMATE_TRIANGLES = 20_000;\nexport const MAX_BAKE_SIMPLIFY_TRIANGLES = 50_000;\n/** Stand-ins one bake may build. Each is up to three traced passes. */\nexport const MAX_BAKE_SIMPLIFY = 32;\n\n/**\n * How many bake passes a stand-in costs: color, normal and light for a mesh,\n * one still for a billboard, one light pass for a decimated object. The\n * estimate multiplies by this.\n */\nexport function bakeSimplifyPasses(entry: Pick<BakeSimplify, 'to'>): number {\n return entry.to === 'billboard' || entry.to === 'decimate' ? 1 : 3;\n}\n\n// ---------------------------------------------------------------------------\n// The shadow a product casts on a room it did not light\n// ---------------------------------------------------------------------------\n\n/**\n * A plane under the baked objects carrying the difference they make to the\n * light on it, and nothing else.\n *\n * A preset room is the same room for every customer, so its lightmaps are a\n * library asset, baked once and served from the preset library\n * (`BakeBundle.room`). What that prebaked room cannot know is the product\n * standing in it: the contact shadow under a vase is what makes it look like\n * it is in the room rather than pasted over it.\n *\n * So the bake traces one small plane just above the floor twice, with the\n * product and its stand present and again with them hidden, and ships the\n * ratio: 1 where they changed nothing, darker where they blocked light. The\n * viewer lays it over the floor and multiplies. A ratio, not irradiance,\n * because it has to compose with a floor lightmap traced at another time and\n * another resolution.\n *\n * It only darkens. Light the product throws onto the floor (a red vase's\n * bounce) is in neither the prebaked room nor this plane; that is the\n * approximation this mode makes.\n */\nexport type BakeShadow = {\n /**\n * Scene units, Y-up: the plane's centre, on the surface it lies over.\n *\n * Absent is the usual case: the worker measures the footprint of whatever\n * casts, then finds the surface beneath with rays cast straight down and\n * the casters hidden. A plinth is often modelled a centimetre into the\n * floor, so the bottom of its bounds is inside the slab, where a plane\n * would be lit in neither trace.\n */\n center?: [number, number, number];\n /**\n * Metres. Absent takes the casters' footprint plus the room a shadow needs\n * to spread into.\n */\n width?: number;\n height?: number;\n /**\n * How far above the surface the plane sits, in metres. Small enough to\n * read as contact, large enough that nothing z-fights with the floor.\n */\n lift?: number;\n /** Texels a side. Default `BAKE_SHADOW_SIZE`. */\n textureSize?: BakeTextureSize;\n /**\n * objectIds whose shadow this is: hidden for the second trace. Absent\n * means every object the bake includes.\n */\n cast?: string[];\n /**\n * objectIds the plane is placed beneath and sized to cover. Absent means\n * the casters, which is the usual case.\n *\n * The two lists come apart when a product stands on something this bake is\n * lightmapping anyway. A vase on a plinth casts on the plinth's top, and\n * the plinth's own lightmap carries that. What the plinth must not do is\n * cast into this plane: the prebaked room was lit with the plinth standing\n * in it, so its shadow is already on that floor, and casting it again\n * would lay it down twice.\n *\n * So the plinth spans but does not cast. It is present in both traces and\n * divides out of the ratio exactly, leaving only what the vase adds; and\n * because it is hidden while the surface beneath is looked for, the plane\n * still lands on the floor rather than on the plinth's top.\n */\n spans?: string[];\n};\n\n/**\n * Where a room's prebaked lightmaps live, relative to the preset library.\n * Relative because the library has more than one home (the CDN, a local\n * directory, a worker's own mirror); each end joins the path to the library\n * it is using.\n */\nexport function presetRoomBakePath(name: string, lighting: string): string {\n return `rooms/${name}/bake/${lighting}/bundle.json`;\n}\n\n/**\n * A shadow plane's default resolution. Small on purpose: a contact shadow is\n * a soft, low-frequency thing over a metre or two of floor.\n */\nexport const BAKE_SHADOW_SIZE = 512;\n\n/**\n * What the bake produced for `BakeSettings.shadow`.\n *\n * `uri` is a ratio in 0..1 per channel, linear, one value per texel: what\n * fraction of the light still arrives with the product there. Lay the plane\n * at `center` + `lift` on its up axis, map this across it, and multiply.\n */\nexport type BakedShadow = {\n uri: string;\n format: 'exr' | 'png';\n size: number;\n center: [number, number, number];\n width: number;\n height: number;\n lift: number;\n /**\n * The darkest ratio in the map, for a panel and for a sanity check: a\n * shadow that came out 1.0 everywhere means nothing was cast.\n */\n darkest: number;\n};\n\n/**\n * What to bake and how well.\n *\n * `include` is explicit and authoritative: the SDK decides which objects can\n * take a lightmap (it can see the live materials; the worker cannot) and the\n * worker bakes exactly that list. An id the worker cannot find is a refusal\n * with the id and a fix, never a silent skip.\n */\nexport type BakeSettings = {\n quality: BakeQuality;\n /**\n * Per-object resolution ceiling. The worker may choose a smaller size for\n * a small object (by world-space surface area) so a screw does not cost\n * what a wall costs. It never chooses larger.\n */\n textureSize: BakeTextureSize;\n /** objectIds to bake, from the same id space as `RuntimeState`. */\n include: string[];\n /** Per-object override of `textureSize`, keyed by objectId. */\n sizes?: Record<string, BakeTextureSize>;\n /**\n * Bake every object into one shared lightmap of `atlas.size`² texels\n * instead of one texture each: see `BakeAtlas`. `textureSize` and `sizes`\n * then set each object's relative share of the map rather than its own\n * texture.\n */\n atlas?: BakeAtlas;\n /** Seam dilation in pixels. Default 4. */\n margin?: number;\n /**\n * Also produce the portable baked GLB: every baked object with an unlit\n * material (`KHR_materials_unlit`) whose one texture is the finished\n * surface (color, direct and bounced light, reflections as seen from\n * `view`, through the scene's tone curve). Loads anywhere with no lights\n * and no SDK. A second bake pass per object, so it roughly doubles GPU\n * time and is opt-in.\n */\n deliverGlb?: boolean;\n /**\n * Objects whose `uv1` is the developer's own layout, not one a previous\n * bake wrote. Present, the worker may use an object's own layout instead\n * of unwrapping it: its `uv1` when listed here, its texture `uv` when the\n * mesh is too fragmented or too dense to unwrap well. Either is used only\n * when it passes the worker's test (inside the unit square, no stacked\n * islands, enough texels an island, an even density), and the result says\n * so with `BakeUv` `channel`. Absent, every object is unwrapped.\n */\n existingUv?: { uv1: string[] };\n /**\n * Where the scene is looked at from, for seam placement only.\n *\n * The unwrap puts island boundaries on the side of each object that faces\n * away from this point, on its underside, or where it is hidden behind\n * other geometry, never across the surface this viewpoint sees. Nothing\n * about the lighting depends on it.\n *\n * It is not the manifest camera. The camera is excluded from the bake\n * cache key so that orbiting and baking again is free, and a seam layout\n * that followed the exact camera would make every orbit a re-cut. The SDK\n * sends the camera quantized (`quantizeBakeView`): direction in 30° steps,\n * distance in octaves around the baked objects, so nearby viewpoints share\n * a bake and only a real change of side re-cuts.\n *\n * Absent: seams prefer the underside and hard edges alone.\n */\n view?: BakeView;\n /**\n * Where the reflection probe is taken, and what is hidden from it.\n *\n * Every bake delivers one. The lightmap replaces a baked surface's\n * diffuse, but the objects the bake is for (the product kept live on its\n * plinth, so it can turn and change finish) still reflect the scene's own\n * environment map, which in a baked room is not the room. The probe is the\n * room as seen from where those objects stand, with those objects hidden\n * so they do not reflect themselves, and the SDK puts it on their\n * materials as `envMap`.\n *\n * The SDK decides both fields: the objects it hides are the ones that\n * could have been baked and were deliberately left out. An object that\n * cannot take a lightmap at all (a glass sphere, an instanced hedge) is\n * scenery and stays in the map, so the product reflects the glass beside\n * it. Absent, the worker takes the probe from the center of the baked\n * objects and hides nothing.\n */\n probe?: BakeProbe;\n /**\n * Groups of objects to deliver as a stand-in rather than light as they\n * are: see `BakeSimplify`. In request order; stand-in `n` of the bundle is\n * entry `n` here.\n */\n simplify?: BakeSimplify[];\n /**\n * Trace a shadow plane under the baked objects: see `BakeShadow`. What a\n * bake of a product standing in a prebaked room needs, and the only part\n * of that room the bake has to pay for.\n */\n shadow?: BakeShadow;\n /**\n * How a preset room in the scene is lit.\n *\n * `'full'` (the default) bakes the room's own surfaces along with\n * everything else.\n *\n * `'preset'` says the room is unmodified, so its lightmaps are a library\n * asset and this bake covers only what the library cannot know: the\n * product, the stand it is on, and the shadow it casts. The SDK only asks\n * for it when the room really is unmodified, and the worker records which\n * room the result stands on (`BakeBundle.room`).\n */\n room?: 'full' | 'preset';\n};\n\n/** A viewpoint, in the scene's own (Y-up, meters) coordinates. */\nexport type BakeView = {\n position: [number, number, number];\n};\n\n/** The reflection probe's request: where, and what not to see. */\nexport type BakeProbe = {\n /** Scene units, Y-up: the center of the objects the probe is for. */\n position: [number, number, number];\n /** objectIds hidden from the probe: the objects it will be applied to. */\n hide: string[];\n};\n\n/**\n * A manifest plus what to bake from it: the bake request body. The manifest\n * is the ordinary capture, camera included but unused by the bake itself, so\n * two captures that differ only by viewpoint hit the same bake cache entry.\n */\nexport type BakeSpec = {\n manifest: import('./manifest.js').SceneManifest;\n bake: BakeSettings;\n advanced?: { timeoutSeconds?: number };\n /**\n * Triangles in the objects `bake.include` names, for the quote only.\n *\n * The estimate has two triangle terms: syncing the whole scene (priced from\n * the manifest's own count) and unwrapping the objects being baked (priced\n * from this). Without it the unwrap term is priced against the whole\n * scene, which over-quotes a partial bake of a big room. The SDK sends it;\n * a request without it is quoted the higher way.\n *\n * Deliberately not inside `bake`: that object is the bake cache key, and a\n * number that only moves a price must not move what is cached.\n */\n bakedTriangles?: number;\n /**\n * Hard ceiling in USD, refused at estimate time. The same contract as\n * `RenderSpec.maxCost`.\n */\n maxCost?: number;\n metadata?: Record<string, unknown>;\n idempotencyKey?: string;\n};\n\n// ---------------------------------------------------------------------------\n// The result\n// ---------------------------------------------------------------------------\n\n/**\n * The second UV set for one object, in the live geometry's own vertex order.\n *\n * `values` alone: `2 * vertexCount` floats, writable straight into a `uv1`\n * BufferAttribute on the existing geometry. The common case, and the one\n * that leaves the developer's geometry object untouched.\n *\n * `topology`: the unwrap needed vertex splits. `vertexSource[i]` names the\n * original vertex that new vertex `i` was copied from, so every original\n * attribute (custom ones included) can be gathered through it and nothing\n * the developer authored is lost in the rebuild. `index` is the new triangle\n * list over the new vertices.\n */\nexport type BakeUv =\n | { values: number[] }\n | {\n topology: {\n vertexSource: number[];\n index: number[];\n values: number[];\n };\n }\n /**\n * The lightmap is laid on a uv set the mesh already has: `0` is its\n * texture uv (three's `uv`, glTF `TEXCOORD_0`), `1` its second set (`uv1`,\n * `TEXCOORD_1`). Nothing is shipped and nothing is rebuilt: the SDK points\n * the lightmap at that attribute (`texture.channel`). Such a lightmap is\n * written in the glTF orientation (v = 0 at the top), the one the\n * attribute is in; every unwrap the worker ships itself is bottom-up.\n * Only sent to an SDK that asked for it (`BakeSettings.existingUv`).\n */\n | { channel: 0 | 1 };\n\n/**\n * One baked object. `uri` is resolved against the bundle's own location: a\n * relative path on disk, an absolute URL from a server.\n */\nexport type BakedObject = {\n objectId: string;\n /** The object's name at capture time, for humans reading a bundle. */\n name?: string;\n /** HDR diffuse irradiance (direct + indirect, no albedo), linear. */\n lightmap: { uri: string; format: 'exr' | 'png'; size: number };\n /** Small LDR thumbnail for pickers and panels. */\n preview?: { uri: string };\n uv: BakeUv;\n /**\n * How the surface was cut, measured by the worker, so a panel can say\n * \"2 islands, seams out of view\" and mean it.\n *\n * `cut: 'hidden'` is the viewpoint-aware layout; `'angle'` is the plain\n * angle-limited fallback, used when the hidden-seam layout failed its\n * checks for this object (the job log says so too). `seamsInView` is the\n * fraction of seam length across smooth surface whose both sides face\n * `BakeSettings.view`; 0 is the goal. Seams on creases are not counted,\n * because they do not show.\n */\n layout?: {\n islands: number;\n seamsInView?: number;\n /**\n * `'uv0'`/`'uv1'`: the object's own uv set, chosen over an unwrap\n * because it passed the worker's layout test.\n */\n cut: 'hidden' | 'angle' | 'uv0' | 'uv1';\n };\n /**\n * In an atlas bake: the fraction of the shared map this object's islands\n * cover, which is what its budget and weight actually bought it.\n */\n atlasShare?: number;\n};\n\n/**\n * One stand-in, ready to be put in the scene: see `BakeSimplify`.\n *\n * A mesh carries its geometry inline, in the scene's own coordinates (world\n * space, Y-up, meters), because it is a few hundred vertices and a loader\n * for it would weigh more than it does. `uv` is the one unwrap every map\n * uses. `tangent` (xyzw) is the frame the normal map was baked in; a viewer\n * that derives tangents from uv derivatives instead will be close, not\n * exact. `color` is an sRGB PNG and `normal` a linear one, both the right\n * way up for an ordinary image loader (`flipY` on); `lightmap` follows the\n * lightmap contract above.\n *\n * A billboard is an upright rectangle `width` by `height` centered on\n * `center`, to be turned about the vertical to face the camera, and `map` is\n * linear radiance with alpha: the objects as traced from `BakeSettings.view`'s\n * side, lit by the whole scene, with everything else cut away. It goes\n * through the viewer's own tone mapping like everything around it.\n */\nexport type BakedProxy =\n | {\n kind: 'mesh';\n /** Index into `settings.simplify`. */\n index: number;\n replaces: string[];\n name?: string;\n geometry: {\n position: number[];\n normal: number[];\n uv: number[];\n tangent?: number[];\n index: number[];\n };\n color: { uri: string };\n normal: { uri: string };\n lightmap: { uri: string; format: 'exr'; size: number };\n /** The replaced surfaces' roughness and metalness, area weighted. */\n roughness: number;\n metalness: number;\n layout?: BakedObject['layout'];\n }\n | {\n /** `to: 'decimate'`: the replaced object with fewer triangles. */\n kind: 'decimated';\n index: number;\n /** The one object it replaces, whose materials it wears. */\n replaces: [string];\n name?: string;\n /**\n * In the scene's frame, like a mesh stand-in's. `uv` is the object's\n * own texture uv, carried through the decimation, in the glTF\n * orientation its texture maps expect; `uv1` is present when the\n * lightmap needed an unwrap of its own. `groups` are its material\n * slots, in the order of the replaced mesh's materials.\n */\n geometry: {\n position: number[];\n normal: number[];\n uv: number[];\n uv1?: number[];\n /** Its vertex colors (glTF COLOR_0), linear RGB, when it had them. */\n color?: number[];\n index: number[];\n groups?: Array<{ start: number; count: number; materialIndex: number }>;\n };\n lightmap: { uri: string; format: 'exr'; size: number };\n /** The uv set the lightmap reads: 0 is `uv`, 1 is `uv1`. */\n lightmapChannel: 0 | 1;\n triangles: { from: number; to: number };\n layout?: BakedObject['layout'];\n }\n | {\n kind: 'billboard';\n index: number;\n replaces: string[];\n name?: string;\n center: [number, number, number];\n width: number;\n height: number;\n map: { uri: string; format: 'exr'; width: number; height: number };\n };\n\n/**\n * An object the bake did not produce, with the reason and the fix, per\n * object: \"the bake finished\" must never quietly mean \"except for the glass\n * and the instanced chairs\".\n */\nexport type BakeSkip = {\n objectId: string;\n name?: string;\n code: string;\n message: string;\n why?: string;\n fix?: string;\n /**\n * The object was baked; this is a complaint about how well.\n *\n * An ordinary entry here means a lightmap was not made and the object is\n * still lit the way it was. A `warning` means one was made and is worse\n * than it should be. `BakeCoarseUnwrap` is the case: a surface whose\n * geometry is far finer than the map it earns cuts into more islands than\n * the map has room for, and the result is padding with a little light in\n * it.\n */\n warning?: true;\n};\n\n/**\n * Everything one bake produced. The bundle file is written last, so an\n * interrupted bake never looks finished.\n */\nexport type BakeBundle = {\n bundleVersion: 1;\n /** The `BAKE_VERSION` that produced this. */\n bakeVersion: number;\n settings: BakeSettings;\n objects: BakedObject[];\n skipped: BakeSkip[];\n /**\n * When `settings.atlas` was set: the one lightmap every object's\n * `lightmap.uri` names, so a loader can fetch it once and share it. Each\n * object's `uv` is already in the atlas's space.\n */\n atlas?: {\n uri: string;\n format: 'exr' | 'png';\n size: number;\n preview?: { uri: string };\n /** Fraction of the map's texels some island maps to: packing efficiency. */\n coverage?: number;\n };\n /**\n * The stand-ins `settings.simplify` asked for. One that could not be built\n * is in `skipped` under the id `simplify[n]`, and the objects it would have\n * replaced are left as they were.\n */\n proxies?: BakedProxy[];\n /** The shadow plane `settings.shadow` asked for. */\n shadow?: BakedShadow;\n /**\n * The prebaked room this bake stands its objects in: an unmodified preset\n * room whose own lightmaps are a library asset, not something this bake\n * paid for. The SDK fetches that bundle and applies it underneath this\n * one. Absent from an ordinary bake, which lights everything it covers.\n */\n room?: {\n /** The preset and the lighting it was baked under. */\n name: string;\n lighting: string;\n /**\n * The published room's build hash, which is what makes the prebake this\n * room and not a later edit of it.\n */\n build: string;\n /** Where the prebaked bundle is, on the preset library. */\n uri: string;\n };\n /** The portable unlit GLB, when `deliverGlb` was set. */\n glb?: { uri: string };\n /**\n * The reflection probe: a linear HDR equirectangular map of the scene from\n * `position`, in three's own equirect convention (load it, set\n * `EquirectangularReflectionMapping`, and it is an `envMap`). `hidden` are\n * the objects it was taken without, which are the ones to put it on.\n */\n probe?: {\n uri: string;\n format: 'exr';\n width: number;\n height: number;\n position: [number, number, number];\n hidden: string[];\n /**\n * The probe as an LDR picture, for a panel, a drawer or a figure: the\n * same map through `exposure` (chosen by the worker to put its mean at\n * middle gray), Reinhard and sRGB. A label on the file, not something to\n * light with.\n */\n preview?: { uri: string; exposure?: number };\n };\n stats?: {\n texels: number;\n gpuSeconds?: number;\n /**\n * Where those seconds went, by step (`scene`, `probe`, `unwrap`,\n * `trace`, `repair`, `denoise`, `export`, `standIns`), summed over the\n * objects.\n */\n seconds?: Record<string, number>;\n };\n};\n\n/**\n * A bundle without its uv arrays and stand-in geometry: what a job result\n * carries once the files are in storage. The arrays are the bulk of a bundle\n * (a dense room's are megabytes) and belong in `bundle.json`, which the SDK\n * reads from storage.\n */\nexport type BakeBundleSummary = Omit<BakeBundle, 'objects' | 'proxies'> & {\n objects: Array<Omit<BakedObject, 'uv'>>;\n /** Stand-ins without their geometry, for the same reason. */\n proxies?: Array<DistributiveOmit<BakedProxy, 'geometry'>>;\n};\n\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown\n ? Omit<T, K>\n : never;\n\nexport function stripBundleUvs(bundle: BakeBundle): BakeBundleSummary {\n const { proxies, ...rest } = bundle;\n return {\n ...rest,\n objects: bundle.objects.map(({ uv: _uv, ...object }) => object),\n ...(proxies\n ? {\n proxies: proxies.map((proxy) => {\n if (proxy.kind === 'billboard') return proxy;\n const { geometry: _geometry, ...summary } = proxy;\n return summary;\n }),\n }\n : {}),\n };\n}\n\n/**\n * The files a bake writes, predicted from its settings.\n *\n * The worker uploads on presigned PUTs, one per file name, and a name has to\n * be signed before the bake runs. So the set is fixed here, from the request\n * alone, and the worker writes exactly these names. Objects are numbered in\n * include order, skipping nothing: object `n` is the n-th one that baked, so\n * a skipped object shifts the numbering down and the signatures for the\n * highest numbers go unused.\n */\nexport const BAKE_SHADOW_FILE = 'shadow.exr';\n\nexport function bakeUploadNames(\n settings: Pick<\n BakeSettings,\n 'include' | 'atlas' | 'deliverGlb' | 'simplify' | 'shadow'\n >,\n): string[] {\n const names = ['bundle.json'];\n if (settings.atlas) {\n names.push('atlas.exr', 'preview_atlas.png');\n } else {\n for (let n = 0; n < settings.include.length; n++) {\n names.push(`lm_${n}.exr`, `preview_${n}.png`);\n }\n }\n // Stand-ins are numbered by their place in the request, not by which of\n // them succeeded: a stand-in has no neighbors whose numbering it shifts.\n (settings.simplify ?? []).forEach((entry, n) =>\n names.push(...bakeProxyFileNames(entry, n)),\n );\n if (settings.shadow) names.push(BAKE_SHADOW_FILE);\n if (settings.deliverGlb) names.push('baked.glb');\n // Every bake delivers a reflection probe: see `BakeSettings.probe`.\n names.push('probe.exr', 'preview_probe.png');\n return names;\n}\n\n/** The files stand-in `n` writes. */\nexport function bakeProxyFileNames(\n entry: Pick<BakeSimplify, 'to'>,\n n: number,\n): string[] {\n if (entry.to === 'billboard') return [`proxy_${n}_billboard.exr`];\n if (entry.to === 'decimate') return [`proxy_${n}_light.exr`];\n return [\n `proxy_${n}_color.png`,\n `proxy_${n}_normal.png`,\n `proxy_${n}_light.exr`,\n ];\n}\n\n/**\n * Texels the shadow plane asks for. It is traced twice (with the product and\n * without), so it is counted twice.\n */\nexport function bakeShadowTexels(\n settings: Pick<BakeSettings, 'shadow'>,\n): number {\n if (!settings.shadow) return 0;\n const side = settings.shadow.textureSize ?? BAKE_SHADOW_SIZE;\n return 2 * side * side;\n}\n\n/**\n * Texels the stand-ins ask for: one map's worth each. The color and normal\n * passes are a sample apiece and are priced as bake passes, not as texels.\n */\nexport function bakeSimplifyTexels(\n settings: Pick<BakeSettings, 'textureSize' | 'simplify'>,\n): number {\n return (settings.simplify ?? []).reduce((sum, entry) => {\n const size = entry.textureSize ?? settings.textureSize;\n return sum + size * size;\n }, 0);\n}\n\n/** Content type for a bake file, by name. */\nexport function bakeFileContentType(name: string): string {\n if (name.endsWith('.exr')) return 'image/x-exr';\n if (name.endsWith('.png')) return 'image/png';\n if (name.endsWith('.glb')) return 'model/gltf-binary';\n if (name.endsWith('.json')) return 'application/json';\n return 'application/octet-stream';\n}\n","/**\n * A preset room lit by its own library lightmaps, the moment it is added.\n *\n * A room that has been prebaked under a lighting mode (`ROOM_PRESETS[name]\n * .prebaked`) has its light on the preset CDN already: the same bundle a\n * product bake in `room: 'preset'` mode stands on. `createPresetRoom` applies\n * it to the stand-in through a bake session of the room's own, so the\n * viewport shows traced bounce and contact shadows before anyone bakes\n * anything, and costs nothing but the download.\n *\n * Two sessions, one set of materials. A developer's own bake session covers\n * the whole scene, room included, and a bundle that stands its product in a\n * preset room lays the same room lightmaps down again under its own. Two\n * patches on one material would each record the other's edit as the thing to\n * restore. So the room's patch is registered here, and a session about to\n * light any of the room's objects suspends it first and resumes it after its\n * own patch is gone: always nested, never interleaved.\n *\n * Only an unmodified room is lit this way, by the same rule the bake uses\n * (`roomPrebakeChanges`): the library's light is a photograph of one\n * arrangement, and another floor or another view outside is another light.\n */\n\nimport type { Object3D } from 'three';\nimport type { BakeSessionOptions } from '../bake/session.js';\nimport {\n presetRoomBakePath,\n type BakeBundle,\n type RoomPresetSpec,\n} from '../protocol/index.js';\nimport { ROOM_PRESETS, type RoomPresetName } from './rooms.gen.js';\n\ntype RoomChoices = Pick<\n RoomPresetSpec,\n 'name' | 'lighting' | 'surfaces' | 'hdri' | 'pedestal'\n>;\n\n/** What a modified room has changed, in the developer's words. Empty means\n * the library's light is this room's light. */\nexport function roomPrebakeChanges(room: RoomChoices): string[] {\n const changed: string[] = [];\n if (room.surfaces) changed.push('its materials');\n if (room.hdri !== undefined) changed.push('what is outside its windows');\n if (room.pedestal !== undefined) changed.push('its stand');\n return changed;\n}\n\n/** Whether the library holds lightmaps for this room under this mode. */\nexport function roomIsPrebaked(name: string, lighting: string): boolean {\n const published: readonly string[] =\n ROOM_PRESETS[name as RoomPresetName]?.prebaked ?? [];\n return published.includes(lighting);\n}\n\n/** What `createPresetRoom` resolves `userData.prebaked` to. */\nexport type PresetRoomPrebake = {\n /** Room surfaces that took their lightmap. */\n applied: number;\n /** Surfaces the library lit that found no mesh here: a stand-in that\n * changed since the prebake. */\n unmatched: number;\n};\n\ntype Handle = {\n suspend(): void;\n resume(): void;\n set(on: boolean): void;\n};\n\nconst handles = new WeakMap<Object3D, Handle>();\n\n/** The room groups under `root` that carry library lightmaps. */\nexport function prebakedRoomsUnder(\n root: Object3D,\n): Array<{ group: Object3D; handle: Handle }> {\n const found: Array<{ group: Object3D; handle: Handle }> = [];\n root.traverse((node) => {\n const handle = handles.get(node);\n if (handle) found.push({ group: node, handle });\n });\n return found;\n}\n\n/**\n * Show or hide a preset room's library lightmaps. Off, the room is lit by its\n * stand-in lights alone, exactly as it was created; on puts them back. A room\n * that was never prebaked is left as it is.\n */\nexport function setPresetRoomPrebaked(room: Object3D, on: boolean): void {\n handles.get(room)?.set(on);\n}\n\n/**\n * Fetch the room's library bundle and apply it to `group` in a session of\n * its own. Resolves `null` when the room has nothing published for this mode\n * or has been changed; never rejects, because a room without its prebake is\n * still a room, lit by its stand-in lights as it always was.\n */\nexport async function applyRoomPrebake(\n group: Object3D,\n spec: RoomChoices,\n base: string,\n presetsVersion: number,\n /** For tests: a texture factory that does not decode EXRs. */\n sessionOptions: Pick<BakeSessionOptions, 'textureFactory'> = {},\n): Promise<PresetRoomPrebake | null> {\n if (\n !roomIsPrebaked(spec.name, spec.lighting) ||\n roomPrebakeChanges(spec).length\n )\n return null;\n const url = `${base.replace(/\\/$/, '')}/v${presetsVersion}/${presetRoomBakePath(spec.name, spec.lighting)}`;\n try {\n const response = await fetch(url);\n if (!response.ok)\n throw new Error(`${response.status} ${response.statusText}`);\n // The library bundle names itself as its own room; applied here it is the\n // room, so that field would only send the session to fetch it twice.\n const { room: _self, ...bundle } = (await response.json()) as BakeBundle;\n // Its own chunk: the session pulls EXRLoader, which a page that shows no\n // prebaked room should not download.\n const { createBakeSession } = await import('../bake/session.js');\n const session = createBakeSession({\n ...sessionOptions,\n scene: group,\n probe: false,\n presetBaseUrl: base,\n });\n const generation = session.addGeneration(bundle, {\n baseUrl: url,\n label: 'prebaked',\n });\n\n let wanted = true;\n let suspended = 0;\n const show = () => session.compare(!(wanted && suspended === 0));\n handles.set(group, {\n suspend() {\n suspended++;\n show();\n },\n resume() {\n suspended = Math.max(0, suspended - 1);\n show();\n },\n set(on) {\n wanted = on;\n show();\n },\n });\n\n const report = await session.apply(generation);\n // A developer's bake may have suspended the room while this loaded.\n show();\n return {\n applied: report.applied.length,\n unmatched: report.unmatched.length,\n };\n } catch (error) {\n console.warn(\n `[bakery3] The prebaked light for room \"${spec.name}\" (${spec.lighting}) did not load, so it is lit by its preview lights. ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n return null;\n }\n}\n"]}
|