@luma.gl/gltf 9.3.0-alpha.6 → 9.3.0-alpha.8

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 (65) hide show
  1. package/dist/dist.dev.js +942 -141
  2. package/dist/dist.min.js +4 -4
  3. package/dist/gltf/create-gltf-model.d.ts +9 -1
  4. package/dist/gltf/create-gltf-model.d.ts.map +1 -1
  5. package/dist/gltf/create-gltf-model.js +58 -4
  6. package/dist/gltf/create-gltf-model.js.map +1 -1
  7. package/dist/gltf/create-scenegraph-from-gltf.d.ts +22 -1
  8. package/dist/gltf/create-scenegraph-from-gltf.d.ts.map +1 -1
  9. package/dist/gltf/create-scenegraph-from-gltf.js +63 -1
  10. package/dist/gltf/create-scenegraph-from-gltf.js.map +1 -1
  11. package/dist/gltf/gltf-extension-support.d.ts +10 -0
  12. package/dist/gltf/gltf-extension-support.d.ts.map +1 -0
  13. package/dist/gltf/gltf-extension-support.js +173 -0
  14. package/dist/gltf/gltf-extension-support.js.map +1 -0
  15. package/dist/index.cjs +899 -114
  16. package/dist/index.cjs.map +4 -4
  17. package/dist/index.d.ts +2 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/parsers/parse-gltf-animations.d.ts.map +1 -1
  22. package/dist/parsers/parse-gltf-animations.js +34 -12
  23. package/dist/parsers/parse-gltf-animations.js.map +1 -1
  24. package/dist/parsers/parse-gltf-lights.d.ts.map +1 -1
  25. package/dist/parsers/parse-gltf-lights.js +86 -20
  26. package/dist/parsers/parse-gltf-lights.js.map +1 -1
  27. package/dist/parsers/parse-gltf.d.ts +3 -1
  28. package/dist/parsers/parse-gltf.d.ts.map +1 -1
  29. package/dist/parsers/parse-gltf.js +41 -9
  30. package/dist/parsers/parse-gltf.js.map +1 -1
  31. package/dist/parsers/parse-pbr-material.d.ts +69 -1
  32. package/dist/parsers/parse-pbr-material.d.ts.map +1 -1
  33. package/dist/parsers/parse-pbr-material.js +429 -42
  34. package/dist/parsers/parse-pbr-material.js.map +1 -1
  35. package/dist/pbr/pbr-environment.d.ts.map +1 -1
  36. package/dist/pbr/pbr-environment.js +14 -12
  37. package/dist/pbr/pbr-environment.js.map +1 -1
  38. package/dist/pbr/pbr-material.d.ts +8 -3
  39. package/dist/pbr/pbr-material.d.ts.map +1 -1
  40. package/dist/webgl-to-webgpu/convert-webgl-sampler.d.ts +5 -5
  41. package/dist/webgl-to-webgpu/convert-webgl-sampler.d.ts.map +1 -1
  42. package/dist/webgl-to-webgpu/convert-webgl-sampler.js +12 -12
  43. package/dist/webgl-to-webgpu/convert-webgl-sampler.js.map +1 -1
  44. package/dist/webgl-to-webgpu/convert-webgl-topology.d.ts +1 -10
  45. package/dist/webgl-to-webgpu/convert-webgl-topology.d.ts.map +1 -1
  46. package/dist/webgl-to-webgpu/convert-webgl-topology.js +1 -15
  47. package/dist/webgl-to-webgpu/convert-webgl-topology.js.map +1 -1
  48. package/dist/webgl-to-webgpu/gltf-webgl-constants.d.ts +27 -0
  49. package/dist/webgl-to-webgpu/gltf-webgl-constants.d.ts.map +1 -0
  50. package/dist/webgl-to-webgpu/gltf-webgl-constants.js +34 -0
  51. package/dist/webgl-to-webgpu/gltf-webgl-constants.js.map +1 -0
  52. package/package.json +5 -6
  53. package/src/gltf/create-gltf-model.ts +113 -5
  54. package/src/gltf/create-scenegraph-from-gltf.ts +97 -6
  55. package/src/gltf/gltf-extension-support.ts +214 -0
  56. package/src/index.ts +10 -1
  57. package/src/parsers/parse-gltf-animations.ts +39 -15
  58. package/src/parsers/parse-gltf-lights.ts +114 -25
  59. package/src/parsers/parse-gltf.ts +86 -19
  60. package/src/parsers/parse-pbr-material.ts +664 -69
  61. package/src/pbr/pbr-environment.ts +29 -16
  62. package/src/pbr/pbr-material.ts +13 -3
  63. package/src/webgl-to-webgpu/convert-webgl-sampler.ts +29 -29
  64. package/src/webgl-to-webgpu/convert-webgl-topology.ts +1 -15
  65. package/src/webgl-to-webgpu/gltf-webgl-constants.ts +35 -0
package/dist/dist.dev.js CHANGED
@@ -52,13 +52,6 @@ var __exports__ = (() => {
52
52
  }
53
53
  });
54
54
 
55
- // external-global-plugin:@luma.gl/constants
56
- var require_constants = __commonJS({
57
- "external-global-plugin:@luma.gl/constants"(exports, module) {
58
- module.exports = globalThis.luma;
59
- }
60
- });
61
-
62
55
  // external-global-plugin:@luma.gl/shadertools
