@forgeax/engine-assets-runtime 0.1.4 → 0.1.7
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 +453 -175
- package/dist/.tsbuildinfo +1 -1
- package/dist/__tests__/aabb.unit.test.d.ts +2 -0
- package/dist/__tests__/aabb.unit.test.d.ts.map +1 -0
- package/dist/__tests__/artifact-integrity.unit.test.d.ts +2 -0
- package/dist/__tests__/artifact-integrity.unit.test.d.ts.map +1 -0
- package/dist/__tests__/artifact-io.unit.test.d.ts +2 -0
- package/dist/__tests__/artifact-io.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-evidence-sdk.integration.test.d.ts +2 -0
- package/dist/__tests__/asset-evidence-sdk.integration.test.d.ts.map +1 -0
- package/dist/__tests__/asset-evidence.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-evidence.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-kind.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-kind.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-catalog-reconcile.integration.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-catalog-reconcile.integration.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-catalog-reconcile.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-catalog-reconcile.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-catalog.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-catalog.unit.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.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-runtime-error-code-owner.test-d.d.ts +2 -0
- package/dist/__tests__/asset-runtime-error-code-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/catalog-replica.unit.test.d.ts +2 -0
- package/dist/__tests__/catalog-replica.unit.test.d.ts.map +1 -0
- package/dist/__tests__/catalog-source-v2.unit.test.d.ts +2 -0
- package/dist/__tests__/catalog-source-v2.unit.test.d.ts.map +1 -0
- package/dist/__tests__/catalog-source.unit.test.d.ts +2 -0
- package/dist/__tests__/catalog-source.unit.test.d.ts.map +1 -0
- package/dist/__tests__/catalog-subscription-order.unit.test.d.ts +2 -0
- package/dist/__tests__/catalog-subscription-order.unit.test.d.ts.map +1 -0
- package/dist/__tests__/catalog-subscription.unit.test.d.ts +2 -0
- package/dist/__tests__/catalog-subscription.unit.test.d.ts.map +1 -0
- package/dist/__tests__/decode-image-bytes-pass-through.test.d.ts +2 -0
- package/dist/__tests__/decode-image-bytes-pass-through.test.d.ts.map +1 -0
- package/dist/__tests__/decode-image-bytes.browser.test.d.ts +2 -0
- package/dist/__tests__/decode-image-bytes.browser.test.d.ts.map +1 -0
- package/dist/__tests__/decode-image-bytes.test-d.d.ts +2 -0
- package/dist/__tests__/decode-image-bytes.test-d.d.ts.map +1 -0
- package/dist/__tests__/decode-image-bytes.test.d.ts +2 -0
- package/dist/__tests__/decode-image-bytes.test.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__/errors-asset.unit.test.d.ts +2 -0
- package/dist/__tests__/errors-asset.unit.test.d.ts.map +1 -0
- package/dist/__tests__/inline-pack-loaders.unit.test.d.ts +2 -0
- package/dist/__tests__/inline-pack-loaders.unit.test.d.ts.map +1 -0
- package/dist/__tests__/instantiate-context.unit.test.d.ts +2 -0
- package/dist/__tests__/instantiate-context.unit.test.d.ts.map +1 -0
- package/dist/__tests__/instantiate-publication-fence.unit.test.d.ts +2 -0
- package/dist/__tests__/instantiate-publication-fence.unit.test.d.ts.map +1 -0
- package/dist/__tests__/instantiate.unit.test.d.ts +2 -0
- package/dist/__tests__/instantiate.unit.test.d.ts.map +1 -0
- package/dist/__tests__/loader-input-contract.unit.test.d.ts +2 -0
- package/dist/__tests__/loader-input-contract.unit.test.d.ts.map +1 -0
- package/dist/__tests__/loader-registry.unit.test.d.ts +2 -0
- package/dist/__tests__/loader-registry.unit.test.d.ts.map +1 -0
- package/dist/__tests__/material-dependency-invalidation.unit.test.d.ts +2 -0
- package/dist/__tests__/material-dependency-invalidation.unit.test.d.ts.map +1 -0
- package/dist/__tests__/material-generation-cache.unit.test.d.ts +2 -0
- package/dist/__tests__/material-generation-cache.unit.test.d.ts.map +1 -0
- package/dist/__tests__/material-load-consumer.test-d.d.ts +2 -0
- package/dist/__tests__/material-load-consumer.test-d.d.ts.map +1 -0
- package/dist/__tests__/material-ready.integration.test.d.ts +2 -0
- package/dist/__tests__/material-ready.integration.test.d.ts.map +1 -0
- package/dist/__tests__/material-stale-generation.unit.test.d.ts +2 -0
- package/dist/__tests__/material-stale-generation.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin-morph-v3.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin-morph-v3.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin.unit.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mipmap-generator.unit.test.d.ts +2 -0
- package/dist/__tests__/mipmap-generator.unit.test.d.ts.map +1 -0
- package/dist/__tests__/pack-artifact-transcode-model-owner.test.d.ts +2 -0
- package/dist/__tests__/pack-artifact-transcode-model-owner.test.d.ts.map +1 -0
- package/dist/__tests__/pack-basis-load.integration.test.d.ts +2 -0
- package/dist/__tests__/pack-basis-load.integration.test.d.ts.map +1 -0
- package/dist/__tests__/payload-validate.unit.test.d.ts +2 -0
- package/dist/__tests__/payload-validate.unit.test.d.ts.map +1 -0
- package/dist/__tests__/plugin.test.d.ts +2 -0
- package/dist/__tests__/plugin.test.d.ts.map +1 -0
- package/dist/__tests__/registry-concurrency.bench.test.d.ts +2 -0
- package/dist/__tests__/registry-concurrency.bench.test.d.ts.map +1 -0
- package/dist/__tests__/registry-failure-retry.integration.test.d.ts +2 -0
- package/dist/__tests__/registry-failure-retry.integration.test.d.ts.map +1 -0
- package/dist/__tests__/registry-load-graph.integration.test.d.ts +2 -0
- package/dist/__tests__/registry-load-graph.integration.test.d.ts.map +1 -0
- package/dist/__tests__/registry-load-state.unit.test.d.ts +2 -0
- package/dist/__tests__/registry-load-state.unit.test.d.ts.map +1 -0
- package/dist/__tests__/registry-package-cache.unit.test.d.ts +2 -0
- package/dist/__tests__/registry-package-cache.unit.test.d.ts.map +1 -0
- package/dist/__tests__/registry-public-ready.unit.test.d.ts +2 -0
- package/dist/__tests__/registry-public-ready.unit.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__/scene-handle-fields.unit.test.d.ts +2 -0
- package/dist/__tests__/scene-handle-fields.unit.test.d.ts.map +1 -0
- package/dist/__tests__/scene-payload.unit.test.d.ts +2 -0
- package/dist/__tests__/scene-payload.unit.test.d.ts.map +1 -0
- package/dist/__tests__/scene-publication-fence.test.d.ts +2 -0
- package/dist/__tests__/scene-publication-fence.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack-public-surface.test-d.d.ts +2 -0
- package/dist/__tests__/scriptable-pack-public-surface.test-d.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__/validate-material.unit.test.d.ts +2 -0
- package/dist/__tests__/validate-material.unit.test.d.ts.map +1 -0
- package/dist/__tests__/video-pack-loader.integration.test.d.ts +2 -0
- package/dist/__tests__/video-pack-loader.integration.test.d.ts.map +1 -0
- package/dist/aabb.d.ts +3 -0
- package/dist/aabb.d.ts.map +1 -0
- package/dist/asset-registry.d.ts +554 -0
- package/dist/asset-registry.d.ts.map +1 -0
- package/dist/builtin-asset-registry.d.ts +22 -0
- package/dist/builtin-asset-registry.d.ts.map +1 -0
- package/dist/catalog-source.d.ts +2 -1
- package/dist/catalog-source.d.ts.map +1 -1
- package/dist/decode-image-bytes.d.ts +57 -0
- package/dist/decode-image-bytes.d.ts.map +1 -0
- package/dist/dynamic-texture-store.d.ts +89 -0
- package/dist/dynamic-texture-store.d.ts.map +1 -0
- package/dist/errors/asset.d.ts +201 -0
- package/dist/errors/asset.d.ts.map +1 -0
- package/dist/handles.d.ts +143 -0
- package/dist/handles.d.ts.map +1 -0
- package/dist/image-error.d.ts +6 -0
- package/dist/image-error.d.ts.map +1 -0
- package/dist/index.d.ts +32 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +7233 -114
- package/dist/index.mjs.map +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/loader-registry.d.ts +63 -0
- package/dist/loader-registry.d.ts.map +1 -0
- package/dist/loaders/inline-pack.d.ts +72 -0
- package/dist/loaders/inline-pack.d.ts.map +1 -0
- package/dist/loaders/mesh-bin.d.ts +21 -0
- package/dist/loaders/mesh-bin.d.ts.map +1 -0
- package/dist/loaders/pack-artifact.d.ts +20 -0
- package/dist/loaders/pack-artifact.d.ts.map +1 -0
- package/dist/material/dependency-graph.d.ts +9 -0
- package/dist/material/dependency-graph.d.ts.map +1 -0
- package/dist/material/generation-cache.d.ts +24 -0
- package/dist/material/generation-cache.d.ts.map +1 -0
- package/dist/material/inspection.d.ts +24 -0
- package/dist/material/inspection.d.ts.map +1 -0
- package/dist/material/loader.d.ts +59 -0
- package/dist/material/loader.d.ts.map +1 -0
- package/dist/material/runtime-shader.d.ts +14 -0
- package/dist/material/runtime-shader.d.ts.map +1 -0
- package/dist/mesh-bin.d.ts +3 -0
- package/dist/mesh-bin.d.ts.map +1 -0
- package/dist/mipmap-generator.d.ts +132 -0
- package/dist/mipmap-generator.d.ts.map +1 -0
- package/dist/payload-validate.d.ts +60 -0
- package/dist/payload-validate.d.ts.map +1 -0
- package/dist/plugin.d.ts +16 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/registry/artifact-io.d.ts +16 -0
- package/dist/registry/artifact-io.d.ts.map +1 -0
- package/dist/registry/asset-evidence.d.ts +13 -0
- package/dist/registry/asset-evidence.d.ts.map +1 -0
- package/dist/registry/catalog-state.d.ts +47 -0
- package/dist/registry/catalog-state.d.ts.map +1 -0
- package/dist/registry/catalog.d.ts +48 -0
- package/dist/registry/catalog.d.ts.map +1 -0
- package/dist/registry/instantiate.d.ts +153 -0
- package/dist/registry/instantiate.d.ts.map +1 -0
- package/dist/registry/load-by-guid.d.ts +247 -0
- package/dist/registry/load-by-guid.d.ts.map +1 -0
- package/dist/registry/load-state.d.ts +26 -0
- package/dist/registry/load-state.d.ts.map +1 -0
- package/dist/registry/runtime-projection.d.ts +16 -0
- package/dist/registry/runtime-projection.d.ts.map +1 -0
- package/dist/registry/scene-publication-fence.d.ts +38 -0
- package/dist/registry/scene-publication-fence.d.ts.map +1 -0
- package/dist/registry/validate-material.d.ts +77 -0
- package/dist/registry/validate-material.d.ts.map +1 -0
- package/dist/resolve-asset-handle.d.ts +43 -0
- package/dist/resolve-asset-handle.d.ts.map +1 -0
- package/dist/resolve-tileset-runtime.d.ts +23 -0
- package/dist/resolve-tileset-runtime.d.ts.map +1 -0
- package/dist/scene-handle-fields.d.ts +88 -0
- package/dist/scene-handle-fields.d.ts.map +1 -0
- package/dist/scene-payload.d.ts +15 -0
- package/dist/scene-payload.d.ts.map +1 -0
- package/dist/wire-default-loaders.d.ts +29 -0
- package/dist/wire-default-loaders.d.ts.map +1 -0
- package/package.json +22 -9
- package/src/__tests__/aabb.unit.test.ts +97 -0
- package/src/__tests__/artifact-integrity.unit.test.ts +99 -0
- package/src/__tests__/artifact-io.unit.test.ts +127 -0
- package/src/__tests__/asset-evidence-sdk.integration.test.ts +32 -0
- package/src/__tests__/asset-evidence.unit.test.ts +32 -0
- package/src/__tests__/{asset-kind.test.ts → asset-kind.unit.test.ts} +1 -1
- package/src/__tests__/asset-registry-catalog-reconcile.integration.test.ts +74 -0
- package/src/__tests__/asset-registry-catalog-reconcile.unit.test.ts +89 -0
- package/src/__tests__/asset-registry-catalog.unit.test.ts +170 -0
- package/src/__tests__/asset-registry.pack-cache.test.ts +96 -0
- package/src/__tests__/asset-registry.unit.test.ts +511 -0
- package/src/__tests__/asset-runtime-error-code-owner.test-d.ts +63 -0
- package/src/__tests__/catalog-replica.unit.test.ts +196 -0
- package/src/__tests__/catalog-source-v2.unit.test.ts +158 -0
- package/src/__tests__/catalog-source.unit.test.ts +126 -0
- package/src/__tests__/catalog-subscription-order.unit.test.ts +65 -0
- package/src/__tests__/catalog-subscription.unit.test.ts +28 -0
- package/src/__tests__/decode-image-bytes-pass-through.test.ts +34 -0
- package/src/__tests__/decode-image-bytes.browser.test.ts +102 -0
- package/src/__tests__/decode-image-bytes.test-d.ts +108 -0
- package/src/__tests__/decode-image-bytes.test.ts +90 -0
- package/src/__tests__/decode-image-mime-owner.test.ts +2 -5
- package/src/__tests__/dynamic-texture-store.unit.test.ts +115 -0
- package/src/__tests__/errors-asset.unit.test.ts +92 -0
- package/src/__tests__/inline-pack-loaders.unit.test.ts +548 -0
- package/src/__tests__/instantiate-context.unit.test.ts +65 -0
- package/src/__tests__/instantiate-publication-fence.unit.test.ts +158 -0
- package/src/__tests__/instantiate.unit.test.ts +684 -0
- package/src/__tests__/loader-input-contract.unit.test.ts +89 -0
- package/src/__tests__/loader-registry.unit.test.ts +101 -0
- package/src/__tests__/material-dependency-invalidation.unit.test.ts +14 -0
- package/src/__tests__/material-generation-cache.unit.test.ts +46 -0
- package/src/__tests__/material-load-consumer.test-d.ts +42 -0
- package/src/__tests__/material-ready.integration.test.ts +508 -0
- package/src/__tests__/material-stale-generation.unit.test.ts +147 -0
- package/src/__tests__/mesh-bin-morph-v3.test.ts +22 -0
- package/src/__tests__/mesh-bin.unit.test.ts +152 -0
- package/src/__tests__/mipmap-generator.unit.test.ts +202 -0
- package/src/__tests__/pack-artifact-transcode-model-owner.test.ts +32 -0
- package/src/__tests__/pack-basis-load.integration.test.ts +168 -0
- package/src/__tests__/payload-validate.unit.test.ts +285 -0
- package/src/__tests__/plugin.test.ts +55 -0
- package/src/__tests__/registry-concurrency.bench.test.ts +40 -0
- package/src/__tests__/registry-failure-retry.integration.test.ts +69 -0
- package/src/__tests__/registry-load-graph.integration.test.ts +246 -0
- package/src/__tests__/registry-load-state.unit.test.ts +35 -0
- package/src/__tests__/registry-package-cache.unit.test.ts +53 -0
- package/src/__tests__/registry-public-ready.unit.test.ts +38 -0
- package/src/__tests__/resolve-asset-handle.unit.test.ts +238 -0
- package/src/__tests__/scene-handle-fields.unit.test.ts +129 -0
- package/src/__tests__/scene-payload.unit.test.ts +151 -0
- package/src/__tests__/scene-publication-fence.test.ts +129 -0
- package/src/__tests__/scriptable-pack-public-surface.test-d.ts +27 -0
- package/src/__tests__/static-rig.integration.test.ts +8 -0
- package/src/__tests__/validate-material.unit.test.ts +359 -0
- package/src/__tests__/video-pack-loader.integration.test.ts +83 -0
- package/src/aabb.ts +107 -0
- package/src/asset-registry.ts +2045 -0
- package/src/builtin-asset-registry.ts +164 -0
- package/src/catalog-source.ts +14 -70
- package/src/decode-image-bytes.ts +126 -0
- package/src/dynamic-texture-store.ts +234 -0
- package/src/errors/asset.ts +347 -0
- package/src/handles.ts +186 -0
- package/src/image-error.ts +45 -0
- package/src/index.ts +183 -16
- package/src/internal.ts +1 -0
- package/src/loader-registry.ts +157 -0
- package/src/loaders/inline-pack.ts +837 -0
- package/src/loaders/mesh-bin.ts +304 -0
- package/src/loaders/pack-artifact.ts +665 -0
- package/src/material/dependency-graph.ts +46 -0
- package/src/material/generation-cache.ts +177 -0
- package/src/material/inspection.ts +46 -0
- package/src/material/loader.ts +326 -0
- package/src/material/runtime-shader.ts +133 -0
- package/src/mesh-bin.ts +5 -0
- package/src/mipmap-generator.ts +450 -0
- package/src/payload-validate.ts +573 -0
- package/src/plugin.ts +48 -0
- package/src/registry/artifact-io.ts +259 -0
- package/src/registry/asset-evidence.ts +51 -0
- package/src/registry/catalog-state.ts +409 -0
- package/src/registry/catalog.ts +369 -0
- package/src/registry/instantiate.ts +1056 -0
- package/src/registry/load-by-guid.ts +1657 -0
- package/src/registry/load-state.ts +115 -0
- package/src/registry/runtime-projection.ts +63 -0
- package/src/registry/scene-publication-fence.ts +256 -0
- package/src/registry/validate-material.ts +385 -0
- package/src/resolve-asset-handle.ts +147 -0
- package/src/resolve-tileset-runtime.ts +72 -0
- package/src/scene-handle-fields.ts +221 -0
- package/src/scene-payload.ts +207 -0
- package/src/wire-default-loaders.ts +79 -0
- package/dist/__tests__/asset-graph-red.integration.test.d.ts +0 -2
- package/dist/__tests__/asset-graph-red.integration.test.d.ts.map +0 -1
- package/dist/__tests__/asset-kind.test.d.ts +0 -2
- package/dist/__tests__/asset-kind.test.d.ts.map +0 -1
- package/dist/__tests__/asset-registry-core.integration.test.d.ts +0 -2
- package/dist/__tests__/asset-registry-core.integration.test.d.ts.map +0 -1
- package/dist/__tests__/asset-registry-public-api.test-d.d.ts +0 -2
- package/dist/__tests__/asset-registry-public-api.test-d.d.ts.map +0 -1
- package/dist/__tests__/asset-runtime-core-lifecycle.integration.test.d.ts +0 -2
- package/dist/__tests__/asset-runtime-core-lifecycle.integration.test.d.ts.map +0 -1
- package/dist/__tests__/asset-runtime-snapshot.unit.test.d.ts +0 -2
- package/dist/__tests__/asset-runtime-snapshot.unit.test.d.ts.map +0 -1
- package/dist/__tests__/catalog-session-red.unit.test.d.ts +0 -2
- package/dist/__tests__/catalog-session-red.unit.test.d.ts.map +0 -1
- package/dist/__tests__/registry-lifecycle-red.integration.test.d.ts +0 -2
- package/dist/__tests__/registry-lifecycle-red.integration.test.d.ts.map +0 -1
- package/src/__tests__/asset-graph-red.integration.test.ts +0 -113
- package/src/__tests__/asset-registry-core.integration.test.ts +0 -161
- package/src/__tests__/asset-registry-public-api.test-d.ts +0 -31
- package/src/__tests__/asset-runtime-core-lifecycle.integration.test.ts +0 -79
- package/src/__tests__/asset-runtime-snapshot.unit.test.ts +0 -23
- package/src/__tests__/catalog-session-red.unit.test.ts +0 -276
- package/src/__tests__/registry-lifecycle-red.integration.test.ts +0 -80
|
@@ -0,0 +1,837 @@
|
|
|
1
|
+
// @forgeax/engine-assets-runtime -- inline pack-payload loader bodies
|
|
2
|
+
// (feat-20260705-runtime-tier2-decomposition M1 / w4, D-4 F1 straight-cut).
|
|
3
|
+
// Pure move from asset-registry.ts; zero identifier changes.
|
|
4
|
+
|
|
5
|
+
import { PROCEDURAL_FLOATS_PER_VERTEX } from '@forgeax/engine-geometry';
|
|
6
|
+
import { AssetGuid } from '@forgeax/engine-pack/guid';
|
|
7
|
+
import type {
|
|
8
|
+
AddressMode,
|
|
9
|
+
AnimationChannel,
|
|
10
|
+
AnimationGraph,
|
|
11
|
+
AnimationGraphNode,
|
|
12
|
+
Asset,
|
|
13
|
+
AssetGuid as AssetGuidType,
|
|
14
|
+
AudioClipAsset,
|
|
15
|
+
CompareFunction,
|
|
16
|
+
FilterMode,
|
|
17
|
+
LoadContext,
|
|
18
|
+
Loader,
|
|
19
|
+
MaterialAsset,
|
|
20
|
+
MaterialPass,
|
|
21
|
+
MaterialTextureCoordinates,
|
|
22
|
+
ParseErrorDetail,
|
|
23
|
+
ParticleEffectAsset,
|
|
24
|
+
MeshAsset as TypesMeshAsset,
|
|
25
|
+
} from '@forgeax/engine-types';
|
|
26
|
+
import { resolveMaterialTextureCoordinates } from '@forgeax/engine-types';
|
|
27
|
+
import { MeshBinAssetError } from '../errors/asset';
|
|
28
|
+
import { parseScenePayload } from '../scene-payload';
|
|
29
|
+
import { unpackMeshBinV4 } from './mesh-bin';
|
|
30
|
+
import { renderPipelineLoader, tilesetLoader } from './pack-artifact';
|
|
31
|
+
|
|
32
|
+
// === Inline pack-payload loader bodies (feat-20260603-asset-import-loader-injection
|
|
33
|
+
// M1 / w4) ===
|
|
34
|
+
//
|
|
35
|
+
// The eight `if (kind === ...)` arms that lived inside
|
|
36
|
+
// `AssetRegistry.parseAssetPayload` (research Finding 1) are extracted here as
|
|
37
|
+
// module-level `{ kind, load }` objects so they register into a
|
|
38
|
+
// `LoaderRegistry` (D-1) and can be imported by `wireDefaultLoaders` (w5). The
|
|
39
|
+
// body logic is copied verbatim — M1 is a pure refactor (AC-03), no behavioural
|
|
40
|
+
// change. Each parses an inline `.pack.json` payload synchronously and returns
|
|
41
|
+
// the `Asset` POD or `undefined` (parse rejected). The `scene` arm routes its
|
|
42
|
+
// structured out-of-bounds-ref error back through the LoaderOutput return
|
|
43
|
+
// value instead of the old shared instance slot (D-8 channel replaced by F21).
|
|
44
|
+
|
|
45
|
+
/** mesh loader — Float32Array / Uint16Array | Uint32Array normalisation -> MeshAsset.
|
|
46
|
+
*
|
|
47
|
+
* feat-20260611: skinIndex (Uint16Array) and skinWeight (Float32Array) accept
|
|
48
|
+
* both their native typed-array shape (in-memory: dawn smoke / direct
|
|
49
|
+
* `register` test) AND `number[]` (post-`JSON.stringify` shape produced by the
|
|
50
|
+
* dev-server / build-mode pack-body round-trip — `JSON.stringify(pack) -> fetch
|
|
51
|
+
* -> JSON.parse` flattens every typed array to a plain Array). Same dual
|
|
52
|
+
* contract `skeletonLoader` / `animationClipLoader` already honour (PR #350);
|
|
53
|
+
* without the array arm, every Fox.glb / Khronos skinned glTF surfaces as
|
|
54
|
+
* `asset-parse-failed` on the browser path while dawn smoke stays green.
|
|
55
|
+
*/
|
|
56
|
+
export const meshLoader: Loader = {
|
|
57
|
+
kind: 'mesh',
|
|
58
|
+
load(payload) {
|
|
59
|
+
const vertexData = payload.vertices;
|
|
60
|
+
const indexData = payload.indices;
|
|
61
|
+
const rawAttributes = (payload.attributes as Record<string, unknown> | undefined) ?? {};
|
|
62
|
+
const attributes: Record<string, unknown> = { ...rawAttributes };
|
|
63
|
+
const rawAabb = payload.aabb;
|
|
64
|
+
let aabb: Float32Array | undefined;
|
|
65
|
+
if (rawAabb instanceof Float32Array) {
|
|
66
|
+
aabb = rawAabb;
|
|
67
|
+
} else if (Array.isArray(rawAabb)) {
|
|
68
|
+
aabb = new Float32Array(rawAabb as number[]);
|
|
69
|
+
} else if (rawAabb !== undefined) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let morphTargets: TypesMeshAsset['morphTargets'];
|
|
74
|
+
const rawMorphTargets = payload.morphTargets;
|
|
75
|
+
if (rawMorphTargets !== undefined) {
|
|
76
|
+
if (
|
|
77
|
+
!Array.isArray(rawMorphTargets) ||
|
|
78
|
+
rawMorphTargets.length < 1 ||
|
|
79
|
+
rawMorphTargets.length > 8
|
|
80
|
+
) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
const parsedTargets: NonNullable<TypesMeshAsset['morphTargets']>[number][] = [];
|
|
84
|
+
for (const rawTarget of rawMorphTargets) {
|
|
85
|
+
if (typeof rawTarget !== 'object' || rawTarget === null) return undefined;
|
|
86
|
+
const source = rawTarget as Record<string, unknown>;
|
|
87
|
+
const target: {
|
|
88
|
+
position?: Float32Array;
|
|
89
|
+
normal?: Float32Array;
|
|
90
|
+
tangent?: Float32Array;
|
|
91
|
+
} = {};
|
|
92
|
+
for (const [key, value] of Object.entries(source)) {
|
|
93
|
+
if (key !== 'position' && key !== 'normal' && key !== 'tangent') return undefined;
|
|
94
|
+
if (value instanceof Float32Array) target[key] = new Float32Array(value);
|
|
95
|
+
else if (Array.isArray(value)) target[key] = new Float32Array(value as number[]);
|
|
96
|
+
else return undefined;
|
|
97
|
+
}
|
|
98
|
+
if (Object.keys(target).length === 0) return undefined;
|
|
99
|
+
parsedTargets.push(target);
|
|
100
|
+
}
|
|
101
|
+
morphTargets = parsedTargets;
|
|
102
|
+
}
|
|
103
|
+
let morphWeights: Float32Array | undefined;
|
|
104
|
+
const rawMorphWeights = payload.morphWeights;
|
|
105
|
+
if (rawMorphWeights !== undefined) {
|
|
106
|
+
if (rawMorphWeights instanceof Float32Array) morphWeights = new Float32Array(rawMorphWeights);
|
|
107
|
+
else if (Array.isArray(rawMorphWeights))
|
|
108
|
+
morphWeights = new Float32Array(rawMorphWeights as number[]);
|
|
109
|
+
else return undefined;
|
|
110
|
+
if (morphTargets !== undefined && morphWeights.length !== morphTargets.length)
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const skinIndexRaw = rawAttributes.skinIndex;
|
|
115
|
+
if (skinIndexRaw instanceof Uint16Array) {
|
|
116
|
+
attributes.skinIndex = skinIndexRaw;
|
|
117
|
+
} else if (Array.isArray(skinIndexRaw)) {
|
|
118
|
+
attributes.skinIndex = new Uint16Array(skinIndexRaw as number[]);
|
|
119
|
+
} else if (skinIndexRaw !== undefined) {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const skinWeightRaw = rawAttributes.skinWeight;
|
|
124
|
+
if (skinWeightRaw instanceof Float32Array) {
|
|
125
|
+
attributes.skinWeight = skinWeightRaw;
|
|
126
|
+
} else if (Array.isArray(skinWeightRaw)) {
|
|
127
|
+
attributes.skinWeight = new Float32Array(skinWeightRaw as number[]);
|
|
128
|
+
} else if (skinWeightRaw !== undefined) {
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let vertices: Float32Array;
|
|
133
|
+
let indices: Uint16Array | Uint32Array | undefined;
|
|
134
|
+
|
|
135
|
+
if (vertexData instanceof Float32Array) {
|
|
136
|
+
vertices = vertexData;
|
|
137
|
+
} else if (Array.isArray(vertexData)) {
|
|
138
|
+
vertices = new Float32Array(vertexData as number[]);
|
|
139
|
+
} else {
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// bug-20260610: index width must follow vertex count, not a hard-coded
|
|
144
|
+
// Uint16Array. A glTF mesh (e.g. Sponza, ~192k merged verts) overflows
|
|
145
|
+
// Uint16; round-tripping through Uint16Array silently wraps and
|
|
146
|
+
// `mesh-vertex-stride-mismatch` then fires because `maxIndex + 1` no
|
|
147
|
+
// longer equals `vertexCount`. Mirrors `meshIrToMeshAsset` in
|
|
148
|
+
// packages/gltf/src/bridge.ts which picks Uint32 above 0xffff.
|
|
149
|
+
//
|
|
150
|
+
// feat-20260612 M2 fixup: when the input carries an empty index array
|
|
151
|
+
// (Fox.glb-style non-indexed primitives flattened through the mesh-bin
|
|
152
|
+
// sidecar with `ilen=0`), drop the indices field rather than emit a
|
|
153
|
+
// 0-byte typed array. The downstream `gpu-resource-store` chooses the
|
|
154
|
+
// indexed-vs-vertex-only path on `mesh.indices !== undefined`; a 0-byte
|
|
155
|
+
// typed array still satisfies !== undefined and triggers a 0-size IBO
|
|
156
|
+
// allocation, whose `setIndexBuffer(buffer.slice(0..0), ...)` panics
|
|
157
|
+
// wgpu's `BufferSlice` "buffer slices can not be empty" assertion.
|
|
158
|
+
if (indexData instanceof Uint16Array || indexData instanceof Uint32Array) {
|
|
159
|
+
indices = indexData.length > 0 ? indexData : undefined;
|
|
160
|
+
} else if (Array.isArray(indexData)) {
|
|
161
|
+
const arr = indexData as number[];
|
|
162
|
+
if (arr.length === 0) {
|
|
163
|
+
indices = undefined;
|
|
164
|
+
} else {
|
|
165
|
+
const vertexCount = vertices.length / PROCEDURAL_FLOATS_PER_VERTEX;
|
|
166
|
+
const useUint32 = vertexCount > 0xffff;
|
|
167
|
+
indices = useUint32 ? new Uint32Array(arr) : new Uint16Array(arr);
|
|
168
|
+
}
|
|
169
|
+
} else if (indexData === undefined) {
|
|
170
|
+
indices = undefined;
|
|
171
|
+
} else {
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// feat-20260608 M5 / w27: pack-payload mesh assets default to a single
|
|
176
|
+
// triangle-list submesh covering the full index/vertex range. Inline
|
|
177
|
+
// .pack.json mesh payloads do not carry submesh tables (single-prim
|
|
178
|
+
// legacy shape); render code unconditionally reads `submeshes[0]`.
|
|
179
|
+
// vertexCount stored as full vertices.length (downstream computes per-
|
|
180
|
+
// attribute strides; submesh keeps the buffer-element-count for now).
|
|
181
|
+
//
|
|
182
|
+
// bug-20260610: when the payload carries an explicit `submeshes` table
|
|
183
|
+
// (gltf importer emits one per primitive), respect it. The
|
|
184
|
+
// `triangle-list 0..indices.length` default fits only single-prim packs.
|
|
185
|
+
const payloadSubmeshes = payload.submeshes;
|
|
186
|
+
const rawSubmeshes =
|
|
187
|
+
Array.isArray(payloadSubmeshes) && payloadSubmeshes.length > 0
|
|
188
|
+
? (payloadSubmeshes as unknown as TypesMeshAsset['submeshes'])
|
|
189
|
+
: [
|
|
190
|
+
{
|
|
191
|
+
indexOffset: 0,
|
|
192
|
+
indexCount: indices?.length ?? 0,
|
|
193
|
+
vertexCount: vertices.length,
|
|
194
|
+
topology: 'triangle-list' as const,
|
|
195
|
+
},
|
|
196
|
+
];
|
|
197
|
+
const payloadSlots = payload.materialSlots;
|
|
198
|
+
const materialSlots: TypesMeshAsset['materialSlots'] = Array.isArray(payloadSlots)
|
|
199
|
+
? payloadSlots.map((raw, slotIndex) => {
|
|
200
|
+
if (typeof raw !== 'object' || raw === null)
|
|
201
|
+
return { slotName: `LegacySlot_${slotIndex}` };
|
|
202
|
+
const slot = raw as Record<string, unknown>;
|
|
203
|
+
let defaultMaterial: AssetGuidType | undefined;
|
|
204
|
+
if (slot.defaultMaterial instanceof Uint8Array) {
|
|
205
|
+
defaultMaterial = slot.defaultMaterial as AssetGuidType;
|
|
206
|
+
} else if (typeof slot.defaultMaterial === 'string') {
|
|
207
|
+
const parsed = AssetGuid.parse(slot.defaultMaterial);
|
|
208
|
+
if (!parsed.ok) return { slotName: `LegacySlot_${slotIndex}` };
|
|
209
|
+
defaultMaterial = parsed.value;
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
slotName:
|
|
213
|
+
typeof slot.slotName === 'string' && slot.slotName.trim().length > 0
|
|
214
|
+
? slot.slotName
|
|
215
|
+
: `LegacySlot_${slotIndex}`,
|
|
216
|
+
...(typeof slot.sourceKey === 'string' ? { sourceKey: slot.sourceKey } : {}),
|
|
217
|
+
...(defaultMaterial !== undefined ? { defaultMaterial } : {}),
|
|
218
|
+
};
|
|
219
|
+
})
|
|
220
|
+
: rawSubmeshes.map((_, slotIndex) => ({ slotName: `LegacySlot_${slotIndex}` }));
|
|
221
|
+
const submeshes = rawSubmeshes.map((submesh, submeshIndex) => ({
|
|
222
|
+
...submesh,
|
|
223
|
+
materialSlot: Number.isInteger((submesh as { materialSlot?: unknown }).materialSlot)
|
|
224
|
+
? (submesh as { materialSlot: number }).materialSlot
|
|
225
|
+
: submeshIndex,
|
|
226
|
+
}));
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
kind: 'mesh',
|
|
230
|
+
vertices,
|
|
231
|
+
...(indices !== undefined ? { indices } : {}),
|
|
232
|
+
attributes: attributes as TypesMeshAsset['attributes'],
|
|
233
|
+
...(aabb !== undefined ? { aabb } : {}),
|
|
234
|
+
...(morphTargets !== undefined ? { morphTargets } : {}),
|
|
235
|
+
...(morphWeights !== undefined ? { morphWeights } : {}),
|
|
236
|
+
submeshes,
|
|
237
|
+
materialSlots,
|
|
238
|
+
};
|
|
239
|
+
},
|
|
240
|
+
loadPack(input, ctx) {
|
|
241
|
+
const artifact = input.artifacts.body;
|
|
242
|
+
if (artifact === undefined) return meshLoader.load(input.payload, input.refs, ctx);
|
|
243
|
+
const decoded = unpackMeshBinV4(artifact.bytes, input.guid);
|
|
244
|
+
if (!decoded.ok) return { ok: false, error: decoded.error } as never;
|
|
245
|
+
const materialSlots = decoded.value.materialSlots.map((slot) => {
|
|
246
|
+
const refIndex = slot.defaultMaterialRef;
|
|
247
|
+
const ref = refIndex === undefined ? undefined : input.refs[refIndex];
|
|
248
|
+
if (refIndex !== undefined && ref === undefined) return undefined;
|
|
249
|
+
if (ref === undefined) {
|
|
250
|
+
return {
|
|
251
|
+
slotName: String(slot.slotName),
|
|
252
|
+
...(typeof slot.sourceKey === 'string' ? { sourceKey: slot.sourceKey } : {}),
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
const parsed = AssetGuid.parse(ref);
|
|
256
|
+
if (!parsed.ok) return undefined;
|
|
257
|
+
return {
|
|
258
|
+
slotName: String(slot.slotName),
|
|
259
|
+
...(typeof slot.sourceKey === 'string' ? { sourceKey: slot.sourceKey } : {}),
|
|
260
|
+
defaultMaterial: parsed.value,
|
|
261
|
+
};
|
|
262
|
+
});
|
|
263
|
+
if (materialSlots.some((slot) => slot === undefined)) {
|
|
264
|
+
return {
|
|
265
|
+
ok: false,
|
|
266
|
+
error: new MeshBinAssetError({
|
|
267
|
+
sourceKey: input.guid,
|
|
268
|
+
expected: 'material slot references must resolve through the pack refs table',
|
|
269
|
+
actual: 'material reference is out of bounds or is not a valid AssetGuid',
|
|
270
|
+
reason: 'metadata-invalid',
|
|
271
|
+
actualFacts: { field: 'metadata' },
|
|
272
|
+
}),
|
|
273
|
+
} as never;
|
|
274
|
+
}
|
|
275
|
+
return meshLoader.load(
|
|
276
|
+
{
|
|
277
|
+
vertices: decoded.value.vertices,
|
|
278
|
+
...(decoded.value.indices !== undefined ? { indices: decoded.value.indices } : {}),
|
|
279
|
+
submeshes: decoded.value.submeshes,
|
|
280
|
+
materialSlots,
|
|
281
|
+
...(decoded.value.aabb !== undefined ? { aabb: decoded.value.aabb } : {}),
|
|
282
|
+
...(decoded.value.morphTargets !== undefined
|
|
283
|
+
? { morphTargets: decoded.value.morphTargets }
|
|
284
|
+
: {}),
|
|
285
|
+
...(decoded.value.morphWeights !== undefined
|
|
286
|
+
? { morphWeights: decoded.value.morphWeights }
|
|
287
|
+
: {}),
|
|
288
|
+
attributes: decoded.value.attributes,
|
|
289
|
+
},
|
|
290
|
+
input.refs,
|
|
291
|
+
ctx,
|
|
292
|
+
);
|
|
293
|
+
},
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
/** scene loader — delegates to parseScenePayload; routes structured ref error via ctx. */
|
|
297
|
+
export const sceneLoader: Loader = {
|
|
298
|
+
kind: 'scene',
|
|
299
|
+
load(payload, refs, _ctx: LoadContext) {
|
|
300
|
+
const result = parseScenePayload(payload, refs === undefined ? undefined : [...refs]);
|
|
301
|
+
if (result === undefined) return undefined;
|
|
302
|
+
// Structured ParseSceneError (has an `index` field absent on SceneAsset):
|
|
303
|
+
// return it inline through LoaderOutput so the caller (parseAndReturnAsset)
|
|
304
|
+
// can build a precise AssetError without a shared instance slot (F21).
|
|
305
|
+
if ('index' in result) {
|
|
306
|
+
return { ok: false, error: result as ParseErrorDetail };
|
|
307
|
+
}
|
|
308
|
+
return result as Asset;
|
|
309
|
+
},
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* feat-20260613-material-paramschema-driven-binding M4 / w22 (D-5 graceful):
|
|
314
|
+
* the legacy hardcoded texture-field allowlist Set has been removed
|
|
315
|
+
* (AC-03). The materialLoader now consults `ctx.getMaterialShaderTextureFieldNames`
|
|
316
|
+
* (paramSchema-derived via derive()) to know which values fields carry
|
|
317
|
+
* refs[] indices. When the shader is not yet registered (cross-worktree
|
|
318
|
+
* shader-late-register path, plan R-4), the loader falls back to attempting
|
|
319
|
+
* resolution on every int-typed paramValue in [0, refs.length) — M4 / w23's
|
|
320
|
+
* extract-layer paramSchema validation catches misclassifications and routes
|
|
321
|
+
* unresolved texture slots through `MISSING_TEXTURE_HANDLE`.
|
|
322
|
+
*/
|
|
323
|
+
function collectShaderTextureFieldNames(
|
|
324
|
+
passesFromPayload: unknown,
|
|
325
|
+
ctx: LoadContext,
|
|
326
|
+
): ReadonlySet<string> | undefined {
|
|
327
|
+
if (!Array.isArray(passesFromPayload) || passesFromPayload.length === 0) return undefined;
|
|
328
|
+
const lookup = ctx.getMaterialShaderTextureFieldNames;
|
|
329
|
+
if (lookup === undefined) return undefined;
|
|
330
|
+
const collected = new Set<string>();
|
|
331
|
+
let anyResolved = false;
|
|
332
|
+
for (const pass of passesFromPayload) {
|
|
333
|
+
const shaderId = (pass as { program?: { module?: unknown } }).program?.module;
|
|
334
|
+
if (typeof shaderId !== 'string' || shaderId.length === 0) continue;
|
|
335
|
+
const fields = lookup(shaderId);
|
|
336
|
+
if (fields === undefined) continue;
|
|
337
|
+
anyResolved = true;
|
|
338
|
+
for (const name of fields) collected.add(name);
|
|
339
|
+
}
|
|
340
|
+
return anyResolved ? collected : undefined;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function refGuidAt(refs: readonly unknown[] | undefined, index: number): string | undefined {
|
|
344
|
+
if (!Number.isInteger(index) || index < 0 || index >= (refs?.length ?? 0)) return undefined;
|
|
345
|
+
const guid = refs?.[index];
|
|
346
|
+
if (typeof guid === 'string') return guid;
|
|
347
|
+
if (typeof guid === 'object' && guid !== null && 'guid' in guid) {
|
|
348
|
+
const nestedGuid = (guid as { guid?: unknown }).guid;
|
|
349
|
+
return typeof nestedGuid === 'string' ? nestedGuid : undefined;
|
|
350
|
+
}
|
|
351
|
+
return undefined;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function isIdentityTextureCoordinates(value: unknown): boolean {
|
|
355
|
+
if (value === undefined) return true;
|
|
356
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) return false;
|
|
357
|
+
const coordinates = value as Record<string, unknown>;
|
|
358
|
+
if (Object.keys(coordinates).some((key) => key !== 'set' && key !== 'transform')) return false;
|
|
359
|
+
const transform = coordinates.transform;
|
|
360
|
+
if (
|
|
361
|
+
transform !== undefined &&
|
|
362
|
+
(typeof transform !== 'object' || transform === null || Array.isArray(transform))
|
|
363
|
+
) {
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
if (
|
|
367
|
+
transform !== undefined &&
|
|
368
|
+
Object.keys(transform as Record<string, unknown>).some(
|
|
369
|
+
(key) => key !== 'offset' && key !== 'scale' && key !== 'rotation',
|
|
370
|
+
)
|
|
371
|
+
) {
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
const resolved = resolveMaterialTextureCoordinates(coordinates as MaterialTextureCoordinates);
|
|
375
|
+
return (
|
|
376
|
+
resolved.set === 0 &&
|
|
377
|
+
resolved.transform.offset[0] === 0 &&
|
|
378
|
+
resolved.transform.offset[1] === 0 &&
|
|
379
|
+
resolved.transform.scale[0] === 1 &&
|
|
380
|
+
resolved.transform.scale[1] === 1 &&
|
|
381
|
+
resolved.transform.rotation === 0
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function compactTextureValue(value: Record<string, unknown>): unknown {
|
|
386
|
+
const compact = { ...value };
|
|
387
|
+
if (isIdentityTextureCoordinates(compact.coordinates)) delete compact.coordinates;
|
|
388
|
+
return compact;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/** material loader — passes + values + serialized parent GUID -> parentGuid. */
|
|
392
|
+
export const materialLoader: Loader = {
|
|
393
|
+
kind: 'material',
|
|
394
|
+
load(payload, refs, ctx: LoadContext) {
|
|
395
|
+
const matPayload = payload;
|
|
396
|
+
const passesFromPayload = matPayload.passes;
|
|
397
|
+
const rawParamValues = (matPayload.values as Record<string, unknown>) ?? {};
|
|
398
|
+
|
|
399
|
+
let parentGuid: string | undefined;
|
|
400
|
+
if (typeof matPayload.parent === 'string') {
|
|
401
|
+
parentGuid = matPayload.parent;
|
|
402
|
+
} else if (typeof matPayload.parent === 'number') {
|
|
403
|
+
const idx = matPayload.parent;
|
|
404
|
+
const refsArr = refs ?? [];
|
|
405
|
+
if (idx >= 0 && idx < refsArr.length) {
|
|
406
|
+
parentGuid = refGuidAt(refsArr, idx);
|
|
407
|
+
}
|
|
408
|
+
if (parentGuid === undefined) {
|
|
409
|
+
return undefined;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// bug-20260610: values fields that are typed `handle<TextureAsset>`
|
|
414
|
+
// arrive on disk as a refs[] index (small int 0..refs.length-1). The
|
|
415
|
+
// build-time gltfImporter writes these as refs indices, mirroring the
|
|
416
|
+
// scene's HANDLE_FIELD_NAMES treatment.
|
|
417
|
+
//
|
|
418
|
+
// feat-20260613-material-paramschema-driven-binding M4 / w22 (D-5 graceful):
|
|
419
|
+
// texture-field discovery now derives from the registered shader's
|
|
420
|
+
// paramSchema via `ctx.getMaterialShaderTextureFieldNames`. When the
|
|
421
|
+
// shader is registered (the common case), only declared texture fields
|
|
422
|
+
// are resolved — identical to the old hardcoded-Set behaviour without the
|
|
423
|
+
// SSOT duplication. Authored material parameters are an equally valid
|
|
424
|
+
// schema source and remain available before shader registration; this is
|
|
425
|
+
// what keeps a scalar zero (for example metallic) distinct from texture
|
|
426
|
+
// ref index zero. Only legacy payloads with neither schema retain the
|
|
427
|
+
// try-every-integer fallback.
|
|
428
|
+
const values: Record<string, unknown> = { ...rawParamValues };
|
|
429
|
+
if (refs && refs.length > 0) {
|
|
430
|
+
const shaderTextureFields = collectShaderTextureFieldNames(passesFromPayload, ctx);
|
|
431
|
+
const authoredTextureFields = Array.isArray(matPayload.parameters)
|
|
432
|
+
? new Set(
|
|
433
|
+
matPayload.parameters
|
|
434
|
+
.filter(
|
|
435
|
+
(parameter): parameter is { name: string; type: string } =>
|
|
436
|
+
typeof parameter === 'object' &&
|
|
437
|
+
parameter !== null &&
|
|
438
|
+
'name' in parameter &&
|
|
439
|
+
typeof parameter.name === 'string' &&
|
|
440
|
+
'type' in parameter &&
|
|
441
|
+
parameter.type === 'texture',
|
|
442
|
+
)
|
|
443
|
+
.map((parameter) => parameter.name),
|
|
444
|
+
)
|
|
445
|
+
: undefined;
|
|
446
|
+
const textureFields =
|
|
447
|
+
authoredTextureFields !== undefined
|
|
448
|
+
? new Set([...(shaderTextureFields ?? []), ...authoredTextureFields])
|
|
449
|
+
: shaderTextureFields;
|
|
450
|
+
// Walk every value so the structured MaterialTextureValue shape remains
|
|
451
|
+
// resolvable even when a shader is registered with an empty/late
|
|
452
|
+
// paramSchema (pbr-skin currently takes this path). Scalar values still
|
|
453
|
+
// require schema membership below, so metallic/roughness integers are
|
|
454
|
+
// never mistaken for refs indices merely because the schema is empty.
|
|
455
|
+
const candidateFields = Object.keys(values);
|
|
456
|
+
for (const fieldName of candidateFields) {
|
|
457
|
+
const value = values[fieldName];
|
|
458
|
+
if (typeof value === 'number' && Number.isInteger(value)) {
|
|
459
|
+
if (textureFields !== undefined && !textureFields.has(fieldName)) {
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
const refGuid = refGuidAt(refs, value);
|
|
463
|
+
if (refGuid === undefined) {
|
|
464
|
+
// Only emit a parse-error breadcrumb when the field is declared as
|
|
465
|
+
// a texture by the shader paramSchema (the OOB is unambiguous).
|
|
466
|
+
// For the graceful "try every int" fallback, OOB simply means
|
|
467
|
+
// "this scalar was not a refs index" — don't spam parse errors.
|
|
468
|
+
if (textureFields !== undefined) delete values[fieldName];
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
values[fieldName] = textureFields === undefined ? refGuid : { texture: refGuid };
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// glTF materials use the engine's structured MaterialTextureValue
|
|
476
|
+
// shape (`{ texture: <refs index> }`), not the legacy scalar form.
|
|
477
|
+
// Resolve that nested index at the same loader boundary; leaving zero
|
|
478
|
+
// here is a valid-looking handle that silently renders the material
|
|
479
|
+
// white and never reaches the browser console as an exception.
|
|
480
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
481
|
+
const textureIndex = (value as { texture?: unknown }).texture;
|
|
482
|
+
if (typeof textureIndex !== 'number' || !Number.isInteger(textureIndex)) continue;
|
|
483
|
+
const textureGuid = refGuidAt(refs, textureIndex);
|
|
484
|
+
if (textureGuid === undefined) {
|
|
485
|
+
if (textureFields !== undefined) delete values[fieldName];
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
const resolved: Record<string, unknown> = {
|
|
489
|
+
...(value as Record<string, unknown>),
|
|
490
|
+
texture: textureGuid,
|
|
491
|
+
};
|
|
492
|
+
const samplerIndex = resolved.sampler;
|
|
493
|
+
if (typeof samplerIndex === 'number' && Number.isInteger(samplerIndex)) {
|
|
494
|
+
const samplerGuid = refGuidAt(refs, samplerIndex);
|
|
495
|
+
if (samplerGuid === undefined) delete resolved.sampler;
|
|
496
|
+
else resolved.sampler = samplerGuid;
|
|
497
|
+
}
|
|
498
|
+
values[fieldName] = compactTextureValue(resolved);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (Array.isArray(passesFromPayload) && passesFromPayload.length > 0) {
|
|
504
|
+
return {
|
|
505
|
+
kind: 'material',
|
|
506
|
+
passes: passesFromPayload as readonly MaterialPass[],
|
|
507
|
+
parameters: matPayload.parameters,
|
|
508
|
+
values,
|
|
509
|
+
colorSpace: matPayload.colorSpace,
|
|
510
|
+
parentGuid,
|
|
511
|
+
} as MaterialAsset & { parentGuid?: string };
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (parentGuid !== undefined) {
|
|
515
|
+
return {
|
|
516
|
+
kind: 'material',
|
|
517
|
+
parameters: matPayload.parameters,
|
|
518
|
+
values,
|
|
519
|
+
colorSpace: matPayload.colorSpace,
|
|
520
|
+
parentGuid,
|
|
521
|
+
} as unknown as MaterialAsset & { parentGuid?: string };
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
return undefined;
|
|
525
|
+
},
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
/** sampler loader — preserve the serialised SamplerAsset descriptor. */
|
|
529
|
+
export const samplerLoader: Loader = {
|
|
530
|
+
kind: 'sampler',
|
|
531
|
+
load(payload) {
|
|
532
|
+
const filters = new Set<FilterMode>(['nearest', 'linear']);
|
|
533
|
+
const mipmapFilters = new Set<GPUMipmapFilterMode>(['nearest', 'linear']);
|
|
534
|
+
const addressModes = new Set<AddressMode>(['clamp-to-edge', 'repeat', 'mirror-repeat']);
|
|
535
|
+
const compareFunctions = new Set<CompareFunction>([
|
|
536
|
+
'never',
|
|
537
|
+
'less',
|
|
538
|
+
'equal',
|
|
539
|
+
'less-equal',
|
|
540
|
+
'greater',
|
|
541
|
+
'not-equal',
|
|
542
|
+
'greater-equal',
|
|
543
|
+
'always',
|
|
544
|
+
]);
|
|
545
|
+
const stringField = <T extends string>(
|
|
546
|
+
key: string,
|
|
547
|
+
allowed: ReadonlySet<T>,
|
|
548
|
+
): T | undefined | null => {
|
|
549
|
+
const value = payload[key];
|
|
550
|
+
if (value === undefined) return undefined;
|
|
551
|
+
return typeof value === 'string' && allowed.has(value as T) ? (value as T) : null;
|
|
552
|
+
};
|
|
553
|
+
const magFilter = stringField('magFilter', filters);
|
|
554
|
+
const minFilter = stringField('minFilter', filters);
|
|
555
|
+
const mipmapFilter = stringField('mipmapFilter', mipmapFilters);
|
|
556
|
+
const addressModeU = stringField('addressModeU', addressModes);
|
|
557
|
+
const addressModeV = stringField('addressModeV', addressModes);
|
|
558
|
+
const addressModeW = stringField('addressModeW', addressModes);
|
|
559
|
+
const compare = stringField('compare', compareFunctions);
|
|
560
|
+
if (
|
|
561
|
+
magFilter === null ||
|
|
562
|
+
minFilter === null ||
|
|
563
|
+
mipmapFilter === null ||
|
|
564
|
+
addressModeU === null ||
|
|
565
|
+
addressModeV === null ||
|
|
566
|
+
addressModeW === null ||
|
|
567
|
+
compare === null
|
|
568
|
+
) {
|
|
569
|
+
return undefined;
|
|
570
|
+
}
|
|
571
|
+
const numericField = (key: string): number | undefined | null => {
|
|
572
|
+
const value = payload[key];
|
|
573
|
+
if (value === undefined) return undefined;
|
|
574
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
575
|
+
};
|
|
576
|
+
const lodMinClamp = numericField('lodMinClamp');
|
|
577
|
+
const lodMaxClamp = numericField('lodMaxClamp');
|
|
578
|
+
const maxAnisotropy = numericField('maxAnisotropy');
|
|
579
|
+
if (lodMinClamp === null || lodMaxClamp === null || maxAnisotropy === null) return undefined;
|
|
580
|
+
return {
|
|
581
|
+
kind: 'sampler',
|
|
582
|
+
...(magFilter === undefined ? {} : { magFilter }),
|
|
583
|
+
...(minFilter === undefined ? {} : { minFilter }),
|
|
584
|
+
...(mipmapFilter === undefined ? {} : { mipmapFilter }),
|
|
585
|
+
...(addressModeU === undefined ? {} : { addressModeU }),
|
|
586
|
+
...(addressModeV === undefined ? {} : { addressModeV }),
|
|
587
|
+
...(addressModeW === undefined ? {} : { addressModeW }),
|
|
588
|
+
...(lodMinClamp === undefined ? {} : { lodMinClamp }),
|
|
589
|
+
...(lodMaxClamp === undefined ? {} : { lodMaxClamp }),
|
|
590
|
+
...(compare === undefined ? {} : { compare }),
|
|
591
|
+
...(maxAnisotropy === undefined ? {} : { maxAnisotropy }),
|
|
592
|
+
};
|
|
593
|
+
},
|
|
594
|
+
};
|
|
595
|
+
|
|
596
|
+
/** skeleton loader — inverseBindMatrices stride validation.
|
|
597
|
+
*
|
|
598
|
+
* bug-20260611: accept both `Float32Array` (in-memory: dawn smoke / direct
|
|
599
|
+
* `register` test) AND `number[]` (post-`JSON.stringify` shape: `normaliseForPack`
|
|
600
|
+
* in @forgeax/engine-import flattens every typed array to a plain Array so
|
|
601
|
+
* `JSON.stringify(pack)` survives the dev-server / build-mode round-trip --
|
|
602
|
+
* the same dual contract `meshLoader` already honours). Without the array arm
|
|
603
|
+
* the .pack.json -> fetch -> JSON.parse path lands a plain object whose
|
|
604
|
+
* `instanceof Float32Array` check fails, surfacing as `asset-parse-failed`
|
|
605
|
+
* for any glTF carrying a Skin (e.g. Khronos Fox.glb).
|
|
606
|
+
*/
|
|
607
|
+
export const skeletonLoader: Loader = {
|
|
608
|
+
kind: 'skeleton',
|
|
609
|
+
load(payload) {
|
|
610
|
+
const ibmRaw = payload.inverseBindMatrices;
|
|
611
|
+
const jointCount = typeof payload.jointCount === 'number' ? payload.jointCount : 0;
|
|
612
|
+
let ibm: Float32Array;
|
|
613
|
+
if (ibmRaw instanceof Float32Array) {
|
|
614
|
+
ibm = ibmRaw;
|
|
615
|
+
} else if (Array.isArray(ibmRaw)) {
|
|
616
|
+
ibm = new Float32Array(ibmRaw as number[]);
|
|
617
|
+
} else {
|
|
618
|
+
return undefined;
|
|
619
|
+
}
|
|
620
|
+
if (ibm.byteLength !== jointCount * 64) return undefined;
|
|
621
|
+
return {
|
|
622
|
+
kind: 'skeleton',
|
|
623
|
+
inverseBindMatrices: ibm,
|
|
624
|
+
jointCount,
|
|
625
|
+
};
|
|
626
|
+
},
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
/** skin loader — skeletonGuid + jointPaths validation. */
|
|
630
|
+
export const skinLoader: Loader = {
|
|
631
|
+
kind: 'skin',
|
|
632
|
+
load(payload) {
|
|
633
|
+
const skeletonGuid = payload.skeletonGuid;
|
|
634
|
+
const jointPathsRaw = payload.jointPaths;
|
|
635
|
+
if (typeof skeletonGuid !== 'string') return undefined;
|
|
636
|
+
if (!Array.isArray(jointPathsRaw)) return undefined;
|
|
637
|
+
const jointPaths: string[] = [];
|
|
638
|
+
for (const item of jointPathsRaw) {
|
|
639
|
+
if (typeof item !== 'string') return undefined;
|
|
640
|
+
jointPaths.push(item);
|
|
641
|
+
}
|
|
642
|
+
return { kind: 'skin', skeletonGuid, jointPaths };
|
|
643
|
+
},
|
|
644
|
+
};
|
|
645
|
+
|
|
646
|
+
/** animation-clip loader — channels / sampler validation.
|
|
647
|
+
*
|
|
648
|
+
* bug-20260611: sampler.input / sampler.output accept both `Float32Array`
|
|
649
|
+
* (in-memory) and `number[]` (post-`JSON.stringify` shape produced by
|
|
650
|
+
* `normaliseForPack`). Same dual contract as `skeletonLoader` /
|
|
651
|
+
* `meshLoader`; without it the dev `.pack.json` round-trip surfaces every
|
|
652
|
+
* skinned-with-animation glTF as `asset-parse-failed`.
|
|
653
|
+
*/
|
|
654
|
+
export const animationClipLoader: Loader = {
|
|
655
|
+
kind: 'animation-clip',
|
|
656
|
+
load(payload) {
|
|
657
|
+
const duration = typeof payload.duration === 'number' ? payload.duration : 0;
|
|
658
|
+
const channelsRaw = payload.channels;
|
|
659
|
+
if (!Array.isArray(channelsRaw)) return undefined;
|
|
660
|
+
const channels: AnimationChannel[] = [];
|
|
661
|
+
for (const ch of channelsRaw) {
|
|
662
|
+
if (typeof ch !== 'object' || ch === null) return undefined;
|
|
663
|
+
const chObj = ch as Record<string, unknown>;
|
|
664
|
+
const targetId = chObj.targetId;
|
|
665
|
+
const property = chObj.property;
|
|
666
|
+
const samplerObj = chObj.sampler as Record<string, unknown> | undefined;
|
|
667
|
+
if (
|
|
668
|
+
typeof targetId !== 'string' ||
|
|
669
|
+
!/^[0-9a-f]{32}$/.test(targetId) ||
|
|
670
|
+
Object.keys(chObj).some(
|
|
671
|
+
(key) => key !== 'targetId' && key !== 'property' && key !== 'sampler',
|
|
672
|
+
)
|
|
673
|
+
) {
|
|
674
|
+
return undefined;
|
|
675
|
+
}
|
|
676
|
+
if (
|
|
677
|
+
property !== 'translation' &&
|
|
678
|
+
property !== 'rotation' &&
|
|
679
|
+
property !== 'scale' &&
|
|
680
|
+
property !== 'weights'
|
|
681
|
+
)
|
|
682
|
+
return undefined;
|
|
683
|
+
if (samplerObj === undefined) return undefined;
|
|
684
|
+
const inputRaw = samplerObj.input;
|
|
685
|
+
const outputRaw = samplerObj.output;
|
|
686
|
+
const interpolation = samplerObj.interpolation;
|
|
687
|
+
let input: Float32Array;
|
|
688
|
+
if (inputRaw instanceof Float32Array) {
|
|
689
|
+
input = inputRaw;
|
|
690
|
+
} else if (Array.isArray(inputRaw)) {
|
|
691
|
+
input = new Float32Array(inputRaw as number[]);
|
|
692
|
+
} else {
|
|
693
|
+
return undefined;
|
|
694
|
+
}
|
|
695
|
+
let output: Float32Array;
|
|
696
|
+
if (outputRaw instanceof Float32Array) {
|
|
697
|
+
output = outputRaw;
|
|
698
|
+
} else if (Array.isArray(outputRaw)) {
|
|
699
|
+
output = new Float32Array(outputRaw as number[]);
|
|
700
|
+
} else {
|
|
701
|
+
return undefined;
|
|
702
|
+
}
|
|
703
|
+
if (interpolation !== 'LINEAR' && interpolation !== 'STEP') return undefined;
|
|
704
|
+
channels.push({
|
|
705
|
+
targetId: targetId as AnimationChannel['targetId'],
|
|
706
|
+
property: property as AnimationChannel['property'],
|
|
707
|
+
sampler: { input, output, interpolation },
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
return { kind: 'animation-clip', duration, channels };
|
|
711
|
+
},
|
|
712
|
+
};
|
|
713
|
+
|
|
714
|
+
/** animation-graph loader — pack payload -> AnimationGraph POD.
|
|
715
|
+
*
|
|
716
|
+
* feat-20260713-animation-state-machine-plugin M4 / w30 (plan D-4 landing (3)):
|
|
717
|
+
* the inverse of `serializeAnimationGraph` (runtime). Each Clip leaf's `clip`
|
|
718
|
+
* field arrives as a `refs[]` index (small int); the loader resolves it back to
|
|
719
|
+
* the GUID string verbatim, leaving GUID -> handle re-resolution to the ECS/use
|
|
720
|
+
* time (the same "GUID at load, handle at use" contract materialLoader honours
|
|
721
|
+
* for parentGuid / values texture fields, asset-registry D-19). Blend/Add
|
|
722
|
+
* node references + `root` are intra-graph node indices, validated against the
|
|
723
|
+
* node count. A malformed payload (out-of-range refs / node index, bad node
|
|
724
|
+
* shape, non-finite weight) returns `undefined` so the load-by-guid path
|
|
725
|
+
* surfaces the existing `asset-parse-failed` error code (AC-14).
|
|
726
|
+
*
|
|
727
|
+
* OOS-7: reconstructs only an engine-authored graph's topology; no DCC-import
|
|
728
|
+
* metadata is consumed.
|
|
729
|
+
*/
|
|
730
|
+
export const animationGraphLoader: Loader = {
|
|
731
|
+
kind: 'animation-graph',
|
|
732
|
+
load(payload, refs) {
|
|
733
|
+
const rawNodes = payload.nodes;
|
|
734
|
+
const root = payload.root;
|
|
735
|
+
if (!Array.isArray(rawNodes)) return undefined;
|
|
736
|
+
const nodeCount = rawNodes.length;
|
|
737
|
+
const isNodeIndex = (value: unknown): value is number =>
|
|
738
|
+
typeof value === 'number' && Number.isInteger(value) && value >= 0 && value < nodeCount;
|
|
739
|
+
if (!isNodeIndex(root)) return undefined;
|
|
740
|
+
const refsArr = refs ?? [];
|
|
741
|
+
|
|
742
|
+
const nodes: AnimationGraphNode[] = [];
|
|
743
|
+
for (const rawNode of rawNodes) {
|
|
744
|
+
if (typeof rawNode !== 'object' || rawNode === null) return undefined;
|
|
745
|
+
const node = rawNode as Record<string, unknown>;
|
|
746
|
+
const weight = node.weight;
|
|
747
|
+
if (typeof weight !== 'number' || !Number.isFinite(weight)) return undefined;
|
|
748
|
+
|
|
749
|
+
if (node.type === 'clip') {
|
|
750
|
+
const refIndex = node.clip;
|
|
751
|
+
if (typeof refIndex !== 'number' || !Number.isInteger(refIndex)) return undefined;
|
|
752
|
+
if (refIndex < 0 || refIndex >= refsArr.length) return undefined;
|
|
753
|
+
const guid = refsArr[refIndex];
|
|
754
|
+
if (typeof guid !== 'string') return undefined;
|
|
755
|
+
// GUID verbatim at load; re-resolved to a handle at use time (D-19).
|
|
756
|
+
nodes.push({
|
|
757
|
+
type: 'clip',
|
|
758
|
+
clip: guid,
|
|
759
|
+
weight,
|
|
760
|
+
});
|
|
761
|
+
} else if (node.type === 'blend') {
|
|
762
|
+
const children = node.children;
|
|
763
|
+
if (!Array.isArray(children) || !children.every(isNodeIndex)) return undefined;
|
|
764
|
+
nodes.push({ type: 'blend', children: [...(children as number[])], weight });
|
|
765
|
+
} else if (node.type === 'add') {
|
|
766
|
+
const base = node.base;
|
|
767
|
+
const additive = node.additive;
|
|
768
|
+
if (!isNodeIndex(base)) return undefined;
|
|
769
|
+
if (!Array.isArray(additive) || !additive.every(isNodeIndex)) return undefined;
|
|
770
|
+
nodes.push({ type: 'add', base, additive: [...(additive as number[])], weight });
|
|
771
|
+
} else {
|
|
772
|
+
return undefined;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
return { kind: 'animation-graph', nodes, root } as AnimationGraph;
|
|
777
|
+
},
|
|
778
|
+
};
|
|
779
|
+
|
|
780
|
+
/** audio loader -- restore the durable descriptor; host decode stays elsewhere. */
|
|
781
|
+
export const audioLoader: Loader = {
|
|
782
|
+
kind: 'audio',
|
|
783
|
+
load(payload) {
|
|
784
|
+
const sourceKey = payload.sourceKey;
|
|
785
|
+
const mediaType = payload.mediaType;
|
|
786
|
+
if (typeof sourceKey !== 'string' || sourceKey.length === 0) return undefined;
|
|
787
|
+
if (typeof mediaType !== 'string' || mediaType.length === 0) return undefined;
|
|
788
|
+
const rawBytes = payload.bytes;
|
|
789
|
+
const bytes =
|
|
790
|
+
rawBytes instanceof Uint8Array
|
|
791
|
+
? rawBytes
|
|
792
|
+
: Array.isArray(rawBytes)
|
|
793
|
+
? new Uint8Array(rawBytes as number[])
|
|
794
|
+
: undefined;
|
|
795
|
+
return {
|
|
796
|
+
kind: 'audio',
|
|
797
|
+
sourceKey,
|
|
798
|
+
mediaType,
|
|
799
|
+
...(bytes === undefined ? {} : { bytes }),
|
|
800
|
+
} as AudioClipAsset;
|
|
801
|
+
},
|
|
802
|
+
};
|
|
803
|
+
|
|
804
|
+
/** particle loader -- restore the complete cooked program without a GPU device. */
|
|
805
|
+
export const particleEffectLoader: Loader = {
|
|
806
|
+
kind: 'particle-effect',
|
|
807
|
+
load(payload) {
|
|
808
|
+
if (typeof payload.program !== 'object' || payload.program === null) return undefined;
|
|
809
|
+
if (typeof (payload.program as Record<string, unknown>).fingerprint !== 'string') {
|
|
810
|
+
return undefined;
|
|
811
|
+
}
|
|
812
|
+
return { ...payload, kind: 'particle-effect' } as ParticleEffectAsset;
|
|
813
|
+
},
|
|
814
|
+
};
|
|
815
|
+
|
|
816
|
+
export { renderPipelineLoader, tilesetLoader } from './pack-artifact';
|
|
817
|
+
|
|
818
|
+
/**
|
|
819
|
+
* The eight inline pack-payload loaders, in the historical `if`-chain order
|
|
820
|
+
* (the animation-graph loader, feat-20260713 M4 / w30, appends after the clip
|
|
821
|
+
* loader). `wireDefaultLoaders` (w5) registers these plus the texture / font /
|
|
822
|
+
* equirect loaders (w6) and the audio placeholder (w8).
|
|
823
|
+
*/
|
|
824
|
+
export const INLINE_PACK_LOADERS: readonly Loader[] = [
|
|
825
|
+
meshLoader,
|
|
826
|
+
sceneLoader,
|
|
827
|
+
samplerLoader,
|
|
828
|
+
materialLoader,
|
|
829
|
+
skeletonLoader,
|
|
830
|
+
skinLoader,
|
|
831
|
+
animationClipLoader,
|
|
832
|
+
animationGraphLoader,
|
|
833
|
+
renderPipelineLoader,
|
|
834
|
+
tilesetLoader,
|
|
835
|
+
audioLoader,
|
|
836
|
+
particleEffectLoader,
|
|
837
|
+
];
|