@forgeax/engine-gltf 0.1.23 → 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.
Files changed (43) hide show
  1. package/README.md +56 -3
  2. package/dist/__tests__/physical-clearcoat.unit.test.d.ts +2 -0
  3. package/dist/__tests__/physical-clearcoat.unit.test.d.ts.map +1 -0
  4. package/dist/__tests__/physical-material-import.integration.test.d.ts +2 -0
  5. package/dist/__tests__/physical-material-import.integration.test.d.ts.map +1 -0
  6. package/dist/bridge.d.ts +1 -0
  7. package/dist/bridge.d.ts.map +1 -1
  8. package/dist/check-extensions.d.ts +1 -1
  9. package/dist/check-extensions.d.ts.map +1 -1
  10. package/dist/cli-gltf.mjs +181 -8
  11. package/dist/cli-gltf.mjs.map +1 -1
  12. package/dist/errors.d.ts +7 -0
  13. package/dist/errors.d.ts.map +1 -1
  14. package/dist/gltf-importer.d.ts.map +1 -1
  15. package/dist/image-color-space.d.ts +12 -0
  16. package/dist/image-color-space.d.ts.map +1 -1
  17. package/dist/importer-entry.mjs +373 -24
  18. package/dist/importer-entry.mjs.map +1 -1
  19. package/dist/index.d.ts +2 -2
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.mjs +374 -25
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/material/parse-material.d.ts +66 -0
  24. package/dist/material/parse-material.d.ts.map +1 -1
  25. package/dist/node-file-entry.mjs +181 -8
  26. package/dist/node-file-entry.mjs.map +1 -1
  27. package/package.json +12 -12
  28. package/src/__tests__/bridge-material-values.unit.test.ts +84 -1
  29. package/src/__tests__/bridge-skin-stride.unit.test.ts +22 -0
  30. package/src/__tests__/gltf-error-derived-views.test-d.ts +4 -0
  31. package/src/__tests__/gltf-error-migration.test.ts +3 -3
  32. package/src/__tests__/gltf.unit.test.ts +52 -6
  33. package/src/__tests__/material-pack-refs.integration.test.ts +53 -0
  34. package/src/__tests__/parse-gltf.unit.test.ts +29 -1
  35. package/src/__tests__/physical-clearcoat.unit.test.ts +110 -0
  36. package/src/__tests__/physical-material-import.integration.test.ts +239 -0
  37. package/src/bridge.ts +258 -13
  38. package/src/check-extensions.ts +16 -10
  39. package/src/errors.ts +21 -2
  40. package/src/gltf-importer.ts +35 -0
  41. package/src/image-color-space.ts +29 -4
  42. package/src/index.ts +8 -1
  43. package/src/material/parse-material.ts +307 -0
package/dist/index.d.ts CHANGED
@@ -4,10 +4,10 @@ export { COMPONENT_TYPE, decodeAccessor } from './accessor/decode-accessor.js';
4
4
  export type { DecodeColorAccessorInput } from './accessor/decode-color.js';
5
5
  export { decodeColorAccessor } from './accessor/decode-color.js';
6
6
  export type { GltfBridgeContext, MaterialBridgeContext } from './bridge.js';
7
- export { gltfDocToSceneAsset, meshIrToMeshAsset, toMaterialAsset } from './bridge.js';
7
+ export { gltfDocToSceneAsset, meshIrToMeshAsset, toMaterialAsset, validateMaterialTangentInputs, validateMaterialUvSets, } from './bridge.js';
8
8
  export type { ExtensionsCheckResult, GltfExtensionsJson } from './check-extensions.js';
9
9
  export { checkExtensions, EXTENSION_ALLOWLIST } from './check-extensions.js';
10
- export type { GltfAccessorTypeMismatchDetail, GltfAnimationCubicsplineUnsupportedDetail, GltfBufferOutOfBoundsDetail, GltfColorAccessorMalformedDetail, GltfColorAccessorUnsupportedDetail, GltfError, GltfErrorCode, GltfErrorDetail, GltfExtensionUnsupportedDetail, GltfImageExtractFailedDetail, GltfImageMimeUnsupportedDetail, GltfInstancingCountMismatchDetail, GltfMalformedHeaderDetail, GltfMaterialTransmissionInvalidDetail, GltfMetaMissingDetail, GltfMorphInvalidDetail, GltfMorphUnsupportedDetail, GltfSkinAttrAsymmetricDetail, GltfSkinJointCountExceededDetail, GltfSkinJointNameMissingDetail, GltfTextureLoadFailedDetail, GltfVersionUnsupportedDetail, Result, } from './errors.js';
10
+ export type { GltfAccessorTypeMismatchDetail, GltfAnimationCubicsplineUnsupportedDetail, GltfBufferOutOfBoundsDetail, GltfColorAccessorMalformedDetail, GltfColorAccessorUnsupportedDetail, GltfError, GltfErrorCode, GltfErrorDetail, GltfExtensionUnsupportedDetail, GltfImageExtractFailedDetail, GltfImageMimeUnsupportedDetail, GltfInstancingCountMismatchDetail, GltfMalformedHeaderDetail, GltfMaterialPhysicalInvalidDetail, GltfMaterialTransmissionInvalidDetail, GltfMetaMissingDetail, GltfMorphInvalidDetail, GltfMorphUnsupportedDetail, GltfSkinAttrAsymmetricDetail, GltfSkinJointCountExceededDetail, GltfSkinJointNameMissingDetail, GltfTextureLoadFailedDetail, GltfVersionUnsupportedDetail, Result, } from './errors.js';
11
11
  export { err, GLTF_ERROR_HINTS, gltfErr, ok } from './errors.js';
