@forgeax/engine-runtime 0.0.0-dev.8d955ade1c79
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/LICENSE +202 -0
- package/README.md +165 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/__tests__/active-camera.unit.test.d.ts +2 -0
- package/dist/__tests__/active-camera.unit.test.d.ts.map +1 -0
- package/dist/__tests__/animation-graph-add.unit.test.d.ts +2 -0
- package/dist/__tests__/animation-graph-add.unit.test.d.ts.map +1 -0
- package/dist/__tests__/animation-graph-asset-union.test-d.d.ts +2 -0
- package/dist/__tests__/animation-graph-asset-union.test-d.d.ts.map +1 -0
- package/dist/__tests__/animation-graph-blend.unit.test.d.ts +2 -0
- package/dist/__tests__/animation-graph-blend.unit.test.d.ts.map +1 -0
- package/dist/__tests__/animation-graph-build.unit.test.d.ts +2 -0
- package/dist/__tests__/animation-graph-build.unit.test.d.ts.map +1 -0
- package/dist/__tests__/animation-graph-clip-missing.unit.test.d.ts +2 -0
- package/dist/__tests__/animation-graph-clip-missing.unit.test.d.ts.map +1 -0
- package/dist/__tests__/animation-graph-clip.unit.test.d.ts +2 -0
- package/dist/__tests__/animation-graph-clip.unit.test.d.ts.map +1 -0
- package/dist/__tests__/animation-graph-collect-roundtrip.test.d.ts +2 -0
- package/dist/__tests__/animation-graph-collect-roundtrip.test.d.ts.map +1 -0
- package/dist/__tests__/animation-graph-default-path.integration.test.d.ts +2 -0
- package/dist/__tests__/animation-graph-default-path.integration.test.d.ts.map +1 -0
- package/dist/__tests__/animation-graph-describe.unit.test.d.ts +2 -0
- package/dist/__tests__/animation-graph-describe.unit.test.d.ts.map +1 -0
- package/dist/__tests__/animation-graph-errors.unit.test.d.ts +2 -0
- package/dist/__tests__/animation-graph-errors.unit.test.d.ts.map +1 -0
- package/dist/__tests__/animation-graph-nesting.unit.test.d.ts +2 -0
- package/dist/__tests__/animation-graph-nesting.unit.test.d.ts.map +1 -0
- package/dist/__tests__/animation-graph-orthogonal-weight.unit.test.d.ts +2 -0
- package/dist/__tests__/animation-graph-orthogonal-weight.unit.test.d.ts.map +1 -0
- package/dist/__tests__/animation-graph-serialize.unit.test.d.ts +2 -0
- package/dist/__tests__/animation-graph-serialize.unit.test.d.ts.map +1 -0
- package/dist/__tests__/animation-graph-slot-fill.unit.test.d.ts +2 -0
- package/dist/__tests__/animation-graph-slot-fill.unit.test.d.ts.map +1 -0
- package/dist/__tests__/animation-nslot.unit.test.d.ts +2 -0
- package/dist/__tests__/animation-nslot.unit.test.d.ts.map +1 -0
- package/dist/__tests__/animation-slot-length-guard.unit.test.d.ts +2 -0
- package/dist/__tests__/animation-slot-length-guard.unit.test.d.ts.map +1 -0
- package/dist/__tests__/append-injection.test.d.ts +2 -0
- package/dist/__tests__/append-injection.test.d.ts.map +1 -0
- package/dist/__tests__/asi-world-baseline-export.unit.test.d.ts +2 -0
- package/dist/__tests__/asi-world-baseline-export.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asi-world-ci-config.unit.test.d.ts +2 -0
- package/dist/__tests__/asi-world-ci-config.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asi-world-smoke.unit.test.d.ts +2 -0
- package/dist/__tests__/asi-world-smoke.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-brand-and-name-retirement.test.d.ts +2 -0
- package/dist/__tests__/asset-brand-and-name-retirement.test.d.ts.map +1 -0
- package/dist/__tests__/asset-error-exhaustive.test-d.d.ts +11 -0
- package/dist/__tests__/asset-error-exhaustive.test-d.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-guid-reverse.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-guid-reverse.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-hdr-equirect.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-hdr-equirect.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-mesh-pack-payload.dawn.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-mesh-pack-payload.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-mounts.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-mounts.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-scene.test-d.d.ts +2 -0
- package/dist/__tests__/asset-registry-scene.test-d.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.browser.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.browser.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.dawn.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.listCatalog.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.listCatalog.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.mesh-bin.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.mesh-bin.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.pack-cache.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.pack-cache.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.raw-container-failfast.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.raw-container-failfast.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.recursive.spec.d.ts +2 -0
- package/dist/__tests__/asset-registry.recursive.spec.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset.unit.test.d.ts +2 -0
- package/dist/__tests__/asset.unit.test.d.ts.map +1 -0
- package/dist/__tests__/audio-load-by-guid.unit.test.d.ts +2 -0
- package/dist/__tests__/audio-load-by-guid.unit.test.d.ts.map +1 -0
- package/dist/__tests__/backend-selection.integration.test.d.ts +2 -0
- package/dist/__tests__/backend-selection.integration.test.d.ts.map +1 -0
- package/dist/__tests__/basis-catalog-dispatch.integration.test.d.ts +2 -0
- package/dist/__tests__/basis-catalog-dispatch.integration.test.d.ts.map +1 -0
- package/dist/__tests__/bind-group-cache-keying.test.d.ts +2 -0
- package/dist/__tests__/bind-group-cache-keying.test.d.ts.map +1 -0
- package/dist/__tests__/block-upload-math.unit.test.d.ts +2 -0
- package/dist/__tests__/block-upload-math.unit.test.d.ts.map +1 -0
- package/dist/__tests__/builtin-asset-registry.unit.test.d.ts +2 -0
- package/dist/__tests__/builtin-asset-registry.unit.test.d.ts.map +1 -0
- package/dist/__tests__/builtin-shader-register-e2e.test.d.ts +2 -0
- package/dist/__tests__/builtin-shader-register-e2e.test.d.ts.map +1 -0
- package/dist/__tests__/cache-false-eviction.test.d.ts +2 -0
- package/dist/__tests__/cache-false-eviction.test.d.ts.map +1 -0
- package/dist/__tests__/cache-isolation-matrix.test.d.ts +2 -0
- package/dist/__tests__/cache-isolation-matrix.test.d.ts.map +1 -0
- package/dist/__tests__/camera-clear-snapshot-equivalence.unit.test.d.ts +2 -0
- package/dist/__tests__/camera-clear-snapshot-equivalence.unit.test.d.ts.map +1 -0
- package/dist/__tests__/clamp-to-last.e2e.browser.test.d.ts +2 -0
- package/dist/__tests__/clamp-to-last.e2e.browser.test.d.ts.map +1 -0
- package/dist/__tests__/clamp-to-last.e2e.dawn.test.d.ts +2 -0
- package/dist/__tests__/clamp-to-last.e2e.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/collect-scene-asset.test.d.ts +2 -0
- package/dist/__tests__/collect-scene-asset.test.d.ts.map +1 -0
- package/dist/__tests__/collect-scene-mount-collapse.test.d.ts +2 -0
- package/dist/__tests__/collect-scene-mount-collapse.test.d.ts.map +1 -0
- package/dist/__tests__/collect-subtree.unit.test.d.ts +2 -0
- package/dist/__tests__/collect-subtree.unit.test.d.ts.map +1 -0
- package/dist/__tests__/collect-transient-children-absent.test.d.ts +2 -0
- package/dist/__tests__/collect-transient-children-absent.test.d.ts.map +1 -0
- package/dist/__tests__/collect-transient-roundtrip.test.d.ts +2 -0
- package/dist/__tests__/collect-transient-roundtrip.test.d.ts.map +1 -0
- package/dist/__tests__/collect-transient-skip.test.d.ts +2 -0
- package/dist/__tests__/collect-transient-skip.test.d.ts.map +1 -0
- package/dist/__tests__/components.test-d.d.ts +2 -0
- package/dist/__tests__/components.test-d.d.ts.map +1 -0
- package/dist/__tests__/components.unit.test.d.ts +2 -0
- package/dist/__tests__/components.unit.test.d.ts.map +1 -0
- package/dist/__tests__/composite-skybox-cross-world.browser.test.d.ts +2 -0
- package/dist/__tests__/composite-skybox-cross-world.browser.test.d.ts.map +1 -0
- package/dist/__tests__/compressed-texture-physical-extent.integration.test.d.ts +2 -0
- package/dist/__tests__/compressed-texture-physical-extent.integration.test.d.ts.map +1 -0
- package/dist/__tests__/create-renderer-assembly.integration.test.d.ts +2 -0
- package/dist/__tests__/create-renderer-assembly.integration.test.d.ts.map +1 -0
- package/dist/__tests__/createRenderer-dispose.test.d.ts +2 -0
- package/dist/__tests__/createRenderer-dispose.test.d.ts.map +1 -0
- package/dist/__tests__/createRenderer-requiredFeatures.unit.test.d.ts +2 -0
- package/dist/__tests__/createRenderer-requiredFeatures.unit.test.d.ts.map +1 -0
- package/dist/__tests__/createRenderer.browser-no-webgpu.test.d.ts +2 -0
- package/dist/__tests__/createRenderer.browser-no-webgpu.test.d.ts.map +1 -0
- package/dist/__tests__/createRenderer.unit.test.d.ts +2 -0
- package/dist/__tests__/createRenderer.unit.test.d.ts.map +1 -0
- package/dist/__tests__/dawn/hello-sprite-nineslice-section.dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/hello-sprite-nineslice-section.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/dawn/ibl-compose.dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/ibl-compose.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/dawn/ibl-modules-compose.dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/ibl-modules-compose.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/dawn/ibl-precompute-readback.dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/ibl-precompute-readback.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/dawn/instances-per-instance-pbr.dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/instances-per-instance-pbr.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/dawn/instances-shadow.dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/instances-shadow.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/dawn/instances-uniform-fallback.dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/instances-uniform-fallback.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/dawn/material-alpha.dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/material-alpha.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/dawn/material-cooked-fixture.dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/material-cooked-fixture.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/dawn/mesh-ssbo-normal-matrix.dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/mesh-ssbo-normal-matrix.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/dawn/mesh-update-dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/mesh-update-dawn.test.d.ts.map +1 -0
- package/dist/__tests__/dawn/mipmap-srgb-linear.dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/mipmap-srgb-linear.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/dawn/per-entity-material-texture-isolation.dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/per-entity-material-texture-isolation.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/dawn/sprite-nineslice-mesh-bind.dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/sprite-nineslice-mesh-bind.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/dawn/tonemap.dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/tonemap.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/dawn/upload-texture.dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/upload-texture.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/dawn/user-handle-mesh-render.dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/user-handle-mesh-render.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/dawn/user-mesh-upload.dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/user-mesh-upload.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/dawn/zero-manifest-mesh-fail.dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/zero-manifest-mesh-fail.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/dawn/zero-manifest-ready.dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/zero-manifest-ready.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/demo-sortscope-migration.unit.test.d.ts +2 -0
- package/dist/__tests__/demo-sortscope-migration.unit.test.d.ts.map +1 -0
- package/dist/__tests__/derive-bgl-integration.test.d.ts +2 -0
- package/dist/__tests__/derive-bgl-integration.test.d.ts.map +1 -0
- package/dist/__tests__/derive-render-data-skin.unit.test.d.ts +2 -0
- package/dist/__tests__/derive-render-data-skin.unit.test.d.ts.map +1 -0
- package/dist/__tests__/draw-published.d.ts +5 -0
- package/dist/__tests__/draw-published.d.ts.map +1 -0
- package/dist/__tests__/dynamic-texture-store.unit.test.d.ts +2 -0
- package/dist/__tests__/dynamic-texture-store.unit.test.d.ts.map +1 -0
- package/dist/__tests__/environment-error-exhaustive.test-d.d.ts +8 -0
- package/dist/__tests__/environment-error-exhaustive.test-d.d.ts.map +1 -0
- package/dist/__tests__/equirect-bc6h.integration.test.d.ts +2 -0
- package/dist/__tests__/equirect-bc6h.integration.test.d.ts.map +1 -0
- package/dist/__tests__/error-exhaustive.test.d.ts +2 -0
- package/dist/__tests__/error-exhaustive.test.d.ts.map +1 -0
- package/dist/__tests__/errors.unit.test.d.ts +2 -0
- package/dist/__tests__/errors.unit.test.d.ts.map +1 -0
- package/dist/__tests__/extract-frames-graceful-degradation.test.d.ts +2 -0
- package/dist/__tests__/extract-frames-graceful-degradation.test.d.ts.map +1 -0
- package/dist/__tests__/extract-frames-merge.test.d.ts +2 -0
- package/dist/__tests__/extract-frames-merge.test.d.ts.map +1 -0
- package/dist/__tests__/extract-frames-reset.test.d.ts +2 -0
- package/dist/__tests__/extract-frames-reset.test.d.ts.map +1 -0
- package/dist/__tests__/extract-record-no-hardcoded-texture-fields.test.d.ts +2 -0
- package/dist/__tests__/extract-record-no-hardcoded-texture-fields.test.d.ts.map +1 -0
- package/dist/__tests__/extract-skin-mismatch.unit.test.d.ts +2 -0
- package/dist/__tests__/extract-skin-mismatch.unit.test.d.ts.map +1 -0
- package/dist/__tests__/fetch-binary-decompress.test.d.ts +2 -0
- package/dist/__tests__/fetch-binary-decompress.test.d.ts.map +1 -0
- package/dist/__tests__/four-path-compression.integration.test.d.ts +2 -0
- package/dist/__tests__/four-path-compression.integration.test.d.ts.map +1 -0
- package/dist/__tests__/frame-targets-shadow-regression.unit.test.d.ts +2 -0
- package/dist/__tests__/frame-targets-shadow-regression.unit.test.d.ts.map +1 -0
- package/dist/__tests__/fullscreen-feature-host.dawn.test.d.ts +2 -0
- package/dist/__tests__/fullscreen-feature-host.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/fullscreen-post-bgl-depth.test.d.ts +2 -0
- package/dist/__tests__/fullscreen-post-bgl-depth.test.d.ts.map +1 -0
- package/dist/__tests__/fullscreen-post-depth-params-arity.test.d.ts +2 -0
- package/dist/__tests__/fullscreen-post-depth-params-arity.test.d.ts.map +1 -0
- package/dist/__tests__/fullscreen-post-process-pass.dawn.test.d.ts +2 -0
- package/dist/__tests__/fullscreen-post-process-pass.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/fullscreen-post-process-pass.unit.test.d.ts +2 -0
- package/dist/__tests__/fullscreen-post-process-pass.unit.test.d.ts.map +1 -0
- package/dist/__tests__/fxaa-pixel-diff.dawn.test.d.ts +2 -0
- package/dist/__tests__/fxaa-pixel-diff.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/fxaa-validation-and-resize.dawn.test.d.ts +2 -0
- package/dist/__tests__/fxaa-validation-and-resize.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/fxaa-zero-overhead.dawn.test.d.ts +2 -0
- package/dist/__tests__/fxaa-zero-overhead.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/geometry.unit.test.d.ts +2 -0
- package/dist/__tests__/geometry.unit.test.d.ts.map +1 -0
- package/dist/__tests__/glyph-tilemap-worldid.test.d.ts +2 -0
- package/dist/__tests__/glyph-tilemap-worldid.test.d.ts.map +1 -0
- package/dist/__tests__/gpu-resource-cow-survivor.test.d.ts +2 -0
- package/dist/__tests__/gpu-resource-cow-survivor.test.d.ts.map +1 -0
- package/dist/__tests__/guid-for-asset.unit.test.d.ts +2 -0
- package/dist/__tests__/guid-for-asset.unit.test.d.ts.map +1 -0
- package/dist/__tests__/handle-migration.test.d.ts +2 -0
- package/dist/__tests__/handle-migration.test.d.ts.map +1 -0
- package/dist/__tests__/hello-tilemap-smoke-upgrade.unit.test.d.ts +2 -0
- package/dist/__tests__/hello-tilemap-smoke-upgrade.unit.test.d.ts.map +1 -0
- package/dist/__tests__/helpers/mock-shader-registry.d.ts +11 -0
- package/dist/__tests__/helpers/mock-shader-registry.d.ts.map +1 -0
- package/dist/__tests__/helpers/register-runtime-components.d.ts +4 -0
- package/dist/__tests__/helpers/register-runtime-components.d.ts.map +1 -0
- package/dist/__tests__/helpers/register-scene-components.d.ts +4 -0
- package/dist/__tests__/helpers/register-scene-components.d.ts.map +1 -0
- package/dist/__tests__/helpers/tilemap-assets.d.ts +6 -0
- package/dist/__tests__/helpers/tilemap-assets.d.ts.map +1 -0
- package/dist/__tests__/host-custom-kind-contract.test-d.d.ts +2 -0
- package/dist/__tests__/host-custom-kind-contract.test-d.d.ts.map +1 -0
- package/dist/__tests__/host-custom-kind-contract.test.d.ts +2 -0
- package/dist/__tests__/host-custom-kind-contract.test.d.ts.map +1 -0
- package/dist/__tests__/inspect-name.test.d.ts +2 -0
- package/dist/__tests__/inspect-name.test.d.ts.map +1 -0
- package/dist/__tests__/instances.test-d.d.ts +2 -0
- package/dist/__tests__/instances.test-d.d.ts.map +1 -0
- package/dist/__tests__/instantiate-mount-deferred-wiring.test.d.ts +2 -0
- package/dist/__tests__/instantiate-mount-deferred-wiring.test.d.ts.map +1 -0
- package/dist/__tests__/light-attenuation-formula.test.d.ts +2 -0
- package/dist/__tests__/light-attenuation-formula.test.d.ts.map +1 -0
- package/dist/__tests__/light-casters-9-light.browser.test.d.ts +2 -0
- package/dist/__tests__/light-casters-9-light.browser.test.d.ts.map +1 -0
- package/dist/__tests__/light-gpu-byte-neutral.unit.test.d.ts +2 -0
- package/dist/__tests__/light-gpu-byte-neutral.unit.test.d.ts.map +1 -0
- package/dist/__tests__/light-snapshot.test-d.d.ts +2 -0
- package/dist/__tests__/light-snapshot.test-d.d.ts.map +1 -0
- package/dist/__tests__/lights-vec-negative.unit.test.d.ts +2 -0
- package/dist/__tests__/lights-vec-negative.unit.test.d.ts.map +1 -0
- package/dist/__tests__/lights.unit.test.d.ts +2 -0
- package/dist/__tests__/lights.unit.test.d.ts.map +1 -0
- package/dist/__tests__/loadbyguid-payload.unit.test.d.ts +2 -0
- package/dist/__tests__/loadbyguid-payload.unit.test.d.ts.map +1 -0
- package/dist/__tests__/loadbyguid-texture-intern.unit.test.d.ts +2 -0
- package/dist/__tests__/loadbyguid-texture-intern.unit.test.d.ts.map +1 -0
- package/dist/__tests__/loader-extract-graceful.test.d.ts +2 -0
- package/dist/__tests__/loader-extract-graceful.test.d.ts.map +1 -0
- package/dist/__tests__/loader-name-survival.test.d.ts +2 -0
- package/dist/__tests__/loader-name-survival.test.d.ts.map +1 -0
- package/dist/__tests__/m3-vec-negative.unit.test.d.ts +2 -0
- package/dist/__tests__/m3-vec-negative.unit.test.d.ts.map +1 -0
- package/dist/__tests__/m5-browser-dawn-sweep.integration.test.d.ts +2 -0
- package/dist/__tests__/m5-browser-dawn-sweep.integration.test.d.ts.map +1 -0
- package/dist/__tests__/material-texture-uv-scale.unit.test.d.ts +2 -0
- package/dist/__tests__/material-texture-uv-scale.unit.test.d.ts.map +1 -0
- package/dist/__tests__/materials.unit.test.d.ts +2 -0
- package/dist/__tests__/materials.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin-contract.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin-contract.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin-skin-roundtrip.unit.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin-skin-roundtrip.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-loader-skin-contract.unit.test.d.ts +2 -0
- package/dist/__tests__/mesh-loader-skin-contract.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-ssbo-grow.unit.test.d.ts +2 -0
- package/dist/__tests__/mesh-ssbo-grow.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mip-gate.unit.test.d.ts +2 -0
- package/dist/__tests__/mip-gate.unit.test.d.ts.map +1 -0
- package/dist/__tests__/msaa-sprite-pixel-diff.dawn.test.d.ts +2 -0
- package/dist/__tests__/msaa-sprite-pixel-diff.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/multi-uv-8set.e2e.dawn.test.d.ts +2 -0
- package/dist/__tests__/multi-uv-8set.e2e.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/name.unit.test.d.ts +2 -0
- package/dist/__tests__/name.unit.test.d.ts.map +1 -0
- package/dist/__tests__/new-kind-refs-contract.test.d.ts +2 -0
- package/dist/__tests__/new-kind-refs-contract.test.d.ts.map +1 -0
- package/dist/__tests__/package-promotion.test.d.ts +2 -0
- package/dist/__tests__/package-promotion.test.d.ts.map +1 -0
- package/dist/__tests__/package-rename.test.d.ts +2 -0
- package/dist/__tests__/package-rename.test.d.ts.map +1 -0
- package/dist/__tests__/pass-selector.unit.test.d.ts +2 -0
- package/dist/__tests__/pass-selector.unit.test.d.ts.map +1 -0
- package/dist/__tests__/pbr-pipeline.unit.test.d.ts +2 -0
- package/dist/__tests__/pbr-pipeline.unit.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline-builder-msaa.unit.test.d.ts +2 -0
- package/dist/__tests__/pipeline-builder-msaa.unit.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline-cache-keying.unit.test.d.ts +2 -0
- package/dist/__tests__/pipeline-cache-keying.unit.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline-spec.test.d.ts +2 -0
- package/dist/__tests__/pipeline-spec.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline.unit.test.d.ts +2 -0
- package/dist/__tests__/pipeline.unit.test.d.ts.map +1 -0
- package/dist/__tests__/point-light-shadow.browser.test.d.ts +2 -0
- package/dist/__tests__/point-light-shadow.browser.test.d.ts.map +1 -0
- package/dist/__tests__/point-light-shadow.dawn.test.d.ts +2 -0
- package/dist/__tests__/point-light-shadow.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/point-light-shadow.unit.test.d.ts +2 -0
- package/dist/__tests__/point-light-shadow.unit.test.d.ts.map +1 -0
- package/dist/__tests__/post-process-error-codes.unit.test.d.ts +2 -0
- package/dist/__tests__/post-process-error-codes.unit.test.d.ts.map +1 -0
- package/dist/__tests__/post-process-params-component.unit.test.d.ts +2 -0
- package/dist/__tests__/post-process-params-component.unit.test.d.ts.map +1 -0
- package/dist/__tests__/post-process-params-extract.unit.test.d.ts +2 -0
- package/dist/__tests__/post-process-params-extract.unit.test.d.ts.map +1 -0
- package/dist/__tests__/postprocess-camera-consistency.test.d.ts +2 -0
- package/dist/__tests__/postprocess-camera-consistency.test.d.ts.map +1 -0
- package/dist/__tests__/profiler-assembly.integration.test.d.ts +2 -0
- package/dist/__tests__/profiler-assembly.integration.test.d.ts.map +1 -0
- package/dist/__tests__/propagate-transforms.dawn.test.d.ts +2 -0
- package/dist/__tests__/propagate-transforms.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/record-worldid-isolation.test.d.ts +2 -0
- package/dist/__tests__/record-worldid-isolation.test.d.ts.map +1 -0
- package/dist/__tests__/recover-error-exhaustive.test-d.d.ts +11 -0
- package/dist/__tests__/recover-error-exhaustive.test-d.d.ts.map +1 -0
- package/dist/__tests__/render-error-exhaustive.test-d.d.ts +11 -0
- package/dist/__tests__/render-error-exhaustive.test-d.d.ts.map +1 -0
- package/dist/__tests__/render-feature-assembly.test-d.d.ts +2 -0
- package/dist/__tests__/render-feature-assembly.test-d.d.ts.map +1 -0
- package/dist/__tests__/render-feature-prepared-graphics-docs.unit.test.d.ts +2 -0
- package/dist/__tests__/render-feature-prepared-graphics-docs.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-prepared-graphics.browser.test.d.ts +2 -0
- package/dist/__tests__/render-feature-prepared-graphics.browser.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-prepared-graphics.dawn.test.d.ts +2 -0
- package/dist/__tests__/render-feature-prepared-graphics.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-prepared-graphics.fixture.d.ts +10 -0
- package/dist/__tests__/render-feature-prepared-graphics.fixture.d.ts.map +1 -0
- package/dist/__tests__/render-feature-prepared-graphics.integration.test.d.ts +2 -0
- package/dist/__tests__/render-feature-prepared-graphics.integration.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-prepared-graphics.regression.integration.test.d.ts +2 -0
- package/dist/__tests__/render-feature-prepared-graphics.regression.integration.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-recover.integration.test.d.ts +2 -0
- package/dist/__tests__/render-feature-recover.integration.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-runtime-surface.test-d.d.ts +2 -0
- package/dist/__tests__/render-feature-runtime-surface.test-d.d.ts.map +1 -0
- package/dist/__tests__/render-schema-consumer.integration.test.d.ts +2 -0
- package/dist/__tests__/render-schema-consumer.integration.test.d.ts.map +1 -0
- package/dist/__tests__/render-schema-owner-boundary.unit.test.d.ts +2 -0
- package/dist/__tests__/render-schema-owner-boundary.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-system-cross-world-overlay-queue.test.d.ts +2 -0
- package/dist/__tests__/render-system-cross-world-overlay-queue.test.d.ts.map +1 -0
- package/dist/__tests__/render-system-extract-dispatch-ordering.test.d.ts +2 -0
- package/dist/__tests__/render-system-extract-dispatch-ordering.test.d.ts.map +1 -0
- package/dist/__tests__/render-system-extract.test.d.ts +2 -0
- package/dist/__tests__/render-system-extract.test.d.ts.map +1 -0
- package/dist/__tests__/render-system-extract.unit.test.d.ts +2 -0
- package/dist/__tests__/render-system-extract.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-system-fold-bucket.unit.test.d.ts +2 -0
- package/dist/__tests__/render-system-fold-bucket.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-system-fold-fallback.unit.test.d.ts +2 -0
- package/dist/__tests__/render-system-fold-fallback.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-system-fold-material.unit.test.d.ts +2 -0
- package/dist/__tests__/render-system-fold-material.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-system-fold-metrics.unit.test.d.ts +2 -0
- package/dist/__tests__/render-system-fold-metrics.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-system-fold-modegate.unit.test.d.ts +2 -0
- package/dist/__tests__/render-system-fold-modegate.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-system-fold-scene-instances.unit.test.d.ts +2 -0
- package/dist/__tests__/render-system-fold-scene-instances.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-system-fold.dawn.test.d.ts +2 -0
- package/dist/__tests__/render-system-fold.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/render-system-mega.test.d.ts +2 -0
- package/dist/__tests__/render-system-mega.test.d.ts.map +1 -0
- package/dist/__tests__/render-system-owner-split-contract.test.d.ts +2 -0
- package/dist/__tests__/render-system-owner-split-contract.test.d.ts.map +1 -0
- package/dist/__tests__/render-system-record-multi-material-textureview.test.d.ts +2 -0
- package/dist/__tests__/render-system-record-multi-material-textureview.test.d.ts.map +1 -0
- package/dist/__tests__/render-system-record-per-submesh-transparency.test.d.ts +2 -0
- package/dist/__tests__/render-system-record-per-submesh-transparency.test.d.ts.map +1 -0
- package/dist/__tests__/render-system-record.test.d.ts +2 -0
- package/dist/__tests__/render-system-record.test.d.ts.map +1 -0
- package/dist/__tests__/render-system-record.unit.test.d.ts +2 -0
- package/dist/__tests__/render-system-record.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-system-skin-bg.unit.test.d.ts +2 -0
- package/dist/__tests__/render-system-skin-bg.unit.test.d.ts.map +1 -0
- package/dist/__tests__/renderer-consumer.test-d.d.ts +2 -0
- package/dist/__tests__/renderer-consumer.test-d.d.ts.map +1 -0
- package/dist/__tests__/renderer-health.unit.test.d.ts +2 -0
- package/dist/__tests__/renderer-health.unit.test.d.ts.map +1 -0
- package/dist/__tests__/renderer-lifecycle.integration.test.d.ts +2 -0
- package/dist/__tests__/renderer-lifecycle.integration.test.d.ts.map +1 -0
- package/dist/__tests__/renderer-recover.unit.test.d.ts +2 -0
- package/dist/__tests__/renderer-recover.unit.test.d.ts.map +1 -0
- package/dist/__tests__/renderer-result.test-d.d.ts +2 -0
- package/dist/__tests__/renderer-result.test-d.d.ts.map +1 -0
- package/dist/__tests__/renderer-surface.unit.test.d.ts +2 -0
- package/dist/__tests__/renderer-surface.unit.test.d.ts.map +1 -0
- package/dist/__tests__/renderer-test-utils.d.ts +10 -0
- package/dist/__tests__/renderer-test-utils.d.ts.map +1 -0
- package/dist/__tests__/renderer.test-d.d.ts +2 -0
- package/dist/__tests__/renderer.test-d.d.ts.map +1 -0
- package/dist/__tests__/resolve-asset-handle-err.test.d.ts +2 -0
- package/dist/__tests__/resolve-asset-handle-err.test.d.ts.map +1 -0
- package/dist/__tests__/resolve-asset-handle.unit.test.d.ts +2 -0
- package/dist/__tests__/resolve-asset-handle.unit.test.d.ts.map +1 -0
- package/dist/__tests__/resolve-depth-only-view.test.d.ts +2 -0
- package/dist/__tests__/resolve-depth-only-view.test.d.ts.map +1 -0
- package/dist/__tests__/resolveName-fallback.test.d.ts +2 -0
- package/dist/__tests__/resolveName-fallback.test.d.ts.map +1 -0
- package/dist/__tests__/resolveName-null-package.test.d.ts +2 -0
- package/dist/__tests__/resolveName-null-package.test.d.ts.map +1 -0
- package/dist/__tests__/resolveName-unified-entry.test.d.ts +2 -0
- package/dist/__tests__/resolveName-unified-entry.test.d.ts.map +1 -0
- package/dist/__tests__/resolveName.test.d.ts +2 -0
- package/dist/__tests__/resolveName.test.d.ts.map +1 -0
- package/dist/__tests__/rhi-null-command-flow.unit.test.d.ts +2 -0
- package/dist/__tests__/rhi-null-command-flow.unit.test.d.ts.map +1 -0
- package/dist/__tests__/rhi-null-noop-behavior.unit.test.d.ts +2 -0
- package/dist/__tests__/rhi-null-noop-behavior.unit.test.d.ts.map +1 -0
- package/dist/__tests__/rhi-null-renderer-lifecycle.unit.test.d.ts +2 -0
- package/dist/__tests__/rhi-null-renderer-lifecycle.unit.test.d.ts.map +1 -0
- package/dist/__tests__/roots-to-scene-asset.test.d.ts +2 -0
- package/dist/__tests__/roots-to-scene-asset.test.d.ts.map +1 -0
- package/dist/__tests__/roundtrip-primitive.test.d.ts +2 -0
- package/dist/__tests__/roundtrip-primitive.test.d.ts.map +1 -0
- package/dist/__tests__/runtime-authority-scan.unit.test.d.ts +2 -0
- package/dist/__tests__/runtime-authority-scan.unit.test.d.ts.map +1 -0
- package/dist/__tests__/runtime-domain-residue.unit.test.d.ts +2 -0
- package/dist/__tests__/runtime-domain-residue.unit.test.d.ts.map +1 -0
- package/dist/__tests__/scene-bare-guid-refs-instantiate.test.d.ts +2 -0
- package/dist/__tests__/scene-bare-guid-refs-instantiate.test.d.ts.map +1 -0
- package/dist/__tests__/scene-equirect-roundtrip.test.d.ts +2 -0
- package/dist/__tests__/scene-equirect-roundtrip.test.d.ts.map +1 -0
- package/dist/__tests__/scene-instance-authoring.integration.test.d.ts +2 -0
- package/dist/__tests__/scene-instance-authoring.integration.test.d.ts.map +1 -0
- package/dist/__tests__/scene-instance-bridge.integration.test.d.ts +2 -0
- package/dist/__tests__/scene-instance-bridge.integration.test.d.ts.map +1 -0
- package/dist/__tests__/scene-instance-component.test.d.ts +2 -0
- package/dist/__tests__/scene-instance-component.test.d.ts.map +1 -0
- package/dist/__tests__/scene-instantiate-cycle-runtime.test.d.ts +2 -0
- package/dist/__tests__/scene-instantiate-cycle-runtime.test.d.ts.map +1 -0
- package/dist/__tests__/scene-mount-fixed-point.test.d.ts +2 -0
- package/dist/__tests__/scene-mount-fixed-point.test.d.ts.map +1 -0
- package/dist/__tests__/scene-mount-roundtrip.test.d.ts +2 -0
- package/dist/__tests__/scene-mount-roundtrip.test.d.ts.map +1 -0
- package/dist/__tests__/scene-mount-wrapper-roundtrip.test.d.ts +2 -0
- package/dist/__tests__/scene-mount-wrapper-roundtrip.test.d.ts.map +1 -0
- package/dist/__tests__/scene-sugar-instantiate.test.d.ts +2 -0
- package/dist/__tests__/scene-sugar-instantiate.test.d.ts.map +1 -0
- package/dist/__tests__/serialize-mounts-roundtrip.test.d.ts +2 -0
- package/dist/__tests__/serialize-mounts-roundtrip.test.d.ts.map +1 -0
- package/dist/__tests__/serialize-scene-asset.test.d.ts +2 -0
- package/dist/__tests__/serialize-scene-asset.test.d.ts.map +1 -0
- package/dist/__tests__/shadow-caster-empty-schema.test.d.ts +2 -0
- package/dist/__tests__/shadow-caster-empty-schema.test.d.ts.map +1 -0
- package/dist/__tests__/shadow-csm-atlas.test.d.ts +2 -0
- package/dist/__tests__/shadow-csm-atlas.test.d.ts.map +1 -0
- package/dist/__tests__/shadow-csm-blend.dawn.test.d.ts +2 -0
- package/dist/__tests__/shadow-csm-blend.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/shadow-csm-cascade-loadop.test.d.ts +2 -0
- package/dist/__tests__/shadow-csm-cascade-loadop.test.d.ts.map +1 -0
- package/dist/__tests__/shadow-csm-component.test.d.ts +2 -0
- package/dist/__tests__/shadow-csm-component.test.d.ts.map +1 -0
- package/dist/__tests__/shadow-csm-extract.test.d.ts +2 -0
- package/dist/__tests__/shadow-csm-extract.test.d.ts.map +1 -0
- package/dist/__tests__/shadow-csm-orthoz.test.d.ts +2 -0
- package/dist/__tests__/shadow-csm-orthoz.test.d.ts.map +1 -0
- package/dist/__tests__/shadow-csm-runtime-vary.dawn.test.d.ts +2 -0
- package/dist/__tests__/shadow-csm-runtime-vary.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/shadow-csm-shader.dawn.test.d.ts +2 -0
- package/dist/__tests__/shadow-csm-shader.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/shadow-csm-tile-consistency.test.d.ts +2 -0
- package/dist/__tests__/shadow-csm-tile-consistency.test.d.ts.map +1 -0
- package/dist/__tests__/shadow-csm-ubo.dawn.test.d.ts +2 -0
- package/dist/__tests__/shadow-csm-ubo.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/shadow-csm-ubo.test.d.ts +2 -0
- package/dist/__tests__/shadow-csm-ubo.test.d.ts.map +1 -0
- package/dist/__tests__/shadow-csm-viewport.browser.test.d.ts +2 -0
- package/dist/__tests__/shadow-csm-viewport.browser.test.d.ts.map +1 -0
- package/dist/__tests__/shadow-fields-observable.dawn.test.d.ts +2 -0
- package/dist/__tests__/shadow-fields-observable.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/shadow-m1.dawn.test.d.ts +2 -0
- package/dist/__tests__/shadow-m1.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/shadow-mesh-ssbo-order.test.d.ts +2 -0
- package/dist/__tests__/shadow-mesh-ssbo-order.test.d.ts.map +1 -0
- package/dist/__tests__/skin-error-exhaustive.test-d.d.ts +11 -0
- package/dist/__tests__/skin-error-exhaustive.test-d.d.ts.map +1 -0
- package/dist/__tests__/skin-errors.unit.test.d.ts +2 -0
- package/dist/__tests__/skin-errors.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skin-extract-getarrayview-count.unit.test.d.ts +2 -0
- package/dist/__tests__/skin-extract-getarrayview-count.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skin-parented-double-transform.test.d.ts +14 -0
- package/dist/__tests__/skin-parented-double-transform.test.d.ts.map +1 -0
- package/dist/__tests__/skylight-lazy-projection.test.d.ts +2 -0
- package/dist/__tests__/skylight-lazy-projection.test.d.ts.map +1 -0
- package/dist/__tests__/sparse-tag-scene-roundtrip.test.d.ts +2 -0
- package/dist/__tests__/sparse-tag-scene-roundtrip.test.d.ts.map +1 -0
- package/dist/__tests__/sprite-cullmode-flip.dawn.test.d.ts +2 -0
- package/dist/__tests__/sprite-cullmode-flip.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/sprite-lit-bgl-byte-identical.test.d.ts +2 -0
- package/dist/__tests__/sprite-lit-bgl-byte-identical.test.d.ts.map +1 -0
- package/dist/__tests__/sprite-lit-extract-branch.test.d.ts +2 -0
- package/dist/__tests__/sprite-lit-extract-branch.test.d.ts.map +1 -0
- package/dist/__tests__/sprite-lit-pipeline-cache-key.test.d.ts +2 -0
- package/dist/__tests__/sprite-lit-pipeline-cache-key.test.d.ts.map +1 -0
- package/dist/__tests__/sprite-param-values.test-d.d.ts +2 -0
- package/dist/__tests__/sprite-param-values.test-d.d.ts.map +1 -0
- package/dist/__tests__/ssao-bgl.test.d.ts +2 -0
- package/dist/__tests__/ssao-bgl.test.d.ts.map +1 -0
- package/dist/__tests__/ssao-buffers.test.d.ts +2 -0
- package/dist/__tests__/ssao-buffers.test.d.ts.map +1 -0
- package/dist/__tests__/ssao-data.test.d.ts +2 -0
- package/dist/__tests__/ssao-data.test.d.ts.map +1 -0
- package/dist/__tests__/ssao-errors.test.d.ts +2 -0
- package/dist/__tests__/ssao-errors.test.d.ts.map +1 -0
- package/dist/__tests__/ssao-manifest.test.d.ts +2 -0
- package/dist/__tests__/ssao-manifest.test.d.ts.map +1 -0
- package/dist/__tests__/ssao-passes.test.d.ts +2 -0
- package/dist/__tests__/ssao-passes.test.d.ts.map +1 -0
- package/dist/__tests__/ssao-pipelines.test.d.ts +2 -0
- package/dist/__tests__/ssao-pipelines.test.d.ts.map +1 -0
- package/dist/__tests__/ssao-uniform-layout.test.d.ts +2 -0
- package/dist/__tests__/ssao-uniform-layout.test.d.ts.map +1 -0
- package/dist/__tests__/static-rig.integration.test.d.ts +2 -0
- package/dist/__tests__/static-rig.integration.test.d.ts.map +1 -0
- package/dist/__tests__/stencil-outline-pixel.dawn.test.d.ts +2 -0
- package/dist/__tests__/stencil-outline-pixel.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/swap-chain-format.unit.test.d.ts +2 -0
- package/dist/__tests__/swap-chain-format.unit.test.d.ts.map +1 -0
- package/dist/__tests__/systems.unit.test.d.ts +2 -0
- package/dist/__tests__/systems.unit.test.d.ts.map +1 -0
- package/dist/__tests__/template-game-default-api-contract.test.d.ts +2 -0
- package/dist/__tests__/template-game-default-api-contract.test.d.ts.map +1 -0
- package/dist/__tests__/text.unit.test.d.ts +2 -0
- package/dist/__tests__/text.unit.test.d.ts.map +1 -0
- package/dist/__tests__/tilemap-childed-despawn.test.d.ts +2 -0
- package/dist/__tests__/tilemap-childed-despawn.test.d.ts.map +1 -0
- package/dist/__tests__/tilemap-chunk-extract.overhang-bounds.test.d.ts +2 -0
- package/dist/__tests__/tilemap-chunk-extract.overhang-bounds.test.d.ts.map +1 -0
- package/dist/__tests__/tilemap-chunk-extract.propagate-order.test.d.ts +2 -0
- package/dist/__tests__/tilemap-chunk-extract.propagate-order.test.d.ts.map +1 -0
- package/dist/__tests__/tilemap-chunk-extract.terrain-rewrite.unit.test.d.ts +2 -0
- package/dist/__tests__/tilemap-chunk-extract.terrain-rewrite.unit.test.d.ts.map +1 -0
- package/dist/__tests__/tilemap-chunk-extract.test.d.ts +2 -0
- package/dist/__tests__/tilemap-chunk-extract.test.d.ts.map +1 -0
- package/dist/__tests__/tilemap-chunk-extract.unit.test.d.ts +2 -0
- package/dist/__tests__/tilemap-chunk-extract.unit.test.d.ts.map +1 -0
- package/dist/__tests__/tilemap-chunk-y-sort-bench.unit.test.d.ts +2 -0
- package/dist/__tests__/tilemap-chunk-y-sort-bench.unit.test.d.ts.map +1 -0
- package/dist/__tests__/tilemap-dirty-rebuild.test.d.ts +2 -0
- package/dist/__tests__/tilemap-dirty-rebuild.test.d.ts.map +1 -0
- package/dist/__tests__/tilemap-material-cache.test.d.ts +2 -0
- package/dist/__tests__/tilemap-material-cache.test.d.ts.map +1 -0
- package/dist/__tests__/tilemap-multi-atlas.test.d.ts +2 -0
- package/dist/__tests__/tilemap-multi-atlas.test.d.ts.map +1 -0
- package/dist/__tests__/tilemap-owner-boundary.unit.test.d.ts +2 -0
- package/dist/__tests__/tilemap-owner-boundary.unit.test.d.ts.map +1 -0
- package/dist/__tests__/tilemap-region-pack.test.d.ts +2 -0
- package/dist/__tests__/tilemap-region-pack.test.d.ts.map +1 -0
- package/dist/__tests__/tilemap-spawn-flip-pivot.test.d.ts +2 -0
- package/dist/__tests__/tilemap-spawn-flip-pivot.test.d.ts.map +1 -0
- package/dist/__tests__/tilemap-uv-inset.test.d.ts +2 -0
- package/dist/__tests__/tilemap-uv-inset.test.d.ts.map +1 -0
- package/dist/__tests__/tilemap-validate.test.d.ts +2 -0
- package/dist/__tests__/tilemap-validate.test.d.ts.map +1 -0
- package/dist/__tests__/tileset-asset-validate.test.d.ts +2 -0
- package/dist/__tests__/tileset-asset-validate.test.d.ts.map +1 -0
- package/dist/__tests__/transcode-fallback.unit.test.d.ts +2 -0
- package/dist/__tests__/transcode-fallback.unit.test.d.ts.map +1 -0
- package/dist/__tests__/transform-hierarchy-pixel-diff.dawn.test.d.ts +2 -0
- package/dist/__tests__/transform-hierarchy-pixel-diff.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/transient-fields-collect.test.d.ts +2 -0
- package/dist/__tests__/transient-fields-collect.test.d.ts.map +1 -0
- package/dist/__tests__/transient-runtime-behavior.test.d.ts +2 -0
- package/dist/__tests__/transient-runtime-behavior.test.d.ts.map +1 -0
- package/dist/__tests__/transitional-authoring.integration.test.d.ts +2 -0
- package/dist/__tests__/transitional-authoring.integration.test.d.ts.map +1 -0
- package/dist/__tests__/transparent-sort-tilemap-sprite-interleave.test.d.ts +2 -0
- package/dist/__tests__/transparent-sort-tilemap-sprite-interleave.test.d.ts.map +1 -0
- package/dist/__tests__/variant-selection.unit.test.d.ts +2 -0
- package/dist/__tests__/variant-selection.unit.test.d.ts.map +1 -0
- package/dist/__tests__/vertex-attribute-layout.test.d.ts +2 -0
- package/dist/__tests__/vertex-attribute-layout.test.d.ts.map +1 -0
- package/dist/__tests__/video-extract-bindgroup.dawn.test.d.ts +2 -0
- package/dist/__tests__/video-extract-bindgroup.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/video-loader-consumption.test.d.ts +2 -0
- package/dist/__tests__/video-loader-consumption.test.d.ts.map +1 -0
- package/dist/__tests__/video-loader-smoke.dawn.test.d.ts +2 -0
- package/dist/__tests__/video-loader-smoke.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/view-ubo-layout.browser.test.d.ts +2 -0
- package/dist/__tests__/view-ubo-layout.browser.test.d.ts.map +1 -0
- package/dist/__tests__/world-entity-key.test.d.ts +2 -0
- package/dist/__tests__/world-entity-key.test.d.ts.map +1 -0
- package/dist/__tests__/world-scene-despawn.test.d.ts +2 -0
- package/dist/__tests__/world-scene-despawn.test.d.ts.map +1 -0
- package/dist/__tests__/world-scene-instantiate.test.d.ts +2 -0
- package/dist/__tests__/world-scene-instantiate.test.d.ts.map +1 -0
- package/dist/__tests__/world-scene-nested.test.d.ts +2 -0
- package/dist/__tests__/world-scene-nested.test.d.ts.map +1 -0
- package/dist/__tests__/world-scene-override.test.d.ts +2 -0
- package/dist/__tests__/world-scene-override.test.d.ts.map +1 -0
- package/dist/__tests__/zero-compression-zero-load.integration.test.d.ts +2 -0
- package/dist/__tests__/zero-compression-zero-load.integration.test.d.ts.map +1 -0
- package/dist/asset-contributions.d.ts +3 -0
- package/dist/asset-contributions.d.ts.map +1 -0
- package/dist/backend-selection.d.ts +13 -0
- package/dist/backend-selection.d.ts.map +1 -0
- package/dist/collect-scene-asset.d.ts +19 -0
- package/dist/collect-scene-asset.d.ts.map +1 -0
- package/dist/components/__tests__/camera.test-d.d.ts +2 -0
- package/dist/components/__tests__/camera.test-d.d.ts.map +1 -0
- package/dist/components/__tests__/camera.test.d.ts +2 -0
- package/dist/components/__tests__/camera.test.d.ts.map +1 -0
- package/dist/components/__tests__/child-of.test.d.ts +2 -0
- package/dist/components/__tests__/child-of.test.d.ts.map +1 -0
- package/dist/components/__tests__/sprite-animation.test-d.d.ts +2 -0
- package/dist/components/__tests__/sprite-animation.test-d.d.ts.map +1 -0
- package/dist/components/__tests__/sprite-instances.unit.test.d.ts +2 -0
- package/dist/components/__tests__/sprite-instances.unit.test.d.ts.map +1 -0
- package/dist/components/__tests__/sprite-region-override.test-d.d.ts +2 -0
- package/dist/components/__tests__/sprite-region-override.test-d.d.ts.map +1 -0
- package/dist/components/__tests__/tile-layer.test-d.d.ts +2 -0
- package/dist/components/__tests__/tile-layer.test-d.d.ts.map +1 -0
- package/dist/components/__tests__/tile-layer.unit.test.d.ts +2 -0
- package/dist/components/__tests__/tile-layer.unit.test.d.ts.map +1 -0
- package/dist/components/__tests__/tilemap.test-d.d.ts +2 -0
- package/dist/components/__tests__/tilemap.test-d.d.ts.map +1 -0
- package/dist/components/__tests__/transform-vec-schema.test.d.ts +2 -0
- package/dist/components/__tests__/transform-vec-schema.test.d.ts.map +1 -0
- package/dist/create-renderer-env-classify.d.ts +21 -0
- package/dist/create-renderer-env-classify.d.ts.map +1 -0
- package/dist/createRenderer.d.ts +15 -0
- package/dist/createRenderer.d.ts.map +1 -0
- package/dist/dev-import-transport.d.ts +24 -0
- package/dist/dev-import-transport.d.ts.map +1 -0
- package/dist/errors/environment.d.ts +2 -0
- package/dist/errors/environment.d.ts.map +1 -0
- package/dist/index.d.ts +218 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +813 -0
- package/dist/index.mjs.map +1 -0
- package/dist/renderer-host.d.ts +9 -0
- package/dist/renderer-host.d.ts.map +1 -0
- package/dist/renderer-host.mjs +62 -0
- package/dist/renderer-host.mjs.map +1 -0
- package/dist/scene-instances/__tests__/post-spawn-resolve-joints.unit.test.d.ts +2 -0
- package/dist/scene-instances/__tests__/post-spawn-resolve-joints.unit.test.d.ts.map +1 -0
- package/dist/scene-instances/index.d.ts +2 -0
- package/dist/scene-instances/index.d.ts.map +1 -0
- package/dist/scene-utils/mount-override-fold.d.ts +15 -0
- package/dist/scene-utils/mount-override-fold.d.ts.map +1 -0
- package/dist/sprite-param-values.d.ts +62 -0
- package/dist/sprite-param-values.d.ts.map +1 -0
- package/dist/systems/__tests__/_xorshift.d.ts +28 -0
- package/dist/systems/__tests__/_xorshift.d.ts.map +1 -0
- package/dist/systems/__tests__/argsort-in-place.test.d.ts +2 -0
- package/dist/systems/__tests__/argsort-in-place.test.d.ts.map +1 -0
- package/dist/systems/__tests__/transparent-sort.bench.d.ts +2 -0
- package/dist/systems/__tests__/transparent-sort.bench.d.ts.map +1 -0
- package/dist/systems/sprite-animation-tick.d.ts +50 -0
- package/dist/systems/sprite-animation-tick.d.ts.map +1 -0
- package/dist/systems/tonemap.d.ts +27 -0
- package/dist/systems/tonemap.d.ts.map +1 -0
- package/dist/systems/transparent-sort.d.ts +50 -0
- package/dist/systems/transparent-sort.d.ts.map +1 -0
- package/dist/tilemap-register-validate.d.ts +21 -0
- package/dist/tilemap-register-validate.d.ts.map +1 -0
- package/package.json +101 -0
- package/src/__tests__/active-camera.unit.test.ts +203 -0
- package/src/__tests__/animation-graph-add.unit.test.ts +67 -0
- package/src/__tests__/animation-graph-asset-union.test-d.ts +83 -0
- package/src/__tests__/animation-graph-blend.unit.test.ts +66 -0
- package/src/__tests__/animation-graph-build.unit.test.ts +100 -0
- package/src/__tests__/animation-graph-clip-missing.unit.test.ts +62 -0
- package/src/__tests__/animation-graph-clip.unit.test.ts +77 -0
- package/src/__tests__/animation-graph-collect-roundtrip.test.ts +213 -0
- package/src/__tests__/animation-graph-default-path.integration.test.ts +65 -0
- package/src/__tests__/animation-graph-describe.unit.test.ts +68 -0
- package/src/__tests__/animation-graph-errors.unit.test.ts +75 -0
- package/src/__tests__/animation-graph-nesting.unit.test.ts +84 -0
- package/src/__tests__/animation-graph-orthogonal-weight.unit.test.ts +98 -0
- package/src/__tests__/animation-graph-serialize.unit.test.ts +171 -0
- package/src/__tests__/animation-graph-slot-fill.unit.test.ts +95 -0
- package/src/__tests__/animation-nslot.unit.test.ts +63 -0
- package/src/__tests__/animation-slot-length-guard.unit.test.ts +81 -0
- package/src/__tests__/append-injection.test.ts +105 -0
- package/src/__tests__/asi-world-baseline-export.unit.test.ts +173 -0
- package/src/__tests__/asi-world-ci-config.unit.test.ts +186 -0
- package/src/__tests__/asi-world-smoke.unit.test.ts +192 -0
- package/src/__tests__/asset-brand-and-name-retirement.test.ts +102 -0
- package/src/__tests__/asset-error-exhaustive.test-d.ts +62 -0
- package/src/__tests__/asset-registry-guid-reverse.test.ts +608 -0
- package/src/__tests__/asset-registry-hdr-equirect.test.ts +78 -0
- package/src/__tests__/asset-registry-mesh-pack-payload.dawn.test.ts +232 -0
- package/src/__tests__/asset-registry-mounts.test.ts +806 -0
- package/src/__tests__/asset-registry-scene.test-d.ts +49 -0
- package/src/__tests__/asset-registry.browser.test.ts +81 -0
- package/src/__tests__/asset-registry.dawn.test.ts +94 -0
- package/src/__tests__/asset-registry.listCatalog.test.ts +170 -0
- package/src/__tests__/asset-registry.mesh-bin.test.ts +316 -0
- package/src/__tests__/asset-registry.pack-cache.test.ts +969 -0
- package/src/__tests__/asset-registry.raw-container-failfast.test.ts +225 -0
- package/src/__tests__/asset-registry.recursive.spec.ts +1397 -0
- package/src/__tests__/asset-registry.unit.test.ts +1030 -0
- package/src/__tests__/asset.unit.test.ts +7107 -0
- package/src/__tests__/audio-load-by-guid.unit.test.ts +126 -0
- package/src/__tests__/backend-selection.integration.test.ts +11 -0
- package/src/__tests__/basis-catalog-dispatch.integration.test.ts +320 -0
- package/src/__tests__/bind-group-cache-keying.test.ts +295 -0
- package/src/__tests__/block-upload-math.unit.test.ts +192 -0
- package/src/__tests__/builtin-asset-registry.unit.test.ts +112 -0
- package/src/__tests__/builtin-shader-register-e2e.test.ts +151 -0
- package/src/__tests__/cache-false-eviction.test.ts +127 -0
- package/src/__tests__/cache-isolation-matrix.test.ts +183 -0
- package/src/__tests__/camera-clear-snapshot-equivalence.unit.test.ts +75 -0
- package/src/__tests__/clamp-to-last.e2e.browser.test.ts +193 -0
- package/src/__tests__/clamp-to-last.e2e.dawn.test.ts +325 -0
- package/src/__tests__/collect-scene-asset.test.ts +873 -0
- package/src/__tests__/collect-scene-mount-collapse.test.ts +391 -0
- package/src/__tests__/collect-subtree.unit.test.ts +75 -0
- package/src/__tests__/collect-transient-children-absent.test.ts +168 -0
- package/src/__tests__/collect-transient-roundtrip.test.ts +269 -0
- package/src/__tests__/collect-transient-skip.test.ts +176 -0
- package/src/__tests__/components.test-d.ts +231 -0
- package/src/__tests__/components.unit.test.ts +2666 -0
- package/src/__tests__/composite-skybox-cross-world.browser.test.ts +410 -0
- package/src/__tests__/compressed-texture-physical-extent.integration.test.ts +139 -0
- package/src/__tests__/create-renderer-assembly.integration.test.ts +55 -0
- package/src/__tests__/createRenderer-dispose.test.ts +49 -0
- package/src/__tests__/createRenderer-requiredFeatures.unit.test.ts +134 -0
- package/src/__tests__/createRenderer.browser-no-webgpu.test.ts +54 -0
- package/src/__tests__/createRenderer.unit.test.ts +231 -0
- package/src/__tests__/dawn/hello-sprite-nineslice-section.dawn.test.ts +357 -0
- package/src/__tests__/dawn/ibl-compose.dawn.test.ts +151 -0
- package/src/__tests__/dawn/ibl-modules-compose.dawn.test.ts +223 -0
- package/src/__tests__/dawn/ibl-precompute-readback.dawn.test.ts +250 -0
- package/src/__tests__/dawn/instances-per-instance-pbr.dawn.test.ts +631 -0
- package/src/__tests__/dawn/instances-shadow.dawn.test.ts +454 -0
- package/src/__tests__/dawn/instances-uniform-fallback.dawn.test.ts +312 -0
- package/src/__tests__/dawn/material-alpha.dawn.test.ts +296 -0
- package/src/__tests__/dawn/material-cooked-fixture.dawn.test.ts +79 -0
- package/src/__tests__/dawn/mesh-ssbo-normal-matrix.dawn.test.ts +106 -0
- package/src/__tests__/dawn/mesh-update-dawn.test.ts +250 -0
- package/src/__tests__/dawn/mipmap-srgb-linear.dawn.test.ts +124 -0
- package/src/__tests__/dawn/per-entity-material-texture-isolation.dawn.test.ts +555 -0
- package/src/__tests__/dawn/sprite-nineslice-mesh-bind.dawn.test.ts +235 -0
- package/src/__tests__/dawn/tonemap.dawn.test.ts +107 -0
- package/src/__tests__/dawn/upload-texture.dawn.test.ts +198 -0
- package/src/__tests__/dawn/user-handle-mesh-render.dawn.test.ts +289 -0
- package/src/__tests__/dawn/user-mesh-upload.dawn.test.ts +209 -0
- package/src/__tests__/dawn/zero-manifest-mesh-fail.dawn.test.ts +119 -0
- package/src/__tests__/dawn/zero-manifest-ready.dawn.test.ts +98 -0
- package/src/__tests__/demo-sortscope-migration.unit.test.ts +115 -0
- package/src/__tests__/derive-bgl-integration.test.ts +388 -0
- package/src/__tests__/derive-render-data-skin.unit.test.ts +90 -0
- package/src/__tests__/draw-published.ts +17 -0
- package/src/__tests__/dynamic-texture-store.unit.test.ts +190 -0
- package/src/__tests__/environment-error-exhaustive.test-d.ts +28 -0
- package/src/__tests__/equirect-bc6h.integration.test.ts +158 -0
- package/src/__tests__/error-exhaustive.test.ts +220 -0
- package/src/__tests__/errors.unit.test.ts +1227 -0
- package/src/__tests__/extract-frames-graceful-degradation.test.ts +294 -0
- package/src/__tests__/extract-frames-merge.test.ts +656 -0
- package/src/__tests__/extract-frames-reset.test.ts +158 -0
- package/src/__tests__/extract-record-no-hardcoded-texture-fields.test.ts +81 -0
- package/src/__tests__/extract-skin-mismatch.unit.test.ts +322 -0
- package/src/__tests__/fetch-binary-decompress.test.ts +356 -0
- package/src/__tests__/four-path-compression.integration.test.ts +395 -0
- package/src/__tests__/frame-targets-shadow-regression.unit.test.ts +28 -0
- package/src/__tests__/fullscreen-feature-host.dawn.test.ts +80 -0
- package/src/__tests__/fullscreen-post-bgl-depth.test.ts +133 -0
- package/src/__tests__/fullscreen-post-depth-params-arity.test.ts +84 -0
- package/src/__tests__/fullscreen-post-process-pass.dawn.test.ts +336 -0
- package/src/__tests__/fullscreen-post-process-pass.unit.test.ts +109 -0
- package/src/__tests__/fxaa-pixel-diff.dawn.test.ts +303 -0
- package/src/__tests__/fxaa-validation-and-resize.dawn.test.ts +369 -0
- package/src/__tests__/fxaa-zero-overhead.dawn.test.ts +258 -0
- package/src/__tests__/geometry.unit.test.ts +2631 -0
- package/src/__tests__/glyph-tilemap-worldid.test.ts +146 -0
- package/src/__tests__/gpu-resource-cow-survivor.test.ts +390 -0
- package/src/__tests__/guid-for-asset.unit.test.ts +102 -0
- package/src/__tests__/handle-migration.test.ts +136 -0
- package/src/__tests__/hello-tilemap-smoke-upgrade.unit.test.ts +147 -0
- package/src/__tests__/helpers/mock-shader-registry.ts +139 -0
- package/src/__tests__/helpers/register-runtime-components.ts +71 -0
- package/src/__tests__/helpers/register-scene-components.ts +10 -0
- package/src/__tests__/helpers/tilemap-assets.ts +29 -0
- package/src/__tests__/host-custom-kind-contract.test-d.ts +47 -0
- package/src/__tests__/host-custom-kind-contract.test.ts +578 -0
- package/src/__tests__/inspect-name.test.ts +139 -0
- package/src/__tests__/instances.test-d.ts +61 -0
- package/src/__tests__/instantiate-mount-deferred-wiring.test.ts +112 -0
- package/src/__tests__/light-attenuation-formula.test.ts +171 -0
- package/src/__tests__/light-casters-9-light.browser.test.ts +877 -0
- package/src/__tests__/light-gpu-byte-neutral.unit.test.ts +160 -0
- package/src/__tests__/light-snapshot.test-d.ts +84 -0
- package/src/__tests__/lights-vec-negative.unit.test.ts +52 -0
- package/src/__tests__/lights.unit.test.ts +2456 -0
- package/src/__tests__/loadbyguid-payload.unit.test.ts +117 -0
- package/src/__tests__/loadbyguid-texture-intern.unit.test.ts +213 -0
- package/src/__tests__/loader-extract-graceful.test.ts +146 -0
- package/src/__tests__/loader-name-survival.test.ts +213 -0
- package/src/__tests__/m3-vec-negative.unit.test.ts +46 -0
- package/src/__tests__/m5-browser-dawn-sweep.integration.test.ts +8 -0
- package/src/__tests__/material-texture-uv-scale.unit.test.ts +119 -0
- package/src/__tests__/materials.unit.test.ts +254 -0
- package/src/__tests__/mesh-bin-contract.test.ts +28 -0
- package/src/__tests__/mesh-bin-skin-roundtrip.unit.test.ts +149 -0
- package/src/__tests__/mesh-bin.test.ts +38 -0
- package/src/__tests__/mesh-loader-skin-contract.unit.test.ts +120 -0
- package/src/__tests__/mesh-ssbo-grow.unit.test.ts +244 -0
- package/src/__tests__/mip-gate.unit.test.ts +79 -0
- package/src/__tests__/msaa-sprite-pixel-diff.dawn.test.ts +367 -0
- package/src/__tests__/multi-uv-8set.e2e.dawn.test.ts +296 -0
- package/src/__tests__/name.unit.test.ts +123 -0
- package/src/__tests__/new-kind-refs-contract.test.ts +132 -0
- package/src/__tests__/node-shims.d.ts +31 -0
- package/src/__tests__/package-promotion.test.ts +88 -0
- package/src/__tests__/package-rename.test.ts +103 -0
- package/src/__tests__/pass-selector.unit.test.ts +102 -0
- package/src/__tests__/pbr-pipeline.unit.test.ts +200 -0
- package/src/__tests__/pipeline-builder-msaa.unit.test.ts +221 -0
- package/src/__tests__/pipeline-cache-keying.unit.test.ts +313 -0
- package/src/__tests__/pipeline-spec.test.ts +900 -0
- package/src/__tests__/pipeline.unit.test.ts +5435 -0
- package/src/__tests__/point-light-shadow.browser.test.ts +278 -0
- package/src/__tests__/point-light-shadow.dawn.test.ts +813 -0
- package/src/__tests__/point-light-shadow.unit.test.ts +675 -0
- package/src/__tests__/post-process-error-codes.unit.test.ts +161 -0
- package/src/__tests__/post-process-params-component.unit.test.ts +153 -0
- package/src/__tests__/post-process-params-extract.unit.test.ts +97 -0
- package/src/__tests__/postprocess-camera-consistency.test.ts +375 -0
- package/src/__tests__/profiler-assembly.integration.test.ts +74 -0
- package/src/__tests__/propagate-transforms.dawn.test.ts +254 -0
- package/src/__tests__/record-worldid-isolation.test.ts +225 -0
- package/src/__tests__/recover-error-exhaustive.test-d.ts +35 -0
- package/src/__tests__/render-error-exhaustive.test-d.ts +291 -0
- package/src/__tests__/render-feature-assembly.test-d.ts +39 -0
- package/src/__tests__/render-feature-prepared-graphics-docs.unit.test.ts +126 -0
- package/src/__tests__/render-feature-prepared-graphics.browser.test.ts +88 -0
- package/src/__tests__/render-feature-prepared-graphics.dawn.test.ts +115 -0
- package/src/__tests__/render-feature-prepared-graphics.fixture.ts +104 -0
- package/src/__tests__/render-feature-prepared-graphics.integration.test.ts +56 -0
- package/src/__tests__/render-feature-prepared-graphics.regression.integration.test.ts +104 -0
- package/src/__tests__/render-feature-recover.integration.test.ts +102 -0
- package/src/__tests__/render-feature-runtime-surface.test-d.ts +42 -0
- package/src/__tests__/render-schema-consumer.integration.test.ts +32 -0
- package/src/__tests__/render-schema-owner-boundary.unit.test.ts +22 -0
- package/src/__tests__/render-system-cross-world-overlay-queue.test.ts +172 -0
- package/src/__tests__/render-system-extract-dispatch-ordering.test.ts +307 -0
- package/src/__tests__/render-system-extract.test.ts +1384 -0
- package/src/__tests__/render-system-extract.unit.test.ts +360 -0
- package/src/__tests__/render-system-fold-bucket.unit.test.ts +286 -0
- package/src/__tests__/render-system-fold-fallback.unit.test.ts +151 -0
- package/src/__tests__/render-system-fold-material.unit.test.ts +143 -0
- package/src/__tests__/render-system-fold-metrics.unit.test.ts +170 -0
- package/src/__tests__/render-system-fold-modegate.unit.test.ts +166 -0
- package/src/__tests__/render-system-fold-scene-instances.unit.test.ts +51 -0
- package/src/__tests__/render-system-fold.dawn.test.ts +308 -0
- package/src/__tests__/render-system-mega.test.ts +1680 -0
- package/src/__tests__/render-system-owner-split-contract.test.ts +178 -0
- package/src/__tests__/render-system-record-multi-material-textureview.test.ts +698 -0
- package/src/__tests__/render-system-record-per-submesh-transparency.test.ts +458 -0
- package/src/__tests__/render-system-record.test.ts +1668 -0
- package/src/__tests__/render-system-record.unit.test.ts +269 -0
- package/src/__tests__/render-system-skin-bg.unit.test.ts +143 -0
- package/src/__tests__/renderer-consumer.test-d.ts +18 -0
- package/src/__tests__/renderer-health.unit.test.ts +45 -0
- package/src/__tests__/renderer-lifecycle.integration.test.ts +42 -0
- package/src/__tests__/renderer-recover.unit.test.ts +55 -0
- package/src/__tests__/renderer-result.test-d.ts +36 -0
- package/src/__tests__/renderer-surface.unit.test.ts +414 -0
- package/src/__tests__/renderer-test-utils.ts +56 -0
- package/src/__tests__/renderer.test-d.ts +61 -0
- package/src/__tests__/resolve-asset-handle-err.test.ts +111 -0
- package/src/__tests__/resolve-asset-handle.unit.test.ts +64 -0
- package/src/__tests__/resolve-depth-only-view.test.ts +173 -0
- package/src/__tests__/resolveName-fallback.test.ts +64 -0
- package/src/__tests__/resolveName-null-package.test.ts +38 -0
- package/src/__tests__/resolveName-unified-entry.test.ts +65 -0
- package/src/__tests__/resolveName.test.ts +64 -0
- package/src/__tests__/rhi-null-command-flow.unit.test.ts +69 -0
- package/src/__tests__/rhi-null-noop-behavior.unit.test.ts +196 -0
- package/src/__tests__/rhi-null-renderer-lifecycle.unit.test.ts +54 -0
- package/src/__tests__/roots-to-scene-asset.test.ts +711 -0
- package/src/__tests__/roundtrip-primitive.test.ts +351 -0
- package/src/__tests__/runtime-authority-scan.unit.test.ts +19 -0
- package/src/__tests__/runtime-domain-residue.unit.test.ts +13 -0
- package/src/__tests__/scene-bare-guid-refs-instantiate.test.ts +123 -0
- package/src/__tests__/scene-equirect-roundtrip.test.ts +120 -0
- package/src/__tests__/scene-instance-authoring.integration.test.ts +8 -0
- package/src/__tests__/scene-instance-bridge.integration.test.ts +8 -0
- package/src/__tests__/scene-instance-component.test.ts +45 -0
- package/src/__tests__/scene-instantiate-cycle-runtime.test.ts +66 -0
- package/src/__tests__/scene-mount-fixed-point.test.ts +257 -0
- package/src/__tests__/scene-mount-roundtrip.test.ts +987 -0
- package/src/__tests__/scene-mount-wrapper-roundtrip.test.ts +202 -0
- package/src/__tests__/scene-sugar-instantiate.test.ts +324 -0
- package/src/__tests__/serialize-mounts-roundtrip.test.ts +533 -0
- package/src/__tests__/serialize-scene-asset.test.ts +533 -0
- package/src/__tests__/shadow-caster-empty-schema.test.ts +83 -0
- package/src/__tests__/shadow-csm-atlas.test.ts +178 -0
- package/src/__tests__/shadow-csm-blend.dawn.test.ts +230 -0
- package/src/__tests__/shadow-csm-cascade-loadop.test.ts +354 -0
- package/src/__tests__/shadow-csm-component.test.ts +284 -0
- package/src/__tests__/shadow-csm-extract.test.ts +236 -0
- package/src/__tests__/shadow-csm-orthoz.test.ts +137 -0
- package/src/__tests__/shadow-csm-runtime-vary.dawn.test.ts +203 -0
- package/src/__tests__/shadow-csm-shader.dawn.test.ts +358 -0
- package/src/__tests__/shadow-csm-tile-consistency.test.ts +425 -0
- package/src/__tests__/shadow-csm-ubo.dawn.test.ts +252 -0
- package/src/__tests__/shadow-csm-ubo.test.ts +278 -0
- package/src/__tests__/shadow-csm-viewport.browser.test.ts +85 -0
- package/src/__tests__/shadow-fields-observable.dawn.test.ts +626 -0
- package/src/__tests__/shadow-m1.dawn.test.ts +104 -0
- package/src/__tests__/shadow-mesh-ssbo-order.test.ts +48 -0
- package/src/__tests__/skin-error-exhaustive.test-d.ts +74 -0
- package/src/__tests__/skin-errors.unit.test.ts +10 -0
- package/src/__tests__/skin-extract-getarrayview-count.unit.test.ts +25 -0
- package/src/__tests__/skin-parented-double-transform.test.ts +268 -0
- package/src/__tests__/skylight-lazy-projection.test.ts +380 -0
- package/src/__tests__/sparse-tag-scene-roundtrip.test.ts +43 -0
- package/src/__tests__/sprite-cullmode-flip.dawn.test.ts +453 -0
- package/src/__tests__/sprite-lit-bgl-byte-identical.test.ts +142 -0
- package/src/__tests__/sprite-lit-extract-branch.test.ts +117 -0
- package/src/__tests__/sprite-lit-pipeline-cache-key.test.ts +123 -0
- package/src/__tests__/sprite-param-values.test-d.ts +104 -0
- package/src/__tests__/ssao-bgl.test.ts +354 -0
- package/src/__tests__/ssao-buffers.test.ts +211 -0
- package/src/__tests__/ssao-data.test.ts +121 -0
- package/src/__tests__/ssao-errors.test.ts +162 -0
- package/src/__tests__/ssao-manifest.test.ts +109 -0
- package/src/__tests__/ssao-passes.test.ts +905 -0
- package/src/__tests__/ssao-pipelines.test.ts +275 -0
- package/src/__tests__/ssao-uniform-layout.test.ts +180 -0
- package/src/__tests__/static-rig.integration.test.ts +9 -0
- package/src/__tests__/stencil-outline-pixel.dawn.test.ts +303 -0
- package/src/__tests__/swap-chain-format.unit.test.ts +115 -0
- package/src/__tests__/systems.unit.test.ts +9167 -0
- package/src/__tests__/template-game-default-api-contract.test.ts +362 -0
- package/src/__tests__/text.unit.test.ts +489 -0
- package/src/__tests__/tilemap-childed-despawn.test.ts +462 -0
- package/src/__tests__/tilemap-chunk-extract.overhang-bounds.test.ts +310 -0
- package/src/__tests__/tilemap-chunk-extract.propagate-order.test.ts +81 -0
- package/src/__tests__/tilemap-chunk-extract.terrain-rewrite.unit.test.ts +424 -0
- package/src/__tests__/tilemap-chunk-extract.test.ts +232 -0
- package/src/__tests__/tilemap-chunk-extract.unit.test.ts +383 -0
- package/src/__tests__/tilemap-chunk-y-sort-bench.unit.test.ts +109 -0
- package/src/__tests__/tilemap-dirty-rebuild.test.ts +296 -0
- package/src/__tests__/tilemap-material-cache.test.ts +132 -0
- package/src/__tests__/tilemap-multi-atlas.test.ts +217 -0
- package/src/__tests__/tilemap-owner-boundary.unit.test.ts +66 -0
- package/src/__tests__/tilemap-region-pack.test.ts +149 -0
- package/src/__tests__/tilemap-spawn-flip-pivot.test.ts +270 -0
- package/src/__tests__/tilemap-uv-inset.test.ts +201 -0
- package/src/__tests__/tilemap-validate.test.ts +201 -0
- package/src/__tests__/tileset-asset-validate.test.ts +638 -0
- package/src/__tests__/transcode-fallback.unit.test.ts +207 -0
- package/src/__tests__/transform-hierarchy-pixel-diff.dawn.test.ts +282 -0
- package/src/__tests__/transient-fields-collect.test.ts +210 -0
- package/src/__tests__/transient-runtime-behavior.test.ts +227 -0
- package/src/__tests__/transitional-authoring.integration.test.ts +7 -0
- package/src/__tests__/transparent-sort-tilemap-sprite-interleave.test.ts +227 -0
- package/src/__tests__/variant-selection.unit.test.ts +21 -0
- package/src/__tests__/vertex-attribute-layout.test.ts +324 -0
- package/src/__tests__/video-extract-bindgroup.dawn.test.ts +403 -0
- package/src/__tests__/video-loader-consumption.test.ts +86 -0
- package/src/__tests__/video-loader-smoke.dawn.test.ts +60 -0
- package/src/__tests__/view-ubo-layout.browser.test.ts +161 -0
- package/src/__tests__/world-entity-key.test.ts +92 -0
- package/src/__tests__/world-scene-despawn.test.ts +132 -0
- package/src/__tests__/world-scene-instantiate.test.ts +194 -0
- package/src/__tests__/world-scene-nested.test.ts +230 -0
- package/src/__tests__/world-scene-override.test.ts +199 -0
- package/src/__tests__/zero-compression-zero-load.integration.test.ts +107 -0
- package/src/asset-contributions.ts +48 -0
- package/src/backend-selection.ts +69 -0
- package/src/collect-scene-asset.ts +701 -0
- package/src/components/__tests__/camera.test-d.ts +57 -0
- package/src/components/__tests__/camera.test.ts +95 -0
- package/src/components/__tests__/child-of.test.ts +35 -0
- package/src/components/__tests__/sprite-animation.test-d.ts +160 -0
- package/src/components/__tests__/sprite-instances.unit.test.ts +108 -0
- package/src/components/__tests__/sprite-region-override.test-d.ts +101 -0
- package/src/components/__tests__/tile-layer.test-d.ts +33 -0
- package/src/components/__tests__/tile-layer.unit.test.ts +125 -0
- package/src/components/__tests__/tilemap.test-d.ts +35 -0
- package/src/components/__tests__/transform-vec-schema.test.ts +121 -0
- package/src/create-renderer-env-classify.ts +110 -0
- package/src/createRenderer.ts +49 -0
- package/src/dev-import-transport.ts +97 -0
- package/src/errors/environment.ts +6 -0
- package/src/index.ts +380 -0
- package/src/renderer-host.ts +33 -0
- package/src/scene-instances/__tests__/post-spawn-resolve-joints.unit.test.ts +183 -0
- package/src/scene-instances/index.ts +4 -0
- package/src/scene-utils/mount-override-fold.ts +213 -0
- package/src/sprite-param-values.ts +61 -0
- package/src/systems/__tests__/_xorshift.ts +83 -0
- package/src/systems/__tests__/argsort-in-place.test.ts +155 -0
- package/src/systems/__tests__/transparent-sort.bench.ts +218 -0
- package/src/systems/sprite-animation-tick.ts +302 -0
- package/src/systems/tonemap.ts +68 -0
- package/src/systems/transparent-sort.ts +434 -0
- package/src/tilemap-register-validate.ts +100 -0
|
@@ -0,0 +1,1668 @@
|
|
|
1
|
+
// Consolidated by feat-20260608-ci-time-cut M5 w13/w14: merges
|
|
2
|
+
// - render-system-record-pbr-ubo-stable.test.ts
|
|
3
|
+
// - render-system-record-scale-too-small.test.ts
|
|
4
|
+
// - render-system-record-sprite-region-override.test.ts
|
|
5
|
+
// - render-system-record-sprite-ubo-bytes.test.ts
|
|
6
|
+
// Each original file body sits in its own block-scope so helper names
|
|
7
|
+
// cannot collide; describe() inside still registers globally with vitest.
|
|
8
|
+
|
|
9
|
+
import { AssetRegistry } from '@forgeax/engine-assets-runtime';
|
|
10
|
+
import { World } from '@forgeax/engine-ecs';
|
|
11
|
+
import { Camera, MeshFilter, MeshRenderer } from '@forgeax/engine-render';
|
|
12
|
+
import {
|
|
13
|
+
SPRITE_PREMULTIPLIED_ALPHA_BLEND,
|
|
14
|
+
SpriteRegionOverride,
|
|
15
|
+
} from '@forgeax/engine-render/authoring';
|
|
16
|
+
import { propagateTransforms, Transform } from '@forgeax/engine-scene';
|
|
17
|
+
import { ShaderRegistry, type ShaderRegistryDevice } from '@forgeax/engine-shader';
|
|
18
|
+
import type {
|
|
19
|
+
Handle,
|
|
20
|
+
MaterialAsset,
|
|
21
|
+
MaterialPass,
|
|
22
|
+
MeshAsset,
|
|
23
|
+
ParamSchemaEntry,
|
|
24
|
+
} from '@forgeax/engine-types';
|
|
25
|
+
import { derive } from '@forgeax/engine-types';
|
|
26
|
+
import { describe, expect, it } from 'vitest';
|
|
27
|
+
import { createEngineMetrics } from '../../../render/src/engine-metrics';
|
|
28
|
+
import { isLitMaterialSnapshot } from '../../../render/src/record/helpers';
|
|
29
|
+
import * as recordModule from '../../../render/src/record/main-pass-material';
|
|
30
|
+
import { detectNineSliceScaleTooSmall } from '../../../render/src/record/main-pass-material';
|
|
31
|
+
import * as spriteRecordModule from '../../../render/src/record/main-pass-sprite-draws';
|
|
32
|
+
import { filterDispatchBySelector } from '../../../render/src/record/shadow-pass';
|
|
33
|
+
import type { DispatchEntry, MaterialSnapshot } from '../../../render/src/render-system-extract';
|
|
34
|
+
import { extractFrame, prepareExtractContext } from '../../../render/src/render-system-extract';
|
|
35
|
+
|
|
36
|
+
// ─── from render-system-record-pbr-ubo-stable.test.ts ───
|
|
37
|
+
{
|
|
38
|
+
// render-system-record-pbr-ubo-stable - feat-20260527-sprite-nineslice M2 / w6.
|
|
39
|
+
//
|
|
40
|
+
// PBR Material UBO byte-sequence regression net (D-7 isolation):
|
|
41
|
+
// any deviation in the PBR write path's byte sequence (even 1 byte) caused
|
|
42
|
+
// by the sprite-branch schema-driven UBO extension (w11) MUST fail this test
|
|
43
|
+
// — that triggers R-8 fallback (revert D-7 to physically isolated sprite
|
|
44
|
+
// write function).
|
|
45
|
+
//
|
|
46
|
+
// w11 will extract the inline PBR UBO write path
|
|
47
|
+
// (render-system-record.ts:2194-2277 -- baseColor, metallic, roughness,
|
|
48
|
+
// channelMap u32 packing, emissive, occlusionStrength, paramSnapshot
|
|
49
|
+
// schema-driven overlay) into a pure helper
|
|
50
|
+
// `buildPbrMaterialUboPayload(material) -> Uint8Array(288B)` so this
|
|
51
|
+
// regression test can exercise the PBR write byte-for-byte.
|
|
52
|
+
//
|
|
53
|
+
// The helper MUST produce the exact byte sequence the inline path produced
|
|
54
|
+
// pre-feat (no slices-related side effects on the PBR layout). This test
|
|
55
|
+
// asserts the PBR baseline against literal byte values.
|
|
56
|
+
//
|
|
57
|
+
// Coverage:
|
|
58
|
+
// - 304 B payload (STANDARD_PBR_UBO_SIZE)
|
|
59
|
+
// - slot 0 = baseColor.rgb + 1 (alpha hardcoded)
|
|
60
|
+
// - slot 1 first 8 B = metallic, roughness (f32x2)
|
|
61
|
+
// - slot 1 last 4 u32 (channelMap) = [2, 1, 0, 0]
|
|
62
|
+
// - slot 2 = emissive.rgb + emissiveIntensity
|
|
63
|
+
// - slot 3 first 4 B = occlusionStrength; rest stays 0 (no schema overlay
|
|
64
|
+
// in default-standard-pbr path; paramSnapshot reuses slot 0/1/2 by
|
|
65
|
+
// position).
|
|
66
|
+
//
|
|
67
|
+
// Anchors: plan-strategy §R-8, §5.3 key tests #2; AGENTS.md §Error model
|
|
68
|
+
// (closed-union AssetErrorCode unchanged, baseline test does not depend on
|
|
69
|
+
// new error members).
|
|
70
|
+
|
|
71
|
+
const mod = recordModule as unknown as {
|
|
72
|
+
buildPbrMaterialUboPayload?: (material: MaterialSnapshot) => Uint8Array;
|
|
73
|
+
applyMaterialTextureUvScales?: (
|
|
74
|
+
payload: ArrayBuffer | Uint8Array,
|
|
75
|
+
material: MaterialSnapshot,
|
|
76
|
+
world: World,
|
|
77
|
+
) => void;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
function makePbrSnapshot(opts: {
|
|
81
|
+
baseColor?: readonly [number, number, number, number];
|
|
82
|
+
metallic?: number;
|
|
83
|
+
roughness?: number;
|
|
84
|
+
emissive?: readonly [number, number, number];
|
|
85
|
+
emissiveIntensity?: number;
|
|
86
|
+
occlusionStrength?: number;
|
|
87
|
+
clearcoat?: number;
|
|
88
|
+
clearcoatRoughness?: number;
|
|
89
|
+
specularTint?: readonly [number, number, number];
|
|
90
|
+
}): MaterialSnapshot {
|
|
91
|
+
return {
|
|
92
|
+
baseColor: opts.baseColor ?? [0.5, 0.6, 0.7, 1],
|
|
93
|
+
metallic: opts.metallic ?? 0.1,
|
|
94
|
+
roughness: opts.roughness ?? 0.4,
|
|
95
|
+
materialShaderId: 'forgeax::default-standard-pbr',
|
|
96
|
+
paramSnapshot: undefined,
|
|
97
|
+
...(opts.emissive !== undefined && { emissive: opts.emissive }),
|
|
98
|
+
...(opts.emissiveIntensity !== undefined && { emissiveIntensity: opts.emissiveIntensity }),
|
|
99
|
+
...(opts.occlusionStrength !== undefined && { occlusionStrength: opts.occlusionStrength }),
|
|
100
|
+
...(opts.clearcoat !== undefined && { clearcoat: opts.clearcoat }),
|
|
101
|
+
...(opts.clearcoatRoughness !== undefined && { clearcoatRoughness: opts.clearcoatRoughness }),
|
|
102
|
+
...(opts.specularTint !== undefined && { specularTint: opts.specularTint }),
|
|
103
|
+
} as unknown as MaterialSnapshot;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
describe('PBR Material UBO byte sequence regression net (M2 / w6, D-7 isolation)', () => {
|
|
107
|
+
it('export: buildPbrMaterialUboPayload helper is exported from render-system-record', () => {
|
|
108
|
+
expect(typeof mod.buildPbrMaterialUboPayload).toBe('function');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('304 B payload size + standard PBR slot layout (baseline)', () => {
|
|
112
|
+
if (typeof mod.buildPbrMaterialUboPayload !== 'function') {
|
|
113
|
+
throw new Error('helper not exported yet');
|
|
114
|
+
}
|
|
115
|
+
const snap = makePbrSnapshot({
|
|
116
|
+
baseColor: [0.5, 0.6, 0.7, 1],
|
|
117
|
+
metallic: 0.1,
|
|
118
|
+
roughness: 0.4,
|
|
119
|
+
emissive: [0, 0, 0],
|
|
120
|
+
emissiveIntensity: 0,
|
|
121
|
+
occlusionStrength: 1,
|
|
122
|
+
clearcoat: 0.85,
|
|
123
|
+
clearcoatRoughness: 0.12,
|
|
124
|
+
specularTint: [0.11, 0.22, 0.33],
|
|
125
|
+
});
|
|
126
|
+
const buf = mod.buildPbrMaterialUboPayload(snap);
|
|
127
|
+
expect(buf.byteLength).toBe(304);
|
|
128
|
+
const f32 = new Float32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4);
|
|
129
|
+
// feat-20260613 fix-issue-1 (D-8 channelMap split): the 4 channelMap
|
|
130
|
+
// u32 slots collapse into 4 independent f32 channel selectors at
|
|
131
|
+
// f32[6..9] (metallicChannel/roughnessChannel/aoChannel/extraChannel).
|
|
132
|
+
// baseColor.rgb + alpha=1 (alpha hardcoded in PBR write path).
|
|
133
|
+
expect(f32[0]).toBeCloseTo(0.5);
|
|
134
|
+
expect(f32[1]).toBeCloseTo(0.6);
|
|
135
|
+
expect(f32[2]).toBeCloseTo(0.7);
|
|
136
|
+
expect(f32[3]).toBe(1);
|
|
137
|
+
// metallic + roughness (first 8 B of slot 1).
|
|
138
|
+
expect(f32[4]).toBeCloseTo(0.1);
|
|
139
|
+
expect(f32[5]).toBeCloseTo(0.4);
|
|
140
|
+
// 4 channel selectors as f32 (default = B/G/R/_ glTF packing).
|
|
141
|
+
expect(f32[6]).toBe(2); // metallicChannel <- B
|
|
142
|
+
expect(f32[7]).toBe(1); // roughnessChannel <- G
|
|
143
|
+
expect(f32[8]).toBe(0); // aoChannel <- R
|
|
144
|
+
expect(f32[9]).toBe(0); // extraChannel <- reserved
|
|
145
|
+
// offsets 40..47 implicit pad (vec3 align=16 before emissive).
|
|
146
|
+
expect(f32[10]).toBe(0);
|
|
147
|
+
expect(f32[11]).toBe(0);
|
|
148
|
+
// emissive.rgb + emissiveIntensity (offset 48..63).
|
|
149
|
+
expect(f32[12]).toBe(0);
|
|
150
|
+
expect(f32[13]).toBe(0);
|
|
151
|
+
expect(f32[14]).toBe(0);
|
|
152
|
+
expect(f32[15]).toBe(0);
|
|
153
|
+
// occlusionStrength (offset 64..67).
|
|
154
|
+
expect(f32[16]).toBe(1);
|
|
155
|
+
// alphaCutoff (offset 68).
|
|
156
|
+
expect(f32[17]).toBe(0);
|
|
157
|
+
// clearcoat layer (offsets 72..79).
|
|
158
|
+
expect(f32[18]).toBeCloseTo(0.85);
|
|
159
|
+
expect(f32[19]).toBeCloseTo(0.12);
|
|
160
|
+
// specularTint vec3 (offsets 80..92).
|
|
161
|
+
expect(f32[20]).toBeCloseTo(0.11);
|
|
162
|
+
expect(f32[21]).toBeCloseTo(0.22);
|
|
163
|
+
expect(f32[22]).toBeCloseTo(0.33);
|
|
164
|
+
expect(f32[72]).toBe(1);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('writes schema-carried alphaCutoff into the baseline PBR payload', () => {
|
|
168
|
+
if (typeof mod.buildPbrMaterialUboPayload !== 'function') {
|
|
169
|
+
throw new Error('helper not exported yet');
|
|
170
|
+
}
|
|
171
|
+
const payload = mod.buildPbrMaterialUboPayload({
|
|
172
|
+
...makePbrSnapshot({}),
|
|
173
|
+
paramSnapshot: { alphaCutoff: 0.5 },
|
|
174
|
+
} as MaterialSnapshot);
|
|
175
|
+
const f32 = new Float32Array(payload.buffer, payload.byteOffset, payload.byteLength / 4);
|
|
176
|
+
expect(f32[17]).toBeCloseTo(0.5);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('keeps engine-owned texture UV tails aligned across PBR and sprite layouts', () => {
|
|
180
|
+
const apply = mod.applyMaterialTextureUvScales;
|
|
181
|
+
if (typeof apply !== 'function') throw new Error('helper not exported yet');
|
|
182
|
+
const world = new World();
|
|
183
|
+
const pbrPayload = new ArrayBuffer(304);
|
|
184
|
+
apply(pbrPayload, makePbrSnapshot({}), world);
|
|
185
|
+
const pbrF32 = new Float32Array(pbrPayload);
|
|
186
|
+
expect(pbrF32[26]).toBe(1);
|
|
187
|
+
expect(pbrF32[34]).toBe(1);
|
|
188
|
+
expect(pbrF32[42]).toBe(1);
|
|
189
|
+
expect(pbrF32[50]).toBe(1);
|
|
190
|
+
expect(pbrF32[58]).toBe(1);
|
|
191
|
+
expect(pbrF32[66]).toBe(1);
|
|
192
|
+
|
|
193
|
+
const spritePayload = new ArrayBuffer(128);
|
|
194
|
+
apply(
|
|
195
|
+
spritePayload,
|
|
196
|
+
{ ...makePbrSnapshot({}), materialShaderId: 'forgeax::sprite' } as MaterialSnapshot,
|
|
197
|
+
world,
|
|
198
|
+
);
|
|
199
|
+
const spriteF32 = new Float32Array(spritePayload);
|
|
200
|
+
expect(Array.from(spriteF32.slice(16, 24))).toEqual([0, 0, 1, 1, 0, 0, 1, 1]);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('byte-stable across two equivalent PBR snapshots (idempotent write)', () => {
|
|
204
|
+
if (typeof mod.buildPbrMaterialUboPayload !== 'function') {
|
|
205
|
+
throw new Error('helper not exported yet');
|
|
206
|
+
}
|
|
207
|
+
const snap = makePbrSnapshot({
|
|
208
|
+
baseColor: [0.2, 0.3, 0.4, 1],
|
|
209
|
+
metallic: 0.5,
|
|
210
|
+
roughness: 0.5,
|
|
211
|
+
});
|
|
212
|
+
const a = mod.buildPbrMaterialUboPayload(snap);
|
|
213
|
+
const b = mod.buildPbrMaterialUboPayload(snap);
|
|
214
|
+
expect(new Uint8Array(a)).toEqual(new Uint8Array(b));
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ─── from render-system-record-scale-too-small.test.ts ───
|
|
220
|
+
{
|
|
221
|
+
// render-system-record-scale-too-small — feat-20260527-sprite-nineslice M4 / w17 (a).
|
|
222
|
+
//
|
|
223
|
+
// AC-16 end-to-end: when a 9-slice sprite entity carries a Transform.scale
|
|
224
|
+
// below the four corner anchors, the detection path bumps
|
|
225
|
+
// the owner metrics snapshot's `nineslice.scale-too-small` key once per offending
|
|
226
|
+
// renderableIndex per RenderSystem lifetime. AI users observe the breach
|
|
227
|
+
// through the EngineMetrics counter rather than parsing console.warn text
|
|
228
|
+
// (charter P3 machine-readable signals).
|
|
229
|
+
//
|
|
230
|
+
// The test exercises `detectNineSliceScaleTooSmall` (the pure helper extracted
|
|
231
|
+
// from recordFrame in w17 prep) so the assertion runs without a GPU device.
|
|
232
|
+
// recordFrame's inline call site forwards the same arguments to this helper,
|
|
233
|
+
// so the unit test reflects production behaviour byte-for-byte.
|
|
234
|
+
//
|
|
235
|
+
// Anchors: requirements §AC-16; plan-strategy §5.3 key tests #9; plan-strategy
|
|
236
|
+
// §D-5 EngineMetrics machine-readable counter.
|
|
237
|
+
|
|
238
|
+
function diagonalWorld(scaleX: number, scaleY: number): Float32Array {
|
|
239
|
+
// Column-major mat4 with diagonal scale, no rotation/translation.
|
|
240
|
+
// biome-ignore format: matrix layout
|
|
241
|
+
return new Float32Array([
|
|
242
|
+
scaleX, 0, 0, 0,
|
|
243
|
+
0, scaleY, 0, 0,
|
|
244
|
+
0, 0, 1, 0,
|
|
245
|
+
0, 0, 0, 1,
|
|
246
|
+
]);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
describe('detectNineSliceScaleTooSmall (M4 / w17, AC-16 E2E)', () => {
|
|
250
|
+
it('single offending entity bumps the counter to 1', () => {
|
|
251
|
+
const metrics = createEngineMetrics();
|
|
252
|
+
const seen = new Set<number>();
|
|
253
|
+
// anchor budget: |0.3| + |0.3| = 0.6 horizontal / 0.6 vertical.
|
|
254
|
+
// scale 0.4 < 0.6 -> breach on both axes.
|
|
255
|
+
detectNineSliceScaleTooSmall(diagonalWorld(0.4, 0.4), [0.3, 0.3, 0.3, 0.3], 0, seen, metrics);
|
|
256
|
+
expect(metrics.snapshot()['nineslice.scale-too-small']).toBe(1);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('repeat call on the SAME renderableIndex stays at 1 (warn-once anchor)', () => {
|
|
260
|
+
const metrics = createEngineMetrics();
|
|
261
|
+
const seen = new Set<number>();
|
|
262
|
+
for (let i = 0; i < 10; i++) {
|
|
263
|
+
detectNineSliceScaleTooSmall(
|
|
264
|
+
diagonalWorld(0.4, 0.4),
|
|
265
|
+
[0.3, 0.3, 0.3, 0.3],
|
|
266
|
+
0,
|
|
267
|
+
seen,
|
|
268
|
+
metrics,
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
expect(metrics.snapshot()['nineslice.scale-too-small']).toBe(1);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('N distinct offending entities yield counter === N', () => {
|
|
275
|
+
const metrics = createEngineMetrics();
|
|
276
|
+
const seen = new Set<number>();
|
|
277
|
+
for (let r = 0; r < 5; r++) {
|
|
278
|
+
detectNineSliceScaleTooSmall(
|
|
279
|
+
diagonalWorld(0.4, 0.4),
|
|
280
|
+
[0.3, 0.3, 0.3, 0.3],
|
|
281
|
+
r,
|
|
282
|
+
seen,
|
|
283
|
+
metrics,
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
expect(metrics.snapshot()['nineslice.scale-too-small']).toBe(5);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('compliant scale (>= anchor) does NOT bump the counter', () => {
|
|
290
|
+
const metrics = createEngineMetrics();
|
|
291
|
+
const seen = new Set<number>();
|
|
292
|
+
detectNineSliceScaleTooSmall(diagonalWorld(1.0, 1.0), [0.3, 0.3, 0.3, 0.3], 0, seen, metrics);
|
|
293
|
+
expect(metrics.snapshot()['nineslice.scale-too-small']).toBeUndefined();
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it('all-zero slices is a no-op (legacy quad path, no detection)', () => {
|
|
297
|
+
const metrics = createEngineMetrics();
|
|
298
|
+
const seen = new Set<number>();
|
|
299
|
+
// Even with a tiny scale the all-zero slices means "this is not a 9-slice"
|
|
300
|
+
detectNineSliceScaleTooSmall(diagonalWorld(0.01, 0.01), [0, 0, 0, 0], 0, seen, metrics);
|
|
301
|
+
expect(metrics.snapshot()['nineslice.scale-too-small']).toBeUndefined();
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it('tile-mode sentinel (slices.w < 0) consumed via abs() — no false negative', () => {
|
|
305
|
+
const metrics = createEngineMetrics();
|
|
306
|
+
const seen = new Set<number>();
|
|
307
|
+
// sliceMode=1 negates slices.w; the helper takes abs() so the anchor budget
|
|
308
|
+
// still totals 0.6 on the vertical axis (|0.3| + |-0.3|).
|
|
309
|
+
detectNineSliceScaleTooSmall(
|
|
310
|
+
diagonalWorld(0.4, 0.4),
|
|
311
|
+
[0.3, 0.3, 0.3, -0.3],
|
|
312
|
+
0,
|
|
313
|
+
seen,
|
|
314
|
+
metrics,
|
|
315
|
+
);
|
|
316
|
+
expect(metrics.snapshot()['nineslice.scale-too-small']).toBe(1);
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// ─── from render-system-record-sprite-region-override.test.ts ───
|
|
322
|
+
{
|
|
323
|
+
// render-system-record-sprite-region-override — feat-20260527-sprite-nineslice M4 / w17 (b).
|
|
324
|
+
//
|
|
325
|
+
// AC-14: SpriteRegionOverride per-entity UV sub-rectangle composes with 9-slice.
|
|
326
|
+
// When the entity carries the override component, the extract stage replaces
|
|
327
|
+
// the asset-side `values.region` with the override's 4-float region
|
|
328
|
+
// before downstream snapshot construction; downstream 9-slice paths therefore
|
|
329
|
+
// measure slices against the override's region.zw, not the asset's. Two
|
|
330
|
+
// renderables sharing the same MaterialAsset can render with different
|
|
331
|
+
// effective regions when only one carries the override.
|
|
332
|
+
//
|
|
333
|
+
// Coverage:
|
|
334
|
+
// (1) entity WITHOUT SpriteRegionOverride uses the asset-side region as the
|
|
335
|
+
// effective region in the snapshot.
|
|
336
|
+
// (2) entity WITH SpriteRegionOverride uses the override's region in the
|
|
337
|
+
// snapshot — a half-region [0, 0, 0.5, 1] flows through verbatim,
|
|
338
|
+
// displacing the asset's [0, 0, 1, 1].
|
|
339
|
+
// (3) Two renderables sharing the same material asset diverge on effective
|
|
340
|
+
// region — the override-bearing entity sees the override; the other
|
|
341
|
+
// sees the asset's region. Slices=[0.25,0.25,0.25,0.25] flow through
|
|
342
|
+
// both unchanged (slices remain in the [0..1] local-UV unit; the
|
|
343
|
+
// effective region only governs UV sampling, not the slices unit).
|
|
344
|
+
//
|
|
345
|
+
// Anchors: requirements §AC-14; plan-strategy §5.3 key tests #8.
|
|
346
|
+
|
|
347
|
+
function makeShaderRegistryWithSprite(): ShaderRegistry {
|
|
348
|
+
const mockDevice: ShaderRegistryDevice = {
|
|
349
|
+
createShaderModule() {
|
|
350
|
+
return {
|
|
351
|
+
ok: true,
|
|
352
|
+
value: undefined,
|
|
353
|
+
unwrap: () => undefined,
|
|
354
|
+
unwrapOr: (d: unknown) => d,
|
|
355
|
+
} as unknown as ReturnType<ShaderRegistryDevice['createShaderModule']>;
|
|
356
|
+
},
|
|
357
|
+
};
|
|
358
|
+
const sr = new ShaderRegistry({ device: mockDevice, manifestUrl: undefined });
|
|
359
|
+
// feat-20260625-refactor-sprite-as-transparent-mesh M3 / w11+w12: the
|
|
360
|
+
// sprite paramSchema mirrors the WGSL UBO struct field set (4 vec4 +
|
|
361
|
+
// baseColorTexture). Legacy user inputs (slices/sliceMode/flipX/flipY/
|
|
362
|
+
// pivot) are still accepted by extract and folded into the UBO-aligned
|
|
363
|
+
// paramSnapshot vec4 entries (D-8).
|
|
364
|
+
sr.installMaterialArtifact('forgeax::sprite', {
|
|
365
|
+
source: 'fn main() {}',
|
|
366
|
+
paramSchema: [
|
|
367
|
+
{ name: 'colorTint', type: 'vec4', default: [1.0, 1.0, 1.0, 1.0] },
|
|
368
|
+
{ name: 'region', type: 'vec4', default: [0.0, 0.0, 1.0, 1.0] },
|
|
369
|
+
{ name: 'pivotAndSize', type: 'vec4', default: [0.5, 0.5, 1.0, 1.0] },
|
|
370
|
+
{ name: 'slicesAndMode', type: 'vec4', default: [0.0, 0.0, 0.0, 0.0] },
|
|
371
|
+
{ name: 'baseColorTexture', type: 'texture2d' },
|
|
372
|
+
],
|
|
373
|
+
});
|
|
374
|
+
return sr;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const SPRITE_PASS: MaterialPass = {
|
|
378
|
+
name: 'Sprite',
|
|
379
|
+
program: { module: 'forgeax::sprite' },
|
|
380
|
+
renderState: { queue: 3000 },
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
function identity() {
|
|
384
|
+
return {
|
|
385
|
+
pos: [0, 0, 0],
|
|
386
|
+
quat: [0, 0, 0, 1],
|
|
387
|
+
scale: [1, 1, 1],
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function registerSpriteMesh(world: World): Handle<'MeshAsset', 'shared'> {
|
|
392
|
+
const positions = new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]);
|
|
393
|
+
return world.allocSharedRef<'MeshAsset', MeshAsset>('MeshAsset', {
|
|
394
|
+
kind: 'mesh',
|
|
395
|
+
vertices: new Float32Array([
|
|
396
|
+
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0,
|
|
397
|
+
1, 0, 0, 0, 0,
|
|
398
|
+
]),
|
|
399
|
+
indices: new Uint16Array([0, 1, 2]),
|
|
400
|
+
attributes: { position: positions },
|
|
401
|
+
aabb: new Float32Array([0, 0, 0, 1, 1, 1]),
|
|
402
|
+
submeshes: [
|
|
403
|
+
{
|
|
404
|
+
indexOffset: 0,
|
|
405
|
+
indexCount: 3,
|
|
406
|
+
vertexCount: 36,
|
|
407
|
+
topology: 'triangle-list',
|
|
408
|
+
materialSlot: 0,
|
|
409
|
+
},
|
|
410
|
+
],
|
|
411
|
+
|
|
412
|
+
materialSlots: [{ slotName: 'Default' }],
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function spawnCamera(world: World): void {
|
|
417
|
+
world
|
|
418
|
+
.spawn(
|
|
419
|
+
{ component: Transform, data: { ...identity(), pos: [0, 0, 5] } },
|
|
420
|
+
{
|
|
421
|
+
component: Camera,
|
|
422
|
+
data: {
|
|
423
|
+
fov: Math.PI / 4,
|
|
424
|
+
aspect: 1,
|
|
425
|
+
near: 0.1,
|
|
426
|
+
far: 100,
|
|
427
|
+
projection: 0,
|
|
428
|
+
left: -1,
|
|
429
|
+
right: 1,
|
|
430
|
+
bottom: -1,
|
|
431
|
+
top: 1,
|
|
432
|
+
},
|
|
433
|
+
},
|
|
434
|
+
)
|
|
435
|
+
.unwrap();
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
describe('SpriteRegionOverride compose 9-slice (M4 / w17b, AC-14)', () => {
|
|
439
|
+
it('(1) entity without override uses asset-side region', () => {
|
|
440
|
+
const world = new World();
|
|
441
|
+
const assets = new AssetRegistry(makeShaderRegistryWithSprite());
|
|
442
|
+
const mesh = registerSpriteMesh(world);
|
|
443
|
+
const matHandle = world.allocSharedRef<'MaterialAsset', MaterialAsset>('MaterialAsset', {
|
|
444
|
+
kind: 'material',
|
|
445
|
+
passes: [SPRITE_PASS],
|
|
446
|
+
values: { region: [0.1, 0.1, 0.8, 0.8] },
|
|
447
|
+
});
|
|
448
|
+
spawnCamera(world);
|
|
449
|
+
world
|
|
450
|
+
.spawn(
|
|
451
|
+
{ component: Transform, data: identity() },
|
|
452
|
+
{ component: MeshFilter, data: { assetHandle: mesh } },
|
|
453
|
+
{ component: MeshRenderer, data: { materials: [matHandle] } },
|
|
454
|
+
)
|
|
455
|
+
.unwrap();
|
|
456
|
+
propagateTransforms(world);
|
|
457
|
+
const frame = extractFrame(world, prepareExtractContext(world, { assets }));
|
|
458
|
+
// feat-20260625 M3 / w12: sprite filter is now materialShaderId, not
|
|
459
|
+
// shadingModel; region lands on paramSnapshot.region (D-8 / AC-07).
|
|
460
|
+
const sprite = frame.renderables.find(
|
|
461
|
+
(r) => r.material.materialShaderId === 'forgeax::sprite',
|
|
462
|
+
);
|
|
463
|
+
expect(sprite).toBeDefined();
|
|
464
|
+
expect(sprite?.material.paramSnapshot?.region).toEqual([0.1, 0.1, 0.8, 0.8]);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it('(2) entity with override uses the override region in the snapshot', () => {
|
|
468
|
+
const world = new World();
|
|
469
|
+
const assets = new AssetRegistry(makeShaderRegistryWithSprite());
|
|
470
|
+
const mesh = registerSpriteMesh(world);
|
|
471
|
+
const matHandle = world.allocSharedRef<'MaterialAsset', MaterialAsset>('MaterialAsset', {
|
|
472
|
+
kind: 'material',
|
|
473
|
+
passes: [SPRITE_PASS],
|
|
474
|
+
values: { region: [0.0, 0.0, 1.0, 1.0] },
|
|
475
|
+
});
|
|
476
|
+
spawnCamera(world);
|
|
477
|
+
world
|
|
478
|
+
.spawn(
|
|
479
|
+
{ component: Transform, data: identity() },
|
|
480
|
+
{ component: MeshFilter, data: { assetHandle: mesh } },
|
|
481
|
+
{ component: MeshRenderer, data: { materials: [matHandle] } },
|
|
482
|
+
{
|
|
483
|
+
component: SpriteRegionOverride,
|
|
484
|
+
data: { region: new Float32Array([0.0, 0.0, 0.5, 1.0]) },
|
|
485
|
+
},
|
|
486
|
+
)
|
|
487
|
+
.unwrap();
|
|
488
|
+
propagateTransforms(world);
|
|
489
|
+
const frame = extractFrame(world, prepareExtractContext(world, { assets }));
|
|
490
|
+
const sprite = frame.renderables.find(
|
|
491
|
+
(r) => r.material.materialShaderId === 'forgeax::sprite',
|
|
492
|
+
);
|
|
493
|
+
expect(sprite).toBeDefined();
|
|
494
|
+
// The override displaces the asset-side [0, 0, 1, 1] region with the
|
|
495
|
+
// half-width [0, 0, 0.5, 1] override, so downstream consumers (record
|
|
496
|
+
// stage UBO write, 9-slice anchor budget) see the override values via
|
|
497
|
+
// paramSnapshot.region (post-w12).
|
|
498
|
+
expect(sprite?.material.paramSnapshot?.region).toEqual([0, 0, 0.5, 1]);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
it('(3) two entities sharing one material diverge on effective region (compose with 9-slice)', () => {
|
|
502
|
+
const world = new World();
|
|
503
|
+
const assets = new AssetRegistry(makeShaderRegistryWithSprite());
|
|
504
|
+
const mesh = registerSpriteMesh(world);
|
|
505
|
+
const matHandle = world.allocSharedRef<'MaterialAsset', MaterialAsset>('MaterialAsset', {
|
|
506
|
+
kind: 'material',
|
|
507
|
+
passes: [SPRITE_PASS],
|
|
508
|
+
values: {
|
|
509
|
+
region: [0.0, 0.0, 1.0, 1.0],
|
|
510
|
+
slicesAndMode: [0.25, 0.25, 0.25, 0.25],
|
|
511
|
+
},
|
|
512
|
+
});
|
|
513
|
+
spawnCamera(world);
|
|
514
|
+
// entity A: no override, sees the asset-side region.
|
|
515
|
+
world
|
|
516
|
+
.spawn(
|
|
517
|
+
{ component: Transform, data: { ...identity(), pos: [-1, 0, 0] } },
|
|
518
|
+
{ component: MeshFilter, data: { assetHandle: mesh } },
|
|
519
|
+
{ component: MeshRenderer, data: { materials: [matHandle] } },
|
|
520
|
+
)
|
|
521
|
+
.unwrap();
|
|
522
|
+
// entity B: half-width override, sees the override region.
|
|
523
|
+
world
|
|
524
|
+
.spawn(
|
|
525
|
+
{ component: Transform, data: { ...identity(), pos: [1, 0, 0] } },
|
|
526
|
+
{ component: MeshFilter, data: { assetHandle: mesh } },
|
|
527
|
+
{ component: MeshRenderer, data: { materials: [matHandle] } },
|
|
528
|
+
{
|
|
529
|
+
component: SpriteRegionOverride,
|
|
530
|
+
data: { region: new Float32Array([0.0, 0.0, 0.5, 1.0]) },
|
|
531
|
+
},
|
|
532
|
+
)
|
|
533
|
+
.unwrap();
|
|
534
|
+
propagateTransforms(world);
|
|
535
|
+
const frame = extractFrame(world, prepareExtractContext(world, { assets }));
|
|
536
|
+
const sprites = frame.renderables.filter(
|
|
537
|
+
(r) => r.material.materialShaderId === 'forgeax::sprite',
|
|
538
|
+
);
|
|
539
|
+
expect(sprites.length).toBe(2);
|
|
540
|
+
const regions = sprites.map((s) => s.material.paramSnapshot?.region);
|
|
541
|
+
// One renderable lands on the asset-side region; the other lands on the
|
|
542
|
+
// override. Order is archetype-walk order (we check the set, not which is
|
|
543
|
+
// which, so the test stays robust to archetype iteration order).
|
|
544
|
+
expect(regions).toContainEqual([0, 0, 1, 1]);
|
|
545
|
+
expect(regions).toContainEqual([0, 0, 0.5, 1]);
|
|
546
|
+
// Slices ride through unchanged on both renderables -- slicesAndMode
|
|
547
|
+
// stretch encoding (positive .w, plan-strategy D-3); effective region
|
|
548
|
+
// governs UV sampling, not the slices unit.
|
|
549
|
+
for (const s of sprites) {
|
|
550
|
+
expect(s.material.paramSnapshot?.slicesAndMode).toEqual([0.25, 0.25, 0.25, 0.25]);
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// --- from render-system-record-sprite-ubo-bytes.test.ts ---
|
|
557
|
+
{
|
|
558
|
+
// feat-20260625-refactor-sprite-as-transparent-mesh M3 / w8 (TDD red).
|
|
559
|
+
//
|
|
560
|
+
// Sprite UBO bytes via the GENERIC paramSnapshot writer (M1 / w3
|
|
561
|
+
// applyParamSnapshotToUbo). The sprite path consumes the same
|
|
562
|
+
// derive(uboLayout).entries-driven overlay every other paramSchema-bound
|
|
563
|
+
// material uses, so the 4 vec4 sprite layout (colorTint / region /
|
|
564
|
+
// pivotAndSize / slicesAndMode) is produced by `applyParamSnapshotToUbo`
|
|
565
|
+
// walking a sprite-shaped paramSchema -- not by the legacy
|
|
566
|
+
// `buildSpriteMaterialUboPayload` POD helper.
|
|
567
|
+
//
|
|
568
|
+
// Plan anchors:
|
|
569
|
+
// - plan-strategy section 2 D-2 / D-6: generic std140 writer is the
|
|
570
|
+
// single UBO write path; sprite-specific builder is ablated in w13.
|
|
571
|
+
// - plan-strategy section 5.3 #1: 4-vec4 std140 layout byte correctness.
|
|
572
|
+
// - requirements AC-03: buildSpriteMaterialUboPayload export does not
|
|
573
|
+
// survive M3 (closed-loop reverse-grep gate).
|
|
574
|
+
//
|
|
575
|
+
// Red phase: the legacy helper still ships; we assert here that the new
|
|
576
|
+
// production path (generic writer + sprite-shaped paramSnapshot) produces
|
|
577
|
+
// the expected bytes AND that `buildSpriteMaterialUboPayload` is no longer
|
|
578
|
+
// exported. Both flip green when w13 lands.
|
|
579
|
+
|
|
580
|
+
const mod = recordModule as unknown as {
|
|
581
|
+
applyParamSnapshotToUbo?: (
|
|
582
|
+
payload: ArrayBuffer | Uint8Array,
|
|
583
|
+
paramSchema: readonly ParamSchemaEntry[] | undefined,
|
|
584
|
+
paramSnapshot:
|
|
585
|
+
| Readonly<Record<string, number | readonly number[] | string | undefined>>
|
|
586
|
+
| undefined,
|
|
587
|
+
) => void;
|
|
588
|
+
buildSpriteMaterialUboPayload?: unknown;
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
// Sprite UBO layout (matches sprite.wgsl post-w11 unit-quad struct: four
|
|
592
|
+
// vec4 fields, total 64 B; the writer fills the leading 64 B of the
|
|
593
|
+
// STANDARD_PBR_UBO_SIZE-sized payload buffer the runtime reuses).
|
|
594
|
+
const SPRITE_UBO_SCHEMA: readonly ParamSchemaEntry[] = [
|
|
595
|
+
{ name: 'colorTint', type: 'vec4', default: [1, 1, 1, 1] },
|
|
596
|
+
{ name: 'region', type: 'vec4', default: [0, 0, 1, 1] },
|
|
597
|
+
{ name: 'pivotAndSize', type: 'vec4', default: [0.5, 0.5, 1, 1] },
|
|
598
|
+
{ name: 'slicesAndMode', type: 'vec4', default: [0, 0, 0, 0] },
|
|
599
|
+
];
|
|
600
|
+
|
|
601
|
+
function makeSpriteSnapshot(opts: {
|
|
602
|
+
colorTintAlpha?: number;
|
|
603
|
+
region?: readonly [number, number, number, number];
|
|
604
|
+
pivot?: readonly [number, number];
|
|
605
|
+
slices?: readonly [number, number, number, number];
|
|
606
|
+
}): {
|
|
607
|
+
colorTint: readonly [number, number, number, number];
|
|
608
|
+
region: readonly [number, number, number, number];
|
|
609
|
+
pivotAndSize: readonly [number, number, number, number];
|
|
610
|
+
slicesAndMode: readonly [number, number, number, number];
|
|
611
|
+
} {
|
|
612
|
+
const pivot = opts.pivot ?? [0.5, 0.5];
|
|
613
|
+
return {
|
|
614
|
+
colorTint: [1, 1, 1, opts.colorTintAlpha ?? 1] as const,
|
|
615
|
+
region: opts.region ?? ([0, 0, 1, 1] as const),
|
|
616
|
+
// Unit quad: pivotAndSize.zw=(1,1) is the placeholder slot the shader
|
|
617
|
+
// no longer reads -- world scale flows entirely through worldFromLocal
|
|
618
|
+
// (plan-strategy section 2 D-6).
|
|
619
|
+
pivotAndSize: [pivot[0], pivot[1], 1, 1] as const,
|
|
620
|
+
slicesAndMode: (opts.slices ?? ([0, 0, 0, 0] as const)) as readonly [
|
|
621
|
+
number,
|
|
622
|
+
number,
|
|
623
|
+
number,
|
|
624
|
+
number,
|
|
625
|
+
],
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
describe('sprite Material UBO bytes via generic writer (M3 / w8)', () => {
|
|
630
|
+
it('grep: buildSpriteMaterialUboPayload is no longer exported from render-system-record (AC-03)', () => {
|
|
631
|
+
expect(mod.buildSpriteMaterialUboPayload).toBeUndefined();
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
it('(1) no slices -> 64 B leading bytes; slot 3 = [0,0,0,0]; slots 0..2 = identity defaults', () => {
|
|
635
|
+
if (typeof mod.applyParamSnapshotToUbo !== 'function') {
|
|
636
|
+
throw new Error('applyParamSnapshotToUbo not exported');
|
|
637
|
+
}
|
|
638
|
+
// 80 B buffer mirrors the per-entity slot stride the runtime allocates
|
|
639
|
+
// (STANDARD_PBR_UBO_SIZE). The sprite writer only touches the leading
|
|
640
|
+
// 64 B; trailing bytes stay zero on a fresh ArrayBuffer.
|
|
641
|
+
const buf = new ArrayBuffer(80);
|
|
642
|
+
const snap = makeSpriteSnapshot({});
|
|
643
|
+
mod.applyParamSnapshotToUbo(buf, SPRITE_UBO_SCHEMA, snap);
|
|
644
|
+
const f32 = new Float32Array(buf);
|
|
645
|
+
// colorTint slot 0 - default identity (1,1,1,1).
|
|
646
|
+
expect(Array.from(f32.slice(0, 4))).toEqual([1, 1, 1, 1]);
|
|
647
|
+
// region slot 1 - identity (no flip applied at this layer).
|
|
648
|
+
expect(Array.from(f32.slice(4, 8))).toEqual([0, 0, 1, 1]);
|
|
649
|
+
// pivotAndSize slot 2 - pivot=(0.5,0.5); .zw=(1,1) dead-slot placeholder.
|
|
650
|
+
expect(Array.from(f32.slice(8, 12))).toEqual([0.5, 0.5, 1, 1]);
|
|
651
|
+
// slicesAndMode slot 3 - no slices, all zero.
|
|
652
|
+
expect(Array.from(f32.slice(12, 16))).toEqual([0, 0, 0, 0]);
|
|
653
|
+
// Trailing 16 B left untouched (overlay semantics, charter P4).
|
|
654
|
+
expect(Array.from(f32.slice(16, 20))).toEqual([0, 0, 0, 0]);
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
it('(2) stretch slices [.25,.25,.25,.25] -> slot 3 verbatim; slots 0..2 byte-stable vs baseline', () => {
|
|
658
|
+
if (typeof mod.applyParamSnapshotToUbo !== 'function') {
|
|
659
|
+
throw new Error('applyParamSnapshotToUbo not exported');
|
|
660
|
+
}
|
|
661
|
+
const baseBuf = new ArrayBuffer(80);
|
|
662
|
+
mod.applyParamSnapshotToUbo(baseBuf, SPRITE_UBO_SCHEMA, makeSpriteSnapshot({}));
|
|
663
|
+
const stretchBuf = new ArrayBuffer(80);
|
|
664
|
+
mod.applyParamSnapshotToUbo(
|
|
665
|
+
stretchBuf,
|
|
666
|
+
SPRITE_UBO_SCHEMA,
|
|
667
|
+
makeSpriteSnapshot({ slices: [0.25, 0.25, 0.25, 0.25] }),
|
|
668
|
+
);
|
|
669
|
+
const baseF32 = new Float32Array(baseBuf);
|
|
670
|
+
const stretchF32 = new Float32Array(stretchBuf);
|
|
671
|
+
// First 48 B (slots 0..2, 12 floats) byte-stable across slices presence
|
|
672
|
+
// (plan-strategy section D-7 isolation: 9-slice expressions live only in
|
|
673
|
+
// slot 3).
|
|
674
|
+
expect(Array.from(stretchF32.slice(0, 12))).toEqual(Array.from(baseF32.slice(0, 12)));
|
|
675
|
+
// Slot 3 verbatim copy of slicesAndMode (sliceMode=0 stretch).
|
|
676
|
+
expect(Array.from(stretchF32.slice(12, 16))).toEqual([0.25, 0.25, 0.25, 0.25]);
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
it('(3) tile slicesAndMode [.25,.25,.25,-.25] -> slot 3 carries sentinel (w negative)', () => {
|
|
680
|
+
if (typeof mod.applyParamSnapshotToUbo !== 'function') {
|
|
681
|
+
throw new Error('applyParamSnapshotToUbo not exported');
|
|
682
|
+
}
|
|
683
|
+
const buf = new ArrayBuffer(80);
|
|
684
|
+
// Extract folds sliceMode=1 into a negative slicesAndMode.w (plan-
|
|
685
|
+
// strategy D-3 sentinel); record writes the snapshot verbatim.
|
|
686
|
+
mod.applyParamSnapshotToUbo(
|
|
687
|
+
buf,
|
|
688
|
+
SPRITE_UBO_SCHEMA,
|
|
689
|
+
makeSpriteSnapshot({ slices: [0.25, 0.25, 0.25, -0.25] }),
|
|
690
|
+
);
|
|
691
|
+
const baseBuf = new ArrayBuffer(80);
|
|
692
|
+
mod.applyParamSnapshotToUbo(baseBuf, SPRITE_UBO_SCHEMA, makeSpriteSnapshot({}));
|
|
693
|
+
const f32 = new Float32Array(buf);
|
|
694
|
+
const baseF32 = new Float32Array(baseBuf);
|
|
695
|
+
// First 48 B still byte-identical to baseline across slot 3 changes.
|
|
696
|
+
expect(Array.from(f32.slice(0, 12))).toEqual(Array.from(baseF32.slice(0, 12)));
|
|
697
|
+
// Slot 3 carries the tile sentinel.
|
|
698
|
+
expect(f32[12]).toBe(0.25);
|
|
699
|
+
expect(f32[13]).toBe(0.25);
|
|
700
|
+
expect(f32[14]).toBe(0.25);
|
|
701
|
+
expect(f32[15]).toBe(-0.25);
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
it('flipped region [0.5,0,-0.5,1] -> slot 1 carries flip-folded coords verbatim (D-8)', () => {
|
|
705
|
+
if (typeof mod.applyParamSnapshotToUbo !== 'function') {
|
|
706
|
+
throw new Error('applyParamSnapshotToUbo not exported');
|
|
707
|
+
}
|
|
708
|
+
const buf = new ArrayBuffer(80);
|
|
709
|
+
// Extract folds flipX into region (region.x += region.z; region.z = -region.z).
|
|
710
|
+
// Starting from identity region [0,0,1,1], flipX yields [1,0,-1,1]; the
|
|
711
|
+
// snapshot reaches the writer with the fold already applied.
|
|
712
|
+
mod.applyParamSnapshotToUbo(
|
|
713
|
+
buf,
|
|
714
|
+
SPRITE_UBO_SCHEMA,
|
|
715
|
+
makeSpriteSnapshot({ region: [1, 0, -1, 1] }),
|
|
716
|
+
);
|
|
717
|
+
const f32 = new Float32Array(buf);
|
|
718
|
+
expect(Array.from(f32.slice(4, 8))).toEqual([1, 0, -1, 1]);
|
|
719
|
+
});
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// ─── from feat-20260609 M2: dispatch selector filtering ───
|
|
724
|
+
{
|
|
725
|
+
// feat-20260609-pipeline-driven-pass-selector-shadowcaster-via-mat M2
|
|
726
|
+
// T-005: dispatch entry selector filtering unit tests.
|
|
727
|
+
//
|
|
728
|
+
// AC-03: selector { LightMode: ['Forward'] } filters to only Forward entries.
|
|
729
|
+
// AC-04: selector { LightMode: ['ShadowCaster'] } filters to only matching entity.
|
|
730
|
+
// AC-18: multiple ShadowCaster passes per entity -> filtered count = N.
|
|
731
|
+
//
|
|
732
|
+
// Anchors: plan-strategy section 5.3 key tests; requirements AC-03/AC-04/AC-18.
|
|
733
|
+
|
|
734
|
+
function makeDispatchEntry(
|
|
735
|
+
renderableIndex: number,
|
|
736
|
+
passIndex: number,
|
|
737
|
+
tags: Record<string, string>,
|
|
738
|
+
): DispatchEntry {
|
|
739
|
+
return {
|
|
740
|
+
entityIndex: renderableIndex,
|
|
741
|
+
materialHandle: 1,
|
|
742
|
+
renderableIndex,
|
|
743
|
+
passIndex,
|
|
744
|
+
queue: 2000,
|
|
745
|
+
layer: 0,
|
|
746
|
+
tags,
|
|
747
|
+
renderState: undefined,
|
|
748
|
+
defines: undefined,
|
|
749
|
+
vertexEntry: undefined,
|
|
750
|
+
fragmentEntry: undefined,
|
|
751
|
+
materialShaderId: 'forgeax::default-unlit',
|
|
752
|
+
paramSnapshot: undefined,
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
describe('dispatch entry selector filtering (M2, T-005)', () => {
|
|
757
|
+
it('AC-03: empty selector returns all dispatch entries unchanged', () => {
|
|
758
|
+
if (typeof filterDispatchBySelector !== 'function') {
|
|
759
|
+
throw new Error('filterDispatchBySelector not exported yet');
|
|
760
|
+
}
|
|
761
|
+
const dispatch: DispatchEntry[] = [
|
|
762
|
+
makeDispatchEntry(0, 0, { LightMode: 'Forward' }),
|
|
763
|
+
makeDispatchEntry(1, 0, { LightMode: 'ShadowCaster' }),
|
|
764
|
+
];
|
|
765
|
+
const result = filterDispatchBySelector(dispatch, {});
|
|
766
|
+
expect(result.length).toBe(2);
|
|
767
|
+
});
|
|
768
|
+
|
|
769
|
+
it('AC-03: selector { LightMode: [Forward] } returns only Forward entries', () => {
|
|
770
|
+
if (typeof filterDispatchBySelector !== 'function') {
|
|
771
|
+
throw new Error('filterDispatchBySelector not exported yet');
|
|
772
|
+
}
|
|
773
|
+
const dispatch: DispatchEntry[] = [
|
|
774
|
+
makeDispatchEntry(0, 0, { LightMode: 'Forward' }),
|
|
775
|
+
makeDispatchEntry(1, 0, { LightMode: 'ShadowCaster' }),
|
|
776
|
+
makeDispatchEntry(2, 0, { LightMode: 'Forward' }),
|
|
777
|
+
];
|
|
778
|
+
const result = filterDispatchBySelector(dispatch, { LightMode: ['Forward'] });
|
|
779
|
+
expect(result.length).toBe(2);
|
|
780
|
+
for (const e of result) {
|
|
781
|
+
expect(e.tags.LightMode).toBe('Forward');
|
|
782
|
+
}
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
it('AC-04: selector { LightMode: [ShadowCaster] } returns only ShadowCaster entries', () => {
|
|
786
|
+
if (typeof filterDispatchBySelector !== 'function') {
|
|
787
|
+
throw new Error('filterDispatchBySelector not exported yet');
|
|
788
|
+
}
|
|
789
|
+
const dispatch: DispatchEntry[] = [
|
|
790
|
+
makeDispatchEntry(0, 0, { LightMode: 'Forward' }),
|
|
791
|
+
makeDispatchEntry(1, 0, { LightMode: 'ShadowCaster' }),
|
|
792
|
+
makeDispatchEntry(2, 0, { LightMode: 'Forward' }),
|
|
793
|
+
];
|
|
794
|
+
const result = filterDispatchBySelector(dispatch, { LightMode: ['ShadowCaster'] });
|
|
795
|
+
expect(result.length).toBe(1);
|
|
796
|
+
expect(result[0]?.tags.LightMode).toBe('ShadowCaster');
|
|
797
|
+
expect(result[0]?.renderableIndex).toBe(1);
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
it('AC-18: multiple ShadowCaster passes per entity -> filtered count equals N', () => {
|
|
801
|
+
if (typeof filterDispatchBySelector !== 'function') {
|
|
802
|
+
throw new Error('filterDispatchBySelector not exported yet');
|
|
803
|
+
}
|
|
804
|
+
// Entity at renderableIndex=0 has 3 ShadowCaster passes.
|
|
805
|
+
const dispatch: DispatchEntry[] = [
|
|
806
|
+
makeDispatchEntry(0, 0, { LightMode: 'ShadowCaster' }),
|
|
807
|
+
makeDispatchEntry(0, 1, { LightMode: 'ShadowCaster' }),
|
|
808
|
+
makeDispatchEntry(0, 2, { LightMode: 'ShadowCaster' }),
|
|
809
|
+
makeDispatchEntry(1, 0, { LightMode: 'Forward' }),
|
|
810
|
+
];
|
|
811
|
+
const result = filterDispatchBySelector(dispatch, { LightMode: ['ShadowCaster'] });
|
|
812
|
+
expect(result.length).toBe(3);
|
|
813
|
+
for (const e of result) {
|
|
814
|
+
expect(e.tags.LightMode).toBe('ShadowCaster');
|
|
815
|
+
expect(e.renderableIndex).toBe(0);
|
|
816
|
+
}
|
|
817
|
+
// Each pass has a distinct passIndex.
|
|
818
|
+
expect(result[0]?.passIndex).toBe(0);
|
|
819
|
+
expect(result[1]?.passIndex).toBe(1);
|
|
820
|
+
expect(result[2]?.passIndex).toBe(2);
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
it('selector with non-matching key returns empty array', () => {
|
|
824
|
+
if (typeof filterDispatchBySelector !== 'function') {
|
|
825
|
+
throw new Error('filterDispatchBySelector not exported yet');
|
|
826
|
+
}
|
|
827
|
+
const dispatch: DispatchEntry[] = [makeDispatchEntry(0, 0, { LightMode: 'Forward' })];
|
|
828
|
+
const result = filterDispatchBySelector(dispatch, { RenderType: ['Opaque'] });
|
|
829
|
+
expect(result.length).toBe(0);
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
it('selector matching on multiple keys requires both to match', () => {
|
|
833
|
+
if (typeof filterDispatchBySelector !== 'function') {
|
|
834
|
+
throw new Error('filterDispatchBySelector not exported yet');
|
|
835
|
+
}
|
|
836
|
+
const dispatch: DispatchEntry[] = [
|
|
837
|
+
makeDispatchEntry(0, 0, { LightMode: 'Forward', RenderQueue: 'Geometry' }),
|
|
838
|
+
makeDispatchEntry(1, 0, { LightMode: 'Forward', RenderQueue: 'Transparent' }),
|
|
839
|
+
];
|
|
840
|
+
const result = filterDispatchBySelector(dispatch, {
|
|
841
|
+
LightMode: ['Forward'],
|
|
842
|
+
RenderQueue: ['Geometry'],
|
|
843
|
+
});
|
|
844
|
+
expect(result.length).toBe(1);
|
|
845
|
+
expect(result[0]?.renderableIndex).toBe(0);
|
|
846
|
+
});
|
|
847
|
+
|
|
848
|
+
it('entry with empty tags is filtered out by a non-empty selector', () => {
|
|
849
|
+
if (typeof filterDispatchBySelector !== 'function') {
|
|
850
|
+
throw new Error('filterDispatchBySelector not exported yet');
|
|
851
|
+
}
|
|
852
|
+
const dispatch: DispatchEntry[] = [
|
|
853
|
+
makeDispatchEntry(0, 0, {}),
|
|
854
|
+
makeDispatchEntry(1, 0, { LightMode: 'Forward' }),
|
|
855
|
+
];
|
|
856
|
+
const result = filterDispatchBySelector(dispatch, { LightMode: ['Forward'] });
|
|
857
|
+
expect(result.length).toBe(1);
|
|
858
|
+
expect(result[0]?.renderableIndex).toBe(1);
|
|
859
|
+
});
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// --- from feat-20260625-refactor-sprite-as-transparent-mesh M1 / w1 ---
|
|
864
|
+
{
|
|
865
|
+
// Generic std140 UBO writer driven by derive(paramSchema).uboLayout.entries.
|
|
866
|
+
//
|
|
867
|
+
// Plan anchors:
|
|
868
|
+
// - plan-strategy section 2 D-2: the inline overlay at render-system-
|
|
869
|
+
// record.ts:4334-4374 is generalised to read every uboLayout.entries
|
|
870
|
+
// std140 offset and write paramSnapshot[name] (numeric / numeric[])
|
|
871
|
+
// into the payload buffer. standard-pbr stays byte-identical to the
|
|
872
|
+
// pre-feat buildPbrMaterialUboPayload output (covered by the w2 suite
|
|
873
|
+
// appended below); sprite-shaped layouts (4 x vec4 stride 16) are
|
|
874
|
+
// honoured per offset/size declared by derive.
|
|
875
|
+
// - requirements AC-03 sets buildSpriteMaterialUboPayload ablation as
|
|
876
|
+
// the M3 deliverable; M1 only establishes that the generic writer can
|
|
877
|
+
// produce sprite-shaped bytes from paramSnapshot alone (no asset get,
|
|
878
|
+
// gate R-H from plan-strategy section 5.6 prevents reading internals).
|
|
879
|
+
//
|
|
880
|
+
// The suite reads render-system-record dynamically so it sits RED until
|
|
881
|
+
// w3 lands. The expected helper is a pure function with the signature:
|
|
882
|
+
//
|
|
883
|
+
// applyParamSnapshotToUbo(
|
|
884
|
+
// payload: ArrayBuffer,
|
|
885
|
+
// paramSchema: readonly ParamSchemaEntry[] | undefined,
|
|
886
|
+
// paramSnapshot: { readonly [name: string]: number | readonly number[] }
|
|
887
|
+
// | undefined,
|
|
888
|
+
// ): void
|
|
889
|
+
//
|
|
890
|
+
// The helper is internal (export-for-test) and walks
|
|
891
|
+
// derive(paramSchema).uboLayout.entries to dispatch each field by its
|
|
892
|
+
// std140 offset. Vec / color entries pull numeric arrays from the
|
|
893
|
+
// snapshot; f32 / i32 / u32 entries pull scalars.
|
|
894
|
+
|
|
895
|
+
const mod = recordModule as unknown as {
|
|
896
|
+
applyParamSnapshotToUbo?: (
|
|
897
|
+
payload: ArrayBuffer | Uint8Array,
|
|
898
|
+
paramSchema: readonly ParamSchemaEntry[] | undefined,
|
|
899
|
+
paramSnapshot:
|
|
900
|
+
| Readonly<Record<string, number | readonly number[] | string | undefined>>
|
|
901
|
+
| undefined,
|
|
902
|
+
) => void;
|
|
903
|
+
};
|
|
904
|
+
|
|
905
|
+
// Sprite-shaped paramSchema: four vec4 entries at std140 offsets
|
|
906
|
+
// 0 / 16 / 32 / 48; total = 64 bytes rounded to 16 (still 64).
|
|
907
|
+
// This mirrors the post-ablation sprite.wgsl UBO layout (M3 / w11) but
|
|
908
|
+
// the writer must already accept it in M1 so M3 only needs to wire it in.
|
|
909
|
+
const SPRITE_SHAPED_SCHEMA: readonly ParamSchemaEntry[] = [
|
|
910
|
+
{ name: 'colorTint', type: 'vec4', default: [1, 1, 1, 1] },
|
|
911
|
+
{ name: 'region', type: 'vec4', default: [0, 0, 1, 1] },
|
|
912
|
+
{ name: 'pivotAndSize', type: 'vec4', default: [0.5, 0.5, 1, 1] },
|
|
913
|
+
{ name: 'slicesAndMode', type: 'vec4', default: [0, 0, 0, 0] },
|
|
914
|
+
];
|
|
915
|
+
|
|
916
|
+
describe('applyParamSnapshotToUbo: sprite-shaped 4x vec4 std140 (M1 / w1)', () => {
|
|
917
|
+
it('export: applyParamSnapshotToUbo helper is exported from render-system-record', () => {
|
|
918
|
+
expect(typeof mod.applyParamSnapshotToUbo).toBe('function');
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
it('derive(uboLayout).entries produces four vec4 entries at offsets 0/16/32/48', () => {
|
|
922
|
+
const { uboLayout } = derive(SPRITE_SHAPED_SCHEMA);
|
|
923
|
+
expect(uboLayout.entries.length).toBe(4);
|
|
924
|
+
expect(uboLayout.entries[0]).toMatchObject({
|
|
925
|
+
name: 'colorTint',
|
|
926
|
+
offset: 0,
|
|
927
|
+
size: 16,
|
|
928
|
+
type: 'vec4',
|
|
929
|
+
});
|
|
930
|
+
expect(uboLayout.entries[1]).toMatchObject({
|
|
931
|
+
name: 'region',
|
|
932
|
+
offset: 16,
|
|
933
|
+
size: 16,
|
|
934
|
+
type: 'vec4',
|
|
935
|
+
});
|
|
936
|
+
expect(uboLayout.entries[2]).toMatchObject({
|
|
937
|
+
name: 'pivotAndSize',
|
|
938
|
+
offset: 32,
|
|
939
|
+
size: 16,
|
|
940
|
+
type: 'vec4',
|
|
941
|
+
});
|
|
942
|
+
expect(uboLayout.entries[3]).toMatchObject({
|
|
943
|
+
name: 'slicesAndMode',
|
|
944
|
+
offset: 48,
|
|
945
|
+
size: 16,
|
|
946
|
+
type: 'vec4',
|
|
947
|
+
});
|
|
948
|
+
// std140 struct alignment rounds up to 16 -- 64 already aligns.
|
|
949
|
+
expect(uboLayout.totalBytes).toBe(64);
|
|
950
|
+
});
|
|
951
|
+
|
|
952
|
+
it('writes each vec4 at its std140 offset (byte-exact, no f32-slot heuristic)', () => {
|
|
953
|
+
if (typeof mod.applyParamSnapshotToUbo !== 'function') {
|
|
954
|
+
throw new Error('helper not exported yet (red phase)');
|
|
955
|
+
}
|
|
956
|
+
const buf = new ArrayBuffer(64);
|
|
957
|
+
// Values chosen to round-trip exactly in IEEE 754 f32 (powers of 2 /
|
|
958
|
+
// sums thereof) so we can assert with toEqual; the non-exact-encoding
|
|
959
|
+
// values (0.1 / 0.2 / 0.3) would lose the trailing bit on f32 storage
|
|
960
|
+
// and force a toBeCloseTo, weakening the byte-level signal.
|
|
961
|
+
const snapshot = {
|
|
962
|
+
colorTint: [0.25, 0.5, 0.75, 1] as readonly number[],
|
|
963
|
+
region: [0.125, 0.25, 0.5, 0.75] as readonly number[],
|
|
964
|
+
pivotAndSize: [0.5, 0.5, 2, 4] as readonly number[],
|
|
965
|
+
slicesAndMode: [0.125, 0.125, 0.125, -0.125] as readonly number[],
|
|
966
|
+
};
|
|
967
|
+
mod.applyParamSnapshotToUbo(buf, SPRITE_SHAPED_SCHEMA, snapshot);
|
|
968
|
+
const f32 = new Float32Array(buf);
|
|
969
|
+
// colorTint at offset 0
|
|
970
|
+
expect(Array.from(f32.slice(0, 4))).toEqual([0.25, 0.5, 0.75, 1]);
|
|
971
|
+
// region at offset 16 (f32 index 4)
|
|
972
|
+
expect(Array.from(f32.slice(4, 8))).toEqual([0.125, 0.25, 0.5, 0.75]);
|
|
973
|
+
// pivotAndSize at offset 32 (f32 index 8)
|
|
974
|
+
expect(Array.from(f32.slice(8, 12))).toEqual([0.5, 0.5, 2, 4]);
|
|
975
|
+
// slicesAndMode at offset 48 (f32 index 12) -- carries 9-slice tile
|
|
976
|
+
// sentinel via negative w (verbatim from snapshot, charter P3 explicit
|
|
977
|
+
// signal vs the legacy spriteFields.sliceMode side channel).
|
|
978
|
+
expect(Array.from(f32.slice(12, 16))).toEqual([0.125, 0.125, 0.125, -0.125]);
|
|
979
|
+
});
|
|
980
|
+
|
|
981
|
+
it('missing snapshot fields leave existing bytes untouched (overlay semantics)', () => {
|
|
982
|
+
if (typeof mod.applyParamSnapshotToUbo !== 'function') {
|
|
983
|
+
throw new Error('helper not exported yet (red phase)');
|
|
984
|
+
}
|
|
985
|
+
const buf = new ArrayBuffer(64);
|
|
986
|
+
const pre = new Float32Array(buf);
|
|
987
|
+
// Seed the buffer with sentinel pre-values; the writer must only
|
|
988
|
+
// touch offsets it has snapshot values for (charter P4 -- the writer
|
|
989
|
+
// is an overlay, not a clear-and-fill stamp).
|
|
990
|
+
pre[0] = 9;
|
|
991
|
+
pre[1] = 9;
|
|
992
|
+
pre[2] = 9;
|
|
993
|
+
pre[3] = 9;
|
|
994
|
+
pre[12] = 7;
|
|
995
|
+
pre[13] = 7;
|
|
996
|
+
pre[14] = 7;
|
|
997
|
+
pre[15] = 7;
|
|
998
|
+
const snapshot = {
|
|
999
|
+
region: [0.125, 0.25, 0.5, 0.75] as readonly number[],
|
|
1000
|
+
pivotAndSize: [0.5, 0.5, 2, 4] as readonly number[],
|
|
1001
|
+
};
|
|
1002
|
+
mod.applyParamSnapshotToUbo(buf, SPRITE_SHAPED_SCHEMA, snapshot);
|
|
1003
|
+
const f32 = new Float32Array(buf);
|
|
1004
|
+
// colorTint (no snapshot value) preserved
|
|
1005
|
+
expect(Array.from(f32.slice(0, 4))).toEqual([9, 9, 9, 9]);
|
|
1006
|
+
// region written
|
|
1007
|
+
expect(Array.from(f32.slice(4, 8))).toEqual([0.125, 0.25, 0.5, 0.75]);
|
|
1008
|
+
// pivotAndSize written
|
|
1009
|
+
expect(Array.from(f32.slice(8, 12))).toEqual([0.5, 0.5, 2, 4]);
|
|
1010
|
+
// slicesAndMode (no snapshot value) preserved
|
|
1011
|
+
expect(Array.from(f32.slice(12, 16))).toEqual([7, 7, 7, 7]);
|
|
1012
|
+
});
|
|
1013
|
+
|
|
1014
|
+
it('no-op when paramSchema or paramSnapshot is undefined', () => {
|
|
1015
|
+
if (typeof mod.applyParamSnapshotToUbo !== 'function') {
|
|
1016
|
+
throw new Error('helper not exported yet (red phase)');
|
|
1017
|
+
}
|
|
1018
|
+
const buf = new ArrayBuffer(64);
|
|
1019
|
+
const pre = new Float32Array(buf);
|
|
1020
|
+
for (let i = 0; i < 16; i++) pre[i] = i + 1;
|
|
1021
|
+
const snapshot = { colorTint: [0.5, 0.5, 0.5, 0.5] as readonly number[] };
|
|
1022
|
+
mod.applyParamSnapshotToUbo(buf, undefined, snapshot);
|
|
1023
|
+
// Schema undefined -> no writes.
|
|
1024
|
+
for (let i = 0; i < 16; i++) expect(pre[i]).toBe(i + 1);
|
|
1025
|
+
mod.applyParamSnapshotToUbo(buf, SPRITE_SHAPED_SCHEMA, undefined);
|
|
1026
|
+
// Snapshot undefined -> no writes.
|
|
1027
|
+
for (let i = 0; i < 16; i++) expect(pre[i]).toBe(i + 1);
|
|
1028
|
+
});
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
// --- from feat-20260625-refactor-sprite-as-transparent-mesh M1 / w2 ---
|
|
1033
|
+
{
|
|
1034
|
+
// standard-pbr regression: the generic writer over the engine-shipped
|
|
1035
|
+
// default-standard-pbr.material.json paramSchema (15 numeric entries
|
|
1036
|
+
// std140-packed into 96 B) must produce byte-identical output to
|
|
1037
|
+
// buildPbrMaterialUboPayload. This is the gate plan-strategy section 2
|
|
1038
|
+
// D-2 sets so the generic writer can replace the inline overlay without
|
|
1039
|
+
// perturbing PBR rendering. RED until w3 lands the helper.
|
|
1040
|
+
|
|
1041
|
+
const mod = recordModule as unknown as {
|
|
1042
|
+
applyParamSnapshotToUbo?: (
|
|
1043
|
+
payload: ArrayBuffer | Uint8Array,
|
|
1044
|
+
paramSchema: readonly ParamSchemaEntry[] | undefined,
|
|
1045
|
+
paramSnapshot:
|
|
1046
|
+
| Readonly<Record<string, number | readonly number[] | string | undefined>>
|
|
1047
|
+
| undefined,
|
|
1048
|
+
) => void;
|
|
1049
|
+
buildPbrMaterialUboPayload?: (material: MaterialSnapshot) => Uint8Array;
|
|
1050
|
+
};
|
|
1051
|
+
|
|
1052
|
+
// Mirror the engine-shipped default-standard-pbr.material.json schema
|
|
1053
|
+
// (the on-disk SSOT; inline here to keep the unit suite free of asset
|
|
1054
|
+
// loads).
|
|
1055
|
+
const STANDARD_PBR_SCHEMA: readonly ParamSchemaEntry[] = [
|
|
1056
|
+
{ name: 'baseColor', type: 'color', default: [1, 1, 1, 1] },
|
|
1057
|
+
{ name: 'metallic', type: 'f32', default: 0 },
|
|
1058
|
+
{ name: 'roughness', type: 'f32', default: 0.5 },
|
|
1059
|
+
{ name: 'metallicChannel', type: 'f32', default: 2 },
|
|
1060
|
+
{ name: 'roughnessChannel', type: 'f32', default: 1 },
|
|
1061
|
+
{ name: 'aoChannel', type: 'f32', default: 0 },
|
|
1062
|
+
{ name: 'extraChannel', type: 'f32', default: 0 },
|
|
1063
|
+
{ name: 'emissive', type: 'vec3', default: [0, 0, 0] },
|
|
1064
|
+
{ name: 'emissiveIntensity', type: 'f32', default: 0 },
|
|
1065
|
+
{ name: 'occlusionStrength', type: 'f32', default: 1 },
|
|
1066
|
+
{ name: 'alphaCutoff', type: 'f32', default: 0 },
|
|
1067
|
+
{ name: 'clearcoat', type: 'f32', default: 0 },
|
|
1068
|
+
{ name: 'clearcoatRoughness', type: 'f32', default: 0.5 },
|
|
1069
|
+
{ name: 'specularTint', type: 'vec3', default: [1, 1, 1] },
|
|
1070
|
+
];
|
|
1071
|
+
|
|
1072
|
+
describe('applyParamSnapshotToUbo: standard-pbr byte-identical via derive (M1 / w2)', () => {
|
|
1073
|
+
it('derive(standard-pbr).uboLayout matches the 96 B authored numeric layout', () => {
|
|
1074
|
+
const { uboLayout } = derive(STANDARD_PBR_SCHEMA);
|
|
1075
|
+
expect(uboLayout.totalBytes).toBe(96);
|
|
1076
|
+
const byName = new Map(uboLayout.entries.map((e) => [e.name, e]));
|
|
1077
|
+
expect(byName.get('baseColor')?.offset).toBe(0);
|
|
1078
|
+
expect(byName.get('metallic')?.offset).toBe(16);
|
|
1079
|
+
expect(byName.get('roughness')?.offset).toBe(20);
|
|
1080
|
+
expect(byName.get('metallicChannel')?.offset).toBe(24);
|
|
1081
|
+
expect(byName.get('roughnessChannel')?.offset).toBe(28);
|
|
1082
|
+
expect(byName.get('aoChannel')?.offset).toBe(32);
|
|
1083
|
+
expect(byName.get('extraChannel')?.offset).toBe(36);
|
|
1084
|
+
// vec3 aligns to 16: cursor at 40 rounds up to 48.
|
|
1085
|
+
expect(byName.get('emissive')?.offset).toBe(48);
|
|
1086
|
+
expect(byName.get('emissiveIntensity')?.offset).toBe(60);
|
|
1087
|
+
expect(byName.get('occlusionStrength')?.offset).toBe(64);
|
|
1088
|
+
expect(byName.get('alphaCutoff')?.offset).toBe(68);
|
|
1089
|
+
expect(byName.get('clearcoat')?.offset).toBe(72);
|
|
1090
|
+
expect(byName.get('clearcoatRoughness')?.offset).toBe(76);
|
|
1091
|
+
expect(byName.get('specularTint')?.offset).toBe(80);
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
it('generic writer over standard-pbr snapshot equals buildPbrMaterialUboPayload bytes', () => {
|
|
1095
|
+
if (typeof mod.applyParamSnapshotToUbo !== 'function') {
|
|
1096
|
+
throw new Error('helper not exported yet (red phase)');
|
|
1097
|
+
}
|
|
1098
|
+
if (typeof mod.buildPbrMaterialUboPayload !== 'function') {
|
|
1099
|
+
throw new Error('buildPbrMaterialUboPayload missing -- baseline broken');
|
|
1100
|
+
}
|
|
1101
|
+
// Construct a snapshot equivalent to the explicit material fields the
|
|
1102
|
+
// legacy helper consumes (baseColor / metallic / roughness / emissive /
|
|
1103
|
+
// emissiveIntensity / occlusionStrength + the channelMap defaults the
|
|
1104
|
+
// helper hard-codes).
|
|
1105
|
+
const baseColor = [0.5, 0.6, 0.7, 1] as readonly number[];
|
|
1106
|
+
const metallic = 0.1;
|
|
1107
|
+
const roughness = 0.4;
|
|
1108
|
+
const emissive = [0, 0, 0] as readonly number[];
|
|
1109
|
+
const emissiveIntensity = 0;
|
|
1110
|
+
const occlusionStrength = 1;
|
|
1111
|
+
const material = {
|
|
1112
|
+
baseColor,
|
|
1113
|
+
metallic,
|
|
1114
|
+
roughness,
|
|
1115
|
+
materialShaderId: 'forgeax::default-standard-pbr',
|
|
1116
|
+
paramSnapshot: undefined,
|
|
1117
|
+
emissive,
|
|
1118
|
+
emissiveIntensity,
|
|
1119
|
+
occlusionStrength,
|
|
1120
|
+
alphaCutoff: 0,
|
|
1121
|
+
clearcoat: 0,
|
|
1122
|
+
clearcoatRoughness: 0.5,
|
|
1123
|
+
specularTint: [1, 1, 1],
|
|
1124
|
+
} as unknown as MaterialSnapshot;
|
|
1125
|
+
const baseline = mod.buildPbrMaterialUboPayload(material);
|
|
1126
|
+
// Construct the generic-writer output: start from the same explicit
|
|
1127
|
+
// PBR baseline, then apply the generic overlay over a paramSnapshot
|
|
1128
|
+
// carrying the same values. The overlay must not perturb any byte
|
|
1129
|
+
// the explicit baseline already wrote (charter P4 -- one writer, one
|
|
1130
|
+
// byte layout).
|
|
1131
|
+
const candidate = mod.buildPbrMaterialUboPayload(material);
|
|
1132
|
+
const snapshot = {
|
|
1133
|
+
baseColor,
|
|
1134
|
+
metallic,
|
|
1135
|
+
roughness,
|
|
1136
|
+
metallicChannel: 2,
|
|
1137
|
+
roughnessChannel: 1,
|
|
1138
|
+
aoChannel: 0,
|
|
1139
|
+
extraChannel: 0,
|
|
1140
|
+
emissive,
|
|
1141
|
+
emissiveIntensity,
|
|
1142
|
+
occlusionStrength,
|
|
1143
|
+
specularTint: [1, 1, 1],
|
|
1144
|
+
};
|
|
1145
|
+
mod.applyParamSnapshotToUbo(candidate, STANDARD_PBR_SCHEMA, snapshot);
|
|
1146
|
+
expect(new Uint8Array(candidate)).toEqual(new Uint8Array(baseline));
|
|
1147
|
+
});
|
|
1148
|
+
|
|
1149
|
+
it('overlays non-opaque standard-PBR baseColor alpha over the opaque baseline', () => {
|
|
1150
|
+
if (typeof mod.applyParamSnapshotToUbo !== 'function') {
|
|
1151
|
+
throw new Error('helper not exported yet (red phase)');
|
|
1152
|
+
}
|
|
1153
|
+
if (typeof mod.buildPbrMaterialUboPayload !== 'function') {
|
|
1154
|
+
throw new Error('buildPbrMaterialUboPayload missing -- baseline broken');
|
|
1155
|
+
}
|
|
1156
|
+
const material = {
|
|
1157
|
+
baseColor: [0.5, 0.6, 0.7, 0.5],
|
|
1158
|
+
metallic: 0,
|
|
1159
|
+
roughness: 0.5,
|
|
1160
|
+
materialShaderId: 'forgeax::default-standard-pbr',
|
|
1161
|
+
paramSnapshot: undefined,
|
|
1162
|
+
} as unknown as MaterialSnapshot;
|
|
1163
|
+
const payload = mod.buildPbrMaterialUboPayload(material);
|
|
1164
|
+
// The explicit baseline is intentionally opaque; importer-supplied
|
|
1165
|
+
// paramSnapshot values must overwrite all four baseColor components.
|
|
1166
|
+
expect(new Float32Array(payload.buffer, payload.byteOffset, payload.byteLength / 4)[3]).toBe(
|
|
1167
|
+
1,
|
|
1168
|
+
);
|
|
1169
|
+
mod.applyParamSnapshotToUbo(payload, STANDARD_PBR_SCHEMA, {
|
|
1170
|
+
baseColor: [0.5, 0.6, 0.7, 0.5],
|
|
1171
|
+
});
|
|
1172
|
+
expect(new Float32Array(payload.buffer, payload.byteOffset, payload.byteLength / 4)[3]).toBe(
|
|
1173
|
+
0.5,
|
|
1174
|
+
);
|
|
1175
|
+
});
|
|
1176
|
+
|
|
1177
|
+
it('honours f32 slot offsets independently (slot 1 width = 4 floats from metallic..extraChannel)', () => {
|
|
1178
|
+
if (typeof mod.applyParamSnapshotToUbo !== 'function') {
|
|
1179
|
+
throw new Error('helper not exported yet (red phase)');
|
|
1180
|
+
}
|
|
1181
|
+
const buf = new ArrayBuffer(80);
|
|
1182
|
+
// f32-exact values (powers of 2 / sums) so we can assert toEqual on
|
|
1183
|
+
// every slot; toBeCloseTo would let off-by-one-bit drifts hide.
|
|
1184
|
+
const snapshot = {
|
|
1185
|
+
baseColor: [0.125, 0.25, 0.5, 1] as readonly number[],
|
|
1186
|
+
metallic: 0.5,
|
|
1187
|
+
roughness: 0.25,
|
|
1188
|
+
metallicChannel: 2,
|
|
1189
|
+
roughnessChannel: 1,
|
|
1190
|
+
aoChannel: 0,
|
|
1191
|
+
extraChannel: 0,
|
|
1192
|
+
emissive: [0.5, 0.5, 0.5] as readonly number[],
|
|
1193
|
+
emissiveIntensity: 1.5,
|
|
1194
|
+
occlusionStrength: 0.75,
|
|
1195
|
+
};
|
|
1196
|
+
mod.applyParamSnapshotToUbo(buf, STANDARD_PBR_SCHEMA, snapshot);
|
|
1197
|
+
const f32 = new Float32Array(buf);
|
|
1198
|
+
// baseColor occupies slot 0 (offset 0).
|
|
1199
|
+
expect(Array.from(f32.slice(0, 4))).toEqual([0.125, 0.25, 0.5, 1]);
|
|
1200
|
+
// 4 f32 channels packed at offset 16..36 -- distinct slots, not folded
|
|
1201
|
+
// into one vec4 (regression against legacy "first two f32 only"
|
|
1202
|
+
// overlay at render-system-record.ts:4334-4374).
|
|
1203
|
+
expect(f32[4]).toBe(0.5);
|
|
1204
|
+
expect(f32[5]).toBe(0.25);
|
|
1205
|
+
expect(f32[6]).toBe(2);
|
|
1206
|
+
expect(f32[7]).toBe(1);
|
|
1207
|
+
expect(f32[8]).toBe(0);
|
|
1208
|
+
expect(f32[9]).toBe(0);
|
|
1209
|
+
// emissive vec3 at offset 48 (f32 index 12).
|
|
1210
|
+
expect(Array.from(f32.slice(12, 15))).toEqual([0.5, 0.5, 0.5]);
|
|
1211
|
+
expect(f32[15]).toBe(1.5);
|
|
1212
|
+
expect(f32[16]).toBe(0.75);
|
|
1213
|
+
});
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
// --- from feat-20260625-refactor-sprite-as-transparent-mesh M2 / w4 ---
|
|
1218
|
+
{
|
|
1219
|
+
// AC-05: a MaterialSnapshot carrying transparent:true on its source pass
|
|
1220
|
+
// (recorded as MaterialSnapshot.transparent === true) must drive the LDR
|
|
1221
|
+
// split-pass decision and the blend-state lookup INDEPENDENTLY of the
|
|
1222
|
+
// shader id. The shader here is a generic 'forgeax::unlit-test' stub -- if
|
|
1223
|
+
// splitLdrSprite or the blend-state helper still keys on
|
|
1224
|
+
// 'forgeax::sprite', this suite stays red.
|
|
1225
|
+
//
|
|
1226
|
+
// Plan anchors:
|
|
1227
|
+
// - requirements AC-05 (transparent decoupled from sprite shader)
|
|
1228
|
+
// - plan-strategy section 2 D-3 (MaterialSnapshot.transparent
|
|
1229
|
+
// transparent passthrough; split reads snapshot, not pass)
|
|
1230
|
+
// - plan-strategy section 5.4 falsification check (the inline comment
|
|
1231
|
+
// below records the variant: flipping blend src=one/dst=zero turns
|
|
1232
|
+
// premultiplied alpha into hard-edge composition and fails the dawn
|
|
1233
|
+
// msaa-sprite-pixel-diff baseline -- documenting that the pixel
|
|
1234
|
+
// diff smoke is sensitive to blend factor correctness).
|
|
1235
|
+
//
|
|
1236
|
+
// The `computeSplitLdrSprite` helper is added by w7. The dynamic-import
|
|
1237
|
+
// sentinel keeps this suite red until it lands.
|
|
1238
|
+
//
|
|
1239
|
+
// feat-20260626-sprite-transparent-collapse M2 / M4: the sibling
|
|
1240
|
+
// `resolveTransparentBlendState` helper has been removed — premultiplied-
|
|
1241
|
+
// alpha blend factors are now pinned to the exported
|
|
1242
|
+
// `SPRITE_PREMULTIPLIED_ALPHA_BLEND` constant (the SSOT for asset-side
|
|
1243
|
+
// `renderState.blend`); the tests that exercised the resolver are deleted
|
|
1244
|
+
// and the falsification check now lives on the dawn pixel-diff
|
|
1245
|
+
// baseline + the asset-side constant export check.
|
|
1246
|
+
|
|
1247
|
+
type DispatchEntryLike = {
|
|
1248
|
+
readonly source: {
|
|
1249
|
+
readonly material: MaterialSnapshot;
|
|
1250
|
+
readonly materials: readonly MaterialSnapshot[];
|
|
1251
|
+
};
|
|
1252
|
+
};
|
|
1253
|
+
|
|
1254
|
+
const spriteMod = spriteRecordModule as unknown as {
|
|
1255
|
+
computeSplitLdrSprite?: (
|
|
1256
|
+
validatedOrdered: readonly (DispatchEntryLike | undefined)[],
|
|
1257
|
+
tonemapActive: boolean,
|
|
1258
|
+
) => boolean;
|
|
1259
|
+
};
|
|
1260
|
+
const mod = recordModule as unknown as {
|
|
1261
|
+
isEntityFullyTransparent?: (source: {
|
|
1262
|
+
material: MaterialSnapshot;
|
|
1263
|
+
materials?: readonly MaterialSnapshot[];
|
|
1264
|
+
}) => boolean;
|
|
1265
|
+
entityHasTransparentSubmesh?: (source: {
|
|
1266
|
+
material: MaterialSnapshot;
|
|
1267
|
+
materials?: readonly MaterialSnapshot[];
|
|
1268
|
+
}) => boolean;
|
|
1269
|
+
};
|
|
1270
|
+
|
|
1271
|
+
function makeTransparentSnap(opts: {
|
|
1272
|
+
transparent?: boolean;
|
|
1273
|
+
materialShaderId?: string;
|
|
1274
|
+
}): MaterialSnapshot {
|
|
1275
|
+
return {
|
|
1276
|
+
baseColor: [1, 1, 1, 1] as const,
|
|
1277
|
+
metallic: 0,
|
|
1278
|
+
roughness: 1,
|
|
1279
|
+
materialShaderId: opts.materialShaderId ?? 'forgeax::unlit-test',
|
|
1280
|
+
paramSnapshot: {},
|
|
1281
|
+
...(opts.transparent === true && { transparent: true }),
|
|
1282
|
+
} as unknown as MaterialSnapshot;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
function makeValidatedEntry(material: MaterialSnapshot): DispatchEntryLike {
|
|
1286
|
+
return { source: { material, materials: [material] } };
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
describe('transparent decouples from sprite shader (M2 / w4, AC-05)', () => {
|
|
1290
|
+
it('export: computeSplitLdrSprite helper is exported from render-system-record', () => {
|
|
1291
|
+
expect(typeof spriteMod.computeSplitLdrSprite).toBe('function');
|
|
1292
|
+
});
|
|
1293
|
+
|
|
1294
|
+
it('non-sprite shader with transparent:true triggers LDR split', () => {
|
|
1295
|
+
if (typeof spriteMod.computeSplitLdrSprite !== 'function') {
|
|
1296
|
+
throw new Error('computeSplitLdrSprite helper not exported yet (red phase)');
|
|
1297
|
+
}
|
|
1298
|
+
const transparentSnap = makeTransparentSnap({
|
|
1299
|
+
transparent: true,
|
|
1300
|
+
materialShaderId: 'forgeax::unlit-test',
|
|
1301
|
+
});
|
|
1302
|
+
const opaqueSnap = makeTransparentSnap({
|
|
1303
|
+
transparent: false,
|
|
1304
|
+
materialShaderId: 'forgeax::unlit-test',
|
|
1305
|
+
});
|
|
1306
|
+
// tonemapActive=false (LDR path) + a transparent entry in the
|
|
1307
|
+
// validated list -- the split decision must trip even though the
|
|
1308
|
+
// shader is not forgeax::sprite (AC-05 proves the decoupling).
|
|
1309
|
+
const split = spriteMod.computeSplitLdrSprite(
|
|
1310
|
+
[makeValidatedEntry(opaqueSnap), makeValidatedEntry(transparentSnap)],
|
|
1311
|
+
false,
|
|
1312
|
+
);
|
|
1313
|
+
expect(split).toBe(true);
|
|
1314
|
+
});
|
|
1315
|
+
|
|
1316
|
+
it('LDR split stays false on pure-opaque non-sprite list', () => {
|
|
1317
|
+
if (typeof spriteMod.computeSplitLdrSprite !== 'function') {
|
|
1318
|
+
throw new Error('computeSplitLdrSprite helper not exported yet (red phase)');
|
|
1319
|
+
}
|
|
1320
|
+
const opaqueA = makeTransparentSnap({
|
|
1321
|
+
transparent: false,
|
|
1322
|
+
materialShaderId: 'forgeax::unlit-test',
|
|
1323
|
+
});
|
|
1324
|
+
const opaqueB = makeTransparentSnap({
|
|
1325
|
+
transparent: false,
|
|
1326
|
+
materialShaderId: 'forgeax::default-standard-pbr',
|
|
1327
|
+
});
|
|
1328
|
+
const split = spriteMod.computeSplitLdrSprite(
|
|
1329
|
+
[makeValidatedEntry(opaqueA), makeValidatedEntry(opaqueB)],
|
|
1330
|
+
false,
|
|
1331
|
+
);
|
|
1332
|
+
expect(split).toBe(false);
|
|
1333
|
+
});
|
|
1334
|
+
|
|
1335
|
+
it('HDR path (tonemapActive=true) suppresses split even with transparent entries', () => {
|
|
1336
|
+
if (typeof spriteMod.computeSplitLdrSprite !== 'function') {
|
|
1337
|
+
throw new Error('computeSplitLdrSprite helper not exported yet (red phase)');
|
|
1338
|
+
}
|
|
1339
|
+
const transparentSnap = makeTransparentSnap({
|
|
1340
|
+
transparent: true,
|
|
1341
|
+
materialShaderId: 'forgeax::unlit-test',
|
|
1342
|
+
});
|
|
1343
|
+
const split = spriteMod.computeSplitLdrSprite([makeValidatedEntry(transparentSnap)], true);
|
|
1344
|
+
expect(split).toBe(false);
|
|
1345
|
+
});
|
|
1346
|
+
|
|
1347
|
+
it('M3 / w13: legacy sprite material without transparent:true does NOT trigger split (union arm deleted)', () => {
|
|
1348
|
+
if (typeof spriteMod.computeSplitLdrSprite !== 'function') {
|
|
1349
|
+
throw new Error('computeSplitLdrSprite helper not exported yet (red phase)');
|
|
1350
|
+
}
|
|
1351
|
+
// feat-20260625-refactor-sprite-as-transparent-mesh M3 / w13 (D-3):
|
|
1352
|
+
// the M2 union (`transparent || shadingModel === 'sprite'`) is gone
|
|
1353
|
+
// — transparent is the single SSOT. A legacy snapshot carrying
|
|
1354
|
+
// `materialShaderId: 'forgeax::sprite'` but no `transparent: true`
|
|
1355
|
+
// declaration MUST NOT trigger the LDR split (forces explicit
|
|
1356
|
+
// transparent attribution on the asset side; AC-04 / AC-05).
|
|
1357
|
+
const legacySpriteNoTransparent = {
|
|
1358
|
+
baseColor: [1, 1, 1, 1] as const,
|
|
1359
|
+
metallic: 0,
|
|
1360
|
+
roughness: 1,
|
|
1361
|
+
materialShaderId: 'forgeax::sprite',
|
|
1362
|
+
paramSnapshot: {},
|
|
1363
|
+
} as unknown as MaterialSnapshot;
|
|
1364
|
+
const splitWithoutTransparent = spriteMod.computeSplitLdrSprite(
|
|
1365
|
+
[makeValidatedEntry(legacySpriteNoTransparent)],
|
|
1366
|
+
false,
|
|
1367
|
+
);
|
|
1368
|
+
expect(splitWithoutTransparent).toBe(false);
|
|
1369
|
+
|
|
1370
|
+
// Same snapshot with `transparent: true` declared on the pass DOES
|
|
1371
|
+
// trigger the split (the only path that should).
|
|
1372
|
+
const spriteTransparent = {
|
|
1373
|
+
...legacySpriteNoTransparent,
|
|
1374
|
+
transparent: true,
|
|
1375
|
+
} as unknown as MaterialSnapshot;
|
|
1376
|
+
const splitWithTransparent = spriteMod.computeSplitLdrSprite(
|
|
1377
|
+
[makeValidatedEntry(spriteTransparent)],
|
|
1378
|
+
false,
|
|
1379
|
+
);
|
|
1380
|
+
expect(splitWithTransparent).toBe(true);
|
|
1381
|
+
});
|
|
1382
|
+
|
|
1383
|
+
it('transparent:true resolves premultiplied-alpha blend state (src=one/dst=one-minus-src-alpha)', () => {
|
|
1384
|
+
// feat-20260626-sprite-transparent-collapse M2 / M4: the
|
|
1385
|
+
// `resolveTransparentBlendState` resolver is gone — premultiplied-alpha
|
|
1386
|
+
// factors are pinned to the exported `SPRITE_PREMULTIPLIED_ALPHA_BLEND`
|
|
1387
|
+
// constant (the asset-side SSOT for `renderState.blend`). This test
|
|
1388
|
+
// pins the constant's shape so a future maintainer flipping the color
|
|
1389
|
+
// blend to {src:'one', dst:'zero'} (opaque copy) still trips a unit
|
|
1390
|
+
// gate before the dawn msaa-sprite-pixel-diff smoke catches the
|
|
1391
|
+
// regression. AC-10 reaches the human arbiter through both paths.
|
|
1392
|
+
expect(SPRITE_PREMULTIPLIED_ALPHA_BLEND.color.srcFactor).toBe('one');
|
|
1393
|
+
expect(SPRITE_PREMULTIPLIED_ALPHA_BLEND.color.dstFactor).toBe('one-minus-src-alpha');
|
|
1394
|
+
expect(SPRITE_PREMULTIPLIED_ALPHA_BLEND.alpha.srcFactor).toBe('one');
|
|
1395
|
+
expect(SPRITE_PREMULTIPLIED_ALPHA_BLEND.alpha.dstFactor).toBe('one-minus-src-alpha');
|
|
1396
|
+
});
|
|
1397
|
+
|
|
1398
|
+
// feat-city-glb Bug 5 (per-submesh transparency): the split must trip when
|
|
1399
|
+
// a NON-zero submesh material is transparent even though the entity-level
|
|
1400
|
+
// material[0] is opaque (the crosswalk case: opaque road submesh[0] +
|
|
1401
|
+
// BLEND decal submesh[1] on one multi-material mesh).
|
|
1402
|
+
it('per-submesh: split trips when only a non-zero submesh is transparent', () => {
|
|
1403
|
+
if (typeof spriteMod.computeSplitLdrSprite !== 'function') {
|
|
1404
|
+
throw new Error('computeSplitLdrSprite helper not exported yet');
|
|
1405
|
+
}
|
|
1406
|
+
const opaque0 = makeTransparentSnap({
|
|
1407
|
+
transparent: false,
|
|
1408
|
+
materialShaderId: 'forgeax::default-standard-pbr',
|
|
1409
|
+
});
|
|
1410
|
+
const transparent1 = makeTransparentSnap({
|
|
1411
|
+
transparent: true,
|
|
1412
|
+
materialShaderId: 'forgeax::default-standard-pbr',
|
|
1413
|
+
});
|
|
1414
|
+
// Entity-level material (=materials[0]) is opaque; materials[1] transparent.
|
|
1415
|
+
const mixedEntry: DispatchEntryLike = {
|
|
1416
|
+
source: { material: opaque0, materials: [opaque0, transparent1] },
|
|
1417
|
+
};
|
|
1418
|
+
expect(spriteMod.computeSplitLdrSprite([mixedEntry], false)).toBe(true);
|
|
1419
|
+
});
|
|
1420
|
+
|
|
1421
|
+
it('per-submesh: split stays false when every submesh material is opaque', () => {
|
|
1422
|
+
if (typeof spriteMod.computeSplitLdrSprite !== 'function') {
|
|
1423
|
+
throw new Error('computeSplitLdrSprite helper not exported yet');
|
|
1424
|
+
}
|
|
1425
|
+
const opaque0 = makeTransparentSnap({
|
|
1426
|
+
transparent: false,
|
|
1427
|
+
materialShaderId: 'forgeax::default-standard-pbr',
|
|
1428
|
+
});
|
|
1429
|
+
const opaque1 = makeTransparentSnap({
|
|
1430
|
+
transparent: false,
|
|
1431
|
+
materialShaderId: 'forgeax::default-standard-pbr',
|
|
1432
|
+
});
|
|
1433
|
+
const opaqueMixed: DispatchEntryLike = {
|
|
1434
|
+
source: { material: opaque0, materials: [opaque0, opaque1] },
|
|
1435
|
+
};
|
|
1436
|
+
expect(spriteMod.computeSplitLdrSprite([opaqueMixed], false)).toBe(false);
|
|
1437
|
+
});
|
|
1438
|
+
|
|
1439
|
+
it('isEntityFullyTransparent / entityHasTransparentSubmesh classify mixed meshes', () => {
|
|
1440
|
+
if (
|
|
1441
|
+
typeof mod.isEntityFullyTransparent !== 'function' ||
|
|
1442
|
+
typeof mod.entityHasTransparentSubmesh !== 'function'
|
|
1443
|
+
) {
|
|
1444
|
+
throw new Error('per-submesh transparency helpers not exported yet');
|
|
1445
|
+
}
|
|
1446
|
+
const opaque = makeTransparentSnap({ transparent: false });
|
|
1447
|
+
const transp = makeTransparentSnap({ transparent: true });
|
|
1448
|
+
// Mixed: opaque[0] + transparent[1] -> has a transparent submesh but not
|
|
1449
|
+
// fully transparent (must draw in BOTH geometry pass and blend sub-pass).
|
|
1450
|
+
const mixed = { material: opaque, materials: [opaque, transp] };
|
|
1451
|
+
expect(mod.entityHasTransparentSubmesh(mixed)).toBe(true);
|
|
1452
|
+
expect(mod.isEntityFullyTransparent(mixed)).toBe(false);
|
|
1453
|
+
// Fully transparent (e.g. a sprite / 4.3 window quad).
|
|
1454
|
+
const full = { material: transp, materials: [transp] };
|
|
1455
|
+
expect(mod.entityHasTransparentSubmesh(full)).toBe(true);
|
|
1456
|
+
expect(mod.isEntityFullyTransparent(full)).toBe(true);
|
|
1457
|
+
// Fully opaque.
|
|
1458
|
+
const none = { material: opaque, materials: [opaque, opaque] };
|
|
1459
|
+
expect(mod.entityHasTransparentSubmesh(none)).toBe(false);
|
|
1460
|
+
expect(mod.isEntityFullyTransparent(none)).toBe(false);
|
|
1461
|
+
});
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
// ─── bug-20260622-tilemap-ysort-transparent-sort-modes-followup M2 m2-1 ───
|
|
1466
|
+
//
|
|
1467
|
+
// AC-04 (error signal SSOT) + AC-05 (LAYER_Y footY ordering) + R-2
|
|
1468
|
+
// (mode=DISTANCE without a cameraPos falls back to the original list).
|
|
1469
|
+
// `sortTransparentDispatch` itself is render-system.ts-private (closure-
|
|
1470
|
+
// scoped helper invoked once per draw); the public-surface contract sits on
|
|
1471
|
+
// `transparentSortEntries` (same primary `layer ASC` + secondary mode-formula
|
|
1472
|
+
// + identical fallback semantics). Asserting against the exported helper
|
|
1473
|
+
// gives byte-exact coverage of the 4-mode dispatch SSOT without instantiating
|
|
1474
|
+
// the full RenderSystem (which would need a real GPU device + canvas, far
|
|
1475
|
+
// outside unit-test scope; plan-strategy R-2 mitigation).
|
|
1476
|
+
// biome-ignore lint/complexity/noUselessLoneBlockStatements: mirrors the per-test-file block-scope idiom this consolidated test file already uses (lines 31/159/261/501/633 -- each ported slab from a pre-consolidation file lives in its own block so helper names cannot collide).
|
|
1477
|
+
{
|
|
1478
|
+
// --- AC-04: setTransparentSortConfig mode=99 -> Result.err with 4 SSOT fields ---
|
|
1479
|
+
describe('AC-04 setTransparentSortConfig mode=99 returns Result.err (KV untouched)', () => {
|
|
1480
|
+
it('Result.err carries code/expected/hint/detail + KV resource is NOT inserted', async () => {
|
|
1481
|
+
const { setTransparentSortConfig, TRANSPARENT_SORT_CONFIG_KEY } = await import(
|
|
1482
|
+
'../../../render/src/systems/transparent-sort-config'
|
|
1483
|
+
);
|
|
1484
|
+
const world = new World();
|
|
1485
|
+
// Pre-check: resource MUST be absent before the rejected call.
|
|
1486
|
+
expect(world.hasResource(TRANSPARENT_SORT_CONFIG_KEY)).toBe(false);
|
|
1487
|
+
|
|
1488
|
+
const r = setTransparentSortConfig(world, { mode: 99, yzAlpha: 1.0 });
|
|
1489
|
+
expect(r.ok).toBe(false);
|
|
1490
|
+
if (r.ok) return;
|
|
1491
|
+
// The 4 SSOT fields locked by plan-strategy D-4. The math symbol
|
|
1492
|
+
// U+2208 ("is element of") goes through the ASCII `∈` escape
|
|
1493
|
+
// so this source file stays English-only per AGENTS.md §Conventions
|
|
1494
|
+
// (mirrors EXPECTED_EXPECTED in systems.unit.test.ts).
|
|
1495
|
+
expect(r.error.code).toBe('resource-invalid-value');
|
|
1496
|
+
expect(r.error.expected).toBe('mode ∈ {0, 1, 2, 3}');
|
|
1497
|
+
expect(r.error.hint).toBe('0=layer-z, 1=layer-y, 2=layer-yz, 3=distance');
|
|
1498
|
+
expect((r.error.detail as { receivedMode: number }).receivedMode).toBe(99);
|
|
1499
|
+
// The KV resource MUST stay un-inserted after a rejected write
|
|
1500
|
+
// (charter P3 -- structured failure, never silently coerce).
|
|
1501
|
+
expect(world.hasResource(TRANSPARENT_SORT_CONFIG_KEY)).toBe(false);
|
|
1502
|
+
});
|
|
1503
|
+
});
|
|
1504
|
+
|
|
1505
|
+
// --- AC-05: mode=LAYER_Y, 3 entities footY=10/20/30 same layer -> 30/20/10 ---
|
|
1506
|
+
describe('AC-05 LAYER_Y footY ordering (same layer, deeper foot draws later)', () => {
|
|
1507
|
+
it('footY=10/20/30 same layer -> output order footY=30/20/10 (back-to-front)', async () => {
|
|
1508
|
+
const { setTransparentSortConfig, TRANSPARENT_SORT_MODE_LAYER_Y } = await import(
|
|
1509
|
+
'../../../render/src/systems/transparent-sort-config'
|
|
1510
|
+
);
|
|
1511
|
+
const { transparentSortEntries } = await import('../systems/transparent-sort');
|
|
1512
|
+
const world = new World();
|
|
1513
|
+
setTransparentSortConfig(world, {
|
|
1514
|
+
mode: TRANSPARENT_SORT_MODE_LAYER_Y,
|
|
1515
|
+
yzAlpha: 1.0,
|
|
1516
|
+
}).unwrap();
|
|
1517
|
+
|
|
1518
|
+
// footY = posY - pivotY * sizeY. Pin pivotY=0 + sizeY=1 so footY === posY
|
|
1519
|
+
// -- the 10/20/30 numbers land verbatim, no algebra to second-guess.
|
|
1520
|
+
// mode=1 sortValue = -footY; ASC over [-10, -20, -30] yields entries
|
|
1521
|
+
// ordered footY=30, 20, 10 (deepest foot draws last = back-to-front).
|
|
1522
|
+
const entries = [
|
|
1523
|
+
{
|
|
1524
|
+
entityIndex: 0,
|
|
1525
|
+
materialHandle: 0,
|
|
1526
|
+
layer: 0,
|
|
1527
|
+
posX: 0,
|
|
1528
|
+
posY: 10,
|
|
1529
|
+
posZ: 0,
|
|
1530
|
+
pivotY: 0,
|
|
1531
|
+
sizeY: 1,
|
|
1532
|
+
},
|
|
1533
|
+
{
|
|
1534
|
+
entityIndex: 1,
|
|
1535
|
+
materialHandle: 0,
|
|
1536
|
+
layer: 0,
|
|
1537
|
+
posX: 0,
|
|
1538
|
+
posY: 20,
|
|
1539
|
+
posZ: 0,
|
|
1540
|
+
pivotY: 0,
|
|
1541
|
+
sizeY: 1,
|
|
1542
|
+
},
|
|
1543
|
+
{
|
|
1544
|
+
entityIndex: 2,
|
|
1545
|
+
materialHandle: 0,
|
|
1546
|
+
layer: 0,
|
|
1547
|
+
posX: 0,
|
|
1548
|
+
posY: 30,
|
|
1549
|
+
posZ: 0,
|
|
1550
|
+
pivotY: 0,
|
|
1551
|
+
sizeY: 1,
|
|
1552
|
+
},
|
|
1553
|
+
];
|
|
1554
|
+
const sorted = transparentSortEntries(entries, world);
|
|
1555
|
+
expect(sorted.map((e) => e.posY)).toEqual([30, 20, 10]);
|
|
1556
|
+
});
|
|
1557
|
+
});
|
|
1558
|
+
|
|
1559
|
+
// --- R-2: mode=DISTANCE without a cameraPos returns entries unchanged ---
|
|
1560
|
+
// plan-strategy R-2 risk mitigation: sortTransparentDispatch mode=3 +
|
|
1561
|
+
// cameras[0] missing must keep the original dispatch list (PR #401
|
|
1562
|
+
// baseline). transparentSortEntries surfaces the same fallback through
|
|
1563
|
+
// the public helper -- the 2-arg call (no cameraPos) falls through to
|
|
1564
|
+
// the mode=0 posZ formula; with posZ pinned constant the sort becomes
|
|
1565
|
+
// a no-op preserving insertion order (charter P3 deterministic output).
|
|
1566
|
+
describe('R-2 mode=DISTANCE + cameraPos absent preserves insertion order', () => {
|
|
1567
|
+
it('transparentSortEntries(entries, world) with mode=3 + no cameraPos = insertion order', async () => {
|
|
1568
|
+
const { setTransparentSortConfig, TRANSPARENT_SORT_MODE_DISTANCE } = await import(
|
|
1569
|
+
'../../../render/src/systems/transparent-sort-config'
|
|
1570
|
+
);
|
|
1571
|
+
const { transparentSortEntries } = await import('../systems/transparent-sort');
|
|
1572
|
+
const world = new World();
|
|
1573
|
+
setTransparentSortConfig(world, {
|
|
1574
|
+
mode: TRANSPARENT_SORT_MODE_DISTANCE,
|
|
1575
|
+
yzAlpha: 1.0,
|
|
1576
|
+
}).unwrap();
|
|
1577
|
+
|
|
1578
|
+
// posZ pinned identical so the fallback (posZ ASC) is a no-op; the
|
|
1579
|
+
// assertion guards both R-2 (no crash, no reorder) and "fallback is
|
|
1580
|
+
// deterministic" together.
|
|
1581
|
+
const entries = [
|
|
1582
|
+
{
|
|
1583
|
+
entityIndex: 7,
|
|
1584
|
+
materialHandle: 0,
|
|
1585
|
+
layer: 0,
|
|
1586
|
+
posX: 1,
|
|
1587
|
+
posY: 0,
|
|
1588
|
+
posZ: 0,
|
|
1589
|
+
pivotY: 0.5,
|
|
1590
|
+
sizeY: 1,
|
|
1591
|
+
},
|
|
1592
|
+
{
|
|
1593
|
+
entityIndex: 8,
|
|
1594
|
+
materialHandle: 0,
|
|
1595
|
+
layer: 0,
|
|
1596
|
+
posX: 2,
|
|
1597
|
+
posY: 0,
|
|
1598
|
+
posZ: 0,
|
|
1599
|
+
pivotY: 0.5,
|
|
1600
|
+
sizeY: 1,
|
|
1601
|
+
},
|
|
1602
|
+
{
|
|
1603
|
+
entityIndex: 9,
|
|
1604
|
+
materialHandle: 0,
|
|
1605
|
+
layer: 0,
|
|
1606
|
+
posX: 3,
|
|
1607
|
+
posY: 0,
|
|
1608
|
+
posZ: 0,
|
|
1609
|
+
pivotY: 0.5,
|
|
1610
|
+
sizeY: 1,
|
|
1611
|
+
},
|
|
1612
|
+
];
|
|
1613
|
+
const sorted = transparentSortEntries(entries, world);
|
|
1614
|
+
expect(sorted.map((e) => e.entityIndex)).toEqual([7, 8, 9]);
|
|
1615
|
+
});
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
// --- tweak-20260701 M2 AC-02: zero-light warning polarity ---
|
|
1620
|
+
{
|
|
1621
|
+
// AC-02: the zero-light black-screen warning must still fire for
|
|
1622
|
+
// builtin PBR/standard materials and remain silent for custom, unlit /
|
|
1623
|
+
// sprite / default materials. Custom shader lighting contracts are authored
|
|
1624
|
+
// by the shader and cannot be inferred by this generic diagnostic.
|
|
1625
|
+
//
|
|
1626
|
+
// The test imports `isLitMaterialSnapshot` from the production
|
|
1627
|
+
// render-system-record module — the same function that recordFrame's
|
|
1628
|
+
// zero-light warning gate (line ~2122) calls to compute
|
|
1629
|
+
// hasStandardMaterial. A test-local copy would not anchor to the
|
|
1630
|
+
// production path.
|
|
1631
|
+
|
|
1632
|
+
function makeSnap(materialShaderId: string | undefined): MaterialSnapshot {
|
|
1633
|
+
return {
|
|
1634
|
+
baseColor: [1, 1, 1, 1] as const,
|
|
1635
|
+
metallic: 0,
|
|
1636
|
+
roughness: 1,
|
|
1637
|
+
materialShaderId,
|
|
1638
|
+
paramSnapshot: undefined,
|
|
1639
|
+
} as unknown as MaterialSnapshot;
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
describe('AC-02: zero-light warning polarity', () => {
|
|
1643
|
+
it('standard / PBR material (materialShaderId !== undefined, !== default-unlit) -> lit', () => {
|
|
1644
|
+
const snap = makeSnap('forgeax::default-standard-pbr');
|
|
1645
|
+
expect(isLitMaterialSnapshot(snap)).toBe(true);
|
|
1646
|
+
});
|
|
1647
|
+
|
|
1648
|
+
it('custom shader material -> not lit (custom lighting contract)', () => {
|
|
1649
|
+
const snap = makeSnap('my-custom-shader');
|
|
1650
|
+
expect(isLitMaterialSnapshot(snap)).toBe(false);
|
|
1651
|
+
});
|
|
1652
|
+
|
|
1653
|
+
it('unlit material (materialShaderId === forgeax::default-unlit) -> not lit (no warn)', () => {
|
|
1654
|
+
const snap = makeSnap('forgeax::default-unlit');
|
|
1655
|
+
expect(isLitMaterialSnapshot(snap)).toBe(false);
|
|
1656
|
+
});
|
|
1657
|
+
|
|
1658
|
+
it('sprite material (materialShaderId === forgeax::sprite) -> not lit (no warn)', () => {
|
|
1659
|
+
const snap = makeSnap('forgeax::sprite');
|
|
1660
|
+
expect(isLitMaterialSnapshot(snap)).toBe(false);
|
|
1661
|
+
});
|
|
1662
|
+
|
|
1663
|
+
it('default material (materialShaderId === undefined) -> not lit (no warn)', () => {
|
|
1664
|
+
const snap = makeSnap(undefined);
|
|
1665
|
+
expect(isLitMaterialSnapshot(snap)).toBe(false);
|
|
1666
|
+
});
|
|
1667
|
+
});
|
|
1668
|
+
}
|