@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/importer-entry.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { MeshoptDecoder } from 'meshoptimizer';
|
|
2
|
+
import { deriveDefaultLodScreenCoverages } from '@forgeax/engine-import';
|
|
2
3
|
import { packMeshBinV4 } from '@forgeax/engine-import/mesh-bin';
|
|
3
4
|
import { AssetGuid } from '@forgeax/engine-pack/guid';
|
|
4
|
-
import { ImportError, toShared, err, ok, reconcileMeshMaterialSlotTopology, IMPORT_ERROR_HINTS, resolveMeshMaterialSlotDefaultGuid, createMaterialError } from '@forgeax/engine-types';
|
|
5
|
-
import { packInterleavedVertexAttributes } from '@forgeax/engine-geometry';
|
|
5
|
+
import { ImportError, toShared, err, ok, reconcileMeshMaterialSlotTopology, IMPORT_ERROR_HINTS, resolveMeshMaterialSlotDefaultGuid, createMaterialError, standardMaterialParameters, STANDARD_MATERIAL_PARAM_SCHEMA, STANDARD_PHYSICAL_PARAMETER_NAMES, STANDARD_TRANSMISSION_PARAMETER_NAMES, STANDARD_LAYER_PARAMETER_GROUPS } from '@forgeax/engine-types';
|
|
6
|
+
import { computeTangentVec4, packInterleavedVertexAttributes } from '@forgeax/engine-geometry';
|
|
6
7
|
import { box3, mat4, vec3, quat } from '@forgeax/engine-math';
|
|
7
8
|
import { deriveAnimationTargetId } from '@forgeax/engine-animation/target-id';
|
|
8
9
|
|
|
@@ -23,13 +24,21 @@ var gltfErrorPolicy = {
|
|
|
23
24
|
hint: "rebuild .gltf with valid bufferViews; check accessor index; ensure accessor.byteOffset + EFFECTIVE_STRIDE * (count - 1) + element_size <= bufferView.byteLength"
|
|
24
25
|
},
|
|
25
26
|
"gltf-extension-unsupported": {
|
|
26
|
-
expected: "extension listed in
|
|
27
|
-
hint: "
|
|
27
|
+
expected: "extension listed in the supported allowlist (see EXTENSION_ALLOWLIST in @forgeax/engine-gltf)",
|
|
28
|
+
hint: "remove the unsupported required extension or use a supported glTF extension; extensionsUsed-only entries remain diagnostic"
|
|
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"
|
|
28
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"
|
|
32
37
|
},
|
|
38
|
+
"gltf-material-physical-invalid": {
|
|
39
|
+
expected: "KHR clearcoat, anisotropy, sheen, iridescence, and specular values are finite and within their glTF ranges",
|
|
40
|
+
hint: "repair the named physical material extension value and re-import the source"
|
|
41
|
+
},
|
|
33
42
|
"gltf-accessor-type-mismatch": {
|
|
34
43
|
expected: "dense fixed-stride accessor with supported componentType",
|
|
35
44
|
hint: "sparse: see feat-future-gltf-sparse-accessor; morph: see feat-future-gltf-morph; interleaved: see feat-future-gltf-mesh-multi-section"
|
|
@@ -213,6 +222,16 @@ function meshIrToMeshAsset(prims, materials = {}) {
|
|
|
213
222
|
const materialSlot = slotFor(mesh.materialIndex);
|
|
214
223
|
const primVertexCount = mesh.positions.length / 3;
|
|
215
224
|
const primIndexCount = mesh.indices === void 0 ? 0 : mesh.indices.length;
|
|
225
|
+
let generatedTangents;
|
|
226
|
+
if (mesh.tangents === void 0 && mesh.normals !== void 0 && mesh.texcoord0 !== void 0) {
|
|
227
|
+
const generated = computeTangentVec4(
|
|
228
|
+
mesh.positions,
|
|
229
|
+
mesh.normals,
|
|
230
|
+
mesh.texcoord0,
|
|
231
|
+
mesh.indices
|
|
232
|
+
);
|
|
233
|
+
if (generated.ok) generatedTangents = generated.value;
|
|
234
|
+
}
|
|
216
235
|
if (mesh.colors0 !== void 0 && mesh.colors0.length !== primVertexCount * 4) {
|
|
217
236
|
return err(
|
|
218
237
|
gltfErr("gltf-mesh-bridge-invalid", {
|
|
@@ -244,12 +263,13 @@ function meshIrToMeshAsset(prims, materials = {}) {
|
|
|
244
263
|
uvsCat[(vertexCursor + i) * 2 + 0] = mesh.texcoord0[t + 0];
|
|
245
264
|
uvsCat[(vertexCursor + i) * 2 + 1] = mesh.texcoord0[t + 1];
|
|
246
265
|
}
|
|
247
|
-
|
|
266
|
+
const sourceTangents = mesh.tangents ?? generatedTangents;
|
|
267
|
+
if (sourceTangents !== void 0) {
|
|
248
268
|
const g = i * 4;
|
|
249
|
-
tangentsCat[(vertexCursor + i) * 4 + 0] =
|
|
250
|
-
tangentsCat[(vertexCursor + i) * 4 + 1] =
|
|
251
|
-
tangentsCat[(vertexCursor + i) * 4 + 2] =
|
|
252
|
-
tangentsCat[(vertexCursor + i) * 4 + 3] =
|
|
269
|
+
tangentsCat[(vertexCursor + i) * 4 + 0] = sourceTangents[g + 0];
|
|
270
|
+
tangentsCat[(vertexCursor + i) * 4 + 1] = sourceTangents[g + 1];
|
|
271
|
+
tangentsCat[(vertexCursor + i) * 4 + 2] = sourceTangents[g + 2];
|
|
272
|
+
tangentsCat[(vertexCursor + i) * 4 + 3] = sourceTangents[g + 3];
|
|
253
273
|
} else {
|
|
254
274
|
tangentsCat[(vertexCursor + i) * 4 + 0] = 1;
|
|
255
275
|
tangentsCat[(vertexCursor + i) * 4 + 3] = 1;
|
|
@@ -584,6 +604,12 @@ function textureValue(info, slot, ctx) {
|
|
|
584
604
|
return { ...value, normalScale: normal.scale };
|
|
585
605
|
}
|
|
586
606
|
}
|
|
607
|
+
if (slot === "clearcoatNormalTexture") {
|
|
608
|
+
const normal = info;
|
|
609
|
+
if (typeof normal === "object" && normal?.scale !== void 0) {
|
|
610
|
+
return { ...value, normalScale: normal.scale };
|
|
611
|
+
}
|
|
612
|
+
}
|
|
587
613
|
if (slot === "occlusionTexture") {
|
|
588
614
|
const occlusion = info;
|
|
589
615
|
if (typeof occlusion === "object" && occlusion?.strength !== void 0) {
|
|
@@ -592,6 +618,47 @@ function textureValue(info, slot, ctx) {
|
|
|
592
618
|
}
|
|
593
619
|
return value;
|
|
594
620
|
}
|
|
621
|
+
function standardRootParameterNames(mat) {
|
|
622
|
+
const names = new Set(
|
|
623
|
+
STANDARD_MATERIAL_PARAM_SCHEMA.filter(
|
|
624
|
+
(entry) => !STANDARD_PHYSICAL_PARAMETER_NAMES.has(entry.name) && !STANDARD_TRANSMISSION_PARAMETER_NAMES.has(entry.name)
|
|
625
|
+
).map((entry) => entry.name)
|
|
626
|
+
);
|
|
627
|
+
names.add("ior");
|
|
628
|
+
const addLayer = (layer) => {
|
|
629
|
+
for (const name of STANDARD_LAYER_PARAMETER_GROUPS[layer]) names.add(name);
|
|
630
|
+
};
|
|
631
|
+
const addTexture = (name, info) => {
|
|
632
|
+
if (info !== void 0) names.add(name);
|
|
633
|
+
};
|
|
634
|
+
const clearcoat = mat.clearcoatFactor !== void 0 || mat.clearcoatRoughnessFactor !== void 0 || mat.clearcoatNormalTexture !== void 0 || mat.clearcoatTexture !== void 0 || mat.clearcoatRoughnessTexture !== void 0;
|
|
635
|
+
const anisotropy = mat.anisotropyStrength !== void 0 || mat.anisotropyRotation !== void 0 || mat.anisotropyTexture !== void 0;
|
|
636
|
+
const sheen = mat.sheenColorFactor !== void 0 || mat.sheenRoughnessFactor !== void 0 || mat.sheenColorTexture !== void 0 || mat.sheenRoughnessTexture !== void 0;
|
|
637
|
+
const iridescence = mat.iridescenceFactor !== void 0 || mat.iridescenceIor !== void 0 || mat.iridescenceThicknessMinimum !== void 0 || mat.iridescenceThicknessMaximum !== void 0 || mat.iridescenceTexture !== void 0 || mat.iridescenceThicknessTexture !== void 0;
|
|
638
|
+
if (clearcoat) addLayer("clearcoat");
|
|
639
|
+
if (anisotropy) addLayer("anisotropy");
|
|
640
|
+
if (sheen) addLayer("sheen");
|
|
641
|
+
if (iridescence) addLayer("iridescence");
|
|
642
|
+
if (clearcoat && mat.clearcoatNormalTexture !== void 0) names.add("clearcoatNormalScale");
|
|
643
|
+
addTexture("clearcoatTexture", mat.clearcoatTexture);
|
|
644
|
+
addTexture("clearcoatRoughnessTexture", mat.clearcoatRoughnessTexture);
|
|
645
|
+
addTexture("clearcoatNormalTexture", mat.clearcoatNormalTexture);
|
|
646
|
+
addTexture("anisotropyTexture", mat.anisotropyTexture);
|
|
647
|
+
addTexture("sheenColorTexture", mat.sheenColorTexture);
|
|
648
|
+
addTexture("sheenRoughnessTexture", mat.sheenRoughnessTexture);
|
|
649
|
+
addTexture("iridescenceTexture", mat.iridescenceTexture);
|
|
650
|
+
addTexture("iridescenceThicknessTexture", mat.iridescenceThicknessTexture);
|
|
651
|
+
addTexture("specularTexture", mat.specularTexture);
|
|
652
|
+
addTexture("specularColorTexture", mat.specularColorTexture);
|
|
653
|
+
const transmission = mat.transmissionFactor !== void 0 || mat.transmissionTexture !== void 0 || mat.ior !== void 0 || mat.thicknessFactor !== void 0 || mat.thicknessTexture !== void 0 || mat.attenuationColor !== void 0 || mat.attenuationDistance !== void 0;
|
|
654
|
+
if (transmission) {
|
|
655
|
+
for (const name of STANDARD_TRANSMISSION_PARAMETER_NAMES) names.add(name);
|
|
656
|
+
}
|
|
657
|
+
return {
|
|
658
|
+
names,
|
|
659
|
+
extended: clearcoat || anisotropy || sheen || iridescence || transmission || mat.specularTexture !== void 0 || mat.specularColorTexture !== void 0
|
|
660
|
+
};
|
|
661
|
+
}
|
|
595
662
|
function validateMaterialUvSets(mat, primitive, availableSets) {
|
|
596
663
|
const available = new Set(availableSets);
|
|
597
664
|
const slots = [
|
|
@@ -601,7 +668,17 @@ function validateMaterialUvSets(mat, primitive, availableSets) {
|
|
|
601
668
|
["occlusionTexture", mat.occlusionTexture],
|
|
602
669
|
["emissiveTexture", mat.emissiveTexture],
|
|
603
670
|
["transmissionTexture", mat.transmissionTexture],
|
|
604
|
-
["thicknessTexture", mat.thicknessTexture]
|
|
671
|
+
["thicknessTexture", mat.thicknessTexture],
|
|
672
|
+
["clearcoatTexture", mat.clearcoatTexture],
|
|
673
|
+
["clearcoatRoughnessTexture", mat.clearcoatRoughnessTexture],
|
|
674
|
+
["clearcoatNormalTexture", mat.clearcoatNormalTexture],
|
|
675
|
+
["anisotropyTexture", mat.anisotropyTexture],
|
|
676
|
+
["sheenColorTexture", mat.sheenColorTexture],
|
|
677
|
+
["sheenRoughnessTexture", mat.sheenRoughnessTexture],
|
|
678
|
+
["iridescenceTexture", mat.iridescenceTexture],
|
|
679
|
+
["iridescenceThicknessTexture", mat.iridescenceThicknessTexture],
|
|
680
|
+
["specularTexture", mat.specularTexture],
|
|
681
|
+
["specularColorTexture", mat.specularColorTexture]
|
|
605
682
|
];
|
|
606
683
|
for (const [slot, rawBinding] of slots) {
|
|
607
684
|
const binding = textureInfo(rawBinding);
|
|
@@ -621,7 +698,44 @@ function validateMaterialUvSets(mat, primitive, availableSets) {
|
|
|
621
698
|
}
|
|
622
699
|
return ok(void 0);
|
|
623
700
|
}
|
|
701
|
+
function validateMaterialTangentInputs(mat, mesh, layer = "clearcoat") {
|
|
702
|
+
const anisotropyDeclared = mat.anisotropyStrength !== void 0 || mat.anisotropyRotation !== void 0 || mat.anisotropyTexture !== void 0;
|
|
703
|
+
const tangentSlot = anisotropyDeclared ? { layer: "anisotropy", info: mat.anisotropyTexture } : mat.clearcoatNormalTexture !== void 0 ? { layer: "clearcoat", info: mat.clearcoatNormalTexture } : void 0;
|
|
704
|
+
if (tangentSlot === void 0) return ok(void 0);
|
|
705
|
+
const selected = textureInfo(tangentSlot.info);
|
|
706
|
+
const uvSet = selected?.texCoord ?? 0;
|
|
707
|
+
const uv = mesh[`texcoord${uvSet === 0 ? "0" : uvSet}`];
|
|
708
|
+
const attributes = ["NORMAL", `TEXCOORD_${uvSet}`, "TANGENT"];
|
|
709
|
+
const fail = (reason) => err(
|
|
710
|
+
createMaterialError("material-tangent-required", {
|
|
711
|
+
code: "material-tangent-required",
|
|
712
|
+
material: mat.name ?? "<unnamed>",
|
|
713
|
+
mesh: mesh.name ?? "<unnamed>",
|
|
714
|
+
layer: tangentSlot.layer ?? layer,
|
|
715
|
+
uv: `TEXCOORD_${uvSet}`,
|
|
716
|
+
attributes,
|
|
717
|
+
reason
|
|
718
|
+
})
|
|
719
|
+
);
|
|
720
|
+
if (mesh.tangents !== void 0) {
|
|
721
|
+
if (mesh.tangents.length !== mesh.positions.length / 3 * 4 || mesh.tangents.some((value) => !Number.isFinite(value))) {
|
|
722
|
+
return fail("imported TANGENT must be finite vec4 per vertex");
|
|
723
|
+
}
|
|
724
|
+
return ok(void 0);
|
|
725
|
+
}
|
|
726
|
+
if (mesh.normals === void 0) return fail("NORMAL is required to generate tangent");
|
|
727
|
+
if (!(uv instanceof Float32Array))
|
|
728
|
+
return fail("the selected UV set is required to generate tangent");
|
|
729
|
+
const generated = computeTangentVec4(mesh.positions, mesh.normals, uv, mesh.indices);
|
|
730
|
+
if (!generated.ok) {
|
|
731
|
+
const detail = generated.error.detail;
|
|
732
|
+
const reason = detail !== void 0 && "reason" in detail ? String(detail.reason) : "tangent producer rejected topology";
|
|
733
|
+
return fail(reason);
|
|
734
|
+
}
|
|
735
|
+
return ok(void 0);
|
|
736
|
+
}
|
|
624
737
|
function toMaterialAsset(mat, ctx) {
|
|
738
|
+
const rootContract = standardRootParameterNames(mat);
|
|
625
739
|
const values = {
|
|
626
740
|
baseColor: mat.baseColorFactor,
|
|
627
741
|
metallic: mat.metallicFactor,
|
|
@@ -638,7 +752,17 @@ function toMaterialAsset(mat, ctx) {
|
|
|
638
752
|
["occlusionTexture", mat.occlusionTexture],
|
|
639
753
|
["emissiveTexture", mat.emissiveTexture],
|
|
640
754
|
["transmissionTexture", mat.transmissionTexture],
|
|
641
|
-
["thicknessTexture", mat.thicknessTexture]
|
|
755
|
+
["thicknessTexture", mat.thicknessTexture],
|
|
756
|
+
["clearcoatTexture", mat.clearcoatTexture],
|
|
757
|
+
["clearcoatRoughnessTexture", mat.clearcoatRoughnessTexture],
|
|
758
|
+
["clearcoatNormalTexture", mat.clearcoatNormalTexture],
|
|
759
|
+
["anisotropyTexture", mat.anisotropyTexture],
|
|
760
|
+
["sheenColorTexture", mat.sheenColorTexture],
|
|
761
|
+
["sheenRoughnessTexture", mat.sheenRoughnessTexture],
|
|
762
|
+
["iridescenceTexture", mat.iridescenceTexture],
|
|
763
|
+
["iridescenceThicknessTexture", mat.iridescenceThicknessTexture],
|
|
764
|
+
["specularTexture", mat.specularTexture],
|
|
765
|
+
["specularColorTexture", mat.specularColorTexture]
|
|
642
766
|
];
|
|
643
767
|
for (const [slot, info] of textureSlots) {
|
|
644
768
|
const value = textureValue(info, slot, ctx);
|
|
@@ -647,11 +771,35 @@ function toMaterialAsset(mat, ctx) {
|
|
|
647
771
|
if (mat.occlusionTexture !== void 0 && values.occlusionStrength === void 0) {
|
|
648
772
|
values.occlusionStrength = 1;
|
|
649
773
|
}
|
|
650
|
-
if (
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
774
|
+
if (rootContract.extended && rootContract.names.has("transmission")) {
|
|
775
|
+
values.transmission = mat.transmissionFactor ?? 0;
|
|
776
|
+
values.ior = mat.ior ?? 1.5;
|
|
777
|
+
values.thickness = mat.thicknessFactor ?? 0;
|
|
778
|
+
values.attenuationColor = mat.attenuationColor ?? [1, 1, 1];
|
|
779
|
+
if (mat.attenuationDistance !== void 0) values.attenuationDistance = mat.attenuationDistance;
|
|
780
|
+
}
|
|
781
|
+
if (rootContract.names.has("clearcoat")) {
|
|
782
|
+
values.clearcoat = mat.clearcoatFactor ?? 0;
|
|
783
|
+
values.clearcoatRoughness = mat.clearcoatRoughnessFactor ?? 0;
|
|
784
|
+
}
|
|
785
|
+
const clearcoatNormal = textureInfo(mat.clearcoatNormalTexture);
|
|
786
|
+
if (clearcoatNormal?.scale !== void 0) values.clearcoatNormalScale = clearcoatNormal.scale;
|
|
787
|
+
if (rootContract.names.has("anisotropyStrength")) {
|
|
788
|
+
values.anisotropyStrength = mat.anisotropyStrength ?? 0;
|
|
789
|
+
values.anisotropyRotation = mat.anisotropyRotation ?? 0;
|
|
790
|
+
}
|
|
791
|
+
if (rootContract.names.has("sheenColor")) {
|
|
792
|
+
values.sheenColor = mat.sheenColorFactor ?? [0, 0, 0];
|
|
793
|
+
values.sheenRoughness = mat.sheenRoughnessFactor ?? 0;
|
|
794
|
+
}
|
|
795
|
+
if (rootContract.names.has("iridescence")) {
|
|
796
|
+
values.iridescence = mat.iridescenceFactor ?? 0;
|
|
797
|
+
values.iridescenceIor = mat.iridescenceIor ?? 1.3;
|
|
798
|
+
values.iridescenceThicknessMinimum = mat.iridescenceThicknessMinimum ?? 100;
|
|
799
|
+
values.iridescenceThicknessMaximum = mat.iridescenceThicknessMaximum ?? 400;
|
|
800
|
+
}
|
|
801
|
+
if (mat.specularFactor !== void 0) values.specular = mat.specularFactor;
|
|
802
|
+
if (mat.specularColorFactor !== void 0) values.specularColor = mat.specularColorFactor;
|
|
655
803
|
const module = ctx?.skinned === true ? "forgeax::pbr-skin" : "forgeax::default-standard-pbr";
|
|
656
804
|
const isMask = mat.alphaMode === "MASK";
|
|
657
805
|
const alphaCutoff = isMask ? mat.alphaCutoff ?? 0.5 : void 0;
|
|
@@ -684,8 +832,9 @@ function toMaterialAsset(mat, ctx) {
|
|
|
684
832
|
return {
|
|
685
833
|
kind: "material",
|
|
686
834
|
colorSpace: "linear",
|
|
687
|
-
|
|
835
|
+
...!rootContract.extended && ctx?.standardRootGuid !== void 0 ? { parent: ctx.standardRootGuid } : {},
|
|
688
836
|
passes: [pass],
|
|
837
|
+
...ctx?.standardRootGuid !== void 0 && !rootContract.extended ? {} : { parameters: standardMaterialParameters(rootContract.names) },
|
|
689
838
|
values
|
|
690
839
|
};
|
|
691
840
|
}
|
|
@@ -1104,9 +1253,21 @@ function deriveTextureColorSpace(input) {
|
|
|
1104
1253
|
for (const mat of input.materials) {
|
|
1105
1254
|
record(imageOfTexture(mat.baseColorTexture), "srgb");
|
|
1106
1255
|
record(imageOfTexture(mat.emissiveTexture), "srgb");
|
|
1256
|
+
record(imageOfTexture(mat.sheenColorTexture), "srgb");
|
|
1257
|
+
record(imageOfTexture(mat.specularColorTexture), "srgb");
|
|
1107
1258
|
record(imageOfTexture(mat.metallicRoughnessTexture), "linear");
|
|
1108
1259
|
record(imageOfTexture(mat.normalTexture), "linear");
|
|
1109
1260
|
record(imageOfTexture(mat.occlusionTexture), "linear");
|
|
1261
|
+
record(imageOfTexture(mat.transmissionTexture), "linear");
|
|
1262
|
+
record(imageOfTexture(mat.thicknessTexture), "linear");
|
|
1263
|
+
record(imageOfTexture(mat.clearcoatTexture), "linear");
|
|
1264
|
+
record(imageOfTexture(mat.clearcoatRoughnessTexture), "linear");
|
|
1265
|
+
record(imageOfTexture(mat.clearcoatNormalTexture), "linear");
|
|
1266
|
+
record(imageOfTexture(mat.anisotropyTexture), "linear");
|
|
1267
|
+
record(imageOfTexture(mat.sheenRoughnessTexture), "linear");
|
|
1268
|
+
record(imageOfTexture(mat.iridescenceTexture), "linear");
|
|
1269
|
+
record(imageOfTexture(mat.iridescenceThicknessTexture), "linear");
|
|
1270
|
+
record(imageOfTexture(mat.specularTexture), "linear");
|
|
1110
1271
|
}
|
|
1111
1272
|
for (let i = 0; i < input.imageCount; i++) {
|
|
1112
1273
|
if (!result.has(i)) {
|
|
@@ -1394,15 +1555,22 @@ function decodeColorAccessor(input, flags = {}) {
|
|
|
1394
1555
|
// src/check-extensions.ts
|
|
1395
1556
|
var EXTENSION_ALLOWLIST = [
|
|
1396
1557
|
"EXT_mesh_gpu_instancing",
|
|
1397
|
-
"EXT_meshopt_compression"
|
|
1398
|
-
];
|
|
1399
|
-
var SUPPORTED_EXTENSIONS = [
|
|
1400
|
-
...EXTENSION_ALLOWLIST,
|
|
1558
|
+
"EXT_meshopt_compression",
|
|
1401
1559
|
"KHR_lights_punctual",
|
|
1402
1560
|
"KHR_texture_transform",
|
|
1403
1561
|
"KHR_materials_transmission",
|
|
1404
1562
|
"KHR_materials_ior",
|
|
1405
|
-
"KHR_materials_volume"
|
|
1563
|
+
"KHR_materials_volume",
|
|
1564
|
+
"KHR_materials_clearcoat",
|
|
1565
|
+
"KHR_materials_anisotropy",
|
|
1566
|
+
"KHR_materials_sheen",
|
|
1567
|
+
"KHR_materials_iridescence",
|
|
1568
|
+
"KHR_materials_specular"
|
|
1569
|
+
];
|
|
1570
|
+
var SUPPORTED_EXTENSIONS = [
|
|
1571
|
+
...EXTENSION_ALLOWLIST,
|
|
1572
|
+
"MSFT_lod",
|
|
1573
|
+
"MSFT_screencoverage"
|
|
1406
1574
|
];
|
|
1407
1575
|
function checkExtensions(json) {
|
|
1408
1576
|
const required = json.extensionsRequired ?? [];
|
|
@@ -1426,6 +1594,81 @@ function checkExtensions(json) {
|
|
|
1426
1594
|
return ok({ unsupportedUsed });
|
|
1427
1595
|
}
|
|
1428
1596
|
|
|
1597
|
+
// src/lod/parse-lod.ts
|
|
1598
|
+
function parseGltfLodExtension(json) {
|
|
1599
|
+
const doc = json;
|
|
1600
|
+
const nodes = doc.nodes ?? [];
|
|
1601
|
+
const rawCoverage = doc.extensions?.MSFT_screencoverage?.scales;
|
|
1602
|
+
const groups = [];
|
|
1603
|
+
for (let rootNode = 0; rootNode < nodes.length; rootNode += 1) {
|
|
1604
|
+
if (nodes[rootNode]?.extensions?.MSFT_lod === void 0) continue;
|
|
1605
|
+
const idsRaw = nodes[rootNode]?.extensions?.MSFT_lod?.ids;
|
|
1606
|
+
if (!Array.isArray(idsRaw)) {
|
|
1607
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids: [], reason: "not-integer" }));
|
|
1608
|
+
}
|
|
1609
|
+
const ids = idsRaw.filter((value) => typeof value === "number");
|
|
1610
|
+
if (ids.length !== idsRaw.length || ids.some((id) => !Number.isInteger(id) || id < 0 || id >= nodes.length)) {
|
|
1611
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids, reason: "missing-node" }));
|
|
1612
|
+
}
|
|
1613
|
+
if (new Set(ids).size !== ids.length || ids.includes(rootNode)) {
|
|
1614
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids, reason: "duplicate-node" }));
|
|
1615
|
+
}
|
|
1616
|
+
const hasMesh = (node) => {
|
|
1617
|
+
if (typeof node?.mesh !== "number" || !Number.isInteger(node.mesh) || node.mesh < 0) {
|
|
1618
|
+
return false;
|
|
1619
|
+
}
|
|
1620
|
+
const mesh = doc.meshes?.[node.mesh];
|
|
1621
|
+
return mesh !== void 0 && Array.isArray(mesh.primitives) && mesh.primitives.length > 0;
|
|
1622
|
+
};
|
|
1623
|
+
if (!hasMesh(nodes[rootNode]) || ids.some((id) => !hasMesh(nodes[id]))) {
|
|
1624
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids, reason: "missing-node" }));
|
|
1625
|
+
}
|
|
1626
|
+
const nodeCoverage = nodes[rootNode]?.extras?.MSFT_screencoverage;
|
|
1627
|
+
const coverage = normalizeScreenCoverages(
|
|
1628
|
+
nodeCoverage === void 0 ? rawCoverage : nodeCoverage,
|
|
1629
|
+
ids.length,
|
|
1630
|
+
nodeCoverage !== void 0
|
|
1631
|
+
);
|
|
1632
|
+
if (coverage === "invalid") {
|
|
1633
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids, reason: "coverage" }));
|
|
1634
|
+
}
|
|
1635
|
+
groups.push({
|
|
1636
|
+
rootNode,
|
|
1637
|
+
lodNodeIds: ids,
|
|
1638
|
+
screenCoverages: coverage ?? []
|
|
1639
|
+
});
|
|
1640
|
+
}
|
|
1641
|
+
const first = groups[0];
|
|
1642
|
+
if (first === void 0) {
|
|
1643
|
+
if (doc.extensionsRequired?.some((extension) => extension === "MSFT_lod")) {
|
|
1644
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode: 0, ids: [], reason: "missing-node" }));
|
|
1645
|
+
}
|
|
1646
|
+
return ok({ rootNode: 0, lodNodeIds: [], screenCoverages: [], groups: [] });
|
|
1647
|
+
}
|
|
1648
|
+
return ok({
|
|
1649
|
+
rootNode: first.rootNode,
|
|
1650
|
+
lodNodeIds: first.lodNodeIds,
|
|
1651
|
+
screenCoverages: first.screenCoverages,
|
|
1652
|
+
groups
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
function normalizeScreenCoverages(raw, lodCount, officialNodeForm) {
|
|
1656
|
+
if (raw === void 0) return void 0;
|
|
1657
|
+
if (!Array.isArray(raw)) return "invalid";
|
|
1658
|
+
if (raw.some(
|
|
1659
|
+
(value) => typeof value !== "number" || !Number.isFinite(value) || value <= 0 || value > 1
|
|
1660
|
+
)) {
|
|
1661
|
+
return "invalid";
|
|
1662
|
+
}
|
|
1663
|
+
const expectedLengths = officialNodeForm ? [lodCount + 1] : [lodCount, lodCount + 1];
|
|
1664
|
+
if (!expectedLengths.includes(raw.length)) return "invalid";
|
|
1665
|
+
const values = raw.slice(0, lodCount);
|
|
1666
|
+
for (let index = 1; index < values.length; index += 1) {
|
|
1667
|
+
if (values[index] >= values[index - 1]) return "invalid";
|
|
1668
|
+
}
|
|
1669
|
+
return values;
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1429
1672
|
// src/material/parse-material.ts
|
|
1430
1673
|
function tuple2(values) {
|
|
1431
1674
|
if (values === void 0 || values.length < 2) return void 0;
|
|
@@ -1470,6 +1713,11 @@ function parseMaterial(matJson, textures) {
|
|
|
1470
1713
|
const transmission = matJson.extensions?.KHR_materials_transmission;
|
|
1471
1714
|
const ior = matJson.extensions?.KHR_materials_ior;
|
|
1472
1715
|
const volume = matJson.extensions?.KHR_materials_volume;
|
|
1716
|
+
const clearcoat = matJson.extensions?.KHR_materials_clearcoat;
|
|
1717
|
+
const anisotropy = matJson.extensions?.KHR_materials_anisotropy;
|
|
1718
|
+
const sheen = matJson.extensions?.KHR_materials_sheen;
|
|
1719
|
+
const iridescence = matJson.extensions?.KHR_materials_iridescence;
|
|
1720
|
+
const specular = matJson.extensions?.KHR_materials_specular;
|
|
1473
1721
|
const attenuationColor = tuple3(volume?.attenuationColor);
|
|
1474
1722
|
const invalid = (extension, field, reason, actual) => err(
|
|
1475
1723
|
gltfErr("gltf-material-transmission-invalid", {
|
|
@@ -1479,6 +1727,14 @@ function parseMaterial(matJson, textures) {
|
|
|
1479
1727
|
...actual === void 0 ? {} : { actual }
|
|
1480
1728
|
})
|
|
1481
1729
|
);
|
|
1730
|
+
const physicalInvalid = (extension, field, reason, actual) => err(
|
|
1731
|
+
gltfErr("gltf-material-physical-invalid", {
|
|
1732
|
+
extension,
|
|
1733
|
+
field,
|
|
1734
|
+
reason,
|
|
1735
|
+
...actual === void 0 ? {} : { actual }
|
|
1736
|
+
})
|
|
1737
|
+
);
|
|
1482
1738
|
if (transmission?.transmissionFactor !== void 0 && (typeof transmission.transmissionFactor !== "number" || !Number.isFinite(transmission.transmissionFactor))) {
|
|
1483
1739
|
return invalid(
|
|
1484
1740
|
"KHR_materials_transmission",
|
|
@@ -1529,6 +1785,98 @@ function parseMaterial(matJson, textures) {
|
|
|
1529
1785
|
if (alphaMode === "BLEND" && (transmission?.transmissionFactor ?? 0) > 0) {
|
|
1530
1786
|
return invalid("KHR_materials_transmission", "alphaMode", "blend", alphaMode);
|
|
1531
1787
|
}
|
|
1788
|
+
if (clearcoat?.clearcoatFactor !== void 0 && (typeof clearcoat.clearcoatFactor !== "number" || !Number.isFinite(clearcoat.clearcoatFactor))) {
|
|
1789
|
+
return physicalInvalid(
|
|
1790
|
+
"KHR_materials_clearcoat",
|
|
1791
|
+
"clearcoatFactor",
|
|
1792
|
+
"non-finite",
|
|
1793
|
+
clearcoat.clearcoatFactor
|
|
1794
|
+
);
|
|
1795
|
+
}
|
|
1796
|
+
if (clearcoat?.clearcoatFactor !== void 0 && (clearcoat.clearcoatFactor < 0 || clearcoat.clearcoatFactor > 1)) {
|
|
1797
|
+
return physicalInvalid(
|
|
1798
|
+
"KHR_materials_clearcoat",
|
|
1799
|
+
"clearcoatFactor",
|
|
1800
|
+
"range",
|
|
1801
|
+
clearcoat.clearcoatFactor
|
|
1802
|
+
);
|
|
1803
|
+
}
|
|
1804
|
+
if (clearcoat?.clearcoatRoughnessFactor !== void 0 && (typeof clearcoat.clearcoatRoughnessFactor !== "number" || !Number.isFinite(clearcoat.clearcoatRoughnessFactor))) {
|
|
1805
|
+
return physicalInvalid(
|
|
1806
|
+
"KHR_materials_clearcoat",
|
|
1807
|
+
"clearcoatRoughnessFactor",
|
|
1808
|
+
"non-finite",
|
|
1809
|
+
clearcoat.clearcoatRoughnessFactor
|
|
1810
|
+
);
|
|
1811
|
+
}
|
|
1812
|
+
if (clearcoat?.clearcoatRoughnessFactor !== void 0 && (clearcoat.clearcoatRoughnessFactor < 0 || clearcoat.clearcoatRoughnessFactor > 1)) {
|
|
1813
|
+
return physicalInvalid(
|
|
1814
|
+
"KHR_materials_clearcoat",
|
|
1815
|
+
"clearcoatRoughnessFactor",
|
|
1816
|
+
"range",
|
|
1817
|
+
clearcoat.clearcoatRoughnessFactor
|
|
1818
|
+
);
|
|
1819
|
+
}
|
|
1820
|
+
if (clearcoat?.clearcoatNormalTexture?.scale !== void 0 && (typeof clearcoat.clearcoatNormalTexture.scale !== "number" || !Number.isFinite(clearcoat.clearcoatNormalTexture.scale) || clearcoat.clearcoatNormalTexture.scale < 0)) {
|
|
1821
|
+
return physicalInvalid(
|
|
1822
|
+
"KHR_materials_clearcoat",
|
|
1823
|
+
"clearcoatNormalTexture.scale",
|
|
1824
|
+
"range",
|
|
1825
|
+
clearcoat.clearcoatNormalTexture.scale
|
|
1826
|
+
);
|
|
1827
|
+
}
|
|
1828
|
+
const validateUnit = (extension, field, value) => {
|
|
1829
|
+
if (value === void 0) return void 0;
|
|
1830
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
1831
|
+
return physicalInvalid(extension, field, "non-finite", value);
|
|
1832
|
+
if (value < 0 || value > 1) return physicalInvalid(extension, field, "range", value);
|
|
1833
|
+
return void 0;
|
|
1834
|
+
};
|
|
1835
|
+
const validateFinite = (extension, field, value, minimum) => {
|
|
1836
|
+
if (value === void 0) return void 0;
|
|
1837
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
1838
|
+
return physicalInvalid(extension, field, "non-finite", value);
|
|
1839
|
+
if (minimum !== void 0 && value < minimum)
|
|
1840
|
+
return physicalInvalid(extension, field, "range", value);
|
|
1841
|
+
return void 0;
|
|
1842
|
+
};
|
|
1843
|
+
const validateColor = (extension, field, value) => {
|
|
1844
|
+
if (value === void 0) return void 0;
|
|
1845
|
+
if (value.length < 3 || value.slice(0, 3).some((channel) => typeof channel !== "number" || !Number.isFinite(channel))) {
|
|
1846
|
+
return physicalInvalid(extension, field, "type", value);
|
|
1847
|
+
}
|
|
1848
|
+
if (value.slice(0, 3).some((channel) => channel < 0 || channel > 1)) {
|
|
1849
|
+
return physicalInvalid(extension, field, "range", value);
|
|
1850
|
+
}
|
|
1851
|
+
return void 0;
|
|
1852
|
+
};
|
|
1853
|
+
const physicalChecks = [
|
|
1854
|
+
validateUnit("KHR_materials_anisotropy", "anisotropyStrength", anisotropy?.anisotropyStrength),
|
|
1855
|
+
validateFinite(
|
|
1856
|
+
"KHR_materials_anisotropy",
|
|
1857
|
+
"anisotropyRotation",
|
|
1858
|
+
anisotropy?.anisotropyRotation
|
|
1859
|
+
),
|
|
1860
|
+
validateUnit("KHR_materials_sheen", "sheenRoughnessFactor", sheen?.sheenRoughnessFactor),
|
|
1861
|
+
validateColor("KHR_materials_sheen", "sheenColorFactor", sheen?.sheenColorFactor),
|
|
1862
|
+
validateUnit("KHR_materials_iridescence", "iridescenceFactor", iridescence?.iridescenceFactor),
|
|
1863
|
+
validateFinite("KHR_materials_iridescence", "iridescenceIor", iridescence?.iridescenceIor, 1),
|
|
1864
|
+
validateFinite(
|
|
1865
|
+
"KHR_materials_iridescence",
|
|
1866
|
+
"iridescenceThicknessMinimum",
|
|
1867
|
+
iridescence?.iridescenceThicknessMinimum,
|
|
1868
|
+
0
|
|
1869
|
+
),
|
|
1870
|
+
validateFinite(
|
|
1871
|
+
"KHR_materials_iridescence",
|
|
1872
|
+
"iridescenceThicknessMaximum",
|
|
1873
|
+
iridescence?.iridescenceThicknessMaximum,
|
|
1874
|
+
0
|
|
1875
|
+
),
|
|
1876
|
+
validateUnit("KHR_materials_specular", "specularFactor", specular?.specularFactor),
|
|
1877
|
+
validateColor("KHR_materials_specular", "specularColorFactor", specular?.specularColorFactor)
|
|
1878
|
+
];
|
|
1879
|
+
for (const check of physicalChecks) if (check !== void 0) return check;
|
|
1532
1880
|
return ok({
|
|
1533
1881
|
...matJson.name === void 0 ? {} : { name: matJson.name },
|
|
1534
1882
|
baseColorFactor: baseColor4,
|
|
@@ -1565,6 +1913,65 @@ function parseMaterial(matJson, textures) {
|
|
|
1565
1913
|
...attenuationColor === void 0 ? {} : { attenuationColor },
|
|
1566
1914
|
...finiteOrUndefined(volume.attenuationDistance) === void 0 ? {} : { attenuationDistance: volume.attenuationDistance }
|
|
1567
1915
|
},
|
|
1916
|
+
...clearcoat === void 0 ? {} : {
|
|
1917
|
+
clearcoatFactor: clearcoat.clearcoatFactor ?? 0,
|
|
1918
|
+
clearcoatChannel: 0,
|
|
1919
|
+
clearcoatRoughnessFactor: clearcoat.clearcoatRoughnessFactor ?? 0,
|
|
1920
|
+
clearcoatRoughnessChannel: 1,
|
|
1921
|
+
clearcoatNormalChannel: [0, 1],
|
|
1922
|
+
...clearcoat.clearcoatTexture === void 0 ? {} : { clearcoatTexture: parseTextureInfo(clearcoat.clearcoatTexture, textures) },
|
|
1923
|
+
...clearcoat.clearcoatRoughnessTexture === void 0 ? {} : {
|
|
1924
|
+
clearcoatRoughnessTexture: parseTextureInfo(
|
|
1925
|
+
clearcoat.clearcoatRoughnessTexture,
|
|
1926
|
+
textures
|
|
1927
|
+
)
|
|
1928
|
+
},
|
|
1929
|
+
...clearcoat.clearcoatNormalTexture === void 0 ? {} : {
|
|
1930
|
+
clearcoatNormalTexture: {
|
|
1931
|
+
...parseTextureInfo(clearcoat.clearcoatNormalTexture, textures),
|
|
1932
|
+
...clearcoat.clearcoatNormalTexture.scale === void 0 ? {} : { scale: clearcoat.clearcoatNormalTexture.scale }
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
},
|
|
1936
|
+
...anisotropy === void 0 ? {} : {
|
|
1937
|
+
anisotropyStrength: anisotropy.anisotropyStrength ?? 0,
|
|
1938
|
+
anisotropyRotation: anisotropy.anisotropyRotation ?? 0,
|
|
1939
|
+
anisotropyChannel: [0, 1, 2],
|
|
1940
|
+
...anisotropy.anisotropyTexture === void 0 ? {} : { anisotropyTexture: parseTextureInfo(anisotropy.anisotropyTexture, textures) }
|
|
1941
|
+
},
|
|
1942
|
+
...sheen === void 0 ? {} : {
|
|
1943
|
+
sheenColorFactor: tuple3(sheen.sheenColorFactor) ?? [0, 0, 0],
|
|
1944
|
+
sheenColorChannel: [0, 1, 2],
|
|
1945
|
+
sheenRoughnessFactor: sheen.sheenRoughnessFactor ?? 0,
|
|
1946
|
+
sheenRoughnessChannel: 3,
|
|
1947
|
+
...sheen.sheenColorTexture === void 0 ? {} : { sheenColorTexture: parseTextureInfo(sheen.sheenColorTexture, textures) },
|
|
1948
|
+
...sheen.sheenRoughnessTexture === void 0 ? {} : { sheenRoughnessTexture: parseTextureInfo(sheen.sheenRoughnessTexture, textures) }
|
|
1949
|
+
},
|
|
1950
|
+
...iridescence === void 0 ? {} : {
|
|
1951
|
+
iridescenceFactor: iridescence.iridescenceFactor ?? 0,
|
|
1952
|
+
iridescenceIor: iridescence.iridescenceIor ?? 1.3,
|
|
1953
|
+
iridescenceThicknessMinimum: iridescence.iridescenceThicknessMinimum ?? 100,
|
|
1954
|
+
iridescenceThicknessMaximum: iridescence.iridescenceThicknessMaximum ?? 400,
|
|
1955
|
+
iridescenceChannel: 0,
|
|
1956
|
+
iridescenceThicknessChannel: 1,
|
|
1957
|
+
...iridescence.iridescenceTexture === void 0 ? {} : { iridescenceTexture: parseTextureInfo(iridescence.iridescenceTexture, textures) },
|
|
1958
|
+
...iridescence.iridescenceThicknessTexture === void 0 ? {} : {
|
|
1959
|
+
iridescenceThicknessTexture: parseTextureInfo(
|
|
1960
|
+
iridescence.iridescenceThicknessTexture,
|
|
1961
|
+
textures
|
|
1962
|
+
)
|
|
1963
|
+
}
|
|
1964
|
+
},
|
|
1965
|
+
...specular === void 0 ? {} : {
|
|
1966
|
+
specularFactor: specular.specularFactor ?? 1,
|
|
1967
|
+
specularChannel: 3,
|
|
1968
|
+
specularColorFactor: tuple3(specular.specularColorFactor) ?? [1, 1, 1],
|
|
1969
|
+
specularColorChannel: [0, 1, 2],
|
|
1970
|
+
...specular.specularTexture === void 0 ? {} : { specularTexture: parseTextureInfo(specular.specularTexture, textures) },
|
|
1971
|
+
...specular.specularColorTexture === void 0 ? {} : {
|
|
1972
|
+
specularColorTexture: parseTextureInfo(specular.specularColorTexture, textures)
|
|
1973
|
+
}
|
|
1974
|
+
},
|
|
1568
1975
|
...alphaMode === void 0 ? {} : { alphaMode },
|
|
1569
1976
|
...alphaCutoff === void 0 ? {} : { alphaCutoff },
|
|
1570
1977
|
...matJson.doubleSided === true ? { doubleSided: true } : {},
|
|
@@ -2210,6 +2617,8 @@ async function parseGltfWithBin(json, ctx) {
|
|
|
2210
2617
|
const extResult = checkExtensions(json);
|
|
2211
2618
|
if (!extResult.ok) return err(extResult.error);
|
|
2212
2619
|
const unsupportedExtensions = extResult.value.unsupportedUsed;
|
|
2620
|
+
const lodResult = parseGltfLodExtension(json);
|
|
2621
|
+
if (!lodResult.ok) return err(lodResult.error);
|
|
2213
2622
|
const lightsResult = parsePunctualLights(json, ctx.filePath);
|
|
2214
2623
|
if (!lightsResult.ok) return err(lightsResult.error);
|
|
2215
2624
|
const meshesJson = json.meshes ?? [];
|
|
@@ -2750,7 +3159,8 @@ async function parseGltfWithBin(json, ctx) {
|
|
|
2750
3159
|
matrixTrsCoexistNodes: diagnostics.matrixTrsCoexistNodes
|
|
2751
3160
|
},
|
|
2752
3161
|
meshPrimitiveCount,
|
|
2753
|
-
lights: lightsResult.value
|
|
3162
|
+
lights: lightsResult.value,
|
|
3163
|
+
...lodResult.value.lodNodeIds.length === 0 ? {} : { lod: lodResult.value }
|
|
2754
3164
|
});
|
|
2755
3165
|
}
|
|
2756
3166
|
async function parseGltfForImporter(json, externalLoader, filePath, options = {}) {
|
|
@@ -3052,6 +3462,16 @@ function materialRefsForPack(mat, doc, textureGuidByIndex, samplerGuidByIndex =
|
|
|
3052
3462
|
pushRefsForSlot(mat.emissiveTexture, "emissiveTexture");
|
|
3053
3463
|
pushRefsForSlot(mat.transmissionTexture, "transmissionTexture");
|
|
3054
3464
|
pushRefsForSlot(mat.thicknessTexture, "thicknessTexture");
|
|
3465
|
+
pushRefsForSlot(mat.clearcoatTexture, "clearcoatTexture");
|
|
3466
|
+
pushRefsForSlot(mat.clearcoatRoughnessTexture, "clearcoatRoughnessTexture");
|
|
3467
|
+
pushRefsForSlot(mat.clearcoatNormalTexture, "clearcoatNormalTexture");
|
|
3468
|
+
pushRefsForSlot(mat.anisotropyTexture, "anisotropyTexture");
|
|
3469
|
+
pushRefsForSlot(mat.sheenColorTexture, "sheenColorTexture");
|
|
3470
|
+
pushRefsForSlot(mat.sheenRoughnessTexture, "sheenRoughnessTexture");
|
|
3471
|
+
pushRefsForSlot(mat.iridescenceTexture, "iridescenceTexture");
|
|
3472
|
+
pushRefsForSlot(mat.iridescenceThicknessTexture, "iridescenceThicknessTexture");
|
|
3473
|
+
pushRefsForSlot(mat.specularTexture, "specularTexture");
|
|
3474
|
+
pushRefsForSlot(mat.specularColorTexture, "specularColorTexture");
|
|
3055
3475
|
return refs;
|
|
3056
3476
|
}
|
|
3057
3477
|
function availableUvSets(mesh) {
|
|
@@ -3072,7 +3492,17 @@ function rewriteMaterialAssetRefs(matAsset, mat, doc, maps) {
|
|
|
3072
3492
|
["occlusionTexture", mat.occlusionTexture],
|
|
3073
3493
|
["emissiveTexture", mat.emissiveTexture],
|
|
3074
3494
|
["transmissionTexture", mat.transmissionTexture],
|
|
3075
|
-
["thicknessTexture", mat.thicknessTexture]
|
|
3495
|
+
["thicknessTexture", mat.thicknessTexture],
|
|
3496
|
+
["clearcoatTexture", mat.clearcoatTexture],
|
|
3497
|
+
["clearcoatRoughnessTexture", mat.clearcoatRoughnessTexture],
|
|
3498
|
+
["clearcoatNormalTexture", mat.clearcoatNormalTexture],
|
|
3499
|
+
["anisotropyTexture", mat.anisotropyTexture],
|
|
3500
|
+
["sheenColorTexture", mat.sheenColorTexture],
|
|
3501
|
+
["sheenRoughnessTexture", mat.sheenRoughnessTexture],
|
|
3502
|
+
["iridescenceTexture", mat.iridescenceTexture],
|
|
3503
|
+
["iridescenceThicknessTexture", mat.iridescenceThicknessTexture],
|
|
3504
|
+
["specularTexture", mat.specularTexture],
|
|
3505
|
+
["specularColorTexture", mat.specularColorTexture]
|
|
3076
3506
|
];
|
|
3077
3507
|
let cursor = 0;
|
|
3078
3508
|
for (const [slot, rawBinding] of slots) {
|
|
@@ -3167,7 +3597,55 @@ async function importGltf(ctx, meshopt) {
|
|
|
3167
3597
|
})
|
|
3168
3598
|
};
|
|
3169
3599
|
}
|
|
3170
|
-
const
|
|
3600
|
+
const stabilizedMesh = stabilizeMeshMaterialSlots(
|
|
3601
|
+
bridged.value,
|
|
3602
|
+
ctx,
|
|
3603
|
+
sub.guid,
|
|
3604
|
+
sub.sourceKey
|
|
3605
|
+
);
|
|
3606
|
+
const lodGroup = doc.lod?.groups?.find(
|
|
3607
|
+
(group) => doc.nodes[group.rootNode]?.meshIndex === sub.sourceIndex
|
|
3608
|
+
) ?? (doc.lod?.rootNode !== void 0 && doc.nodes[doc.lod.rootNode]?.meshIndex === sub.sourceIndex ? doc.lod : void 0);
|
|
3609
|
+
const rootMeshIndex = lodGroup === void 0 ? void 0 : sub.sourceIndex;
|
|
3610
|
+
if (lodGroup !== void 0) {
|
|
3611
|
+
const referencedMeshIndices = [
|
|
3612
|
+
sub.sourceIndex,
|
|
3613
|
+
...lodGroup.lodNodeIds.map((nodeIndex) => doc.nodes[nodeIndex]?.meshIndex)
|
|
3614
|
+
];
|
|
3615
|
+
const missingMeshIndex = referencedMeshIndices.find(
|
|
3616
|
+
(meshIndex) => !Number.isInteger(meshIndex) || meshIndex === null || meshIndex === void 0 || maps.meshGuidByIndex.get(meshIndex) === void 0
|
|
3617
|
+
);
|
|
3618
|
+
if (missingMeshIndex !== void 0) {
|
|
3619
|
+
return {
|
|
3620
|
+
ok: false,
|
|
3621
|
+
error: new ImportError({
|
|
3622
|
+
code: "import-internal-error",
|
|
3623
|
+
expected: "every MSFT_lod node to resolve a cooked mesh sub-asset",
|
|
3624
|
+
actual: String(missingMeshIndex),
|
|
3625
|
+
hint: "repair the referenced glTF mesh primitives and re-run the importer",
|
|
3626
|
+
detail: {
|
|
3627
|
+
reason: `MSFT_lod group for mesh ${sub.sourceIndex} references an unprojected mesh`
|
|
3628
|
+
}
|
|
3629
|
+
})
|
|
3630
|
+
};
|
|
3631
|
+
}
|
|
3632
|
+
}
|
|
3633
|
+
const authoredLods = sub.sourceKey === void 0 ? void 0 : ctx.sourceOverrides?.[sub.sourceKey]?.lods;
|
|
3634
|
+
const lodEntries = Array.isArray(authoredLods) ? authoredLods : [];
|
|
3635
|
+
const lodLevels = rootMeshIndex === sub.sourceIndex && lodGroup !== void 0 ? lodGroup.lodNodeIds.flatMap((nodeIndex, index) => {
|
|
3636
|
+
const meshIndex = doc.nodes[nodeIndex]?.meshIndex;
|
|
3637
|
+
const guid = meshIndex === void 0 || meshIndex === null ? void 0 : maps.meshGuidByIndex.get(meshIndex);
|
|
3638
|
+
if (guid === void 0) return [];
|
|
3639
|
+
const authored = lodEntries[index];
|
|
3640
|
+
const authoredCoverage = authored !== null && typeof authored === "object" ? authored.screenCoverage : void 0;
|
|
3641
|
+
const coverage = typeof authoredCoverage === "number" ? authoredCoverage : lodGroup.screenCoverages[index] ?? deriveDefaultLodScreenCoverages(lodGroup.lodNodeIds.length + 1)[index];
|
|
3642
|
+
const parsed2 = AssetGuid.parse(guid);
|
|
3643
|
+
return parsed2.ok && coverage !== void 0 ? [{ mesh: parsed2.value, screenCoverage: coverage, guid }] : [];
|
|
3644
|
+
}) : [];
|
|
3645
|
+
const meshPayload = lodLevels.length === 0 ? stabilizedMesh : {
|
|
3646
|
+
...stabilizedMesh,
|
|
3647
|
+
lods: lodLevels.map(({ mesh, screenCoverage }) => ({ mesh, screenCoverage }))
|
|
3648
|
+
};
|
|
3171
3649
|
const materialRefs = [];
|
|
3172
3650
|
const seenMaterialGuids = /* @__PURE__ */ new Set();
|
|
3173
3651
|
for (let slotIndex = 0; slotIndex < meshPayload.materialSlots.length; slotIndex++) {
|
|
@@ -3181,6 +3659,15 @@ async function importGltf(ctx, meshopt) {
|
|
|
3181
3659
|
});
|
|
3182
3660
|
}
|
|
3183
3661
|
}
|
|
3662
|
+
for (const [lodIndex, level] of lodLevels.entries()) {
|
|
3663
|
+
if (!seenMaterialGuids.has(level.guid.toLowerCase())) {
|
|
3664
|
+
seenMaterialGuids.add(level.guid.toLowerCase());
|
|
3665
|
+
materialRefs.push({
|
|
3666
|
+
guid: level.guid,
|
|
3667
|
+
sourceField: { fieldName: "lods", arrayIndex: lodIndex }
|
|
3668
|
+
});
|
|
3669
|
+
}
|
|
3670
|
+
}
|
|
3184
3671
|
out.push({
|
|
3185
3672
|
guid: sub.guid,
|
|
3186
3673
|
kind: "mesh",
|
|
@@ -3232,6 +3719,10 @@ async function importGltf(ctx, meshopt) {
|
|
|
3232
3719
|
if (!uvResult.ok) {
|
|
3233
3720
|
throw Object.assign(new Error(uvResult.error.message), uvResult.error);
|
|
3234
3721
|
}
|
|
3722
|
+
const tangentResult = validateMaterialTangentInputs(mat, meshIr);
|
|
3723
|
+
if (!tangentResult.ok) {
|
|
3724
|
+
throw Object.assign(new Error(tangentResult.error.message), tangentResult.error);
|
|
3725
|
+
}
|
|
3235
3726
|
}
|
|
3236
3727
|
const matAsset = toMaterialAsset(mat, {
|
|
3237
3728
|
textureHandles: maps.textureHandles,
|