@forgeax/engine-render 0.1.19 → 0.1.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +215 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/assembly/device-feature-admission.d.ts +18 -0
- package/dist/assembly/device-feature-admission.d.ts.map +1 -0
- package/dist/assembly/directional-shadow-inspection.d.ts +38 -0
- package/dist/assembly/directional-shadow-inspection.d.ts.map +1 -0
- package/dist/assembly/factory.d.ts +34 -20
- package/dist/assembly/factory.d.ts.map +1 -1
- package/dist/assembly/host-contract.d.ts +9 -0
- package/dist/assembly/host-contract.d.ts.map +1 -1
- package/dist/assembly/render-target-host.d.ts +46 -0
- package/dist/assembly/render-target-host.d.ts.map +1 -0
- package/dist/assets/render-asset-port.d.ts.map +1 -1
- package/dist/authoring.mjs +3 -0
- package/dist/authoring.mjs.map +1 -1
- package/dist/capture/cube-views.d.ts +21 -0
- package/dist/capture/cube-views.d.ts.map +1 -0
- package/dist/capture/scheduler.d.ts +54 -0
- package/dist/capture/scheduler.d.ts.map +1 -0
- package/dist/{chunk-RJRUS55L.mjs → chunk-5YADUCA7.mjs} +1896 -1624
- package/dist/chunk-5YADUCA7.mjs.map +1 -0
- package/dist/{chunk-LOSO4CTV.mjs → chunk-6OPXDQ2E.mjs} +423 -46
- package/dist/chunk-6OPXDQ2E.mjs.map +1 -0
- package/dist/{chunk-IPXCABEP.mjs → chunk-BXBQQKFQ.mjs} +3 -9
- package/dist/chunk-BXBQQKFQ.mjs.map +1 -0
- package/dist/chunk-FS2KNUQH.mjs +1294 -0
- package/dist/chunk-FS2KNUQH.mjs.map +1 -0
- package/dist/components/camera.d.ts +1 -0
- package/dist/components/camera.d.ts.map +1 -1
- package/dist/components/cube-camera.d.ts +20 -0
- package/dist/components/cube-camera.d.ts.map +1 -0
- package/dist/components/directional-light.d.ts +11 -2
- package/dist/components/directional-light.d.ts.map +1 -1
- package/dist/components/directional-shadow-filter.d.ts +25 -0
- package/dist/components/directional-shadow-filter.d.ts.map +1 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/light-helpers.d.ts.map +1 -1
- package/dist/components/reflection-probe.d.ts +17 -0
- package/dist/components/reflection-probe.d.ts.map +1 -0
- package/dist/components/spot-light.d.ts +12 -0
- package/dist/components/spot-light.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +8552 -1612
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/gpu-residency.d.ts +19 -2
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/environment/frame.d.ts.map +1 -1
- package/dist/environment/lifecycle.d.ts +5 -0
- package/dist/environment/lifecycle.d.ts.map +1 -1
- package/dist/errors/render.d.ts +209 -93
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/extract/directional-shadow-projection.d.ts +23 -0
- package/dist/extract/directional-shadow-projection.d.ts.map +1 -0
- package/dist/extract/environment.d.ts +14 -0
- package/dist/extract/environment.d.ts.map +1 -1
- package/dist/extract/visibility.d.ts.map +1 -1
- package/dist/features/fullscreen.d.ts.map +1 -1
- package/dist/features/targets.d.ts.map +1 -1
- package/dist/features/types.d.ts +10 -0
- package/dist/features/types.d.ts.map +1 -1
- package/dist/fullscreen-post-process-pass.d.ts +13 -2
- package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
- package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts +0 -1
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/gpu-usage.d.ts +2 -0
- package/dist/gpu-usage.d.ts.map +1 -1
- package/dist/hdrp-buffers.d.ts +19 -17
- package/dist/hdrp-buffers.d.ts.map +1 -1
- package/dist/ibl/IblPipelineCache.d.ts +4 -0
- package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
- package/dist/ibl/kernel-cache.d.ts +36 -0
- package/dist/ibl/kernel-cache.d.ts.map +1 -0
- package/dist/index.d.ts +35 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +4 -4
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +51 -0
- package/dist/inspection-types.d.ts.map +1 -1
- package/dist/instances-derived-bounds.d.ts +50 -0
- package/dist/instances-derived-bounds.d.ts.map +1 -0
- package/dist/internal.mjs +1 -1
- package/dist/light-buffer-layout.d.ts +30 -13
- package/dist/light-buffer-layout.d.ts.map +1 -1
- package/dist/mesh-material-bindings.d.ts +54 -0
- package/dist/mesh-material-bindings.d.ts.map +1 -1
- package/dist/pbr-pipeline.d.ts +20 -7
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-forward-lane.d.ts +2 -1
- package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
- package/dist/pipeline/standard-lighting/errors.d.ts +4 -0
- package/dist/pipeline/standard-lighting/errors.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/graph.d.ts +24 -0
- package/dist/pipeline/standard-lighting/graph.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/inspection.d.ts +38 -0
- package/dist/pipeline/standard-lighting/inspection.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/layout.d.ts +28 -0
- package/dist/pipeline/standard-lighting/layout.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/light-frame.d.ts +33 -0
- package/dist/pipeline/standard-lighting/light-frame.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/membership.d.ts +14 -0
- package/dist/pipeline/standard-lighting/membership.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/prepare.d.ts +16 -0
- package/dist/pipeline/standard-lighting/prepare.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/topology.d.ts +45 -0
- package/dist/pipeline/standard-lighting/topology.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/transport.d.ts +36 -0
- package/dist/pipeline/standard-lighting/transport.d.ts.map +1 -0
- package/dist/pipeline/standard-pipeline.d.ts +4 -13
- package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-post.d.ts.map +1 -1
- package/dist/pipeline/standard-profile.d.ts +12 -20
- package/dist/pipeline/standard-profile.d.ts.map +1 -1
- package/dist/pipeline-spec.d.ts +40 -7
- package/dist/pipeline-spec.d.ts.map +1 -1
- package/dist/point-shadow-inspection.d.ts +21 -0
- package/dist/point-shadow-inspection.d.ts.map +1 -0
- package/dist/record/frame-lighting.d.ts +29 -32
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +65 -15
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame-targets.d.ts +5 -0
- package/dist/record/frame-targets.d.ts.map +1 -1
- package/dist/record/frame.d.ts +12 -5
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/gpu-pass-timing/contract.d.ts +96 -0
- package/dist/record/gpu-pass-timing/contract.d.ts.map +1 -0
- package/dist/record/gpu-pass-timing/errors.d.ts +18 -0
- package/dist/record/gpu-pass-timing/errors.d.ts.map +1 -0
- package/dist/record/gpu-pass-timing/index.d.ts +8 -0
- package/dist/record/gpu-pass-timing/index.d.ts.map +1 -0
- package/dist/record/gpu-pass-timing/parser.d.ts +11 -0
- package/dist/record/gpu-pass-timing/parser.d.ts.map +1 -0
- package/dist/record/gpu-pass-timing/session.d.ts +43 -0
- package/dist/record/gpu-pass-timing/session.d.ts.map +1 -0
- package/dist/record/gpu-timing.d.ts +37 -0
- package/dist/record/gpu-timing.d.ts.map +1 -0
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +23 -2
- package/dist/record/main-pass-material.d.ts.map +1 -1
- package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts +2 -0
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +48 -3
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/skybox-post-pass.d.ts.map +1 -1
- package/dist/record/typed-frame-graph.d.ts +81 -5
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/record/view-ubo.d.ts +7 -4
- package/dist/record/view-ubo.d.ts.map +1 -1
- package/dist/reflection/filter.d.ts +22 -0
- package/dist/reflection/filter.d.ts.map +1 -0
- package/dist/reflection/gpu-table.d.ts +25 -0
- package/dist/reflection/gpu-table.d.ts.map +1 -0
- package/dist/reflection/projection.d.ts +91 -0
- package/dist/reflection/projection.d.ts.map +1 -0
- package/dist/render-contract.d.ts +114 -7
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-data.d.ts +10 -6
- package/dist/render-data.d.ts.map +1 -1
- package/dist/render-graph-primitives.d.ts +1 -0
- package/dist/render-graph-primitives.d.ts.map +1 -1
- package/dist/render-pipeline.d.ts +78 -2
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +113 -20
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts +24 -3
- package/dist/render-system.d.ts.map +1 -1
- package/dist/renderer-inspect.d.ts +29 -0
- package/dist/renderer-inspect.d.ts.map +1 -0
- package/dist/renderer.d.ts +43 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/scene/render-scene.d.ts +14 -0
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/dist/shadow-atlas.d.ts +3 -3
- package/dist/shadow-atlas.d.ts.map +1 -1
- package/dist/ssao-buffers.d.ts +3 -0
- package/dist/ssao-buffers.d.ts.map +1 -1
- package/dist/targets/contracts.d.ts +66 -0
- package/dist/targets/contracts.d.ts.map +1 -0
- package/dist/targets/graph-projection.d.ts +29 -0
- package/dist/targets/graph-projection.d.ts.map +1 -0
- package/dist/targets/material-source.d.ts +34 -0
- package/dist/targets/material-source.d.ts.map +1 -0
- package/dist/targets/owner.d.ts +32 -0
- package/dist/targets/owner.d.ts.map +1 -0
- package/dist/targets/physical.d.ts +18 -0
- package/dist/targets/physical.d.ts.map +1 -0
- package/dist/targets/readback.d.ts +39 -0
- package/dist/targets/readback.d.ts.map +1 -0
- package/dist/temporal/frame.d.ts +44 -0
- package/dist/temporal/frame.d.ts.map +1 -0
- package/dist/temporal/index.mjs +3 -3
- package/dist/typed-render-graph-primitives.d.ts +6 -1
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/typed-shadow-atlas.d.ts +10 -0
- package/dist/typed-shadow-atlas.d.ts.map +1 -0
- package/dist/typed-shadow-passes.d.ts +1 -1
- package/dist/typed-shadow-passes.d.ts.map +1 -1
- package/dist/volume/capability.d.ts +57 -0
- package/dist/volume/capability.d.ts.map +1 -0
- package/dist/volume/component.d.ts +61 -0
- package/dist/volume/component.d.ts.map +1 -0
- package/dist/volume/extract.d.ts +11 -0
- package/dist/volume/extract.d.ts.map +1 -0
- package/dist/volume/inspection.d.ts +38 -0
- package/dist/volume/inspection.d.ts.map +1 -0
- package/dist/volume/passes.d.ts +38 -0
- package/dist/volume/passes.d.ts.map +1 -0
- package/dist/volume/recovery.d.ts +38 -0
- package/dist/volume/recovery.d.ts.map +1 -0
- package/dist/volume/resources.d.ts +57 -0
- package/dist/volume/resources.d.ts.map +1 -0
- package/dist/volume/temporal.d.ts +35 -0
- package/dist/volume/temporal.d.ts.map +1 -0
- package/package.json +20 -20
- package/src/__tests__/bloom-exact-zero.unit.test.ts +48 -19
- package/src/__tests__/bloom-lifecycle.integration.test.ts +41 -13
- package/src/__tests__/camera-target-consumption.unit.test.ts +53 -0
- package/src/__tests__/cluster-binner.unit.test.ts +35 -0
- package/src/__tests__/cluster-grid-default-surface.unit.test.ts +7 -2
- package/src/__tests__/cube-camera-scheduler.unit.test.ts +70 -0
- package/src/__tests__/cube-camera-single-submit.perf.test.ts +75 -0
- package/src/__tests__/cube-camera-view-matrices.unit.test.ts +41 -0
- package/src/__tests__/cube-camera-vocabulary.unit.test.ts +47 -0
- package/src/__tests__/cube-camera.integration.test.ts +42 -0
- package/src/__tests__/device-feature-admission.unit.test.ts +73 -0
- package/src/__tests__/direct-light-snapshot.unit.test.ts +3 -3
- package/src/__tests__/docs-gate-green.unit.test.ts +38 -0
- package/src/__tests__/factory-contract.integration.test.ts +255 -29
- package/src/__tests__/fog-component.unit.test.ts +67 -0
- package/src/__tests__/fog.integration.test.ts +86 -0
- package/src/__tests__/fullscreen-feature-plan.unit.test.ts +37 -0
- package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +10 -9
- package/src/__tests__/gpu-driven-indirect-raster.dawn.test.ts +17 -1
- package/src/__tests__/gpu-driven-production.integration.test.ts +16 -17
- package/src/__tests__/gpu-driven-view-graph.integration.test.ts +12 -4
- package/src/__tests__/gpu-pass-timing-browser-runner.ts +281 -0
- package/src/__tests__/gpu-pass-timing-coexistence.integration.test.ts +80 -0
- package/src/__tests__/gpu-pass-timing-contract.unit.test.ts +243 -0
- package/src/__tests__/gpu-pass-timing-exact-zero.unit.test.ts +131 -0
- package/src/__tests__/gpu-pass-timing-lifecycle.integration.test.ts +230 -0
- package/src/__tests__/gpu-pass-timing-observation.unit.test.ts +233 -0
- package/src/__tests__/gpu-pass-timing-session.unit.test.ts +438 -0
- package/src/__tests__/gpu-pass-timing.browser.test.ts +26 -0
- package/src/__tests__/gpu-pass-timing.rhi-null.unit.test.ts +125 -0
- package/src/__tests__/hdrp-group2-visibility.unit.test.ts +24 -0
- package/src/__tests__/instances-culling.integration.test.ts +94 -0
- package/src/__tests__/instances-derived-bounds.unit.test.ts +118 -0
- package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +14 -14
- package/src/__tests__/material-observation.integration.test.ts +101 -0
- package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +1 -1
- package/src/__tests__/pbr-view-layout.unit.test.ts +16 -0
- package/src/__tests__/point-shadow-inspection.unit.test.ts +60 -0
- package/src/__tests__/public-surface.test-d.ts +10 -0
- package/src/__tests__/reflection-probe-authoring.unit.test.ts +71 -0
- package/src/__tests__/reflection-probe-filter.unit.test.ts +54 -0
- package/src/__tests__/reflection-probe-ibl-topology.unit.test.ts +45 -0
- package/src/__tests__/reflection-probe-pass-encode.integration.test.ts +101 -0
- package/src/__tests__/reflection-probe-performance.perf.test.ts +60 -0
- package/src/__tests__/reflection-probe-recovery.integration.test.ts +55 -0
- package/src/__tests__/reflection-probe-selection.unit.test.ts +46 -0
- package/src/__tests__/render-error-code-owner.test-d.ts +23 -3
- package/src/__tests__/render-feature-docs.unit.test.ts +1 -1
- package/src/__tests__/render-target-admission.unit.test.ts +80 -0
- package/src/__tests__/render-target-graph-projection.unit.test.ts +102 -0
- package/src/__tests__/render-target-inspection.unit.test.ts +52 -0
- package/src/__tests__/render-target-material-source.test-d.ts +30 -0
- package/src/__tests__/render-target-material-source.unit.test.ts +97 -0
- package/src/__tests__/render-target-owner-placement.unit.test.ts +203 -0
- package/src/__tests__/render-target-owner.unit.test.ts +122 -0
- package/src/__tests__/render-target-probe-soak.perf.test.ts +94 -0
- package/src/__tests__/render-target-public-schema.json +107 -0
- package/src/__tests__/render-target-public-schema.unit.test.ts +91 -0
- package/src/__tests__/render-target-public-surface.test-d.ts +83 -0
- package/src/__tests__/render-target-readback.integration.test.ts +90 -0
- package/src/__tests__/render-target-recovery.integration.test.ts +85 -0
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +76 -0
- package/src/__tests__/renderer-frame-transaction.integration.test.ts +1 -1
- package/src/__tests__/renderer-lifecycle.integration.test.ts +14 -0
- package/src/__tests__/shader-stage-surface.unit.test.ts +19 -4
- package/src/__tests__/skin-motion-regression.unit.test.ts +132 -0
- package/src/__tests__/skinned-shadow-caster.test.ts +32 -2
- package/src/__tests__/standard-cluster-capability.integration.test.ts +63 -0
- package/src/__tests__/standard-cluster-transport.unit.test.ts +121 -0
- package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +232 -0
- package/src/__tests__/standard-clustered-pipeline.unit.test.ts +8 -5
- package/src/__tests__/standard-lighting-inspection.integration.test.ts +138 -0
- package/src/__tests__/standard-lighting-ssot.unit.test.ts +18 -0
- package/src/__tests__/standard-lighting-write.unit.test.ts +377 -0
- package/src/__tests__/standard-lighting.integration.test.ts +67 -0
- package/src/__tests__/standard-pipeline.dawn.test.ts +206 -5
- package/src/__tests__/standard-pipeline.integration.test.ts +181 -150
- package/src/__tests__/standard-probe-binding.unit.test.ts +25 -0
- package/src/__tests__/standard-profile-oracle.unit.test.ts +23 -36
- package/src/__tests__/typed-pipeline-topology.unit.test.ts +395 -14
- package/src/__tests__/urp-pipeline-targets.unit.test.ts +2 -2
- package/src/__tests__/visibility-resolver.unit.test.ts +15 -0
- package/src/__tests__/volume-optics-oracle.unit.test.ts +85 -0
- package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +655 -0
- package/src/__tests__/volumetric-fog-component.unit.test.ts +71 -0
- package/src/__tests__/volumetric-fog-composite-stage-readback.dawn.test.ts +376 -0
- package/src/__tests__/volumetric-fog-errors.unit.test.ts +51 -0
- package/src/__tests__/volumetric-fog-inspection.unit.test.ts +77 -0
- package/src/__tests__/volumetric-fog-light-capability.unit.test.ts +19 -0
- package/src/__tests__/volumetric-fog-light-consumers.integration.test.ts +18 -0
- package/src/__tests__/volumetric-fog-light-errors.unit.test.ts +17 -0
- package/src/__tests__/volumetric-fog-light-topology.unit.test.ts +21 -0
- package/src/__tests__/volumetric-fog-light-transaction.integration.test.ts +53 -0
- package/src/__tests__/volumetric-fog-m9-watcher.integration.test.ts +27 -0
- package/src/__tests__/volumetric-fog-point-light-owner.integration.test.ts +44 -0
- package/src/__tests__/volumetric-fog-point-spot-accumulation.integration.test.ts +49 -0
- package/src/__tests__/volumetric-fog-point-spot-contract.unit.test.ts +28 -0
- package/src/__tests__/volumetric-fog-point-spot-lkg.integration.test.ts +65 -0
- package/src/__tests__/volumetric-fog-point-spot-selection.integration.test.ts +49 -0
- package/src/__tests__/volumetric-fog-point-spot-temporal.unit.test.ts +55 -0
- package/src/__tests__/volumetric-fog-projector-contract.unit.test.ts +22 -0
- package/src/__tests__/volumetric-fog-projector-inspection.unit.test.ts +12 -0
- package/src/__tests__/volumetric-fog-projector-surface-volume.integration.test.ts +11 -0
- package/src/__tests__/volumetric-fog-projector-transaction.integration.test.ts +26 -0
- package/src/__tests__/volumetric-fog-recovery.integration.test.ts +47 -0
- package/src/__tests__/volumetric-fog-selected-light-resolution.integration.test.ts +63 -0
- package/src/__tests__/volumetric-fog-spot-closure.integration.test.ts +26 -0
- package/src/__tests__/volumetric-fog-spot-contract.test-d.ts +21 -0
- package/src/__tests__/volumetric-fog-spot-contract.unit.test.ts +27 -0
- package/src/__tests__/volumetric-fog-spot-frame-optics.integration.test.ts +14 -0
- package/src/__tests__/volumetric-fog-spot-optics.integration.test.ts +29 -0
- package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +550 -0
- package/src/__tests__/volumetric-fog-temporal-selected-light.unit.test.ts +33 -0
- package/src/__tests__/volumetric-fog-temporal.unit.test.ts +45 -0
- package/src/__tests__/volumetric-fog-topology.unit.test.ts +237 -0
- package/src/__tests__/volumetric-fog-transparent.integration.test.ts +27 -0
- package/src/assembly/device-feature-admission.ts +73 -0
- package/src/assembly/directional-shadow-inspection.ts +122 -0
- package/src/assembly/factory.ts +1093 -246
- package/src/assembly/host-contract.ts +25 -0
- package/src/assembly/render-target-host.ts +847 -0
- package/src/assets/asset-decoders.ts +1 -1
- package/src/assets/render-asset-port.ts +7 -1
- package/src/capture/cube-views.ts +69 -0
- package/src/capture/scheduler.ts +265 -0
- package/src/components/__tests__/schema-contract.unit.test.ts +12 -0
- package/src/components/__tests__/schema-parity.unit.test.ts +9 -0
- package/src/components/camera.ts +4 -0
- package/src/components/cube-camera.ts +52 -0
- package/src/components/directional-light.ts +22 -2
- package/src/components/directional-shadow-filter.ts +57 -0
- package/src/components/index.ts +2 -0
- package/src/components/light-helpers.ts +85 -7
- package/src/components/reflection-probe.ts +49 -0
- package/src/components/spot-light.ts +22 -3
- package/src/device/gpu-residency.ts +168 -58
- package/src/environment/frame.ts +6 -24
- package/src/environment/lifecycle.ts +12 -0
- package/src/errors/render.ts +405 -143
- package/src/extract/directional-shadow-projection.ts +61 -0
- package/src/extract/environment.ts +57 -0
- package/src/extract/visibility.ts +25 -1
- package/src/features/fullscreen.ts +3 -0
- package/src/features/targets.ts +4 -1
- package/src/features/types.ts +10 -0
- package/src/fullscreen-post-process-pass.ts +43 -2
- package/src/gpu-driven/batch-topology.ts +1 -0
- package/src/gpu-driven/production-raster.ts +1 -3
- package/src/gpu-scene.ts +8 -1
- package/src/gpu-usage.ts +2 -0
- package/src/hdrp-buffers.ts +165 -59
- package/src/ibl/IblPipelineCache.ts +11 -0
- package/src/ibl/kernel-cache.ts +88 -0
- package/src/index.ts +186 -1
- package/src/inspection-types.ts +56 -0
- package/src/instances-derived-bounds.ts +171 -0
- package/src/light-buffer-layout.ts +74 -26
- package/src/mesh-material-bindings.ts +132 -0
- package/src/pbr-pipeline.ts +139 -27
- package/src/pipeline/standard-forward-lane.ts +65 -15
- package/src/pipeline/standard-lighting/errors.ts +6 -0
- package/src/pipeline/standard-lighting/graph.ts +157 -0
- package/src/pipeline/standard-lighting/inspection.ts +88 -0
- package/src/pipeline/standard-lighting/layout.ts +31 -0
- package/src/pipeline/standard-lighting/light-frame.ts +34 -0
- package/src/pipeline/standard-lighting/membership.ts +43 -0
- package/src/pipeline/standard-lighting/prepare.ts +37 -0
- package/src/pipeline/standard-lighting/topology.ts +120 -0
- package/src/pipeline/standard-lighting/transport.ts +77 -0
- package/src/pipeline/standard-pipeline.ts +141 -192
- package/src/pipeline/standard-post.ts +26 -7
- package/src/pipeline/standard-profile.ts +23 -30
- package/src/pipeline-spec.ts +84 -21
- package/src/point-shadow-inspection.ts +55 -0
- package/src/record/__tests__/temporal-payload.unit.test.ts +42 -1
- package/src/record/__tests__/transparent-group2-selection.unit.test.ts +73 -0
- package/src/record/__tests__/view-ubo-layout.unit.test.ts +40 -0
- package/src/record/frame-lighting.ts +470 -516
- package/src/record/frame-snapshot.ts +76 -28
- package/src/record/frame-targets.ts +23 -2
- package/src/record/frame.ts +1169 -79
- package/src/record/gpu-pass-timing/contract.ts +180 -0
- package/src/record/gpu-pass-timing/errors.ts +34 -0
- package/src/record/gpu-pass-timing/index.ts +37 -0
- package/src/record/gpu-pass-timing/parser.ts +97 -0
- package/src/record/gpu-pass-timing/session.ts +938 -0
- package/src/record/gpu-timing.ts +233 -0
- package/src/record/main-pass-geometry.ts +120 -80
- package/src/record/main-pass-material.ts +101 -11
- package/src/record/main-pass-sprite-draws.ts +54 -25
- package/src/record/main-pass.ts +71 -15
- package/src/record/render-context.ts +77 -0
- package/src/record/shadow-pass.ts +104 -66
- package/src/record/skybox-post-pass.ts +20 -4
- package/src/record/typed-frame-graph.ts +1016 -32
- package/src/record/view-ubo.ts +63 -28
- package/src/reflection/filter.ts +81 -0
- package/src/reflection/gpu-table.ts +44 -0
- package/src/reflection/projection.ts +281 -0
- package/src/render-contract.ts +147 -4
- package/src/render-data.ts +50 -22
- package/src/render-graph-primitives.ts +9 -4
- package/src/render-pipeline.ts +161 -2
- package/src/render-system-extract.ts +886 -103
- package/src/render-system.ts +1174 -66
- package/src/renderer-inspect.ts +66 -0
- package/src/renderer.ts +222 -0
- package/src/scene/render-scene.ts +117 -9
- package/src/shadow-atlas.ts +5 -5
- package/src/ssao-buffers.ts +9 -2
- package/src/targets/contracts.ts +182 -0
- package/src/targets/graph-projection.ts +169 -0
- package/src/targets/material-source.ts +143 -0
- package/src/targets/owner.ts +214 -0
- package/src/targets/physical.ts +18 -0
- package/src/targets/readback.ts +153 -0
- package/src/temporal/frame.ts +128 -0
- package/src/transmission/__tests__/standard-transmission.dawn.test.ts +61 -23
- package/src/typed-render-graph-primitives.ts +34 -2
- package/src/typed-shadow-atlas.ts +17 -0
- package/src/typed-shadow-passes.ts +47 -40
- package/src/volume/capability.ts +173 -0
- package/src/volume/component.ts +110 -0
- package/src/volume/extract.ts +21 -0
- package/src/volume/inspection.ts +78 -0
- package/src/volume/passes.ts +686 -0
- package/src/volume/recovery.ts +86 -0
- package/src/volume/resources.ts +236 -0
- package/src/volume/temporal.ts +89 -0
- package/dist/chunk-IPXCABEP.mjs.map +0 -1
- package/dist/chunk-LOSO4CTV.mjs.map +0 -1
- package/dist/chunk-RJRUS55L.mjs.map +0 -1
- package/dist/chunk-YTQ5C7YQ.mjs +0 -584
- package/dist/chunk-YTQ5C7YQ.mjs.map +0 -1
package/src/record/view-ubo.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { mat4 } from '@forgeax/engine-math';
|
|
10
10
|
import type { Buffer, RhiQueue } from '@forgeax/engine-rhi';
|
|
11
|
+
import type { DirectionalShadowQuality } from '../components/directional-shadow-filter';
|
|
11
12
|
import type { FogFrame } from '../extract/environment';
|
|
12
13
|
import type { PointsLinesStyle } from '../points-lines/snapshot';
|
|
13
14
|
import type { CameraSnapshot } from '../render-contract';
|
|
@@ -16,9 +17,9 @@ import type {
|
|
|
16
17
|
ExtractedLights,
|
|
17
18
|
SpotLightSnapshot,
|
|
18
19
|
} from '../render-system-extract';
|
|
20
|
+
import { SHADOW_ATLAS_DEFAULT_LAYERS } from '../shadow-atlas';
|
|
19
21
|
import type { TemporalView as LegacyTemporalView } from '../temporal/temporal-view';
|
|
20
22
|
import type { TemporalView } from '../temporal/view';
|
|
21
|
-
import { clampPcfKernelSize } from './frame-snapshot';
|
|
22
23
|
import { computeProjectionMatrix, computeViewMatrix } from './helpers';
|
|
23
24
|
|
|
24
25
|
export const VIEW_UNIFORM_BYTES = 960;
|
|
@@ -29,13 +30,39 @@ export const POINTS_LINES_VIEW_BUFFER_SIZE =
|
|
|
29
30
|
POINTS_LINES_VIEW_SLOT_STRIDE * POINTS_LINES_VIEW_SLOT_COUNT;
|
|
30
31
|
export const VIEW_UNIFORM_SLOT_STRIDE = 1024;
|
|
31
32
|
export const POINT_SHADOW_VIEW_SLOT_COUNT = 24;
|
|
33
|
+
export const CUBE_CAPTURE_VIEW_SLOT_BASE = 1 + POINT_SHADOW_VIEW_SLOT_COUNT;
|
|
34
|
+
export const REFLECTION_PROBE_VIEW_SLOT_BASE = CUBE_CAPTURE_VIEW_SLOT_BASE + 6;
|
|
35
|
+
export const REFLECTION_PROBE_VIEW_SLOT_COUNT = 16;
|
|
32
36
|
export const VIEW_UNIFORM_BUFFER_SIZE =
|
|
33
|
-
VIEW_UNIFORM_SLOT_STRIDE * (
|
|
37
|
+
VIEW_UNIFORM_SLOT_STRIDE * (REFLECTION_PROBE_VIEW_SLOT_BASE + REFLECTION_PROBE_VIEW_SLOT_COUNT);
|
|
34
38
|
|
|
35
39
|
export function pointShadowViewOffset(layer: number, face: number): number {
|
|
36
40
|
return VIEW_UNIFORM_SLOT_STRIDE * (1 + layer * 6 + face);
|
|
37
41
|
}
|
|
38
42
|
|
|
43
|
+
/** Project the accepted Directional quality union into the frozen View ABI. */
|
|
44
|
+
function directionalShadowFilterCarrier(
|
|
45
|
+
quality: DirectionalShadowQuality | undefined,
|
|
46
|
+
): readonly [number, number, number, number] {
|
|
47
|
+
if (quality === undefined) return [0, 0, 0, 0];
|
|
48
|
+
if (quality.kind === 'pcss') {
|
|
49
|
+
return [
|
|
50
|
+
quality.preset === 'medium' ? 4 : 5,
|
|
51
|
+
quality.angularRadiusRadians,
|
|
52
|
+
quality.maxPenumbraTexels,
|
|
53
|
+
0,
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
switch (quality.kernel) {
|
|
57
|
+
case 1:
|
|
58
|
+
return [1, 0, 0, 0];
|
|
59
|
+
case 3:
|
|
60
|
+
return [2, 0, 0, 0];
|
|
61
|
+
case 5:
|
|
62
|
+
return [3, 0, 0, 0];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
39
66
|
export function writePointsLinesViewUbo(
|
|
40
67
|
queue: RhiQueue,
|
|
41
68
|
buffer: Buffer,
|
|
@@ -95,10 +122,10 @@ export function writePointsLinesViewUbo(
|
|
|
95
122
|
* [24..26] cameraPos, [28..43] lightViewProj0 (was lightSpaceMatrix),
|
|
96
123
|
* [44..59] inverseViewProj, [60..75] lightViewProj1,
|
|
97
124
|
* [76..91] lightViewProj2, [92..107] lightViewProj3,
|
|
98
|
-
* [108]
|
|
125
|
+
* [108..123] splitPlanes (vec4 lanes: split/world texel/depth span/reserved),
|
|
99
126
|
* [124] cascadeCount, [125] cascadeBlend,
|
|
100
|
-
* [126] depthBias, [127] normalBias,
|
|
101
|
-
*
|
|
127
|
+
* [126] depthBias, [127] normalBias,
|
|
128
|
+
* [128..131] directionalShadowFilter (profile/radius/max penumbra/reserved),
|
|
102
129
|
* [132..195] spotLightViewProj array<mat4x4<f32>, 4> (feat-20260625 w25:
|
|
103
130
|
* folded from standalone binding 9 to fix WebGL2 fragment uniform-buffer
|
|
104
131
|
* overflow; lane N = spot with shadowAtlasTile === N, 16 f32 / lane,
|
|
@@ -113,9 +140,13 @@ export function writeViewUbo(
|
|
|
113
140
|
light: DirectionalLightSnapshot,
|
|
114
141
|
lights: ExtractedLights,
|
|
115
142
|
spotShadowSnapshots: readonly SpotLightSnapshot[],
|
|
116
|
-
|
|
143
|
+
fogOrTemporalOrOffset?: FogFrame | TemporalView | LegacyTemporalView | number,
|
|
117
144
|
temporal?: TemporalView,
|
|
145
|
+
projectorSpotIndex?: number,
|
|
118
146
|
): void {
|
|
147
|
+
const byteOffset = typeof fogOrTemporalOrOffset === 'number' ? fogOrTemporalOrOffset : 0;
|
|
148
|
+
const fogOrTemporal =
|
|
149
|
+
typeof fogOrTemporalOrOffset === 'number' ? undefined : fogOrTemporalOrOffset;
|
|
119
150
|
const isTemporal =
|
|
120
151
|
fogOrTemporal !== undefined &&
|
|
121
152
|
('currentJitterUv' in fogOrTemporal || 'currentUnjitteredViewProjection' in fogOrTemporal);
|
|
@@ -185,38 +216,39 @@ export function writeViewUbo(
|
|
|
185
216
|
viewPayload[228] = camera.near;
|
|
186
217
|
viewPayload[229] = camera.far;
|
|
187
218
|
viewPayload[230] = camera.projection === 'orthographic' ? 1 : 0;
|
|
188
|
-
// splitPlanes at [108]
|
|
219
|
+
// splitPlanes at [108..123] (four vec4 lanes: split/world texel/depth span/reserved).
|
|
189
220
|
if (lights.splitPlanes !== undefined) {
|
|
190
221
|
for (let s = 0; s < 4; s++) {
|
|
191
|
-
|
|
222
|
+
for (let lane = 0; lane < 4; lane++) {
|
|
223
|
+
viewPayload[108 + s * 4 + lane] = lights.splitPlanes[s * 4 + lane] ?? 0;
|
|
224
|
+
}
|
|
192
225
|
}
|
|
193
226
|
}
|
|
194
227
|
// cascadeCount / cascadeBlend at [124..125].
|
|
195
228
|
viewPayload[124] = lights.cascadeCount ?? 0;
|
|
196
229
|
viewPayload[125] = lights.cascadeBlend ?? 0;
|
|
197
|
-
//
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
//
|
|
201
|
-
// the WGSL View struct (common.wgsl) appends matching f32 at the same
|
|
202
|
-
// slots; the host tail pad shrinks 88 B -> 64 B, rest stays zero.
|
|
203
|
-
// pcfKernelSize is host-clamped to the nearest valid odd kernel {1,3,5}
|
|
204
|
-
// (cap 5, matching lighting-directional.wgsl MAX_PCF_HALF=2 from the
|
|
205
|
-
// merged 5.3-production-shadow-demos AC-14 variant-free loop) so the
|
|
206
|
-
// per-iteration radius clip has a fixed, legal bound; undefined
|
|
207
|
-
// (no cast-shadow / no shadow fields) defaults to 3.
|
|
230
|
+
// M1 directional shadow carrier: bias remains at [126]/[127], while the
|
|
231
|
+
// accepted quality union occupies the existing four-float tail [128..131].
|
|
232
|
+
// Point and Spot pcfKernelSize stay in their own snapshot paths and never
|
|
233
|
+
// enter this Directional View projection.
|
|
208
234
|
viewPayload[126] = lights.depthBias ?? 0.005;
|
|
209
235
|
viewPayload[127] = lights.normalBias ?? 0.05;
|
|
210
|
-
|
|
236
|
+
const filterCarrier = directionalShadowFilterCarrier(lights.directionalShadowQuality);
|
|
237
|
+
viewPayload[128] = filterCarrier[0] ?? 0;
|
|
238
|
+
viewPayload[129] = filterCarrier[1] ?? 0;
|
|
239
|
+
viewPayload[130] = filterCarrier[2] ?? 0;
|
|
240
|
+
viewPayload[131] = filterCarrier[3] ?? 0;
|
|
211
241
|
|
|
212
242
|
// feat-20260625-spot-light-shadow-mapping w25 (scope-amend webkit-fallback):
|
|
213
243
|
// spotLightViewProj array<mat4x4<f32>, 4> at floats [132..195] (byte 528,
|
|
214
|
-
// 16 B-aligned after
|
|
244
|
+
// 16 B-aligned after directionalShadowFilter). Lane N = the spot with
|
|
215
245
|
// `shadowAtlasTile === N` (cap = 4); the perspective matrix was already
|
|
216
246
|
// computed by the extract stage (SpotLightSnapshot.lightViewProj) — record
|
|
217
|
-
// never recomputes it (Derive).
|
|
218
|
-
//
|
|
219
|
-
//
|
|
247
|
+
// never recomputes it (Derive). The accepted projector is identified by its
|
|
248
|
+
// spot index, not by a negative shadow tile: a projector-only spot publishes
|
|
249
|
+
// its matrix in lane 0 so the shader never samples a zero matrix. Shadowed
|
|
250
|
+
// projector spots keep their shadow tile lane and carry the projector bit in
|
|
251
|
+
// the LightSlot identity word.
|
|
220
252
|
// Folded from the former standalone binding 9 UBO to keep the WebGL2
|
|
221
253
|
// fallback fragment uniform-buffer count <= 11 (GLES 3.0).
|
|
222
254
|
{
|
|
@@ -228,10 +260,12 @@ export function writeViewUbo(
|
|
|
228
260
|
const ss = spotSnaps[i];
|
|
229
261
|
if (ss === undefined) continue;
|
|
230
262
|
const tile = ss.shadowAtlasTile;
|
|
231
|
-
if (tile < 0 || tile >= SPOT_LVP_LANE_COUNT) continue;
|
|
232
263
|
const lvp = ss.lightViewProj;
|
|
233
264
|
if (lvp === undefined) continue;
|
|
234
|
-
const
|
|
265
|
+
const lane =
|
|
266
|
+
tile >= 0 && tile < SPOT_LVP_LANE_COUNT ? tile : i === projectorSpotIndex ? 0 : -1;
|
|
267
|
+
if (lane < 0) continue;
|
|
268
|
+
const base = SPOT_LVP_BASE_FLOAT + lane * SPOT_LVP_FLOATS_PER_LANE;
|
|
235
269
|
for (let f = 0; f < SPOT_LVP_FLOATS_PER_LANE; f++) {
|
|
236
270
|
viewPayload[base + f] = lvp[f] ?? 0;
|
|
237
271
|
}
|
|
@@ -250,11 +284,12 @@ export function writeViewUbo(
|
|
|
250
284
|
viewPayload[237] = fog.maxOpacity;
|
|
251
285
|
}
|
|
252
286
|
|
|
253
|
-
const viewUploadResult = queue.writeBuffer(viewUniformBuffer,
|
|
287
|
+
const viewUploadResult = queue.writeBuffer(viewUniformBuffer, byteOffset, viewPayload);
|
|
254
288
|
if (!viewUploadResult.ok) throw viewUploadResult.error;
|
|
255
289
|
|
|
256
290
|
for (const snapshot of lights.pointShadow ?? []) {
|
|
257
|
-
if (snapshot.shadowAtlasLayer < 0 || snapshot.shadowAtlasLayer >=
|
|
291
|
+
if (snapshot.shadowAtlasLayer < 0 || snapshot.shadowAtlasLayer >= SHADOW_ATLAS_DEFAULT_LAYERS)
|
|
292
|
+
continue;
|
|
258
293
|
for (let face = 0; face < 6; face += 1) {
|
|
259
294
|
const facePayload = viewPayload.slice();
|
|
260
295
|
const matrix = snapshot.shadowMatrices.subarray(face * 16, (face + 1) * 16);
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export interface ProbeFilterState {
|
|
2
|
+
readonly probeIndex: number;
|
|
3
|
+
readonly faceCount: number;
|
|
4
|
+
readonly mipCount: number;
|
|
5
|
+
readonly cursor: number;
|
|
6
|
+
readonly activeGeneration: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function createProbeFilterState(input: {
|
|
10
|
+
readonly probeIndex: number;
|
|
11
|
+
readonly faceCount?: number;
|
|
12
|
+
readonly mipCount: number;
|
|
13
|
+
readonly activeGeneration?: number;
|
|
14
|
+
}): ProbeFilterState {
|
|
15
|
+
return {
|
|
16
|
+
probeIndex: input.probeIndex,
|
|
17
|
+
faceCount: input.faceCount ?? 6,
|
|
18
|
+
mipCount: input.mipCount,
|
|
19
|
+
cursor: 0,
|
|
20
|
+
activeGeneration: input.activeGeneration ?? 0,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function advanceProbeFilter(
|
|
25
|
+
state: ProbeFilterState,
|
|
26
|
+
):
|
|
27
|
+
| { readonly probeIndex: number; readonly faceIndex: number; readonly mipLevel: number }
|
|
28
|
+
| undefined {
|
|
29
|
+
const total = state.faceCount * state.mipCount;
|
|
30
|
+
if (state.cursor >= total) return undefined;
|
|
31
|
+
return {
|
|
32
|
+
probeIndex: state.probeIndex,
|
|
33
|
+
faceIndex: state.cursor % state.faceCount,
|
|
34
|
+
mipLevel: Math.floor(state.cursor / state.faceCount),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function commitProbeFilterStep(state: ProbeFilterState, success: boolean): ProbeFilterState {
|
|
39
|
+
if (!success) return state;
|
|
40
|
+
const nextCursor = state.cursor + 1;
|
|
41
|
+
const complete = nextCursor >= state.faceCount * state.mipCount;
|
|
42
|
+
return {
|
|
43
|
+
...state,
|
|
44
|
+
cursor: nextCursor,
|
|
45
|
+
activeGeneration: complete ? state.activeGeneration + 1 : state.activeGeneration,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function probeFilterIsSteady(state: ProbeFilterState): boolean {
|
|
50
|
+
return state.cursor >= state.faceCount * state.mipCount;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function normalize(vector: readonly [number, number, number]): [number, number, number] {
|
|
54
|
+
const length = Math.hypot(vector[0], vector[1], vector[2]);
|
|
55
|
+
if (length === 0) return [0, 0, 1];
|
|
56
|
+
return [vector[0] / length, vector[1] / length, vector[2] / length];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function boxProjectReflectionDirection(
|
|
60
|
+
direction: readonly [number, number, number],
|
|
61
|
+
boxCenter: readonly [number, number, number],
|
|
62
|
+
halfExtents: readonly [number, number, number],
|
|
63
|
+
point: readonly [number, number, number],
|
|
64
|
+
): [number, number, number] {
|
|
65
|
+
const ray = normalize(direction);
|
|
66
|
+
let distance = Number.POSITIVE_INFINITY;
|
|
67
|
+
for (let axis = 0; axis < 3; axis += 1) {
|
|
68
|
+
const component = ray[axis] ?? 0;
|
|
69
|
+
if (component === 0) continue;
|
|
70
|
+
const extent = halfExtents[axis] ?? 0;
|
|
71
|
+
const edge = component > 0 ? (boxCenter[axis] ?? 0) + extent : (boxCenter[axis] ?? 0) - extent;
|
|
72
|
+
const candidate = (edge - (point[axis] ?? 0)) / component;
|
|
73
|
+
if (candidate > 0) distance = Math.min(distance, candidate);
|
|
74
|
+
}
|
|
75
|
+
if (!Number.isFinite(distance)) return ray;
|
|
76
|
+
return normalize([
|
|
77
|
+
(point[0] ?? 0) + ray[0] * distance - (boxCenter[0] ?? 0),
|
|
78
|
+
(point[1] ?? 0) + ray[1] * distance - (boxCenter[1] ?? 0),
|
|
79
|
+
(point[2] ?? 0) + ray[2] * distance - (boxCenter[2] ?? 0),
|
|
80
|
+
]);
|
|
81
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Buffer, Sampler, TextureView } from '@forgeax/engine-rhi';
|
|
2
|
+
|
|
3
|
+
export interface ReflectionProbeTableRow {
|
|
4
|
+
readonly primitiveKey?: string;
|
|
5
|
+
readonly index: number;
|
|
6
|
+
readonly worldId: number;
|
|
7
|
+
readonly entityKey: number;
|
|
8
|
+
readonly center: readonly [number, number, number];
|
|
9
|
+
readonly halfExtents: readonly [number, number, number];
|
|
10
|
+
readonly intensity: number;
|
|
11
|
+
readonly generation: number;
|
|
12
|
+
/** Renderer-owned filtered cube used by the Standard material lane. */
|
|
13
|
+
readonly filteredView?: TextureView;
|
|
14
|
+
/** Renderer-owned sampler paired with filteredView. */
|
|
15
|
+
readonly sampler?: Sampler;
|
|
16
|
+
/** Per-probe uniform payload; keeps MaterialAsset ABI unchanged. */
|
|
17
|
+
readonly uniformBuffer?: Buffer;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ReflectionProbeTable {
|
|
21
|
+
readonly rows: readonly ReflectionProbeTableRow[];
|
|
22
|
+
readonly cpuIndexByPrimitive: (primitiveKey: string) => number | undefined;
|
|
23
|
+
readonly gpuIndexByPrimitive: (primitiveKey: string) => number | undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function buildReflectionProbeTable(
|
|
27
|
+
rows: readonly ReflectionProbeTableRow[],
|
|
28
|
+
): ReflectionProbeTable {
|
|
29
|
+
const ordered = rows.slice().sort((a, b) => a.index - b.index);
|
|
30
|
+
const indices = new Map<string, number>();
|
|
31
|
+
for (const row of ordered) {
|
|
32
|
+
if (row.primitiveKey !== undefined) indices.set(row.primitiveKey, row.index);
|
|
33
|
+
}
|
|
34
|
+
const indexFor = (primitiveKey: string): number | undefined => indices.get(primitiveKey);
|
|
35
|
+
return {
|
|
36
|
+
rows: Object.freeze(ordered),
|
|
37
|
+
cpuIndexByPrimitive: indexFor,
|
|
38
|
+
gpuIndexByPrimitive: indexFor,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function reflectionProbeTableBytes(table: ReflectionProbeTable): number {
|
|
43
|
+
return table.rows.length * 48;
|
|
44
|
+
}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import type { ReflectionProbeUpdateIntent } from '../components/reflection-probe';
|
|
2
|
+
|
|
3
|
+
export const REFLECTION_PROBE_BYTES_PER_TEXEL = 8;
|
|
4
|
+
export const DEFAULT_REFLECTION_PROBE_LIMITS = Object.freeze({
|
|
5
|
+
maxProbes: 16,
|
|
6
|
+
maxResolution: 256,
|
|
7
|
+
maxBytes: 64 * 1024 * 1024,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export interface ReflectionProbeInput {
|
|
11
|
+
readonly halfExtents: readonly [number, number, number];
|
|
12
|
+
readonly priority: number;
|
|
13
|
+
readonly intensity: number;
|
|
14
|
+
readonly resolution: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ReflectionProbeFact extends ReflectionProbeInput {
|
|
18
|
+
readonly worldId: number;
|
|
19
|
+
readonly entityKey: number;
|
|
20
|
+
readonly center: readonly [number, number, number];
|
|
21
|
+
readonly revision: number;
|
|
22
|
+
readonly updateIntent?: ReflectionProbeUpdateIntent;
|
|
23
|
+
readonly invalidationVersion?: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ReflectionProbeAdmissionLimits {
|
|
27
|
+
readonly maxProbes?: number;
|
|
28
|
+
readonly maxResolution?: number;
|
|
29
|
+
readonly maxBytes?: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ReflectionProbeInvalidInput {
|
|
33
|
+
readonly code: 'reflection-probe-input-invalid';
|
|
34
|
+
readonly field: string;
|
|
35
|
+
readonly value: unknown;
|
|
36
|
+
readonly expected: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ReflectionProbeBudgetFailure {
|
|
40
|
+
readonly code: 'reflection-probe-budget-exceeded';
|
|
41
|
+
readonly requested: number;
|
|
42
|
+
readonly activeCount: number;
|
|
43
|
+
readonly activeBytes: number;
|
|
44
|
+
readonly maxProbes: number;
|
|
45
|
+
readonly maxBytes: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type ReflectionProbeAdmission =
|
|
49
|
+
| { readonly ok: true; readonly acceptedBytes: number }
|
|
50
|
+
| { readonly ok: false; readonly error: ReflectionProbeBudgetFailure };
|
|
51
|
+
|
|
52
|
+
export function validateReflectionProbeInput(
|
|
53
|
+
input: ReflectionProbeInput,
|
|
54
|
+
): { readonly ok: true } | { readonly ok: false; readonly error: ReflectionProbeInvalidInput } {
|
|
55
|
+
for (let index = 0; index < input.halfExtents.length; index += 1) {
|
|
56
|
+
const value = input.halfExtents[index];
|
|
57
|
+
if (value === undefined || !Number.isFinite(value) || value <= 0) {
|
|
58
|
+
return {
|
|
59
|
+
ok: false,
|
|
60
|
+
error: {
|
|
61
|
+
code: 'reflection-probe-input-invalid',
|
|
62
|
+
field: `halfExtents[${index}]`,
|
|
63
|
+
value,
|
|
64
|
+
expected: 'a finite number greater than zero',
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (!Number.isFinite(input.priority)) {
|
|
70
|
+
return {
|
|
71
|
+
ok: false,
|
|
72
|
+
error: {
|
|
73
|
+
code: 'reflection-probe-input-invalid',
|
|
74
|
+
field: 'priority',
|
|
75
|
+
value: input.priority,
|
|
76
|
+
expected: 'a finite number',
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
if (!Number.isFinite(input.intensity) || input.intensity < 0) {
|
|
81
|
+
return {
|
|
82
|
+
ok: false,
|
|
83
|
+
error: {
|
|
84
|
+
code: 'reflection-probe-input-invalid',
|
|
85
|
+
field: 'intensity',
|
|
86
|
+
value: input.intensity,
|
|
87
|
+
expected: 'a finite number greater than or equal to zero',
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (!Number.isInteger(input.resolution) || input.resolution <= 0) {
|
|
92
|
+
return {
|
|
93
|
+
ok: false,
|
|
94
|
+
error: {
|
|
95
|
+
code: 'reflection-probe-input-invalid',
|
|
96
|
+
field: 'resolution',
|
|
97
|
+
value: input.resolution,
|
|
98
|
+
expected: 'a positive integer',
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return { ok: true };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function estimateReflectionProbeBytes(
|
|
106
|
+
resolution: number,
|
|
107
|
+
limits: ReflectionProbeAdmissionLimits = DEFAULT_REFLECTION_PROBE_LIMITS,
|
|
108
|
+
): number {
|
|
109
|
+
const maxResolution = limits.maxResolution ?? DEFAULT_REFLECTION_PROBE_LIMITS.maxResolution;
|
|
110
|
+
const clamped = Math.min(resolution, maxResolution);
|
|
111
|
+
let mipTexels = 0;
|
|
112
|
+
for (let size = clamped; size >= 1; size = Math.floor(size / 2)) {
|
|
113
|
+
mipTexels += size * size;
|
|
114
|
+
}
|
|
115
|
+
return mipTexels * 6 * REFLECTION_PROBE_BYTES_PER_TEXEL;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function admitReflectionProbe(
|
|
119
|
+
input: ReflectionProbeInput,
|
|
120
|
+
current: { readonly acceptedCount: number; readonly acceptedBytes: number },
|
|
121
|
+
limits: ReflectionProbeAdmissionLimits = DEFAULT_REFLECTION_PROBE_LIMITS,
|
|
122
|
+
): ReflectionProbeAdmission {
|
|
123
|
+
const maxProbes = limits.maxProbes ?? DEFAULT_REFLECTION_PROBE_LIMITS.maxProbes;
|
|
124
|
+
const maxBytes = limits.maxBytes ?? DEFAULT_REFLECTION_PROBE_LIMITS.maxBytes;
|
|
125
|
+
const maxResolution = limits.maxResolution ?? DEFAULT_REFLECTION_PROBE_LIMITS.maxResolution;
|
|
126
|
+
const requested = estimateReflectionProbeBytes(input.resolution, { maxResolution });
|
|
127
|
+
if (
|
|
128
|
+
input.resolution > maxResolution ||
|
|
129
|
+
current.acceptedCount >= maxProbes ||
|
|
130
|
+
current.acceptedBytes + requested > maxBytes
|
|
131
|
+
) {
|
|
132
|
+
return {
|
|
133
|
+
ok: false,
|
|
134
|
+
error: {
|
|
135
|
+
code: 'reflection-probe-budget-exceeded',
|
|
136
|
+
requested,
|
|
137
|
+
activeCount: current.acceptedCount,
|
|
138
|
+
activeBytes: current.acceptedBytes,
|
|
139
|
+
maxProbes,
|
|
140
|
+
maxBytes,
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
return { ok: true, acceptedBytes: current.acceptedBytes + requested };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export interface ReflectionProbeSelection {
|
|
148
|
+
readonly kind: 'probe';
|
|
149
|
+
readonly worldId: number;
|
|
150
|
+
readonly entityKey: number;
|
|
151
|
+
readonly normalizedDistance: number;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface SkylightSelection {
|
|
155
|
+
readonly kind: 'skylight';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export type ReflectionProbeSelectionResult = ReflectionProbeSelection | SkylightSelection;
|
|
159
|
+
|
|
160
|
+
function contains(fact: ReflectionProbeFact, point: readonly [number, number, number]): boolean {
|
|
161
|
+
return fact.halfExtents.every((extent, axis) => {
|
|
162
|
+
const center = fact.center[axis] ?? 0;
|
|
163
|
+
const coordinate = point[axis] ?? 0;
|
|
164
|
+
return Math.abs(coordinate - center) <= extent;
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function normalizedDistance(
|
|
169
|
+
fact: ReflectionProbeFact,
|
|
170
|
+
point: readonly [number, number, number],
|
|
171
|
+
): number {
|
|
172
|
+
let squared = 0;
|
|
173
|
+
for (let axis = 0; axis < 3; axis += 1) {
|
|
174
|
+
const delta = (point[axis] ?? 0) - (fact.center[axis] ?? 0);
|
|
175
|
+
const extent = fact.halfExtents[axis] ?? 1;
|
|
176
|
+
squared += (delta / extent) ** 2;
|
|
177
|
+
}
|
|
178
|
+
return squared;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function isBetter(
|
|
182
|
+
candidate: ReflectionProbeSelection,
|
|
183
|
+
current: ReflectionProbeSelection,
|
|
184
|
+
facts: readonly ReflectionProbeFact[],
|
|
185
|
+
): boolean {
|
|
186
|
+
const candidateFact = facts.find(
|
|
187
|
+
(fact) => fact.worldId === candidate.worldId && fact.entityKey === candidate.entityKey,
|
|
188
|
+
);
|
|
189
|
+
const currentFact = facts.find(
|
|
190
|
+
(fact) => fact.worldId === current.worldId && fact.entityKey === current.entityKey,
|
|
191
|
+
);
|
|
192
|
+
if (candidateFact === undefined || currentFact === undefined) return false;
|
|
193
|
+
if (candidateFact.priority !== currentFact.priority)
|
|
194
|
+
return candidateFact.priority > currentFact.priority;
|
|
195
|
+
if (candidate.normalizedDistance !== current.normalizedDistance) {
|
|
196
|
+
return candidate.normalizedDistance < current.normalizedDistance;
|
|
197
|
+
}
|
|
198
|
+
return (
|
|
199
|
+
candidate.worldId < current.worldId ||
|
|
200
|
+
(candidate.worldId === current.worldId && candidate.entityKey < current.entityKey)
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function selectReflectionProbe(
|
|
205
|
+
facts: readonly ReflectionProbeFact[],
|
|
206
|
+
primitiveCenter: readonly [number, number, number],
|
|
207
|
+
): ReflectionProbeSelectionResult {
|
|
208
|
+
let selected: ReflectionProbeSelection | undefined;
|
|
209
|
+
for (const fact of facts) {
|
|
210
|
+
if (!contains(fact, primitiveCenter)) continue;
|
|
211
|
+
const candidate: ReflectionProbeSelection = {
|
|
212
|
+
kind: 'probe',
|
|
213
|
+
worldId: fact.worldId,
|
|
214
|
+
entityKey: fact.entityKey,
|
|
215
|
+
normalizedDistance: normalizedDistance(fact, primitiveCenter),
|
|
216
|
+
};
|
|
217
|
+
if (selected === undefined || isBetter(candidate, selected, facts)) selected = candidate;
|
|
218
|
+
}
|
|
219
|
+
return selected ?? { kind: 'skylight' };
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export interface ReflectionProbePrimitive {
|
|
223
|
+
readonly worldId: number;
|
|
224
|
+
readonly entityKey: number;
|
|
225
|
+
readonly center: readonly [number, number, number];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export interface ReflectionProbeProjectionSnapshot {
|
|
229
|
+
readonly revision: number;
|
|
230
|
+
readonly facts: readonly ReflectionProbeFact[];
|
|
231
|
+
readonly selected: ReadonlyMap<string, ReflectionProbeSelectionResult>;
|
|
232
|
+
readonly scannedPrimitives: number;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function primitiveKey(worldId: number, entityKey: number): string {
|
|
236
|
+
return `${worldId}:${entityKey}`;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export class ReflectionProbeProjection {
|
|
240
|
+
private revision = 0;
|
|
241
|
+
private facts: readonly ReflectionProbeFact[] = [];
|
|
242
|
+
private selected = new Map<string, ReflectionProbeSelectionResult>();
|
|
243
|
+
private signature = '';
|
|
244
|
+
private scannedPrimitives = 0;
|
|
245
|
+
|
|
246
|
+
update(
|
|
247
|
+
facts: readonly ReflectionProbeFact[],
|
|
248
|
+
primitives: readonly ReflectionProbePrimitive[],
|
|
249
|
+
): ReflectionProbeProjectionSnapshot {
|
|
250
|
+
const nextFacts = facts
|
|
251
|
+
.slice()
|
|
252
|
+
.sort((a, b) => a.worldId - b.worldId || a.entityKey - b.entityKey);
|
|
253
|
+
const signature = JSON.stringify({ facts: nextFacts, primitives });
|
|
254
|
+
if (signature === this.signature) return this.snapshot();
|
|
255
|
+
this.signature = signature;
|
|
256
|
+
this.facts = nextFacts;
|
|
257
|
+
this.selected = new Map();
|
|
258
|
+
for (const primitive of primitives) {
|
|
259
|
+
this.selected.set(
|
|
260
|
+
primitiveKey(primitive.worldId, primitive.entityKey),
|
|
261
|
+
selectReflectionProbe(nextFacts, primitive.center),
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
this.scannedPrimitives = primitives.length;
|
|
265
|
+
this.revision += 1;
|
|
266
|
+
return this.snapshot();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
snapshot(): ReflectionProbeProjectionSnapshot {
|
|
270
|
+
return {
|
|
271
|
+
revision: this.revision,
|
|
272
|
+
facts: this.facts,
|
|
273
|
+
selected: this.selected,
|
|
274
|
+
scannedPrimitives: this.scannedPrimitives,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
selection(worldId: number, entityKey: number): ReflectionProbeSelectionResult {
|
|
279
|
+
return this.selected.get(primitiveKey(worldId, entityKey)) ?? { kind: 'skylight' };
|
|
280
|
+
}
|
|
281
|
+
}
|