@forgeax/engine-runtime 0.1.28 → 0.1.29

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 (113) hide show
  1. package/README.md +98 -0
  2. package/dist/collect-scene-asset.d.ts.map +1 -1
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.mjs +480 -15
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/scene-utils/mount-override-fold.d.ts +3 -1
  8. package/dist/scene-utils/mount-override-fold.d.ts.map +1 -1
  9. package/package.json +37 -32
  10. package/src/__tests__/animation-graph-collect-roundtrip.test.ts +4 -2
  11. package/src/__tests__/asset-registry-aabb.cases.unit.test.ts +5 -4
  12. package/src/__tests__/asset-registry-guid-reverse.test.ts +22 -27
  13. package/src/__tests__/asset-registry-mounts.test.ts +174 -734
  14. package/src/__tests__/asset-registry-scene.cases.unit.test.ts +9 -18
  15. package/src/__tests__/asset-registry.recursive.spec.ts +23 -29
  16. package/src/__tests__/camera-ortho.cases.unit.test.ts +12 -3
  17. package/src/__tests__/collect-scene-asset.test.ts +96 -113
  18. package/src/__tests__/collect-scene-mount-collapse.test.ts +64 -66
  19. package/src/__tests__/collect-transient-children-absent.test.ts +11 -17
  20. package/src/__tests__/collect-transient-roundtrip.test.ts +12 -13
  21. package/src/__tests__/collect-transient-skip.test.ts +20 -27
  22. package/src/__tests__/components.test-d.ts +17 -1
  23. package/src/__tests__/components.unit.test.ts +75 -27
  24. package/src/__tests__/composite-skybox-cross-world.browser.test.ts +4 -2
  25. package/src/__tests__/dawn/instances-per-instance-pbr.dawn.test.ts +8 -4
  26. package/src/__tests__/dawn/instances-shadow.dawn.test.ts +4 -1
  27. package/src/__tests__/dawn/instances-uniform-fallback.dawn.test.ts +66 -92
  28. package/src/__tests__/dawn/material-cooked-fixture.dawn.test.ts +200 -4
  29. package/src/__tests__/errors.unit.test.ts +13 -0
  30. package/src/__tests__/feature-depth-material.dawn.test.ts +23 -0
  31. package/src/__tests__/feature-depth-material.fixture.ts +473 -0
  32. package/src/__tests__/fullscreen-post-process-pass.dawn.test.ts +14 -14
  33. package/src/__tests__/geometry.unit.test.ts +42 -43
  34. package/src/__tests__/gpu-resource-release.cases.unit.test.ts +16 -9
  35. package/src/__tests__/handle-migration.test.ts +0 -1
  36. package/src/__tests__/instances-renderer-lifecycle.integration.test.ts +20 -14
  37. package/src/__tests__/instances.test-d.ts +13 -52
  38. package/src/__tests__/instantiate-mount-deferred-wiring.test.ts +42 -80
  39. package/src/__tests__/lazy-catalog.cases.unit.test.ts +17 -3
  40. package/src/__tests__/loader-registry.cases.unit.test.ts +4 -4
  41. package/src/__tests__/material-publication.browser.test.ts +29 -0
  42. package/src/__tests__/material-publication.commands.ts +23 -0
  43. package/src/__tests__/material-publication.dawn.test.ts +24 -0
  44. package/src/__tests__/material-publication.fixture.ts +349 -0
  45. package/src/__tests__/material-publication.server.ts +109 -0
  46. package/src/__tests__/parse-scene-payload-refs.cases.unit.test.ts +73 -96
  47. package/src/__tests__/pbr-pipeline.unit.test.ts +25 -11
  48. package/src/__tests__/point-light-shadow.browser.test.ts +8 -2
  49. package/src/__tests__/point-light-shadow.unit.test.ts +37 -8
  50. package/src/__tests__/record-all-topology.cases.unit.test.ts +52 -0
  51. package/src/__tests__/record-draw-branch.cases.unit.test.ts +1 -0
  52. package/src/__tests__/record-worldid-isolation.test.ts +47 -28
  53. package/src/__tests__/render-error-exhaustive.test-d.ts +24 -0
  54. package/src/__tests__/render-system-extract-dispatch-ordering.test.ts +15 -4
  55. package/src/__tests__/render-system-extract.test.ts +61 -0
  56. package/src/__tests__/render-system-extract.unit.test.ts +21 -7
  57. package/src/__tests__/render-system-fold-bucket.unit.test.ts +33 -1
  58. package/src/__tests__/render-system-fold-material.unit.test.ts +32 -6
  59. package/src/__tests__/render-system-fold-modegate.unit.test.ts +26 -4
  60. package/src/__tests__/render-system-fold.dawn.test.ts +4 -1
  61. package/src/__tests__/render-system-mega.test.ts +50 -62
  62. package/src/__tests__/renderer-device-loss-provider-feasibility.dawn.test.ts +0 -1
  63. package/src/__tests__/renderer-device-loss-recovery.dawn.test.ts +0 -3
  64. package/src/__tests__/renderer-draw-world.cases.unit.test.ts +16 -513
  65. package/src/__tests__/resolve-scene-guids.cases.unit.test.ts +48 -43
  66. package/src/__tests__/rhi-null-command-flow.unit.test.ts +9 -7
  67. package/src/__tests__/roots-to-scene-asset.test.ts +27 -28
  68. package/src/__tests__/roundtrip-primitive.test.ts +24 -23
  69. package/src/__tests__/scene-equirect-roundtrip.test.ts +5 -16
  70. package/src/__tests__/scene-instantiate-cycle-runtime.test.ts +13 -29
  71. package/src/__tests__/scene-mount-fixed-point.test.ts +104 -227
  72. package/src/__tests__/scene-mount-roundtrip.test.ts +95 -961
  73. package/src/__tests__/scene-mount-wrapper-roundtrip.test.ts +36 -182
  74. package/src/__tests__/scene-sugar-instantiate.test.ts +20 -15
  75. package/src/__tests__/serialize-mounts-roundtrip.test.ts +108 -492
  76. package/src/__tests__/serialize-scene-asset.test.ts +36 -50
  77. package/src/__tests__/shadow-caster-empty-schema.test.ts +3 -1
  78. package/src/__tests__/shadow-fields-observable.dawn.test.ts +34 -19
  79. package/src/__tests__/shadow-mesh-ssbo-order.test.ts +5 -12
  80. package/src/__tests__/skin-extract-getarrayview-count.unit.test.ts +6 -4
  81. package/src/__tests__/skin-m2-extract-happy.cases.unit.test.ts +50 -2
  82. package/src/__tests__/skin-m2-extract-joint-error.cases.unit.test.ts +50 -2
  83. package/src/__tests__/skin-m2-extract-null-assets.cases.unit.test.ts +50 -2
  84. package/src/__tests__/skin-m2-extract-skeleton-error.cases.unit.test.ts +50 -2
  85. package/src/__tests__/skin-palette-extract.cases.unit.test.ts +2 -0
  86. package/src/__tests__/skinned-shadow-mixed.dawn.test.ts +93 -48
  87. package/src/__tests__/skylight-pipeline-layout.cases.unit.test.ts +6 -2
  88. package/src/__tests__/sparse-tag-scene-roundtrip.test.ts +1 -1
  89. package/src/__tests__/sprite-lit-bgl-byte-identical.test.ts +11 -3
  90. package/src/__tests__/template-game-default-api-contract.test.ts +21 -21
  91. package/src/__tests__/tonemap-hdr-target.cases.unit.test.ts +8 -5
  92. package/src/__tests__/tonemap-pipeline-split.cases.unit.test.ts +3 -0
  93. package/src/__tests__/transient-fields-collect.test.ts +4 -4
  94. package/src/__tests__/transient-runtime-behavior.test.ts +9 -9
  95. package/src/__tests__/vfx-depth-material.dawn.test.ts +42 -0
  96. package/src/__tests__/vfx-mesh-lighting.browser.test.ts +16 -0
  97. package/src/__tests__/vfx-mesh-lighting.commands.ts +14 -0
  98. package/src/__tests__/vfx-mesh-lighting.dawn.test.ts +16 -0
  99. package/src/__tests__/vfx-mesh-lighting.fixture.ts +883 -0
  100. package/src/__tests__/wave1-rendering-materials.browser.test.ts +617 -0
  101. package/src/__tests__/wave1-rendering-p0.browser.test.ts +751 -0
  102. package/src/__tests__/wave1-rendering-recovery.browser.test.ts +666 -0
  103. package/src/__tests__/wave1-shadow-diagnostic.browser.test.ts +582 -0
  104. package/src/__tests__/world-scene-despawn.test.ts +8 -8
  105. package/src/__tests__/world-scene-instantiate.test.ts +23 -29
  106. package/src/__tests__/world-scene-nested.test.ts +90 -163
  107. package/src/__tests__/world-scene-override.test.ts +17 -16
  108. package/src/__tests__/zero-compression-zero-load.integration.test.ts +1 -0
  109. package/src/collect-scene-asset.ts +637 -19
  110. package/src/components/__tests__/sprite-animation.test-d.ts +2 -2
  111. package/src/index.ts +1 -0
  112. package/src/scene-utils/mount-override-fold.ts +5 -14
  113. package/src/__tests__/node-shims.d.ts +0 -31
