@flighthq/scene3d-resources 0.3.0-next.906.07cea63 → 0.3.0
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/dist/anisotropyPbrScene3DMaterialTextures.d.ts +3 -0
- package/dist/anisotropyPbrScene3DMaterialTextures.d.ts.map +1 -0
- package/dist/anisotropyPbrScene3DMaterialTextures.js +10 -0
- package/dist/anisotropyPbrScene3DMaterialTextures.js.map +1 -0
- package/dist/awd2Load.d.ts.map +1 -1
- package/dist/awd2Load.js +3 -2
- package/dist/awd2Load.js.map +1 -1
- package/dist/clearcoatPbrScene3DMaterialTextures.d.ts +3 -0
- package/dist/clearcoatPbrScene3DMaterialTextures.d.ts.map +1 -0
- package/dist/clearcoatPbrScene3DMaterialTextures.js +14 -0
- package/dist/clearcoatPbrScene3DMaterialTextures.js.map +1 -0
- package/dist/contract.d.ts +10 -1
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +10 -1
- package/dist/contract.js.map +1 -1
- package/dist/enableScene3DResourceFailureGuards.d.ts.map +1 -1
- package/dist/enableScene3DResourceFailureGuards.js +2 -2
- package/dist/enableScene3DResourceFailureGuards.js.map +1 -1
- package/dist/explainScene3DResourceCoverage.d.ts +4 -0
- package/dist/explainScene3DResourceCoverage.d.ts.map +1 -0
- package/dist/explainScene3DResourceCoverage.js +42 -0
- package/dist/explainScene3DResourceCoverage.js.map +1 -0
- package/dist/getScene3DResourceTextures.d.ts +3 -2
- package/dist/getScene3DResourceTextures.d.ts.map +1 -1
- package/dist/getScene3DResourceTextures.js +23 -27
- package/dist/getScene3DResourceTextures.js.map +1 -1
- package/dist/imageResourceFetch.d.ts +2 -2
- package/dist/imageResourceFetch.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/iridescencePbrScene3DMaterialTextures.d.ts +3 -0
- package/dist/iridescencePbrScene3DMaterialTextures.d.ts.map +1 -0
- package/dist/iridescencePbrScene3DMaterialTextures.js +12 -0
- package/dist/iridescencePbrScene3DMaterialTextures.js.map +1 -0
- package/dist/loadScene3DResources.d.ts +4 -0
- package/dist/loadScene3DResources.d.ts.map +1 -0
- package/dist/{resolveScene3DResourcesAndWait.js → loadScene3DResources.js} +11 -21
- package/dist/loadScene3DResources.js.map +1 -0
- package/dist/resolveScene3DResources.d.ts +4 -3
- package/dist/resolveScene3DResources.d.ts.map +1 -1
- package/dist/resolveScene3DResources.js +49 -26
- package/dist/resolveScene3DResources.js.map +1 -1
- package/dist/revealScene3DResourcesOnResolve.d.ts +2 -2
- package/dist/revealScene3DResourcesOnResolve.d.ts.map +1 -1
- package/dist/revealScene3DResourcesOnResolve.js +9 -6
- package/dist/revealScene3DResourcesOnResolve.js.map +1 -1
- package/dist/sceneMaterialTextureRegistry.d.ts +6 -2
- package/dist/sceneMaterialTextureRegistry.d.ts.map +1 -1
- package/dist/sceneMaterialTextureRegistry.js +31 -8
- package/dist/sceneMaterialTextureRegistry.js.map +1 -1
- package/dist/sceneResourceRecovery.d.ts +2 -4
- package/dist/sceneResourceRecovery.d.ts.map +1 -1
- package/dist/sceneResourceRecovery.js +9 -28
- package/dist/sceneResourceRecovery.js.map +1 -1
- package/dist/sceneResourceResolver.d.ts.map +1 -1
- package/dist/sceneResourceResolver.js +6 -2
- package/dist/sceneResourceResolver.js.map +1 -1
- package/dist/shadedScene3DMaterialTextures.d.ts +3 -0
- package/dist/shadedScene3DMaterialTextures.d.ts.map +1 -0
- package/dist/shadedScene3DMaterialTextures.js +30 -0
- package/dist/shadedScene3DMaterialTextures.js.map +1 -0
- package/dist/sheenPbrScene3DMaterialTextures.d.ts +3 -0
- package/dist/sheenPbrScene3DMaterialTextures.d.ts.map +1 -0
- package/dist/sheenPbrScene3DMaterialTextures.js +12 -0
- package/dist/sheenPbrScene3DMaterialTextures.js.map +1 -0
- package/dist/specularPbrScene3DMaterialTextures.d.ts +3 -0
- package/dist/specularPbrScene3DMaterialTextures.d.ts.map +1 -0
- package/dist/specularPbrScene3DMaterialTextures.js +12 -0
- package/dist/specularPbrScene3DMaterialTextures.js.map +1 -0
- package/dist/transmissionVolumePbrScene3DMaterialTextures.d.ts +3 -0
- package/dist/transmissionVolumePbrScene3DMaterialTextures.d.ts.map +1 -0
- package/dist/transmissionVolumePbrScene3DMaterialTextures.js +12 -0
- package/dist/transmissionVolumePbrScene3DMaterialTextures.js.map +1 -0
- package/dist/wrappedDiffusePbrScene3DMaterialTextures.d.ts +3 -0
- package/dist/wrappedDiffusePbrScene3DMaterialTextures.d.ts.map +1 -0
- package/dist/wrappedDiffusePbrScene3DMaterialTextures.js +12 -0
- package/dist/wrappedDiffusePbrScene3DMaterialTextures.js.map +1 -0
- package/package.json +16 -13
- package/src/anisotropyPbrScene3DMaterialTextures.test.ts +26 -0
- package/src/awd2Load.test.ts +2 -2
- package/src/clearcoatPbrScene3DMaterialTextures.test.ts +36 -0
- package/src/enableScene3DResourceFailureGuards.test.ts +9 -8
- package/src/explainScene3DResourceCoverage.test.ts +115 -0
- package/src/getScene3DResourceTextures.test.ts +65 -38
- package/src/gltfLoad.test.ts +2 -2
- package/src/imageResourceFetch.test.ts +3 -3
- package/src/iridescencePbrScene3DMaterialTextures.test.ts +34 -0
- package/src/{resolveScene3DResourcesAndWait.test.ts → loadScene3DResources.test.ts} +40 -18
- package/src/md2Load.test.ts +2 -2
- package/src/md5Load.test.ts +2 -2
- package/src/objLoad.test.ts +2 -2
- package/src/resolveScene3DResources.test.ts +136 -53
- package/src/revealScene3DResourcesOnResolve.test.ts +39 -21
- package/src/sceneMaterialTextureRegistry.test.ts +118 -26
- package/src/sceneResourceRecovery.test.ts +13 -47
- package/src/sceneResourceResolver.test.ts +14 -11
- package/src/sceneResourceResolverTreeShaking.test.ts +1 -1
- package/src/shadedScene3DMaterialTextures.test.ts +52 -0
- package/src/sheenPbrScene3DMaterialTextures.test.ts +29 -0
- package/src/specularPbrScene3DMaterialTextures.test.ts +29 -0
- package/src/threeDsLoad.test.ts +2 -2
- package/src/transmissionVolumePbrScene3DMaterialTextures.test.ts +34 -0
- package/src/wrappedDiffusePbrScene3DMaterialTextures.test.ts +34 -0
- package/dist/resolveScene3DResourcesAndWait.d.ts +0 -4
- package/dist/resolveScene3DResourcesAndWait.d.ts.map +0 -1
- package/dist/resolveScene3DResourcesAndWait.js.map +0 -1
|
@@ -3,18 +3,20 @@ import { createBoxMeshGeometry } from '@flighthq/mesh/contract';
|
|
|
3
3
|
import { addNodeChild } from '@flighthq/node/contract';
|
|
4
4
|
import { createMesh, createScene3D } from '@flighthq/scene3d/contract';
|
|
5
5
|
import { connectSignal, createSignal } from '@flighthq/signals/contract';
|
|
6
|
-
import { createTexture } from '@flighthq/texture/contract';
|
|
7
|
-
import type {
|
|
6
|
+
import { createTexture, getTextureSource } from '@flighthq/texture/contract';
|
|
7
|
+
import type { Image, ImageResourceReference, Scene3DResourceLoadProgress, Texture } from '@flighthq/types/contract';
|
|
8
8
|
import { ResourceResolutionState, ImageResourceReferenceKind } from '@flighthq/types/contract';
|
|
9
9
|
import { describe, expect, it, vi } from 'vitest';
|
|
10
10
|
|
|
11
|
-
import { loadScene3DResources, waitForScene3DResourceResolver } from './
|
|
11
|
+
import { loadScene3DResources, waitForScene3DResourceResolver } from './loadScene3DResources';
|
|
12
12
|
import { createBuiltInScene3DResourceResolver, disposeScene3DResourceResolver } from './sceneResourceResolver';
|
|
13
13
|
|
|
14
|
-
const fakeImage = { height: 1, width: 1 } as unknown as
|
|
14
|
+
const fakeImage = { height: 1, width: 1 } as unknown as Image;
|
|
15
|
+
const testResources: ImageResourceReference[] = [];
|
|
16
|
+
let sceneResources: ImageResourceReference[] = [];
|
|
15
17
|
|
|
16
18
|
function externalRef(): ImageResourceReference {
|
|
17
|
-
|
|
19
|
+
const ref: ImageResourceReference = {
|
|
18
20
|
basePath: null,
|
|
19
21
|
failure: null,
|
|
20
22
|
kind: ImageResourceReferenceKind.External,
|
|
@@ -22,6 +24,21 @@ function externalRef(): ImageResourceReference {
|
|
|
22
24
|
state: ResourceResolutionState.Unresolved,
|
|
23
25
|
uri: 'leaf.png',
|
|
24
26
|
};
|
|
27
|
+
testResources.push(ref);
|
|
28
|
+
return ref;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Hands the scene only the references its own test created, then drains the accumulator. Copying the
|
|
32
|
+
// whole accumulator used to give every scene the previous tests' references as well — harmless only
|
|
33
|
+
// while discovery narrowed by mesh attachment and quietly dropped whatever no mesh referenced.
|
|
34
|
+
function configureResources(scene: ReturnType<typeof createScene3D>): void {
|
|
35
|
+
sceneResources = testResources.slice();
|
|
36
|
+
testResources.length = 0;
|
|
37
|
+
scene.resources = sceneResources;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function resourceOf(texture: Texture): ImageResourceReference | undefined {
|
|
41
|
+
return sceneResources.find((ref) => ref.textures?.includes(texture) === true);
|
|
25
42
|
}
|
|
26
43
|
|
|
27
44
|
describe('loadScene3DResources', () => {
|
|
@@ -30,22 +47,24 @@ describe('loadScene3DResources', () => {
|
|
|
30
47
|
const a = createTexture({ resource: externalRef() });
|
|
31
48
|
const b = createTexture({ resource: externalRef() });
|
|
32
49
|
const scene = createScene3D();
|
|
50
|
+
configureResources(scene);
|
|
33
51
|
addNodeChild(scene.root, createMesh(createBoxMeshGeometry(), [createUnlitMaterial({ baseColorMap: a })]));
|
|
34
52
|
addNodeChild(scene.root, createMesh(createBoxMeshGeometry(), [createUnlitMaterial({ baseColorMap: b })]));
|
|
35
53
|
const resolver = createBuiltInScene3DResourceResolver({ fetch });
|
|
36
54
|
|
|
37
|
-
await loadScene3DResources(scene
|
|
55
|
+
await loadScene3DResources(scene, resolver);
|
|
38
56
|
|
|
39
|
-
expect(a
|
|
40
|
-
expect(b
|
|
41
|
-
expect(a
|
|
57
|
+
expect(getTextureSource(a)).toBe(fakeImage);
|
|
58
|
+
expect(getTextureSource(b)).toBe(fakeImage);
|
|
59
|
+
expect(resourceOf(a)?.state).toBe(ResourceResolutionState.Resolved);
|
|
42
60
|
disposeScene3DResourceResolver(resolver);
|
|
43
61
|
});
|
|
44
62
|
|
|
45
63
|
it('resolves immediately when there is nothing pending', async () => {
|
|
46
64
|
const scene = createScene3D();
|
|
65
|
+
configureResources(scene);
|
|
47
66
|
const resolver = createBuiltInScene3DResourceResolver({ fetch: async () => fakeImage });
|
|
48
|
-
await expect(loadScene3DResources(scene
|
|
67
|
+
await expect(loadScene3DResources(scene, resolver)).resolves.toBeUndefined();
|
|
49
68
|
disposeScene3DResourceResolver(resolver);
|
|
50
69
|
});
|
|
51
70
|
|
|
@@ -53,6 +72,7 @@ describe('loadScene3DResources', () => {
|
|
|
53
72
|
const a = createTexture({ resource: externalRef() });
|
|
54
73
|
const b = createTexture({ resource: externalRef() });
|
|
55
74
|
const scene = createScene3D();
|
|
75
|
+
configureResources(scene);
|
|
56
76
|
addNodeChild(scene.root, createMesh(createBoxMeshGeometry(), [createUnlitMaterial({ baseColorMap: a })]));
|
|
57
77
|
addNodeChild(scene.root, createMesh(createBoxMeshGeometry(), [createUnlitMaterial({ baseColorMap: b })]));
|
|
58
78
|
const resolver = createBuiltInScene3DResourceResolver({ fetch: async () => fakeImage });
|
|
@@ -60,7 +80,7 @@ describe('loadScene3DResources', () => {
|
|
|
60
80
|
const progress = createSignal<(event: Readonly<Scene3DResourceLoadProgress>) => void>();
|
|
61
81
|
connectSignal(progress, (event) => events.push({ ...event }));
|
|
62
82
|
|
|
63
|
-
await loadScene3DResources(scene
|
|
83
|
+
await loadScene3DResources(scene, resolver, { progress });
|
|
64
84
|
|
|
65
85
|
expect(events[0]).toEqual({ loaded: 0, total: 2 });
|
|
66
86
|
expect(events.at(-1)).toEqual({ loaded: 2, total: 2 });
|
|
@@ -70,16 +90,17 @@ describe('loadScene3DResources', () => {
|
|
|
70
90
|
it('counts failed references as terminal without hiding their failure state', async () => {
|
|
71
91
|
const texture = createTexture({ resource: externalRef() });
|
|
72
92
|
const scene = createScene3D();
|
|
93
|
+
configureResources(scene);
|
|
73
94
|
addNodeChild(scene.root, createMesh(createBoxMeshGeometry(), [createUnlitMaterial({ baseColorMap: texture })]));
|
|
74
95
|
const resolver = createBuiltInScene3DResourceResolver({ fetch: async () => null });
|
|
75
96
|
const events: Scene3DResourceLoadProgress[] = [];
|
|
76
97
|
const progress = createSignal<(event: Readonly<Scene3DResourceLoadProgress>) => void>();
|
|
77
98
|
connectSignal(progress, (event) => events.push({ ...event }));
|
|
78
99
|
|
|
79
|
-
await loadScene3DResources(scene
|
|
100
|
+
await loadScene3DResources(scene, resolver, { progress });
|
|
80
101
|
|
|
81
|
-
expect(texture
|
|
82
|
-
expect(texture
|
|
102
|
+
expect(getTextureSource(texture)).toBeNull();
|
|
103
|
+
expect(resourceOf(texture)?.state).toBe(ResourceResolutionState.Failed);
|
|
83
104
|
expect(events).toEqual([
|
|
84
105
|
{ loaded: 0, total: 1 },
|
|
85
106
|
{ loaded: 1, total: 1 },
|
|
@@ -91,6 +112,7 @@ describe('loadScene3DResources', () => {
|
|
|
91
112
|
const selected = createTexture({ resource: externalRef() });
|
|
92
113
|
const deferred = createTexture({ resource: externalRef() });
|
|
93
114
|
const scene = createScene3D();
|
|
115
|
+
configureResources(scene);
|
|
94
116
|
addNodeChild(scene.root, createMesh(createBoxMeshGeometry(), [createUnlitMaterial({ baseColorMap: selected })]));
|
|
95
117
|
addNodeChild(scene.root, createMesh(createBoxMeshGeometry(), [createUnlitMaterial({ baseColorMap: deferred })]));
|
|
96
118
|
const fetch = vi.fn(async () => fakeImage);
|
|
@@ -99,11 +121,11 @@ describe('loadScene3DResources', () => {
|
|
|
99
121
|
const progress = createSignal<(event: Readonly<Scene3DResourceLoadProgress>) => void>();
|
|
100
122
|
connectSignal(progress, (event) => events.push({ ...event }));
|
|
101
123
|
|
|
102
|
-
await loadScene3DResources(scene
|
|
124
|
+
await loadScene3DResources(scene, resolver, { progress, select: (texture) => texture === selected });
|
|
103
125
|
|
|
104
|
-
expect(selected
|
|
105
|
-
expect(deferred
|
|
106
|
-
expect(deferred
|
|
126
|
+
expect(getTextureSource(selected)).toBe(fakeImage);
|
|
127
|
+
expect(getTextureSource(deferred)).toBeNull();
|
|
128
|
+
expect(resourceOf(deferred)?.state).toBe(ResourceResolutionState.Unresolved);
|
|
107
129
|
expect(fetch).toHaveBeenCalledOnce();
|
|
108
130
|
expect(events.at(-1)).toEqual({ loaded: 1, total: 1 });
|
|
109
131
|
disposeScene3DResourceResolver(resolver);
|
package/src/md2Load.test.ts
CHANGED
|
@@ -39,8 +39,8 @@ beforeAll(async () => {
|
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
afterAll(() => {
|
|
42
|
-
vi.doUnmock('@flighthq/net');
|
|
43
|
-
vi.doUnmock('@flighthq/scene3d-formats');
|
|
42
|
+
vi.doUnmock('@flighthq/net/contract');
|
|
43
|
+
vi.doUnmock('@flighthq/scene3d-formats/contract');
|
|
44
44
|
vi.resetModules();
|
|
45
45
|
});
|
|
46
46
|
|
package/src/md5Load.test.ts
CHANGED
|
@@ -39,8 +39,8 @@ beforeAll(async () => {
|
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
afterAll(() => {
|
|
42
|
-
vi.doUnmock('@flighthq/net');
|
|
43
|
-
vi.doUnmock('@flighthq/scene3d-formats');
|
|
42
|
+
vi.doUnmock('@flighthq/net/contract');
|
|
43
|
+
vi.doUnmock('@flighthq/scene3d-formats/contract');
|
|
44
44
|
vi.resetModules();
|
|
45
45
|
});
|
|
46
46
|
|
package/src/objLoad.test.ts
CHANGED
|
@@ -39,8 +39,8 @@ beforeAll(async () => {
|
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
afterAll(() => {
|
|
42
|
-
vi.doUnmock('@flighthq/net');
|
|
43
|
-
vi.doUnmock('@flighthq/scene3d-formats');
|
|
42
|
+
vi.doUnmock('@flighthq/net/contract');
|
|
43
|
+
vi.doUnmock('@flighthq/scene3d-formats/contract');
|
|
44
44
|
vi.resetModules();
|
|
45
45
|
});
|
|
46
46
|
|
|
@@ -4,22 +4,23 @@ import { createBoxMeshGeometry } from '@flighthq/mesh/contract';
|
|
|
4
4
|
import { addNodeChild } from '@flighthq/node/contract';
|
|
5
5
|
import { createMesh, createScene3D } from '@flighthq/scene3d/contract';
|
|
6
6
|
import { connectSignal } from '@flighthq/signals/contract';
|
|
7
|
-
import { createTexture } from '@flighthq/texture/contract';
|
|
8
|
-
import type {
|
|
7
|
+
import { createTexture, getTextureSource } from '@flighthq/texture/contract';
|
|
8
|
+
import type { Image, ImageResourceReference, Scene3DResourceResolver, Texture } from '@flighthq/types/contract';
|
|
9
9
|
import {
|
|
10
10
|
ImageResourceFailureKind,
|
|
11
|
+
ImageTextureSourceKind,
|
|
11
12
|
ResourceResolutionState,
|
|
12
13
|
ImageResourceReferenceKind,
|
|
13
14
|
} from '@flighthq/types/contract';
|
|
14
15
|
import type { Mock } from 'vitest';
|
|
15
16
|
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from 'vitest';
|
|
16
17
|
|
|
18
|
+
import type * as LoadScene3DResourcesModule from './loadScene3DResources';
|
|
17
19
|
import type * as ResolveScene3DResourcesModule from './resolveScene3DResources';
|
|
18
|
-
import type * as ResolveScene3DResourcesAndWaitModule from './resolveScene3DResourcesAndWait';
|
|
19
20
|
import type * as Scene3DResourceResolverModule from './sceneResourceResolver';
|
|
20
21
|
import type * as Scene3DResourceSignalsModule from './sceneResourceSignals';
|
|
21
22
|
|
|
22
|
-
const fakeImage = { height: 2, width: 2 } as unknown as
|
|
23
|
+
const fakeImage = { height: 2, kind: ImageTextureSourceKind, width: 2 } as unknown as Image;
|
|
23
24
|
type LoadImageResourceFromBytes = typeof ImageModule.loadImageResourceFromBytes;
|
|
24
25
|
|
|
25
26
|
let createBuiltInScene3DResourceResolver: typeof Scene3DResourceResolverModule.createBuiltInScene3DResourceResolver;
|
|
@@ -28,20 +29,25 @@ let enableScene3DResourceSignals: typeof Scene3DResourceSignalsModule.enableScen
|
|
|
28
29
|
let loadFromBytes: Mock<LoadImageResourceFromBytes>;
|
|
29
30
|
let resolveOneScene3DResourceTexture: typeof ResolveScene3DResourcesModule.resolveOneScene3DResourceTexture;
|
|
30
31
|
let resolveScene3DResources: typeof ResolveScene3DResourcesModule.resolveScene3DResources;
|
|
31
|
-
let
|
|
32
|
+
let updateScene3DResourceStreaming: typeof ResolveScene3DResourcesModule.updateScene3DResourceStreaming;
|
|
33
|
+
let waitForScene3DResourceResolver: typeof LoadScene3DResourcesModule.waitForScene3DResourceResolver;
|
|
34
|
+
const testResources: ImageResourceReference[] = [];
|
|
35
|
+
let sceneResources: ImageResourceReference[] = [];
|
|
32
36
|
|
|
33
37
|
function embeddedRef(mimeType: string | null = 'image/png'): ImageResourceReference {
|
|
34
|
-
|
|
38
|
+
const ref: ImageResourceReference = {
|
|
35
39
|
bytes: new Uint8Array([9, 9]),
|
|
36
40
|
failure: null,
|
|
37
41
|
kind: ImageResourceReferenceKind.Embedded,
|
|
38
42
|
mimeType,
|
|
39
43
|
state: ResourceResolutionState.Unresolved,
|
|
40
44
|
};
|
|
45
|
+
testResources.push(ref);
|
|
46
|
+
return ref;
|
|
41
47
|
}
|
|
42
48
|
|
|
43
49
|
function externalRef(uri = 'leaf.png'): ImageResourceReference {
|
|
44
|
-
|
|
50
|
+
const ref: ImageResourceReference = {
|
|
45
51
|
basePath: null,
|
|
46
52
|
failure: null,
|
|
47
53
|
kind: ImageResourceReferenceKind.External,
|
|
@@ -49,6 +55,12 @@ function externalRef(uri = 'leaf.png'): ImageResourceReference {
|
|
|
49
55
|
state: ResourceResolutionState.Unresolved,
|
|
50
56
|
uri,
|
|
51
57
|
};
|
|
58
|
+
testResources.push(ref);
|
|
59
|
+
return ref;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function resourceOf(texture: Texture): ImageResourceReference | undefined {
|
|
63
|
+
return sceneResources.find((ref) => ref.textures?.includes(texture) === true);
|
|
52
64
|
}
|
|
53
65
|
|
|
54
66
|
function pendingTexture(): Texture {
|
|
@@ -57,6 +69,11 @@ function pendingTexture(): Texture {
|
|
|
57
69
|
|
|
58
70
|
function meshScene3D(...textures: Texture[]) {
|
|
59
71
|
const scene = createScene3D();
|
|
72
|
+
// Only this test's references. Copying the whole accumulator used to hand each scene the previous
|
|
73
|
+
// tests' references too, which discovery quietly dropped while it narrowed by mesh attachment.
|
|
74
|
+
sceneResources = testResources.slice();
|
|
75
|
+
testResources.length = 0;
|
|
76
|
+
scene.resources = sceneResources;
|
|
60
77
|
for (const texture of textures) {
|
|
61
78
|
addNodeChild(scene.root, createMesh(createBoxMeshGeometry(), [createUnlitMaterial({ baseColorMap: texture })]));
|
|
62
79
|
}
|
|
@@ -70,18 +87,22 @@ async function settle(resolver: Scene3DResourceResolver): Promise<void> {
|
|
|
70
87
|
beforeAll(async () => {
|
|
71
88
|
vi.resetModules();
|
|
72
89
|
loadFromBytes = vi.fn<LoadImageResourceFromBytes>();
|
|
73
|
-
|
|
90
|
+
// Override only the two loaders; the rest of the module stays real, so growing @flighthq/image cannot
|
|
91
|
+
// silently blank out a code path under test.
|
|
92
|
+
vi.doMock('@flighthq/image/contract', async (importOriginal) => ({
|
|
93
|
+
...(await importOriginal<typeof ImageModule>()),
|
|
74
94
|
loadImageResourceFromBytes: loadFromBytes,
|
|
75
95
|
loadImageResourceFromUrl: vi.fn(),
|
|
76
96
|
}));
|
|
77
|
-
({ resolveOneScene3DResourceTexture, resolveScene3DResources } =
|
|
78
|
-
|
|
97
|
+
({ resolveOneScene3DResourceTexture, resolveScene3DResources, updateScene3DResourceStreaming } =
|
|
98
|
+
await import('./resolveScene3DResources'));
|
|
99
|
+
({ waitForScene3DResourceResolver } = await import('./loadScene3DResources'));
|
|
79
100
|
({ createBuiltInScene3DResourceResolver, disposeScene3DResourceResolver } = await import('./sceneResourceResolver'));
|
|
80
101
|
({ enableScene3DResourceSignals } = await import('./sceneResourceSignals'));
|
|
81
102
|
});
|
|
82
103
|
|
|
83
104
|
afterAll(() => {
|
|
84
|
-
vi.doUnmock('@flighthq/image');
|
|
105
|
+
vi.doUnmock('@flighthq/image/contract');
|
|
85
106
|
vi.resetModules();
|
|
86
107
|
});
|
|
87
108
|
|
|
@@ -122,6 +143,68 @@ describe('resolveOneScene3DResourceTexture', () => {
|
|
|
122
143
|
});
|
|
123
144
|
|
|
124
145
|
describe('resolveScene3DResources', () => {
|
|
146
|
+
it('does not cancel acquisition owned by a streaming update', async () => {
|
|
147
|
+
let loadSignal: AbortSignal | undefined;
|
|
148
|
+
loadFromBytes.mockImplementation(
|
|
149
|
+
(_bytes, _mime, signal) =>
|
|
150
|
+
new Promise<Image>((_resolve, reject) => {
|
|
151
|
+
loadSignal = signal;
|
|
152
|
+
signal?.addEventListener('abort', () => reject(signal.reason));
|
|
153
|
+
}),
|
|
154
|
+
);
|
|
155
|
+
const ref = embeddedRef();
|
|
156
|
+
const scene = meshScene3D(createTexture({ resource: ref }));
|
|
157
|
+
const resolver = createBuiltInScene3DResourceResolver();
|
|
158
|
+
updateScene3DResourceStreaming(scene, resolver);
|
|
159
|
+
await vi.waitFor(() => expect(loadSignal).toBeDefined());
|
|
160
|
+
|
|
161
|
+
const resources = resolveScene3DResources(scene, resolver, { select: () => false });
|
|
162
|
+
|
|
163
|
+
expect(resources.resolved).toEqual([]);
|
|
164
|
+
expect(resources.unresolved).toEqual([]);
|
|
165
|
+
expect(loadSignal?.aborted).toBe(false);
|
|
166
|
+
expect(ref.state).toBe(ResourceResolutionState.Loading);
|
|
167
|
+
disposeScene3DResourceResolver(resolver);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('returns the selected unresolved working set without starting acquisition', () => {
|
|
171
|
+
loadFromBytes.mockResolvedValue(fakeImage);
|
|
172
|
+
const ref = embeddedRef();
|
|
173
|
+
const a = createTexture({ resource: ref });
|
|
174
|
+
const b = createTexture({ resource: ref });
|
|
175
|
+
const scene = meshScene3D(a, b);
|
|
176
|
+
const resolver = createBuiltInScene3DResourceResolver();
|
|
177
|
+
|
|
178
|
+
const resources = resolveScene3DResources(scene, resolver);
|
|
179
|
+
|
|
180
|
+
expect(resources.scene).toBe(scene);
|
|
181
|
+
expect(resources.resolved).toEqual([]);
|
|
182
|
+
expect(resources.unresolved).toEqual([{ ref, textures: [a, b] }]);
|
|
183
|
+
expect(ref.state).toBe(ResourceResolutionState.Unresolved);
|
|
184
|
+
expect(loadFromBytes).not.toHaveBeenCalled();
|
|
185
|
+
disposeScene3DResourceResolver(resolver);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('binds ready content across subscribers and returns its resolved group', () => {
|
|
189
|
+
const ref = embeddedRef();
|
|
190
|
+
const a = createTexture({ resource: ref });
|
|
191
|
+
const b = createTexture({ resource: ref });
|
|
192
|
+
if (a.dimension !== '2d') throw new Error('test texture must be 2d');
|
|
193
|
+
a.source = fakeImage;
|
|
194
|
+
const scene = meshScene3D(a, b);
|
|
195
|
+
const resolver = createBuiltInScene3DResourceResolver();
|
|
196
|
+
|
|
197
|
+
const resources = resolveScene3DResources(scene, resolver);
|
|
198
|
+
|
|
199
|
+
expect(resources.unresolved).toEqual([]);
|
|
200
|
+
expect(resources.resolved).toEqual([{ image: fakeImage, ref, textures: [a, b] }]);
|
|
201
|
+
expect(getTextureSource(b)).toBe(fakeImage);
|
|
202
|
+
expect(ref.state).toBe(ResourceResolutionState.Resolved);
|
|
203
|
+
disposeScene3DResourceResolver(resolver);
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
describe('updateScene3DResourceStreaming', () => {
|
|
125
208
|
it('resolves every pending texture, binding the image and advancing state', async () => {
|
|
126
209
|
loadFromBytes.mockResolvedValue(fakeImage);
|
|
127
210
|
const a = pendingTexture();
|
|
@@ -129,14 +212,14 @@ describe('resolveScene3DResources', () => {
|
|
|
129
212
|
const scene = meshScene3D(a, b);
|
|
130
213
|
const resolver = createBuiltInScene3DResourceResolver();
|
|
131
214
|
|
|
132
|
-
|
|
215
|
+
updateScene3DResourceStreaming(scene, resolver);
|
|
133
216
|
await settle(resolver);
|
|
134
217
|
|
|
135
|
-
expect(a
|
|
136
|
-
expect(b
|
|
137
|
-
expect(a
|
|
138
|
-
expect(a
|
|
139
|
-
expect(b
|
|
218
|
+
expect(getTextureSource(a)).toBe(fakeImage);
|
|
219
|
+
expect(getTextureSource(b)).toBe(fakeImage);
|
|
220
|
+
expect(resourceOf(a)?.state).toBe(ResourceResolutionState.Resolved);
|
|
221
|
+
expect(resourceOf(a)?.failure).toBeNull();
|
|
222
|
+
expect(resourceOf(b)?.state).toBe(ResourceResolutionState.Resolved);
|
|
140
223
|
disposeScene3DResourceResolver(resolver);
|
|
141
224
|
});
|
|
142
225
|
|
|
@@ -148,12 +231,12 @@ describe('resolveScene3DResources', () => {
|
|
|
148
231
|
const scene = meshScene3D(a, b);
|
|
149
232
|
const resolver = createBuiltInScene3DResourceResolver();
|
|
150
233
|
|
|
151
|
-
|
|
234
|
+
updateScene3DResourceStreaming(scene, resolver);
|
|
152
235
|
await settle(resolver);
|
|
153
236
|
|
|
154
237
|
expect(loadFromBytes).toHaveBeenCalledTimes(1);
|
|
155
|
-
expect(a
|
|
156
|
-
expect(b
|
|
238
|
+
expect(getTextureSource(a)).toBe(fakeImage);
|
|
239
|
+
expect(getTextureSource(b)).toBe(fakeImage);
|
|
157
240
|
expect(ref.state).toBe(ResourceResolutionState.Resolved);
|
|
158
241
|
disposeScene3DResourceResolver(resolver);
|
|
159
242
|
});
|
|
@@ -162,7 +245,7 @@ describe('resolveScene3DResources', () => {
|
|
|
162
245
|
let loadSignal: AbortSignal | undefined;
|
|
163
246
|
loadFromBytes.mockImplementation(
|
|
164
247
|
(_bytes, _mime, signal) =>
|
|
165
|
-
new Promise<
|
|
248
|
+
new Promise<Image>((_resolve, reject) => {
|
|
166
249
|
loadSignal = signal;
|
|
167
250
|
signal?.addEventListener('abort', () => reject(signal.reason));
|
|
168
251
|
}),
|
|
@@ -173,13 +256,13 @@ describe('resolveScene3DResources', () => {
|
|
|
173
256
|
const scene = meshScene3D(a, b);
|
|
174
257
|
const resolver = createBuiltInScene3DResourceResolver();
|
|
175
258
|
|
|
176
|
-
|
|
259
|
+
updateScene3DResourceStreaming(scene, resolver);
|
|
177
260
|
await vi.waitFor(() => expect(loadSignal).toBeDefined());
|
|
178
|
-
|
|
261
|
+
updateScene3DResourceStreaming(scene, resolver, { select: (texture) => texture === b });
|
|
179
262
|
expect(loadSignal?.aborted).toBe(false);
|
|
180
263
|
expect(ref.state).toBe(ResourceResolutionState.Loading);
|
|
181
264
|
|
|
182
|
-
|
|
265
|
+
updateScene3DResourceStreaming(scene, resolver, { select: () => false });
|
|
183
266
|
expect(loadSignal?.aborted).toBe(true);
|
|
184
267
|
expect(ref.state).toBe(ResourceResolutionState.Unresolved);
|
|
185
268
|
disposeScene3DResourceResolver(resolver);
|
|
@@ -193,13 +276,13 @@ describe('resolveScene3DResources', () => {
|
|
|
193
276
|
const scene = meshScene3D(a, b);
|
|
194
277
|
const resolver = createBuiltInScene3DResourceResolver();
|
|
195
278
|
|
|
196
|
-
|
|
279
|
+
updateScene3DResourceStreaming(scene, resolver, { select: (texture) => texture === a });
|
|
197
280
|
await settle(resolver);
|
|
198
|
-
expect(a
|
|
199
|
-
expect(b
|
|
281
|
+
expect(getTextureSource(a)).toBe(fakeImage);
|
|
282
|
+
expect(getTextureSource(b)).toBeNull();
|
|
200
283
|
|
|
201
|
-
|
|
202
|
-
expect(b
|
|
284
|
+
updateScene3DResourceStreaming(scene, resolver, { select: (texture) => texture === b });
|
|
285
|
+
expect(getTextureSource(b)).toBe(fakeImage);
|
|
203
286
|
expect(loadFromBytes).toHaveBeenCalledTimes(1);
|
|
204
287
|
disposeScene3DResourceResolver(resolver);
|
|
205
288
|
});
|
|
@@ -211,12 +294,12 @@ describe('resolveScene3DResources', () => {
|
|
|
211
294
|
const scene = meshScene3D(wanted, skipped);
|
|
212
295
|
const resolver = createBuiltInScene3DResourceResolver();
|
|
213
296
|
|
|
214
|
-
|
|
297
|
+
updateScene3DResourceStreaming(scene, resolver, { select: (texture) => texture === wanted });
|
|
215
298
|
await settle(resolver);
|
|
216
299
|
|
|
217
|
-
expect(wanted
|
|
218
|
-
expect(skipped
|
|
219
|
-
expect(skipped
|
|
300
|
+
expect(resourceOf(wanted)?.state).toBe(ResourceResolutionState.Resolved);
|
|
301
|
+
expect(resourceOf(skipped)?.state).toBe(ResourceResolutionState.Unresolved);
|
|
302
|
+
expect(getTextureSource(skipped)).toBeNull();
|
|
220
303
|
disposeScene3DResourceResolver(resolver);
|
|
221
304
|
});
|
|
222
305
|
|
|
@@ -225,12 +308,12 @@ describe('resolveScene3DResources', () => {
|
|
|
225
308
|
const scene = meshScene3D(texture);
|
|
226
309
|
const resolver = createBuiltInScene3DResourceResolver({ fetch: async () => null });
|
|
227
310
|
|
|
228
|
-
|
|
311
|
+
updateScene3DResourceStreaming(scene, resolver);
|
|
229
312
|
await settle(resolver);
|
|
230
313
|
|
|
231
|
-
expect(texture
|
|
232
|
-
expect(texture
|
|
233
|
-
expect(texture
|
|
314
|
+
expect(getTextureSource(texture)).toBeNull();
|
|
315
|
+
expect(resourceOf(texture)?.state).toBe(ResourceResolutionState.Failed);
|
|
316
|
+
expect(resourceOf(texture)?.failure).toEqual({
|
|
234
317
|
kind: ImageResourceFailureKind.Unavailable,
|
|
235
318
|
message: 'Image resource resolution returned no image',
|
|
236
319
|
name: null,
|
|
@@ -244,11 +327,11 @@ describe('resolveScene3DResources', () => {
|
|
|
244
327
|
const scene = meshScene3D(texture);
|
|
245
328
|
const resolver = createBuiltInScene3DResourceResolver();
|
|
246
329
|
|
|
247
|
-
|
|
330
|
+
updateScene3DResourceStreaming(scene, resolver);
|
|
248
331
|
await settle(resolver);
|
|
249
332
|
|
|
250
|
-
expect(texture
|
|
251
|
-
expect(texture
|
|
333
|
+
expect(resourceOf(texture)?.state).toBe(ResourceResolutionState.Failed);
|
|
334
|
+
expect(resourceOf(texture)?.failure).toEqual({
|
|
252
335
|
kind: ImageResourceFailureKind.Error,
|
|
253
336
|
message: 'bad image',
|
|
254
337
|
name: 'Error',
|
|
@@ -261,7 +344,7 @@ describe('resolveScene3DResources', () => {
|
|
|
261
344
|
// A load that hangs until its signal aborts, so we can drop it mid-flight.
|
|
262
345
|
loadFromBytes.mockImplementation(
|
|
263
346
|
(_bytes, _mime, signal) =>
|
|
264
|
-
new Promise<
|
|
347
|
+
new Promise<Image>((_resolve, reject) => {
|
|
265
348
|
if (signal !== undefined) loadSignals.push(signal);
|
|
266
349
|
signal?.addEventListener('abort', () => reject(signal.reason));
|
|
267
350
|
}),
|
|
@@ -270,33 +353,33 @@ describe('resolveScene3DResources', () => {
|
|
|
270
353
|
const scene = meshScene3D(texture);
|
|
271
354
|
const resolver = createBuiltInScene3DResourceResolver();
|
|
272
355
|
|
|
273
|
-
|
|
274
|
-
expect(texture
|
|
356
|
+
updateScene3DResourceStreaming(scene, resolver, { select: () => true });
|
|
357
|
+
expect(resourceOf(texture)?.state).toBe(ResourceResolutionState.Loading);
|
|
275
358
|
await vi.waitFor(() => expect(loadSignals).toHaveLength(1));
|
|
276
359
|
|
|
277
360
|
// Drop it: not in the working set this pass → abort + revert.
|
|
278
|
-
|
|
279
|
-
expect(texture
|
|
280
|
-
expect(texture
|
|
361
|
+
updateScene3DResourceStreaming(scene, resolver, { select: () => false });
|
|
362
|
+
expect(resourceOf(texture)?.state).toBe(ResourceResolutionState.Unresolved);
|
|
363
|
+
expect(getTextureSource(texture)).toBeNull();
|
|
281
364
|
expect(loadSignals[0].aborted).toBe(true);
|
|
282
365
|
|
|
283
366
|
// Re-entry re-requests from scratch.
|
|
284
|
-
|
|
285
|
-
expect(texture
|
|
367
|
+
updateScene3DResourceStreaming(scene, resolver, { select: () => true });
|
|
368
|
+
expect(resourceOf(texture)?.state).toBe(ResourceResolutionState.Loading);
|
|
286
369
|
await vi.waitFor(() => expect(loadSignals).toHaveLength(2));
|
|
287
370
|
|
|
288
371
|
disposeScene3DResourceResolver(resolver);
|
|
289
372
|
});
|
|
290
373
|
|
|
291
374
|
it('does not re-request a texture already in flight', async () => {
|
|
292
|
-
loadFromBytes.mockImplementation(() => new Promise<
|
|
375
|
+
loadFromBytes.mockImplementation(() => new Promise<Image>(() => {}));
|
|
293
376
|
const texture = pendingTexture();
|
|
294
377
|
const scene = meshScene3D(texture);
|
|
295
378
|
const resolver = createBuiltInScene3DResourceResolver();
|
|
296
379
|
|
|
297
|
-
|
|
380
|
+
updateScene3DResourceStreaming(scene, resolver);
|
|
298
381
|
await vi.waitFor(() => expect(loadFromBytes).toHaveBeenCalledTimes(1));
|
|
299
|
-
|
|
382
|
+
updateScene3DResourceStreaming(scene, resolver);
|
|
300
383
|
expect(loadFromBytes).toHaveBeenCalledTimes(1);
|
|
301
384
|
|
|
302
385
|
disposeScene3DResourceResolver(resolver);
|
|
@@ -313,7 +396,7 @@ describe('resolveScene3DResources', () => {
|
|
|
313
396
|
connectSignal(signals.onResourceResolved, (event) => resolved.push(event.texture));
|
|
314
397
|
connectSignal(signals.onResourceFailed, (event) => failed.push(event.texture));
|
|
315
398
|
|
|
316
|
-
|
|
399
|
+
updateScene3DResourceStreaming(scene, resolver);
|
|
317
400
|
await settle(resolver);
|
|
318
401
|
|
|
319
402
|
expect(resolved).toEqual([texture]);
|
|
@@ -330,7 +413,7 @@ describe('resolveScene3DResources', () => {
|
|
|
330
413
|
const failed: Texture[] = [];
|
|
331
414
|
connectSignal(signals.onResourceFailed, (event) => failed.push(event.texture));
|
|
332
415
|
|
|
333
|
-
|
|
416
|
+
updateScene3DResourceStreaming(scene, resolver);
|
|
334
417
|
await settle(resolver);
|
|
335
418
|
|
|
336
419
|
expect(failed).toEqual([texture]);
|