@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
|
@@ -112,15 +112,12 @@ import {
|
|
|
112
112
|
SpriteInstancesMutuallyExclusiveWithInstancesError,
|
|
113
113
|
SpriteInstancesRequiresSpriteShaderError,
|
|
114
114
|
} from '@forgeax/engine-ecs/projection';
|
|
115
|
+
import { deriveVertexLayoutProjection } from '@forgeax/engine-geometry';
|
|
115
116
|
import { box3, frustum, type Mat4, mat4, type Vec3, vec3 } from '@forgeax/engine-math';
|
|
116
117
|
import { AssetGuid, type AssetGuid as AssetGuidBytes } from '@forgeax/engine-pack/guid';
|
|
117
118
|
import { RhiError } from '@forgeax/engine-rhi';
|
|
118
|
-
import {
|
|
119
|
-
|
|
120
|
-
projectHierarchy,
|
|
121
|
-
type SceneHierarchySnapshot,
|
|
122
|
-
Transform,
|
|
123
|
-
} from '@forgeax/engine-scene';
|
|
119
|
+
import { GlobalTransform, MorphWeights, Transform } from '@forgeax/engine-scene';
|
|
120
|
+
import type { MaterialShaderArtifact } from '@forgeax/engine-shader';
|
|
124
121
|
import {
|
|
125
122
|
JointCountMismatchError,
|
|
126
123
|
JointEntityDanglingError,
|
|
@@ -131,6 +128,7 @@ import {
|
|
|
131
128
|
import type {
|
|
132
129
|
Asset,
|
|
133
130
|
Handle,
|
|
131
|
+
IesProfileAsset,
|
|
134
132
|
MaterialAsset,
|
|
135
133
|
MaterialColorParameterSchema,
|
|
136
134
|
MaterialParameter,
|
|
@@ -139,6 +137,7 @@ import type {
|
|
|
139
137
|
MaterialTextureCoordinates,
|
|
140
138
|
MaterialTextureValue,
|
|
141
139
|
MeshAsset,
|
|
140
|
+
MeshLodLevel,
|
|
142
141
|
ParamSchemaEntry,
|
|
143
142
|
PrimitiveTopology,
|
|
144
143
|
SkeletonAsset,
|
|
@@ -148,6 +147,7 @@ import {
|
|
|
148
147
|
ASSET_ERROR_HINTS,
|
|
149
148
|
AssetError,
|
|
150
149
|
derive,
|
|
150
|
+
handleSlot,
|
|
151
151
|
materialGuidText,
|
|
152
152
|
materialValuesToLinearRuntime,
|
|
153
153
|
toShared,
|
|
@@ -164,6 +164,7 @@ import {
|
|
|
164
164
|
Fog,
|
|
165
165
|
Instances,
|
|
166
166
|
Layer,
|
|
167
|
+
LightProbe,
|
|
167
168
|
Lines,
|
|
168
169
|
MeshFilter,
|
|
169
170
|
MeshRenderer,
|
|
@@ -173,6 +174,7 @@ import {
|
|
|
173
174
|
Points,
|
|
174
175
|
PostProcessParams,
|
|
175
176
|
pointShapeFromU32,
|
|
177
|
+
RectAreaLight,
|
|
176
178
|
ReflectionProbe,
|
|
177
179
|
reflectionProbeUpdateIntentFromF32,
|
|
178
180
|
SkyboxBackground,
|
|
@@ -194,6 +196,8 @@ import {
|
|
|
194
196
|
degToCos,
|
|
195
197
|
type LightValidationError,
|
|
196
198
|
validateDirectionalLightData,
|
|
199
|
+
validateLightProbeData,
|
|
200
|
+
validateRectAreaLightData,
|
|
197
201
|
} from './components/light-helpers';
|
|
198
202
|
import { selectEnvironment } from './environment/frame';
|
|
199
203
|
import {
|
|
@@ -212,12 +216,14 @@ import type {
|
|
|
212
216
|
FogFrame,
|
|
213
217
|
FogSelectionFailure,
|
|
214
218
|
} from './extract/environment';
|
|
219
|
+
import { buildGpuDrivenDraws, resolvePreparedGpuDrivenDraw } from './extract/gpu-driven';
|
|
215
220
|
import { resolveVisibility, type VisibilitySnapshot } from './extract/visibility';
|
|
216
221
|
import type {
|
|
217
222
|
RenderFeatureHiddenEntityReport,
|
|
218
223
|
RenderFeatureWorldVisibilitySnapshot,
|
|
219
224
|
} from './features/types';
|
|
220
225
|
import { ensureGlyphMeshMaterialSlots } from './glyph-text-layout-system';
|
|
226
|
+
import type { GpuDrivenGeometryReceipt } from './gpu-driven/prepared-draw';
|
|
221
227
|
import { deriveInstancesUnionBounds } from './instances-derived-bounds';
|
|
222
228
|
import {
|
|
223
229
|
type MeshMaterialBindingDiagnostic,
|
|
@@ -227,6 +233,8 @@ import {
|
|
|
227
233
|
import { isStandardPbrMaterialShader } from './pbr-pipeline';
|
|
228
234
|
import { expandPointsLinesBounds } from './points-lines/bounds';
|
|
229
235
|
import type { PointsLinesRetainedSnapshot, PointsLinesStyle } from './points-lines/snapshot';
|
|
236
|
+
import { COOKIE_SLICE_CAPACITY, IES_SLICE_CAPACITY } from './prepare/extended-lighting/resources';
|
|
237
|
+
import { prepareCookieProjection } from './prepare/extended-lighting/spot-modifiers';
|
|
230
238
|
import type { ReflectionProbeFact } from './reflection/projection';
|
|
231
239
|
import {
|
|
232
240
|
type CameraSnapshot,
|
|
@@ -289,18 +297,19 @@ export interface PointLightSnapshot {
|
|
|
289
297
|
* plan-strategy §D-2 — record stage / shader skips shadow sampling when the
|
|
290
298
|
* lane equals the sentinel. Joined with `pointShadow[]` by entity at the
|
|
291
299
|
* end of extract; HDRP record stage threads `shadowAtlasLayer + shadowNear +
|
|
292
|
-
* shadowFar` through
|
|
300
|
+
* shadowFar` through the unified direct-light metadata payload.
|
|
293
301
|
*/
|
|
294
302
|
readonly shadowAtlasLayer?: number;
|
|
295
303
|
/**
|
|
296
304
|
* Per-face perspective near plane (matches `PointLightShadow.nearPlane`).
|
|
297
|
-
* Used by HDRP `evalPointShadowed` for depth-ref reconstruction;
|
|
298
|
-
*
|
|
305
|
+
* Used by HDRP `evalPointShadowed` for depth-ref reconstruction; URP keeps
|
|
306
|
+
* the projection constants in its dedicated shadow-parameter buffer.
|
|
299
307
|
*/
|
|
300
308
|
readonly shadowNear?: number;
|
|
301
309
|
/**
|
|
302
310
|
* Per-face perspective far plane (matches `PointLightShadow.farPlane`).
|
|
303
|
-
*
|
|
311
|
+
* It is consumed with the shadow atlas layer through the shared
|
|
312
|
+
* shadow-parameter buffer; DirectLightSlot carries only the layer identity.
|
|
304
313
|
*/
|
|
305
314
|
readonly shadowFar?: number;
|
|
306
315
|
}
|
|
@@ -324,8 +333,7 @@ export interface PointLightSnapshot {
|
|
|
324
333
|
* cube atlas faces and feed shader the proj constants without re-querying ECS.
|
|
325
334
|
* `shadowAtlasLayer` is assigned by extract in spawn order up to the renderer
|
|
326
335
|
* atlas capacity; over-budget requests retain the sentinel `-1` so the
|
|
327
|
-
* downstream
|
|
328
|
-
* pointPadW (T-M1-8 will rename to shadowAtlasLayer).
|
|
336
|
+
* downstream DirectLightSlot packing carries no invalid layer identity.
|
|
329
337
|
*/
|
|
330
338
|
export interface PointShadowSnapshot {
|
|
331
339
|
/** Spawning entity index (joins PointLight + PointLightShadow + Transform). */
|
|
@@ -339,7 +347,7 @@ export interface PointShadowSnapshot {
|
|
|
339
347
|
/**
|
|
340
348
|
* Atlas layer assigned by extract in spawn order while capacity remains.
|
|
341
349
|
* Sentinel -1 is used for a request that cannot be admitted and is
|
|
342
|
-
* reserved by the shader-side
|
|
350
|
+
* reserved by the shader-side DirectLightSlot metadata for no-shadow lights.
|
|
343
351
|
*/
|
|
344
352
|
readonly shadowAtlasLayer: number;
|
|
345
353
|
/**
|
|
@@ -378,6 +386,27 @@ export interface SpotLightSnapshot {
|
|
|
378
386
|
readonly invRangeSquared: number;
|
|
379
387
|
readonly cosInner: number;
|
|
380
388
|
readonly cosOuter: number;
|
|
389
|
+
/** Shared asset identities projected from the authored modifier fields. */
|
|
390
|
+
readonly iesProfileHandle?: number;
|
|
391
|
+
readonly cookieHandle?: number;
|
|
392
|
+
/** Resource-array slice identities; absent means the modifier falls back. */
|
|
393
|
+
readonly iesProfileSlice?: number;
|
|
394
|
+
readonly cookieSlice?: number;
|
|
395
|
+
/** Resolved authored payloads; absent means the renderer keeps identity LKG. */
|
|
396
|
+
readonly iesProfileData?: Uint8Array;
|
|
397
|
+
readonly cookieData?: Uint8Array;
|
|
398
|
+
/** Aspect-only UV matrix paired with `cookieSlice`; upload is dirty-ranged. */
|
|
399
|
+
readonly cookieMatrix?: Float32Array;
|
|
400
|
+
/**
|
|
401
|
+
* Legacy projector payload projected into the shared Cookie array lane when
|
|
402
|
+
* the adapter exposes the extended-lighting topology. The projector keeps
|
|
403
|
+
* its lightViewProj sampling semantics; only the texture transport is
|
|
404
|
+
* shared with authored Cookie resources.
|
|
405
|
+
*/
|
|
406
|
+
readonly projectorSlice?: number;
|
|
407
|
+
readonly projectorData?: Uint8Array;
|
|
408
|
+
readonly projectorMatrix?: Float32Array;
|
|
409
|
+
readonly rollDeg?: number;
|
|
381
410
|
// ── shadow fields (feat-20260625-spot-light-shadow-mapping M1) ──
|
|
382
411
|
readonly castShadow: boolean;
|
|
383
412
|
readonly lightViewProj: Float32Array | undefined;
|
|
@@ -398,20 +427,111 @@ export interface SpotLightSnapshot {
|
|
|
398
427
|
readonly projectorRevision?: number;
|
|
399
428
|
}
|
|
400
429
|
|
|
430
|
+
/** RectArea direct-light snapshot carried by the unified slot contract. */
|
|
431
|
+
export interface RectAreaDirectLightSnapshot {
|
|
432
|
+
readonly kind: 'rect-area';
|
|
433
|
+
readonly position: Vec3;
|
|
434
|
+
readonly color: Vec3;
|
|
435
|
+
readonly intensity: number;
|
|
436
|
+
readonly invRangeSquared: number;
|
|
437
|
+
readonly halfWidth: number;
|
|
438
|
+
readonly halfHeight: number;
|
|
439
|
+
readonly axisX: Vec3;
|
|
440
|
+
readonly axisY: Vec3;
|
|
441
|
+
readonly shadowAtlasTile?: number;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/** Normalized world-space frame used by Rect extraction, culling, and BRDF. */
|
|
445
|
+
export interface RectAreaWorldFrame {
|
|
446
|
+
readonly center: Vec3;
|
|
447
|
+
readonly axisX: Vec3;
|
|
448
|
+
readonly axisY: Vec3;
|
|
449
|
+
readonly normal: Vec3;
|
|
450
|
+
readonly halfWidth: number;
|
|
451
|
+
readonly halfHeight: number;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/** Build the single normalized Rect frame shared by direct and cluster paths. */
|
|
455
|
+
export function buildRectAreaWorldFrame(input: {
|
|
456
|
+
readonly center: ArrayLike<number>;
|
|
457
|
+
readonly axisX: ArrayLike<number>;
|
|
458
|
+
readonly axisY: ArrayLike<number>;
|
|
459
|
+
readonly width: number;
|
|
460
|
+
readonly height: number;
|
|
461
|
+
}): RectAreaWorldFrame {
|
|
462
|
+
const center = vec3.create(input.center[0] ?? 0, input.center[1] ?? 0, input.center[2] ?? 0);
|
|
463
|
+
const axisX = vec3.normalize(
|
|
464
|
+
vec3.create(),
|
|
465
|
+
vec3.create(input.axisX[0] ?? 0, input.axisX[1] ?? 0, input.axisX[2] ?? 0),
|
|
466
|
+
);
|
|
467
|
+
const axisY = vec3.normalize(
|
|
468
|
+
vec3.create(),
|
|
469
|
+
vec3.create(input.axisY[0] ?? 0, input.axisY[1] ?? 0, input.axisY[2] ?? 0),
|
|
470
|
+
);
|
|
471
|
+
const normal = vec3.normalize(vec3.create(), vec3.cross(vec3.create(), axisX, axisY));
|
|
472
|
+
normal[0] = normal[0] === 0 ? 0 : (normal[0] ?? 0);
|
|
473
|
+
normal[1] = normal[1] === 0 ? 0 : (normal[1] ?? 0);
|
|
474
|
+
normal[2] = normal[2] === 0 ? 0 : (normal[2] ?? 0);
|
|
475
|
+
return {
|
|
476
|
+
center,
|
|
477
|
+
axisX,
|
|
478
|
+
axisY,
|
|
479
|
+
normal,
|
|
480
|
+
halfWidth: Math.max(0, input.width * 0.5),
|
|
481
|
+
halfHeight: Math.max(0, input.height * 0.5),
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/** Return true only on the authored front side of a one-sided Rect. */
|
|
486
|
+
export function rectAreaFacesPoint(frame: RectAreaWorldFrame, point: ArrayLike<number>): boolean {
|
|
487
|
+
const toPoint = vec3.create(
|
|
488
|
+
(point[0] ?? 0) - (frame.center[0] ?? 0),
|
|
489
|
+
(point[1] ?? 0) - (frame.center[1] ?? 0),
|
|
490
|
+
(point[2] ?? 0) - (frame.center[2] ?? 0),
|
|
491
|
+
);
|
|
492
|
+
return vec3.dot(frame.normal, toPoint) > 0;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/** Project a world point onto the finite authored Rect boundary. */
|
|
496
|
+
export function closestPointOnRectArea(frame: RectAreaWorldFrame, point: ArrayLike<number>): Vec3 {
|
|
497
|
+
const toPoint = vec3.create(
|
|
498
|
+
(point[0] ?? 0) - (frame.center[0] ?? 0),
|
|
499
|
+
(point[1] ?? 0) - (frame.center[1] ?? 0),
|
|
500
|
+
(point[2] ?? 0) - (frame.center[2] ?? 0),
|
|
501
|
+
);
|
|
502
|
+
const localX = Math.min(
|
|
503
|
+
frame.halfWidth,
|
|
504
|
+
Math.max(-frame.halfWidth, vec3.dot(toPoint, frame.axisX)),
|
|
505
|
+
);
|
|
506
|
+
const localY = Math.min(
|
|
507
|
+
frame.halfHeight,
|
|
508
|
+
Math.max(-frame.halfHeight, vec3.dot(toPoint, frame.axisY)),
|
|
509
|
+
);
|
|
510
|
+
return vec3.create(
|
|
511
|
+
(frame.center[0] ?? 0) + (frame.axisX[0] ?? 0) * localX + (frame.axisY[0] ?? 0) * localY,
|
|
512
|
+
(frame.center[1] ?? 0) + (frame.axisX[1] ?? 0) * localX + (frame.axisY[1] ?? 0) * localY,
|
|
513
|
+
(frame.center[2] ?? 0) + (frame.axisX[2] ?? 0) * localX + (frame.axisY[2] ?? 0) * localY,
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
|
|
401
517
|
/**
|
|
402
|
-
* LightSnapshot — discriminated union of the
|
|
518
|
+
* LightSnapshot — discriminated union of the directional and punctual
|
|
403
519
|
* variants. AI users + the record stage perform an exhaustive switch on
|
|
404
520
|
* `kind`; missing arms are caught at compile time (no `default`,
|
|
405
521
|
* `assertNever`-style guards). Plan-strategy R-10.
|
|
406
522
|
*/
|
|
407
|
-
export type LightSnapshot =
|
|
523
|
+
export type LightSnapshot =
|
|
524
|
+
| DirectionalLightSnapshot
|
|
525
|
+
| PointLightSnapshot
|
|
526
|
+
| SpotLightSnapshot
|
|
527
|
+
| RectAreaDirectLightSnapshot;
|
|
408
528
|
|
|
409
529
|
/**
|
|
410
530
|
* ExtractedLights — three-bucket output of the extractFrame three-query
|
|
411
531
|
* union (M2 / w16 / AC-03). Plan-strategy section 3.1 EXT node:
|
|
412
532
|
* - `directional` — at most one (record-stage N>1 fail-fast lands in M3)
|
|
413
|
-
* - `point[]`
|
|
414
|
-
*
|
|
533
|
+
* - `point[]` / `spot[]` / `rect[]` — authored local-light snapshots; the
|
|
534
|
+
* record stage preserves this order in one bounded Cluster corpus
|
|
415
535
|
* `directionalCount` exposes the raw count of DirectionalLight entities the
|
|
416
536
|
* extract observed (so the record stage can fire `render-system-multi-light`
|
|
417
537
|
* when N>1 without re-running the query). M3 / w19 will widen this with
|
|
@@ -423,6 +543,7 @@ export interface ExtractedLights {
|
|
|
423
543
|
readonly directionalCount: number;
|
|
424
544
|
readonly point: readonly PointLightSnapshot[];
|
|
425
545
|
readonly spot: readonly SpotLightSnapshot[];
|
|
546
|
+
readonly rect: readonly RectAreaDirectLightSnapshot[];
|
|
426
547
|
/**
|
|
427
548
|
* feat-20260613-csm-cascaded-shadow-maps M2 / w9: per-cascade light-view-
|
|
428
549
|
* projection matrices (one per cascade, length 4 pre-allocated). Each matrix
|
|
@@ -482,7 +603,8 @@ export interface ExtractedLights {
|
|
|
482
603
|
* shadow-casting point lights (PointLight + PointLightShadow + Transform
|
|
483
604
|
* archetype join). Each entry carries the per-light 6-face VP matrices and
|
|
484
605
|
* the assigned cube_array atlas layer (0..3 in spawn order; sentinel -1 is
|
|
485
|
-
* reserved by the shader-side
|
|
606
|
+
* reserved by the shader-side DirectLightSlot metadata for no-shadow point
|
|
607
|
+
* lights).
|
|
486
608
|
*
|
|
487
609
|
* Empty array when no PointLightShadow components exist (zero-cost gate per
|
|
488
610
|
* AC-09; record stage skips atlas allocation + shadow pass dispatch).
|
|
@@ -542,6 +664,15 @@ export interface SkylightSnapshot {
|
|
|
542
664
|
readonly entityHandle: number;
|
|
543
665
|
}
|
|
544
666
|
|
|
667
|
+
/** Local diffuse probe facts; specular remains owned by Skylight/ReflectionProbe. */
|
|
668
|
+
export interface LightProbeSnapshot {
|
|
669
|
+
readonly identity: string;
|
|
670
|
+
readonly position: readonly [number, number, number];
|
|
671
|
+
readonly radius: number;
|
|
672
|
+
readonly irradiance: Float32Array;
|
|
673
|
+
readonly admitted: boolean;
|
|
674
|
+
}
|
|
675
|
+
|
|
545
676
|
/**
|
|
546
677
|
* SkyboxSnapshot -- extract-stage view of one SkyboxBackground entity
|
|
547
678
|
* (feat-20260531-skybox-env-background M2 / w5).
|
|
@@ -598,6 +729,9 @@ export interface ExtractedVolumetricFog {
|
|
|
598
729
|
export interface RenderableSnapshot {
|
|
599
730
|
readonly assetHandle: number;
|
|
600
731
|
readonly transform: TransformSnapshot;
|
|
732
|
+
/** Producer-authored lower-detail references projected from MeshAsset. */
|
|
733
|
+
readonly lods?: readonly MeshLodLevel[];
|
|
734
|
+
readonly lodHysteresis?: number;
|
|
601
735
|
/** Finite producer-owned local bounds retained for persistent CPU view work. */
|
|
602
736
|
readonly localAabb?: Float32Array;
|
|
603
737
|
/** Indexed single-submesh facts consumed by the first GPU-driven rigid lane. */
|
|
@@ -705,6 +839,8 @@ export interface RenderableSnapshot {
|
|
|
705
839
|
readonly temporal?: RenderableTemporalSnapshot;
|
|
706
840
|
/** Detached M2 authoring facts for the optional Points/Lines projection. */
|
|
707
841
|
readonly pointsLines?: PointsLinesRetainedSnapshot;
|
|
842
|
+
/** Renderer-owned per-object probe record uploaded at group(3) binding(1). */
|
|
843
|
+
readonly probeBlendRecord?: import('./scene/probe-blend-record').ProbeBlendRecord;
|
|
708
844
|
}
|
|
709
845
|
|
|
710
846
|
export type RenderablePreviousSource = 'last-submitted' | 'current-seed';
|
|
@@ -741,6 +877,16 @@ export interface GpuDrivenDrawSnapshot {
|
|
|
741
877
|
readonly topology: PrimitiveTopology;
|
|
742
878
|
readonly pipelineClass: string;
|
|
743
879
|
readonly materialResourceClass: string;
|
|
880
|
+
/** Producer-owned prepared ABI facts used by the Standard PBR GPU lane. */
|
|
881
|
+
readonly prepared?: import('./gpu-driven/prepared-draw').PreparedGpuDrivenDraw;
|
|
882
|
+
/** Optional projected height supplied by direct GPU-view evidence/tests. */
|
|
883
|
+
readonly projectedHeight?: number;
|
|
884
|
+
/** Root plus lower-detail index/vertex ranges, in LOD order. */
|
|
885
|
+
readonly lodRanges?: readonly {
|
|
886
|
+
readonly first: number;
|
|
887
|
+
readonly count: number;
|
|
888
|
+
readonly baseVertex: number;
|
|
889
|
+
}[];
|
|
744
890
|
}
|
|
745
891
|
|
|
746
892
|
export interface InstancesSnapshot {
|
|
@@ -793,7 +939,7 @@ export interface SpriteInstancesSnapshot {
|
|
|
793
939
|
/**
|
|
794
940
|
* TransformSnapshot: extract-stage view of one entity's resolved world
|
|
795
941
|
* transform (feat-20260601 D-3). Holds the single `world` mat4 (column-major
|
|
796
|
-
* 16 floats, copied from the entity's `
|
|
942
|
+
* 16 floats, copied from the entity's `GlobalTransform.world` view written by
|
|
797
943
|
* propagateTransforms). The record stage copies these 16 floats straight into
|
|
798
944
|
* the mesh SSBO slot (zero `mat4.compose`); position / scale consumers derive
|
|
799
945
|
* from the mat4 via `mat4.getTranslation` / basis-column lengths.
|
|
@@ -981,20 +1127,6 @@ export interface MaterialSnapshotCacheEntry {
|
|
|
981
1127
|
export type MaterialSnapshotCache = Map<number, MaterialSnapshotCacheEntry>;
|
|
982
1128
|
export type MaterialSnapshotCachesByWorld = WeakMap<World, MaterialSnapshotCache>;
|
|
983
1129
|
|
|
984
|
-
function gpuDrivenMaterialResourceClass(material: MaterialSnapshot): string {
|
|
985
|
-
const textures = [...(material.textureHandles?.entries() ?? [])]
|
|
986
|
-
.map(([name, handle]) => [name, Number(handle)] as const)
|
|
987
|
-
.sort(([left], [right]) => left.localeCompare(right));
|
|
988
|
-
const samplers = [...(material.samplerHandles?.entries() ?? [])]
|
|
989
|
-
.map(([name, handle]) => [name, Number(handle)] as const)
|
|
990
|
-
.sort(([left], [right]) => left.localeCompare(right));
|
|
991
|
-
return JSON.stringify({
|
|
992
|
-
textures,
|
|
993
|
-
samplers,
|
|
994
|
-
video: [...(material.videoTextureFields?.keys() ?? [])].sort(),
|
|
995
|
-
});
|
|
996
|
-
}
|
|
997
|
-
|
|
998
1130
|
// === DispatchEntry — M3 / w26 single dispatch list (feat-20260526-material-asset-multipass-renderstate) ===
|
|
999
1131
|
//
|
|
1000
1132
|
// Plan-strategy D-3: single dispatch list sorted by queue value,
|
|
@@ -1115,6 +1247,8 @@ export interface ExtractedFrame {
|
|
|
1115
1247
|
readonly dispatch: DispatchEntry[];
|
|
1116
1248
|
readonly skylight: SkylightSnapshot | undefined;
|
|
1117
1249
|
readonly skylightCount: number;
|
|
1250
|
+
/** World-local diffuse probe candidates; never a direct-light slot. */
|
|
1251
|
+
readonly lightProbes?: readonly LightProbeSnapshot[];
|
|
1118
1252
|
readonly skybox: SkyboxSnapshot | undefined;
|
|
1119
1253
|
readonly skyboxCount: number;
|
|
1120
1254
|
/** Resource-owner Fog facts selected once at the frame boundary. */
|
|
@@ -2449,21 +2583,33 @@ export interface PreparedExtractContext {
|
|
|
2449
2583
|
readonly resourceOwnerFog: FogFrame | null | undefined;
|
|
2450
2584
|
readonly cull: 'self' | 'none' | 'external';
|
|
2451
2585
|
readonly cullCameras: readonly CameraSnapshot[] | undefined;
|
|
2452
|
-
readonly
|
|
2586
|
+
readonly renderables: 'full' | 'none';
|
|
2453
2587
|
readonly visibility: VisibilitySnapshot;
|
|
2588
|
+
/** Producer-owned artifact lookup; extraction only, never record-time asset reads. */
|
|
2589
|
+
readonly getMaterialShaderArtifact:
|
|
2590
|
+
| ((materialShaderId: string) => MaterialShaderArtifact | undefined)
|
|
2591
|
+
| undefined;
|
|
2454
2592
|
}
|
|
2455
2593
|
|
|
2594
|
+
const ALL_VISIBLE: VisibilitySnapshot = {
|
|
2595
|
+
diagnostics: [],
|
|
2596
|
+
hasAnyIntent: false,
|
|
2597
|
+
hasAnyHiddenIntent: false,
|
|
2598
|
+
get: () => undefined,
|
|
2599
|
+
effective: () => 'visible',
|
|
2600
|
+
};
|
|
2601
|
+
|
|
2456
2602
|
function collectCameraSnapshots(world: World): CameraSnapshot[] {
|
|
2457
2603
|
const worldInternal = createWorldInternalView(world);
|
|
2458
2604
|
const cameras: CameraSnapshot[] = [];
|
|
2459
2605
|
const cameraQuery = world
|
|
2460
|
-
.query({ read: [Camera], optional: [MotionBlur], with: [Transform] })
|
|
2606
|
+
.query({ read: [Camera], optional: [MotionBlur], with: [Transform, GlobalTransform] })
|
|
2461
2607
|
.unwrap();
|
|
2462
2608
|
for (const row of cameraQuery) {
|
|
2463
2609
|
const cam = row.get(Camera);
|
|
2464
2610
|
const motionBlur = row.get(MotionBlur);
|
|
2465
2611
|
const entity = row.entity;
|
|
2466
|
-
const view = worldInternal._getArrayView(entity,
|
|
2612
|
+
const view = worldInternal._getArrayView(entity, GlobalTransform, 'world');
|
|
2467
2613
|
if (view === undefined) continue;
|
|
2468
2614
|
const worldMat = new Float32Array(view);
|
|
2469
2615
|
const target = resolveCameraTarget(world, cam.target);
|
|
@@ -2513,12 +2659,12 @@ function collectCameraSnapshots(world: World): CameraSnapshot[] {
|
|
|
2513
2659
|
function collectCubeCameraSnapshots(world: World): CubeCameraSnapshot[] {
|
|
2514
2660
|
const worldInternal = createWorldInternalView(world);
|
|
2515
2661
|
const snapshots: CubeCameraSnapshot[] = [];
|
|
2516
|
-
const query = world.query({ read: [CubeCamera], with: [Transform] }).unwrap();
|
|
2662
|
+
const query = world.query({ read: [CubeCamera], with: [Transform, GlobalTransform] }).unwrap();
|
|
2517
2663
|
for (const row of query) {
|
|
2518
2664
|
const camera = row.get(CubeCamera);
|
|
2519
2665
|
const target = resolveCameraTarget(world, camera.target);
|
|
2520
2666
|
if (target === undefined) continue;
|
|
2521
|
-
const view = worldInternal._getArrayView(row.entity,
|
|
2667
|
+
const view = worldInternal._getArrayView(row.entity, GlobalTransform, 'world');
|
|
2522
2668
|
if (view === undefined) continue;
|
|
2523
2669
|
snapshots.push({
|
|
2524
2670
|
entityKey: row.entity as number,
|
|
@@ -2537,10 +2683,12 @@ function collectCubeCameraSnapshots(world: World): CubeCameraSnapshot[] {
|
|
|
2537
2683
|
function collectReflectionProbeFacts(world: World): ReflectionProbeFact[] {
|
|
2538
2684
|
const worldInternal = createWorldInternalView(world);
|
|
2539
2685
|
const facts: ReflectionProbeFact[] = [];
|
|
2540
|
-
const query = world
|
|
2686
|
+
const query = world
|
|
2687
|
+
.query({ read: [ReflectionProbe], with: [Transform, GlobalTransform] })
|
|
2688
|
+
.unwrap();
|
|
2541
2689
|
for (const row of query) {
|
|
2542
2690
|
const probe = row.get(ReflectionProbe);
|
|
2543
|
-
const view = worldInternal._getArrayView(row.entity,
|
|
2691
|
+
const view = worldInternal._getArrayView(row.entity, GlobalTransform, 'world');
|
|
2544
2692
|
if (view === undefined) {
|
|
2545
2693
|
continue;
|
|
2546
2694
|
}
|
|
@@ -2627,9 +2775,13 @@ export function prepareExtractContext(
|
|
|
2627
2775
|
readonly resourceOwnerFog?: FogFrame | null;
|
|
2628
2776
|
readonly cull?: 'self' | 'none' | 'external';
|
|
2629
2777
|
readonly cullCameras?: readonly CameraSnapshot[];
|
|
2778
|
+
readonly renderables?: 'full' | 'none';
|
|
2779
|
+
readonly getMaterialShaderArtifact?: (
|
|
2780
|
+
materialShaderId: string,
|
|
2781
|
+
) => MaterialShaderArtifact | undefined;
|
|
2630
2782
|
} = {},
|
|
2631
2783
|
): PreparedExtractContext {
|
|
2632
|
-
const
|
|
2784
|
+
const renderables = options.renderables ?? 'full';
|
|
2633
2785
|
return {
|
|
2634
2786
|
assets: options.assets,
|
|
2635
2787
|
pipelineState: options.pipelineState,
|
|
@@ -2637,8 +2789,9 @@ export function prepareExtractContext(
|
|
|
2637
2789
|
resourceOwnerFog: options.resourceOwnerFog,
|
|
2638
2790
|
cull: options.cull ?? 'self',
|
|
2639
2791
|
cullCameras: options.cullCameras,
|
|
2640
|
-
|
|
2641
|
-
visibility: resolveVisibility(world
|
|
2792
|
+
renderables,
|
|
2793
|
+
visibility: renderables === 'none' ? ALL_VISIBLE : resolveVisibility(world),
|
|
2794
|
+
getMaterialShaderArtifact: options.getMaterialShaderArtifact,
|
|
2642
2795
|
};
|
|
2643
2796
|
}
|
|
2644
2797
|
|
|
@@ -2882,7 +3035,13 @@ export function extractFrames(
|
|
|
2882
3035
|
assets?: AssetRegistry | null,
|
|
2883
3036
|
pipelineState?: ExtractPipelineSurface | null,
|
|
2884
3037
|
materialSnapshotCachesByWorld?: MaterialSnapshotCachesByWorld,
|
|
2885
|
-
options: {
|
|
3038
|
+
options: {
|
|
3039
|
+
readonly cull?: 'normal' | 'none';
|
|
3040
|
+
readonly renderables?: 'full' | 'none';
|
|
3041
|
+
readonly getMaterialShaderArtifact?: (
|
|
3042
|
+
materialShaderId: string,
|
|
3043
|
+
) => MaterialShaderArtifact | undefined;
|
|
3044
|
+
} = {},
|
|
2886
3045
|
): ExtractedFrame {
|
|
2887
3046
|
// w4: normalize the owner argument. A bare number is the legacy single-owner
|
|
2888
3047
|
// form (cameraOwner === resourceOwner); an object carries the two split
|
|
@@ -2952,7 +3111,11 @@ export function extractFrames(
|
|
|
2952
3111
|
})(),
|
|
2953
3112
|
}),
|
|
2954
3113
|
cull: options.cull === 'none' ? 'none' : isCameraOwner ? 'self' : 'external',
|
|
3114
|
+
...(options.getMaterialShaderArtifact === undefined
|
|
3115
|
+
? {}
|
|
3116
|
+
: { getMaterialShaderArtifact: options.getMaterialShaderArtifact }),
|
|
2955
3117
|
...(cameraOwnerFrame === undefined ? {} : { cullCameras: cameraOwnerFrame.cameras }),
|
|
3118
|
+
renderables: options.renderables ?? 'full',
|
|
2956
3119
|
});
|
|
2957
3120
|
const frame = extractFrame(world, prepared);
|
|
2958
3121
|
|
|
@@ -3023,6 +3186,7 @@ export function extractFrames(
|
|
|
3023
3186
|
// frames); directionalCount sum.
|
|
3024
3187
|
const point: PointLightSnapshot[] = [];
|
|
3025
3188
|
const spot: SpotLightSnapshot[] = [];
|
|
3189
|
+
const rect: RectAreaDirectLightSnapshot[] = [];
|
|
3026
3190
|
let directional: DirectionalLightSnapshot | undefined;
|
|
3027
3191
|
let directionalCount = 0;
|
|
3028
3192
|
let lightViewProj: readonly Float32Array[] | undefined;
|
|
@@ -3044,6 +3208,7 @@ export function extractFrames(
|
|
|
3044
3208
|
for (const f of succeededFrames) {
|
|
3045
3209
|
for (const p of f.lights.point) point.push(p);
|
|
3046
3210
|
for (const s of f.lights.spot) spot.push(s);
|
|
3211
|
+
for (const r of f.lights.rect) rect.push(r);
|
|
3047
3212
|
for (const ps of f.lights.pointShadow) pointShadow.push(ps);
|
|
3048
3213
|
if (directional === undefined && f.lights.directional !== undefined) {
|
|
3049
3214
|
directional = f.lights.directional;
|
|
@@ -3127,6 +3292,7 @@ export function extractFrames(
|
|
|
3127
3292
|
directionalCount,
|
|
3128
3293
|
point,
|
|
3129
3294
|
spot,
|
|
3295
|
+
rect,
|
|
3130
3296
|
lightViewProj,
|
|
3131
3297
|
splitPlanes,
|
|
3132
3298
|
cascadeCount,
|
|
@@ -3148,6 +3314,7 @@ export function extractFrames(
|
|
|
3148
3314
|
const fog = resourceOwnerFrame?.fog;
|
|
3149
3315
|
const fogFailure = resourceOwnerFrame?.fogFailure;
|
|
3150
3316
|
const environment = resourceOwnerFrame?.environment;
|
|
3317
|
+
const lightProbes = succeededFrames.flatMap((frame) => frame.lightProbes ?? []);
|
|
3151
3318
|
// User-authored PostProcessParams entities are singleton scene resources, so
|
|
3152
3319
|
// they come from the resourceOwner world (holistic snapshot selection).
|
|
3153
3320
|
const postProcessParams = new Map(resourceOwnerFrame?.postProcessParams);
|
|
@@ -3204,6 +3371,7 @@ export function extractFrames(
|
|
|
3204
3371
|
dispatch: dispatchEntries,
|
|
3205
3372
|
skylight,
|
|
3206
3373
|
skylightCount,
|
|
3374
|
+
lightProbes,
|
|
3207
3375
|
skybox,
|
|
3208
3376
|
skyboxCount,
|
|
3209
3377
|
fog,
|
|
@@ -3298,6 +3466,7 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
3298
3466
|
materialSnapshotCache: persistentMaterialSnapshotCache,
|
|
3299
3467
|
cull: cullMode,
|
|
3300
3468
|
resourceOwnerFog,
|
|
3469
|
+
getMaterialShaderArtifact,
|
|
3301
3470
|
} = context;
|
|
3302
3471
|
// Environment reads are owned by the retained resource projection and are
|
|
3303
3472
|
// established at the frame boundary; record never revisits World state.
|
|
@@ -3310,7 +3479,7 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
3310
3479
|
const directionalLightQuery = world.query({ read: [DirectionalLight] }).unwrap();
|
|
3311
3480
|
|
|
3312
3481
|
// feat-20260601 D-3: camera / point / spot light world transforms are read
|
|
3313
|
-
// through the single resolved `
|
|
3482
|
+
// through the single resolved `GlobalTransform.world` mat4 (written by
|
|
3314
3483
|
// propagateTransforms), not the retired GlobalTransform-column-switch.
|
|
3315
3484
|
//
|
|
3316
3485
|
// Each segment routes through a World-owned Query with explicit read and
|
|
@@ -3411,22 +3580,22 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
3411
3580
|
const pointLightQuery = world
|
|
3412
3581
|
.query({
|
|
3413
3582
|
read: [PointLight],
|
|
3414
|
-
optional: [Transform],
|
|
3583
|
+
optional: [Transform, GlobalTransform],
|
|
3415
3584
|
})
|
|
3416
3585
|
.unwrap();
|
|
3417
3586
|
for (const row of pointLightQuery) {
|
|
3418
3587
|
const p = row.get(PointLight);
|
|
3419
3588
|
// K-2 scheme B: archetype-edge sniff -- `bundle.Transform` key is absent
|
|
3420
3589
|
// when the archetype does not carry the Transform column.
|
|
3421
|
-
const hasTransform = row.get(
|
|
3590
|
+
const hasTransform = row.get(GlobalTransform) !== undefined;
|
|
3422
3591
|
const intensity = p.intensity;
|
|
3423
3592
|
const range = p.range;
|
|
3424
3593
|
const entityId = row.entity;
|
|
3425
|
-
// Position = world-space translation extracted from
|
|
3594
|
+
// Position = world-space translation extracted from GlobalTransform.world.
|
|
3426
3595
|
// A point light archetype without a Transform column sits at the origin.
|
|
3427
3596
|
let worldMat: Float32Array | undefined;
|
|
3428
3597
|
if (hasTransform) {
|
|
3429
|
-
const view = worldInternal._getArrayView(entityId,
|
|
3598
|
+
const view = worldInternal._getArrayView(entityId, GlobalTransform, 'world');
|
|
3430
3599
|
if (view !== undefined) worldMat = new Float32Array(view);
|
|
3431
3600
|
}
|
|
3432
3601
|
const position =
|
|
@@ -3450,16 +3619,19 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
3450
3619
|
const spotLightQuery = world
|
|
3451
3620
|
.query({
|
|
3452
3621
|
read: [SpotLight],
|
|
3453
|
-
optional: [Transform],
|
|
3622
|
+
optional: [Transform, GlobalTransform],
|
|
3454
3623
|
})
|
|
3455
3624
|
.unwrap();
|
|
3456
3625
|
// feat-20260625-spot-light-shadow-mapping M1 w5: tile allocation for castShadow spots.
|
|
3457
3626
|
// Cap = 4 (OOS-5), sentinel -1 = unassigned (plan-strategy D-4).
|
|
3458
3627
|
// Direction degeneration (near-zero) also skips shadow (requirements $112).
|
|
3459
3628
|
let spotTileNext = 0;
|
|
3629
|
+
const iesSlices = new Map<number, number>();
|
|
3630
|
+
const cookieSlices = new Map<number, number>();
|
|
3631
|
+
const projectorSlices = new Map<number, number>();
|
|
3460
3632
|
for (const row of spotLightQuery) {
|
|
3461
3633
|
const s = row.get(SpotLight);
|
|
3462
|
-
const hasTransform = row.get(
|
|
3634
|
+
const hasTransform = row.get(GlobalTransform) !== undefined;
|
|
3463
3635
|
const intensity = s.intensity;
|
|
3464
3636
|
const range = s.range;
|
|
3465
3637
|
const innerConeDeg = s.innerConeDeg;
|
|
@@ -3467,7 +3639,7 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
3467
3639
|
let worldMat: Float32Array | undefined;
|
|
3468
3640
|
if (hasTransform) {
|
|
3469
3641
|
const entity = row.entity;
|
|
3470
|
-
const view = worldInternal._getArrayView(entity,
|
|
3642
|
+
const view = worldInternal._getArrayView(entity, GlobalTransform, 'world');
|
|
3471
3643
|
if (view !== undefined) worldMat = new Float32Array(view);
|
|
3472
3644
|
}
|
|
3473
3645
|
const position =
|
|
@@ -3505,13 +3677,93 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
3505
3677
|
const sMapSize = s.mapSize;
|
|
3506
3678
|
const sNearPlane = s.nearPlane;
|
|
3507
3679
|
const sFarPlane = s.farPlane;
|
|
3680
|
+
const iesProfileAsset =
|
|
3681
|
+
s.iesProfile === undefined
|
|
3682
|
+
? undefined
|
|
3683
|
+
: resolveAssetHandle<IesProfileAsset>(world, s.iesProfile);
|
|
3684
|
+
const cookieAsset =
|
|
3685
|
+
s.cookie === undefined ? undefined : resolveAssetHandle<TextureAsset>(world, s.cookie);
|
|
3686
|
+
// Resolve the projector before assigning array slices so the
|
|
3687
|
+
// extended-lighting path can reuse its texture transport while retaining
|
|
3688
|
+
// the authored lightViewProj projection semantics.
|
|
3689
|
+
const hasProjector = Number(s.projector) > 0;
|
|
3690
|
+
let projectorHandle: Handle<'TextureAsset', 'shared'> | undefined;
|
|
3691
|
+
let projectorAsset: TextureAsset | undefined;
|
|
3692
|
+
let projectorGuid: string | undefined;
|
|
3693
|
+
if (hasProjector) {
|
|
3694
|
+
const candidate = toShared<'TextureAsset'>(Math.round(Number(s.projector)));
|
|
3695
|
+
const resolvedProjector = resolveAssetHandle<TextureAsset>(world, candidate);
|
|
3696
|
+
if (!resolvedProjector.ok || resolvedProjector.value.kind !== 'texture') {
|
|
3697
|
+
worldInternal._routeError(
|
|
3698
|
+
resolvedProjector.ok
|
|
3699
|
+
? new RhiError({
|
|
3700
|
+
code: 'asset-not-registered',
|
|
3701
|
+
expected: 'SpotLight.projector resolves to a TextureAsset',
|
|
3702
|
+
hint: 'load the authored projector TextureAsset before assigning SpotLight.projector',
|
|
3703
|
+
detail: { assetHandle: Number(candidate) },
|
|
3704
|
+
})
|
|
3705
|
+
: resolvedProjector.error,
|
|
3706
|
+
{ severity: Severity.Error, systemName: 'RenderSystem.extract (spot-projector)' },
|
|
3707
|
+
);
|
|
3708
|
+
} else {
|
|
3709
|
+
projectorHandle = candidate;
|
|
3710
|
+
projectorAsset = resolvedProjector.value;
|
|
3711
|
+
projectorGuid = assets?.guidOf(projectorAsset) ?? `handle:${Number(candidate)}`;
|
|
3712
|
+
}
|
|
3713
|
+
}
|
|
3714
|
+
const iesProfileData =
|
|
3715
|
+
iesProfileAsset?.ok && iesProfileAsset.value.kind === 'ies-profile'
|
|
3716
|
+
? new Uint8Array(iesProfileAsset.value.data)
|
|
3717
|
+
: undefined;
|
|
3718
|
+
const cookieProjection =
|
|
3719
|
+
cookieAsset?.ok && cookieAsset.value.kind === 'texture'
|
|
3720
|
+
? prepareCookieProjection(cookieAsset.value)
|
|
3721
|
+
: undefined;
|
|
3722
|
+
const cookieData = cookieProjection?.data;
|
|
3723
|
+
const projectorProjection =
|
|
3724
|
+
projectorAsset === undefined ? undefined : prepareCookieProjection(projectorAsset);
|
|
3725
|
+
const projectorData = projectorProjection?.data;
|
|
3726
|
+
const iesProfileHandle = s.iesProfile === undefined ? undefined : handleSlot(s.iesProfile);
|
|
3727
|
+
const cookieHandle = s.cookie === undefined ? undefined : handleSlot(s.cookie);
|
|
3728
|
+
let iesProfileSlice =
|
|
3729
|
+
iesProfileHandle === undefined ? undefined : iesSlices.get(iesProfileHandle);
|
|
3730
|
+
if (
|
|
3731
|
+
iesProfileSlice === undefined &&
|
|
3732
|
+
iesProfileHandle !== undefined &&
|
|
3733
|
+
iesProfileData !== undefined &&
|
|
3734
|
+
iesSlices.size < IES_SLICE_CAPACITY
|
|
3735
|
+
) {
|
|
3736
|
+
iesProfileSlice = iesSlices.size;
|
|
3737
|
+
iesSlices.set(iesProfileHandle, iesProfileSlice);
|
|
3738
|
+
}
|
|
3739
|
+
let cookieSlice = cookieHandle === undefined ? undefined : cookieSlices.get(cookieHandle);
|
|
3740
|
+
if (
|
|
3741
|
+
cookieSlice === undefined &&
|
|
3742
|
+
cookieHandle !== undefined &&
|
|
3743
|
+
cookieData !== undefined &&
|
|
3744
|
+
cookieSlices.size < COOKIE_SLICE_CAPACITY
|
|
3745
|
+
) {
|
|
3746
|
+
cookieSlice = cookieSlices.size;
|
|
3747
|
+
cookieSlices.set(cookieHandle, cookieSlice);
|
|
3748
|
+
}
|
|
3749
|
+
let projectorSlice =
|
|
3750
|
+
projectorHandle === undefined
|
|
3751
|
+
? undefined
|
|
3752
|
+
: (projectorSlices.get(projectorHandle) ?? cookieSlices.get(projectorHandle));
|
|
3753
|
+
if (
|
|
3754
|
+
projectorSlice === undefined &&
|
|
3755
|
+
projectorHandle !== undefined &&
|
|
3756
|
+
projectorData !== undefined &&
|
|
3757
|
+
cookieSlices.size + projectorSlices.size < COOKIE_SLICE_CAPACITY
|
|
3758
|
+
) {
|
|
3759
|
+
projectorSlice = cookieSlices.size + projectorSlices.size;
|
|
3760
|
+
projectorSlices.set(projectorHandle, projectorSlice);
|
|
3761
|
+
}
|
|
3508
3762
|
const shadowIntensity = s.shadowIntensity;
|
|
3509
3763
|
// `shared<T>` columns are u32-backed and zero-filled when the optional
|
|
3510
3764
|
// field is absent. Treat only a positive handle as an authored projector;
|
|
3511
3765
|
// checking nullish values alone would turn the zero sentinel into a
|
|
3512
3766
|
// phantom projector matrix and an avoidable asset-not-found diagnostic.
|
|
3513
|
-
const hasProjector = Number(s.projector) > 0;
|
|
3514
|
-
|
|
3515
3767
|
let lightViewProj: Float32Array | undefined;
|
|
3516
3768
|
let shadowAtlasTile = -1;
|
|
3517
3769
|
|
|
@@ -3543,31 +3795,6 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
3543
3795
|
}
|
|
3544
3796
|
}
|
|
3545
3797
|
|
|
3546
|
-
let projectorHandle: Handle<'TextureAsset', 'shared'> | undefined;
|
|
3547
|
-
let projectorAsset: TextureAsset | undefined;
|
|
3548
|
-
let projectorGuid: string | undefined;
|
|
3549
|
-
if (hasProjector) {
|
|
3550
|
-
const candidate = toShared<'TextureAsset'>(Math.round(Number(s.projector)));
|
|
3551
|
-
const resolvedProjector = resolveAssetHandle<TextureAsset>(world, candidate);
|
|
3552
|
-
if (!resolvedProjector.ok || resolvedProjector.value.kind !== 'texture') {
|
|
3553
|
-
worldInternal._routeError(
|
|
3554
|
-
resolvedProjector.ok
|
|
3555
|
-
? new RhiError({
|
|
3556
|
-
code: 'asset-not-registered',
|
|
3557
|
-
expected: 'SpotLight.projector resolves to a TextureAsset',
|
|
3558
|
-
hint: 'load the authored projector TextureAsset before assigning SpotLight.projector',
|
|
3559
|
-
detail: { assetHandle: Number(candidate) },
|
|
3560
|
-
})
|
|
3561
|
-
: resolvedProjector.error,
|
|
3562
|
-
{ severity: Severity.Error, systemName: 'RenderSystem.extract (spot-projector)' },
|
|
3563
|
-
);
|
|
3564
|
-
} else {
|
|
3565
|
-
projectorHandle = candidate;
|
|
3566
|
-
projectorAsset = resolvedProjector.value;
|
|
3567
|
-
projectorGuid = assets?.guidOf(projectorAsset) ?? `handle:${Number(candidate)}`;
|
|
3568
|
-
}
|
|
3569
|
-
}
|
|
3570
|
-
|
|
3571
3798
|
spotSnapshots.push({
|
|
3572
3799
|
kind: 'spot',
|
|
3573
3800
|
entity: row.entity,
|
|
@@ -3584,6 +3811,25 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
3584
3811
|
invRangeSquared: computeInvRangeSquared(range),
|
|
3585
3812
|
cosInner: degToCos(innerConeDeg),
|
|
3586
3813
|
cosOuter: degToCos(outerConeDeg),
|
|
3814
|
+
...(s.iesProfile === undefined
|
|
3815
|
+
? {}
|
|
3816
|
+
: {
|
|
3817
|
+
iesProfileHandle: handleSlot(s.iesProfile),
|
|
3818
|
+
...(iesProfileSlice === undefined ? {} : { iesProfileSlice }),
|
|
3819
|
+
}),
|
|
3820
|
+
...(s.cookie === undefined
|
|
3821
|
+
? {}
|
|
3822
|
+
: {
|
|
3823
|
+
cookieHandle: handleSlot(s.cookie),
|
|
3824
|
+
...(cookieSlice === undefined ? {} : { cookieSlice }),
|
|
3825
|
+
}),
|
|
3826
|
+
...(iesProfileData === undefined ? {} : { iesProfileData }),
|
|
3827
|
+
...(cookieData === undefined ? {} : { cookieData }),
|
|
3828
|
+
...(cookieProjection === undefined ? {} : { cookieMatrix: cookieProjection.matrix }),
|
|
3829
|
+
...(projectorSlice === undefined ? {} : { projectorSlice }),
|
|
3830
|
+
...(projectorData === undefined ? {} : { projectorData }),
|
|
3831
|
+
...(projectorProjection === undefined ? {} : { projectorMatrix: projectorProjection.matrix }),
|
|
3832
|
+
rollDeg: s.rollDeg,
|
|
3587
3833
|
// ── shadow fields ──
|
|
3588
3834
|
castShadow,
|
|
3589
3835
|
lightViewProj,
|
|
@@ -3609,6 +3855,59 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
3609
3855
|
});
|
|
3610
3856
|
}
|
|
3611
3857
|
|
|
3858
|
+
const rectSnapshots: RectAreaDirectLightSnapshot[] = [];
|
|
3859
|
+
const rectLightQuery = world
|
|
3860
|
+
.query({
|
|
3861
|
+
read: [RectAreaLight],
|
|
3862
|
+
optional: [Transform, GlobalTransform],
|
|
3863
|
+
})
|
|
3864
|
+
.unwrap();
|
|
3865
|
+
for (const row of rectLightQuery) {
|
|
3866
|
+
const light = row.get(RectAreaLight);
|
|
3867
|
+
const validation = validateRectAreaLightData({
|
|
3868
|
+
intensity: light.intensity,
|
|
3869
|
+
color: light.color,
|
|
3870
|
+
width: light.width,
|
|
3871
|
+
height: light.height,
|
|
3872
|
+
range: light.range,
|
|
3873
|
+
});
|
|
3874
|
+
if (!validation.ok) {
|
|
3875
|
+
worldInternal._routeError(validation.error, {
|
|
3876
|
+
severity: Severity.Error,
|
|
3877
|
+
systemName: 'RenderSystem.extract (rect-area-light)',
|
|
3878
|
+
});
|
|
3879
|
+
continue;
|
|
3880
|
+
}
|
|
3881
|
+
const worldView = row.get(GlobalTransform);
|
|
3882
|
+
const worldMat =
|
|
3883
|
+
worldView === undefined
|
|
3884
|
+
? undefined
|
|
3885
|
+
: new Float32Array(worldInternal._getArrayView(row.entity, GlobalTransform, 'world') ?? []);
|
|
3886
|
+
const position =
|
|
3887
|
+
worldMat !== undefined ? mat4.getTranslation(vec3.create(), worldMat) : vec3.create(0, 0, 0);
|
|
3888
|
+
const frame = buildRectAreaWorldFrame({
|
|
3889
|
+
center: position,
|
|
3890
|
+
axisX: [worldMat?.[0] ?? 1, worldMat?.[1] ?? 0, worldMat?.[2] ?? 0],
|
|
3891
|
+
axisY: [worldMat?.[4] ?? 0, worldMat?.[5] ?? 1, worldMat?.[6] ?? 0],
|
|
3892
|
+
width: light.width,
|
|
3893
|
+
height: light.height,
|
|
3894
|
+
});
|
|
3895
|
+
rectSnapshots.push({
|
|
3896
|
+
kind: 'rect-area',
|
|
3897
|
+
position,
|
|
3898
|
+
color: vec3.create(
|
|
3899
|
+
(light.color[0] ?? 1) * light.intensity,
|
|
3900
|
+
(light.color[1] ?? 1) * light.intensity,
|
|
3901
|
+
(light.color[2] ?? 1) * light.intensity,
|
|
3902
|
+
),
|
|
3903
|
+
intensity: light.intensity,
|
|
3904
|
+
invRangeSquared: computeInvRangeSquared(light.range),
|
|
3905
|
+
halfWidth: frame.halfWidth,
|
|
3906
|
+
halfHeight: frame.halfHeight,
|
|
3907
|
+
axisX: frame.axisX,
|
|
3908
|
+
axisY: frame.axisY,
|
|
3909
|
+
});
|
|
3910
|
+
}
|
|
3612
3911
|
// The SpotLight is the sole projector owner. Once all light snapshots have
|
|
3613
3912
|
// been extracted, publish its accepted TextureAsset tuple on the volume
|
|
3614
3913
|
// snapshot so surface and volume consume the same identity and revision.
|
|
@@ -3752,12 +4051,11 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
3752
4051
|
const pointShadowSnapshots: PointShadowSnapshot[] = [];
|
|
3753
4052
|
{
|
|
3754
4053
|
const pointShadowQuery = world
|
|
3755
|
-
.query({ read: [Transform, PointLightShadow], with: [PointLight] })
|
|
4054
|
+
.query({ read: [Transform, GlobalTransform, PointLightShadow], with: [PointLight] })
|
|
3756
4055
|
.unwrap();
|
|
3757
4056
|
for (const row of pointShadowQuery) {
|
|
3758
|
-
const t = row.get(Transform);
|
|
3759
4057
|
const ps = row.get(PointLightShadow);
|
|
3760
|
-
// Read world-space position from
|
|
4058
|
+
// Read world-space position from GlobalTransform.world (mat4 column-major;
|
|
3761
4059
|
// translation lives at indices 12..14, mirroring CameraSnapshot.world
|
|
3762
4060
|
// semantics in this file).
|
|
3763
4061
|
// feat-20260614 M4 / w13: TypedArrayFor for `array<f32, 16>` now
|
|
@@ -3767,7 +4065,7 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
3767
4065
|
// `t.world?.[i]` form silently returned a single element under the
|
|
3768
4066
|
// `never`-typed bundle path and `wRow[12]` widened to `undefined ?? 0`
|
|
3769
4067
|
// so light positions clamped to the origin.
|
|
3770
|
-
const wRow =
|
|
4068
|
+
const wRow = row.get(GlobalTransform).world;
|
|
3771
4069
|
if (wRow === undefined) continue;
|
|
3772
4070
|
const px = wRow[12] ?? 0;
|
|
3773
4071
|
const py = wRow[13] ?? 0;
|
|
@@ -3808,7 +4106,7 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
3808
4106
|
// feat-20260612-point-light-shadows-urp-hdrp M4 / T-M4-4 (plan-strategy §D-8):
|
|
3809
4107
|
// join pointShadow snapshots into the matching PointLightSnapshot so the
|
|
3810
4108
|
// record stage threads `shadowAtlasLayer + shadowNear + shadowFar` through
|
|
3811
|
-
//
|
|
4109
|
+
// the unified direct-light metadata payload.
|
|
3812
4110
|
// Mutates the freshly-built PointLightSnapshot in place; the snapshot is
|
|
3813
4111
|
// not exposed elsewhere this frame yet (consumed only by lights.point[]).
|
|
3814
4112
|
if (pointShadowSnapshots.length > 0) {
|
|
@@ -3833,6 +4131,7 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
3833
4131
|
directionalCount,
|
|
3834
4132
|
point: pointSnapshots,
|
|
3835
4133
|
spot: spotSnapshots,
|
|
4134
|
+
rect: rectSnapshots,
|
|
3836
4135
|
lightViewProj,
|
|
3837
4136
|
splitPlanes: splitPlanes !== undefined ? paddedSplitPlanes : undefined,
|
|
3838
4137
|
cascadeCount,
|
|
@@ -3886,6 +4185,31 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
3886
4185
|
}
|
|
3887
4186
|
}
|
|
3888
4187
|
|
|
4188
|
+
const lightProbeQuery = world.query({ read: [LightProbe, GlobalTransform] }).unwrap();
|
|
4189
|
+
const lightProbes: LightProbeSnapshot[] = [];
|
|
4190
|
+
for (const row of lightProbeQuery) {
|
|
4191
|
+
const probe = row.get(LightProbe);
|
|
4192
|
+
const validation = validateLightProbeData({
|
|
4193
|
+
irradiance: probe.irradiance,
|
|
4194
|
+
radius: probe.radius,
|
|
4195
|
+
});
|
|
4196
|
+
if (!validation.ok) {
|
|
4197
|
+
worldInternal._routeError(validation.error, {
|
|
4198
|
+
severity: Severity.Error,
|
|
4199
|
+
systemName: 'RenderSystem.extract (light-probe)',
|
|
4200
|
+
});
|
|
4201
|
+
continue;
|
|
4202
|
+
}
|
|
4203
|
+
const transform = row.get(GlobalTransform);
|
|
4204
|
+
lightProbes.push({
|
|
4205
|
+
identity: String(row.entity),
|
|
4206
|
+
position: [transform.world[12] ?? 0, transform.world[13] ?? 0, transform.world[14] ?? 0],
|
|
4207
|
+
radius: probe.radius,
|
|
4208
|
+
irradiance: new Float32Array(probe.irradiance),
|
|
4209
|
+
admitted: true,
|
|
4210
|
+
});
|
|
4211
|
+
}
|
|
4212
|
+
|
|
3889
4213
|
// feat-20260531-skybox-env-background M2 / w5: query SkyboxBackground entities.
|
|
3890
4214
|
// First archetype hit wins (mirrors Skylight pattern); multi-entity
|
|
3891
4215
|
// once-warn in record stage uses skyboxCount.
|
|
@@ -4080,24 +4404,28 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
4080
4404
|
// column id for the sprite-bucket per-entity region override read
|
|
4081
4405
|
void SpriteRegionOverride;
|
|
4082
4406
|
|
|
4083
|
-
const meshRendererQuery =
|
|
4084
|
-
.
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4407
|
+
const meshRendererQuery =
|
|
4408
|
+
context.renderables === 'none'
|
|
4409
|
+
? []
|
|
4410
|
+
: world
|
|
4411
|
+
.query({
|
|
4412
|
+
read: [MeshRenderer],
|
|
4413
|
+
optional: [
|
|
4414
|
+
Transform,
|
|
4415
|
+
GlobalTransform,
|
|
4416
|
+
MeshFilter,
|
|
4417
|
+
Instances,
|
|
4418
|
+
Skin,
|
|
4419
|
+
Layer,
|
|
4420
|
+
MorphWeights,
|
|
4421
|
+
SpriteRegionOverride,
|
|
4422
|
+
SpriteInstances,
|
|
4423
|
+
Points,
|
|
4424
|
+
Lines,
|
|
4425
|
+
SortKey,
|
|
4426
|
+
],
|
|
4427
|
+
})
|
|
4428
|
+
.unwrap();
|
|
4101
4429
|
// QueryRow deliberately exposes component data, not an archetype version.
|
|
4102
4430
|
// The optimized ECS owner keeps table storage private, so instance-buffer
|
|
4103
4431
|
// invalidation uses a content fingerprint of the managed arrays instead.
|
|
@@ -4154,12 +4482,11 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
4154
4482
|
points !== undefined ? 'Points' : lines !== undefined ? 'Lines' : undefined;
|
|
4155
4483
|
const isRenderable = hasTransform && hasMeshFilter;
|
|
4156
4484
|
|
|
4157
|
-
//
|
|
4158
|
-
//
|
|
4159
|
-
//
|
|
4160
|
-
//
|
|
4161
|
-
//
|
|
4162
|
-
// "ChildOf but forgot GlobalTransform" misconfiguration cannot occur.
|
|
4485
|
+
// The resolved world transform is read per-entity from the single
|
|
4486
|
+
// `GlobalTransform.world` mat4 (propagateTransforms output) inside the row
|
|
4487
|
+
// loop below. Normal structural paths materialize this carrier through
|
|
4488
|
+
// Transform's generic ECS requirement; keep the defensive row check because
|
|
4489
|
+
// an owner can explicitly remove a required component during recovery.
|
|
4163
4490
|
const fAssetHandle = meshFilter?.assetHandle;
|
|
4164
4491
|
// feat-20260520-2d-sprite-layer-mvp M-3 / w22: Layer column read here;
|
|
4165
4492
|
// value folded into each DispatchEntry so the render-system sort can use
|
|
@@ -4251,6 +4578,7 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
4251
4578
|
let materialBindingDiagnostics: MeshMaterialBindingDiagnostic[] = [];
|
|
4252
4579
|
let gpuDrivenSubmeshes: readonly MeshAsset['submeshes'][number][] = [];
|
|
4253
4580
|
let gpuDrivenIndexed = false;
|
|
4581
|
+
let gpuDrivenGeometry: GpuDrivenGeometryReceipt | undefined;
|
|
4254
4582
|
|
|
4255
4583
|
// Resolve instance overrides against mesh-owned slot defaults once.
|
|
4256
4584
|
const fAssetHandleVal = fAssetHandle;
|
|
@@ -4264,8 +4592,20 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
4264
4592
|
const meshRes = resolveAssetHandle<Asset>(world, meshHandle);
|
|
4265
4593
|
if (meshRes.ok && meshRes.value.kind === 'mesh') {
|
|
4266
4594
|
const meshAsset = meshRes.value as MeshAsset;
|
|
4595
|
+
const guid = (meshRes.value as { guid?: string }).guid ?? '<no-guid>';
|
|
4267
4596
|
gpuDrivenSubmeshes = meshAsset.submeshes;
|
|
4268
4597
|
gpuDrivenIndexed = meshAsset.indices !== undefined;
|
|
4598
|
+
const layoutProjection = deriveVertexLayoutProjection(meshAsset.attributes);
|
|
4599
|
+
gpuDrivenGeometry = {
|
|
4600
|
+
identity: `${guid}:${layoutProjection.digest}`,
|
|
4601
|
+
vertexInputs: layoutProjection.attributes.map((attribute) => ({
|
|
4602
|
+
semantic: attribute.key,
|
|
4603
|
+
location: attribute.shaderLocation,
|
|
4604
|
+
format: attribute.format,
|
|
4605
|
+
})),
|
|
4606
|
+
topology: meshAsset.submeshes[0]?.topology ?? 'triangle-list',
|
|
4607
|
+
indexed: gpuDrivenIndexed,
|
|
4608
|
+
};
|
|
4269
4609
|
// extractFrames is also a public read path and can run before the
|
|
4270
4610
|
// Renderer.draw pre-render stage. GlyphText proves the derived mesh
|
|
4271
4611
|
// owns exactly one Default slot; arbitrary meshes still fail closed
|
|
@@ -4273,7 +4613,6 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
4273
4613
|
if (!Array.isArray(meshAsset.materialSlots) && world.get(entity, GlyphText).ok) {
|
|
4274
4614
|
ensureGlyphMeshMaterialSlots(world, meshHandle);
|
|
4275
4615
|
}
|
|
4276
|
-
const guid = (meshRes.value as { guid?: string }).guid ?? '<no-guid>';
|
|
4277
4616
|
const resolvedBindings = resolveMeshMaterialBindings(meshAsset, materialsView ?? [], {
|
|
4278
4617
|
isValidOverride(handle) {
|
|
4279
4618
|
const resolved = resolveAssetHandle(world, toShared<'MaterialAsset'>(handle));
|
|
@@ -4999,7 +5338,7 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
4999
5338
|
);
|
|
5000
5339
|
continue;
|
|
5001
5340
|
}
|
|
5002
|
-
// (c) Resolve only each joint's
|
|
5341
|
+
// (c) Resolve only each joint's GlobalTransform.world column. The
|
|
5003
5342
|
// transient view is consumed before a structural mutation and
|
|
5004
5343
|
// therefore preserves the same dangling-joint behavior without
|
|
5005
5344
|
// constructing every other Transform field.
|
|
@@ -5010,7 +5349,7 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
5010
5349
|
for (let jIdx = 0; jIdx < skeleton.jointCount; jIdx++) {
|
|
5011
5350
|
const jointEntityRaw = skinJoints[jIdx] ?? 0;
|
|
5012
5351
|
const jointEntity = jointEntityRaw as EntityHandle;
|
|
5013
|
-
const jointWorld = worldInternal._getArrayView(jointEntity,
|
|
5352
|
+
const jointWorld = worldInternal._getArrayView(jointEntity, GlobalTransform, 'world');
|
|
5014
5353
|
if (jointWorld === undefined) {
|
|
5015
5354
|
jointDangling = jIdx;
|
|
5016
5355
|
break;
|
|
@@ -5062,13 +5401,13 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
5062
5401
|
}
|
|
5063
5402
|
|
|
5064
5403
|
// feat-20260601 D-3: read the resolved world mat4 (propagateTransforms
|
|
5065
|
-
// output) straight from the
|
|
5404
|
+
// output) straight from the GlobalTransform.world column array view. The
|
|
5066
5405
|
// record stage copies these 16 floats into the mesh SSBO with zero
|
|
5067
5406
|
// per-snapshot `mat4.compose` (AC-07). A stale slot (generation gone)
|
|
5068
5407
|
// skips the renderable, mirroring the Instances dangling-row sweep.
|
|
5069
5408
|
// tweak-20260611 M1 / K-3: `_getArrayView` call survives untouched;
|
|
5070
5409
|
// only the `entity` source switched to `bundle.Entity.self[i]`.
|
|
5071
|
-
const worldView = worldInternal._getArrayView(entity,
|
|
5410
|
+
const worldView = worldInternal._getArrayView(entity, GlobalTransform, 'world');
|
|
5072
5411
|
if (worldView === undefined) continue;
|
|
5073
5412
|
const worldMat = new Float32Array(worldView);
|
|
5074
5413
|
const transformSnap: TransformSnapshot = { world: worldMat };
|
|
@@ -5199,12 +5538,16 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
5199
5538
|
}
|
|
5200
5539
|
|
|
5201
5540
|
let localAabb: Float32Array | undefined;
|
|
5541
|
+
let lods: readonly MeshLodLevel[] | undefined;
|
|
5542
|
+
let lodHysteresis: number | undefined;
|
|
5202
5543
|
let morph: MorphSnapshot | undefined;
|
|
5203
5544
|
const assetHandleRaw = Math.round(fAssetHandle ?? 0);
|
|
5204
5545
|
if (assetHandleRaw !== 0) {
|
|
5205
5546
|
const meshRes = resolveAssetHandle(world, toShared<'MeshAsset'>(assetHandleRaw));
|
|
5206
5547
|
if (meshRes.ok && meshRes.value.kind === 'mesh') {
|
|
5207
5548
|
const meshAsset = meshRes.value as MeshAsset;
|
|
5549
|
+
lods = meshAsset.lods;
|
|
5550
|
+
lodHysteresis = meshAsset.lodHysteresis;
|
|
5208
5551
|
morph = morphSnapshotFor(meshAsset, morphWeightsView);
|
|
5209
5552
|
const meshAabb = meshAsset.aabb;
|
|
5210
5553
|
// Morph targets can expand the authored bounds. Keep the
|
|
@@ -5287,34 +5630,16 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
5287
5630
|
}
|
|
5288
5631
|
}
|
|
5289
5632
|
|
|
5290
|
-
let nonIndexedFirst = 0;
|
|
5291
|
-
const gpuDrivenDraws = gpuDrivenSubmeshes.flatMap((submesh) => {
|
|
5292
|
-
const drawMaterial = materialsArr[submesh.materialSlot] ?? materialSnap;
|
|
5293
|
-
const first = gpuDrivenIndexed ? submesh.indexOffset : nonIndexedFirst;
|
|
5294
|
-
nonIndexedFirst += submesh.vertexCount;
|
|
5295
|
-
if (drawMaterial.transparent === true) return [];
|
|
5296
|
-
return [
|
|
5297
|
-
{
|
|
5298
|
-
kind: gpuDrivenIndexed ? ('indexed' as const) : ('non-indexed' as const),
|
|
5299
|
-
first,
|
|
5300
|
-
count: gpuDrivenIndexed ? submesh.indexCount : submesh.vertexCount,
|
|
5301
|
-
baseVertex: 0,
|
|
5302
|
-
materialSlot: submesh.materialSlot,
|
|
5303
|
-
topology: submesh.topology,
|
|
5304
|
-
pipelineClass: `${drawMaterial.materialShaderId ?? 'forgeax::default-unlit'}|${submesh.topology}|${JSON.stringify(drawMaterial.renderState ?? null)}`,
|
|
5305
|
-
materialResourceClass: gpuDrivenMaterialResourceClass(drawMaterial),
|
|
5306
|
-
},
|
|
5307
|
-
];
|
|
5308
|
-
});
|
|
5309
5633
|
const baseRenderable: RenderableSnapshot = {
|
|
5310
5634
|
assetHandle: Math.round(fAssetHandle ?? 0),
|
|
5311
5635
|
transform: transformSnap,
|
|
5636
|
+
...(lods === undefined ? {} : { lods }),
|
|
5637
|
+
...(lodHysteresis === undefined ? {} : { lodHysteresis }),
|
|
5312
5638
|
...(localAabb !== undefined ? { localAabb: cullingLocalAabb } : {}),
|
|
5313
5639
|
material: materialSnap,
|
|
5314
5640
|
materials: materialsArr,
|
|
5315
5641
|
materialBindingSources,
|
|
5316
5642
|
materialBindingDiagnostics,
|
|
5317
|
-
...(morph === undefined && gpuDrivenDraws.length > 0 ? { gpuDrivenDraws } : {}),
|
|
5318
5643
|
worldId: 0,
|
|
5319
5644
|
entityKey: entity,
|
|
5320
5645
|
...(skinSlice !== undefined ? { skin: skinSlice } : {}),
|
|
@@ -5323,6 +5648,39 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
5323
5648
|
...(spriteInstancesSnap !== undefined ? { spriteInstances: spriteInstancesSnap } : {}),
|
|
5324
5649
|
...(pointsLines !== undefined ? { pointsLines } : {}),
|
|
5325
5650
|
};
|
|
5651
|
+
const gpuDrivenDraws = buildGpuDrivenDraws({
|
|
5652
|
+
submeshes: gpuDrivenSubmeshes,
|
|
5653
|
+
indexed: gpuDrivenIndexed,
|
|
5654
|
+
materials: materialsArr,
|
|
5655
|
+
fallbackMaterial: materialSnap,
|
|
5656
|
+
...(gpuDrivenGeometry === undefined || getMaterialShaderArtifact === undefined
|
|
5657
|
+
? {}
|
|
5658
|
+
: {
|
|
5659
|
+
prepare(draw, drawMaterial) {
|
|
5660
|
+
const materialShaderId = drawMaterial.materialShaderId;
|
|
5661
|
+
if (materialShaderId === undefined) return undefined;
|
|
5662
|
+
const artifact = getMaterialShaderArtifact(materialShaderId);
|
|
5663
|
+
if (artifact?.receipt === undefined) return undefined;
|
|
5664
|
+
const geometry: GpuDrivenGeometryReceipt = {
|
|
5665
|
+
...gpuDrivenGeometry,
|
|
5666
|
+
topology: draw.topology,
|
|
5667
|
+
indexed: draw.kind === 'indexed',
|
|
5668
|
+
};
|
|
5669
|
+
const result = resolvePreparedGpuDrivenDraw({
|
|
5670
|
+
snapshot: { ...baseRenderable, material: drawMaterial },
|
|
5671
|
+
artifact,
|
|
5672
|
+
geometry,
|
|
5673
|
+
generation: artifact.receipt.generation,
|
|
5674
|
+
draw,
|
|
5675
|
+
});
|
|
5676
|
+
return result.ok ? result.value : undefined;
|
|
5677
|
+
},
|
|
5678
|
+
}),
|
|
5679
|
+
});
|
|
5680
|
+
const renderable =
|
|
5681
|
+
morph === undefined && gpuDrivenDraws.length > 0
|
|
5682
|
+
? { ...baseRenderable, gpuDrivenDraws }
|
|
5683
|
+
: baseRenderable;
|
|
5326
5684
|
|
|
5327
5685
|
// feat-20260528-frustum-culling M3 / w10: frustum culling check.
|
|
5328
5686
|
// Skip the entity if a valid AABB exists AND ALL cameras' frusta
|
|
@@ -5394,7 +5752,7 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
5394
5752
|
}
|
|
5395
5753
|
|
|
5396
5754
|
flushPendingDispatch(pendingDispatch);
|
|
5397
|
-
renderables.push(
|
|
5755
|
+
renderables.push(renderable);
|
|
5398
5756
|
}
|
|
5399
5757
|
|
|
5400
5758
|
// feat-20260520-2d-sprite-layer-mvp M-3 / w22 + w25: finalise the
|
|
@@ -5456,6 +5814,7 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
5456
5814
|
dispatch,
|
|
5457
5815
|
skylight,
|
|
5458
5816
|
skylightCount,
|
|
5817
|
+
lightProbes,
|
|
5459
5818
|
skybox,
|
|
5460
5819
|
skyboxCount,
|
|
5461
5820
|
fog,
|