@forgeax/engine-runtime 0.1.3 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -83
- package/dist/.tsbuildinfo +1 -1
- 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-serialize.unit.test.d.ts +2 -0
- package/dist/__tests__/animation-graph-serialize.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.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__/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__/collect-scene-asset.test.d.ts +1 -2
- package/dist/__tests__/collect-scene-asset.test.d.ts.map +1 -1
- 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-transient-roundtrip.test.d.ts +1 -1
- package/dist/__tests__/collect-transient-roundtrip.test.d.ts.map +1 -1
- package/dist/__tests__/collect-transient-skip.test.d.ts +1 -2
- package/dist/__tests__/collect-transient-skip.test.d.ts.map +1 -1
- 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__/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/upload-texture.dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/upload-texture.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/draw-published.d.ts +2 -2
- package/dist/__tests__/draw-published.d.ts.map +1 -1
- 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__/errors.unit.test.d.ts +2 -0
- package/dist/__tests__/errors.unit.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__/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__/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__/helpers/register-runtime-components.d.ts.map +1 -1
- 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__/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__/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__/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__/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__/recover-error-exhaustive.test-d.d.ts +1 -1
- package/dist/__tests__/recover-error-exhaustive.test-d.d.ts.map +1 -1
- package/dist/__tests__/render-error-exhaustive.test-d.d.ts +1 -1
- package/dist/__tests__/render-error-exhaustive.test-d.d.ts.map +1 -1
- 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-system-extract.unit.test.d.ts +2 -0
- package/dist/__tests__/render-system-extract.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__/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__/roots-to-scene-asset.test.d.ts +1 -1
- package/dist/__tests__/roots-to-scene-asset.test.d.ts.map +1 -1
- package/dist/__tests__/roundtrip-primitive.test.d.ts +2 -0
- package/dist/__tests__/roundtrip-primitive.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-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-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 +1 -1
- package/dist/__tests__/serialize-scene-asset.test.d.ts.map +1 -1
- 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__/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__/transient-runtime-behavior.test.d.ts +2 -0
- package/dist/__tests__/transient-runtime-behavior.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/backend-selection.d.ts +13 -0
- package/dist/backend-selection.d.ts.map +1 -0
- package/dist/collect-scene-asset.d.ts +3 -3
- package/dist/collect-scene-asset.d.ts.map +1 -1
- package/dist/createRenderer.d.ts +14 -2
- package/dist/createRenderer.d.ts.map +1 -1
- package/dist/errors/environment.d.ts +1 -1
- package/dist/errors/environment.d.ts.map +1 -1
- package/dist/index.d.ts +19 -81
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +105 -108
- package/dist/index.mjs.map +1 -1
- 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/index.d.ts +2 -0
- package/dist/scene-instances/index.d.ts.map +1 -0
- package/dist/scene-utils/mount-override-fold.d.ts +1 -2
- package/dist/scene-utils/mount-override-fold.d.ts.map +1 -1
- package/dist/systems/sprite-animation-tick.d.ts +1 -1
- package/dist/systems/sprite-animation-tick.d.ts.map +1 -1
- package/dist/systems/transparent-sort.d.ts +1 -2
- package/dist/systems/transparent-sort.d.ts.map +1 -1
- package/dist/tilemap-register-validate.d.ts.map +1 -1
- package/package.json +34 -33
- package/src/__tests__/active-camera.unit.test.ts +19 -9
- package/src/__tests__/animation-graph-collect-roundtrip.test.ts +213 -0
- package/src/__tests__/animation-graph-serialize.unit.test.ts +171 -0
- package/src/__tests__/append-injection.test.ts +1 -1
- 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 +575 -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.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 +4 -4
- package/src/__tests__/block-upload-math.unit.test.ts +2 -2
- package/src/__tests__/builtin-asset-registry.unit.test.ts +112 -0
- package/src/__tests__/builtin-shader-register-e2e.test.ts +1 -1
- package/src/__tests__/cache-false-eviction.test.ts +2 -1
- package/src/__tests__/cache-isolation-matrix.test.ts +2 -1
- package/src/__tests__/camera-clear-snapshot-equivalence.unit.test.ts +5 -6
- package/src/__tests__/clamp-to-last.e2e.browser.test.ts +29 -19
- package/src/__tests__/collect-scene-asset.test.ts +49 -52
- package/src/__tests__/collect-scene-mount-collapse.test.ts +391 -0
- package/src/__tests__/collect-subtree.unit.test.ts +1 -2
- package/src/__tests__/collect-transient-children-absent.test.ts +24 -10
- package/src/__tests__/collect-transient-roundtrip.test.ts +44 -20
- package/src/__tests__/collect-transient-skip.test.ts +24 -13
- package/src/__tests__/components.test-d.ts +1 -6
- package/src/__tests__/components.unit.test.ts +20 -19
- package/src/__tests__/composite-skybox-cross-world.browser.test.ts +59 -27
- package/src/__tests__/compressed-texture-physical-extent.integration.test.ts +139 -0
- package/src/__tests__/create-renderer-assembly.integration.test.ts +42 -29
- package/src/__tests__/createRenderer-dispose.test.ts +35 -402
- package/src/__tests__/createRenderer.browser-no-webgpu.test.ts +25 -30
- package/src/__tests__/createRenderer.unit.test.ts +1 -1
- package/src/__tests__/dawn/hello-sprite-nineslice-section.dawn.test.ts +27 -63
- package/src/__tests__/dawn/ibl-precompute-readback.dawn.test.ts +56 -47
- package/src/__tests__/dawn/instances-per-instance-pbr.dawn.test.ts +49 -29
- package/src/__tests__/dawn/instances-shadow.dawn.test.ts +26 -15
- package/src/__tests__/dawn/instances-uniform-fallback.dawn.test.ts +13 -14
- package/src/__tests__/dawn/material-alpha.dawn.test.ts +23 -31
- package/src/__tests__/dawn/material-cooked-fixture.dawn.test.ts +0 -18
- package/src/__tests__/dawn/{mesh-update.dawn.test.ts → mesh-update-dawn.test.ts} +42 -134
- package/src/__tests__/dawn/mipmap-srgb-linear.dawn.test.ts +14 -7
- package/src/__tests__/dawn/per-entity-material-texture-isolation.dawn.test.ts +16 -28
- package/src/__tests__/dawn/sprite-nineslice-mesh-bind.dawn.test.ts +23 -39
- package/src/__tests__/dawn/upload-texture.dawn.test.ts +198 -0
- package/src/__tests__/dawn/user-handle-mesh-render.dawn.test.ts +12 -14
- package/src/__tests__/dawn/user-mesh-upload.dawn.test.ts +27 -22
- package/src/__tests__/dawn/zero-manifest-mesh-fail.dawn.test.ts +25 -28
- package/src/__tests__/dawn/zero-manifest-ready.dawn.test.ts +10 -11
- package/src/__tests__/demo-sortscope-migration.unit.test.ts +5 -5
- package/src/__tests__/derive-bgl-integration.test.ts +34 -44
- package/src/__tests__/derive-render-data-skin.unit.test.ts +1 -1
- package/src/__tests__/draw-published.ts +11 -4
- package/src/__tests__/dynamic-texture-store.unit.test.ts +190 -0
- package/src/__tests__/equirect-bc6h.integration.test.ts +144 -21
- package/src/__tests__/errors.unit.test.ts +1203 -0
- package/src/__tests__/extract-frames-graceful-degradation.test.ts +3 -7
- package/src/__tests__/extract-frames-merge.test.ts +35 -22
- package/src/__tests__/extract-record-no-hardcoded-texture-fields.test.ts +5 -16
- 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 +1 -1
- package/src/__tests__/fullscreen-feature-host.dawn.test.ts +80 -0
- package/src/__tests__/fullscreen-post-bgl-depth.test.ts +4 -2
- package/src/__tests__/fullscreen-post-depth-params-arity.test.ts +3 -3
- package/src/__tests__/fullscreen-post-process-pass.dawn.test.ts +39 -23
- package/src/__tests__/fullscreen-post-process-pass.unit.test.ts +4 -2
- package/src/__tests__/fxaa-pixel-diff.dawn.test.ts +28 -17
- package/src/__tests__/fxaa-validation-and-resize.dawn.test.ts +29 -18
- package/src/__tests__/fxaa-zero-overhead.dawn.test.ts +16 -12
- package/src/__tests__/geometry.unit.test.ts +525 -159
- package/src/__tests__/glyph-tilemap-worldid.test.ts +1 -1
- package/src/__tests__/gpu-resource-cow-survivor.test.ts +6 -4
- package/src/__tests__/guid-for-asset.unit.test.ts +102 -0
- package/src/__tests__/handle-migration.test.ts +136 -0
- package/src/__tests__/helpers/register-runtime-components.ts +4 -2
- package/src/__tests__/helpers/register-scene-components.ts +1 -1
- 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 +1 -1
- package/src/__tests__/instantiate-mount-deferred-wiring.test.ts +112 -0
- package/src/__tests__/light-casters-9-light.browser.test.ts +73 -76
- package/src/__tests__/light-gpu-byte-neutral.unit.test.ts +7 -10
- package/src/__tests__/light-snapshot.test-d.ts +2 -2
- package/src/__tests__/lights-vec-negative.unit.test.ts +1 -1
- package/src/__tests__/lights.unit.test.ts +16 -500
- 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 +3 -2
- package/src/__tests__/material-texture-uv-scale.unit.test.ts +101 -31
- package/src/__tests__/materials.unit.test.ts +10 -27
- 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 +3 -3
- package/src/__tests__/mip-gate.unit.test.ts +1 -1
- package/src/__tests__/msaa-sprite-pixel-diff.dawn.test.ts +29 -68
- package/src/__tests__/new-kind-refs-contract.test.ts +132 -0
- package/src/__tests__/node-shims.d.ts +4 -6
- 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 +2 -2
- package/src/__tests__/pbr-pipeline.unit.test.ts +14 -21
- package/src/__tests__/pipeline-builder-msaa.unit.test.ts +6 -9
- package/src/__tests__/pipeline-cache-keying.unit.test.ts +6 -9
- package/src/__tests__/pipeline-spec.test.ts +16 -177
- package/src/__tests__/pipeline.unit.test.ts +154 -1693
- package/src/__tests__/point-light-shadow.dawn.test.ts +33 -42
- package/src/__tests__/point-light-shadow.unit.test.ts +19 -60
- package/src/__tests__/post-process-error-codes.unit.test.ts +4 -1
- package/src/__tests__/post-process-params-component.unit.test.ts +1 -1
- package/src/__tests__/post-process-params-extract.unit.test.ts +2 -5
- package/src/__tests__/postprocess-camera-consistency.test.ts +4 -7
- package/src/__tests__/profiler-assembly.integration.test.ts +1 -1
- package/src/__tests__/propagate-transforms.dawn.test.ts +12 -11
- package/src/__tests__/record-worldid-isolation.test.ts +7 -29
- package/src/__tests__/recover-error-exhaustive.test-d.ts +1 -1
- package/src/__tests__/render-error-exhaustive.test-d.ts +81 -71
- package/src/__tests__/render-feature-assembly.test-d.ts +20 -9
- package/src/__tests__/render-feature-prepared-graphics-docs.unit.test.ts +8 -6
- package/src/__tests__/render-feature-prepared-graphics.browser.test.ts +60 -558
- package/src/__tests__/render-feature-prepared-graphics.dawn.test.ts +57 -544
- package/src/__tests__/render-feature-prepared-graphics.fixture.ts +104 -0
- package/src/__tests__/render-feature-prepared-graphics.integration.test.ts +40 -248
- package/src/__tests__/render-feature-prepared-graphics.regression.integration.test.ts +83 -340
- package/src/__tests__/render-feature-recover.integration.test.ts +9 -49
- package/src/__tests__/render-feature-runtime-surface.test-d.ts +17 -20
- package/src/__tests__/render-schema-consumer.integration.test.ts +20 -13
- package/src/__tests__/render-schema-owner-boundary.unit.test.ts +2 -6
- package/src/__tests__/render-system-cross-world-overlay-queue.test.ts +5 -5
- package/src/__tests__/render-system-extract-dispatch-ordering.test.ts +16 -31
- package/src/__tests__/render-system-extract.test.ts +36 -51
- package/src/__tests__/render-system-extract.unit.test.ts +360 -0
- package/src/__tests__/render-system-fold-bucket.unit.test.ts +3 -5
- package/src/__tests__/render-system-fold-fallback.unit.test.ts +6 -3
- package/src/__tests__/render-system-fold-material.unit.test.ts +3 -5
- package/src/__tests__/render-system-fold-metrics.unit.test.ts +5 -5
- package/src/__tests__/render-system-fold-modegate.unit.test.ts +6 -6
- package/src/__tests__/render-system-fold.dawn.test.ts +7 -5
- package/src/__tests__/render-system-mega.test.ts +292 -190
- package/src/__tests__/render-system-owner-split-contract.test.ts +10 -32
- package/src/__tests__/render-system-record-multi-material-textureview.test.ts +59 -45
- package/src/__tests__/render-system-record-per-submesh-transparency.test.ts +23 -29
- package/src/__tests__/render-system-record.test.ts +398 -235
- package/src/__tests__/render-system-record.unit.test.ts +25 -26
- package/src/__tests__/render-system-skin-bg.unit.test.ts +8 -12
- package/src/__tests__/renderer-consumer.test-d.ts +16 -3
- package/src/__tests__/renderer-health.unit.test.ts +26 -916
- package/src/__tests__/renderer-lifecycle.integration.test.ts +19 -6
- package/src/__tests__/renderer-recover.unit.test.ts +49 -1069
- package/src/__tests__/renderer-result.test-d.ts +19 -9
- package/src/__tests__/renderer-surface.unit.test.ts +3 -5
- package/src/__tests__/renderer-test-utils.ts +56 -0
- package/src/__tests__/renderer.test-d.ts +59 -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 +1 -1
- 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 +43 -261
- package/src/__tests__/rhi-null-renderer-lifecycle.unit.test.ts +41 -169
- package/src/__tests__/roots-to-scene-asset.test.ts +199 -93
- package/src/__tests__/roundtrip-primitive.test.ts +351 -0
- package/src/__tests__/scene-bare-guid-refs-instantiate.test.ts +123 -0
- package/src/__tests__/scene-equirect-roundtrip.test.ts +103 -16
- package/src/__tests__/scene-instance-authoring.integration.test.ts +8 -0
- package/src/__tests__/scene-instance-bridge.integration.test.ts +1 -1
- package/src/__tests__/scene-instance-component.test.ts +1 -1
- 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 +219 -22
- package/src/__tests__/shadow-caster-empty-schema.test.ts +3 -3
- package/src/__tests__/shadow-csm-cascade-loadop.test.ts +37 -17
- package/src/__tests__/shadow-csm-component.test.ts +3 -5
- package/src/__tests__/shadow-csm-extract.test.ts +2 -100
- package/src/__tests__/shadow-csm-orthoz.test.ts +2 -16
- package/src/__tests__/shadow-csm-runtime-vary.dawn.test.ts +19 -14
- package/src/__tests__/shadow-csm-tile-consistency.test.ts +32 -26
- package/src/__tests__/shadow-csm-ubo.dawn.test.ts +8 -49
- package/src/__tests__/shadow-csm-ubo.test.ts +3 -25
- package/src/__tests__/shadow-csm-viewport.browser.test.ts +1 -1
- package/src/__tests__/shadow-fields-observable.dawn.test.ts +67 -56
- package/src/__tests__/shadow-m1.dawn.test.ts +8 -9
- package/src/__tests__/shadow-mesh-ssbo-order.test.ts +11 -27
- package/src/__tests__/skin-errors.unit.test.ts +1 -1
- package/src/__tests__/skin-extract-getarrayview-count.unit.test.ts +7 -5
- package/src/__tests__/skylight-lazy-projection.test.ts +36 -15
- package/src/__tests__/sparse-tag-scene-roundtrip.test.ts +5 -5
- package/src/__tests__/sprite-cullmode-flip.dawn.test.ts +47 -64
- package/src/__tests__/sprite-lit-bgl-byte-identical.test.ts +12 -11
- package/src/__tests__/sprite-lit-extract-branch.test.ts +1 -1
- package/src/__tests__/sprite-lit-pipeline-cache-key.test.ts +1 -1
- package/src/__tests__/ssao-bgl.test.ts +16 -9
- package/src/__tests__/ssao-buffers.test.ts +2 -2
- package/src/__tests__/ssao-data.test.ts +2 -2
- package/src/__tests__/ssao-errors.test.ts +1 -1
- package/src/__tests__/ssao-passes.test.ts +64 -392
- package/src/__tests__/ssao-pipelines.test.ts +1 -1
- package/src/__tests__/ssao-uniform-layout.test.ts +8 -6
- package/src/__tests__/stencil-outline-pixel.dawn.test.ts +25 -17
- package/src/__tests__/swap-chain-format.unit.test.ts +5 -7
- package/src/__tests__/systems.unit.test.ts +536 -1326
- package/src/__tests__/template-game-default-api-contract.test.ts +362 -0
- package/src/__tests__/text.unit.test.ts +56 -33
- package/src/__tests__/tilemap-childed-despawn.test.ts +8 -14
- package/src/__tests__/tilemap-chunk-extract.overhang-bounds.test.ts +8 -12
- package/src/__tests__/tilemap-chunk-extract.propagate-order.test.ts +2 -1
- package/src/__tests__/tilemap-chunk-extract.test.ts +13 -17
- package/src/__tests__/tilemap-dirty-rebuild.test.ts +7 -11
- package/src/__tests__/tilemap-material-cache.test.ts +8 -13
- package/src/__tests__/tilemap-multi-atlas.test.ts +12 -32
- package/src/__tests__/tilemap-owner-boundary.unit.test.ts +5 -2
- package/src/__tests__/tilemap-region-pack.test.ts +8 -12
- package/src/__tests__/tilemap-spawn-flip-pivot.test.ts +8 -12
- package/src/__tests__/tilemap-uv-inset.test.ts +11 -14
- package/src/__tests__/tilemap-validate.test.ts +1 -1
- 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 +36 -28
- package/src/__tests__/transient-fields-collect.test.ts +28 -21
- package/src/__tests__/transient-runtime-behavior.test.ts +227 -0
- package/src/__tests__/transparent-sort-tilemap-sprite-interleave.test.ts +4 -4
- package/src/__tests__/variant-selection.unit.test.ts +1 -1
- package/src/__tests__/video-extract-bindgroup.dawn.test.ts +89 -136
- 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 +0 -22
- package/src/__tests__/world-entity-key.test.ts +1 -1
- package/src/__tests__/world-scene-despawn.test.ts +1 -1
- package/src/__tests__/world-scene-instantiate.test.ts +2 -36
- package/src/__tests__/world-scene-nested.test.ts +1 -1
- package/src/__tests__/world-scene-override.test.ts +1 -1
- package/src/backend-selection.ts +69 -0
- package/src/collect-scene-asset.ts +28 -23
- package/src/components/__tests__/camera.test-d.ts +1 -1
- package/src/components/__tests__/camera.test.ts +1 -1
- package/src/components/__tests__/sprite-animation.test-d.ts +2 -2
- package/src/components/__tests__/tile-layer.test-d.ts +1 -1
- package/src/components/__tests__/tile-layer.unit.test.ts +2 -2
- package/src/createRenderer.ts +24 -36
- package/src/errors/environment.ts +1 -1
- package/src/index.ts +47 -113
- package/src/renderer-host.ts +33 -0
- package/src/scene-instances/__tests__/post-spawn-resolve-joints.unit.test.ts +1 -1
- package/src/scene-instances/index.ts +4 -0
- package/src/scene-utils/mount-override-fold.ts +3 -7
- package/src/systems/__tests__/transparent-sort.bench.ts +3 -3
- package/src/systems/sprite-animation-tick.ts +1 -1
- package/src/systems/transparent-sort.ts +38 -38
- package/src/tilemap-register-validate.ts +4 -4
- package/dist/__tests__/bind-group-cache-binding.test.d.ts +0 -2
- package/dist/__tests__/bind-group-cache-binding.test.d.ts.map +0 -1
- package/dist/__tests__/bind-group-cache-frame.test.d.ts +0 -2
- package/dist/__tests__/bind-group-cache-frame.test.d.ts.map +0 -1
- package/dist/__tests__/dawn/mesh-update.dawn.test.d.ts +0 -2
- package/dist/__tests__/dawn/mesh-update.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/dispatch-fullscreen-pass-composite-swapchain.dawn.test.d.ts +0 -2
- package/dist/__tests__/dispatch-fullscreen-pass-composite-swapchain.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/dispatch-fullscreen-pass-custom-reads.dawn.test.d.ts +0 -2
- package/dist/__tests__/dispatch-fullscreen-pass-custom-reads.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/dispatch-fullscreen-pass-depth-composite.dawn.test.d.ts +0 -2
- package/dist/__tests__/dispatch-fullscreen-pass-depth-composite.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/dispatch-fullscreen-pass-params.unit.test.d.ts +0 -2
- package/dist/__tests__/dispatch-fullscreen-pass-params.unit.test.d.ts.map +0 -1
- package/dist/__tests__/fog-producer-manifest.d.ts +0 -131
- package/dist/__tests__/fog-producer-manifest.d.ts.map +0 -1
- package/dist/__tests__/fog-producer-pixel-probe.d.ts +0 -42
- package/dist/__tests__/fog-producer-pixel-probe.d.ts.map +0 -1
- package/dist/__tests__/fog-producer-pixel.browser.test.d.ts +0 -2
- package/dist/__tests__/fog-producer-pixel.browser.test.d.ts.map +0 -1
- package/dist/__tests__/fog-producer-pixel.dawn.test.d.ts +0 -2
- package/dist/__tests__/fog-producer-pixel.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/fog-producer-real-fixture.d.ts +0 -5
- package/dist/__tests__/fog-producer-real-fixture.d.ts.map +0 -1
- package/dist/__tests__/fog-producer-vfx-effect.d.ts +0 -3
- package/dist/__tests__/fog-producer-vfx-effect.d.ts.map +0 -1
- package/dist/__tests__/fog-scene-asset.integration.test.d.ts +0 -2
- package/dist/__tests__/fog-scene-asset.integration.test.d.ts.map +0 -1
- package/dist/__tests__/gpu-resource-store-cubemap-status.test.d.ts +0 -2
- package/dist/__tests__/gpu-resource-store-cubemap-status.test.d.ts.map +0 -1
- package/dist/__tests__/gpu-resource.test.d.ts +0 -2
- package/dist/__tests__/gpu-resource.test.d.ts.map +0 -1
- package/dist/__tests__/hdrp-bgl-slots.browser.test.d.ts +0 -2
- package/dist/__tests__/hdrp-bgl-slots.browser.test.d.ts.map +0 -1
- package/dist/__tests__/hdrp-buffers.unit.test.d.ts +0 -2
- package/dist/__tests__/hdrp-buffers.unit.test.d.ts.map +0 -1
- package/dist/__tests__/hdrp-execute-filter.unit.test.d.ts +0 -2
- package/dist/__tests__/hdrp-execute-filter.unit.test.d.ts.map +0 -1
- package/dist/__tests__/hdrp-gbuffer-attachments.browser.test.d.ts +0 -2
- package/dist/__tests__/hdrp-gbuffer-attachments.browser.test.d.ts.map +0 -1
- package/dist/__tests__/hdrp-pipeline-asset-type.test-d.d.ts +0 -2
- package/dist/__tests__/hdrp-pipeline-asset-type.test-d.d.ts.map +0 -1
- package/dist/__tests__/hdrp-ssao-boundary.test.d.ts +0 -2
- package/dist/__tests__/hdrp-ssao-boundary.test.d.ts.map +0 -1
- package/dist/__tests__/hdrp-ssao-falsification.test.d.ts +0 -2
- package/dist/__tests__/hdrp-ssao-falsification.test.d.ts.map +0 -1
- package/dist/__tests__/helpers/primitive-world.d.ts +0 -8
- package/dist/__tests__/helpers/primitive-world.d.ts.map +0 -1
- package/dist/__tests__/helpers/render-asset-port.d.ts +0 -15
- package/dist/__tests__/helpers/render-asset-port.d.ts.map +0 -1
- package/dist/__tests__/helpers/scene-asset-port.d.ts +0 -9
- package/dist/__tests__/helpers/scene-asset-port.d.ts.map +0 -1
- package/dist/__tests__/material-derived-coordinates.integration.test.d.ts +0 -2
- package/dist/__tests__/material-derived-coordinates.integration.test.d.ts.map +0 -1
- package/dist/__tests__/pipeline-layout-selection.test.d.ts +0 -2
- package/dist/__tests__/pipeline-layout-selection.test.d.ts.map +0 -1
- package/dist/__tests__/post-process-reads-narrowing.test-d.d.ts +0 -2
- package/dist/__tests__/post-process-reads-narrowing.test-d.d.ts.map +0 -1
- package/dist/__tests__/post-process-register-roundtrip.dawn.test.d.ts +0 -2
- package/dist/__tests__/post-process-register-roundtrip.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-zero-feature.browser.test.d.ts +0 -2
- package/dist/__tests__/render-feature-zero-feature.browser.test.d.ts.map +0 -1
- package/dist/__tests__/render-pipeline-trivial.dawn.test.d.ts +0 -2
- package/dist/__tests__/render-pipeline-trivial.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-csm-caster-membership.test.d.ts +0 -2
- package/dist/__tests__/shadow-csm-caster-membership.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-csm-pssm.test.d.ts +0 -2
- package/dist/__tests__/shadow-csm-pssm.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-csm-temporal.browser.test.d.ts +0 -2
- package/dist/__tests__/shadow-csm-temporal.browser.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-csm-viewport.test.d.ts +0 -2
- package/dist/__tests__/shadow-csm-viewport.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-m2.dawn.test.d.ts +0 -2
- package/dist/__tests__/shadow-m2.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-m3-calibrate-run.dawn.test.d.ts +0 -2
- package/dist/__tests__/shadow-m3-calibrate-run.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-m3-calibrate.d.ts +0 -83
- package/dist/__tests__/shadow-m3-calibrate.d.ts.map +0 -1
- package/dist/__tests__/shadow-m3.dawn.test.d.ts +0 -2
- package/dist/__tests__/shadow-m3.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-probe-control-flow.dawn.test.d.ts +0 -2
- package/dist/__tests__/shadow-probe-control-flow.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-quality-metrics.dawn.test.d.ts +0 -2
- package/dist/__tests__/shadow-quality-metrics.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/temporal-off-path.unit.test.d.ts +0 -2
- package/dist/__tests__/temporal-off-path.unit.test.d.ts.map +0 -1
- package/dist/__tests__/video-cache-regression.unit.test.d.ts +0 -2
- package/dist/__tests__/video-cache-regression.unit.test.d.ts.map +0 -1
- package/dist/asset-kind.d.ts +0 -4
- package/dist/asset-kind.d.ts.map +0 -1
- package/dist/errors/sprite-animation.d.ts +0 -3
- package/dist/errors/sprite-animation.d.ts.map +0 -1
- package/dist/scene-asset-port.d.ts +0 -17
- package/dist/scene-asset-port.d.ts.map +0 -1
- package/dist/scene-collect-errors.d.ts +0 -22
- package/dist/scene-collect-errors.d.ts.map +0 -1
- package/dist/shadow-m3-calibrate.d.ts +0 -40
- package/dist/shadow-m3-calibrate.d.ts.map +0 -1
- package/src/__tests__/bind-group-cache-binding.test.ts +0 -2513
- package/src/__tests__/bind-group-cache-frame.test.ts +0 -1511
- package/src/__tests__/dispatch-fullscreen-pass-composite-swapchain.dawn.test.ts +0 -204
- package/src/__tests__/dispatch-fullscreen-pass-custom-reads.dawn.test.ts +0 -297
- package/src/__tests__/dispatch-fullscreen-pass-depth-composite.dawn.test.ts +0 -271
- package/src/__tests__/dispatch-fullscreen-pass-params.unit.test.ts +0 -321
- package/src/__tests__/fog-producer-manifest.ts +0 -162
- package/src/__tests__/fog-producer-pixel-probe.ts +0 -263
- package/src/__tests__/fog-producer-pixel.browser.test.ts +0 -26
- package/src/__tests__/fog-producer-pixel.dawn.test.ts +0 -55
- package/src/__tests__/fog-producer-real-fixture.ts +0 -805
- package/src/__tests__/fog-producer-vfx-effect.ts +0 -272
- package/src/__tests__/fog-scene-asset.integration.test.ts +0 -123
- package/src/__tests__/gpu-resource-store-cubemap-status.test.ts +0 -207
- package/src/__tests__/gpu-resource.test.ts +0 -1206
- package/src/__tests__/hdrp-bgl-slots.browser.test.ts +0 -50
- package/src/__tests__/hdrp-buffers.unit.test.ts +0 -70
- package/src/__tests__/hdrp-execute-filter.unit.test.ts +0 -200
- package/src/__tests__/hdrp-gbuffer-attachments.browser.test.ts +0 -64
- package/src/__tests__/hdrp-pipeline-asset-type.test-d.ts +0 -139
- package/src/__tests__/hdrp-ssao-boundary.test.ts +0 -237
- package/src/__tests__/hdrp-ssao-falsification.test.ts +0 -289
- package/src/__tests__/helpers/primitive-world.ts +0 -91
- package/src/__tests__/helpers/render-asset-port.ts +0 -65
- package/src/__tests__/helpers/scene-asset-port.ts +0 -23
- package/src/__tests__/material-derived-coordinates.integration.test.ts +0 -100
- package/src/__tests__/pipeline-layout-selection.test.ts +0 -298
- package/src/__tests__/post-process-reads-narrowing.test-d.ts +0 -52
- package/src/__tests__/post-process-register-roundtrip.dawn.test.ts +0 -230
- package/src/__tests__/render-feature-zero-feature.browser.test.ts +0 -152
- package/src/__tests__/render-pipeline-trivial.dawn.test.ts +0 -525
- package/src/__tests__/shadow-csm-caster-membership.test.ts +0 -232
- package/src/__tests__/shadow-csm-pssm.test.ts +0 -142
- package/src/__tests__/shadow-csm-temporal.browser.test.ts +0 -145
- package/src/__tests__/shadow-csm-viewport.test.ts +0 -74
- package/src/__tests__/shadow-m2.dawn.test.ts +0 -394
- package/src/__tests__/shadow-m3-calibrate-run.dawn.test.ts +0 -248
- package/src/__tests__/shadow-m3-calibrate.ts +0 -203
- package/src/__tests__/shadow-m3.dawn.test.ts +0 -288
- package/src/__tests__/shadow-probe-control-flow.dawn.test.ts +0 -264
- package/src/__tests__/shadow-quality-metrics.dawn.test.ts +0 -324
- package/src/__tests__/temporal-off-path.unit.test.ts +0 -69
- package/src/__tests__/video-cache-regression.unit.test.ts +0 -186
- package/src/asset-kind.ts +0 -7
- package/src/errors/sprite-animation.ts +0 -5
- package/src/scene-asset-port.ts +0 -32
- package/src/scene-collect-errors.ts +0 -30
- package/src/shadow-m3-calibrate.ts +0 -171
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
import * as SceneOwner from '@forgeax/engine-scene';
|
|
2
|
+
|
|
3
|
+
// asset-registry-guid-reverse.test.ts — M1 origin-index reverse-lookup TDD
|
|
4
|
+
// (feat-20260703-collect-nested-sceneinstance-to-mount-roundtrip).
|
|
5
|
+
//
|
|
6
|
+
// m1-t1: HIT path — after catalog + AssetRegistry.instantiate, the resolved
|
|
7
|
+
// copy payload is reachable via world.getSceneAssetForInstance +
|
|
8
|
+
// resolveAssetHandle, and _guidForAsset(copy) should return the original
|
|
9
|
+
// catalog GUID (currently RED — Finding 1 MISS).
|
|
10
|
+
//
|
|
11
|
+
// m1-t2: MISS path — an uncatalogued, hand-assembled SceneAsset
|
|
12
|
+
// allocated via world.allocSharedRef and instantiated still returns
|
|
13
|
+
// undefined from _guidForAsset (AC-05 structural error path); also
|
|
14
|
+
// rootsToSceneAsset on uncatalogued shared refs produces
|
|
15
|
+
// SceneCollectAssetGuidUnresolvedError with complete .code / .expected /
|
|
16
|
+
// .hint / .detail fields.
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
type Asset,
|
|
20
|
+
AssetRegistry,
|
|
21
|
+
resolveAssetHandle,
|
|
22
|
+
SceneCollectAssetGuidUnresolvedError,
|
|
23
|
+
} from '@forgeax/engine-assets-runtime';
|
|
24
|
+
import { World } from '@forgeax/engine-ecs';
|
|
25
|
+
import { createBoxGeometry } from '@forgeax/engine-geometry';
|
|
26
|
+
import { AssetGuid } from '@forgeax/engine-pack/guid';
|
|
27
|
+
import { MeshFilter, MeshRenderer, SceneInstance } from '@forgeax/engine-render';
|
|
28
|
+
import type {
|
|
29
|
+
AnimationClip,
|
|
30
|
+
Handle,
|
|
31
|
+
MaterialAsset,
|
|
32
|
+
MeshAsset,
|
|
33
|
+
SceneAsset,
|
|
34
|
+
} from '@forgeax/engine-types';
|
|
35
|
+
import { describe, expect, it } from 'vitest';
|
|
36
|
+
import { rootsToSceneAsset } from '../collect-scene-asset';
|
|
37
|
+
import { makeMockShaderRegistry } from './helpers/mock-shader-registry';
|
|
38
|
+
import { registerSceneComponents } from './helpers/register-scene-components';
|
|
39
|
+
|
|
40
|
+
function makeReg(): AssetRegistry {
|
|
41
|
+
return new AssetRegistry(makeMockShaderRegistry());
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function prepareWorld(world: World): void {
|
|
45
|
+
registerSceneComponents(world, [MeshFilter, MeshRenderer]);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function cubeMesh(): MeshAsset {
|
|
49
|
+
const r = createBoxGeometry(1, 1, 1);
|
|
50
|
+
if (!r.ok) throw new Error('createBoxGeometry(1,1,1) failed');
|
|
51
|
+
return r.value;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function unlitMaterial(): MaterialAsset {
|
|
55
|
+
return {
|
|
56
|
+
kind: 'material',
|
|
57
|
+
passes: [
|
|
58
|
+
{
|
|
59
|
+
name: 'Forward',
|
|
60
|
+
program: { module: 'forgeax::default-unlit' },
|
|
61
|
+
renderState: { tags: { LightMode: 'Forward' }, queue: 2000 },
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
values: { baseColor: [1, 1, 1] },
|
|
65
|
+
} as MaterialAsset;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Walk a SceneInstance subtree and return the first live MeshRenderer.materials[0]. */
|
|
69
|
+
function firstLiveMaterialHandle(world: World, root: Handle<string, 'shared'>): number | undefined {
|
|
70
|
+
for (const c of world.iterDescendants(root as never)) {
|
|
71
|
+
const mr = world.get(c, MeshRenderer);
|
|
72
|
+
if (!mr.ok) continue;
|
|
73
|
+
const mats = mr.value.materials as unknown as number[];
|
|
74
|
+
if (mats && mats.length > 0 && (mats[0] as number) !== 0) return mats[0] as number;
|
|
75
|
+
}
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function makeScene(entities?: SceneAsset['entities']): SceneAsset {
|
|
80
|
+
return {
|
|
81
|
+
kind: 'scene',
|
|
82
|
+
entities: entities ?? [{ localId: 0 as never, components: { Transform: {} } }],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const GUID_A = 'cbe42beb-8975-5096-b3a1-3dda4cb4c077';
|
|
87
|
+
|
|
88
|
+
function parseGuid(s: string): AssetGuid {
|
|
89
|
+
const r = AssetGuid.parse(s);
|
|
90
|
+
if (!r.ok) throw new Error(`invalid test GUID: ${s}`);
|
|
91
|
+
return r.value;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ── m1-t1: HIT path ─────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
describe('m1-t1 — origin index reverse-lookup HIT', () => {
|
|
97
|
+
it('_guidForAsset on instantiate-resolved copy returns the original catalog GUID', () => {
|
|
98
|
+
const reg = makeReg();
|
|
99
|
+
const world = new World();
|
|
100
|
+
prepareWorld(world);
|
|
101
|
+
|
|
102
|
+
// Catalog the scene asset.
|
|
103
|
+
const scene = makeScene();
|
|
104
|
+
reg.catalog(parseGuid(GUID_A), scene);
|
|
105
|
+
|
|
106
|
+
// Allocate a shared ref from the world (the column-handle path).
|
|
107
|
+
const handle = world.allocSharedRef<'SceneAsset', SceneAsset>('SceneAsset', scene);
|
|
108
|
+
|
|
109
|
+
// Instantiate via AssetRegistry.
|
|
110
|
+
const instRes = reg.instantiate(handle, world);
|
|
111
|
+
expect(instRes.ok).toBe(true);
|
|
112
|
+
if (!instRes.ok) return;
|
|
113
|
+
const root = instRes.value;
|
|
114
|
+
|
|
115
|
+
// Get the SceneInstance's source handle and resolve to payload.
|
|
116
|
+
const srcHandleRes = SceneOwner.worldGetSceneAssetForInstance(world, root);
|
|
117
|
+
expect(srcHandleRes.ok).toBe(true);
|
|
118
|
+
if (!srcHandleRes.ok) return;
|
|
119
|
+
const srcHandle = srcHandleRes.value;
|
|
120
|
+
|
|
121
|
+
const payloadRes = resolveAssetHandle<SceneAsset>(
|
|
122
|
+
world,
|
|
123
|
+
srcHandle as unknown as Handle<string, 'shared'>,
|
|
124
|
+
);
|
|
125
|
+
expect(payloadRes.ok).toBe(true);
|
|
126
|
+
if (!payloadRes.ok) return;
|
|
127
|
+
const copyPayload = payloadRes.value;
|
|
128
|
+
|
|
129
|
+
// The copy is a different object from the catalogued original
|
|
130
|
+
// (_resolveSceneGuids produces a deep copy).
|
|
131
|
+
expect(copyPayload).not.toBe(scene);
|
|
132
|
+
|
|
133
|
+
// Should return the catalog GUID after m1-i1; currently RED (undefined).
|
|
134
|
+
const guid = reg._guidForAsset(copyPayload as Asset);
|
|
135
|
+
expect(guid).toBe(GUID_A);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// ── m1-t2: MISS path ────────────────────────────────────────────────────
|
|
140
|
+
|
|
141
|
+
describe('m1-t2 — uncatalogued scene MISS path', () => {
|
|
142
|
+
it('_guidForAsset on uncatalogued payload returns undefined', () => {
|
|
143
|
+
const reg = makeReg();
|
|
144
|
+
const scene = makeScene();
|
|
145
|
+
|
|
146
|
+
// Never catalogued; just allocate a shared ref directly.
|
|
147
|
+
const guid = reg._guidForAsset(scene as Asset);
|
|
148
|
+
expect(guid).toBeUndefined();
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('rootsToSceneAsset on uncatalogued shared ref fails with SceneCollectAssetGuidUnresolvedError', () => {
|
|
152
|
+
const reg = makeReg();
|
|
153
|
+
const world = new World();
|
|
154
|
+
prepareWorld(world);
|
|
155
|
+
|
|
156
|
+
// Construct a scene with an entity that carries a shared<> field
|
|
157
|
+
// referencing an uncatalogued MeshAsset (never catalogued).
|
|
158
|
+
// world.allocSharedRef for the MeshAsset creates a user-tier handle,
|
|
159
|
+
// but _guidForAsset has no catalogue entry for it.
|
|
160
|
+
const mesh: Asset = {
|
|
161
|
+
kind: 'mesh',
|
|
162
|
+
vertices: new Float32Array(12 * 3),
|
|
163
|
+
indices: new Uint16Array([0, 1, 2]),
|
|
164
|
+
} as unknown as Asset;
|
|
165
|
+
|
|
166
|
+
const meshHandle = world.allocSharedRef('MeshAsset', mesh);
|
|
167
|
+
const asset: SceneAsset = {
|
|
168
|
+
kind: 'scene',
|
|
169
|
+
entities: [
|
|
170
|
+
{
|
|
171
|
+
localId: 0 as never,
|
|
172
|
+
components: {
|
|
173
|
+
MeshFilter: { assetHandle: meshHandle as never },
|
|
174
|
+
Transform: {},
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// Allocate shared ref for the scene and instantiate.
|
|
181
|
+
const sceneHandle = world.allocSharedRef<'SceneAsset', SceneAsset>('SceneAsset', asset);
|
|
182
|
+
const instRes = SceneOwner.worldInstantiateScene(world, sceneHandle);
|
|
183
|
+
expect(instRes.ok).toBe(true);
|
|
184
|
+
if (!instRes.ok) return;
|
|
185
|
+
const root = instRes.value.root;
|
|
186
|
+
|
|
187
|
+
// rootsToSceneAsset should fail-fast with the structured error.
|
|
188
|
+
const res = rootsToSceneAsset(reg, world, [root]);
|
|
189
|
+
expect(res.ok).toBe(false);
|
|
190
|
+
if (res.ok) return;
|
|
191
|
+
|
|
192
|
+
const err = res.error;
|
|
193
|
+
expect(err).toBeInstanceOf(SceneCollectAssetGuidUnresolvedError);
|
|
194
|
+
expect(err.code).toBe('scene-collect-asset-guid-unresolved');
|
|
195
|
+
expect(typeof err.expected).toBe('string');
|
|
196
|
+
expect(err.expected.length).toBeGreaterThan(0);
|
|
197
|
+
expect(typeof err.hint).toBe('string');
|
|
198
|
+
expect(err.hint.length).toBeGreaterThan(0);
|
|
199
|
+
expect(err.detail).toBeDefined();
|
|
200
|
+
expect(typeof err.detail.field).toBe('string');
|
|
201
|
+
// detail.handle is optional on SceneCollectAssetGuidUnresolvedDetail;
|
|
202
|
+
// the collect path emits handle (not guid).
|
|
203
|
+
const detail = err.detail as { field: string; handle?: number; guid?: string };
|
|
204
|
+
expect(typeof detail.handle).toBe('number');
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// ── w14 (M4) PROBE: mounted-scene-with-material identity anchor ──────────
|
|
209
|
+
//
|
|
210
|
+
// feat-20260713 M4 / w14 (D-6, probe-first). Empirically anchors the true
|
|
211
|
+
// root cause of the 2026-07-06 owned-payload reverse-lookup MISS
|
|
212
|
+
// (`2026-07-06-nested-mount-material-guid-unresolved-on-save.md`).
|
|
213
|
+
//
|
|
214
|
+
// research Finding 3 FALSIFIED the original feedback premise ("instantiate
|
|
215
|
+
// deep-copies material payload"): the mount path does NOT deep-copy material
|
|
216
|
+
// payloads — identity (===) is preserved end-to-end (allocSharedRef stores the
|
|
217
|
+
// reference, shared-ref-store.ts:159). The only object deep-copied is the
|
|
218
|
+
// SceneAsset envelope itself (already covered by `_originIndex`).
|
|
219
|
+
//
|
|
220
|
+
// The probe below runs both candidate scenarios live to pin the MISS to:
|
|
221
|
+
// (a) catalog override after a handle was minted -> the already-minted handle
|
|
222
|
+
// dangles at the SUPERSEDED payload object (asset-registry.ts:976 replaces
|
|
223
|
+
// the envelope.payload with a fresh object). `_guidForAsset` identity scan
|
|
224
|
+
// then finds only the NEW object; the old one is in neither the catalog
|
|
225
|
+
// (by identity) nor `_originIndex` -> undefined MISS. THIS is the observed
|
|
226
|
+
// 2026-07-06 state ("catalog has content-matching but different objects").
|
|
227
|
+
// (b) minting a handle from a non-catalog POD (glyph-text / tilemap systems) —
|
|
228
|
+
// ruled out for the material-save flow: those POD payloads never enter the
|
|
229
|
+
// catalog and are never saved through rootsToSceneAsset.
|
|
230
|
+
//
|
|
231
|
+
// PROBE CONCLUSION (run 2026-07-13): root cause is (a). The basic mount case
|
|
232
|
+
// (probe test 1) HITs — proving no deep-copy (Finding 3). The catalog-override
|
|
233
|
+
// case (probe test 2) MISSes — the anchored root cause. w15 fixes (a) by
|
|
234
|
+
// recording the SUPERSEDED payload -> GUID in `_originIndex` (key widened to
|
|
235
|
+
// `WeakMap<object,string>`) at the catalog-override point (asset-registry.ts:976),
|
|
236
|
+
// so `_guidForAsset` reverse-resolves handles minted before the override.
|
|
237
|
+
|
|
238
|
+
const MAT_GUID = '11111111-2222-4333-8444-555566667777';
|
|
239
|
+
const MESH_GUID = '00d274da-e863-41d8-bafe-10b97d1468d4';
|
|
240
|
+
const CHILD_GUID = '22222222-3333-4444-8555-666677778888';
|
|
241
|
+
const PARENT_GUID = '33333333-4444-4555-8666-777788889999';
|
|
242
|
+
|
|
243
|
+
describe('w14 M4 probe — owned payload identity anchor', () => {
|
|
244
|
+
it('atomically adopts a recooked Mesh identity for old handles, repeat loads, and collect/save', async () => {
|
|
245
|
+
const reg = makeReg();
|
|
246
|
+
const world = new World();
|
|
247
|
+
prepareWorld(world);
|
|
248
|
+
expect(reg.catalog(parseGuid(MESH_GUID), cubeMesh() as Asset).ok).toBe(true);
|
|
249
|
+
const liveMesh = reg.lookup<MeshAsset>(MESH_GUID);
|
|
250
|
+
expect(liveMesh).toBeDefined();
|
|
251
|
+
if (liveMesh === undefined) return;
|
|
252
|
+
const oldHandle = world.allocSharedRef<'MeshAsset', MeshAsset>('MeshAsset', liveMesh);
|
|
253
|
+
|
|
254
|
+
const livePositions = liveMesh.attributes.position;
|
|
255
|
+
expect(livePositions).toBeInstanceOf(Float32Array);
|
|
256
|
+
if (!(livePositions instanceof Float32Array)) return;
|
|
257
|
+
const oldPosition = livePositions[0];
|
|
258
|
+
expect(oldPosition).toBeDefined();
|
|
259
|
+
if (oldPosition === undefined) return;
|
|
260
|
+
const recooked = cubeMesh();
|
|
261
|
+
const recookedPositions = recooked.attributes.position;
|
|
262
|
+
expect(recookedPositions).toBeInstanceOf(Float32Array);
|
|
263
|
+
if (!(recookedPositions instanceof Float32Array)) return;
|
|
264
|
+
recookedPositions[0] = oldPosition + 10;
|
|
265
|
+
Object.assign(recooked, { materialSlots: [{ slotName: 'Recooked' }] });
|
|
266
|
+
expect(reg.catalog(parseGuid(MESH_GUID), recooked as Asset).ok).toBe(true);
|
|
267
|
+
expect(reg.adoptReloadedMeshMaterialSlots(MESH_GUID, liveMesh as Asset).ok).toBe(true);
|
|
268
|
+
|
|
269
|
+
const oldResolved = resolveAssetHandle<MeshAsset>(world, oldHandle);
|
|
270
|
+
expect(oldResolved.ok).toBe(true);
|
|
271
|
+
if (!oldResolved.ok) return;
|
|
272
|
+
expect(oldResolved.value).toBe(liveMesh);
|
|
273
|
+
expect(oldResolved.value.materialSlots).toEqual([{ slotName: 'Recooked' }]);
|
|
274
|
+
expect(oldResolved.value.attributes.position).toBe(livePositions);
|
|
275
|
+
expect(livePositions[0]).toBe(oldPosition);
|
|
276
|
+
|
|
277
|
+
const repeated = await reg.loadByGuid<MeshAsset>(reg.parseGuid(MESH_GUID));
|
|
278
|
+
expect(repeated.ok).toBe(true);
|
|
279
|
+
if (!repeated.ok) return;
|
|
280
|
+
expect(repeated.value).toBe(liveMesh);
|
|
281
|
+
expect(reg._guidForAsset(repeated.value as Asset)).toBe(MESH_GUID);
|
|
282
|
+
|
|
283
|
+
const newHandle = world.allocSharedRef<'MeshAsset', MeshAsset>('MeshAsset', repeated.value);
|
|
284
|
+
const scene: SceneAsset = {
|
|
285
|
+
kind: 'scene',
|
|
286
|
+
entities: [
|
|
287
|
+
{
|
|
288
|
+
localId: 0 as never,
|
|
289
|
+
components: {
|
|
290
|
+
Transform: {},
|
|
291
|
+
MeshFilter: { assetHandle: newHandle as never },
|
|
292
|
+
MeshRenderer: { materials: [] },
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
};
|
|
297
|
+
const sceneHandle = world.allocSharedRef<'SceneAsset', SceneAsset>('SceneAsset', scene);
|
|
298
|
+
const instantiated = SceneOwner.worldInstantiateScene(world, sceneHandle);
|
|
299
|
+
expect(instantiated.ok).toBe(true);
|
|
300
|
+
if (!instantiated.ok) return;
|
|
301
|
+
const collected = rootsToSceneAsset(reg, world, [instantiated.value.root]);
|
|
302
|
+
expect(collected.ok).toBe(true);
|
|
303
|
+
if (!collected.ok) return;
|
|
304
|
+
expect(collected.value.entities).toContainEqual(
|
|
305
|
+
expect.objectContaining({
|
|
306
|
+
components: expect.objectContaining({
|
|
307
|
+
MeshFilter: expect.objectContaining({ assetHandle: MESH_GUID }),
|
|
308
|
+
}),
|
|
309
|
+
}),
|
|
310
|
+
);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
it('basic mount preserves material payload identity end-to-end (research Finding 3)', () => {
|
|
314
|
+
const reg = makeReg();
|
|
315
|
+
const world = new World();
|
|
316
|
+
prepareWorld(world);
|
|
317
|
+
|
|
318
|
+
const mesh = cubeMesh();
|
|
319
|
+
const mat = unlitMaterial();
|
|
320
|
+
expect(reg.catalog(parseGuid(MESH_GUID), mesh as Asset).ok).toBe(true);
|
|
321
|
+
expect(reg.catalog(parseGuid(MAT_GUID), mat as Asset).ok).toBe(true);
|
|
322
|
+
|
|
323
|
+
// Child scene: one owned entity with a MeshRenderer whose materials[0] is a
|
|
324
|
+
// GUID string (post-parse intermediate shape), catalogued with refs edges.
|
|
325
|
+
const child: SceneAsset = {
|
|
326
|
+
kind: 'scene',
|
|
327
|
+
entities: [
|
|
328
|
+
{
|
|
329
|
+
localId: 0 as never,
|
|
330
|
+
components: {
|
|
331
|
+
Transform: {},
|
|
332
|
+
MeshFilter: { assetHandle: MESH_GUID as never },
|
|
333
|
+
MeshRenderer: { materials: [MAT_GUID] as never },
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
],
|
|
337
|
+
};
|
|
338
|
+
reg.catalog(parseGuid(CHILD_GUID), child as Asset, [{ guid: MESH_GUID }, { guid: MAT_GUID }]);
|
|
339
|
+
|
|
340
|
+
const parent: SceneAsset = {
|
|
341
|
+
kind: 'scene',
|
|
342
|
+
entities: [{ localId: 0 as never, components: { Transform: {} } }],
|
|
343
|
+
mounts: [
|
|
344
|
+
{ localId: 1 as never, source: CHILD_GUID, memberFirst: 2 as never, memberCount: 1 },
|
|
345
|
+
],
|
|
346
|
+
};
|
|
347
|
+
reg.catalog(parseGuid(PARENT_GUID), parent as Asset);
|
|
348
|
+
|
|
349
|
+
const ph = world.allocSharedRef<'SceneAsset', SceneAsset>('SceneAsset', parent);
|
|
350
|
+
const inst = reg.instantiate(ph, world);
|
|
351
|
+
expect(inst.ok).toBe(true);
|
|
352
|
+
if (!inst.ok) return;
|
|
353
|
+
|
|
354
|
+
// Confirm a child SceneInstance materialised (the owned material lives under it).
|
|
355
|
+
let hasChildSi = false;
|
|
356
|
+
for (const c of world.iterDescendants(inst.value)) {
|
|
357
|
+
if (c === inst.value) continue;
|
|
358
|
+
if (world.get(c, SceneInstance).ok) hasChildSi = true;
|
|
359
|
+
}
|
|
360
|
+
expect(hasChildSi).toBe(true);
|
|
361
|
+
|
|
362
|
+
const matHandle = firstLiveMaterialHandle(
|
|
363
|
+
world,
|
|
364
|
+
inst.value as unknown as Handle<string, 'shared'>,
|
|
365
|
+
);
|
|
366
|
+
expect(matHandle).toBeDefined();
|
|
367
|
+
if (matHandle === undefined) return;
|
|
368
|
+
|
|
369
|
+
const liveRes = resolveAssetHandle<MaterialAsset>(
|
|
370
|
+
world,
|
|
371
|
+
matHandle as unknown as Handle<string, 'shared'>,
|
|
372
|
+
);
|
|
373
|
+
expect(liveRes.ok).toBe(true);
|
|
374
|
+
if (!liveRes.ok) return;
|
|
375
|
+
|
|
376
|
+
// Finding 3: the live handle payload IS the catalogued object (no deep-copy).
|
|
377
|
+
const catalogPayload = reg.assetCatalog.get(MAT_GUID.toLowerCase())?.payload;
|
|
378
|
+
expect(liveRes.value).toBe(catalogPayload);
|
|
379
|
+
expect(liveRes.value).toBe(mat);
|
|
380
|
+
|
|
381
|
+
// Reverse-lookup HITs via the catalog identity scan.
|
|
382
|
+
expect(reg._guidForAsset(liveRes.value as Asset)).toBe(MAT_GUID);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it('catalog override leaves already-minted handle reverse-lookupable (root cause a)', () => {
|
|
386
|
+
// Reproduces the anchored 2026-07-06 state: a handle is minted from the
|
|
387
|
+
// catalogued material, then the SAME GUID is re-catalogued with a fresh
|
|
388
|
+
// object (re-import / loadByGuid refetch). The live handle still points at
|
|
389
|
+
// the FIRST object; the catalog now holds a DIFFERENT object.
|
|
390
|
+
const reg = makeReg();
|
|
391
|
+
const world = new World();
|
|
392
|
+
prepareWorld(world);
|
|
393
|
+
|
|
394
|
+
const mat1 = unlitMaterial();
|
|
395
|
+
expect(reg.catalog(parseGuid(MAT_GUID), mat1 as Asset).ok).toBe(true);
|
|
396
|
+
|
|
397
|
+
// Mint a handle from the first catalogued payload (instantiate mints from
|
|
398
|
+
// envelope.payload — identity preserved at mint time).
|
|
399
|
+
const handle = world.allocSharedRef<'MaterialAsset', MaterialAsset>('MaterialAsset', mat1);
|
|
400
|
+
|
|
401
|
+
// Re-catalog the SAME GUID with a different object.
|
|
402
|
+
const mat2 = unlitMaterial();
|
|
403
|
+
expect(mat2).not.toBe(mat1);
|
|
404
|
+
expect(reg.catalog(parseGuid(MAT_GUID), mat2 as Asset).ok).toBe(true);
|
|
405
|
+
|
|
406
|
+
const liveRes = resolveAssetHandle<MaterialAsset>(
|
|
407
|
+
world,
|
|
408
|
+
handle as unknown as Handle<string, 'shared'>,
|
|
409
|
+
);
|
|
410
|
+
expect(liveRes.ok).toBe(true);
|
|
411
|
+
if (!liveRes.ok) return;
|
|
412
|
+
|
|
413
|
+
// The live handle dangles at the superseded object (root cause a).
|
|
414
|
+
expect(liveRes.value).toBe(mat1);
|
|
415
|
+
expect(liveRes.value).not.toBe(reg.assetCatalog.get(MAT_GUID.toLowerCase())?.payload);
|
|
416
|
+
|
|
417
|
+
// DESIRED (RED until w15): reverse-lookup must still resolve the GUID for
|
|
418
|
+
// the superseded-but-live payload — otherwise collect/save fails with a
|
|
419
|
+
// GUID-unresolved error (the 2026-07-06 crash).
|
|
420
|
+
expect(reg._guidForAsset(liveRes.value as Asset)).toBe(MAT_GUID);
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
// ── w16 (M4) AC-08: reverse-lookup HIT across payload kinds ─────────────
|
|
425
|
+
//
|
|
426
|
+
// feat-20260713 M4 / w16 (AC-08). With w15's fix in place, the reverse-lookup
|
|
427
|
+
// (`_guidForAsset`) hits for both material and AnimationClip payloads across the
|
|
428
|
+
// re-catalog boundary, hits consistently for a payload referenced by many
|
|
429
|
+
// entities, and correctly returns undefined (leading to a structured collect
|
|
430
|
+
// error, never a silent zero) for a fresh copy that was never catalogued
|
|
431
|
+
// (requirements edge case "modified payload judged as a new asset").
|
|
432
|
+
|
|
433
|
+
const CLIP_GUID = '44444444-5555-4666-8777-888899990000';
|
|
434
|
+
const MAT_GUID_B = '55555555-6666-4777-8888-99990000aaaa';
|
|
435
|
+
|
|
436
|
+
function mkClip(duration: number): AnimationClip {
|
|
437
|
+
return { kind: 'animation-clip', duration, channels: [] } as AnimationClip;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
describe('w16 M4 — AC-08 reverse-lookup hit across kinds', () => {
|
|
441
|
+
it('material payload GUID reverse-lookup hits across a catalog override', () => {
|
|
442
|
+
const reg = makeReg();
|
|
443
|
+
const world = new World();
|
|
444
|
+
|
|
445
|
+
const mat1 = unlitMaterial();
|
|
446
|
+
expect(reg.catalog(parseGuid(MAT_GUID), mat1 as Asset).ok).toBe(true);
|
|
447
|
+
const handle = world.allocSharedRef<'MaterialAsset', MaterialAsset>('MaterialAsset', mat1);
|
|
448
|
+
|
|
449
|
+
// Re-import: same GUID re-catalogued with a fresh object.
|
|
450
|
+
expect(reg.catalog(parseGuid(MAT_GUID), unlitMaterial() as Asset).ok).toBe(true);
|
|
451
|
+
|
|
452
|
+
const live = resolveAssetHandle<MaterialAsset>(
|
|
453
|
+
world,
|
|
454
|
+
handle as unknown as Handle<string, 'shared'>,
|
|
455
|
+
);
|
|
456
|
+
expect(live.ok).toBe(true);
|
|
457
|
+
if (!live.ok) return;
|
|
458
|
+
expect(reg._guidForAsset(live.value as Asset)).toBe(MAT_GUID);
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
it('AnimationClip payload GUID reverse-lookup hits across a catalog override', () => {
|
|
462
|
+
const reg = makeReg();
|
|
463
|
+
const world = new World();
|
|
464
|
+
|
|
465
|
+
const clip1 = mkClip(1.5);
|
|
466
|
+
expect(reg.catalog(parseGuid(CLIP_GUID), clip1 as Asset).ok).toBe(true);
|
|
467
|
+
const handle = world.allocSharedRef<'AnimationClip', AnimationClip>('AnimationClip', clip1);
|
|
468
|
+
|
|
469
|
+
expect(reg.catalog(parseGuid(CLIP_GUID), mkClip(1.5) as Asset).ok).toBe(true);
|
|
470
|
+
|
|
471
|
+
const live = resolveAssetHandle<AnimationClip>(
|
|
472
|
+
world,
|
|
473
|
+
handle as unknown as Handle<string, 'shared'>,
|
|
474
|
+
);
|
|
475
|
+
expect(live.ok).toBe(true);
|
|
476
|
+
if (!live.ok) return;
|
|
477
|
+
expect(reg._guidForAsset(live.value as Asset)).toBe(CLIP_GUID);
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
it('same shared payload referenced by many entities reverse-looks-up to one GUID', () => {
|
|
481
|
+
const reg = makeReg();
|
|
482
|
+
const world = new World();
|
|
483
|
+
|
|
484
|
+
// Two distinct catalogued materials, each minted once, then both overridden.
|
|
485
|
+
const matA1 = unlitMaterial();
|
|
486
|
+
const matB1 = unlitMaterial();
|
|
487
|
+
expect(reg.catalog(parseGuid(MAT_GUID), matA1 as Asset).ok).toBe(true);
|
|
488
|
+
expect(reg.catalog(parseGuid(MAT_GUID_B), matB1 as Asset).ok).toBe(true);
|
|
489
|
+
|
|
490
|
+
// Mint several handles that all share matA1 (one shared payload, N refs) and
|
|
491
|
+
// one handle sharing matB1.
|
|
492
|
+
const hA1 = world.allocSharedRef<'MaterialAsset', MaterialAsset>('MaterialAsset', matA1);
|
|
493
|
+
const hA2 = world.allocSharedRef<'MaterialAsset', MaterialAsset>('MaterialAsset', matA1);
|
|
494
|
+
const hB1 = world.allocSharedRef<'MaterialAsset', MaterialAsset>('MaterialAsset', matB1);
|
|
495
|
+
|
|
496
|
+
// Override both GUIDs (re-import).
|
|
497
|
+
expect(reg.catalog(parseGuid(MAT_GUID), unlitMaterial() as Asset).ok).toBe(true);
|
|
498
|
+
expect(reg.catalog(parseGuid(MAT_GUID_B), unlitMaterial() as Asset).ok).toBe(true);
|
|
499
|
+
|
|
500
|
+
for (const h of [hA1, hA2]) {
|
|
501
|
+
const live = resolveAssetHandle<MaterialAsset>(
|
|
502
|
+
world,
|
|
503
|
+
h as unknown as Handle<string, 'shared'>,
|
|
504
|
+
);
|
|
505
|
+
expect(live.ok).toBe(true);
|
|
506
|
+
if (!live.ok) return;
|
|
507
|
+
// All references to the same shared payload resolve to the same GUID.
|
|
508
|
+
expect(reg._guidForAsset(live.value as Asset)).toBe(MAT_GUID);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const liveB = resolveAssetHandle<MaterialAsset>(
|
|
512
|
+
world,
|
|
513
|
+
hB1 as unknown as Handle<string, 'shared'>,
|
|
514
|
+
);
|
|
515
|
+
expect(liveB.ok).toBe(true);
|
|
516
|
+
if (!liveB.ok) return;
|
|
517
|
+
expect(reg._guidForAsset(liveB.value as Asset)).toBe(MAT_GUID_B);
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
it('a fresh copy that was never catalogued is judged a new asset (structured collect error, not silent zero)', () => {
|
|
521
|
+
// Edge case: "deep-copied payload modified before collect -> judged a new
|
|
522
|
+
// asset, structured error, never silently zeroed". A payload object that was
|
|
523
|
+
// never the catalog identity (a fresh copy) reverse-looks-up to undefined,
|
|
524
|
+
// and collect surfaces SceneCollectAssetGuidUnresolvedError — not a silent 0.
|
|
525
|
+
const reg = makeReg();
|
|
526
|
+
const world = new World();
|
|
527
|
+
prepareWorld(world);
|
|
528
|
+
|
|
529
|
+
const mesh = cubeMesh();
|
|
530
|
+
const mat = unlitMaterial();
|
|
531
|
+
expect(reg.catalog(parseGuid(MESH_GUID), mesh as Asset).ok).toBe(true);
|
|
532
|
+
expect(reg.catalog(parseGuid(MAT_GUID), mat as Asset).ok).toBe(true);
|
|
533
|
+
|
|
534
|
+
// A structurally-modified COPY (fresh object, never catalogued).
|
|
535
|
+
const modifiedCopy: MaterialAsset = {
|
|
536
|
+
...mat,
|
|
537
|
+
values: { baseColor: [0.2, 0.2, 0.2] },
|
|
538
|
+
} as MaterialAsset;
|
|
539
|
+
expect(modifiedCopy).not.toBe(mat);
|
|
540
|
+
|
|
541
|
+
// reverse-lookup misses (correctly — it is a new asset, not the catalogued one).
|
|
542
|
+
expect(reg._guidForAsset(modifiedCopy as Asset)).toBeUndefined();
|
|
543
|
+
|
|
544
|
+
// Drive it through a real collect: mint the copy onto a MeshRenderer and
|
|
545
|
+
// confirm collect fails with the STRUCTURED error (not a silent zero).
|
|
546
|
+
const meshHandle = world.allocSharedRef<'MeshAsset', MeshAsset>('MeshAsset', mesh);
|
|
547
|
+
const matHandle = world.allocSharedRef<'MaterialAsset', MaterialAsset>(
|
|
548
|
+
'MaterialAsset',
|
|
549
|
+
modifiedCopy,
|
|
550
|
+
);
|
|
551
|
+
const scene: SceneAsset = {
|
|
552
|
+
kind: 'scene',
|
|
553
|
+
entities: [
|
|
554
|
+
{
|
|
555
|
+
localId: 0 as never,
|
|
556
|
+
components: {
|
|
557
|
+
Transform: {},
|
|
558
|
+
MeshFilter: { assetHandle: meshHandle as never },
|
|
559
|
+
MeshRenderer: { materials: [matHandle] as never },
|
|
560
|
+
},
|
|
561
|
+
},
|
|
562
|
+
],
|
|
563
|
+
};
|
|
564
|
+
const sceneHandle = world.allocSharedRef<'SceneAsset', SceneAsset>('SceneAsset', scene);
|
|
565
|
+
const instRes = SceneOwner.worldInstantiateScene(world, sceneHandle);
|
|
566
|
+
expect(instRes.ok).toBe(true);
|
|
567
|
+
if (!instRes.ok) return;
|
|
568
|
+
|
|
569
|
+
const res = rootsToSceneAsset(reg, world, [instRes.value.root]);
|
|
570
|
+
expect(res.ok).toBe(false);
|
|
571
|
+
if (res.ok) return;
|
|
572
|
+
expect(res.error).toBeInstanceOf(SceneCollectAssetGuidUnresolvedError);
|
|
573
|
+
expect(res.error.code).toBe('scene-collect-asset-guid-unresolved');
|
|
574
|
+
});
|
|
575
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// asset-registry-hdr-equirect.test.ts
|
|
2
|
+
// feat-20260630-equirect-kind-internalized-ibl-declarative-skyligh M1 / w1.
|
|
3
|
+
//
|
|
4
|
+
// TDD red->green: locks the runtime equirectLoader loading an EquirectAsset
|
|
5
|
+
// POD from a Pack v2 asset-local rgba16float artifact.
|
|
6
|
+
|
|
7
|
+
import { equirectLoader, PACK_ARTIFACT_LOADERS } from '@forgeax/engine-assets-runtime';
|
|
8
|
+
import type { LoadContext, LoaderAsyncResult } from '@forgeax/engine-types';
|
|
9
|
+
import { describe, expect, it } from 'vitest';
|
|
10
|
+
|
|
11
|
+
function mockCtx(binaries: Record<string, Uint8Array>): LoadContext {
|
|
12
|
+
return {
|
|
13
|
+
fetchBinary: async (url: string) => {
|
|
14
|
+
const b = binaries[url];
|
|
15
|
+
return b !== undefined
|
|
16
|
+
? { ok: true as const, value: b }
|
|
17
|
+
: { ok: false as const, error: new Error(`no binary for ${url}`) };
|
|
18
|
+
},
|
|
19
|
+
resolveRef: async () => ({ ok: false as const, error: new Error('no ref') }),
|
|
20
|
+
transcodeCaps: { bc: false, etc2: false, astc: false },
|
|
21
|
+
device: undefined,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe('equirectLoader (w1)', () => {
|
|
26
|
+
it('(a) PACK_ARTIFACT_LOADERS includes equirect after texture + font', () => {
|
|
27
|
+
expect(PACK_ARTIFACT_LOADERS.map((l) => l.kind)).toContain('equirect');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('(b) loads an EquirectAsset POD (kind:"equirect" + rgba16float) from .bin bytes', async () => {
|
|
31
|
+
// rgba16float = 8 bytes per pixel; 2x2 = 32 bytes.
|
|
32
|
+
const data = new Uint8Array(2 * 2 * 4 * 2).fill(0x3c);
|
|
33
|
+
const input = {
|
|
34
|
+
guid: '11111111-1111-4111-8111-111111111111',
|
|
35
|
+
kind: 'equirect',
|
|
36
|
+
payload: {
|
|
37
|
+
kind: 'equirect',
|
|
38
|
+
width: 2,
|
|
39
|
+
height: 2,
|
|
40
|
+
format: 'rgba16float',
|
|
41
|
+
colorSpace: 'linear',
|
|
42
|
+
},
|
|
43
|
+
refs: [],
|
|
44
|
+
artifacts: {
|
|
45
|
+
body: {
|
|
46
|
+
descriptor: { path: 'env.bin', mediaType: 'application/x-forgeax-rgba16f' },
|
|
47
|
+
bytes: data,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
if (equirectLoader.loadPack === undefined) throw new Error('equirect Pack loader is missing');
|
|
52
|
+
const out = (await equirectLoader.loadPack(input, mockCtx({}))) as LoaderAsyncResult;
|
|
53
|
+
expect(out.ok).toBe(true);
|
|
54
|
+
if (out.ok) {
|
|
55
|
+
expect(out.value).toMatchObject({
|
|
56
|
+
kind: 'equirect',
|
|
57
|
+
width: 2,
|
|
58
|
+
height: 2,
|
|
59
|
+
format: 'rgba16float',
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('(c) fails when the source is not an imported .bin', async () => {
|
|
65
|
+
if (equirectLoader.loadPack === undefined) throw new Error('equirect Pack loader is missing');
|
|
66
|
+
const out = (await equirectLoader.loadPack(
|
|
67
|
+
{
|
|
68
|
+
guid: '11111111-1111-4111-8111-111111111111',
|
|
69
|
+
kind: 'equirect',
|
|
70
|
+
payload: { kind: 'equirect' },
|
|
71
|
+
refs: [],
|
|
72
|
+
artifacts: {},
|
|
73
|
+
},
|
|
74
|
+
mockCtx({}),
|
|
75
|
+
)) as LoaderAsyncResult;
|
|
76
|
+
expect(out.ok).toBe(false);
|
|
77
|
+
});
|
|
78
|
+
});
|