@forgeax/engine-assets-runtime 0.1.6 → 0.1.19

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.
Files changed (35) hide show
  1. package/README.md +11 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/asset-registry.d.ts +2 -0
  4. package/dist/asset-registry.d.ts.map +1 -1
  5. package/dist/handles.d.ts +2 -0
  6. package/dist/handles.d.ts.map +1 -1
  7. package/dist/index.d.ts +4 -5
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.mjs +62 -9
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/mipmap-generator.d.ts +3 -1
  12. package/dist/mipmap-generator.d.ts.map +1 -1
  13. package/dist/registry/instantiate.d.ts +2 -0
  14. package/dist/registry/instantiate.d.ts.map +1 -1
  15. package/dist/registry/validate-material.d.ts +1 -0
  16. package/dist/registry/validate-material.d.ts.map +1 -1
  17. package/package.json +14 -14
  18. package/src/__tests__/catalog-replica.unit.test.ts +36 -0
  19. package/src/__tests__/inline-pack-loaders.unit.test.ts +37 -0
  20. package/src/__tests__/instantiate-publication-fence.unit.test.ts +59 -1
  21. package/src/__tests__/material-ready.integration.test.ts +87 -6
  22. package/src/__tests__/material-stale-generation.unit.test.ts +1 -6
  23. package/src/__tests__/resolve-asset-handle.unit.test.ts +1 -1
  24. package/src/__tests__/scriptable-pack-public-surface.test-d.ts +11 -0
  25. package/src/__tests__/validate-material.unit.test.ts +46 -1
  26. package/src/asset-registry.ts +5 -0
  27. package/src/handles.ts +7 -1
  28. package/src/index.ts +17 -3
  29. package/src/material/inspection.ts +1 -1
  30. package/src/material/loader.ts +9 -9
  31. package/src/material/runtime-shader.ts +1 -1
  32. package/src/mipmap-generator.ts +43 -1
  33. package/src/registry/instantiate.ts +14 -4
  34. package/src/registry/load-by-guid.ts +1 -1
  35. package/src/registry/validate-material.ts +96 -3
@@ -1,4 +1,4 @@
1
- import { type Result, RhiError } from '@forgeax/engine-rhi';
1
+ import { type Result, RhiError, type RhiRenderPassEncoder } from '@forgeax/engine-rhi';
2
2
  /**
3
3
  * Subset of `RhiDevice` consumed by the mipmap utility (charter F2: only
4
4
  * the methods the algorithm actually invokes; the dawn integration tests
@@ -129,4 +129,6 @@ export declare function blitMipmapsSync(device: MipmapBlitDevice, texture: any,
129
129
  readonly height: number;
130
130
  readonly levels?: number;
131
131
  }): Result<void, RhiError>;
132
+ /** Encode one prewarmed mip level into a caller-owned graph render pass. */
133
+ export declare function encodeMipmapLevel(device: MipmapBlitDevice, pass: RhiRenderPassEncoder, sourceView: unknown, format: GPUTextureFormat): Result<void, RhiError>;
132
134
  //# sourceMappingURL=mipmap-generator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mipmap-generator.d.ts","sourceRoot":"","sources":["../src/mipmap-generator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mipmap-generator.d.ts","sourceRoot":"","sources":["../src/mipmap-generator.ts"],"names":[],"mappings":"AA+CA,OAAO,EAAW,KAAK,MAAM,EAAE,QAAQ,EAAE,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAEhG;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,YAAY,GAAG;IAEzB,aAAa,CAAC,IAAI,CAAC,EAAE,GAAG,GAAG,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAE5C,qBAAqB,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAEnD,oBAAoB,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAElD,oBAAoB,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;CACnD,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,yBAAyB,GAAG,CACtC,MAAM,EAAE,YAAY,EACpB,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,KACnC,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;AAgExC;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAI5E;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAG5D;AAED;;;;;;;GAOG;AACH,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,gBAAgB,EACxB,uBAAuB,EAAE,yBAAyB,GAEjD,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CA0B/B;AAgDD;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,YAAY,GAAG;IAE5C,aAAa,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAE3C,eAAe,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAE7C,iBAAiB,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAE7D,oBAAoB,CAAC,IAAI,CAAC,EAAE,GAAG,GAAG,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACnD,QAAQ,CAAC,KAAK,EAAE;QAEd,MAAM,CAAC,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;KACxD,CAAC;CACH,CAAC;AA8DF;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,gBAAgB,EAExB,OAAO,EAAE,GAAG,EACZ,UAAU,EAAE;IACV,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B,EACD,uBAAuB,EAAE,yBAAyB,GACjD,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAehC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,gBAAgB,EAExB,OAAO,EAAE,GAAG,EACZ,UAAU,EAAE;IACV,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B,GACA,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CA0BxB;AAED,4EAA4E;AAC5E,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,oBAAoB,EAC1B,UAAU,EAAE,OAAO,EACnB,MAAM,EAAE,gBAAgB,GACvB,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAkCxB"}
