@forgeax/engine-render 0.1.21 → 0.1.23
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 +150 -29
- package/dist/assembly/backend-contract.d.ts +13 -0
- package/dist/assembly/backend-contract.d.ts.map +1 -1
- package/dist/assembly/device-feature-admission.d.ts.map +1 -1
- package/dist/assembly/factory.d.ts +14 -6
- package/dist/assembly/factory.d.ts.map +1 -1
- package/dist/assembly/host-contract.d.ts +10 -0
- package/dist/assembly/host-contract.d.ts.map +1 -1
- package/dist/authoring.mjs +1 -1
- package/dist/{chunk-PCHBPYML.mjs → chunk-4L3P6DT5.mjs} +2 -2
- package/dist/chunk-4L3P6DT5.mjs.map +1 -0
- package/dist/{chunk-VFPLTKCU.mjs → chunk-4NPUASZR.mjs} +15 -4
- package/dist/chunk-4NPUASZR.mjs.map +1 -0
- package/dist/chunk-DIYW3M5D.mjs +5085 -0
- package/dist/chunk-DIYW3M5D.mjs.map +1 -0
- package/dist/chunk-FBC4R44J.mjs +1003 -0
- package/dist/chunk-FBC4R44J.mjs.map +1 -0
- package/dist/{chunk-FS2KNUQH.mjs → chunk-FF7K3FSO.mjs} +253 -489
- package/dist/chunk-FF7K3FSO.mjs.map +1 -0
- package/dist/chunk-HZUPSYW7.mjs +1104 -0
- package/dist/chunk-HZUPSYW7.mjs.map +1 -0
- package/dist/{chunk-6OPXDQ2E.mjs → chunk-QODA5UKU.mjs} +33 -294
- package/dist/chunk-QODA5UKU.mjs.map +1 -0
- package/dist/chunk-YALPS44A.mjs +296 -0
- package/dist/chunk-YALPS44A.mjs.map +1 -0
- package/dist/{chunk-5YADUCA7.mjs → chunk-ZUWKAZ2I.mjs} +585 -991
- package/dist/chunk-ZUWKAZ2I.mjs.map +1 -0
- package/dist/cluster-binner.d.ts +5 -0
- package/dist/cluster-binner.d.ts.map +1 -1
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/light-helpers.d.ts +4 -0
- package/dist/components/light-helpers.d.ts.map +1 -1
- package/dist/components/light-probe.d.ts +9 -0
- package/dist/components/light-probe.d.ts.map +1 -0
- package/dist/components/rect-area-light.d.ts +12 -0
- package/dist/components/rect-area-light.d.ts.map +1 -0
- package/dist/components/scene-instance.d.ts +4 -0
- package/dist/components/scene-instance.d.ts.map +1 -1
- package/dist/components/spot-light.d.ts +3 -0
- package/dist/components/spot-light.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +10437 -10044
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/gpu-residency.d.ts +8 -1
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/device/lifecycle-transaction.d.ts +2 -0
- package/dist/device/lifecycle-transaction.d.ts.map +1 -0
- package/dist/environment/lifecycle.d.ts.map +1 -1
- package/dist/errors/gpu-driven.d.ts +26 -0
- package/dist/errors/gpu-driven.d.ts.map +1 -0
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/recover.d.ts +9 -1
- package/dist/errors/recover.d.ts.map +1 -1
- package/dist/errors/render.d.ts +26 -1
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/extract/gpu-driven.d.ts +50 -0
- package/dist/extract/gpu-driven.d.ts.map +1 -0
- package/dist/extract/visibility.d.ts.map +1 -1
- package/dist/gpu-driven/batch-topology.d.ts +34 -0
- package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
- package/dist/gpu-driven/prepared-draw.d.ts +63 -0
- package/dist/gpu-driven/prepared-draw.d.ts.map +1 -0
- package/dist/gpu-driven/production-raster.d.ts +98 -2
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-driven/view-gpu.d.ts +64 -1
- package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
- package/dist/gpu-scene-schema.d.ts +14 -0
- package/dist/gpu-scene-schema.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts +8 -1
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/gpu-usage.d.ts +0 -1
- package/dist/gpu-usage.d.ts.map +1 -1
- package/dist/hdrp-buffers.d.ts +3 -3
- package/dist/hdrp-buffers.d.ts.map +1 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +90 -5
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +163 -1
- package/dist/inspection-types.d.ts.map +1 -1
- package/dist/internal.d.ts +9 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.mjs +23 -2
- package/dist/internal.mjs.map +1 -1
- package/dist/light-buffer-layout.d.ts +58 -149
- package/dist/light-buffer-layout.d.ts.map +1 -1
- package/dist/materials.d.ts.map +1 -1
- package/dist/pbr-pipeline.d.ts +26 -12
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
- package/dist/pipeline/standard-lighting/graph.d.ts.map +1 -1
- package/dist/pipeline/standard-lighting/light-frame.d.ts +3 -3
- package/dist/pipeline/standard-lighting/light-frame.d.ts.map +1 -1
- package/dist/pipeline/standard-lighting/topology.d.ts +1 -1
- package/dist/pipeline/standard-lighting/topology.d.ts.map +1 -1
- package/dist/pipeline/standard-lighting/transport.d.ts.map +1 -1
- package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
- package/dist/pipeline-builder.d.ts +2 -0
- package/dist/pipeline-builder.d.ts.map +1 -1
- package/dist/pipeline-spec.d.ts +1 -1
- package/dist/pipeline-spec.d.ts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/prepare/extended-lighting/ltc-resources.d.ts +31 -0
- package/dist/prepare/extended-lighting/ltc-resources.d.ts.map +1 -0
- package/dist/prepare/extended-lighting/resources.d.ts +72 -0
- package/dist/prepare/extended-lighting/resources.d.ts.map +1 -0
- package/dist/prepare/extended-lighting/spot-modifiers.d.ts +42 -0
- package/dist/prepare/extended-lighting/spot-modifiers.d.ts.map +1 -0
- package/dist/prepare/extended-lighting/state.d.ts +24 -0
- package/dist/prepare/extended-lighting/state.d.ts.map +1 -0
- package/dist/record/frame-lighting.d.ts +8 -6
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +50 -10
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame.d.ts +7 -3
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/gpu-timing.d.ts +19 -7
- package/dist/record/gpu-timing.d.ts.map +1 -1
- package/dist/record/helpers.d.ts +0 -20
- package/dist/record/helpers.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +6 -1
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +3 -1
- 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/mesh-ssbo.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +57 -6
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/typed-frame-graph.d.ts +49 -2
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/record/view-ubo.d.ts +1 -2
- package/dist/record/view-ubo.d.ts.map +1 -1
- package/dist/reflection/inspection.d.ts +10 -0
- package/dist/reflection/inspection.d.ts.map +1 -0
- package/dist/reflection/projection.d.ts +49 -0
- package/dist/reflection/projection.d.ts.map +1 -1
- package/dist/reflection/record-owner.d.ts +60 -0
- package/dist/reflection/record-owner.d.ts.map +1 -0
- package/dist/render-contract.d.ts +14 -8
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-pipeline.d.ts +7 -0
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +104 -16
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts +12 -1
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/probe-blend-record.d.ts +22 -0
- package/dist/scene/probe-blend-record.d.ts.map +1 -0
- package/dist/scene/probe-blend.d.ts +142 -0
- package/dist/scene/probe-blend.d.ts.map +1 -0
- package/dist/scene/render-scene-types.d.ts +0 -6
- package/dist/scene/render-scene-types.d.ts.map +1 -1
- package/dist/scene/render-scene.d.ts +103 -9
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/dist/scene/visibility/bounds.d.ts +20 -0
- package/dist/scene/visibility/bounds.d.ts.map +1 -0
- package/dist/scene/visibility/budget.d.ts +16 -0
- package/dist/scene/visibility/budget.d.ts.map +1 -0
- package/dist/scene/visibility/facet.d.ts +73 -0
- package/dist/scene/visibility/facet.d.ts.map +1 -0
- package/dist/scene/visibility/gpu-frame-timing.d.ts +41 -0
- package/dist/scene/visibility/gpu-frame-timing.d.ts.map +1 -0
- package/dist/scene/visibility/gpu-lod.d.ts +46 -0
- package/dist/scene/visibility/gpu-lod.d.ts.map +1 -0
- package/dist/scene/visibility/inspection.d.ts +132 -0
- package/dist/scene/visibility/inspection.d.ts.map +1 -0
- package/dist/scene/visibility/lod-selector.d.ts +25 -0
- package/dist/scene/visibility/lod-selector.d.ts.map +1 -0
- package/dist/scene/visibility/occlusion-confidence.d.ts +81 -0
- package/dist/scene/visibility/occlusion-confidence.d.ts.map +1 -0
- package/dist/scene/visibility/occlusion-pass.d.ts +57 -0
- package/dist/scene/visibility/occlusion-pass.d.ts.map +1 -0
- package/dist/scene/visibility/occlusion-query-pool.d.ts +57 -0
- package/dist/scene/visibility/occlusion-query-pool.d.ts.map +1 -0
- package/dist/scene/visibility/occlusion-runtime.d.ts +96 -0
- package/dist/scene/visibility/occlusion-runtime.d.ts.map +1 -0
- package/dist/scene/visibility/types.d.ts +34 -0
- package/dist/scene/visibility/types.d.ts.map +1 -0
- package/dist/ssr/admission.d.ts +122 -0
- package/dist/ssr/admission.d.ts.map +1 -0
- package/dist/ssr/composition.d.ts +32 -0
- package/dist/ssr/composition.d.ts.map +1 -0
- package/dist/ssr/errors.d.ts +39 -0
- package/dist/ssr/errors.d.ts.map +1 -0
- package/dist/ssr/identity.d.ts +13 -0
- package/dist/ssr/identity.d.ts.map +1 -0
- package/dist/systems/skin-palette-allocator.d.ts +1 -1
- package/dist/temporal/frame-coordinator.d.ts +13 -0
- package/dist/temporal/frame-coordinator.d.ts.map +1 -1
- package/dist/temporal/frame.d.ts +2 -0
- package/dist/temporal/frame.d.ts.map +1 -1
- package/dist/temporal/index.mjs +6 -5
- package/dist/temporal/view.d.ts +7 -0
- package/dist/temporal/view.d.ts.map +1 -1
- package/dist/tilemap-chunk-extract-system.d.ts +1 -1
- package/dist/typed-render-graph-primitives.d.ts +4 -0
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/volume/passes.d.ts +8 -4
- package/dist/volume/passes.d.ts.map +1 -1
- package/package.json +20 -20
- package/src/__tests__/cluster-grid-default-surface.unit.test.ts +5 -0
- package/src/__tests__/detached-inspection.integration.test.ts +6 -0
- package/src/__tests__/device-feature-admission.unit.test.ts +13 -1
- package/src/__tests__/direct-light-snapshot.unit.test.ts +59 -3
- package/src/__tests__/environment-generation.integration.test.ts +16 -0
- package/src/__tests__/evidence-identity.ts +13 -0
- package/src/__tests__/evidence-identity.unit.test.ts +24 -0
- package/src/__tests__/extended-lighting-recovery.browser.test.ts +14 -0
- package/src/__tests__/extended-lighting-recovery.dawn.test.ts +14 -0
- package/src/__tests__/extended-lighting-recovery.integration.test.ts +81 -0
- package/src/__tests__/extended-lighting-resources.unit.test.ts +51 -0
- package/src/__tests__/extended-lighting-zero-feature.integration.test.ts +52 -0
- package/src/__tests__/factory-contract.integration.test.ts +33 -0
- package/src/__tests__/frame-observation.contract.test.ts +15 -0
- package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +146 -0
- package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +1 -0
- package/src/__tests__/gpu-driven-indirect-raster.dawn.test.ts +1 -0
- package/src/__tests__/gpu-driven-production.integration.test.ts +572 -9
- package/src/__tests__/gpu-driven-scaling.unit.test.ts +66 -2
- package/src/__tests__/gpu-driven-view-gpu-evidence.ts +26 -1
- package/src/__tests__/gpu-driven-view-graph.integration.test.ts +89 -2
- package/src/__tests__/gpu-driven-view.browser.test.ts +3 -3
- package/src/__tests__/gpu-driven-view.dawn.test.ts +3 -3
- package/src/__tests__/gpu-driven-view.unit.test.ts +50 -0
- package/src/__tests__/gpu-lod-evidence.ts +188 -0
- package/src/__tests__/gpu-lod-fallback.integration.test.ts +10 -0
- package/src/__tests__/gpu-lod-indirect.integration.test.ts +40 -0
- package/src/__tests__/gpu-lod-parity.dawn.test.ts +29 -0
- package/src/__tests__/gpu-lod-readiness.integration.test.ts +52 -0
- package/src/__tests__/gpu-lod-schema.unit.test.ts +66 -0
- package/src/__tests__/gpu-lod-selector.unit.test.ts +28 -0
- package/src/__tests__/gpu-lod-silhouette.browser.test.ts +13 -0
- package/src/__tests__/gpu-resource-store-stride.unit.test.ts +66 -1
- package/src/__tests__/gpu-scene.dawn.test.ts +8 -1
- package/src/__tests__/gpu-scene.unit.test.ts +49 -3
- package/src/__tests__/light-buffer-layout.unit.test.ts +113 -3
- package/src/__tests__/light-inspection.unit.test.ts +55 -0
- package/src/__tests__/light-resource-error.unit.test.ts +43 -0
- package/src/__tests__/lighting-scene-roundtrip.integration.test.ts +109 -0
- package/src/__tests__/lod-bounds.unit.test.ts +42 -0
- package/src/__tests__/lod-cpu-lane.integration.test.ts +55 -0
- package/src/__tests__/lod-cpu-no-change.perf.test.ts +19 -0
- package/src/__tests__/lod-gpu-frame-timing.unit.test.ts +49 -0
- package/src/__tests__/lod-multi-view.integration.test.ts +63 -0
- package/src/__tests__/lod-occlusion-device-loss.integration.test.ts +27 -0
- package/src/__tests__/lod-occlusion-final-decision.integration.test.ts +47 -0
- package/src/__tests__/lod-occlusion-inspection.integration.test.ts +217 -0
- package/src/__tests__/lod-occlusion-inspection.schema.test-d.ts +14 -0
- package/src/__tests__/lod-occlusion-inspection.unit.test.ts +72 -0
- package/src/__tests__/lod-occlusion-visual-evidence.ts +91 -0
- package/src/__tests__/lod-occlusion.browser.test.ts +15 -0
- package/src/__tests__/lod-occlusion.dawn.test.ts +20 -0
- package/src/__tests__/lod-selector.profile.test.ts +35 -0
- package/src/__tests__/lod-selector.unit.test.ts +68 -0
- package/src/__tests__/lod-visibility-identity.unit.test.ts +198 -0
- package/src/__tests__/lod-visibility-lifecycle.integration.test.ts +132 -0
- package/src/__tests__/ltc-resources.unit.test.ts +32 -0
- package/src/__tests__/material-contract-inventory.unit.test.ts +17 -0
- package/src/__tests__/occlusion-confidence.unit.test.ts +168 -0
- package/src/__tests__/occlusion-invalidation.integration.test.ts +37 -0
- package/src/__tests__/occlusion-pass-routing.unit.test.ts +16 -0
- package/src/__tests__/occlusion-pass.dawn.test.ts +20 -0
- package/src/__tests__/occlusion-pass.integration.test.ts +43 -0
- package/src/__tests__/occlusion-pass.rhi-null.test.ts +22 -0
- package/src/__tests__/occlusion-query-pool.rhi-null.test.ts +21 -0
- package/src/__tests__/occlusion-query-pool.unit.test.ts +95 -0
- package/src/__tests__/occlusion-query-ticket.integration.test.ts +47 -0
- package/src/__tests__/occlusion-query.dawn.test.ts +16 -0
- package/src/__tests__/occlusion-query.evidence.ts +178 -0
- package/src/__tests__/occlusion-query.rhi-null.test.ts +31 -0
- package/src/__tests__/occlusion-runtime.unit.test.ts +120 -0
- package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +196 -0
- package/src/__tests__/probe-bindings.unit.test.ts +200 -0
- package/src/__tests__/probe-blend-numeric.unit.test.ts +67 -0
- package/src/__tests__/probe-blend-oracle.unit.test.ts +136 -0
- package/src/__tests__/probe-capacity.integration.test.ts +97 -0
- package/src/__tests__/probe-recovery.integration.test.ts +104 -0
- package/src/__tests__/probe-scene-dirty.integration.test.ts +211 -0
- package/src/__tests__/profiler-phase-catalog.test.ts +4 -1
- package/src/__tests__/profiler-phase-token.test.ts +1 -0
- package/src/__tests__/rect-area-boundary.unit.test.ts +24 -0
- package/src/__tests__/reflection-fallback-graph.unit.test.ts +67 -0
- package/src/__tests__/reflection-fallback-inspection.unit.test.ts +77 -0
- package/src/__tests__/reflection-fallback-lifecycle.integration.test.ts +21 -0
- package/src/__tests__/reflection-fallback-projection.unit.test.ts +90 -0
- package/src/__tests__/reflection-fallback-source.unit.test.ts +32 -0
- package/src/__tests__/reflection-probe-owner-placement.unit.test.ts +39 -0
- package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +120 -0
- package/src/__tests__/render-error-code-owner.test-d.ts +1 -0
- package/src/__tests__/render-feature-docs.unit.test.ts +12 -7
- package/src/__tests__/render-graph-fallback.integration.test.ts +24 -0
- package/src/__tests__/render-perf/extended-lighting.bench.test.ts +66 -0
- package/src/__tests__/render-scene-projection.unit.test.ts +447 -18
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +76 -0
- package/src/__tests__/skin-motion-regression.unit.test.ts +22 -0
- package/src/__tests__/skinned-shadow-caster.test.ts +2 -2
- package/src/__tests__/spot-modifier-projection.unit.test.ts +70 -0
- package/src/__tests__/ssr-admission-matrix.integration.test.ts +206 -0
- package/src/__tests__/ssr-composition.unit.test.ts +46 -0
- package/src/__tests__/ssr-fallback-zero-work.bench.test.ts +94 -0
- package/src/__tests__/ssr-recovery-errors.unit.test.ts +58 -0
- package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +67 -0
- package/src/__tests__/standard-clustered-pipeline.unit.test.ts +7 -0
- package/src/__tests__/standard-lighting-inspection.integration.test.ts +1 -1
- package/src/__tests__/standard-lighting-write.unit.test.ts +66 -2
- package/src/__tests__/temporal-fallback-generation.integration.test.ts +16 -0
- package/src/__tests__/temporal-view.unit.test.ts +3 -3
- package/src/__tests__/transmission-prewarm.unit.test.ts +69 -0
- package/src/__tests__/visibility-budget.integration.test.ts +140 -0
- package/src/__tests__/visibility-budget.unit.test.ts +70 -0
- package/src/__tests__/visibility-extract.integration.test.ts +11 -6
- package/src/__tests__/volumetric-fog-point-spot-accumulation.integration.test.ts +6 -3
- package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +31 -28
- package/src/assembly/backend-contract.ts +13 -0
- package/src/assembly/device-feature-admission.ts +16 -9
- package/src/assembly/factory.ts +547 -93
- package/src/assembly/host-contract.ts +10 -0
- package/src/cluster-binner.ts +31 -0
- package/src/components/__tests__/lighting-authoring.unit.test.ts +39 -0
- package/src/components/__tests__/rect-area-light.unit.test.ts +33 -0
- package/src/components/index.ts +3 -0
- package/src/components/light-helpers.ts +52 -1
- package/src/components/light-probe.ts +13 -0
- package/src/components/rect-area-light.ts +15 -0
- package/src/components/scene-instance.ts +6 -0
- package/src/components/spot-light.ts +3 -0
- package/src/device/__tests__/extended-lighting-recovery.unit.test.ts +24 -0
- package/src/device/gpu-residency.ts +140 -9
- package/src/device/lifecycle-transaction.ts +12 -0
- package/src/environment/lifecycle.ts +8 -1
- package/src/errors/gpu-driven.ts +92 -0
- package/src/errors/index.ts +1 -0
- package/src/errors/recover.ts +32 -0
- package/src/errors/render.ts +55 -1
- package/src/extract/camera.ts +3 -3
- package/src/extract/gpu-driven.ts +108 -0
- package/src/extract/visibility.ts +0 -23
- package/src/gpu-driven/batch-topology.ts +206 -28
- package/src/gpu-driven/prepared-draw.ts +208 -0
- package/src/gpu-driven/production-raster.ts +953 -41
- package/src/gpu-driven/view-gpu.ts +411 -29
- package/src/gpu-scene-schema.ts +13 -0
- package/src/gpu-scene.ts +134 -65
- package/src/gpu-usage.ts +0 -1
- package/src/hdrp-buffers.ts +6 -6
- package/src/index.ts +74 -0
- package/src/inspection-types.ts +220 -1
- package/src/internal.ts +38 -0
- package/src/light-buffer-layout.ts +154 -324
- package/src/materials.ts +16 -2
- package/src/pbr-pipeline.ts +151 -45
- package/src/pipeline/standard-forward-lane.ts +47 -0
- package/src/pipeline/standard-lighting/graph.ts +2 -1
- package/src/pipeline/standard-lighting/light-frame.ts +3 -2
- package/src/pipeline/standard-lighting/topology.ts +1 -1
- package/src/pipeline/standard-lighting/transport.ts +2 -1
- package/src/pipeline/standard-pipeline.ts +2 -0
- package/src/pipeline-builder.ts +5 -2
- package/src/pipeline-spec.ts +12 -1
- package/src/plugin.ts +4 -0
- package/src/prepare/__tests__/extended-lighting-capability.unit.test.ts +56 -0
- package/src/prepare/extended-lighting/ltc-resources.ts +136 -0
- package/src/prepare/extended-lighting/resources.ts +389 -0
- package/src/prepare/extended-lighting/spot-modifiers.ts +194 -0
- package/src/prepare/extended-lighting/state.ts +92 -0
- package/src/record/__tests__/gpu-timing.unit.test.ts +177 -0
- package/src/record/__tests__/temporal-payload.unit.test.ts +23 -1
- package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +20 -1
- package/src/record/frame-lighting.ts +252 -53
- package/src/record/frame-snapshot.ts +57 -12
- package/src/record/frame.ts +410 -93
- package/src/record/gpu-timing.ts +108 -44
- package/src/record/helpers.ts +0 -58
- package/src/record/main-pass-geometry.ts +204 -39
- package/src/record/main-pass-material.ts +8 -1
- package/src/record/main-pass-sprite-draws.ts +52 -85
- package/src/record/mesh-ssbo.ts +47 -36
- package/src/record/render-context.ts +65 -4
- package/src/record/shadow-pass.ts +66 -40
- package/src/record/typed-frame-graph.ts +225 -83
- package/src/record/view-ubo.ts +14 -24
- package/src/reflection/inspection.ts +67 -0
- package/src/reflection/projection.ts +196 -1
- package/src/reflection/record-owner.ts +1283 -0
- package/src/render-contract.ts +17 -5
- package/src/render-pipeline.ts +5 -0
- package/src/render-system-extract.ts +493 -134
- package/src/render-system.ts +811 -615
- package/src/scene/__tests__/changed-block-summary.candidate.test.ts +36 -0
- package/src/scene/probe-blend-record.ts +54 -0
- package/src/scene/probe-blend.ts +813 -0
- package/src/scene/render-scene-types.ts +0 -7
- package/src/scene/render-scene.ts +848 -183
- package/src/scene/visibility/bounds.ts +57 -0
- package/src/scene/visibility/budget.ts +40 -0
- package/src/scene/visibility/facet.ts +400 -0
- package/src/scene/visibility/gpu-frame-timing.ts +101 -0
- package/src/scene/visibility/gpu-lod.ts +119 -0
- package/src/scene/visibility/inspection.ts +313 -0
- package/src/scene/visibility/lod-selector.ts +84 -0
- package/src/scene/visibility/occlusion-confidence.ts +478 -0
- package/src/scene/visibility/occlusion-pass.ts +165 -0
- package/src/scene/visibility/occlusion-query-pool.ts +178 -0
- package/src/scene/visibility/occlusion-runtime.ts +672 -0
- package/src/scene/visibility/types.ts +50 -0
- package/src/ssr/admission.ts +451 -0
- package/src/ssr/composition.ts +73 -0
- package/src/ssr/errors.ts +76 -0
- package/src/ssr/identity.ts +12 -0
- package/src/systems/skin-palette-allocator.ts +1 -1
- package/src/temporal/frame-coordinator.ts +26 -0
- package/src/temporal/frame.ts +14 -1
- package/src/temporal/view.ts +11 -0
- package/src/tilemap-chunk-extract-system.ts +4 -4
- package/src/typed-render-graph-primitives.ts +48 -0
- package/src/volume/passes.ts +46 -49
- package/dist/.tsbuildinfo +0 -1
- package/dist/chunk-5YADUCA7.mjs.map +0 -1
- package/dist/chunk-6OPXDQ2E.mjs.map +0 -1
- package/dist/chunk-BXBQQKFQ.mjs +0 -67
- package/dist/chunk-BXBQQKFQ.mjs.map +0 -1
- package/dist/chunk-FS2KNUQH.mjs.map +0 -1
- package/dist/chunk-PCHBPYML.mjs.map +0 -1
- package/dist/chunk-VFPLTKCU.mjs.map +0 -1
|
@@ -0,0 +1,813 @@
|
|
|
1
|
+
import { R_MIN } from '@forgeax/engine-types';
|
|
2
|
+
import type { ProbeBlendInspection } from '../inspection-types';
|
|
3
|
+
import {
|
|
4
|
+
PROBE_BLEND_RECORD_BYTE_SIZE,
|
|
5
|
+
PROBE_BLEND_RECORD_CAPACITY,
|
|
6
|
+
type ProbeBlendRecord,
|
|
7
|
+
type ProbeBlendRecordSentinel,
|
|
8
|
+
} from './probe-blend-record';
|
|
9
|
+
|
|
10
|
+
export const PROBE_R_MIN = R_MIN;
|
|
11
|
+
export const PROBE_MAX_CONTRIBUTORS = PROBE_BLEND_RECORD_CAPACITY;
|
|
12
|
+
|
|
13
|
+
export interface ProbeBlendContributor {
|
|
14
|
+
readonly identity: string;
|
|
15
|
+
readonly admitted: boolean;
|
|
16
|
+
readonly distance: number;
|
|
17
|
+
readonly radius: number;
|
|
18
|
+
readonly irradiance: ArrayLike<number>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ProbeBlendInput {
|
|
22
|
+
readonly contributors: readonly ProbeBlendContributor[];
|
|
23
|
+
readonly normal: readonly [number, number, number];
|
|
24
|
+
readonly skyIrradiance: readonly [number, number, number];
|
|
25
|
+
readonly radiusMinimum?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ProbeSceneObjectInput {
|
|
29
|
+
readonly objectKey: number;
|
|
30
|
+
readonly generation: number;
|
|
31
|
+
readonly position: readonly [number, number, number];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ProbeSceneProbeInput {
|
|
35
|
+
readonly identity: string;
|
|
36
|
+
readonly position: readonly [number, number, number];
|
|
37
|
+
readonly radius: number;
|
|
38
|
+
readonly irradiance: ArrayLike<number>;
|
|
39
|
+
readonly admitted: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface ProbeAdmissionReceipt {
|
|
43
|
+
readonly activeIdentities: readonly string[];
|
|
44
|
+
readonly admittedIdentities: readonly string[];
|
|
45
|
+
readonly rejectedIdentities: readonly string[];
|
|
46
|
+
readonly stableOrder: readonly string[];
|
|
47
|
+
readonly capacity: number;
|
|
48
|
+
readonly overflowReason?: 'active-count-exceeds-capacity';
|
|
49
|
+
readonly scaleRadius: number;
|
|
50
|
+
readonly finite: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type ProbeAdmissionError = {
|
|
54
|
+
readonly code: 'capacity-exceeded' | 'invalid-admitted-prefix';
|
|
55
|
+
readonly activeCount: number;
|
|
56
|
+
readonly capacity: number;
|
|
57
|
+
readonly receipt: ProbeAdmissionReceipt;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export interface ProbeAdmissionResult {
|
|
61
|
+
readonly admitted: readonly ProbeBlendContributor[];
|
|
62
|
+
readonly active: readonly ProbeBlendContributor[];
|
|
63
|
+
readonly rejected: readonly ProbeBlendContributor[];
|
|
64
|
+
readonly receipt: ProbeAdmissionReceipt;
|
|
65
|
+
readonly error?: ProbeAdmissionError;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface ProbeSceneProjectionInput {
|
|
69
|
+
readonly objects: readonly ProbeSceneObjectInput[];
|
|
70
|
+
readonly probes: readonly ProbeSceneProbeInput[];
|
|
71
|
+
/** Optional analytic oracle input; records never store or replace Sky. */
|
|
72
|
+
readonly skyIrradiance?: readonly [number, number, number];
|
|
73
|
+
readonly sky?: {
|
|
74
|
+
readonly available: boolean;
|
|
75
|
+
readonly identity?: string;
|
|
76
|
+
readonly sourceKey?: string;
|
|
77
|
+
readonly irradiance: readonly [number, number, number];
|
|
78
|
+
readonly fallbackReason?: string;
|
|
79
|
+
};
|
|
80
|
+
readonly worldRevision?: number;
|
|
81
|
+
readonly lastKnownGood?: boolean;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface ProbeSceneProjectionResult {
|
|
85
|
+
readonly records: readonly ProbeBlendRecord[];
|
|
86
|
+
readonly contributors: readonly ProbeBlendTerm[];
|
|
87
|
+
readonly activeContributorCount: number;
|
|
88
|
+
readonly admittedProbeCount: number;
|
|
89
|
+
readonly coverage: number;
|
|
90
|
+
readonly skyResidualFraction: number;
|
|
91
|
+
readonly dirtyReasons: readonly ProbeSceneDirtyReason[];
|
|
92
|
+
readonly affectedObjectKeys: readonly number[];
|
|
93
|
+
readonly visits: number;
|
|
94
|
+
readonly allocations: number;
|
|
95
|
+
readonly uploads: number;
|
|
96
|
+
readonly receipt: ProbeAdmissionReceipt;
|
|
97
|
+
readonly error?: ProbeAdmissionError;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export type ProbeSceneDirtyReason =
|
|
101
|
+
| 'object-boundary'
|
|
102
|
+
| 'probe-fact'
|
|
103
|
+
| 'sky'
|
|
104
|
+
| 'object-position'
|
|
105
|
+
| 'world'
|
|
106
|
+
| 'generation';
|
|
107
|
+
|
|
108
|
+
export interface ProbeBlendTerm {
|
|
109
|
+
readonly identity: string;
|
|
110
|
+
readonly distance: number;
|
|
111
|
+
readonly radius: number;
|
|
112
|
+
readonly coverage: number;
|
|
113
|
+
readonly q: number;
|
|
114
|
+
readonly scaledQ: number;
|
|
115
|
+
readonly qHat: number;
|
|
116
|
+
readonly alpha: number;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface ProbeBlendResult {
|
|
120
|
+
readonly terms: readonly ProbeBlendTerm[];
|
|
121
|
+
readonly rStar: number;
|
|
122
|
+
readonly Q: number;
|
|
123
|
+
readonly C: number;
|
|
124
|
+
readonly S: number;
|
|
125
|
+
readonly SHPreblend: readonly number[];
|
|
126
|
+
readonly skyIrradiance: readonly [number, number, number];
|
|
127
|
+
readonly diffuse: readonly [number, number, number];
|
|
128
|
+
readonly finite: boolean;
|
|
129
|
+
readonly receipt: ProbeAdmissionReceipt;
|
|
130
|
+
readonly error?: ProbeAdmissionError;
|
|
131
|
+
readonly scaledQLogOffset: number;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const SH_C0 = 0.28209479177387814;
|
|
135
|
+
const SH_C1 = 0.4886025119029199;
|
|
136
|
+
const SH_C2 = 1.0925484305920792;
|
|
137
|
+
const SH_C20 = 0.31539156525252005;
|
|
138
|
+
const SH_C22 = 0.5462742152960396;
|
|
139
|
+
|
|
140
|
+
function finiteVector(values: ArrayLike<number>, length: number): boolean {
|
|
141
|
+
if (values.length !== length) return false;
|
|
142
|
+
for (let index = 0; index < values.length; index += 1) {
|
|
143
|
+
if (!Number.isFinite(values[index])) return false;
|
|
144
|
+
}
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function contributorIsActive(probe: ProbeBlendContributor, radiusMinimum: number): boolean {
|
|
149
|
+
return (
|
|
150
|
+
probe.admitted &&
|
|
151
|
+
Number.isFinite(probe.distance) &&
|
|
152
|
+
probe.distance >= 0 &&
|
|
153
|
+
Number.isFinite(probe.radius) &&
|
|
154
|
+
probe.radius >= radiusMinimum &&
|
|
155
|
+
probe.distance < probe.radius &&
|
|
156
|
+
finiteVector(probe.irradiance, 27)
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function emptyReceipt(radiusMinimum: number): ProbeAdmissionReceipt {
|
|
161
|
+
return {
|
|
162
|
+
activeIdentities: [],
|
|
163
|
+
admittedIdentities: [],
|
|
164
|
+
rejectedIdentities: [],
|
|
165
|
+
stableOrder: [],
|
|
166
|
+
capacity: PROBE_MAX_CONTRIBUTORS,
|
|
167
|
+
scaleRadius: radiusMinimum,
|
|
168
|
+
finite: true,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Admission is the only owner allowed to order and bound probe candidates. */
|
|
173
|
+
export function admitProbeContributors(
|
|
174
|
+
candidates: readonly ProbeBlendContributor[],
|
|
175
|
+
radiusMinimum = PROBE_R_MIN,
|
|
176
|
+
): ProbeAdmissionResult {
|
|
177
|
+
const active = candidates.filter((candidate) => contributorIsActive(candidate, radiusMinimum));
|
|
178
|
+
const overflowed = active.length > PROBE_MAX_CONTRIBUTORS;
|
|
179
|
+
// Admission is a deterministic stable prefix, not an all-or-nothing gate.
|
|
180
|
+
// Sort the complete active set by identity before clipping so query order
|
|
181
|
+
// cannot change the selected contributors.
|
|
182
|
+
const stableActive = active
|
|
183
|
+
.slice()
|
|
184
|
+
.sort((left, right) => left.identity.localeCompare(right.identity));
|
|
185
|
+
const admitted = stableActive.slice(0, PROBE_MAX_CONTRIBUTORS);
|
|
186
|
+
const rejected = stableActive.slice(PROBE_MAX_CONTRIBUTORS);
|
|
187
|
+
const scaleRadius = active.reduce(
|
|
188
|
+
(minimum, candidate) => Math.min(minimum, candidate.radius),
|
|
189
|
+
active[0]?.radius ?? radiusMinimum,
|
|
190
|
+
);
|
|
191
|
+
const receipt: ProbeAdmissionReceipt = {
|
|
192
|
+
activeIdentities: stableActive.map((candidate) => candidate.identity),
|
|
193
|
+
admittedIdentities: admitted.map((candidate) => candidate.identity),
|
|
194
|
+
rejectedIdentities: rejected.map((candidate) => candidate.identity),
|
|
195
|
+
stableOrder: admitted.map((candidate) => candidate.identity),
|
|
196
|
+
capacity: PROBE_MAX_CONTRIBUTORS,
|
|
197
|
+
...(overflowed ? { overflowReason: 'active-count-exceeds-capacity' as const } : {}),
|
|
198
|
+
scaleRadius,
|
|
199
|
+
finite: stableActive.every((candidate) => Number.isFinite(candidate.radius)),
|
|
200
|
+
};
|
|
201
|
+
return {
|
|
202
|
+
active: stableActive,
|
|
203
|
+
admitted,
|
|
204
|
+
rejected,
|
|
205
|
+
receipt,
|
|
206
|
+
...(overflowed
|
|
207
|
+
? {
|
|
208
|
+
error: {
|
|
209
|
+
code: 'capacity-exceeded' as const,
|
|
210
|
+
activeCount: active.length,
|
|
211
|
+
capacity: PROBE_MAX_CONTRIBUTORS,
|
|
212
|
+
receipt,
|
|
213
|
+
},
|
|
214
|
+
}
|
|
215
|
+
: {}),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function invalidInputResult(
|
|
220
|
+
radiusMinimum: number,
|
|
221
|
+
code: ProbeAdmissionError['code'],
|
|
222
|
+
): ProbeBlendResult {
|
|
223
|
+
const receipt = emptyReceipt(radiusMinimum);
|
|
224
|
+
return {
|
|
225
|
+
terms: [],
|
|
226
|
+
rStar: radiusMinimum,
|
|
227
|
+
Q: 0,
|
|
228
|
+
C: 0,
|
|
229
|
+
S: 1,
|
|
230
|
+
SHPreblend: new Array<number>(27).fill(0),
|
|
231
|
+
skyIrradiance: [0, 0, 0],
|
|
232
|
+
diffuse: [0, 0, 0],
|
|
233
|
+
finite: false,
|
|
234
|
+
receipt,
|
|
235
|
+
scaledQLogOffset: 0,
|
|
236
|
+
error: { code, activeCount: 0, capacity: PROBE_MAX_CONTRIBUTORS, receipt },
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** Neumaier compensated sum in JavaScript's IEEE-754 binary64 number type. */
|
|
241
|
+
export function neumaierSum(values: readonly number[]): number {
|
|
242
|
+
let sum = 0;
|
|
243
|
+
let correction = 0;
|
|
244
|
+
for (const value of values) {
|
|
245
|
+
const next = sum + value;
|
|
246
|
+
if (Math.abs(sum) >= Math.abs(value)) correction += sum - next + value;
|
|
247
|
+
else correction += value - next + sum;
|
|
248
|
+
sum = next;
|
|
249
|
+
}
|
|
250
|
+
return sum + correction;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** Round a finite analytic value at the single f32 boundary. */
|
|
254
|
+
export function roundProbeToF32(value: number): number | undefined {
|
|
255
|
+
return Number.isFinite(value) ? new Float32Array([value])[0] : undefined;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export function scaledProbeWeight(
|
|
259
|
+
distance: number,
|
|
260
|
+
radius: number,
|
|
261
|
+
radiusMinimum = PROBE_R_MIN,
|
|
262
|
+
): { readonly coverage: number; readonly q: number } {
|
|
263
|
+
if (
|
|
264
|
+
!Number.isFinite(distance) ||
|
|
265
|
+
distance < 0 ||
|
|
266
|
+
!Number.isFinite(radius) ||
|
|
267
|
+
radius < radiusMinimum ||
|
|
268
|
+
distance >= radius
|
|
269
|
+
) {
|
|
270
|
+
return { coverage: 0, q: 0 };
|
|
271
|
+
}
|
|
272
|
+
const ratio = distance / radius;
|
|
273
|
+
const coverage = 1 - ratio * ratio;
|
|
274
|
+
// q is intentionally diagnostic only. The kernel consumes scaledQ below.
|
|
275
|
+
return { coverage, q: coverage / (radius * radius) };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function sh9Irradiance(
|
|
279
|
+
coefficients: readonly number[],
|
|
280
|
+
normal: readonly [number, number, number],
|
|
281
|
+
): number[] {
|
|
282
|
+
const x = normal[0];
|
|
283
|
+
const y = normal[1];
|
|
284
|
+
const z = normal[2];
|
|
285
|
+
const basis = [
|
|
286
|
+
SH_C0,
|
|
287
|
+
SH_C1 * y,
|
|
288
|
+
SH_C1 * z,
|
|
289
|
+
SH_C1 * x,
|
|
290
|
+
SH_C2 * x * y,
|
|
291
|
+
SH_C2 * y * z,
|
|
292
|
+
SH_C20 * (3 * z * z - 1),
|
|
293
|
+
SH_C2 * x * z,
|
|
294
|
+
SH_C22 * (x * x - y * y),
|
|
295
|
+
];
|
|
296
|
+
const result = [0, 0, 0];
|
|
297
|
+
for (let band = 0; band < basis.length; band += 1) {
|
|
298
|
+
const factor = basis[band] ?? 0;
|
|
299
|
+
const offset = band * 3;
|
|
300
|
+
result[0] = (result[0] ?? 0) + (coefficients[offset] ?? 0) * factor;
|
|
301
|
+
result[1] = (result[1] ?? 0) + (coefficients[offset + 1] ?? 0) * factor;
|
|
302
|
+
result[2] = (result[2] ?? 0) + (coefficients[offset + 2] ?? 0) * factor;
|
|
303
|
+
}
|
|
304
|
+
return result;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function vectorNeumaierSum(values: readonly (readonly number[])[], width: number): number[] {
|
|
308
|
+
return Array.from({ length: width }, (_, channel) =>
|
|
309
|
+
neumaierSum(values.map((value) => value[channel] ?? 0)),
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
interface ProbeBlendCoefficients {
|
|
314
|
+
readonly terms: readonly ProbeBlendTerm[];
|
|
315
|
+
readonly rStar: number;
|
|
316
|
+
readonly Q: number;
|
|
317
|
+
readonly C: number;
|
|
318
|
+
readonly S: number;
|
|
319
|
+
readonly SHPreblend: readonly number[];
|
|
320
|
+
readonly receipt: ProbeAdmissionReceipt;
|
|
321
|
+
readonly finite: boolean;
|
|
322
|
+
readonly scaledQLogOffset: number;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function blendProbeCoefficients(
|
|
326
|
+
contributors: readonly ProbeBlendContributor[],
|
|
327
|
+
radiusMinimum: number,
|
|
328
|
+
): ProbeBlendCoefficients | undefined {
|
|
329
|
+
if (
|
|
330
|
+
contributors.length > PROBE_MAX_CONTRIBUTORS ||
|
|
331
|
+
contributors.some((contributor) => !contributorIsActive(contributor, radiusMinimum))
|
|
332
|
+
) {
|
|
333
|
+
return undefined;
|
|
334
|
+
}
|
|
335
|
+
const rStar = contributors.reduce(
|
|
336
|
+
(minimum, contributor) => Math.min(minimum, contributor.radius),
|
|
337
|
+
contributors[0]?.radius ?? radiusMinimum,
|
|
338
|
+
);
|
|
339
|
+
const rawTerms = contributors.map((contributor) => {
|
|
340
|
+
const { coverage } = scaledProbeWeight(contributor.distance, contributor.radius, radiusMinimum);
|
|
341
|
+
const scaledRatio = rStar / contributor.radius;
|
|
342
|
+
return {
|
|
343
|
+
contributor,
|
|
344
|
+
coverage,
|
|
345
|
+
// Diagnostic q is never used for Q, alpha, C, or SH accumulation.
|
|
346
|
+
q: coverage / (contributor.radius * contributor.radius),
|
|
347
|
+
scaledQ: coverage * scaledRatio * scaledRatio,
|
|
348
|
+
};
|
|
349
|
+
});
|
|
350
|
+
// The frozen minimum radius bounds rStar/r_i by one. The kernel consumes
|
|
351
|
+
// only scaledQ, never diagnostic q or a reciprocal-square value.
|
|
352
|
+
const scaledQLogOffset = 0;
|
|
353
|
+
const finiteTerms = rawTerms;
|
|
354
|
+
if (finiteTerms.some((term) => !Number.isFinite(term.scaledQ))) return undefined;
|
|
355
|
+
const Q = neumaierSum(finiteTerms.map((term) => term.scaledQ));
|
|
356
|
+
const terms = finiteTerms.map((term) => ({
|
|
357
|
+
identity: term.contributor.identity,
|
|
358
|
+
distance: term.contributor.distance,
|
|
359
|
+
radius: term.contributor.radius,
|
|
360
|
+
coverage: term.coverage,
|
|
361
|
+
q: term.q,
|
|
362
|
+
scaledQ: term.scaledQ,
|
|
363
|
+
qHat: term.scaledQ,
|
|
364
|
+
alpha: Q > 0 ? term.scaledQ / Q : 0,
|
|
365
|
+
}));
|
|
366
|
+
let product = 1;
|
|
367
|
+
for (const term of finiteTerms) product *= 1 - term.coverage;
|
|
368
|
+
const C = 1 - product;
|
|
369
|
+
const S = 1 - C;
|
|
370
|
+
const weightedSh = finiteTerms.map((term) =>
|
|
371
|
+
Array.from(term.contributor.irradiance, (value) => value * (Q > 0 ? term.scaledQ / Q : 0)),
|
|
372
|
+
);
|
|
373
|
+
const SHPreblend = vectorNeumaierSum(weightedSh, 27).map((value) => value * C);
|
|
374
|
+
const finite =
|
|
375
|
+
Number.isFinite(Q) &&
|
|
376
|
+
Number.isFinite(C) &&
|
|
377
|
+
Number.isFinite(S) &&
|
|
378
|
+
SHPreblend.every(Number.isFinite) &&
|
|
379
|
+
finiteTerms.every((term) => Number.isFinite(term.scaledQ));
|
|
380
|
+
const receipt: ProbeAdmissionReceipt = {
|
|
381
|
+
activeIdentities: contributors.map((contributor) => contributor.identity),
|
|
382
|
+
admittedIdentities: contributors.map((contributor) => contributor.identity),
|
|
383
|
+
rejectedIdentities: [],
|
|
384
|
+
stableOrder: contributors.map((contributor) => contributor.identity),
|
|
385
|
+
capacity: PROBE_MAX_CONTRIBUTORS,
|
|
386
|
+
scaleRadius: rStar,
|
|
387
|
+
finite,
|
|
388
|
+
};
|
|
389
|
+
return {
|
|
390
|
+
terms,
|
|
391
|
+
rStar,
|
|
392
|
+
Q,
|
|
393
|
+
C,
|
|
394
|
+
S,
|
|
395
|
+
SHPreblend,
|
|
396
|
+
finite,
|
|
397
|
+
receipt,
|
|
398
|
+
scaledQLogOffset,
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Evaluate the CPU analytic oracle. Sky is deliberately an explicit residual
|
|
404
|
+
* input and never becomes a contributor or a q/Q term.
|
|
405
|
+
*/
|
|
406
|
+
export function blendLightProbes(input: ProbeBlendInput): ProbeBlendResult {
|
|
407
|
+
const radiusMinimum = input.radiusMinimum ?? PROBE_R_MIN;
|
|
408
|
+
const normal = input.normal;
|
|
409
|
+
const sky = input.skyIrradiance;
|
|
410
|
+
if (!finiteVector(normal, 3) || !finiteVector(sky, 3)) {
|
|
411
|
+
return invalidInputResult(radiusMinimum, 'invalid-admitted-prefix');
|
|
412
|
+
}
|
|
413
|
+
const coefficients = blendProbeCoefficients(input.contributors, radiusMinimum);
|
|
414
|
+
if (coefficients === undefined)
|
|
415
|
+
return invalidInputResult(radiusMinimum, 'invalid-admitted-prefix');
|
|
416
|
+
const shIrradiance = sh9Irradiance(coefficients.SHPreblend, normal);
|
|
417
|
+
const diffuse = [0, 1, 2].map(
|
|
418
|
+
(channel) => Math.max(shIrradiance[channel] ?? 0, 0) + coefficients.S * (sky[channel] ?? 0),
|
|
419
|
+
) as [number, number, number];
|
|
420
|
+
return {
|
|
421
|
+
...coefficients,
|
|
422
|
+
skyIrradiance: [sky[0], sky[1], sky[2]],
|
|
423
|
+
diffuse,
|
|
424
|
+
finite: coefficients.finite && diffuse.every(Number.isFinite),
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function vectorDistance(
|
|
429
|
+
left: readonly [number, number, number],
|
|
430
|
+
right: readonly [number, number, number],
|
|
431
|
+
): number {
|
|
432
|
+
const dx = left[0] - right[0];
|
|
433
|
+
const dy = left[1] - right[1];
|
|
434
|
+
const dz = left[2] - right[2];
|
|
435
|
+
return Math.hypot(dx, dy, dz);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function encodeProbeRecord(
|
|
439
|
+
objectKey: number,
|
|
440
|
+
generation: number,
|
|
441
|
+
localBlendFraction: number,
|
|
442
|
+
shPreblend: readonly number[],
|
|
443
|
+
candidate: boolean,
|
|
444
|
+
accepted: boolean,
|
|
445
|
+
lastKnownGood: boolean,
|
|
446
|
+
sentinel: ProbeBlendRecordSentinel | undefined,
|
|
447
|
+
): ProbeBlendRecord {
|
|
448
|
+
const values = new Float32Array(PROBE_BLEND_RECORD_BYTE_SIZE / Float32Array.BYTES_PER_ELEMENT);
|
|
449
|
+
values[0] = objectKey;
|
|
450
|
+
values[1] = generation;
|
|
451
|
+
values[2] = roundProbeToF32(localBlendFraction) ?? 0;
|
|
452
|
+
values[3] = (candidate ? 1 : 0) | (accepted ? 2 : 0) | (lastKnownGood ? 4 : 0);
|
|
453
|
+
for (let band = 0; band < 9; band += 1) {
|
|
454
|
+
const lane = 4 + band * 4;
|
|
455
|
+
values[lane] = roundProbeToF32(shPreblend[band * 3] ?? 0) ?? 0;
|
|
456
|
+
values[lane + 1] = roundProbeToF32(shPreblend[band * 3 + 1] ?? 0) ?? 0;
|
|
457
|
+
values[lane + 2] = roundProbeToF32(shPreblend[band * 3 + 2] ?? 0) ?? 0;
|
|
458
|
+
values[lane + 3] = 0;
|
|
459
|
+
}
|
|
460
|
+
const packedSh = new Array<number>(27);
|
|
461
|
+
for (let band = 0; band < 9; band += 1) {
|
|
462
|
+
packedSh[band * 3] = values[4 + band * 4] ?? 0;
|
|
463
|
+
packedSh[band * 3 + 1] = values[5 + band * 4] ?? 0;
|
|
464
|
+
packedSh[band * 3 + 2] = values[6 + band * 4] ?? 0;
|
|
465
|
+
}
|
|
466
|
+
return {
|
|
467
|
+
objectKey,
|
|
468
|
+
generation,
|
|
469
|
+
localBlendFraction: values[2] ?? 0,
|
|
470
|
+
shPreblend: packedSh,
|
|
471
|
+
bytes: new Uint8Array(values.buffer),
|
|
472
|
+
byteLength: values.byteLength,
|
|
473
|
+
candidate,
|
|
474
|
+
accepted,
|
|
475
|
+
lastKnownGood,
|
|
476
|
+
...(sentinel === undefined ? {} : { sentinel }),
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function sameList(left: readonly string[], right: readonly string[]): boolean {
|
|
481
|
+
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function sameVector3(
|
|
485
|
+
left: readonly [number, number, number],
|
|
486
|
+
right: readonly [number, number, number],
|
|
487
|
+
): boolean {
|
|
488
|
+
return left[0] === right[0] && left[1] === right[1] && left[2] === right[2];
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function sameProbeFacts(
|
|
492
|
+
left: readonly ProbeSceneProbeInput[],
|
|
493
|
+
right: readonly ProbeSceneProbeInput[],
|
|
494
|
+
): boolean {
|
|
495
|
+
if (left.length !== right.length) return false;
|
|
496
|
+
return left.every((probe, index) => {
|
|
497
|
+
const other = right[index];
|
|
498
|
+
if (
|
|
499
|
+
other === undefined ||
|
|
500
|
+
probe.identity !== other.identity ||
|
|
501
|
+
probe.radius !== other.radius ||
|
|
502
|
+
probe.admitted !== other.admitted ||
|
|
503
|
+
!sameVector3(probe.position, other.position) ||
|
|
504
|
+
probe.irradiance.length !== other.irradiance.length
|
|
505
|
+
)
|
|
506
|
+
return false;
|
|
507
|
+
for (let value = 0; value < probe.irradiance.length; value += 1) {
|
|
508
|
+
if (probe.irradiance[value] !== other.irradiance[value]) return false;
|
|
509
|
+
}
|
|
510
|
+
return true;
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
function sameSkyFacts(left: ProbeSceneProjectionInput, right: ProbeSceneProjectionInput): boolean {
|
|
515
|
+
const leftSky = left.sky;
|
|
516
|
+
const rightSky = right.sky;
|
|
517
|
+
if ((leftSky === undefined) !== (rightSky === undefined)) return false;
|
|
518
|
+
if (
|
|
519
|
+
leftSky !== undefined &&
|
|
520
|
+
rightSky !== undefined &&
|
|
521
|
+
(leftSky.available !== rightSky.available ||
|
|
522
|
+
leftSky.identity !== rightSky.identity ||
|
|
523
|
+
leftSky.sourceKey !== rightSky.sourceKey ||
|
|
524
|
+
leftSky.fallbackReason !== rightSky.fallbackReason ||
|
|
525
|
+
!sameVector3(leftSky.irradiance, rightSky.irradiance))
|
|
526
|
+
)
|
|
527
|
+
return false;
|
|
528
|
+
if ((left.skyIrradiance === undefined) !== (right.skyIrradiance === undefined)) return false;
|
|
529
|
+
return (
|
|
530
|
+
left.skyIrradiance === undefined ||
|
|
531
|
+
(right.skyIrradiance !== undefined && sameVector3(left.skyIrradiance, right.skyIrradiance))
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/** Renderer-owned bounded projection; admission is explicit and fail-closed. */
|
|
536
|
+
export class ProbeBlendSceneProjection {
|
|
537
|
+
private previous?: ProbeSceneProjectionInput;
|
|
538
|
+
private records = new Map<number, ProbeBlendRecord>();
|
|
539
|
+
private lastError: ProbeAdmissionError | undefined;
|
|
540
|
+
private lastReceipt = emptyReceipt(PROBE_R_MIN);
|
|
541
|
+
private lastActiveContributorCount = 0;
|
|
542
|
+
private lastAdmittedProbeCount = 0;
|
|
543
|
+
private lastCoverage = 0;
|
|
544
|
+
private lastFinite = true;
|
|
545
|
+
private lastSky: ProbeSceneProjectionInput['sky'] = {
|
|
546
|
+
available: false,
|
|
547
|
+
irradiance: [0, 0, 0],
|
|
548
|
+
fallbackReason: 'no-skylight',
|
|
549
|
+
};
|
|
550
|
+
private lastDirtyVisitCount = 0;
|
|
551
|
+
private lastDirtyReasons: readonly ProbeSceneDirtyReason[] = [];
|
|
552
|
+
private recordFacts = new Map<
|
|
553
|
+
number,
|
|
554
|
+
{
|
|
555
|
+
readonly contributors: readonly ProbeBlendTerm[];
|
|
556
|
+
readonly qHatSum: number;
|
|
557
|
+
readonly rStar: number;
|
|
558
|
+
readonly fallbackReason: string | undefined;
|
|
559
|
+
}
|
|
560
|
+
>();
|
|
561
|
+
|
|
562
|
+
getRecord(objectKey: number): ProbeBlendRecord | undefined {
|
|
563
|
+
return this.records.get(objectKey);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
hasRecords(): boolean {
|
|
567
|
+
return this.records.size > 0;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
inspect(): ProbeBlendInspection {
|
|
571
|
+
return {
|
|
572
|
+
activeContributorCount: this.lastActiveContributorCount,
|
|
573
|
+
admittedProbeCount: this.lastAdmittedProbeCount,
|
|
574
|
+
coverage: this.lastCoverage,
|
|
575
|
+
skyResidualFraction: 1 - this.lastCoverage,
|
|
576
|
+
finite: this.lastFinite,
|
|
577
|
+
errorCode: this.lastError?.code,
|
|
578
|
+
records: [...this.records.values()].map((record) => {
|
|
579
|
+
const facts = this.recordFacts.get(record.objectKey);
|
|
580
|
+
return {
|
|
581
|
+
objectKey: record.objectKey,
|
|
582
|
+
generation: record.generation,
|
|
583
|
+
localBlendFraction: record.localBlendFraction,
|
|
584
|
+
shPreblend: [...record.shPreblend],
|
|
585
|
+
byteLength: record.byteLength,
|
|
586
|
+
candidate: record.candidate,
|
|
587
|
+
accepted: record.accepted,
|
|
588
|
+
lastKnownGood: record.lastKnownGood,
|
|
589
|
+
sentinel: record.sentinel,
|
|
590
|
+
probeBlendIndex: record.objectKey,
|
|
591
|
+
contributors: (facts?.contributors ?? []).map((term) => ({
|
|
592
|
+
identity: term.identity,
|
|
593
|
+
distance: term.distance,
|
|
594
|
+
radius: term.radius,
|
|
595
|
+
coverage: term.coverage,
|
|
596
|
+
q: term.q,
|
|
597
|
+
qHat: term.qHat,
|
|
598
|
+
alpha: term.alpha,
|
|
599
|
+
})),
|
|
600
|
+
qHatSum: facts?.qHatSum ?? 0,
|
|
601
|
+
rStar: facts?.rStar ?? PROBE_R_MIN,
|
|
602
|
+
fallbackReason: facts?.fallbackReason,
|
|
603
|
+
};
|
|
604
|
+
}),
|
|
605
|
+
sky: {
|
|
606
|
+
available: this.lastSky?.available ?? false,
|
|
607
|
+
identity: this.lastSky?.identity,
|
|
608
|
+
sourceKey: this.lastSky?.sourceKey,
|
|
609
|
+
irradiance: [...(this.lastSky?.irradiance ?? [0, 0, 0])] as [number, number, number],
|
|
610
|
+
fallbackReason: this.lastSky?.fallbackReason,
|
|
611
|
+
},
|
|
612
|
+
dirtyVisitCount: this.lastDirtyVisitCount,
|
|
613
|
+
dirtyReasons: [...this.lastDirtyReasons],
|
|
614
|
+
receipt: {
|
|
615
|
+
activeIdentities: [...this.lastReceipt.activeIdentities],
|
|
616
|
+
admittedIdentities: [...this.lastReceipt.admittedIdentities],
|
|
617
|
+
rejectedIdentities: [...this.lastReceipt.rejectedIdentities],
|
|
618
|
+
stableOrder: [...this.lastReceipt.stableOrder],
|
|
619
|
+
capacity: this.lastReceipt.capacity,
|
|
620
|
+
scaleRadius: this.lastReceipt.scaleRadius,
|
|
621
|
+
finite: this.lastReceipt.finite,
|
|
622
|
+
overflowReason: this.lastReceipt.overflowReason,
|
|
623
|
+
},
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
apply(input: ProbeSceneProjectionInput): ProbeSceneProjectionResult {
|
|
628
|
+
const dirtyReasons: ProbeSceneDirtyReason[] = [];
|
|
629
|
+
const affectedObjectKeys = new Set<number>();
|
|
630
|
+
const previous = this.previous;
|
|
631
|
+
const objectKeys = input.objects.map((object) => String(object.objectKey)).sort();
|
|
632
|
+
const previousObjectKeys =
|
|
633
|
+
previous?.objects.map((object) => String(object.objectKey)).sort() ?? [];
|
|
634
|
+
if (previous === undefined || !sameList(objectKeys, previousObjectKeys)) {
|
|
635
|
+
dirtyReasons.push('object-boundary');
|
|
636
|
+
for (const object of input.objects) affectedObjectKeys.add(object.objectKey);
|
|
637
|
+
}
|
|
638
|
+
if (previous !== undefined && input.worldRevision !== previous.worldRevision)
|
|
639
|
+
dirtyReasons.push('world');
|
|
640
|
+
if (previous !== undefined && !sameProbeFacts(input.probes, previous.probes))
|
|
641
|
+
dirtyReasons.push('probe-fact');
|
|
642
|
+
if (previous !== undefined && !sameSkyFacts(input, previous)) dirtyReasons.push('sky');
|
|
643
|
+
const previousObjects = new Map(previous?.objects.map((object) => [object.objectKey, object]));
|
|
644
|
+
for (const object of input.objects) {
|
|
645
|
+
const oldObject = previousObjects.get(object.objectKey);
|
|
646
|
+
if (oldObject === undefined) {
|
|
647
|
+
affectedObjectKeys.add(object.objectKey);
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
if (!sameVector3(object.position, oldObject.position)) {
|
|
651
|
+
dirtyReasons.push('object-position');
|
|
652
|
+
affectedObjectKeys.add(object.objectKey);
|
|
653
|
+
}
|
|
654
|
+
if (object.generation !== oldObject.generation) {
|
|
655
|
+
dirtyReasons.push('generation');
|
|
656
|
+
affectedObjectKeys.add(object.objectKey);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
if (
|
|
660
|
+
dirtyReasons.includes('world') ||
|
|
661
|
+
dirtyReasons.includes('probe-fact') ||
|
|
662
|
+
dirtyReasons.includes('sky')
|
|
663
|
+
)
|
|
664
|
+
for (const object of input.objects) affectedObjectKeys.add(object.objectKey);
|
|
665
|
+
const uniqueDirtyReasons = [...new Set(dirtyReasons)];
|
|
666
|
+
if (previous !== undefined && uniqueDirtyReasons.length === 0) {
|
|
667
|
+
return {
|
|
668
|
+
records: [...this.records.values()],
|
|
669
|
+
contributors: [],
|
|
670
|
+
activeContributorCount: this.lastActiveContributorCount,
|
|
671
|
+
admittedProbeCount: this.lastAdmittedProbeCount,
|
|
672
|
+
coverage: this.lastCoverage,
|
|
673
|
+
skyResidualFraction: 1 - this.lastCoverage,
|
|
674
|
+
dirtyReasons: [],
|
|
675
|
+
affectedObjectKeys: [],
|
|
676
|
+
visits: 0,
|
|
677
|
+
allocations: 0,
|
|
678
|
+
uploads: 0,
|
|
679
|
+
receipt: this.lastReceipt,
|
|
680
|
+
...(this.lastError === undefined ? {} : { error: this.lastError }),
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
const nextRecords = new Map(this.records);
|
|
684
|
+
const currentObjectKeys = new Set(input.objects.map((object) => object.objectKey));
|
|
685
|
+
for (const objectKey of nextRecords.keys()) {
|
|
686
|
+
if (!currentObjectKeys.has(objectKey)) {
|
|
687
|
+
nextRecords.delete(objectKey);
|
|
688
|
+
this.recordFacts.delete(objectKey);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
let firstTerms: readonly ProbeBlendTerm[] = [];
|
|
692
|
+
let firstReceipt = emptyReceipt(PROBE_R_MIN);
|
|
693
|
+
let firstError: ProbeAdmissionError | undefined;
|
|
694
|
+
let firstActiveCount = 0;
|
|
695
|
+
let firstAdmittedCount = 0;
|
|
696
|
+
let firstCoverage = 0;
|
|
697
|
+
let firstObjectSeen = false;
|
|
698
|
+
let visited = 0;
|
|
699
|
+
for (const object of input.objects) {
|
|
700
|
+
if (!affectedObjectKeys.has(object.objectKey)) continue;
|
|
701
|
+
visited += 1;
|
|
702
|
+
const candidates = input.probes.map((probe) => ({
|
|
703
|
+
identity: probe.identity,
|
|
704
|
+
admitted: probe.admitted,
|
|
705
|
+
distance: vectorDistance(object.position, probe.position),
|
|
706
|
+
radius: probe.radius,
|
|
707
|
+
irradiance: probe.irradiance,
|
|
708
|
+
}));
|
|
709
|
+
const admission = admitProbeContributors(candidates);
|
|
710
|
+
if (!firstObjectSeen) {
|
|
711
|
+
firstObjectSeen = true;
|
|
712
|
+
firstActiveCount = admission.active.length;
|
|
713
|
+
firstAdmittedCount = admission.admitted.length;
|
|
714
|
+
firstReceipt = admission.receipt;
|
|
715
|
+
}
|
|
716
|
+
const previousRecord = this.records.get(object.objectKey);
|
|
717
|
+
const lastKnownGood = input.lastKnownGood ?? previousRecord?.lastKnownGood ?? false;
|
|
718
|
+
let record: ProbeBlendRecord;
|
|
719
|
+
if (admission.error !== undefined) firstError ??= admission.error;
|
|
720
|
+
if (admission.admitted.length === 0) {
|
|
721
|
+
if (input.probes.length === 0) {
|
|
722
|
+
nextRecords.delete(object.objectKey);
|
|
723
|
+
this.recordFacts.delete(object.objectKey);
|
|
724
|
+
continue;
|
|
725
|
+
}
|
|
726
|
+
const sentinel: ProbeBlendRecordSentinel = lastKnownGood ? 'no-active' : 'no-lkg';
|
|
727
|
+
record = encodeProbeRecord(
|
|
728
|
+
object.objectKey,
|
|
729
|
+
object.generation,
|
|
730
|
+
0,
|
|
731
|
+
new Array(27).fill(0),
|
|
732
|
+
true,
|
|
733
|
+
false,
|
|
734
|
+
lastKnownGood,
|
|
735
|
+
sentinel,
|
|
736
|
+
);
|
|
737
|
+
this.recordFacts.set(object.objectKey, {
|
|
738
|
+
contributors: [],
|
|
739
|
+
qHatSum: 0,
|
|
740
|
+
rStar: PROBE_R_MIN,
|
|
741
|
+
fallbackReason: admission.error?.code ?? sentinel,
|
|
742
|
+
});
|
|
743
|
+
} else {
|
|
744
|
+
const blend = blendProbeCoefficients(admission.admitted, PROBE_R_MIN);
|
|
745
|
+
if (blend === undefined) {
|
|
746
|
+
firstError ??= {
|
|
747
|
+
code: 'invalid-admitted-prefix',
|
|
748
|
+
activeCount: admission.active.length,
|
|
749
|
+
capacity: PROBE_MAX_CONTRIBUTORS,
|
|
750
|
+
receipt: admission.receipt,
|
|
751
|
+
};
|
|
752
|
+
firstReceipt = admission.receipt;
|
|
753
|
+
continue;
|
|
754
|
+
}
|
|
755
|
+
record = encodeProbeRecord(
|
|
756
|
+
object.objectKey,
|
|
757
|
+
object.generation,
|
|
758
|
+
blend.C,
|
|
759
|
+
blend.SHPreblend,
|
|
760
|
+
true,
|
|
761
|
+
true,
|
|
762
|
+
true,
|
|
763
|
+
undefined,
|
|
764
|
+
);
|
|
765
|
+
this.recordFacts.set(object.objectKey, {
|
|
766
|
+
contributors: blend.terms,
|
|
767
|
+
qHatSum: blend.Q,
|
|
768
|
+
rStar: blend.rStar,
|
|
769
|
+
fallbackReason:
|
|
770
|
+
admission.error === undefined ? undefined : 'capacity-exceeded-stable-prefix',
|
|
771
|
+
});
|
|
772
|
+
if (firstTerms.length === 0) firstTerms = blend.terms;
|
|
773
|
+
firstCoverage = blend.C;
|
|
774
|
+
}
|
|
775
|
+
nextRecords.set(object.objectKey, record);
|
|
776
|
+
firstError ??= admission.error;
|
|
777
|
+
}
|
|
778
|
+
const allocations = previous === undefined ? nextRecords.size : 0;
|
|
779
|
+
const result = {
|
|
780
|
+
records: [...nextRecords.values()],
|
|
781
|
+
contributors: firstTerms,
|
|
782
|
+
activeContributorCount: firstActiveCount,
|
|
783
|
+
admittedProbeCount: firstAdmittedCount,
|
|
784
|
+
coverage: firstCoverage,
|
|
785
|
+
skyResidualFraction: 1 - firstCoverage,
|
|
786
|
+
dirtyReasons: uniqueDirtyReasons,
|
|
787
|
+
affectedObjectKeys: [...affectedObjectKeys].sort((left, right) => left - right),
|
|
788
|
+
visits: visited,
|
|
789
|
+
allocations,
|
|
790
|
+
uploads: nextRecords.size,
|
|
791
|
+
receipt: firstReceipt,
|
|
792
|
+
...(firstError === undefined ? {} : { error: firstError }),
|
|
793
|
+
};
|
|
794
|
+
this.previous = input;
|
|
795
|
+
this.records = nextRecords;
|
|
796
|
+
this.lastError = firstError;
|
|
797
|
+
this.lastReceipt = firstReceipt;
|
|
798
|
+
this.lastActiveContributorCount = firstActiveCount;
|
|
799
|
+
this.lastAdmittedProbeCount = firstAdmittedCount;
|
|
800
|
+
this.lastCoverage = firstCoverage;
|
|
801
|
+
this.lastFinite = [...nextRecords.values()].every(
|
|
802
|
+
(record) => record.bytes.length === PROBE_BLEND_RECORD_BYTE_SIZE,
|
|
803
|
+
);
|
|
804
|
+
this.lastSky =
|
|
805
|
+
input.sky ??
|
|
806
|
+
(input.skyIrradiance === undefined
|
|
807
|
+
? { available: false, irradiance: [0, 0, 0] as const, fallbackReason: 'no-skylight' }
|
|
808
|
+
: { available: true, irradiance: input.skyIrradiance });
|
|
809
|
+
this.lastDirtyVisitCount = visited;
|
|
810
|
+
this.lastDirtyReasons = uniqueDirtyReasons;
|
|
811
|
+
return result;
|
|
812
|
+
}
|
|
813
|
+
}
|