@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.
Files changed (108) hide show
  1. package/dist/anisotropyPbrScene3DMaterialTextures.d.ts +3 -0
  2. package/dist/anisotropyPbrScene3DMaterialTextures.d.ts.map +1 -0
  3. package/dist/anisotropyPbrScene3DMaterialTextures.js +10 -0
  4. package/dist/anisotropyPbrScene3DMaterialTextures.js.map +1 -0
  5. package/dist/awd2Load.d.ts.map +1 -1
  6. package/dist/awd2Load.js +3 -2
  7. package/dist/awd2Load.js.map +1 -1
  8. package/dist/clearcoatPbrScene3DMaterialTextures.d.ts +3 -0
  9. package/dist/clearcoatPbrScene3DMaterialTextures.d.ts.map +1 -0
  10. package/dist/clearcoatPbrScene3DMaterialTextures.js +14 -0
  11. package/dist/clearcoatPbrScene3DMaterialTextures.js.map +1 -0
  12. package/dist/contract.d.ts +10 -1
  13. package/dist/contract.d.ts.map +1 -1
  14. package/dist/contract.js +10 -1
  15. package/dist/contract.js.map +1 -1
  16. package/dist/enableScene3DResourceFailureGuards.d.ts.map +1 -1
  17. package/dist/enableScene3DResourceFailureGuards.js +2 -2
  18. package/dist/enableScene3DResourceFailureGuards.js.map +1 -1
  19. package/dist/explainScene3DResourceCoverage.d.ts +4 -0
  20. package/dist/explainScene3DResourceCoverage.d.ts.map +1 -0
  21. package/dist/explainScene3DResourceCoverage.js +42 -0
  22. package/dist/explainScene3DResourceCoverage.js.map +1 -0
  23. package/dist/getScene3DResourceTextures.d.ts +3 -2
  24. package/dist/getScene3DResourceTextures.d.ts.map +1 -1
  25. package/dist/getScene3DResourceTextures.js +23 -27
  26. package/dist/getScene3DResourceTextures.js.map +1 -1
  27. package/dist/imageResourceFetch.d.ts +2 -2
  28. package/dist/imageResourceFetch.d.ts.map +1 -1
  29. package/dist/index.d.ts +1 -1
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +1 -1
  32. package/dist/index.js.map +1 -1
  33. package/dist/iridescencePbrScene3DMaterialTextures.d.ts +3 -0
  34. package/dist/iridescencePbrScene3DMaterialTextures.d.ts.map +1 -0
  35. package/dist/iridescencePbrScene3DMaterialTextures.js +12 -0
  36. package/dist/iridescencePbrScene3DMaterialTextures.js.map +1 -0
  37. package/dist/loadScene3DResources.d.ts +4 -0
  38. package/dist/loadScene3DResources.d.ts.map +1 -0
  39. package/dist/{resolveScene3DResourcesAndWait.js → loadScene3DResources.js} +11 -21
  40. package/dist/loadScene3DResources.js.map +1 -0
  41. package/dist/resolveScene3DResources.d.ts +4 -3
  42. package/dist/resolveScene3DResources.d.ts.map +1 -1
  43. package/dist/resolveScene3DResources.js +49 -26
  44. package/dist/resolveScene3DResources.js.map +1 -1
  45. package/dist/revealScene3DResourcesOnResolve.d.ts +2 -2
  46. package/dist/revealScene3DResourcesOnResolve.d.ts.map +1 -1
  47. package/dist/revealScene3DResourcesOnResolve.js +9 -6
  48. package/dist/revealScene3DResourcesOnResolve.js.map +1 -1
  49. package/dist/sceneMaterialTextureRegistry.d.ts +6 -2
  50. package/dist/sceneMaterialTextureRegistry.d.ts.map +1 -1
  51. package/dist/sceneMaterialTextureRegistry.js +31 -8
  52. package/dist/sceneMaterialTextureRegistry.js.map +1 -1
  53. package/dist/sceneResourceRecovery.d.ts +2 -4
  54. package/dist/sceneResourceRecovery.d.ts.map +1 -1
  55. package/dist/sceneResourceRecovery.js +9 -28
  56. package/dist/sceneResourceRecovery.js.map +1 -1
  57. package/dist/sceneResourceResolver.d.ts.map +1 -1
  58. package/dist/sceneResourceResolver.js +6 -2
  59. package/dist/sceneResourceResolver.js.map +1 -1
  60. package/dist/shadedScene3DMaterialTextures.d.ts +3 -0
  61. package/dist/shadedScene3DMaterialTextures.d.ts.map +1 -0
  62. package/dist/shadedScene3DMaterialTextures.js +30 -0
  63. package/dist/shadedScene3DMaterialTextures.js.map +1 -0
  64. package/dist/sheenPbrScene3DMaterialTextures.d.ts +3 -0
  65. package/dist/sheenPbrScene3DMaterialTextures.d.ts.map +1 -0
  66. package/dist/sheenPbrScene3DMaterialTextures.js +12 -0
  67. package/dist/sheenPbrScene3DMaterialTextures.js.map +1 -0
  68. package/dist/specularPbrScene3DMaterialTextures.d.ts +3 -0
  69. package/dist/specularPbrScene3DMaterialTextures.d.ts.map +1 -0
  70. package/dist/specularPbrScene3DMaterialTextures.js +12 -0
  71. package/dist/specularPbrScene3DMaterialTextures.js.map +1 -0
  72. package/dist/transmissionVolumePbrScene3DMaterialTextures.d.ts +3 -0
  73. package/dist/transmissionVolumePbrScene3DMaterialTextures.d.ts.map +1 -0
  74. package/dist/transmissionVolumePbrScene3DMaterialTextures.js +12 -0
  75. package/dist/transmissionVolumePbrScene3DMaterialTextures.js.map +1 -0
  76. package/dist/wrappedDiffusePbrScene3DMaterialTextures.d.ts +3 -0
  77. package/dist/wrappedDiffusePbrScene3DMaterialTextures.d.ts.map +1 -0
  78. package/dist/wrappedDiffusePbrScene3DMaterialTextures.js +12 -0
  79. package/dist/wrappedDiffusePbrScene3DMaterialTextures.js.map +1 -0
  80. package/package.json +16 -13
  81. package/src/anisotropyPbrScene3DMaterialTextures.test.ts +26 -0
  82. package/src/awd2Load.test.ts +2 -2
  83. package/src/clearcoatPbrScene3DMaterialTextures.test.ts +36 -0
  84. package/src/enableScene3DResourceFailureGuards.test.ts +9 -8
  85. package/src/explainScene3DResourceCoverage.test.ts +115 -0
  86. package/src/getScene3DResourceTextures.test.ts +65 -38
  87. package/src/gltfLoad.test.ts +2 -2
  88. package/src/imageResourceFetch.test.ts +3 -3
  89. package/src/iridescencePbrScene3DMaterialTextures.test.ts +34 -0
  90. package/src/{resolveScene3DResourcesAndWait.test.ts → loadScene3DResources.test.ts} +40 -18
  91. package/src/md2Load.test.ts +2 -2
  92. package/src/md5Load.test.ts +2 -2
  93. package/src/objLoad.test.ts +2 -2
  94. package/src/resolveScene3DResources.test.ts +136 -53
  95. package/src/revealScene3DResourcesOnResolve.test.ts +39 -21
  96. package/src/sceneMaterialTextureRegistry.test.ts +118 -26
  97. package/src/sceneResourceRecovery.test.ts +13 -47
  98. package/src/sceneResourceResolver.test.ts +14 -11
  99. package/src/sceneResourceResolverTreeShaking.test.ts +1 -1
  100. package/src/shadedScene3DMaterialTextures.test.ts +52 -0
  101. package/src/sheenPbrScene3DMaterialTextures.test.ts +29 -0
  102. package/src/specularPbrScene3DMaterialTextures.test.ts +29 -0
  103. package/src/threeDsLoad.test.ts +2 -2
  104. package/src/transmissionVolumePbrScene3DMaterialTextures.test.ts +34 -0
  105. package/src/wrappedDiffusePbrScene3DMaterialTextures.test.ts +34 -0
  106. package/dist/resolveScene3DResourcesAndWait.d.ts +0 -4
  107. package/dist/resolveScene3DResourcesAndWait.d.ts.map +0 -1
  108. package/dist/resolveScene3DResourcesAndWait.js.map +0 -1