@@ -12,6 +12,8 @@ import { type ScenePublicationFence, type ScenePublicationFenceError } from './s
12
12
  export interface SkinJointResolver {
13
13
  resolveSkinAsset(skeletonHandleRaw: number): SkinAsset | undefined;
14
14
  }
15
+ /** Derive a publication fence from the registry's unified live projections. */
16
+ export declare function scenePublicationFenceFromRegistry(registry: AssetRegistry, outputGuid: string): Result<ScenePublicationFence, ScenePublicationFenceError>;
15
17
  /**
16
18
  * Post-spawn hook contract (D-1). A hook runs after instantiate spawns the
17
19
  * scene subtree; the shipped implementation is runtime's `postSpawnResolveJoints`
@@ -1 +1 @@
1
- {"version":3,"file":"instantiate.d.ts","sourceRoot":"","sources":["../../src/registry/instantiate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"instantiate.d.ts","sourceRoot":"","sources":["../../src/registry/instantiate.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,7 @@ import type { AssetRegistry } from '../asset-registry';
15
15
  * @returns AssetError on failure, null on success
16
16
  */
17
17
  export declare function validateMaterialPasses(registry: AssetRegistry, asset: MaterialAsset): AssetError | null;
18
+ export declare function validateMaterialTransmissionContract(asset: MaterialAsset): MaterialError | null;
18
19
  export interface MaterialEffectiveRootIdentity {
19
20
  readonly guid: string;
20
21
  readonly layoutIdentity: string;
@@ -1 +1 @@
1
- {"version":3,"file":"validate-material.d.ts","sourceRoot":"","sources":["../../src/registry/validate-material.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EACL,UAAU,EAEV,KAAK,aAAa,EAClB,KAAK,aAAa,EAEnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,aAAa,EACvB,KAAK,EAAE,aAAa,GACnB,UAAU,GAAG,IAAI,CAoFnB;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC,EACtD,IAAI,EAAE,6BAA6B,GAClC,aAAa,GAAG,IAAI,CAkBtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,aAAa,EACxB,KAAK,EAAE,aAAa,GACnB,UAAU,GAAG,IAAI,CA8GnB;AAED,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,aAAa,EACxB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,GACb,OAAO,CAgCT;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,CA8BhG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,aAAa,EACvB,QAAQ,EAAE,MAAM,GACf,WAAW,CAAC,MAAM,CAAC,GAAG,SAAS,CAIjC"}
1
+ {"version":3,"file":"validate-material.d.ts","sourceRoot":"","sources":["../../src/registry/validate-material.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EACL,UAAU,EAEV,KAAK,aAAa,EAClB,KAAK,aAAa,EAEnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,aAAa,EACvB,KAAK,EAAE,aAAa,GACnB,UAAU,GAAG,IAAI,CAoFnB;AA4CD,wBAAgB,oCAAoC,CAAC,KAAK,EAAE,aAAa,GAAG,aAAa,GAAG,IAAI,CAkD/F;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC,EACtD,IAAI,EAAE,6BAA6B,GAClC,aAAa,GAAG,IAAI,CAiBtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,aAAa,EACxB,KAAK,EAAE,aAAa,GACnB,UAAU,GAAG,IAAI,CA8GnB;AAED,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,aAAa,EACxB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,GACb,OAAO,CAgCT;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,CA8BhG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,aAAa,EACvB,QAAQ,EAAE,MAAM,GACf,WAAW,CAAC,MAAM,CAAC,GAAG,SAAS,CAIjC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgeax/engine-assets-runtime",
3
- "version": "0.1.6",
3
+ "version": "0.1.19",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -26,22 +26,22 @@
26
26
  "LICENSE"
27
27
  ],
28
28
  "dependencies": {
29
- "@forgeax/engine-codec": "0.1.6",
30
- "@forgeax/engine-ecs": "0.1.6",
31
- "@forgeax/engine-geometry": "0.1.6",
32
- "@forgeax/engine-graphics-extras": "0.1.6",
33
- "@forgeax/engine-image": "0.1.6",
34
- "@forgeax/engine-pack": "0.1.6",
35
- "@forgeax/engine-plugin": "0.1.6",
36
- "@forgeax/engine-rhi": "0.1.6",
37
- "@forgeax/engine-scene": "0.1.6",
38
- "@forgeax/engine-shader": "0.1.6",
39
- "@forgeax/engine-types": "0.1.6",
40
- "@forgeax/engine-ui": "0.1.6",
29
+ "@forgeax/engine-codec": "0.1.19",
30
+ "@forgeax/engine-ecs": "0.1.19",
31
+ "@forgeax/engine-geometry": "0.1.19",
32
+ "@forgeax/engine-graphics-extras": "0.1.19",
33
+ "@forgeax/engine-image": "0.1.19",
34
+ "@forgeax/engine-pack": "0.1.19",
35
+ "@forgeax/engine-plugin": "0.1.19",
36
+ "@forgeax/engine-rhi": "0.1.19",
37
+ "@forgeax/engine-scene": "0.1.19",
38
+ "@forgeax/engine-shader": "0.1.19",
39
+ "@forgeax/engine-types": "0.1.19",
40
+ "@forgeax/engine-ui": "0.1.19",
41
41
  "@webgpu/types": "^0.1.71"
42
42
  },
43
43
  "devDependencies": {
44
- "@forgeax/engine-skinning": "0.1.6",
44
+ "@forgeax/engine-skinning": "0.1.19",
45
45
  "@webgpu/types": "^0.1.71"
46
46
  },
47
47
  "forgeax": {
@@ -193,4 +193,40 @@ describe('CatalogReplica', () => {
193
193
  diagnostics: [{ code: 'catalog-revision-conflict', severity: 'blocking' }],
194
194
  });
195
195
  });
196
+
197
+ it('applies a failed lifecycle projection when the producer advances its observation revision', async () => {
198
+ const fixture = source();
199
+ const replica = new CatalogReplica(fixture.source as never);
200
+ await replica.start();
201
+
202
+ fixture.emit({
203
+ added: [],
204
+ changed: [
205
+ {
206
+ ...base,
207
+ lifecycle: 'failed',
208
+ revision: {
209
+ digest: 'failure:source-package-ddc-failed:sha256:old',
210
+ observedAt: 2,
211
+ rootId: base.revision.rootId,
212
+ },
213
+ diagnostics: [
214
+ {
215
+ code: 'source-package-ddc-failed',
216
+ severity: 'blocking',
217
+ hint: 'repair and retry',
218
+ },
219
+ ],
220
+ },
221
+ ],
222
+ removed: [],
223
+ });
224
+
225
+ expect(replica.snapshot().entries[0]).toMatchObject({
226
+ guid: base.guid,
227
+ lifecycle: 'failed',
228
+ revision: { observedAt: 2 },
229
+ });
230
+ expect(replica.snapshot().stale).toBe(false);
231
+ });
196
232
  });
