@babylonjs/lite 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{_mat4-storage-f64-CjDoht2w.js → _mat4-storage-f64-BW9sTaVh.js} +2 -2
- package/{_mat4-storage-f64-CjDoht2w.js.map → _mat4-storage-f64-BW9sTaVh.js.map} +1 -1
- package/{alpha-test-fragment-B7DjSnF7.js → alpha-test-fragment-eUG971h3.js} +2 -2
- package/{alpha-test-fragment-B7DjSnF7.js.map → alpha-test-fragment-eUG971h3.js.map} +1 -1
- package/{background-dds-skybox-BEX309u3.js → background-dds-skybox-BwG0kYQP.js} +3 -3
- package/{background-dds-skybox-BEX309u3.js.map → background-dds-skybox-BwG0kYQP.js.map} +1 -1
- package/{background-ground-BU0HOcM4.js → background-ground-DiFpKJzF.js} +2 -2
- package/{background-ground-BU0HOcM4.js.map → background-ground-DiFpKJzF.js.map} +1 -1
- package/{background-hdr-skybox--RRRic_K.js → background-hdr-skybox-DIgJhvfj.js} +3 -3
- package/{background-hdr-skybox--RRRic_K.js.map → background-hdr-skybox-DIgJhvfj.js.map} +1 -1
- package/{background-solid-skybox-BrH2fXSu.js → background-solid-skybox--fqHdan_.js} +2 -2
- package/{background-solid-skybox-BrH2fXSu.js.map → background-solid-skybox--fqHdan_.js.map} +1 -1
- package/{billboard-renderable-BHWryAeC.js → billboard-renderable-HY2XCd52.js} +2 -2
- package/{billboard-renderable-BHWryAeC.js.map → billboard-renderable-HY2XCd52.js.map} +1 -1
- package/{clamp-block-DqbwnQGW.js → clamp-block-XHdUk2Va.js} +2 -2
- package/{clamp-block-DqbwnQGW.js.map → clamp-block-XHdUk2Va.js.map} +1 -1
- package/{clearcoat-fragment-D6FSCie1.js → clearcoat-fragment-CHYw8MPB.js} +2 -2
- package/{clearcoat-fragment-D6FSCie1.js.map → clearcoat-fragment-CHYw8MPB.js.map} +1 -1
- package/{create-skeleton-D_uplboC.js → create-skeleton-9tdiUjRP.js} +2 -2
- package/{create-skeleton-D_uplboC.js.map → create-skeleton-9tdiUjRP.js.map} +1 -1
- package/{cubemap-skybox-material-DQcMMdf-.js → cubemap-skybox-material-DqQ0dyz8.js} +2 -2
- package/{cubemap-skybox-material-DQcMMdf-.js.map → cubemap-skybox-material-DqQ0dyz8.js.map} +1 -1
- package/{curve-block-21rT0JjG.js → curve-block-S27sXrJQ.js} +2 -2
- package/{curve-block-21rT0JjG.js.map → curve-block-S27sXrJQ.js.map} +1 -1
- package/{emissive-fragment-C5FtBs3y.js → emissive-fragment-CZMQ0_bF.js} +2 -2
- package/{emissive-fragment-C5FtBs3y.js.map → emissive-fragment-CZMQ0_bF.js.map} +1 -1
- package/{esm-shadow-view-Gtd1LWRP.js → esm-shadow-view-CUwxbnMR.js} +2 -2
- package/{esm-shadow-view-Gtd1LWRP.js.map → esm-shadow-view-CUwxbnMR.js.map} +1 -1
- package/{esm-shadow-view-c5YV4Eg9.js → esm-shadow-view-Cl36rOrK.js} +2 -2
- package/{esm-shadow-view-c5YV4Eg9.js.map → esm-shadow-view-Cl36rOrK.js.map} +1 -1
- package/{esm-shadow-view-Cl3rPGof.js → esm-shadow-view-DKQ-FSoV.js} +2 -2
- package/{esm-shadow-view-Cl3rPGof.js.map → esm-shadow-view-DKQ-FSoV.js.map} +1 -1
- package/{gaussian-splatting-pipeline-sh-7J31V23x.js → gaussian-splatting-pipeline-sh-DDo7QQ8l.js} +2 -2
- package/{gaussian-splatting-pipeline-sh-7J31V23x.js.map → gaussian-splatting-pipeline-sh-DDo7QQ8l.js.map} +1 -1
- package/{geometry-texture-output-dXk4E9uu.js → geometry-texture-output-BmuAquio.js} +2 -2
- package/{geometry-texture-output-dXk4E9uu.js.map → geometry-texture-output-BmuAquio.js.map} +1 -1
- package/{geometry-view-BsFJpBJa.js → geometry-view-xWZmq799.js} +4 -4
- package/{geometry-view-BsFJpBJa.js.map → geometry-view-xWZmq799.js.map} +1 -1
- package/{gltf-animation-K_zZxj_d.js → gltf-animation-Bq7k_5HA.js} +2 -2
- package/{gltf-animation-K_zZxj_d.js.map → gltf-animation-Bq7k_5HA.js.map} +1 -1
- package/{gltf-ext-basisu-CDbPclzZ.js → gltf-ext-basisu-C5teqxzQ.js} +2 -202
- package/gltf-ext-basisu-C5teqxzQ.js.map +1 -0
- package/{gltf-ext-node-visibility-DXCJEYr6.js → gltf-ext-node-visibility-DnGTKkMf.js} +2 -2
- package/{gltf-ext-node-visibility-DXCJEYr6.js.map → gltf-ext-node-visibility-DnGTKkMf.js.map} +1 -1
- package/{gltf-ext-quantization-CvHI_0Vg.js → gltf-ext-quantization-DheC7FhB.js} +2 -2
- package/{gltf-ext-quantization-CvHI_0Vg.js.map → gltf-ext-quantization-DheC7FhB.js.map} +1 -1
- package/{gltf-ext-uv-transform-DgYazJBs.js → gltf-ext-uv-transform-DljdVllE.js} +2 -2
- package/{gltf-ext-uv-transform-DgYazJBs.js.map → gltf-ext-uv-transform-DljdVllE.js.map} +1 -1
- package/{gltf-feature-animation-pointer-D1RJRFBw.js → gltf-feature-animation-pointer-DVhymFLK.js} +3 -3
- package/{gltf-feature-animation-pointer-D1RJRFBw.js.map → gltf-feature-animation-pointer-DVhymFLK.js.map} +1 -1
- package/{gltf-feature-animations-Cmc1uoIu.js → gltf-feature-animations-hxC3y3bJ.js} +2 -2
- package/{gltf-feature-animations-Cmc1uoIu.js.map → gltf-feature-animations-hxC3y3bJ.js.map} +1 -1
- package/{gltf-feature-draco-CKKzT5E3.js → gltf-feature-draco-B7Q_cMUv.js} +2 -2
- package/{gltf-feature-draco-CKKzT5E3.js.map → gltf-feature-draco-B7Q_cMUv.js.map} +1 -1
- package/{gltf-feature-gpu-instancing-n87SO6Vh.js → gltf-feature-gpu-instancing-C7sRzWv7.js} +2 -2
- package/{gltf-feature-gpu-instancing-n87SO6Vh.js.map → gltf-feature-gpu-instancing-C7sRzWv7.js.map} +1 -1
- package/{gltf-feature-lights-punctual-Ckm3ciL8.js → gltf-feature-lights-punctual-DF7kya14.js} +5 -5
- package/{gltf-feature-lights-punctual-Ckm3ciL8.js.map → gltf-feature-lights-punctual-DF7kya14.js.map} +1 -1
- package/{gltf-feature-meshopt-DLC4SF1E.js → gltf-feature-meshopt-DRG9hEqT.js} +2 -2
- package/{gltf-feature-meshopt-DLC4SF1E.js.map → gltf-feature-meshopt-DRG9hEqT.js.map} +1 -1
- package/{gltf-feature-morph-Cjtu7hYa.js → gltf-feature-morph-DZydYgWp.js} +3 -3
- package/{gltf-feature-morph-Cjtu7hYa.js.map → gltf-feature-morph-DZydYgWp.js.map} +1 -1
- package/{gltf-feature-registry-C63Hjp9w.js → gltf-feature-registry-DeYdy3DV.js} +15 -15
- package/{gltf-feature-registry-C63Hjp9w.js.map → gltf-feature-registry-DeYdy3DV.js.map} +1 -1
- package/{gltf-feature-skeleton-DKbOGidp.js → gltf-feature-skeleton-B9och1W0.js} +3 -3
- package/{gltf-feature-skeleton-DKbOGidp.js.map → gltf-feature-skeleton-B9och1W0.js.map} +1 -1
- package/{gltf-feature-variants-Cmzu0O0e.js → gltf-feature-variants-CY_Qft7f.js} +2 -2
- package/{gltf-feature-variants-Cmzu0O0e.js.map → gltf-feature-variants-CY_Qft7f.js.map} +1 -1
- package/{gltf-glb-parser-Cj5MHS-v.js → gltf-glb-parser-CqOeXFOz.js} +2 -2
- package/{gltf-glb-parser-Cj5MHS-v.js.map → gltf-glb-parser-CqOeXFOz.js.map} +1 -1
- package/{gltf-interleave-gHf9_t0i.js → gltf-interleave-DWf27t-h.js} +2 -3
- package/{gltf-interleave-gHf9_t0i.js.map → gltf-interleave-DWf27t-h.js.map} +1 -1
- package/{gltf-pbr-builder-ext-edNcjwPf.js → gltf-pbr-builder-ext-DvFxuOqN.js} +2 -2
- package/{gltf-pbr-builder-ext-edNcjwPf.js.map → gltf-pbr-builder-ext-DvFxuOqN.js.map} +1 -1
- package/{gltf-variants-CPxNdtP4.js → gltf-variants-CUvzYGYX.js} +4 -4
- package/{gltf-variants-CPxNdtP4.js.map → gltf-variants-CUvzYGYX.js.map} +1 -1
- package/gpu-task-timer-Dgkff80h.js +236 -0
- package/gpu-task-timer-Dgkff80h.js.map +1 -0
- package/gpu-timer-CUpqT_hK.js +55 -0
- package/gpu-timer-CUpqT_hK.js.map +1 -0
- package/{gs-picking-pipeline-DYaW_Lg3.js → gs-picking-pipeline-55sM5LzV.js} +2 -2
- package/{gs-picking-pipeline-DYaW_Lg3.js.map → gs-picking-pipeline-55sM5LzV.js.map} +1 -1
- package/{havok-floating-origin-Dr-18Nds.js → havok-floating-origin-5xp32P-C.js} +2 -2
- package/{havok-floating-origin-Dr-18Nds.js.map → havok-floating-origin-5xp32P-C.js.map} +1 -1
- package/{index-CLElg2Bo.js → index-C-tEgwbZ.js} +2712 -865
- package/index-C-tEgwbZ.js.map +1 -0
- package/index.d.ts +486 -6
- package/index.js +473 -444
- package/{input-block-DqEedWF2.js → input-block-DbRYCnet.js} +2 -2
- package/{input-block-DqEedWF2.js.map → input-block-DbRYCnet.js.map} +1 -1
- package/{iridescence-fragment-BHU59-gQ.js → iridescence-fragment-S3Ko1jvC.js} +2 -2
- package/{iridescence-fragment-BHU59-gQ.js.map → iridescence-fragment-S3Ko1jvC.js.map} +1 -1
- package/{light-block-Bv37V8vl.js → light-block-CAqWkucp.js} +2 -2
- package/{light-block-Bv37V8vl.js.map → light-block-CAqWkucp.js.map} +1 -1
- package/{loop-block-qTg8vb99.js → loop-block-ch-biPFY.js} +2 -2
- package/{loop-block-qTg8vb99.js.map → loop-block-ch-biPFY.js.map} +1 -1
- package/{morph-fragment-BRCUr2wQ.js → morph-fragment-D9he3Ksk.js} +2 -2
- package/{morph-fragment-BRCUr2wQ.js.map → morph-fragment-D9he3Ksk.js.map} +1 -1
- package/{multilight-wgsl-DMeppAdZ.js → multilight-wgsl-74aXpcJG.js} +2 -2
- package/{multilight-wgsl-DMeppAdZ.js.map → multilight-wgsl-74aXpcJG.js.map} +1 -1
- package/{node-env-Bc559GmY.js → node-env-B2bjGcMS.js} +2 -2
- package/{node-env-Bc559GmY.js.map → node-env-B2bjGcMS.js.map} +1 -1
- package/{node-geometry-view-COmWsRXK.js → node-geometry-view-CSXlEAhG.js} +3 -3
- package/{node-geometry-view-COmWsRXK.js.map → node-geometry-view-CSXlEAhG.js.map} +1 -1
- package/{node-registry-extra-compat-dWrv7gpS.js → node-registry-extra-compat-BEQH_ksg.js} +2 -2
- package/{node-registry-extra-compat-dWrv7gpS.js.map → node-registry-extra-compat-BEQH_ksg.js.map} +1 -1
- package/{node-registry-extra-math-Bn854sX9.js → node-registry-extra-math-Bm32WBAa.js} +2 -2
- package/{node-registry-extra-math-Bn854sX9.js.map → node-registry-extra-math-Bm32WBAa.js.map} +1 -1
- package/{node-renderable-B5G8WcdH.js → node-renderable-BMHny4tC.js} +2 -2
- package/{node-renderable-B5G8WcdH.js.map → node-renderable-BMHny4tC.js.map} +1 -1
- package/{node-shadow-CVIUlNf0.js → node-shadow-BRiz7CT1.js} +2 -2
- package/{node-shadow-CVIUlNf0.js.map → node-shadow-BRiz7CT1.js.map} +1 -1
- package/{normal-map-fragment-CQSxhjCy.js → normal-map-fragment-sE3TjF4U.js} +2 -2
- package/{normal-map-fragment-CQSxhjCy.js.map → normal-map-fragment-sE3TjF4U.js.map} +1 -1
- package/package.json +3 -3
- package/{parse-camera-pBRT_6i5.js → parse-camera-CmZBS423.js} +2 -2
- package/{parse-camera-pBRT_6i5.js.map → parse-camera-CmZBS423.js.map} +1 -1
- package/{pbr-geometry-view-NiZY_juX.js → pbr-geometry-view-T3vMABM8.js} +3 -3
- package/{pbr-geometry-view-NiZY_juX.js.map → pbr-geometry-view-T3vMABM8.js.map} +1 -1
- package/{pbr-metallic-roughness-block-JBSi-tQN.js → pbr-metallic-roughness-block-DbozMlHU.js} +2 -2
- package/{pbr-metallic-roughness-block-JBSi-tQN.js.map → pbr-metallic-roughness-block-DbozMlHU.js.map} +1 -1
- package/{pbr-metallic-roughness-block-full-Ta9lR2cz.js → pbr-metallic-roughness-block-full-CHC8w-Uv.js} +2 -2
- package/{pbr-metallic-roughness-block-full-Ta9lR2cz.js.map → pbr-metallic-roughness-block-full-CHC8w-Uv.js.map} +1 -1
- package/{pbr-mr-helper-core-BVWNR08D.js → pbr-mr-helper-core-DGRgbRXl.js} +2 -2
- package/{pbr-mr-helper-core-BVWNR08D.js.map → pbr-mr-helper-core-DGRgbRXl.js.map} +1 -1
- package/{pbr-refraction-C9FvFmAp.js → pbr-refraction-CquDP9JO.js} +2 -2
- package/{pbr-refraction-C9FvFmAp.js.map → pbr-refraction-CquDP9JO.js.map} +1 -1
- package/{pbr-renderable-DzUF2QIk.js → pbr-renderable-CaHKHU0g.js} +22 -22
- package/{pbr-renderable-DzUF2QIk.js.map → pbr-renderable-CaHKHU0g.js.map} +1 -1
- package/{pbr-shadow-fragment-CnqnbGYS.js → pbr-shadow-fragment-DmnNe6yz.js} +2 -2
- package/{pbr-shadow-fragment-CnqnbGYS.js.map → pbr-shadow-fragment-DmnNe6yz.js.map} +1 -1
- package/{pbr-tracking-3tU1kqea.js → pbr-tracking-Bo7RTANK.js} +2 -2
- package/{pbr-tracking-3tU1kqea.js.map → pbr-tracking-Bo7RTANK.js.map} +1 -1
- package/{pbr-transmission-ext-BcLjRxfB.js → pbr-transmission-ext-CoGcJBGE.js} +2 -2
- package/{pbr-transmission-ext-BcLjRxfB.js.map → pbr-transmission-ext-CoGcJBGE.js.map} +1 -1
- package/{reflectance-fragment-Dbpgw3Jt.js → reflectance-fragment-CExe6qDY.js} +2 -2
- package/{reflectance-fragment-Dbpgw3Jt.js.map → reflectance-fragment-CExe6qDY.js.map} +1 -1
- package/{rgbd-decode-DoyUquy3.js → rgbd-decode-DkiiiIlt.js} +2 -2
- package/{rgbd-decode-DoyUquy3.js.map → rgbd-decode-DkiiiIlt.js.map} +1 -1
- package/{scene-material-swap-nNUH4nGn.js → scene-material-swap-4qM0tpBK.js} +1 -2
- package/scene-material-swap-4qM0tpBK.js.map +1 -0
- package/{screenshot-readback-D0Sj9qq3.js → screenshot-readback-avr_tYGZ.js} +2 -2
- package/{screenshot-readback-D0Sj9qq3.js.map → screenshot-readback-avr_tYGZ.js.map} +1 -1
- package/{shader-composer-BUD_pSX4.js → shader-composer-CZagsJDS.js} +2 -2
- package/{shader-composer-BUD_pSX4.js.map → shader-composer-CZagsJDS.js.map} +1 -1
- package/{shader-renderable-D7-RyVxa.js → shader-renderable-D5sbgzxt.js} +41 -9
- package/shader-renderable-D5sbgzxt.js.map +1 -0
- package/{shader-thin-instance-DuBotxDO.js → shader-thin-instance-CkQ8rrfH.js} +2 -2
- package/{shader-thin-instance-DuBotxDO.js.map → shader-thin-instance-CkQ8rrfH.js.map} +1 -1
- package/{sheen-fragment-1MkEMcbc.js → sheen-fragment-BEigjpTX.js} +2 -2
- package/{sheen-fragment-1MkEMcbc.js.map → sheen-fragment-BEigjpTX.js.map} +1 -1
- package/{singlelight-directional-wgsl-BsV8G456.js → singlelight-directional-wgsl-Ccsk-ys3.js} +2 -2
- package/{singlelight-directional-wgsl-BsV8G456.js.map → singlelight-directional-wgsl-Ccsk-ys3.js.map} +1 -1
- package/{singlelight-hemispheric-wgsl-Bo0jKlW5.js → singlelight-hemispheric-wgsl-DL-jpc97.js} +2 -2
- package/{singlelight-hemispheric-wgsl-Bo0jKlW5.js.map → singlelight-hemispheric-wgsl-DL-jpc97.js.map} +1 -1
- package/{singlelight-point-wgsl-DV39UP5Y.js → singlelight-point-wgsl-hYmiP6ys.js} +2 -2
- package/{singlelight-point-wgsl-DV39UP5Y.js.map → singlelight-point-wgsl-hYmiP6ys.js.map} +1 -1
- package/{singlelight-spot-wgsl-yg3od6vL.js → singlelight-spot-wgsl-DSjp1p1C.js} +2 -2
- package/{singlelight-spot-wgsl-yg3od6vL.js.map → singlelight-spot-wgsl-DSjp1p1C.js.map} +1 -1
- package/{skeleton-fragment-DdxYG6kv.js → skeleton-fragment-B__bUbPK.js} +2 -2
- package/{skeleton-fragment-DdxYG6kv.js.map → skeleton-fragment-B__bUbPK.js.map} +1 -1
- package/{skybox-renderable-CJD4XmX5.js → skybox-renderable-BH6uUkal.js} +2 -2
- package/{skybox-renderable-CJD4XmX5.js.map → skybox-renderable-BH6uUkal.js.map} +1 -1
- package/{splat-ply-compressed-DHjyiVmI.js → splat-ply-compressed-BGNK6dnh.js} +2 -2
- package/{splat-ply-compressed-DHjyiVmI.js.map → splat-ply-compressed-BGNK6dnh.js.map} +1 -1
- package/{standard-pipeline-XTbHL7MY.js → standard-pipeline-BvFynkwL.js} +3 -3
- package/{standard-pipeline-XTbHL7MY.js.map → standard-pipeline-BvFynkwL.js.map} +1 -1
- package/{standard-renderable-CREWLNHI.js → standard-renderable-1Q3zemys.js} +3 -3
- package/{standard-renderable-CREWLNHI.js.map → standard-renderable-1Q3zemys.js.map} +1 -1
- package/{std-ambient-fragment-Bjx3VFrr.js → std-ambient-fragment-__F1KTEu.js} +2 -2
- package/{std-ambient-fragment-Bjx3VFrr.js.map → std-ambient-fragment-__F1KTEu.js.map} +1 -1
- package/{std-cube-reflection-fragment-y9WWdXUt.js → std-cube-reflection-fragment-DidM0byH.js} +2 -2
- package/{std-cube-reflection-fragment-y9WWdXUt.js.map → std-cube-reflection-fragment-DidM0byH.js.map} +1 -1
- package/{std-emissive-fragment-C8Lnmojh.js → std-emissive-fragment-Bj62X4Np.js} +2 -2
- package/{std-emissive-fragment-C8Lnmojh.js.map → std-emissive-fragment-Bj62X4Np.js.map} +1 -1
- package/{std-lightmap-fragment-DFxGcoA5.js → std-lightmap-fragment-DXvfWvKc.js} +2 -2
- package/{std-lightmap-fragment-DFxGcoA5.js.map → std-lightmap-fragment-DXvfWvKc.js.map} +1 -1
- package/{std-opacity-fragment-EXzFWiSp.js → std-opacity-fragment-BzMMb1K_.js} +2 -2
- package/{std-opacity-fragment-EXzFWiSp.js.map → std-opacity-fragment-BzMMb1K_.js.map} +1 -1
- package/{std-reflection-fragment-BoJORqpG.js → std-reflection-fragment-DC9Kvu1C.js} +2 -2
- package/{std-reflection-fragment-BoJORqpG.js.map → std-reflection-fragment-DC9Kvu1C.js.map} +1 -1
- package/{std-shadow-fragment-Bq-Wc8UJ.js → std-shadow-fragment-BnMHeF1-.js} +2 -2
- package/{std-shadow-fragment-Bq-Wc8UJ.js.map → std-shadow-fragment-BnMHeF1-.js.map} +1 -1
- package/{std-specular-fragment-CM5R5j2g.js → std-specular-fragment-Bio681OG.js} +2 -2
- package/{std-specular-fragment-CM5R5j2g.js.map → std-specular-fragment-Bio681OG.js.map} +1 -1
- package/{std-tracking-Cif_wXeT.js → std-tracking-BTcrry2o.js} +2 -2
- package/{std-tracking-Cif_wXeT.js.map → std-tracking-BTcrry2o.js.map} +1 -1
- package/{subsurface-fragment-BEaAXYXz.js → subsurface-fragment-DpKib445.js} +2 -2
- package/{subsurface-fragment-BEaAXYXz.js.map → subsurface-fragment-DpKib445.js.map} +1 -1
- package/{thin-instance-cull-binding-DWKUt5ZN.js → thin-instance-cull-binding-DwZi7mlE.js} +3 -3
- package/{thin-instance-cull-binding-DWKUt5ZN.js.map → thin-instance-cull-binding-DwZi7mlE.js.map} +1 -1
- package/{thin-instance-gpu-BDdRcNAh.js → thin-instance-gpu-uY2NOv0J.js} +2 -2
- package/{thin-instance-gpu-BDdRcNAh.js.map → thin-instance-gpu-uY2NOv0J.js.map} +1 -1
- package/{tracking-primitives-CglRNTlX.js → tracking-primitives-Ck5bgCuo.js} +2 -2
- package/{tracking-primitives-CglRNTlX.js.map → tracking-primitives-Ck5bgCuo.js.map} +1 -1
- package/{unlit-fragment-kxfZWlnp.js → unlit-fragment-nc6hu3Mw.js} +2 -2
- package/{unlit-fragment-kxfZWlnp.js.map → unlit-fragment-nc6hu3Mw.js.map} +1 -1
- package/gltf-ext-basisu-CDbPclzZ.js.map +0 -1
- package/index-CLElg2Bo.js.map +0 -1
- package/scene-material-swap-nNUH4nGn.js.map +0 -1
- package/shader-renderable-D7-RyVxa.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gpu-task-timer-Dgkff80h.js","sources":["../src/engine/gpu-task-timer.ts"],"sourcesContent":["import type { EngineContext, RenderingContext } from \"./engine.js\";\nimport type { FrameGraph } from \"../frame-graph/frame-graph.js\";\nimport type { Task } from \"../frame-graph/task.js\";\nimport type { SurfaceContext } from \"./surface.js\";\nimport { makeTimingSnapshot, type RenderTaskGpuTiming, type RenderTaskGpuTimings } from \"./gpu-task-timing.js\";\n\nconst INITIAL_TASK_CAPACITY = 64;\nconst MAX_IN_FLIGHT_READBACKS = 3;\n\ninterface TaskTimingRecord {\n readonly index: number;\n readonly name: string;\n readonly beginQueryIndex: number;\n readonly endQueryIndex: number;\n}\n\ninterface PendingTaskTimingReadback {\n readonly buffer: GPUBuffer;\n readonly byteLength: number;\n readonly frameIndex: number;\n readonly records: readonly TaskTimingRecord[];\n readonly droppedTaskCount: number;\n readonly publish: (snapshot: RenderTaskGpuTimings) => void;\n}\n\ninterface WrappedFrameGraph {\n readonly graph: FrameGraph;\n readonly execute: () => number;\n}\n\ninterface PatchedContextList {\n readonly list: RenderingContext[];\n readonly push: (...items: RenderingContext[]) => number;\n}\n\ninterface PatchedSurfaceList {\n readonly list: SurfaceContext[];\n readonly push: (...items: SurfaceContext[]) => number;\n}\n\n/** @internal GPU resources/state for opt-in per-frame-graph-task timestamp queries. */\nexport interface GpuTaskTimer {\n readonly device: GPUDevice;\n readonly querySet: GPUQuerySet;\n readonly resolveBuffer: GPUBuffer;\n readonly readbackPool: GPUBuffer[];\n readonly records: TaskTimingRecord[];\n readonly wrappedGraphs: WrappedFrameGraph[];\n readonly patchedContextLists: PatchedContextList[];\n readonly patchedSurfaceLists: PatchedSurfaceList[];\n readonly taskCapacity: number;\n currentEncoder: GPUCommandEncoder | null;\n frameIndex: number;\n droppedTaskCount: number;\n inFlight: number;\n skipFrame: boolean;\n}\n\n/** Create the per-task GPU timer, or null when timestamp queries are unsupported. */\nexport function createGpuTaskTimer(device: GPUDevice): GpuTaskTimer | null {\n if (!device.features.has(\"timestamp-query\")) {\n return null;\n }\n const queryCount = INITIAL_TASK_CAPACITY * 2;\n return {\n device,\n querySet: device.createQuerySet({ type: \"timestamp\", count: queryCount }),\n resolveBuffer: device.createBuffer({\n size: queryCount * 8,\n usage: GPUBufferUsage.QUERY_RESOLVE | GPUBufferUsage.COPY_SRC,\n }),\n readbackPool: [],\n records: [],\n wrappedGraphs: [],\n patchedContextLists: [],\n patchedSurfaceLists: [],\n taskCapacity: INITIAL_TASK_CAPACITY,\n currentEncoder: null,\n frameIndex: 0,\n droppedTaskCount: 0,\n inFlight: 0,\n skipFrame: false,\n };\n}\n\n/** Install timed frame-graph execute wrappers on all contexts currently registered with the engine. */\nexport function installGpuTaskTimer(timer: GpuTaskTimer, engine: EngineContext, publish: (snapshot: RenderTaskGpuTimings) => void): () => void {\n patchSurfaceList(timer, engine._surfaces);\n for (const surface of engine.surfaces) {\n patchSurface(timer, surface);\n }\n const previousResolve = engine._gpuTimerResolve;\n const resolveTaskTiming = () => finishTaskTimingFrame(timer, publish);\n const resolveBoth = () => {\n previousResolve?.();\n resolveTaskTiming();\n };\n engine._gpuTaskTimerResolve = resolveTaskTiming;\n engine._gpuTimerResolve = resolveBoth;\n return () => restoreWrappedFrameGraphs(timer, engine, previousResolve, resolveTaskTiming, resolveBoth);\n}\n\nfunction patchSurfaceList(timer: GpuTaskTimer, list: SurfaceContext[]): void {\n for (const patched of timer.patchedSurfaceLists) {\n if (patched.list === list) {\n return;\n }\n }\n const push = list.push;\n list.push = (...items: SurfaceContext[]) => {\n const length = push.apply(list, items);\n for (const surface of items) {\n patchSurface(timer, surface);\n }\n return length;\n };\n timer.patchedSurfaceLists.push({ list, push });\n}\n\nfunction patchSurface(timer: GpuTaskTimer, surface: SurfaceContext): void {\n const contexts = surface._renderingContexts;\n patchContextList(timer, contexts);\n for (const context of contexts) {\n const graph = getFrameGraphFromContext(context);\n if (graph) {\n wrapFrameGraph(timer, graph);\n }\n }\n}\n\nfunction patchContextList(timer: GpuTaskTimer, list: RenderingContext[]): void {\n for (const patched of timer.patchedContextLists) {\n if (patched.list === list) {\n return;\n }\n }\n const push = list.push;\n list.push = (...items: RenderingContext[]) => {\n const length = push.apply(list, items);\n for (const context of items) {\n const graph = getFrameGraphFromContext(context);\n if (graph) {\n wrapFrameGraph(timer, graph);\n }\n }\n return length;\n };\n timer.patchedContextLists.push({ list, push });\n}\n\nfunction wrapFrameGraph(timer: GpuTaskTimer, graph: FrameGraph): void {\n for (const wrapped of timer.wrappedGraphs) {\n if (wrapped.graph === graph) {\n return;\n }\n }\n const original = graph.execute;\n const timed = () => executeTimedFrameGraph(timer, graph);\n graph.execute = timed;\n timer.wrappedGraphs.push({ graph, execute: original });\n}\n\nfunction restoreWrappedFrameGraphs(\n timer: GpuTaskTimer,\n engine: EngineContext,\n previousResolve: (() => void) | undefined,\n resolveTaskTiming: () => void,\n resolveBoth: () => void\n): void {\n for (const patched of timer.patchedSurfaceLists) {\n patched.list.push = patched.push;\n }\n timer.patchedSurfaceLists.length = 0;\n for (const patched of timer.patchedContextLists) {\n patched.list.push = patched.push;\n }\n timer.patchedContextLists.length = 0;\n for (const wrapped of timer.wrappedGraphs) {\n wrapped.graph.execute = wrapped.execute;\n }\n timer.wrappedGraphs.length = 0;\n timer.currentEncoder = null;\n if (engine._gpuTaskTimerResolve === resolveTaskTiming) {\n engine._gpuTaskTimerResolve = undefined;\n }\n if (engine._gpuTimerResolve === resolveBoth) {\n engine._gpuTimerResolve = previousResolve;\n } else if (engine._gpuTimerResolve === resolveTaskTiming) {\n engine._gpuTimerResolve = undefined;\n }\n}\n\nfunction getFrameGraphFromContext(context: RenderingContext): FrameGraph | null {\n const owner = context as RenderingContext & { _frameGraph?: unknown; frameGraph?: unknown };\n const graph = owner._frameGraph ?? owner.frameGraph;\n return isFrameGraph(graph) ? graph : null;\n}\n\nfunction isFrameGraph(value: unknown): value is FrameGraph {\n return typeof value === \"object\" && value !== null && \"_tasks\" in value && \"execute\" in value;\n}\n\nfunction executeTimedFrameGraph(timer: GpuTaskTimer, graph: FrameGraph): number {\n let drawCalls = 0;\n for (const task of graph._tasks) {\n drawCalls += gpuTaskTimerExecute(timer, task);\n }\n return drawCalls;\n}\n\n/** Execute one frame-graph task bracketed by timestamp writes. */\nfunction gpuTaskTimerExecute(timer: GpuTaskTimer, task: Task): number {\n const encoder = task.engine._currentEncoder;\n if (timer.currentEncoder !== encoder) {\n beginTaskTimingFrame(timer, encoder);\n }\n if (timer.skipFrame) {\n return executeTask(task);\n }\n\n const measuredCount = timer.records.length;\n const taskIndex = measuredCount + timer.droppedTaskCount;\n if (measuredCount >= timer.taskCapacity) {\n timer.droppedTaskCount++;\n return executeTask(task);\n }\n const beginQueryIndex = measuredCount * 2;\n const endQueryIndex = beginQueryIndex + 1;\n encoder.beginComputePass({ timestampWrites: { querySet: timer.querySet, beginningOfPassWriteIndex: beginQueryIndex } }).end();\n const drawCalls = executeTask(task);\n encoder.beginComputePass({ timestampWrites: { querySet: timer.querySet, endOfPassWriteIndex: endQueryIndex } }).end();\n timer.records.push({ index: taskIndex, name: task.name, beginQueryIndex, endQueryIndex });\n return drawCalls;\n}\n\nfunction executeTask(task: Task): number {\n if (task.execute) {\n return task.execute();\n }\n let drawCalls = 0;\n for (const pass of task._passes) {\n drawCalls += pass._execute();\n }\n return drawCalls;\n}\n\nfunction beginTaskTimingFrame(timer: GpuTaskTimer, encoder: GPUCommandEncoder): void {\n timer.currentEncoder = encoder;\n timer.records.length = 0;\n timer.droppedTaskCount = 0;\n timer.skipFrame = timer.inFlight > MAX_IN_FLIGHT_READBACKS;\n}\n\n/** Resolve this frame's task timestamps after renderFrame has submitted the command buffer. */\nfunction finishTaskTimingFrame(timer: GpuTaskTimer, publish: (snapshot: RenderTaskGpuTimings) => void): void {\n timer.frameIndex++;\n const taskCount = timer.records.length;\n if (timer.skipFrame || taskCount === 0 || timer.inFlight > MAX_IN_FLIGHT_READBACKS) {\n return;\n }\n\n const queryCount = taskCount * 2;\n const byteLength = queryCount * 8;\n const readback = timer.readbackPool.pop() ?? createReadbackBuffer(timer);\n const encoder = timer.device.createCommandEncoder({ label: \"gpu-task-timing-resolve\" });\n encoder.resolveQuerySet(timer.querySet, 0, queryCount, timer.resolveBuffer, 0);\n encoder.copyBufferToBuffer(timer.resolveBuffer, 0, readback, 0, byteLength);\n timer.device.queue.submit([encoder.finish()]);\n timer.inFlight++;\n void finishTaskTimingReadback(timer, {\n buffer: readback,\n byteLength,\n frameIndex: timer.frameIndex,\n records: timer.records.slice(),\n droppedTaskCount: timer.droppedTaskCount,\n publish,\n });\n}\n\nfunction createReadbackBuffer(timer: GpuTaskTimer): GPUBuffer {\n return timer.device.createBuffer({\n size: timer.taskCapacity * 16,\n usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,\n });\n}\n\nasync function finishTaskTimingReadback(timer: GpuTaskTimer, pending: PendingTaskTimingReadback): Promise<void> {\n const buffer = pending.buffer;\n try {\n // Let the resolve/copy submit leave the JavaScript stack before mapping.\n await Promise.resolve();\n await buffer.mapAsync(GPUMapMode.READ, 0, pending.byteLength);\n const raw = new BigUint64Array(buffer.getMappedRange(0, pending.byteLength));\n const tasks: RenderTaskGpuTiming[] = [];\n for (const record of pending.records) {\n const begin = raw[record.beginQueryIndex]!;\n const end = raw[record.endQueryIndex]!;\n if (end >= begin) {\n tasks.push({ index: record.index, name: record.name, durationMs: Number(end - begin) / 1e6 });\n }\n }\n buffer.unmap();\n timer.readbackPool.push(buffer);\n timer.inFlight--;\n pending.publish(makeTimingSnapshot(\"available\", true, true, pending.frameIndex, tasks, pending.droppedTaskCount));\n } catch (error) {\n timer.inFlight--;\n buffer.destroy();\n pending.publish(makeTimingSnapshot(\"error\", true, true, pending.frameIndex, [], pending.droppedTaskCount, readbackErrorMessage(error)));\n }\n}\n\nfunction readbackErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"],"names":[],"mappings":";AAMA,MAAM,wBAAwB;AAC9B,MAAM,0BAA0B;AAoDzB,SAAS,mBAAmB,QAAwC;AACvE,MAAI,CAAC,OAAO,SAAS,IAAI,iBAAiB,GAAG;AACzC,WAAO;AAAA,EACX;AACA,QAAM,aAAa,wBAAwB;AAC3C,SAAO;AAAA,IACH;AAAA,IACA,UAAU,OAAO,eAAe,EAAE,MAAM,aAAa,OAAO,YAAY;AAAA,IACxE,eAAe,OAAO,aAAa;AAAA,MAC/B,MAAM,aAAa;AAAA,MACnB,OAAO,eAAe,gBAAgB,eAAe;AAAA,IAAA,CACxD;AAAA,IACD,cAAc,CAAA;AAAA,IACd,SAAS,CAAA;AAAA,IACT,eAAe,CAAA;AAAA,IACf,qBAAqB,CAAA;AAAA,IACrB,qBAAqB,CAAA;AAAA,IACrB,cAAc;AAAA,IACd,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,WAAW;AAAA,EAAA;AAEnB;AAGO,SAAS,oBAAoB,OAAqB,QAAuB,SAA+D;AAC3I,mBAAiB,OAAO,OAAO,SAAS;AACxC,aAAW,WAAW,OAAO,UAAU;AACnC,iBAAa,OAAO,OAAO;AAAA,EAC/B;AACA,QAAM,kBAAkB,OAAO;AAC/B,QAAM,oBAAoB,MAAM,sBAAsB,OAAO,OAAO;AACpE,QAAM,cAAc,MAAM;AACtB;AACA,sBAAA;AAAA,EACJ;AACA,SAAO,uBAAuB;AAC9B,SAAO,mBAAmB;AAC1B,SAAO,MAAM,0BAA0B,OAAO,QAAQ,iBAAiB,mBAAmB,WAAW;AACzG;AAEA,SAAS,iBAAiB,OAAqB,MAA8B;AACzE,aAAW,WAAW,MAAM,qBAAqB;AAC7C,QAAI,QAAQ,SAAS,MAAM;AACvB;AAAA,IACJ;AAAA,EACJ;AACA,QAAM,OAAO,KAAK;AAClB,OAAK,OAAO,IAAI,UAA4B;AACxC,UAAM,SAAS,KAAK,MAAM,MAAM,KAAK;AACrC,eAAW,WAAW,OAAO;AACzB,mBAAa,OAAO,OAAO;AAAA,IAC/B;AACA,WAAO;AAAA,EACX;AACA,QAAM,oBAAoB,KAAK,EAAE,MAAM,MAAM;AACjD;AAEA,SAAS,aAAa,OAAqB,SAA+B;AACtE,QAAM,WAAW,QAAQ;AACzB,mBAAiB,OAAO,QAAQ;AAChC,aAAW,WAAW,UAAU;AAC5B,UAAM,QAAQ,yBAAyB,OAAO;AAC9C,QAAI,OAAO;AACP,qBAAe,OAAO,KAAK;AAAA,IAC/B;AAAA,EACJ;AACJ;AAEA,SAAS,iBAAiB,OAAqB,MAAgC;AAC3E,aAAW,WAAW,MAAM,qBAAqB;AAC7C,QAAI,QAAQ,SAAS,MAAM;AACvB;AAAA,IACJ;AAAA,EACJ;AACA,QAAM,OAAO,KAAK;AAClB,OAAK,OAAO,IAAI,UAA8B;AAC1C,UAAM,SAAS,KAAK,MAAM,MAAM,KAAK;AACrC,eAAW,WAAW,OAAO;AACzB,YAAM,QAAQ,yBAAyB,OAAO;AAC9C,UAAI,OAAO;AACP,uBAAe,OAAO,KAAK;AAAA,MAC/B;AAAA,IACJ;AACA,WAAO;AAAA,EACX;AACA,QAAM,oBAAoB,KAAK,EAAE,MAAM,MAAM;AACjD;AAEA,SAAS,eAAe,OAAqB,OAAyB;AAClE,aAAW,WAAW,MAAM,eAAe;AACvC,QAAI,QAAQ,UAAU,OAAO;AACzB;AAAA,IACJ;AAAA,EACJ;AACA,QAAM,WAAW,MAAM;AACvB,QAAM,QAAQ,MAAM,uBAAuB,OAAO,KAAK;AACvD,QAAM,UAAU;AAChB,QAAM,cAAc,KAAK,EAAE,OAAO,SAAS,UAAU;AACzD;AAEA,SAAS,0BACL,OACA,QACA,iBACA,mBACA,aACI;AACJ,aAAW,WAAW,MAAM,qBAAqB;AAC7C,YAAQ,KAAK,OAAO,QAAQ;AAAA,EAChC;AACA,QAAM,oBAAoB,SAAS;AACnC,aAAW,WAAW,MAAM,qBAAqB;AAC7C,YAAQ,KAAK,OAAO,QAAQ;AAAA,EAChC;AACA,QAAM,oBAAoB,SAAS;AACnC,aAAW,WAAW,MAAM,eAAe;AACvC,YAAQ,MAAM,UAAU,QAAQ;AAAA,EACpC;AACA,QAAM,cAAc,SAAS;AAC7B,QAAM,iBAAiB;AACvB,MAAI,OAAO,yBAAyB,mBAAmB;AACnD,WAAO,uBAAuB;AAAA,EAClC;AACA,MAAI,OAAO,qBAAqB,aAAa;AACzC,WAAO,mBAAmB;AAAA,EAC9B,WAAW,OAAO,qBAAqB,mBAAmB;AACtD,WAAO,mBAAmB;AAAA,EAC9B;AACJ;AAEA,SAAS,yBAAyB,SAA8C;AAC5E,QAAM,QAAQ;AACd,QAAM,QAAQ,MAAM,eAAe,MAAM;AACzC,SAAO,aAAa,KAAK,IAAI,QAAQ;AACzC;AAEA,SAAS,aAAa,OAAqC;AACvD,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,YAAY,SAAS,aAAa;AAC5F;AAEA,SAAS,uBAAuB,OAAqB,OAA2B;AAC5E,MAAI,YAAY;AAChB,aAAW,QAAQ,MAAM,QAAQ;AAC7B,iBAAa,oBAAoB,OAAO,IAAI;AAAA,EAChD;AACA,SAAO;AACX;AAGA,SAAS,oBAAoB,OAAqB,MAAoB;AAClE,QAAM,UAAU,KAAK,OAAO;AAC5B,MAAI,MAAM,mBAAmB,SAAS;AAClC,yBAAqB,OAAO,OAAO;AAAA,EACvC;AACA,MAAI,MAAM,WAAW;AACjB,WAAO,YAAY,IAAI;AAAA,EAC3B;AAEA,QAAM,gBAAgB,MAAM,QAAQ;AACpC,QAAM,YAAY,gBAAgB,MAAM;AACxC,MAAI,iBAAiB,MAAM,cAAc;AACrC,UAAM;AACN,WAAO,YAAY,IAAI;AAAA,EAC3B;AACA,QAAM,kBAAkB,gBAAgB;AACxC,QAAM,gBAAgB,kBAAkB;AACxC,UAAQ,iBAAiB,EAAE,iBAAiB,EAAE,UAAU,MAAM,UAAU,2BAA2B,kBAAgB,CAAG,EAAE,IAAA;AACxH,QAAM,YAAY,YAAY,IAAI;AAClC,UAAQ,iBAAiB,EAAE,iBAAiB,EAAE,UAAU,MAAM,UAAU,qBAAqB,gBAAc,CAAG,EAAE,IAAA;AAChH,QAAM,QAAQ,KAAK,EAAE,OAAO,WAAW,MAAM,KAAK,MAAM,iBAAiB,cAAA,CAAe;AACxF,SAAO;AACX;AAEA,SAAS,YAAY,MAAoB;AACrC,MAAI,KAAK,SAAS;AACd,WAAO,KAAK,QAAA;AAAA,EAChB;AACA,MAAI,YAAY;AAChB,aAAW,QAAQ,KAAK,SAAS;AAC7B,iBAAa,KAAK,SAAA;AAAA,EACtB;AACA,SAAO;AACX;AAEA,SAAS,qBAAqB,OAAqB,SAAkC;AACjF,QAAM,iBAAiB;AACvB,QAAM,QAAQ,SAAS;AACvB,QAAM,mBAAmB;AACzB,QAAM,YAAY,MAAM,WAAW;AACvC;AAGA,SAAS,sBAAsB,OAAqB,SAAyD;AACzG,QAAM;AACN,QAAM,YAAY,MAAM,QAAQ;AAChC,MAAI,MAAM,aAAa,cAAc,KAAK,MAAM,WAAW,yBAAyB;AAChF;AAAA,EACJ;AAEA,QAAM,aAAa,YAAY;AAC/B,QAAM,aAAa,aAAa;AAChC,QAAM,WAAW,MAAM,aAAa,IAAA,KAAS,qBAAqB,KAAK;AACvE,QAAM,UAAU,MAAM,OAAO,qBAAqB,EAAE,OAAO,2BAA2B;AACtF,UAAQ,gBAAgB,MAAM,UAAU,GAAG,YAAY,MAAM,eAAe,CAAC;AAC7E,UAAQ,mBAAmB,MAAM,eAAe,GAAG,UAAU,GAAG,UAAU;AAC1E,QAAM,OAAO,MAAM,OAAO,CAAC,QAAQ,OAAA,CAAQ,CAAC;AAC5C,QAAM;AACN,OAAK,yBAAyB,OAAO;AAAA,IACjC,QAAQ;AAAA,IACR;AAAA,IACA,YAAY,MAAM;AAAA,IAClB,SAAS,MAAM,QAAQ,MAAA;AAAA,IACvB,kBAAkB,MAAM;AAAA,IACxB;AAAA,EAAA,CACH;AACL;AAEA,SAAS,qBAAqB,OAAgC;AAC1D,SAAO,MAAM,OAAO,aAAa;AAAA,IAC7B,MAAM,MAAM,eAAe;AAAA,IAC3B,OAAO,eAAe,WAAW,eAAe;AAAA,EAAA,CACnD;AACL;AAEA,eAAe,yBAAyB,OAAqB,SAAmD;AAC5G,QAAM,SAAS,QAAQ;AACvB,MAAI;AAEA,UAAM,QAAQ,QAAA;AACd,UAAM,OAAO,SAAS,WAAW,MAAM,GAAG,QAAQ,UAAU;AAC5D,UAAM,MAAM,IAAI,eAAe,OAAO,eAAe,GAAG,QAAQ,UAAU,CAAC;AAC3E,UAAM,QAA+B,CAAA;AACrC,eAAW,UAAU,QAAQ,SAAS;AAClC,YAAM,QAAQ,IAAI,OAAO,eAAe;AACxC,YAAM,MAAM,IAAI,OAAO,aAAa;AACpC,UAAI,OAAO,OAAO;AACd,cAAM,KAAK,EAAE,OAAO,OAAO,OAAO,MAAM,OAAO,MAAM,YAAY,OAAO,MAAM,KAAK,IAAI,KAAK;AAAA,MAChG;AAAA,IACJ;AACA,WAAO,MAAA;AACP,UAAM,aAAa,KAAK,MAAM;AAC9B,UAAM;AACN,YAAQ,QAAQ,mBAAmB,aAAa,MAAM,MAAM,QAAQ,YAAY,OAAO,QAAQ,gBAAgB,CAAC;AAAA,EACpH,SAAS,OAAO;AACZ,UAAM;AACN,WAAO,QAAA;AACP,YAAQ,QAAQ,mBAAmB,SAAS,MAAM,MAAM,QAAQ,YAAY,CAAA,GAAI,QAAQ,kBAAkB,qBAAqB,KAAK,CAAC,CAAC;AAAA,EAC1I;AACJ;AAEA,SAAS,qBAAqB,OAAwB;AAClD,SAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAChE;"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
function gpuTimingSupportedFor(device) {
|
|
2
|
+
return device.features.has("timestamp-query");
|
|
3
|
+
}
|
|
4
|
+
function createGpuFrameTimer(device) {
|
|
5
|
+
if (!gpuTimingSupportedFor(device)) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
const querySet = device.createQuerySet({ type: "timestamp", count: 2 });
|
|
9
|
+
const resolveBuf = device.createBuffer({
|
|
10
|
+
size: 16,
|
|
11
|
+
usage: GPUBufferUsage.QUERY_RESOLVE | GPUBufferUsage.COPY_SRC
|
|
12
|
+
});
|
|
13
|
+
return { device, querySet, resolveBuf, pool: [], lastMs: 0, inFlight: 0 };
|
|
14
|
+
}
|
|
15
|
+
function gpuFrameTimerBegin(timer, encoder) {
|
|
16
|
+
encoder.beginComputePass({ timestampWrites: { querySet: timer.querySet, beginningOfPassWriteIndex: 0 } }).end();
|
|
17
|
+
}
|
|
18
|
+
function gpuFrameTimerEnd(timer, encoder) {
|
|
19
|
+
encoder.beginComputePass({ timestampWrites: { querySet: timer.querySet, endOfPassWriteIndex: 1 } }).end();
|
|
20
|
+
}
|
|
21
|
+
function gpuFrameTimerResolve(timer) {
|
|
22
|
+
if (timer.inFlight > 3) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const dev = timer.device;
|
|
26
|
+
const rb = timer.pool.pop() ?? dev.createBuffer({ size: 16, usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ });
|
|
27
|
+
const enc = dev.createCommandEncoder();
|
|
28
|
+
enc.resolveQuerySet(timer.querySet, 0, 2, timer.resolveBuf, 0);
|
|
29
|
+
enc.copyBufferToBuffer(timer.resolveBuf, 0, rb, 0, 16);
|
|
30
|
+
dev.queue.submit([enc.finish()]);
|
|
31
|
+
timer.inFlight++;
|
|
32
|
+
rb.mapAsync(GPUMapMode.READ).then(
|
|
33
|
+
() => {
|
|
34
|
+
const a = new BigInt64Array(rb.getMappedRange());
|
|
35
|
+
const ms = Number(a[1] - a[0]) / 1e6;
|
|
36
|
+
if (ms >= 0 && ms < 5e3) {
|
|
37
|
+
timer.lastMs = timer.lastMs > 0 ? timer.lastMs * 0.8 + ms * 0.2 : ms;
|
|
38
|
+
}
|
|
39
|
+
rb.unmap();
|
|
40
|
+
timer.pool.push(rb);
|
|
41
|
+
timer.inFlight--;
|
|
42
|
+
},
|
|
43
|
+
() => {
|
|
44
|
+
timer.inFlight--;
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
export {
|
|
49
|
+
createGpuFrameTimer,
|
|
50
|
+
gpuFrameTimerBegin,
|
|
51
|
+
gpuFrameTimerEnd,
|
|
52
|
+
gpuFrameTimerResolve,
|
|
53
|
+
gpuTimingSupportedFor
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=gpu-timer-CUpqT_hK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gpu-timer-CUpqT_hK.js","sources":["../src/engine/gpu-timer.ts"],"sourcesContent":["// engine/gpu-timer.ts — optional GPU frame-time measurement.\n//\n// Measures how long the GPU spends on a frame using a WebGPU `timestamp-query`. Two EMPTY pass attachments\n// bracket the frame's recorded work: `gpuFrameTimerBegin` records an empty compute pass (as the encoder's first\n// command) whose BEGINNING timestamp is its slot-0 write, and `gpuFrameTimerEnd` records an empty compute pass\n// (as the last command) whose END timestamp is its slot-1 write — so the GPU runs them contiguously around\n// exactly that frame's passes, measuring the frame's GPU work, not the CPU time spent recording it. This uses\n// the STANDARD `timestampWrites` pass attachments (NOT the legacy `GPUCommandEncoder.writeTimestamp`, which\n// Chromium exposes only behind `--enable-unsafe-webgpu`), so it works in stock Chrome/Edge whenever the adapter\n// offers the `timestamp-query` feature. After the frame is submitted, `gpuFrameTimerResolve` copies the pair\n// into a mapped readback buffer ASYNCHRONOUSLY — off the render critical path — so the measurement barely\n// perturbs the number it reports (no pipeline stall, no per-draw cost).\n//\n// Entirely opt-in: the timer is only created on first enable, its per-frame hooks are installed only while\n// timing is on (so nothing is written while disabled), and the whole feature degrades to a no-op on\n// adapters lacking the `timestamp-query` feature. renderFrame ships only three optional-chain short-circuits.\n// Pure state + free functions (no methods, no import side effects) per the engine's data-oriented style.\n\nexport interface GpuFrameTimer {\n readonly device: GPUDevice;\n /** Two slots: [frame begin, frame end]. */\n readonly querySet: GPUQuerySet;\n /** Destination for `resolveQuerySet` (2 × u64 = 16 bytes). */\n readonly resolveBuf: GPUBuffer;\n /** Idle MAP_READ buffers, recycled across frames so we never allocate in steady state. */\n readonly pool: GPUBuffer[];\n /** Last GPU frame time read back, in ms (0 until the first readback lands). Lightly smoothed. */\n lastMs: number;\n /** In-flight async readbacks, capped so a GPU stall can't spin up unbounded buffers. */\n inFlight: number;\n}\n\n/** Whether a device can measure GPU time — it offered the `timestamp-query` feature (which also enables the\n * standard `timestampWrites` pass attachments this timer uses; no `--enable-unsafe-webgpu` flag required). */\nexport function gpuTimingSupportedFor(device: GPUDevice): boolean {\n return device.features.has(\"timestamp-query\");\n}\n\n/** Create a GPU frame timer, or null when the device can't support timestamp queries. */\nexport function createGpuFrameTimer(device: GPUDevice): GpuFrameTimer | null {\n if (!gpuTimingSupportedFor(device)) {\n return null;\n }\n const querySet = device.createQuerySet({ type: \"timestamp\", count: 2 });\n const resolveBuf = device.createBuffer({\n size: 16,\n usage: GPUBufferUsage.QUERY_RESOLVE | GPUBufferUsage.COPY_SRC,\n });\n return { device, querySet, resolveBuf, pool: [], lastMs: 0, inFlight: 0 };\n}\n\n/** Write the frame's opening timestamp into the frame encoder (call right after it is created, before any\n * passes are recorded), so it bookends the start of this frame's GPU work. Uses the STANDARD `timestampWrites`\n * attachment (no `GPUCommandEncoder.writeTimestamp`, which Chromium gates behind `--enable-unsafe-webgpu`): an\n * EMPTY compute pass whose BEGINNING timestamp lands in slot 0. Empty pass = no dispatch, negligible cost. */\nexport function gpuFrameTimerBegin(timer: GpuFrameTimer, encoder: GPUCommandEncoder): void {\n encoder.beginComputePass({ timestampWrites: { querySet: timer.querySet, beginningOfPassWriteIndex: 0 } }).end();\n}\n\n/** Write the frame's closing timestamp into the frame encoder (call right before it is finished/submitted), so\n * it bookends the end of this frame's GPU work — an EMPTY compute pass whose END timestamp lands in slot 1.\n * slot1 − slot0 is then the GPU time of exactly the passes recorded between the two empty passes. */\nexport function gpuFrameTimerEnd(timer: GpuFrameTimer, encoder: GPUCommandEncoder): void {\n encoder.beginComputePass({ timestampWrites: { querySet: timer.querySet, endOfPassWriteIndex: 1 } }).end();\n}\n\n/** Resolve the just-submitted timestamp pair and update `lastMs` when the readback maps. Submitted as its\n * own tiny command buffer AFTER the frame's submit, so the bracketing timestamps are already written; the\n * recycled MAP_READ buffer is mapped asynchronously, so reading never stalls the frame. */\nexport function gpuFrameTimerResolve(timer: GpuFrameTimer): void {\n if (timer.inFlight > 3) {\n return; // GPU/readback is lagging — skip this sample rather than allocate more buffers\n }\n const dev = timer.device;\n const rb = timer.pool.pop() ?? dev.createBuffer({ size: 16, usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ });\n const enc = dev.createCommandEncoder();\n enc.resolveQuerySet(timer.querySet, 0, 2, timer.resolveBuf, 0);\n enc.copyBufferToBuffer(timer.resolveBuf, 0, rb, 0, 16);\n dev.queue.submit([enc.finish()]);\n timer.inFlight++;\n rb.mapAsync(GPUMapMode.READ).then(\n () => {\n const a = new BigInt64Array(rb.getMappedRange());\n const ms = Number(a[1]! - a[0]!) / 1e6;\n // Guard against counter wrap / garbage (a real frame is never multiple seconds; a wrapped\n // u64 delta is wildly larger or negative), then smooth lightly so the readout is steady.\n if (ms >= 0 && ms < 5000) {\n timer.lastMs = timer.lastMs > 0 ? timer.lastMs * 0.8 + ms * 0.2 : ms;\n }\n rb.unmap();\n timer.pool.push(rb);\n timer.inFlight--;\n },\n () => {\n // Device lost / buffer destroyed — drop this readback (don't recycle a bad buffer).\n timer.inFlight--;\n }\n );\n}\n\n/** Release the timer's GPU resources. */\nexport function destroyGpuFrameTimer(timer: GpuFrameTimer): void {\n timer.querySet.destroy();\n timer.resolveBuf.destroy();\n for (const b of timer.pool) {\n b.destroy();\n }\n timer.pool.length = 0;\n}\n"],"names":[],"mappings":"AAkCO,SAAS,sBAAsB,QAA4B;AAC9D,SAAO,OAAO,SAAS,IAAI,iBAAiB;AAChD;AAGO,SAAS,oBAAoB,QAAyC;AACzE,MAAI,CAAC,sBAAsB,MAAM,GAAG;AAChC,WAAO;AAAA,EACX;AACA,QAAM,WAAW,OAAO,eAAe,EAAE,MAAM,aAAa,OAAO,GAAG;AACtE,QAAM,aAAa,OAAO,aAAa;AAAA,IACnC,MAAM;AAAA,IACN,OAAO,eAAe,gBAAgB,eAAe;AAAA,EAAA,CACxD;AACD,SAAO,EAAE,QAAQ,UAAU,YAAY,MAAM,CAAA,GAAI,QAAQ,GAAG,UAAU,EAAA;AAC1E;AAMO,SAAS,mBAAmB,OAAsB,SAAkC;AACvF,UAAQ,iBAAiB,EAAE,iBAAiB,EAAE,UAAU,MAAM,UAAU,2BAA2B,IAAE,CAAG,EAAE,IAAA;AAC9G;AAKO,SAAS,iBAAiB,OAAsB,SAAkC;AACrF,UAAQ,iBAAiB,EAAE,iBAAiB,EAAE,UAAU,MAAM,UAAU,qBAAqB,IAAE,CAAG,EAAE,IAAA;AACxG;AAKO,SAAS,qBAAqB,OAA4B;AAC7D,MAAI,MAAM,WAAW,GAAG;AACpB;AAAA,EACJ;AACA,QAAM,MAAM,MAAM;AAClB,QAAM,KAAK,MAAM,KAAK,IAAA,KAAS,IAAI,aAAa,EAAE,MAAM,IAAI,OAAO,eAAe,WAAW,eAAe,UAAU;AACtH,QAAM,MAAM,IAAI,qBAAA;AAChB,MAAI,gBAAgB,MAAM,UAAU,GAAG,GAAG,MAAM,YAAY,CAAC;AAC7D,MAAI,mBAAmB,MAAM,YAAY,GAAG,IAAI,GAAG,EAAE;AACrD,MAAI,MAAM,OAAO,CAAC,IAAI,OAAA,CAAQ,CAAC;AAC/B,QAAM;AACN,KAAG,SAAS,WAAW,IAAI,EAAE;AAAA,IACzB,MAAM;AACF,YAAM,IAAI,IAAI,cAAc,GAAG,gBAAgB;AAC/C,YAAM,KAAK,OAAO,EAAE,CAAC,IAAK,EAAE,CAAC,CAAE,IAAI;AAGnC,UAAI,MAAM,KAAK,KAAK,KAAM;AACtB,cAAM,SAAS,MAAM,SAAS,IAAI,MAAM,SAAS,MAAM,KAAK,MAAM;AAAA,MACtE;AACA,SAAG,MAAA;AACH,YAAM,KAAK,KAAK,EAAE;AAClB,YAAM;AAAA,IACV;AAAA,IACA,MAAM;AAEF,YAAM;AAAA,IACV;AAAA,EAAA;AAER;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as BU, a as F32,
|
|
1
|
+
import { B as BU, a as F32, I as getRenderTargetSize, J as getViewMatrix, K as getProjectionMatrix, X as encodeIdToColor, S as SS, Y as getPickingSceneBGL, E as applyGsFragments, Z as gsGpuPickingFragment } from "./index-C-tEgwbZ.js";
|
|
2
2
|
let _cache = null;
|
|
3
3
|
function buildPickingWgsl() {
|
|
4
4
|
const wgsl = (
|
|
@@ -277,4 +277,4 @@ export {
|
|
|
277
277
|
drawGsForPicking,
|
|
278
278
|
gsPickWritePickMatrixAndBind
|
|
279
279
|
};
|
|
280
|
-
//# sourceMappingURL=gs-picking-pipeline-
|
|
280
|
+
//# sourceMappingURL=gs-picking-pipeline-55sM5LzV.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gs-picking-pipeline-DYaW_Lg3.js","sources":["../src/picking/gs-picking-pipeline.ts"],"sourcesContent":["/** GS GPU-picking pipeline — dynamic-imported by `gpu-picker.ts` when a scene\n * contains a Gaussian-Splatting mesh.\n *\n * The pipeline is a variant of the base Gaussian-splatting render pipeline\n * (`gaussian-splatting.wgsl`) with three modifications, applied via string\n * substitution + the `applyGsFragments` plugin system so the base shader stays\n * untouched:\n *\n * 1. A scene UBO at `@group(0) @binding(0)` carries a `pickMatrix`\n * (column-major 4x4) that zooms NDC onto the pick pixel — same maths as\n * `computePickVP` in `gpu-picker.ts`, but applied *after* the GS-specific\n * projection so the EWA Jacobian / `u.focal` math is untouched.\n * 2. The fragment returns `FsOut { @location(0) color, @location(1) depth }`\n * so the picker can read a pick id + NDC z from the 1×1 render target.\n * 3. `gsGpuPickingFragment` is applied (via `applyGsFragments`) to inject the\n * per-mesh `pickingColor` UBO at `@group(2) @binding(0)` and override the\n * fragment color with the encoded pick id. This mirrors BJS\n * `GaussianSplattingGpuPickingMaterialPlugin`'s `getCustomCode`/`bindForSubMesh`\n * split.\n *\n * The pipeline has no blending and `depthWriteEnabled = true`, so the\n * closest-splat wins at each pick pixel (matching BJS GPU picker behaviour). */\n\nimport { F32 } from \"../engine/typed-arrays.js\";\nimport { BU, SS } from \"../engine/gpu-flags.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { GaussianSplattingMesh } from \"../mesh/GaussianSplatting/gaussian-splatting-mesh.js\";\nimport { applyGsFragments } from \"../mesh/GaussianSplatting/gaussian-splatting-pipeline.js\";\nimport { gsGpuPickingFragment, encodeIdToColor } from \"../mesh/GaussianSplatting/gs-gpu-picking-fragment.js\";\nimport { getPickingSceneBGL } from \"./picking-pipeline.js\";\nimport { getRenderTargetSize } from \"../engine/engine.js\";\nimport { getViewMatrix, getProjectionMatrix } from \"../camera/camera.js\";\nimport type { SceneContext } from \"../scene/scene-core.js\";\n\ninterface GsPickingCache {\n device: GPUDevice;\n pipeline: GPURenderPipeline;\n meshBGL: GPUBindGroupLayout;\n pickingBGL: GPUBindGroupLayout;\n /** Shared \"pick scene\" UBO holding the 4x4 pickMatrix (set per pick). */\n pickMatrixUbo: GPUBuffer;\n /** Bind group exposing `pickMatrixUbo` at group(0) binding(0). */\n sceneBG: GPUBindGroup;\n}\n\nlet _cache: GsPickingCache | null = null;\n\n/** Build the GS picking WGSL as a self-contained template literal.\n *\n * This is a variant of the base GS shader (`gaussian-splatting.wgsl`) with two\n * modifications baked in directly (rather than string-patched at runtime), so\n * it survives identifier-mangling minification in production bundles:\n *\n * 1. The final `out.pos` is pre-multiplied by `gsPickScene.pickMatrix` (a 4×4\n * UBO at `@group(0) @binding(0)`) — applied *after* the GS-specific\n * projection so the EWA Jacobian / `u.focal` math is untouched.\n * 2. The fragment returns `FsOut { @location(0) color, @location(1) depth }`\n * so the picker can read pick id + NDC z from the 1×1 render target.\n *\n * The fragment keeps the `/* GS_FRAGMENT_* *\\/` slot markers so\n * `gsGpuPickingFragment` is still spliced in via `applyGsFragments` to\n * override `finalColor` with the encoded pick id. Template-literal WGSL\n * goes through `minifyTemplateWgsl` (preserves block comments) in the\n * bundle pipeline, so the slot markers survive minification.\n */\nfunction buildPickingWgsl(): string {\n const wgsl = /* wgsl */ `\nstruct GsPickScene { pickMatrix: mat4x4<f32> };\n@group(0) @binding(0) var<uniform> gsPickScene: GsPickScene;\n\nstruct U {\n world: mat4x4<f32>,\n view: mat4x4<f32>,\n projection: mat4x4<f32>,\n viewport: vec2<f32>,\n focal: vec2<f32>,\n dataSize: vec2<f32>,\n alpha: f32,\n _pad: f32,\n};\n@group(1) @binding(0) var<uniform> u: U;\n@group(1) @binding(1) var samp: sampler;\n@group(1) @binding(2) var centersTex: texture_2d<f32>;\n@group(1) @binding(3) var covATex: texture_2d<f32>;\n@group(1) @binding(4) var covBTex: texture_2d<f32>;\n@group(1) @binding(5) var colorsTex: texture_2d<f32>;\n\nstruct VOut {\n @builtin(position) pos: vec4<f32>,\n @location(0) vColor: vec4<f32>,\n @location(1) vPos: vec2<f32>,\n};\n\nfn dataUv(idx: f32) -> vec2<f32> {\n let y = floor(idx / u.dataSize.x);\n let x = idx - y * u.dataSize.x;\n return vec2<f32>((x + 0.5) / u.dataSize.x, (y + 0.5) / u.dataSize.y);\n}\n\n@vertex\nfn vs(@location(0) corner: vec2<f32>, @location(1) splatIndex: f32) -> VOut {\n var out: VOut;\n let uv = dataUv(splatIndex);\n let center = textureSampleLevel(centersTex, samp, uv, 0.0).xyz;\n let color = textureSampleLevel(colorsTex, samp, uv, 0.0);\n let covA = textureSampleLevel(covATex, samp, uv, 0.0).xyz;\n let covB = textureSampleLevel(covBTex, samp, uv, 0.0).xyz;\n\n let worldPos = u.world * vec4<f32>(center, 1.0);\n let modelView = u.view * u.world;\n let camspace = u.view * worldPos;\n let pos2d = u.projection * camspace;\n\n let bounds = 1.2 * pos2d.w;\n if (pos2d.z < 0.0\n || pos2d.x < -bounds || pos2d.x > bounds\n || pos2d.y < -bounds || pos2d.y > bounds) {\n out.pos = vec4<f32>(0.0, 0.0, 2.0, 1.0);\n out.vColor = vec4<f32>(0.0);\n out.vPos = vec2<f32>(0.0);\n return out;\n }\n\n let Vrk = mat3x3<f32>(\n vec3<f32>(covA.x, covA.y, covA.z),\n vec3<f32>(covA.y, covB.x, covB.y),\n vec3<f32>(covA.z, covB.y, covB.z));\n\n let invZ = 1.0 / camspace.z;\n let invZ2 = invZ * invZ;\n let J = mat3x3<f32>(\n vec3<f32>(u.focal.x * invZ, 0.0, -u.focal.x * camspace.x * invZ2),\n vec3<f32>(0.0, u.focal.y * invZ, -u.focal.y * camspace.y * invZ2),\n vec3<f32>(0.0, 0.0, 0.0));\n\n let mv3 = mat3x3<f32>(modelView[0].xyz, modelView[1].xyz, modelView[2].xyz);\n let T = transpose(mv3) * J;\n var cov2d = transpose(T) * Vrk * T;\n\n let kernelSize: f32 = 0.3;\n cov2d[0][0] += kernelSize;\n cov2d[1][1] += kernelSize;\n\n let mid = (cov2d[0][0] + cov2d[1][1]) * 0.5;\n let dxy = (cov2d[0][0] - cov2d[1][1]) * 0.5;\n let radius = length(vec2<f32>(dxy, cov2d[0][1]));\n let epsilon: f32 = 0.0001;\n let lambda1 = mid + radius + epsilon;\n let lambda2 = mid - radius + epsilon;\n if (lambda2 < 0.0) {\n out.pos = vec4<f32>(0.0, 0.0, 2.0, 1.0);\n out.vColor = vec4<f32>(0.0);\n out.vPos = vec2<f32>(0.0);\n return out;\n }\n\n let diag = normalize(vec2<f32>(cov2d[0][1], lambda1 - cov2d[0][0]));\n let majorAxis = min(sqrt(2.0 * lambda1), 1024.0) * diag;\n let minorAxis = min(sqrt(2.0 * lambda2), 1024.0) * vec2<f32>(diag.y, -diag.x);\n\n let vCenter = pos2d.xy;\n out.pos = gsPickScene.pickMatrix * vec4<f32>(\n vCenter + (corner.x * majorAxis + corner.y * minorAxis) * pos2d.w / u.viewport,\n pos2d.z, pos2d.w);\n out.vColor = vec4<f32>(color.rgb, color.a * u.alpha);\n out.vPos = corner;\n return out;\n}\n\n/*GS_FRAGMENT_DEFINITIONS*/\nstruct FsOut { @location(0) color: vec4<f32>, @location(1) depth: vec4<f32> };\n@fragment\nfn fs(in: VOut) -> FsOut {\n /*GS_FRAGMENT_MAIN_BEGIN*/\n let A = -dot(in.vPos, in.vPos);\n var finalColor: vec4<f32>;\n if (A > -4.0) {\n let B = exp(A) * in.vColor.a;\n finalColor = vec4<f32>(in.vColor.rgb, B);\n } else {\n finalColor = vec4<f32>(0.0);\n }\n /*GS_FRAGMENT_BEFORE_FRAGCOLOR*/\n /*GS_FRAGMENT_MAIN_END*/\n return FsOut(finalColor, vec4<f32>(in.pos.z, 0.0, 0.0, 0.0));\n}\n`;\n return applyGsFragments(wgsl, [gsGpuPickingFragment]);\n}\n\nfunction getCache(engine: EngineContext): GsPickingCache {\n const device = engine._device;\n if (_cache && _cache.device === device) {\n return _cache;\n }\n const meshBGL = device.createBindGroupLayout({\n label: \"gs-picking-mesh-bgl\",\n entries: [\n { binding: 0, visibility: SS.VERTEX | SS.FRAGMENT, buffer: { type: \"uniform\" } },\n { binding: 1, visibility: SS.VERTEX, sampler: { type: \"non-filtering\" } },\n { binding: 2, visibility: SS.VERTEX, texture: { sampleType: \"unfilterable-float\" } },\n { binding: 3, visibility: SS.VERTEX, texture: { sampleType: \"unfilterable-float\" } },\n { binding: 4, visibility: SS.VERTEX, texture: { sampleType: \"unfilterable-float\" } },\n { binding: 5, visibility: SS.VERTEX, texture: { sampleType: \"unfilterable-float\" } },\n ],\n });\n const pickingBGL = device.createBindGroupLayout({\n label: \"gs-picking-pick-bgl\",\n entries: [{ binding: 0, visibility: SS.FRAGMENT, buffer: { type: \"uniform\" } }],\n });\n const module = device.createShaderModule({ label: \"gs-picking-shader\", code: buildPickingWgsl() });\n const pipeline = device.createRenderPipeline({\n label: \"gs-picking-pipeline\",\n layout: device.createPipelineLayout({ bindGroupLayouts: [getPickingSceneBGL(engine), meshBGL, pickingBGL] }),\n vertex: {\n module,\n entryPoint: \"vs\",\n buffers: [\n { arrayStride: 8, stepMode: \"vertex\", attributes: [{ shaderLocation: 0, offset: 0, format: \"float32x2\" }] },\n { arrayStride: 4, stepMode: \"instance\", attributes: [{ shaderLocation: 1, offset: 0, format: \"float32\" }] },\n ],\n },\n fragment: {\n module,\n entryPoint: \"fs\",\n targets: [{ format: \"rgba8unorm\" }, { format: \"r32float\" }],\n },\n primitive: { topology: \"triangle-list\", cullMode: \"none\" },\n depthStencil: { format: \"depth24plus\", depthCompare: \"less\", depthWriteEnabled: true },\n multisample: { count: 1 },\n });\n const pickMatrixUbo = device.createBuffer({ size: 64, usage: BU.UNIFORM | BU.COPY_DST, label: \"gs-picking-scene-ubo\" });\n const sceneBG = device.createBindGroup({\n label: \"gs-picking-scene-bg\",\n layout: getPickingSceneBGL(engine),\n entries: [{ binding: 0, resource: { buffer: pickMatrixUbo } }],\n });\n _cache = { device, pipeline, meshBGL, pickingBGL, pickMatrixUbo, sceneBG };\n return _cache;\n}\n\n/** Write a 4x4 pickMatrix into the shared scene UBO and bind group 0 on `pass`. */\nexport function gsPickWritePickMatrixAndBind(pass: GPURenderPassEncoder, engine: EngineContext, pickMatrix: Float32Array): void {\n const cache = getCache(engine);\n engine._device.queue.writeBuffer(cache.pickMatrixUbo, 0, pickMatrix.buffer, pickMatrix.byteOffset, pickMatrix.byteLength);\n pass.setBindGroup(0, cache.sceneBG);\n}\n\n/** Per-GS-mesh state allocated on first pick of that mesh. */\nexport interface GsPickMeshResources {\n /** Per-mesh UBO (same 224-byte layout as the regular GS pipeline UBO). */\n meshUbo: GPUBuffer;\n /** Group 1 bind group: per-mesh UBO + sampler + 4 textures. */\n meshBG: GPUBindGroup;\n /** Per-pick picking-color UBO (16 bytes: vec3<f32> + pad). */\n pickingUbo: GPUBuffer;\n /** Group 2 bind group with the picking-color UBO. */\n pickingBG: GPUBindGroup;\n /** Scratch buffer for the per-mesh UBO. */\n meshCpu: Float32Array;\n /** Scratch buffer for the picking-color UBO. */\n pickingCpu: Float32Array;\n}\n\nexport function createGsPickMeshResources(engine: EngineContext, mesh: GaussianSplattingMesh): GsPickMeshResources {\n const device = engine._device;\n const cache = getCache(engine);\n\n const UBO_BYTES = 16 * 4 * 3 + 8 * 4;\n const meshUbo = device.createBuffer({ size: UBO_BYTES, usage: BU.UNIFORM | BU.COPY_DST, label: \"gs-picking-mesh-ubo\" });\n const meshCpu = new F32(UBO_BYTES / 4);\n meshCpu[48 + 4] = mesh.textureWidth;\n meshCpu[48 + 5] = mesh.textureHeight;\n meshCpu[48 + 6] = 1;\n\n const meshBG = device.createBindGroup({\n label: \"gs-picking-mesh-bg\",\n layout: cache.meshBGL,\n entries: [\n { binding: 0, resource: { buffer: meshUbo } },\n { binding: 1, resource: mesh._gs._sampler },\n { binding: 2, resource: mesh._gs._centersView },\n { binding: 3, resource: mesh._gs._covAView },\n { binding: 4, resource: mesh._gs._covBView },\n { binding: 5, resource: mesh._gs._colorsView },\n ],\n });\n\n const pickingUbo = device.createBuffer({ size: 16, usage: BU.UNIFORM | BU.COPY_DST, label: \"gs-picking-color-ubo\" });\n const pickingCpu = new F32(4);\n\n const pickingBG = device.createBindGroup({\n label: \"gs-picking-color-bg\",\n layout: cache.pickingBGL,\n entries: [{ binding: 0, resource: { buffer: pickingUbo } }],\n });\n\n return { meshUbo, meshBG, pickingUbo, pickingBG, meshCpu, pickingCpu };\n}\n\nexport function disposeGsPickMeshResources(res: GsPickMeshResources): void {\n res.meshUbo.destroy();\n res.pickingUbo.destroy();\n}\n\n/** Issue a pick draw for a single GS mesh. The caller must have already bound\n * the scene bind group (`pickMatrix` at group 0) on the pass; `pickId` is the\n * 24-bit pick id assigned by the picker. */\nexport function drawGsForPicking(\n pass: GPURenderPassEncoder,\n engine: EngineContext,\n scene: SceneContext,\n mesh: GaussianSplattingMesh,\n res: GsPickMeshResources,\n pickId: number,\n targetWidth: number,\n targetHeight: number\n): void {\n const cache = getCache(engine);\n\n // ── Per-mesh UBO ────────────────────────────────────────────────\n const cam = scene.camera;\n if (!cam) {\n return;\n }\n const size = getRenderTargetSize(engine);\n const aspect = (targetWidth || size.width) / (targetHeight || size.height);\n const view = getViewMatrix(cam) as unknown as Float32Array;\n const proj = getProjectionMatrix(cam, aspect) as unknown as Float32Array;\n const world = mesh.worldMatrix as unknown as Float32Array;\n const cpu = res.meshCpu;\n cpu.set(world, 0);\n cpu.set(view, 16);\n cpu.set(proj, 32);\n // Note: the GS picking pipeline still runs over the full-canvas viewport;\n // the picker output is a 1×1 target reached via the scene pickMatrix.\n cpu[48] = size.width;\n cpu[48 + 1] = size.height;\n cpu[48 + 2] = size.width * 0.5 * proj[0]!;\n cpu[48 + 3] = size.height * 0.5 * proj[5]!;\n // dataSize/alpha already written at construction.\n engine._device.queue.writeBuffer(res.meshUbo, 0, cpu.buffer, 0, cpu.byteLength);\n\n // ── Picking-color UBO ───────────────────────────────────────────\n const [r, g, b] = encodeIdToColor(pickId);\n res.pickingCpu[0] = r;\n res.pickingCpu[1] = g;\n res.pickingCpu[2] = b;\n res.pickingCpu[3] = 0;\n engine._device.queue.writeBuffer(res.pickingUbo, 0, res.pickingCpu.buffer, 0, 16);\n\n pass.setPipeline(cache.pipeline);\n pass.setBindGroup(1, res.meshBG);\n pass.setBindGroup(2, res.pickingBG);\n pass.setVertexBuffer(0, mesh._gs._quadBuffer);\n pass.setVertexBuffer(1, mesh._gs._splatIndexBuffer);\n pass.setIndexBuffer(mesh._gs._indexBuffer, \"uint16\");\n pass.drawIndexed(6, mesh.vertexCount);\n}\n\n/** Compute the pickMatrix for GS picking — same matrix `computePickVP` builds,\n * but applied to the clip-space output of the GS shader rather than the world.\n *\n * Maps canvas-NDC (px, py) ↦ (0, 0), and scales by (w, h) so a single canvas\n * pixel covers the full 1×1 pick render target. */\nexport function computeGsPickMatrix(out: Float32Array, px: number, py: number, w: number, h: number): void {\n const ndcX = (2 * (px + 0.5)) / w - 1;\n const ndcY = 1 - (2 * (py + 0.5)) / h;\n // column-major mat4\n out[0] = w;\n out[1] = 0;\n out[2] = 0;\n out[3] = 0;\n out[4] = 0;\n out[5] = h;\n out[6] = 0;\n out[7] = 0;\n out[8] = 0;\n out[9] = 0;\n out[10] = 1;\n out[11] = 0;\n out[12] = -ndcX * w;\n out[13] = -ndcY * h;\n out[14] = 0;\n out[15] = 1;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"gs-picking-pipeline-55sM5LzV.js","sources":["../src/picking/gs-picking-pipeline.ts"],"sourcesContent":["/** GS GPU-picking pipeline — dynamic-imported by `gpu-picker.ts` when a scene\n * contains a Gaussian-Splatting mesh.\n *\n * The pipeline is a variant of the base Gaussian-splatting render pipeline\n * (`gaussian-splatting.wgsl`) with three modifications, applied via string\n * substitution + the `applyGsFragments` plugin system so the base shader stays\n * untouched:\n *\n * 1. A scene UBO at `@group(0) @binding(0)` carries a `pickMatrix`\n * (column-major 4x4) that zooms NDC onto the pick pixel — same maths as\n * `computePickVP` in `gpu-picker.ts`, but applied *after* the GS-specific\n * projection so the EWA Jacobian / `u.focal` math is untouched.\n * 2. The fragment returns `FsOut { @location(0) color, @location(1) depth }`\n * so the picker can read a pick id + NDC z from the 1×1 render target.\n * 3. `gsGpuPickingFragment` is applied (via `applyGsFragments`) to inject the\n * per-mesh `pickingColor` UBO at `@group(2) @binding(0)` and override the\n * fragment color with the encoded pick id. This mirrors BJS\n * `GaussianSplattingGpuPickingMaterialPlugin`'s `getCustomCode`/`bindForSubMesh`\n * split.\n *\n * The pipeline has no blending and `depthWriteEnabled = true`, so the\n * closest-splat wins at each pick pixel (matching BJS GPU picker behaviour). */\n\nimport { F32 } from \"../engine/typed-arrays.js\";\nimport { BU, SS } from \"../engine/gpu-flags.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { GaussianSplattingMesh } from \"../mesh/GaussianSplatting/gaussian-splatting-mesh.js\";\nimport { applyGsFragments } from \"../mesh/GaussianSplatting/gaussian-splatting-pipeline.js\";\nimport { gsGpuPickingFragment, encodeIdToColor } from \"../mesh/GaussianSplatting/gs-gpu-picking-fragment.js\";\nimport { getPickingSceneBGL } from \"./picking-pipeline.js\";\nimport { getRenderTargetSize } from \"../engine/engine.js\";\nimport { getViewMatrix, getProjectionMatrix } from \"../camera/camera.js\";\nimport type { SceneContext } from \"../scene/scene-core.js\";\n\ninterface GsPickingCache {\n device: GPUDevice;\n pipeline: GPURenderPipeline;\n meshBGL: GPUBindGroupLayout;\n pickingBGL: GPUBindGroupLayout;\n /** Shared \"pick scene\" UBO holding the 4x4 pickMatrix (set per pick). */\n pickMatrixUbo: GPUBuffer;\n /** Bind group exposing `pickMatrixUbo` at group(0) binding(0). */\n sceneBG: GPUBindGroup;\n}\n\nlet _cache: GsPickingCache | null = null;\n\n/** Build the GS picking WGSL as a self-contained template literal.\n *\n * This is a variant of the base GS shader (`gaussian-splatting.wgsl`) with two\n * modifications baked in directly (rather than string-patched at runtime), so\n * it survives identifier-mangling minification in production bundles:\n *\n * 1. The final `out.pos` is pre-multiplied by `gsPickScene.pickMatrix` (a 4×4\n * UBO at `@group(0) @binding(0)`) — applied *after* the GS-specific\n * projection so the EWA Jacobian / `u.focal` math is untouched.\n * 2. The fragment returns `FsOut { @location(0) color, @location(1) depth }`\n * so the picker can read pick id + NDC z from the 1×1 render target.\n *\n * The fragment keeps the `/* GS_FRAGMENT_* *\\/` slot markers so\n * `gsGpuPickingFragment` is still spliced in via `applyGsFragments` to\n * override `finalColor` with the encoded pick id. Template-literal WGSL\n * goes through `minifyTemplateWgsl` (preserves block comments) in the\n * bundle pipeline, so the slot markers survive minification.\n */\nfunction buildPickingWgsl(): string {\n const wgsl = /* wgsl */ `\nstruct GsPickScene { pickMatrix: mat4x4<f32> };\n@group(0) @binding(0) var<uniform> gsPickScene: GsPickScene;\n\nstruct U {\n world: mat4x4<f32>,\n view: mat4x4<f32>,\n projection: mat4x4<f32>,\n viewport: vec2<f32>,\n focal: vec2<f32>,\n dataSize: vec2<f32>,\n alpha: f32,\n _pad: f32,\n};\n@group(1) @binding(0) var<uniform> u: U;\n@group(1) @binding(1) var samp: sampler;\n@group(1) @binding(2) var centersTex: texture_2d<f32>;\n@group(1) @binding(3) var covATex: texture_2d<f32>;\n@group(1) @binding(4) var covBTex: texture_2d<f32>;\n@group(1) @binding(5) var colorsTex: texture_2d<f32>;\n\nstruct VOut {\n @builtin(position) pos: vec4<f32>,\n @location(0) vColor: vec4<f32>,\n @location(1) vPos: vec2<f32>,\n};\n\nfn dataUv(idx: f32) -> vec2<f32> {\n let y = floor(idx / u.dataSize.x);\n let x = idx - y * u.dataSize.x;\n return vec2<f32>((x + 0.5) / u.dataSize.x, (y + 0.5) / u.dataSize.y);\n}\n\n@vertex\nfn vs(@location(0) corner: vec2<f32>, @location(1) splatIndex: f32) -> VOut {\n var out: VOut;\n let uv = dataUv(splatIndex);\n let center = textureSampleLevel(centersTex, samp, uv, 0.0).xyz;\n let color = textureSampleLevel(colorsTex, samp, uv, 0.0);\n let covA = textureSampleLevel(covATex, samp, uv, 0.0).xyz;\n let covB = textureSampleLevel(covBTex, samp, uv, 0.0).xyz;\n\n let worldPos = u.world * vec4<f32>(center, 1.0);\n let modelView = u.view * u.world;\n let camspace = u.view * worldPos;\n let pos2d = u.projection * camspace;\n\n let bounds = 1.2 * pos2d.w;\n if (pos2d.z < 0.0\n || pos2d.x < -bounds || pos2d.x > bounds\n || pos2d.y < -bounds || pos2d.y > bounds) {\n out.pos = vec4<f32>(0.0, 0.0, 2.0, 1.0);\n out.vColor = vec4<f32>(0.0);\n out.vPos = vec2<f32>(0.0);\n return out;\n }\n\n let Vrk = mat3x3<f32>(\n vec3<f32>(covA.x, covA.y, covA.z),\n vec3<f32>(covA.y, covB.x, covB.y),\n vec3<f32>(covA.z, covB.y, covB.z));\n\n let invZ = 1.0 / camspace.z;\n let invZ2 = invZ * invZ;\n let J = mat3x3<f32>(\n vec3<f32>(u.focal.x * invZ, 0.0, -u.focal.x * camspace.x * invZ2),\n vec3<f32>(0.0, u.focal.y * invZ, -u.focal.y * camspace.y * invZ2),\n vec3<f32>(0.0, 0.0, 0.0));\n\n let mv3 = mat3x3<f32>(modelView[0].xyz, modelView[1].xyz, modelView[2].xyz);\n let T = transpose(mv3) * J;\n var cov2d = transpose(T) * Vrk * T;\n\n let kernelSize: f32 = 0.3;\n cov2d[0][0] += kernelSize;\n cov2d[1][1] += kernelSize;\n\n let mid = (cov2d[0][0] + cov2d[1][1]) * 0.5;\n let dxy = (cov2d[0][0] - cov2d[1][1]) * 0.5;\n let radius = length(vec2<f32>(dxy, cov2d[0][1]));\n let epsilon: f32 = 0.0001;\n let lambda1 = mid + radius + epsilon;\n let lambda2 = mid - radius + epsilon;\n if (lambda2 < 0.0) {\n out.pos = vec4<f32>(0.0, 0.0, 2.0, 1.0);\n out.vColor = vec4<f32>(0.0);\n out.vPos = vec2<f32>(0.0);\n return out;\n }\n\n let diag = normalize(vec2<f32>(cov2d[0][1], lambda1 - cov2d[0][0]));\n let majorAxis = min(sqrt(2.0 * lambda1), 1024.0) * diag;\n let minorAxis = min(sqrt(2.0 * lambda2), 1024.0) * vec2<f32>(diag.y, -diag.x);\n\n let vCenter = pos2d.xy;\n out.pos = gsPickScene.pickMatrix * vec4<f32>(\n vCenter + (corner.x * majorAxis + corner.y * minorAxis) * pos2d.w / u.viewport,\n pos2d.z, pos2d.w);\n out.vColor = vec4<f32>(color.rgb, color.a * u.alpha);\n out.vPos = corner;\n return out;\n}\n\n/*GS_FRAGMENT_DEFINITIONS*/\nstruct FsOut { @location(0) color: vec4<f32>, @location(1) depth: vec4<f32> };\n@fragment\nfn fs(in: VOut) -> FsOut {\n /*GS_FRAGMENT_MAIN_BEGIN*/\n let A = -dot(in.vPos, in.vPos);\n var finalColor: vec4<f32>;\n if (A > -4.0) {\n let B = exp(A) * in.vColor.a;\n finalColor = vec4<f32>(in.vColor.rgb, B);\n } else {\n finalColor = vec4<f32>(0.0);\n }\n /*GS_FRAGMENT_BEFORE_FRAGCOLOR*/\n /*GS_FRAGMENT_MAIN_END*/\n return FsOut(finalColor, vec4<f32>(in.pos.z, 0.0, 0.0, 0.0));\n}\n`;\n return applyGsFragments(wgsl, [gsGpuPickingFragment]);\n}\n\nfunction getCache(engine: EngineContext): GsPickingCache {\n const device = engine._device;\n if (_cache && _cache.device === device) {\n return _cache;\n }\n const meshBGL = device.createBindGroupLayout({\n label: \"gs-picking-mesh-bgl\",\n entries: [\n { binding: 0, visibility: SS.VERTEX | SS.FRAGMENT, buffer: { type: \"uniform\" } },\n { binding: 1, visibility: SS.VERTEX, sampler: { type: \"non-filtering\" } },\n { binding: 2, visibility: SS.VERTEX, texture: { sampleType: \"unfilterable-float\" } },\n { binding: 3, visibility: SS.VERTEX, texture: { sampleType: \"unfilterable-float\" } },\n { binding: 4, visibility: SS.VERTEX, texture: { sampleType: \"unfilterable-float\" } },\n { binding: 5, visibility: SS.VERTEX, texture: { sampleType: \"unfilterable-float\" } },\n ],\n });\n const pickingBGL = device.createBindGroupLayout({\n label: \"gs-picking-pick-bgl\",\n entries: [{ binding: 0, visibility: SS.FRAGMENT, buffer: { type: \"uniform\" } }],\n });\n const module = device.createShaderModule({ label: \"gs-picking-shader\", code: buildPickingWgsl() });\n const pipeline = device.createRenderPipeline({\n label: \"gs-picking-pipeline\",\n layout: device.createPipelineLayout({ bindGroupLayouts: [getPickingSceneBGL(engine), meshBGL, pickingBGL] }),\n vertex: {\n module,\n entryPoint: \"vs\",\n buffers: [\n { arrayStride: 8, stepMode: \"vertex\", attributes: [{ shaderLocation: 0, offset: 0, format: \"float32x2\" }] },\n { arrayStride: 4, stepMode: \"instance\", attributes: [{ shaderLocation: 1, offset: 0, format: \"float32\" }] },\n ],\n },\n fragment: {\n module,\n entryPoint: \"fs\",\n targets: [{ format: \"rgba8unorm\" }, { format: \"r32float\" }],\n },\n primitive: { topology: \"triangle-list\", cullMode: \"none\" },\n depthStencil: { format: \"depth24plus\", depthCompare: \"less\", depthWriteEnabled: true },\n multisample: { count: 1 },\n });\n const pickMatrixUbo = device.createBuffer({ size: 64, usage: BU.UNIFORM | BU.COPY_DST, label: \"gs-picking-scene-ubo\" });\n const sceneBG = device.createBindGroup({\n label: \"gs-picking-scene-bg\",\n layout: getPickingSceneBGL(engine),\n entries: [{ binding: 0, resource: { buffer: pickMatrixUbo } }],\n });\n _cache = { device, pipeline, meshBGL, pickingBGL, pickMatrixUbo, sceneBG };\n return _cache;\n}\n\n/** Write a 4x4 pickMatrix into the shared scene UBO and bind group 0 on `pass`. */\nexport function gsPickWritePickMatrixAndBind(pass: GPURenderPassEncoder, engine: EngineContext, pickMatrix: Float32Array): void {\n const cache = getCache(engine);\n engine._device.queue.writeBuffer(cache.pickMatrixUbo, 0, pickMatrix.buffer, pickMatrix.byteOffset, pickMatrix.byteLength);\n pass.setBindGroup(0, cache.sceneBG);\n}\n\n/** Per-GS-mesh state allocated on first pick of that mesh. */\nexport interface GsPickMeshResources {\n /** Per-mesh UBO (same 224-byte layout as the regular GS pipeline UBO). */\n meshUbo: GPUBuffer;\n /** Group 1 bind group: per-mesh UBO + sampler + 4 textures. */\n meshBG: GPUBindGroup;\n /** Per-pick picking-color UBO (16 bytes: vec3<f32> + pad). */\n pickingUbo: GPUBuffer;\n /** Group 2 bind group with the picking-color UBO. */\n pickingBG: GPUBindGroup;\n /** Scratch buffer for the per-mesh UBO. */\n meshCpu: Float32Array;\n /** Scratch buffer for the picking-color UBO. */\n pickingCpu: Float32Array;\n}\n\nexport function createGsPickMeshResources(engine: EngineContext, mesh: GaussianSplattingMesh): GsPickMeshResources {\n const device = engine._device;\n const cache = getCache(engine);\n\n const UBO_BYTES = 16 * 4 * 3 + 8 * 4;\n const meshUbo = device.createBuffer({ size: UBO_BYTES, usage: BU.UNIFORM | BU.COPY_DST, label: \"gs-picking-mesh-ubo\" });\n const meshCpu = new F32(UBO_BYTES / 4);\n meshCpu[48 + 4] = mesh.textureWidth;\n meshCpu[48 + 5] = mesh.textureHeight;\n meshCpu[48 + 6] = 1;\n\n const meshBG = device.createBindGroup({\n label: \"gs-picking-mesh-bg\",\n layout: cache.meshBGL,\n entries: [\n { binding: 0, resource: { buffer: meshUbo } },\n { binding: 1, resource: mesh._gs._sampler },\n { binding: 2, resource: mesh._gs._centersView },\n { binding: 3, resource: mesh._gs._covAView },\n { binding: 4, resource: mesh._gs._covBView },\n { binding: 5, resource: mesh._gs._colorsView },\n ],\n });\n\n const pickingUbo = device.createBuffer({ size: 16, usage: BU.UNIFORM | BU.COPY_DST, label: \"gs-picking-color-ubo\" });\n const pickingCpu = new F32(4);\n\n const pickingBG = device.createBindGroup({\n label: \"gs-picking-color-bg\",\n layout: cache.pickingBGL,\n entries: [{ binding: 0, resource: { buffer: pickingUbo } }],\n });\n\n return { meshUbo, meshBG, pickingUbo, pickingBG, meshCpu, pickingCpu };\n}\n\nexport function disposeGsPickMeshResources(res: GsPickMeshResources): void {\n res.meshUbo.destroy();\n res.pickingUbo.destroy();\n}\n\n/** Issue a pick draw for a single GS mesh. The caller must have already bound\n * the scene bind group (`pickMatrix` at group 0) on the pass; `pickId` is the\n * 24-bit pick id assigned by the picker. */\nexport function drawGsForPicking(\n pass: GPURenderPassEncoder,\n engine: EngineContext,\n scene: SceneContext,\n mesh: GaussianSplattingMesh,\n res: GsPickMeshResources,\n pickId: number,\n targetWidth: number,\n targetHeight: number\n): void {\n const cache = getCache(engine);\n\n // ── Per-mesh UBO ────────────────────────────────────────────────\n const cam = scene.camera;\n if (!cam) {\n return;\n }\n const size = getRenderTargetSize(engine);\n const aspect = (targetWidth || size.width) / (targetHeight || size.height);\n const view = getViewMatrix(cam) as unknown as Float32Array;\n const proj = getProjectionMatrix(cam, aspect) as unknown as Float32Array;\n const world = mesh.worldMatrix as unknown as Float32Array;\n const cpu = res.meshCpu;\n cpu.set(world, 0);\n cpu.set(view, 16);\n cpu.set(proj, 32);\n // Note: the GS picking pipeline still runs over the full-canvas viewport;\n // the picker output is a 1×1 target reached via the scene pickMatrix.\n cpu[48] = size.width;\n cpu[48 + 1] = size.height;\n cpu[48 + 2] = size.width * 0.5 * proj[0]!;\n cpu[48 + 3] = size.height * 0.5 * proj[5]!;\n // dataSize/alpha already written at construction.\n engine._device.queue.writeBuffer(res.meshUbo, 0, cpu.buffer, 0, cpu.byteLength);\n\n // ── Picking-color UBO ───────────────────────────────────────────\n const [r, g, b] = encodeIdToColor(pickId);\n res.pickingCpu[0] = r;\n res.pickingCpu[1] = g;\n res.pickingCpu[2] = b;\n res.pickingCpu[3] = 0;\n engine._device.queue.writeBuffer(res.pickingUbo, 0, res.pickingCpu.buffer, 0, 16);\n\n pass.setPipeline(cache.pipeline);\n pass.setBindGroup(1, res.meshBG);\n pass.setBindGroup(2, res.pickingBG);\n pass.setVertexBuffer(0, mesh._gs._quadBuffer);\n pass.setVertexBuffer(1, mesh._gs._splatIndexBuffer);\n pass.setIndexBuffer(mesh._gs._indexBuffer, \"uint16\");\n pass.drawIndexed(6, mesh.vertexCount);\n}\n\n/** Compute the pickMatrix for GS picking — same matrix `computePickVP` builds,\n * but applied to the clip-space output of the GS shader rather than the world.\n *\n * Maps canvas-NDC (px, py) ↦ (0, 0), and scales by (w, h) so a single canvas\n * pixel covers the full 1×1 pick render target. */\nexport function computeGsPickMatrix(out: Float32Array, px: number, py: number, w: number, h: number): void {\n const ndcX = (2 * (px + 0.5)) / w - 1;\n const ndcY = 1 - (2 * (py + 0.5)) / h;\n // column-major mat4\n out[0] = w;\n out[1] = 0;\n out[2] = 0;\n out[3] = 0;\n out[4] = 0;\n out[5] = h;\n out[6] = 0;\n out[7] = 0;\n out[8] = 0;\n out[9] = 0;\n out[10] = 1;\n out[11] = 0;\n out[12] = -ndcX * w;\n out[13] = -ndcY * h;\n out[14] = 0;\n out[15] = 1;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ad as PhysicsMotionType } from "./index-C-tEgwbZ.js";
|
|
2
2
|
function createHavokFloatingOriginContext(hkWorld, gravity, radius) {
|
|
3
3
|
return {
|
|
4
4
|
regions: [{ _world: hkWorld, origin: { x: 0, y: 0, z: 0 }, gravity: [...gravity] }],
|
|
@@ -195,4 +195,4 @@ function _syncNodeToBody(hknp, body) {
|
|
|
195
195
|
export {
|
|
196
196
|
createHavokFloatingOriginContext
|
|
197
197
|
};
|
|
198
|
-
//# sourceMappingURL=havok-floating-origin-
|
|
198
|
+
//# sourceMappingURL=havok-floating-origin-5xp32P-C.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"havok-floating-origin-Dr-18Nds.js","sources":["../src/physics/havok-floating-origin.ts"],"sourcesContent":["/**\n * Havok multi-region floating origin (Large World Rendering).\n *\n * This module is **dynamic-imported only when the engine has `useFloatingOrigin: true`**\n * (from `createHavokWorld`), so non-floating-origin physics scenes never pull this code into\n * their bundle.\n *\n * Under floating origin, bodies far apart in world space are simulated in separate regions —\n * each a native Havok world with a fixed `origin`. Bodies are stored in region-local coordinates\n * (`worldPos - origin`, near zero), so the float32 Havok solver keeps full precision at large\n * world coordinates. Node transforms remain true world coordinates; the eye-relative render path\n * is handled independently.\n *\n * Mirrors Babylon.js's `scene.floatingOriginMode` + Havok plugin `floatingOriginWorldRadius`.\n */\n\nimport type { Vec3 } from \"../math/types.js\";\nimport type { PhysicsBody, PhysicsWorld } from \"./havok.js\";\nimport { PhysicsMotionType } from \"./havok.js\";\n\n/**\n * A simulation region: a native Havok world whose bodies are simulated relative to a fixed\n * `origin`. `_regions[0]` is the default region (origin at the world origin).\n */\nexport interface WorldRegion {\n /** @internal */ _world: any;\n /** Floating origin (world-space centre) this region's bodies are stored relative to. */\n origin: Vec3;\n /** This region's gravity vector `[x, y, z]`. */\n gravity: number[];\n}\n\n/**\n * Floating-origin runtime stored on `PhysicsWorld._fo` (present only when floating origin is on).\n * Holds region state plus the standalone hooks the core physics module calls in place of its\n * single-world fast path.\n */\nexport interface HavokFloatingOriginContext {\n /** All simulation regions; `regions[0]` is the default (origin-centred) region. */\n regions: WorldRegion[];\n /** Region capture radius (metres). */\n radius: number;\n /** Most recently set world-wide gravity `[x, y, z]`; seeds newly created regions. */\n gravity: number[];\n placeBody(world: PhysicsWorld, body: PhysicsBody, startsAsleep: boolean): void;\n step(world: PhysicsWorld): void;\n setGravity(world: PhysicsWorld, gravity: number[], worldPosition?: Vec3): void;\n getRegionGravity(world: PhysicsWorld, worldPosition: Vec3): number[];\n setVelocityLimits(world: PhysicsWorld, maxLinear: number, maxAngular: number): void;\n dispose(world: PhysicsWorld): void;\n}\n\n/**\n * Builds the floating-origin context, seeding it with the world's default region (the native world\n * already created by `createHavokWorld`, centred at the origin).\n */\nexport function createHavokFloatingOriginContext(hkWorld: any, gravity: number[], radius: number): HavokFloatingOriginContext {\n return {\n regions: [{ _world: hkWorld, origin: { x: 0, y: 0, z: 0 }, gravity: [...gravity] }],\n radius,\n gravity: [...gravity],\n placeBody: _placeBody,\n step: _step,\n setGravity: _setGravity,\n getRegionGravity: _getRegionGravity,\n setVelocityLimits: _setVelocityLimits,\n dispose: _dispose,\n };\n}\n\n// ─── Region lookup / creation ────────────────────────────────────────\n\n/** Returns the region whose origin is within the capture radius of `pos`, or null. */\nfunction _findRegion(fo: HavokFloatingOriginContext, pos: Vec3): WorldRegion | null {\n const r2 = fo.radius * fo.radius;\n for (const region of fo.regions) {\n const dx = pos.x - region.origin.x;\n const dy = pos.y - region.origin.y;\n const dz = pos.z - region.origin.z;\n if (dx * dx + dy * dy + dz * dz <= r2) {\n return region;\n }\n }\n return null;\n}\n\n/** Returns an existing region containing `pos`, or creates a new one centred at `pos`. */\nfunction _getOrCreateRegion(world: PhysicsWorld, pos: Vec3): WorldRegion {\n const fo = world._fo!;\n const found = _findRegion(fo, pos);\n if (found) {\n return found;\n }\n const hknp = world._hknp;\n const newWorld = hknp.HP_World_Create()[1];\n hknp.HP_World_SetGravity(newWorld, fo.gravity);\n const limits = hknp.HP_World_GetSpeedLimit(world._hkWorld);\n hknp.HP_World_SetSpeedLimit(newWorld, limits[1], limits[2]);\n const region: WorldRegion = {\n _world: newWorld,\n origin: { x: pos.x, y: pos.y, z: pos.z },\n gravity: [...fo.gravity],\n };\n fo.regions.push(region);\n return region;\n}\n\n// ─── Hooks ───────────────────────────────────────────────────────────\n\nfunction _placeBody(world: PhysicsWorld, body: PhysicsBody, startsAsleep: boolean): void {\n const hknp = world._hknp;\n const node = body.node;\n const region = _getOrCreateRegion(world, node.position);\n hknp.HP_World_AddBody(region._world, body._hkBody, startsAsleep);\n const p = node.position;\n const q = node.rotationQuaternion;\n const o = region.origin;\n hknp.HP_Body_SetQTransform(body._hkBody, [\n [p.x - o.x, p.y - o.y, p.z - o.z],\n [q.x, q.y, q.z, q.w],\n ]);\n body._region = region;\n}\n\nfunction _step(world: PhysicsWorld): void {\n const hknp = world._hknp;\n const bodies = world._bodies;\n const regions = world._fo!.regions;\n\n // Re-region bodies that drifted out of their region BEFORE stepping.\n for (let i = 0; i < bodies.length; i++) {\n _reRegionBody(world, bodies[i]!);\n }\n\n // Pre-step: sync ANIMATED bodies from node → Havok.\n for (let i = 0; i < bodies.length; i++) {\n const b = bodies[i]!;\n if (b.motionType === (PhysicsMotionType.ANIMATED as number)) {\n _syncNodeToBody(hknp, b);\n }\n }\n\n // Step every region world.\n for (let i = 0; i < regions.length; i++) {\n hknp.HP_World_Step(regions[i]!._world, world._timestep);\n }\n\n // Post-step: sync DYNAMIC bodies from Havok → node.\n for (let i = 0; i < bodies.length; i++) {\n const b = bodies[i]!;\n if (b.motionType === (PhysicsMotionType.DYNAMIC as number)) {\n _syncBodyToNode(hknp, b);\n }\n }\n\n // Reclaim regions emptied by migration.\n _gcRegions(world);\n}\n\nfunction _setGravity(world: PhysicsWorld, gravity: number[], worldPosition?: Vec3): void {\n const fo = world._fo!;\n const hknp = world._hknp;\n if (worldPosition) {\n const region = _getOrCreateRegion(world, worldPosition);\n region.gravity = gravity;\n hknp.HP_World_SetGravity(region._world, gravity);\n return;\n }\n fo.gravity = gravity;\n for (const region of fo.regions) {\n region.gravity = gravity;\n hknp.HP_World_SetGravity(region._world, gravity);\n }\n}\n\nfunction _getRegionGravity(world: PhysicsWorld, worldPosition: Vec3): number[] {\n return _getOrCreateRegion(world, worldPosition).gravity;\n}\n\nfunction _setVelocityLimits(world: PhysicsWorld, maxLinear: number, maxAngular: number): void {\n for (const region of world._fo!.regions) {\n world._hknp.HP_World_SetSpeedLimit(region._world, maxLinear, maxAngular);\n }\n}\n\nfunction _dispose(world: PhysicsWorld): void {\n const hknp = world._hknp;\n const bodies = world._bodies;\n for (let i = bodies.length - 1; i >= 0; i--) {\n const b = bodies[i]!;\n hknp.HP_World_RemoveBody(b._region!._world, b._hkBody);\n hknp.HP_Body_Release(b._hkBody);\n }\n bodies.length = 0;\n const regions = world._fo!.regions;\n for (const region of regions) {\n hknp.HP_World_Release(region._world);\n }\n regions.length = 0;\n}\n\n// ─── Migration & sync ────────────────────────────────────────────────\n\n/**\n * Moves a body to the correct region if it has drifted past `radius * 1.2` (hysteresis) from its\n * current region's origin, preserving linear and angular velocity. Uses a one-second velocity\n * look-ahead to prefer joining an existing target region over spawning a throwaway one.\n */\nfunction _reRegionBody(world: PhysicsWorld, body: PhysicsBody): void {\n const hknp = world._hknp;\n const fo = world._fo!;\n const current = body._region!;\n\n const t = hknp.HP_Body_GetQTransform(body._hkBody)[1];\n const localPos = t[0];\n const orientation = t[1];\n\n // Distance from region origin == magnitude of the local position.\n const margin = fo.radius * 1.2;\n if (localPos[0] * localPos[0] + localPos[1] * localPos[1] + localPos[2] * localPos[2] <= margin * margin) {\n return;\n }\n\n const wx = localPos[0] + current.origin.x;\n const wy = localPos[1] + current.origin.y;\n const wz = localPos[2] + current.origin.z;\n\n const linVel = hknp.HP_Body_GetLinearVelocity(body._hkBody)[1];\n const angVel = hknp.HP_Body_GetAngularVelocity(body._hkBody)[1];\n\n const worldPos: Vec3 = { x: wx, y: wy, z: wz };\n const lookAhead: Vec3 = { x: wx + linVel[0], y: wy + linVel[1], z: wz + linVel[2] };\n\n let next = _findRegion(fo, lookAhead);\n if (!next || next === current) {\n next = _findRegion(fo, worldPos);\n }\n if (!next || next === current) {\n next = _getOrCreateRegion(world, worldPos);\n }\n if (next === current) {\n return;\n }\n\n hknp.HP_World_RemoveBody(current._world, body._hkBody);\n const o = next.origin;\n hknp.HP_Body_SetQTransform(body._hkBody, [[wx - o.x, wy - o.y, wz - o.z], orientation]);\n hknp.HP_World_AddBody(next._world, body._hkBody, false);\n hknp.HP_Body_SetLinearVelocity(body._hkBody, linVel);\n hknp.HP_Body_SetAngularVelocity(body._hkBody, angVel);\n body._region = next;\n}\n\n/** Releases any non-default region that no longer holds any bodies. */\nfunction _gcRegions(world: PhysicsWorld): void {\n const regions = world._fo!.regions;\n if (regions.length <= 1) {\n return;\n }\n const hknp = world._hknp;\n const used = new Set<WorldRegion>();\n for (let i = 0; i < world._bodies.length; i++) {\n used.add(world._bodies[i]!._region!);\n }\n for (let i = regions.length - 1; i >= 1; i--) {\n const region = regions[i]!;\n if (!used.has(region)) {\n hknp.HP_World_Release(region._world);\n regions.splice(i, 1);\n }\n }\n}\n\nfunction _syncBodyToNode(hknp: any, body: PhysicsBody): void {\n const t = hknp.HP_Body_GetQTransform(body._hkBody)[1];\n const pos = t[0]; // [x, y, z] in region-local space\n const rot = t[1]; // [x, y, z, w]\n const o = body._region!.origin;\n const node = body.node;\n node.position.set(pos[0] + o.x, pos[1] + o.y, pos[2] + o.z);\n node.rotationQuaternion.set(rot[0], rot[1], rot[2], rot[3]);\n}\n\nfunction _syncNodeToBody(hknp: any, body: PhysicsBody): void {\n const node = body.node;\n const p = node.position;\n const q = node.rotationQuaternion;\n const o = body._region!.origin;\n hknp.HP_Body_SetQTransform(body._hkBody, [\n [p.x - o.x, p.y - o.y, p.z - o.z],\n [q.x, q.y, q.z, q.w],\n ]);\n}\n"],"names":[],"mappings":";AAwDO,SAAS,iCAAiC,SAAc,SAAmB,QAA4C;AAC1H,SAAO;AAAA,IACH,SAAS,CAAC,EAAE,QAAQ,SAAS,QAAQ,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,KAAK,SAAS,CAAC,GAAG,OAAO,GAAG;AAAA,IAClF;AAAA,IACA,SAAS,CAAC,GAAG,OAAO;AAAA,IACpB,WAAW;AAAA,IACX,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,kBAAkB;AAAA,IAClB,mBAAmB;AAAA,IACnB,SAAS;AAAA,EAAA;AAEjB;AAKA,SAAS,YAAY,IAAgC,KAA+B;AAChF,QAAM,KAAK,GAAG,SAAS,GAAG;AAC1B,aAAW,UAAU,GAAG,SAAS;AAC7B,UAAM,KAAK,IAAI,IAAI,OAAO,OAAO;AACjC,UAAM,KAAK,IAAI,IAAI,OAAO,OAAO;AACjC,UAAM,KAAK,IAAI,IAAI,OAAO,OAAO;AACjC,QAAI,KAAK,KAAK,KAAK,KAAK,KAAK,MAAM,IAAI;AACnC,aAAO;AAAA,IACX;AAAA,EACJ;AACA,SAAO;AACX;AAGA,SAAS,mBAAmB,OAAqB,KAAwB;AACrE,QAAM,KAAK,MAAM;AACjB,QAAM,QAAQ,YAAY,IAAI,GAAG;AACjC,MAAI,OAAO;AACP,WAAO;AAAA,EACX;AACA,QAAM,OAAO,MAAM;AACnB,QAAM,WAAW,KAAK,gBAAA,EAAkB,CAAC;AACzC,OAAK,oBAAoB,UAAU,GAAG,OAAO;AAC7C,QAAM,SAAS,KAAK,uBAAuB,MAAM,QAAQ;AACzD,OAAK,uBAAuB,UAAU,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC;AAC1D,QAAM,SAAsB;AAAA,IACxB,QAAQ;AAAA,IACR,QAAQ,EAAE,GAAG,IAAI,GAAG,GAAG,IAAI,GAAG,GAAG,IAAI,EAAA;AAAA,IACrC,SAAS,CAAC,GAAG,GAAG,OAAO;AAAA,EAAA;AAE3B,KAAG,QAAQ,KAAK,MAAM;AACtB,SAAO;AACX;AAIA,SAAS,WAAW,OAAqB,MAAmB,cAA6B;AACrF,QAAM,OAAO,MAAM;AACnB,QAAM,OAAO,KAAK;AAClB,QAAM,SAAS,mBAAmB,OAAO,KAAK,QAAQ;AACtD,OAAK,iBAAiB,OAAO,QAAQ,KAAK,SAAS,YAAY;AAC/D,QAAM,IAAI,KAAK;AACf,QAAM,IAAI,KAAK;AACf,QAAM,IAAI,OAAO;AACjB,OAAK,sBAAsB,KAAK,SAAS;AAAA,IACrC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;AAAA,IAChC,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAAA,EAAA,CACtB;AACD,OAAK,UAAU;AACnB;AAEA,SAAS,MAAM,OAA2B;AACtC,QAAM,OAAO,MAAM;AACnB,QAAM,SAAS,MAAM;AACrB,QAAM,UAAU,MAAM,IAAK;AAG3B,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACpC,kBAAc,OAAO,OAAO,CAAC,CAAE;AAAA,EACnC;AAGA,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACpC,UAAM,IAAI,OAAO,CAAC;AAClB,QAAI,EAAE,eAAgB,kBAAkB,UAAqB;AACzD,sBAAgB,MAAM,CAAC;AAAA,IAC3B;AAAA,EACJ;AAGA,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACrC,SAAK,cAAc,QAAQ,CAAC,EAAG,QAAQ,MAAM,SAAS;AAAA,EAC1D;AAGA,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACpC,UAAM,IAAI,OAAO,CAAC;AAClB,QAAI,EAAE,eAAgB,kBAAkB,SAAoB;AACxD,sBAAgB,MAAM,CAAC;AAAA,IAC3B;AAAA,EACJ;AAGA,aAAW,KAAK;AACpB;AAEA,SAAS,YAAY,OAAqB,SAAmB,eAA4B;AACrF,QAAM,KAAK,MAAM;AACjB,QAAM,OAAO,MAAM;AACnB,MAAI,eAAe;AACf,UAAM,SAAS,mBAAmB,OAAO,aAAa;AACtD,WAAO,UAAU;AACjB,SAAK,oBAAoB,OAAO,QAAQ,OAAO;AAC/C;AAAA,EACJ;AACA,KAAG,UAAU;AACb,aAAW,UAAU,GAAG,SAAS;AAC7B,WAAO,UAAU;AACjB,SAAK,oBAAoB,OAAO,QAAQ,OAAO;AAAA,EACnD;AACJ;AAEA,SAAS,kBAAkB,OAAqB,eAA+B;AAC3E,SAAO,mBAAmB,OAAO,aAAa,EAAE;AACpD;AAEA,SAAS,mBAAmB,OAAqB,WAAmB,YAA0B;AAC1F,aAAW,UAAU,MAAM,IAAK,SAAS;AACrC,UAAM,MAAM,uBAAuB,OAAO,QAAQ,WAAW,UAAU;AAAA,EAC3E;AACJ;AAEA,SAAS,SAAS,OAA2B;AACzC,QAAM,OAAO,MAAM;AACnB,QAAM,SAAS,MAAM;AACrB,WAAS,IAAI,OAAO,SAAS,GAAG,KAAK,GAAG,KAAK;AACzC,UAAM,IAAI,OAAO,CAAC;AAClB,SAAK,oBAAoB,EAAE,QAAS,QAAQ,EAAE,OAAO;AACrD,SAAK,gBAAgB,EAAE,OAAO;AAAA,EAClC;AACA,SAAO,SAAS;AAChB,QAAM,UAAU,MAAM,IAAK;AAC3B,aAAW,UAAU,SAAS;AAC1B,SAAK,iBAAiB,OAAO,MAAM;AAAA,EACvC;AACA,UAAQ,SAAS;AACrB;AASA,SAAS,cAAc,OAAqB,MAAyB;AACjE,QAAM,OAAO,MAAM;AACnB,QAAM,KAAK,MAAM;AACjB,QAAM,UAAU,KAAK;AAErB,QAAM,IAAI,KAAK,sBAAsB,KAAK,OAAO,EAAE,CAAC;AACpD,QAAM,WAAW,EAAE,CAAC;AACpB,QAAM,cAAc,EAAE,CAAC;AAGvB,QAAM,SAAS,GAAG,SAAS;AAC3B,MAAI,SAAS,CAAC,IAAI,SAAS,CAAC,IAAI,SAAS,CAAC,IAAI,SAAS,CAAC,IAAI,SAAS,CAAC,IAAI,SAAS,CAAC,KAAK,SAAS,QAAQ;AACtG;AAAA,EACJ;AAEA,QAAM,KAAK,SAAS,CAAC,IAAI,QAAQ,OAAO;AACxC,QAAM,KAAK,SAAS,CAAC,IAAI,QAAQ,OAAO;AACxC,QAAM,KAAK,SAAS,CAAC,IAAI,QAAQ,OAAO;AAExC,QAAM,SAAS,KAAK,0BAA0B,KAAK,OAAO,EAAE,CAAC;AAC7D,QAAM,SAAS,KAAK,2BAA2B,KAAK,OAAO,EAAE,CAAC;AAE9D,QAAM,WAAiB,EAAE,GAAG,IAAI,GAAG,IAAI,GAAG,GAAA;AAC1C,QAAM,YAAkB,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,GAAG,KAAK,OAAO,CAAC,GAAG,GAAG,KAAK,OAAO,CAAC,EAAA;AAEhF,MAAI,OAAO,YAAY,IAAI,SAAS;AACpC,MAAI,CAAC,QAAQ,SAAS,SAAS;AAC3B,WAAO,YAAY,IAAI,QAAQ;AAAA,EACnC;AACA,MAAI,CAAC,QAAQ,SAAS,SAAS;AAC3B,WAAO,mBAAmB,OAAO,QAAQ;AAAA,EAC7C;AACA,MAAI,SAAS,SAAS;AAClB;AAAA,EACJ;AAEA,OAAK,oBAAoB,QAAQ,QAAQ,KAAK,OAAO;AACrD,QAAM,IAAI,KAAK;AACf,OAAK,sBAAsB,KAAK,SAAS,CAAC,CAAC,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,CAAC,GAAG,WAAW,CAAC;AACtF,OAAK,iBAAiB,KAAK,QAAQ,KAAK,SAAS,KAAK;AACtD,OAAK,0BAA0B,KAAK,SAAS,MAAM;AACnD,OAAK,2BAA2B,KAAK,SAAS,MAAM;AACpD,OAAK,UAAU;AACnB;AAGA,SAAS,WAAW,OAA2B;AAC3C,QAAM,UAAU,MAAM,IAAK;AAC3B,MAAI,QAAQ,UAAU,GAAG;AACrB;AAAA,EACJ;AACA,QAAM,OAAO,MAAM;AACnB,QAAM,2BAAW,IAAA;AACjB,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,QAAQ,KAAK;AAC3C,SAAK,IAAI,MAAM,QAAQ,CAAC,EAAG,OAAQ;AAAA,EACvC;AACA,WAAS,IAAI,QAAQ,SAAS,GAAG,KAAK,GAAG,KAAK;AAC1C,UAAM,SAAS,QAAQ,CAAC;AACxB,QAAI,CAAC,KAAK,IAAI,MAAM,GAAG;AACnB,WAAK,iBAAiB,OAAO,MAAM;AACnC,cAAQ,OAAO,GAAG,CAAC;AAAA,IACvB;AAAA,EACJ;AACJ;AAEA,SAAS,gBAAgB,MAAW,MAAyB;AACzD,QAAM,IAAI,KAAK,sBAAsB,KAAK,OAAO,EAAE,CAAC;AACpD,QAAM,MAAM,EAAE,CAAC;AACf,QAAM,MAAM,EAAE,CAAC;AACf,QAAM,IAAI,KAAK,QAAS;AACxB,QAAM,OAAO,KAAK;AAClB,OAAK,SAAS,IAAI,IAAI,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;AAC1D,OAAK,mBAAmB,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;AAC9D;AAEA,SAAS,gBAAgB,MAAW,MAAyB;AACzD,QAAM,OAAO,KAAK;AAClB,QAAM,IAAI,KAAK;AACf,QAAM,IAAI,KAAK;AACf,QAAM,IAAI,KAAK,QAAS;AACxB,OAAK,sBAAsB,KAAK,SAAS;AAAA,IACrC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;AAAA,IAChC,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAAA,EAAA,CACtB;AACL;"}
|
|
1
|
+
{"version":3,"file":"havok-floating-origin-5xp32P-C.js","sources":["../src/physics/havok-floating-origin.ts"],"sourcesContent":["/**\n * Havok multi-region floating origin (Large World Rendering).\n *\n * This module is **dynamic-imported only when the engine has `useFloatingOrigin: true`**\n * (from `createHavokWorld`), so non-floating-origin physics scenes never pull this code into\n * their bundle.\n *\n * Under floating origin, bodies far apart in world space are simulated in separate regions —\n * each a native Havok world with a fixed `origin`. Bodies are stored in region-local coordinates\n * (`worldPos - origin`, near zero), so the float32 Havok solver keeps full precision at large\n * world coordinates. Node transforms remain true world coordinates; the eye-relative render path\n * is handled independently.\n *\n * Mirrors Babylon.js's `scene.floatingOriginMode` + Havok plugin `floatingOriginWorldRadius`.\n */\n\nimport type { Vec3 } from \"../math/types.js\";\nimport type { PhysicsBody, PhysicsWorld } from \"./havok.js\";\nimport { PhysicsMotionType } from \"./havok.js\";\n\n/**\n * A simulation region: a native Havok world whose bodies are simulated relative to a fixed\n * `origin`. `_regions[0]` is the default region (origin at the world origin).\n */\nexport interface WorldRegion {\n /** @internal */ _world: any;\n /** Floating origin (world-space centre) this region's bodies are stored relative to. */\n origin: Vec3;\n /** This region's gravity vector `[x, y, z]`. */\n gravity: number[];\n}\n\n/**\n * Floating-origin runtime stored on `PhysicsWorld._fo` (present only when floating origin is on).\n * Holds region state plus the standalone hooks the core physics module calls in place of its\n * single-world fast path.\n */\nexport interface HavokFloatingOriginContext {\n /** All simulation regions; `regions[0]` is the default (origin-centred) region. */\n regions: WorldRegion[];\n /** Region capture radius (metres). */\n radius: number;\n /** Most recently set world-wide gravity `[x, y, z]`; seeds newly created regions. */\n gravity: number[];\n placeBody(world: PhysicsWorld, body: PhysicsBody, startsAsleep: boolean): void;\n step(world: PhysicsWorld): void;\n setGravity(world: PhysicsWorld, gravity: number[], worldPosition?: Vec3): void;\n getRegionGravity(world: PhysicsWorld, worldPosition: Vec3): number[];\n setVelocityLimits(world: PhysicsWorld, maxLinear: number, maxAngular: number): void;\n dispose(world: PhysicsWorld): void;\n}\n\n/**\n * Builds the floating-origin context, seeding it with the world's default region (the native world\n * already created by `createHavokWorld`, centred at the origin).\n */\nexport function createHavokFloatingOriginContext(hkWorld: any, gravity: number[], radius: number): HavokFloatingOriginContext {\n return {\n regions: [{ _world: hkWorld, origin: { x: 0, y: 0, z: 0 }, gravity: [...gravity] }],\n radius,\n gravity: [...gravity],\n placeBody: _placeBody,\n step: _step,\n setGravity: _setGravity,\n getRegionGravity: _getRegionGravity,\n setVelocityLimits: _setVelocityLimits,\n dispose: _dispose,\n };\n}\n\n// ─── Region lookup / creation ────────────────────────────────────────\n\n/** Returns the region whose origin is within the capture radius of `pos`, or null. */\nfunction _findRegion(fo: HavokFloatingOriginContext, pos: Vec3): WorldRegion | null {\n const r2 = fo.radius * fo.radius;\n for (const region of fo.regions) {\n const dx = pos.x - region.origin.x;\n const dy = pos.y - region.origin.y;\n const dz = pos.z - region.origin.z;\n if (dx * dx + dy * dy + dz * dz <= r2) {\n return region;\n }\n }\n return null;\n}\n\n/** Returns an existing region containing `pos`, or creates a new one centred at `pos`. */\nfunction _getOrCreateRegion(world: PhysicsWorld, pos: Vec3): WorldRegion {\n const fo = world._fo!;\n const found = _findRegion(fo, pos);\n if (found) {\n return found;\n }\n const hknp = world._hknp;\n const newWorld = hknp.HP_World_Create()[1];\n hknp.HP_World_SetGravity(newWorld, fo.gravity);\n const limits = hknp.HP_World_GetSpeedLimit(world._hkWorld);\n hknp.HP_World_SetSpeedLimit(newWorld, limits[1], limits[2]);\n const region: WorldRegion = {\n _world: newWorld,\n origin: { x: pos.x, y: pos.y, z: pos.z },\n gravity: [...fo.gravity],\n };\n fo.regions.push(region);\n return region;\n}\n\n// ─── Hooks ───────────────────────────────────────────────────────────\n\nfunction _placeBody(world: PhysicsWorld, body: PhysicsBody, startsAsleep: boolean): void {\n const hknp = world._hknp;\n const node = body.node;\n const region = _getOrCreateRegion(world, node.position);\n hknp.HP_World_AddBody(region._world, body._hkBody, startsAsleep);\n const p = node.position;\n const q = node.rotationQuaternion;\n const o = region.origin;\n hknp.HP_Body_SetQTransform(body._hkBody, [\n [p.x - o.x, p.y - o.y, p.z - o.z],\n [q.x, q.y, q.z, q.w],\n ]);\n body._region = region;\n}\n\nfunction _step(world: PhysicsWorld): void {\n const hknp = world._hknp;\n const bodies = world._bodies;\n const regions = world._fo!.regions;\n\n // Re-region bodies that drifted out of their region BEFORE stepping.\n for (let i = 0; i < bodies.length; i++) {\n _reRegionBody(world, bodies[i]!);\n }\n\n // Pre-step: sync ANIMATED bodies from node → Havok.\n for (let i = 0; i < bodies.length; i++) {\n const b = bodies[i]!;\n if (b.motionType === (PhysicsMotionType.ANIMATED as number)) {\n _syncNodeToBody(hknp, b);\n }\n }\n\n // Step every region world.\n for (let i = 0; i < regions.length; i++) {\n hknp.HP_World_Step(regions[i]!._world, world._timestep);\n }\n\n // Post-step: sync DYNAMIC bodies from Havok → node.\n for (let i = 0; i < bodies.length; i++) {\n const b = bodies[i]!;\n if (b.motionType === (PhysicsMotionType.DYNAMIC as number)) {\n _syncBodyToNode(hknp, b);\n }\n }\n\n // Reclaim regions emptied by migration.\n _gcRegions(world);\n}\n\nfunction _setGravity(world: PhysicsWorld, gravity: number[], worldPosition?: Vec3): void {\n const fo = world._fo!;\n const hknp = world._hknp;\n if (worldPosition) {\n const region = _getOrCreateRegion(world, worldPosition);\n region.gravity = gravity;\n hknp.HP_World_SetGravity(region._world, gravity);\n return;\n }\n fo.gravity = gravity;\n for (const region of fo.regions) {\n region.gravity = gravity;\n hknp.HP_World_SetGravity(region._world, gravity);\n }\n}\n\nfunction _getRegionGravity(world: PhysicsWorld, worldPosition: Vec3): number[] {\n return _getOrCreateRegion(world, worldPosition).gravity;\n}\n\nfunction _setVelocityLimits(world: PhysicsWorld, maxLinear: number, maxAngular: number): void {\n for (const region of world._fo!.regions) {\n world._hknp.HP_World_SetSpeedLimit(region._world, maxLinear, maxAngular);\n }\n}\n\nfunction _dispose(world: PhysicsWorld): void {\n const hknp = world._hknp;\n const bodies = world._bodies;\n for (let i = bodies.length - 1; i >= 0; i--) {\n const b = bodies[i]!;\n hknp.HP_World_RemoveBody(b._region!._world, b._hkBody);\n hknp.HP_Body_Release(b._hkBody);\n }\n bodies.length = 0;\n const regions = world._fo!.regions;\n for (const region of regions) {\n hknp.HP_World_Release(region._world);\n }\n regions.length = 0;\n}\n\n// ─── Migration & sync ────────────────────────────────────────────────\n\n/**\n * Moves a body to the correct region if it has drifted past `radius * 1.2` (hysteresis) from its\n * current region's origin, preserving linear and angular velocity. Uses a one-second velocity\n * look-ahead to prefer joining an existing target region over spawning a throwaway one.\n */\nfunction _reRegionBody(world: PhysicsWorld, body: PhysicsBody): void {\n const hknp = world._hknp;\n const fo = world._fo!;\n const current = body._region!;\n\n const t = hknp.HP_Body_GetQTransform(body._hkBody)[1];\n const localPos = t[0];\n const orientation = t[1];\n\n // Distance from region origin == magnitude of the local position.\n const margin = fo.radius * 1.2;\n if (localPos[0] * localPos[0] + localPos[1] * localPos[1] + localPos[2] * localPos[2] <= margin * margin) {\n return;\n }\n\n const wx = localPos[0] + current.origin.x;\n const wy = localPos[1] + current.origin.y;\n const wz = localPos[2] + current.origin.z;\n\n const linVel = hknp.HP_Body_GetLinearVelocity(body._hkBody)[1];\n const angVel = hknp.HP_Body_GetAngularVelocity(body._hkBody)[1];\n\n const worldPos: Vec3 = { x: wx, y: wy, z: wz };\n const lookAhead: Vec3 = { x: wx + linVel[0], y: wy + linVel[1], z: wz + linVel[2] };\n\n let next = _findRegion(fo, lookAhead);\n if (!next || next === current) {\n next = _findRegion(fo, worldPos);\n }\n if (!next || next === current) {\n next = _getOrCreateRegion(world, worldPos);\n }\n if (next === current) {\n return;\n }\n\n hknp.HP_World_RemoveBody(current._world, body._hkBody);\n const o = next.origin;\n hknp.HP_Body_SetQTransform(body._hkBody, [[wx - o.x, wy - o.y, wz - o.z], orientation]);\n hknp.HP_World_AddBody(next._world, body._hkBody, false);\n hknp.HP_Body_SetLinearVelocity(body._hkBody, linVel);\n hknp.HP_Body_SetAngularVelocity(body._hkBody, angVel);\n body._region = next;\n}\n\n/** Releases any non-default region that no longer holds any bodies. */\nfunction _gcRegions(world: PhysicsWorld): void {\n const regions = world._fo!.regions;\n if (regions.length <= 1) {\n return;\n }\n const hknp = world._hknp;\n const used = new Set<WorldRegion>();\n for (let i = 0; i < world._bodies.length; i++) {\n used.add(world._bodies[i]!._region!);\n }\n for (let i = regions.length - 1; i >= 1; i--) {\n const region = regions[i]!;\n if (!used.has(region)) {\n hknp.HP_World_Release(region._world);\n regions.splice(i, 1);\n }\n }\n}\n\nfunction _syncBodyToNode(hknp: any, body: PhysicsBody): void {\n const t = hknp.HP_Body_GetQTransform(body._hkBody)[1];\n const pos = t[0]; // [x, y, z] in region-local space\n const rot = t[1]; // [x, y, z, w]\n const o = body._region!.origin;\n const node = body.node;\n node.position.set(pos[0] + o.x, pos[1] + o.y, pos[2] + o.z);\n node.rotationQuaternion.set(rot[0], rot[1], rot[2], rot[3]);\n}\n\nfunction _syncNodeToBody(hknp: any, body: PhysicsBody): void {\n const node = body.node;\n const p = node.position;\n const q = node.rotationQuaternion;\n const o = body._region!.origin;\n hknp.HP_Body_SetQTransform(body._hkBody, [\n [p.x - o.x, p.y - o.y, p.z - o.z],\n [q.x, q.y, q.z, q.w],\n ]);\n}\n"],"names":[],"mappings":"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"}
|