63
56
  var require_shadertools = __commonJS({
64
57
  "external-global-plugin:@luma.gl/shadertools"(exports, module) {
@@ -71,6 +64,7 @@ var __exports__ = (() => {
71
64
  __export(bundle_exports, {
72
65
  GLTFAnimator: () => GLTFAnimator,
73
66
  createScenegraphsFromGLTF: () => createScenegraphsFromGLTF,
67
+ getGLTFExtensionSupport: () => getGLTFExtensionSupport,
74
68
  loadPBREnvironment: () => loadPBREnvironment,
75
69
  parseGLTFLights: () => parseGLTFLights,
76
70
  parsePBRMaterial: () => parsePBRMaterial
@@ -620,6 +614,7 @@ var __exports__ = (() => {
620
614
 
621
615
  // src/pbr/pbr-environment.ts
622
616
  function loadPBREnvironment(device, props) {
617
+ const specularMipLevels = props.specularMipLevels ?? 1;
623
618
  const brdfLutTexture = new import_engine.DynamicTexture(device, {
624
619
  id: "brdfLUT",
625
620
  sampler: {
@@ -633,7 +628,9 @@ var __exports__ = (() => {
633
628
  });
634
629
  const diffuseEnvSampler = makeCube(device, {
635
630
  id: "DiffuseEnvSampler",
636
- getTextureForFace: (dir) => loadImageTexture(props.getTexUrl("diffuse", dir, 0)),
631
+ getTextureForFace: (face) => loadImageTexture(
632
+ props.getTexUrl("diffuse", FACES.indexOf(face), 0)
633
+ ),
637
634
  sampler: {
638
635
  addressModeU: "clamp-to-edge",
639
636
  addressModeV: "clamp-to-edge",
@@ -643,12 +640,13 @@ var __exports__ = (() => {
643
640
  });
644
641
  const specularEnvSampler = makeCube(device, {
645
642
  id: "SpecularEnvSampler",
646
- getTextureForFace: (dir) => {
643
+ getTextureForFace: (face) => {
647
644
  const imageArray = [];
648
- for (let lod = 0; lod <= props.specularMipLevels - 1; lod++) {
649
- imageArray.push(loadImageTexture(props.getTexUrl("specular", dir, lod)));
645
+ const direction = FACES.indexOf(face);
646
+ for (let lod = 0; lod < specularMipLevels; lod++) {
647
+ imageArray.push(loadImageTexture(props.getTexUrl("specular", direction, lod)));
650
648
  }
651
- return imageArray;
649
+ return Promise.all(imageArray);
652
650
  },
653
651
  sampler: {
654
652
  addressModeU: "clamp-to-edge",
@@ -664,32 +662,34 @@ var __exports__ = (() => {
664
662
  specularEnvSampler
665
663
  };
666
664
  }
667
- var FACES = [0, 1, 2, 3, 4, 5];
665
+ var FACES = ["+X", "-X", "+Y", "-Y", "+Z", "-Z"];
668
666
  function makeCube(device, {
669
667
  id,
670
668
  getTextureForFace,
671
669
  sampler
672
670
  }) {
673
- const data = {};
674
- FACES.forEach((face) => {
675
- data[String(face)] = getTextureForFace(face);
671
+ const data = Promise.all(
672
+ FACES.map((face) => getTextureForFace(face))
673
+ ).then((faceDataArray) => {
674
+ const cubeData = {};
675
+ FACES.forEach((face, index) => {
676
+ cubeData[face] = faceDataArray[index];
677
+ });
678
+ return cubeData;
676
679
  });
677
680
  return new import_engine.DynamicTexture(device, {
678
681
  id,
679
682
  dimension: "cube",
680
683
  mipmaps: false,
681
684
  sampler,
682
- // @ts-expect-error
683
685
  data
684
686
  });
685
687
  }
686
688
 
687
689
  // src/parsers/parse-pbr-material.ts
688
690
  var import_core = __toESM(require_core(), 1);
689
- var import_constants2 = __toESM(require_constants(), 1);
690
691
 
691
692
  // src/webgl-to-webgpu/convert-webgl-sampler.ts
692
- var import_constants = __toESM(require_constants(), 1);
693
693
  function convertSampler(gltfSampler) {
694
694
  return {
695
695
  addressModeU: convertSamplerWrapMode(gltfSampler.wrapS),
@@ -700,11 +700,11 @@ var __exports__ = (() => {
700
700
  }
701
701
  function convertSamplerWrapMode(mode) {
702
702
  switch (mode) {
703
- case import_constants.GL.CLAMP_TO_EDGE:
703
+ case 33071 /* CLAMP_TO_EDGE */:
704
704
  return "clamp-to-edge";
705
- case import_constants.GL.REPEAT:
705
+ case 10497 /* REPEAT */:
706
706
  return "repeat";
707
- case import_constants.GL.MIRRORED_REPEAT:
707
+ case 33648 /* MIRRORED_REPEAT */:
708
708
  return "mirror-repeat";
709
709
  default:
710
710
  return void 0;
@@ -712,9 +712,9 @@ var __exports__ = (() => {
712
712
  }
713
713
  function convertSamplerMagFilter(mode) {
714
714
  switch (mode) {
715
- case import_constants.GL.NEAREST:
715
+ case 9728 /* NEAREST */:
716
716
  return "nearest";
717
- case import_constants.GL.LINEAR:
717
+ case 9729 /* LINEAR */:
718
718
  return "linear";
719
719
  default:
720
720
  return void 0;
@@ -722,17 +722,17 @@ var __exports__ = (() => {
722
722
  }
723
723
  function convertSamplerMinFilter(mode) {
724
724
  switch (mode) {
725
- case import_constants.GL.NEAREST:
725
+ case 9728 /* NEAREST */:
726
726
  return { minFilter: "nearest" };
727
- case import_constants.GL.LINEAR:
727
+ case 9729 /* LINEAR */:
728
728
  return { minFilter: "linear" };
729
- case import_constants.GL.NEAREST_MIPMAP_NEAREST:
729
+ case 9984 /* NEAREST_MIPMAP_NEAREST */:
730
730
  return { minFilter: "nearest", mipmapFilter: "nearest" };
731
- case import_constants.GL.LINEAR_MIPMAP_NEAREST:
731
+ case 9985 /* LINEAR_MIPMAP_NEAREST */:
732
732
  return { minFilter: "linear", mipmapFilter: "nearest" };
733
- case import_constants.GL.NEAREST_MIPMAP_LINEAR:
733
+ case 9986 /* NEAREST_MIPMAP_LINEAR */:
734
734
  return { minFilter: "nearest", mipmapFilter: "linear" };
735
- case import_constants.GL.LINEAR_MIPMAP_LINEAR:
735
+ case 9987 /* LINEAR_MIPMAP_LINEAR */:
736
736
  return { minFilter: "linear", mipmapFilter: "linear" };
737
737
  default:
738
738
  return {};
@@ -764,7 +764,8 @@ var __exports__ = (() => {
764
764
  if (imageBasedLightingEnvironment) {
765
765
  parsedMaterial.bindings.pbr_diffuseEnvSampler = imageBasedLightingEnvironment.diffuseEnvSampler.texture;
766
766
  parsedMaterial.bindings.pbr_specularEnvSampler = imageBasedLightingEnvironment.specularEnvSampler.texture;
767
- parsedMaterial.bindings.pbr_BrdfLUT = imageBasedLightingEnvironment.brdfLutTexture.texture;
767
+ parsedMaterial.bindings.pbr_brdfLUT = imageBasedLightingEnvironment.brdfLutTexture.texture;
768
+ parsedMaterial.uniforms.IBLenabled = true;
768
769
  parsedMaterial.uniforms.scaleIBLAmbient = [1, 1];
769
770
  }
770
771
  if (options?.pbrDebug) {
@@ -787,81 +788,181 @@ var __exports__ = (() => {
787
788
  if (options?.lights)
788
789
  parsedMaterial.defines["USE_LIGHTS"] = true;
789
790
  if (material) {
790
- parseMaterial(device, material, parsedMaterial);
791
+ if (options.validateAttributes !== false) {
792
+ warnOnMissingExpectedAttributes(material, attributes);
793
+ }
794
+ parseMaterial(device, material, parsedMaterial, options.gltf);
791
795
  }
792
796
  return parsedMaterial;
793
797
  }
794
- function parseMaterial(device, material, parsedMaterial) {
795
- parsedMaterial.uniforms.unlit = Boolean(material.unlit);
798
+ function warnOnMissingExpectedAttributes(material, attributes) {
799
+ const uvDependentTextureSlots = getUvDependentTextureSlots(material);
800
+ if (uvDependentTextureSlots.length > 0 && !attributes["TEXCOORD_0"]) {
801
+ import_core.log.warn(
802
+ `glTF material uses ${uvDependentTextureSlots.join(", ")} but primitive is missing TEXCOORD_0; textured shading will sample the default UV coordinates`
803
+ )();
804
+ }
805
+ const isUnlitMaterial = Boolean(material.unlit || material.extensions?.KHR_materials_unlit);
806
+ if (isUnlitMaterial || attributes["NORMAL"]) {
807
+ return;
808
+ }
809
+ const missingNormalReason = material.normalTexture ? "lit PBR shading with normalTexture" : "lit PBR shading";
810
+ import_core.log.warn(
811
+ `glTF primitive is missing NORMAL while using ${missingNormalReason}; shading will fall back to geometric normals`
812
+ )();
813
+ }
814
+ function getUvDependentTextureSlots(material) {
815
+ const uvDependentTextureSlots = [];
816
+ if (material.pbrMetallicRoughness?.baseColorTexture) {
817
+ uvDependentTextureSlots.push("baseColorTexture");
818
+ }
819
+ if (material.pbrMetallicRoughness?.metallicRoughnessTexture) {
820
+ uvDependentTextureSlots.push("metallicRoughnessTexture");
821
+ }
822
+ if (material.normalTexture) {
823
+ uvDependentTextureSlots.push("normalTexture");
824
+ }
825
+ if (material.occlusionTexture) {
826
+ uvDependentTextureSlots.push("occlusionTexture");
827
+ }
828
+ if (material.emissiveTexture) {
829
+ uvDependentTextureSlots.push("emissiveTexture");
830
+ }
831
+ if (material.extensions?.KHR_materials_specular?.specularTexture) {
832
+ uvDependentTextureSlots.push("KHR_materials_specular.specularTexture");
833
+ }
834
+ if (material.extensions?.KHR_materials_specular?.specularColorTexture) {
835
+ uvDependentTextureSlots.push("KHR_materials_specular.specularColorTexture");
836
+ }
837
+ if (material.extensions?.KHR_materials_transmission?.transmissionTexture) {
838
+ uvDependentTextureSlots.push("KHR_materials_transmission.transmissionTexture");
839
+ }
840
+ if (material.extensions?.KHR_materials_clearcoat?.clearcoatTexture) {
841
+ uvDependentTextureSlots.push("KHR_materials_clearcoat.clearcoatTexture");
842
+ }
843
+ if (material.extensions?.KHR_materials_clearcoat?.clearcoatRoughnessTexture) {
844
+ uvDependentTextureSlots.push("KHR_materials_clearcoat.clearcoatRoughnessTexture");
845
+ }
846
+ if (material.extensions?.KHR_materials_sheen?.sheenColorTexture) {
847
+ uvDependentTextureSlots.push("KHR_materials_sheen.sheenColorTexture");
848
+ }
849
+ if (material.extensions?.KHR_materials_sheen?.sheenRoughnessTexture) {
850
+ uvDependentTextureSlots.push("KHR_materials_sheen.sheenRoughnessTexture");
851
+ }
852
+ if (material.extensions?.KHR_materials_iridescence?.iridescenceTexture) {
853
+ uvDependentTextureSlots.push("KHR_materials_iridescence.iridescenceTexture");
854
+ }
855
+ if (material.extensions?.KHR_materials_anisotropy?.anisotropyTexture) {
856
+ uvDependentTextureSlots.push("KHR_materials_anisotropy.anisotropyTexture");
857
+ }
858
+ return uvDependentTextureSlots;
859
+ }
860
+ function parseMaterial(device, material, parsedMaterial, gltf) {
861
+ parsedMaterial.uniforms.unlit = Boolean(
862
+ material.unlit || material.extensions?.KHR_materials_unlit
863
+ );
796
864
  if (material.pbrMetallicRoughness) {
797
- parsePbrMetallicRoughness(device, material.pbrMetallicRoughness, parsedMaterial);
865
+ parsePbrMetallicRoughness(device, material.pbrMetallicRoughness, parsedMaterial, gltf);
798
866
  }
799
867
  if (material.normalTexture) {
800
- addTexture(
801
- device,
802
- material.normalTexture,
803
- "pbr_normalSampler",
804
- "HAS_NORMALMAP",
805
- parsedMaterial
806
- );
868
+ addTexture(device, material.normalTexture, "pbr_normalSampler", parsedMaterial, {
869
+ featureOptions: {
870
+ define: "HAS_NORMALMAP",
871
+ enabledUniformName: "normalMapEnabled"
872
+ },
873
+ gltf
874
+ });
807
875
  const { scale: scale4 = 1 } = material.normalTexture;
808
876
  parsedMaterial.uniforms.normalScale = scale4;
809
877
  }
810
878
  if (material.occlusionTexture) {
811
- addTexture(
812
- device,
813
- material.occlusionTexture,
814
- "pbr_occlusionSampler",
815
- "HAS_OCCLUSIONMAP",
816
- parsedMaterial
817
- );
879
+ addTexture(device, material.occlusionTexture, "pbr_occlusionSampler", parsedMaterial, {
880
+ featureOptions: {
881
+ define: "HAS_OCCLUSIONMAP",
882
+ enabledUniformName: "occlusionMapEnabled"
883
+ },
884
+ gltf
885
+ });
818
886
  const { strength = 1 } = material.occlusionTexture;
819
887
  parsedMaterial.uniforms.occlusionStrength = strength;
820
888
  }
889
+ parsedMaterial.uniforms.emissiveFactor = material.emissiveFactor || [0, 0, 0];
821
890
  if (material.emissiveTexture) {
822
- addTexture(
823
- device,
824
- material.emissiveTexture,
825
- "pbr_emissiveSampler",
826
- "HAS_EMISSIVEMAP",
827
- parsedMaterial
828
- );
829
- parsedMaterial.uniforms.emissiveFactor = material.emissiveFactor || [0, 0, 0];
891
+ addTexture(device, material.emissiveTexture, "pbr_emissiveSampler", parsedMaterial, {
892
+ featureOptions: {
893
+ define: "HAS_EMISSIVEMAP",
894
+ enabledUniformName: "emissiveMapEnabled"
895
+ },
896
+ gltf
897
+ });
830
898
  }
831
- switch (material.alphaMode || "MASK") {
832
- case "MASK":
899
+ parseMaterialExtensions(device, material.extensions, parsedMaterial, gltf);
900
+ switch (material.alphaMode || "OPAQUE") {
901
+ case "OPAQUE":
902
+ break;
903
+ case "MASK": {
833
904
  const { alphaCutoff = 0.5 } = material;
834
905
  parsedMaterial.defines["ALPHA_CUTOFF"] = true;
906
+ parsedMaterial.uniforms.alphaCutoffEnabled = true;
835
907
  parsedMaterial.uniforms.alphaCutoff = alphaCutoff;
836
908
  break;
909
+ }
837
910
  case "BLEND":
838
911
  import_core.log.warn("glTF BLEND alphaMode might not work well because it requires mesh sorting")();
839
- parsedMaterial.parameters.blend = true;
840
- parsedMaterial.parameters.blendColorOperation = "add";
841
- parsedMaterial.parameters.blendColorSrcFactor = "src-alpha";
842
- parsedMaterial.parameters.blendColorDstFactor = "one-minus-src-alpha";
843
- parsedMaterial.parameters.blendAlphaOperation = "add";
844
- parsedMaterial.parameters.blendAlphaSrcFactor = "one";
845
- parsedMaterial.parameters.blendAlphaDstFactor = "one-minus-src-alpha";
846
- parsedMaterial.glParameters["blend"] = true;
847
- parsedMaterial.glParameters["blendEquation"] = import_constants2.GL.FUNC_ADD;
848
- parsedMaterial.glParameters["blendFunc"] = [
849
- import_constants2.GL.SRC_ALPHA,
850
- import_constants2.GL.ONE_MINUS_SRC_ALPHA,
851
- import_constants2.GL.ONE,
852
- import_constants2.GL.ONE_MINUS_SRC_ALPHA
853
- ];
912
+ applyAlphaBlendParameters(parsedMaterial);
854
913
  break;
855
914
  }
856
915
  }
857
- function parsePbrMetallicRoughness(device, pbrMetallicRoughness, parsedMaterial) {
916
+ function applyAlphaBlendParameters(parsedMaterial) {
917
+ parsedMaterial.parameters.blend = true;
918
+ parsedMaterial.parameters.blendColorOperation = "add";
919
+ parsedMaterial.parameters.blendColorSrcFactor = "src-alpha";
920
+ parsedMaterial.parameters.blendColorDstFactor = "one-minus-src-alpha";
921
+ parsedMaterial.parameters.blendAlphaOperation = "add";
922
+ parsedMaterial.parameters.blendAlphaSrcFactor = "one";
923
+ parsedMaterial.parameters.blendAlphaDstFactor = "one-minus-src-alpha";
924
+ parsedMaterial.glParameters["blend"] = true;
925
+ parsedMaterial.glParameters["blendEquation"] = 32774 /* FUNC_ADD */;
926
+ parsedMaterial.glParameters["blendFunc"] = [
927
+ 770 /* SRC_ALPHA */,
928
+ 771 /* ONE_MINUS_SRC_ALPHA */,
929
+ 1 /* ONE */,
930
+ 771 /* ONE_MINUS_SRC_ALPHA */
931
+ ];
932
+ }
933
+ function applyTransmissionBlendApproximation(parsedMaterial) {
934
+ parsedMaterial.parameters.blend = true;
935
+ parsedMaterial.parameters.depthWriteEnabled = false;
936
+ parsedMaterial.parameters.blendColorOperation = "add";
937
+ parsedMaterial.parameters.blendColorSrcFactor = "one";
938
+ parsedMaterial.parameters.blendColorDstFactor = "one-minus-src-alpha";
939
+ parsedMaterial.parameters.blendAlphaOperation = "add";
940
+ parsedMaterial.parameters.blendAlphaSrcFactor = "one";
941
+ parsedMaterial.parameters.blendAlphaDstFactor = "one-minus-src-alpha";
942
+ parsedMaterial.glParameters["blend"] = true;
943
+ parsedMaterial.glParameters["depthMask"] = false;
944
+ parsedMaterial.glParameters["blendEquation"] = 32774 /* FUNC_ADD */;
945
+ parsedMaterial.glParameters["blendFunc"] = [
946
+ 1 /* ONE */,
947
+ 771 /* ONE_MINUS_SRC_ALPHA */,
948
+ 1 /* ONE */,
949
+ 771 /* ONE_MINUS_SRC_ALPHA */
950
+ ];
951
+ }
952
+ function parsePbrMetallicRoughness(device, pbrMetallicRoughness, parsedMaterial, gltf) {
858
953
  if (pbrMetallicRoughness.baseColorTexture) {
859
954
  addTexture(
860
955
  device,
861
956
  pbrMetallicRoughness.baseColorTexture,
862
957
  "pbr_baseColorSampler",
863
- "HAS_BASECOLORMAP",
864
- parsedMaterial
958
+ parsedMaterial,
959
+ {
960
+ featureOptions: {
961
+ define: "HAS_BASECOLORMAP",
962
+ enabledUniformName: "baseColorMapEnabled"
963
+ },
964
+ gltf
965
+ }
865
966
  );
866
967
  }
867
968
  parsedMaterial.uniforms.baseColorFactor = pbrMetallicRoughness.baseColorFactor || [1, 1, 1, 1];
@@ -870,15 +971,278 @@ var __exports__ = (() => {
870
971
  device,
871
972
  pbrMetallicRoughness.metallicRoughnessTexture,
872
973
  "pbr_metallicRoughnessSampler",
873
- "HAS_METALROUGHNESSMAP",
874
- parsedMaterial
974
+ parsedMaterial,
975
+ {
976
+ featureOptions: {
977
+ define: "HAS_METALROUGHNESSMAP",
978
+ enabledUniformName: "metallicRoughnessMapEnabled"
979
+ },
980
+ gltf
981
+ }
875
982
  );
876
983
  }
877
984
  const { metallicFactor = 1, roughnessFactor = 1 } = pbrMetallicRoughness;
878
985
  parsedMaterial.uniforms.metallicRoughnessValues = [metallicFactor, roughnessFactor];
879
986
  }
880
- function addTexture(device, gltfTexture, uniformName, define, parsedMaterial) {
881
- const image = gltfTexture.texture.source.image;
987
+ function parseMaterialExtensions(device, extensions, parsedMaterial, gltf) {
988
+ if (!extensions) {
989
+ return;
990
+ }
991
+ if (hasMaterialExtensionShading(extensions)) {
992
+ parsedMaterial.defines["USE_MATERIAL_EXTENSIONS"] = true;
993
+ }
994
+ parseSpecularExtension(device, extensions.KHR_materials_specular, parsedMaterial, gltf);
995
+ parseIorExtension(extensions.KHR_materials_ior, parsedMaterial);
996
+ parseTransmissionExtension(device, extensions.KHR_materials_transmission, parsedMaterial, gltf);
997
+ parseVolumeExtension(device, extensions.KHR_materials_volume, parsedMaterial, gltf);
998
+ parseClearcoatExtension(device, extensions.KHR_materials_clearcoat, parsedMaterial, gltf);
999
+ parseSheenExtension(device, extensions.KHR_materials_sheen, parsedMaterial, gltf);
1000
+ parseIridescenceExtension(device, extensions.KHR_materials_iridescence, parsedMaterial, gltf);
1001
+ parseAnisotropyExtension(device, extensions.KHR_materials_anisotropy, parsedMaterial, gltf);
1002
+ parseEmissiveStrengthExtension(extensions.KHR_materials_emissive_strength, parsedMaterial);
1003
+ }
1004
+ function hasMaterialExtensionShading(extensions) {
1005
+ return Boolean(
1006
+ extensions.KHR_materials_specular || extensions.KHR_materials_ior || extensions.KHR_materials_transmission || extensions.KHR_materials_volume || extensions.KHR_materials_clearcoat || extensions.KHR_materials_sheen || extensions.KHR_materials_iridescence || extensions.KHR_materials_anisotropy
1007
+ );
1008
+ }
1009
+ function parseSpecularExtension(device, extension, parsedMaterial, gltf) {
1010
+ if (!extension) {
1011
+ return;
1012
+ }
1013
+ if (extension.specularColorFactor) {
1014
+ parsedMaterial.uniforms.specularColorFactor = extension.specularColorFactor;
1015
+ }
1016
+ if (extension.specularFactor !== void 0) {
1017
+ parsedMaterial.uniforms.specularIntensityFactor = extension.specularFactor;
1018
+ }
1019
+ if (extension.specularColorTexture) {
1020
+ addTexture(device, extension.specularColorTexture, "pbr_specularColorSampler", parsedMaterial, {
1021
+ featureOptions: {
1022
+ define: "HAS_SPECULARCOLORMAP",
1023
+ enabledUniformName: "specularColorMapEnabled"
1024
+ },
1025
+ gltf
1026
+ });
1027
+ }
1028
+ if (extension.specularTexture) {
1029
+ addTexture(device, extension.specularTexture, "pbr_specularIntensitySampler", parsedMaterial, {
1030
+ featureOptions: {
1031
+ define: "HAS_SPECULARINTENSITYMAP",
1032
+ enabledUniformName: "specularIntensityMapEnabled"
1033
+ },
1034
+ gltf
1035
+ });
1036
+ }
1037
+ }
1038
+ function parseIorExtension(extension, parsedMaterial) {
1039
+ if (extension?.ior !== void 0) {
1040
+ parsedMaterial.uniforms.ior = extension.ior;
1041
+ }
1042
+ }
1043
+ function parseTransmissionExtension(device, extension, parsedMaterial, gltf) {
1044
+ if (!extension) {
1045
+ return;
1046
+ }
1047
+ if (extension.transmissionFactor !== void 0) {
1048
+ parsedMaterial.uniforms.transmissionFactor = extension.transmissionFactor;
1049
+ }
1050
+ if (extension.transmissionTexture) {
1051
+ addTexture(device, extension.transmissionTexture, "pbr_transmissionSampler", parsedMaterial, {
1052
+ featureOptions: {
1053
+ define: "HAS_TRANSMISSIONMAP",
1054
+ enabledUniformName: "transmissionMapEnabled"
1055
+ },
1056
+ gltf
1057
+ });
1058
+ }
1059
+ if ((extension.transmissionFactor ?? 0) > 0 || extension.transmissionTexture) {
1060
+ import_core.log.warn(
1061
+ "KHR_materials_transmission uses a premultiplied-alpha blending approximation and may require mesh sorting"
1062
+ )();
1063
+ applyTransmissionBlendApproximation(parsedMaterial);
1064
+ }
1065
+ }
1066
+ function parseVolumeExtension(device, extension, parsedMaterial, gltf) {
1067
+ if (!extension) {
1068
+ return;
1069
+ }
1070
+ if (extension.thicknessFactor !== void 0) {
1071
+ parsedMaterial.uniforms.thicknessFactor = extension.thicknessFactor;
1072
+ }
1073
+ if (extension.thicknessTexture) {
1074
+ addTexture(device, extension.thicknessTexture, "pbr_thicknessSampler", parsedMaterial, {
1075
+ featureOptions: {
1076
+ define: "HAS_THICKNESSMAP"
1077
+ },
1078
+ gltf
1079
+ });
1080
+ }
1081
+ if (extension.attenuationDistance !== void 0) {
1082
+ parsedMaterial.uniforms.attenuationDistance = extension.attenuationDistance;
1083
+ }
1084
+ if (extension.attenuationColor) {
1085
+ parsedMaterial.uniforms.attenuationColor = extension.attenuationColor;
1086
+ }
1087
+ }
1088
+ function parseClearcoatExtension(device, extension, parsedMaterial, gltf) {
1089
+ if (!extension) {
1090
+ return;
1091
+ }
1092
+ if (extension.clearcoatFactor !== void 0) {
1093
+ parsedMaterial.uniforms.clearcoatFactor = extension.clearcoatFactor;
1094
+ }
1095
+ if (extension.clearcoatRoughnessFactor !== void 0) {
1096
+ parsedMaterial.uniforms.clearcoatRoughnessFactor = extension.clearcoatRoughnessFactor;
1097
+ }
1098
+ if (extension.clearcoatTexture) {
1099
+ addTexture(device, extension.clearcoatTexture, "pbr_clearcoatSampler", parsedMaterial, {
1100
+ featureOptions: {
1101
+ define: "HAS_CLEARCOATMAP",
1102
+ enabledUniformName: "clearcoatMapEnabled"
1103
+ },
1104
+ gltf
1105
+ });
1106
+ }
1107
+ if (extension.clearcoatRoughnessTexture) {
1108
+ addTexture(
1109
+ device,
1110
+ extension.clearcoatRoughnessTexture,
1111
+ "pbr_clearcoatRoughnessSampler",
1112
+ parsedMaterial,
1113
+ {
1114
+ featureOptions: {
1115
+ define: "HAS_CLEARCOATROUGHNESSMAP",
1116
+ enabledUniformName: "clearcoatRoughnessMapEnabled"
1117
+ },
1118
+ gltf
1119
+ }
1120
+ );
1121
+ }
1122
+ if (extension.clearcoatNormalTexture) {
1123
+ addTexture(
1124
+ device,
1125
+ extension.clearcoatNormalTexture,
1126
+ "pbr_clearcoatNormalSampler",
1127
+ parsedMaterial,
1128
+ {
1129
+ featureOptions: {
1130
+ define: "HAS_CLEARCOATNORMALMAP"
1131
+ },
1132
+ gltf
1133
+ }
1134
+ );
1135
+ }
1136
+ }
1137
+ function parseSheenExtension(device, extension, parsedMaterial, gltf) {
1138
+ if (!extension) {
1139
+ return;
1140
+ }
1141
+ if (extension.sheenColorFactor) {
1142
+ parsedMaterial.uniforms.sheenColorFactor = extension.sheenColorFactor;
1143
+ }
1144
+ if (extension.sheenRoughnessFactor !== void 0) {
1145
+ parsedMaterial.uniforms.sheenRoughnessFactor = extension.sheenRoughnessFactor;
1146
+ }
1147
+ if (extension.sheenColorTexture) {
1148
+ addTexture(device, extension.sheenColorTexture, "pbr_sheenColorSampler", parsedMaterial, {
1149
+ featureOptions: {
1150
+ define: "HAS_SHEENCOLORMAP",
1151
+ enabledUniformName: "sheenColorMapEnabled"
1152
+ },
1153
+ gltf
1154
+ });
1155
+ }
1156
+ if (extension.sheenRoughnessTexture) {
1157
+ addTexture(
1158
+ device,
1159
+ extension.sheenRoughnessTexture,
1160
+ "pbr_sheenRoughnessSampler",
1161
+ parsedMaterial,
1162
+ {
1163
+ featureOptions: {
1164
+ define: "HAS_SHEENROUGHNESSMAP",
1165
+ enabledUniformName: "sheenRoughnessMapEnabled"
1166
+ },
1167
+ gltf
1168
+ }
1169
+ );
1170
+ }
1171
+ }
1172
+ function parseIridescenceExtension(device, extension, parsedMaterial, gltf) {
1173
+ if (!extension) {
1174
+ return;
1175
+ }
1176
+ if (extension.iridescenceFactor !== void 0) {
1177
+ parsedMaterial.uniforms.iridescenceFactor = extension.iridescenceFactor;
1178
+ }
1179
+ if (extension.iridescenceIor !== void 0) {
1180
+ parsedMaterial.uniforms.iridescenceIor = extension.iridescenceIor;
1181
+ }
1182
+ if (extension.iridescenceThicknessMinimum !== void 0 || extension.iridescenceThicknessMaximum !== void 0) {
1183
+ parsedMaterial.uniforms.iridescenceThicknessRange = [
1184
+ extension.iridescenceThicknessMinimum ?? 100,
1185
+ extension.iridescenceThicknessMaximum ?? 400
1186
+ ];
1187
+ }
1188
+ if (extension.iridescenceTexture) {
1189
+ addTexture(device, extension.iridescenceTexture, "pbr_iridescenceSampler", parsedMaterial, {
1190
+ featureOptions: {
1191
+ define: "HAS_IRIDESCENCEMAP",
1192
+ enabledUniformName: "iridescenceMapEnabled"
1193
+ },
1194
+ gltf
1195
+ });
1196
+ }
1197
+ if (extension.iridescenceThicknessTexture) {
1198
+ addTexture(
1199
+ device,
1200
+ extension.iridescenceThicknessTexture,
1201
+ "pbr_iridescenceThicknessSampler",
1202
+ parsedMaterial,
1203
+ {
1204
+ featureOptions: {
1205
+ define: "HAS_IRIDESCENCETHICKNESSMAP"
1206
+ },
1207
+ gltf
1208
+ }
1209
+ );
1210
+ }
1211
+ }
1212
+ function parseAnisotropyExtension(device, extension, parsedMaterial, gltf) {
1213
+ if (!extension) {
1214
+ return;
1215
+ }
1216
+ if (extension.anisotropyStrength !== void 0) {
1217
+ parsedMaterial.uniforms.anisotropyStrength = extension.anisotropyStrength;
1218
+ }
1219
+ if (extension.anisotropyRotation !== void 0) {
1220
+ parsedMaterial.uniforms.anisotropyRotation = extension.anisotropyRotation;
1221
+ }
1222
+ if (extension.anisotropyTexture) {
1223
+ addTexture(device, extension.anisotropyTexture, "pbr_anisotropySampler", parsedMaterial, {
1224
+ featureOptions: {
1225
+ define: "HAS_ANISOTROPYMAP",
1226
+ enabledUniformName: "anisotropyMapEnabled"
1227
+ },
1228
+ gltf
1229
+ });
1230
+ }
1231
+ }
1232
+ function parseEmissiveStrengthExtension(extension, parsedMaterial) {
1233
+ if (extension?.emissiveStrength !== void 0) {
1234
+ parsedMaterial.uniforms.emissiveStrength = extension.emissiveStrength;
1235
+ }
1236
+ }
1237
+ function addTexture(device, gltfTexture, uniformName, parsedMaterial, textureParseOptions = {}) {
1238
+ const { featureOptions = {}, gltf } = textureParseOptions;
1239
+ const { define, enabledUniformName } = featureOptions;
1240
+ const resolvedTextureInfo = resolveTextureInfo(gltfTexture, gltf);
1241
+ const image = resolvedTextureInfo.texture?.source?.image;
1242
+ if (!image) {
1243
+ import_core.log.warn(`Skipping unresolved glTF texture for ${String(uniformName)}`)();
1244
+ return;
1245
+ }
882
1246
  const gltfSampler = {
883
1247
  wrapS: 10497,
884
1248
  // default REPEAT S (U) wrapping mode.
@@ -888,10 +1252,10 @@ var __exports__ = (() => {
888
1252
  // default LINEAR filtering
889
1253
  magFilter: 9729,
890
1254
  // default LINEAR filtering
891
- ...gltfTexture?.texture?.sampler
1255
+ ...resolvedTextureInfo?.texture?.sampler
892
1256
  };
893
1257
  const baseOptions = {
894
- id: gltfTexture.uniformName || gltfTexture.id,
1258
+ id: resolvedTextureInfo.uniformName || resolvedTextureInfo.id,
895
1259
  sampler: convertSampler(gltfSampler)
896
1260
  };
897
1261
  let texture;
@@ -909,8 +1273,34 @@ var __exports__ = (() => {
909
1273
  parsedMaterial.bindings[uniformName] = texture;
910
1274
  if (define)
911
1275
  parsedMaterial.defines[define] = true;
1276
+ if (enabledUniformName) {
1277
+ parsedMaterial.uniforms[enabledUniformName] = true;
1278
+ }
912
1279
  parsedMaterial.generatedTextures.push(texture);
913
1280
  }
1281
+ function resolveTextureInfo(gltfTexture, gltf) {
1282
+ if (gltfTexture.texture || gltfTexture.index === void 0 || !gltf?.textures) {
1283
+ return gltfTexture;
1284
+ }
1285
+ const resolvedTextureEntry = gltf.textures[gltfTexture.index];
1286
+ if (!resolvedTextureEntry) {
1287
+ return gltfTexture;
1288
+ }
1289
+ if ("texture" in resolvedTextureEntry && resolvedTextureEntry.texture) {
1290
+ return {
1291
+ ...resolvedTextureEntry,
1292
+ ...gltfTexture,
1293
+ texture: resolvedTextureEntry.texture
1294
+ };
1295
+ }
1296
+ if (!("source" in resolvedTextureEntry)) {
1297
+ return gltfTexture;
1298
+ }
1299
+ return {
1300
+ ...gltfTexture,
1301
+ texture: resolvedTextureEntry
1302
+ };
1303
+ }
914
1304
  function createCompressedTextureFallback(device, baseOptions) {
915
1305
  return device.createTexture({
916
1306
  ...baseOptions,
@@ -3579,6 +3969,7 @@ var __exports__ = (() => {
3579
3969
  };
3580
3970
 
3581
3971
  // src/parsers/parse-gltf-lights.ts
3972
+ var GLTF_COLOR_FACTOR = 255;
3582
3973
  function parseGLTFLights(gltf) {
3583
3974
  const lightDefs = (
3584
3975
  // `postProcessGLTF()` moves KHR_lights_punctual into `gltf.lights`.
@@ -3588,6 +3979,8 @@ var __exports__ = (() => {
3588
3979
  return [];
3589
3980
  }
3590
3981
  const lights = [];
3982
+ const parentNodeById = createParentNodeMap(gltf.nodes || []);
3983
+ const worldMatrixByNodeId = /* @__PURE__ */ new Map();
3591
3984
  for (const node of gltf.nodes || []) {
3592
3985
  const lightIndex = node.light ?? node.extensions?.KHR_lights_punctual?.light;
3593
3986
  if (typeof lightIndex !== "number") {
@@ -3597,18 +3990,21 @@ var __exports__ = (() => {
3597
3990
  if (!gltfLight) {
3598
3991
  continue;
3599
3992
  }
3600
- const color = gltfLight.color || [1, 1, 1];
3993
+ const color = normalizeGLTFLightColor(
3994
+ gltfLight.color || [1, 1, 1]
3995
+ );
3601
3996
  const intensity = gltfLight.intensity ?? 1;
3602
3997
  const range = gltfLight.range;
3998
+ const worldMatrix = getNodeWorldMatrix(node, parentNodeById, worldMatrixByNodeId);
3603
3999
  switch (gltfLight.type) {
3604
4000
  case "directional":
3605
- lights.push(parseDirectionalLight(node, color, intensity));
4001
+ lights.push(parseDirectionalLight(worldMatrix, color, intensity));
3606
4002
  break;
3607
4003
  case "point":
3608
- lights.push(parsePointLight(node, color, intensity, range));
4004
+ lights.push(parsePointLight(worldMatrix, color, intensity, range));
3609
4005
  break;
3610
4006
  case "spot":
3611
- lights.push(parsePointLight(node, color, intensity, range));
4007
+ lights.push(parseSpotLight(worldMatrix, color, intensity, range, gltfLight.spot));
3612
4008
  break;
3613
4009
  default:
3614
4010
  break;
@@ -3616,8 +4012,11 @@ var __exports__ = (() => {
3616
4012
  }
3617
4013
  return lights;
3618
4014
  }
3619
- function parsePointLight(node, color, intensity, range) {
3620
- const position = getNodePosition(node);
4015
+ function normalizeGLTFLightColor(color) {
4016
+ return color.map((component) => component * GLTF_COLOR_FACTOR);
4017
+ }
4018
+ function parsePointLight(worldMatrix, color, intensity, range) {
4019
+ const position = getLightPosition(worldMatrix);
3621
4020
  let attenuation = [1, 0, 0];
3622
4021
  if (range !== void 0 && range > 0) {
3623
4022
  attenuation = [1, 0, 1 / (range * range)];
@@ -3630,8 +4029,8 @@ var __exports__ = (() => {
3630
4029
  attenuation
3631
4030
  };
3632
4031
  }
3633
- function parseDirectionalLight(node, color, intensity) {
3634
- const direction = getNodeDirection(node);
4032
+ function parseDirectionalLight(worldMatrix, color, intensity) {
4033
+ const direction = getLightDirection(worldMatrix);
3635
4034
  return {
3636
4035
  type: "directional",
3637
4036
  direction,
@@ -3639,27 +4038,72 @@ var __exports__ = (() => {
3639
4038
  intensity
3640
4039
  };
3641
4040
  }
3642
- function getNodePosition(node) {
3643
- if (node.matrix) {
3644
- return new Matrix4(node.matrix).transformAsPoint([0, 0, 0]);
4041
+ function parseSpotLight(worldMatrix, color, intensity, range, spot = {}) {
4042
+ const position = getLightPosition(worldMatrix);
4043
+ const direction = getLightDirection(worldMatrix);
4044
+ let attenuation = [1, 0, 0];
4045
+ if (range !== void 0 && range > 0) {
4046
+ attenuation = [1, 0, 1 / (range * range)];
3645
4047
  }
3646
- if (node.translation) {
3647
- return [...node.translation];
4048
+ return {
4049
+ type: "spot",
4050
+ position,
4051
+ direction,
4052
+ color,
4053
+ intensity,
4054
+ attenuation,
4055
+ innerConeAngle: spot.innerConeAngle ?? 0,
4056
+ outerConeAngle: spot.outerConeAngle ?? Math.PI / 4
4057
+ };
4058
+ }
4059
+ function createParentNodeMap(nodes) {
4060
+ const parentNodeById = /* @__PURE__ */ new Map();
4061
+ for (const node of nodes) {
4062
+ for (const childNode of node.children || []) {
4063
+ parentNodeById.set(childNode.id, node);
4064
+ }
4065
+ }
4066
+ return parentNodeById;
4067
+ }
4068
+ function getNodeWorldMatrix(node, parentNodeById, worldMatrixByNodeId) {
4069
+ const cachedWorldMatrix = worldMatrixByNodeId.get(node.id);
4070
+ if (cachedWorldMatrix) {
4071
+ return cachedWorldMatrix;
3648
4072
  }
3649
- return [0, 0, 0];
4073
+ const localMatrix = getNodeLocalMatrix(node);
4074
+ const parentNode = parentNodeById.get(node.id);
4075
+ const worldMatrix = parentNode ? new Matrix4(
4076
+ getNodeWorldMatrix(parentNode, parentNodeById, worldMatrixByNodeId)
4077
+ ).multiplyRight(localMatrix) : localMatrix;
4078
+ worldMatrixByNodeId.set(node.id, worldMatrix);
4079
+ return worldMatrix;
3650
4080
  }
3651
- function getNodeDirection(node) {
4081
+ function getNodeLocalMatrix(node) {
3652
4082
  if (node.matrix) {
3653
- return new Matrix4(node.matrix).transformDirection([0, 0, -1]);
4083
+ return new Matrix4(node.matrix);
4084
+ }
4085
+ const matrix = new Matrix4();
4086
+ if (node.translation) {
4087
+ matrix.translate(node.translation);
3654
4088
  }
3655
4089
  if (node.rotation) {
3656
- return new Matrix4().fromQuaternion(node.rotation).transformDirection([0, 0, -1]);
4090
+ matrix.multiplyRight(new Matrix4().fromQuaternion(node.rotation));
4091
+ }
4092
+ if (node.scale) {
4093
+ matrix.scale(node.scale);
3657
4094
  }
3658
- return [0, 0, -1];
4095
+ return matrix;
4096
+ }
4097
+ function getLightPosition(worldMatrix) {
4098
+ return worldMatrix.transformAsPoint([0, 0, 0]);
4099
+ }
4100
+ function getLightDirection(worldMatrix) {
4101
+ return worldMatrix.transformDirection([0, 0, -1]);
3659
4102
  }
3660
4103
 
3661
4104
  // src/parsers/parse-gltf.ts
3662
- var import_engine3 = __toESM(require_engine(), 1);
4105
+ var import_engine4 = __toESM(require_engine(), 1);
4106
+ var import_shadertools2 = __toESM(require_shadertools(), 1);
3663
4107
 
3664
4108
  // src/webgl-to-webgpu/convert-webgl-topology.ts
3665
4109
  function convertGLDrawModeToTopology(drawMode) {
@@ -3681,8 +4125,9 @@ var __exports__ = (() => {
3681
4125
 
3682
4126
  // src/gltf/create-gltf-model.ts
3683
4127
  var import_core3 = __toESM(require_core(), 1);
3684
- var import_shadertools = __toESM(require_shadertools(), 1);
3685
4128
  var import_engine2 = __toESM(require_engine(), 1);
4129
+ var import_shadertools = __toESM(require_shadertools(), 1);
4130
+ var import_engine3 = __toESM(require_engine(), 1);
3686
4131
  var SHADER = (
3687
4132
  /* WGSL */
3688
4133
  `
@@ -3838,6 +4283,25 @@ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
3838
4283
  }
3839
4284
  `
3840
4285
  );
4286
+ function createGLTFMaterial(device, options) {
4287
+ const materialFactory = options.materialFactory || new import_engine3.MaterialFactory(device, { modules: [import_shadertools.pbrMaterial] });
4288
+ const pbrMaterialProps = { ...options.parsedPPBRMaterial.uniforms };
4289
+ delete pbrMaterialProps.camera;
4290
+ const materialBindings = Object.fromEntries(
4291
+ Object.entries({
4292
+ ...pbrMaterialProps,
4293
+ ...options.parsedPPBRMaterial.bindings
4294
+ }).filter(
4295
+ ([name, value]) => materialFactory.ownsBinding(name) && isMaterialBindingResource(value)
4296
+ )
4297
+ );
4298
+ const material = materialFactory.createMaterial({
4299
+ id: options.id,
4300
+ bindings: materialBindings
4301
+ });
4302
+ material.setProps({ pbrMaterial: pbrMaterialProps });
4303
+ return material;
4304
+ }
3841
4305
  function createGLTFModel(device, options) {
3842
4306
  const { id, geometry, parsedPPBRMaterial, vertexCount, modelOptions = {} } = options;
3843
4307
  import_core3.log.info(4, "createGLTFModel defines: ", parsedPPBRMaterial.defines)();
@@ -3861,15 +4325,52 @@ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
3861
4325
  defines: { ...parsedPPBRMaterial.defines, ...modelOptions.defines },
3862
4326
  parameters: { ...parameters, ...parsedPPBRMaterial.parameters, ...modelOptions.parameters }
3863
4327
  };
3864
- const model = new import_engine2.Model(device, modelProps);
3865
- const { camera, ...pbrMaterialProps } = {
4328
+ const material = options.material || createGLTFMaterial(device, {
4329
+ id: id ? `${id}-material` : void 0,
4330
+ parsedPPBRMaterial
4331
+ });
4332
+ modelProps.material = material;
4333
+ const model = new import_engine3.Model(device, modelProps);
4334
+ const sceneShaderInputValues = {
3866
4335
  ...parsedPPBRMaterial.uniforms,
3867
4336
  ...modelOptions.uniforms,
3868
4337
  ...parsedPPBRMaterial.bindings,
3869
4338
  ...modelOptions.bindings
3870
4339
  };
3871
- model.shaderInputs.setProps({ pbrMaterial: pbrMaterialProps, pbrProjection: { camera } });
3872
- return new import_engine2.ModelNode({ managedResources, model });
4340
+ const sceneShaderInputProps = getSceneShaderInputProps(
4341
+ model.shaderInputs.getModules(),
4342
+ material,
4343
+ sceneShaderInputValues
4344
+ );
4345
+ model.shaderInputs.setProps(sceneShaderInputProps);
4346
+ return new import_engine3.ModelNode({ managedResources, model });
4347
+ }
4348
+ function isMaterialBindingResource(value) {
4349
+ return value instanceof import_core3.Buffer || value instanceof import_engine2.DynamicTexture || value instanceof import_core3.Sampler || value instanceof import_core3.Texture || value instanceof import_core3.TextureView;
4350
+ }
4351
+ function getSceneShaderInputProps(modules, material, shaderInputValues) {
4352
+ const propertyToModuleNameMap = /* @__PURE__ */ new Map();
4353
+ for (const module of modules) {
4354
+ for (const uniformName of Object.keys(module.uniformTypes || {})) {
4355
+ propertyToModuleNameMap.set(uniformName, module.name);
4356
+ }
4357
+ for (const binding of module.bindingLayout || []) {
4358
+ propertyToModuleNameMap.set(binding.name, module.name);
4359
+ }
4360
+ }
4361
+ const sceneShaderInputProps = {};
4362
+ for (const [propertyName, value] of Object.entries(shaderInputValues)) {
4363
+ if (value === void 0) {
4364
+ continue;
4365
+ }
4366
+ const moduleName = propertyToModuleNameMap.get(propertyName);
4367
+ if (!moduleName || material.ownsModule(moduleName)) {
4368
+ continue;
4369
+ }
4370
+ sceneShaderInputProps[moduleName] ||= {};
4371
+ sceneShaderInputProps[moduleName][propertyName] = value;
4372
+ }
4373
+ return sceneShaderInputProps;
3873
4374
  }
3874
4375
 
3875
4376
  // src/parsers/parse-gltf.ts
@@ -3882,9 +4383,36 @@ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
3882
4383
  };
3883
4384
  function parseGLTF(device, gltf, options = {}) {
3884
4385
  const combinedOptions = { ...defaultOptions, ...options };
4386
+ const materialFactory = new import_engine4.MaterialFactory(device, { modules: [import_shadertools2.pbrMaterial] });
4387
+ const materials = (gltf.materials || []).map(
4388
+ (gltfMaterial, materialIndex) => createGLTFMaterial(device, {
4389
+ id: getGLTFMaterialId(gltfMaterial, materialIndex),
4390
+ parsedPPBRMaterial: parsePBRMaterial(
4391
+ device,
4392
+ gltfMaterial,
4393
+ {},
4394
+ {
4395
+ ...combinedOptions,
4396
+ gltf,
4397
+ validateAttributes: false
4398
+ }
4399
+ ),
4400
+ materialFactory
4401
+ })
4402
+ );
4403
+ const gltfMaterialIdToMaterialMap = /* @__PURE__ */ new Map();
4404
+ (gltf.materials || []).forEach((gltfMaterial, materialIndex) => {
4405
+ gltfMaterialIdToMaterialMap.set(gltfMaterial.id, materials[materialIndex]);
4406
+ });
3885
4407
  const gltfMeshIdToNodeMap = /* @__PURE__ */ new Map();
3886
4408
  gltf.meshes.forEach((gltfMesh, idx) => {
3887
- const newMesh = createNodeForGLTFMesh(device, gltfMesh, combinedOptions);
4409
+ const newMesh = createNodeForGLTFMesh(
4410
+ device,
4411
+ gltfMesh,
4412
+ gltf,
4413
+ gltfMaterialIdToMaterialMap,
4414
+ combinedOptions
4415
+ );
3888
4416
  gltfMeshIdToNodeMap.set(gltfMesh.id, newMesh);
3889
4417
  });
3890
4418
  const gltfNodeIndexToNodeMap = /* @__PURE__ */ new Map();
@@ -3918,15 +4446,15 @@ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
3918
4446
  throw new Error(`Cannot find child ${id} of scene ${gltfScene.name || gltfScene.id}`);
3919
4447
  return child;
3920
4448
  });
3921
- return new import_engine3.GroupNode({
4449
+ return new import_engine4.GroupNode({
3922
4450
  id: gltfScene.name || gltfScene.id,
3923
4451
  children
3924
4452
  });
3925
4453
  });
3926
- return { scenes, gltfMeshIdToNodeMap, gltfNodeIdToNodeMap, gltfNodeIndexToNodeMap };
4454
+ return { scenes, materials, gltfMeshIdToNodeMap, gltfNodeIdToNodeMap, gltfNodeIndexToNodeMap };
3927
4455
  }
3928
4456
  function createNodeForGLTFNode(device, gltfNode, options) {
3929
- return new import_engine3.GroupNode({
4457
+ return new import_engine4.GroupNode({
3930
4458
  id: gltfNode.name || gltfNode.id,
3931
4459
  children: [],
3932
4460
  matrix: gltfNode.matrix,
@@ -3935,31 +4463,46 @@ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
3935
4463
  scale: gltfNode.scale
3936
4464
  });
3937
4465
  }
3938
- function createNodeForGLTFMesh(device, gltfMesh, options) {
4466
+ function createNodeForGLTFMesh(device, gltfMesh, gltf, gltfMaterialIdToMaterialMap, options) {
3939
4467
  const gltfPrimitives = gltfMesh.primitives || [];
3940
4468
  const primitives = gltfPrimitives.map(
3941
- (gltfPrimitive, i) => createNodeForGLTFPrimitive(device, gltfPrimitive, i, gltfMesh, options)
4469
+ (gltfPrimitive, i) => createNodeForGLTFPrimitive({
4470
+ device,
4471
+ gltfPrimitive,
4472
+ primitiveIndex: i,
4473
+ gltfMesh,
4474
+ gltf,
4475
+ gltfMaterialIdToMaterialMap,
4476
+ options
4477
+ })
3942
4478
  );
3943
- const mesh = new import_engine3.GroupNode({
4479
+ const mesh = new import_engine4.GroupNode({
3944
4480
  id: gltfMesh.name || gltfMesh.id,
3945
4481
  children: primitives
3946
4482
  });
3947
4483
  return mesh;
3948
4484
  }
3949
- function createNodeForGLTFPrimitive(device, gltfPrimitive, i, gltfMesh, options) {
3950
- const id = gltfPrimitive.name || `${gltfMesh.name || gltfMesh.id}-primitive-${i}`;
4485
+ function createNodeForGLTFPrimitive({
4486
+ device,
4487
+ gltfPrimitive,
4488
+ primitiveIndex,
4489
+ gltfMesh,
4490
+ gltf,
4491
+ gltfMaterialIdToMaterialMap,
4492
+ options
4493
+ }) {
4494
+ const id = gltfPrimitive.name || `${gltfMesh.name || gltfMesh.id}-primitive-${primitiveIndex}`;
3951
4495
  const topology = convertGLDrawModeToTopology(gltfPrimitive.mode || 4);
3952
4496
  const vertexCount = gltfPrimitive.indices ? gltfPrimitive.indices.count : getVertexCount(gltfPrimitive.attributes);
3953
4497
  const geometry = createGeometry(id, gltfPrimitive, topology);
3954
- const parsedPPBRMaterial = parsePBRMaterial(
3955
- device,
3956
- gltfPrimitive.material,
3957
- geometry.attributes,
3958
- options
3959
- );
4498
+ const parsedPPBRMaterial = parsePBRMaterial(device, gltfPrimitive.material, geometry.attributes, {
4499
+ ...options,
4500
+ gltf
4501
+ });
3960
4502
  const modelNode = createGLTFModel(device, {
3961
4503
  id,
3962
4504
  geometry: createGeometry(id, gltfPrimitive, topology),
4505
+ material: gltfPrimitive.material ? gltfMaterialIdToMaterialMap.get(gltfPrimitive.material.id) || null : null,
3963
4506
  parsedPPBRMaterial,
3964
4507
  modelOptions: options.modelOptions,
3965
4508
  vertexCount
@@ -3976,13 +4519,16 @@ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
3976
4519
  const { components, size, value } = attribute;
3977
4520
  attributes[attributeName] = { size: size ?? components, value };
3978
4521
  }
3979
- return new import_engine3.Geometry({
4522
+ return new import_engine4.Geometry({
3980
4523
  id,
3981
4524
  topology,
3982
4525
  indices: gltfPrimitive.indices.value,
3983
4526
  attributes
3984
4527
  });
3985
4528
  }
4529
+ function getGLTFMaterialId(gltfMaterial, materialIndex) {
4530
+ return gltfMaterial.name || gltfMaterial.id || `material-${materialIndex}`;
4531
+ }
3986
4532
 
3987
4533
  // src/gltf/gltf-animator.ts
3988
4534
  var import_core6 = __toESM(require_core(), 1);
@@ -4129,6 +4675,9 @@ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
4129
4675
  }
4130
4676
  };
4131
4677
 
4678
+ // src/parsers/parse-gltf-animations.ts
4679
+ var import_core7 = __toESM(require_core(), 1);
4680
+
4132
4681
  // src/webgl-to-webgpu/convert-webgl-attribute.ts
4133
4682
  var ATTRIBUTE_TYPE_TO_COMPONENTS = {
4134
4683
  SCALAR: 1,
@@ -4161,29 +4710,48 @@ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
4161
4710
  const gltfAnimations = gltf.animations || [];
4162
4711
  const accessorCache1D = /* @__PURE__ */ new Map();
4163
4712
  const accessorCache2D = /* @__PURE__ */ new Map();
4164
- return gltfAnimations.map((animation, index) => {
4713
+ return gltfAnimations.flatMap((animation, index) => {
4165
4714
  const name = animation.name || `Animation-${index}`;
4166
- const samplers = animation.samplers.map(
4167
- ({ input, interpolation = "LINEAR", output }) => ({
4168
- input: accessorToJsArray1D(gltf.accessors[input], accessorCache1D),
4169
- interpolation,
4170
- output: accessorToJsArray2D(gltf.accessors[output], accessorCache2D)
4171
- })
4172
- );
4173
- const channels = animation.channels.map(({ sampler, target }) => {
4715
+ const samplerCache = /* @__PURE__ */ new Map();
4716
+ const channels = animation.channels.flatMap(({ sampler, target }) => {
4717
+ const path = getSupportedAnimationPath(target.path);
4718
+ if (!path) {
4719
+ return [];
4720
+ }
4174
4721
  const targetNode = gltf.nodes[target.node ?? 0];
4175
4722
  if (!targetNode) {
4176
4723
  throw new Error(`Cannot find animation target ${target.node}`);
4177
4724
  }
4725
+ let parsedSampler = samplerCache.get(sampler);
4726
+ if (!parsedSampler) {
4727
+ const gltfSampler = animation.samplers[sampler];
4728
+ if (!gltfSampler) {
4729
+ throw new Error(`Cannot find animation sampler ${sampler}`);
4730
+ }
4731
+ const { input, interpolation = "LINEAR", output } = gltfSampler;
4732
+ parsedSampler = {
4733
+ input: accessorToJsArray1D(gltf.accessors[input], accessorCache1D),
4734
+ interpolation,
4735
+ output: accessorToJsArray2D(gltf.accessors[output], accessorCache2D)
4736
+ };
4737
+ samplerCache.set(sampler, parsedSampler);
4738
+ }
4178
4739
  return {
4179
- sampler: samplers[sampler],
4740
+ sampler: parsedSampler,
4180
4741
  targetNodeId: targetNode.id,
4181
- path: target.path
4742
+ path
4182
4743
  };
4183
4744
  });
4184
- return { name, channels };
4745
+ return channels.length ? [{ name, channels }] : [];
4185
4746
  });
4186
4747
  }
4748
+ function getSupportedAnimationPath(path) {
4749
+ if (path === "pointer") {
4750
+ import_core7.log.warn("KHR_animation_pointer channels are not supported and will be skipped")();
4751
+ return null;
4752
+ }
4753
+ return path;
4754
+ }
4187
4755
  function accessorToJsArray1D(accessor, accessorCache) {
4188
4756
  if (accessorCache.has(accessor)) {
4189
4757
  return accessorCache.get(accessor);
@@ -4199,7 +4767,7 @@ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
4199
4767
  return accessorCache.get(accessor);
4200
4768
  }
4201
4769
  const { typedArray: array, components } = accessorToTypedArray(accessor);
4202
- assert3(components > 1, "accessorToJsArray2D must have more than 1 component");
4770
+ assert3(components >= 1, "accessorToJsArray2D must have at least 1 component");
4203
4771
  const result = [];
4204
4772
  for (let i = 0; i < array.length; i += components) {
4205
4773
  result.push(Array.from(array.slice(i, i + components)));
@@ -4213,26 +4781,259 @@ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
4213
4781
  }
4214
4782
  }
4215
4783
 
4784
+ // src/gltf/gltf-extension-support.ts
4785
+ var UNKNOWN_EXTENSION_SUPPORT = {
4786
+ supportLevel: "none",
4787
+ comment: "Not currently listed in the luma.gl glTF extension support registry."
4788
+ };
4789
+ var GLTF_EXTENSION_SUPPORT_REGISTRY = {
4790
+ KHR_draco_mesh_compression: {
4791
+ supportLevel: "built-in",
4792
+ comment: "Decoded by loaders.gl before luma.gl builds the scenegraph."
4793
+ },
4794
+ EXT_meshopt_compression: {
4795
+ supportLevel: "built-in",
4796
+ comment: "Meshopt-compressed primitives are decoded during load."
4797
+ },
4798
+ KHR_mesh_quantization: {
4799
+ supportLevel: "built-in",
4800
+ comment: "Quantized accessors are unpacked before geometry creation."
4801
+ },
4802
+ KHR_lights_punctual: {
4803
+ supportLevel: "built-in",
4804
+ comment: "Parsed into luma.gl Light objects."
4805
+ },
4806
+ KHR_materials_unlit: {
4807
+ supportLevel: "built-in",
4808
+ comment: "Unlit materials bypass the default lighting path."
4809
+ },
4810
+ KHR_materials_emissive_strength: {
4811
+ supportLevel: "built-in",
4812
+ comment: "Applied by the stock PBR shader."
4813
+ },
4814
+ KHR_texture_basisu: {
4815
+ supportLevel: "built-in",
4816
+ comment: "BasisU / KTX2 textures pass through when the device supports them."
4817
+ },
4818
+ KHR_texture_transform: {
4819
+ supportLevel: "built-in",
4820
+ comment: "UV transforms are applied during load."
4821
+ },
4822
+ EXT_texture_webp: {
4823
+ supportLevel: "loader-only",
4824
+ comment: "Texture source is resolved during load; final support depends on browser and device decode support."
4825
+ },
4826
+ EXT_texture_avif: {
4827
+ supportLevel: "loader-only",
4828
+ comment: "Texture source is resolved during load; final support depends on browser and device decode support."
4829
+ },
4830
+ KHR_materials_specular: {
4831
+ supportLevel: "built-in",
4832
+ comment: "The stock shader now applies specular factors and textures to the dielectric F0 term."
4833
+ },
4834
+ KHR_materials_ior: {
4835
+ supportLevel: "built-in",
4836
+ comment: "The stock shader now drives dielectric reflectance from the glTF IOR value."
4837
+ },
4838
+ KHR_materials_transmission: {
4839
+ supportLevel: "built-in",
4840
+ comment: "The stock shader now applies transmission to the base layer and exposes transparency through alpha, without a scene-color refraction buffer."
4841
+ },
4842
+ KHR_materials_volume: {
4843
+ supportLevel: "built-in",
4844
+ comment: "Thickness and attenuation now tint transmitted light in the stock shader."
4845
+ },
4846
+ KHR_materials_clearcoat: {
4847
+ supportLevel: "built-in",
4848
+ comment: "The stock shader now adds a secondary clearcoat specular lobe."
4849
+ },
4850
+ KHR_materials_sheen: {
4851
+ supportLevel: "built-in",
4852
+ comment: "The stock shader now adds a sheen lobe for cloth-like materials."
4853
+ },
4854
+ KHR_materials_iridescence: {
4855
+ supportLevel: "built-in",
4856
+ comment: "The stock shader now tints specular response with a view-dependent thin-film iridescence approximation."
4857
+ },
4858
+ KHR_materials_anisotropy: {
4859
+ supportLevel: "built-in",
4860
+ comment: "The stock shader now shapes highlights and IBL response with an anisotropy-direction approximation."
4861
+ },
4862
+ KHR_materials_pbrSpecularGlossiness: {
4863
+ supportLevel: "loader-only",
4864
+ comment: "Extension data can be loaded, but it is not translated into the default metallic-roughness material path."
4865
+ },
4866
+ KHR_materials_variants: {
4867
+ supportLevel: "loader-only",
4868
+ comment: "Variant metadata can be loaded, but applications must choose and apply variants."
4869
+ },
4870
+ EXT_mesh_gpu_instancing: {
4871
+ supportLevel: "none",
4872
+ comment: "GPU instancing data is not yet converted into luma.gl instanced draw setup."
4873
+ },
4874
+ KHR_node_visibility: {
4875
+ supportLevel: "none",
4876
+ comment: "Node-visibility animations and toggles are not mapped onto runtime scenegraph state."
4877
+ },
4878
+ KHR_animation_pointer: {
4879
+ supportLevel: "none",
4880
+ comment: "Animation pointers are not mapped onto runtime scenegraph updates."
4881
+ },
4882
+ KHR_materials_diffuse_transmission: {
4883
+ supportLevel: "none",
4884
+ comment: "Diffuse-transmission shading is not implemented in the stock PBR shader."
4885
+ },
4886
+ KHR_materials_dispersion: {
4887
+ supportLevel: "none",
4888
+ comment: "Chromatic dispersion is not implemented in the stock PBR shader."
4889
+ },
4890
+ KHR_materials_volume_scatter: {
4891
+ supportLevel: "none",
4892
+ comment: "Volume scattering is not implemented in the stock PBR shader."
4893
+ },
4894
+ KHR_xmp: {
4895
+ supportLevel: "none",
4896
+ comment: "Metadata payloads remain in the loaded glTF, but luma.gl does not interpret them."
4897
+ },
4898
+ KHR_xmp_json_ld: {
4899
+ supportLevel: "none",
4900
+ comment: "Metadata is preserved in the glTF, but luma.gl does not interpret it."
4901
+ },
4902
+ EXT_lights_image_based: {
4903
+ supportLevel: "none",
4904
+ comment: "Use loadPBREnvironment() or custom environment setup instead."
4905
+ },
4906
+ EXT_texture_video: {
4907
+ supportLevel: "none",
4908
+ comment: "Video textures are not created automatically by the stock pipeline."
4909
+ },
4910
+ MSFT_lod: {
4911
+ supportLevel: "none",
4912
+ comment: "Level-of-detail switching is not implemented in the stock scenegraph loader."
4913
+ }
4914
+ };
4915
+ function getGLTFExtensionSupport(gltf) {
4916
+ const extensionNames = Array.from(collectGLTFExtensionNames(gltf)).sort();
4917
+ const extensionSupportEntries = extensionNames.map(
4918
+ (extensionName) => {
4919
+ const extensionSupportDefinition = GLTF_EXTENSION_SUPPORT_REGISTRY[extensionName] || UNKNOWN_EXTENSION_SUPPORT;
4920
+ return [
4921
+ extensionName,
4922
+ {
4923
+ extensionName,
4924
+ supported: extensionSupportDefinition.supportLevel === "built-in",
4925
+ supportLevel: extensionSupportDefinition.supportLevel,
4926
+ comment: extensionSupportDefinition.comment
4927
+ }
4928
+ ];
4929
+ }
4930
+ );
4931
+ return new Map(extensionSupportEntries);
4932
+ }
4933
+ function collectGLTFExtensionNames(gltf) {
4934
+ const gltfWithRemovedExtensions = gltf;
4935
+ const extensionNames = /* @__PURE__ */ new Set();
4936
+ addExtensionNames(extensionNames, gltf.extensionsUsed);
4937
+ addExtensionNames(extensionNames, gltf.extensionsRequired);
4938
+ addExtensionNames(extensionNames, gltfWithRemovedExtensions.extensionsRemoved);
4939
+ addExtensionNames(extensionNames, Object.keys(gltf.extensions || {}));
4940
+ if (gltfWithRemovedExtensions.lights?.length || gltf.nodes.some((node) => "light" in node)) {
4941
+ extensionNames.add("KHR_lights_punctual");
4942
+ }
4943
+ if (gltf.materials.some((material) => {
4944
+ const gltfMaterial = material;
4945
+ return gltfMaterial.unlit || gltfMaterial.extensions?.KHR_materials_unlit;
4946
+ })) {
4947
+ extensionNames.add("KHR_materials_unlit");
4948
+ }
4949
+ return extensionNames;
4950
+ }
4951
+ function addExtensionNames(extensionNames, newExtensionNames = []) {
4952
+ for (const extensionName of newExtensionNames) {
4953
+ extensionNames.add(extensionName);
4954
+ }
4955
+ }
4956
+
4216
4957
  // src/gltf/create-scenegraph-from-gltf.ts
4217
4958
  function createScenegraphsFromGLTF(device, gltf, options) {
4218
- const { scenes, gltfMeshIdToNodeMap, gltfNodeIdToNodeMap, gltfNodeIndexToNodeMap } = parseGLTF(
4219
- device,
4220
- gltf,
4221
- options
4222
- );
4959
+ const { scenes, materials, gltfMeshIdToNodeMap, gltfNodeIdToNodeMap, gltfNodeIndexToNodeMap } = parseGLTF(device, gltf, options);
4223
4960
  const animations = parseGLTFAnimations(gltf);
4224
4961
  const animator = new GLTFAnimator({ animations, gltfNodeIdToNodeMap });
4225
4962
  const lights = parseGLTFLights(gltf);
4963
+ const extensionSupport = getGLTFExtensionSupport(gltf);
4964
+ const sceneBounds = scenes.map((scene) => getScenegraphBounds(scene.getBounds()));
4965
+ const modelBounds = getCombinedScenegraphBounds(sceneBounds);
4226
4966
  return {
4227
4967
  scenes,
4968
+ materials,
4228
4969
  animator,
4229
4970
  lights,
4971
+ extensionSupport,
4972
+ sceneBounds,
4973
+ modelBounds,
4230
4974
  gltfMeshIdToNodeMap,
4231
4975
  gltfNodeIdToNodeMap,
4232
4976
  gltfNodeIndexToNodeMap,
4233
4977
  gltf
4234
4978
  };
4235
4979
  }
4980
+ function getScenegraphBounds(bounds) {
4981
+ if (!bounds) {
4982
+ return {
4983
+ bounds: null,
4984
+ center: [0, 0, 0],
4985
+ size: [0, 0, 0],
4986
+ radius: 0.5,
4987
+ recommendedOrbitDistance: 1
4988
+ };
4989
+ }
4990
+ const normalizedBounds = [
4991
+ [bounds[0][0], bounds[0][1], bounds[0][2]],
4992
+ [bounds[1][0], bounds[1][1], bounds[1][2]]
4993
+ ];
4994
+ const size = [
4995
+ normalizedBounds[1][0] - normalizedBounds[0][0],
4996
+ normalizedBounds[1][1] - normalizedBounds[0][1],
4997
+ normalizedBounds[1][2] - normalizedBounds[0][2]
4998
+ ];
4999
+ const center = [
5000
+ normalizedBounds[0][0] + size[0] * 0.5,
5001
+ normalizedBounds[0][1] + size[1] * 0.5,
5002
+ normalizedBounds[0][2] + size[2] * 0.5
5003
+ ];
5004
+ const maxHalfExtent = Math.max(size[0], size[1], size[2]) * 0.5;
5005
+ const radius = Math.max(0.5 * Math.hypot(size[0], size[1], size[2]), 1e-3);
5006
+ return {
5007
+ bounds: normalizedBounds,
5008
+ center,
5009
+ size,
5010
+ radius,
5011
+ recommendedOrbitDistance: Math.max(
5012
+ Math.max(maxHalfExtent, 1e-3) / Math.tan(Math.PI / 6) * 1.15,
5013
+ radius * 1.1
5014
+ )
5015
+ };
5016
+ }
5017
+ function getCombinedScenegraphBounds(sceneBounds) {
5018
+ let combinedBounds = null;
5019
+ for (const sceneBoundInfo of sceneBounds) {
5020
+ if (!sceneBoundInfo.bounds) {
5021
+ continue;
5022
+ }
5023
+ if (!combinedBounds) {
5024
+ combinedBounds = [
5025
+ [...sceneBoundInfo.bounds[0]],
5026
+ [...sceneBoundInfo.bounds[1]]
5027
+ ];
5028
+ continue;
5029
+ }
5030
+ for (let axis = 0; axis < 3; axis++) {
5031
+ combinedBounds[0][axis] = Math.min(combinedBounds[0][axis], sceneBoundInfo.bounds[0][axis]);
5032
+ combinedBounds[1][axis] = Math.max(combinedBounds[1][axis], sceneBoundInfo.bounds[1][axis]);
5033
+ }
5034
+ }
5035
+ return getScenegraphBounds(combinedBounds);
5036
+ }
4236
5037
  return __toCommonJS(bundle_exports);
4237
5038
  })();
4238
5039
  return __exports__;