@luma.gl/gltf 9.2.6 → 9.3.0-alpha.10

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 (91) hide show
  1. package/dist/dist.dev.js +1362 -313
  2. package/dist/dist.min.js +98 -46
  3. package/dist/gltf/animations/animations.d.ts +16 -4
  4. package/dist/gltf/animations/animations.d.ts.map +1 -1
  5. package/dist/gltf/animations/interpolate.d.ts +4 -3
  6. package/dist/gltf/animations/interpolate.d.ts.map +1 -1
  7. package/dist/gltf/animations/interpolate.js +27 -36
  8. package/dist/gltf/animations/interpolate.js.map +1 -1
  9. package/dist/gltf/create-gltf-model.d.ts +15 -1
  10. package/dist/gltf/create-gltf-model.d.ts.map +1 -1
  11. package/dist/gltf/create-gltf-model.js +154 -48
  12. package/dist/gltf/create-gltf-model.js.map +1 -1
  13. package/dist/gltf/create-scenegraph-from-gltf.d.ts +39 -2
  14. package/dist/gltf/create-scenegraph-from-gltf.d.ts.map +1 -1
  15. package/dist/gltf/create-scenegraph-from-gltf.js +76 -6
  16. package/dist/gltf/create-scenegraph-from-gltf.js.map +1 -1
  17. package/dist/gltf/gltf-animator.d.ts +26 -0
  18. package/dist/gltf/gltf-animator.d.ts.map +1 -1
  19. package/dist/gltf/gltf-animator.js +22 -19
  20. package/dist/gltf/gltf-animator.js.map +1 -1
  21. package/dist/gltf/gltf-extension-support.d.ts +10 -0
  22. package/dist/gltf/gltf-extension-support.d.ts.map +1 -0
  23. package/dist/gltf/gltf-extension-support.js +173 -0
  24. package/dist/gltf/gltf-extension-support.js.map +1 -0
  25. package/dist/index.cjs +1302 -276
  26. package/dist/index.cjs.map +4 -4
  27. package/dist/index.d.ts +3 -2
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +2 -0
  30. package/dist/index.js.map +1 -1
  31. package/dist/parsers/parse-gltf-animations.d.ts +1 -0
  32. package/dist/parsers/parse-gltf-animations.d.ts.map +1 -1
  33. package/dist/parsers/parse-gltf-animations.js +73 -28
  34. package/dist/parsers/parse-gltf-animations.js.map +1 -1
  35. package/dist/parsers/parse-gltf-lights.d.ts +5 -0
  36. package/dist/parsers/parse-gltf-lights.d.ts.map +1 -0
  37. package/dist/parsers/parse-gltf-lights.js +163 -0
  38. package/dist/parsers/parse-gltf-lights.js.map +1 -0
  39. package/dist/parsers/parse-gltf.d.ts +19 -2
  40. package/dist/parsers/parse-gltf.d.ts.map +1 -1
  41. package/dist/parsers/parse-gltf.js +101 -61
  42. package/dist/parsers/parse-gltf.js.map +1 -1
  43. package/dist/parsers/parse-pbr-material.d.ts +115 -2
  44. package/dist/parsers/parse-pbr-material.d.ts.map +1 -1
  45. package/dist/parsers/parse-pbr-material.js +570 -54
  46. package/dist/parsers/parse-pbr-material.js.map +1 -1
  47. package/dist/pbr/pbr-environment.d.ts +10 -4
  48. package/dist/pbr/pbr-environment.d.ts.map +1 -1
  49. package/dist/pbr/pbr-environment.js +18 -15
  50. package/dist/pbr/pbr-environment.js.map +1 -1
  51. package/dist/pbr/pbr-material.d.ts +13 -3
  52. package/dist/pbr/pbr-material.d.ts.map +1 -1
  53. package/dist/webgl-to-webgpu/convert-webgl-attribute.d.ts +12 -1
  54. package/dist/webgl-to-webgpu/convert-webgl-attribute.d.ts.map +1 -1
  55. package/dist/webgl-to-webgpu/convert-webgl-attribute.js +3 -0
  56. package/dist/webgl-to-webgpu/convert-webgl-attribute.js.map +1 -1
  57. package/dist/webgl-to-webgpu/convert-webgl-sampler.d.ts +11 -5
  58. package/dist/webgl-to-webgpu/convert-webgl-sampler.d.ts.map +1 -1
  59. package/dist/webgl-to-webgpu/convert-webgl-sampler.js +16 -12
  60. package/dist/webgl-to-webgpu/convert-webgl-sampler.js.map +1 -1
  61. package/dist/webgl-to-webgpu/convert-webgl-topology.d.ts +2 -9
  62. package/dist/webgl-to-webgpu/convert-webgl-topology.d.ts.map +1 -1
  63. package/dist/webgl-to-webgpu/convert-webgl-topology.js +2 -14
  64. package/dist/webgl-to-webgpu/convert-webgl-topology.js.map +1 -1
  65. package/dist/webgl-to-webgpu/gltf-webgl-constants.d.ts +27 -0
  66. package/dist/webgl-to-webgpu/gltf-webgl-constants.d.ts.map +1 -0
  67. package/dist/webgl-to-webgpu/gltf-webgl-constants.js +34 -0
  68. package/dist/webgl-to-webgpu/gltf-webgl-constants.js.map +1 -0
  69. package/package.json +8 -9
  70. package/src/gltf/animations/animations.ts +17 -5
  71. package/src/gltf/animations/interpolate.ts +49 -68
  72. package/src/gltf/create-gltf-model.ts +214 -48
  73. package/src/gltf/create-scenegraph-from-gltf.ts +134 -11
  74. package/src/gltf/gltf-animator.ts +34 -25
  75. package/src/gltf/gltf-extension-support.ts +214 -0
  76. package/src/index.ts +11 -2
  77. package/src/parsers/parse-gltf-animations.ts +94 -33
  78. package/src/parsers/parse-gltf-lights.ts +218 -0
  79. package/src/parsers/parse-gltf.ts +170 -90
  80. package/src/parsers/parse-pbr-material.ts +870 -80
  81. package/src/pbr/pbr-environment.ts +44 -21
  82. package/src/pbr/pbr-material.ts +18 -3
  83. package/src/webgl-to-webgpu/convert-webgl-attribute.ts +12 -1
  84. package/src/webgl-to-webgpu/convert-webgl-sampler.ts +38 -29
  85. package/src/webgl-to-webgpu/convert-webgl-topology.ts +2 -14
  86. package/src/webgl-to-webgpu/gltf-webgl-constants.ts +35 -0
  87. package/dist/utils/deep-copy.d.ts +0 -3
  88. package/dist/utils/deep-copy.d.ts.map +0 -1
  89. package/dist/utils/deep-copy.js +0 -21
  90. package/dist/utils/deep-copy.js.map +0 -1
  91. package/src/utils/deep-copy.ts +0 -22
package/dist/index.cjs CHANGED
@@ -22,7 +22,9 @@ var dist_exports = {};
22
22
  __export(dist_exports, {
23
23
  GLTFAnimator: () => GLTFAnimator,
24
24
  createScenegraphsFromGLTF: () => createScenegraphsFromGLTF,
25
+ getGLTFExtensionSupport: () => getGLTFExtensionSupport,
25
26
  loadPBREnvironment: () => loadPBREnvironment,
27
+ parseGLTFLights: () => parseGLTFLights,
26
28
  parsePBRMaterial: () => parsePBRMaterial
27
29
  });
28
30
  module.exports = __toCommonJS(dist_exports);
@@ -31,7 +33,8 @@ module.exports = __toCommonJS(dist_exports);
31
33
  var import_engine = require("@luma.gl/engine");
32
34
  var import_textures = require("@loaders.gl/textures");
