@forgeax/engine-gltf 0.1.21 → 0.1.24

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 (68) hide show
  1. package/README.md +115 -3
  2. package/dist/__tests__/lod-import.integration.test.d.ts +2 -0
  3. package/dist/__tests__/lod-import.integration.test.d.ts.map +1 -0
  4. package/dist/__tests__/lod-meta-publication.integration.test.d.ts +2 -0
  5. package/dist/__tests__/lod-meta-publication.integration.test.d.ts.map +1 -0
  6. package/dist/__tests__/physical-clearcoat.unit.test.d.ts +2 -0
  7. package/dist/__tests__/physical-clearcoat.unit.test.d.ts.map +1 -0
  8. package/dist/__tests__/physical-material-import.integration.test.d.ts +2 -0
  9. package/dist/__tests__/physical-material-import.integration.test.d.ts.map +1 -0
  10. package/dist/bridge.d.ts +1 -0
  11. package/dist/bridge.d.ts.map +1 -1
  12. package/dist/check-extensions.d.ts +1 -1
  13. package/dist/check-extensions.d.ts.map +1 -1
  14. package/dist/cli-gltf.mjs +362 -14
  15. package/dist/cli-gltf.mjs.map +1 -1
  16. package/dist/errors.d.ts +13 -0
  17. package/dist/errors.d.ts.map +1 -1
  18. package/dist/gltf-importer.d.ts.map +1 -1
  19. package/dist/image-color-space.d.ts +12 -0
  20. package/dist/image-color-space.d.ts.map +1 -1
  21. package/dist/importer-entry.mjs +516 -25
  22. package/dist/importer-entry.mjs.map +1 -1
  23. package/dist/index.d.ts +4 -2
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.mjs +609 -28
  26. package/dist/index.mjs.map +1 -1
  27. package/dist/lod/parse-lod.d.ts +15 -0
  28. package/dist/lod/parse-lod.d.ts.map +1 -0
  29. package/dist/lod/project-meta.d.ts +23 -0
  30. package/dist/lod/project-meta.d.ts.map +1 -0
  31. package/dist/material/parse-material.d.ts +66 -0
  32. package/dist/material/parse-material.d.ts.map +1 -1
  33. package/dist/node-file-entry.mjs +265 -8
  34. package/dist/node-file-entry.mjs.map +1 -1
  35. package/dist/parse-gltf.d.ts +3 -1
  36. package/dist/parse-gltf.d.ts.map +1 -1
  37. package/dist/reimport-reuse-meta.d.ts +1 -1
  38. package/dist/reimport-reuse-meta.d.ts.map +1 -1
  39. package/package.json +12 -12
  40. package/src/__tests__/bridge-material-values.unit.test.ts +84 -1
  41. package/src/__tests__/bridge-skin-stride.unit.test.ts +22 -0
  42. package/src/__tests__/fixtures/lod/core-only.gltf +1 -0
  43. package/src/__tests__/fixtures/lod/msft-lod-malformed.gltf +1 -0
  44. package/src/__tests__/fixtures/lod/msft-lod-multi-root.gltf +1 -0
  45. package/src/__tests__/fixtures/lod/msft-lod-node-extras.gltf +1 -0
  46. package/src/__tests__/fixtures/lod/msft-lod.gltf +1 -0
  47. package/src/__tests__/gltf-error-derived-views.test-d.ts +8 -0
  48. package/src/__tests__/gltf-error-migration.test.ts +3 -3
  49. package/src/__tests__/gltf.unit.test.ts +60 -5
  50. package/src/__tests__/lod-import.integration.test.ts +106 -0
  51. package/src/__tests__/lod-meta-publication.integration.test.ts +24 -0
  52. package/src/__tests__/material-pack-refs.integration.test.ts +53 -0
  53. package/src/__tests__/parse-gltf.unit.test.ts +50 -0
  54. package/src/__tests__/physical-clearcoat.unit.test.ts +110 -0
  55. package/src/__tests__/physical-material-import.integration.test.ts +239 -0
  56. package/src/__tests__/source-key-producer.integration.test.ts +6 -2
  57. package/src/bridge.ts +259 -14
  58. package/src/check-extensions.ts +16 -8
  59. package/src/errors.ts +32 -2
  60. package/src/gltf-importer.ts +122 -1
  61. package/src/image-color-space.ts +29 -4
  62. package/src/index.ts +10 -1
  63. package/src/lod/parse-lod.ts +129 -0
  64. package/src/lod/project-meta.ts +48 -0
  65. package/src/material/parse-material.ts +307 -0
  66. package/src/parse-gltf.ts +97 -1
  67. package/src/reimport-reuse-meta.ts +3 -1
  68. package/dist/.tsbuildinfo +0 -1
@@ -1,6 +1,7 @@
1
1
  import { ImportError } from '@forgeax/engine-types';
2
2
  import { COMPONENT_TYPE } from './accessor/decode-accessor.js';
3
3
  import { type GltfError, type Result } from './errors.js';