@@ -8,25 +8,37 @@ import { createTweenManager, hasTweensOf, updateTweens } from '@flighthq/tween/c
8
8
  import type { Scene3D } from '@flighthq/types/contract';
9
9
  import type {
10
10
  EmbeddedImageResourceReference,
11
- ImageResource,
11
+ Image,
12
12
  Node3D,
13
13
  Scene3DResourceEvent,
14
14
  Texture,
15
15
  } from '@flighthq/types/contract';
16
- import { ResourceResolutionState } from '@flighthq/types/contract';
16
+ import { ImageTextureSourceKind, ResourceResolutionState } from '@flighthq/types/contract';
17
17
 
18
18
  import { revealScene3DResourcesOnResolve } from './revealScene3DResourcesOnResolve';
19
19
  import { createBuiltInScene3DResourceResolver } from './sceneResourceResolver';
20
20
  import { enableScene3DResourceSignals } from './sceneResourceSignals';
21
21
 
22
+ const testResources: EmbeddedImageResourceReference[] = [];
23
+
22
24
  function pendingRef(): EmbeddedImageResourceReference {
23
- return {
25
+ const ref: EmbeddedImageResourceReference = {
24
26
  bytes: new Uint8Array([0x89, 0x50, 0x4e, 0x47]),
25
27
  failure: null,
26
28
  kind: 'Embedded',
27
29
  mimeType: 'image/png',
28
30
  state: ResourceResolutionState.Unresolved,
29
31
  };
32
+ testResources.push(ref);
33
+ return ref;
34
+ }
35
+
36
+ function resourceOf(texture: Texture): EmbeddedImageResourceReference {
37
+ return testResources.find((ref) => ref.textures?.includes(texture) === true)!;
38
+ }
39
+
40
+ function configureResources(scene: Scene3D): void {
41
+ scene.resources = testResources.slice();
30
42
  }
31
43
 
32
44
  function sceneWithPendingTexture(): { mesh: Node3D; scene: Scene3D; texture: Texture } {
@@ -34,6 +46,7 @@ function sceneWithPendingTexture(): { mesh: Node3D; scene: Scene3D; texture: Tex
34
46
  const material = createStandardPbrMaterial({ baseColorMap: texture });
35
47
  const mesh = createMesh(createBoxMeshGeometry(), [material]);
36
48
  const scene = createScene3D();
49
+ configureResources(scene);
37
50
  addNodeChild(scene.root, mesh);
38
51
  return { mesh, scene, texture };
39
52
  }
@@ -42,14 +55,14 @@ describe('revealScene3DResourcesOnResolve', () => {
42
55
  it('hides every object carrying a pending texture to the start opacity up front', () => {
43
56
  const { mesh, scene } = sceneWithPendingTexture();
44
57
  const resolver = createBuiltInScene3DResourceResolver();
45
- revealScene3DResourcesOnResolve(resolver, scene.root, createTweenManager());
58
+ revealScene3DResourcesOnResolve(resolver, scene, createTweenManager());
46
59
  expect(mesh.alpha).toBe(0);
47
60
  });
48
61
 
49
62
  it('honors a custom from opacity', () => {
50
63
  const { mesh, scene } = sceneWithPendingTexture();
51
64
  const resolver = createBuiltInScene3DResourceResolver();
52
- revealScene3DResourcesOnResolve(resolver, scene.root, createTweenManager(), { from: 0.2 });
65
+ revealScene3DResourcesOnResolve(resolver, scene, createTweenManager(), { from: 0.2 });
53
66
  expect(mesh.alpha).toBeCloseTo(0.2);
54
67
  });
55
68
 
@@ -57,11 +70,11 @@ describe('revealScene3DResourcesOnResolve', () => {
57
70
  const { mesh, scene, texture } = sceneWithPendingTexture();
58
71
  const resolver = createBuiltInScene3DResourceResolver();
59
72
  const manager = createTweenManager();
60
- revealScene3DResourcesOnResolve(resolver, scene.root, manager, { fadeSeconds: 0.5 });
73
+ revealScene3DResourcesOnResolve(resolver, scene, manager, { fadeSeconds: 0.5 });
61
74
  expect(mesh.alpha).toBe(0);
62
75
 
63
76
  const signals = enableScene3DResourceSignals(resolver);
64
- const event: Scene3DResourceEvent = { ref: texture.resource!, texture };
77
+ const event: Scene3DResourceEvent = { ref: resourceOf(texture), texture };
65
78
  emitSignal(signals.onResourceResolved, event);
66
79
  expect(hasTweensOf(manager, mesh)).toBe(true);
67
80
 
@@ -75,16 +88,17 @@ describe('revealScene3DResourcesOnResolve', () => {
75
88
  const material = createStandardPbrMaterial({ baseColorMap, normalMap });
76
89
  const mesh = createMesh(createBoxMeshGeometry(), [material]);
77
90
  const scene = createScene3D();
91
+ configureResources(scene);
78
92
  addNodeChild(scene.root, mesh);
79
93
  const resolver = createBuiltInScene3DResourceResolver();
80
94
  const manager = createTweenManager();
81
- revealScene3DResourcesOnResolve(resolver, scene.root, manager, { fadeSeconds: 0.5 });
95
+ revealScene3DResourcesOnResolve(resolver, scene, manager, { fadeSeconds: 0.5 });
82
96
 
83
97
  const signals = enableScene3DResourceSignals(resolver);
84
- emitSignal(signals.onResourceResolved, { ref: baseColorMap.resource!, texture: baseColorMap });
98
+ emitSignal(signals.onResourceResolved, { ref: resourceOf(baseColorMap), texture: baseColorMap });
85
99
  expect(hasTweensOf(manager, mesh)).toBe(false);
86
100
 
87
- emitSignal(signals.onResourceResolved, { ref: normalMap.resource!, texture: normalMap });
101
+ emitSignal(signals.onResourceResolved, { ref: resourceOf(normalMap), texture: normalMap });
88
102
  expect(hasTweensOf(manager, mesh)).toBe(true);
89
103
  });
90
104
 
@@ -94,23 +108,25 @@ describe('revealScene3DResourcesOnResolve', () => {
94
108
  const material = createStandardPbrMaterial({ baseColorMap, normalMap });
95
109
  const mesh = createMesh(createBoxMeshGeometry(), [material]);
96
110
  const scene = createScene3D();
111
+ configureResources(scene);
97
112
  addNodeChild(scene.root, mesh);
98
113
  const resolver = createBuiltInScene3DResourceResolver();
99
114
  const manager = createTweenManager();
100
- revealScene3DResourcesOnResolve(resolver, scene.root, manager, { fadeSeconds: 0.5 });
115
+ revealScene3DResourcesOnResolve(resolver, scene, manager, { fadeSeconds: 0.5 });
101
116
 
102
117
  const signals = enableScene3DResourceSignals(resolver);
103
- emitSignal(signals.onResourceFailed, { ref: normalMap.resource!, texture: normalMap });
118
+ emitSignal(signals.onResourceFailed, { ref: resourceOf(normalMap), texture: normalMap });
104
119
  expect(hasTweensOf(manager, mesh)).toBe(false);
105
120
 
106
- emitSignal(signals.onResourceResolved, { ref: baseColorMap.resource!, texture: baseColorMap });
121
+ emitSignal(signals.onResourceResolved, { ref: resourceOf(baseColorMap), texture: baseColorMap });
107
122
  expect(hasTweensOf(manager, mesh)).toBe(true);
108
123
  });
109
124
 
110
125
  it('does not hide an owner whose resource was already bound or had already failed', () => {
111
126
  const bound = createTexture({
112
127
  resource: pendingRef(),
113
- storage: { dimension: '2d', image: { height: 1, width: 1 } as ImageResource },
128
+ dimension: '2d',
129
+ source: { height: 1, kind: ImageTextureSourceKind, width: 1 } as Image,
114
130
  });
115
131
  const failedRef = pendingRef();
116
132
  failedRef.state = ResourceResolutionState.Failed;
@@ -118,9 +134,10 @@ describe('revealScene3DResourcesOnResolve', () => {
118
134
  const material = createStandardPbrMaterial({ baseColorMap: bound, normalMap: failed });
119
135
  const mesh = createMesh(createBoxMeshGeometry(), [material]);
120
136
  const scene = createScene3D();
137
+ configureResources(scene);
121
138
  addNodeChild(scene.root, mesh);
122
139
 
123
- revealScene3DResourcesOnResolve(createBuiltInScene3DResourceResolver(), scene.root, createTweenManager());
140
+ revealScene3DResourcesOnResolve(createBuiltInScene3DResourceResolver(), scene, createTweenManager());
124
141
  expect(mesh.alpha).toBe(1);
125
142
  });
126
143
 
@@ -129,13 +146,14 @@ describe('revealScene3DResourcesOnResolve', () => {
129
146
  const a = createMesh(createBoxMeshGeometry(), [createStandardPbrMaterial({ baseColorMap: texture })]);
130
147
  const b = createMesh(createBoxMeshGeometry(), [createStandardPbrMaterial({ baseColorMap: texture })]);
131
148
  const scene = createScene3D();
149
+ configureResources(scene);
132
150
  addNodeChild(scene.root, a);
133
151
  addNodeChild(scene.root, b);
134
152
  const resolver = createBuiltInScene3DResourceResolver();
135
153
  const manager = createTweenManager();
136
- revealScene3DResourcesOnResolve(resolver, scene.root, manager);
154
+ revealScene3DResourcesOnResolve(resolver, scene, manager);
137
155
 
138
- emitSignal(enableScene3DResourceSignals(resolver).onResourceResolved, { ref: texture.resource!, texture });
156
+ emitSignal(enableScene3DResourceSignals(resolver).onResourceResolved, { ref: resourceOf(texture), texture });
139
157
  expect(hasTweensOf(manager, a)).toBe(true);
140
158
  expect(hasTweensOf(manager, b)).toBe(true);
141
159
  });
@@ -144,10 +162,10 @@ describe('revealScene3DResourcesOnResolve', () => {
144
162
  const { scene } = sceneWithPendingTexture();
145
163
  const resolver = createBuiltInScene3DResourceResolver();
146
164
  const manager = createTweenManager();
147
- revealScene3DResourcesOnResolve(resolver, scene.root, manager, { fadeSeconds: 0.5 });
165
+ revealScene3DResourcesOnResolve(resolver, scene, manager, { fadeSeconds: 0.5 });
148
166
 
149
167
  const stray = createTexture({ resource: pendingRef() });
150
- emitSignal(enableScene3DResourceSignals(resolver).onResourceResolved, { ref: stray.resource!, texture: stray });
168
+ emitSignal(enableScene3DResourceSignals(resolver).onResourceResolved, { ref: resourceOf(stray), texture: stray });
151
169
  expect(hasTweensOf(manager, stray as unknown as object)).toBe(false);
152
170
  });
153
171
 
@@ -155,10 +173,10 @@ describe('revealScene3DResourcesOnResolve', () => {
155
173
  const { mesh, scene, texture } = sceneWithPendingTexture();
156
174
  const resolver = createBuiltInScene3DResourceResolver();
157
175
  const manager = createTweenManager();
158
- const dispose = revealScene3DResourcesOnResolve(resolver, scene.root, manager, { fadeSeconds: 0.5 });
176
+ const dispose = revealScene3DResourcesOnResolve(resolver, scene, manager, { fadeSeconds: 0.5 });
159
177
  dispose();
160
178
 
161
- emitSignal(enableScene3DResourceSignals(resolver).onResourceResolved, { ref: texture.resource!, texture });
179
+ emitSignal(enableScene3DResourceSignals(resolver).onResourceResolved, { ref: resourceOf(texture), texture });
162
180
  expect(hasTweensOf(manager, mesh)).toBe(false);
163
181
  });
164
182
  });
@@ -1,13 +1,29 @@
1
- import { createStandardPbrMaterial, createUnlitMaterial } from '@flighthq/materials/contract';
1
+ import {
2
+ createAnisotropyPbrExtension,
3
+ createExtendedPbrMaterial,
4
+ createStandardPbrMaterial,
5
+ createStandardPbrMaterialProperties,
6
+ createUnlitMaterial,
7
+ } from '@flighthq/materials/contract';
2
8
  import { createTexture } from '@flighthq/texture/contract';
3
9
  import type { Material, Texture } from '@flighthq/types/contract';
4
- import { EntityRuntimeKey, StandardPbrMaterialKind, UnlitMaterialKind } from '@flighthq/types/contract';
10
+ import {
11
+ AnisotropyPbrExtensionKind,
12
+ EntityRuntimeKey,
13
+ ExtendedPbrMaterialKind,
14
+ StandardPbrMaterialKind,
15
+ UnlitMaterialKind,
16
+ } from '@flighthq/types/contract';
5
17
  import { describe, expect, it } from 'vitest';
6
18
 
7
19
  import {
8
20
  createScene3DMaterialTextureRegistry,
9
21
  getScene3DMaterialTextures,
10
- registerBuiltInScene3DMaterialTextures,
22
+ hasScene3DMaterialTextureLister,
23
+ registerStandardPbrScene3DMaterialTextures,
24
+ registerUnlitScene3DMaterialTextures,
25
+ registerExtendedPbrScene3DMaterialTextures,
26
+ registerScene3DPbrExtensionTextures,
11
27
  registerScene3DMaterialTextures,
12
28
  } from './sceneMaterialTextureRegistry';
13
29
 
@@ -15,6 +31,7 @@ describe('createScene3DMaterialTextureRegistry', () => {
15
31
  it('creates an empty registry', () => {
16
32
  const registry = createScene3DMaterialTextureRegistry();
17
33
  expect(EntityRuntimeKey in registry).toBe(true);
34
+ expect(registry.extensionListers.size).toBe(0);
18
35
  expect(registry.listers.size).toBe(0);
19
36
  });
20
37
  });
@@ -29,7 +46,9 @@ describe('getScene3DMaterialTextures', () => {
29
46
 
30
47
  it('accumulates across calls without clearing out', () => {
31
48
  const registry = createScene3DMaterialTextureRegistry();
32
- registerBuiltInScene3DMaterialTextures(registry);
49
+ registerStandardPbrScene3DMaterialTextures(registry);
50
+ registerUnlitScene3DMaterialTextures(registry);
51
+ registerExtendedPbrScene3DMaterialTextures(registry);
33
52
  const a = createTexture();
34
53
  const b = createTexture();
35
54
  const out: Texture[] = [];
@@ -39,34 +58,37 @@ describe('getScene3DMaterialTextures', () => {
39
58
  });
40
59
  });
41
60
 
42
- describe('registerBuiltInScene3DMaterialTextures', () => {
43
- it('registers the standard-pbr and unlit listers', () => {
44
- const registry = createScene3DMaterialTextureRegistry();
45
- registerBuiltInScene3DMaterialTextures(registry);
46
- expect(registry.listers.has(StandardPbrMaterialKind)).toBe(true);
47
- expect(registry.listers.has(UnlitMaterialKind)).toBe(true);
48
- });
49
-
50
- it('lists every non-null standard-pbr texture slot and skips null ones', () => {
61
+ describe('hasScene3DMaterialTextureLister', () => {
62
+ it('separates an unregistered kind from a material that genuinely has no maps', () => {
63
+ // getScene3DMaterialTextures appends nothing in both cases, so this is the only way to tell them
64
+ // apart — which is what explainScene3DResourceCoverage reports through.
51
65
  const registry = createScene3DMaterialTextureRegistry();
52
- registerBuiltInScene3DMaterialTextures(registry);
53
- const baseColorMap = createTexture();
54
- const normalMap = createTexture();
55
- const material = createStandardPbrMaterial({ baseColorMap, normalMap });
56
- const out: Texture[] = [];
57
- getScene3DMaterialTextures(registry, material, out);
58
- expect(out).toContain(baseColorMap);
59
- expect(out).toContain(normalMap);
60
- expect(out).toHaveLength(2);
66
+ registerUnlitScene3DMaterialTextures(registry);
67
+ expect(hasScene3DMaterialTextureLister(registry, UnlitMaterialKind)).toBe(true);
68
+ expect(hasScene3DMaterialTextureLister(registry, StandardPbrMaterialKind)).toBe(false);
61
69
  });
70
+ });
62
71
 
63
- it('lists the unlit base-color slot', () => {
72
+ describe('registerExtendedPbrScene3DMaterialTextures', () => {
73
+ it('lists standard maps and dispatches extensions through the nested kind registry', () => {
64
74
  const registry = createScene3DMaterialTextureRegistry();
65
- registerBuiltInScene3DMaterialTextures(registry);
75
+ registerExtendedPbrScene3DMaterialTextures(registry);
66
76
  const baseColorMap = createTexture();
77
+ const anisotropyMap = createTexture();
78
+ registerScene3DPbrExtensionTextures(registry, AnisotropyPbrExtensionKind, (extension, out) => {
79
+ const anisotropy = extension as ReturnType<typeof createAnisotropyPbrExtension>;
80
+ if (anisotropy.anisotropyMap !== null) out.push(anisotropy.anisotropyMap);
81
+ });
67
82
  const out: Texture[] = [];
68
- getScene3DMaterialTextures(registry, createUnlitMaterial({ baseColorMap }), out);
69
- expect(out).toEqual([baseColorMap]);
83
+ getScene3DMaterialTextures(
84
+ registry,
85
+ createExtendedPbrMaterial({
86
+ extensions: [createAnisotropyPbrExtension({ anisotropyMap })],
87
+ standard: createStandardPbrMaterialProperties({ baseColorMap }),
88
+ }),
89
+ out,
90
+ );
91
+ expect(out).toEqual([baseColorMap, anisotropyMap]);
70
92
  });
71
93
  });
72
94
 
@@ -93,3 +115,73 @@ describe('registerScene3DMaterialTextures', () => {
93
115
  expect(out).toEqual([second]);
94
116
  });
95
117
  });
118
+
119
+ describe('registerScene3DPbrExtensionTextures', () => {
120
+ it('is last-write-wins for vendor extension listers', () => {
121
+ const registry = createScene3DMaterialTextureRegistry();
122
+ const first = createTexture();
123
+ const second = createTexture();
124
+ registerScene3DPbrExtensionTextures(registry, AnisotropyPbrExtensionKind, (_extension, out) => out.push(first));
125
+ registerScene3DPbrExtensionTextures(registry, AnisotropyPbrExtensionKind, (_extension, out) => out.push(second));
126
+ const out: Texture[] = [];
127
+ registry.extensionListers.get(AnisotropyPbrExtensionKind)?.(createAnisotropyPbrExtension(), out);
128
+ expect(out).toEqual([second]);
129
+ });
130
+ });
131
+
132
+ describe('registerStandardPbrScene3DMaterialTextures', () => {
133
+ it('composes with the sibling doors to cover the three surface families', () => {
134
+ // What createBuiltInScene3DResourceResolver now spells out in its own body, rather than hiding it
135
+ // behind one registrar whose name did not say which families it covered.
136
+ const registry = createScene3DMaterialTextureRegistry();
137
+ registerStandardPbrScene3DMaterialTextures(registry);
138
+ registerUnlitScene3DMaterialTextures(registry);
139
+ registerExtendedPbrScene3DMaterialTextures(registry);
140
+ expect(registry.listers.has(ExtendedPbrMaterialKind)).toBe(true);
141
+ expect(registry.listers.has(StandardPbrMaterialKind)).toBe(true);
142
+ expect(registry.listers.has(UnlitMaterialKind)).toBe(true);
143
+ });
144
+
145
+ it('lists every non-null standard-pbr texture slot and skips null ones', () => {
146
+ const registry = createScene3DMaterialTextureRegistry();
147
+ registerStandardPbrScene3DMaterialTextures(registry);
148
+ registerUnlitScene3DMaterialTextures(registry);
149
+ registerExtendedPbrScene3DMaterialTextures(registry);
150
+ const baseColorMap = createTexture();
151
+ const normalMap = createTexture();
152
+ const material = createStandardPbrMaterial({ baseColorMap, normalMap });
153
+ const out: Texture[] = [];
154
+ getScene3DMaterialTextures(registry, material, out);
155
+ expect(out).toContain(baseColorMap);
156
+ expect(out).toContain(normalMap);
157
+ expect(out).toHaveLength(2);
158
+ });
159
+
160
+ it('lists the unlit base-color slot', () => {
161
+ const registry = createScene3DMaterialTextureRegistry();
162
+ registerStandardPbrScene3DMaterialTextures(registry);
163
+ registerUnlitScene3DMaterialTextures(registry);
164
+ registerExtendedPbrScene3DMaterialTextures(registry);
165
+ const baseColorMap = createTexture();
166
+ const out: Texture[] = [];
167
+ getScene3DMaterialTextures(registry, createUnlitMaterial({ baseColorMap }), out);
168
+ expect(out).toEqual([baseColorMap]);
169
+ });
170
+ });
171
+
172
+ describe('registerUnlitScene3DMaterialTextures', () => {
173
+ it('registers only the Unlit lister, leaving other kinds unlisted', () => {
174
+ // The point of splitting the built-in bag apart: each door registers exactly the family it names,
175
+ // so a registry holds what the caller asked for and nothing else.
176
+ const registry = createScene3DMaterialTextureRegistry();
177
+ registerUnlitScene3DMaterialTextures(registry);
178
+ const baseColorMap = createTexture();
179
+ const out: Texture[] = [];
180
+ getScene3DMaterialTextures(registry, createUnlitMaterial({ baseColorMap }), out);
181
+ expect(out).toEqual([baseColorMap]);
182
+
183
+ out.length = 0;
184
+ getScene3DMaterialTextures(registry, createStandardPbrMaterial({ baseColorMap }), out);
185
+ expect(out).toEqual([]);
186
+ });
187
+ });
@@ -2,8 +2,8 @@ import { createUnlitMaterial } from '@flighthq/materials/contract';
2
2
  import { createBoxMeshGeometry } from '@flighthq/mesh/contract';
3
3
  import { addNodeChild } from '@flighthq/node/contract';
4
4
  import { createMesh, createScene3D } from '@flighthq/scene3d/contract';
5
- import { createTexture } from '@flighthq/texture/contract';
6
- import type { ImageResource, ImageResourceReference } from '@flighthq/types/contract';
5
+ import { createTexture, getTextureSource } from '@flighthq/texture/contract';
6
+ import type { Image, ImageResourceReference } from '@flighthq/types/contract';
7
7
  import {
8
8
  ImageResourceFailureKind,
9
9
  ImageResourceReferenceKind,
@@ -11,16 +11,11 @@ import {
11
11
  } from '@flighthq/types/contract';
12
12
  import { describe, expect, it, vi } from 'vitest';
13
13
 
14
- import { loadScene3DResources } from './resolveScene3DResourcesAndWait';
15
- import { waitForScene3DResourceResolver } from './resolveScene3DResourcesAndWait';
16
- import {
17
- explainImageResourceReferenceResolution,
18
- resetFailedImageResourceReference,
19
- retryFailedScene3DResources,
20
- } from './sceneResourceRecovery';
14
+ import { loadScene3DResources, waitForScene3DResourceResolver } from './loadScene3DResources';
15
+ import { retryFailedScene3DResources } from './sceneResourceRecovery';
21
16
  import { createBuiltInScene3DResourceResolver, disposeScene3DResourceResolver } from './sceneResourceResolver';
22
17
 
23
- const fakeImage = { height: 1, width: 1 } as ImageResource;
18
+ const fakeImage = { height: 1, width: 1 } as Image;
24
19
 
25
20
  function externalRef(state: ResourceResolutionState = ResourceResolutionState.Unresolved): ImageResourceReference {
26
21
  return {
@@ -33,58 +28,28 @@ function externalRef(state: ResourceResolutionState = ResourceResolutionState.Un
33
28
  };
34
29
  }
35
30
 
36
- describe('explainImageResourceReferenceResolution', () => {
37
- it('returns detached plain data with retry eligibility and the preserved cause', () => {
38
- const ref = externalRef(ResourceResolutionState.Failed);
39
- ref.failure = { kind: ImageResourceFailureKind.Error, message: 'decode failed', name: 'CodecError' };
40
- const explanation = explainImageResourceReferenceResolution(ref);
41
- expect(explanation).toEqual({
42
- failure: { kind: ImageResourceFailureKind.Error, message: 'decode failed', name: 'CodecError' },
43
- kind: ImageResourceReferenceKind.External,
44
- retryable: true,
45
- state: ResourceResolutionState.Failed,
46
- });
47
- expect(explanation.failure).not.toBe(ref.failure);
48
- });
49
- });
50
-
51
- describe('resetFailedImageResourceReference', () => {
52
- it('clears a failed cause and returns the reference to Unresolved', () => {
53
- const ref = externalRef(ResourceResolutionState.Failed);
54
- ref.failure = { kind: ImageResourceFailureKind.Unavailable, message: 'missing', name: null };
55
- expect(resetFailedImageResourceReference(ref)).toBe(true);
56
- expect(ref.state).toBe(ResourceResolutionState.Unresolved);
57
- expect(ref.failure).toBeNull();
58
- });
59
-
60
- it('does not invalidate a non-failed reference', () => {
61
- const ref = externalRef(ResourceResolutionState.Resolved);
62
- expect(resetFailedImageResourceReference(ref)).toBe(false);
63
- expect(ref.state).toBe(ResourceResolutionState.Resolved);
64
- });
65
- });
66
-
67
31
  describe('retryFailedScene3DResources', () => {
68
32
  it('resets one shared failed identity and re-requests it for every subscriber', async () => {
69
- const fetch = vi.fn<() => Promise<ImageResource | null>>().mockResolvedValueOnce(null).mockResolvedValue(fakeImage);
33
+ const fetch = vi.fn<() => Promise<Image | null>>().mockResolvedValueOnce(null).mockResolvedValue(fakeImage);
70
34
  const ref = externalRef();
71
35
  const a = createTexture({ resource: ref });
72
36
  const b = createTexture({ resource: ref });
73
37
  const scene = createScene3D();
38
+ scene.resources.push(ref);
74
39
  addNodeChild(scene.root, createMesh(createBoxMeshGeometry(), [createUnlitMaterial({ baseColorMap: a })]));
75
40
  addNodeChild(scene.root, createMesh(createBoxMeshGeometry(), [createUnlitMaterial({ baseColorMap: b })]));
76
41
  const resolver = createBuiltInScene3DResourceResolver({ fetch });
77
42
 
78
- await loadScene3DResources(scene.root, resolver);
43
+ await loadScene3DResources(scene, resolver);
79
44
  expect(ref.state).toBe(ResourceResolutionState.Failed);
80
45
  expect(ref.failure?.kind).toBe(ImageResourceFailureKind.Unavailable);
81
46
 
82
- expect(retryFailedScene3DResources(scene.root, resolver)).toBe(1);
47
+ expect(retryFailedScene3DResources(scene, resolver)).toBe(1);
83
48
  await waitForScene3DResourceResolver(resolver);
84
49
  expect(fetch).toHaveBeenCalledTimes(2);
85
50
  expect(ref.failure).toBeNull();
86
- expect(a.storage.image).toBe(fakeImage);
87
- expect(b.storage.image).toBe(fakeImage);
51
+ expect(getTextureSource(a)).toBe(fakeImage);
52
+ expect(getTextureSource(b)).toBe(fakeImage);
88
53
  disposeScene3DResourceResolver(resolver);
89
54
  });
90
55
 
@@ -93,9 +58,10 @@ describe('retryFailedScene3DResources', () => {
93
58
  ref.failure = { kind: ImageResourceFailureKind.Unavailable, message: 'missing', name: null };
94
59
  const texture = createTexture({ resource: ref });
95
60
  const scene = createScene3D();
61
+ scene.resources.push(ref);
96
62
  addNodeChild(scene.root, createMesh(createBoxMeshGeometry(), [createUnlitMaterial({ baseColorMap: texture })]));
97
63
  const resolver = createBuiltInScene3DResourceResolver();
98
- expect(retryFailedScene3DResources(scene.root, resolver, { select: () => false })).toBe(0);
64
+ expect(retryFailedScene3DResources(scene, resolver, { select: () => false })).toBe(0);
99
65
  expect(ref.state).toBe(ResourceResolutionState.Failed);
100
66
  disposeScene3DResourceResolver(resolver);
101
67
  });
@@ -7,7 +7,11 @@ import {
7
7
  UnlitMaterialKind,
8
8
  } from '@flighthq/types/contract';
9
9
  import { Scene3DResourceResolverRuntimeKey } from '@flighthq/types/contract';
10
- import type { Scene3DResourceInFlight, Scene3DResourceResolverWithRuntime } from '@flighthq/types/contract';
10
+ import type {
11
+ ImageResourceReference,
12
+ Scene3DResourceInFlight,
13
+ Scene3DResourceResolverWithRuntime,
14
+ } from '@flighthq/types/contract';
11
15
  import { describe, expect, it } from 'vitest';
12
16
 
13
17
  import { createScene3DMaterialTextureRegistry } from './sceneMaterialTextureRegistry';
@@ -56,22 +60,21 @@ describe('disposeScene3DResourceResolver', () => {
56
60
  it('aborts every in-flight controller and clears the map', () => {
57
61
  const resolver = createScene3DResourceResolver();
58
62
  const controller = new AbortController();
59
- const texture = createTexture({
60
- resource: {
61
- bytes: new Uint8Array(0),
62
- failure: null,
63
- kind: ImageResourceReferenceKind.Embedded,
64
- mimeType: null,
65
- state: ResourceResolutionState.Loading,
66
- },
67
- });
63
+ const ref = {
64
+ bytes: new Uint8Array(0),
65
+ failure: null,
66
+ kind: ImageResourceReferenceKind.Embedded,
67
+ mimeType: null,
68
+ state: ResourceResolutionState.Loading,
69
+ } as ImageResourceReference;
70
+ const texture = createTexture({ resource: ref });
68
71
  const entry: Scene3DResourceInFlight = {
69
72
  controller,
70
73
  promise: Promise.resolve(),
71
74
  subscribers: new Set([texture]),
72
75
  };
73
76
  const runtime = (resolver as Scene3DResourceResolverWithRuntime)[Scene3DResourceResolverRuntimeKey];
74
- runtime.inFlight.set(texture.resource!, entry);
77
+ runtime.inFlight.set(ref, entry);
75
78
 
76
79
  disposeScene3DResourceResolver(resolver);
77
80
  expect(controller.signal.aborted).toBe(true);
@@ -39,7 +39,7 @@ describe('scene resource resolver tree shaking', () => {
39
39
 
40
40
  it('includes failure logging only through the separately imported guard', async () => {
41
41
  const output = await bundleScene3DResourceResolverExport('enableScene3DResourceFailureGuards');
42
- expect(output).toContain('image resource resolution failed');
42
+ expect(output).toContain('Scene3D resource acquisition failed');
43
43
  });
44
44
  });
45
45
 
@@ -0,0 +1,52 @@
1
+ import { createShadedMaterial } from '@flighthq/shading/contract';
2
+ import { createTexture } from '@flighthq/texture/contract';
3
+ import type { Texture } from '@flighthq/types/contract';
4
+
5
+ import { createScene3DMaterialTextureRegistry, getScene3DMaterialTextures } from './sceneMaterialTextureRegistry';
6
+ import { registerShadedScene3DMaterialTextures } from './shadedScene3DMaterialTextures';
7
+
8
+ describe('registerShadedScene3DMaterialTextures', () => {
9
+ it('lists the diffuse, normal and specular maps', () => {
10
+ const registry = createScene3DMaterialTextureRegistry();
11
+ registerShadedScene3DMaterialTextures(registry);
12
+ const diffuse = createTexture();
13
+ const normal = createTexture();
14
+ const specular = createTexture();
15
+ const out: Texture[] = [];
16
+ getScene3DMaterialTextures(
17
+ registry,
18
+ createShadedMaterial({ diffuseMap: diffuse, normalMap: normal, specularMap: specular }),
19
+ out,
20
+ );
21
+ expect(out).toEqual([diffuse, normal, specular]);
22
+ });
23
+
24
+ it('skips the empty slots of a map-less material', () => {
25
+ const registry = createScene3DMaterialTextureRegistry();
26
+ registerShadedScene3DMaterialTextures(registry);
27
+ const out: Texture[] = [];
28
+ getScene3DMaterialTextures(registry, createShadedMaterial(), out);
29
+ expect(out).toEqual([]);
30
+ });
31
+
32
+ it('appends without clearing, so one mesh accumulates across its materials', () => {
33
+ const registry = createScene3DMaterialTextureRegistry();
34
+ registerShadedScene3DMaterialTextures(registry);
35
+ const first = createTexture();
36
+ const second = createTexture();
37
+ const out: Texture[] = [];
38
+ getScene3DMaterialTextures(registry, createShadedMaterial({ diffuseMap: first }), out);
39
+ getScene3DMaterialTextures(registry, createShadedMaterial({ specularMap: second }), out);
40
+ expect(out).toEqual([first, second]);
41
+ });
42
+
43
+ it('appends nothing for a ShadedMaterial while unregistered', () => {
44
+ // The registry cannot distinguish this from a material with no maps, which is why callers ask
45
+ // hasScene3DMaterialTextureLister instead and why getScene3DResourceTextures widens rather than
46
+ // trusting an empty answer.
47
+ const registry = createScene3DMaterialTextureRegistry();
48
+ const out: Texture[] = [];
49
+ getScene3DMaterialTextures(registry, createShadedMaterial({ diffuseMap: createTexture() }), out);
50
+ expect(out).toEqual([]);
51
+ });
52
+ });
@@ -0,0 +1,29 @@
1
+ import { createExtendedPbrMaterial, createSheenPbrExtension } from '@flighthq/materials/contract';
2
+ import { createTexture } from '@flighthq/texture/contract';
3
+ import type { Texture } from '@flighthq/types/contract';
4
+
5
+ import {
6
+ createScene3DMaterialTextureRegistry,
7
+ getScene3DMaterialTextures,
8
+ registerExtendedPbrScene3DMaterialTextures,
9
+ } from './sceneMaterialTextureRegistry';
10
+ import { registerSheenPbrScene3DMaterialTextures } from './sheenPbrScene3DMaterialTextures';
11
+
12
+ describe('registerSheenPbrScene3DMaterialTextures', () => {
13
+ it('lists the sheen color and roughness maps', () => {
14
+ const registry = createScene3DMaterialTextureRegistry();
15
+ registerExtendedPbrScene3DMaterialTextures(registry);
16
+ registerSheenPbrScene3DMaterialTextures(registry);
17
+ const color = createTexture();
18
+ const roughness = createTexture();
19
+ const out: Texture[] = [];
20
+ getScene3DMaterialTextures(
21
+ registry,
22
+ createExtendedPbrMaterial({
23
+ extensions: [createSheenPbrExtension({ sheenColorMap: color, sheenRoughnessMap: roughness })],
24
+ }),
25
+ out,
26
+ );
27
+ expect(out).toEqual([color, roughness]);
28
+ });
29
+ });
@@ -0,0 +1,29 @@
1
+ import { createExtendedPbrMaterial, createSpecularPbrExtension } from '@flighthq/materials/contract';
2
+ import { createTexture } from '@flighthq/texture/contract';
3
+ import type { Texture } from '@flighthq/types/contract';
4
+
5
+ import {
6
+ createScene3DMaterialTextureRegistry,
7
+ getScene3DMaterialTextures,
8
+ registerExtendedPbrScene3DMaterialTextures,
9
+ } from './sceneMaterialTextureRegistry';
10
+ import { registerSpecularPbrScene3DMaterialTextures } from './specularPbrScene3DMaterialTextures';
11
+
12
+ describe('registerSpecularPbrScene3DMaterialTextures', () => {
13
+ it('lists the specular factor and color maps', () => {
14
+ const registry = createScene3DMaterialTextureRegistry();
15
+ registerExtendedPbrScene3DMaterialTextures(registry);
16
+ registerSpecularPbrScene3DMaterialTextures(registry);
17
+ const factor = createTexture();
18
+ const color = createTexture();
19
+ const out: Texture[] = [];
20
+ getScene3DMaterialTextures(
21
+ registry,
22
+ createExtendedPbrMaterial({
23
+ extensions: [createSpecularPbrExtension({ specularColorMap: color, specularMap: factor })],
24
+ }),
25
+ out,
26
+ );
27
+ expect(out).toEqual([factor, color]);
28
+ });
29
+ });
@@ -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