@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.
- package/README.md +115 -3
- 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/__tests__/physical-clearcoat.unit.test.d.ts +2 -0
- package/dist/__tests__/physical-clearcoat.unit.test.d.ts.map +1 -0
- package/dist/__tests__/physical-material-import.integration.test.d.ts +2 -0
- package/dist/__tests__/physical-material-import.integration.test.d.ts.map +1 -0
- package/dist/bridge.d.ts +1 -0
- package/dist/bridge.d.ts.map +1 -1
- package/dist/check-extensions.d.ts +1 -1
- package/dist/check-extensions.d.ts.map +1 -1
- package/dist/cli-gltf.mjs +362 -14
- package/dist/cli-gltf.mjs.map +1 -1
- package/dist/errors.d.ts +13 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/gltf-importer.d.ts.map +1 -1
- package/dist/image-color-space.d.ts +12 -0
- package/dist/image-color-space.d.ts.map +1 -1
- package/dist/importer-entry.mjs +516 -25
- package/dist/importer-entry.mjs.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +609 -28
- 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/material/parse-material.d.ts +66 -0
- package/dist/material/parse-material.d.ts.map +1 -1
- package/dist/node-file-entry.mjs +265 -8
- 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__/bridge-material-values.unit.test.ts +84 -1
- package/src/__tests__/bridge-skin-stride.unit.test.ts +22 -0
- 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 +8 -0
- package/src/__tests__/gltf-error-migration.test.ts +3 -3
- package/src/__tests__/gltf.unit.test.ts +60 -5
- package/src/__tests__/lod-import.integration.test.ts +106 -0
- package/src/__tests__/lod-meta-publication.integration.test.ts +24 -0
- package/src/__tests__/material-pack-refs.integration.test.ts +53 -0
- package/src/__tests__/parse-gltf.unit.test.ts +50 -0
- package/src/__tests__/physical-clearcoat.unit.test.ts +110 -0
- package/src/__tests__/physical-material-import.integration.test.ts +239 -0
- package/src/__tests__/source-key-producer.integration.test.ts +6 -2
- package/src/bridge.ts +259 -14
- package/src/check-extensions.ts +16 -8
- package/src/errors.ts +32 -2
- package/src/gltf-importer.ts +122 -1
- package/src/image-color-space.ts +29 -4
- package/src/index.ts +10 -1
- package/src/lod/parse-lod.ts +129 -0
- package/src/lod/project-meta.ts +48 -0
- package/src/material/parse-material.ts +307 -0
- package/src/parse-gltf.ts +97 -1
- package/src/reimport-reuse-meta.ts +3 -1
- package/dist/.tsbuildinfo +0 -1
package/dist/cli-gltf.mjs
CHANGED
|
@@ -3,8 +3,9 @@ import { realpath, stat, readFile, writeFile, readdir } from 'fs/promises';
|
|
|
3
3
|
import { resolve, dirname, join } from 'path';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import { parseArgs } from 'util';
|
|
6
|
-
import {
|
|
6
|
+
import { err, reconcileMeshMaterialSlotTopology, MESH_MATERIAL_SLOT_SOURCE_OVERRIDE_PAYLOAD_SCHEMA, ok, ImportError, IMPORT_ERROR_HINTS } from '@forgeax/engine-types';
|
|
7
7
|
import { vec3, quat, mat4 } from '@forgeax/engine-math';
|
|
8
|
+
import { deriveDefaultLodScreenCoverages, reconcileMeshLodMeta } from '@forgeax/engine-import';
|
|
8
9
|
import { deriveAnimationTargetId } from '@forgeax/engine-animation/target-id';
|
|
9
10
|
import { AssetGuid } from '@forgeax/engine-pack/guid';
|
|
10
11
|
|
|
@@ -24,13 +25,21 @@ var gltfErrorPolicy = {
|
|
|
24
25
|
hint: "rebuild .gltf with valid bufferViews; check accessor index; ensure accessor.byteOffset + EFFECTIVE_STRIDE * (count - 1) + element_size <= bufferView.byteLength"
|
|
25
26
|
},
|
|
26
27
|
"gltf-extension-unsupported": {
|
|
27
|
-
expected: "extension listed in
|
|
28
|
-
hint: "
|
|
28
|
+
expected: "extension listed in the supported allowlist (see EXTENSION_ALLOWLIST in @forgeax/engine-gltf)",
|
|
29
|
+
hint: "remove the unsupported required extension or use a supported glTF extension; extensionsUsed-only entries remain diagnostic"
|
|
30
|
+
},
|
|
31
|
+
"gltf-lod-invalid": {
|
|
32
|
+
expected: "MSFT_lod ids to reference unique existing node indices with valid coverage",
|
|
33
|
+
hint: "repair the MSFT_lod node relation or remove it from extensionsRequired before re-importing"
|
|
29
34
|
},
|
|
30
35
|
"gltf-material-transmission-invalid": {
|
|
31
36
|
expected: "KHR transmission, IOR, and volume values are finite and within their glTF ranges",
|
|
32
37
|
hint: "repair the named glTF material extension value and re-import the source"
|
|
33
38
|
},
|
|
39
|
+
"gltf-material-physical-invalid": {
|
|
40
|
+
expected: "KHR clearcoat, anisotropy, sheen, iridescence, and specular values are finite and within their glTF ranges",
|
|
41
|
+
hint: "repair the named physical material extension value and re-import the source"
|
|
42
|
+
},
|
|
34
43
|
"gltf-accessor-type-mismatch": {
|
|
35
44
|
expected: "dense fixed-stride accessor with supported componentType",
|
|
36
45
|
hint: "sparse: see feat-future-gltf-sparse-accessor; morph: see feat-future-gltf-morph; interleaved: see feat-future-gltf-mesh-multi-section"
|
|
@@ -394,15 +403,22 @@ function decodeColorAccessor(input, flags = {}) {
|
|
|
394
403
|
// src/check-extensions.ts
|
|
395
404
|
var EXTENSION_ALLOWLIST = [
|
|
396
405
|
"EXT_mesh_gpu_instancing",
|
|
397
|
-
"EXT_meshopt_compression"
|
|
398
|
-
];
|
|
399
|
-
var SUPPORTED_EXTENSIONS = [
|
|
400
|
-
...EXTENSION_ALLOWLIST,
|
|
406
|
+
"EXT_meshopt_compression",
|
|
401
407
|
"KHR_lights_punctual",
|
|
402
408
|
"KHR_texture_transform",
|
|
403
409
|
"KHR_materials_transmission",
|
|
404
410
|
"KHR_materials_ior",
|
|
405
|
-
"KHR_materials_volume"
|
|
411
|
+
"KHR_materials_volume",
|
|
412
|
+
"KHR_materials_clearcoat",
|
|
413
|
+
"KHR_materials_anisotropy",
|
|
414
|
+
"KHR_materials_sheen",
|
|
415
|
+
"KHR_materials_iridescence",
|
|
416
|
+
"KHR_materials_specular"
|
|
417
|
+
];
|
|
418
|
+
var SUPPORTED_EXTENSIONS = [
|
|
419
|
+
...EXTENSION_ALLOWLIST,
|
|
420
|
+
"MSFT_lod",
|
|
421
|
+
"MSFT_screencoverage"
|
|
406
422
|
];
|
|
407
423
|
function checkExtensions(json) {
|
|
408
424
|
const required = json.extensionsRequired ?? [];
|
|
@@ -454,6 +470,103 @@ function decodeBase64(b64) {
|
|
|
454
470
|
return out;
|
|
455
471
|
}
|
|
456
472
|
|
|
473
|
+
// src/lod/parse-lod.ts
|
|
474
|
+
function parseGltfLodExtension(json) {
|
|
475
|
+
const doc = json;
|
|
476
|
+
const nodes = doc.nodes ?? [];
|
|
477
|
+
const rawCoverage = doc.extensions?.MSFT_screencoverage?.scales;
|
|
478
|
+
const groups = [];
|
|
479
|
+
for (let rootNode = 0; rootNode < nodes.length; rootNode += 1) {
|
|
480
|
+
if (nodes[rootNode]?.extensions?.MSFT_lod === void 0) continue;
|
|
481
|
+
const idsRaw = nodes[rootNode]?.extensions?.MSFT_lod?.ids;
|
|
482
|
+
if (!Array.isArray(idsRaw)) {
|
|
483
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids: [], reason: "not-integer" }));
|
|
484
|
+
}
|
|
485
|
+
const ids = idsRaw.filter((value) => typeof value === "number");
|
|
486
|
+
if (ids.length !== idsRaw.length || ids.some((id) => !Number.isInteger(id) || id < 0 || id >= nodes.length)) {
|
|
487
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids, reason: "missing-node" }));
|
|
488
|
+
}
|
|
489
|
+
if (new Set(ids).size !== ids.length || ids.includes(rootNode)) {
|
|
490
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids, reason: "duplicate-node" }));
|
|
491
|
+
}
|
|
492
|
+
const hasMesh = (node) => {
|
|
493
|
+
if (typeof node?.mesh !== "number" || !Number.isInteger(node.mesh) || node.mesh < 0) {
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
496
|
+
const mesh = doc.meshes?.[node.mesh];
|
|
497
|
+
return mesh !== void 0 && Array.isArray(mesh.primitives) && mesh.primitives.length > 0;
|
|
498
|
+
};
|
|
499
|
+
if (!hasMesh(nodes[rootNode]) || ids.some((id) => !hasMesh(nodes[id]))) {
|
|
500
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids, reason: "missing-node" }));
|
|
501
|
+
}
|
|
502
|
+
const nodeCoverage = nodes[rootNode]?.extras?.MSFT_screencoverage;
|
|
503
|
+
const coverage = normalizeScreenCoverages(
|
|
504
|
+
nodeCoverage === void 0 ? rawCoverage : nodeCoverage,
|
|
505
|
+
ids.length,
|
|
506
|
+
nodeCoverage !== void 0
|
|
507
|
+
);
|
|
508
|
+
if (coverage === "invalid") {
|
|
509
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids, reason: "coverage" }));
|
|
510
|
+
}
|
|
511
|
+
groups.push({
|
|
512
|
+
rootNode,
|
|
513
|
+
lodNodeIds: ids,
|
|
514
|
+
screenCoverages: coverage ?? []
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
const first = groups[0];
|
|
518
|
+
if (first === void 0) {
|
|
519
|
+
if (doc.extensionsRequired?.some((extension) => extension === "MSFT_lod")) {
|
|
520
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode: 0, ids: [], reason: "missing-node" }));
|
|
521
|
+
}
|
|
522
|
+
return ok({ rootNode: 0, lodNodeIds: [], screenCoverages: [], groups: [] });
|
|
523
|
+
}
|
|
524
|
+
return ok({
|
|
525
|
+
rootNode: first.rootNode,
|
|
526
|
+
lodNodeIds: first.lodNodeIds,
|
|
527
|
+
screenCoverages: first.screenCoverages,
|
|
528
|
+
groups
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
function normalizeScreenCoverages(raw, lodCount, officialNodeForm) {
|
|
532
|
+
if (raw === void 0) return void 0;
|
|
533
|
+
if (!Array.isArray(raw)) return "invalid";
|
|
534
|
+
if (raw.some(
|
|
535
|
+
(value) => typeof value !== "number" || !Number.isFinite(value) || value <= 0 || value > 1
|
|
536
|
+
)) {
|
|
537
|
+
return "invalid";
|
|
538
|
+
}
|
|
539
|
+
const expectedLengths = officialNodeForm ? [lodCount + 1] : [lodCount, lodCount + 1];
|
|
540
|
+
if (!expectedLengths.includes(raw.length)) return "invalid";
|
|
541
|
+
const values = raw.slice(0, lodCount);
|
|
542
|
+
for (let index = 1; index < values.length; index += 1) {
|
|
543
|
+
if (values[index] >= values[index - 1]) return "invalid";
|
|
544
|
+
}
|
|
545
|
+
return values;
|
|
546
|
+
}
|
|
547
|
+
function projectGltfLodMeta(input) {
|
|
548
|
+
const defaults = deriveDefaultLodScreenCoverages(input.levels.length + 1);
|
|
549
|
+
const previous = (input.previous ?? []).map((level) => ({
|
|
550
|
+
sourceKey: level.sourceKey,
|
|
551
|
+
meshGuid: level.guid,
|
|
552
|
+
...level.screenCoverage === void 0 ? {} : { screenCoverage: level.screenCoverage }
|
|
553
|
+
}));
|
|
554
|
+
const next = input.levels.map((level, index) => ({
|
|
555
|
+
sourceKey: level.sourceKey,
|
|
556
|
+
meshGuid: level.guid,
|
|
557
|
+
screenCoverage: level.screenCoverage ?? defaults[index] ?? 0
|
|
558
|
+
}));
|
|
559
|
+
const reconciled = reconcileMeshLodMeta(previous, next);
|
|
560
|
+
if (!reconciled.ok) return err({ code: reconciled.error.code, reason: reconciled.error.reason });
|
|
561
|
+
return ok({
|
|
562
|
+
lods: reconciled.value.lods.map((level) => ({
|
|
563
|
+
sourceKey: level.sourceKey,
|
|
564
|
+
meshGuid: level.meshGuid,
|
|
565
|
+
screenCoverage: level.screenCoverage
|
|
566
|
+
}))
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
|
|
457
570
|
// src/material/parse-material.ts
|
|
458
571
|
function tuple2(values) {
|
|
459
572
|
if (values === void 0 || values.length < 2) return void 0;
|
|
@@ -498,6 +611,11 @@ function parseMaterial(matJson, textures) {
|
|
|
498
611
|
const transmission = matJson.extensions?.KHR_materials_transmission;
|
|
499
612
|
const ior = matJson.extensions?.KHR_materials_ior;
|
|
500
613
|
const volume = matJson.extensions?.KHR_materials_volume;
|
|
614
|
+
const clearcoat = matJson.extensions?.KHR_materials_clearcoat;
|
|
615
|
+
const anisotropy = matJson.extensions?.KHR_materials_anisotropy;
|
|
616
|
+
const sheen = matJson.extensions?.KHR_materials_sheen;
|
|
617
|
+
const iridescence = matJson.extensions?.KHR_materials_iridescence;
|
|
618
|
+
const specular = matJson.extensions?.KHR_materials_specular;
|
|
501
619
|
const attenuationColor = tuple3(volume?.attenuationColor);
|
|
502
620
|
const invalid = (extension, field, reason, actual) => err(
|
|
503
621
|
gltfErr("gltf-material-transmission-invalid", {
|
|
@@ -507,6 +625,14 @@ function parseMaterial(matJson, textures) {
|
|
|
507
625
|
...actual === void 0 ? {} : { actual }
|
|
508
626
|
})
|
|
509
627
|
);
|
|
628
|
+
const physicalInvalid = (extension, field, reason, actual) => err(
|
|
629
|
+
gltfErr("gltf-material-physical-invalid", {
|
|
630
|
+
extension,
|
|
631
|
+
field,
|
|
632
|
+
reason,
|
|
633
|
+
...actual === void 0 ? {} : { actual }
|
|
634
|
+
})
|
|
635
|
+
);
|
|
510
636
|
if (transmission?.transmissionFactor !== void 0 && (typeof transmission.transmissionFactor !== "number" || !Number.isFinite(transmission.transmissionFactor))) {
|
|
511
637
|
return invalid(
|
|
512
638
|
"KHR_materials_transmission",
|
|
@@ -557,6 +683,98 @@ function parseMaterial(matJson, textures) {
|
|
|
557
683
|
if (alphaMode === "BLEND" && (transmission?.transmissionFactor ?? 0) > 0) {
|
|
558
684
|
return invalid("KHR_materials_transmission", "alphaMode", "blend", alphaMode);
|
|
559
685
|
}
|
|
686
|
+
if (clearcoat?.clearcoatFactor !== void 0 && (typeof clearcoat.clearcoatFactor !== "number" || !Number.isFinite(clearcoat.clearcoatFactor))) {
|
|
687
|
+
return physicalInvalid(
|
|
688
|
+
"KHR_materials_clearcoat",
|
|
689
|
+
"clearcoatFactor",
|
|
690
|
+
"non-finite",
|
|
691
|
+
clearcoat.clearcoatFactor
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
if (clearcoat?.clearcoatFactor !== void 0 && (clearcoat.clearcoatFactor < 0 || clearcoat.clearcoatFactor > 1)) {
|
|
695
|
+
return physicalInvalid(
|
|
696
|
+
"KHR_materials_clearcoat",
|
|
697
|
+
"clearcoatFactor",
|
|
698
|
+
"range",
|
|
699
|
+
clearcoat.clearcoatFactor
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
if (clearcoat?.clearcoatRoughnessFactor !== void 0 && (typeof clearcoat.clearcoatRoughnessFactor !== "number" || !Number.isFinite(clearcoat.clearcoatRoughnessFactor))) {
|
|
703
|
+
return physicalInvalid(
|
|
704
|
+
"KHR_materials_clearcoat",
|
|
705
|
+
"clearcoatRoughnessFactor",
|
|
706
|
+
"non-finite",
|
|
707
|
+
clearcoat.clearcoatRoughnessFactor
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
if (clearcoat?.clearcoatRoughnessFactor !== void 0 && (clearcoat.clearcoatRoughnessFactor < 0 || clearcoat.clearcoatRoughnessFactor > 1)) {
|
|
711
|
+
return physicalInvalid(
|
|
712
|
+
"KHR_materials_clearcoat",
|
|
713
|
+
"clearcoatRoughnessFactor",
|
|
714
|
+
"range",
|
|
715
|
+
clearcoat.clearcoatRoughnessFactor
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
if (clearcoat?.clearcoatNormalTexture?.scale !== void 0 && (typeof clearcoat.clearcoatNormalTexture.scale !== "number" || !Number.isFinite(clearcoat.clearcoatNormalTexture.scale) || clearcoat.clearcoatNormalTexture.scale < 0)) {
|
|
719
|
+
return physicalInvalid(
|
|
720
|
+
"KHR_materials_clearcoat",
|
|
721
|
+
"clearcoatNormalTexture.scale",
|
|
722
|
+
"range",
|
|
723
|
+
clearcoat.clearcoatNormalTexture.scale
|
|
724
|
+
);
|
|
725
|
+
}
|
|
726
|
+
const validateUnit = (extension, field, value) => {
|
|
727
|
+
if (value === void 0) return void 0;
|
|
728
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
729
|
+
return physicalInvalid(extension, field, "non-finite", value);
|
|
730
|
+
if (value < 0 || value > 1) return physicalInvalid(extension, field, "range", value);
|
|
731
|
+
return void 0;
|
|
732
|
+
};
|
|
733
|
+
const validateFinite = (extension, field, value, minimum) => {
|
|
734
|
+
if (value === void 0) return void 0;
|
|
735
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
736
|
+
return physicalInvalid(extension, field, "non-finite", value);
|
|
737
|
+
if (minimum !== void 0 && value < minimum)
|
|
738
|
+
return physicalInvalid(extension, field, "range", value);
|
|
739
|
+
return void 0;
|
|
740
|
+
};
|
|
741
|
+
const validateColor = (extension, field, value) => {
|
|
742
|
+
if (value === void 0) return void 0;
|
|
743
|
+
if (value.length < 3 || value.slice(0, 3).some((channel) => typeof channel !== "number" || !Number.isFinite(channel))) {
|
|
744
|
+
return physicalInvalid(extension, field, "type", value);
|
|
745
|
+
}
|
|
746
|
+
if (value.slice(0, 3).some((channel) => channel < 0 || channel > 1)) {
|
|
747
|
+
return physicalInvalid(extension, field, "range", value);
|
|
748
|
+
}
|
|
749
|
+
return void 0;
|
|
750
|
+
};
|
|
751
|
+
const physicalChecks = [
|
|
752
|
+
validateUnit("KHR_materials_anisotropy", "anisotropyStrength", anisotropy?.anisotropyStrength),
|
|
753
|
+
validateFinite(
|
|
754
|
+
"KHR_materials_anisotropy",
|
|
755
|
+
"anisotropyRotation",
|
|
756
|
+
anisotropy?.anisotropyRotation
|
|
757
|
+
),
|
|
758
|
+
validateUnit("KHR_materials_sheen", "sheenRoughnessFactor", sheen?.sheenRoughnessFactor),
|
|
759
|
+
validateColor("KHR_materials_sheen", "sheenColorFactor", sheen?.sheenColorFactor),
|
|
760
|
+
validateUnit("KHR_materials_iridescence", "iridescenceFactor", iridescence?.iridescenceFactor),
|
|
761
|
+
validateFinite("KHR_materials_iridescence", "iridescenceIor", iridescence?.iridescenceIor, 1),
|
|
762
|
+
validateFinite(
|
|
763
|
+
"KHR_materials_iridescence",
|
|
764
|
+
"iridescenceThicknessMinimum",
|
|
765
|
+
iridescence?.iridescenceThicknessMinimum,
|
|
766
|
+
0
|
|
767
|
+
),
|
|
768
|
+
validateFinite(
|
|
769
|
+
"KHR_materials_iridescence",
|
|
770
|
+
"iridescenceThicknessMaximum",
|
|
771
|
+
iridescence?.iridescenceThicknessMaximum,
|
|
772
|
+
0
|
|
773
|
+
),
|
|
774
|
+
validateUnit("KHR_materials_specular", "specularFactor", specular?.specularFactor),
|
|
775
|
+
validateColor("KHR_materials_specular", "specularColorFactor", specular?.specularColorFactor)
|
|
776
|
+
];
|
|
777
|
+
for (const check of physicalChecks) if (check !== void 0) return check;
|
|
560
778
|
return ok({
|
|
561
779
|
...matJson.name === void 0 ? {} : { name: matJson.name },
|
|
562
780
|
baseColorFactor: baseColor4,
|
|
@@ -593,6 +811,65 @@ function parseMaterial(matJson, textures) {
|
|
|
593
811
|
...attenuationColor === void 0 ? {} : { attenuationColor },
|
|
594
812
|
...finiteOrUndefined(volume.attenuationDistance) === void 0 ? {} : { attenuationDistance: volume.attenuationDistance }
|
|
595
813
|
},
|
|
814
|
+
...clearcoat === void 0 ? {} : {
|
|
815
|
+
clearcoatFactor: clearcoat.clearcoatFactor ?? 0,
|
|
816
|
+
clearcoatChannel: 0,
|
|
817
|
+
clearcoatRoughnessFactor: clearcoat.clearcoatRoughnessFactor ?? 0,
|
|
818
|
+
clearcoatRoughnessChannel: 1,
|
|
819
|
+
clearcoatNormalChannel: [0, 1],
|
|
820
|
+
...clearcoat.clearcoatTexture === void 0 ? {} : { clearcoatTexture: parseTextureInfo(clearcoat.clearcoatTexture, textures) },
|
|
821
|
+
...clearcoat.clearcoatRoughnessTexture === void 0 ? {} : {
|
|
822
|
+
clearcoatRoughnessTexture: parseTextureInfo(
|
|
823
|
+
clearcoat.clearcoatRoughnessTexture,
|
|
824
|
+
textures
|
|
825
|
+
)
|
|
826
|
+
},
|
|
827
|
+
...clearcoat.clearcoatNormalTexture === void 0 ? {} : {
|
|
828
|
+
clearcoatNormalTexture: {
|
|
829
|
+
...parseTextureInfo(clearcoat.clearcoatNormalTexture, textures),
|
|
830
|
+
...clearcoat.clearcoatNormalTexture.scale === void 0 ? {} : { scale: clearcoat.clearcoatNormalTexture.scale }
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
},
|
|
834
|
+
...anisotropy === void 0 ? {} : {
|
|
835
|
+
anisotropyStrength: anisotropy.anisotropyStrength ?? 0,
|
|
836
|
+
anisotropyRotation: anisotropy.anisotropyRotation ?? 0,
|
|
837
|
+
anisotropyChannel: [0, 1, 2],
|
|
838
|
+
...anisotropy.anisotropyTexture === void 0 ? {} : { anisotropyTexture: parseTextureInfo(anisotropy.anisotropyTexture, textures) }
|
|
839
|
+
},
|
|
840
|
+
...sheen === void 0 ? {} : {
|
|
841
|
+
sheenColorFactor: tuple3(sheen.sheenColorFactor) ?? [0, 0, 0],
|
|
842
|
+
sheenColorChannel: [0, 1, 2],
|
|
843
|
+
sheenRoughnessFactor: sheen.sheenRoughnessFactor ?? 0,
|
|
844
|
+
sheenRoughnessChannel: 3,
|
|
845
|
+
...sheen.sheenColorTexture === void 0 ? {} : { sheenColorTexture: parseTextureInfo(sheen.sheenColorTexture, textures) },
|
|
846
|
+
...sheen.sheenRoughnessTexture === void 0 ? {} : { sheenRoughnessTexture: parseTextureInfo(sheen.sheenRoughnessTexture, textures) }
|
|
847
|
+
},
|
|
848
|
+
...iridescence === void 0 ? {} : {
|
|
849
|
+
iridescenceFactor: iridescence.iridescenceFactor ?? 0,
|
|
850
|
+
iridescenceIor: iridescence.iridescenceIor ?? 1.3,
|
|
851
|
+
iridescenceThicknessMinimum: iridescence.iridescenceThicknessMinimum ?? 100,
|
|
852
|
+
iridescenceThicknessMaximum: iridescence.iridescenceThicknessMaximum ?? 400,
|
|
853
|
+
iridescenceChannel: 0,
|
|
854
|
+
iridescenceThicknessChannel: 1,
|
|
855
|
+
...iridescence.iridescenceTexture === void 0 ? {} : { iridescenceTexture: parseTextureInfo(iridescence.iridescenceTexture, textures) },
|
|
856
|
+
...iridescence.iridescenceThicknessTexture === void 0 ? {} : {
|
|
857
|
+
iridescenceThicknessTexture: parseTextureInfo(
|
|
858
|
+
iridescence.iridescenceThicknessTexture,
|
|
859
|
+
textures
|
|
860
|
+
)
|
|
861
|
+
}
|
|
862
|
+
},
|
|
863
|
+
...specular === void 0 ? {} : {
|
|
864
|
+
specularFactor: specular.specularFactor ?? 1,
|
|
865
|
+
specularChannel: 3,
|
|
866
|
+
specularColorFactor: tuple3(specular.specularColorFactor) ?? [1, 1, 1],
|
|
867
|
+
specularColorChannel: [0, 1, 2],
|
|
868
|
+
...specular.specularTexture === void 0 ? {} : { specularTexture: parseTextureInfo(specular.specularTexture, textures) },
|
|
869
|
+
...specular.specularColorTexture === void 0 ? {} : {
|
|
870
|
+
specularColorTexture: parseTextureInfo(specular.specularColorTexture, textures)
|
|
871
|
+
}
|
|
872
|
+
},
|
|
596
873
|
...alphaMode === void 0 ? {} : { alphaMode },
|
|
597
874
|
...alphaCutoff === void 0 ? {} : { alphaCutoff },
|
|
598
875
|
...matJson.doubleSided === true ? { doubleSided: true } : {},
|
|
@@ -1479,6 +1756,8 @@ async function parseGltfWithBin(json, ctx) {
|
|
|
1479
1756
|
const extResult = checkExtensions(json);
|
|
1480
1757
|
if (!extResult.ok) return err(extResult.error);
|
|
1481
1758
|
const unsupportedExtensions = extResult.value.unsupportedUsed;
|
|
1759
|
+
const lodResult = parseGltfLodExtension(json);
|
|
1760
|
+
if (!lodResult.ok) return err(lodResult.error);
|
|
1482
1761
|
const lightsResult = parsePunctualLights(json, ctx.filePath);
|
|
1483
1762
|
if (!lightsResult.ok) return err(lightsResult.error);
|
|
1484
1763
|
const meshesJson = json.meshes ?? [];
|
|
@@ -2019,7 +2298,8 @@ async function parseGltfWithBin(json, ctx) {
|
|
|
2019
2298
|
matrixTrsCoexistNodes: diagnostics.matrixTrsCoexistNodes
|
|
2020
2299
|
},
|
|
2021
2300
|
meshPrimitiveCount,
|
|
2022
|
-
lights: lightsResult.value
|
|
2301
|
+
lights: lightsResult.value,
|
|
2302
|
+
...lodResult.value.lodNodeIds.length === 0 ? {} : { lod: lodResult.value }
|
|
2023
2303
|
});
|
|
2024
2304
|
}
|
|
2025
2305
|
async function parseGltfForImporter(json, externalLoader, filePath, options = {}) {
|
|
@@ -2158,6 +2438,74 @@ function toAssetPack(doc, existingMeta, source) {
|
|
|
2158
2438
|
const sourceOverrides = {
|
|
2159
2439
|
...existingMeta?.sourceOverrides ?? {}
|
|
2160
2440
|
};
|
|
2441
|
+
const lodGroups = doc.lod === void 0 ? [] : doc.lod.groups.length > 0 ? doc.lod.groups : [doc.lod];
|
|
2442
|
+
for (const lodGroup of lodGroups) {
|
|
2443
|
+
if (lodGroup.lodNodeIds.length === 0) continue;
|
|
2444
|
+
const rootMeshIndex = doc.nodes[lodGroup.rootNode]?.meshIndex;
|
|
2445
|
+
const referencedMeshIndices = [
|
|
2446
|
+
rootMeshIndex,
|
|
2447
|
+
...lodGroup.lodNodeIds.map((nodeIndex) => doc.nodes[nodeIndex]?.meshIndex)
|
|
2448
|
+
];
|
|
2449
|
+
if (referencedMeshIndices.some(
|
|
2450
|
+
(meshIndex) => !Number.isInteger(meshIndex) || meshIndex === null || meshIndex === void 0 || !seenMeshIndices.has(meshIndex)
|
|
2451
|
+
)) {
|
|
2452
|
+
return err(
|
|
2453
|
+
gltfErr("gltf-lod-invalid", {
|
|
2454
|
+
rootNode: lodGroup.rootNode,
|
|
2455
|
+
ids: lodGroup.lodNodeIds,
|
|
2456
|
+
reason: "missing-node"
|
|
2457
|
+
})
|
|
2458
|
+
);
|
|
2459
|
+
}
|
|
2460
|
+
const rootOutput = rootMeshIndex === void 0 || rootMeshIndex === null ? void 0 : subAssetByKindIndex.get(`mesh:${rootMeshIndex}`);
|
|
2461
|
+
const levels = lodGroup.lodNodeIds.map((nodeIndex, index) => {
|
|
2462
|
+
const meshIndex = doc.nodes[nodeIndex]?.meshIndex;
|
|
2463
|
+
const output = meshIndex === void 0 || meshIndex === null ? void 0 : subAssetByKindIndex.get(`mesh:${meshIndex}`);
|
|
2464
|
+
return {
|
|
2465
|
+
sourceKey: output?.sourceKey ?? `mesh:${meshIndex ?? nodeIndex}`,
|
|
2466
|
+
meshGuid: output?.guid ?? "",
|
|
2467
|
+
...lodGroup.screenCoverages[index] === void 0 ? {} : { screenCoverage: lodGroup.screenCoverages[index] }
|
|
2468
|
+
};
|
|
2469
|
+
});
|
|
2470
|
+
if (rootOutput?.sourceKey !== void 0 && levels.every((level) => level.meshGuid !== "")) {
|
|
2471
|
+
const previousLodMeta = (() => {
|
|
2472
|
+
const raw = existingMeta?.sourceOverrides?.[rootOutput.sourceKey]?.lods;
|
|
2473
|
+
if (!Array.isArray(raw)) return void 0;
|
|
2474
|
+
return raw.flatMap((entry) => {
|
|
2475
|
+
if (entry === null || typeof entry !== "object") return [];
|
|
2476
|
+
const value = entry;
|
|
2477
|
+
return typeof value.sourceKey === "string" && typeof value.meshGuid === "string" ? [
|
|
2478
|
+
{
|
|
2479
|
+
sourceKey: value.sourceKey,
|
|
2480
|
+
guid: value.meshGuid,
|
|
2481
|
+
...typeof value.screenCoverage === "number" ? { screenCoverage: value.screenCoverage } : {}
|
|
2482
|
+
}
|
|
2483
|
+
] : [];
|
|
2484
|
+
});
|
|
2485
|
+
})();
|
|
2486
|
+
const projected = projectGltfLodMeta({
|
|
2487
|
+
rootSourceKey: rootOutput.sourceKey,
|
|
2488
|
+
levels: levels.map((level) => ({
|
|
2489
|
+
sourceKey: level.sourceKey,
|
|
2490
|
+
guid: level.meshGuid,
|
|
2491
|
+
...level.screenCoverage === void 0 ? {} : { screenCoverage: level.screenCoverage }
|
|
2492
|
+
})),
|
|
2493
|
+
...previousLodMeta === void 0 ? {} : { previous: previousLodMeta }
|
|
2494
|
+
});
|
|
2495
|
+
if (!projected.ok)
|
|
2496
|
+
return err(
|
|
2497
|
+
gltfErr("gltf-lod-invalid", {
|
|
2498
|
+
rootNode: lodGroup.rootNode,
|
|
2499
|
+
ids: lodGroup.lodNodeIds,
|
|
2500
|
+
reason: "coverage"
|
|
2501
|
+
})
|
|
2502
|
+
);
|
|
2503
|
+
sourceOverrides[rootOutput.sourceKey] = {
|
|
2504
|
+
...sourceOverrides[rootOutput.sourceKey] ?? {},
|
|
2505
|
+
lods: projected.value.lods
|
|
2506
|
+
};
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2161
2509
|
for (const meshIndex of seenMeshIndices) {
|
|
2162
2510
|
const meshOutput = subAssetByKindIndex.get(`mesh:${meshIndex}`);
|
|
2163
2511
|
if (meshOutput?.sourceKey === void 0) continue;
|
|
@@ -2252,13 +2600,13 @@ function serializeMetaJson(meta) {
|
|
|
2252
2600
|
}
|
|
2253
2601
|
|
|
2254
2602
|
// src/cli-gltf.ts
|
|
2255
|
-
function emitError(ctx,
|
|
2603
|
+
function emitError(ctx, err3) {
|
|
2256
2604
|
const payload = {
|
|
2257
|
-
code:
|
|
2258
|
-
expected:
|
|
2259
|
-
hint:
|
|
2605
|
+
code: err3.code,
|
|
2606
|
+
expected: err3.expected,
|
|
2607
|
+
hint: err3.hint
|
|
2260
2608
|
};
|
|
2261
|
-
if (
|
|
2609
|
+
if (err3.detail !== void 0) payload.detail = err3.detail;
|
|
2262
2610
|
ctx.stderrWrite(JSON.stringify(payload));
|
|
2263
2611
|
return 1;
|
|
2264
2612
|
}
|