@forgeax/engine-runtime 0.1.27 → 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.
- package/README.md +98 -0
- package/dist/collect-scene-asset.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +480 -15
- package/dist/index.mjs.map +1 -1
- package/dist/scene-utils/mount-override-fold.d.ts +3 -1
- package/dist/scene-utils/mount-override-fold.d.ts.map +1 -1
- package/package.json +37 -30
- package/src/__tests__/animation-graph-collect-roundtrip.test.ts +4 -2
- package/src/__tests__/asset-registry-aabb.cases.unit.test.ts +5 -4
- package/src/__tests__/asset-registry-guid-reverse.test.ts +22 -27
- package/src/__tests__/asset-registry-mounts.test.ts +174 -734
- package/src/__tests__/asset-registry-scene.cases.unit.test.ts +9 -18
- package/src/__tests__/asset-registry.recursive.spec.ts +23 -29
- package/src/__tests__/camera-ortho.cases.unit.test.ts +12 -3
- package/src/__tests__/collect-scene-asset.test.ts +96 -113
- package/src/__tests__/collect-scene-mount-collapse.test.ts +64 -66
- package/src/__tests__/collect-transient-children-absent.test.ts +11 -17
- package/src/__tests__/collect-transient-roundtrip.test.ts +12 -13
- package/src/__tests__/collect-transient-skip.test.ts +20 -27
- package/src/__tests__/components.test-d.ts +17 -1
- package/src/__tests__/components.unit.test.ts +75 -27
- package/src/__tests__/composite-skybox-cross-world.browser.test.ts +4 -2
- package/src/__tests__/dawn/instances-per-instance-pbr.dawn.test.ts +8 -4
- package/src/__tests__/dawn/instances-shadow.dawn.test.ts +4 -1
- package/src/__tests__/dawn/instances-uniform-fallback.dawn.test.ts +66 -92
- package/src/__tests__/dawn/material-cooked-fixture.dawn.test.ts +200 -4
- package/src/__tests__/errors.unit.test.ts +13 -0
- package/src/__tests__/feature-depth-material.dawn.test.ts +23 -0
- package/src/__tests__/feature-depth-material.fixture.ts +473 -0
- package/src/__tests__/fullscreen-post-process-pass.dawn.test.ts +14 -14
- package/src/__tests__/geometry.unit.test.ts +42 -43
- package/src/__tests__/gpu-resource-release.cases.unit.test.ts +16 -9
- package/src/__tests__/handle-migration.test.ts +0 -1
- package/src/__tests__/instances-renderer-lifecycle.integration.test.ts +20 -14
- package/src/__tests__/instances.test-d.ts +13 -52
- package/src/__tests__/instantiate-mount-deferred-wiring.test.ts +42 -80
- package/src/__tests__/lazy-catalog.cases.unit.test.ts +17 -3
- package/src/__tests__/loader-registry.cases.unit.test.ts +4 -4
- package/src/__tests__/material-publication.browser.test.ts +29 -0
- package/src/__tests__/material-publication.commands.ts +23 -0
- package/src/__tests__/material-publication.dawn.test.ts +24 -0
- package/src/__tests__/material-publication.fixture.ts +349 -0
- package/src/__tests__/material-publication.server.ts +109 -0
- package/src/__tests__/parse-scene-payload-refs.cases.unit.test.ts +73 -96
- package/src/__tests__/pbr-pipeline.unit.test.ts +25 -11
- package/src/__tests__/point-light-shadow.browser.test.ts +8 -2
- package/src/__tests__/point-light-shadow.unit.test.ts +37 -8
- package/src/__tests__/record-all-topology.cases.unit.test.ts +52 -0
- package/src/__tests__/record-draw-branch.cases.unit.test.ts +1 -0
- package/src/__tests__/record-worldid-isolation.test.ts +47 -28
- package/src/__tests__/render-error-exhaustive.test-d.ts +24 -0
- package/src/__tests__/render-system-extract-dispatch-ordering.test.ts +15 -4
- package/src/__tests__/render-system-extract.test.ts +61 -0
- package/src/__tests__/render-system-extract.unit.test.ts +21 -7
- package/src/__tests__/render-system-fold-bucket.unit.test.ts +33 -1
- package/src/__tests__/render-system-fold-material.unit.test.ts +32 -6
- package/src/__tests__/render-system-fold-modegate.unit.test.ts +26 -4
- package/src/__tests__/render-system-fold.dawn.test.ts +4 -1
- package/src/__tests__/render-system-mega.test.ts +50 -62
- package/src/__tests__/renderer-device-loss-provider-feasibility.dawn.test.ts +0 -1
- package/src/__tests__/renderer-device-loss-recovery.dawn.test.ts +0 -3
- package/src/__tests__/renderer-draw-world.cases.unit.test.ts +16 -513
- package/src/__tests__/resolve-scene-guids.cases.unit.test.ts +48 -43
- package/src/__tests__/rhi-null-command-flow.unit.test.ts +9 -7
- package/src/__tests__/roots-to-scene-asset.test.ts +27 -28
- package/src/__tests__/roundtrip-primitive.test.ts +24 -23
- package/src/__tests__/scene-equirect-roundtrip.test.ts +5 -16
- package/src/__tests__/scene-instantiate-cycle-runtime.test.ts +13 -29
- package/src/__tests__/scene-mount-fixed-point.test.ts +104 -227
- package/src/__tests__/scene-mount-roundtrip.test.ts +95 -961
- package/src/__tests__/scene-mount-wrapper-roundtrip.test.ts +36 -182
- package/src/__tests__/scene-sugar-instantiate.test.ts +20 -15
- package/src/__tests__/serialize-mounts-roundtrip.test.ts +108 -492
- package/src/__tests__/serialize-scene-asset.test.ts +36 -50
- package/src/__tests__/shadow-caster-empty-schema.test.ts +3 -1
- package/src/__tests__/shadow-fields-observable.dawn.test.ts +34 -19
- package/src/__tests__/shadow-mesh-ssbo-order.test.ts +5 -12
- package/src/__tests__/skin-extract-getarrayview-count.unit.test.ts +6 -4
- package/src/__tests__/skin-m2-extract-happy.cases.unit.test.ts +50 -2
- package/src/__tests__/skin-m2-extract-joint-error.cases.unit.test.ts +50 -2
- package/src/__tests__/skin-m2-extract-null-assets.cases.unit.test.ts +50 -2
- package/src/__tests__/skin-m2-extract-skeleton-error.cases.unit.test.ts +50 -2
- package/src/__tests__/skin-palette-extract.cases.unit.test.ts +2 -0
- package/src/__tests__/skinned-shadow-mixed.dawn.test.ts +93 -48
- package/src/__tests__/skylight-pipeline-layout.cases.unit.test.ts +6 -2
- package/src/__tests__/sparse-tag-scene-roundtrip.test.ts +1 -1
- package/src/__tests__/sprite-lit-bgl-byte-identical.test.ts +11 -3
- package/src/__tests__/template-game-default-api-contract.test.ts +21 -21
- package/src/__tests__/tonemap-hdr-target.cases.unit.test.ts +8 -5
- package/src/__tests__/tonemap-pipeline-split.cases.unit.test.ts +3 -0
- package/src/__tests__/transient-fields-collect.test.ts +4 -4
- package/src/__tests__/transient-runtime-behavior.test.ts +9 -9
- package/src/__tests__/vfx-depth-material.dawn.test.ts +42 -0
- package/src/__tests__/vfx-mesh-lighting.browser.test.ts +16 -0
- package/src/__tests__/vfx-mesh-lighting.commands.ts +14 -0
- package/src/__tests__/vfx-mesh-lighting.dawn.test.ts +16 -0
- package/src/__tests__/vfx-mesh-lighting.fixture.ts +883 -0
- package/src/__tests__/wave1-dynamic-geometry.browser.test.ts +326 -0
- package/src/__tests__/wave1-rendering-materials.browser.test.ts +617 -0
- package/src/__tests__/wave1-rendering-p0.browser.test.ts +751 -0
- package/src/__tests__/wave1-rendering-recovery.browser.test.ts +666 -0
- package/src/__tests__/wave1-shadow-diagnostic.browser.test.ts +582 -0
- package/src/__tests__/world-scene-despawn.test.ts +8 -8
- package/src/__tests__/world-scene-instantiate.test.ts +23 -29
- package/src/__tests__/world-scene-nested.test.ts +90 -163
- package/src/__tests__/world-scene-override.test.ts +17 -16
- package/src/__tests__/zero-compression-zero-load.integration.test.ts +1 -0
- package/src/collect-scene-asset.ts +637 -19
- package/src/components/__tests__/sprite-animation.test-d.ts +2 -2
- package/src/index.ts +1 -0
- package/src/scene-utils/mount-override-fold.ts +5 -14
- package/src/__tests__/node-shims.d.ts +0 -31
|
@@ -417,19 +417,16 @@ type __MergedKeep =
|
|
|
417
417
|
return parsed.value;
|
|
418
418
|
}
|
|
419
419
|
|
|
420
|
-
|
|
421
|
-
return n as LocalEntityId;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
/** Build a minimal SceneAsset suitable for _resolveSceneGuids testing. */
|
|
420
|
+
/** Build a minimal keyed SceneAsset suitable for _resolveSceneGuids testing. */
|
|
425
421
|
function buildTestAsset(
|
|
426
422
|
nodes: Array<{ localId: number; components: Record<string, Record<string, unknown>> }>,
|
|
427
423
|
): SceneAsset {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
424
|
+
return {
|
|
425
|
+
kind: 'scene',
|
|
426
|
+
entities: Object.fromEntries(
|
|
427
|
+
nodes.map((n) => [`entity-${n.localId}`, { components: n.components }]),
|
|
428
|
+
),
|
|
429
|
+
};
|
|
433
430
|
}
|
|
434
431
|
|
|
435
432
|
describe('w4 - _resolveSceneGuids success path', () => {
|
|
@@ -503,9 +500,9 @@ type __MergedKeep =
|
|
|
503
500
|
if (!result.ok) return;
|
|
504
501
|
const resolvedAsset = result.value;
|
|
505
502
|
expect(resolvedAsset.kind).toBe('scene');
|
|
506
|
-
expect(resolvedAsset.entities.length).toBe(1);
|
|
503
|
+
expect(Object.keys(resolvedAsset.entities).length).toBe(1);
|
|
507
504
|
|
|
508
|
-
const resolvedComp = resolvedAsset.entities[0]?.components as Record<
|
|
505
|
+
const resolvedComp = resolvedAsset.entities['entity-0']?.components as Record<
|
|
509
506
|
string,
|
|
510
507
|
Record<string, unknown>
|
|
511
508
|
>;
|
|
@@ -555,7 +552,7 @@ type __MergedKeep =
|
|
|
555
552
|
expect(result.ok).toBe(true);
|
|
556
553
|
if (!result.ok) return;
|
|
557
554
|
const resolvedAsset = result.value;
|
|
558
|
-
const resolvedComp = resolvedAsset.entities[0]?.components as Record<
|
|
555
|
+
const resolvedComp = resolvedAsset.entities['entity-0']?.components as Record<
|
|
559
556
|
string,
|
|
560
557
|
Record<string, unknown>
|
|
561
558
|
>;
|
|
@@ -618,10 +615,10 @@ type __MergedKeep =
|
|
|
618
615
|
if (!result.ok) return;
|
|
619
616
|
const resolvedAsset = result.value;
|
|
620
617
|
const comp0 = (
|
|
621
|
-
resolvedAsset.entities[0]?.components as Record<string, Record<string, unknown>>
|
|
618
|
+
resolvedAsset.entities['entity-0']?.components as Record<string, Record<string, unknown>>
|
|
622
619
|
).MeshFilter;
|
|
623
620
|
const comp1 = (
|
|
624
|
-
resolvedAsset.entities[1]?.components as Record<string, Record<string, unknown>>
|
|
621
|
+
resolvedAsset.entities['entity-1']?.components as Record<string, Record<string, unknown>>
|
|
625
622
|
).MeshFilter;
|
|
626
623
|
expect(comp0?.assetHandle).toBe(comp1?.assetHandle);
|
|
627
624
|
expect(comp0?.assetHandle).toBeTypeOf('number');
|
|
@@ -648,7 +645,7 @@ type __MergedKeep =
|
|
|
648
645
|
if (!result.ok) return;
|
|
649
646
|
const resolvedAsset = result.value;
|
|
650
647
|
const comp = (
|
|
651
|
-
resolvedAsset.entities[0]?.components as Record<string, Record<string, unknown>>
|
|
648
|
+
resolvedAsset.entities['entity-0']?.components as Record<string, Record<string, unknown>>
|
|
652
649
|
).Transform;
|
|
653
650
|
expect(comp).toEqual({ pos: [1, 2, 3] });
|
|
654
651
|
});
|
|
@@ -686,7 +683,7 @@ type __MergedKeep =
|
|
|
686
683
|
expect(e.code).toBe('asset-not-found');
|
|
687
684
|
// Hint must contain the GUID, localId, and field for AI-user debuggability (AC-04)
|
|
688
685
|
expect(e.hint).toContain(UNREGISTERED_GUID_STR);
|
|
689
|
-
expect(e.hint).toContain('0'); //
|
|
686
|
+
expect(e.hint).toContain('entity-0'); // keyed entity address
|
|
690
687
|
expect(e.hint).toContain('assetHandle');
|
|
691
688
|
});
|
|
692
689
|
|
|
@@ -749,7 +746,7 @@ type __MergedKeep =
|
|
|
749
746
|
expect(e.code).toBe('asset-not-found');
|
|
750
747
|
// Hint should reference the first failing node (localId=1), not the second
|
|
751
748
|
expect(e.hint).toContain(UNREGISTERED_GUID_STR);
|
|
752
|
-
expect(e.hint).toContain('1'); //
|
|
749
|
+
expect(e.hint).toContain('entity-1'); // keyed entity address of the first failing node
|
|
753
750
|
// Must NOT contain the second GUID (stopped before reaching it)
|
|
754
751
|
expect(e.hint).not.toContain(SECOND_UNREGISTERED.slice(0, 8)); // prefix match sufficient
|
|
755
752
|
});
|
|
@@ -820,7 +817,7 @@ type __MergedKeep =
|
|
|
820
817
|
{
|
|
821
818
|
guid: MESH_GUID_STR,
|
|
822
819
|
sourceField: { componentName: 'MeshFilter', fieldName: 'assetHandle' },
|
|
823
|
-
|
|
820
|
+
sceneEntityKey: 'entity-0',
|
|
824
821
|
},
|
|
825
822
|
{
|
|
826
823
|
guid: MATERIAL_GUID_STR,
|
|
@@ -829,7 +826,7 @@ type __MergedKeep =
|
|
|
829
826
|
fieldName: 'materials',
|
|
830
827
|
arrayIndex: 0,
|
|
831
828
|
},
|
|
832
|
-
|
|
829
|
+
sceneEntityKey: 'entity-0',
|
|
833
830
|
},
|
|
834
831
|
{
|
|
835
832
|
guid: MATERIAL2_GUID_STR,
|
|
@@ -838,7 +835,7 @@ type __MergedKeep =
|
|
|
838
835
|
fieldName: 'materials',
|
|
839
836
|
arrayIndex: 1,
|
|
840
837
|
},
|
|
841
|
-
|
|
838
|
+
sceneEntityKey: 'entity-0',
|
|
842
839
|
},
|
|
843
840
|
{
|
|
844
841
|
guid: MATERIAL3_GUID_STR,
|
|
@@ -847,14 +844,18 @@ type __MergedKeep =
|
|
|
847
844
|
fieldName: 'materials',
|
|
848
845
|
arrayIndex: 2,
|
|
849
846
|
},
|
|
850
|
-
|
|
847
|
+
sceneEntityKey: 'entity-0',
|
|
851
848
|
},
|
|
852
849
|
];
|
|
853
850
|
reg.catalog(sceneGuid, sceneAsset, refs);
|
|
854
851
|
|
|
855
852
|
const world = new World();
|
|
856
|
-
|
|
857
|
-
|
|
853
|
+
world.components
|
|
854
|
+
.register(defineComponent('MeshFilter', { assetHandle: 'shared<MeshAsset>' }))
|
|
855
|
+
.unwrap();
|
|
856
|
+
world.components
|
|
857
|
+
.register(defineComponent('MeshRenderer', { materials: 'array<shared<MaterialAsset>>' }))
|
|
858
|
+
.unwrap();
|
|
858
859
|
|
|
859
860
|
// biome-ignore lint/suspicious/noExplicitAny: private helper access
|
|
860
861
|
const internal = reg as any as {
|
|
@@ -870,9 +871,9 @@ type __MergedKeep =
|
|
|
870
871
|
if (!result.ok) return;
|
|
871
872
|
const resolvedAsset = result.value;
|
|
872
873
|
expect(resolvedAsset.kind).toBe('scene');
|
|
873
|
-
expect(resolvedAsset.entities.length).toBe(1);
|
|
874
|
+
expect(Object.keys(resolvedAsset.entities).length).toBe(1);
|
|
874
875
|
|
|
875
|
-
const resolvedComp = resolvedAsset.entities[0]?.components as Record<
|
|
876
|
+
const resolvedComp = resolvedAsset.entities['entity-0']?.components as Record<
|
|
876
877
|
string,
|
|
877
878
|
Record<string, unknown>
|
|
878
879
|
>;
|
|
@@ -918,7 +919,7 @@ type __MergedKeep =
|
|
|
918
919
|
fieldName: 'materials',
|
|
919
920
|
arrayIndex: 0,
|
|
920
921
|
},
|
|
921
|
-
|
|
922
|
+
sceneEntityKey: 'entity-0',
|
|
922
923
|
},
|
|
923
924
|
{
|
|
924
925
|
guid: MATERIAL2_GUID_STR,
|
|
@@ -927,7 +928,7 @@ type __MergedKeep =
|
|
|
927
928
|
fieldName: 'materials',
|
|
928
929
|
arrayIndex: 1,
|
|
929
930
|
},
|
|
930
|
-
|
|
931
|
+
sceneEntityKey: 'entity-0',
|
|
931
932
|
},
|
|
932
933
|
{
|
|
933
934
|
guid: MATERIAL3_GUID_STR,
|
|
@@ -936,13 +937,15 @@ type __MergedKeep =
|
|
|
936
937
|
fieldName: 'materials',
|
|
937
938
|
arrayIndex: 2,
|
|
938
939
|
},
|
|
939
|
-
|
|
940
|
+
sceneEntityKey: 'entity-0',
|
|
940
941
|
},
|
|
941
942
|
];
|
|
942
943
|
reg.catalog(sceneGuid, sceneAsset, refs);
|
|
943
944
|
|
|
944
945
|
const world = new World();
|
|
945
|
-
|
|
946
|
+
world.components
|
|
947
|
+
.register(defineComponent('MeshRenderer', { materials: 'array<shared<MaterialAsset>>' }))
|
|
948
|
+
.unwrap();
|
|
946
949
|
|
|
947
950
|
// biome-ignore lint/suspicious/noExplicitAny: private helper access
|
|
948
951
|
const internal = reg as any as {
|
|
@@ -956,7 +959,7 @@ type __MergedKeep =
|
|
|
956
959
|
|
|
957
960
|
expect(result.ok).toBe(true);
|
|
958
961
|
if (!result.ok) return;
|
|
959
|
-
const resolvedComp = result.value.entities[0]?.components as Record<
|
|
962
|
+
const resolvedComp = result.value.entities['entity-0']?.components as Record<
|
|
960
963
|
string,
|
|
961
964
|
Record<string, unknown>
|
|
962
965
|
>;
|
|
@@ -994,18 +997,20 @@ type __MergedKeep =
|
|
|
994
997
|
{
|
|
995
998
|
guid: MESH_GUID_STR,
|
|
996
999
|
sourceField: { componentName: 'MeshFilter', fieldName: 'assetHandle' },
|
|
997
|
-
|
|
1000
|
+
sceneEntityKey: 'entity-0',
|
|
998
1001
|
},
|
|
999
1002
|
{
|
|
1000
1003
|
guid: MESH_GUID_STR,
|
|
1001
1004
|
sourceField: { componentName: 'MeshFilter', fieldName: 'assetHandle' },
|
|
1002
|
-
|
|
1005
|
+
sceneEntityKey: 'entity-1',
|
|
1003
1006
|
},
|
|
1004
1007
|
];
|
|
1005
1008
|
reg.catalog(sceneGuid, sceneAsset, refs);
|
|
1006
1009
|
|
|
1007
1010
|
const world = new World();
|
|
1008
|
-
|
|
1011
|
+
world.components
|
|
1012
|
+
.register(defineComponent('MeshFilter', { assetHandle: 'shared<MeshAsset>' }))
|
|
1013
|
+
.unwrap();
|
|
1009
1014
|
|
|
1010
1015
|
// biome-ignore lint/suspicious/noExplicitAny: private helper access
|
|
1011
1016
|
const internal = reg as any as {
|
|
@@ -1020,17 +1025,17 @@ type __MergedKeep =
|
|
|
1020
1025
|
expect(result.ok).toBe(true);
|
|
1021
1026
|
if (!result.ok) return;
|
|
1022
1027
|
const comp0 = (
|
|
1023
|
-
result.value.entities[0]?.components as Record<string, Record<string, unknown>>
|
|
1028
|
+
result.value.entities['entity-0']?.components as Record<string, Record<string, unknown>>
|
|
1024
1029
|
).MeshFilter;
|
|
1025
1030
|
const comp1 = (
|
|
1026
|
-
result.value.entities[1]?.components as Record<string, Record<string, unknown>>
|
|
1031
|
+
result.value.entities['entity-1']?.components as Record<string, Record<string, unknown>>
|
|
1027
1032
|
).MeshFilter;
|
|
1028
1033
|
// Same GUID → same handle (one allocSharedRef per unique payload)
|
|
1029
1034
|
expect(comp0?.assetHandle).toBe(comp1?.assetHandle);
|
|
1030
1035
|
expect(comp0?.assetHandle).toBeTypeOf('number');
|
|
1031
1036
|
});
|
|
1032
1037
|
|
|
1033
|
-
it('(d) breadcrumb from envelope.refs: buildSceneChildContext returns correct
|
|
1038
|
+
it('(d) breadcrumb from envelope.refs: buildSceneChildContext returns correct sceneEntityKey + componentField', () => {
|
|
1034
1039
|
const reg = new AssetRegistry(makeMockShaderRegistry());
|
|
1035
1040
|
|
|
1036
1041
|
registerAsset(reg, MESH_GUID_STR, makeTestMesh());
|
|
@@ -1048,7 +1053,7 @@ type __MergedKeep =
|
|
|
1048
1053
|
{
|
|
1049
1054
|
guid: MESH_GUID_STR,
|
|
1050
1055
|
sourceField: { componentName: 'MeshFilter', fieldName: 'assetHandle' },
|
|
1051
|
-
|
|
1056
|
+
sceneEntityKey: 'entity-5',
|
|
1052
1057
|
},
|
|
1053
1058
|
];
|
|
1054
1059
|
reg.catalog(sceneGuid, sceneAsset, refs);
|
|
@@ -1056,7 +1061,7 @@ type __MergedKeep =
|
|
|
1056
1061
|
const ctx = buildSceneChildContext(reg, sceneAsset, MESH_GUID_STR.toLowerCase());
|
|
1057
1062
|
|
|
1058
1063
|
expect(ctx).toBeDefined();
|
|
1059
|
-
expect(ctx?.
|
|
1064
|
+
expect(ctx?.sceneEntityKey).toBe('entity-5');
|
|
1060
1065
|
expect(ctx?.componentField).toBe('MeshFilter.assetHandle');
|
|
1061
1066
|
});
|
|
1062
1067
|
|
|
@@ -1093,17 +1098,17 @@ type __MergedKeep =
|
|
|
1093
1098
|
fieldName: 'materials',
|
|
1094
1099
|
arrayIndex: 0,
|
|
1095
1100
|
},
|
|
1096
|
-
|
|
1101
|
+
sceneEntityKey: 'entity-0',
|
|
1097
1102
|
},
|
|
1098
1103
|
{
|
|
1099
1104
|
guid: TEXTURE_GUID_STR,
|
|
1100
1105
|
// sourceField intentionally omitted — texture edge, D-2
|
|
1101
|
-
|
|
1106
|
+
sceneEntityKey: undefined,
|
|
1102
1107
|
},
|
|
1103
1108
|
];
|
|
1104
1109
|
reg.catalog(sceneGuid, sceneAsset, refs);
|
|
1105
1110
|
|
|
1106
|
-
// Look up the material GUID — should find
|
|
1111
|
+
// Look up the material GUID — should find sceneEntityKey + componentField
|
|
1107
1112
|
const materialCtx = buildSceneChildContext(
|
|
1108
1113
|
reg,
|
|
1109
1114
|
sceneAsset,
|
|
@@ -1115,7 +1120,7 @@ type __MergedKeep =
|
|
|
1115
1120
|
// Look up the texture GUID — sourceField=undefined → componentField undefined
|
|
1116
1121
|
const textureCtx = buildSceneChildContext(reg, sceneAsset, TEXTURE_GUID_STR.toLowerCase());
|
|
1117
1122
|
expect(textureCtx).toBeDefined();
|
|
1118
|
-
expect(textureCtx?.
|
|
1123
|
+
expect(textureCtx?.sceneEntityKey).toBeUndefined();
|
|
1119
1124
|
expect(textureCtx?.componentField).toBeUndefined();
|
|
1120
1125
|
});
|
|
1121
1126
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { World } from '@forgeax/engine-ecs';
|
|
2
|
-
import type { Renderer } from '@forgeax/engine-render';
|
|
2
|
+
import type { Renderer, RendererEvent } from '@forgeax/engine-render';
|
|
3
3
|
import { rhi } from '@forgeax/engine-rhi-null';
|
|
4
4
|
import { describe, expect, it } from 'vitest';
|
|
5
5
|
import { requireRenderer } from './renderer-test-utils';
|
|
@@ -17,11 +17,7 @@ function frameRequest(lease: Parameters<Renderer['draw']>[0]['leases'][number])
|
|
|
17
17
|
describe('RhiNull command flow', () => {
|
|
18
18
|
it('returns the synchronous receipt after the host submits a frame', async () => {
|
|
19
19
|
const renderer = await requireRenderer(canvas(), { rhi }, { shaderManifestUrl: manifest });
|
|
20
|
-
const events:
|
|
21
|
-
readonly kind: string;
|
|
22
|
-
readonly frameId?: number;
|
|
23
|
-
readonly deviceGeneration?: number;
|
|
24
|
-
}> = [];
|
|
20
|
+
const events: RendererEvent[] = [];
|
|
25
21
|
const unsubscribe = renderer.subscribe((event) => events.push(event));
|
|
26
22
|
const world = new World();
|
|
27
23
|
const attached = renderer.attach(world);
|
|
@@ -32,11 +28,17 @@ describe('RhiNull command flow', () => {
|
|
|
32
28
|
const frame = renderer.draw(frameRequest(attached.value));
|
|
33
29
|
expect(frame.ok).toBe(true);
|
|
34
30
|
if (!frame.ok) return;
|
|
35
|
-
expect(
|
|
31
|
+
expect(
|
|
32
|
+
events.filter(
|
|
33
|
+
(event): event is Extract<RendererEvent, { kind: 'frame-submitted' }> =>
|
|
34
|
+
event.kind === 'frame-submitted',
|
|
35
|
+
),
|
|
36
|
+
).toEqual([
|
|
36
37
|
{
|
|
37
38
|
kind: 'frame-submitted',
|
|
38
39
|
frameId: frame.value.frameId,
|
|
39
40
|
deviceGeneration: frame.value.deviceGeneration,
|
|
41
|
+
receipt: frame.value,
|
|
40
42
|
},
|
|
41
43
|
]);
|
|
42
44
|
const completed = await frame.value.completed;
|
|
@@ -106,7 +106,7 @@ describe('m2-t1: forest multi-root BFS closure + localId renumbering', () => {
|
|
|
106
106
|
]);
|
|
107
107
|
expect(result.ok).toBe(true);
|
|
108
108
|
if (!result.ok) return;
|
|
109
|
-
expect(result.value.entities).toHaveLength(6);
|
|
109
|
+
expect(Object.keys(result.value.entities)).toHaveLength(6);
|
|
110
110
|
});
|
|
111
111
|
|
|
112
112
|
it('ancestor-descendant overlapping roots silently de-duplicate', () => {
|
|
@@ -123,7 +123,7 @@ describe('m2-t1: forest multi-root BFS closure + localId renumbering', () => {
|
|
|
123
123
|
const result = rootsToSceneAsset(reg, world, [e0 as EntityHandle, e1 as EntityHandle]);
|
|
124
124
|
expect(result.ok).toBe(true);
|
|
125
125
|
if (!result.ok) return;
|
|
126
|
-
expect(result.value.entities).toHaveLength(4);
|
|
126
|
+
expect(Object.keys(result.value.entities)).toHaveLength(4);
|
|
127
127
|
});
|
|
128
128
|
|
|
129
129
|
it('localIds are 0..N-1 continuous in BFS order', () => {
|
|
@@ -142,10 +142,10 @@ describe('m2-t1: forest multi-root BFS closure + localId renumbering', () => {
|
|
|
142
142
|
expect(result.ok).toBe(true);
|
|
143
143
|
if (!result.ok) return;
|
|
144
144
|
const scene = result.value;
|
|
145
|
-
expect(scene.entities).toHaveLength(5);
|
|
145
|
+
expect(Object.keys(scene.entities)).toHaveLength(5);
|
|
146
146
|
|
|
147
|
-
const
|
|
148
|
-
expect(
|
|
147
|
+
const keys = Object.keys(scene.entities);
|
|
148
|
+
expect(keys).toEqual(['entity-0', 'entity-1', 'entity-2', 'entity-3', 'entity-4']);
|
|
149
149
|
});
|
|
150
150
|
|
|
151
151
|
it('empty roots produces empty SceneAsset (no error)', () => {
|
|
@@ -154,7 +154,7 @@ describe('m2-t1: forest multi-root BFS closure + localId renumbering', () => {
|
|
|
154
154
|
const result = rootsToSceneAsset(reg, world, []);
|
|
155
155
|
expect(result.ok).toBe(true);
|
|
156
156
|
if (!result.ok) return;
|
|
157
|
-
expect(result.value.entities).toHaveLength(0);
|
|
157
|
+
expect(Object.keys(result.value.entities)).toHaveLength(0);
|
|
158
158
|
});
|
|
159
159
|
});
|
|
160
160
|
|
|
@@ -175,15 +175,14 @@ describe('m2-t2: entity / array<entity> -> localId', () => {
|
|
|
175
175
|
expect(result.ok).toBe(true);
|
|
176
176
|
if (!result.ok) return;
|
|
177
177
|
const scene = result.value;
|
|
178
|
-
expect(scene.entities).toHaveLength(2);
|
|
178
|
+
expect(Object.keys(scene.entities)).toHaveLength(2);
|
|
179
179
|
|
|
180
|
-
const e1 = scene.entities[1];
|
|
180
|
+
const e1 = scene.entities['entity-1'];
|
|
181
181
|
if (!e1) throw new Error('entity 1 missing');
|
|
182
182
|
const refComps = (e1.components as Record<string, Record<string, unknown>>).Test_EntityRef;
|
|
183
183
|
expect(refComps).toBeDefined();
|
|
184
184
|
if (!refComps) throw new Error('Test_EntityRef missing');
|
|
185
|
-
expect(refComps.target).
|
|
186
|
-
expect(refComps.target).toBe(0);
|
|
185
|
+
expect(refComps.target).toBe('entity-0');
|
|
187
186
|
});
|
|
188
187
|
|
|
189
188
|
it('array<entity> field round-trips each element as localId', () => {
|
|
@@ -205,9 +204,9 @@ describe('m2-t2: entity / array<entity> -> localId', () => {
|
|
|
205
204
|
expect(result.ok).toBe(true);
|
|
206
205
|
if (!result.ok) return;
|
|
207
206
|
const scene = result.value;
|
|
208
|
-
expect(scene.entities).toHaveLength(3);
|
|
207
|
+
expect(Object.keys(scene.entities)).toHaveLength(3);
|
|
209
208
|
|
|
210
|
-
const rootEnt = scene.entities[0];
|
|
209
|
+
const rootEnt = scene.entities['entity-0'];
|
|
211
210
|
if (!rootEnt) throw new Error('entity 0 missing');
|
|
212
211
|
const elComps = (rootEnt.components as Record<string, Record<string, unknown>>)
|
|
213
212
|
.Test_EntityArray;
|
|
@@ -215,7 +214,7 @@ describe('m2-t2: entity / array<entity> -> localId', () => {
|
|
|
215
214
|
if (!elComps) throw new Error('Test_EntityArray missing');
|
|
216
215
|
const targetsArr = elComps.targets as unknown[];
|
|
217
216
|
expect(Array.isArray(targetsArr)).toBe(true);
|
|
218
|
-
expect(targetsArr).toEqual([1, 2]);
|
|
217
|
+
expect(targetsArr).toEqual(['entity-1', 'entity-2']);
|
|
219
218
|
});
|
|
220
219
|
});
|
|
221
220
|
|
|
@@ -248,7 +247,7 @@ describe('m2-t3: shared<> / array<shared<>> -> GUID', () => {
|
|
|
248
247
|
expect(result.ok).toBe(true);
|
|
249
248
|
if (!result.ok) return;
|
|
250
249
|
const scene = result.value;
|
|
251
|
-
const ent0 = scene.entities[0];
|
|
250
|
+
const ent0 = scene.entities['entity-0'];
|
|
252
251
|
if (!ent0) throw new Error('entity 0 missing');
|
|
253
252
|
const comps = (ent0.components as Record<string, Record<string, unknown>>).Test_HasShared;
|
|
254
253
|
expect(comps).toBeDefined();
|
|
@@ -286,7 +285,7 @@ describe('m2-t3: shared<> / array<shared<>> -> GUID', () => {
|
|
|
286
285
|
expect(result.ok).toBe(true);
|
|
287
286
|
if (!result.ok) return;
|
|
288
287
|
const scene = result.value;
|
|
289
|
-
const ent0 = scene.entities[0];
|
|
288
|
+
const ent0 = scene.entities['entity-0'];
|
|
290
289
|
if (!ent0) throw new Error('entity 0 missing');
|
|
291
290
|
const comps = (ent0.components as Record<string, Record<string, unknown>>).Test_HasSharedArray;
|
|
292
291
|
expect(comps).toBeDefined();
|
|
@@ -323,7 +322,7 @@ describe('m2-t3: shared<> / array<shared<>> -> GUID', () => {
|
|
|
323
322
|
const result = rootsToSceneAsset(reg, world, [r0 as EntityHandle]);
|
|
324
323
|
expect(result.ok).toBe(true);
|
|
325
324
|
if (!result.ok) return;
|
|
326
|
-
const ent0 = result.value.entities[0];
|
|
325
|
+
const ent0 = result.value.entities['entity-0'];
|
|
327
326
|
if (!ent0) throw new Error('entity 0 missing');
|
|
328
327
|
const comps = (ent0.components as Record<string, Record<string, unknown>>).Test_EmptyMaterials;
|
|
329
328
|
// An empty shared array serializes to [] (or the component is omitted when it
|
|
@@ -390,7 +389,7 @@ describe('m2-t3: shared<> / array<shared<>> -> GUID', () => {
|
|
|
390
389
|
const result = rootsToSceneAsset(reg, world, [r0 as EntityHandle]);
|
|
391
390
|
expect(result.ok).toBe(true);
|
|
392
391
|
if (!result.ok) return;
|
|
393
|
-
const ent0 = result.value.entities[0];
|
|
392
|
+
const ent0 = result.value.entities['entity-0'];
|
|
394
393
|
if (!ent0) throw new Error('entity 0 missing');
|
|
395
394
|
const comps = (ent0.components as Record<string, Record<string, unknown>>)
|
|
396
395
|
.Test_UnsetScalarShared;
|
|
@@ -429,7 +428,7 @@ describe('m2-t3: shared<> / array<shared<>> -> GUID', () => {
|
|
|
429
428
|
const result = rootsToSceneAsset(reg, world, [r0 as EntityHandle]);
|
|
430
429
|
expect(result.ok).toBe(true);
|
|
431
430
|
if (!result.ok) return;
|
|
432
|
-
const ent0 = result.value.entities[0];
|
|
431
|
+
const ent0 = result.value.entities['entity-0'];
|
|
433
432
|
if (!ent0) throw new Error('entity 0 missing');
|
|
434
433
|
const comps = (ent0.components as Record<string, Record<string, unknown>>)
|
|
435
434
|
.Test_SparseSharedArray;
|
|
@@ -460,9 +459,9 @@ describe('m2-t4: root ChildOf strip + cross-root closure refs', () => {
|
|
|
460
459
|
expect(result.ok).toBe(true);
|
|
461
460
|
if (!result.ok) return;
|
|
462
461
|
const scene = result.value;
|
|
463
|
-
expect(scene.entities).toHaveLength(1);
|
|
462
|
+
expect(Object.keys(scene.entities)).toHaveLength(1);
|
|
464
463
|
|
|
465
|
-
const ent0 = scene.entities[0];
|
|
464
|
+
const ent0 = scene.entities['entity-0'];
|
|
466
465
|
if (!ent0) throw new Error('entity missing');
|
|
467
466
|
const comps = ent0.components as Record<string, Record<string, unknown>>;
|
|
468
467
|
expect(comps.ChildOf).toBeUndefined();
|
|
@@ -481,9 +480,9 @@ describe('m2-t4: root ChildOf strip + cross-root closure refs', () => {
|
|
|
481
480
|
expect(result.ok).toBe(true);
|
|
482
481
|
if (!result.ok) return;
|
|
483
482
|
const scene = result.value;
|
|
484
|
-
expect(scene.entities).toHaveLength(3);
|
|
483
|
+
expect(Object.keys(scene.entities)).toHaveLength(3);
|
|
485
484
|
|
|
486
|
-
const childEntity = scene.entities.find(
|
|
485
|
+
const childEntity = Object.values(scene.entities).find(
|
|
487
486
|
(e) =>
|
|
488
487
|
(e.components as Record<string, Record<string, unknown>>).Test_EntityRef?.target !==
|
|
489
488
|
undefined,
|
|
@@ -493,9 +492,8 @@ describe('m2-t4: root ChildOf strip + cross-root closure refs', () => {
|
|
|
493
492
|
const refVal = (childEntity.components as Record<string, Record<string, unknown>>)
|
|
494
493
|
.Test_EntityRef;
|
|
495
494
|
if (!refVal) throw new Error('Test_EntityRef missing');
|
|
496
|
-
expect(typeof refVal.target).toBe('
|
|
497
|
-
expect(refVal.target
|
|
498
|
-
expect(refVal.target as number).toBeLessThan(3);
|
|
495
|
+
expect(typeof refVal.target).toBe('string');
|
|
496
|
+
expect(refVal.target).toBe('entity-2');
|
|
499
497
|
});
|
|
500
498
|
});
|
|
501
499
|
|
|
@@ -639,13 +637,14 @@ describe('w19 — end-to-end round-trip value equivalence (AC-07)', () => {
|
|
|
639
637
|
// Child (GLB-like) scene + parent scene that mounts it.
|
|
640
638
|
const child: SceneAsset = {
|
|
641
639
|
kind: 'scene',
|
|
642
|
-
entities:
|
|
640
|
+
entities: { 'entity-0': { components: { Transform: { pos: [1, 0, 0] } } } },
|
|
643
641
|
};
|
|
644
642
|
reg.catalog(pgc(W19_CHILD), child as Asset);
|
|
645
643
|
const parent: SceneAsset = {
|
|
646
644
|
kind: 'scene',
|
|
647
|
-
entities:
|
|
648
|
-
|
|
645
|
+
entities: {
|
|
646
|
+
wrapper: { components: { Transform: { pos: [0, 0, 0] } }, instance: { source: W19_CHILD } },
|
|
647
|
+
},
|
|
649
648
|
};
|
|
650
649
|
reg.catalog(pgc(W19_PARENT), parent as Asset);
|
|
651
650
|
|
|
@@ -75,9 +75,9 @@ describe('m3-t3(a): individual primitive field round-trip', () => {
|
|
|
75
75
|
expect(collected.ok).toBe(true);
|
|
76
76
|
if (!collected.ok) return;
|
|
77
77
|
const scene = collected.value;
|
|
78
|
-
expect(scene.entities).toHaveLength(1);
|
|
78
|
+
expect(Object.keys(scene.entities)).toHaveLength(1);
|
|
79
79
|
|
|
80
|
-
const ent0 = scene.entities[0];
|
|
80
|
+
const ent0 = scene.entities['entity-0'];
|
|
81
81
|
if (!ent0) throw new Error('entity 0 missing');
|
|
82
82
|
const comps = ent0.components as Record<string, Record<string, unknown>>;
|
|
83
83
|
const cf = comps.Test_RT_F32;
|
|
@@ -98,9 +98,9 @@ describe('m3-t3(a): individual primitive field round-trip', () => {
|
|
|
98
98
|
expect(collected.ok).toBe(true);
|
|
99
99
|
if (!collected.ok) return;
|
|
100
100
|
const scene = collected.value;
|
|
101
|
-
expect(scene.entities).toHaveLength(1);
|
|
101
|
+
expect(Object.keys(scene.entities)).toHaveLength(1);
|
|
102
102
|
|
|
103
|
-
const ent0 = scene.entities[0];
|
|
103
|
+
const ent0 = scene.entities['entity-0'];
|
|
104
104
|
if (!ent0) throw new Error('entity 0 missing');
|
|
105
105
|
const comps = ent0.components as Record<string, Record<string, unknown>>;
|
|
106
106
|
const cf = comps.Test_RT_F64;
|
|
@@ -122,9 +122,9 @@ describe('m3-t3(a): individual primitive field round-trip', () => {
|
|
|
122
122
|
expect(collected.ok).toBe(true);
|
|
123
123
|
if (!collected.ok) return;
|
|
124
124
|
const scene = collected.value;
|
|
125
|
-
expect(scene.entities).toHaveLength(1);
|
|
125
|
+
expect(Object.keys(scene.entities)).toHaveLength(1);
|
|
126
126
|
|
|
127
|
-
const ent0 = scene.entities[0];
|
|
127
|
+
const ent0 = scene.entities['entity-0'];
|
|
128
128
|
if (!ent0) throw new Error('entity 0 missing');
|
|
129
129
|
const comps = ent0.components as Record<string, Record<string, unknown>>;
|
|
130
130
|
const cf = comps.Test_RT_I32;
|
|
@@ -145,9 +145,9 @@ describe('m3-t3(a): individual primitive field round-trip', () => {
|
|
|
145
145
|
expect(collected.ok).toBe(true);
|
|
146
146
|
if (!collected.ok) return;
|
|
147
147
|
const scene = collected.value;
|
|
148
|
-
expect(scene.entities).toHaveLength(1);
|
|
148
|
+
expect(Object.keys(scene.entities)).toHaveLength(1);
|
|
149
149
|
|
|
150
|
-
const ent0 = scene.entities[0];
|
|
150
|
+
const ent0 = scene.entities['entity-0'];
|
|
151
151
|
if (!ent0) throw new Error('entity 0 missing');
|
|
152
152
|
const comps = ent0.components as Record<string, Record<string, unknown>>;
|
|
153
153
|
const cf = comps.Test_RT_U32;
|
|
@@ -170,16 +170,18 @@ describe('m3-t3(a): individual primitive field round-trip', () => {
|
|
|
170
170
|
expect(collected.ok).toBe(true);
|
|
171
171
|
if (!collected.ok) return;
|
|
172
172
|
const scene = collected.value;
|
|
173
|
-
expect(scene.entities).toHaveLength(2);
|
|
173
|
+
expect(Object.keys(scene.entities)).toHaveLength(2);
|
|
174
174
|
|
|
175
|
-
const comps0 = (
|
|
176
|
-
.
|
|
175
|
+
const comps0 = (
|
|
176
|
+
scene.entities['entity-0']?.components as Record<string, Record<string, unknown>>
|
|
177
|
+
).Test_RT_BOOL;
|
|
177
178
|
expect(comps0).toBeDefined();
|
|
178
179
|
if (!comps0) throw new Error('Test_RT_BOOL missing on e0');
|
|
179
180
|
expect(comps0.flag).toBe(true);
|
|
180
181
|
|
|
181
|
-
const comps1 = (
|
|
182
|
-
.
|
|
182
|
+
const comps1 = (
|
|
183
|
+
scene.entities['entity-1']?.components as Record<string, Record<string, unknown>>
|
|
184
|
+
).Test_RT_BOOL;
|
|
183
185
|
expect(comps1).toBeDefined();
|
|
184
186
|
if (!comps1) throw new Error('Test_RT_BOOL missing on e1');
|
|
185
187
|
expect(comps1.flag).toBe(false);
|
|
@@ -197,9 +199,9 @@ describe('m3-t3(a): individual primitive field round-trip', () => {
|
|
|
197
199
|
expect(collected.ok).toBe(true);
|
|
198
200
|
if (!collected.ok) return;
|
|
199
201
|
const scene = collected.value;
|
|
200
|
-
expect(scene.entities).toHaveLength(1);
|
|
202
|
+
expect(Object.keys(scene.entities)).toHaveLength(1);
|
|
201
203
|
|
|
202
|
-
const ent0 = scene.entities[0];
|
|
204
|
+
const ent0 = scene.entities['entity-0'];
|
|
203
205
|
if (!ent0) throw new Error('entity 0 missing');
|
|
204
206
|
const comps = ent0.components as Record<string, Record<string, unknown>>;
|
|
205
207
|
const cf = comps.Test_RT_STR;
|
|
@@ -270,10 +272,10 @@ describe('m3-t3(b): mixed entity+shared+primitive round-trip', () => {
|
|
|
270
272
|
expect(collected.ok).toBe(true);
|
|
271
273
|
if (!collected.ok) return;
|
|
272
274
|
const scene = collected.value;
|
|
273
|
-
expect(scene.entities).toHaveLength(2);
|
|
275
|
+
expect(Object.keys(scene.entities)).toHaveLength(2);
|
|
274
276
|
|
|
275
277
|
// Entity 0 (r0): primitives
|
|
276
|
-
const ent0 = scene.entities[0];
|
|
278
|
+
const ent0 = scene.entities['entity-0'];
|
|
277
279
|
if (!ent0) throw new Error('e0 missing');
|
|
278
280
|
const mp = (ent0.components as Record<string, Record<string, unknown>>).Test_RT_MixedPrim;
|
|
279
281
|
expect(mp).toBeDefined();
|
|
@@ -286,14 +288,13 @@ describe('m3-t3(b): mixed entity+shared+primitive round-trip', () => {
|
|
|
286
288
|
expect(mp.name).toBe('mixed-entity');
|
|
287
289
|
|
|
288
290
|
// Entity 1 (r1): entity ref + shared ref
|
|
289
|
-
const ent1 = scene.entities[1];
|
|
291
|
+
const ent1 = scene.entities['entity-1'];
|
|
290
292
|
if (!ent1) throw new Error('e1 missing');
|
|
291
293
|
const er = (ent1.components as Record<string, Record<string, unknown>>).Test_EntityRef;
|
|
292
294
|
expect(er).toBeDefined();
|
|
293
295
|
if (!er) throw new Error('Test_EntityRef missing');
|
|
294
|
-
//
|
|
295
|
-
expect(
|
|
296
|
-
expect(er.target as number).toBeGreaterThanOrEqual(0);
|
|
296
|
+
// Entity references retain the keyed authoring identity.
|
|
297
|
+
expect(er.target).toBe('entity-0');
|
|
297
298
|
|
|
298
299
|
// shared<> field must resolve to GUID string.
|
|
299
300
|
const hs = (ent1.components as Record<string, Record<string, unknown>>).Test_HasShared;
|
|
@@ -326,9 +327,9 @@ describe('m3-t3(c): TypedArray->Array round-trip normalization', () => {
|
|
|
326
327
|
expect(collected.ok).toBe(true);
|
|
327
328
|
if (!collected.ok) return;
|
|
328
329
|
const scene = collected.value;
|
|
329
|
-
expect(scene.entities).toHaveLength(1);
|
|
330
|
+
expect(Object.keys(scene.entities)).toHaveLength(1);
|
|
330
331
|
|
|
331
|
-
const ent0 = scene.entities[0];
|
|
332
|
+
const ent0 = scene.entities['entity-0'];
|
|
332
333
|
if (!ent0) throw new Error('entity 0 missing');
|
|
333
334
|
const comps = ent0.components as Record<string, Record<string, unknown>>;
|
|
334
335
|
const cf = comps.Test_RT_ArrF32;
|
|
@@ -24,22 +24,12 @@ import { AssetRegistry } from '@forgeax/engine-assets-runtime';
|
|
|
24
24
|
import type { EntityHandle } from '@forgeax/engine-ecs';
|
|
25
25
|
import { World } from '@forgeax/engine-ecs';
|
|
26
26
|
import { Skylight } from '@forgeax/engine-render';
|
|
27
|
-
import type {
|
|
28
|
-
EquirectAsset,
|
|
29
|
-
Handle,
|
|
30
|
-
LocalEntityId,
|
|
31
|
-
SceneAsset,
|
|
32
|
-
SceneEntity,
|
|
33
|
-
} from '@forgeax/engine-types';
|
|
27
|
+
import type { EquirectAsset, Handle, SceneAsset } from '@forgeax/engine-types';
|
|
34
28
|
import { BUILTIN_BASE } from '@forgeax/engine-types';
|
|
35
29
|
import { describe, expect, it } from 'vitest';
|
|
36
30
|
import { makeMockShaderRegistry } from './helpers/mock-shader-registry';
|
|
37
31
|
import { registerSceneComponents } from './helpers/register-scene-components';
|
|
38
32
|
|
|
39
|
-
function localId(n: number): LocalEntityId {
|
|
40
|
-
return n as LocalEntityId;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
33
|
// A minimal valid EquirectAsset POD (kind:'equirect', single 2D rgba16float
|
|
44
34
|
// image). The roundtrip never decodes the bytes; a tiny tight-packed buffer is
|
|
45
35
|
// enough to catalogue + resolve the handle.
|
|
@@ -72,14 +62,13 @@ describe('scene equirect roundtrip + instantiate handle resolution (M3 / w15)',
|
|
|
72
62
|
// A scene whose single Skylight references the equirect source by GUID
|
|
73
63
|
// string — the post-parseScenePayload intermediate state where handle
|
|
74
64
|
// fields hold GUID strings awaiting resolution.
|
|
75
|
-
const entities
|
|
76
|
-
{
|
|
77
|
-
localId: localId(0),
|
|
65
|
+
const entities = {
|
|
66
|
+
'entity-0': {
|
|
78
67
|
components: {
|
|
79
68
|
Skylight: { equirect: EQUIRECT_GUID, intensity: 1.0 },
|
|
80
69
|
},
|
|
81
|
-
}
|
|
82
|
-
|
|
70
|
+
},
|
|
71
|
+
};
|
|
83
72
|
const scene: SceneAsset = { kind: 'scene', entities };
|
|
84
73
|
|
|
85
74
|
const sceneHandle = world.allocSharedRef('SceneAsset', scene);
|