@forgeax/engine-gltf 0.1.20 → 0.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -0
- package/dist/__tests__/lod-import.integration.test.d.ts +2 -0
- package/dist/__tests__/lod-import.integration.test.d.ts.map +1 -0
- package/dist/__tests__/lod-meta-publication.integration.test.d.ts +2 -0
- package/dist/__tests__/lod-meta-publication.integration.test.d.ts.map +1 -0
- package/dist/check-extensions.d.ts.map +1 -1
- package/dist/cli-gltf.mjs +183 -8
- package/dist/cli-gltf.mjs.map +1 -1
- package/dist/errors.d.ts +6 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/gltf-importer.d.ts.map +1 -1
- package/dist/importer-entry.mjs +145 -3
- package/dist/importer-entry.mjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +238 -6
- package/dist/index.mjs.map +1 -1
- package/dist/lod/parse-lod.d.ts +15 -0
- package/dist/lod/parse-lod.d.ts.map +1 -0
- package/dist/lod/project-meta.d.ts +23 -0
- package/dist/lod/project-meta.d.ts.map +1 -0
- package/dist/node-file-entry.mjs +86 -2
- package/dist/node-file-entry.mjs.map +1 -1
- package/dist/parse-gltf.d.ts +3 -1
- package/dist/parse-gltf.d.ts.map +1 -1
- package/dist/reimport-reuse-meta.d.ts +1 -1
- package/dist/reimport-reuse-meta.d.ts.map +1 -1
- package/package.json +12 -12
- package/src/__tests__/fixtures/lod/core-only.gltf +1 -0
- package/src/__tests__/fixtures/lod/msft-lod-malformed.gltf +1 -0
- package/src/__tests__/fixtures/lod/msft-lod-multi-root.gltf +1 -0
- package/src/__tests__/fixtures/lod/msft-lod-node-extras.gltf +1 -0
- package/src/__tests__/fixtures/lod/msft-lod.gltf +1 -0
- package/src/__tests__/gltf-error-derived-views.test-d.ts +4 -0
- package/src/__tests__/gltf.unit.test.ts +12 -3
- package/src/__tests__/lod-import.integration.test.ts +106 -0
- package/src/__tests__/lod-meta-publication.integration.test.ts +24 -0
- package/src/__tests__/parse-gltf.unit.test.ts +22 -0
- package/src/__tests__/source-key-producer.integration.test.ts +6 -2
- package/src/bridge.ts +1 -1
- package/src/check-extensions.ts +2 -0
- package/src/errors.ts +11 -0
- package/src/gltf-importer.ts +87 -1
- package/src/index.ts +2 -0
- package/src/lod/parse-lod.ts +129 -0
- package/src/lod/project-meta.ts +48 -0
- package/src/parse-gltf.ts +97 -1
- package/src/reimport-reuse-meta.ts +3 -1
- package/dist/.tsbuildinfo +0 -1
package/dist/parse-gltf.d.ts
CHANGED
|
@@ -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
|
package/dist/parse-gltf.d.ts.map
CHANGED
|
@@ -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,
|
|
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.
|
|
3
|
+
"version": "0.1.23",
|
|
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.
|
|
42
|
-
"@forgeax/engine-geometry": "0.1.
|
|
43
|
-
"@forgeax/engine-import": "0.1.
|
|
44
|
-
"@forgeax/engine-math": "0.1.
|
|
45
|
-
"@forgeax/engine-pack": "0.1.
|
|
46
|
-
"@forgeax/engine-scene": "0.1.
|
|
47
|
-
"@forgeax/engine-types": "0.1.
|
|
41
|
+
"@forgeax/engine-animation": "0.1.23",
|
|
42
|
+
"@forgeax/engine-geometry": "0.1.23",
|
|
43
|
+
"@forgeax/engine-import": "0.1.23",
|
|
44
|
+
"@forgeax/engine-math": "0.1.23",
|
|
45
|
+
"@forgeax/engine-pack": "0.1.23",
|
|
46
|
+
"@forgeax/engine-scene": "0.1.23",
|
|
47
|
+
"@forgeax/engine-types": "0.1.23",
|
|
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.
|
|
53
|
-
"@forgeax/engine-picking": "0.1.
|
|
54
|
-
"@forgeax/engine-render": "0.1.
|
|
55
|
-
"@forgeax/engine-runtime": "0.1.
|
|
52
|
+
"@forgeax/engine-assets-runtime": "0.1.23",
|
|
53
|
+
"@forgeax/engine-picking": "0.1.23",
|
|
54
|
+
"@forgeax/engine-render": "0.1.23",
|
|
55
|
+
"@forgeax/engine-runtime": "0.1.23",
|
|
56
56
|
"@types/node": "^20.14.0"
|
|
57
57
|
},
|
|
58
58
|
"forgeax": {
|
|
@@ -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,6 +13,7 @@ import {
|
|
|
13
13
|
type GltfImageExtractFailedDetail,
|
|
14
14
|
type GltfImageMimeUnsupportedDetail,
|
|
15
15
|
type GltfInstancingCountMismatchDetail,
|
|
16
|
+
type GltfLodInvalidDetail,
|
|
16
17
|
type GltfMalformedHeaderDetail,
|
|
17
18
|
type GltfMaterialTransmissionInvalidDetail,
|
|
18
19
|
type GltfMeshBridgeInvalidDetail,
|
|
@@ -52,6 +53,7 @@ type ExpectedDetails = {
|
|
|
52
53
|
readonly 'gltf-color-accessor-unsupported': GltfColorAccessorUnsupportedDetail;
|
|
53
54
|
readonly 'gltf-color-accessor-malformed': GltfColorAccessorMalformedDetail;
|
|
54
55
|
readonly 'gltf-mesh-bridge-invalid': GltfMeshBridgeInvalidDetail;
|
|
56
|
+
readonly 'gltf-lod-invalid': GltfLodInvalidDetail;
|
|
55
57
|
readonly 'gltf-material-transmission-invalid': GltfMaterialTransmissionInvalidDetail;
|
|
56
58
|
};
|
|
57
59
|
|
|
@@ -75,6 +77,8 @@ function exhaustive(error: GltfError): string {
|
|
|
75
77
|
return `${error.detail.accessor}:${error.detail.bufferIndex}`;
|
|
76
78
|
case 'gltf-extension-unsupported':
|
|
77
79
|
return error.detail.extension;
|
|
80
|
+
case 'gltf-lod-invalid':
|
|
81
|
+
return String(error.detail.rootNode);
|
|
78
82
|
case 'gltf-accessor-type-mismatch':
|
|
79
83
|
return error.detail.reason;
|
|
80
84
|
case 'gltf-texture-load-failed':
|
|
@@ -309,6 +309,8 @@ function unwrapReimport(result: ReturnType<typeof reimportReuseMeta>) {
|
|
|
309
309
|
return 'oob';
|
|
310
310
|
case 'gltf-extension-unsupported':
|
|
311
311
|
return 'ext';
|
|
312
|
+
case 'gltf-lod-invalid':
|
|
313
|
+
return 'lod';
|
|
312
314
|
case 'gltf-material-transmission-invalid':
|
|
313
315
|
return 'material-transmission-invalid';
|
|
314
316
|
case 'gltf-accessor-type-mismatch':
|
|
@@ -360,6 +362,8 @@ function unwrapReimport(result: ReturnType<typeof reimportReuseMeta>) {
|
|
|
360
362
|
return gltfErr(code, { accessor: 0, byteOffset: 0, byteLength: 0, bufferIndex: 0 });
|
|
361
363
|
case 'gltf-extension-unsupported':
|
|
362
364
|
return gltfErr(code, { extension: 'KHR_x', source: 'extensionsRequired' });
|
|
365
|
+
case 'gltf-lod-invalid':
|
|
366
|
+
return gltfErr(code, { rootNode: 0, ids: [1], reason: 'missing-node' });
|
|
363
367
|
case 'gltf-material-transmission-invalid':
|
|
364
368
|
return gltfErr(code, {
|
|
365
369
|
extension: 'KHR_materials_transmission',
|
|
@@ -456,8 +460,8 @@ function unwrapReimport(result: ReturnType<typeof reimportReuseMeta>) {
|
|
|
456
460
|
});
|
|
457
461
|
|
|
458
462
|
describe('GltfErrorCode roster', () => {
|
|
459
|
-
it('GLTF_ERROR_HINTS exposes exactly
|
|
460
|
-
expect(Object.keys(GLTF_ERROR_HINTS).length).toBe(
|
|
463
|
+
it('GLTF_ERROR_HINTS exposes exactly 24 keys', () => {
|
|
464
|
+
expect(Object.keys(GLTF_ERROR_HINTS).length).toBe(24);
|
|
461
465
|
});
|
|
462
466
|
|
|
463
467
|
it.each(ALL_CODES)('hint for %s is a non-empty string', (code) => {
|
|
@@ -2020,7 +2024,12 @@ function unwrapReimport(result: ReturnType<typeof reimportReuseMeta>) {
|
|
|
2020
2024
|
});
|
|
2021
2025
|
|
|
2022
2026
|
it('v1 allowlist contains EXT_mesh_gpu_instancing (literal)', () => {
|
|
2023
|
-
expect(EXTENSION_ALLOWLIST).toEqual([
|
|
2027
|
+
expect(EXTENSION_ALLOWLIST).toEqual([
|
|
2028
|
+
'EXT_mesh_gpu_instancing',
|
|
2029
|
+
'EXT_meshopt_compression',
|
|
2030
|
+
'MSFT_lod',
|
|
2031
|
+
'MSFT_screencoverage',
|
|
2032
|
+
]);
|
|
2024
2033
|
});
|
|
2025
2034
|
|
|
2026
2035
|
it('(a) rejects unsupported extensionsRequired entries', () => {
|
|
@@ -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
|
+
});
|
|
@@ -162,6 +162,28 @@ describe('glTF transmission material IR', () => {
|
|
|
162
162
|
});
|
|
163
163
|
});
|
|
164
164
|
|
|
165
|
+
describe('glTF required extension admission', () => {
|
|
166
|
+
it.each([
|
|
167
|
+
'KHR_materials_transmission',
|
|
168
|
+
'KHR_materials_ior',
|
|
169
|
+
'KHR_materials_volume',
|
|
170
|
+
])('accepts %s through the parse path', async (extension) => {
|
|
171
|
+
const result = await parseGltf(
|
|
172
|
+
{
|
|
173
|
+
asset: { version: '2.0' },
|
|
174
|
+
extensionsUsed: [extension],
|
|
175
|
+
extensionsRequired: [extension],
|
|
176
|
+
},
|
|
177
|
+
noopLoader,
|
|
178
|
+
`/required-${extension}.gltf`,
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
expect(result.ok).toBe(true);
|
|
182
|
+
if (!result.ok) return;
|
|
183
|
+
expect(result.value.diagnostics.unsupportedExtensions).toEqual([]);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
165
187
|
describe('glTF COLOR_0 importer carrier', () => {
|
|
166
188
|
it('publishes normalized VEC3 FLOAT colors as importer-owned RGBA', async () => {
|
|
167
189
|
const fixture = JSON.parse(
|
|
@@ -86,7 +86,9 @@ describe('glTF producer source-key boundary', () => {
|
|
|
86
86
|
expect(result.ok).toBe(false);
|
|
87
87
|
if (result.ok) return;
|
|
88
88
|
expect(result.error.code).toBe('duplicate-source-key');
|
|
89
|
-
expect(result.error.detail.sourceIndices).toEqual([
|
|
89
|
+
expect((result.error.detail as { sourceIndices: readonly number[] }).sourceIndices).toEqual([
|
|
90
|
+
0, 1,
|
|
91
|
+
]);
|
|
90
92
|
});
|
|
91
93
|
|
|
92
94
|
it('applies the same preflight to duplicate material names', () => {
|
|
@@ -95,7 +97,9 @@ describe('glTF producer source-key boundary', () => {
|
|
|
95
97
|
expect(result.ok).toBe(false);
|
|
96
98
|
if (result.ok) return;
|
|
97
99
|
expect(result.error.code).toBe('duplicate-source-key');
|
|
98
|
-
expect(result.error.detail.sourceIndices).toEqual([
|
|
100
|
+
expect((result.error.detail as { sourceIndices: readonly number[] }).sourceIndices).toEqual([
|
|
101
|
+
0, 1,
|
|
102
|
+
]);
|
|
99
103
|
});
|
|
100
104
|
|
|
101
105
|
it('publishes producer-valid output when semantic identity is unique', () => {
|
package/src/bridge.ts
CHANGED
|
@@ -541,7 +541,7 @@ export function gltfDocToSceneAsset(doc: GltfDoc, ctx: GltfBridgeContext): Scene
|
|
|
541
541
|
|
|
542
542
|
// bug-20260613: SceneAsset entities also emit ChildOf when a glTF node has a
|
|
543
543
|
// parent (line 547 below). Runtime propagateTransforms then derives
|
|
544
|
-
//
|
|
544
|
+
// GlobalTransform.world via `parent.world * compose(child.local TRS)`. If we
|
|
545
545
|
// wrote the *world* TRS into Transform here, every child node would get
|
|
546
546
|
// baked twice -- once at importer time and again at propagate time --
|
|
547
547
|
// collapsing the skin so vertices fly to (parent.world)^2 space and the
|
package/src/check-extensions.ts
CHANGED
|
@@ -27,6 +27,8 @@ import { err, type GltfError, gltfErr, ok, type Result } from './errors.js';
|
|
|
27
27
|
export const EXTENSION_ALLOWLIST: readonly string[] = [
|
|
28
28
|
'EXT_mesh_gpu_instancing',
|
|
29
29
|
'EXT_meshopt_compression',
|
|
30
|
+
'MSFT_lod',
|
|
31
|
+
'MSFT_screencoverage',
|
|
30
32
|
];
|
|
31
33
|
const SUPPORTED_EXTENSIONS: readonly string[] = [
|
|
32
34
|
...EXTENSION_ALLOWLIST,
|
package/src/errors.ts
CHANGED
|
@@ -60,6 +60,12 @@ export interface GltfExtensionUnsupportedDetail {
|
|
|
60
60
|
readonly source: 'extensionsRequired' | 'extensionsUsed';
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
export interface GltfLodInvalidDetail {
|
|
64
|
+
readonly rootNode: number;
|
|
65
|
+
readonly ids: readonly number[];
|
|
66
|
+
readonly reason: 'missing-node' | 'duplicate-node' | 'not-integer' | 'coverage';
|
|
67
|
+
}
|
|
68
|
+
|
|
63
69
|
export interface GltfMaterialTransmissionInvalidDetail {
|
|
64
70
|
readonly extension: 'KHR_materials_transmission' | 'KHR_materials_ior' | 'KHR_materials_volume';
|
|
65
71
|
readonly field: string;
|
|
@@ -263,6 +269,10 @@ const gltfErrorPolicy = {
|
|
|
263
269
|
expected: 'extension listed in v1 allowlist (see EXTENSION_ALLOWLIST in @forgeax/engine-gltf)',
|
|
264
270
|
hint: 'see feat-future-gltf-extensions-allowlist; remove this extension or wait for the allowlist to expand',
|
|
265
271
|
},
|
|
272
|
+
'gltf-lod-invalid': {
|
|
273
|
+
expected: 'MSFT_lod ids to reference unique existing node indices with valid coverage',
|
|
274
|
+
hint: 'repair the MSFT_lod node relation or remove it from extensionsRequired before re-importing',
|
|
275
|
+
},
|
|
266
276
|
'gltf-material-transmission-invalid': {
|
|
267
277
|
expected: 'KHR transmission, IOR, and volume values are finite and within their glTF ranges',
|
|
268
278
|
hint: 'repair the named glTF material extension value and re-import the source',
|
|
@@ -357,6 +367,7 @@ interface DetailFor {
|
|
|
357
367
|
readonly 'gltf-version-unsupported': GltfVersionUnsupportedDetail;
|
|
358
368
|
readonly 'gltf-buffer-out-of-bounds': GltfBufferOutOfBoundsDetail;
|
|
359
369
|
readonly 'gltf-extension-unsupported': GltfExtensionUnsupportedDetail;
|
|
370
|
+
readonly 'gltf-lod-invalid': GltfLodInvalidDetail;
|
|
360
371
|
readonly 'gltf-accessor-type-mismatch': GltfAccessorTypeMismatchDetail;
|
|
361
372
|
readonly 'gltf-texture-load-failed': GltfTextureLoadFailedDetail;
|
|
362
373
|
readonly 'gltf-meta-missing': GltfMetaMissingDetail;
|
package/src/gltf-importer.ts
CHANGED
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
// gap as `import-produced-no-assets`. A texture sub-asset that fails byte
|
|
33
33
|
// extraction surfaces as `gltf-image-extract-failed` (D-6).
|
|
34
34
|
|
|
35
|
+
import { deriveDefaultLodScreenCoverages } from '@forgeax/engine-import';
|
|
35
36
|
import { packMeshBinV4 } from '@forgeax/engine-import/mesh-bin';
|
|
36
37
|
import { AssetGuid as AssetGuidCodec } from '@forgeax/engine-pack/guid';
|
|
37
38
|
import type {
|
|
@@ -591,7 +592,83 @@ async function importGltf(
|
|
|
591
592
|
}),
|
|
592
593
|
};
|
|
593
594
|
}
|
|
594
|
-
const
|
|
595
|
+
const stabilizedMesh = stabilizeMeshMaterialSlots(
|
|
596
|
+
bridged.value,
|
|
597
|
+
ctx,
|
|
598
|
+
sub.guid,
|
|
599
|
+
sub.sourceKey,
|
|
600
|
+
);
|
|
601
|
+
const lodGroup =
|
|
602
|
+
doc.lod?.groups?.find(
|
|
603
|
+
(group) => doc.nodes[group.rootNode]?.meshIndex === sub.sourceIndex,
|
|
604
|
+
) ??
|
|
605
|
+
(doc.lod?.rootNode !== undefined &&
|
|
606
|
+
doc.nodes[doc.lod.rootNode]?.meshIndex === sub.sourceIndex
|
|
607
|
+
? doc.lod
|
|
608
|
+
: undefined);
|
|
609
|
+
const rootMeshIndex = lodGroup === undefined ? undefined : sub.sourceIndex;
|
|
610
|
+
if (lodGroup !== undefined) {
|
|
611
|
+
const referencedMeshIndices = [
|
|
612
|
+
sub.sourceIndex,
|
|
613
|
+
...lodGroup.lodNodeIds.map((nodeIndex) => doc.nodes[nodeIndex]?.meshIndex),
|
|
614
|
+
];
|
|
615
|
+
const missingMeshIndex = referencedMeshIndices.find(
|
|
616
|
+
(meshIndex) =>
|
|
617
|
+
!Number.isInteger(meshIndex) ||
|
|
618
|
+
meshIndex === null ||
|
|
619
|
+
meshIndex === undefined ||
|
|
620
|
+
maps.meshGuidByIndex.get(meshIndex as number) === undefined,
|
|
621
|
+
);
|
|
622
|
+
if (missingMeshIndex !== undefined) {
|
|
623
|
+
return {
|
|
624
|
+
ok: false,
|
|
625
|
+
error: new ImportError({
|
|
626
|
+
code: 'import-internal-error',
|
|
627
|
+
expected: 'every MSFT_lod node to resolve a cooked mesh sub-asset',
|
|
628
|
+
actual: String(missingMeshIndex),
|
|
629
|
+
hint: 'repair the referenced glTF mesh primitives and re-run the importer',
|
|
630
|
+
detail: {
|
|
631
|
+
reason: `MSFT_lod group for mesh ${sub.sourceIndex} references an unprojected mesh`,
|
|
632
|
+
},
|
|
633
|
+
}),
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
const authoredLods =
|
|
638
|
+
sub.sourceKey === undefined ? undefined : ctx.sourceOverrides?.[sub.sourceKey]?.lods;
|
|
639
|
+
const lodEntries = Array.isArray(authoredLods) ? authoredLods : [];
|
|
640
|
+
const lodLevels =
|
|
641
|
+
rootMeshIndex === sub.sourceIndex && lodGroup !== undefined
|
|
642
|
+
? lodGroup.lodNodeIds.flatMap((nodeIndex, index) => {
|
|
643
|
+
const meshIndex = doc.nodes[nodeIndex]?.meshIndex;
|
|
644
|
+
const guid =
|
|
645
|
+
meshIndex === undefined || meshIndex === null
|
|
646
|
+
? undefined
|
|
647
|
+
: maps.meshGuidByIndex.get(meshIndex);
|
|
648
|
+
if (guid === undefined) return [];
|
|
649
|
+
const authored = lodEntries[index];
|
|
650
|
+
const authoredCoverage =
|
|
651
|
+
authored !== null && typeof authored === 'object'
|
|
652
|
+
? (authored as { readonly screenCoverage?: unknown }).screenCoverage
|
|
653
|
+
: undefined;
|
|
654
|
+
const coverage =
|
|
655
|
+
typeof authoredCoverage === 'number'
|
|
656
|
+
? authoredCoverage
|
|
657
|
+
: (lodGroup.screenCoverages[index] ??
|
|
658
|
+
deriveDefaultLodScreenCoverages(lodGroup.lodNodeIds.length + 1)[index]);
|
|
659
|
+
const parsed = AssetGuidCodec.parse(guid);
|
|
660
|
+
return parsed.ok && coverage !== undefined
|
|
661
|
+
? [{ mesh: parsed.value, screenCoverage: coverage, guid }]
|
|
662
|
+
: [];
|
|
663
|
+
})
|
|
664
|
+
: [];
|
|
665
|
+
const meshPayload: MeshAsset =
|
|
666
|
+
lodLevels.length === 0
|
|
667
|
+
? stabilizedMesh
|
|
668
|
+
: {
|
|
669
|
+
...stabilizedMesh,
|
|
670
|
+
lods: lodLevels.map(({ mesh, screenCoverage }) => ({ mesh, screenCoverage })),
|
|
671
|
+
};
|
|
595
672
|
const materialRefs: AssetRef[] = [];
|
|
596
673
|
const seenMaterialGuids = new Set<string>();
|
|
597
674
|
for (let slotIndex = 0; slotIndex < meshPayload.materialSlots.length; slotIndex++) {
|
|
@@ -606,6 +683,15 @@ async function importGltf(
|
|
|
606
683
|
});
|
|
607
684
|
}
|
|
608
685
|
}
|
|
686
|
+
for (const [lodIndex, level] of lodLevels.entries()) {
|
|
687
|
+
if (!seenMaterialGuids.has(level.guid.toLowerCase())) {
|
|
688
|
+
seenMaterialGuids.add(level.guid.toLowerCase());
|
|
689
|
+
materialRefs.push({
|
|
690
|
+
guid: level.guid,
|
|
691
|
+
sourceField: { fieldName: 'lods', arrayIndex: lodIndex },
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
}
|
|
609
695
|
out.push({
|
|
610
696
|
guid: sub.guid,
|
|
611
697
|
kind: 'mesh',
|
package/src/index.ts
CHANGED
|
@@ -81,6 +81,8 @@ export { err, GLTF_ERROR_HINTS, gltfErr, ok } from './errors.js';
|
|
|
81
81
|
// gltfImporter: the build-time `{ key: 'gltf', import }` Importer
|
|
82
82
|
// (feat-20260603-asset-import-loader-injection M2 / w19).
|
|
83
83
|
export { gltfImporter } from './gltf-importer.js';
|
|
84
|
+
export { type GltfLodRelation, parseGltfLodExtension } from './lod/parse-lod.js';
|
|
85
|
+
export { projectGltfLodMeta } from './lod/project-meta.js';
|
|
84
86
|
export type { GlbChunks } from './parse-glb-chunks.js';
|
|
85
87
|
// Header / chunk parsers (w8).
|
|
86
88
|
export { parseGlbChunks, parseGltfHeader } from './parse-glb-chunks.js';
|