@@ -407,10 +407,7 @@ type __MergedKeep =
407
407
  function makeSceneAsset(): SceneAsset {
408
408
  return {
409
409
  kind: 'scene',
410
- entities: [
411
- { localId: 0 as LocalEntityId, components: {} },
412
- { localId: 1 as LocalEntityId, components: {} },
413
- ],
410
+ entities: { 'entity-0': { components: {} }, 'entity-1': { components: {} } },
414
411
  };
415
412
  }
416
413
 
@@ -424,7 +421,7 @@ type __MergedKeep =
424
421
  expect(looked).toBeDefined();
425
422
  if (looked === undefined) return;
426
423
  expect(looked.kind).toBe('scene');
427
- expect(looked.entities.length).toBe(2);
424
+ expect(Object.keys(looked.entities)).toHaveLength(2);
428
425
  });
429
426
 
430
427
  it('inspect() reports kind `scene` for a catalogued scene', () => {
@@ -478,24 +475,18 @@ type __MergedKeep =
478
475
  };
479
476
  const fn = internal.parseAssetPayload.bind(reg);
480
477
  const payload = {
481
- entities: [
482
- { localId: 0, components: { Transform: { pos: [1, 2, 3] } } },
483
- { localId: 1, components: { ChildOf: { parent: 0 } } },
484
- ],
478
+ entities: {
479
+ 'entity-0': { components: { Transform: { pos: [1, 2, 3] } } },
480
+ 'entity-1': { components: { ChildOf: { parent: 'entity-0' } } },
481
+ },
485
482
  };
486
483
  const asset = fn('scene', payload) as SceneAsset | undefined;
487
484
  expect(asset).toBeDefined();
488
485
  if (!asset) return;
489
486
  expect(asset.kind).toBe('scene');
490
- expect(asset.entities.length).toBe(2);
491
- // LocalEntityId extends number, so widening is implicit at the
492
- // structural layer; explicit annotation keeps the assertion intent
493
- // readable.
494
- const firstLocalId: number = asset.entities[0]?.localId ?? -1;
495
- expect(firstLocalId).toBe(0);
496
- expect(
497
- (asset.entities[1]?.components as Record<string, Record<string, unknown>>).ChildOf?.parent,
498
- ).toBe(0);
487
+ expect(Object.keys(asset.entities)).toHaveLength(2);
488
+ expect(asset.entities['entity-0']).toBeDefined();
489
+ expect(asset.entities['entity-1']?.components.ChildOf?.parent).toBe('entity-0');
499
490
  });
