@forgeax/engine-gltf 0.1.23 → 0.1.25

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 +377 -26
  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 +378 -27
  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 +107 -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 +61 -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 +260 -15
  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;
@@ -959,11 +1102,14 @@ function toMaterialAsset(mat, ctx) {
959
1102
  } : {}
960
1103
  }
961
1104
  };
1105
+ const child = !rootContract.extended && ctx?.standardRootGuid !== void 0;
962
1106
  return {
963
1107
  kind: "material",
964
- colorSpace: "linear",
965
- ...ctx?.standardRootGuid === void 0 ? {} : { parent: ctx.standardRootGuid },
966
- passes: [pass],
1108
+ ...child ? { parent: ctx.standardRootGuid } : {
1109
+ colorSpace: "linear",
1110
+ passes: [pass],
1111
+ parameters: standardMaterialParameters(rootContract.names)
1112
+ },
967
1113
  values
968
1114
  };
969
1115
  }
@@ -972,16 +1118,21 @@ function toMaterialAsset(mat, ctx) {
972
1118
  var EXTENSION_ALLOWLIST = [
973
1119
  "EXT_mesh_gpu_instancing",
974
1120
  "EXT_meshopt_compression",
975
- "MSFT_lod",
976
- "MSFT_screencoverage"
977
- ];
978
- var SUPPORTED_EXTENSIONS = [
979
- ...EXTENSION_ALLOWLIST,
980
1121
  "KHR_lights_punctual",
981
1122
  "KHR_texture_transform",
982
1123
  "KHR_materials_transmission",
983
1124
  "KHR_materials_ior",
984
- "KHR_materials_volume"
1125
+ "KHR_materials_volume",
1126
+ "KHR_materials_clearcoat",
1127
+ "KHR_materials_anisotropy",
1128
+ "KHR_materials_sheen",
1129
+ "KHR_materials_iridescence",
1130
+ "KHR_materials_specular"
1131
+ ];
1132
+ var SUPPORTED_EXTENSIONS = [
1133
+ ...EXTENSION_ALLOWLIST,
1134
+ "MSFT_lod",
1135
+ "MSFT_screencoverage"
985
1136
  ];
986
1137
  function checkExtensions(json) {
987
1138
  const required = json.extensionsRequired ?? [];
@@ -1419,9 +1570,21 @@ function deriveTextureColorSpace(input) {
1419
1570
  for (const mat of input.materials) {
1420
1571
  record(imageOfTexture(mat.baseColorTexture), "srgb");
1421
1572
  record(imageOfTexture(mat.emissiveTexture), "srgb");
1573
+ record(imageOfTexture(mat.sheenColorTexture), "srgb");
1574
+ record(imageOfTexture(mat.specularColorTexture), "srgb");
1422
1575
  record(imageOfTexture(mat.metallicRoughnessTexture), "linear");
1423
1576
  record(imageOfTexture(mat.normalTexture), "linear");
1424
1577
  record(imageOfTexture(mat.occlusionTexture), "linear");
1578
+ record(imageOfTexture(mat.transmissionTexture), "linear");
1579
+ record(imageOfTexture(mat.thicknessTexture), "linear");
1580
+ record(imageOfTexture(mat.clearcoatTexture), "linear");
1581
+ record(imageOfTexture(mat.clearcoatRoughnessTexture), "linear");
1582
+ record(imageOfTexture(mat.clearcoatNormalTexture), "linear");
1583
+ record(imageOfTexture(mat.anisotropyTexture), "linear");
1584
+ record(imageOfTexture(mat.sheenRoughnessTexture), "linear");
1585
+ record(imageOfTexture(mat.iridescenceTexture), "linear");
1586
+ record(imageOfTexture(mat.iridescenceThicknessTexture), "linear");
1587
+ record(imageOfTexture(mat.specularTexture), "linear");
1425
1588
  }
1426
1589
  for (let i = 0; i < input.imageCount; i++) {
1427
1590
  if (!result.has(i)) {
@@ -1572,6 +1735,11 @@ function parseMaterial(matJson, textures) {
1572
1735
  const transmission = matJson.extensions?.KHR_materials_transmission;
1573
1736
  const ior = matJson.extensions?.KHR_materials_ior;
1574
1737
  const volume = matJson.extensions?.KHR_materials_volume;
1738
+ const clearcoat = matJson.extensions?.KHR_materials_clearcoat;
1739
+ const anisotropy = matJson.extensions?.KHR_materials_anisotropy;
1740
+ const sheen = matJson.extensions?.KHR_materials_sheen;
1741
+ const iridescence = matJson.extensions?.KHR_materials_iridescence;
1742
+ const specular = matJson.extensions?.KHR_materials_specular;
1575
1743
  const attenuationColor = tuple3(volume?.attenuationColor);
1576
1744
  const invalid = (extension, field, reason, actual) => err(
1577
1745
  gltfErr("gltf-material-transmission-invalid", {
@@ -1581,6 +1749,14 @@ function parseMaterial(matJson, textures) {
1581
1749
  ...actual === void 0 ? {} : { actual }
1582
1750
  })
1583
1751
  );
1752
+ const physicalInvalid = (extension, field, reason, actual) => err(
1753
+ gltfErr("gltf-material-physical-invalid", {
1754
+ extension,
1755
+ field,
1756
+ reason,
1757
+ ...actual === void 0 ? {} : { actual }
1758
+ })
1759
+ );
1584
1760
  if (transmission?.transmissionFactor !== void 0 && (typeof transmission.transmissionFactor !== "number" || !Number.isFinite(transmission.transmissionFactor))) {
1585
1761
  return invalid(
1586
1762
  "KHR_materials_transmission",
@@ -1631,6 +1807,98 @@ function parseMaterial(matJson, textures) {
1631
1807
  if (alphaMode === "BLEND" && (transmission?.transmissionFactor ?? 0) > 0) {
1632
1808
  return invalid("KHR_materials_transmission", "alphaMode", "blend", alphaMode);
1633
1809
  }
1810
+ if (clearcoat?.clearcoatFactor !== void 0 && (typeof clearcoat.clearcoatFactor !== "number" || !Number.isFinite(clearcoat.clearcoatFactor))) {
1811
+ return physicalInvalid(
1812
+ "KHR_materials_clearcoat",
1813
+ "clearcoatFactor",
1814
+ "non-finite",
1815
+ clearcoat.clearcoatFactor
1816
+ );
1817
+ }
1818
+ if (clearcoat?.clearcoatFactor !== void 0 && (clearcoat.clearcoatFactor < 0 || clearcoat.clearcoatFactor > 1)) {
1819
+ return physicalInvalid(
1820
+ "KHR_materials_clearcoat",
1821
+ "clearcoatFactor",
1822
+ "range",
1823
+ clearcoat.clearcoatFactor
1824
+ );
1825
+ }
1826
+ if (clearcoat?.clearcoatRoughnessFactor !== void 0 && (typeof clearcoat.clearcoatRoughnessFactor !== "number" || !Number.isFinite(clearcoat.clearcoatRoughnessFactor))) {
1827
+ return physicalInvalid(
1828
+ "KHR_materials_clearcoat",
1829
+ "clearcoatRoughnessFactor",
1830
+ "non-finite",
1831
+ clearcoat.clearcoatRoughnessFactor
1832
+ );
1833
+ }
1834
+ if (clearcoat?.clearcoatRoughnessFactor !== void 0 && (clearcoat.clearcoatRoughnessFactor < 0 || clearcoat.clearcoatRoughnessFactor > 1)) {
1835
+ return physicalInvalid(
1836
+ "KHR_materials_clearcoat",
1837
+ "clearcoatRoughnessFactor",
1838
+ "range",
1839
+ clearcoat.clearcoatRoughnessFactor
1840
+ );
1841
+ }
1842
+ if (clearcoat?.clearcoatNormalTexture?.scale !== void 0 && (typeof clearcoat.clearcoatNormalTexture.scale !== "number" || !Number.isFinite(clearcoat.clearcoatNormalTexture.scale) || clearcoat.clearcoatNormalTexture.scale < 0)) {
1843
+ return physicalInvalid(
1844
+ "KHR_materials_clearcoat",
1845
+ "clearcoatNormalTexture.scale",
1846
+ "range",
1847
+ clearcoat.clearcoatNormalTexture.scale
1848
+ );
1849
+ }
1850
+ const validateUnit = (extension, field, value) => {
1851
+ if (value === void 0) return void 0;
1852
+ if (typeof value !== "number" || !Number.isFinite(value))
1853
+ return physicalInvalid(extension, field, "non-finite", value);
1854
+ if (value < 0 || value > 1) return physicalInvalid(extension, field, "range", value);
1855
+ return void 0;
1856
+ };
1857
+ const validateFinite = (extension, field, value, minimum) => {
1858
+ if (value === void 0) return void 0;
1859
+ if (typeof value !== "number" || !Number.isFinite(value))
1860
+ return physicalInvalid(extension, field, "non-finite", value);
1861
+ if (minimum !== void 0 && value < minimum)
1862
+ return physicalInvalid(extension, field, "range", value);
1863
+ return void 0;
1864
+ };
1865
+ const validateColor = (extension, field, value) => {
1866
+ if (value === void 0) return void 0;
1867
+ if (value.length < 3 || value.slice(0, 3).some((channel) => typeof channel !== "number" || !Number.isFinite(channel))) {
1868
+ return physicalInvalid(extension, field, "type", value);
1869
+ }
1870
+ if (value.slice(0, 3).some((channel) => channel < 0 || channel > 1)) {
1871
+ return physicalInvalid(extension, field, "range", value);
1872
+ }
1873
+ return void 0;
1874
+ };
1875
+ const physicalChecks = [
1876
+ validateUnit("KHR_materials_anisotropy", "anisotropyStrength", anisotropy?.anisotropyStrength),
1877
+ validateFinite(
1878
+ "KHR_materials_anisotropy",
1879
+ "anisotropyRotation",
1880
+ anisotropy?.anisotropyRotation
1881
+ ),
1882
+ validateUnit("KHR_materials_sheen", "sheenRoughnessFactor", sheen?.sheenRoughnessFactor),
1883
+ validateColor("KHR_materials_sheen", "sheenColorFactor", sheen?.sheenColorFactor),
1884
+ validateUnit("KHR_materials_iridescence", "iridescenceFactor", iridescence?.iridescenceFactor),
1885
+ validateFinite("KHR_materials_iridescence", "iridescenceIor", iridescence?.iridescenceIor, 1),
1886
+ validateFinite(
1887
+ "KHR_materials_iridescence",
1888
+ "iridescenceThicknessMinimum",
1889
+ iridescence?.iridescenceThicknessMinimum,
1890
+ 0
1891
+ ),
1892
+ validateFinite(
1893
+ "KHR_materials_iridescence",
1894
+ "iridescenceThicknessMaximum",
1895
+ iridescence?.iridescenceThicknessMaximum,
1896
+ 0
1897
+ ),
1898
+ validateUnit("KHR_materials_specular", "specularFactor", specular?.specularFactor),
1899
+ validateColor("KHR_materials_specular", "specularColorFactor", specular?.specularColorFactor)
1900
+ ];
1901
+ for (const check of physicalChecks) if (check !== void 0) return check;
1634
1902
  return ok({
1635
1903
  ...matJson.name === void 0 ? {} : { name: matJson.name },
1636
1904
  baseColorFactor: baseColor4,
@@ -1667,6 +1935,65 @@ function parseMaterial(matJson, textures) {
1667
1935
  ...attenuationColor === void 0 ? {} : { attenuationColor },
1668
1936
  ...finiteOrUndefined(volume.attenuationDistance) === void 0 ? {} : { attenuationDistance: volume.attenuationDistance }
1669
1937
  },
1938
+ ...clearcoat === void 0 ? {} : {
1939
+ clearcoatFactor: clearcoat.clearcoatFactor ?? 0,
1940
+ clearcoatChannel: 0,
1941
+ clearcoatRoughnessFactor: clearcoat.clearcoatRoughnessFactor ?? 0,
1942
+ clearcoatRoughnessChannel: 1,
1943
+ clearcoatNormalChannel: [0, 1],
1944
+ ...clearcoat.clearcoatTexture === void 0 ? {} : { clearcoatTexture: parseTextureInfo(clearcoat.clearcoatTexture, textures) },
1945
+ ...clearcoat.clearcoatRoughnessTexture === void 0 ? {} : {
1946
+ clearcoatRoughnessTexture: parseTextureInfo(
1947
+ clearcoat.clearcoatRoughnessTexture,
1948
+ textures
1949
+ )
1950
+ },
1951
+ ...clearcoat.clearcoatNormalTexture === void 0 ? {} : {
1952
+ clearcoatNormalTexture: {
1953
+ ...parseTextureInfo(clearcoat.clearcoatNormalTexture, textures),
1954
+ ...clearcoat.clearcoatNormalTexture.scale === void 0 ? {} : { scale: clearcoat.clearcoatNormalTexture.scale }
1955
+ }
1956
+ }
1957
+ },
1958
+ ...anisotropy === void 0 ? {} : {
1959
+ anisotropyStrength: anisotropy.anisotropyStrength ?? 0,
1960
+ anisotropyRotation: anisotropy.anisotropyRotation ?? 0,
1961
+ anisotropyChannel: [0, 1, 2],
1962
+ ...anisotropy.anisotropyTexture === void 0 ? {} : { anisotropyTexture: parseTextureInfo(anisotropy.anisotropyTexture, textures) }
1963
+ },
1964
+ ...sheen === void 0 ? {} : {
1965
+ sheenColorFactor: tuple3(sheen.sheenColorFactor) ?? [0, 0, 0],
1966
+ sheenColorChannel: [0, 1, 2],
1967
+ sheenRoughnessFactor: sheen.sheenRoughnessFactor ?? 0,
1968
+ sheenRoughnessChannel: 3,
1969
+ ...sheen.sheenColorTexture === void 0 ? {} : { sheenColorTexture: parseTextureInfo(sheen.sheenColorTexture, textures) },
1970
+ ...sheen.sheenRoughnessTexture === void 0 ? {} : { sheenRoughnessTexture: parseTextureInfo(sheen.sheenRoughnessTexture, textures) }
1971
+ },
1972
+ ...iridescence === void 0 ? {} : {
1973
+ iridescenceFactor: iridescence.iridescenceFactor ?? 0,
1974
+ iridescenceIor: iridescence.iridescenceIor ?? 1.3,
1975
+ iridescenceThicknessMinimum: iridescence.iridescenceThicknessMinimum ?? 100,
1976
+ iridescenceThicknessMaximum: iridescence.iridescenceThicknessMaximum ?? 400,
1977
+ iridescenceChannel: 0,
1978
+ iridescenceThicknessChannel: 1,
1979
+ ...iridescence.iridescenceTexture === void 0 ? {} : { iridescenceTexture: parseTextureInfo(iridescence.iridescenceTexture, textures) },
1980
+ ...iridescence.iridescenceThicknessTexture === void 0 ? {} : {
1981
+ iridescenceThicknessTexture: parseTextureInfo(
1982
+ iridescence.iridescenceThicknessTexture,
1983
+ textures
1984
+ )
1985
+ }
1986
+ },
1987
+ ...specular === void 0 ? {} : {
1988
+ specularFactor: specular.specularFactor ?? 1,
1989
+ specularChannel: 3,
1990
+ specularColorFactor: tuple3(specular.specularColorFactor) ?? [1, 1, 1],
1991
+ specularColorChannel: [0, 1, 2],
1992
+ ...specular.specularTexture === void 0 ? {} : { specularTexture: parseTextureInfo(specular.specularTexture, textures) },
1993
+ ...specular.specularColorTexture === void 0 ? {} : {
1994
+ specularColorTexture: parseTextureInfo(specular.specularColorTexture, textures)
1995
+ }
1996
+ },
1670
1997
  ...alphaMode === void 0 ? {} : { alphaMode },
1671
1998
  ...alphaCutoff === void 0 ? {} : { alphaCutoff },
1672
1999
  ...matJson.doubleSided === true ? { doubleSided: true } : {},
@@ -3516,6 +3843,16 @@ function materialRefsForPack(mat, doc, textureGuidByIndex, samplerGuidByIndex =
3516
3843
  pushRefsForSlot(mat.emissiveTexture, "emissiveTexture");
3517
3844
  pushRefsForSlot(mat.transmissionTexture, "transmissionTexture");
3518
3845
  pushRefsForSlot(mat.thicknessTexture, "thicknessTexture");
3846
+ pushRefsForSlot(mat.clearcoatTexture, "clearcoatTexture");
3847
+ pushRefsForSlot(mat.clearcoatRoughnessTexture, "clearcoatRoughnessTexture");
3848
+ pushRefsForSlot(mat.clearcoatNormalTexture, "clearcoatNormalTexture");
3849
+ pushRefsForSlot(mat.anisotropyTexture, "anisotropyTexture");
3850
+ pushRefsForSlot(mat.sheenColorTexture, "sheenColorTexture");
3851
+ pushRefsForSlot(mat.sheenRoughnessTexture, "sheenRoughnessTexture");
3852
+ pushRefsForSlot(mat.iridescenceTexture, "iridescenceTexture");
3853
+ pushRefsForSlot(mat.iridescenceThicknessTexture, "iridescenceThicknessTexture");
3854
+ pushRefsForSlot(mat.specularTexture, "specularTexture");
3855
+ pushRefsForSlot(mat.specularColorTexture, "specularColorTexture");
3519
3856
  return refs;
3520
3857
  }
3521
3858
  function availableUvSets(mesh) {
@@ -3536,7 +3873,17 @@ function rewriteMaterialAssetRefs(matAsset, mat, doc, maps) {
3536
3873
  ["occlusionTexture", mat.occlusionTexture],
3537
3874
  ["emissiveTexture", mat.emissiveTexture],
3538
3875
  ["transmissionTexture", mat.transmissionTexture],
3539
- ["thicknessTexture", mat.thicknessTexture]
3876
+ ["thicknessTexture", mat.thicknessTexture],
3877
+ ["clearcoatTexture", mat.clearcoatTexture],
3878
+ ["clearcoatRoughnessTexture", mat.clearcoatRoughnessTexture],
3879
+ ["clearcoatNormalTexture", mat.clearcoatNormalTexture],
3880
+ ["anisotropyTexture", mat.anisotropyTexture],
3881
+ ["sheenColorTexture", mat.sheenColorTexture],
3882
+ ["sheenRoughnessTexture", mat.sheenRoughnessTexture],
3883
+ ["iridescenceTexture", mat.iridescenceTexture],
3884
+ ["iridescenceThicknessTexture", mat.iridescenceThicknessTexture],
3885
+ ["specularTexture", mat.specularTexture],
3886
+ ["specularColorTexture", mat.specularColorTexture]
3540
3887
  ];
3541
3888
  let cursor = 0;
3542
3889
  for (const [slot, rawBinding] of slots) {
@@ -3753,6 +4100,10 @@ async function importGltf(ctx, meshopt) {
3753
4100
  if (!uvResult.ok) {
3754
4101
  throw Object.assign(new Error(uvResult.error.message), uvResult.error);
3755
4102
  }
4103
+ const tangentResult = validateMaterialTangentInputs(mat, meshIr);
4104
+ if (!tangentResult.ok) {
4105
+ throw Object.assign(new Error(tangentResult.error.message), tangentResult.error);
4106
+ }
3756
4107
  }
3757
4108
  const matAsset = toMaterialAsset(mat, {
3758
4109
  textureHandles: maps.textureHandles,
@@ -4005,6 +4356,6 @@ function serializeMetaJson(meta) {
4005
4356
  `;
4006
4357
  }
4007
4358
 
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 };
4359
+ 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
4360
  //# sourceMappingURL=index.mjs.map
4010
4361
  //# sourceMappingURL=index.mjs.map