@@ -279,6 +279,43 @@ describe('materialLoader', () => {
279
279
  });
280
280
  });
281
281
 
282
+ it('resolves transmission and thickness texture refs through authored contract', () => {
283
+ const out = materialLoader.load(
284
+ {
285
+ passes: [{ name: 'Forward', program: { module: 'forgeax::default-standard-pbr' } }],
286
+ parameters: [
287
+ { name: 'transmissionTexture', type: 'texture' },
288
+ { name: 'thicknessTexture', type: 'texture' },
289
+ ],
290
+ values: {
291
+ transmission: 0.8,
292
+ ior: 1.45,
293
+ thickness: 0.25,
294
+ transmissionTexture: { texture: 0, sampler: 1, coordinates: { set: 2 } },
295
+ thicknessTexture: { texture: 2, sampler: 3, coordinates: { set: 3 } },
296
+ },
297
+ },
298
+ ['texture-transmission', 'sampler-linear', 'texture-thickness', 'sampler-nearest'],
299
+ emptyCtx,
300
+ ) as MaterialAsset;
301
+
302
+ expect(out.values).toMatchObject({
303
+ transmission: 0.8,
304
+ ior: 1.45,
305
+ thickness: 0.25,
306
+ transmissionTexture: {
307
+ texture: 'texture-transmission',
308
+ sampler: 'sampler-linear',
309
+ coordinates: { set: 2 },
310
+ },
311
+ thicknessTexture: {
312
+ texture: 'texture-thickness',
313
+ sampler: 'sampler-nearest',
314
+ coordinates: { set: 3 },
315
+ },
316
+ });
317
+ });
318
+
282
319
  it('resolves nested texture values when the shader schema is present but empty', () => {
283
320
  const out = materialLoader.load(
284
321
  {
@@ -6,7 +6,7 @@ import {
6
6
  } from '@forgeax/engine-types';
7
7
  import { describe, expect, it } from 'vitest';
8
8
  import type { AssetRegistry } from '../asset-registry';
9
- import { resolveMountsRec } from '../registry/instantiate';
9
+ import { resolveMountsRec, scenePublicationFenceFromRegistry } from '../registry/instantiate';
10
10
  import { createScenePublicationFence } from '../registry/scene-publication-fence';
11
11
 
12
12
  const SCENE_GUID = '019ffdb4-1000-7000-8000-000000000008';
@@ -155,4 +155,62 @@ describe('resolveMountsRec publication fence identity', () => {
155
155
  );
156
156
  expect(result).toMatchObject({ ok: true, value: [{ source: 6 }] });
157
157
  });
158
+
159
+ it('prefers the complete pack-index tuple during a CatalogReplica handoff', () => {
160
+ const world = new World();
161
+ const currentSourcePath = 'assets/current.glb';
162
+ const currentPublication = {
163
+ ...publication(),
164
+ sourcePath: currentSourcePath,
165
+ receipt: { ...publication().receipt, sourcePath: currentSourcePath },
166
+ };
167
+ const fence = createScenePublicationFence(currentPublication);
168
+ expect(fence.ok).toBe(true);
169
+ if (!fence.ok) return;
170
+ const registry = {
171
+ assetCatalog: new Map(),
172
+ // The replica may still expose the prior complete tuple while the
173
+ // latest pack-index has already been hydrated after a source write.
174
+ catalogSnapshot: () => ({
175
+ entries: [
176
+ {
177
+ guid: SCENE_GUID,
178
+ kind: 'scene',
179
+ sourcePath: publication().sourcePath,
180
+ packageUrl: '/assets/scene.pack.json',
181
+ publication: publication(),
182
+ },
183
+ ],
184
+ }),
185
+ packIndexCache: new Map([
186
+ [
187
+ SCENE_GUID,
188
+ {
189
+ guid: SCENE_GUID,
190
+ kind: 'scene',
191
+ sourcePath: currentSourcePath,
192
+ packageUrl: '/assets/current.pack.json',
193
+ publication: currentPublication,
194
+ },
195
+ ],
196
+ ]),
197
+ _guidForAsset: () => undefined,
198
+ } as unknown as AssetRegistry;
199
+ const result = resolveMountsRec(
200
+ registry,
201
+ [
202
+ {
203
+ localId: 0 as never,
204
+ source: 6,
205
+ memberFirst: 0 as never,
206
+ memberCount: 0,
207
+ publicationFence: fence.value,
208
+ },
209
+ ],
210
+ world,
211
+ new Set(),
212
+ );
213
+ expect(result).toMatchObject({ ok: true, value: [{ source: 6 }] });
214
+ expect(scenePublicationFenceFromRegistry(registry, SCENE_GUID)).toEqual(fence);
215
+ });
158
216
  });
@@ -5,6 +5,7 @@ import {
5
5
  } from '@forgeax/engine-pack';
6
6
  import { AssetGuid } from '@forgeax/engine-pack/guid';
7
7
  import { ShaderRegistry } from '@forgeax/engine-shader';
8
+ import type { MaterialParameter } from '@forgeax/engine-types';
8
9
  import { describe, expect, it, vi } from 'vitest';
9
10
  import { AssetRegistry } from '../asset-registry';
10
11
  import { MaterialGenerationCache } from '../material/generation-cache';
@@ -19,7 +20,7 @@ const PRODUCTION_GUID = '019f0000-0000-7000-8000-000000000701';
19
20
  function cookedRecord(guid = MATERIAL_GUID): CookedMaterialRecord {
20
21
  const artifactDigest = createMaterialArtifactDigest(ARTIFACT_BYTES);
21
22
  return {
22
- schemaVersion: 'material-cook/2',
23
+ schemaVersion: 'material-cook/3',
23
24
  guid,
24
25
  materialGuid: guid,
25
26
  publicationGeneration: 7,
@@ -36,15 +37,31 @@ function cookedRecord(guid = MATERIAL_GUID): CookedMaterialRecord {
36
37
  bytes: ARTIFACT_BYTES,
37
38
  },
38
39
  receipt: {
39
- schemaVersion: 'material-cook/2',
40
+ schemaVersion: 'material-cook/3',
40
41
  sourceClosure: ['materials/ready.material.json', 'shaders/ready.wgsl'],
41
42
  profile: 'webgpu/v1',
42
43
  compilerVersion: 'compiler/1',
43
- inputDigest: 'input-digest',
44
- outputDigest: artifactDigest,
45
- layoutIdentity: 'sha256:ready-layout',
44
+ identity: {
45
+ materialContractDigest: 'sha256:ready-contract',
46
+ sourceRevision: 'sha256:ready-source',
47
+ sourceClosureDigest: 'sha256:ready-closure',
48
+ layoutIdentity: 'sha256:ready-layout',
49
+ programIdentity: 'sha256:ready-program',
50
+ pipelineIdentity: 'sha256:ready-pipeline',
51
+ materialPublicationIdentity: 'sha256:ready-publication',
52
+ cookIdentity: 'sha256:ready-cook',
53
+ compilerFingerprint: 'sha256:ready-compiler',
54
+ wasm: {
55
+ sourceContentKey: 'unavailable',
56
+ artifactSha256: 'unavailable',
57
+ glueSha256: 'unavailable',
58
+ },
59
+ artifactDigest,
60
+ valueGeneration: 7,
61
+ dependencyGeneration: 7,
62
+ cookGeneration: 7,
63
+ },
46
64
  derivedInterface: { layoutIdentity: 'sha256:ready-layout' },
47
- publicationGeneration: 7,
48
65
  },
49
66
  };
50
67
  }
@@ -87,6 +104,70 @@ describe('material runtime readiness', () => {
87
104
  ]);
88
105
  });