12
12
  export { gltfImporter } from './gltf-importer.js';
13
13
  export { type GltfLodRelation, parseGltfLodExtension } from './lod/parse-lod.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { err, ok, reconcileMeshMaterialSlotTopology, MESH_MATERIAL_SLOT_SOURCE_OVERRIDE_PAYLOAD_SCHEMA, ImportError, IMPORT_ERROR_HINTS, toShared, resolveMeshMaterialSlotDefaultGuid, createMaterialError } from '@forgeax/engine-types';
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
6
  import { deriveDefaultLodScreenCoverages, reconcileMeshLodMeta } from '@forgeax/engine-import';
@@ -24,8 +24,8 @@ var gltfErrorPolicy = {
24
24
  hint: "rebuild .gltf with valid bufferViews; check accessor index; ensure accessor.byteOffset + EFFECTIVE_STRIDE * (count - 1) + element_size <= bufferView.byteLength"
25
25
  },
26
26
  "gltf-extension-unsupported": {
27
- expected: "extension listed in v1 allowlist (see EXTENSION_ALLOWLIST in @forgeax/engine-gltf)",
28
- hint: "see feat-future-gltf-extensions-allowlist; remove this extension or wait for the allowlist to expand"
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
29
  },
30
30
  "gltf-lod-invalid": {
31
31
  expected: "MSFT_lod ids to reference unique existing node indices with valid coverage",
@@ -35,6 +35,10 @@ var gltfErrorPolicy = {
35
35
  expected: "KHR transmission, IOR, and volume values are finite and within their glTF ranges",
36
36
  hint: "repair the named glTF material extension value and re-import the source"
37
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
+ },
38
42
  "gltf-accessor-type-mismatch": {
39
43
  expected: "dense fixed-stride accessor with supported componentType",
40
44
  hint: "sparse: see feat-future-gltf-sparse-accessor; morph: see feat-future-gltf-morph; interleaved: see feat-future-gltf-mesh-multi-section"
@@ -491,6 +495,16 @@ function meshIrToMeshAsset(prims, materials = {}) {
491
495
  const materialSlot = slotFor(mesh.materialIndex);
492
496
  const primVertexCount = mesh.positions.length / 3;
493
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
+ }
494
508
  if (mesh.colors0 !== void 0 && mesh.colors0.length !== primVertexCount * 4) {
495
509
  return err(
496
510
  gltfErr("gltf-mesh-bridge-invalid", {
@@ -522,12 +536,13 @@ function meshIrToMeshAsset(prims, materials = {}) {
522
536
  uvsCat[(vertexCursor + i) * 2 + 0] = mesh.texcoord0[t + 0];
523
537
  uvsCat[(vertexCursor + i) * 2 + 1] = mesh.texcoord0[t + 1];
524
538
  }
525
- if (mesh.tangents !== void 0) {
539
+ const sourceTangents = mesh.tangents ?? generatedTangents;
540
+ if (sourceTangents !== void 0) {
526
541
  const g = i * 4;
527
- tangentsCat[(vertexCursor + i) * 4 + 0] = mesh.tangents[g + 0];
528
- tangentsCat[(vertexCursor + i) * 4 + 1] = mesh.tangents[g + 1];
529
- tangentsCat[(vertexCursor + i) * 4 + 2] = mesh.tangents[g + 2];
530
- tangentsCat[(vertexCursor + i) * 4 + 3] = mesh.tangents[g + 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];
531
546
  } else {
532
547
  tangentsCat[(vertexCursor + i) * 4 + 0] = 1;
533
548
  tangentsCat[(vertexCursor + i) * 4 + 3] = 1;
@@ -862,6 +877,12 @@ function textureValue(info, slot, ctx) {
862
877
  return { ...value, normalScale: normal.scale };
863
878
  }
864
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
+ }
865
886
  if (slot === "occlusionTexture") {
866
887
  const occlusion = info;
867
888
  if (typeof occlusion === "object" && occlusion?.strength !== void 0) {
@@ -870,6 +891,47 @@ function textureValue(info, slot, ctx) {
870
891
  }
871
892
  return value;
872
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
+ }
873
935
  function validateMaterialUvSets(mat, primitive, availableSets) {
874
936
  const available = new Set(availableSets);
875
937
  const slots = [
@@ -879,7 +941,17 @@ function validateMaterialUvSets(mat, primitive, availableSets) {
879
941
  ["occlusionTexture", mat.occlusionTexture],
880
942
  ["emissiveTexture", mat.emissiveTexture],
881
943
  ["transmissionTexture", mat.transmissionTexture],
882
- ["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]
883
955
  ];
884
956
  for (const [slot, rawBinding] of slots) {
885
957
  const binding = textureInfo(rawBinding);
@@ -899,7 +971,44 @@ function validateMaterialUvSets(mat, primitive, availableSets) {
899
971
  }
900
972
  return ok(void 0);
901
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
+ }
902
1010
  function toMaterialAsset(mat, ctx) {
1011
+ const rootContract = standardRootParameterNames(mat);
903
1012
  const values = {
904
1013
  baseColor: mat.baseColorFactor,
905
1014
  metallic: mat.metallicFactor,
@@ -916,7 +1025,17 @@ function toMaterialAsset(mat, ctx) {
916
1025
  ["occlusionTexture", mat.occlusionTexture],
917
1026
  ["emissiveTexture", mat.emissiveTexture],
918
1027
  ["transmissionTexture", mat.transmissionTexture],
919
- ["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]
920
1039
  ];
921
1040
  for (const [slot, info] of textureSlots) {
922
1041
  const value = textureValue(info, slot, ctx);
@@ -925,11 +1044,35 @@ function toMaterialAsset(mat, ctx) {
925
1044
  if (mat.occlusionTexture !== void 0 && values.occlusionStrength === void 0) {
926
1045
  values.occlusionStrength = 1;
927
1046
  }
928
- if (mat.transmissionFactor !== void 0) values.transmission = mat.transmissionFactor;
929
- if (mat.ior !== void 0) values.ior = mat.ior;
930
- if (mat.thicknessFactor !== void 0) values.thickness = mat.thicknessFactor;
931
- if (mat.attenuationColor !== void 0) values.attenuationColor = mat.attenuationColor;
932
- if (mat.attenuationDistance !== void 0) values.attenuationDistance = mat.attenuationDistance;
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;
933
1076
  const module = ctx?.skinned === true ? "forgeax::pbr-skin" : "forgeax::default-standard-pbr";
934
1077
  const isMask = mat.alphaMode === "MASK";
935
1078
  const alphaCutoff = isMask ? mat.alphaCutoff ?? 0.5 : void 0;
@@ -962,8 +1105,9 @@ function toMaterialAsset(mat, ctx) {
962
1105
  return {
963
1106
  kind: "material",
964
1107
  colorSpace: "linear",
965
- ...ctx?.standardRootGuid === void 0 ? {} : { parent: ctx.standardRootGuid },
1108
+ ...!rootContract.extended && ctx?.standardRootGuid !== void 0 ? { parent: ctx.standardRootGuid } : {},
966
1109
  passes: [pass],
1110
+ ...ctx?.standardRootGuid !== void 0 && !rootContract.extended ? {} : { parameters: standardMaterialParameters(rootContract.names) },
967
1111
  values
968
1112
  };
969
1113
  }
@@ -972,16 +1116,21 @@ function toMaterialAsset(mat, ctx) {
972
1116
  var EXTENSION_ALLOWLIST = [
973
1117
  "EXT_mesh_gpu_instancing",
974
1118
  "EXT_meshopt_compression",
975
- "MSFT_lod",
976
- "MSFT_screencoverage"
977
- ];
978
- var SUPPORTED_EXTENSIONS = [
979
- ...EXTENSION_ALLOWLIST,
980
1119
  "KHR_lights_punctual",
981
1120
  "KHR_texture_transform",
982
1121
  "KHR_materials_transmission",
983
1122
  "KHR_materials_ior",
984
- "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"
985
1134
  ];
986
1135
  function checkExtensions(json) {
987
1136
  const required = json.extensionsRequired ?? [];
@@ -1419,9 +1568,21 @@ function deriveTextureColorSpace(input) {
1419
1568
  for (const mat of input.materials) {
1420
1569
  record(imageOfTexture(mat.baseColorTexture), "srgb");
1421
1570
  record(imageOfTexture(mat.emissiveTexture), "srgb");
1571
+ record(imageOfTexture(mat.sheenColorTexture), "srgb");
1572
+ record(imageOfTexture(mat.specularColorTexture), "srgb");
1422
1573
  record(imageOfTexture(mat.metallicRoughnessTexture), "linear");
1423
1574
  record(imageOfTexture(mat.normalTexture), "linear");
1424
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");
1425
1586
  }
1426
1587
  for (let i = 0; i < input.imageCount; i++) {
1427
1588
  if (!result.has(i)) {
@@ -1572,6 +1733,11 @@ function parseMaterial(matJson, textures) {
1572
1733
  const transmission = matJson.extensions?.KHR_materials_transmission;
1573
1734
  const ior = matJson.extensions?.KHR_materials_ior;
1574
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;
1575
1741
  const attenuationColor = tuple3(volume?.attenuationColor);
1576
1742
  const invalid = (extension, field, reason, actual) => err(
1577
1743
  gltfErr("gltf-material-transmission-invalid", {
@@ -1581,6 +1747,14 @@ function parseMaterial(matJson, textures) {
1581
1747
  ...actual === void 0 ? {} : { actual }
1582
1748
  })
1583
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
+ );
1584
1758
  if (transmission?.transmissionFactor !== void 0 && (typeof transmission.transmissionFactor !== "number" || !Number.isFinite(transmission.transmissionFactor))) {
1585
1759
  return invalid(
1586
1760
  "KHR_materials_transmission",
@@ -1631,6 +1805,98 @@ function parseMaterial(matJson, textures) {
1631
1805
  if (alphaMode === "BLEND" && (transmission?.transmissionFactor ?? 0) > 0) {
1632
1806
  return invalid("KHR_materials_transmission", "alphaMode", "blend", alphaMode);
1633
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;
1634
1900
  return ok({
1635
1901
  ...matJson.name === void 0 ? {} : { name: matJson.name },
1636
1902
  baseColorFactor: baseColor4,
@@ -1667,6 +1933,65 @@ function parseMaterial(matJson, textures) {
1667
1933
  ...attenuationColor === void 0 ? {} : { attenuationColor },
1668
1934
  ...finiteOrUndefined(volume.attenuationDistance) === void 0 ? {} : { attenuationDistance: volume.attenuationDistance }
1669
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
+ },
1670
1995
  ...alphaMode === void 0 ? {} : { alphaMode },
1671
1996
  ...alphaCutoff === void 0 ? {} : { alphaCutoff },
1672
1997
  ...matJson.doubleSided === true ? { doubleSided: true } : {},
@@ -3516,6 +3841,16 @@ function materialRefsForPack(mat, doc, textureGuidByIndex, samplerGuidByIndex =
3516
3841
  pushRefsForSlot(mat.emissiveTexture, "emissiveTexture");
3517
3842
  pushRefsForSlot(mat.transmissionTexture, "transmissionTexture");
3518
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");
3519
3854
  return refs;
3520
3855
  }
3521
3856
  function availableUvSets(mesh) {
@@ -3536,7 +3871,17 @@ function rewriteMaterialAssetRefs(matAsset, mat, doc, maps) {
3536
3871
  ["occlusionTexture", mat.occlusionTexture],
3537
3872
  ["emissiveTexture", mat.emissiveTexture],
3538
3873
  ["transmissionTexture", mat.transmissionTexture],
3539
- ["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]
3540
3885
  ];
3541
3886
  let cursor = 0;
3542
3887
  for (const [slot, rawBinding] of slots) {
@@ -3753,6 +4098,10 @@ async function importGltf(ctx, meshopt) {
3753
4098
  if (!uvResult.ok) {
3754
4099
  throw Object.assign(new Error(uvResult.error.message), uvResult.error);
3755
4100
  }
4101
+ const tangentResult = validateMaterialTangentInputs(mat, meshIr);
4102
+ if (!tangentResult.ok) {
4103
+ throw Object.assign(new Error(tangentResult.error.message), tangentResult.error);
4104
+ }
3756
4105
  }
3757
4106
  const matAsset = toMaterialAsset(mat, {
3758
4107
  textureHandles: maps.textureHandles,
@@ -4005,6 +4354,6 @@ function serializeMetaJson(meta) {
4005
4354
  `;
4006
4355
  }
4007
4356
 
4008
- export { COMPONENT_TYPE, EXTENSION_ALLOWLIST, GLTF_ERROR_HINTS, checkExtensions, decodeAccessor, decodeColorAccessor, decomposeNodeTransform, deriveGltfSourceKeys, gltfDocToSceneAsset, gltfErr, gltfImporter, meshIrToMeshAsset, parseGlb, parseGlbChunks, parseGltf, parseGltfHeader, parseGltfLodExtension, projectGltfLodMeta, reimportReuseMeta, serializeMetaJson, sourceKeyForGltfOutput, subAssetKey, toAssetPack, toMaterialAsset };
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 };
4009
4358
  //# sourceMappingURL=index.mjs.map
4010
4359
  //# sourceMappingURL=index.mjs.map