@forgeax/engine-gltf 0.1.21 → 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
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { gltfErr, type Result } from '../errors.js';
|
|
2
|
+
export interface GltfLodRelation {
|
|
3
|
+
readonly rootNode: number;
|
|
4
|
+
readonly lodNodeIds: readonly number[];
|
|
5
|
+
readonly screenCoverages: readonly number[];
|
|
6
|
+
/** All node-level MSFT_lod groups, retained for multi-root scenes. */
|
|
7
|
+
readonly groups: readonly GltfLodGroup[];
|
|
8
|
+
}
|
|
9
|
+
export interface GltfLodGroup {
|
|
10
|
+
readonly rootNode: number;
|
|
11
|
+
readonly lodNodeIds: readonly number[];
|
|
12
|
+
readonly screenCoverages: readonly number[];
|
|
13
|
+
}
|
|
14
|
+
export declare function parseGltfLodExtension(json: unknown): Result<GltfLodRelation, ReturnType<typeof gltfErr<'gltf-lod-invalid'>>>;
|
|
15
|
+
//# sourceMappingURL=parse-lod.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-lod.d.ts","sourceRoot":"","sources":["../../src/lod/parse-lod.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,OAAO,EAAM,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAE7D,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,sEAAsE;IACtE,QAAQ,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7C;AAmBD,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,OAAO,GACZ,MAAM,CAAC,eAAe,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAgEzE"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type Result } from '@forgeax/engine-types';
|
|
2
|
+
export interface GltfLodMetaLevel {
|
|
3
|
+
readonly sourceKey: string;
|
|
4
|
+
readonly guid: string;
|
|
5
|
+
readonly screenCoverage?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface GltfLodMetaCandidate {
|
|
8
|
+
readonly rootSourceKey: string;
|
|
9
|
+
readonly levels: readonly GltfLodMetaLevel[];
|
|
10
|
+
readonly previous?: readonly GltfLodMetaLevel[] | undefined;
|
|
11
|
+
}
|
|
12
|
+
export interface GltfLodMetaOutput {
|
|
13
|
+
readonly sourceKey: string;
|
|
14
|
+
readonly meshGuid: string;
|
|
15
|
+
readonly screenCoverage: number;
|
|
16
|
+
}
|
|
17
|
+
export declare function projectGltfLodMeta(input: GltfLodMetaCandidate): Result<{
|
|
18
|
+
readonly lods: readonly GltfLodMetaOutput[];
|
|
19
|
+
}, {
|
|
20
|
+
readonly code: string;
|
|
21
|
+
readonly reason: string;
|
|
22
|
+
}>;
|
|
23
|
+
//# sourceMappingURL=project-meta.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-meta.d.ts","sourceRoot":"","sources":["../../src/lod/project-meta.ts"],"names":[],"mappings":"AACA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAE7D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC7C,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,gBAAgB,EAAE,GAAG,SAAS,CAAC;CAC7D;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,oBAAoB,GAC1B,MAAM,CACP;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,iBAAiB,EAAE,CAAA;CAAE,EAC/C;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CACnD,CAqBA"}
|
package/dist/node-file-entry.mjs
CHANGED
|
@@ -22,6 +22,10 @@ var gltfErrorPolicy = {
|
|
|
22
22
|
expected: "extension listed in v1 allowlist (see EXTENSION_ALLOWLIST in @forgeax/engine-gltf)",
|
|
23
23
|
hint: "see feat-future-gltf-extensions-allowlist; remove this extension or wait for the allowlist to expand"
|
|
24
24
|
},
|
|
25
|
+
"gltf-lod-invalid": {
|
|
26
|
+
expected: "MSFT_lod ids to reference unique existing node indices with valid coverage",
|
|
27
|
+
hint: "repair the MSFT_lod node relation or remove it from extensionsRequired before re-importing"
|
|
28
|
+
},
|
|
25
29
|
"gltf-material-transmission-invalid": {
|
|
26
30
|
expected: "KHR transmission, IOR, and volume values are finite and within their glTF ranges",
|
|
27
31
|
hint: "repair the named glTF material extension value and re-import the source"
|
|
@@ -389,7 +393,9 @@ function decodeColorAccessor(input, flags = {}) {
|
|
|
389
393
|
// src/check-extensions.ts
|
|
390
394
|
var EXTENSION_ALLOWLIST = [
|
|
391
395
|
"EXT_mesh_gpu_instancing",
|
|
392
|
-
"EXT_meshopt_compression"
|
|
396
|
+
"EXT_meshopt_compression",
|
|
397
|
+
"MSFT_lod",
|
|
398
|
+
"MSFT_screencoverage"
|
|
393
399
|
];
|
|
394
400
|
var SUPPORTED_EXTENSIONS = [
|
|
395
401
|
...EXTENSION_ALLOWLIST,
|
|
@@ -449,6 +455,81 @@ function decodeBase64(b64) {
|
|
|
449
455
|
return out;
|
|
450
456
|
}
|
|
451
457
|
|
|
458
|
+
// src/lod/parse-lod.ts
|
|
459
|
+
function parseGltfLodExtension(json) {
|
|
460
|
+
const doc = json;
|
|
461
|
+
const nodes = doc.nodes ?? [];
|
|
462
|
+
const rawCoverage = doc.extensions?.MSFT_screencoverage?.scales;
|
|
463
|
+
const groups = [];
|
|
464
|
+
for (let rootNode = 0; rootNode < nodes.length; rootNode += 1) {
|
|
465
|
+
if (nodes[rootNode]?.extensions?.MSFT_lod === void 0) continue;
|
|
466
|
+
const idsRaw = nodes[rootNode]?.extensions?.MSFT_lod?.ids;
|
|
467
|
+
if (!Array.isArray(idsRaw)) {
|
|
468
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids: [], reason: "not-integer" }));
|
|
469
|
+
}
|
|
470
|
+
const ids = idsRaw.filter((value) => typeof value === "number");
|
|
471
|
+
if (ids.length !== idsRaw.length || ids.some((id) => !Number.isInteger(id) || id < 0 || id >= nodes.length)) {
|
|
472
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids, reason: "missing-node" }));
|
|
473
|
+
}
|
|
474
|
+
if (new Set(ids).size !== ids.length || ids.includes(rootNode)) {
|
|
475
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids, reason: "duplicate-node" }));
|
|
476
|
+
}
|
|
477
|
+
const hasMesh = (node) => {
|
|
478
|
+
if (typeof node?.mesh !== "number" || !Number.isInteger(node.mesh) || node.mesh < 0) {
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
const mesh = doc.meshes?.[node.mesh];
|
|
482
|
+
return mesh !== void 0 && Array.isArray(mesh.primitives) && mesh.primitives.length > 0;
|
|
483
|
+
};
|
|
484
|
+
if (!hasMesh(nodes[rootNode]) || ids.some((id) => !hasMesh(nodes[id]))) {
|
|
485
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids, reason: "missing-node" }));
|
|
486
|
+
}
|
|
487
|
+
const nodeCoverage = nodes[rootNode]?.extras?.MSFT_screencoverage;
|
|
488
|
+
const coverage = normalizeScreenCoverages(
|
|
489
|
+
nodeCoverage === void 0 ? rawCoverage : nodeCoverage,
|
|
490
|
+
ids.length,
|
|
491
|
+
nodeCoverage !== void 0
|
|
492
|
+
);
|
|
493
|
+
if (coverage === "invalid") {
|
|
494
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids, reason: "coverage" }));
|
|
495
|
+
}
|
|
496
|
+
groups.push({
|
|
497
|
+
rootNode,
|
|
498
|
+
lodNodeIds: ids,
|
|
499
|
+
screenCoverages: coverage ?? []
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
const first = groups[0];
|
|
503
|
+
if (first === void 0) {
|
|
504
|
+
if (doc.extensionsRequired?.some((extension) => extension === "MSFT_lod")) {
|
|
505
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode: 0, ids: [], reason: "missing-node" }));
|
|
506
|
+
}
|
|
507
|
+
return ok({ rootNode: 0, lodNodeIds: [], screenCoverages: [], groups: [] });
|
|
508
|
+
}
|
|
509
|
+
return ok({
|
|
510
|
+
rootNode: first.rootNode,
|
|
511
|
+
lodNodeIds: first.lodNodeIds,
|
|
512
|
+
screenCoverages: first.screenCoverages,
|
|
513
|
+
groups
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
function normalizeScreenCoverages(raw, lodCount, officialNodeForm) {
|
|
517
|
+
if (raw === void 0) return void 0;
|
|
518
|
+
if (!Array.isArray(raw)) return "invalid";
|
|
519
|
+
if (raw.some(
|
|
520
|
+
(value) => typeof value !== "number" || !Number.isFinite(value) || value <= 0 || value > 1
|
|
521
|
+
)) {
|
|
522
|
+
return "invalid";
|
|
523
|
+
}
|
|
524
|
+
const expectedLengths = officialNodeForm ? [lodCount + 1] : [lodCount, lodCount + 1];
|
|
525
|
+
if (!expectedLengths.includes(raw.length)) return "invalid";
|
|
526
|
+
const values = raw.slice(0, lodCount);
|
|
527
|
+
for (let index = 1; index < values.length; index += 1) {
|
|
528
|
+
if (values[index] >= values[index - 1]) return "invalid";
|
|
529
|
+
}
|
|
530
|
+
return values;
|
|
531
|
+
}
|
|
532
|
+
|
|
452
533
|
// src/material/parse-material.ts
|
|
453
534
|
function tuple2(values) {
|
|
454
535
|
if (values === void 0 || values.length < 2) return void 0;
|
|
@@ -1374,6 +1455,8 @@ async function parseGltfWithBin(json, ctx) {
|
|
|
1374
1455
|
const extResult = checkExtensions(json);
|
|
1375
1456
|
if (!extResult.ok) return err(extResult.error);
|
|
1376
1457
|
const unsupportedExtensions = extResult.value.unsupportedUsed;
|
|
1458
|
+
const lodResult = parseGltfLodExtension(json);
|
|
1459
|
+
if (!lodResult.ok) return err(lodResult.error);
|
|
1377
1460
|
const lightsResult = parsePunctualLights(json, ctx.filePath);
|
|
1378
1461
|
if (!lightsResult.ok) return err(lightsResult.error);
|
|
1379
1462
|
const meshesJson = json.meshes ?? [];
|
|
@@ -1914,7 +1997,8 @@ async function parseGltfWithBin(json, ctx) {
|
|
|
1914
1997
|
matrixTrsCoexistNodes: diagnostics.matrixTrsCoexistNodes
|
|
1915
1998
|
},
|
|
1916
1999
|
meshPrimitiveCount,
|
|
1917
|
-
lights: lightsResult.value
|
|
2000
|
+
lights: lightsResult.value,
|
|
2001
|
+
...lodResult.value.lodNodeIds.length === 0 ? {} : { lod: lodResult.value }
|
|
1918
2002
|
});
|
|
1919
2003
|
}
|
|
1920
2004
|
async function parseGltfForImporter(json, externalLoader, filePath, options = {}) {
|