@volter/game-editor 0.5.82 → 0.5.84
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/BUNDLED_NOTICES +22 -22
- package/catalog/entries/blender.json +2 -2
- package/catalog/entries/music.json +2 -2
- package/catalog/entries/reflections.json +1 -1
- package/catalog/project-source/src/lib/reflections/ReflectionProbe.tsx +34 -5
- package/catalog/project-source/src/lib/reflections/index.ts +1 -0
- package/catalog/project-source/src/lib/reflections/probe-controller.ts +44 -3
- package/catalog/project-source/src/lib/reflections/reflection-probe-registry.ts +32 -7
- package/catalog/project-source/src/lib/reflections/volume-reflection-material.ts +638 -160
- package/dist/.vite/manifest.json +2213 -2195
- package/dist/assets/{AssetBudgetPanel-Iip49rxI.js → AssetBudgetPanel-BZy2XeqE.js} +3 -3
- package/dist/assets/{AudioViewer-DmkOvskF.js → AudioViewer-DIuzLzET.js} +1 -1
- package/dist/assets/{CanvasBoardDocument-DCS8uzQo.js → CanvasBoardDocument-3ZxWTKes.js} +1 -1
- package/dist/assets/{CanvasSceneViewport-CfOuQiQ0.js → CanvasSceneViewport-BP7FC2z5.js} +1 -1
- package/dist/assets/{ColorInput-7CPpl8MI.js → ColorInput-Bm2EzmOy.js} +1 -1
- package/dist/assets/EntityModelDocument-Cy-Qproq.js +1 -0
- package/dist/assets/{EnvironmentAssetDocument-DlS6KFJu.js → EnvironmentAssetDocument-B8VmbxfT.js} +1 -1
- package/dist/assets/{GameplaySessionTimeline-C3zH6edi.js → GameplaySessionTimeline-CDcfr07j.js} +1 -1
- package/dist/assets/{ImageViewer-C0lQ6YO0.js → ImageViewer-BNFjkWbV.js} +1 -1
- package/dist/assets/InspectorObjectPreview-DZS_A4lG.js +1 -0
- package/dist/assets/{JsonAssetDocument-DjmH9CQH.js → JsonAssetDocument-BkdsklFn.js} +1 -1
- package/dist/assets/{LiveModuleDocument-Hiot8Q5L.js → LiveModuleDocument-BEDGzgpM.js} +1 -1
- package/dist/assets/{LutAssetDocument-6bBwNMOn.js → LutAssetDocument-RmvH4zzl.js} +1 -1
- package/dist/assets/ModelAssetDocument-BgkVc8nF.js +1 -0
- package/dist/assets/{NonThreeAuthoringBootstrap-DpieYAMC.js → NonThreeAuthoringBootstrap-CcXUHOp3.js} +1 -1
- package/dist/assets/{Object3DDocumentToolbar-BtZkDxKa.js → Object3DDocumentToolbar-DaqNoA9f.js} +1 -1
- package/dist/assets/Object3DDocumentViewport-DFMCzcLc.js +2 -0
- package/dist/assets/{Object3DPreview-DdrYVJUZ.js → Object3DPreview-Dr4GQgGl.js} +1 -1
- package/dist/assets/{OnlineAssetDetail-D06eTe3y.js → OnlineAssetDetail-BPAK7Ras.js} +1 -1
- package/dist/assets/{PlayBar-BbzjgLFJ.js → PlayBar--cmmDXMt.js} +1 -1
- package/dist/assets/{QuarksAssetDocument-wGHNt4ov.js → QuarksAssetDocument-DYr9l2tH.js} +1 -1
- package/dist/assets/{ReactCanvasControls-BL2tOUj6.js → ReactCanvasControls-CoK-IJcH.js} +1 -1
- package/dist/assets/{RootSelectionOverlay-C6Wu8nIs.js → RootSelectionOverlay-Jz5Ll7BT.js} +1 -1
- package/dist/assets/{ShaderAssetDocument-BDi8iNLA.js → ShaderAssetDocument-Fn1SuMaS.js} +3 -3
- package/dist/assets/{ShadowEditor-C3L0UWSH.js → ShadowEditor-IrEJWfde.js} +1 -1
- package/dist/assets/{SourceAssetViewer-D832bARo.js → SourceAssetViewer-C6gxbMdi.js} +1 -1
- package/dist/assets/StageHost-eySsTLUI.js +2 -0
- package/dist/assets/{StoryPreviewMount-CmOEFzZY.js → StoryPreviewMount-CkBS-oOC.js} +1 -1
- package/dist/assets/{ThreeBoardDocument-BtBMGtiQ.js → ThreeBoardDocument-DiQabLNJ.js} +2 -2
- package/dist/assets/{ThreeIsolationSceneContent-BEHNSEsi.js → ThreeIsolationSceneContent-_p4Y5QZ6.js} +1 -1
- package/dist/assets/{ToolObject3DPreview-76LMkfin.js → ToolObject3DPreview-CSADrilV.js} +1 -1
- package/dist/assets/{Toolbar-BEBQdxRz.js → Toolbar-DhQAiAOw.js} +1 -1
- package/dist/assets/{TransientHint-CzsdY8W6.js → TransientHint-B3roDMmv.js} +1 -1
- package/dist/assets/{VideoViewer-Ckrawyuq.js → VideoViewer-DARXMIes.js} +1 -1
- package/dist/assets/{ViewportShadingMenu-BofQt6_4.js → ViewportShadingMenu-DC5996me.js} +1 -1
- package/dist/assets/{active-ingest-D_ryI_R4.js → active-ingest-BQBCy_Hx.js} +3 -3
- package/dist/assets/{animation-clock-QjtJDIsR.js → animation-clock-DAhQxVz9.js} +1 -1
- package/dist/assets/{animation-mixers.service-BInUs1US.js → animation-mixers.service-cc5qCZnh.js} +1 -1
- package/dist/assets/{animation-timeline.utility-DUrfNSwn.js → animation-timeline.utility-DXa0g-qN.js} +1 -1
- package/dist/assets/{asset-budget-asset.menu-DVQEksFp.js → asset-budget-asset.menu-DjEcxL08.js} +1 -1
- package/dist/assets/{asset-budget.action-C-ENAa52.js → asset-budget.action-D7-3Scj7.js} +1 -1
- package/dist/assets/{asset-budget.document-Bb1PyL3H.js → asset-budget.document-Dmn7QkMR.js} +1 -1
- package/dist/assets/{asset-budget.menu-CPi2yjBA.js → asset-budget.menu-djdA3Q-S.js} +1 -1
- package/dist/assets/{asset-editor-persistence-Duoq7o22.js → asset-editor-persistence-Dcy1Joo-.js} +1 -1
- package/dist/assets/{asset-loaders-BftJ2m7-.js → asset-loaders-B0eV8Nk_.js} +1 -1
- package/dist/assets/{asset-preview-BS71mXsZ.js → asset-preview-CGO8kmg5.js} +1 -1
- package/dist/assets/{audio-unlock.service-C3ygvDjU.js → audio-unlock.service-Bgp8fUjH.js} +1 -1
- package/dist/assets/{audio.utility-DF55sgHW.js → audio.utility-DM2IZjJR.js} +1 -1
- package/dist/assets/autoplay.service-aGTnDCDQ.js +1 -0
- package/dist/assets/{babylon-authoring-adapter-CmeehOBt.js → babylon-authoring-adapter-CNYnKhpl.js} +1 -1
- package/dist/assets/{binding-resolver-DUz8C8Fs.js → binding-resolver-By6DQSMJ.js} +2 -2
- package/dist/assets/blender-emscripten-engine-3RrRUwov.js +2 -0
- package/dist/assets/{blender-node-editor.document-B8S2TVMM.js → blender-node-editor.document-DDxLnK24.js} +1 -1
- package/dist/assets/{blender-properties-tab-y-_BeZDx.js → blender-properties-tab-DoLvP4a6.js} +1 -1
- package/dist/assets/{blender-runtime-host-CUH6peXj.js → blender-runtime-host-pYPr56UP.js} +2 -2
- package/dist/assets/{blender-runtime-skin-B1tYITn2.js → blender-runtime-skin-DGw7Uk4l.js} +13 -13
- package/dist/assets/blender-runtime.document-Bvlzayok.js +13 -0
- package/dist/assets/blender-screenshot.menu-BSfC-1EL.js +1 -0
- package/dist/assets/{blender-timeline.document-CeCylyXB.js → blender-timeline.document-D0P1AfdK.js} +1 -1
- package/dist/assets/{blender-uv-editor.document-DEllMw3a.js → blender-uv-editor.document-DEb_AwLC.js} +1 -1
- package/dist/assets/{blender-version.status-CrT9NqK-.js → blender-version.status-LifaPI0C.js} +1 -1
- package/dist/assets/blender-wali-engine-DGwF1KiV.js +2 -0
- package/dist/assets/{blender.command-GIjMG1-m.js → blender.command-DDOcs1dZ.js} +1 -1
- package/dist/assets/{board-guides-B6TEPOPi.js → board-guides-CQWXbsSW.js} +1 -1
- package/dist/assets/bridge.command-CJCc-avL.js +3 -0
- package/dist/assets/{browserAll-k35XRFil.js → browserAll-_6V_eM5P.js} +1 -1
- package/dist/assets/{build-player.document-CHxj4P8q.js → build-player.document-Dz_Mq8Pd.js} +1 -1
- package/dist/assets/{build-profiles.document-CZCk1HYA.js → build-profiles.document-CLCc3EbW.js} +2 -2
- package/dist/assets/{build-progress.status-BNwBxP2a.js → build-progress.status-BfR8rtJ_.js} +1 -1
- package/dist/assets/{build-session-CW2UyYFF.js → build-session-BkZeQ_S3.js} +1 -1
- package/dist/assets/{build.action-EmCcMu47.js → build.action-SVlllu1R.js} +1 -1
- package/dist/assets/{build.command-CzEckGcR.js → build.command-D6c8WqIx.js} +1 -1
- package/dist/assets/{build.header-CsYLpZbh.js → build.header-B_QEABFH.js} +1 -1
- package/dist/assets/{build.menu-CaDkDQuy.js → build.menu-wIEk8uq4.js} +1 -1
- package/dist/assets/{build.service-BcKtbU0-.js → build.service-C24aQmEn.js} +1 -1
- package/dist/assets/{camera-BwWZUyKt.js → camera-AsAJwixA.js} +1 -1
- package/dist/assets/{camera-runtime.inspector-CJyi3zmp.js → camera-runtime.inspector-EWiln7HZ.js} +1 -1
- package/dist/assets/{canvas-design-mount-B_uwZkEU.js → canvas-design-mount-7wA1io5I.js} +4 -4
- package/dist/assets/{canvas-entry-runtime-DskaohHK.js → canvas-entry-runtime-DxHv6Yxk.js} +2 -2
- package/dist/assets/{canvas-preview-frames-BzLeTumX.js → canvas-preview-frames-BJNr8ArE.js} +1 -1
- package/dist/assets/{canvas-story-capture.service-C80CsS8r.js → canvas-story-capture.service-DaHquc4I.js} +2 -2
- package/dist/assets/{capability-coverage-wd6bAhv3.js → capability-coverage-Fjv84QQN.js} +3 -3
- package/dist/assets/collaboration-presence-BYi9wylK.js +1 -0
- package/dist/assets/{collider-Dw2iWV3F.js → collider-BVans4YU.js} +1 -1
- package/dist/assets/{component-board.service-XAxyLCnM.js → component-board.service-6eXUhS3p.js} +2 -2
- package/dist/assets/component-board.service-C1VJog-q.js +2 -0
- package/dist/assets/{component-board.service-CH99sgj0.js → component-board.service-cNKaXoxN.js} +2 -2
- package/dist/assets/{component-contracts.service-DerT0BP-.js → component-contracts.service-B9e64gRX.js} +1 -1
- package/dist/assets/{component-instance-root-Cw_-ajBX.js → component-instance-root-Cw2jcbxa.js} +1 -1
- package/dist/assets/{component-verbs.command-BHK9M2Rk.js → component-verbs.command-CKmDlrFh.js} +1 -1
- package/dist/assets/component-verbs.service-C6MrL6R1.js +1 -0
- package/dist/assets/{config-1rCvm02z.js → config-CXA3itWR.js} +3 -3
- package/dist/assets/{configurations-CRTYw0r9.js → configurations-fjyekkEX.js} +1 -1
- package/dist/assets/{connection.status-D4fjnH24.js → connection.status-DmthQqVz.js} +1 -1
- package/dist/assets/{constraints.inspector-BgnafOLf.js → constraints.inspector-CvJlw3rw.js} +1 -1
- package/dist/assets/{content-bounds-D5pXeHJN.js → content-bounds-vfJMc-IO.js} +1 -1
- package/dist/assets/{contract-system-adapters-rVGdworX.js → contract-system-adapters-BL22QlsE.js} +1 -1
- package/dist/assets/{coverage.service-BNme5Agb.js → coverage.service-BO-eNB08.js} +2 -2
- package/dist/assets/{create-runtime-CcuQTSfy.js → create-runtime-DJ21Fa_i.js} +2 -2
- package/dist/assets/{curve-DorFv33R.js → curve-CiO2-ZoW.js} +1 -1
- package/dist/assets/{data-asset-CAE35Emh.js → data-asset-ByLXUC98.js} +1 -1
- package/dist/assets/{data-ref-DXqRnZsJ.js → data-ref-DpW1ZAc3.js} +1 -1
- package/dist/assets/{debug-bridge-DdCJSEni.js → debug-bridge-Bjh7HoG_.js} +1 -1
- package/dist/assets/{debug-registry-xQJcVubm.js → debug-registry-Bty6AiHN.js} +1 -1
- package/dist/assets/{deferred-ingest-play-CEnsC3uN.js → deferred-ingest-play-BDtHDAut.js} +2 -2
- package/dist/assets/{design-time-canvas-mount-CPRrCPFA.js → design-time-canvas-mount-CpNAFajF.js} +2 -2
- package/dist/assets/{design-time-layers-hmGqBp06.js → design-time-layers-BjbCzOaH.js} +1 -1
- package/dist/assets/{design-time-mount.service-CEmJjRIJ.js → design-time-mount.service-D2nvXCrm.js} +2 -2
- package/dist/assets/{design-time-mount.service-CLL-1Za4.js → design-time-mount.service-x4-8taot.js} +2 -2
- package/dist/assets/{design-time-react-mount-DmXi3GBy.js → design-time-react-mount-DlBtYE3L.js} +1 -1
- package/dist/assets/{document-preview-three-B4j_zNef.js → document-preview-three-CSHYA1BH.js} +1 -1
- package/dist/assets/{dom-authoring-adapter-BklRbjZB.js → dom-authoring-adapter-BRkZp4sE.js} +1 -1
- package/dist/assets/{draco3d-CFWI1XDT.js → draco3d-az59P8mF.js} +1 -1
- package/dist/assets/{edit-mode-audio.service-SUzv0nHs.js → edit-mode-audio.service--N5HyK0y.js} +1 -1
- package/dist/assets/{edit-mode-authoring-CSt3zzmS.js → edit-mode-authoring-BeMAYXYR.js} +1 -1
- package/dist/assets/{edit-mode-networking.service-D6UohrCr.js → edit-mode-networking.service-ByUOzpRh.js} +1 -1
- package/dist/assets/editor-api-dfe-lCMZ.js +1 -0
- package/dist/assets/{editor-layers-CjeYrUlm.js → editor-layers-Djcn6Scw.js} +1 -1
- package/dist/assets/{editor-shell-store-DckLm6m8.js → editor-shell-store-Br2L1Fw1.js} +1 -1
- package/dist/assets/{entity-object-V13bGmay.js → entity-object-BiOrbQZM.js} +1 -1
- package/dist/assets/{extend-BQeRA-aV.js → extend-BMnknpN1.js} +1 -1
- package/dist/assets/fork-menu-BOn71mbn.js +1 -0
- package/dist/assets/{frame-debugger-store-DDxWlDPK.js → frame-debugger-store-DVchJN-z.js} +1 -1
- package/dist/assets/{game-contract-seams-DrYyE5PX.js → game-contract-seams-gIFojMW2.js} +1 -1
- package/dist/assets/{game-css-scope-transform-D5hQWaci.js → game-css-scope-transform-Dab-AkD5.js} +1 -1
- package/dist/assets/{game-document.service-cJ1yKc2V.js → game-document.service-CFLg-vRz.js} +1 -1
- package/dist/assets/{game-eval.command-CKo-W17_.js → game-eval.command-Xch06afq.js} +1 -1
- package/dist/assets/{game-module-access-DuptGLGx.js → game-module-access-BDliSO9v.js} +1 -1
- package/dist/assets/{game-network-CAH_Jsh6.js → game-network-CTdS02CN.js} +3 -3
- package/dist/assets/{gameplay-export-B-JE4uSD.js → gameplay-export-Ct4wopUb.js} +1 -1
- package/dist/assets/gameplay-replay-xk1wnQ88.js +1 -0
- package/dist/assets/{gameplay.command-D4cY3nOq.js → gameplay.command-W3Sq7zn-.js} +1 -1
- package/dist/assets/{generation-jobs-CcCrhKLM.js → generation-jobs-BTP2zuZi.js} +1 -1
- package/dist/assets/{generation.service-CQwmLAOQ.js → generation.service-CHPmGb7B.js} +1 -1
- package/dist/assets/{generations-qfhCRH8C.js → generations-C7ppzxDW.js} +1 -1
- package/dist/assets/{generations.status-CcbR2OfF.js → generations.status-Cxb0-UxC.js} +1 -1
- package/dist/assets/{hierarchy-marks-B1jSqxSv.js → hierarchy-marks-3Iq1RFIh.js} +1 -1
- package/dist/assets/{hosted-asset-materialization-BZf-8JA1.js → hosted-asset-materialization-BK8ZfMnr.js} +2 -2
- package/dist/assets/ibl-override-material-C09OAhCc.js +6 -0
- package/dist/assets/{index-V4J7xfRg.js → index-BXH2Jsun.js} +294 -294
- package/dist/assets/index-CAf6mYXW.js +1 -0
- package/dist/assets/{index-aREcGExk.js → index-CP5WJuE9.js} +1 -1
- package/dist/assets/{index-D_ViOFCz.js → index-D9H8FkR3.js} +2 -2
- package/dist/assets/{index-DrEVdQvN.js → index-DAuA0xMo.js} +1 -1
- package/dist/assets/{index-CHa43s2p.js → index-DkTiDXNY.js} +1 -1
- package/dist/assets/{index-D-RMHSzb.js → index-Kznwz4yS.js} +1 -1
- package/dist/assets/{index-DXtfej3G.js → index-Yw9pOoLh.js} +1 -1
- package/dist/assets/{index-Cj_b0Ygc.js → index-k1tDFKMg.js} +1 -1
- package/dist/assets/{inference-diagnostics-DoZQ1mpg.js → inference-diagnostics-CxWL3UhK.js} +1 -1
- package/dist/assets/{ingest-boot-viewport-DWdOtGGE.js → ingest-boot-viewport-BeYyFYst.js} +1 -1
- package/dist/assets/{ingest-siblings-DyvasVDH.js → ingest-siblings-DGjWGyZo.js} +1 -1
- package/dist/assets/{ingest-source-persistence-CQOJeGhj.js → ingest-source-persistence-BFkLdeMy.js} +1 -1
- package/dist/assets/{ingest.service-CqIcydem.js → ingest.service-adZJNY83.js} +1 -1
- package/dist/assets/{inspect-DiSjB-N1.js → inspect-Dah00n9O.js} +1 -1
- package/dist/assets/instances.command-neq95jyN.js +1 -0
- package/dist/assets/{json-history-resource-CH2nzMZh.js → json-history-resource-DYW614tc.js} +1 -1
- package/dist/assets/layered-pick-Cn98HSnj.js +1 -0
- package/dist/assets/{light-CxiGIJma.js → light-nb6H9zi_.js} +1 -1
- package/dist/assets/{live-project-verbs-CnknlUlT.js → live-project-verbs-CyWrvvzK.js} +1 -1
- package/dist/assets/{machine.document-D7jMrzXt.js → machine.document-BlWIYspE.js} +1 -1
- package/dist/assets/{machines.finder-Dor3O0uf.js → machines.finder-D-nBx1QJ.js} +1 -1
- package/dist/assets/material-CcQQ-mP8.js +1 -0
- package/dist/assets/{material-factory-BN3Lax5C.js → material-factory-CKO4sgCS.js} +1 -1
- package/dist/assets/{mesh-Cj-CYroI.js → mesh-CGB8fhbT.js} +1 -1
- package/dist/assets/{model-asset-sections.service-mC4WJFTA.js → model-asset-sections.service-oZwOlxI-.js} +1 -1
- package/dist/assets/{model-inspection-8o57YSZW.js → model-inspection-B5p-uq9q.js} +1 -1
- package/dist/assets/{model-thumbnail-chePRlH7.js → model-thumbnail-Cgyfafqg.js} +1 -1
- package/dist/assets/{models.finder-CKkT5ZdU.js → models.finder-v4ShdgaO.js} +1 -1
- package/dist/assets/{module-mode-BMiMhHI3.js → module-mode-BBCbx1-D.js} +2 -2
- package/dist/assets/{mount-canvas-ingest-root-B1swVIUJ.js → mount-canvas-ingest-root-9CLvzAsA.js} +2 -2
- package/dist/assets/{mount-dom-ingest-root-B8hO5mht.js → mount-dom-ingest-root-CZA1Dw6t.js} +2 -2
- package/dist/assets/{mount-ingest-root-Cbnh-mSw.js → mount-ingest-root-hudyfrT7.js} +2 -2
- package/dist/assets/{mount-manifest-B7oL1Abg.js → mount-manifest-C_z-xK97.js} +1 -1
- package/dist/assets/{mount-three-ingest-root-BdUJx6ix.js → mount-three-ingest-root-DWoCP70F.js} +1 -1
- package/dist/assets/{mounted-authoring-C47L5H5t.js → mounted-authoring-DMWjUr2Z.js} +1 -1
- package/dist/assets/{mounted-story-viewport-source-CyEK_vaq.js → mounted-story-viewport-source-Iq6tisms.js} +1 -1
- package/dist/assets/{native-debug-module-JJOFtIVz.js → native-debug-module-BO5dlpfD.js} +1 -1
- package/dist/assets/{navigation-ROJM2w84.js → navigation-BzH049Ad.js} +1 -1
- package/dist/assets/{navmesh.menu-DjefqtkI.js → navmesh.menu-CicRFavg.js} +1 -1
- package/dist/assets/{navmesh.service-BPqjEQiq.js → navmesh.service-B0zUTaL0.js} +1 -1
- package/dist/assets/network-observer.service-CvC30rDc.js +1 -0
- package/dist/assets/{network.utility-6Y5u53n8.js → network.utility-DTpK-Ri9.js} +1 -1
- package/dist/assets/{object3d-document-session-registry-DNCOGA3i.js → object3d-document-session-registry-CTeivmwK.js} +2 -2
- package/dist/assets/{oid-transform-DQzN_Gl_.js → oid-transform-xxKy1722.js} +1 -1
- package/dist/assets/{particles-DGdUmqOr.js → particles-CeOU09tg.js} +1 -1
- package/dist/assets/{pixi-authoring-adapter-BRktdcIz.js → pixi-authoring-adapter-qwgLB4nb.js} +1 -1
- package/dist/assets/{pixi-live-write-target-Dg0mdLaa.js → pixi-live-write-target-B-E2O-WC.js} +1 -1
- package/dist/assets/{play.command-4IDAqR5W.js → play.command-CAhUsYcp.js} +2 -2
- package/dist/assets/{presentation-8m-RDKIS.js → presentation-BKcWD0nx.js} +1 -1
- package/dist/assets/{product-t6tauO7R.js → product-C8OFGza7.js} +1 -1
- package/dist/assets/{profiler.action-CXA4IkQB.js → profiler.action-CyjXGHbx.js} +1 -1
- package/dist/assets/{profiler.menu-BgIBv4-I.js → profiler.menu-DXHAGF3Z.js} +1 -1
- package/dist/assets/{profiler.utility-BYoRbfIK.js → profiler.utility-Qx-aaTdo.js} +1 -1
- package/dist/assets/{project-authoring-session-DkekCxNv.js → project-authoring-session-B0YSloNS.js} +2 -2
- package/dist/assets/{properties-bone-constraints.inspector-DXpiCtf9.js → properties-bone-constraints.inspector-C-dwjglB.js} +1 -1
- package/dist/assets/{properties-bone.inspector-wIDsP4c5.js → properties-bone.inspector-CeZIE99V.js} +1 -1
- package/dist/assets/{properties-collection.inspector-5SRPbqfM.js → properties-collection.inspector-DiSBJpTu.js} +1 -1
- package/dist/assets/{properties-constraints.inspector-DIjKz692.js → properties-constraints.inspector-2DxnBAIf.js} +1 -1
- package/dist/assets/{properties-data.inspector-DE6i8biZ.js → properties-data.inspector-DvSzTW7_.js} +1 -1
- package/dist/assets/{properties-material.inspector-Cu1UCPY4.js → properties-material.inspector-XPNSk9iW.js} +1 -1
- package/dist/assets/{properties-modifiers.inspector-DsQzA638.js → properties-modifiers.inspector-8pjl5OY9.js} +1 -1
- package/dist/assets/{properties-object.inspector-C57X0GJq.js → properties-object.inspector-a30WyA7Q.js} +1 -1
- package/dist/assets/{properties-output.inspector-DzrcFuRM.js → properties-output.inspector-B9JochaR.js} +1 -1
- package/dist/assets/{properties-particles.inspector-DGqSnelN.js → properties-particles.inspector-D6rRhjyA.js} +1 -1
- package/dist/assets/{properties-physics.inspector-DIjhtiLe.js → properties-physics.inspector-BcJFxv6L.js} +1 -1
- package/dist/assets/{properties-render.inspector-CX0orwn9.js → properties-render.inspector-DHPbElDL.js} +1 -1
- package/dist/assets/{properties-scene.inspector-DaHNL_sq.js → properties-scene.inspector-ZJueIZwx.js} +1 -1
- package/dist/assets/{properties-texture.inspector-B5hmxPRy.js → properties-texture.inspector-CvUhb-u8.js} +1 -1
- package/dist/assets/{properties-view-layer.inspector-C7hdJgei.js → properties-view-layer.inspector-CaaGgMEq.js} +1 -1
- package/dist/assets/{properties-world.inspector-EWTC6jNw.js → properties-world.inspector-10GD8Fpy.js} +1 -1
- package/dist/assets/{r3f-design-session-CXlGV9hp.js → r3f-design-session-CVxU3s62.js} +2 -2
- package/dist/assets/{r3f-physics-binding-DZSIB0d_.js → r3f-physics-binding-jUszsaB2.js} +1 -1
- package/dist/assets/{r3f-source-authoring-adapter-GR97EmII.js → r3f-source-authoring-adapter-Cmxf0Hvz.js} +1 -1
- package/dist/assets/{react-canvas-navigation-L_mOnybL.js → react-canvas-navigation-B-5CNIty.js} +1 -1
- package/dist/assets/{react-inspector.service-B4P3UnSY.js → react-inspector.service-wpEmCNFv.js} +1 -1
- package/dist/assets/{react-story-board-CT1_3sk_.js → react-story-board-CMghMNcN.js} +1 -1
- package/dist/assets/{react-three-rapier.esm-DyMNYoA_.js → react-three-rapier.esm-LvsEG944.js} +1 -1
- package/dist/assets/{react-world-authoring-adapter-OR1SW2jI.js → react-world-authoring-adapter-qVYWC2NO.js} +1 -1
- package/dist/assets/{realm-services-u0mnjXU9.js → realm-services-DvRrMpdg.js} +1 -1
- package/dist/assets/{reflection-probe-capture.inspector-CTNlSaGW.js → reflection-probe-capture.inspector-vo8ShT8w.js} +1 -1
- package/dist/assets/{registry-wqakYaDW.js → registry-CC-Vb6qS.js} +1 -1
- package/dist/assets/{relative-import-specifier-DXWVQf0o.js → relative-import-specifier-CT_YQo7I.js} +1 -1
- package/dist/assets/{render-env-CN09NtDf.js → render-env-YEmoFtX3.js} +1 -1
- package/dist/assets/{render-features-HCEupUJ6.js → render-features-B7OI7f1i.js} +1 -1
- package/dist/assets/{render-settings-Clt6d05o.js → render-settings-Mh_ddGef.js} +1 -1
- package/dist/assets/{resolve-three-CLzKVMx2.js → resolve-three-0A-COMTr.js} +3 -3
- package/dist/assets/{root-seam-contract-D2uQHWF9.js → root-seam-contract-SVcCHdYl.js} +1 -1
- package/dist/assets/{run-selection-AyelFc9T.js → run-selection-HF9_7IwN.js} +1 -1
- package/dist/assets/{scene-capture-WbPcLfJl.js → scene-capture-BvNIvwYD.js} +1 -1
- package/dist/assets/{scene-document.service-4U6rDhCp.js → scene-document.service-Ck2pGl4I.js} +2 -2
- package/dist/assets/{scene-framing-DzSL2Cld.js → scene-framing-BgT3O3rp.js} +1 -1
- package/dist/assets/{seeded-random-tpdJjdqy.js → seeded-random-BVxDLbEH.js} +1 -1
- package/dist/assets/{setup-renderer-B5vZmCyu.js → setup-renderer-BCSUurTm.js} +1 -1
- package/dist/assets/{source-object3d-authoring-adapter-BQnQ3Qzg.js → source-object3d-authoring-adapter-CShEQbUV.js} +1 -1
- package/dist/assets/{source-persistence-backend-uyd6NiWD.js → source-persistence-backend-DOkAuppW.js} +1 -1
- package/dist/assets/{stage-keyboard-B2yrk2zu.js → stage-keyboard-BJEP_zFM.js} +1 -1
- package/dist/assets/{stage-overlay-set-Cqtz3s06.js → stage-overlay-set-U3RYk04i.js} +1 -1
- package/dist/assets/{stage-presence-markers-BgLs0Aj5.js → stage-presence-markers-C9j53BR-.js} +1 -1
- package/dist/assets/stage-transform-tools-D9eCTssP.js +1 -0
- package/dist/assets/{standard-viewport-dressing-uhv_3JvQ.js → standard-viewport-dressing-CCVrTojE.js} +3 -3
- package/dist/assets/{state-watch.action-BJ08HgcO.js → state-watch.action-fGEIwj5w.js} +1 -1
- package/dist/assets/{state-watch.menu-rAULwijr.js → state-watch.menu-Bvi0XsP3.js} +1 -1
- package/dist/assets/{state-watch.utility-eEbQcj8t.js → state-watch.utility-BNhj8Nnx.js} +1 -1
- package/dist/assets/story-capture-D0GNxNYo.js +1 -0
- package/dist/assets/{story-documents-Cm4jCkW5.js → story-documents-BF53RkDW.js} +2 -2
- package/dist/assets/{story-documents.service-BDvQCslK.js → story-documents.service-cTbf9p1e.js} +1 -1
- package/dist/assets/{story-documents.service-Cgtg6rXn.js → story-documents.service-mW6CBXLc.js} +1 -1
- package/dist/assets/story-media-presence-DsyVqDVF.js +1 -0
- package/dist/assets/{story-pixi-preview-D62Q_vIW.js → story-pixi-preview-DpjZcKtE.js} +1 -1
- package/dist/assets/{story-presentation-CofUdEU7.js → story-presentation-CNkoc9rw.js} +1 -1
- package/dist/assets/{story-three-preview-B9O1wapP.js → story-three-preview--fB-XR3A.js} +2 -2
- package/dist/assets/{studio-environment-BIhqrtFc.js → studio-environment-TibfpTB9.js} +1 -1
- package/dist/assets/{surface-state-DrM3wsrr.js → surface-state-Bt3iAf79.js} +1 -1
- package/dist/assets/{syntactic-prop-specs-BGz0N7Dg.js → syntactic-prop-specs-CN0U9uqz.js} +1 -1
- package/dist/assets/{team-playtest.service-CiE8UJAe.js → team-playtest.service-DiziYIhq.js} +1 -1
- package/dist/assets/{three-BQfhEwUz.js → three-DfbLdx-e.js} +1 -1
- package/dist/assets/{three-authoring-adapter-COBwREQO.js → three-authoring-adapter-CtjX4v6r.js} +1 -1
- package/dist/assets/three-authoring.service-BruYh1LE.js +2 -0
- package/dist/assets/{three-ingest-runtime-BnRF_nJN.js → three-ingest-runtime-V4l_ZpBp.js} +1 -1
- package/dist/assets/three-integration-BxG_D6RK.js +2 -0
- package/dist/assets/three-integration.service-CBSTLULk.js +1 -0
- package/dist/assets/{three-projection-core-PQm-CO61.js → three-projection-core-CyqdOvLG.js} +1 -1
- package/dist/assets/{three-state-C7khRIQE.js → three-state-B9ax7cgA.js} +1 -1
- package/dist/assets/{three-story-capture.service-B0oiGIfp.js → three-story-capture.service-CUVsgZa5.js} +2 -2
- package/dist/assets/three-story-model-NEuWKJe1.js +1 -0
- package/dist/assets/{three-viewport-presentation-B_tBudM-.js → three-viewport-presentation-Da0lYO0_.js} +1 -1
- package/dist/assets/{transform-mode-request-DWM6BdRA.js → transform-mode-request-Bfse_kvX.js} +1 -1
- package/dist/assets/{transport-strip-vgXGDgkI.js → transport-strip-DYbGjuX-.js} +1 -1
- package/dist/assets/transport.header-BglFgDzu.js +1 -0
- package/dist/assets/{tuples-BnYrlQqS.js → tuples-CMTm7T5T.js} +1 -1
- package/dist/assets/ui-board-document-GaFlpf81.js +2 -0
- package/dist/assets/{user-data-CifDDvSr.js → user-data-CYeyy9Su.js} +1 -1
- package/dist/assets/{utility-view-state-BPl8Asup.js → utility-view-state-Ye00iOCB.js} +1 -1
- package/dist/assets/{viewport-door-BCXEYmon.js → viewport-door-CCXNhxOW.js} +1 -1
- package/dist/assets/{viewport-root-presentation-DJLpUuf0.js → viewport-root-presentation-52V6g9Ae.js} +1 -1
- package/dist/assets/{viewport-surface-status-jwLhIK_M.js → viewport-surface-status-BtHfBssH.js} +1 -1
- package/dist/assets/{webworkerAll-P6jePa6X.js → webworkerAll-D7_8g3e0.js} +1 -1
- package/dist/assets/worker-BRY0I3XJ.js +2 -0
- package/dist/assets/{workspace-arrangement-B_OcbQbj.js → workspace-arrangement-D8CXsifv.js} +1 -1
- package/dist/assets/{world-documents-Bx3j867R.js → world-documents-C4AdTIHw.js} +1 -1
- package/dist/assets/{world-root-stage-binding-LEhjWDkn.js → world-root-stage-binding-CvEyzq0c.js} +1 -1
- package/dist/assets/{xstate-animation-binding-Dionc_fC.js → xstate-animation-binding-CmmOqqwV.js} +1 -1
- package/dist/assets/{xstate-animation-meta-XEmD_qc_.js → xstate-animation-meta-BSBJhOAT.js} +1 -1
- package/dist-node/cli.js +21 -18
- package/package.json +16 -16
- package/template/src/lib/reflections/ReflectionProbe.tsx +34 -5
- package/template/src/lib/reflections/index.ts +1 -0
- package/template/src/lib/reflections/probe-controller.ts +44 -3
- package/template/src/lib/reflections/reflection-probe-registry.ts +32 -7
- package/template/src/lib/reflections/volume-reflection-material.ts +638 -160
- package/dist/assets/EntityModelDocument-CplmAib0.js +0 -1
- package/dist/assets/InspectorObjectPreview-Bjc1Tn-I.js +0 -1
- package/dist/assets/ModelAssetDocument-G6ajiV51.js +0 -1
- package/dist/assets/Object3DDocumentViewport-BhH8afDi.js +0 -2
- package/dist/assets/StageHost-Bor1eHEO.js +0 -2
- package/dist/assets/autoplay.service-DaonKf7O.js +0 -1
- package/dist/assets/blender-emscripten-engine-DQljvfR7.js +0 -2
- package/dist/assets/blender-runtime.document-DnSl0twI.js +0 -12
- package/dist/assets/blender-wali-engine-CO-jG_ik.js +0 -2
- package/dist/assets/bridge.command-D6qTcgje.js +0 -3
- package/dist/assets/collaboration-presence-DcPhahtF.js +0 -1
- package/dist/assets/component-board.service-Cd1c9oSN.js +0 -2
- package/dist/assets/component-verbs.service-DsxhVcvk.js +0 -1
- package/dist/assets/editor-api-WeHVNlEo.js +0 -1
- package/dist/assets/fork-menu-Bthg9viY.js +0 -1
- package/dist/assets/gameplay-replay-WyopKGyB.js +0 -1
- package/dist/assets/ibl-override-material-CMo_a49U.js +0 -6
- package/dist/assets/index-yXSK_qPW.js +0 -1
- package/dist/assets/instances.command-COw2QpbN.js +0 -1
- package/dist/assets/layered-pick-BUr1U8lU.js +0 -1
- package/dist/assets/material-B6cH1Oqw.js +0 -1
- package/dist/assets/network-observer.service-GFzXuBMb.js +0 -1
- package/dist/assets/stage-transform-tools-BHcz53td.js +0 -1
- package/dist/assets/story-capture-B8wfSf5s.js +0 -1
- package/dist/assets/story-media-presence-SGlp_afL.js +0 -1
- package/dist/assets/three-authoring.service-C0azvoq8.js +0 -2
- package/dist/assets/three-integration-DeWYFf1x.js +0 -2
- package/dist/assets/three-integration.service-CoWCBTmg.js +0 -1
- package/dist/assets/three-story-model-D7g2pqfJ.js +0 -1
- package/dist/assets/transport.header-CfVHXjrT.js +0 -1
- package/dist/assets/ui-board-document-B_-K9luq.js +0 -2
- package/dist/assets/worker-CJIsrsP6.js +0 -2
|
@@ -32,11 +32,12 @@ import {
|
|
|
32
32
|
baseIblFunctions,
|
|
33
33
|
createIblSentinelTexture,
|
|
34
34
|
IBL_WORLD_POSITION,
|
|
35
|
-
type IblOverride,
|
|
36
|
-
type IblOverrideShader,
|
|
37
35
|
overrideMaterialIbl,
|
|
38
36
|
} from '@volter/threejs-runtime/render/ibl-override-material';
|
|
39
37
|
import {
|
|
38
|
+
type Camera,
|
|
39
|
+
type Material,
|
|
40
|
+
Matrix4,
|
|
40
41
|
MeshPhysicalMaterial,
|
|
41
42
|
type MeshPhysicalMaterialParameters,
|
|
42
43
|
MeshStandardMaterial,
|
|
@@ -47,8 +48,11 @@ import {
|
|
|
47
48
|
Vector3,
|
|
48
49
|
type WebGLRenderer,
|
|
49
50
|
} from 'three';
|
|
51
|
+
import type { ReflectionProbeDiffuse } from './probe-controller';
|
|
50
52
|
import {
|
|
51
53
|
acquireReflectionProbeRegistry,
|
|
54
|
+
type ReflectionProbeObserver,
|
|
55
|
+
type ReflectionProbeRegistry,
|
|
52
56
|
type ReflectionProbeRegistryLease,
|
|
53
57
|
type ReflectionProbeRuntime,
|
|
54
58
|
} from './reflection-probe-registry';
|
|
@@ -66,7 +70,11 @@ export interface VolumeReflectionMaterialHandle<
|
|
|
66
70
|
> {
|
|
67
71
|
/** The material this factory created. Use it like any `MeshStandardMaterial`. */
|
|
68
72
|
readonly material: T;
|
|
69
|
-
/**
|
|
73
|
+
/**
|
|
74
|
+
* Releases the registry leases and the material's own GPU resources: the
|
|
75
|
+
* same as `material.dispose()`, which lets go of everything this handle
|
|
76
|
+
* holds too.
|
|
77
|
+
*/
|
|
70
78
|
dispose(): void;
|
|
71
79
|
}
|
|
72
80
|
|
|
@@ -91,15 +99,15 @@ function cubeUvSamplingFunctions(): string {
|
|
|
91
99
|
}
|
|
92
100
|
|
|
93
101
|
/**
|
|
94
|
-
* `(texelWidth, texelHeight, maxMip)` for one PMREM atlas
|
|
95
|
-
* `generateCubeUVSize`, which reads the installed
|
|
96
|
-
* per texture instead.
|
|
102
|
+
* `(texelWidth, texelHeight, maxMip)` for one PMREM atlas, written into
|
|
103
|
+
* `target` — three's own `generateCubeUVSize`, which reads the installed
|
|
104
|
+
* envMap's image height, asked per texture instead.
|
|
97
105
|
*/
|
|
98
|
-
function
|
|
106
|
+
function atlasSizeInto(texture: Texture | null, target: Vector3): Vector3 {
|
|
99
107
|
const height = (texture?.image as { height?: number } | undefined)?.height ?? 0;
|
|
100
|
-
if (height <= 0) return
|
|
108
|
+
if (height <= 0) return target.set(0, 0, 0);
|
|
101
109
|
const maxMip = Math.log2(height) - 2;
|
|
102
|
-
return
|
|
110
|
+
return target.set(1 / (3 * Math.max(2 ** maxMip, 7 * 16)), 1 / height, maxMip);
|
|
103
111
|
}
|
|
104
112
|
|
|
105
113
|
function probeWeight(index: number): string {
|
|
@@ -124,16 +132,32 @@ function probeWeight(index: number): string {
|
|
|
124
132
|
}`;
|
|
125
133
|
}
|
|
126
134
|
|
|
127
|
-
|
|
135
|
+
/** Whether probe `index` takes part in the reflected (`radiance`) or the diffuse (`irradiance`) term. */
|
|
136
|
+
function probeTakesPart(index: number, term: 'radiance' | 'irradiance'): string {
|
|
137
|
+
return term === 'radiance'
|
|
138
|
+
? `volterProbeReady[${index}] > 0.5`
|
|
139
|
+
: `volterProbeReady[${index}] > 0.5 && volterProbeDiffuseMode[${index}] > 0.5`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function probeWeightBlock(index: number, term: 'radiance' | 'irradiance'): string {
|
|
128
143
|
return `
|
|
129
|
-
if (
|
|
144
|
+
if (${probeTakesPart(index, term)}) {${probeWeight(index)}
|
|
130
145
|
if (weight > 0.0) bestPriority = max(bestPriority, volterProbePriority[${index}]);
|
|
131
146
|
}`;
|
|
132
147
|
}
|
|
133
148
|
|
|
134
|
-
function probeSampleBlock(index: number,
|
|
149
|
+
function probeSampleBlock(index: number, term: 'radiance' | 'irradiance'): string {
|
|
150
|
+
const [roughness, direction] = term === 'radiance' ? ['roughness', 'worldReflect'] : ['1.0', 'worldNormal'];
|
|
151
|
+
const sampled = `volterTextureCubeUV(
|
|
152
|
+
volterProbeMap${index}, probeDirection, ${roughness}, volterProbeAtlasSize[${index}]
|
|
153
|
+
).rgb`;
|
|
154
|
+
// The diffuse term is the capture's, or the probe's constant colour (mode 2), with its own weight.
|
|
155
|
+
const light =
|
|
156
|
+
term === 'radiance'
|
|
157
|
+
? `${sampled} * volterProbeIntensity[${index}]`
|
|
158
|
+
: `(volterProbeDiffuseMode[${index}] > 1.5 ? volterProbeDiffuseColor[${index}] : ${sampled}) * volterProbeDiffuseIntensity[${index}]`;
|
|
135
159
|
return `
|
|
136
|
-
if (
|
|
160
|
+
if (${probeTakesPart(index, term)} && abs(volterProbePriority[${index}] - bestPriority) < 0.001) {${probeWeight(index)}
|
|
137
161
|
if (weight > 0.0) {
|
|
138
162
|
vec3 probeDirection = (volterProbeWorldToLocal[${index}] * vec4(${direction}, 0.0)).xyz;
|
|
139
163
|
if (volterProbeParallax[${index}] > 0.5) {
|
|
@@ -146,29 +170,38 @@ function probeSampleBlock(index: number, roughness: string, direction: string):
|
|
|
146
170
|
probeDirection = boxPos + safeDirection * nearestFace
|
|
147
171
|
+ volterProbeParallaxOffset[${index}] - volterProbeCaptureOffset[${index}];
|
|
148
172
|
}
|
|
149
|
-
accumulated +=
|
|
150
|
-
volterProbeMap${index}, probeDirection, ${roughness}, volterProbeAtlasSize[${index}]
|
|
151
|
-
).rgb * volterProbeIntensity[${index}] * weight;
|
|
173
|
+
accumulated += ${light} * weight;
|
|
152
174
|
totalWeight += weight;
|
|
153
175
|
}
|
|
154
176
|
}`;
|
|
155
177
|
}
|
|
156
178
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
179
|
+
/**
|
|
180
|
+
* Three's anisotropic radiance (`getIBLAnisotropyRadiance`, which an
|
|
181
|
+
* anisotropic physical material calls instead of `getIBLRadiance`) bends the
|
|
182
|
+
* normal and samples the radiance; in the renamed base chunk it would sample
|
|
183
|
+
* the environment alone, so it samples the probes' radiance instead (declared
|
|
184
|
+
* ahead of it, defined below).
|
|
185
|
+
*/
|
|
186
|
+
function anisotropyOverProbes(base: string): string {
|
|
187
|
+
return base.replace(
|
|
188
|
+
/(vec3 getIBLAnisotropyRadiance\([\s\S]*?)getBaseIBLRadiance\(/,
|
|
189
|
+
'$1getIBLRadiance(',
|
|
164
190
|
);
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* The IBL chunk for `probeCount` probes, of which the first `sampled` have a
|
|
195
|
+
* sampler (the rest are declared in the arrays, whose length is the program's
|
|
196
|
+
* cache key, and never read).
|
|
197
|
+
*/
|
|
198
|
+
function volumeReflectionShader(probeCount: number, sampled: number): string {
|
|
199
|
+
const each = (block: (index: number) => string) =>
|
|
200
|
+
Array.from({ length: sampled }, (_, index) => block(index)).join('\n');
|
|
201
|
+
return `#ifdef USE_ENVMAP
|
|
202
|
+
vec3 getIBLRadiance(const in vec3 viewDir, const in vec3 normal, const in float roughness);
|
|
203
|
+
#endif
|
|
204
|
+
${anisotropyOverProbes(baseIblFunctions())}
|
|
172
205
|
${cubeUvSamplingFunctions()}
|
|
173
206
|
#ifdef USE_ENVMAP
|
|
174
207
|
uniform float volterProbeReady[${probeCount}];
|
|
@@ -183,19 +216,22 @@ uniform float volterProbeRadius[${probeCount}];
|
|
|
183
216
|
uniform float volterProbeBlendDistance[${probeCount}];
|
|
184
217
|
uniform float volterProbePriority[${probeCount}];
|
|
185
218
|
uniform float volterProbeIntensity[${probeCount}];
|
|
219
|
+
uniform float volterProbeDiffuseMode[${probeCount}];
|
|
220
|
+
uniform float volterProbeDiffuseIntensity[${probeCount}];
|
|
221
|
+
uniform vec3 volterProbeDiffuseColor[${probeCount}];
|
|
186
222
|
uniform float volterProbeParallax[${probeCount}];
|
|
187
223
|
uniform float volterBaseEnvironment;
|
|
188
|
-
${
|
|
224
|
+
${each((index) => `uniform sampler2D volterProbeMap${index};`)}
|
|
189
225
|
|
|
190
226
|
vec3 getIBLIrradiance(const in vec3 normal) {
|
|
191
227
|
#ifdef ENVMAP_TYPE_CUBE_UV
|
|
192
228
|
vec3 baseIrradiance = getBaseIBLIrradiance(normal) * volterBaseEnvironment;
|
|
193
229
|
vec3 worldNormal = inverseTransformDirection(normal, viewMatrix);
|
|
194
230
|
float bestPriority = -1000000.0;
|
|
195
|
-
${
|
|
231
|
+
${each((index) => probeWeightBlock(index, 'irradiance'))}
|
|
196
232
|
vec3 accumulated = vec3(0.0);
|
|
197
233
|
float totalWeight = 0.0;
|
|
198
|
-
${irradiance}
|
|
234
|
+
${each((index) => probeSampleBlock(index, 'irradiance'))}
|
|
199
235
|
return totalWeight > 0.0
|
|
200
236
|
? PI * accumulated / totalWeight * envMapIntensity
|
|
201
237
|
: baseIrradiance;
|
|
@@ -211,10 +247,10 @@ vec3 getIBLRadiance(const in vec3 viewDir, const in vec3 normal, const in float
|
|
|
211
247
|
reflected = normalize(mix(reflected, normal, roughness * roughness));
|
|
212
248
|
vec3 worldReflect = inverseTransformDirection(reflected, viewMatrix);
|
|
213
249
|
float bestPriority = -1000000.0;
|
|
214
|
-
${
|
|
250
|
+
${each((index) => probeWeightBlock(index, 'radiance'))}
|
|
215
251
|
vec3 accumulated = vec3(0.0);
|
|
216
252
|
float totalWeight = 0.0;
|
|
217
|
-
${radiance}
|
|
253
|
+
${each((index) => probeSampleBlock(index, 'radiance'))}
|
|
218
254
|
return totalWeight > 0.0
|
|
219
255
|
? accumulated / totalWeight * envMapIntensity
|
|
220
256
|
: baseRadiance;
|
|
@@ -225,72 +261,194 @@ ${radiance}
|
|
|
225
261
|
#endif`;
|
|
226
262
|
}
|
|
227
263
|
|
|
228
|
-
|
|
229
|
-
|
|
264
|
+
/** Every uniform the chunk declares, by name: one object each, for the material's whole life. */
|
|
265
|
+
type ProbeUniforms = Readonly<Record<string, { value: unknown }>>;
|
|
266
|
+
|
|
267
|
+
const FLOAT_UNIFORMS = [
|
|
268
|
+
'volterProbeReady',
|
|
269
|
+
'volterProbeShape',
|
|
270
|
+
'volterProbeRadius',
|
|
271
|
+
'volterProbeBlendDistance',
|
|
272
|
+
'volterProbePriority',
|
|
273
|
+
'volterProbeIntensity',
|
|
274
|
+
'volterProbeDiffuseMode',
|
|
275
|
+
'volterProbeDiffuseIntensity',
|
|
276
|
+
'volterProbeParallax',
|
|
277
|
+
] as const;
|
|
278
|
+
|
|
279
|
+
const VECTOR_UNIFORMS = [
|
|
280
|
+
'volterProbeExtents',
|
|
281
|
+
'volterProbeParallaxExtents',
|
|
282
|
+
'volterProbeParallaxOffset',
|
|
283
|
+
'volterProbeCaptureOffset',
|
|
284
|
+
'volterProbeAtlasSize',
|
|
285
|
+
'volterProbeDiffuseColor',
|
|
286
|
+
] as const;
|
|
287
|
+
|
|
288
|
+
function createProbeUniforms(): ProbeUniforms {
|
|
289
|
+
const uniforms: Record<string, { value: unknown }> = {
|
|
290
|
+
volterBaseEnvironment: { value: 0 },
|
|
291
|
+
volterProbeWorldToLocal: { value: null },
|
|
292
|
+
};
|
|
293
|
+
for (const name of [...FLOAT_UNIFORMS, ...VECTOR_UNIFORMS]) uniforms[name] = { value: null };
|
|
294
|
+
for (let index = 0; index < MAX_PROBES; index += 1) uniforms[`volterProbeMap${index}`] = { value: null };
|
|
295
|
+
return uniforms;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* One binding's probe values, allocated once and rewritten in place. Every
|
|
300
|
+
* array is {@link MAX_PROBES} long whatever the program declares, the entries
|
|
301
|
+
* past the selected probes neutral (not ready, identity, zero), so a program
|
|
302
|
+
* compiled for more probes than are selected still uploads in full and
|
|
303
|
+
* lights nothing from the extra ones.
|
|
304
|
+
*/
|
|
305
|
+
interface ProbeValues {
|
|
306
|
+
readonly floats: Readonly<Record<(typeof FLOAT_UNIFORMS)[number], Float32Array>>;
|
|
307
|
+
readonly vectors: Readonly<Record<(typeof VECTOR_UNIFORMS)[number], readonly Vector3[]>>;
|
|
308
|
+
readonly worldToLocal: readonly Matrix4[];
|
|
309
|
+
readonly maps: (Texture | null)[];
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function createProbeValues(): ProbeValues {
|
|
313
|
+
const floats = {} as Record<(typeof FLOAT_UNIFORMS)[number], Float32Array>;
|
|
314
|
+
for (const name of FLOAT_UNIFORMS) floats[name] = new Float32Array(MAX_PROBES);
|
|
315
|
+
const vectors = {} as Record<(typeof VECTOR_UNIFORMS)[number], readonly Vector3[]>;
|
|
316
|
+
for (const name of VECTOR_UNIFORMS) vectors[name] = Array.from({ length: MAX_PROBES }, () => new Vector3());
|
|
317
|
+
return {
|
|
318
|
+
floats,
|
|
319
|
+
vectors,
|
|
320
|
+
worldToLocal: Array.from({ length: MAX_PROBES }, () => new Matrix4()),
|
|
321
|
+
maps: Array.from({ length: MAX_PROBES }, () => null),
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/** The probe's diffuse term: 0 none, 1 its capture, 2 its constant colour. */
|
|
326
|
+
function diffuseModeOf(config: ReflectionProbeDiffuse): number {
|
|
327
|
+
if (config.diffuse === 'none') return 0;
|
|
328
|
+
return config.diffuse === 'color' ? 2 : 1;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/** Rewrite `values` in place for `probes`, padding the rest with neutral entries. */
|
|
332
|
+
function fillProbeValues(
|
|
333
|
+
values: ProbeValues,
|
|
230
334
|
probes: readonly ReflectionProbeRuntime[],
|
|
231
|
-
baseEnvironment: boolean,
|
|
232
335
|
capturing: boolean,
|
|
233
336
|
): void {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
337
|
+
const { floats, vectors } = values;
|
|
338
|
+
for (let index = 0; index < MAX_PROBES; index += 1) {
|
|
339
|
+
const probe = probes[index];
|
|
340
|
+
if (!probe) {
|
|
341
|
+
for (const name of FLOAT_UNIFORMS) floats[name][index] = 0;
|
|
342
|
+
for (const name of VECTOR_UNIFORMS) vectors[name][index]!.set(0, 0, 0);
|
|
343
|
+
values.worldToLocal[index]!.identity();
|
|
344
|
+
values.maps[index] = null;
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
const config = probe.mark.config as typeof probe.mark.config & ReflectionProbeDiffuse;
|
|
348
|
+
floats.volterProbeReady[index] = Number(probe.ready && !capturing);
|
|
349
|
+
floats.volterProbeShape[index] = Number(config.shape === 'sphere');
|
|
350
|
+
floats.volterProbeRadius[index] = Math.max(0.01, config.radius);
|
|
351
|
+
floats.volterProbeBlendDistance[index] = Math.max(0, config.blendDistance);
|
|
352
|
+
floats.volterProbePriority[index] = config.priority;
|
|
353
|
+
floats.volterProbeIntensity[index] = Math.max(0, config.intensity);
|
|
354
|
+
floats.volterProbeDiffuseMode[index] = diffuseModeOf(config);
|
|
355
|
+
floats.volterProbeDiffuseIntensity[index] = Math.max(0, config.diffuseIntensity ?? config.intensity);
|
|
356
|
+
floats.volterProbeParallax[index] = Number(config.parallaxProjection);
|
|
357
|
+
vectors.volterProbeExtents[index]!.copy(probe.extents);
|
|
358
|
+
vectors.volterProbeParallaxExtents[index]!.copy(probe.parallaxExtents);
|
|
359
|
+
vectors.volterProbeParallaxOffset[index]!.copy(probe.parallaxOffset);
|
|
360
|
+
vectors.volterProbeCaptureOffset[index]!.copy(probe.captureOffset);
|
|
361
|
+
atlasSizeInto(probe.texture, vectors.volterProbeAtlasSize[index]!);
|
|
362
|
+
const color = config.diffuseColor;
|
|
363
|
+
vectors.volterProbeDiffuseColor[index]!.set(color?.[0] ?? 0, color?.[1] ?? 0, color?.[2] ?? 0);
|
|
364
|
+
values.worldToLocal[index]!.copy(probe.worldToLocal);
|
|
365
|
+
values.maps[index] = probe.texture;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Point the material's uniform objects at `values`. Only `.value` changes:
|
|
371
|
+
* every program compiled for the material, in any renderer, holds these same
|
|
372
|
+
* objects, so the next draw uploads what they point at.
|
|
373
|
+
*/
|
|
374
|
+
function pointProbeUniforms(uniforms: ProbeUniforms, values: ProbeValues): void {
|
|
375
|
+
const set = (name: string, value: unknown) => {
|
|
376
|
+
(uniforms[name] as { value: unknown }).value = value;
|
|
237
377
|
};
|
|
238
|
-
values
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
);
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
'
|
|
288
|
-
|
|
378
|
+
for (const name of FLOAT_UNIFORMS) set(name, values.floats[name]);
|
|
379
|
+
for (const name of VECTOR_UNIFORMS) set(name, values.vectors[name]);
|
|
380
|
+
set('volterProbeWorldToLocal', values.worldToLocal);
|
|
381
|
+
for (let index = 0; index < MAX_PROBES; index += 1) set(`volterProbeMap${index}`, values.maps[index]);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/** Texture units a program three compiles with `parameters` binds before any probe's. */
|
|
385
|
+
const PARAMETER_TEXTURES = [
|
|
386
|
+
'map',
|
|
387
|
+
'matcap',
|
|
388
|
+
'envMap',
|
|
389
|
+
'lightMap',
|
|
390
|
+
'aoMap',
|
|
391
|
+
'bumpMap',
|
|
392
|
+
'normalMap',
|
|
393
|
+
'displacementMap',
|
|
394
|
+
'emissiveMap',
|
|
395
|
+
'metalnessMap',
|
|
396
|
+
'roughnessMap',
|
|
397
|
+
'anisotropyMap',
|
|
398
|
+
'clearcoatMap',
|
|
399
|
+
'clearcoatNormalMap',
|
|
400
|
+
'clearcoatRoughnessMap',
|
|
401
|
+
'iridescenceMap',
|
|
402
|
+
'iridescenceThicknessMap',
|
|
403
|
+
'sheenColorMap',
|
|
404
|
+
'sheenRoughnessMap',
|
|
405
|
+
'specularMap',
|
|
406
|
+
'specularColorMap',
|
|
407
|
+
'specularIntensityMap',
|
|
408
|
+
'transmission',
|
|
409
|
+
'transmissionMap',
|
|
410
|
+
'thicknessMap',
|
|
411
|
+
'gradientMap',
|
|
412
|
+
'alphaMap',
|
|
413
|
+
'skinning',
|
|
414
|
+
'instancingMorph',
|
|
415
|
+
'batchingColor',
|
|
416
|
+
] as const;
|
|
417
|
+
|
|
418
|
+
function texturesBeforeProbes(parameters: Readonly<Record<string, unknown>>): number {
|
|
419
|
+
const count = (name: string) => {
|
|
420
|
+
const value = parameters[name];
|
|
421
|
+
return typeof value === 'number' ? value : 0;
|
|
422
|
+
};
|
|
423
|
+
return (
|
|
424
|
+
PARAMETER_TEXTURES.filter((name) => Boolean(parameters[name])).length +
|
|
425
|
+
// A batched mesh's matrices and ids, a geometry's morph targets.
|
|
426
|
+
(parameters['batching'] ? 2 : 0) +
|
|
427
|
+
(count('morphTargetsCount') > 0 ? 1 : 0) +
|
|
428
|
+
// A rect area light's two LTC lookup textures.
|
|
429
|
+
(count('numRectAreaLights') > 0 ? 2 : 0) +
|
|
430
|
+
count('numDirLightShadows') +
|
|
431
|
+
count('numPointLightShadows') +
|
|
432
|
+
count('numSpotLightShadows') +
|
|
433
|
+
count('numSpotLightMaps')
|
|
289
434
|
);
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/** Warned once each: a registry with more probes than a material blends, a renderer out of units. */
|
|
438
|
+
const warnedRegistries = new WeakSet<ReflectionProbeRegistry>();
|
|
439
|
+
const warnedRenderers = new WeakSet<WebGLRenderer>();
|
|
440
|
+
|
|
441
|
+
/** The parameters and, when given, the renderer and scene of either factory's two forms. */
|
|
442
|
+
function splitArguments<O>(
|
|
443
|
+
first: WebGLRenderer | O | undefined,
|
|
444
|
+
scene: Scene | undefined,
|
|
445
|
+
options: O | undefined,
|
|
446
|
+
): { readonly bound: Binding | null; readonly options: O | undefined } {
|
|
447
|
+
const renderer = first as WebGLRenderer | undefined;
|
|
448
|
+
if (typeof renderer?.render === 'function' && scene !== undefined) {
|
|
449
|
+
return { bound: { renderer, scene }, options };
|
|
450
|
+
}
|
|
451
|
+
return { bound: null, options: first as O | undefined };
|
|
294
452
|
}
|
|
295
453
|
|
|
296
454
|
/**
|
|
@@ -300,21 +458,66 @@ function writeProbeUniforms(
|
|
|
300
458
|
* The material is this factory's: no existing material is mutated, and only
|
|
301
459
|
* meshes the author assigns it to are affected. Assigning `envMap` later works
|
|
302
460
|
* as usual and takes over the fallback term — the black texture that keeps
|
|
303
|
-
* three's IBL entry points compiled when there is no
|
|
304
|
-
* installed behind that property and never replaces an
|
|
461
|
+
* three's IBL entry points compiled when probes apply and there is no
|
|
462
|
+
* environment at all is installed behind that property and never replaces an
|
|
463
|
+
* assigned map. Where the scene has no probes the material draws as three's
|
|
464
|
+
* own.
|
|
465
|
+
*
|
|
466
|
+
* `onBeforeCompile`, `customProgramCacheKey` and `onBeforeRender` are the
|
|
467
|
+
* material's own hooks, and compose as three's hooks do: reading one returns
|
|
468
|
+
* exactly what was last assigned (the material's own before that), and a hook
|
|
469
|
+
* that wraps the value it read and calls it keeps the volume lighting, however
|
|
470
|
+
* many wrap it in turn. The probe chunk is spliced once per program, where the
|
|
471
|
+
* chain reaches the material's own hook; with no probes to light nothing is
|
|
472
|
+
* spliced, so a hook that takes three's IBL chunk for itself still finds it.
|
|
473
|
+
* A hook assigned without calling the one it replaces replaces the volume
|
|
474
|
+
* lighting too, as on any three material.
|
|
475
|
+
*
|
|
476
|
+
* Given no `renderer` and `scene` — a material made before either exists, as
|
|
477
|
+
* a module-level resource is — the material reads the probes of whichever
|
|
478
|
+
* renderer and scene draw it, from their first draw (three's
|
|
479
|
+
* `Material.onBeforeRender`, which runs before that draw compiles its
|
|
480
|
+
* program). It keeps one registry lease per renderer and scene that has drawn
|
|
481
|
+
* it, so two renderers drawing it (an editor viewport and a game view, a
|
|
482
|
+
* throwaway preview renderer) each keep their own probes and never evict each
|
|
483
|
+
* other's; before each draw the material's uniform values are those of the
|
|
484
|
+
* drawing renderer's probes. Given a `renderer` and `scene`, it reads that
|
|
485
|
+
* pair's probes wherever it is drawn. It lets go of every lease when it is
|
|
486
|
+
* disposed (`material.dispose()` or the handle's), taking them up again if it
|
|
487
|
+
* is drawn after that, or when it is garbage collected: whoever holds it owns
|
|
488
|
+
* it as they own any three material.
|
|
489
|
+
*
|
|
490
|
+
* With more probes in the scene than `maxProbes` (at most eight), the ones
|
|
491
|
+
* nearest the camera are blended, reselected as it moves, and the registry
|
|
492
|
+
* warns once. A program blends no more probes than the texture units the
|
|
493
|
+
* renderer has left once the material's maps, environment, shadow maps and
|
|
494
|
+
* area-light tables are bound (`capabilities.maxTextures`); the farthest are
|
|
495
|
+
* dropped from that program, and the renderer warns once.
|
|
496
|
+
*
|
|
497
|
+
* A program compiled without a draw (`renderer.compile`) takes the probe count
|
|
498
|
+
* of the binding that drew last; the renderer's next draw recompiles it if its
|
|
499
|
+
* own probes differ, and until then the program's extra probe slots light
|
|
500
|
+
* nothing.
|
|
501
|
+
*
|
|
502
|
+
* `clone()` and `toJSON()` see the author's `envMap` (null if none), never the
|
|
503
|
+
* black texture that stands in for it while probes apply.
|
|
305
504
|
*/
|
|
505
|
+
export function createVolumeReflectionMaterial(
|
|
506
|
+
options?: VolumeReflectionMaterialOptions,
|
|
507
|
+
): VolumeReflectionMaterialHandle;
|
|
306
508
|
export function createVolumeReflectionMaterial(
|
|
307
509
|
renderer: WebGLRenderer,
|
|
308
510
|
scene: Scene,
|
|
309
|
-
options
|
|
511
|
+
options?: VolumeReflectionMaterialOptions,
|
|
512
|
+
): VolumeReflectionMaterialHandle;
|
|
513
|
+
export function createVolumeReflectionMaterial(
|
|
514
|
+
first?: WebGLRenderer | VolumeReflectionMaterialOptions,
|
|
515
|
+
scene?: Scene,
|
|
516
|
+
options?: VolumeReflectionMaterialOptions,
|
|
310
517
|
): VolumeReflectionMaterialHandle {
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
scene,
|
|
315
|
-
new MeshStandardMaterial(parameters),
|
|
316
|
-
maxProbes,
|
|
317
|
-
);
|
|
518
|
+
const split = splitArguments(first, scene, options);
|
|
519
|
+
const { maxProbes, ...parameters } = split.options ?? {};
|
|
520
|
+
return ownVolumeReflectionMaterial(new MeshStandardMaterial(parameters), maxProbes, split.bound);
|
|
318
521
|
}
|
|
319
522
|
|
|
320
523
|
export interface VolumeReflectionPhysicalMaterialOptions extends MeshPhysicalMaterialParameters {
|
|
@@ -322,95 +525,370 @@ export interface VolumeReflectionPhysicalMaterialOptions extends MeshPhysicalMat
|
|
|
322
525
|
}
|
|
323
526
|
|
|
324
527
|
/** The same explicit volume effect with Three's physical material controls. */
|
|
528
|
+
export function createVolumeReflectionPhysicalMaterial(
|
|
529
|
+
options?: VolumeReflectionPhysicalMaterialOptions,
|
|
530
|
+
): VolumeReflectionMaterialHandle<MeshPhysicalMaterial>;
|
|
325
531
|
export function createVolumeReflectionPhysicalMaterial(
|
|
326
532
|
renderer: WebGLRenderer,
|
|
327
533
|
scene: Scene,
|
|
328
|
-
options
|
|
534
|
+
options?: VolumeReflectionPhysicalMaterialOptions,
|
|
535
|
+
): VolumeReflectionMaterialHandle<MeshPhysicalMaterial>;
|
|
536
|
+
export function createVolumeReflectionPhysicalMaterial(
|
|
537
|
+
first?: WebGLRenderer | VolumeReflectionPhysicalMaterialOptions,
|
|
538
|
+
scene?: Scene,
|
|
539
|
+
options?: VolumeReflectionPhysicalMaterialOptions,
|
|
329
540
|
): VolumeReflectionMaterialHandle<MeshPhysicalMaterial> {
|
|
330
|
-
const
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
541
|
+
const split = splitArguments(first, scene, options);
|
|
542
|
+
const { maxProbes, ...parameters } = split.options ?? {};
|
|
543
|
+
return ownVolumeReflectionMaterial(new MeshPhysicalMaterial(parameters), maxProbes, split.bound);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
interface Binding {
|
|
547
|
+
readonly renderer: WebGLRenderer;
|
|
548
|
+
readonly scene: Scene;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/** One renderer and scene's probes, as this material reads them. */
|
|
552
|
+
interface ProbeBinding extends Binding {
|
|
553
|
+
readonly lease: ReflectionProbeRegistryLease;
|
|
554
|
+
/** Held here, so it lives exactly as long as the binding; the registry reaches it weakly. */
|
|
555
|
+
readonly observer: ReflectionProbeObserver;
|
|
556
|
+
stop: () => void;
|
|
557
|
+
probes: readonly ReflectionProbeRuntime[];
|
|
558
|
+
capturing: boolean;
|
|
559
|
+
/** The probes blended, nearest the camera first; its length is the program's probe count. */
|
|
560
|
+
readonly selected: ReflectionProbeRuntime[];
|
|
561
|
+
/** The registry reported, or the selection changed, since `values` were last filled. */
|
|
562
|
+
dirty: boolean;
|
|
563
|
+
readonly camera: Vector3;
|
|
564
|
+
/** This binding's uniform values, rewritten in place. */
|
|
565
|
+
readonly values: ProbeValues;
|
|
566
|
+
/** Scratch for selection: the nearest probes so far and their distances. */
|
|
567
|
+
readonly nearest: (ReflectionProbeRuntime | undefined)[];
|
|
568
|
+
readonly nearestDistance: Float64Array;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Observe `registry` through a weak reference to `observer`: once the
|
|
573
|
+
* binding that holds the observer is collected (with the material, or with
|
|
574
|
+
* its renderer or scene), the next update stops observing and releases the
|
|
575
|
+
* lease. Module-level on purpose — a closure made inside the material's own
|
|
576
|
+
* scope would hold that scope, and the material with it, from the registry.
|
|
577
|
+
*/
|
|
578
|
+
function observeWeakly(
|
|
579
|
+
lease: ReflectionProbeRegistryLease,
|
|
580
|
+
observer: WeakRef<ReflectionProbeObserver>,
|
|
581
|
+
): () => void {
|
|
582
|
+
let stop = (): void => undefined;
|
|
583
|
+
stop = lease.registry.observe((probes, capturing) => {
|
|
584
|
+
const live = observer.deref();
|
|
585
|
+
if (live) {
|
|
586
|
+
live(probes, capturing);
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
stop();
|
|
590
|
+
lease.release();
|
|
591
|
+
});
|
|
592
|
+
return stop;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/** How far the camera is from a probe's volume: 0 inside it. */
|
|
596
|
+
function distanceToProbe(probe: ReflectionProbeRuntime, camera: Vector3, local: Vector3): number {
|
|
597
|
+
local.copy(camera).applyMatrix4(probe.worldToLocal);
|
|
598
|
+
if (probe.mark.config.shape === 'sphere') return Math.max(0, local.length() - probe.mark.config.radius);
|
|
599
|
+
const dx = Math.max(0, Math.abs(local.x) - probe.extents.x);
|
|
600
|
+
const dy = Math.max(0, Math.abs(local.y) - probe.extents.y);
|
|
601
|
+
const dz = Math.max(0, Math.abs(local.z) - probe.extents.z);
|
|
602
|
+
return Math.hypot(dx, dy, dz);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/** The chunks the probe splice reads from three's program, or the first missing. */
|
|
606
|
+
function missingChunk(shader: { vertexShader: string; fragmentShader: string }): string | null {
|
|
607
|
+
if (!shader.vertexShader.includes('#include <worldpos_vertex>')) return '#include <worldpos_vertex>';
|
|
608
|
+
if (!shader.fragmentShader.includes('#include <envmap_physical_pars_fragment>')) {
|
|
609
|
+
return '#include <envmap_physical_pars_fragment>';
|
|
610
|
+
}
|
|
611
|
+
return null;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
type CompileHook = Material['onBeforeCompile'];
|
|
615
|
+
type RenderHook = Material['onBeforeRender'];
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Make `name` an accessor that reads back exactly what was last assigned, or
|
|
619
|
+
* `own` before anything was (or once `own` is assigned back). Three calls
|
|
620
|
+
* what it reads, so a hook that wraps the value it read chains to `own`.
|
|
621
|
+
*/
|
|
622
|
+
function ownHook<F>(material: Material, name: string, own: F): void {
|
|
623
|
+
let assigned: F | null = null;
|
|
624
|
+
Object.defineProperty(material, name, {
|
|
625
|
+
configurable: true,
|
|
626
|
+
enumerable: true,
|
|
627
|
+
get: () => assigned ?? own,
|
|
628
|
+
set: (value: F) => {
|
|
629
|
+
assigned = value === own ? null : value;
|
|
630
|
+
},
|
|
631
|
+
});
|
|
337
632
|
}
|
|
338
633
|
|
|
339
634
|
function ownVolumeReflectionMaterial<T extends MeshStandardMaterial>(
|
|
340
|
-
renderer: WebGLRenderer,
|
|
341
|
-
scene: Scene,
|
|
342
635
|
material: T,
|
|
343
636
|
maxProbes: number | undefined,
|
|
637
|
+
bound: Binding | null,
|
|
344
638
|
): VolumeReflectionMaterialHandle<T> {
|
|
345
639
|
const limit = Math.min(MAX_PROBES, Math.max(1, Math.round(maxProbes ?? MAX_PROBES)));
|
|
346
640
|
const sentinel = createIblSentinelTexture();
|
|
641
|
+
const uniforms = createProbeUniforms();
|
|
347
642
|
let authoredEnvMap = material.envMap;
|
|
348
|
-
let
|
|
349
|
-
|
|
350
|
-
let
|
|
351
|
-
let
|
|
352
|
-
|
|
353
|
-
let
|
|
643
|
+
let bindings = new WeakMap<WebGLRenderer, WeakMap<Scene, ProbeBinding>>();
|
|
644
|
+
// Every binding made, weakly, so disposing can release their leases.
|
|
645
|
+
let made = new Set<WeakRef<ProbeBinding>>();
|
|
646
|
+
let pinned: ProbeBinding | null = null;
|
|
647
|
+
/** The binding of the draw in progress: the program three compiles next is its. */
|
|
648
|
+
let current: ProbeBinding | null = null;
|
|
649
|
+
/** The binding whose values the uniform objects point at. */
|
|
650
|
+
let written: ProbeBinding | null = null;
|
|
651
|
+
/** Per renderer, the probe count of the program it last resolved for this material. */
|
|
652
|
+
let resolved = new WeakMap<WebGLRenderer, number>();
|
|
653
|
+
/** The samplers the program being compiled declares (its texture units allowing). */
|
|
654
|
+
let sampled = 0;
|
|
655
|
+
/** Inside `clone()`/`toJSON()`, which see the author's envMap. */
|
|
656
|
+
let plain = 0;
|
|
657
|
+
let warnedChunk = false;
|
|
658
|
+
const cameraPosition = new Vector3();
|
|
659
|
+
const scratch = new Vector3();
|
|
660
|
+
|
|
661
|
+
const probeCount = () => current?.selected.length ?? 0;
|
|
354
662
|
|
|
355
663
|
// Three deletes both IBL entry points from a program with no environment at
|
|
356
|
-
// all, so a
|
|
357
|
-
// The author's own value always wins, and `scene.environment` is
|
|
358
|
-
// three whenever it exists.
|
|
359
|
-
const hasBaseEnvironment = () => Boolean(authoredEnvMap ?? scene.environment);
|
|
664
|
+
// all, so a material that probes apply to needs one native input present to
|
|
665
|
+
// keep them. The author's own value always wins, and `scene.environment` is
|
|
666
|
+
// left to three whenever it exists.
|
|
360
667
|
Object.defineProperty(material, 'envMap', {
|
|
361
668
|
configurable: true,
|
|
362
669
|
enumerable: true,
|
|
363
|
-
get: () =>
|
|
670
|
+
get: () =>
|
|
671
|
+
authoredEnvMap ??
|
|
672
|
+
(plain > 0 || probeCount() === 0 || current?.scene.environment ? null : sentinel),
|
|
364
673
|
set: (value: Texture | null) => {
|
|
365
674
|
authoredEnvMap = value;
|
|
366
675
|
},
|
|
367
676
|
});
|
|
677
|
+
const prototype = Object.getPrototypeOf(material) as T;
|
|
678
|
+
const plainly = <R>(run: () => R): R => {
|
|
679
|
+
plain += 1;
|
|
680
|
+
try {
|
|
681
|
+
return run();
|
|
682
|
+
} finally {
|
|
683
|
+
plain -= 1;
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
Object.defineProperty(material, 'clone', {
|
|
687
|
+
configurable: true,
|
|
688
|
+
writable: true,
|
|
689
|
+
value: () => plainly(() => prototype.clone.call(material)),
|
|
690
|
+
});
|
|
691
|
+
Object.defineProperty(material, 'toJSON', {
|
|
692
|
+
configurable: true,
|
|
693
|
+
writable: true,
|
|
694
|
+
value: (meta?: Parameters<T['toJSON']>[0]) => plainly(() => prototype.toJSON.call(material, meta)),
|
|
695
|
+
});
|
|
368
696
|
|
|
369
|
-
const
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
697
|
+
const bind = (renderer: WebGLRenderer, scene: Scene): ProbeBinding => {
|
|
698
|
+
const lease = acquireReflectionProbeRegistry(renderer, scene);
|
|
699
|
+
const binding: ProbeBinding = {
|
|
700
|
+
renderer,
|
|
701
|
+
scene,
|
|
702
|
+
lease,
|
|
703
|
+
observer: (probes, capturing) => {
|
|
704
|
+
binding.probes = probes;
|
|
705
|
+
binding.capturing = capturing;
|
|
706
|
+
binding.dirty = true;
|
|
707
|
+
if (probes.length > limit && !warnedRegistries.has(lease.registry)) {
|
|
708
|
+
warnedRegistries.add(lease.registry);
|
|
709
|
+
// biome-ignore lint/suspicious/noConsole: the uniform arrays have a fixed ceiling; say that probes fell off it.
|
|
710
|
+
console.warn(
|
|
711
|
+
`[reflections] a volume reflection material blends at most ${limit} probes; this scene has ${probes.length}, so the ${limit} nearest the camera are blended.`,
|
|
712
|
+
);
|
|
713
|
+
}
|
|
714
|
+
},
|
|
715
|
+
stop: () => undefined,
|
|
716
|
+
probes: [],
|
|
717
|
+
capturing: false,
|
|
718
|
+
selected: [],
|
|
719
|
+
dirty: true,
|
|
720
|
+
camera: new Vector3(Number.NaN, 0, 0),
|
|
721
|
+
values: createProbeValues(),
|
|
722
|
+
nearest: Array.from({ length: MAX_PROBES }, () => undefined),
|
|
723
|
+
nearestDistance: new Float64Array(MAX_PROBES),
|
|
724
|
+
};
|
|
725
|
+
binding.stop = observeWeakly(lease, new WeakRef(binding.observer));
|
|
726
|
+
for (const reference of made) if (!reference.deref()) made.delete(reference);
|
|
727
|
+
made.add(new WeakRef(binding));
|
|
728
|
+
return binding;
|
|
373
729
|
};
|
|
374
730
|
|
|
375
|
-
const
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
731
|
+
const bindingFor = (renderer: WebGLRenderer, scene: Scene): ProbeBinding => {
|
|
732
|
+
// Made at construction, and again at a draw after a dispose.
|
|
733
|
+
if (bound) {
|
|
734
|
+
pinned ??= bind(bound.renderer, bound.scene);
|
|
735
|
+
return pinned;
|
|
736
|
+
}
|
|
737
|
+
let scenes = bindings.get(renderer);
|
|
738
|
+
if (!scenes) {
|
|
739
|
+
scenes = new WeakMap();
|
|
740
|
+
bindings.set(renderer, scenes);
|
|
741
|
+
}
|
|
742
|
+
let binding = scenes.get(scene);
|
|
743
|
+
if (!binding) {
|
|
744
|
+
binding = bind(renderer, scene);
|
|
745
|
+
scenes.set(scene, binding);
|
|
746
|
+
}
|
|
747
|
+
return binding;
|
|
748
|
+
};
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* The `limit` probes nearest the camera, nearest first (a program short of
|
|
752
|
+
* texture units samples the first ones), chosen without allocating: again
|
|
753
|
+
* whenever the registry reports, and as the camera moves.
|
|
754
|
+
*/
|
|
755
|
+
const select = (binding: ProbeBinding, camera: Camera) => {
|
|
756
|
+
// A capture window zeroes every probe: nothing to choose between.
|
|
757
|
+
if (binding.capturing) return;
|
|
758
|
+
cameraPosition.setFromMatrixPosition(camera.matrixWorld);
|
|
759
|
+
const moved = !cameraPosition.equals(binding.camera);
|
|
760
|
+
if (!binding.dirty && !(moved && binding.probes.length > 1)) return;
|
|
761
|
+
binding.camera.copy(cameraPosition);
|
|
762
|
+
const { nearest, nearestDistance: distance } = binding;
|
|
763
|
+
let count = 0;
|
|
764
|
+
for (const probe of binding.probes) {
|
|
765
|
+
const d = binding.probes.length === 1 ? 0 : distanceToProbe(probe, cameraPosition, scratch);
|
|
766
|
+
if (count === limit && d >= distance[count - 1]!) continue;
|
|
767
|
+
let slot = count < limit ? count++ : count - 1;
|
|
768
|
+
for (; slot > 0 && distance[slot - 1]! > d; slot -= 1) {
|
|
769
|
+
distance[slot] = distance[slot - 1]!;
|
|
770
|
+
nearest[slot] = nearest[slot - 1];
|
|
771
|
+
}
|
|
772
|
+
distance[slot] = d;
|
|
773
|
+
nearest[slot] = probe;
|
|
774
|
+
}
|
|
775
|
+
const selected = binding.selected;
|
|
776
|
+
let same = count === selected.length;
|
|
777
|
+
for (let index = 0; same && index < count; index += 1) same = nearest[index] === selected[index];
|
|
778
|
+
if (!same) {
|
|
779
|
+
selected.length = count;
|
|
780
|
+
for (let index = 0; index < count; index += 1) selected[index] = nearest[index]!;
|
|
781
|
+
binding.dirty = true;
|
|
782
|
+
}
|
|
783
|
+
nearest.fill(undefined);
|
|
784
|
+
};
|
|
785
|
+
|
|
786
|
+
// Before every draw, in whichever renderer: its probes are the ones the
|
|
787
|
+
// uniform objects point at for this draw. Three uploads a material's
|
|
788
|
+
// uniforms whenever its program or the material drawn changes, and at the
|
|
789
|
+
// start of every render, so a value written here reaches this draw.
|
|
790
|
+
const render: RenderHook = (renderer, scene, camera) => {
|
|
791
|
+
const binding = bindingFor(renderer, scene);
|
|
792
|
+
current = binding;
|
|
793
|
+
select(binding, camera);
|
|
794
|
+
// The program this renderer resolved last may be for another binding's
|
|
795
|
+
// count (one compiled by `renderer.compile`, which draws nothing and so
|
|
796
|
+
// never comes here first): have three resolve it again.
|
|
797
|
+
const count = resolved.get(renderer);
|
|
798
|
+
if (count !== undefined && count !== binding.selected.length) material.needsUpdate = true;
|
|
799
|
+
if (binding.dirty) {
|
|
800
|
+
fillProbeValues(binding.values, binding.selected, binding.capturing);
|
|
801
|
+
binding.dirty = false;
|
|
802
|
+
written = null;
|
|
803
|
+
}
|
|
804
|
+
if (written !== binding) {
|
|
805
|
+
pointProbeUniforms(uniforms, binding.values);
|
|
806
|
+
written = binding;
|
|
807
|
+
}
|
|
808
|
+
(uniforms['volterBaseEnvironment'] as { value: unknown }).value = (authoredEnvMap ?? scene.environment) ? 1 : 0;
|
|
384
809
|
};
|
|
385
810
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
811
|
+
// The IBL override goes on ONCE, for the material's whole life: its
|
|
812
|
+
// fragment and cache key are read at compile time from the drawing
|
|
813
|
+
// binding, and every program it compiles holds the same uniform objects.
|
|
814
|
+
overrideMaterialIbl(material, {
|
|
815
|
+
fragment: () =>
|
|
816
|
+
probeCount() === 0
|
|
817
|
+
? ShaderChunk.envmap_physical_pars_fragment
|
|
818
|
+
: volumeReflectionShader(probeCount(), sampled),
|
|
819
|
+
cacheKey: () => `volter-volume-reflections:${probeCount()}`,
|
|
820
|
+
onCompile: (shader) => {
|
|
821
|
+
Object.assign(shader.uniforms, uniforms);
|
|
822
|
+
},
|
|
823
|
+
});
|
|
824
|
+
const volumeCompile: CompileHook = material.onBeforeCompile;
|
|
825
|
+
const volumeKey = material.customProgramCacheKey;
|
|
826
|
+
|
|
827
|
+
// Where a chain of hooks reaches the material's own: the probe splice, once
|
|
828
|
+
// per program three compiles (a wrapper that calls it twice splices once).
|
|
829
|
+
const spliced = new WeakSet<object>();
|
|
830
|
+
const compile: CompileHook = (shader, renderer) => {
|
|
831
|
+
if (spliced.has(shader)) return;
|
|
832
|
+
spliced.add(shader);
|
|
833
|
+
const count = probeCount();
|
|
834
|
+
resolved.set(renderer, count);
|
|
835
|
+
// No probes: three's own program, whose chunks a later hook may still take.
|
|
836
|
+
if (count === 0) return;
|
|
837
|
+
const missing = missingChunk(shader);
|
|
838
|
+
if (missing !== null) {
|
|
839
|
+
if (!warnedChunk) {
|
|
840
|
+
warnedChunk = true;
|
|
841
|
+
// biome-ignore lint/suspicious/noConsole: a hook took a chunk the probe splice needs; say why no probe lights this material.
|
|
393
842
|
console.warn(
|
|
394
|
-
`[reflections] "${material.name
|
|
843
|
+
`[reflections] volume reflection material "${material.name}" draws without its probes: an onBeforeCompile hook replaced ${missing}, which the probe lighting is spliced into.`,
|
|
395
844
|
);
|
|
396
845
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
const free = renderer.capabilities.maxTextures - texturesBeforeProbes(shader as unknown as Record<string, unknown>);
|
|
849
|
+
sampled = Math.max(0, Math.min(count, free));
|
|
850
|
+
if (sampled < count && !warnedRenderers.has(renderer)) {
|
|
851
|
+
warnedRenderers.add(renderer);
|
|
852
|
+
// biome-ignore lint/suspicious/noConsole: a GPU's texture units bound how many probes one program can sample.
|
|
853
|
+
console.warn(
|
|
854
|
+
`[reflections] a volume reflection material samples ${sampled} of its ${count} probes: this GPU has ${renderer.capabilities.maxTextures} texture units and the material's maps and shadow maps use the rest. The probes farthest from the camera are left out.`,
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
volumeCompile.call(material, shader, renderer);
|
|
858
|
+
};
|
|
859
|
+
// Three asks for the key each time it resolves a program: during a draw,
|
|
860
|
+
// for the drawing renderer.
|
|
861
|
+
const cacheKey = (): string => {
|
|
862
|
+
if (current) resolved.set(current.renderer, probeCount());
|
|
863
|
+
return volumeKey.call(material);
|
|
864
|
+
};
|
|
865
|
+
ownHook(material, 'onBeforeCompile', compile);
|
|
866
|
+
ownHook(material, 'customProgramCacheKey', cacheKey);
|
|
867
|
+
ownHook(material, 'onBeforeRender', render);
|
|
868
|
+
|
|
869
|
+
const release = () => {
|
|
870
|
+
for (const reference of made) {
|
|
871
|
+
const binding = reference.deref();
|
|
872
|
+
if (!binding) continue;
|
|
873
|
+
binding.stop();
|
|
874
|
+
binding.lease.release();
|
|
875
|
+
}
|
|
876
|
+
made = new Set();
|
|
877
|
+
bindings = new WeakMap();
|
|
878
|
+
resolved = new WeakMap();
|
|
879
|
+
pinned = null;
|
|
880
|
+
current = null;
|
|
881
|
+
written = null;
|
|
882
|
+
sentinel.dispose();
|
|
883
|
+
};
|
|
884
|
+
material.addEventListener('dispose', release);
|
|
885
|
+
|
|
886
|
+
if (bound) pinned = bind(bound.renderer, bound.scene);
|
|
404
887
|
|
|
405
888
|
return {
|
|
406
889
|
material,
|
|
407
890
|
dispose() {
|
|
408
|
-
unobserve();
|
|
409
|
-
uninstall();
|
|
410
|
-
sentinel.dispose();
|
|
411
891
|
material.dispose();
|
|
412
|
-
lease?.release();
|
|
413
|
-
lease = null;
|
|
414
892
|
},
|
|
415
893
|
};
|
|
416
894
|
}
|