89
106
 
107
+ it('keeps the Standard transmission contract in the published snapshot', async () => {
108
+ const parameters: readonly MaterialParameter[] = [
109
+ { name: 'transmission', type: 'f32', default: 0 },
110
+ { name: 'ior', type: 'f32', default: 1.5 },
111
+ { name: 'thickness', type: 'f32', default: 0 },
112
+ { name: 'attenuationColor', type: 'vec3', default: [1, 1, 1] },
113
+ { name: 'attenuationDistance', type: 'f32' },
114
+ { name: 'transmissionTexture', type: 'texture' },
115
+ { name: 'thicknessTexture', type: 'texture' },
116
+ ];
117
+ expect(materialParametersToParamSchema(parameters, 'published::standard')).toEqual([
118
+ { name: 'transmission', type: 'f32', default: 0 },
119
+ { name: 'ior', type: 'f32', default: 1.5 },
120
+ { name: 'thickness', type: 'f32', default: 0 },
121
+ { name: 'attenuationColor', type: 'vec3', default: [1, 1, 1] },
122
+ { name: 'attenuationDistance', type: 'f32' },
123
+ { name: 'transmissionTexture', type: 'texture2d' },
124
+ { name: 'thicknessTexture', type: 'texture2d' },
125
+ ]);
126
+ const baseRecord = cookedRecord(PRODUCTION_GUID);
127
+ const record: CookedMaterialRecord = {
128
+ ...baseRecord,
129
+ parameterContract: {
130
+ parameters,
131
+ values: {
132
+ transmission: 0.8,
133
+ ior: 1.45,
134
+ thickness: 0.25,
135
+ attenuationColor: [0.9, 0.95, 1],
136
+ attenuationDistance: 4,
137
+ transmissionTexture: { texture: 'texture/transmission' },
138
+ thicknessTexture: { texture: 'texture/thickness' },
139
+ },
140
+ },
141
+ resolved: {
142
+ ...baseRecord.resolved,
143
+ parameters,
144
+ values: {
145
+ transmission: 0.8,
146
+ ior: 1.45,
147
+ thickness: 0.25,
148
+ attenuationColor: [0.9, 0.95, 1],
149
+ attenuationDistance: 4,
150
+ transmissionTexture: { texture: 'texture/transmission' },
151
+ thicknessTexture: { texture: 'texture/thickness' },
152
+ },
153
+ },
154
+ };
155
+ const loader = createMaterialLoader({
156
+ loadPublication: async () => ({ ...publication(), guid: PRODUCTION_GUID, record }),
157
+ loadReference: async () => true,
158
+ });
159
+
160
+ const result = await loader.load({
161
+ guid: PRODUCTION_GUID,
162
+ specializationKey: 'material-specialization/7/ready',
163
+ });
164
+
165
+ expect(result.status).toBe('Ready');
166
+ if (result.status !== 'Ready') return;
167
+ expect(result.parameterContract).toEqual(record.parameterContract);
168
+ expect(inspectMaterialRuntime(result).parameterContract).toEqual(record.parameterContract);
169
+ });
170
+
90
171
  it('loads only a cooked record with its artifact and complete references', async () => {
91
172
  const loader = createLoader(async () => publication());
92
173
 
@@ -30,7 +30,6 @@ function createCookedMaterialLoader() {
30
30
  guid,
31
31
  record: {
32
32
  ...raw,
33
- schemaVersion: 'material-cook/2',
34
33
  materialGuid: raw.guid,
35
34
  publicationGeneration: 1,
36
35
  specializationKey: SPECIALIZATION_KEY,
@@ -40,11 +39,7 @@ function createCookedMaterialLoader() {
40
39
  artifact: { ...raw.artifact, digest, bytes },
41
40
  receipt: {
42
41
  ...raw.receipt,
43
- schemaVersion: 'material-cook/2',
44
- layoutIdentity: 'sha256:pulse-layout',
45
- derivedInterface: { layoutIdentity: 'sha256:pulse-layout' },
46
- publicationGeneration: 1,
47
- outputDigest: digest,
42
+ identity: { ...raw.receipt.identity, artifactDigest: digest, cookGeneration: 1 },
48
43
  },
49
44
  },
50
45
  artifact: { bytes },
@@ -167,7 +167,7 @@ describe('walkMaterialPassesOverSharedRefs', () => {
167
167
  const child = mat({
168
168
  parent: 'p-guid' as never,
169
169
  parameters: [
170
- { name: 'roughness', type: 'f32', static: true },
170
+ { name: 'roughness', type: 'f32' },
171
171
  { name: 'normalTexture', type: 'texture' },
172
172
  ],
173
173
  });
@@ -1,8 +1,10 @@
1
1
  import type {
2
2
  AnimationGraph,
3
3
  AssetRegistry,
4
+ AssetRegistryResolver,
4
5
  AudioClipAsset,
5
6
  ParticleEffectAsset,
7
+ RuntimeAssetRegistry,
6
8
  TilesetAsset,
7
9
  } from '@forgeax/engine-assets-runtime';
8
10
  import { expectTypeOf, it } from 'vitest';
@@ -14,3 +16,12 @@ it('exports concrete loadByGuid payload types from the runtime entry point', ()
14
16
  expectTypeOf<AudioClipAsset['mediaType']>().toMatchTypeOf<`audio/${string}`>();
15
17
  expectTypeOf<ParticleEffectAsset['program']>().toMatchTypeOf<ParticleEffectAsset['program']>();
16
18
  });
19
+
20
+ it('exports the five-action runtime registry and its resolver from the root entry point', () => {
21
+ expectTypeOf<RuntimeAssetRegistry>().toHaveProperty('installDecoder');
22
+ expectTypeOf<RuntimeAssetRegistry>().toHaveProperty('load');
23
+ expectTypeOf<RuntimeAssetRegistry>().toHaveProperty('snapshot');
24
+ expectTypeOf<RuntimeAssetRegistry>().toHaveProperty('subscribe');
25
+ expectTypeOf<AssetRegistryResolver>().toHaveProperty('lookup');
26
+ expectTypeOf<AssetRegistryResolver>().toHaveProperty('guidOf');
27
+ });
@@ -18,6 +18,7 @@ import {
18
18
  materialShaderTextureFieldNames,
19
19
  validateMaterialCookIdentity,
20
20
  validateMaterialPasses,
21
+ validateMaterialTransmissionContract,
21
22
  validateParamType,
22
23
  validateSpriteSlices,
23
24
  } from '../registry/validate-material';
@@ -64,7 +65,7 @@ describe('validateMaterialCookIdentity', () => {
64
65
  const record = {
65
66
  guid: 'mat-child',
66
67
  receipt: {
67
- layoutIdentity: 'sha256:root-layout',
68
+ identity: { layoutIdentity: 'sha256:root-layout' },
68
69
  derivedInterface: { layoutIdentity: 'sha256:root-layout' },
69
70
  },
70
71
  } as never;
@@ -180,6 +181,50 @@ describe('validateMaterialPasses', () => {
180
181
  });
181
182
  });
182
183
 
184
+ describe('validateMaterialTransmissionContract', () => {
185
+ it.each([
186
+ ['transmission range', { transmission: 1.1 }],
187
+ ['ior range', { ior: 0 }],
188
+ ['thickness range', { thickness: -0.1 }],
189
+ ['attenuation distance', { attenuationDistance: Number.POSITIVE_INFINITY }],
190
+ ])('returns a structured error for %s', (_name, value) => {
191
+ const error = validateMaterialTransmissionContract(mat({ values: value }));
192
+ expect(error?.code).toBe('material-transmission-contract-invalid');
193
+ expect(error?.detail).toMatchObject({ reason: expect.any(String) });
194
+ });
195
+
196
+ it('rejects BLEND and depth writes when transmission is active', () => {
197
+ expect(
198
+ validateMaterialTransmissionContract(
199
+ mat({
200
+ values: { transmission: 0.5 },
201
+ passes: [
202
+ {
203
+ name: 'forward',
204
+ program: { module: 'forgeax_material::standard' },
205
+ renderState: { blend: {} },
206
+ },
207
+ ],
208
+ }),
209
+ )?.detail,
210
+ ).toMatchObject({ reason: 'blend' });
211
+ expect(
212
+ validateMaterialTransmissionContract(
213
+ mat({
214
+ values: { transmission: 0.5 },
215
+ passes: [
216
+ {
217
+ name: 'forward',
218
+ program: { module: 'forgeax_material::standard' },
219
+ renderState: { depthWriteEnabled: true },
220
+ },
221
+ ],
222
+ }),
223
+ )?.detail,
224
+ ).toMatchObject({ reason: 'depth-write' });
225
+ });
226
+ });
227
+
183
228
  describe('validateParamType', () => {
184
229
  const reg = makeRegistry({});
185
230
  it('numeric scalars', () => {
@@ -611,6 +611,11 @@ export class AssetRegistry {
611
611
  return this._originIndex.get(asset as object);
612
612
  }
613
613
 
614
+ /** Public identity projection consumed by scene collection boundaries. */
615
+ guidOf(asset: Asset): string | undefined {
616
+ return this._guidForAsset(asset);
617
+ }
618
+
614
619
  /** Return the production MaterialReady/Error result for one material GUID. */
615
620
  getMaterialReadiness(guid: string): MaterialReady | MaterialLoadError | undefined {
616
621
  return this.materialReadiness.get(guid.toLowerCase());
package/src/handles.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  // straight-cut). Pure move from asset-registry.ts; zero identifier changes.
4
4
 
5
5
  import type { Handle } from '@forgeax/engine-types';
6
- import { toShared } from '@forgeax/engine-types';
6
+ import { handleSlot, toShared } from '@forgeax/engine-types';
7
7
 
8
8
  // ─── Builtin handles (D-S9 / backward compat with hello-triangle + hello-cube) ─
9
9
 
@@ -146,6 +146,12 @@ export const BUILTIN_MESH_GUIDS: ReadonlyArray<readonly [Handle<'MeshAsset', 'sh
146
146
  [HANDLE_CYLINDER, 'ab20af21-0764-55be-a7f2-b80ab3d46a0a'],
147
147
  ];
148
148
 
149
+ /** Resolve the stable producer GUID for one process-static builtin mesh handle. */
150
+ export function builtinMeshGuid(handle: Handle<string, 'shared'>): string | undefined {
151
+ const slot = handleSlot(handle);
152
+ return BUILTIN_MESH_GUIDS.find(([candidate]) => handleSlot(candidate) === slot)?.[1];
153
+ }
154
+
149
155
  /**
150
156
  * Field names known to carry handle<> schema-vocab references (plan-strategy
151
157
  * D-4). parseScenePayload uses this allowlist to replace integer values
package/src/index.ts CHANGED
@@ -76,6 +76,7 @@ export {
76
76
  } from './errors/asset';
77
77
  // ─── Builtin mesh handles (re-exported by asset-registry from ./handles) ─────
78
78
  export {
79
+ builtinMeshGuid,
79
80
  HANDLE_CUBE,
80
81
  HANDLE_CYLINDER,
81
82
  HANDLE_NINESLICE_QUAD,
@@ -83,7 +84,15 @@ export {
83
84
  HANDLE_SPHERE,
84
85
  HANDLE_TRIANGLE,
85
86
  } from './handles';
86
- export { createAssetRegistry } from './internal/load-asset.js';
87
+ // The five-action runtime registry is distinct from the legacy authoring
88
+ // AssetRegistry class above. Keep its resolver on the public package boundary
89
+ // so render/VFX hosts never reach through `/internal` to inspect loaded assets.
90
+ export {
91
+ type AssetRegistry as RuntimeAssetRegistry,
92
+ type AssetRegistryResolver,
93
+ createAssetRegistry,
94
+ getAssetRegistryResolver,
95
+ } from './internal/load-asset.js';
87
96
  // ─── Loader-injection surface ───────────────────────────────────────────────
88
97
  export { LoaderRegistry } from './loader-registry';
89
98
  // ─── Default loader tables + individual loaders (pre-w14 consumer face) ──────
@@ -134,6 +143,7 @@ export {
134
143
  // ─── Mipmap generation helpers ──────────────────────────────────────────────
135
144
  export {
136
145
  blitMipmapsSync,
146
+ encodeMipmapLevel,
137
147
  getOrCreateMipmapPipeline,
138
148
  type MipmapBlitDevice,
139
149
  type MipmapShaderModuleFactory,
@@ -148,9 +158,13 @@ export {
148
158
  type RuntimeEvidenceSource,
149
159
  } from './registry/asset-evidence';
150
160
  export { CatalogReplica, type CatalogReplicaSnapshot } from './registry/catalog-state';
151
- export type { PostSpawnHook, SkinJointResolver } from './registry/instantiate';
152
161
  // ─── Scene instantiate collaboration contract types (D-1 injected hook) ─────
153
- export { buildSceneChildContext } from './registry/instantiate';
162
+ export {
163
+ buildSceneChildContext,
164
+ type PostSpawnHook,
165
+ type SkinJointResolver,
166
+ scenePublicationFenceFromRegistry,
167
+ } from './registry/instantiate';
154
168
  export { loadMaterialReadyByGuid } from './registry/load-by-guid';
155
169
  export {
156
170
  compareScenePublicationFences,
@@ -29,7 +29,7 @@ export function inspectMaterialRuntime(ready: MaterialReady): MaterialRuntimeInf
29
29
  publicationGeneration: ready.publicationGeneration,
30
30
  specializationKey: ready.specializationKey,
31
31
  artifactDigest: ready.artifactDigest,
32
- layoutIdentity: ready.record.receipt.layoutIdentity,
32
+ layoutIdentity: ready.record.receipt.identity.layoutIdentity,
33
33
  dependencies: [
34
34
  ...ready.record.refs.parent,
35
35
  ...ready.record.refs.textures,
@@ -108,7 +108,7 @@ function missingCook(request: MaterialLoadRequest): MaterialLoadError {
108
108
  function recordError(
109
109
  request: MaterialLoadRequest,
110
110
  field: string,
111
- expected = 'a complete material-cook/2 publication record',
111
+ expected = 'a complete material-cook/3 publication record',
112
112
  ): MaterialLoadError {
113
113
  return materialError(
114
114
  request,
@@ -174,18 +174,18 @@ function completeTupleField(record: CookedMaterialRecord): string | undefined {
174
174
  receipt.profile.length === 0 ||
175
175
  typeof receipt.compilerVersion !== 'string' ||
176
176
  receipt.compilerVersion.length === 0 ||
177
- typeof receipt.inputDigest !== 'string' ||
178
- receipt.inputDigest.length === 0 ||
179
- receipt.outputDigest !== record.artifactDigest ||
180
- receipt.publicationGeneration !== record.publicationGeneration
177
+ typeof receipt.identity.cookIdentity !== 'string' ||
178
+ receipt.identity.cookIdentity.length === 0 ||
179
+ receipt.identity.artifactDigest !== record.artifactDigest ||
180
+ receipt.identity.cookGeneration !== record.publicationGeneration
181
181
  )
182
182
  return 'receipt';
183
183
  if (
184
- typeof receipt.layoutIdentity !== 'string' ||
185
- receipt.layoutIdentity.length === 0 ||
186
- receipt.derivedInterface?.layoutIdentity !== receipt.layoutIdentity
184
+ typeof receipt.identity.layoutIdentity !== 'string' ||
185
+ receipt.identity.layoutIdentity.length === 0 ||
186
+ receipt.derivedInterface?.layoutIdentity !== receipt.identity.layoutIdentity
187
187
  )
188
- return 'receipt.layoutIdentity';
188
+ return 'receipt.identity.layoutIdentity';
189
189
  if (
190
190
  typeof record.artifact.mediaType !== 'string' ||
191
191
  record.artifact.mediaType.length === 0 ||