33
35
  function loadPBREnvironment(device, props) {
34
- const brdfLutTexture = new import_engine.AsyncTexture(device, {
36
+ const specularMipLevels = props.specularMipLevels ?? 1;
37
+ const brdfLutTexture = new import_engine.DynamicTexture(device, {
35
38
  id: "brdfLUT",
36
39
  sampler: {
37
40
  addressModeU: "clamp-to-edge",
@@ -44,7 +47,7 @@ function loadPBREnvironment(device, props) {
44
47
  });
45
48
  const diffuseEnvSampler = makeCube(device, {
46
49
  id: "DiffuseEnvSampler",
47
- getTextureForFace: (dir) => (0, import_textures.loadImageTexture)(props.getTexUrl("diffuse", dir, 0)),
50
+ getTextureForFace: (face) => (0, import_textures.loadImageTexture)(props.getTexUrl("diffuse", FACES.indexOf(face), 0)),
48
51
  sampler: {
49
52
  addressModeU: "clamp-to-edge",
50
53
  addressModeV: "clamp-to-edge",
@@ -54,12 +57,13 @@ function loadPBREnvironment(device, props) {
54
57
  });
55
58
  const specularEnvSampler = makeCube(device, {
56
59
  id: "SpecularEnvSampler",
57
- getTextureForFace: (dir) => {
60
+ getTextureForFace: (face) => {
58
61
  const imageArray = [];
59
- for (let lod = 0; lod <= props.specularMipLevels - 1; lod++) {
60
- imageArray.push((0, import_textures.loadImageTexture)(props.getTexUrl("specular", dir, lod)));
62
+ const direction = FACES.indexOf(face);
63
+ for (let lod = 0; lod < specularMipLevels; lod++) {
64
+ imageArray.push((0, import_textures.loadImageTexture)(props.getTexUrl("specular", direction, lod)));
61
65
  }
62
- return imageArray;
66
+ return Promise.all(imageArray);
63
67
  },
64
68
  sampler: {
65
69
  addressModeU: "clamp-to-edge",
@@ -75,28 +79,57 @@ function loadPBREnvironment(device, props) {
75
79
  specularEnvSampler
76
80
  };
77
81
  }
78
- var FACES = [0, 1, 2, 3, 4, 5];
82
+ var FACES = ["+X", "-X", "+Y", "-Y", "+Z", "-Z"];
79
83
  function makeCube(device, { id, getTextureForFace, sampler }) {
80
- const data = {};
81
- FACES.forEach((face) => {
82
- data[String(face)] = getTextureForFace(face);
84
+ const data = Promise.all(FACES.map((face) => getTextureForFace(face))).then((faceDataArray) => {
85
+ const cubeData = {};
86
+ FACES.forEach((face, index) => {
87
+ cubeData[face] = faceDataArray[index];
88
+ });
89
+ return cubeData;
83
90
  });
84
- return new import_engine.AsyncTexture(device, {
91
+ return new import_engine.DynamicTexture(device, {
85
92
  id,
86
93
  dimension: "cube",
87
94
  mipmaps: false,
88
95
  sampler,
89
- // @ts-expect-error
90
96
  data
91
97
  });
92
98
  }
93
99
 
94
100
  // dist/parsers/parse-pbr-material.js
95
- var import_constants2 = require("@luma.gl/constants");
96
101
  var import_core = require("@luma.gl/core");
97
102
 
103
+ // dist/webgl-to-webgpu/gltf-webgl-constants.js
104
+ var GLEnum;
105
+ (function(GLEnum2) {
106
+ GLEnum2[GLEnum2["POINTS"] = 0] = "POINTS";
107
+ GLEnum2[GLEnum2["LINES"] = 1] = "LINES";
108
+ GLEnum2[GLEnum2["LINE_LOOP"] = 2] = "LINE_LOOP";
109
+ GLEnum2[GLEnum2["LINE_STRIP"] = 3] = "LINE_STRIP";
110
+ GLEnum2[GLEnum2["TRIANGLES"] = 4] = "TRIANGLES";
111
+ GLEnum2[GLEnum2["TRIANGLE_STRIP"] = 5] = "TRIANGLE_STRIP";
112
+ GLEnum2[GLEnum2["TRIANGLE_FAN"] = 6] = "TRIANGLE_FAN";
113
+ GLEnum2[GLEnum2["ONE"] = 1] = "ONE";
114
+ GLEnum2[GLEnum2["SRC_ALPHA"] = 770] = "SRC_ALPHA";
115
+ GLEnum2[GLEnum2["ONE_MINUS_SRC_ALPHA"] = 771] = "ONE_MINUS_SRC_ALPHA";
116
+ GLEnum2[GLEnum2["FUNC_ADD"] = 32774] = "FUNC_ADD";
117
+ GLEnum2[GLEnum2["LINEAR"] = 9729] = "LINEAR";
118
+ GLEnum2[GLEnum2["NEAREST"] = 9728] = "NEAREST";
119
+ GLEnum2[GLEnum2["NEAREST_MIPMAP_NEAREST"] = 9984] = "NEAREST_MIPMAP_NEAREST";
120
+ GLEnum2[GLEnum2["LINEAR_MIPMAP_NEAREST"] = 9985] = "LINEAR_MIPMAP_NEAREST";
121
+ GLEnum2[GLEnum2["NEAREST_MIPMAP_LINEAR"] = 9986] = "NEAREST_MIPMAP_LINEAR";
122
+ GLEnum2[GLEnum2["LINEAR_MIPMAP_LINEAR"] = 9987] = "LINEAR_MIPMAP_LINEAR";
123
+ GLEnum2[GLEnum2["TEXTURE_MIN_FILTER"] = 10241] = "TEXTURE_MIN_FILTER";
124
+ GLEnum2[GLEnum2["TEXTURE_WRAP_S"] = 10242] = "TEXTURE_WRAP_S";
125
+ GLEnum2[GLEnum2["TEXTURE_WRAP_T"] = 10243] = "TEXTURE_WRAP_T";
126
+ GLEnum2[GLEnum2["REPEAT"] = 10497] = "REPEAT";
127
+ GLEnum2[GLEnum2["CLAMP_TO_EDGE"] = 33071] = "CLAMP_TO_EDGE";
128
+ GLEnum2[GLEnum2["MIRRORED_REPEAT"] = 33648] = "MIRRORED_REPEAT";
129
+ GLEnum2[GLEnum2["UNPACK_FLIP_Y_WEBGL"] = 37440] = "UNPACK_FLIP_Y_WEBGL";
130
+ })(GLEnum || (GLEnum = {}));
131
+
98
132
  // dist/webgl-to-webgpu/convert-webgl-sampler.js
99
- var import_constants = require("@luma.gl/constants");
100
133
  function convertSampler(gltfSampler) {
101
134
  return {
102
135
  addressModeU: convertSamplerWrapMode(gltfSampler.wrapS),
@@ -107,11 +140,11 @@ function convertSampler(gltfSampler) {
107
140
  }
108
141
  function convertSamplerWrapMode(mode) {
109
142
  switch (mode) {
110
- case 33071:
143
+ case GLEnum.CLAMP_TO_EDGE:
111
144
  return "clamp-to-edge";
112
- case 10497:
145
+ case GLEnum.REPEAT:
113
146
  return "repeat";
114
- case 33648:
147
+ case GLEnum.MIRRORED_REPEAT:
115
148
  return "mirror-repeat";
116
149
  default:
117
150
  return void 0;
@@ -119,9 +152,9 @@ function convertSamplerWrapMode(mode) {
119
152
  }
120
153
  function convertSamplerMagFilter(mode) {
121
154
  switch (mode) {
122
- case 9728:
155
+ case GLEnum.NEAREST:
123
156
  return "nearest";
124
- case 9729:
157
+ case GLEnum.LINEAR:
125
158
  return "linear";
126
159
  default:
127
160
  return void 0;
@@ -129,17 +162,17 @@ function convertSamplerMagFilter(mode) {
129
162
  }
130
163
  function convertSamplerMinFilter(mode) {
131
164
  switch (mode) {
132
- case 9728:
165
+ case GLEnum.NEAREST:
133
166
  return { minFilter: "nearest" };
134
- case 9729:
167
+ case GLEnum.LINEAR:
135
168
  return { minFilter: "linear" };
136
- case 9984:
169
+ case GLEnum.NEAREST_MIPMAP_NEAREST:
137
170
  return { minFilter: "nearest", mipmapFilter: "nearest" };
138
- case 9985:
171
+ case GLEnum.LINEAR_MIPMAP_NEAREST:
139
172
  return { minFilter: "linear", mipmapFilter: "nearest" };
140
- case 9986:
173
+ case GLEnum.NEAREST_MIPMAP_LINEAR:
141
174
  return { minFilter: "nearest", mipmapFilter: "linear" };
142
- case 9987:
175
+ case GLEnum.LINEAR_MIPMAP_LINEAR:
143
176
  return { minFilter: "linear", mipmapFilter: "linear" };
144
177
  default:
145
178
  return {};
@@ -171,7 +204,8 @@ function parsePBRMaterial(device, material, attributes, options) {
171
204
  if (imageBasedLightingEnvironment) {
172
205
  parsedMaterial.bindings.pbr_diffuseEnvSampler = imageBasedLightingEnvironment.diffuseEnvSampler.texture;
173
206
  parsedMaterial.bindings.pbr_specularEnvSampler = imageBasedLightingEnvironment.specularEnvSampler.texture;
174
- parsedMaterial.bindings.pbr_BrdfLUT = imageBasedLightingEnvironment.brdfLutTexture.texture;
207
+ parsedMaterial.bindings.pbr_brdfLUT = imageBasedLightingEnvironment.brdfLutTexture.texture;
208
+ parsedMaterial.uniforms.IBLenabled = true;
175
209
  parsedMaterial.uniforms.scaleIBLAmbient = [1, 1];
176
210
  }
177
211
  if (options == null ? void 0 : options.pbrDebug) {
@@ -185,113 +219,713 @@ function parsePBRMaterial(device, material, attributes, options) {
185
219
  parsedMaterial.defines["HAS_TANGENTS"] = true;
186
220
  if (attributes["TEXCOORD_0"])
187
221
  parsedMaterial.defines["HAS_UV"] = true;
222
+ if (attributes["JOINTS_0"] && attributes["WEIGHTS_0"])
223
+ parsedMaterial.defines["HAS_SKIN"] = true;
224
+ if (attributes["COLOR_0"])
225
+ parsedMaterial.defines["HAS_COLORS"] = true;
188
226
  if (options == null ? void 0 : options.imageBasedLightingEnvironment)
189
227
  parsedMaterial.defines["USE_IBL"] = true;
190
228
  if (options == null ? void 0 : options.lights)
191
229
  parsedMaterial.defines["USE_LIGHTS"] = true;
192
230
  if (material) {
193
- parseMaterial(device, material, parsedMaterial);
231
+ if (options.validateAttributes !== false) {
232
+ warnOnMissingExpectedAttributes(material, attributes);
233
+ }
234
+ parseMaterial(device, material, parsedMaterial, options.gltf);
194
235
  }
195
236
  return parsedMaterial;
196
237
  }
197
- function parseMaterial(device, material, parsedMaterial) {
198
- parsedMaterial.uniforms.unlit = Boolean(material.unlit);
238
+ function warnOnMissingExpectedAttributes(material, attributes) {
239
+ var _a;
240
+ const uvDependentTextureSlots = getUvDependentTextureSlots(material);
241
+ if (uvDependentTextureSlots.length > 0 && !attributes["TEXCOORD_0"]) {
242
+ import_core.log.warn(`glTF material uses ${uvDependentTextureSlots.join(", ")} but primitive is missing TEXCOORD_0; textured shading will sample the default UV coordinates`)();
243
+ }
244
+ const isUnlitMaterial = Boolean(material.unlit || ((_a = material.extensions) == null ? void 0 : _a.KHR_materials_unlit));
245
+ if (isUnlitMaterial || attributes["NORMAL"]) {
246
+ return;
247
+ }
248
+ const missingNormalReason = material.normalTexture ? "lit PBR shading with normalTexture" : "lit PBR shading";
249
+ import_core.log.warn(`glTF primitive is missing NORMAL while using ${missingNormalReason}; shading will fall back to geometric normals`)();
250
+ }
251
+ function getUvDependentTextureSlots(material) {
252
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t;
253
+ const uvDependentTextureSlots = [];
254
+ if ((_a = material.pbrMetallicRoughness) == null ? void 0 : _a.baseColorTexture) {
255
+ uvDependentTextureSlots.push("baseColorTexture");
256
+ }
257
+ if ((_b = material.pbrMetallicRoughness) == null ? void 0 : _b.metallicRoughnessTexture) {
258
+ uvDependentTextureSlots.push("metallicRoughnessTexture");
259
+ }
260
+ if (material.normalTexture) {
261
+ uvDependentTextureSlots.push("normalTexture");
262
+ }
263
+ if (material.occlusionTexture) {
264
+ uvDependentTextureSlots.push("occlusionTexture");
265
+ }
266
+ if (material.emissiveTexture) {
267
+ uvDependentTextureSlots.push("emissiveTexture");
268
+ }
269
+ if ((_d = (_c = material.extensions) == null ? void 0 : _c.KHR_materials_specular) == null ? void 0 : _d.specularTexture) {
270
+ uvDependentTextureSlots.push("KHR_materials_specular.specularTexture");
271
+ }
272
+ if ((_f = (_e = material.extensions) == null ? void 0 : _e.KHR_materials_specular) == null ? void 0 : _f.specularColorTexture) {
273
+ uvDependentTextureSlots.push("KHR_materials_specular.specularColorTexture");
274
+ }
275
+ if ((_h = (_g = material.extensions) == null ? void 0 : _g.KHR_materials_transmission) == null ? void 0 : _h.transmissionTexture) {
276
+ uvDependentTextureSlots.push("KHR_materials_transmission.transmissionTexture");
277
+ }
278
+ if ((_j = (_i = material.extensions) == null ? void 0 : _i.KHR_materials_clearcoat) == null ? void 0 : _j.clearcoatTexture) {
279
+ uvDependentTextureSlots.push("KHR_materials_clearcoat.clearcoatTexture");
280
+ }
281
+ if ((_l = (_k = material.extensions) == null ? void 0 : _k.KHR_materials_clearcoat) == null ? void 0 : _l.clearcoatRoughnessTexture) {
282
+ uvDependentTextureSlots.push("KHR_materials_clearcoat.clearcoatRoughnessTexture");
283
+ }
284
+ if ((_n = (_m = material.extensions) == null ? void 0 : _m.KHR_materials_sheen) == null ? void 0 : _n.sheenColorTexture) {
285
+ uvDependentTextureSlots.push("KHR_materials_sheen.sheenColorTexture");
286
+ }
287
+ if ((_p = (_o = material.extensions) == null ? void 0 : _o.KHR_materials_sheen) == null ? void 0 : _p.sheenRoughnessTexture) {
288
+ uvDependentTextureSlots.push("KHR_materials_sheen.sheenRoughnessTexture");
289
+ }
290
+ if ((_r = (_q = material.extensions) == null ? void 0 : _q.KHR_materials_iridescence) == null ? void 0 : _r.iridescenceTexture) {
291
+ uvDependentTextureSlots.push("KHR_materials_iridescence.iridescenceTexture");
292
+ }
293
+ if ((_t = (_s = material.extensions) == null ? void 0 : _s.KHR_materials_anisotropy) == null ? void 0 : _t.anisotropyTexture) {
294
+ uvDependentTextureSlots.push("KHR_materials_anisotropy.anisotropyTexture");
295
+ }
296
+ return uvDependentTextureSlots;
297
+ }
298
+ function parseMaterial(device, material, parsedMaterial, gltf) {
299
+ var _a;
300
+ parsedMaterial.uniforms.unlit = Boolean(material.unlit || ((_a = material.extensions) == null ? void 0 : _a.KHR_materials_unlit));
199
301
  if (material.pbrMetallicRoughness) {
200
- parsePbrMetallicRoughness(device, material.pbrMetallicRoughness, parsedMaterial);
302
+ parsePbrMetallicRoughness(device, material.pbrMetallicRoughness, parsedMaterial, gltf);
201
303
  }
202
304
  if (material.normalTexture) {
203
- addTexture(device, material.normalTexture, "pbr_normalSampler", "HAS_NORMALMAP", parsedMaterial);
305
+ addTexture(device, material.normalTexture, "pbr_normalSampler", parsedMaterial, {
306
+ featureOptions: {
307
+ define: "HAS_NORMALMAP",
308
+ enabledUniformName: "normalMapEnabled"
309
+ },
310
+ gltf
311
+ });
204
312
  const { scale = 1 } = material.normalTexture;
205
313
  parsedMaterial.uniforms.normalScale = scale;
206
314
  }
207
315
  if (material.occlusionTexture) {
208
- addTexture(device, material.occlusionTexture, "pbr_occlusionSampler", "HAS_OCCLUSIONMAP", parsedMaterial);
316
+ addTexture(device, material.occlusionTexture, "pbr_occlusionSampler", parsedMaterial, {
317
+ featureOptions: {
318
+ define: "HAS_OCCLUSIONMAP",
319
+ enabledUniformName: "occlusionMapEnabled"
320
+ },
321
+ gltf
322
+ });
209
323
  const { strength = 1 } = material.occlusionTexture;
210
324
  parsedMaterial.uniforms.occlusionStrength = strength;
211
325
  }
326
+ parsedMaterial.uniforms.emissiveFactor = material.emissiveFactor || [0, 0, 0];
212
327
  if (material.emissiveTexture) {
213
- addTexture(device, material.emissiveTexture, "pbr_emissiveSampler", "HAS_EMISSIVEMAP", parsedMaterial);
214
- parsedMaterial.uniforms.emissiveFactor = material.emissiveFactor || [0, 0, 0];
328
+ addTexture(device, material.emissiveTexture, "pbr_emissiveSampler", parsedMaterial, {
329
+ featureOptions: {
330
+ define: "HAS_EMISSIVEMAP",
331
+ enabledUniformName: "emissiveMapEnabled"
332
+ },
333
+ gltf
334
+ });
215
335
  }
216
- switch (material.alphaMode || "MASK") {
217
- case "MASK":
336
+ parseMaterialExtensions(device, material.extensions, parsedMaterial, gltf);
337
+ switch (material.alphaMode || "OPAQUE") {
338
+ case "OPAQUE":
339
+ break;
340
+ case "MASK": {
218
341
  const { alphaCutoff = 0.5 } = material;
219
342
  parsedMaterial.defines["ALPHA_CUTOFF"] = true;
343
+ parsedMaterial.uniforms.alphaCutoffEnabled = true;
220
344
  parsedMaterial.uniforms.alphaCutoff = alphaCutoff;
221
345
  break;
346
+ }
222
347
  case "BLEND":
223
348
  import_core.log.warn("glTF BLEND alphaMode might not work well because it requires mesh sorting")();
224
- parsedMaterial.parameters.blend = true;
225
- parsedMaterial.parameters.blendColorOperation = "add";
226
- parsedMaterial.parameters.blendColorSrcFactor = "src-alpha";
227
- parsedMaterial.parameters.blendColorDstFactor = "one-minus-src-alpha";
228
- parsedMaterial.parameters.blendAlphaOperation = "add";
229
- parsedMaterial.parameters.blendAlphaSrcFactor = "one";
230
- parsedMaterial.parameters.blendAlphaDstFactor = "one-minus-src-alpha";
231
- parsedMaterial.glParameters["blend"] = true;
232
- parsedMaterial.glParameters["blendEquation"] = 32774;
233
- parsedMaterial.glParameters["blendFunc"] = [
234
- 770,
235
- 771,
236
- 1,
237
- 771
238
- ];
349
+ applyAlphaBlendParameters(parsedMaterial);
239
350
  break;
240
351
  }
241
352
  }
242
- function parsePbrMetallicRoughness(device, pbrMetallicRoughness, parsedMaterial) {
353
+ function applyAlphaBlendParameters(parsedMaterial) {
354
+ parsedMaterial.parameters.blend = true;
355
+ parsedMaterial.parameters.blendColorOperation = "add";
356
+ parsedMaterial.parameters.blendColorSrcFactor = "src-alpha";
357
+ parsedMaterial.parameters.blendColorDstFactor = "one-minus-src-alpha";
358
+ parsedMaterial.parameters.blendAlphaOperation = "add";
359
+ parsedMaterial.parameters.blendAlphaSrcFactor = "one";
360
+ parsedMaterial.parameters.blendAlphaDstFactor = "one-minus-src-alpha";
361
+ parsedMaterial.glParameters["blend"] = true;
362
+ parsedMaterial.glParameters["blendEquation"] = GLEnum.FUNC_ADD;
363
+ parsedMaterial.glParameters["blendFunc"] = [
364
+ GLEnum.SRC_ALPHA,
365
+ GLEnum.ONE_MINUS_SRC_ALPHA,
366
+ GLEnum.ONE,
367
+ GLEnum.ONE_MINUS_SRC_ALPHA
368
+ ];
369
+ }
370
+ function applyTransmissionBlendApproximation(parsedMaterial) {
371
+ parsedMaterial.parameters.blend = true;
372
+ parsedMaterial.parameters.depthWriteEnabled = false;
373
+ parsedMaterial.parameters.blendColorOperation = "add";
374
+ parsedMaterial.parameters.blendColorSrcFactor = "one";
375
+ parsedMaterial.parameters.blendColorDstFactor = "one-minus-src-alpha";
376
+ parsedMaterial.parameters.blendAlphaOperation = "add";
377
+ parsedMaterial.parameters.blendAlphaSrcFactor = "one";
378
+ parsedMaterial.parameters.blendAlphaDstFactor = "one-minus-src-alpha";
379
+ parsedMaterial.glParameters["blend"] = true;
380
+ parsedMaterial.glParameters["depthMask"] = false;
381
+ parsedMaterial.glParameters["blendEquation"] = GLEnum.FUNC_ADD;
382
+ parsedMaterial.glParameters["blendFunc"] = [
383
+ GLEnum.ONE,
384
+ GLEnum.ONE_MINUS_SRC_ALPHA,
385
+ GLEnum.ONE,
386
+ GLEnum.ONE_MINUS_SRC_ALPHA
387
+ ];
388
+ }
389
+ function parsePbrMetallicRoughness(device, pbrMetallicRoughness, parsedMaterial, gltf) {
243
390
  if (pbrMetallicRoughness.baseColorTexture) {
244
- addTexture(device, pbrMetallicRoughness.baseColorTexture, "pbr_baseColorSampler", "HAS_BASECOLORMAP", parsedMaterial);
391
+ addTexture(device, pbrMetallicRoughness.baseColorTexture, "pbr_baseColorSampler", parsedMaterial, {
392
+ featureOptions: {
393
+ define: "HAS_BASECOLORMAP",
394
+ enabledUniformName: "baseColorMapEnabled"
395
+ },
396
+ gltf
397
+ });
245
398
  }
246
399
  parsedMaterial.uniforms.baseColorFactor = pbrMetallicRoughness.baseColorFactor || [1, 1, 1, 1];
247
400
  if (pbrMetallicRoughness.metallicRoughnessTexture) {
248
- addTexture(device, pbrMetallicRoughness.metallicRoughnessTexture, "pbr_metallicRoughnessSampler", "HAS_METALROUGHNESSMAP", parsedMaterial);
401
+ addTexture(device, pbrMetallicRoughness.metallicRoughnessTexture, "pbr_metallicRoughnessSampler", parsedMaterial, {
402
+ featureOptions: {
403
+ define: "HAS_METALROUGHNESSMAP",
404
+ enabledUniformName: "metallicRoughnessMapEnabled"
405
+ },
406
+ gltf
407
+ });
249
408
  }
250
409
  const { metallicFactor = 1, roughnessFactor = 1 } = pbrMetallicRoughness;
251
410
  parsedMaterial.uniforms.metallicRoughnessValues = [metallicFactor, roughnessFactor];
252
411
  }
253
- function addTexture(device, gltfTexture, uniformName, define, parsedMaterial) {
254
- var _a;
255
- const image = gltfTexture.texture.source.image;
256
- let textureOptions;
257
- if (image.compressed) {
258
- textureOptions = image;
259
- } else {
260
- textureOptions = { data: image };
412
+ function parseMaterialExtensions(device, extensions, parsedMaterial, gltf) {
413
+ if (!extensions) {
414
+ return;
415
+ }
416
+ if (hasMaterialExtensionShading(extensions)) {
417
+ parsedMaterial.defines["USE_MATERIAL_EXTENSIONS"] = true;
418
+ }
419
+ parseSpecularExtension(device, extensions.KHR_materials_specular, parsedMaterial, gltf);
420
+ parseIorExtension(extensions.KHR_materials_ior, parsedMaterial);
421
+ parseTransmissionExtension(device, extensions.KHR_materials_transmission, parsedMaterial, gltf);
422
+ parseVolumeExtension(device, extensions.KHR_materials_volume, parsedMaterial, gltf);
423
+ parseClearcoatExtension(device, extensions.KHR_materials_clearcoat, parsedMaterial, gltf);
424
+ parseSheenExtension(device, extensions.KHR_materials_sheen, parsedMaterial, gltf);
425
+ parseIridescenceExtension(device, extensions.KHR_materials_iridescence, parsedMaterial, gltf);
426
+ parseAnisotropyExtension(device, extensions.KHR_materials_anisotropy, parsedMaterial, gltf);
427
+ parseEmissiveStrengthExtension(extensions.KHR_materials_emissive_strength, parsedMaterial);
428
+ }
429
+ function hasMaterialExtensionShading(extensions) {
430
+ return Boolean(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);
431
+ }
432
+ function parseSpecularExtension(device, extension, parsedMaterial, gltf) {
433
+ if (!extension) {
434
+ return;
435
+ }
436
+ if (extension.specularColorFactor) {
437
+ parsedMaterial.uniforms.specularColorFactor = extension.specularColorFactor;
438
+ }
439
+ if (extension.specularFactor !== void 0) {
440
+ parsedMaterial.uniforms.specularIntensityFactor = extension.specularFactor;
441
+ }
442
+ if (extension.specularColorTexture) {
443
+ addTexture(device, extension.specularColorTexture, "pbr_specularColorSampler", parsedMaterial, {
444
+ featureOptions: {
445
+ define: "HAS_SPECULARCOLORMAP",
446
+ enabledUniformName: "specularColorMapEnabled"
447
+ },
448
+ gltf
449
+ });
450
+ }
451
+ if (extension.specularTexture) {
452
+ addTexture(device, extension.specularTexture, "pbr_specularIntensitySampler", parsedMaterial, {
453
+ featureOptions: {
454
+ define: "HAS_SPECULARINTENSITYMAP",
455
+ enabledUniformName: "specularIntensityMapEnabled"
456
+ },
457
+ gltf
458
+ });
459
+ }
460
+ }
461
+ function parseIorExtension(extension, parsedMaterial) {
462
+ if ((extension == null ? void 0 : extension.ior) !== void 0) {
463
+ parsedMaterial.uniforms.ior = extension.ior;
464
+ }
465
+ }
466
+ function parseTransmissionExtension(device, extension, parsedMaterial, gltf) {
467
+ if (!extension) {
468
+ return;
469
+ }
470
+ if (extension.transmissionFactor !== void 0) {
471
+ parsedMaterial.uniforms.transmissionFactor = extension.transmissionFactor;
472
+ }
473
+ if (extension.transmissionTexture) {
474
+ addTexture(device, extension.transmissionTexture, "pbr_transmissionSampler", parsedMaterial, {
475
+ featureOptions: {
476
+ define: "HAS_TRANSMISSIONMAP",
477
+ enabledUniformName: "transmissionMapEnabled"
478
+ },
479
+ gltf
480
+ });
481
+ }
482
+ if ((extension.transmissionFactor ?? 0) > 0 || extension.transmissionTexture) {
483
+ import_core.log.warn("KHR_materials_transmission uses a premultiplied-alpha blending approximation and may require mesh sorting")();
484
+ applyTransmissionBlendApproximation(parsedMaterial);
485
+ }
486
+ }
487
+ function parseVolumeExtension(device, extension, parsedMaterial, gltf) {
488
+ if (!extension) {
489
+ return;
490
+ }
491
+ if (extension.thicknessFactor !== void 0) {
492
+ parsedMaterial.uniforms.thicknessFactor = extension.thicknessFactor;
493
+ }
494
+ if (extension.thicknessTexture) {
495
+ addTexture(device, extension.thicknessTexture, "pbr_thicknessSampler", parsedMaterial, {
496
+ featureOptions: {
497
+ define: "HAS_THICKNESSMAP"
498
+ },
499
+ gltf
500
+ });
501
+ }
502
+ if (extension.attenuationDistance !== void 0) {
503
+ parsedMaterial.uniforms.attenuationDistance = extension.attenuationDistance;
504
+ }
505
+ if (extension.attenuationColor) {
506
+ parsedMaterial.uniforms.attenuationColor = extension.attenuationColor;
507
+ }
508
+ }
509
+ function parseClearcoatExtension(device, extension, parsedMaterial, gltf) {
510
+ if (!extension) {
511
+ return;
512
+ }
513
+ if (extension.clearcoatFactor !== void 0) {
514
+ parsedMaterial.uniforms.clearcoatFactor = extension.clearcoatFactor;
515
+ }
516
+ if (extension.clearcoatRoughnessFactor !== void 0) {
517
+ parsedMaterial.uniforms.clearcoatRoughnessFactor = extension.clearcoatRoughnessFactor;
518
+ }
519
+ if (extension.clearcoatTexture) {
520
+ addTexture(device, extension.clearcoatTexture, "pbr_clearcoatSampler", parsedMaterial, {
521
+ featureOptions: {
522
+ define: "HAS_CLEARCOATMAP",
523
+ enabledUniformName: "clearcoatMapEnabled"
524
+ },
525
+ gltf
526
+ });
527
+ }
528
+ if (extension.clearcoatRoughnessTexture) {
529
+ addTexture(device, extension.clearcoatRoughnessTexture, "pbr_clearcoatRoughnessSampler", parsedMaterial, {
530
+ featureOptions: {
531
+ define: "HAS_CLEARCOATROUGHNESSMAP",
532
+ enabledUniformName: "clearcoatRoughnessMapEnabled"
533
+ },
534
+ gltf
535
+ });
536
+ }
537
+ if (extension.clearcoatNormalTexture) {
538
+ addTexture(device, extension.clearcoatNormalTexture, "pbr_clearcoatNormalSampler", parsedMaterial, {
539
+ featureOptions: {
540
+ define: "HAS_CLEARCOATNORMALMAP"
541
+ },
542
+ gltf
543
+ });
544
+ }
545
+ }
546
+ function parseSheenExtension(device, extension, parsedMaterial, gltf) {
547
+ if (!extension) {
548
+ return;
549
+ }
550
+ if (extension.sheenColorFactor) {
551
+ parsedMaterial.uniforms.sheenColorFactor = extension.sheenColorFactor;
552
+ }
553
+ if (extension.sheenRoughnessFactor !== void 0) {
554
+ parsedMaterial.uniforms.sheenRoughnessFactor = extension.sheenRoughnessFactor;
555
+ }
556
+ if (extension.sheenColorTexture) {
557
+ addTexture(device, extension.sheenColorTexture, "pbr_sheenColorSampler", parsedMaterial, {
558
+ featureOptions: {
559
+ define: "HAS_SHEENCOLORMAP",
560
+ enabledUniformName: "sheenColorMapEnabled"
561
+ },
562
+ gltf
563
+ });
564
+ }
565
+ if (extension.sheenRoughnessTexture) {
566
+ addTexture(device, extension.sheenRoughnessTexture, "pbr_sheenRoughnessSampler", parsedMaterial, {
567
+ featureOptions: {
568
+ define: "HAS_SHEENROUGHNESSMAP",
569
+ enabledUniformName: "sheenRoughnessMapEnabled"
570
+ },
571
+ gltf
572
+ });
573
+ }
574
+ }
575
+ function parseIridescenceExtension(device, extension, parsedMaterial, gltf) {
576
+ if (!extension) {
577
+ return;
578
+ }
579
+ if (extension.iridescenceFactor !== void 0) {
580
+ parsedMaterial.uniforms.iridescenceFactor = extension.iridescenceFactor;
581
+ }
582
+ if (extension.iridescenceIor !== void 0) {
583
+ parsedMaterial.uniforms.iridescenceIor = extension.iridescenceIor;
584
+ }
585
+ if (extension.iridescenceThicknessMinimum !== void 0 || extension.iridescenceThicknessMaximum !== void 0) {
586
+ parsedMaterial.uniforms.iridescenceThicknessRange = [
587
+ extension.iridescenceThicknessMinimum ?? 100,
588
+ extension.iridescenceThicknessMaximum ?? 400
589
+ ];
590
+ }
591
+ if (extension.iridescenceTexture) {
592
+ addTexture(device, extension.iridescenceTexture, "pbr_iridescenceSampler", parsedMaterial, {
593
+ featureOptions: {
594
+ define: "HAS_IRIDESCENCEMAP",
595
+ enabledUniformName: "iridescenceMapEnabled"
596
+ },
597
+ gltf
598
+ });
599
+ }
600
+ if (extension.iridescenceThicknessTexture) {
601
+ addTexture(device, extension.iridescenceThicknessTexture, "pbr_iridescenceThicknessSampler", parsedMaterial, {
602
+ featureOptions: {
603
+ define: "HAS_IRIDESCENCETHICKNESSMAP"
604
+ },
605
+ gltf
606
+ });
607
+ }
608
+ }
609
+ function parseAnisotropyExtension(device, extension, parsedMaterial, gltf) {
610
+ if (!extension) {
611
+ return;
612
+ }
613
+ if (extension.anisotropyStrength !== void 0) {
614
+ parsedMaterial.uniforms.anisotropyStrength = extension.anisotropyStrength;
615
+ }
616
+ if (extension.anisotropyRotation !== void 0) {
617
+ parsedMaterial.uniforms.anisotropyRotation = extension.anisotropyRotation;
618
+ }
619
+ if (extension.anisotropyTexture) {
620
+ addTexture(device, extension.anisotropyTexture, "pbr_anisotropySampler", parsedMaterial, {
621
+ featureOptions: {
622
+ define: "HAS_ANISOTROPYMAP",
623
+ enabledUniformName: "anisotropyMapEnabled"
624
+ },
625
+ gltf
626
+ });
627
+ }
628
+ }
629
+ function parseEmissiveStrengthExtension(extension, parsedMaterial) {
630
+ if ((extension == null ? void 0 : extension.emissiveStrength) !== void 0) {
631
+ parsedMaterial.uniforms.emissiveStrength = extension.emissiveStrength;
632
+ }
633
+ }
634
+ function addTexture(device, gltfTexture, uniformName, parsedMaterial, textureParseOptions = {}) {
635
+ var _a, _b, _c;
636
+ const { featureOptions = {}, gltf } = textureParseOptions;
637
+ const { define, enabledUniformName } = featureOptions;
638
+ const resolvedTextureInfo = resolveTextureInfo(gltfTexture, gltf);
639
+ const image = (_b = (_a = resolvedTextureInfo.texture) == null ? void 0 : _a.source) == null ? void 0 : _b.image;
640
+ if (!image) {
641
+ import_core.log.warn(`Skipping unresolved glTF texture for ${String(uniformName)}`)();
642
+ return;
261
643
  }
262
644
  const gltfSampler = {
263
645
  wrapS: 10497,
264
646
  // default REPEAT S (U) wrapping mode.
265
647
  wrapT: 10497,
266
648
  // default REPEAT T (V) wrapping mode.
267
- ...(_a = gltfTexture == null ? void 0 : gltfTexture.texture) == null ? void 0 : _a.sampler
649
+ minFilter: 9729,
650
+ // default LINEAR filtering
651
+ magFilter: 9729,
652
+ // default LINEAR filtering
653
+ ...(_c = resolvedTextureInfo == null ? void 0 : resolvedTextureInfo.texture) == null ? void 0 : _c.sampler
268
654
  };
269
- const texture = device.createTexture({
270
- id: gltfTexture.uniformName || gltfTexture.id,
271
- sampler: convertSampler(gltfSampler),
272
- ...textureOptions
273
- });
655
+ const baseOptions = {
656
+ id: resolvedTextureInfo.uniformName || resolvedTextureInfo.id,
657
+ sampler: convertSampler(gltfSampler)
658
+ };
659
+ let texture;
660
+ if (image.compressed) {
661
+ texture = createCompressedTexture(device, image, baseOptions);
662
+ } else {
663
+ const { width, height } = device.getExternalImageSize(image);
664
+ texture = device.createTexture({
665
+ ...baseOptions,
666
+ width,
667
+ height,
668
+ data: image
669
+ });
670
+ }
274
671
  parsedMaterial.bindings[uniformName] = texture;
275
672
  if (define)
276
673
  parsedMaterial.defines[define] = true;
674
+ if (enabledUniformName) {
675
+ parsedMaterial.uniforms[enabledUniformName] = true;
676
+ }
277
677
  parsedMaterial.generatedTextures.push(texture);
278
678
  }
679
+ function resolveTextureInfo(gltfTexture, gltf) {
680
+ if (gltfTexture.texture || gltfTexture.index === void 0 || !(gltf == null ? void 0 : gltf.textures)) {
681
+ return gltfTexture;
682
+ }
683
+ const resolvedTextureEntry = gltf.textures[gltfTexture.index];
684
+ if (!resolvedTextureEntry) {
685
+ return gltfTexture;
686
+ }
687
+ if ("texture" in resolvedTextureEntry && resolvedTextureEntry.texture) {
688
+ return {
689
+ ...resolvedTextureEntry,
690
+ ...gltfTexture,
691
+ texture: resolvedTextureEntry.texture
692
+ };
693
+ }
694
+ if (!("source" in resolvedTextureEntry)) {
695
+ return gltfTexture;
696
+ }
697
+ return {
698
+ ...gltfTexture,
699
+ texture: resolvedTextureEntry
700
+ };
701
+ }
702
+ function createCompressedTextureFallback(device, baseOptions) {
703
+ return device.createTexture({
704
+ ...baseOptions,
705
+ format: "rgba8unorm",
706
+ width: 1,
707
+ height: 1,
708
+ mipLevels: 1
709
+ });
710
+ }
711
+ function resolveCompressedTextureFormat(level) {
712
+ return level.textureFormat;
713
+ }
714
+ function getMaxCompressedMipLevels(baseWidth, baseHeight, format) {
715
+ const { blockWidth = 1, blockHeight = 1 } = import_core.textureFormatDecoder.getInfo(format);
716
+ let count = 1;
717
+ for (let i = 1; ; i++) {
718
+ const w = Math.max(1, baseWidth >> i);
719
+ const h = Math.max(1, baseHeight >> i);
720
+ if (w < blockWidth || h < blockHeight)
721
+ break;
722
+ count++;
723
+ }
724
+ return count;
725
+ }
726
+ function createCompressedTexture(device, image, baseOptions) {
727
+ var _a, _b;
728
+ let levels;
729
+ if (Array.isArray(image.data) && ((_a = image.data[0]) == null ? void 0 : _a.data)) {
730
+ levels = image.data;
731
+ } else if ("mipmaps" in image && Array.isArray(image.mipmaps)) {
732
+ levels = image.mipmaps;
733
+ } else {
734
+ levels = [];
735
+ }
736
+ if (levels.length === 0 || !((_b = levels[0]) == null ? void 0 : _b.data)) {
737
+ import_core.log.warn("createCompressedTexture: compressed image has no valid mip levels, creating fallback")();
738
+ return createCompressedTextureFallback(device, baseOptions);
739
+ }
740
+ const baseLevel = levels[0];
741
+ const baseWidth = baseLevel.width ?? image.width ?? 0;
742
+ const baseHeight = baseLevel.height ?? image.height ?? 0;
743
+ if (baseWidth <= 0 || baseHeight <= 0) {
744
+ import_core.log.warn("createCompressedTexture: base level has invalid dimensions, creating fallback")();
745
+ return createCompressedTextureFallback(device, baseOptions);
746
+ }
747
+ const format = resolveCompressedTextureFormat(baseLevel);
748
+ if (!format) {
749
+ import_core.log.warn("createCompressedTexture: compressed image has no textureFormat, creating fallback")();
750
+ return createCompressedTextureFallback(device, baseOptions);
751
+ }
752
+ const maxMipLevels = getMaxCompressedMipLevels(baseWidth, baseHeight, format);
753
+ const levelLimit = Math.min(levels.length, maxMipLevels);
754
+ let validLevelCount = 1;
755
+ for (let i = 1; i < levelLimit; i++) {
756
+ const level = levels[i];
757
+ if (!level.data || level.width <= 0 || level.height <= 0) {
758
+ import_core.log.warn(`createCompressedTexture: mip level ${i} has invalid data/dimensions, truncating`)();
759
+ break;
760
+ }
761
+ const levelFormat = resolveCompressedTextureFormat(level);
762
+ if (levelFormat && levelFormat !== format) {
763
+ import_core.log.warn(`createCompressedTexture: mip level ${i} format '${levelFormat}' differs from base '${format}', truncating`)();
764
+ break;
765
+ }
766
+ const expectedW = Math.max(1, baseWidth >> i);
767
+ const expectedH = Math.max(1, baseHeight >> i);
768
+ if (level.width !== expectedW || level.height !== expectedH) {
769
+ import_core.log.warn(`createCompressedTexture: mip level ${i} dimensions ${level.width}x${level.height} don't match expected ${expectedW}x${expectedH}, truncating`)();
770
+ break;
771
+ }
772
+ validLevelCount++;
773
+ }
774
+ const texture = device.createTexture({
775
+ ...baseOptions,
776
+ format,
777
+ usage: import_core.Texture.TEXTURE | import_core.Texture.COPY_DST,
778
+ width: baseWidth,
779
+ height: baseHeight,
780
+ mipLevels: validLevelCount,
781
+ data: baseLevel.data
782
+ });
783
+ for (let i = 1; i < validLevelCount; i++) {
784
+ texture.writeData(levels[i].data, {
785
+ width: levels[i].width,
786
+ height: levels[i].height,
787
+ mipLevel: i
788
+ });
789
+ }
790
+ return texture;
791
+ }
792
+
793
+ // dist/parsers/parse-gltf-lights.js
794
+ var import_core2 = require("@math.gl/core");
795
+ var GLTF_COLOR_FACTOR = 255;
796
+ function parseGLTFLights(gltf) {
797
+ var _a, _b, _c, _d;
798
+ const lightDefs = (
799
+ // `postProcessGLTF()` moves KHR_lights_punctual into `gltf.lights`.
800
+ gltf.lights || ((_b = (_a = gltf.extensions) == null ? void 0 : _a["KHR_lights_punctual"]) == null ? void 0 : _b["lights"])
801
+ );
802
+ if (!lightDefs || !Array.isArray(lightDefs) || lightDefs.length === 0) {
803
+ return [];
804
+ }
805
+ const lights = [];
806
+ const parentNodeById = createParentNodeMap(gltf.nodes || []);
807
+ const worldMatrixByNodeId = /* @__PURE__ */ new Map();
808
+ for (const node of gltf.nodes || []) {
809
+ const lightIndex = node.light ?? ((_d = (_c = node.extensions) == null ? void 0 : _c.KHR_lights_punctual) == null ? void 0 : _d.light);
810
+ if (typeof lightIndex !== "number") {
811
+ continue;
812
+ }
813
+ const gltfLight = lightDefs[lightIndex];
814
+ if (!gltfLight) {
815
+ continue;
816
+ }
817
+ const color = normalizeGLTFLightColor(gltfLight.color || [1, 1, 1]);
818
+ const intensity = gltfLight.intensity ?? 1;
819
+ const range = gltfLight.range;
820
+ const worldMatrix = getNodeWorldMatrix(node, parentNodeById, worldMatrixByNodeId);
821
+ switch (gltfLight.type) {
822
+ case "directional":
823
+ lights.push(parseDirectionalLight(worldMatrix, color, intensity));
824
+ break;
825
+ case "point":
826
+ lights.push(parsePointLight(worldMatrix, color, intensity, range));
827
+ break;
828
+ case "spot":
829
+ lights.push(parseSpotLight(worldMatrix, color, intensity, range, gltfLight.spot));
830
+ break;
831
+ default:
832
+ break;
833
+ }
834
+ }
835
+ return lights;
836
+ }
837
+ function normalizeGLTFLightColor(color) {
838
+ return color.map((component) => component * GLTF_COLOR_FACTOR);
839
+ }
840
+ function parsePointLight(worldMatrix, color, intensity, range) {
841
+ const position = getLightPosition(worldMatrix);
842
+ let attenuation = [1, 0, 0];
843
+ if (range !== void 0 && range > 0) {
844
+ attenuation = [1, 0, 1 / (range * range)];
845
+ }
846
+ return {
847
+ type: "point",
848
+ position,
849
+ color,
850
+ intensity,
851
+ attenuation
852
+ };
853
+ }
854
+ function parseDirectionalLight(worldMatrix, color, intensity) {
855
+ const direction = getLightDirection(worldMatrix);
856
+ return {
857
+ type: "directional",
858
+ direction,
859
+ color,
860
+ intensity
861
+ };
862
+ }
863
+ function parseSpotLight(worldMatrix, color, intensity, range, spot = {}) {
864
+ const position = getLightPosition(worldMatrix);
865
+ const direction = getLightDirection(worldMatrix);
866
+ let attenuation = [1, 0, 0];
867
+ if (range !== void 0 && range > 0) {
868
+ attenuation = [1, 0, 1 / (range * range)];
869
+ }
870
+ return {
871
+ type: "spot",
872
+ position,
873
+ direction,
874
+ color,
875
+ intensity,
876
+ attenuation,
877
+ innerConeAngle: spot.innerConeAngle ?? 0,
878
+ outerConeAngle: spot.outerConeAngle ?? Math.PI / 4
879
+ };
880
+ }
881
+ function createParentNodeMap(nodes) {
882
+ const parentNodeById = /* @__PURE__ */ new Map();
883
+ for (const node of nodes) {
884
+ for (const childNode of node.children || []) {
885
+ parentNodeById.set(childNode.id, node);
886
+ }
887
+ }
888
+ return parentNodeById;
889
+ }
890
+ function getNodeWorldMatrix(node, parentNodeById, worldMatrixByNodeId) {
891
+ const cachedWorldMatrix = worldMatrixByNodeId.get(node.id);
892
+ if (cachedWorldMatrix) {
893
+ return cachedWorldMatrix;
894
+ }
895
+ const localMatrix = getNodeLocalMatrix(node);
896
+ const parentNode = parentNodeById.get(node.id);
897
+ const worldMatrix = parentNode ? new import_core2.Matrix4(getNodeWorldMatrix(parentNode, parentNodeById, worldMatrixByNodeId)).multiplyRight(localMatrix) : localMatrix;
898
+ worldMatrixByNodeId.set(node.id, worldMatrix);
899
+ return worldMatrix;
900
+ }
901
+ function getNodeLocalMatrix(node) {
902
+ if (node.matrix) {
903
+ return new import_core2.Matrix4(node.matrix);
904
+ }
905
+ const matrix = new import_core2.Matrix4();
906
+ if (node.translation) {
907
+ matrix.translate(node.translation);
908
+ }
909
+ if (node.rotation) {
910
+ matrix.multiplyRight(new import_core2.Matrix4().fromQuaternion(node.rotation));
911
+ }
912
+ if (node.scale) {
913
+ matrix.scale(node.scale);
914
+ }
915
+ return matrix;
916
+ }
917
+ function getLightPosition(worldMatrix) {
918
+ return worldMatrix.transformAsPoint([0, 0, 0]);
919
+ }
920
+ function getLightDirection(worldMatrix) {
921
+ return worldMatrix.transformDirection([0, 0, -1]);
922
+ }
279
923
 
280
924
  // dist/parsers/parse-gltf.js
281
- var import_engine3 = require("@luma.gl/engine");
282
- var import_core3 = require("@math.gl/core");
925
+ var import_engine4 = require("@luma.gl/engine");
926
+ var import_shadertools2 = require("@luma.gl/shadertools");
283
927
 
284
928
  // dist/webgl-to-webgpu/convert-webgl-topology.js
285
- var GLEnum;
286
- (function(GLEnum2) {
287
- GLEnum2[GLEnum2["POINTS"] = 0] = "POINTS";
288
- GLEnum2[GLEnum2["LINES"] = 1] = "LINES";
289
- GLEnum2[GLEnum2["LINE_LOOP"] = 2] = "LINE_LOOP";
290
- GLEnum2[GLEnum2["LINE_STRIP"] = 3] = "LINE_STRIP";
291
- GLEnum2[GLEnum2["TRIANGLES"] = 4] = "TRIANGLES";
292
- GLEnum2[GLEnum2["TRIANGLE_STRIP"] = 5] = "TRIANGLE_STRIP";
293
- GLEnum2[GLEnum2["TRIANGLE_FAN"] = 6] = "TRIANGLE_FAN";
294
- })(GLEnum || (GLEnum = {}));
295
929
  function convertGLDrawModeToTopology(drawMode) {
296
930
  switch (drawMode) {
297
931
  case GLEnum.POINTS:
@@ -310,56 +944,95 @@ function convertGLDrawModeToTopology(drawMode) {
310
944
  }
311
945
 
312
946
  // dist/gltf/create-gltf-model.js
313
- var import_core2 = require("@luma.gl/core");
314
- var import_shadertools = require("@luma.gl/shadertools");
947
+ var import_core3 = require("@luma.gl/core");
315
948
  var import_engine2 = require("@luma.gl/engine");
949
+ var import_shadertools = require("@luma.gl/shadertools");
950
+ var import_engine3 = require("@luma.gl/engine");
316
951
  var SHADER = (
317
952
  /* WGSL */
318
953
  `
319
- layout(0) positions: vec4; // in vec4 POSITION;
320
-
321
- #ifdef HAS_NORMALS
322
- in vec4 normals; // in vec4 NORMAL;
323
- #endif
324
-
325
- #ifdef HAS_TANGENTS
326
- in vec4 TANGENT;
327
- #endif
954
+ struct VertexInputs {
955
+ @location(0) positions: vec3f,
956
+ #ifdef HAS_NORMALS
957
+ @location(1) normals: vec3f,
958
+ #endif
959
+ #ifdef HAS_TANGENTS
960
+ @location(2) TANGENT: vec4f,
961
+ #endif
962
+ #ifdef HAS_UV
963
+ @location(3) texCoords: vec2f,
964
+ #endif
965
+ #ifdef HAS_SKIN
966
+ @location(4) JOINTS_0: vec4u,
967
+ @location(5) WEIGHTS_0: vec4f,
968
+ #endif
969
+ };
328
970
 
329
- #ifdef HAS_UV
330
- // in vec2 TEXCOORD_0;
331
- in vec2 texCoords;
332
- #endif
971
+ struct FragmentInputs {
972
+ @builtin(position) position: vec4f,
973
+ @location(0) pbrPosition: vec3f,
974
+ @location(1) pbrUV: vec2f,
975
+ @location(2) pbrNormal: vec3f,
976
+ #ifdef HAS_TANGENTS
977
+ @location(3) pbrTangent: vec4f,
978
+ #endif
979
+ };
333
980
 
334
981
  @vertex
335
- void main(void) {
336
- vec4 _NORMAL = vec4(0.);
337
- vec4 _TANGENT = vec4(0.);
338
- vec2 _TEXCOORD_0 = vec2(0.);
982
+ fn vertexMain(inputs: VertexInputs) -> FragmentInputs {
983
+ var outputs: FragmentInputs;
984
+ var position = vec4f(inputs.positions, 1.0);
985
+ var normal = vec3f(0.0, 0.0, 1.0);
986
+ var tangent = vec4f(1.0, 0.0, 0.0, 1.0);
987
+ var uv = vec2f(0.0, 0.0);
339
988
 
340
- #ifdef HAS_NORMALS
341
- _NORMAL = normals;
342
- #endif
989
+ #ifdef HAS_NORMALS
990
+ normal = inputs.normals;
991
+ #endif
992
+ #ifdef HAS_UV
993
+ uv = inputs.texCoords;
994
+ #endif
995
+ #ifdef HAS_TANGENTS
996
+ tangent = inputs.TANGENT;
997
+ #endif
998
+ #ifdef HAS_SKIN
999
+ let skinMatrix = getSkinMatrix(inputs.WEIGHTS_0, inputs.JOINTS_0);
1000
+ position = skinMatrix * position;
1001
+ normal = normalize((skinMatrix * vec4f(normal, 0.0)).xyz);
1002
+ #ifdef HAS_TANGENTS
1003
+ tangent = vec4f(normalize((skinMatrix * vec4f(tangent.xyz, 0.0)).xyz), tangent.w);
1004
+ #endif
1005
+ #endif
343
1006
 
344
- #ifdef HAS_TANGENTS
345
- _TANGENT = TANGENT;
346
- #endif
1007
+ let worldPosition = pbrProjection.modelMatrix * position;
347
1008
 
348
- #ifdef HAS_UV
349
- _TEXCOORD_0 = texCoords;
350
- #endif
1009
+ #ifdef HAS_NORMALS
1010
+ normal = normalize((pbrProjection.normalMatrix * vec4f(normal, 0.0)).xyz);
1011
+ #endif
1012
+ #ifdef HAS_TANGENTS
1013
+ let worldTangent = normalize((pbrProjection.modelMatrix * vec4f(tangent.xyz, 0.0)).xyz);
1014
+ outputs.pbrTangent = vec4f(worldTangent, tangent.w);
1015
+ #endif
351
1016
 
352
- pbr_setPositionNormalTangentUV(positions, _NORMAL, _TANGENT, _TEXCOORD_0);
353
- gl_Position = u_MVPMatrix * positions;
354
- }
1017
+ outputs.position = pbrProjection.modelViewProjectionMatrix * position;
1018
+ outputs.pbrPosition = worldPosition.xyz / worldPosition.w;
1019
+ outputs.pbrUV = uv;
1020
+ outputs.pbrNormal = normal;
1021
+ return outputs;
1022
+ }
355
1023
 
356
1024
  @fragment
357
- out vec4 fragmentColor;
358
-
359
- void main(void) {
360
- vec3 pos = pbr_vPosition;
361
- fragmentColor = pbr_filterColor(vec4(1.0));
362
- }
1025
+ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
1026
+ fragmentInputs.pbr_vPosition = inputs.pbrPosition;
1027
+ fragmentInputs.pbr_vUV = inputs.pbrUV;
1028
+ fragmentInputs.pbr_vNormal = inputs.pbrNormal;
1029
+ #ifdef HAS_TANGENTS
1030
+ let tangent = normalize(inputs.pbrTangent.xyz);
1031
+ let bitangent = normalize(cross(inputs.pbrNormal, tangent)) * inputs.pbrTangent.w;
1032
+ fragmentInputs.pbr_vTBN = mat3x3f(tangent, bitangent, inputs.pbrNormal);
1033
+ #endif
1034
+ return pbr_filterColor(vec4f(1.0));
1035
+ }
363
1036
  `
364
1037
  );
365
1038
  var vs = (
@@ -383,6 +1056,11 @@ var vs = (
383
1056
  in vec2 texCoords;
384
1057
  #endif
385
1058
 
1059
+ #ifdef HAS_SKIN
1060
+ in uvec4 JOINTS_0;
1061
+ in vec4 WEIGHTS_0;
1062
+ #endif
1063
+
386
1064
  void main(void) {
387
1065
  vec4 _NORMAL = vec4(0.);
388
1066
  vec4 _TANGENT = vec4(0.);
@@ -400,8 +1078,17 @@ var vs = (
400
1078
  _TEXCOORD_0 = texCoords;
401
1079
  #endif
402
1080
 
403
- pbr_setPositionNormalTangentUV(positions, _NORMAL, _TANGENT, _TEXCOORD_0);
404
- gl_Position = pbrProjection.modelViewProjectionMatrix * positions;
1081
+ vec4 pos = positions;
1082
+
1083
+ #ifdef HAS_SKIN
1084
+ mat4 skinMat = getSkinMatrix(WEIGHTS_0, JOINTS_0);
1085
+ pos = skinMat * pos;
1086
+ _NORMAL = skinMat * _NORMAL;
1087
+ _TANGENT = vec4((skinMat * vec4(_TANGENT.xyz, 0.)).xyz, _TANGENT.w);
1088
+ #endif
1089
+
1090
+ pbr_setPositionNormalTangentUV(pos, _NORMAL, _TANGENT, _TEXCOORD_0);
1091
+ gl_Position = pbrProjection.modelViewProjectionMatrix * pos;
405
1092
  }
406
1093
  `
407
1094
  );
@@ -416,9 +1103,24 @@ var fs = (
416
1103
  }
417
1104
  `
418
1105
  );
1106
+ function createGLTFMaterial(device, options) {
1107
+ const materialFactory = options.materialFactory || new import_engine3.MaterialFactory(device, { modules: [import_shadertools.pbrMaterial] });
1108
+ const pbrMaterialProps = { ...options.parsedPPBRMaterial.uniforms };
1109
+ delete pbrMaterialProps.camera;
1110
+ const materialBindings = Object.fromEntries(Object.entries({
1111
+ ...pbrMaterialProps,
1112
+ ...options.parsedPPBRMaterial.bindings
1113
+ }).filter(([name, value]) => materialFactory.ownsBinding(name) && isMaterialBindingResource(value)));
1114
+ const material = materialFactory.createMaterial({
1115
+ id: options.id,
1116
+ bindings: materialBindings
1117
+ });
1118
+ material.setProps({ pbrMaterial: pbrMaterialProps });
1119
+ return material;
1120
+ }
419
1121
  function createGLTFModel(device, options) {
420
1122
  const { id, geometry, parsedPPBRMaterial, vertexCount, modelOptions = {} } = options;
421
- import_core2.log.info(4, "createGLTFModel defines: ", parsedPPBRMaterial.defines)();
1123
+ import_core3.log.info(4, "createGLTFModel defines: ", parsedPPBRMaterial.defines)();
422
1124
  const managedResources = [];
423
1125
  const parameters = {
424
1126
  depthWriteEnabled: true,
@@ -434,20 +1136,53 @@ function createGLTFModel(device, options) {
434
1136
  geometry,
435
1137
  topology: geometry.topology,
436
1138
  vertexCount,
437
- modules: [import_shadertools.pbrMaterial],
1139
+ modules: [import_shadertools.pbrMaterial, import_shadertools.skin],
438
1140
  ...modelOptions,
439
1141
  defines: { ...parsedPPBRMaterial.defines, ...modelOptions.defines },
440
1142
  parameters: { ...parameters, ...parsedPPBRMaterial.parameters, ...modelOptions.parameters }
441
1143
  };
442
- const model = new import_engine2.Model(device, modelProps);
443
- const { camera, ...pbrMaterialProps } = {
1144
+ const material = options.material || createGLTFMaterial(device, {
1145
+ id: id ? `${id}-material` : void 0,
1146
+ parsedPPBRMaterial
1147
+ });
1148
+ modelProps.material = material;
1149
+ const model = new import_engine3.Model(device, modelProps);
1150
+ const sceneShaderInputValues = {
444
1151
  ...parsedPPBRMaterial.uniforms,
445
1152
  ...modelOptions.uniforms,
446
1153
  ...parsedPPBRMaterial.bindings,
447
1154
  ...modelOptions.bindings
448
1155
  };
449
- model.shaderInputs.setProps({ pbrMaterial: pbrMaterialProps, pbrProjection: { camera } });
450
- return new import_engine2.ModelNode({ managedResources, model });
1156
+ const sceneShaderInputProps = getSceneShaderInputProps(model.shaderInputs.getModules(), material, sceneShaderInputValues);
1157
+ model.shaderInputs.setProps(sceneShaderInputProps);
1158
+ return new import_engine3.ModelNode({ managedResources, model });
1159
+ }
1160
+ function isMaterialBindingResource(value) {
1161
+ 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;
1162
+ }
1163
+ function getSceneShaderInputProps(modules, material, shaderInputValues) {
1164
+ const propertyToModuleNameMap = /* @__PURE__ */ new Map();
1165
+ for (const module2 of modules) {
1166
+ for (const uniformName of Object.keys(module2.uniformTypes || {})) {
1167
+ propertyToModuleNameMap.set(uniformName, module2.name);
1168
+ }
1169
+ for (const binding of module2.bindingLayout || []) {
1170
+ propertyToModuleNameMap.set(binding.name, module2.name);
1171
+ }
1172
+ }
1173
+ const sceneShaderInputProps = {};
1174
+ for (const [propertyName, value] of Object.entries(shaderInputValues)) {
1175
+ if (value === void 0) {
1176
+ continue;
1177
+ }
1178
+ const moduleName = propertyToModuleNameMap.get(propertyName);
1179
+ if (!moduleName || material.ownsModule(moduleName)) {
1180
+ continue;
1181
+ }
1182
+ sceneShaderInputProps[moduleName] ||= {};
1183
+ sceneShaderInputProps[moduleName][propertyName] = value;
1184
+ }
1185
+ return sceneShaderInputProps;
451
1186
  }
452
1187
 
453
1188
  // dist/parsers/parse-gltf.js
@@ -458,73 +1193,103 @@ var defaultOptions = {
458
1193
  lights: true,
459
1194
  useTangents: false
460
1195
  };
461
- function parseGLTF(device, gltf, options_ = {}) {
462
- const options = { ...defaultOptions, ...options_ };
463
- const sceneNodes = gltf.scenes.map((gltfScene) => createScene(device, gltfScene, gltf.nodes, options));
464
- return sceneNodes;
465
- }
466
- function createScene(device, gltfScene, gltfNodes, options) {
467
- const gltfSceneNodes = gltfScene.nodes || [];
468
- const nodes = gltfSceneNodes.map((node) => createNode(device, node, gltfNodes, options));
469
- const sceneNode = new import_engine3.GroupNode({
470
- id: gltfScene.name || gltfScene.id,
471
- children: nodes
1196
+ function parseGLTF(device, gltf, options = {}) {
1197
+ const combinedOptions = { ...defaultOptions, ...options };
1198
+ const materialFactory = new import_engine4.MaterialFactory(device, { modules: [import_shadertools2.pbrMaterial] });
1199
+ const materials = (gltf.materials || []).map((gltfMaterial, materialIndex) => createGLTFMaterial(device, {
1200
+ id: getGLTFMaterialId(gltfMaterial, materialIndex),
1201
+ parsedPPBRMaterial: parsePBRMaterial(device, gltfMaterial, {}, {
1202
+ ...combinedOptions,
1203
+ gltf,
1204
+ validateAttributes: false
1205
+ }),
1206
+ materialFactory
1207
+ }));
1208
+ const gltfMaterialIdToMaterialMap = /* @__PURE__ */ new Map();
1209
+ (gltf.materials || []).forEach((gltfMaterial, materialIndex) => {
1210
+ gltfMaterialIdToMaterialMap.set(gltfMaterial.id, materials[materialIndex]);
472
1211
  });
473
- return sceneNode;
474
- }
475
- function createNode(device, gltfNode, gltfNodes, options) {
476
- if (!gltfNode._node) {
477
- const gltfChildren = gltfNode.children || [];
478
- const children = gltfChildren.map((child) => createNode(device, child, gltfNodes, options));
1212
+ const gltfMeshIdToNodeMap = /* @__PURE__ */ new Map();
1213
+ gltf.meshes.forEach((gltfMesh, idx) => {
1214
+ const newMesh = createNodeForGLTFMesh(device, gltfMesh, gltf, gltfMaterialIdToMaterialMap, combinedOptions);
1215
+ gltfMeshIdToNodeMap.set(gltfMesh.id, newMesh);
1216
+ });
1217
+ const gltfNodeIndexToNodeMap = /* @__PURE__ */ new Map();
1218
+ const gltfNodeIdToNodeMap = /* @__PURE__ */ new Map();
1219
+ gltf.nodes.forEach((gltfNode, idx) => {
1220
+ const newNode = createNodeForGLTFNode(device, gltfNode, combinedOptions);
1221
+ gltfNodeIndexToNodeMap.set(idx, newNode);
1222
+ gltfNodeIdToNodeMap.set(gltfNode.id, newNode);
1223
+ });
1224
+ gltf.nodes.forEach((gltfNode, idx) => {
1225
+ gltfNodeIndexToNodeMap.get(idx).add((gltfNode.children ?? []).map(({ id }) => {
1226
+ const child = gltfNodeIdToNodeMap.get(id);
1227
+ if (!child)
1228
+ throw new Error(`Cannot find child ${id} of node ${idx}`);
1229
+ return child;
1230
+ }));
479
1231
  if (gltfNode.mesh) {
480
- children.push(createMesh(device, gltfNode.mesh, options));
481
- }
482
- const node = new import_engine3.GroupNode({
483
- id: gltfNode.name || gltfNode.id,
484
- children
485
- });
486
- if (gltfNode.matrix) {
487
- node.setMatrix(gltfNode.matrix);
488
- } else {
489
- node.matrix.identity();
490
- if (gltfNode.translation) {
491
- node.matrix.translate(gltfNode.translation);
492
- }
493
- if (gltfNode.rotation) {
494
- const rotationMatrix = new import_core3.Matrix4().fromQuaternion(gltfNode.rotation);
495
- node.matrix.multiplyRight(rotationMatrix);
496
- }
497
- if (gltfNode.scale) {
498
- node.matrix.scale(gltfNode.scale);
1232
+ const mesh = gltfMeshIdToNodeMap.get(gltfNode.mesh.id);
1233
+ if (!mesh) {
1234
+ throw new Error(`Cannot find mesh child ${gltfNode.mesh.id} of node ${idx}`);
499
1235
  }
1236
+ gltfNodeIndexToNodeMap.get(idx).add(mesh);
500
1237
  }
501
- gltfNode._node = node;
502
- }
503
- const topLevelNode = gltfNodes.find((node) => node.id === gltfNode.id);
504
- topLevelNode._node = gltfNode._node;
505
- return gltfNode._node;
506
- }
507
- function createMesh(device, gltfMesh, options) {
508
- if (!gltfMesh._mesh) {
509
- const gltfPrimitives = gltfMesh.primitives || [];
510
- const primitives = gltfPrimitives.map((gltfPrimitive, i) => createPrimitive(device, gltfPrimitive, i, gltfMesh, options));
511
- const mesh = new import_engine3.GroupNode({
512
- id: gltfMesh.name || gltfMesh.id,
513
- children: primitives
1238
+ });
1239
+ const scenes = gltf.scenes.map((gltfScene) => {
1240
+ const children = (gltfScene.nodes || []).map(({ id }) => {
1241
+ const child = gltfNodeIdToNodeMap.get(id);
1242
+ if (!child)
1243
+ throw new Error(`Cannot find child ${id} of scene ${gltfScene.name || gltfScene.id}`);
1244
+ return child;
514
1245
  });
515
- gltfMesh._mesh = mesh;
516
- }
517
- return gltfMesh._mesh;
1246
+ return new import_engine4.GroupNode({
1247
+ id: gltfScene.name || gltfScene.id,
1248
+ children
1249
+ });
1250
+ });
1251
+ return { scenes, materials, gltfMeshIdToNodeMap, gltfNodeIdToNodeMap, gltfNodeIndexToNodeMap };
518
1252
  }
519
- function createPrimitive(device, gltfPrimitive, i, gltfMesh, options) {
520
- const id = gltfPrimitive.name || `${gltfMesh.name || gltfMesh.id}-primitive-${i}`;
1253
+ function createNodeForGLTFNode(device, gltfNode, options) {
1254
+ return new import_engine4.GroupNode({
1255
+ id: gltfNode.name || gltfNode.id,
1256
+ children: [],
1257
+ matrix: gltfNode.matrix,
1258
+ position: gltfNode.translation,
1259
+ rotation: gltfNode.rotation,
1260
+ scale: gltfNode.scale
1261
+ });
1262
+ }
1263
+ function createNodeForGLTFMesh(device, gltfMesh, gltf, gltfMaterialIdToMaterialMap, options) {
1264
+ const gltfPrimitives = gltfMesh.primitives || [];
1265
+ const primitives = gltfPrimitives.map((gltfPrimitive, i) => createNodeForGLTFPrimitive({
1266
+ device,
1267
+ gltfPrimitive,
1268
+ primitiveIndex: i,
1269
+ gltfMesh,
1270
+ gltf,
1271
+ gltfMaterialIdToMaterialMap,
1272
+ options
1273
+ }));
1274
+ const mesh = new import_engine4.GroupNode({
1275
+ id: gltfMesh.name || gltfMesh.id,
1276
+ children: primitives
1277
+ });
1278
+ return mesh;
1279
+ }
1280
+ function createNodeForGLTFPrimitive({ device, gltfPrimitive, primitiveIndex, gltfMesh, gltf, gltfMaterialIdToMaterialMap, options }) {
1281
+ const id = gltfPrimitive.name || `${gltfMesh.name || gltfMesh.id}-primitive-${primitiveIndex}`;
521
1282
  const topology = convertGLDrawModeToTopology(gltfPrimitive.mode || 4);
522
1283
  const vertexCount = gltfPrimitive.indices ? gltfPrimitive.indices.count : getVertexCount(gltfPrimitive.attributes);
523
1284
  const geometry = createGeometry(id, gltfPrimitive, topology);
524
- const parsedPPBRMaterial = parsePBRMaterial(device, gltfPrimitive.material, geometry.attributes, options);
1285
+ const parsedPPBRMaterial = parsePBRMaterial(device, gltfPrimitive.material, geometry.attributes, {
1286
+ ...options,
1287
+ gltf
1288
+ });
525
1289
  const modelNode = createGLTFModel(device, {
526
1290
  id,
527
1291
  geometry: createGeometry(id, gltfPrimitive, topology),
1292
+ material: gltfPrimitive.material ? gltfMaterialIdToMaterialMap.get(gltfPrimitive.material.id) || null : null,
528
1293
  parsedPPBRMaterial,
529
1294
  modelOptions: options.modelOptions,
530
1295
  vertexCount
@@ -541,42 +1306,41 @@ function createGeometry(id, gltfPrimitive, topology) {
541
1306
  const { components, size, value } = attribute;
542
1307
  attributes[attributeName] = { size: size ?? components, value };
543
1308
  }
544
- return new import_engine3.Geometry({
1309
+ return new import_engine4.Geometry({
545
1310
  id,
546
1311
  topology,
547
1312
  indices: gltfPrimitive.indices.value,
548
1313
  attributes
549
1314
  });
550
1315
  }
1316
+ function getGLTFMaterialId(gltfMaterial, materialIndex) {
1317
+ return gltfMaterial.name || gltfMaterial.id || `material-${materialIndex}`;
1318
+ }
551
1319
 
552
1320
  // dist/gltf/gltf-animator.js
553
1321
  var import_core6 = require("@luma.gl/core");
554
- var import_core7 = require("@math.gl/core");
555
1322
 
556
1323
  // dist/gltf/animations/interpolate.js
557
1324
  var import_core4 = require("@luma.gl/core");
558
1325
  var import_core5 = require("@math.gl/core");
559
- var scratchQuaternion = new import_core5.Quaternion();
1326
+ function updateTargetPath(target, path, newValue) {
1327
+ switch (path) {
1328
+ case "translation":
1329
+ return target.setPosition(newValue).updateMatrix();
1330
+ case "rotation":
1331
+ return target.setRotation(newValue).updateMatrix();
1332
+ case "scale":
1333
+ return target.setScale(newValue).updateMatrix();
1334
+ default:
1335
+ import_core4.log.warn(`Bad animation path ${path}`)();
1336
+ return null;
1337
+ }
1338
+ }
560
1339
  function interpolate(time, { input, interpolation, output }, target, path) {
561
1340
  const maxTime = input[input.length - 1];
562
1341
  const animationTime = time % maxTime;
563
1342
  const nextIndex = input.findIndex((t) => t >= animationTime);
564
1343
  const previousIndex = Math.max(0, nextIndex - 1);
565
- if (!Array.isArray(target[path])) {
566
- switch (path) {
567
- case "translation":
568
- target[path] = [0, 0, 0];
569
- break;
570
- case "rotation":
571
- target[path] = [0, 0, 0, 1];
572
- break;
573
- case "scale":
574
- target[path] = [1, 1, 1];
575
- break;
576
- default:
577
- import_core4.log.warn(`Bad animation path ${path}`)();
578
- }
579
- }
580
1344
  const previousTime = input[previousIndex];
581
1345
  const nextTime = input[nextIndex];
582
1346
  switch (interpolation) {
@@ -606,68 +1370,73 @@ function interpolate(time, { input, interpolation, output }, target, path) {
606
1370
  }
607
1371
  }
608
1372
  function linearInterpolate(target, path, start, stop, ratio) {
609
- if (!target[path]) {
610
- throw new Error();
611
- }
612
1373
  if (path === "rotation") {
613
- scratchQuaternion.slerp({ start, target: stop, ratio });
614
- for (let i = 0; i < scratchQuaternion.length; i++) {
615
- target[path][i] = scratchQuaternion[i];
616
- }
1374
+ updateTargetPath(target, path, new import_core5.Quaternion().slerp({ start, target: stop, ratio }));
617
1375
  } else {
1376
+ const newVal = [];
618
1377
  for (let i = 0; i < start.length; i++) {
619
- target[path][i] = ratio * stop[i] + (1 - ratio) * start[i];
1378
+ newVal[i] = ratio * stop[i] + (1 - ratio) * start[i];
620
1379
  }
1380
+ updateTargetPath(target, path, newVal);
621
1381
  }
622
1382
  }
623
1383
  function cubicsplineInterpolate(target, path, { p0, outTangent0, inTangent1, p1, tDiff, ratio: t }) {
624
- if (!target[path]) {
625
- throw new Error();
626
- }
627
- for (let i = 0; i < target[path].length; i++) {
1384
+ const newVal = [];
1385
+ for (let i = 0; i < p0.length; i++) {
628
1386
  const m0 = outTangent0[i] * tDiff;
629
1387
  const m1 = inTangent1[i] * tDiff;
630
- target[path][i] = (2 * Math.pow(t, 3) - 3 * Math.pow(t, 2) + 1) * p0[i] + (Math.pow(t, 3) - 2 * Math.pow(t, 2) + t) * m0 + (-2 * Math.pow(t, 3) + 3 * Math.pow(t, 2)) * p1[i] + (Math.pow(t, 3) - Math.pow(t, 2)) * m1;
1388
+ newVal[i] = (2 * Math.pow(t, 3) - 3 * Math.pow(t, 2) + 1) * p0[i] + (Math.pow(t, 3) - 2 * Math.pow(t, 2) + t) * m0 + (-2 * Math.pow(t, 3) + 3 * Math.pow(t, 2)) * p1[i] + (Math.pow(t, 3) - Math.pow(t, 2)) * m1;
631
1389
  }
1390
+ updateTargetPath(target, path, newVal);
632
1391
  }
633
1392
  function stepInterpolate(target, path, value) {
634
- if (!target[path]) {
635
- throw new Error();
636
- }
637
- for (let i = 0; i < value.length; i++) {
638
- target[path][i] = value[i];
639
- }
1393
+ updateTargetPath(target, path, value);
640
1394
  }
641
1395
 
642
1396
  // dist/gltf/gltf-animator.js
643
1397
  var GLTFSingleAnimator = class {
1398
+ /** Animation definition being played. */
644
1399
  animation;
1400
+ /** Target scenegraph lookup table. */
1401
+ gltfNodeIdToNodeMap;
1402
+ /** Playback start time in seconds. */
645
1403
  startTime = 0;
1404
+ /** Whether playback is currently enabled. */
646
1405
  playing = true;
1406
+ /** Playback speed multiplier. */
647
1407
  speed = 1;
1408
+ /** Creates a single-animation controller. */
648
1409
  constructor(props) {
649
1410
  this.animation = props.animation;
1411
+ this.gltfNodeIdToNodeMap = props.gltfNodeIdToNodeMap;
650
1412
  this.animation.name ||= "unnamed";
651
1413
  Object.assign(this, props);
652
1414
  }
1415
+ /** Advances the animation to the supplied wall-clock time in milliseconds. */
653
1416
  setTime(timeMs) {
654
1417
  if (!this.playing) {
655
1418
  return;
656
1419
  }
657
1420
  const absTime = timeMs / 1e3;
658
1421
  const time = (absTime - this.startTime) * this.speed;
659
- this.animation.channels.forEach(({ sampler, target, path }) => {
660
- interpolate(time, sampler, target, path);
661
- applyTranslationRotationScale(target, target._node);
1422
+ this.animation.channels.forEach(({ sampler, targetNodeId, path }) => {
1423
+ const targetNode = this.gltfNodeIdToNodeMap.get(targetNodeId);
1424
+ if (!targetNode) {
1425
+ throw new Error(`Cannot find animation target node ${targetNodeId}`);
1426
+ }
1427
+ interpolate(time, sampler, targetNode, path);
662
1428
  });
663
1429
  }
664
1430
  };
665
1431
  var GLTFAnimator = class {
1432
+ /** Individual animation controllers. */
666
1433
  animations;
1434
+ /** Creates an animator for the supplied glTF scenegraph. */
667
1435
  constructor(props) {
668
1436
  this.animations = props.animations.map((animation, index) => {
669
1437
  const name = animation.name || `Animation-${index}`;
670
1438
  return new GLTFSingleAnimator({
1439
+ gltfNodeIdToNodeMap: props.gltfNodeIdToNodeMap,
671
1440
  animation: { name, channels: animation.channels }
672
1441
  });
673
1442
  });
@@ -677,27 +1446,18 @@ var GLTFAnimator = class {
677
1446
  import_core6.log.warn("GLTFAnimator#animate is deprecated. Use GLTFAnimator#setTime instead")();
678
1447
  this.setTime(time);
679
1448
  }
1449
+ /** Advances every animation to the supplied wall-clock time in milliseconds. */
680
1450
  setTime(time) {
681
1451
  this.animations.forEach((animation) => animation.setTime(time));
682
1452
  }
1453
+ /** Returns the per-animation controllers managed by this animator. */
683
1454
  getAnimations() {
684
1455
  return this.animations;
685
1456
  }
686
1457
  };
687
- var scratchMatrix = new import_core7.Matrix4();
688
- function applyTranslationRotationScale(gltfNode, node) {
689
- node.matrix.identity();
690
- if (gltfNode.translation) {
691
- node.matrix.translate(gltfNode.translation);
692
- }
693
- if (gltfNode.rotation) {
694
- const rotationMatrix = scratchMatrix.fromQuaternion(gltfNode.rotation);
695
- node.matrix.multiplyRight(rotationMatrix);
696
- }
697
- if (gltfNode.scale) {
698
- node.matrix.scale(gltfNode.scale);
699
- }
700
- }
1458
+
1459
+ // dist/parsers/parse-gltf-animations.js
1460
+ var import_core7 = require("@luma.gl/core");
701
1461
 
702
1462
  // dist/webgl-to-webgpu/convert-webgl-attribute.js
703
1463
  var ATTRIBUTE_TYPE_TO_COMPONENTS = {
@@ -730,61 +1490,327 @@ function accessorToTypedArray(accessor) {
730
1490
  // dist/parsers/parse-gltf-animations.js
731
1491
  function parseGLTFAnimations(gltf) {
732
1492
  const gltfAnimations = gltf.animations || [];
733
- return gltfAnimations.map((animation, index) => {
1493
+ const accessorCache1D = /* @__PURE__ */ new Map();
1494
+ const accessorCache2D = /* @__PURE__ */ new Map();
1495
+ return gltfAnimations.flatMap((animation, index) => {
734
1496
  const name = animation.name || `Animation-${index}`;
735
- const samplers = animation.samplers.map(({ input, interpolation = "LINEAR", output }) => ({
736
- input: accessorToJsArray(gltf.accessors[input]),
737
- interpolation,
738
- output: accessorToJsArray(gltf.accessors[output])
739
- }));
740
- const channels = animation.channels.map(({ sampler, target }) => ({
741
- sampler: samplers[sampler],
742
- target: gltf.nodes[target.node ?? 0],
743
- path: target.path
744
- }));
745
- return { name, channels };
1497
+ const samplerCache = /* @__PURE__ */ new Map();
1498
+ const channels = animation.channels.flatMap(({ sampler, target }) => {
1499
+ const path = getSupportedAnimationPath(target.path);
1500
+ if (!path) {
1501
+ return [];
1502
+ }
1503
+ const targetNode = gltf.nodes[target.node ?? 0];
1504
+ if (!targetNode) {
1505
+ throw new Error(`Cannot find animation target ${target.node}`);
1506
+ }
1507
+ let parsedSampler = samplerCache.get(sampler);
1508
+ if (!parsedSampler) {
1509
+ const gltfSampler = animation.samplers[sampler];
1510
+ if (!gltfSampler) {
1511
+ throw new Error(`Cannot find animation sampler ${sampler}`);
1512
+ }
1513
+ const { input, interpolation = "LINEAR", output } = gltfSampler;
1514
+ parsedSampler = {
1515
+ input: accessorToJsArray1D(gltf.accessors[input], accessorCache1D),
1516
+ interpolation,
1517
+ output: accessorToJsArray2D(gltf.accessors[output], accessorCache2D)
1518
+ };
1519
+ samplerCache.set(sampler, parsedSampler);
1520
+ }
1521
+ return {
1522
+ sampler: parsedSampler,
1523
+ targetNodeId: targetNode.id,
1524
+ path
1525
+ };
1526
+ });
1527
+ return channels.length ? [{ name, channels }] : [];
746
1528
  });
747
1529
  }
748
- function accessorToJsArray(accessor) {
749
- if (!accessor._animation) {
750
- const { typedArray: array, components } = accessorToTypedArray(accessor);
751
- if (components === 1) {
752
- accessor._animation = Array.from(array);
753
- } else {
754
- const slicedArray = [];
755
- for (let i = 0; i < array.length; i += components) {
756
- slicedArray.push(Array.from(array.slice(i, i + components)));
757
- }
758
- accessor._animation = slicedArray;
759
- }
1530
+ function getSupportedAnimationPath(path) {
1531
+ if (path === "pointer") {
1532
+ import_core7.log.warn("KHR_animation_pointer channels are not supported and will be skipped")();
1533
+ return null;
1534
+ }
1535
+ return path;
1536
+ }
1537
+ function accessorToJsArray1D(accessor, accessorCache) {
1538
+ if (accessorCache.has(accessor)) {
1539
+ return accessorCache.get(accessor);
1540
+ }
1541
+ const { typedArray: array, components } = accessorToTypedArray(accessor);
1542
+ assert(components === 1, "accessorToJsArray1D must have exactly 1 component");
1543
+ const result = Array.from(array);
1544
+ accessorCache.set(accessor, result);
1545
+ return result;
1546
+ }
1547
+ function accessorToJsArray2D(accessor, accessorCache) {
1548
+ if (accessorCache.has(accessor)) {
1549
+ return accessorCache.get(accessor);
1550
+ }
1551
+ const { typedArray: array, components } = accessorToTypedArray(accessor);
1552
+ assert(components >= 1, "accessorToJsArray2D must have at least 1 component");
1553
+ const result = [];
1554
+ for (let i = 0; i < array.length; i += components) {
1555
+ result.push(Array.from(array.slice(i, i + components)));
1556
+ }
1557
+ accessorCache.set(accessor, result);
1558
+ return result;
1559
+ }
1560
+ function assert(condition, message) {
1561
+ if (!condition) {
1562
+ throw new Error(message);
760
1563
  }
761
- return accessor._animation;
762
1564
  }
763
1565
 
764
- // dist/utils/deep-copy.js
765
- function deepCopy(object) {
766
- if (ArrayBuffer.isView(object) || object instanceof ArrayBuffer || object instanceof ImageBitmap) {
767
- return object;
1566
+ // dist/gltf/gltf-extension-support.js
1567
+ var UNKNOWN_EXTENSION_SUPPORT = {
1568
+ supportLevel: "none",
1569
+ comment: "Not currently listed in the luma.gl glTF extension support registry."
1570
+ };
1571
+ var GLTF_EXTENSION_SUPPORT_REGISTRY = {
1572
+ KHR_draco_mesh_compression: {
1573
+ supportLevel: "built-in",
1574
+ comment: "Decoded by loaders.gl before luma.gl builds the scenegraph."
1575
+ },
1576
+ EXT_meshopt_compression: {
1577
+ supportLevel: "built-in",
1578
+ comment: "Meshopt-compressed primitives are decoded during load."
1579
+ },
1580
+ KHR_mesh_quantization: {
1581
+ supportLevel: "built-in",
1582
+ comment: "Quantized accessors are unpacked before geometry creation."
1583
+ },
1584
+ KHR_lights_punctual: {
1585
+ supportLevel: "built-in",
1586
+ comment: "Parsed into luma.gl Light objects."
1587
+ },
1588
+ KHR_materials_unlit: {
1589
+ supportLevel: "built-in",
1590
+ comment: "Unlit materials bypass the default lighting path."
1591
+ },
1592
+ KHR_materials_emissive_strength: {
1593
+ supportLevel: "built-in",
1594
+ comment: "Applied by the stock PBR shader."
1595
+ },
1596
+ KHR_texture_basisu: {
1597
+ supportLevel: "built-in",
1598
+ comment: "BasisU / KTX2 textures pass through when the device supports them."
1599
+ },
1600
+ KHR_texture_transform: {
1601
+ supportLevel: "built-in",
1602
+ comment: "UV transforms are applied during load."
1603
+ },
1604
+ EXT_texture_webp: {
1605
+ supportLevel: "loader-only",
1606
+ comment: "Texture source is resolved during load; final support depends on browser and device decode support."
1607
+ },
1608
+ EXT_texture_avif: {
1609
+ supportLevel: "loader-only",
1610
+ comment: "Texture source is resolved during load; final support depends on browser and device decode support."
1611
+ },
1612
+ KHR_materials_specular: {
1613
+ supportLevel: "built-in",
1614
+ comment: "The stock shader now applies specular factors and textures to the dielectric F0 term."
1615
+ },
1616
+ KHR_materials_ior: {
1617
+ supportLevel: "built-in",
1618
+ comment: "The stock shader now drives dielectric reflectance from the glTF IOR value."
1619
+ },
1620
+ KHR_materials_transmission: {
1621
+ supportLevel: "built-in",
1622
+ comment: "The stock shader now applies transmission to the base layer and exposes transparency through alpha, without a scene-color refraction buffer."
1623
+ },
1624
+ KHR_materials_volume: {
1625
+ supportLevel: "built-in",
1626
+ comment: "Thickness and attenuation now tint transmitted light in the stock shader."
1627
+ },
1628
+ KHR_materials_clearcoat: {
1629
+ supportLevel: "built-in",
1630
+ comment: "The stock shader now adds a secondary clearcoat specular lobe."
1631
+ },
1632
+ KHR_materials_sheen: {
1633
+ supportLevel: "built-in",
1634
+ comment: "The stock shader now adds a sheen lobe for cloth-like materials."
1635
+ },
1636
+ KHR_materials_iridescence: {
1637
+ supportLevel: "built-in",
1638
+ comment: "The stock shader now tints specular response with a view-dependent thin-film iridescence approximation."
1639
+ },
1640
+ KHR_materials_anisotropy: {
1641
+ supportLevel: "built-in",
1642
+ comment: "The stock shader now shapes highlights and IBL response with an anisotropy-direction approximation."
1643
+ },
1644
+ KHR_materials_pbrSpecularGlossiness: {
1645
+ supportLevel: "loader-only",
1646
+ comment: "Extension data can be loaded, but it is not translated into the default metallic-roughness material path."
1647
+ },
1648
+ KHR_materials_variants: {
1649
+ supportLevel: "loader-only",
1650
+ comment: "Variant metadata can be loaded, but applications must choose and apply variants."
1651
+ },
1652
+ EXT_mesh_gpu_instancing: {
1653
+ supportLevel: "none",
1654
+ comment: "GPU instancing data is not yet converted into luma.gl instanced draw setup."
1655
+ },
1656
+ KHR_node_visibility: {
1657
+ supportLevel: "none",
1658
+ comment: "Node-visibility animations and toggles are not mapped onto runtime scenegraph state."
1659
+ },
1660
+ KHR_animation_pointer: {
1661
+ supportLevel: "none",
1662
+ comment: "Animation pointers are not mapped onto runtime scenegraph updates."
1663
+ },
1664
+ KHR_materials_diffuse_transmission: {
1665
+ supportLevel: "none",
1666
+ comment: "Diffuse-transmission shading is not implemented in the stock PBR shader."
1667
+ },
1668
+ KHR_materials_dispersion: {
1669
+ supportLevel: "none",
1670
+ comment: "Chromatic dispersion is not implemented in the stock PBR shader."
1671
+ },
1672
+ KHR_materials_volume_scatter: {
1673
+ supportLevel: "none",
1674
+ comment: "Volume scattering is not implemented in the stock PBR shader."
1675
+ },
1676
+ KHR_xmp: {
1677
+ supportLevel: "none",
1678
+ comment: "Metadata payloads remain in the loaded glTF, but luma.gl does not interpret them."
1679
+ },
1680
+ KHR_xmp_json_ld: {
1681
+ supportLevel: "none",
1682
+ comment: "Metadata is preserved in the glTF, but luma.gl does not interpret it."
1683
+ },
1684
+ EXT_lights_image_based: {
1685
+ supportLevel: "none",
1686
+ comment: "Use loadPBREnvironment() or custom environment setup instead."
1687
+ },
1688
+ EXT_texture_video: {
1689
+ supportLevel: "none",
1690
+ comment: "Video textures are not created automatically by the stock pipeline."
1691
+ },
1692
+ MSFT_lod: {
1693
+ supportLevel: "none",
1694
+ comment: "Level-of-detail switching is not implemented in the stock scenegraph loader."
768
1695
  }
769
- if (Array.isArray(object)) {
770
- return object.map(deepCopy);
1696
+ };
1697
+ function getGLTFExtensionSupport(gltf) {
1698
+ const extensionNames = Array.from(collectGLTFExtensionNames(gltf)).sort();
1699
+ const extensionSupportEntries = extensionNames.map((extensionName) => {
1700
+ const extensionSupportDefinition = GLTF_EXTENSION_SUPPORT_REGISTRY[extensionName] || UNKNOWN_EXTENSION_SUPPORT;
1701
+ return [
1702
+ extensionName,
1703
+ {
1704
+ extensionName,
1705
+ supported: extensionSupportDefinition.supportLevel === "built-in",
1706
+ supportLevel: extensionSupportDefinition.supportLevel,
1707
+ comment: extensionSupportDefinition.comment
1708
+ }
1709
+ ];
1710
+ });
1711
+ return new Map(extensionSupportEntries);
1712
+ }
1713
+ function collectGLTFExtensionNames(gltf) {
1714
+ var _a;
1715
+ const gltfWithRemovedExtensions = gltf;
1716
+ const extensionNames = /* @__PURE__ */ new Set();
1717
+ addExtensionNames(extensionNames, gltf.extensionsUsed);
1718
+ addExtensionNames(extensionNames, gltf.extensionsRequired);
1719
+ addExtensionNames(extensionNames, gltfWithRemovedExtensions.extensionsRemoved);
1720
+ addExtensionNames(extensionNames, Object.keys(gltf.extensions || {}));
1721
+ if (((_a = gltfWithRemovedExtensions.lights) == null ? void 0 : _a.length) || gltf.nodes.some((node) => "light" in node)) {
1722
+ extensionNames.add("KHR_lights_punctual");
771
1723
  }
772
- if (object && typeof object === "object") {
773
- const result = {};
774
- for (const key in object) {
775
- result[key] = deepCopy(object[key]);
776
- }
777
- return result;
1724
+ if (gltf.materials.some((material) => {
1725
+ var _a2;
1726
+ const gltfMaterial = material;
1727
+ return gltfMaterial.unlit || ((_a2 = gltfMaterial.extensions) == null ? void 0 : _a2.KHR_materials_unlit);
1728
+ })) {
1729
+ extensionNames.add("KHR_materials_unlit");
1730
+ }
1731
+ return extensionNames;
1732
+ }
1733
+ function addExtensionNames(extensionNames, newExtensionNames = []) {
1734
+ for (const extensionName of newExtensionNames) {
1735
+ extensionNames.add(extensionName);
778
1736
  }
779
- return object;
780
1737
  }
781
1738
 
782
1739
  // dist/gltf/create-scenegraph-from-gltf.js
783
1740
  function createScenegraphsFromGLTF(device, gltf, options) {
784
- gltf = deepCopy(gltf);
785
- const scenes = parseGLTF(device, gltf, options);
1741
+ const { scenes, materials, gltfMeshIdToNodeMap, gltfNodeIdToNodeMap, gltfNodeIndexToNodeMap } = parseGLTF(device, gltf, options);
786
1742
  const animations = parseGLTFAnimations(gltf);
787
- const animator = new GLTFAnimator({ animations });
788
- return { scenes, animator };
1743
+ const animator = new GLTFAnimator({ animations, gltfNodeIdToNodeMap });
1744
+ const lights = parseGLTFLights(gltf);
1745
+ const extensionSupport = getGLTFExtensionSupport(gltf);
1746
+ const sceneBounds = scenes.map((scene) => getScenegraphBounds(scene.getBounds()));
1747
+ const modelBounds = getCombinedScenegraphBounds(sceneBounds);
1748
+ return {
1749
+ scenes,
1750
+ materials,
1751
+ animator,
1752
+ lights,
1753
+ extensionSupport,
1754
+ sceneBounds,
1755
+ modelBounds,
1756
+ gltfMeshIdToNodeMap,
1757
+ gltfNodeIdToNodeMap,
1758
+ gltfNodeIndexToNodeMap,
1759
+ gltf
1760
+ };
1761
+ }
1762
+ function getScenegraphBounds(bounds) {
1763
+ if (!bounds) {
1764
+ return {
1765
+ bounds: null,
1766
+ center: [0, 0, 0],
1767
+ size: [0, 0, 0],
1768
+ radius: 0.5,
1769
+ recommendedOrbitDistance: 1
1770
+ };
1771
+ }
1772
+ const normalizedBounds = [
1773
+ [bounds[0][0], bounds[0][1], bounds[0][2]],
1774
+ [bounds[1][0], bounds[1][1], bounds[1][2]]
1775
+ ];
1776
+ const size = [
1777
+ normalizedBounds[1][0] - normalizedBounds[0][0],
1778
+ normalizedBounds[1][1] - normalizedBounds[0][1],
1779
+ normalizedBounds[1][2] - normalizedBounds[0][2]
1780
+ ];
1781
+ const center = [
1782
+ normalizedBounds[0][0] + size[0] * 0.5,
1783
+ normalizedBounds[0][1] + size[1] * 0.5,
1784
+ normalizedBounds[0][2] + size[2] * 0.5
1785
+ ];
1786
+ const maxHalfExtent = Math.max(size[0], size[1], size[2]) * 0.5;
1787
+ const radius = Math.max(0.5 * Math.hypot(size[0], size[1], size[2]), 1e-3);
1788
+ return {
1789
+ bounds: normalizedBounds,
1790
+ center,
1791
+ size,
1792
+ radius,
1793
+ recommendedOrbitDistance: Math.max(Math.max(maxHalfExtent, 1e-3) / Math.tan(Math.PI / 6) * 1.15, radius * 1.1)
1794
+ };
1795
+ }
1796
+ function getCombinedScenegraphBounds(sceneBounds) {
1797
+ let combinedBounds = null;
1798
+ for (const sceneBoundInfo of sceneBounds) {
1799
+ if (!sceneBoundInfo.bounds) {
1800
+ continue;
1801
+ }
1802
+ if (!combinedBounds) {
1803
+ combinedBounds = [
1804
+ [...sceneBoundInfo.bounds[0]],
1805
+ [...sceneBoundInfo.bounds[1]]
1806
+ ];
1807
+ continue;
1808
+ }
1809
+ for (let axis = 0; axis < 3; axis++) {
1810
+ combinedBounds[0][axis] = Math.min(combinedBounds[0][axis], sceneBoundInfo.bounds[0][axis]);
1811
+ combinedBounds[1][axis] = Math.max(combinedBounds[1][axis], sceneBoundInfo.bounds[1][axis]);
1812
+ }
1813
+ }
1814
+ return getScenegraphBounds(combinedBounds);
789
1815
  }
790
1816
  //# sourceMappingURL=index.cjs.map