@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/index.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { err, ok, reconcileMeshMaterialSlotTopology, MESH_MATERIAL_SLOT_SOURCE_OVERRIDE_PAYLOAD_SCHEMA, ImportError, IMPORT_ERROR_HINTS, toShared, resolveMeshMaterialSlotDefaultGuid
|
|
1
|
+
import { err, ok, createMaterialError, standardMaterialParameters, reconcileMeshMaterialSlotTopology, MESH_MATERIAL_SLOT_SOURCE_OVERRIDE_PAYLOAD_SCHEMA, STANDARD_MATERIAL_PARAM_SCHEMA, STANDARD_PHYSICAL_PARAMETER_NAMES, STANDARD_TRANSMISSION_PARAMETER_NAMES, ImportError, STANDARD_LAYER_PARAMETER_GROUPS, IMPORT_ERROR_HINTS, toShared, resolveMeshMaterialSlotDefaultGuid } from '@forgeax/engine-types';
|
|
2
2
|
export { err, ok } from '@forgeax/engine-types';
|
|
3
|
-
import { packInterleavedVertexAttributes } from '@forgeax/engine-geometry';
|
|
3
|
+
import { computeTangentVec4, 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
|
|
|
@@ -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"
|
|
@@ -486,6 +495,16 @@ function meshIrToMeshAsset(prims, materials = {}) {
|
|
|
486
495
|
const materialSlot = slotFor(mesh.materialIndex);
|
|
487
496
|
const primVertexCount = mesh.positions.length / 3;
|
|
488
497
|
const primIndexCount = mesh.indices === void 0 ? 0 : mesh.indices.length;
|
|
498
|
+
let generatedTangents;
|
|
499
|
+
if (mesh.tangents === void 0 && mesh.normals !== void 0 && mesh.texcoord0 !== void 0) {
|
|
500
|
+
const generated = computeTangentVec4(
|
|
501
|
+
mesh.positions,
|
|
502
|
+
mesh.normals,
|
|
503
|
+
mesh.texcoord0,
|
|
504
|
+
mesh.indices
|
|
505
|
+
);
|
|
506
|
+
if (generated.ok) generatedTangents = generated.value;
|
|
507
|
+
}
|
|
489
508
|
if (mesh.colors0 !== void 0 && mesh.colors0.length !== primVertexCount * 4) {
|
|
490
509
|
return err(
|
|
491
510
|
gltfErr("gltf-mesh-bridge-invalid", {
|
|
@@ -517,12 +536,13 @@ function meshIrToMeshAsset(prims, materials = {}) {
|
|
|
517
536
|
uvsCat[(vertexCursor + i) * 2 + 0] = mesh.texcoord0[t + 0];
|
|
518
537
|
uvsCat[(vertexCursor + i) * 2 + 1] = mesh.texcoord0[t + 1];
|
|
519
538
|
}
|
|
520
|
-
|
|
539
|
+
const sourceTangents = mesh.tangents ?? generatedTangents;
|
|
540
|
+
if (sourceTangents !== void 0) {
|
|
521
541
|
const g = i * 4;
|
|
522
|
-
tangentsCat[(vertexCursor + i) * 4 + 0] =
|
|
523
|
-
tangentsCat[(vertexCursor + i) * 4 + 1] =
|
|
524
|
-
tangentsCat[(vertexCursor + i) * 4 + 2] =
|
|
525
|
-
tangentsCat[(vertexCursor + i) * 4 + 3] =
|
|
542
|
+
tangentsCat[(vertexCursor + i) * 4 + 0] = sourceTangents[g + 0];
|
|
543
|
+
tangentsCat[(vertexCursor + i) * 4 + 1] = sourceTangents[g + 1];
|
|
544
|
+
tangentsCat[(vertexCursor + i) * 4 + 2] = sourceTangents[g + 2];
|
|
545
|
+
tangentsCat[(vertexCursor + i) * 4 + 3] = sourceTangents[g + 3];
|
|
526
546
|
} else {
|
|
527
547
|
tangentsCat[(vertexCursor + i) * 4 + 0] = 1;
|
|
528
548
|
tangentsCat[(vertexCursor + i) * 4 + 3] = 1;
|
|
@@ -857,6 +877,12 @@ function textureValue(info, slot, ctx) {
|
|
|
857
877
|
return { ...value, normalScale: normal.scale };
|
|
858
878
|
}
|
|
859
879
|
}
|
|
880
|
+
if (slot === "clearcoatNormalTexture") {
|
|
881
|
+
const normal = info;
|
|
882
|
+
if (typeof normal === "object" && normal?.scale !== void 0) {
|
|
883
|
+
return { ...value, normalScale: normal.scale };
|
|
884
|
+
}
|
|
885
|
+
}
|
|
860
886
|
if (slot === "occlusionTexture") {
|
|
861
887
|
const occlusion = info;
|
|
862
888
|
if (typeof occlusion === "object" && occlusion?.strength !== void 0) {
|
|
@@ -865,6 +891,47 @@ function textureValue(info, slot, ctx) {
|
|
|
865
891
|
}
|
|
866
892
|
return value;
|
|
867
893
|
}
|
|
894
|
+
function standardRootParameterNames(mat) {
|
|
895
|
+
const names = new Set(
|
|
896
|
+
STANDARD_MATERIAL_PARAM_SCHEMA.filter(
|
|
897
|
+
(entry) => !STANDARD_PHYSICAL_PARAMETER_NAMES.has(entry.name) && !STANDARD_TRANSMISSION_PARAMETER_NAMES.has(entry.name)
|
|
898
|
+
).map((entry) => entry.name)
|
|
899
|
+
);
|
|
900
|
+
names.add("ior");
|
|
901
|
+
const addLayer = (layer) => {
|
|
902
|
+
for (const name of STANDARD_LAYER_PARAMETER_GROUPS[layer]) names.add(name);
|
|
903
|
+
};
|
|
904
|
+
const addTexture = (name, info) => {
|
|
905
|
+
if (info !== void 0) names.add(name);
|
|
906
|
+
};
|
|
907
|
+
const clearcoat = mat.clearcoatFactor !== void 0 || mat.clearcoatRoughnessFactor !== void 0 || mat.clearcoatNormalTexture !== void 0 || mat.clearcoatTexture !== void 0 || mat.clearcoatRoughnessTexture !== void 0;
|
|
908
|
+
const anisotropy = mat.anisotropyStrength !== void 0 || mat.anisotropyRotation !== void 0 || mat.anisotropyTexture !== void 0;
|
|
909
|
+
const sheen = mat.sheenColorFactor !== void 0 || mat.sheenRoughnessFactor !== void 0 || mat.sheenColorTexture !== void 0 || mat.sheenRoughnessTexture !== void 0;
|
|
910
|
+
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;
|
|
911
|
+
if (clearcoat) addLayer("clearcoat");
|
|
912
|
+
if (anisotropy) addLayer("anisotropy");
|
|
913
|
+
if (sheen) addLayer("sheen");
|
|
914
|
+
if (iridescence) addLayer("iridescence");
|
|
915
|
+
if (clearcoat && mat.clearcoatNormalTexture !== void 0) names.add("clearcoatNormalScale");
|
|
916
|
+
addTexture("clearcoatTexture", mat.clearcoatTexture);
|
|
917
|
+
addTexture("clearcoatRoughnessTexture", mat.clearcoatRoughnessTexture);
|
|
918
|
+
addTexture("clearcoatNormalTexture", mat.clearcoatNormalTexture);
|
|
919
|
+
addTexture("anisotropyTexture", mat.anisotropyTexture);
|
|
920
|
+
addTexture("sheenColorTexture", mat.sheenColorTexture);
|
|
921
|
+
addTexture("sheenRoughnessTexture", mat.sheenRoughnessTexture);
|
|
922
|
+
addTexture("iridescenceTexture", mat.iridescenceTexture);
|
|
923
|
+
addTexture("iridescenceThicknessTexture", mat.iridescenceThicknessTexture);
|
|
924
|
+
addTexture("specularTexture", mat.specularTexture);
|
|
925
|
+
addTexture("specularColorTexture", mat.specularColorTexture);
|
|
926
|
+
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;
|
|
927
|
+
if (transmission) {
|
|
928
|
+
for (const name of STANDARD_TRANSMISSION_PARAMETER_NAMES) names.add(name);
|
|
929
|
+
}
|
|
930
|
+
return {
|
|
931
|
+
names,
|
|
932
|
+
extended: clearcoat || anisotropy || sheen || iridescence || transmission || mat.specularTexture !== void 0 || mat.specularColorTexture !== void 0
|
|
933
|
+
};
|
|
934
|
+
}
|
|
868
935
|
function validateMaterialUvSets(mat, primitive, availableSets) {
|
|
869
936
|
const available = new Set(availableSets);
|
|
870
937
|
const slots = [
|
|
@@ -874,7 +941,17 @@ function validateMaterialUvSets(mat, primitive, availableSets) {
|
|
|
874
941
|
["occlusionTexture", mat.occlusionTexture],
|
|
875
942
|
["emissiveTexture", mat.emissiveTexture],
|
|
876
943
|
["transmissionTexture", mat.transmissionTexture],
|
|
877
|
-
["thicknessTexture", mat.thicknessTexture]
|
|
944
|
+
["thicknessTexture", mat.thicknessTexture],
|
|
945
|
+
["clearcoatTexture", mat.clearcoatTexture],
|
|
946
|
+
["clearcoatRoughnessTexture", mat.clearcoatRoughnessTexture],
|
|
947
|
+
["clearcoatNormalTexture", mat.clearcoatNormalTexture],
|
|
948
|
+
["anisotropyTexture", mat.anisotropyTexture],
|
|
949
|
+
["sheenColorTexture", mat.sheenColorTexture],
|
|
950
|
+
["sheenRoughnessTexture", mat.sheenRoughnessTexture],
|
|
951
|
+
["iridescenceTexture", mat.iridescenceTexture],
|
|
952
|
+
["iridescenceThicknessTexture", mat.iridescenceThicknessTexture],
|
|
953
|
+
["specularTexture", mat.specularTexture],
|
|
954
|
+
["specularColorTexture", mat.specularColorTexture]
|
|
878
955
|
];
|
|
879
956
|
for (const [slot, rawBinding] of slots) {
|
|
880
957
|
const binding = textureInfo(rawBinding);
|
|
@@ -894,7 +971,44 @@ function validateMaterialUvSets(mat, primitive, availableSets) {
|
|
|
894
971
|
}
|
|
895
972
|
return ok(void 0);
|
|
896
973
|
}
|
|
974
|
+
function validateMaterialTangentInputs(mat, mesh, layer = "clearcoat") {
|
|
975
|
+
const anisotropyDeclared = mat.anisotropyStrength !== void 0 || mat.anisotropyRotation !== void 0 || mat.anisotropyTexture !== void 0;
|
|
976
|
+
const tangentSlot = anisotropyDeclared ? { layer: "anisotropy", info: mat.anisotropyTexture } : mat.clearcoatNormalTexture !== void 0 ? { layer: "clearcoat", info: mat.clearcoatNormalTexture } : void 0;
|
|
977
|
+
if (tangentSlot === void 0) return ok(void 0);
|
|
978
|
+
const selected = textureInfo(tangentSlot.info);
|
|
979
|
+
const uvSet = selected?.texCoord ?? 0;
|
|
980
|
+
const uv = mesh[`texcoord${uvSet === 0 ? "0" : uvSet}`];
|
|
981
|
+
const attributes = ["NORMAL", `TEXCOORD_${uvSet}`, "TANGENT"];
|
|
982
|
+
const fail = (reason) => err(
|
|
983
|
+
createMaterialError("material-tangent-required", {
|
|
984
|
+
code: "material-tangent-required",
|
|
985
|
+
material: mat.name ?? "<unnamed>",
|
|
986
|
+
mesh: mesh.name ?? "<unnamed>",
|
|
987
|
+
layer: tangentSlot.layer ?? layer,
|
|
988
|
+
uv: `TEXCOORD_${uvSet}`,
|
|
989
|
+
attributes,
|
|
990
|
+
reason
|
|
991
|
+
})
|
|
992
|
+
);
|
|
993
|
+
if (mesh.tangents !== void 0) {
|
|
994
|
+
if (mesh.tangents.length !== mesh.positions.length / 3 * 4 || mesh.tangents.some((value) => !Number.isFinite(value))) {
|
|
995
|
+
return fail("imported TANGENT must be finite vec4 per vertex");
|
|
996
|
+
}
|
|
997
|
+
return ok(void 0);
|
|
998
|
+
}
|
|
999
|
+
if (mesh.normals === void 0) return fail("NORMAL is required to generate tangent");
|
|
1000
|
+
if (!(uv instanceof Float32Array))
|
|
1001
|
+
return fail("the selected UV set is required to generate tangent");
|
|
1002
|
+
const generated = computeTangentVec4(mesh.positions, mesh.normals, uv, mesh.indices);
|
|
1003
|
+
if (!generated.ok) {
|
|
1004
|
+
const detail = generated.error.detail;
|
|
1005
|
+
const reason = detail !== void 0 && "reason" in detail ? String(detail.reason) : "tangent producer rejected topology";
|
|
1006
|
+
return fail(reason);
|
|
1007
|
+
}
|
|
1008
|
+
return ok(void 0);
|
|
1009
|
+
}
|
|
897
1010
|
function toMaterialAsset(mat, ctx) {
|
|
1011
|
+
const rootContract = standardRootParameterNames(mat);
|
|
898
1012
|
const values = {
|
|
899
1013
|
baseColor: mat.baseColorFactor,
|
|
900
1014
|
metallic: mat.metallicFactor,
|
|
@@ -911,7 +1025,17 @@ function toMaterialAsset(mat, ctx) {
|
|
|
911
1025
|
["occlusionTexture", mat.occlusionTexture],
|
|
912
1026
|
["emissiveTexture", mat.emissiveTexture],
|
|
913
1027
|
["transmissionTexture", mat.transmissionTexture],
|
|
914
|
-
["thicknessTexture", mat.thicknessTexture]
|
|
1028
|
+
["thicknessTexture", mat.thicknessTexture],
|
|
1029
|
+
["clearcoatTexture", mat.clearcoatTexture],
|
|
1030
|
+
["clearcoatRoughnessTexture", mat.clearcoatRoughnessTexture],
|
|
1031
|
+
["clearcoatNormalTexture", mat.clearcoatNormalTexture],
|
|
1032
|
+
["anisotropyTexture", mat.anisotropyTexture],
|
|
1033
|
+
["sheenColorTexture", mat.sheenColorTexture],
|
|
1034
|
+
["sheenRoughnessTexture", mat.sheenRoughnessTexture],
|
|
1035
|
+
["iridescenceTexture", mat.iridescenceTexture],
|
|
1036
|
+
["iridescenceThicknessTexture", mat.iridescenceThicknessTexture],
|
|
1037
|
+
["specularTexture", mat.specularTexture],
|
|
1038
|
+
["specularColorTexture", mat.specularColorTexture]
|
|
915
1039
|
];
|
|
916
1040
|
for (const [slot, info] of textureSlots) {
|
|
917
1041
|
const value = textureValue(info, slot, ctx);
|
|
@@ -920,11 +1044,35 @@ function toMaterialAsset(mat, ctx) {
|
|
|
920
1044
|
if (mat.occlusionTexture !== void 0 && values.occlusionStrength === void 0) {
|
|
921
1045
|
values.occlusionStrength = 1;
|
|
922
1046
|
}
|
|
923
|
-
if (
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1047
|
+
if (rootContract.extended && rootContract.names.has("transmission")) {
|
|
1048
|
+
values.transmission = mat.transmissionFactor ?? 0;
|
|
1049
|
+
values.ior = mat.ior ?? 1.5;
|
|
1050
|
+
values.thickness = mat.thicknessFactor ?? 0;
|
|
1051
|
+
values.attenuationColor = mat.attenuationColor ?? [1, 1, 1];
|
|
1052
|
+
if (mat.attenuationDistance !== void 0) values.attenuationDistance = mat.attenuationDistance;
|
|
1053
|
+
}
|
|
1054
|
+
if (rootContract.names.has("clearcoat")) {
|
|
1055
|
+
values.clearcoat = mat.clearcoatFactor ?? 0;
|
|
1056
|
+
values.clearcoatRoughness = mat.clearcoatRoughnessFactor ?? 0;
|
|
1057
|
+
}
|
|
1058
|
+
const clearcoatNormal = textureInfo(mat.clearcoatNormalTexture);
|
|
1059
|
+
if (clearcoatNormal?.scale !== void 0) values.clearcoatNormalScale = clearcoatNormal.scale;
|
|
1060
|
+
if (rootContract.names.has("anisotropyStrength")) {
|
|
1061
|
+
values.anisotropyStrength = mat.anisotropyStrength ?? 0;
|
|
1062
|
+
values.anisotropyRotation = mat.anisotropyRotation ?? 0;
|
|
1063
|
+
}
|
|
1064
|
+
if (rootContract.names.has("sheenColor")) {
|
|
1065
|
+
values.sheenColor = mat.sheenColorFactor ?? [0, 0, 0];
|
|
1066
|
+
values.sheenRoughness = mat.sheenRoughnessFactor ?? 0;
|
|
1067
|
+
}
|
|
1068
|
+
if (rootContract.names.has("iridescence")) {
|
|
1069
|
+
values.iridescence = mat.iridescenceFactor ?? 0;
|
|
1070
|
+
values.iridescenceIor = mat.iridescenceIor ?? 1.3;
|
|
1071
|
+
values.iridescenceThicknessMinimum = mat.iridescenceThicknessMinimum ?? 100;
|
|
1072
|
+
values.iridescenceThicknessMaximum = mat.iridescenceThicknessMaximum ?? 400;
|
|
1073
|
+
}
|
|
1074
|
+
if (mat.specularFactor !== void 0) values.specular = mat.specularFactor;
|
|
1075
|
+
if (mat.specularColorFactor !== void 0) values.specularColor = mat.specularColorFactor;
|
|
928
1076
|
const module = ctx?.skinned === true ? "forgeax::pbr-skin" : "forgeax::default-standard-pbr";
|
|
929
1077
|
const isMask = mat.alphaMode === "MASK";
|
|
930
1078
|
const alphaCutoff = isMask ? mat.alphaCutoff ?? 0.5 : void 0;
|
|
@@ -957,8 +1105,9 @@ function toMaterialAsset(mat, ctx) {
|
|
|
957
1105
|
return {
|
|
958
1106
|
kind: "material",
|
|
959
1107
|
colorSpace: "linear",
|
|
960
|
-
|
|
1108
|
+
...!rootContract.extended && ctx?.standardRootGuid !== void 0 ? { parent: ctx.standardRootGuid } : {},
|
|
961
1109
|
passes: [pass],
|
|
1110
|
+
...ctx?.standardRootGuid !== void 0 && !rootContract.extended ? {} : { parameters: standardMaterialParameters(rootContract.names) },
|
|
962
1111
|
values
|
|
963
1112
|
};
|
|
964
1113
|
}
|
|
@@ -966,15 +1115,22 @@ function toMaterialAsset(mat, ctx) {
|
|
|
966
1115
|
// src/check-extensions.ts
|
|
967
1116
|
var EXTENSION_ALLOWLIST = [
|
|
968
1117
|
"EXT_mesh_gpu_instancing",
|
|
969
|
-
"EXT_meshopt_compression"
|
|
970
|
-
];
|
|
971
|
-
var SUPPORTED_EXTENSIONS = [
|
|
972
|
-
...EXTENSION_ALLOWLIST,
|
|
1118
|
+
"EXT_meshopt_compression",
|
|
973
1119
|
"KHR_lights_punctual",
|
|
974
1120
|
"KHR_texture_transform",
|
|
975
1121
|
"KHR_materials_transmission",
|
|
976
1122
|
"KHR_materials_ior",
|
|
977
|
-
"KHR_materials_volume"
|
|
1123
|
+
"KHR_materials_volume",
|
|
1124
|
+
"KHR_materials_clearcoat",
|
|
1125
|
+
"KHR_materials_anisotropy",
|
|
1126
|
+
"KHR_materials_sheen",
|
|
1127
|
+
"KHR_materials_iridescence",
|
|
1128
|
+
"KHR_materials_specular"
|
|
1129
|
+
];
|
|
1130
|
+
var SUPPORTED_EXTENSIONS = [
|
|
1131
|
+
...EXTENSION_ALLOWLIST,
|
|
1132
|
+
"MSFT_lod",
|
|
1133
|
+
"MSFT_screencoverage"
|
|
978
1134
|
];
|
|
979
1135
|
function checkExtensions(json) {
|
|
980
1136
|
const required = json.extensionsRequired ?? [];
|
|
@@ -1169,8 +1325,8 @@ function parseGlbChunks(buffer, filePath) {
|
|
|
1169
1325
|
|
|
1170
1326
|
// src/extract-image-bytes.ts
|
|
1171
1327
|
var DATA_URI_MIME_RE = /^data:([^;,]+)(?:;[^,;]+)*;base64,/;
|
|
1172
|
-
function describeImportError(
|
|
1173
|
-
return
|
|
1328
|
+
function describeImportError(err3) {
|
|
1329
|
+
return err3.message;
|
|
1174
1330
|
}
|
|
1175
1331
|
function classifyMime(declaredMime, bytes) {
|
|
1176
1332
|
if (declaredMime === "image/png" || declaredMime === "image/jpeg") return declaredMime;
|
|
@@ -1412,9 +1568,21 @@ function deriveTextureColorSpace(input) {
|
|
|
1412
1568
|
for (const mat of input.materials) {
|
|
1413
1569
|
record(imageOfTexture(mat.baseColorTexture), "srgb");
|
|
1414
1570
|
record(imageOfTexture(mat.emissiveTexture), "srgb");
|
|
1571
|
+
record(imageOfTexture(mat.sheenColorTexture), "srgb");
|
|
1572
|
+
record(imageOfTexture(mat.specularColorTexture), "srgb");
|
|
1415
1573
|
record(imageOfTexture(mat.metallicRoughnessTexture), "linear");
|
|
1416
1574
|
record(imageOfTexture(mat.normalTexture), "linear");
|
|
1417
1575
|
record(imageOfTexture(mat.occlusionTexture), "linear");
|
|
1576
|
+
record(imageOfTexture(mat.transmissionTexture), "linear");
|
|
1577
|
+
record(imageOfTexture(mat.thicknessTexture), "linear");
|
|
1578
|
+
record(imageOfTexture(mat.clearcoatTexture), "linear");
|
|
1579
|
+
record(imageOfTexture(mat.clearcoatRoughnessTexture), "linear");
|
|
1580
|
+
record(imageOfTexture(mat.clearcoatNormalTexture), "linear");
|
|
1581
|
+
record(imageOfTexture(mat.anisotropyTexture), "linear");
|
|
1582
|
+
record(imageOfTexture(mat.sheenRoughnessTexture), "linear");
|
|
1583
|
+
record(imageOfTexture(mat.iridescenceTexture), "linear");
|
|
1584
|
+
record(imageOfTexture(mat.iridescenceThicknessTexture), "linear");
|
|
1585
|
+
record(imageOfTexture(mat.specularTexture), "linear");
|
|
1418
1586
|
}
|
|
1419
1587
|
for (let i = 0; i < input.imageCount; i++) {
|
|
1420
1588
|
if (!result.has(i)) {
|
|
@@ -1424,6 +1592,103 @@ function deriveTextureColorSpace(input) {
|
|
|
1424
1592
|
return result;
|
|
1425
1593
|
}
|
|
1426
1594
|
|
|
1595
|
+
// src/lod/parse-lod.ts
|
|
1596
|
+
function parseGltfLodExtension(json) {
|
|
1597
|
+
const doc = json;
|
|
1598
|
+
const nodes = doc.nodes ?? [];
|
|
1599
|
+
const rawCoverage = doc.extensions?.MSFT_screencoverage?.scales;
|
|
1600
|
+
const groups = [];
|
|
1601
|
+
for (let rootNode = 0; rootNode < nodes.length; rootNode += 1) {
|
|
1602
|
+
if (nodes[rootNode]?.extensions?.MSFT_lod === void 0) continue;
|
|
1603
|
+
const idsRaw = nodes[rootNode]?.extensions?.MSFT_lod?.ids;
|
|
1604
|
+
if (!Array.isArray(idsRaw)) {
|
|
1605
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids: [], reason: "not-integer" }));
|
|
1606
|
+
}
|
|
1607
|
+
const ids = idsRaw.filter((value) => typeof value === "number");
|
|
1608
|
+
if (ids.length !== idsRaw.length || ids.some((id) => !Number.isInteger(id) || id < 0 || id >= nodes.length)) {
|
|
1609
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids, reason: "missing-node" }));
|
|
1610
|
+
}
|
|
1611
|
+
if (new Set(ids).size !== ids.length || ids.includes(rootNode)) {
|
|
1612
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids, reason: "duplicate-node" }));
|
|
1613
|
+
}
|
|
1614
|
+
const hasMesh = (node) => {
|
|
1615
|
+
if (typeof node?.mesh !== "number" || !Number.isInteger(node.mesh) || node.mesh < 0) {
|
|
1616
|
+
return false;
|
|
1617
|
+
}
|
|
1618
|
+
const mesh = doc.meshes?.[node.mesh];
|
|
1619
|
+
return mesh !== void 0 && Array.isArray(mesh.primitives) && mesh.primitives.length > 0;
|
|
1620
|
+
};
|
|
1621
|
+
if (!hasMesh(nodes[rootNode]) || ids.some((id) => !hasMesh(nodes[id]))) {
|
|
1622
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids, reason: "missing-node" }));
|
|
1623
|
+
}
|
|
1624
|
+
const nodeCoverage = nodes[rootNode]?.extras?.MSFT_screencoverage;
|
|
1625
|
+
const coverage = normalizeScreenCoverages(
|
|
1626
|
+
nodeCoverage === void 0 ? rawCoverage : nodeCoverage,
|
|
1627
|
+
ids.length,
|
|
1628
|
+
nodeCoverage !== void 0
|
|
1629
|
+
);
|
|
1630
|
+
if (coverage === "invalid") {
|
|
1631
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode, ids, reason: "coverage" }));
|
|
1632
|
+
}
|
|
1633
|
+
groups.push({
|
|
1634
|
+
rootNode,
|
|
1635
|
+
lodNodeIds: ids,
|
|
1636
|
+
screenCoverages: coverage ?? []
|
|
1637
|
+
});
|
|
1638
|
+
}
|
|
1639
|
+
const first = groups[0];
|
|
1640
|
+
if (first === void 0) {
|
|
1641
|
+
if (doc.extensionsRequired?.some((extension) => extension === "MSFT_lod")) {
|
|
1642
|
+
return err(gltfErr("gltf-lod-invalid", { rootNode: 0, ids: [], reason: "missing-node" }));
|
|
1643
|
+
}
|
|
1644
|
+
return ok({ rootNode: 0, lodNodeIds: [], screenCoverages: [], groups: [] });
|
|
1645
|
+
}
|
|
1646
|
+
return ok({
|
|
1647
|
+
rootNode: first.rootNode,
|
|
1648
|
+
lodNodeIds: first.lodNodeIds,
|
|
1649
|
+
screenCoverages: first.screenCoverages,
|
|
1650
|
+
groups
|
|
1651
|
+
});
|
|
1652
|
+
}
|
|
1653
|
+
function normalizeScreenCoverages(raw, lodCount, officialNodeForm) {
|
|
1654
|
+
if (raw === void 0) return void 0;
|
|
1655
|
+
if (!Array.isArray(raw)) return "invalid";
|
|
1656
|
+
if (raw.some(
|
|
1657
|
+
(value) => typeof value !== "number" || !Number.isFinite(value) || value <= 0 || value > 1
|
|
1658
|
+
)) {
|
|
1659
|
+
return "invalid";
|
|
1660
|
+
}
|
|
1661
|
+
const expectedLengths = officialNodeForm ? [lodCount + 1] : [lodCount, lodCount + 1];
|
|
1662
|
+
if (!expectedLengths.includes(raw.length)) return "invalid";
|
|
1663
|
+
const values = raw.slice(0, lodCount);
|
|
1664
|
+
for (let index = 1; index < values.length; index += 1) {
|
|
1665
|
+
if (values[index] >= values[index - 1]) return "invalid";
|
|
1666
|
+
}
|
|
1667
|
+
return values;
|
|
1668
|
+
}
|
|
1669
|
+
function projectGltfLodMeta(input) {
|
|
1670
|
+
const defaults = deriveDefaultLodScreenCoverages(input.levels.length + 1);
|
|
1671
|
+
const previous = (input.previous ?? []).map((level) => ({
|
|
1672
|
+
sourceKey: level.sourceKey,
|
|
1673
|
+
meshGuid: level.guid,
|
|
1674
|
+
...level.screenCoverage === void 0 ? {} : { screenCoverage: level.screenCoverage }
|
|
1675
|
+
}));
|
|
1676
|
+
const next = input.levels.map((level, index) => ({
|
|
1677
|
+
sourceKey: level.sourceKey,
|
|
1678
|
+
meshGuid: level.guid,
|
|
1679
|
+
screenCoverage: level.screenCoverage ?? defaults[index] ?? 0
|
|
1680
|
+
}));
|
|
1681
|
+
const reconciled = reconcileMeshLodMeta(previous, next);
|
|
1682
|
+
if (!reconciled.ok) return err({ code: reconciled.error.code, reason: reconciled.error.reason });
|
|
1683
|
+
return ok({
|
|
1684
|
+
lods: reconciled.value.lods.map((level) => ({
|
|
1685
|
+
sourceKey: level.sourceKey,
|
|
1686
|
+
meshGuid: level.meshGuid,
|
|
1687
|
+
screenCoverage: level.screenCoverage
|
|
1688
|
+
}))
|
|
1689
|
+
});
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1427
1692
|
// src/material/parse-material.ts
|
|
1428
1693
|
function tuple2(values) {
|
|
1429
1694
|
if (values === void 0 || values.length < 2) return void 0;
|
|
@@ -1468,6 +1733,11 @@ function parseMaterial(matJson, textures) {
|
|
|
1468
1733
|
const transmission = matJson.extensions?.KHR_materials_transmission;
|
|
1469
1734
|
const ior = matJson.extensions?.KHR_materials_ior;
|
|
1470
1735
|
const volume = matJson.extensions?.KHR_materials_volume;
|
|
1736
|
+
const clearcoat = matJson.extensions?.KHR_materials_clearcoat;
|
|
1737
|
+
const anisotropy = matJson.extensions?.KHR_materials_anisotropy;
|
|
1738
|
+
const sheen = matJson.extensions?.KHR_materials_sheen;
|
|
1739
|
+
const iridescence = matJson.extensions?.KHR_materials_iridescence;
|
|
1740
|
+
const specular = matJson.extensions?.KHR_materials_specular;
|
|
1471
1741
|
const attenuationColor = tuple3(volume?.attenuationColor);
|
|
1472
1742
|
const invalid = (extension, field, reason, actual) => err(
|
|
1473
1743
|
gltfErr("gltf-material-transmission-invalid", {
|
|
@@ -1477,6 +1747,14 @@ function parseMaterial(matJson, textures) {
|
|
|
1477
1747
|
...actual === void 0 ? {} : { actual }
|
|
1478
1748
|
})
|
|
1479
1749
|
);
|
|
1750
|
+
const physicalInvalid = (extension, field, reason, actual) => err(
|
|
1751
|
+
gltfErr("gltf-material-physical-invalid", {
|
|
1752
|
+
extension,
|
|
1753
|
+
field,
|
|
1754
|
+
reason,
|
|
1755
|
+
...actual === void 0 ? {} : { actual }
|
|
1756
|
+
})
|
|
1757
|
+
);
|
|
1480
1758
|
if (transmission?.transmissionFactor !== void 0 && (typeof transmission.transmissionFactor !== "number" || !Number.isFinite(transmission.transmissionFactor))) {
|
|
1481
1759
|
return invalid(
|
|
1482
1760
|
"KHR_materials_transmission",
|
|
@@ -1527,6 +1805,98 @@ function parseMaterial(matJson, textures) {
|
|
|
1527
1805
|
if (alphaMode === "BLEND" && (transmission?.transmissionFactor ?? 0) > 0) {
|
|
1528
1806
|
return invalid("KHR_materials_transmission", "alphaMode", "blend", alphaMode);
|
|
1529
1807
|
}
|
|
1808
|
+
if (clearcoat?.clearcoatFactor !== void 0 && (typeof clearcoat.clearcoatFactor !== "number" || !Number.isFinite(clearcoat.clearcoatFactor))) {
|
|
1809
|
+
return physicalInvalid(
|
|
1810
|
+
"KHR_materials_clearcoat",
|
|
1811
|
+
"clearcoatFactor",
|
|
1812
|
+
"non-finite",
|
|
1813
|
+
clearcoat.clearcoatFactor
|
|
1814
|
+
);
|
|
1815
|
+
}
|
|
1816
|
+
if (clearcoat?.clearcoatFactor !== void 0 && (clearcoat.clearcoatFactor < 0 || clearcoat.clearcoatFactor > 1)) {
|
|
1817
|
+
return physicalInvalid(
|
|
1818
|
+
"KHR_materials_clearcoat",
|
|
1819
|
+
"clearcoatFactor",
|
|
1820
|
+
"range",
|
|
1821
|
+
clearcoat.clearcoatFactor
|
|
1822
|
+
);
|
|
1823
|
+
}
|
|
1824
|
+
if (clearcoat?.clearcoatRoughnessFactor !== void 0 && (typeof clearcoat.clearcoatRoughnessFactor !== "number" || !Number.isFinite(clearcoat.clearcoatRoughnessFactor))) {
|
|
1825
|
+
return physicalInvalid(
|
|
1826
|
+
"KHR_materials_clearcoat",
|
|
1827
|
+
"clearcoatRoughnessFactor",
|
|
1828
|
+
"non-finite",
|
|
1829
|
+
clearcoat.clearcoatRoughnessFactor
|
|
1830
|
+
);
|
|
1831
|
+
}
|
|
1832
|
+
if (clearcoat?.clearcoatRoughnessFactor !== void 0 && (clearcoat.clearcoatRoughnessFactor < 0 || clearcoat.clearcoatRoughnessFactor > 1)) {
|
|
1833
|
+
return physicalInvalid(
|
|
1834
|
+
"KHR_materials_clearcoat",
|
|
1835
|
+
"clearcoatRoughnessFactor",
|
|
1836
|
+
"range",
|
|
1837
|
+
clearcoat.clearcoatRoughnessFactor
|
|
1838
|
+
);
|
|
1839
|
+
}
|
|
1840
|
+
if (clearcoat?.clearcoatNormalTexture?.scale !== void 0 && (typeof clearcoat.clearcoatNormalTexture.scale !== "number" || !Number.isFinite(clearcoat.clearcoatNormalTexture.scale) || clearcoat.clearcoatNormalTexture.scale < 0)) {
|
|
1841
|
+
return physicalInvalid(
|
|
1842
|
+
"KHR_materials_clearcoat",
|
|
1843
|
+
"clearcoatNormalTexture.scale",
|
|
1844
|
+
"range",
|
|
1845
|
+
clearcoat.clearcoatNormalTexture.scale
|
|
1846
|
+
);
|
|
1847
|
+
}
|
|
1848
|
+
const validateUnit = (extension, field, value) => {
|
|
1849
|
+
if (value === void 0) return void 0;
|
|
1850
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
1851
|
+
return physicalInvalid(extension, field, "non-finite", value);
|
|
1852
|
+
if (value < 0 || value > 1) return physicalInvalid(extension, field, "range", value);
|
|
1853
|
+
return void 0;
|
|
1854
|
+
};
|
|
1855
|
+
const validateFinite = (extension, field, value, minimum) => {
|
|
1856
|
+
if (value === void 0) return void 0;
|
|
1857
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
1858
|
+
return physicalInvalid(extension, field, "non-finite", value);
|
|
1859
|
+
if (minimum !== void 0 && value < minimum)
|
|
1860
|
+
return physicalInvalid(extension, field, "range", value);
|
|
1861
|
+
return void 0;
|
|
1862
|
+
};
|
|
1863
|
+
const validateColor = (extension, field, value) => {
|
|
1864
|
+
if (value === void 0) return void 0;
|
|
1865
|
+
if (value.length < 3 || value.slice(0, 3).some((channel) => typeof channel !== "number" || !Number.isFinite(channel))) {
|
|
1866
|
+
return physicalInvalid(extension, field, "type", value);
|
|
1867
|
+
}
|
|
1868
|
+
if (value.slice(0, 3).some((channel) => channel < 0 || channel > 1)) {
|
|
1869
|
+
return physicalInvalid(extension, field, "range", value);
|
|
1870
|
+
}
|
|
1871
|
+
return void 0;
|
|
1872
|
+
};
|
|
1873
|
+
const physicalChecks = [
|
|
1874
|
+
validateUnit("KHR_materials_anisotropy", "anisotropyStrength", anisotropy?.anisotropyStrength),
|
|
1875
|
+
validateFinite(
|
|
1876
|
+
"KHR_materials_anisotropy",
|
|
1877
|
+
"anisotropyRotation",
|
|
1878
|
+
anisotropy?.anisotropyRotation
|
|
1879
|
+
),
|
|
1880
|
+
validateUnit("KHR_materials_sheen", "sheenRoughnessFactor", sheen?.sheenRoughnessFactor),
|
|
1881
|
+
validateColor("KHR_materials_sheen", "sheenColorFactor", sheen?.sheenColorFactor),
|
|
1882
|
+
validateUnit("KHR_materials_iridescence", "iridescenceFactor", iridescence?.iridescenceFactor),
|
|
1883
|
+
validateFinite("KHR_materials_iridescence", "iridescenceIor", iridescence?.iridescenceIor, 1),
|
|
1884
|
+
validateFinite(
|
|
1885
|
+
"KHR_materials_iridescence",
|
|
1886
|
+
"iridescenceThicknessMinimum",
|
|
1887
|
+
iridescence?.iridescenceThicknessMinimum,
|
|
1888
|
+
0
|
|
1889
|
+
),
|
|
1890
|
+
validateFinite(
|
|
1891
|
+
"KHR_materials_iridescence",
|
|
1892
|
+
"iridescenceThicknessMaximum",
|
|
1893
|
+
iridescence?.iridescenceThicknessMaximum,
|
|
1894
|
+
0
|
|
1895
|
+
),
|
|
1896
|
+
validateUnit("KHR_materials_specular", "specularFactor", specular?.specularFactor),
|
|
1897
|
+
validateColor("KHR_materials_specular", "specularColorFactor", specular?.specularColorFactor)
|
|
1898
|
+
];
|
|
1899
|
+
for (const check of physicalChecks) if (check !== void 0) return check;
|
|
1530
1900
|
return ok({
|
|
1531
1901
|
...matJson.name === void 0 ? {} : { name: matJson.name },
|
|
1532
1902
|
baseColorFactor: baseColor4,
|
|
@@ -1563,6 +1933,65 @@ function parseMaterial(matJson, textures) {
|
|
|
1563
1933
|
...attenuationColor === void 0 ? {} : { attenuationColor },
|
|
1564
1934
|
...finiteOrUndefined(volume.attenuationDistance) === void 0 ? {} : { attenuationDistance: volume.attenuationDistance }
|
|
1565
1935
|
},
|
|
1936
|
+
...clearcoat === void 0 ? {} : {
|
|
1937
|
+
clearcoatFactor: clearcoat.clearcoatFactor ?? 0,
|
|
1938
|
+
clearcoatChannel: 0,
|
|
1939
|
+
clearcoatRoughnessFactor: clearcoat.clearcoatRoughnessFactor ?? 0,
|
|
1940
|
+
clearcoatRoughnessChannel: 1,
|
|
1941
|
+
clearcoatNormalChannel: [0, 1],
|
|
1942
|
+
...clearcoat.clearcoatTexture === void 0 ? {} : { clearcoatTexture: parseTextureInfo(clearcoat.clearcoatTexture, textures) },
|
|
1943
|
+
...clearcoat.clearcoatRoughnessTexture === void 0 ? {} : {
|
|
1944
|
+
clearcoatRoughnessTexture: parseTextureInfo(
|
|
1945
|
+
clearcoat.clearcoatRoughnessTexture,
|
|
1946
|
+
textures
|
|
1947
|
+
)
|
|
1948
|
+
},
|
|
1949
|
+
...clearcoat.clearcoatNormalTexture === void 0 ? {} : {
|
|
1950
|
+
clearcoatNormalTexture: {
|
|
1951
|
+
...parseTextureInfo(clearcoat.clearcoatNormalTexture, textures),
|
|
1952
|
+
...clearcoat.clearcoatNormalTexture.scale === void 0 ? {} : { scale: clearcoat.clearcoatNormalTexture.scale }
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
},
|
|
1956
|
+
...anisotropy === void 0 ? {} : {
|
|
1957
|
+
anisotropyStrength: anisotropy.anisotropyStrength ?? 0,
|
|
1958
|
+
anisotropyRotation: anisotropy.anisotropyRotation ?? 0,
|
|
1959
|
+
anisotropyChannel: [0, 1, 2],
|
|
1960
|
+
...anisotropy.anisotropyTexture === void 0 ? {} : { anisotropyTexture: parseTextureInfo(anisotropy.anisotropyTexture, textures) }
|
|
1961
|
+
},
|
|
1962
|
+
...sheen === void 0 ? {} : {
|
|
1963
|
+
sheenColorFactor: tuple3(sheen.sheenColorFactor) ?? [0, 0, 0],
|
|
1964
|
+
sheenColorChannel: [0, 1, 2],
|
|
1965
|
+
sheenRoughnessFactor: sheen.sheenRoughnessFactor ?? 0,
|
|
1966
|
+
sheenRoughnessChannel: 3,
|
|
1967
|
+
...sheen.sheenColorTexture === void 0 ? {} : { sheenColorTexture: parseTextureInfo(sheen.sheenColorTexture, textures) },
|
|
1968
|
+
...sheen.sheenRoughnessTexture === void 0 ? {} : { sheenRoughnessTexture: parseTextureInfo(sheen.sheenRoughnessTexture, textures) }
|
|
1969
|
+
},
|
|
1970
|
+
...iridescence === void 0 ? {} : {
|
|
1971
|
+
iridescenceFactor: iridescence.iridescenceFactor ?? 0,
|
|
1972
|
+
iridescenceIor: iridescence.iridescenceIor ?? 1.3,
|
|
1973
|
+
iridescenceThicknessMinimum: iridescence.iridescenceThicknessMinimum ?? 100,
|
|
1974
|
+
iridescenceThicknessMaximum: iridescence.iridescenceThicknessMaximum ?? 400,
|
|
1975
|
+
iridescenceChannel: 0,
|
|
1976
|
+
iridescenceThicknessChannel: 1,
|
|
1977
|
+
...iridescence.iridescenceTexture === void 0 ? {} : { iridescenceTexture: parseTextureInfo(iridescence.iridescenceTexture, textures) },
|
|
1978
|
+
...iridescence.iridescenceThicknessTexture === void 0 ? {} : {
|
|
1979
|
+
iridescenceThicknessTexture: parseTextureInfo(
|
|
1980
|
+
iridescence.iridescenceThicknessTexture,
|
|
1981
|
+
textures
|
|
1982
|
+
)
|
|
1983
|
+
}
|
|
1984
|
+
},
|
|
1985
|
+
...specular === void 0 ? {} : {
|
|
1986
|
+
specularFactor: specular.specularFactor ?? 1,
|
|
1987
|
+
specularChannel: 3,
|
|
1988
|
+
specularColorFactor: tuple3(specular.specularColorFactor) ?? [1, 1, 1],
|
|
1989
|
+
specularColorChannel: [0, 1, 2],
|
|
1990
|
+
...specular.specularTexture === void 0 ? {} : { specularTexture: parseTextureInfo(specular.specularTexture, textures) },
|
|
1991
|
+
...specular.specularColorTexture === void 0 ? {} : {
|
|
1992
|
+
specularColorTexture: parseTextureInfo(specular.specularColorTexture, textures)
|
|
1993
|
+
}
|
|
1994
|
+
},
|
|
1566
1995
|
...alphaMode === void 0 ? {} : { alphaMode },
|
|
1567
1996
|
...alphaCutoff === void 0 ? {} : { alphaCutoff },
|
|
1568
1997
|
...matJson.doubleSided === true ? { doubleSided: true } : {},
|
|
@@ -2336,6 +2765,8 @@ async function parseGltfWithBin(json, ctx) {
|
|
|
2336
2765
|
const extResult = checkExtensions(json);
|
|
2337
2766
|
if (!extResult.ok) return err(extResult.error);
|
|
2338
2767
|
const unsupportedExtensions = extResult.value.unsupportedUsed;
|
|
2768
|
+
const lodResult = parseGltfLodExtension(json);
|
|
2769
|
+
if (!lodResult.ok) return err(lodResult.error);
|
|
2339
2770
|
const lightsResult = parsePunctualLights(json, ctx.filePath);
|
|
2340
2771
|
if (!lightsResult.ok) return err(lightsResult.error);
|
|
2341
2772
|
const meshesJson = json.meshes ?? [];
|
|
@@ -2876,7 +3307,8 @@ async function parseGltfWithBin(json, ctx) {
|
|
|
2876
3307
|
matrixTrsCoexistNodes: diagnostics.matrixTrsCoexistNodes
|
|
2877
3308
|
},
|
|
2878
3309
|
meshPrimitiveCount,
|
|
2879
|
-
lights: lightsResult.value
|
|
3310
|
+
lights: lightsResult.value,
|
|
3311
|
+
...lodResult.value.lodNodeIds.length === 0 ? {} : { lod: lodResult.value }
|
|
2880
3312
|
});
|
|
2881
3313
|
}
|
|
2882
3314
|
async function parseGltfForImporter(json, externalLoader, filePath, options = {}) {
|
|
@@ -3015,6 +3447,74 @@ function toAssetPack(doc, existingMeta, source) {
|
|
|
3015
3447
|
const sourceOverrides = {
|
|
3016
3448
|
...existingMeta?.sourceOverrides ?? {}
|
|
3017
3449
|
};
|
|
3450
|
+
const lodGroups = doc.lod === void 0 ? [] : doc.lod.groups.length > 0 ? doc.lod.groups : [doc.lod];
|
|
3451
|
+
for (const lodGroup of lodGroups) {
|
|
3452
|
+
if (lodGroup.lodNodeIds.length === 0) continue;
|
|
3453
|
+
const rootMeshIndex = doc.nodes[lodGroup.rootNode]?.meshIndex;
|
|
3454
|
+
const referencedMeshIndices = [
|
|
3455
|
+
rootMeshIndex,
|
|
3456
|
+
...lodGroup.lodNodeIds.map((nodeIndex) => doc.nodes[nodeIndex]?.meshIndex)
|
|
3457
|
+
];
|
|
3458
|
+
if (referencedMeshIndices.some(
|
|
3459
|
+
(meshIndex) => !Number.isInteger(meshIndex) || meshIndex === null || meshIndex === void 0 || !seenMeshIndices.has(meshIndex)
|
|
3460
|
+
)) {
|
|
3461
|
+
return err(
|
|
3462
|
+
gltfErr("gltf-lod-invalid", {
|
|
3463
|
+
rootNode: lodGroup.rootNode,
|
|
3464
|
+
ids: lodGroup.lodNodeIds,
|
|
3465
|
+
reason: "missing-node"
|
|
3466
|
+
})
|
|
3467
|
+
);
|
|
3468
|
+
}
|
|
3469
|
+
const rootOutput = rootMeshIndex === void 0 || rootMeshIndex === null ? void 0 : subAssetByKindIndex.get(`mesh:${rootMeshIndex}`);
|
|
3470
|
+
const levels = lodGroup.lodNodeIds.map((nodeIndex, index) => {
|
|
3471
|
+
const meshIndex = doc.nodes[nodeIndex]?.meshIndex;
|
|
3472
|
+
const output = meshIndex === void 0 || meshIndex === null ? void 0 : subAssetByKindIndex.get(`mesh:${meshIndex}`);
|
|
3473
|
+
return {
|
|
3474
|
+
sourceKey: output?.sourceKey ?? `mesh:${meshIndex ?? nodeIndex}`,
|
|
3475
|
+
meshGuid: output?.guid ?? "",
|
|
3476
|
+
...lodGroup.screenCoverages[index] === void 0 ? {} : { screenCoverage: lodGroup.screenCoverages[index] }
|
|
3477
|
+
};
|
|
3478
|
+
});
|
|
3479
|
+
if (rootOutput?.sourceKey !== void 0 && levels.every((level) => level.meshGuid !== "")) {
|
|
3480
|
+
const previousLodMeta = (() => {
|
|
3481
|
+
const raw = existingMeta?.sourceOverrides?.[rootOutput.sourceKey]?.lods;
|
|
3482
|
+
if (!Array.isArray(raw)) return void 0;
|
|
3483
|
+
return raw.flatMap((entry) => {
|
|
3484
|
+
if (entry === null || typeof entry !== "object") return [];
|
|
3485
|
+
const value = entry;
|
|
3486
|
+
return typeof value.sourceKey === "string" && typeof value.meshGuid === "string" ? [
|
|
3487
|
+
{
|
|
3488
|
+
sourceKey: value.sourceKey,
|
|
3489
|
+
guid: value.meshGuid,
|
|
3490
|
+
...typeof value.screenCoverage === "number" ? { screenCoverage: value.screenCoverage } : {}
|
|
3491
|
+
}
|
|
3492
|
+
] : [];
|
|
3493
|
+
});
|
|
3494
|
+
})();
|
|
3495
|
+
const projected = projectGltfLodMeta({
|
|
3496
|
+
rootSourceKey: rootOutput.sourceKey,
|
|
3497
|
+
levels: levels.map((level) => ({
|
|
3498
|
+
sourceKey: level.sourceKey,
|
|
3499
|
+
guid: level.meshGuid,
|
|
3500
|
+
...level.screenCoverage === void 0 ? {} : { screenCoverage: level.screenCoverage }
|
|
3501
|
+
})),
|
|
3502
|
+
...previousLodMeta === void 0 ? {} : { previous: previousLodMeta }
|
|
3503
|
+
});
|
|
3504
|
+
if (!projected.ok)
|
|
3505
|
+
return err(
|
|
3506
|
+
gltfErr("gltf-lod-invalid", {
|
|
3507
|
+
rootNode: lodGroup.rootNode,
|
|
3508
|
+
ids: lodGroup.lodNodeIds,
|
|
3509
|
+
reason: "coverage"
|
|
3510
|
+
})
|
|
3511
|
+
);
|
|
3512
|
+
sourceOverrides[rootOutput.sourceKey] = {
|
|
3513
|
+
...sourceOverrides[rootOutput.sourceKey] ?? {},
|
|
3514
|
+
lods: projected.value.lods
|
|
3515
|
+
};
|
|
3516
|
+
}
|
|
3517
|
+
}
|
|
3018
3518
|
for (const meshIndex of seenMeshIndices) {
|
|
3019
3519
|
const meshOutput = subAssetByKindIndex.get(`mesh:${meshIndex}`);
|
|
3020
3520
|
if (meshOutput?.sourceKey === void 0) continue;
|
|
@@ -3341,6 +3841,16 @@ function materialRefsForPack(mat, doc, textureGuidByIndex, samplerGuidByIndex =
|
|
|
3341
3841
|
pushRefsForSlot(mat.emissiveTexture, "emissiveTexture");
|
|
3342
3842
|
pushRefsForSlot(mat.transmissionTexture, "transmissionTexture");
|
|
3343
3843
|
pushRefsForSlot(mat.thicknessTexture, "thicknessTexture");
|
|
3844
|
+
pushRefsForSlot(mat.clearcoatTexture, "clearcoatTexture");
|
|
3845
|
+
pushRefsForSlot(mat.clearcoatRoughnessTexture, "clearcoatRoughnessTexture");
|
|
3846
|
+
pushRefsForSlot(mat.clearcoatNormalTexture, "clearcoatNormalTexture");
|
|
3847
|
+
pushRefsForSlot(mat.anisotropyTexture, "anisotropyTexture");
|
|
3848
|
+
pushRefsForSlot(mat.sheenColorTexture, "sheenColorTexture");
|
|
3849
|
+
pushRefsForSlot(mat.sheenRoughnessTexture, "sheenRoughnessTexture");
|
|
3850
|
+
pushRefsForSlot(mat.iridescenceTexture, "iridescenceTexture");
|
|
3851
|
+
pushRefsForSlot(mat.iridescenceThicknessTexture, "iridescenceThicknessTexture");
|
|
3852
|
+
pushRefsForSlot(mat.specularTexture, "specularTexture");
|
|
3853
|
+
pushRefsForSlot(mat.specularColorTexture, "specularColorTexture");
|
|
3344
3854
|
return refs;
|
|
3345
3855
|
}
|
|
3346
3856
|
function availableUvSets(mesh) {
|
|
@@ -3361,7 +3871,17 @@ function rewriteMaterialAssetRefs(matAsset, mat, doc, maps) {
|
|
|
3361
3871
|
["occlusionTexture", mat.occlusionTexture],
|
|
3362
3872
|
["emissiveTexture", mat.emissiveTexture],
|
|
3363
3873
|
["transmissionTexture", mat.transmissionTexture],
|
|
3364
|
-
["thicknessTexture", mat.thicknessTexture]
|
|
3874
|
+
["thicknessTexture", mat.thicknessTexture],
|
|
3875
|
+
["clearcoatTexture", mat.clearcoatTexture],
|
|
3876
|
+
["clearcoatRoughnessTexture", mat.clearcoatRoughnessTexture],
|
|
3877
|
+
["clearcoatNormalTexture", mat.clearcoatNormalTexture],
|
|
3878
|
+
["anisotropyTexture", mat.anisotropyTexture],
|
|
3879
|
+
["sheenColorTexture", mat.sheenColorTexture],
|
|
3880
|
+
["sheenRoughnessTexture", mat.sheenRoughnessTexture],
|
|
3881
|
+
["iridescenceTexture", mat.iridescenceTexture],
|
|
3882
|
+
["iridescenceThicknessTexture", mat.iridescenceThicknessTexture],
|
|
3883
|
+
["specularTexture", mat.specularTexture],
|
|
3884
|
+
["specularColorTexture", mat.specularColorTexture]
|
|
3365
3885
|
];
|
|
3366
3886
|
let cursor = 0;
|
|
3367
3887
|
for (const [slot, rawBinding] of slots) {
|
|
@@ -3456,7 +3976,55 @@ async function importGltf(ctx, meshopt) {
|
|
|
3456
3976
|
})
|
|
3457
3977
|
};
|
|
3458
3978
|
}
|
|
3459
|
-
const
|
|
3979
|
+
const stabilizedMesh = stabilizeMeshMaterialSlots(
|
|
3980
|
+
bridged.value,
|
|
3981
|
+
ctx,
|
|
3982
|
+
sub.guid,
|
|
3983
|
+
sub.sourceKey
|
|
3984
|
+
);
|
|
3985
|
+
const lodGroup = doc.lod?.groups?.find(
|
|
3986
|
+
(group) => doc.nodes[group.rootNode]?.meshIndex === sub.sourceIndex
|
|
3987
|
+
) ?? (doc.lod?.rootNode !== void 0 && doc.nodes[doc.lod.rootNode]?.meshIndex === sub.sourceIndex ? doc.lod : void 0);
|
|
3988
|
+
const rootMeshIndex = lodGroup === void 0 ? void 0 : sub.sourceIndex;
|
|
3989
|
+
if (lodGroup !== void 0) {
|
|
3990
|
+
const referencedMeshIndices = [
|
|
3991
|
+
sub.sourceIndex,
|
|
3992
|
+
...lodGroup.lodNodeIds.map((nodeIndex) => doc.nodes[nodeIndex]?.meshIndex)
|
|
3993
|
+
];
|
|
3994
|
+
const missingMeshIndex = referencedMeshIndices.find(
|
|
3995
|
+
(meshIndex) => !Number.isInteger(meshIndex) || meshIndex === null || meshIndex === void 0 || maps.meshGuidByIndex.get(meshIndex) === void 0
|
|
3996
|
+
);
|
|
3997
|
+
if (missingMeshIndex !== void 0) {
|
|
3998
|
+
return {
|
|
3999
|
+
ok: false,
|
|
4000
|
+
error: new ImportError({
|
|
4001
|
+
code: "import-internal-error",
|
|
4002
|
+
expected: "every MSFT_lod node to resolve a cooked mesh sub-asset",
|
|
4003
|
+
actual: String(missingMeshIndex),
|
|
4004
|
+
hint: "repair the referenced glTF mesh primitives and re-run the importer",
|
|
4005
|
+
detail: {
|
|
4006
|
+
reason: `MSFT_lod group for mesh ${sub.sourceIndex} references an unprojected mesh`
|
|
4007
|
+
}
|
|
4008
|
+
})
|
|
4009
|
+
};
|
|
4010
|
+
}
|
|
4011
|
+
}
|
|
4012
|
+
const authoredLods = sub.sourceKey === void 0 ? void 0 : ctx.sourceOverrides?.[sub.sourceKey]?.lods;
|
|
4013
|
+
const lodEntries = Array.isArray(authoredLods) ? authoredLods : [];
|
|
4014
|
+
const lodLevels = rootMeshIndex === sub.sourceIndex && lodGroup !== void 0 ? lodGroup.lodNodeIds.flatMap((nodeIndex, index) => {
|
|
4015
|
+
const meshIndex = doc.nodes[nodeIndex]?.meshIndex;
|
|
4016
|
+
const guid = meshIndex === void 0 || meshIndex === null ? void 0 : maps.meshGuidByIndex.get(meshIndex);
|
|
4017
|
+
if (guid === void 0) return [];
|
|
4018
|
+
const authored = lodEntries[index];
|
|
4019
|
+
const authoredCoverage = authored !== null && typeof authored === "object" ? authored.screenCoverage : void 0;
|
|
4020
|
+
const coverage = typeof authoredCoverage === "number" ? authoredCoverage : lodGroup.screenCoverages[index] ?? deriveDefaultLodScreenCoverages(lodGroup.lodNodeIds.length + 1)[index];
|
|
4021
|
+
const parsed2 = AssetGuid.parse(guid);
|
|
4022
|
+
return parsed2.ok && coverage !== void 0 ? [{ mesh: parsed2.value, screenCoverage: coverage, guid }] : [];
|
|
4023
|
+
}) : [];
|
|
4024
|
+
const meshPayload = lodLevels.length === 0 ? stabilizedMesh : {
|
|
4025
|
+
...stabilizedMesh,
|
|
4026
|
+
lods: lodLevels.map(({ mesh, screenCoverage }) => ({ mesh, screenCoverage }))
|
|
4027
|
+
};
|
|
3460
4028
|
const materialRefs = [];
|
|
3461
4029
|
const seenMaterialGuids = /* @__PURE__ */ new Set();
|
|
3462
4030
|
for (let slotIndex = 0; slotIndex < meshPayload.materialSlots.length; slotIndex++) {
|
|
@@ -3470,6 +4038,15 @@ async function importGltf(ctx, meshopt) {
|
|
|
3470
4038
|
});
|
|
3471
4039
|
}
|
|
3472
4040
|
}
|
|
4041
|
+
for (const [lodIndex, level] of lodLevels.entries()) {
|
|
4042
|
+
if (!seenMaterialGuids.has(level.guid.toLowerCase())) {
|
|
4043
|
+
seenMaterialGuids.add(level.guid.toLowerCase());
|
|
4044
|
+
materialRefs.push({
|
|
4045
|
+
guid: level.guid,
|
|
4046
|
+
sourceField: { fieldName: "lods", arrayIndex: lodIndex }
|
|
4047
|
+
});
|
|
4048
|
+
}
|
|
4049
|
+
}
|
|
3473
4050
|
out.push({
|
|
3474
4051
|
guid: sub.guid,
|
|
3475
4052
|
kind: "mesh",
|
|
@@ -3521,6 +4098,10 @@ async function importGltf(ctx, meshopt) {
|
|
|
3521
4098
|
if (!uvResult.ok) {
|
|
3522
4099
|
throw Object.assign(new Error(uvResult.error.message), uvResult.error);
|
|
3523
4100
|
}
|
|
4101
|
+
const tangentResult = validateMaterialTangentInputs(mat, meshIr);
|
|
4102
|
+
if (!tangentResult.ok) {
|
|
4103
|
+
throw Object.assign(new Error(tangentResult.error.message), tangentResult.error);
|
|
4104
|
+
}
|
|
3524
4105
|
}
|
|
3525
4106
|
const matAsset = toMaterialAsset(mat, {
|
|
3526
4107
|
textureHandles: maps.textureHandles,
|
|
@@ -3773,6 +4354,6 @@ function serializeMetaJson(meta) {
|
|
|
3773
4354
|
`;
|
|
3774
4355
|
}
|
|
3775
4356
|
|
|
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 };
|
|
4357
|
+
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, validateMaterialTangentInputs, validateMaterialUvSets };
|
|
3777
4358
|
//# sourceMappingURL=index.mjs.map
|
|
3778
4359
|
//# sourceMappingURL=index.mjs.map
|