500
491
  });
501
492
  }
@@ -50,7 +50,7 @@ function parseGuid(s: string): AssetGuid {
50
50
  return r.value;
51
51
  }
52
52
 
53
- function localId(n: number): LocalEntityId {
53
+ function _localId(n: number): LocalEntityId {
54
54
  return n as LocalEntityId;
55
55
  }
56
56
 
@@ -90,16 +90,15 @@ function makeMaterialAsset(): MaterialAsset {
90
90
  function makeTestSceneAsset(subRefs: { meshGuid: string; materialGuids: string[] }): SceneAsset {
91
91
  return {
92
92
  kind: 'scene',
93
- entities: [
94
- {
95
- localId: localId(0),
93
+ entities: {
94
+ 'entity-0': {
96
95
  components: {
97
96
  Transform: { pos: [0, 0, 0] },
98
97
  MeshFilter: { assetHandle: subRefs.meshGuid },
99
98
  MeshRenderer: { materials: subRefs.materialGuids },
100
99
  },
101
100
  },
102
- ],
101
+ },
103
102
  };
104
103
  }
105
104
 
@@ -256,16 +255,15 @@ describe('AC-07 — transitive failure attribution', () => {
256
255
  guid: SCENE_GUID,
257
256
  kind: 'scene',
258
257
  payload: {
259
- entities: [
260
- {
261
- localId: 0,
258
+ entities: {
259
+ 'entity-0': {
262
260
  components: {
263
261
  Transform: { pos: [0, 0, 0] },
264
262
  MeshFilter: { assetHandle: 0 },
265
263
  MeshRenderer: { materials: [1] },
266
264
  },
267
265
  },
268
- ],
266
+ },
269
267
  },
270
268
  refs: [MESH_GUID, MISSING_SUB_GUID],
271
269
  },
@@ -334,7 +332,7 @@ describe('AC-07 — transitive failure attribution', () => {
334
332
  // (4-segment chain: scene → entity → component.field → sub-asset).
335
333
  expect(err.hint).toContain(MISSING_SUB_GUID);
336
334
  expect(err.hint).toContain(SCENE_GUID);
337
- expect(err.hint).toContain('entity 0');
335
+ expect(err.hint).toContain('entity entity-0');
338
336
  expect(err.hint).toContain('MeshRenderer.materials');
339
337
  // feat-20260622 verify r1: the breadcrumb provenance is ALSO exposed in
340
338
  // structured form on `.detail` so an AI user locates the broken edge by
@@ -345,7 +343,7 @@ describe('AC-07 — transitive failure attribution', () => {
345
343
  | {
346
344
  referencedByGuid?: string;
347
345
  subAssetGuid?: string;
348
- sceneEntityId?: number;
346
+ sceneEntityKey?: string;
349
347
  sourceField?: { componentName?: string; fieldName?: string; arrayIndex?: number };
350
348
  }
351
349
  | undefined;
@@ -354,7 +352,7 @@ describe('AC-07 — transitive failure attribution', () => {
354
352
  // pass vacuously if the provenance silently regressed to hint-only.
355
353
  expect(detail?.referencedByGuid?.toLowerCase()).toBe(SCENE_GUID.toLowerCase());
356
354
  expect(detail?.subAssetGuid?.toLowerCase()).toBe(MISSING_SUB_GUID.toLowerCase());
357
- expect(detail?.sceneEntityId).toBe(0);
355
+ expect(detail?.sceneEntityKey).toBe('entity-0');
358
356
  expect(detail?.sourceField?.componentName).toBe('MeshRenderer');
359
357
  expect(detail?.sourceField?.fieldName).toBe('materials');
360
358
  }
@@ -656,15 +654,14 @@ describe('AC-08 — in-flight dedup + cycle', () => {
656
654
  guid: CYCLE_A_GUID,
657
655
  kind: 'scene',
658
656
  payload: {
659
- entities: [
660
- {
661
- localId: 0,
657
+ entities: {
658
+ 'entity-0': {
662
659
  components: {
663
660
  Transform: { pos: [0, 0, 0] },
664
661
  SceneCycler: { refScene: 0 },
665
662
  },
666
663
  },
667
- ],
664
+ },
668
665
  },
669
666
  refs: [CYCLE_B_GUID],
670
667
  },
@@ -679,15 +676,14 @@ describe('AC-08 — in-flight dedup + cycle', () => {
679
676
  guid: CYCLE_B_GUID,
680
677
  kind: 'scene',
681
678
  payload: {
682
- entities: [
683
- {
684
- localId: 0,
679
+ entities: {
680
+ 'entity-0': {
685
681
  components: {
686
682
  Transform: { pos: [0, 0, 0] },
687
683
  SceneCycler: { refScene: 0 },
688
684
  },
689
685
  },
690
- ],
686
+ },
691
687
  },
692
688
  refs: [CYCLE_A_GUID],
693
689
  },
@@ -926,16 +922,15 @@ describe('AC-03 — gltf-shaped scene composite (via SceneAsset, D-9)', () => {
926
922
  guid: SCENE_GLTF_GUID,
927
923
  kind: 'scene',
928
924
  payload: {
929
- entities: [
930
- {
931
- localId: 0,
925
+ entities: {
926
+ 'entity-0': {
932
927
  components: {
933
928
  Transform: { pos: [0, 0, 0] },
934
929
  MeshFilter: { assetHandle: 0 },
935
930
  MeshRenderer: { materials: [1] },
936
931
  },
937
932
  },
938
- ],
933
+ },
939
934
  },
940
935
  // feat-20260622 M4 / w14: refs[] is the recursion SSOT (D-5).
941
936
  // MESH_GUID -> refs[0], MATERIAL_A_GUID -> refs[1].
@@ -1294,15 +1289,14 @@ describe('feat-20260612 M2 fixup — SceneAsset.skinGuids cross-edge', () => {
1294
1289
  guid: SKIN_FIXUP_SCENE_GUID,
1295
1290
  kind: 'scene',
1296
1291
  payload: {
1297
- entities: [
1298
- {
1299
- localId: 0,
1292
+ entities: {
1293
+ 'entity-0': {
1300
1294
  components: {
1301
1295
  Transform: { pos: [0, 0, 0] },
1302
1296
  Skin: { skeleton: 0 },
1303
1297
  },
1304
1298
  },
1305
- ],
1299
+ },
1306
1300
  // refs[]-index form (browser JSON-roundtrip shape)
1307
1301
  skinGuids: [1],
1308
1302
  },
@@ -1377,7 +1371,7 @@ describe('feat-20260612 M2 fixup — SceneAsset.skinGuids cross-edge', () => {
1377
1371
  // Build a SceneAsset POD with inline skinGuids (no refs[] indices).
1378
1372
  const scene: SceneAsset = {
1379
1373
  kind: 'scene',
1380
- entities: [{ localId: localId(0), components: { Transform: { pos: [0, 0, 0] } } }],
1374
+ entities: { 'entity-0': { components: { Transform: { pos: [0, 0, 0] } } } },
1381
1375
  skinGuids: [SKIN_FIXUP_SKIN_GUID],
1382
1376
  };
1383
1377
  reg.catalog(parseGuid(SKIN_FIXUP_SCENE_GUID), scene);
@@ -365,9 +365,9 @@ type __MergedKeep =
365
365
  ];
366
366
  }
367
367
 
368
- describe('Camera schema (19 fields: 17 f32 + clearColor array + autoAspect bool after w9 + tonemap-mvp + fxaa + bloom + clearColor + aspect-sync extensions)', () => {
369
- it('Camera.schema has 19 fields (17 f32 + clearColor array + autoAspect bool: perspective quartet + projection + ortho quartet + tonemap trio + antialias + bloom quartet + clearColor + autoAspect)', () => {
370
- expect(Object.keys(Camera.fields).length).toBe(21);
368
+ describe('Camera schema (29 fields: projection, exposure, color grading, post-process, target, clearColor, and aspect-sync)', () => {
369
+ it('Camera.schema has 29 fields including auto exposure and color grading state', () => {
370
+ expect(Object.keys(Camera.fields).length).toBe(29);
371
371
  expect(componentFieldType(Camera, 'fov')).toBe('f32');
372
372
  expect(componentFieldType(Camera, 'aspect')).toBe('f32');
373
373
  expect(componentFieldType(Camera, 'near')).toBe('f32');
@@ -380,7 +380,16 @@ type __MergedKeep =
380
380
  // feat-20260519-tonemap-reinhard-mvp / M1 / T-M1.2 (AC-01 + D-1).
381
381
  expect(componentFieldType(Camera, 'tonemap')).toBe('f32');
382
382
  expect(componentFieldType(Camera, 'exposure')).toBe('f32');
383
+ // feat-20260827-auto-exposure-hdr-color-grading: exposure control state.
384
+ expect(componentFieldType(Camera, 'exposureMode')).toBe('f32');
385
+ expect(componentFieldType(Camera, 'compensationEv')).toBe('f32');
386
+ expect(componentFieldType(Camera, 'rangeEv')).toBe('array<f32, 2>');
387
+ expect(componentFieldType(Camera, 'rates')).toBe('array<f32, 2>');
383
388
  expect(componentFieldType(Camera, 'whitePoint')).toBe('f32');
389
+ expect(componentFieldType(Camera, 'temperature')).toBe('f32');
390
+ expect(componentFieldType(Camera, 'tint')).toBe('f32');
391
+ expect(componentFieldType(Camera, 'colorLut')).toBe('shared<TextureAsset>');
392
+ expect(componentFieldType(Camera, 'colorLutStrength')).toBe('f32');
384
393
  expect(componentFieldType(Camera, 'antialias')).toBe('f32');
385
394
  expect(componentFieldType(Camera, 'historyVersion')).toBe('u32');
386
395
  // feat-20260531-bloom-first-declarative-render-graph-pass / w2.