@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/index.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ export { checkExtensions, EXTENSION_ALLOWLIST } from './check-extensions.js';
|
|
|
10
10
|
export type { GltfAccessorTypeMismatchDetail, GltfAnimationCubicsplineUnsupportedDetail, GltfBufferOutOfBoundsDetail, GltfColorAccessorMalformedDetail, GltfColorAccessorUnsupportedDetail, GltfError, GltfErrorCode, GltfErrorDetail, GltfExtensionUnsupportedDetail, GltfImageExtractFailedDetail, GltfImageMimeUnsupportedDetail, GltfInstancingCountMismatchDetail, GltfMalformedHeaderDetail, GltfMaterialTransmissionInvalidDetail, GltfMetaMissingDetail, GltfMorphInvalidDetail, GltfMorphUnsupportedDetail, GltfSkinAttrAsymmetricDetail, GltfSkinJointCountExceededDetail, GltfSkinJointNameMissingDetail, GltfTextureLoadFailedDetail, GltfVersionUnsupportedDetail, Result, } from './errors.js';
|
|
11
11
|
export { err, GLTF_ERROR_HINTS, gltfErr, ok } from './errors.js';
|
|
12
12
|
export { gltfImporter } from './gltf-importer.js';
|
|
13
|
+
export { type GltfLodRelation, parseGltfLodExtension } from './lod/parse-lod.js';
|
|
14
|
+
export { projectGltfLodMeta } from './lod/project-meta.js';
|
|
13
15
|
export type { GlbChunks } from './parse-glb-chunks.js';
|
|
14
16
|
export { parseGlbChunks, parseGltfHeader } from './parse-glb-chunks.js';
|
|
15
17
|
export type { ExternalLoader, GltfAssetPack, GltfAssetPackResult, GltfDiagnosticsIr, GltfDoc, GltfImageIr, GltfMaterialIr, GltfMeshIr, GltfNodeIr, GltfNormalTextureInfoIr, GltfOcclusionTextureInfoIr, GltfSamplerIr, GltfSceneIr, GltfTextureInfoIr, GltfTextureIr, GltfTextureTransformIr, MeshJson, MeshPrimitiveJson, NodeInstancingIr, } from './parse-gltf.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -3,6 +3,7 @@ export { err, ok } from '@forgeax/engine-types';
|
|
|
3
3
|
import { packInterleavedVertexAttributes } from '@forgeax/engine-geometry';
|
|
4
4
|
import { box3, mat4, vec3, quat } from '@forgeax/engine-math';
|
|
5
5
|
import { AssetGuid } from '@forgeax/engine-pack/guid';
|
|
6
|
+
import { deriveDefaultLodScreenCoverages, reconcileMeshLodMeta } from '@forgeax/engine-import';
|
|
6
7
|
import { packMeshBinV4 } from '@forgeax/engine-import/mesh-bin';
|
|
7
8
|
import { deriveAnimationTargetId } from '@forgeax/engine-animation/target-id';
|
|
8
9
|
|
|
@@ -26,6 +27,10 @@ var gltfErrorPolicy = {
|
|
|
26
27
|
expected: "extension listed in v1 allowlist (see EXTENSION_ALLOWLIST in @forgeax/engine-gltf)",
|
|
27
28
|
hint: "see feat-future-gltf-extensions-allowlist; remove this extension or wait for the allowlist to expand"
|
|
28
29
|
},
|
|
30
|
+
"gltf-lod-invalid": {
|
|
31
|
+
expected: "MSFT_lod ids to reference unique existing node indices with valid coverage",
|
|
32
|
+
hint: "repair the MSFT_lod node relation or remove it from extensionsRequired before re-importing"
|
|
33
|
+
},
|
|
29
34
|
"gltf-material-transmission-invalid": {
|
|
30
35
|
expected: "KHR transmission, IOR, and volume values are finite and within their glTF ranges",
|
|
31
36
|
hint: "repair the named glTF material extension value and re-import the source"
|
|
@@ -966,7 +971,9 @@ function toMaterialAsset(mat, ctx) {
|
|
|
966
971
|
// src/check-extensions.ts
|
|
967
972
|
var EXTENSION_ALLOWLIST = [
|
|
968
973
|
"EXT_mesh_gpu_instancing",
|
|
969
|
-
"EXT_meshopt_compression"
|
|
974
|
+
"EXT_meshopt_compression",
|
|
975
|
+
"MSFT_lod",
|
|
976
|
+
"MSFT_screencoverage"
|
|
970
977
|
];
|
|
971
978
|
var SUPPORTED_EXTENSIONS = [
|
|
972
979
|
...EXTENSION_ALLOWLIST,
|
|
@@ -1169,8 +1176,8 @@ function parseGlbChunks(buffer, filePath) {
|
|
|
1169
1176
|
|
|
1170
1177
|
// src/extract-image-bytes.ts
|
|
1171
1178
|
var DATA_URI_MIME_RE = /^data:([^;,]+)(?:;[^,;]+)*;base64,/;
|
|
1172
|
-
function describeImportError(
|
|
1173
|
-
return
|
|
1179
|
+
function describeImportError(err3) {
|
|
1180
|
+
return err3.message;
|
|
1174
1181
|
}
|
|
1175
1182
|
function classifyMime(declaredMime, bytes) {
|
|
1176
1183
|
if (declaredMime === "image/png" || declaredMime === "image/jpeg") return declaredMime;
|
|
@@ -1424,6 +1431,103 @@ function deriveTextureColorSpace(input) {
|
|
|
1424
1431
|
return result;
|
|
1425
1432
|
}
|
|
1426
1433
|
|
|
1434
|
+
// src/lod/parse-lod.ts
|
|
1435
|
+
function parseGltfLodExtension(json) {
|
|
1436
|
+
const doc = json;
|
|
1437
|
+
const nodes = doc.nodes ?? [];
|
|
1438
|
+
const rawCoverage = doc.extensions?.MSFT_screencoverage?.scales;
|
|
1439
|
+
const groups = [];
|
|
1440
|
+
for (let rootNode = 0; rootNode < nodes.length; rootNode += 1) {
|
|
1441
|
+
if (nodes[rootNode]?.extensions?.MSFT_lod === void 0) continue;
|
|
1442
|
+
const idsRaw = nodes[rootNode]?.extensions?.MSFT_lod?.ids;
|
|
1443
|
+
if (!Array.isArray(idsRaw)) {
|
|
1444
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids: [], reason: "not-integer" }));
|
|
1445
|
+
}
|
|
1446
|
+
const ids = idsRaw.filter((value) => typeof value === "number");
|
|
1447
|
+
if (ids.length !== idsRaw.length || ids.some((id) => !Number.isInteger(id) || id < 0 || id >= nodes.length)) {
|
|
1448
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids, reason: "missing-node" }));
|
|
1449
|
+
}
|
|
1450
|
+
if (new Set(ids).size !== ids.length || ids.includes(rootNode)) {
|
|
1451
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids, reason: "duplicate-node" }));
|
|
1452
|
+
}
|
|
1453
|
+
const hasMesh = (node) => {
|
|
1454
|
+
if (typeof node?.mesh !== "number" || !Number.isInteger(node.mesh) || node.mesh < 0) {
|
|
1455
|
+
return false;
|
|
1456
|
+
}
|
|
1457
|
+
const mesh = doc.meshes?.[node.mesh];
|
|
1458
|
+
return mesh !== void 0 && Array.isArray(mesh.primitives) && mesh.primitives.length > 0;
|
|
1459
|
+
};
|
|
1460
|
+
if (!hasMesh(nodes[rootNode]) || ids.some((id) => !hasMesh(nodes[id]))) {
|
|
1461
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids, reason: "missing-node" }));
|
|
1462
|
+
}
|
|
1463
|
+
const nodeCoverage = nodes[rootNode]?.extras?.MSFT_screencoverage;
|
|
1464
|
+
const coverage = normalizeScreenCoverages(
|
|
1465
|
+
nodeCoverage === void 0 ? rawCoverage : nodeCoverage,
|
|
1466
|
+
ids.length,
|
|
1467
|
+
nodeCoverage !== void 0
|
|
1468
|
+
);
|
|
1469
|
+
if (coverage === "invalid") {
|
|
1470
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids, reason: "coverage" }));
|
|
1471
|
+
}
|
|
1472
|
+
groups.push({
|
|
1473
|
+
rootNode,
|
|
1474
|
+
lodNodeIds: ids,
|
|
1475
|
+
screenCoverages: coverage ?? []
|
|
1476
|
+
});
|
|
1477
|
+
}
|
|
1478
|
+
const first = groups[0];
|
|
1479
|
+
if (first === void 0) {
|
|
1480
|
+
if (doc.extensionsRequired?.some((extension) => extension === "MSFT_lod")) {
|
|
1481
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode: 0, ids: [], reason: "missing-node" }));
|
|
1482
|
+
}
|
|
1483
|
+
return ok({ rootNode: 0, lodNodeIds: [], screenCoverages: [], groups: [] });
|
|
1484
|
+
}
|
|
1485
|
+
return ok({
|
|
1486
|
+
rootNode: first.rootNode,
|
|
1487
|
+
lodNodeIds: first.lodNodeIds,
|
|
1488
|
+
screenCoverages: first.screenCoverages,
|
|
1489
|
+
groups
|
|
1490
|
+
});
|
|
1491
|
+
}
|
|
1492
|
+
function normalizeScreenCoverages(raw, lodCount, officialNodeForm) {
|
|
1493
|
+
if (raw === void 0) return void 0;
|
|
1494
|
+
if (!Array.isArray(raw)) return "invalid";
|
|
1495
|
+
if (raw.some(
|
|
1496
|
+
(value) => typeof value !== "number" || !Number.isFinite(value) || value <= 0 || value > 1
|
|
1497
|
+
)) {
|
|
1498
|
+
return "invalid";
|
|
1499
|
+
}
|
|
1500
|
+
const expectedLengths = officialNodeForm ? [lodCount + 1] : [lodCount, lodCount + 1];
|
|
1501
|
+
if (!expectedLengths.includes(raw.length)) return "invalid";
|
|
1502
|
+
const values = raw.slice(0, lodCount);
|
|
1503
|
+
for (let index = 1; index < values.length; index += 1) {
|
|
1504
|
+
if (values[index] >= values[index - 1]) return "invalid";
|
|
1505
|
+
}
|
|
1506
|
+
return values;
|
|
1507
|
+
}
|
|
1508
|
+
function projectGltfLodMeta(input) {
|
|
1509
|
+
const defaults = deriveDefaultLodScreenCoverages(input.levels.length + 1);
|
|
1510
|
+
const previous = (input.previous ?? []).map((level) => ({
|
|
1511
|
+
sourceKey: level.sourceKey,
|
|
1512
|
+
meshGuid: level.guid,
|
|
1513
|
+
...level.screenCoverage === void 0 ? {} : { screenCoverage: level.screenCoverage }
|
|
1514
|
+
}));
|
|
1515
|
+
const next = input.levels.map((level, index) => ({
|
|
1516
|
+
sourceKey: level.sourceKey,
|
|
1517
|
+
meshGuid: level.guid,
|
|
1518
|
+
screenCoverage: level.screenCoverage ?? defaults[index] ?? 0
|
|
1519
|
+
}));
|
|
1520
|
+
const reconciled = reconcileMeshLodMeta(previous, next);
|
|
1521
|
+
if (!reconciled.ok) return err({ code: reconciled.error.code, reason: reconciled.error.reason });
|
|
1522
|
+
return ok({
|
|
1523
|
+
lods: reconciled.value.lods.map((level) => ({
|
|
1524
|
+
sourceKey: level.sourceKey,
|
|
1525
|
+
meshGuid: level.meshGuid,
|
|
1526
|
+
screenCoverage: level.screenCoverage
|
|
1527
|
+
}))
|
|
1528
|
+
});
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1427
1531
|
// src/material/parse-material.ts
|
|
1428
1532
|
function tuple2(values) {
|
|
1429
1533
|
if (values === void 0 || values.length < 2) return void 0;
|
|
@@ -2336,6 +2440,8 @@ async function parseGltfWithBin(json, ctx) {
|
|
|
2336
2440
|
const extResult = checkExtensions(json);
|
|
2337
2441
|
if (!extResult.ok) return err(extResult.error);
|
|
2338
2442
|
const unsupportedExtensions = extResult.value.unsupportedUsed;
|
|
2443
|
+
const lodResult = parseGltfLodExtension(json);
|
|
2444
|
+
if (!lodResult.ok) return err(lodResult.error);
|
|
2339
2445
|
const lightsResult = parsePunctualLights(json, ctx.filePath);
|
|
2340
2446
|
if (!lightsResult.ok) return err(lightsResult.error);
|
|
2341
2447
|
const meshesJson = json.meshes ?? [];
|
|
@@ -2876,7 +2982,8 @@ async function parseGltfWithBin(json, ctx) {
|
|
|
2876
2982
|
matrixTrsCoexistNodes: diagnostics.matrixTrsCoexistNodes
|
|
2877
2983
|
},
|
|
2878
2984
|
meshPrimitiveCount,
|
|
2879
|
-
lights: lightsResult.value
|
|
2985
|
+
lights: lightsResult.value,
|
|
2986
|
+
...lodResult.value.lodNodeIds.length === 0 ? {} : { lod: lodResult.value }
|
|
2880
2987
|
});
|
|
2881
2988
|
}
|
|
2882
2989
|
async function parseGltfForImporter(json, externalLoader, filePath, options = {}) {
|
|
@@ -3015,6 +3122,74 @@ function toAssetPack(doc, existingMeta, source) {
|
|
|
3015
3122
|
const sourceOverrides = {
|
|
3016
3123
|
...existingMeta?.sourceOverrides ?? {}
|
|
3017
3124
|
};
|
|
3125
|
+
const lodGroups = doc.lod === void 0 ? [] : doc.lod.groups.length > 0 ? doc.lod.groups : [doc.lod];
|
|
3126
|
+
for (const lodGroup of lodGroups) {
|
|
3127
|
+
if (lodGroup.lodNodeIds.length === 0) continue;
|
|
3128
|
+
const rootMeshIndex = doc.nodes[lodGroup.rootNode]?.meshIndex;
|
|
3129
|
+
const referencedMeshIndices = [
|
|
3130
|
+
rootMeshIndex,
|
|
3131
|
+
...lodGroup.lodNodeIds.map((nodeIndex) => doc.nodes[nodeIndex]?.meshIndex)
|
|
3132
|
+
];
|
|
3133
|
+
if (referencedMeshIndices.some(
|
|
3134
|
+
(meshIndex) => !Number.isInteger(meshIndex) || meshIndex === null || meshIndex === void 0 || !seenMeshIndices.has(meshIndex)
|
|
3135
|
+
)) {
|
|
3136
|
+
return err(
|
|
3137
|
+
gltfErr("gltf-lod-invalid", {
|
|
3138
|
+
rootNode: lodGroup.rootNode,
|
|
3139
|
+
ids: lodGroup.lodNodeIds,
|
|
3140
|
+
reason: "missing-node"
|
|
3141
|
+
})
|
|
3142
|
+
);
|
|
3143
|
+
}
|
|
3144
|
+
const rootOutput = rootMeshIndex === void 0 || rootMeshIndex === null ? void 0 : subAssetByKindIndex.get(`mesh:${rootMeshIndex}`);
|
|
3145
|
+
const levels = lodGroup.lodNodeIds.map((nodeIndex, index) => {
|
|
3146
|
+
const meshIndex = doc.nodes[nodeIndex]?.meshIndex;
|
|
3147
|
+
const output = meshIndex === void 0 || meshIndex === null ? void 0 : subAssetByKindIndex.get(`mesh:${meshIndex}`);
|
|
3148
|
+
return {
|
|
3149
|
+
sourceKey: output?.sourceKey ?? `mesh:${meshIndex ?? nodeIndex}`,
|
|
3150
|
+
meshGuid: output?.guid ?? "",
|
|
3151
|
+
...lodGroup.screenCoverages[index] === void 0 ? {} : { screenCoverage: lodGroup.screenCoverages[index] }
|
|
3152
|
+
};
|
|
3153
|
+
});
|
|
3154
|
+
if (rootOutput?.sourceKey !== void 0 && levels.every((level) => level.meshGuid !== "")) {
|
|
3155
|
+
const previousLodMeta = (() => {
|
|
3156
|
+
const raw = existingMeta?.sourceOverrides?.[rootOutput.sourceKey]?.lods;
|
|
3157
|
+
if (!Array.isArray(raw)) return void 0;
|
|
3158
|
+
return raw.flatMap((entry) => {
|
|
3159
|
+
if (entry === null || typeof entry !== "object") return [];
|
|
3160
|
+
const value = entry;
|
|
3161
|
+
return typeof value.sourceKey === "string" && typeof value.meshGuid === "string" ? [
|
|
3162
|
+
{
|
|
3163
|
+
sourceKey: value.sourceKey,
|
|
3164
|
+
guid: value.meshGuid,
|
|
3165
|
+
...typeof value.screenCoverage === "number" ? { screenCoverage: value.screenCoverage } : {}
|
|
3166
|
+
}
|
|
3167
|
+
] : [];
|
|
3168
|
+
});
|
|
3169
|
+
})();
|
|
3170
|
+
const projected = projectGltfLodMeta({
|
|
3171
|
+
rootSourceKey: rootOutput.sourceKey,
|
|
3172
|
+
levels: levels.map((level) => ({
|
|
3173
|
+
sourceKey: level.sourceKey,
|
|
3174
|
+
guid: level.meshGuid,
|
|
3175
|
+
...level.screenCoverage === void 0 ? {} : { screenCoverage: level.screenCoverage }
|
|
3176
|
+
})),
|
|
3177
|
+
...previousLodMeta === void 0 ? {} : { previous: previousLodMeta }
|
|
3178
|
+
});
|
|
3179
|
+
if (!projected.ok)
|
|
3180
|
+
return err(
|
|
3181
|
+
gltfErr("gltf-lod-invalid", {
|
|
3182
|
+
rootNode: lodGroup.rootNode,
|
|
3183
|
+
ids: lodGroup.lodNodeIds,
|
|
3184
|
+
reason: "coverage"
|
|
3185
|
+
})
|
|
3186
|
+
);
|
|
3187
|
+
sourceOverrides[rootOutput.sourceKey] = {
|
|
3188
|
+
...sourceOverrides[rootOutput.sourceKey] ?? {},
|
|
3189
|
+
lods: projected.value.lods
|
|
3190
|
+
};
|
|
3191
|
+
}
|
|
3192
|
+
}
|
|
3018
3193
|
for (const meshIndex of seenMeshIndices) {
|
|
3019
3194
|
const meshOutput = subAssetByKindIndex.get(`mesh:${meshIndex}`);
|
|
3020
3195
|
if (meshOutput?.sourceKey === void 0) continue;
|
|
@@ -3456,7 +3631,55 @@ async function importGltf(ctx, meshopt) {
|
|
|
3456
3631
|
})
|
|
3457
3632
|
};
|
|
3458
3633
|
}
|
|
3459
|
-
const
|
|
3634
|
+
const stabilizedMesh = stabilizeMeshMaterialSlots(
|
|
3635
|
+
bridged.value,
|
|
3636
|
+
ctx,
|
|
3637
|
+
sub.guid,
|
|
3638
|
+
sub.sourceKey
|
|
3639
|
+
);
|
|
3640
|
+
const lodGroup = doc.lod?.groups?.find(
|
|
3641
|
+
(group) => doc.nodes[group.rootNode]?.meshIndex === sub.sourceIndex
|
|
3642
|
+
) ?? (doc.lod?.rootNode !== void 0 && doc.nodes[doc.lod.rootNode]?.meshIndex === sub.sourceIndex ? doc.lod : void 0);
|
|
3643
|
+
const rootMeshIndex = lodGroup === void 0 ? void 0 : sub.sourceIndex;
|
|
3644
|
+
if (lodGroup !== void 0) {
|
|
3645
|
+
const referencedMeshIndices = [
|
|
3646
|
+
sub.sourceIndex,
|
|
3647
|
+
...lodGroup.lodNodeIds.map((nodeIndex) => doc.nodes[nodeIndex]?.meshIndex)
|
|
3648
|
+
];
|
|
3649
|
+
const missingMeshIndex = referencedMeshIndices.find(
|
|
3650
|
+
(meshIndex) => !Number.isInteger(meshIndex) || meshIndex === null || meshIndex === void 0 || maps.meshGuidByIndex.get(meshIndex) === void 0
|
|
3651
|
+
);
|
|
3652
|
+
if (missingMeshIndex !== void 0) {
|
|
3653
|
+
return {
|
|
3654
|
+
ok: false,
|
|
3655
|
+
error: new ImportError({
|
|
3656
|
+
code: "import-internal-error",
|
|
3657
|
+
expected: "every MSFT_lod node to resolve a cooked mesh sub-asset",
|
|
3658
|
+
actual: String(missingMeshIndex),
|
|
3659
|
+
hint: "repair the referenced glTF mesh primitives and re-run the importer",
|
|
3660
|
+
detail: {
|
|
3661
|
+
reason: `MSFT_lod group for mesh ${sub.sourceIndex} references an unprojected mesh`
|
|
3662
|
+
}
|
|
3663
|
+
})
|
|
3664
|
+
};
|
|
3665
|
+
}
|
|
3666
|
+
}
|
|
3667
|
+
const authoredLods = sub.sourceKey === void 0 ? void 0 : ctx.sourceOverrides?.[sub.sourceKey]?.lods;
|
|
3668
|
+
const lodEntries = Array.isArray(authoredLods) ? authoredLods : [];
|
|
3669
|
+
const lodLevels = rootMeshIndex === sub.sourceIndex && lodGroup !== void 0 ? lodGroup.lodNodeIds.flatMap((nodeIndex, index) => {
|
|
3670
|
+
const meshIndex = doc.nodes[nodeIndex]?.meshIndex;
|
|
3671
|
+
const guid = meshIndex === void 0 || meshIndex === null ? void 0 : maps.meshGuidByIndex.get(meshIndex);
|
|
3672
|
+
if (guid === void 0) return [];
|
|
3673
|
+
const authored = lodEntries[index];
|
|
3674
|
+
const authoredCoverage = authored !== null && typeof authored === "object" ? authored.screenCoverage : void 0;
|
|
3675
|
+
const coverage = typeof authoredCoverage === "number" ? authoredCoverage : lodGroup.screenCoverages[index] ?? deriveDefaultLodScreenCoverages(lodGroup.lodNodeIds.length + 1)[index];
|
|
3676
|
+
const parsed2 = AssetGuid.parse(guid);
|
|
3677
|
+
return parsed2.ok && coverage !== void 0 ? [{ mesh: parsed2.value, screenCoverage: coverage, guid }] : [];
|
|
3678
|
+
}) : [];
|
|
3679
|
+
const meshPayload = lodLevels.length === 0 ? stabilizedMesh : {
|
|
3680
|
+
...stabilizedMesh,
|
|
3681
|
+
lods: lodLevels.map(({ mesh, screenCoverage }) => ({ mesh, screenCoverage }))
|
|
3682
|
+
};
|
|
3460
3683
|
const materialRefs = [];
|
|
3461
3684
|
const seenMaterialGuids = /* @__PURE__ */ new Set();
|
|
3462
3685
|
for (let slotIndex = 0; slotIndex < meshPayload.materialSlots.length; slotIndex++) {
|
|
@@ -3470,6 +3693,15 @@ async function importGltf(ctx, meshopt) {
|
|
|
3470
3693
|
});
|
|
3471
3694
|
}
|
|
3472
3695
|
}
|
|
3696
|
+
for (const [lodIndex, level] of lodLevels.entries()) {
|
|
3697
|
+
if (!seenMaterialGuids.has(level.guid.toLowerCase())) {
|
|
3698
|
+
seenMaterialGuids.add(level.guid.toLowerCase());
|
|
3699
|
+
materialRefs.push({
|
|
3700
|
+
guid: level.guid,
|
|
3701
|
+
sourceField: { fieldName: "lods", arrayIndex: lodIndex }
|
|
3702
|
+
});
|
|
3703
|
+
}
|
|
3704
|
+
}
|
|
3473
3705
|
out.push({
|
|
3474
3706
|
guid: sub.guid,
|
|
3475
3707
|
kind: "mesh",
|
|
@@ -3773,6 +4005,6 @@ function serializeMetaJson(meta) {
|
|
|
3773
4005
|
`;
|
|
3774
4006
|
}
|
|
3775
4007
|
|
|
3776
|
-
export { COMPONENT_TYPE, EXTENSION_ALLOWLIST, GLTF_ERROR_HINTS, checkExtensions, decodeAccessor, decodeColorAccessor, decomposeNodeTransform, deriveGltfSourceKeys, gltfDocToSceneAsset, gltfErr, gltfImporter, meshIrToMeshAsset, parseGlb, parseGlbChunks, parseGltf, parseGltfHeader, reimportReuseMeta, serializeMetaJson, sourceKeyForGltfOutput, subAssetKey, toAssetPack, toMaterialAsset };
|
|
4008
|
+
export { COMPONENT_TYPE, EXTENSION_ALLOWLIST, GLTF_ERROR_HINTS, checkExtensions, decodeAccessor, decodeColorAccessor, decomposeNodeTransform, deriveGltfSourceKeys, gltfDocToSceneAsset, gltfErr, gltfImporter, meshIrToMeshAsset, parseGlb, parseGlbChunks, parseGltf, parseGltfHeader, parseGltfLodExtension, projectGltfLodMeta, reimportReuseMeta, serializeMetaJson, sourceKeyForGltfOutput, subAssetKey, toAssetPack, toMaterialAsset };
|
|
3777
4009
|
//# sourceMappingURL=index.mjs.map
|
|
3778
4010
|
//# sourceMappingURL=index.mjs.map
|