4
+ import { type GltfLodRelation } from './lod/parse-lod.js';
4
5
  import { type GltfImageIr, type GltfMaterialIr, type GltfSamplerIr, type GltfTextureIr } from './material/parse-material.js';
5
6
  import { type GltfBufferViewDecodeCapability } from './meshopt-decode.js';
6
7
  import { type GltfAnimationClipRecord } from './parse-animation.js';
@@ -129,6 +130,7 @@ export interface GltfDoc {
129
130
  * fixtures; producers from parseGltf / parseGlb always populate it.
130
131
  */
131
132
  readonly meshPrimitiveCount?: ReadonlyMap<number, number>;
133
+ readonly lod?: GltfLodRelation;
132
134
  }
133
135
  export type ExternalLoader = (uri: string) => Promise<ArrayBuffer>;
134
136
  type GltfParseError = GltfError | ImportError;
@@ -156,7 +158,7 @@ export interface GltfAssetPack {
156
158
  readonly meta: GltfMetaJson;
157
159
  readonly subAssets: readonly GltfSubAssetEntry[];
158
160
  }
159
- export type GltfAssetPackResult = Result<GltfAssetPack, GltfSourceKeyError>;
161
+ export type GltfAssetPackResult = Result<GltfAssetPack, GltfSourceKeyError | GltfError>;
160
162
  /**
161
163
  * Project a parsed `GltfDoc` into the disk-shape `<source>.meta.json` plus
162
164
  * the freshly minted `subAssets[]` list (UUIDv7 + reimport-reuse-meta
@@ -1 +1 @@
1
- {"version":3,"file":"parse-gltf.d.ts","sourceRoot":"","sources":["../src/parse-gltf.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"parse-gltf.d.ts","sourceRoot":"","sources":["../src/parse-gltf.ts"],"names":[],"mappings":"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"}
@@ -19,7 +19,7 @@ export interface GltfMetaJson {
19
19
  readonly importer: 'gltf';
20
20
  readonly source: string;
21
21
  readonly subAssets: readonly GltfSubAssetEntry[];
22
- readonly sourceOverrides?: Readonly<Record<string, Readonly<Record<string, unknown>>>>;
22
+ readonly sourceOverrides?: Readonly<Record<string, Readonly<Record<string, unknown>>>> | undefined;
23
23
  readonly sourceOverrideDescriptors?: readonly import('@forgeax/engine-types').SourceOverrideDescriptor[];
24
24
  readonly importSettings: {
25
25
  readonly defaultSceneIndex: number;
@@ -1 +1 @@
1
- {"version":3,"file":"reimport-reuse-meta.d.ts","sourceRoot":"","sources":["../src/reimport-reuse-meta.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAEL,KAAK,kBAAkB,EAExB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,KAAK,eAAe,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEvE,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,CAAC;AACvB,MAAM,MAAM,WAAW,GAAG,eAAe,CAAC;AAE1C,gGAAgG;AAChG,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,iEAAiE;IACjE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,aAAa,EAAE,CAAC,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,wBAAwB,CAAC;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACjD,QAAQ,CAAC,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACvF,QAAQ,CAAC,yBAAyB,CAAC,EAAE,SAAS,OAAO,uBAAuB,EAAE,wBAAwB,EAAE,CAAC;IACzG,QAAQ,CAAC,cAAc,EAAE;QACvB,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;QACnC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;QACvC,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;QACxC,QAAQ,CAAC,WAAW,EAAE;YACpB,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;YACtC,QAAQ,CAAC,qBAAqB,EAAE,SAAS,MAAM,EAAE,CAAC;YAClD,QAAQ,CAAC,qBAAqB,EAAE,SAAS,MAAM,EAAE,CAAC;SACnD,CAAC;KACH,CAAC;CACH;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,SAAS,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAClD;AAED,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,kBAAkB,EAAE,kBAAkB,CAAC,CAAC;AAEjF;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,YAAY,EAAE,YAAY,GAAG,SAAS,GACrC,mBAAmB,CAmDrB"}
1
+ {"version":3,"file":"reimport-reuse-meta.d.ts","sourceRoot":"","sources":["../src/reimport-reuse-meta.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAEL,KAAK,kBAAkB,EAExB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,KAAK,eAAe,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEvE,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,CAAC;AACvB,MAAM,MAAM,WAAW,GAAG,eAAe,CAAC;AAE1C,gGAAgG;AAChG,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,iEAAiE;IACjE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,aAAa,EAAE,CAAC,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,wBAAwB,CAAC;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACjD,QAAQ,CAAC,eAAe,CAAC,EACrB,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,GAC3D,SAAS,CAAC;IACd,QAAQ,CAAC,yBAAyB,CAAC,EAAE,SAAS,OAAO,uBAAuB,EAAE,wBAAwB,EAAE,CAAC;IACzG,QAAQ,CAAC,cAAc,EAAE;QACvB,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;QACnC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;QACvC,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;QACxC,QAAQ,CAAC,WAAW,EAAE;YACpB,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;YACtC,QAAQ,CAAC,qBAAqB,EAAE,SAAS,MAAM,EAAE,CAAC;YAClD,QAAQ,CAAC,qBAAqB,EAAE,SAAS,MAAM,EAAE,CAAC;SACnD,CAAC;KACH,CAAC;CACH;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,SAAS,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAClD;AAED,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,kBAAkB,EAAE,kBAAkB,CAAC,CAAC;AAEjF;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,YAAY,EAAE,YAAY,GAAG,SAAS,GACrC,mBAAmB,CAmDrB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgeax/engine-gltf",
3
- "version": "0.1.21",
3
+ "version": "0.1.24",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -38,21 +38,21 @@
38
38
  "LICENSE"
39
39
  ],
40
40
  "dependencies": {
41
- "@forgeax/engine-animation": "0.1.21",
42
- "@forgeax/engine-geometry": "0.1.21",
43
- "@forgeax/engine-import": "0.1.21",
44
- "@forgeax/engine-math": "0.1.21",
45
- "@forgeax/engine-pack": "0.1.21",
46
- "@forgeax/engine-scene": "0.1.21",
47
- "@forgeax/engine-types": "0.1.21",
41
+ "@forgeax/engine-animation": "0.1.24",
42
+ "@forgeax/engine-geometry": "0.1.24",
43
+ "@forgeax/engine-import": "0.1.24",
44
+ "@forgeax/engine-math": "0.1.24",
45
+ "@forgeax/engine-pack": "0.1.24",
46
+ "@forgeax/engine-scene": "0.1.24",
47
+ "@forgeax/engine-types": "0.1.24",
48
48
  "@webgpu/types": "^0.1.71",
49
49
  "meshoptimizer": "^0.20.0"
50
50
  },
51
51
  "devDependencies": {
52
- "@forgeax/engine-assets-runtime": "0.1.21",
53
- "@forgeax/engine-picking": "0.1.21",
54
- "@forgeax/engine-render": "0.1.21",
55
- "@forgeax/engine-runtime": "0.1.21",
52
+ "@forgeax/engine-assets-runtime": "0.1.24",
53
+ "@forgeax/engine-picking": "0.1.24",
54
+ "@forgeax/engine-render": "0.1.24",
55
+ "@forgeax/engine-runtime": "0.1.24",
56
56
  "@types/node": "^20.14.0"
57
57
  },
58
58
  "forgeax": {
@@ -1,4 +1,4 @@
1
- import type { Handle } from '@forgeax/engine-types';
1
+ import type { AssetGuid, Handle } from '@forgeax/engine-types';
2
2
  import { describe, expect, it } from 'vitest';
3
3
  import { toMaterialAsset } from '../bridge.js';
4
4
  import type { GltfMaterialIr } from '../parse-gltf.js';
@@ -149,4 +149,87 @@ describe('glTF material bridge values', () => {
149
149
  },
150
150
  });
151
151
  });
152
+
153
+ it('owns a complete five-extension Standard root and admits only authored physical textures', () => {
154
+ const material = toMaterialAsset(
155
+ {
156
+ name: 'physical-root',
157
+ baseColorFactor: [0.8, 0.7, 0.6, 1],
158
+ metallicFactor: 0.1,
159
+ roughnessFactor: 0.35,
160
+ clearcoatFactor: 0.75,
161
+ clearcoatRoughnessFactor: 0.2,
162
+ clearcoatNormalTexture: { texture: 0 },
163
+ anisotropyStrength: 0.5,
164
+ anisotropyRotation: 0.25,
165
+ anisotropyTexture: { texture: 1 },
166
+ sheenColorFactor: [0.2, 0.1, 0.05],
167
+ sheenRoughnessFactor: 0.3,
168
+ sheenColorTexture: { texture: 2 },
169
+ iridescenceFactor: 0.6,
170
+ iridescenceIor: 1.4,
171
+ iridescenceThicknessMinimum: 120,
172
+ iridescenceThicknessMaximum: 380,
173
+ iridescenceTexture: { texture: 3 },
174
+ specularFactor: 0.8,
175
+ specularColorFactor: [0.9, 0.85, 0.75],
176
+ specularColorTexture: { texture: 4 },
177
+ } as unknown as GltfMaterialIr,
178
+ {
179
+ standardRootGuid: 'standard-root' as unknown as AssetGuid,
180
+ textureHandles: new Map([
181
+ [0, 100],
182
+ [1, 101],
183
+ [2, 102],
184
+ [3, 103],
185
+ [4, 104],
186
+ ]) as unknown as ReadonlyMap<number, Handle<'TextureAsset', 'shared'>>,
187
+ },
188
+ );
189
+
190
+ expect(material.parent).toBeUndefined();
191
+ const names = new Set((material.parameters ?? []).map((parameter) => parameter.name));
192
+ for (const name of [
193
+ 'clearcoat',
194
+ 'clearcoatRoughness',
195
+ 'clearcoatNormalTexture',
196
+ 'anisotropyStrength',
197
+ 'anisotropyRotation',
198
+ 'anisotropyTexture',
199
+ 'sheenColor',
200
+ 'sheenRoughness',
201
+ 'sheenColorTexture',
202
+ 'iridescence',
203
+ 'iridescenceIor',
204
+ 'iridescenceThicknessMinimum',
205
+ 'iridescenceThicknessMaximum',
206
+ 'iridescenceTexture',
207
+ 'specular',
208
+ 'specularColor',
209
+ 'specularColorTexture',
210
+ ]) {
211
+ expect(names.has(name)).toBe(true);
212
+ }
213
+ for (const name of [
214
+ 'clearcoatTexture',
215
+ 'clearcoatRoughnessTexture',
216
+ 'sheenRoughnessTexture',
217
+ 'iridescenceThicknessTexture',
218
+ 'specularTexture',
219
+ ]) {
220
+ expect(names.has(name)).toBe(false);
221
+ }
222
+ expect(material.values).toMatchObject({
223
+ clearcoat: 0.75,
224
+ anisotropyStrength: 0.5,
225
+ sheenColor: [0.2, 0.1, 0.05],
226
+ iridescenceIor: 1.4,
227
+ specularColor: [0.9, 0.85, 0.75],
228
+ clearcoatNormalTexture: { texture: 100 },
229
+ anisotropyTexture: { texture: 101 },
230
+ sheenColorTexture: { texture: 102 },
231
+ iridescenceTexture: { texture: 103 },
232
+ specularColorTexture: { texture: 104 },
233
+ });
234
+ });
152
235
  });
@@ -102,6 +102,28 @@ describe('meshIrToMeshAsset pure-unskinned path (AC-04 / AC-08 / OOS-9)', () =>
102
102
  });
103
103
 
104
104
  describe('glTF bridge uses geometry-owned packing', () => {
105
+ it('persists a generated tangent frame when source TANGENT is absent', () => {
106
+ const asset = meshIrToMeshAsset([
107
+ {
108
+ meshIndex: 0,
109
+ materialIndex: null,
110
+ positions: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]),
111
+ normals: new Float32Array([0, 0, 1, 0, 0, 1, 0, 0, 1]),
112
+ texcoord0: new Float32Array([0, 0, 1, 0, 0, 1]),
113
+ indices: new Uint16Array([0, 1, 2]),
114
+ },
115
+ ]);
116
+
117
+ const tangent = asset.attributes.tangent;
118
+ expect(tangent).toBeInstanceOf(Float32Array);
119
+ if (!(tangent instanceof Float32Array)) throw new Error('generated tangent stream missing');
120
+ expect(tangent?.length).toBe(12);
121
+ expect(tangent?.[0]).toBeCloseTo(1, 5);
122
+ expect(tangent?.[1]).toBeCloseTo(0, 5);
123
+ expect(tangent?.[2]).toBeCloseTo(0, 5);
124
+ expect(Math.abs(tangent?.[3] ?? 0)).toBeCloseTo(1, 5);
125
+ });
126
+
105
127
  it('does not own a parallel 12F/18F stride or uv count calculation', () => {
106
128
  const asset = meshIrToMeshAsset([makeUnskinnedPrim(0, 4)]);
107
129
  expect(asset.vertices.length / 12).toBe(4);
@@ -0,0 +1 @@
1
+ {"asset":{"version":"2.0"},"nodes":[{"mesh":0}],"meshes":[{"primitives":[]}],"scenes":[{"nodes":[0]}]}
@@ -0,0 +1 @@
1
+ {"asset":{"version":"2.0"},"extensionsRequired":["MSFT_lod"],"extensionsUsed":["MSFT_lod"],"nodes":[{"mesh":0,"extensions":{"MSFT_lod":{"ids":[99]}}}],"scenes":[{"nodes":[0]}]}
@@ -0,0 +1 @@
1
+ {"asset":{"version":"2.0"},"extensionsUsed":["MSFT_lod"],"meshes":[{"primitives":[{}]},{"primitives":[{}]},{"primitives":[{}]},{"primitives":[{}]},{"primitives":[{}]},{"primitives":[{}]}],"nodes":[{"mesh":0,"extensions":{"MSFT_lod":{"ids":[1,2]}}},{"mesh":1},{"mesh":2},{"mesh":3,"extensions":{"MSFT_lod":{"ids":[4,5]}}},{"mesh":4},{"mesh":5}],"extensions":{"MSFT_screencoverage":{"scales":[0.5,0.2]}},"scenes":[{"nodes":[0,3]}]}
@@ -0,0 +1 @@
1
+ {"asset":{"version":"2.0"},"extensionsUsed":["MSFT_lod"],"meshes":[{"primitives":[{}]},{"primitives":[{}]},{"primitives":[{}]}],"nodes":[{"mesh":0,"extensions":{"MSFT_lod":{"ids":[1,2]}},"extras":{"MSFT_screencoverage":[0.5,0.2,0.01]}},{"mesh":1},{"mesh":2}],"scenes":[{"nodes":[0]}]}
@@ -0,0 +1 @@
1
+ {"asset":{"version":"2.0"},"extensionsUsed":["MSFT_lod"],"meshes":[{"primitives":[{}]},{"primitives":[{}]},{"primitives":[{}]}],"nodes":[{"mesh":0,"extensions":{"MSFT_lod":{"ids":[1,2]}}},{"mesh":1},{"mesh":2}],"extensions":{"MSFT_screencoverage":{"scales":[0.5,0.2]}},"scenes":[{"nodes":[0]}]}
@@ -13,7 +13,9 @@ import {
13
13
  type GltfImageExtractFailedDetail,
14
14
  type GltfImageMimeUnsupportedDetail,
15
15
  type GltfInstancingCountMismatchDetail,
16
+ type GltfLodInvalidDetail,
16
17
  type GltfMalformedHeaderDetail,
18
+ type GltfMaterialPhysicalInvalidDetail,
17
19
  type GltfMaterialTransmissionInvalidDetail,
18
20
  type GltfMeshBridgeInvalidDetail,
19
21
  type GltfMeshoptDecodeFailedDetail,
@@ -52,7 +54,9 @@ type ExpectedDetails = {
52
54
  readonly 'gltf-color-accessor-unsupported': GltfColorAccessorUnsupportedDetail;
53
55
  readonly 'gltf-color-accessor-malformed': GltfColorAccessorMalformedDetail;
54
56
  readonly 'gltf-mesh-bridge-invalid': GltfMeshBridgeInvalidDetail;
57
+ readonly 'gltf-lod-invalid': GltfLodInvalidDetail;
55
58
  readonly 'gltf-material-transmission-invalid': GltfMaterialTransmissionInvalidDetail;
59
+ readonly 'gltf-material-physical-invalid': GltfMaterialPhysicalInvalidDetail;
56
60
  };
57
61
 
58
62
  type ExpectedCodes = keyof ExpectedDetails;
@@ -75,6 +79,8 @@ function exhaustive(error: GltfError): string {
75
79
  return `${error.detail.accessor}:${error.detail.bufferIndex}`;
76
80
  case 'gltf-extension-unsupported':
77
81
  return error.detail.extension;
82
+ case 'gltf-lod-invalid':
83
+ return String(error.detail.rootNode);
78
84
  case 'gltf-accessor-type-mismatch':
79
85
  return error.detail.reason;
80
86
  case 'gltf-texture-load-failed':
@@ -113,6 +119,8 @@ function exhaustive(error: GltfError): string {
113
119
  return error.detail.reason;
114
120
  case 'gltf-material-transmission-invalid':
115
121
  return `${error.detail.extension}:${error.detail.field}:${error.detail.reason}`;
122
+ case 'gltf-material-physical-invalid':
123
+ return `${error.detail.extension}:${error.detail.field}:${error.detail.reason}`;
116
124
  }
117
125
  return error;
118
126
  }
@@ -17,7 +17,7 @@ import { resolve } from 'node:path';
17
17
  import { describe, expect, it } from 'vitest';
18
18
 
19
19
  const REPO_ROOT = resolve(import.meta.dirname ?? '.', '..', '..', '..', '..');
20
- const TYPES_INDEX = resolve(REPO_ROOT, 'packages', 'types', 'src', 'index.ts');
20
+ const TYPES_INDEX = resolve(REPO_ROOT, 'packages', 'types', 'src', 'core-contracts.ts');
21
21
  const GLTF_ERRORS = resolve(REPO_ROOT, 'packages', 'gltf', 'src', 'errors.ts');
22
22
 
23
23
  function grepCount(pattern: string, file: string): number {
@@ -125,12 +125,12 @@ function grepCountImportGltfErrorFromTypes(): number {
125
125
  }
126
126
 
127
127
  describe('M1 GltfErrorCode migration grep gate (AC-28)', () => {
128
- it("(a) types/src/index.ts contains zero 'gltf-' string literals", () => {
128
+ it("(a) types/src/core-contracts.ts contains zero 'gltf-' string literals", () => {
129
129
  const count = grepCount("'gltf-", TYPES_INDEX);
130
130
  expect(count).toBe(0);
131
131
  });
132
132
 
133
- it('(b) types/src/index.ts contains zero export type GltfError* exports', () => {
133
+ it('(b) types/src/core-contracts.ts contains zero export type GltfError* exports', () => {
134
134
  const count = grepCount('export type GltfError', TYPES_INDEX);
135
135
  expect(count).toBe(0);
136
136
  });
@@ -278,7 +278,9 @@ function unwrapReimport(result: ReturnType<typeof reimportReuseMeta>) {
278
278
  'gltf-version-unsupported',
279
279
  'gltf-buffer-out-of-bounds',
280
280
  'gltf-extension-unsupported',
281
+ 'gltf-lod-invalid',
281
282
  'gltf-material-transmission-invalid',
283
+ 'gltf-material-physical-invalid',
282
284
  'gltf-accessor-type-mismatch',
283
285
  'gltf-texture-load-failed',
284
286
  'gltf-meta-missing',
@@ -300,7 +302,8 @@ function unwrapReimport(result: ReturnType<typeof reimportReuseMeta>) {
300
302
  ];
301
303
 
302
304
  function classifyByExhaustiveSwitch(err: GltfError): string {
303
- switch (err.code) {
305
+ const code = err.code;
306
+ switch (code) {
304
307
  case 'gltf-malformed-header':
305
308
  return 'malformed';
306
309
  case 'gltf-version-unsupported':
@@ -309,8 +312,12 @@ function unwrapReimport(result: ReturnType<typeof reimportReuseMeta>) {
309
312
  return 'oob';
310
313
  case 'gltf-extension-unsupported':
311
314
  return 'ext';
315
+ case 'gltf-lod-invalid':
316
+ return 'lod';
312
317
  case 'gltf-material-transmission-invalid':
313
318
  return 'material-transmission-invalid';
319
+ case 'gltf-material-physical-invalid':
320
+ return 'material-physical-invalid';
314
321
  case 'gltf-accessor-type-mismatch':
315
322
  return 'accessor';
316
323
  case 'gltf-texture-load-failed':
@@ -348,6 +355,7 @@ function unwrapReimport(result: ReturnType<typeof reimportReuseMeta>) {
348
355
  case 'gltf-mesh-bridge-invalid':
349
356
  return 'mesh-bridge-invalid';
350
357
  }
358
+ throw new Error(`unhandled glTF error code: ${code}`);
351
359
  }
352
360
 
353
361
  function buildErrSample(code: GltfErrorCode): GltfError {
@@ -360,6 +368,8 @@ function unwrapReimport(result: ReturnType<typeof reimportReuseMeta>) {
360
368
  return gltfErr(code, { accessor: 0, byteOffset: 0, byteLength: 0, bufferIndex: 0 });
361
369
  case 'gltf-extension-unsupported':
362
370
  return gltfErr(code, { extension: 'KHR_x', source: 'extensionsRequired' });
371
+ case 'gltf-lod-invalid':
372
+ return gltfErr(code, { rootNode: 0, ids: [1], reason: 'missing-node' });
363
373
  case 'gltf-material-transmission-invalid':
364
374
  return gltfErr(code, {
365
375
  extension: 'KHR_materials_transmission',
@@ -367,6 +377,13 @@ function unwrapReimport(result: ReturnType<typeof reimportReuseMeta>) {
367
377
  reason: 'range',
368
378
  actual: 2,
369
379
  });
380
+ case 'gltf-material-physical-invalid':
381
+ return gltfErr(code, {
382
+ extension: 'KHR_materials_clearcoat',
383
+ field: 'clearcoatFactor',
384
+ reason: 'range',
385
+ actual: 2,
386
+ });
370
387
  case 'gltf-accessor-type-mismatch':
371
388
  return gltfErr(code, { accessorIndex: 0, reason: 'sparse' });
372
389
  case 'gltf-texture-load-failed':
@@ -448,6 +465,7 @@ function unwrapReimport(result: ReturnType<typeof reimportReuseMeta>) {
448
465
  case 'gltf-mesh-bridge-invalid':
449
466
  return gltfErr(code, { reason: 'empty-input', primitiveCount: 0 });
450
467
  }
468
+ throw new Error(`unhandled glTF error code: ${code}`);
451
469
  }
452
470
 
453
471
  describe('errors.test.ts', () => {
@@ -456,8 +474,8 @@ function unwrapReimport(result: ReturnType<typeof reimportReuseMeta>) {
456
474
  });
457
475
 
458
476
  describe('GltfErrorCode roster', () => {
459
- it('GLTF_ERROR_HINTS exposes exactly 23 keys', () => {
460
- expect(Object.keys(GLTF_ERROR_HINTS).length).toBe(23);
477
+ it('GLTF_ERROR_HINTS exposes exactly 25 keys', () => {
478
+ expect(Object.keys(GLTF_ERROR_HINTS).length).toBe(25);
461
479
  });
462
480
 
463
481
  it.each(ALL_CODES)('hint for %s is a non-empty string', (code) => {
@@ -1930,6 +1948,25 @@ function unwrapReimport(result: ReturnType<typeof reimportReuseMeta>) {
1930
1948
  expect(map.get(0)).toBe('srgb');
1931
1949
  });
1932
1950
 
1951
+ it('physical extension texture slots keep their glTF color domains', () => {
1952
+ const map = deriveTextureColorSpace({
1953
+ imageCount: 4,
1954
+ textures: [{ source: 0 }, { source: 1 }, { source: 2 }, { source: 3 }],
1955
+ materials: [
1956
+ {
1957
+ sheenColorTexture: 0,
1958
+ specularColorTexture: 1,
1959
+ anisotropyTexture: 2,
1960
+ thicknessTexture: 3,
1961
+ },
1962
+ ],
1963
+ });
1964
+ expect(map.get(0)).toBe('srgb');
1965
+ expect(map.get(1)).toBe('srgb');
1966
+ expect(map.get(2)).toBe('linear');
1967
+ expect(map.get(3)).toBe('linear');
1968
+ });
1969
+
1933
1970
  it('occlusionTexture image classifies as linear', () => {
1934
1971
  const map = deriveTextureColorSpace({
1935
1972
  imageCount: 1,
@@ -2019,8 +2056,21 @@ function unwrapReimport(result: ReturnType<typeof reimportReuseMeta>) {
2019
2056
  stderrSpy.mockRestore();
2020
2057
  });
2021
2058
 
2022
- it('v1 allowlist contains EXT_mesh_gpu_instancing (literal)', () => {
2023
- expect(EXTENSION_ALLOWLIST).toEqual(['EXT_mesh_gpu_instancing', 'EXT_meshopt_compression']);
2059
+ it('public allowlist contains every currently supported extension (literal)', () => {
2060
+ expect(EXTENSION_ALLOWLIST).toEqual([
2061
+ 'EXT_mesh_gpu_instancing',
2062
+ 'EXT_meshopt_compression',
2063
+ 'KHR_lights_punctual',
2064
+ 'KHR_texture_transform',
2065
+ 'KHR_materials_transmission',
2066
+ 'KHR_materials_ior',
2067
+ 'KHR_materials_volume',
2068
+ 'KHR_materials_clearcoat',
2069
+ 'KHR_materials_anisotropy',
2070
+ 'KHR_materials_sheen',
2071
+ 'KHR_materials_iridescence',
2072
+ 'KHR_materials_specular',
2073
+ ]);
2024
2074
  });
2025
2075
 
2026
2076
  it('(a) rejects unsupported extensionsRequired entries', () => {
@@ -2086,6 +2136,11 @@ function unwrapReimport(result: ReturnType<typeof reimportReuseMeta>) {
2086
2136
  'KHR_materials_transmission',
2087
2137
  'KHR_materials_ior',
2088
2138
  'KHR_materials_volume',
2139
+ 'KHR_materials_clearcoat',
2140
+ 'KHR_materials_anisotropy',
2141
+ 'KHR_materials_sheen',
2142
+ 'KHR_materials_iridescence',
2143
+ 'KHR_materials_specular',
2089
2144
  ])('%s in extensionsRequired routes ok (supported)', (extension) => {
2090
2145
  const result = checkExtensions({ extensionsRequired: [extension] });
2091
2146
  expect(result.ok).toBe(true);
@@ -0,0 +1,106 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { parseGltfLodExtension } from '../lod/parse-lod.js';
4
+
5
+ async function fixture(name: string): Promise<Record<string, unknown>> {
6
+ return JSON.parse(
7
+ await readFile(new URL(`./fixtures/lod/${name}`, import.meta.url), 'utf8'),
8
+ ) as Record<string, unknown>;
9
+ }
10
+
11
+ describe('glTF MSFT_lod producer contract', () => {
12
+ it('adopts node-level ids in author order and suppresses lower nodes', async () => {
13
+ const json = await fixture('msft-lod.gltf');
14
+ const result = parseGltfLodExtension(json);
15
+ expect(result).toMatchObject({
16
+ ok: true,
17
+ value: { rootNode: 0, lodNodeIds: [1, 2], screenCoverages: [0.5, 0.2] },
18
+ });
19
+ });
20
+
21
+ it('adopts the official node extras coverage array and drops its terminal discard hint', async () => {
22
+ const json = await fixture('msft-lod-node-extras.gltf');
23
+ const result = parseGltfLodExtension(json);
24
+ expect(result).toMatchObject({
25
+ ok: true,
26
+ value: {
27
+ rootNode: 0,
28
+ lodNodeIds: [1, 2],
29
+ screenCoverages: [0.5, 0.2],
30
+ groups: [{ rootNode: 0, lodNodeIds: [1, 2], screenCoverages: [0.5, 0.2] }],
31
+ },
32
+ });
33
+ });
34
+
35
+ it('rejects an official node extras array whose root/level count is incomplete', () => {
36
+ expect(
37
+ parseGltfLodExtension({
38
+ meshes: [{ primitives: [{}] }, { primitives: [{}] }, { primitives: [{}] }],
39
+ nodes: [
40
+ {
41
+ extensions: { MSFT_lod: { ids: [1, 2] } },
42
+ extras: { MSFT_screencoverage: [0.5, 0.2] },
43
+ },
44
+ {},
45
+ {},
46
+ ],
47
+ }),
48
+ ).toMatchObject({ ok: false, error: { code: 'gltf-lod-invalid' } });
49
+ });
50
+
51
+ it('fails closed when a required MSFT_lod extension has no normalizable group', () => {
52
+ expect(
53
+ parseGltfLodExtension({ extensionsRequired: ['MSFT_lod'], nodes: [{ mesh: 0 }] }),
54
+ ).toMatchObject({ ok: false, error: { code: 'gltf-lod-invalid' } });
55
+ });
56
+
57
+ it('requires every referenced node to resolve a mesh sub-asset', () => {
58
+ expect(
59
+ parseGltfLodExtension({
60
+ nodes: [{ mesh: 0, extensions: { MSFT_lod: { ids: [1] } } }, {}],
61
+ }),
62
+ ).toMatchObject({ ok: false, error: { code: 'gltf-lod-invalid' } });
63
+ });
64
+
65
+ it('rejects a referenced mesh index outside the source mesh table', () => {
66
+ expect(
67
+ parseGltfLodExtension({
68
+ meshes: [{}],
69
+ nodes: [{ mesh: 0, extensions: { MSFT_lod: { ids: [1] } } }, { mesh: 2 }],
70
+ }),
71
+ ).toMatchObject({ ok: false, error: { code: 'gltf-lod-invalid' } });
72
+ });
73
+
74
+ it('rejects a referenced mesh with no primitives before publication', () => {
75
+ expect(
76
+ parseGltfLodExtension({
77
+ meshes: [{ primitives: [] }, { primitives: [{}] }],
78
+ nodes: [{ mesh: 0, extensions: { MSFT_lod: { ids: [1] } } }, { mesh: 1 }],
79
+ }),
80
+ ).toMatchObject({ ok: false, error: { code: 'gltf-lod-invalid' } });
81
+ });
82
+
83
+ it('rejects malformed required extension instead of loading only LOD0', async () => {
84
+ const json = await fixture('msft-lod-malformed.gltf');
85
+ expect(parseGltfLodExtension(json)).toMatchObject({
86
+ ok: false,
87
+ error: { code: 'gltf-lod-invalid' },
88
+ });
89
+ });
90
+
91
+ it('retains every node-level relation for multi-root scenes', async () => {
92
+ const json = await fixture('msft-lod-multi-root.gltf');
93
+ const result = parseGltfLodExtension(json);
94
+ expect(result).toMatchObject({
95
+ ok: true,
96
+ value: {
97
+ rootNode: 0,
98
+ lodNodeIds: [1, 2],
99
+ groups: [
100
+ { rootNode: 0, lodNodeIds: [1, 2] },
101
+ { rootNode: 3, lodNodeIds: [4, 5] },
102
+ ],
103
+ },
104
+ });
105
+ });
106
+ });
@@ -0,0 +1,24 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { projectGltfLodMeta } from '../lod/project-meta.js';
3
+
4
+ describe('glTF LOD Meta publication candidate', () => {
5
+ it('uses declared GUIDs and leaves publication to the shared host', () => {
6
+ const result = projectGltfLodMeta({
7
+ rootSourceKey: 'mesh/root',
8
+ levels: [
9
+ { sourceKey: 'mesh/lod1', guid: 'guid-1' },
10
+ { sourceKey: 'mesh/lod2', guid: 'guid-2' },
11
+ ],
12
+ previous: undefined,
13
+ });
14
+ expect(result).toMatchObject({
15
+ ok: true,
16
+ value: {
17
+ lods: [
18
+ { meshGuid: 'guid-1', screenCoverage: 0.5 },
19
+ { meshGuid: 'guid-2', screenCoverage: 0.2 },
20
+ ],
21
+ },
22
+ });
23
+ });
24
+ });
@@ -67,4 +67,57 @@ describe('glTF material Pack refs', () => {
67
67
  'sampler-6',
68
68
  ]);
69
69
  });
70
+
71
+ it('keeps the five physical extension texture edges in producer slot order', () => {
72
+ const material = {
73
+ name: 'PhysicalPackMaterial',
74
+ baseColorFactor: [1, 1, 1, 1],
75
+ metallicFactor: 0,
76
+ roughnessFactor: 0.5,
77
+ clearcoatNormalTexture: { texture: 0, sampler: 0 },
78
+ anisotropyTexture: { texture: 1, sampler: 1 },
79
+ sheenColorTexture: { texture: 2, sampler: 2 },
80
+ iridescenceTexture: { texture: 3, sampler: 3 },
81
+ specularColorTexture: { texture: 4, sampler: 4 },
82
+ } as unknown as GltfMaterialIr;
83
+ const doc = {
84
+ textures: [
85
+ { source: 0, sampler: 0 },
86
+ { source: 1, sampler: 1 },
87
+ { source: 2, sampler: 2 },
88
+ { source: 3, sampler: 3 },
89
+ { source: 4, sampler: 4 },
90
+ ],
91
+ } as unknown as GltfDoc;
92
+ const refs = materialRefsForPack(
93
+ material,
94
+ doc,
95
+ new Map([
96
+ [0, 'clearcoat-normal-texture'],
97
+ [1, 'anisotropy-texture'],
98
+ [2, 'sheen-color-texture'],
99
+ [3, 'iridescence-texture'],
100
+ [4, 'specular-color-texture'],
101
+ ]),
102
+ new Map([
103
+ [0, 'clearcoat-normal-sampler'],
104
+ [1, 'anisotropy-sampler'],
105
+ [2, 'sheen-color-sampler'],
106
+ [3, 'iridescence-sampler'],
107
+ [4, 'specular-color-sampler'],
108
+ ]),
109
+ );
110
+ expect(refs.map((ref) => ref.guid)).toEqual([
111
+ 'clearcoat-normal-texture',
112
+ 'clearcoat-normal-sampler',
113
+ 'anisotropy-texture',
114
+ 'anisotropy-sampler',
115
+ 'sheen-color-texture',
116
+ 'sheen-color-sampler',
117
+ 'iridescence-texture',
118
+ 'iridescence-sampler',
119
+ 'specular-color-texture',
120
+ 'specular-color-sampler',
121
+ ]);
122
+ });
70
123
  });