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

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 (64) hide show
  1. package/dist/dist.dev.js +4396 -3081
  2. package/dist/dist.min.js +33 -14
  3. package/dist/gltf/animations/animations.d.ts +43 -3
  4. package/dist/gltf/animations/animations.d.ts.map +1 -1
  5. package/dist/gltf/animations/interpolate.d.ts +2 -0
  6. package/dist/gltf/animations/interpolate.d.ts.map +1 -1
  7. package/dist/gltf/animations/interpolate.js +27 -22
  8. package/dist/gltf/animations/interpolate.js.map +1 -1
  9. package/dist/gltf/create-gltf-model.d.ts.map +1 -1
  10. package/dist/gltf/create-gltf-model.js +29 -10
  11. package/dist/gltf/create-gltf-model.js.map +1 -1
  12. package/dist/gltf/create-scenegraph-from-gltf.js +2 -2
  13. package/dist/gltf/create-scenegraph-from-gltf.js.map +1 -1
  14. package/dist/gltf/gltf-animator.d.ts +12 -1
  15. package/dist/gltf/gltf-animator.d.ts.map +1 -1
  16. package/dist/gltf/gltf-animator.js +98 -6
  17. package/dist/gltf/gltf-animator.js.map +1 -1
  18. package/dist/gltf/gltf-extension-support.d.ts +3 -0
  19. package/dist/gltf/gltf-extension-support.d.ts.map +1 -1
  20. package/dist/gltf/gltf-extension-support.js +10 -5
  21. package/dist/gltf/gltf-extension-support.js.map +1 -1
  22. package/dist/index.cjs +763 -272
  23. package/dist/index.cjs.map +4 -4
  24. package/dist/index.d.ts +1 -1
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/parsers/parse-gltf-animations.d.ts.map +1 -1
  28. package/dist/parsers/parse-gltf-animations.js +319 -18
  29. package/dist/parsers/parse-gltf-animations.js.map +1 -1
  30. package/dist/parsers/parse-gltf-lights.d.ts +6 -2
  31. package/dist/parsers/parse-gltf-lights.d.ts.map +1 -1
  32. package/dist/parsers/parse-gltf-lights.js +9 -6
  33. package/dist/parsers/parse-gltf-lights.js.map +1 -1
  34. package/dist/parsers/parse-gltf.d.ts +2 -0
  35. package/dist/parsers/parse-gltf.d.ts.map +1 -1
  36. package/dist/parsers/parse-gltf.js +21 -7
  37. package/dist/parsers/parse-gltf.js.map +1 -1
  38. package/dist/parsers/parse-pbr-material.d.ts.map +1 -1
  39. package/dist/parsers/parse-pbr-material.js +114 -81
  40. package/dist/parsers/parse-pbr-material.js.map +1 -1
  41. package/dist/pbr/pbr-environment.d.ts.map +1 -1
  42. package/dist/pbr/pbr-environment.js +7 -3
  43. package/dist/pbr/pbr-environment.js.map +1 -1
  44. package/dist/pbr/texture-transform.d.ts +24 -0
  45. package/dist/pbr/texture-transform.d.ts.map +1 -0
  46. package/dist/pbr/texture-transform.js +98 -0
  47. package/dist/pbr/texture-transform.js.map +1 -0
  48. package/dist/webgl-to-webgpu/convert-webgl-topology.js +1 -1
  49. package/dist/webgl-to-webgpu/convert-webgl-topology.js.map +1 -1
  50. package/package.json +5 -5
  51. package/src/gltf/animations/animations.ts +73 -3
  52. package/src/gltf/animations/interpolate.ts +50 -43
  53. package/src/gltf/create-gltf-model.ts +29 -10
  54. package/src/gltf/create-scenegraph-from-gltf.ts +2 -2
  55. package/src/gltf/gltf-animator.ts +177 -8
  56. package/src/gltf/gltf-extension-support.ts +17 -5
  57. package/src/index.ts +1 -1
  58. package/src/parsers/parse-gltf-animations.ts +461 -21
  59. package/src/parsers/parse-gltf-lights.ts +27 -8
  60. package/src/parsers/parse-gltf.ts +23 -7
  61. package/src/parsers/parse-pbr-material.ts +184 -79
  62. package/src/pbr/pbr-environment.ts +10 -3
  63. package/src/pbr/texture-transform.ts +263 -0
  64. package/src/webgl-to-webgpu/convert-webgl-topology.ts +1 -1
package/dist/index.cjs CHANGED
@@ -43,11 +43,11 @@ function loadPBREnvironment(device, props) {
43
43
  magFilter: "linear"
44
44
  },
45
45
  // Texture accepts a promise that returns an image as data (Async Textures)
46
- data: (0, import_textures.loadImageTexture)(props.brdfLutUrl)
46
+ data: (0, import_textures.loadImageTexture)(resolveTextureUrl(props.brdfLutUrl))
47
47
  });
48
48
  const diffuseEnvSampler = makeCube(device, {
49
49
  id: "DiffuseEnvSampler",
50
- getTextureForFace: (face) => (0, import_textures.loadImageTexture)(props.getTexUrl("diffuse", FACES.indexOf(face), 0)),
50
+ getTextureForFace: (face) => (0, import_textures.loadImageTexture)(resolveTextureUrl(props.getTexUrl("diffuse", FACES.indexOf(face), 0))),
51
51
  sampler: {
52
52
  addressModeU: "clamp-to-edge",
53
53
  addressModeV: "clamp-to-edge",
@@ -61,7 +61,7 @@ function loadPBREnvironment(device, props) {
61
61
  const imageArray = [];
62
62
  const direction = FACES.indexOf(face);
63
63
  for (let lod = 0; lod < specularMipLevels; lod++) {
64
- imageArray.push((0, import_textures.loadImageTexture)(props.getTexUrl("specular", direction, lod)));
64
+ imageArray.push((0, import_textures.loadImageTexture)(resolveTextureUrl(props.getTexUrl("specular", direction, lod))));
65
65
  }
66
66
  return Promise.all(imageArray);
67
67
  },
@@ -80,6 +80,11 @@ function loadPBREnvironment(device, props) {
80
80
  };
81
81
  }
82
82
  var FACES = ["+X", "-X", "+Y", "-Y", "+Z", "-Z"];
83
+ function resolveTextureUrl(url) {
84
+ var _a, _b;
85
+ const baseUrl = ((_a = globalThis.document) == null ? void 0 : _a.baseURI) ?? ((_b = globalThis.location) == null ? void 0 : _b.href);
86
+ return baseUrl ? new URL(url, baseUrl).toString() : url;
87
+ }
83
88
  function makeCube(device, { id, getTextureForFace, sampler }) {
84
89
  const data = Promise.all(FACES.map((face) => getTextureForFace(face))).then((faceDataArray) => {
85
90
  const cubeData = {};
@@ -98,7 +103,7 @@ function makeCube(device, { id, getTextureForFace, sampler }) {
98
103
  }
99
104
 
100
105
  // dist/parsers/parse-pbr-material.js
101
- var import_core = require("@luma.gl/core");
106
+ var import_core2 = require("@luma.gl/core");
102
107
 
103
108
  // dist/webgl-to-webgpu/gltf-webgl-constants.js
104
109
  var GLEnum;
@@ -179,6 +184,87 @@ function convertSamplerMinFilter(mode) {
179
184
  }
180
185
  }
181
186
 
187
+ // dist/pbr/texture-transform.js
188
+ var import_core = require("@math.gl/core");
189
+ var TEXTURE_TRANSFORM_SLOT_DEFINITIONS = [
190
+ createTextureTransformSlotDefinition("baseColor", "pbr_baseColorSampler", "baseColorTexture", [
191
+ "pbrMetallicRoughness",
192
+ "baseColorTexture"
193
+ ]),
194
+ createTextureTransformSlotDefinition("metallicRoughness", "pbr_metallicRoughnessSampler", "metallicRoughnessTexture", ["pbrMetallicRoughness", "metallicRoughnessTexture"]),
195
+ createTextureTransformSlotDefinition("normal", "pbr_normalSampler", "normalTexture", [
196
+ "normalTexture"
197
+ ]),
198
+ createTextureTransformSlotDefinition("occlusion", "pbr_occlusionSampler", "occlusionTexture", [
199
+ "occlusionTexture"
200
+ ]),
201
+ createTextureTransformSlotDefinition("emissive", "pbr_emissiveSampler", "emissiveTexture", [
202
+ "emissiveTexture"
203
+ ]),
204
+ createTextureTransformSlotDefinition("specularColor", "pbr_specularColorSampler", "KHR_materials_specular.specularColorTexture", ["extensions", "KHR_materials_specular", "specularColorTexture"]),
205
+ createTextureTransformSlotDefinition("specularIntensity", "pbr_specularIntensitySampler", "KHR_materials_specular.specularTexture", ["extensions", "KHR_materials_specular", "specularTexture"]),
206
+ createTextureTransformSlotDefinition("transmission", "pbr_transmissionSampler", "KHR_materials_transmission.transmissionTexture", ["extensions", "KHR_materials_transmission", "transmissionTexture"]),
207
+ createTextureTransformSlotDefinition("thickness", "pbr_thicknessSampler", "KHR_materials_volume.thicknessTexture", ["extensions", "KHR_materials_volume", "thicknessTexture"]),
208
+ createTextureTransformSlotDefinition("clearcoat", "pbr_clearcoatSampler", "KHR_materials_clearcoat.clearcoatTexture", ["extensions", "KHR_materials_clearcoat", "clearcoatTexture"]),
209
+ createTextureTransformSlotDefinition("clearcoatRoughness", "pbr_clearcoatRoughnessSampler", "KHR_materials_clearcoat.clearcoatRoughnessTexture", ["extensions", "KHR_materials_clearcoat", "clearcoatRoughnessTexture"]),
210
+ createTextureTransformSlotDefinition("clearcoatNormal", "pbr_clearcoatNormalSampler", "KHR_materials_clearcoat.clearcoatNormalTexture", ["extensions", "KHR_materials_clearcoat", "clearcoatNormalTexture"]),
211
+ createTextureTransformSlotDefinition("sheenColor", "pbr_sheenColorSampler", "KHR_materials_sheen.sheenColorTexture", ["extensions", "KHR_materials_sheen", "sheenColorTexture"]),
212
+ createTextureTransformSlotDefinition("sheenRoughness", "pbr_sheenRoughnessSampler", "KHR_materials_sheen.sheenRoughnessTexture", ["extensions", "KHR_materials_sheen", "sheenRoughnessTexture"]),
213
+ createTextureTransformSlotDefinition("iridescence", "pbr_iridescenceSampler", "KHR_materials_iridescence.iridescenceTexture", ["extensions", "KHR_materials_iridescence", "iridescenceTexture"]),
214
+ createTextureTransformSlotDefinition("iridescenceThickness", "pbr_iridescenceThicknessSampler", "KHR_materials_iridescence.iridescenceThicknessTexture", ["extensions", "KHR_materials_iridescence", "iridescenceThicknessTexture"]),
215
+ createTextureTransformSlotDefinition("anisotropy", "pbr_anisotropySampler", "KHR_materials_anisotropy.anisotropyTexture", ["extensions", "KHR_materials_anisotropy", "anisotropyTexture"])
216
+ ];
217
+ var TEXTURE_TRANSFORM_SLOT_DEFINITION_MAP = new Map(TEXTURE_TRANSFORM_SLOT_DEFINITIONS.map((definition) => [definition.slot, definition]));
218
+ function createTextureTransformSlotDefinition(slot, binding, displayName, pathSegments) {
219
+ return {
220
+ slot,
221
+ binding,
222
+ displayName,
223
+ pathSegments,
224
+ uvSetUniform: `${slot}UVSet`,
225
+ uvTransformUniform: `${slot}UVTransform`
226
+ };
227
+ }
228
+ function getTextureTransformSlotDefinitions() {
229
+ return TEXTURE_TRANSFORM_SLOT_DEFINITIONS;
230
+ }
231
+ function getTextureTransformSlotDefinition(slot) {
232
+ const definition = TEXTURE_TRANSFORM_SLOT_DEFINITION_MAP.get(slot);
233
+ if (!definition) {
234
+ throw new Error(`Unknown PBR texture transform slot ${slot}`);
235
+ }
236
+ return definition;
237
+ }
238
+ function resolveTextureTransform(textureInfo) {
239
+ var _a;
240
+ const extensionTextureTransform = (_a = textureInfo == null ? void 0 : textureInfo["extensions"]) == null ? void 0 : _a["KHR_texture_transform"];
241
+ return {
242
+ offset: (extensionTextureTransform == null ? void 0 : extensionTextureTransform.offset) ? [extensionTextureTransform.offset[0], extensionTextureTransform.offset[1]] : [0, 0],
243
+ rotation: (extensionTextureTransform == null ? void 0 : extensionTextureTransform.rotation) ?? 0,
244
+ scale: (extensionTextureTransform == null ? void 0 : extensionTextureTransform.scale) ? [extensionTextureTransform.scale[0], extensionTextureTransform.scale[1]] : [1, 1]
245
+ };
246
+ }
247
+ function resolveTextureCoordinateSet(textureInfo) {
248
+ var _a;
249
+ const extensionTextureTransform = (_a = textureInfo == null ? void 0 : textureInfo["extensions"]) == null ? void 0 : _a["KHR_texture_transform"];
250
+ return (extensionTextureTransform == null ? void 0 : extensionTextureTransform["texCoord"]) ?? (textureInfo == null ? void 0 : textureInfo["texCoord"]) ?? 0;
251
+ }
252
+ function resolveTextureTransformSlot(pointerSegments) {
253
+ return TEXTURE_TRANSFORM_SLOT_DEFINITIONS.find((definition) => definition.pathSegments.length === pointerSegments.length && definition.pathSegments.every((segment, index) => pointerSegments[index] === segment)) || null;
254
+ }
255
+ function getTextureTransformMatrix(transform) {
256
+ const translationMatrix = new import_core.Matrix3().set(1, 0, 0, 0, 1, 0, transform.offset[0], transform.offset[1], 1);
257
+ const rotationMatrix = new import_core.Matrix3().set(Math.cos(transform.rotation), Math.sin(transform.rotation), 0, -Math.sin(transform.rotation), Math.cos(transform.rotation), 0, 0, 0, 1);
258
+ const scaleMatrix = new import_core.Matrix3().set(transform.scale[0], 0, 0, 0, transform.scale[1], 0, 0, 0, 1);
259
+ return Array.from(translationMatrix.multiplyRight(rotationMatrix).multiplyRight(scaleMatrix));
260
+ }
261
+ function getTextureTransformDeltaMatrix(baseTransform, currentTransform) {
262
+ const baseMatrix = new import_core.Matrix3(getTextureTransformMatrix(baseTransform));
263
+ const currentMatrix = new import_core.Matrix3(getTextureTransformMatrix(currentTransform));
264
+ const inverseBaseMatrix = new import_core.Matrix3(baseMatrix).invert();
265
+ return Array.from(currentMatrix.multiplyRight(inverseBaseMatrix));
266
+ }
267
+
182
268
  // dist/parsers/parse-pbr-material.js
183
269
  function parsePBRMaterial(device, material, attributes, options) {
184
270
  const parsedMaterial = {
@@ -219,6 +305,8 @@ function parsePBRMaterial(device, material, attributes, options) {
219
305
  parsedMaterial.defines["HAS_TANGENTS"] = true;
220
306
  if (attributes["TEXCOORD_0"])
221
307
  parsedMaterial.defines["HAS_UV"] = true;
308
+ if (attributes["TEXCOORD_1"])
309
+ parsedMaterial.defines["HAS_UV_1"] = true;
222
310
  if (attributes["JOINTS_0"] && attributes["WEIGHTS_0"])
223
311
  parsedMaterial.defines["HAS_SKIN"] = true;
224
312
  if (attributes["COLOR_0"])
@@ -231,75 +319,55 @@ function parsePBRMaterial(device, material, attributes, options) {
231
319
  if (options.validateAttributes !== false) {
232
320
  warnOnMissingExpectedAttributes(material, attributes);
233
321
  }
234
- parseMaterial(device, material, parsedMaterial, options.gltf);
322
+ parseMaterial(device, material, parsedMaterial, attributes, options.gltf);
235
323
  }
236
324
  return parsedMaterial;
237
325
  }
238
326
  function warnOnMissingExpectedAttributes(material, attributes) {
239
327
  var _a;
240
- const uvDependentTextureSlots = getUvDependentTextureSlots(material);
328
+ const uvDependentTextureSlots = getUvDependentTextureSlots(material, 0);
241
329
  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`)();
330
+ import_core2.log.warn(`glTF material uses ${uvDependentTextureSlots.join(", ")} but primitive is missing TEXCOORD_0; textured shading will sample the default UV coordinates`)();
331
+ }
332
+ const uv1DependentTextureSlots = getUvDependentTextureSlots(material, 1);
333
+ if (uv1DependentTextureSlots.length > 0 && !attributes["TEXCOORD_1"]) {
334
+ import_core2.log.warn(`glTF material uses ${uv1DependentTextureSlots.join(", ")} with TEXCOORD_1 but primitive is missing TEXCOORD_1; those textures will be skipped`)();
243
335
  }
244
336
  const isUnlitMaterial = Boolean(material.unlit || ((_a = material.extensions) == null ? void 0 : _a.KHR_materials_unlit));
245
337
  if (isUnlitMaterial || attributes["NORMAL"]) {
246
338
  return;
247
339
  }
248
340
  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`)();
341
+ import_core2.log.warn(`glTF primitive is missing NORMAL while using ${missingNormalReason}; shading will fall back to geometric normals`)();
250
342
  }
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;
343
+ function getUvDependentTextureSlots(material, textureCoordinateSet) {
253
344
  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");
345
+ for (const slotDefinition of getTextureTransformSlotDefinitions()) {
346
+ const textureInfo = getNestedTextureInfo(material, slotDefinition.pathSegments);
347
+ if (!textureInfo) {
348
+ continue;
349
+ }
350
+ if (resolveTextureCoordinateSet(textureInfo) === textureCoordinateSet) {
351
+ uvDependentTextureSlots.push(slotDefinition.displayName);
352
+ }
295
353
  }
296
354
  return uvDependentTextureSlots;
297
355
  }
298
- function parseMaterial(device, material, parsedMaterial, gltf) {
356
+ function getNestedTextureInfo(material, pathSegments) {
357
+ let value = material;
358
+ for (const pathSegment of pathSegments) {
359
+ value = value == null ? void 0 : value[pathSegment];
360
+ if (!value) {
361
+ return null;
362
+ }
363
+ }
364
+ return value;
365
+ }
366
+ function parseMaterial(device, material, parsedMaterial, attributes, gltf) {
299
367
  var _a;
300
368
  parsedMaterial.uniforms.unlit = Boolean(material.unlit || ((_a = material.extensions) == null ? void 0 : _a.KHR_materials_unlit));
301
369
  if (material.pbrMetallicRoughness) {
302
- parsePbrMetallicRoughness(device, material.pbrMetallicRoughness, parsedMaterial, gltf);
370
+ parsePbrMetallicRoughness(device, material.pbrMetallicRoughness, parsedMaterial, attributes, gltf);
303
371
  }
304
372
  if (material.normalTexture) {
305
373
  addTexture(device, material.normalTexture, "pbr_normalSampler", parsedMaterial, {
@@ -307,7 +375,9 @@ function parseMaterial(device, material, parsedMaterial, gltf) {
307
375
  define: "HAS_NORMALMAP",
308
376
  enabledUniformName: "normalMapEnabled"
309
377
  },
310
- gltf
378
+ gltf,
379
+ attributes,
380
+ textureTransformSlot: "normal"
311
381
  });
312
382
  const { scale = 1 } = material.normalTexture;
313
383
  parsedMaterial.uniforms.normalScale = scale;
@@ -318,7 +388,9 @@ function parseMaterial(device, material, parsedMaterial, gltf) {
318
388
  define: "HAS_OCCLUSIONMAP",
319
389
  enabledUniformName: "occlusionMapEnabled"
320
390
  },
321
- gltf
391
+ gltf,
392
+ attributes,
393
+ textureTransformSlot: "occlusion"
322
394
  });
323
395
  const { strength = 1 } = material.occlusionTexture;
324
396
  parsedMaterial.uniforms.occlusionStrength = strength;
@@ -330,10 +402,12 @@ function parseMaterial(device, material, parsedMaterial, gltf) {
330
402
  define: "HAS_EMISSIVEMAP",
331
403
  enabledUniformName: "emissiveMapEnabled"
332
404
  },
333
- gltf
405
+ gltf,
406
+ attributes,
407
+ textureTransformSlot: "emissive"
334
408
  });
335
409
  }
336
- parseMaterialExtensions(device, material.extensions, parsedMaterial, gltf);
410
+ parseMaterialExtensions(device, material.extensions, parsedMaterial, gltf, attributes);
337
411
  switch (material.alphaMode || "OPAQUE") {
338
412
  case "OPAQUE":
339
413
  break;
@@ -345,7 +419,7 @@ function parseMaterial(device, material, parsedMaterial, gltf) {
345
419
  break;
346
420
  }
347
421
  case "BLEND":
348
- import_core.log.warn("glTF BLEND alphaMode might not work well because it requires mesh sorting")();
422
+ import_core2.log.warn("glTF BLEND alphaMode might not work well because it requires mesh sorting")();
349
423
  applyAlphaBlendParameters(parsedMaterial);
350
424
  break;
351
425
  }
@@ -386,14 +460,16 @@ function applyTransmissionBlendApproximation(parsedMaterial) {
386
460
  GLEnum.ONE_MINUS_SRC_ALPHA
387
461
  ];
388
462
  }
389
- function parsePbrMetallicRoughness(device, pbrMetallicRoughness, parsedMaterial, gltf) {
463
+ function parsePbrMetallicRoughness(device, pbrMetallicRoughness, parsedMaterial, attributes, gltf) {
390
464
  if (pbrMetallicRoughness.baseColorTexture) {
391
465
  addTexture(device, pbrMetallicRoughness.baseColorTexture, "pbr_baseColorSampler", parsedMaterial, {
392
466
  featureOptions: {
393
467
  define: "HAS_BASECOLORMAP",
394
468
  enabledUniformName: "baseColorMapEnabled"
395
469
  },
396
- gltf
470
+ gltf,
471
+ attributes,
472
+ textureTransformSlot: "baseColor"
397
473
  });
398
474
  }
399
475
  parsedMaterial.uniforms.baseColorFactor = pbrMetallicRoughness.baseColorFactor || [1, 1, 1, 1];
@@ -403,33 +479,35 @@ function parsePbrMetallicRoughness(device, pbrMetallicRoughness, parsedMaterial,
403
479
  define: "HAS_METALROUGHNESSMAP",
404
480
  enabledUniformName: "metallicRoughnessMapEnabled"
405
481
  },
406
- gltf
482
+ gltf,
483
+ attributes,
484
+ textureTransformSlot: "metallicRoughness"
407
485
  });
408
486
  }
409
487
  const { metallicFactor = 1, roughnessFactor = 1 } = pbrMetallicRoughness;
410
488
  parsedMaterial.uniforms.metallicRoughnessValues = [metallicFactor, roughnessFactor];
411
489
  }
412
- function parseMaterialExtensions(device, extensions, parsedMaterial, gltf) {
490
+ function parseMaterialExtensions(device, extensions, parsedMaterial, gltf, attributes = {}) {
413
491
  if (!extensions) {
414
492
  return;
415
493
  }
416
494
  if (hasMaterialExtensionShading(extensions)) {
417
495
  parsedMaterial.defines["USE_MATERIAL_EXTENSIONS"] = true;
418
496
  }
419
- parseSpecularExtension(device, extensions.KHR_materials_specular, parsedMaterial, gltf);
497
+ parseSpecularExtension(device, extensions.KHR_materials_specular, parsedMaterial, gltf, attributes);
420
498
  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);
499
+ parseTransmissionExtension(device, extensions.KHR_materials_transmission, parsedMaterial, gltf, attributes);
500
+ parseVolumeExtension(device, extensions.KHR_materials_volume, parsedMaterial, gltf, attributes);
501
+ parseClearcoatExtension(device, extensions.KHR_materials_clearcoat, parsedMaterial, gltf, attributes);
502
+ parseSheenExtension(device, extensions.KHR_materials_sheen, parsedMaterial, gltf, attributes);
503
+ parseIridescenceExtension(device, extensions.KHR_materials_iridescence, parsedMaterial, gltf, attributes);
504
+ parseAnisotropyExtension(device, extensions.KHR_materials_anisotropy, parsedMaterial, gltf, attributes);
427
505
  parseEmissiveStrengthExtension(extensions.KHR_materials_emissive_strength, parsedMaterial);
428
506
  }
429
507
  function hasMaterialExtensionShading(extensions) {
430
508
  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
509
  }
432
- function parseSpecularExtension(device, extension, parsedMaterial, gltf) {
510
+ function parseSpecularExtension(device, extension, parsedMaterial, gltf, attributes = {}) {
433
511
  if (!extension) {
434
512
  return;
435
513
  }
@@ -445,7 +523,9 @@ function parseSpecularExtension(device, extension, parsedMaterial, gltf) {
445
523
  define: "HAS_SPECULARCOLORMAP",
446
524
  enabledUniformName: "specularColorMapEnabled"
447
525
  },
448
- gltf
526
+ gltf,
527
+ attributes,
528
+ textureTransformSlot: "specularColor"
449
529
  });
450
530
  }
451
531
  if (extension.specularTexture) {
@@ -454,7 +534,9 @@ function parseSpecularExtension(device, extension, parsedMaterial, gltf) {
454
534
  define: "HAS_SPECULARINTENSITYMAP",
455
535
  enabledUniformName: "specularIntensityMapEnabled"
456
536
  },
457
- gltf
537
+ gltf,
538
+ attributes,
539
+ textureTransformSlot: "specularIntensity"
458
540
  });
459
541
  }
460
542
  }
@@ -463,7 +545,7 @@ function parseIorExtension(extension, parsedMaterial) {
463
545
  parsedMaterial.uniforms.ior = extension.ior;
464
546
  }
465
547
  }
466
- function parseTransmissionExtension(device, extension, parsedMaterial, gltf) {
548
+ function parseTransmissionExtension(device, extension, parsedMaterial, gltf, attributes = {}) {
467
549
  if (!extension) {
468
550
  return;
469
551
  }
@@ -476,15 +558,17 @@ function parseTransmissionExtension(device, extension, parsedMaterial, gltf) {
476
558
  define: "HAS_TRANSMISSIONMAP",
477
559
  enabledUniformName: "transmissionMapEnabled"
478
560
  },
479
- gltf
561
+ gltf,
562
+ attributes,
563
+ textureTransformSlot: "transmission"
480
564
  });
481
565
  }
482
566
  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")();
567
+ import_core2.log.warn("KHR_materials_transmission uses a premultiplied-alpha blending approximation and may require mesh sorting")();
484
568
  applyTransmissionBlendApproximation(parsedMaterial);
485
569
  }
486
570
  }
487
- function parseVolumeExtension(device, extension, parsedMaterial, gltf) {
571
+ function parseVolumeExtension(device, extension, parsedMaterial, gltf, attributes = {}) {
488
572
  if (!extension) {
489
573
  return;
490
574
  }
@@ -496,7 +580,9 @@ function parseVolumeExtension(device, extension, parsedMaterial, gltf) {
496
580
  featureOptions: {
497
581
  define: "HAS_THICKNESSMAP"
498
582
  },
499
- gltf
583
+ gltf,
584
+ attributes,
585
+ textureTransformSlot: "thickness"
500
586
  });
501
587
  }
502
588
  if (extension.attenuationDistance !== void 0) {
@@ -506,7 +592,7 @@ function parseVolumeExtension(device, extension, parsedMaterial, gltf) {
506
592
  parsedMaterial.uniforms.attenuationColor = extension.attenuationColor;
507
593
  }
508
594
  }
509
- function parseClearcoatExtension(device, extension, parsedMaterial, gltf) {
595
+ function parseClearcoatExtension(device, extension, parsedMaterial, gltf, attributes = {}) {
510
596
  if (!extension) {
511
597
  return;
512
598
  }
@@ -522,7 +608,9 @@ function parseClearcoatExtension(device, extension, parsedMaterial, gltf) {
522
608
  define: "HAS_CLEARCOATMAP",
523
609
  enabledUniformName: "clearcoatMapEnabled"
524
610
  },
525
- gltf
611
+ gltf,
612
+ attributes,
613
+ textureTransformSlot: "clearcoat"
526
614
  });
527
615
  }
528
616
  if (extension.clearcoatRoughnessTexture) {
@@ -531,7 +619,9 @@ function parseClearcoatExtension(device, extension, parsedMaterial, gltf) {
531
619
  define: "HAS_CLEARCOATROUGHNESSMAP",
532
620
  enabledUniformName: "clearcoatRoughnessMapEnabled"
533
621
  },
534
- gltf
622
+ gltf,
623
+ attributes,
624
+ textureTransformSlot: "clearcoatRoughness"
535
625
  });
536
626
  }
537
627
  if (extension.clearcoatNormalTexture) {
@@ -539,11 +629,13 @@ function parseClearcoatExtension(device, extension, parsedMaterial, gltf) {
539
629
  featureOptions: {
540
630
  define: "HAS_CLEARCOATNORMALMAP"
541
631
  },
542
- gltf
632
+ gltf,
633
+ attributes,
634
+ textureTransformSlot: "clearcoatNormal"
543
635
  });
544
636
  }
545
637
  }
546
- function parseSheenExtension(device, extension, parsedMaterial, gltf) {
638
+ function parseSheenExtension(device, extension, parsedMaterial, gltf, attributes = {}) {
547
639
  if (!extension) {
548
640
  return;
549
641
  }
@@ -559,7 +651,9 @@ function parseSheenExtension(device, extension, parsedMaterial, gltf) {
559
651
  define: "HAS_SHEENCOLORMAP",
560
652
  enabledUniformName: "sheenColorMapEnabled"
561
653
  },
562
- gltf
654
+ gltf,
655
+ attributes,
656
+ textureTransformSlot: "sheenColor"
563
657
  });
564
658
  }
565
659
  if (extension.sheenRoughnessTexture) {
@@ -568,11 +662,13 @@ function parseSheenExtension(device, extension, parsedMaterial, gltf) {
568
662
  define: "HAS_SHEENROUGHNESSMAP",
569
663
  enabledUniformName: "sheenRoughnessMapEnabled"
570
664
  },
571
- gltf
665
+ gltf,
666
+ attributes,
667
+ textureTransformSlot: "sheenRoughness"
572
668
  });
573
669
  }
574
670
  }
575
- function parseIridescenceExtension(device, extension, parsedMaterial, gltf) {
671
+ function parseIridescenceExtension(device, extension, parsedMaterial, gltf, attributes = {}) {
576
672
  if (!extension) {
577
673
  return;
578
674
  }
@@ -594,7 +690,9 @@ function parseIridescenceExtension(device, extension, parsedMaterial, gltf) {
594
690
  define: "HAS_IRIDESCENCEMAP",
595
691
  enabledUniformName: "iridescenceMapEnabled"
596
692
  },
597
- gltf
693
+ gltf,
694
+ attributes,
695
+ textureTransformSlot: "iridescence"
598
696
  });
599
697
  }
600
698
  if (extension.iridescenceThicknessTexture) {
@@ -602,11 +700,13 @@ function parseIridescenceExtension(device, extension, parsedMaterial, gltf) {
602
700
  featureOptions: {
603
701
  define: "HAS_IRIDESCENCETHICKNESSMAP"
604
702
  },
605
- gltf
703
+ gltf,
704
+ attributes,
705
+ textureTransformSlot: "iridescenceThickness"
606
706
  });
607
707
  }
608
708
  }
609
- function parseAnisotropyExtension(device, extension, parsedMaterial, gltf) {
709
+ function parseAnisotropyExtension(device, extension, parsedMaterial, gltf, attributes = {}) {
610
710
  if (!extension) {
611
711
  return;
612
712
  }
@@ -622,7 +722,9 @@ function parseAnisotropyExtension(device, extension, parsedMaterial, gltf) {
622
722
  define: "HAS_ANISOTROPYMAP",
623
723
  enabledUniformName: "anisotropyMapEnabled"
624
724
  },
625
- gltf
725
+ gltf,
726
+ attributes,
727
+ textureTransformSlot: "anisotropy"
626
728
  });
627
729
  }
628
730
  }
@@ -633,12 +735,21 @@ function parseEmissiveStrengthExtension(extension, parsedMaterial) {
633
735
  }
634
736
  function addTexture(device, gltfTexture, uniformName, parsedMaterial, textureParseOptions = {}) {
635
737
  var _a, _b, _c;
636
- const { featureOptions = {}, gltf } = textureParseOptions;
738
+ const { featureOptions = {}, gltf, attributes = {}, textureTransformSlot } = textureParseOptions;
637
739
  const { define, enabledUniformName } = featureOptions;
740
+ const textureCoordinateSet = resolveTextureCoordinateSet(gltfTexture);
741
+ if (textureCoordinateSet > 1) {
742
+ import_core2.log.warn(`Skipping ${String(uniformName)} because ${textureCoordinateSet} is not supported; only TEXCOORD_0 and TEXCOORD_1 are currently available`)();
743
+ return;
744
+ }
745
+ if (textureCoordinateSet === 1 && !attributes["TEXCOORD_1"]) {
746
+ import_core2.log.warn(`Skipping ${String(uniformName)} because it requires TEXCOORD_1 but the primitive does not provide TEXCOORD_1`)();
747
+ return;
748
+ }
638
749
  const resolvedTextureInfo = resolveTextureInfo(gltfTexture, gltf);
639
750
  const image = (_b = (_a = resolvedTextureInfo.texture) == null ? void 0 : _a.source) == null ? void 0 : _b.image;
640
751
  if (!image) {
641
- import_core.log.warn(`Skipping unresolved glTF texture for ${String(uniformName)}`)();
752
+ import_core2.log.warn(`Skipping unresolved glTF texture for ${String(uniformName)}`)();
642
753
  return;
643
754
  }
644
755
  const gltfSampler = {
@@ -674,6 +785,11 @@ function addTexture(device, gltfTexture, uniformName, parsedMaterial, texturePar
674
785
  if (enabledUniformName) {
675
786
  parsedMaterial.uniforms[enabledUniformName] = true;
676
787
  }
788
+ if (textureTransformSlot) {
789
+ const textureTransformSlotDefinition = getTextureTransformSlotDefinition(textureTransformSlot);
790
+ parsedMaterial.uniforms[textureTransformSlotDefinition.uvSetUniform] = textureCoordinateSet;
791
+ parsedMaterial.uniforms[textureTransformSlotDefinition.uvTransformUniform] = getTextureTransformMatrix(resolveTextureTransform(gltfTexture));
792
+ }
677
793
  parsedMaterial.generatedTextures.push(texture);
678
794
  }
679
795
  function resolveTextureInfo(gltfTexture, gltf) {
@@ -712,7 +828,7 @@ function resolveCompressedTextureFormat(level) {
712
828
  return level.textureFormat;
713
829
  }
714
830
  function getMaxCompressedMipLevels(baseWidth, baseHeight, format) {
715
- const { blockWidth = 1, blockHeight = 1 } = import_core.textureFormatDecoder.getInfo(format);
831
+ const { blockWidth = 1, blockHeight = 1 } = import_core2.textureFormatDecoder.getInfo(format);
716
832
  let count = 1;
717
833
  for (let i = 1; ; i++) {
718
834
  const w = Math.max(1, baseWidth >> i);
@@ -734,19 +850,19 @@ function createCompressedTexture(device, image, baseOptions) {
734
850
  levels = [];
735
851
  }
736
852
  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")();
853
+ import_core2.log.warn("createCompressedTexture: compressed image has no valid mip levels, creating fallback")();
738
854
  return createCompressedTextureFallback(device, baseOptions);
739
855
  }
740
856
  const baseLevel = levels[0];
741
857
  const baseWidth = baseLevel.width ?? image.width ?? 0;
742
858
  const baseHeight = baseLevel.height ?? image.height ?? 0;
743
859
  if (baseWidth <= 0 || baseHeight <= 0) {
744
- import_core.log.warn("createCompressedTexture: base level has invalid dimensions, creating fallback")();
860
+ import_core2.log.warn("createCompressedTexture: base level has invalid dimensions, creating fallback")();
745
861
  return createCompressedTextureFallback(device, baseOptions);
746
862
  }
747
863
  const format = resolveCompressedTextureFormat(baseLevel);
748
864
  if (!format) {
749
- import_core.log.warn("createCompressedTexture: compressed image has no textureFormat, creating fallback")();
865
+ import_core2.log.warn("createCompressedTexture: compressed image has no textureFormat, creating fallback")();
750
866
  return createCompressedTextureFallback(device, baseOptions);
751
867
  }
752
868
  const maxMipLevels = getMaxCompressedMipLevels(baseWidth, baseHeight, format);
@@ -755,18 +871,18 @@ function createCompressedTexture(device, image, baseOptions) {
755
871
  for (let i = 1; i < levelLimit; i++) {
756
872
  const level = levels[i];
757
873
  if (!level.data || level.width <= 0 || level.height <= 0) {
758
- import_core.log.warn(`createCompressedTexture: mip level ${i} has invalid data/dimensions, truncating`)();
874
+ import_core2.log.warn(`createCompressedTexture: mip level ${i} has invalid data/dimensions, truncating`)();
759
875
  break;
760
876
  }
761
877
  const levelFormat = resolveCompressedTextureFormat(level);
762
878
  if (levelFormat && levelFormat !== format) {
763
- import_core.log.warn(`createCompressedTexture: mip level ${i} format '${levelFormat}' differs from base '${format}', truncating`)();
879
+ import_core2.log.warn(`createCompressedTexture: mip level ${i} format '${levelFormat}' differs from base '${format}', truncating`)();
764
880
  break;
765
881
  }
766
882
  const expectedW = Math.max(1, baseWidth >> i);
767
883
  const expectedH = Math.max(1, baseHeight >> i);
768
884
  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`)();
885
+ import_core2.log.warn(`createCompressedTexture: mip level ${i} dimensions ${level.width}x${level.height} don't match expected ${expectedW}x${expectedH}, truncating`)();
770
886
  break;
771
887
  }
772
888
  validLevelCount++;
@@ -774,7 +890,7 @@ function createCompressedTexture(device, image, baseOptions) {
774
890
  const texture = device.createTexture({
775
891
  ...baseOptions,
776
892
  format,
777
- usage: import_core.Texture.TEXTURE | import_core.Texture.COPY_DST,
893
+ usage: import_core2.Texture.TEXTURE | import_core2.Texture.COPY_DST,
778
894
  width: baseWidth,
779
895
  height: baseHeight,
780
896
  mipLevels: validLevelCount,
@@ -791,9 +907,9 @@ function createCompressedTexture(device, image, baseOptions) {
791
907
  }
792
908
 
793
909
  // dist/parsers/parse-gltf-lights.js
794
- var import_core2 = require("@math.gl/core");
795
- var GLTF_COLOR_FACTOR = 255;
796
- function parseGLTFLights(gltf) {
910
+ var import_core3 = require("@math.gl/core");
911
+ var import_shadertools = require("@luma.gl/shadertools");
912
+ function parseGLTFLights(gltf, options = {}) {
797
913
  var _a, _b, _c, _d;
798
914
  const lightDefs = (
799
915
  // `postProcessGLTF()` moves KHR_lights_punctual into `gltf.lights`.
@@ -814,7 +930,7 @@ function parseGLTFLights(gltf) {
814
930
  if (!gltfLight) {
815
931
  continue;
816
932
  }
817
- const color = normalizeGLTFLightColor(gltfLight.color || [1, 1, 1]);
933
+ const color = normalizeGLTFLightColor(gltfLight.color || [1, 1, 1], options.useByteColors ?? true);
818
934
  const intensity = gltfLight.intensity ?? 1;
819
935
  const range = gltfLight.range;
820
936
  const worldMatrix = getNodeWorldMatrix(node, parentNodeById, worldMatrixByNodeId);
@@ -834,8 +950,11 @@ function parseGLTFLights(gltf) {
834
950
  }
835
951
  return lights;
836
952
  }
837
- function normalizeGLTFLightColor(color) {
838
- return color.map((component) => component * GLTF_COLOR_FACTOR);
953
+ function normalizeGLTFLightColor(color, useByteColors) {
954
+ if (useByteColors) {
955
+ return color.map((component) => component * 255);
956
+ }
957
+ return (0, import_shadertools.normalizeByteColor3)(color, false);
839
958
  }
840
959
  function parsePointLight(worldMatrix, color, intensity, range) {
841
960
  const position = getLightPosition(worldMatrix);
@@ -894,20 +1013,20 @@ function getNodeWorldMatrix(node, parentNodeById, worldMatrixByNodeId) {
894
1013
  }
895
1014
  const localMatrix = getNodeLocalMatrix(node);
896
1015
  const parentNode = parentNodeById.get(node.id);
897
- const worldMatrix = parentNode ? new import_core2.Matrix4(getNodeWorldMatrix(parentNode, parentNodeById, worldMatrixByNodeId)).multiplyRight(localMatrix) : localMatrix;
1016
+ const worldMatrix = parentNode ? new import_core3.Matrix4(getNodeWorldMatrix(parentNode, parentNodeById, worldMatrixByNodeId)).multiplyRight(localMatrix) : localMatrix;
898
1017
  worldMatrixByNodeId.set(node.id, worldMatrix);
899
1018
  return worldMatrix;
900
1019
  }
901
1020
  function getNodeLocalMatrix(node) {
902
1021
  if (node.matrix) {
903
- return new import_core2.Matrix4(node.matrix);
1022
+ return new import_core3.Matrix4(node.matrix);
904
1023
  }
905
- const matrix = new import_core2.Matrix4();
1024
+ const matrix = new import_core3.Matrix4();
906
1025
  if (node.translation) {
907
1026
  matrix.translate(node.translation);
908
1027
  }
909
1028
  if (node.rotation) {
910
- matrix.multiplyRight(new import_core2.Matrix4().fromQuaternion(node.rotation));
1029
+ matrix.multiplyRight(new import_core3.Matrix4().fromQuaternion(node.rotation));
911
1030
  }
912
1031
  if (node.scale) {
913
1032
  matrix.scale(node.scale);
@@ -923,7 +1042,7 @@ function getLightDirection(worldMatrix) {
923
1042
 
924
1043
  // dist/parsers/parse-gltf.js
925
1044
  var import_engine4 = require("@luma.gl/engine");
926
- var import_shadertools2 = require("@luma.gl/shadertools");
1045
+ var import_shadertools3 = require("@luma.gl/shadertools");
927
1046
 
928
1047
  // dist/webgl-to-webgpu/convert-webgl-topology.js
929
1048
  function convertGLDrawModeToTopology(drawMode) {
@@ -944,9 +1063,9 @@ function convertGLDrawModeToTopology(drawMode) {
944
1063
  }
945
1064
 
946
1065
  // dist/gltf/create-gltf-model.js
947
- var import_core3 = require("@luma.gl/core");
1066
+ var import_core4 = require("@luma.gl/core");
948
1067
  var import_engine2 = require("@luma.gl/engine");
949
- var import_shadertools = require("@luma.gl/shadertools");
1068
+ var import_shadertools2 = require("@luma.gl/shadertools");
950
1069
  var import_engine3 = require("@luma.gl/engine");
951
1070
  var SHADER = (
952
1071
  /* WGSL */
@@ -962,19 +1081,23 @@ struct VertexInputs {
962
1081
  #ifdef HAS_UV
963
1082
  @location(3) texCoords: vec2f,
964
1083
  #endif
1084
+ #ifdef HAS_UV_1
1085
+ @location(4) texCoords1: vec2f,
1086
+ #endif
965
1087
  #ifdef HAS_SKIN
966
- @location(4) JOINTS_0: vec4u,
967
- @location(5) WEIGHTS_0: vec4f,
1088
+ @location(5) JOINTS_0: vec4u,
1089
+ @location(6) WEIGHTS_0: vec4f,
968
1090
  #endif
969
1091
  };
970
1092
 
971
1093
  struct FragmentInputs {
972
1094
  @builtin(position) position: vec4f,
973
1095
  @location(0) pbrPosition: vec3f,
974
- @location(1) pbrUV: vec2f,
975
- @location(2) pbrNormal: vec3f,
1096
+ @location(1) pbrUV0: vec2f,
1097
+ @location(2) pbrUV1: vec2f,
1098
+ @location(3) pbrNormal: vec3f,
976
1099
  #ifdef HAS_TANGENTS
977
- @location(3) pbrTangent: vec4f,
1100
+ @location(4) pbrTangent: vec4f,
978
1101
  #endif
979
1102
  };
980
1103
 
@@ -984,13 +1107,17 @@ fn vertexMain(inputs: VertexInputs) -> FragmentInputs {
984
1107
  var position = vec4f(inputs.positions, 1.0);
985
1108
  var normal = vec3f(0.0, 0.0, 1.0);
986
1109
  var tangent = vec4f(1.0, 0.0, 0.0, 1.0);
987
- var uv = vec2f(0.0, 0.0);
1110
+ var uv0 = vec2f(0.0, 0.0);
1111
+ var uv1 = vec2f(0.0, 0.0);
988
1112
 
989
1113
  #ifdef HAS_NORMALS
990
1114
  normal = inputs.normals;
991
1115
  #endif
992
1116
  #ifdef HAS_UV
993
- uv = inputs.texCoords;
1117
+ uv0 = inputs.texCoords;
1118
+ #endif
1119
+ #ifdef HAS_UV_1
1120
+ uv1 = inputs.texCoords1;
994
1121
  #endif
995
1122
  #ifdef HAS_TANGENTS
996
1123
  tangent = inputs.TANGENT;
@@ -1016,7 +1143,8 @@ fn vertexMain(inputs: VertexInputs) -> FragmentInputs {
1016
1143
 
1017
1144
  outputs.position = pbrProjection.modelViewProjectionMatrix * position;
1018
1145
  outputs.pbrPosition = worldPosition.xyz / worldPosition.w;
1019
- outputs.pbrUV = uv;
1146
+ outputs.pbrUV0 = uv0;
1147
+ outputs.pbrUV1 = uv1;
1020
1148
  outputs.pbrNormal = normal;
1021
1149
  return outputs;
1022
1150
  }
@@ -1024,7 +1152,8 @@ fn vertexMain(inputs: VertexInputs) -> FragmentInputs {
1024
1152
  @fragment
1025
1153
  fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
1026
1154
  fragmentInputs.pbr_vPosition = inputs.pbrPosition;
1027
- fragmentInputs.pbr_vUV = inputs.pbrUV;
1155
+ fragmentInputs.pbr_vUV0 = inputs.pbrUV0;
1156
+ fragmentInputs.pbr_vUV1 = inputs.pbrUV1;
1028
1157
  fragmentInputs.pbr_vNormal = inputs.pbrNormal;
1029
1158
  #ifdef HAS_TANGENTS
1030
1159
  let tangent = normalize(inputs.pbrTangent.xyz);
@@ -1056,6 +1185,10 @@ var vs = (
1056
1185
  in vec2 texCoords;
1057
1186
  #endif
1058
1187
 
1188
+ #ifdef HAS_UV_1
1189
+ in vec2 texCoords1;
1190
+ #endif
1191
+
1059
1192
  #ifdef HAS_SKIN
1060
1193
  in uvec4 JOINTS_0;
1061
1194
  in vec4 WEIGHTS_0;
@@ -1065,6 +1198,7 @@ var vs = (
1065
1198
  vec4 _NORMAL = vec4(0.);
1066
1199
  vec4 _TANGENT = vec4(0.);
1067
1200
  vec2 _TEXCOORD_0 = vec2(0.);
1201
+ vec2 _TEXCOORD_1 = vec2(0.);
1068
1202
 
1069
1203
  #ifdef HAS_NORMALS
1070
1204
  _NORMAL = normals;
@@ -1078,6 +1212,10 @@ var vs = (
1078
1212
  _TEXCOORD_0 = texCoords;
1079
1213
  #endif
1080
1214
 
1215
+ #ifdef HAS_UV_1
1216
+ _TEXCOORD_1 = texCoords1;
1217
+ #endif
1218
+
1081
1219
  vec4 pos = positions;
1082
1220
 
1083
1221
  #ifdef HAS_SKIN
@@ -1087,7 +1225,7 @@ var vs = (
1087
1225
  _TANGENT = vec4((skinMat * vec4(_TANGENT.xyz, 0.)).xyz, _TANGENT.w);
1088
1226
  #endif
1089
1227
 
1090
- pbr_setPositionNormalTangentUV(pos, _NORMAL, _TANGENT, _TEXCOORD_0);
1228
+ pbr_setPositionNormalTangentUV(pos, _NORMAL, _TANGENT, _TEXCOORD_0, _TEXCOORD_1);
1091
1229
  gl_Position = pbrProjection.modelViewProjectionMatrix * pos;
1092
1230
  }
1093
1231
  `
@@ -1104,7 +1242,7 @@ var fs = (
1104
1242
  `
1105
1243
  );
1106
1244
  function createGLTFMaterial(device, options) {
1107
- const materialFactory = options.materialFactory || new import_engine3.MaterialFactory(device, { modules: [import_shadertools.pbrMaterial] });
1245
+ const materialFactory = options.materialFactory || new import_engine3.MaterialFactory(device, { modules: [import_shadertools2.pbrMaterial] });
1108
1246
  const pbrMaterialProps = { ...options.parsedPPBRMaterial.uniforms };
1109
1247
  delete pbrMaterialProps.camera;
1110
1248
  const materialBindings = Object.fromEntries(Object.entries({
@@ -1120,7 +1258,7 @@ function createGLTFMaterial(device, options) {
1120
1258
  }
1121
1259
  function createGLTFModel(device, options) {
1122
1260
  const { id, geometry, parsedPPBRMaterial, vertexCount, modelOptions = {} } = options;
1123
- import_core3.log.info(4, "createGLTFModel defines: ", parsedPPBRMaterial.defines)();
1261
+ import_core4.log.info(4, "createGLTFModel defines: ", parsedPPBRMaterial.defines)();
1124
1262
  const managedResources = [];
1125
1263
  const parameters = {
1126
1264
  depthWriteEnabled: true,
@@ -1136,7 +1274,7 @@ function createGLTFModel(device, options) {
1136
1274
  geometry,
1137
1275
  topology: geometry.topology,
1138
1276
  vertexCount,
1139
- modules: [import_shadertools.pbrMaterial, import_shadertools.skin],
1277
+ modules: [import_shadertools2.pbrMaterial, import_shadertools2.skin],
1140
1278
  ...modelOptions,
1141
1279
  defines: { ...parsedPPBRMaterial.defines, ...modelOptions.defines },
1142
1280
  parameters: { ...parameters, ...parsedPPBRMaterial.parameters, ...modelOptions.parameters }
@@ -1158,7 +1296,7 @@ function createGLTFModel(device, options) {
1158
1296
  return new import_engine3.ModelNode({ managedResources, model });
1159
1297
  }
1160
1298
  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;
1299
+ return value instanceof import_core4.Buffer || value instanceof import_engine2.DynamicTexture || value instanceof import_core4.Sampler || value instanceof import_core4.Texture || value instanceof import_core4.TextureView;
1162
1300
  }
1163
1301
  function getSceneShaderInputProps(modules, material, shaderInputValues) {
1164
1302
  const propertyToModuleNameMap = /* @__PURE__ */ new Map();
@@ -1191,11 +1329,12 @@ var defaultOptions = {
1191
1329
  pbrDebug: false,
1192
1330
  imageBasedLightingEnvironment: void 0,
1193
1331
  lights: true,
1194
- useTangents: false
1332
+ useTangents: false,
1333
+ useByteColors: true
1195
1334
  };
1196
1335
  function parseGLTF(device, gltf, options = {}) {
1197
1336
  const combinedOptions = { ...defaultOptions, ...options };
1198
- const materialFactory = new import_engine4.MaterialFactory(device, { modules: [import_shadertools2.pbrMaterial] });
1337
+ const materialFactory = new import_engine4.MaterialFactory(device, { modules: [import_shadertools3.pbrMaterial] });
1199
1338
  const materials = (gltf.materials || []).map((gltfMaterial, materialIndex) => createGLTFMaterial(device, {
1200
1339
  id: getGLTFMaterialId(gltfMaterial, materialIndex),
1201
1340
  parsedPPBRMaterial: parsePBRMaterial(device, gltfMaterial, {}, {
@@ -1279,7 +1418,7 @@ function createNodeForGLTFMesh(device, gltfMesh, gltf, gltfMaterialIdToMaterialM
1279
1418
  }
1280
1419
  function createNodeForGLTFPrimitive({ device, gltfPrimitive, primitiveIndex, gltfMesh, gltf, gltfMaterialIdToMaterialMap, options }) {
1281
1420
  const id = gltfPrimitive.name || `${gltfMesh.name || gltfMesh.id}-primitive-${primitiveIndex}`;
1282
- const topology = convertGLDrawModeToTopology(gltfPrimitive.mode || 4);
1421
+ const topology = convertGLDrawModeToTopology(gltfPrimitive.mode ?? 4);
1283
1422
  const vertexCount = gltfPrimitive.indices ? gltfPrimitive.indices.count : getVertexCount(gltfPrimitive.attributes);
1284
1423
  const geometry = createGeometry(id, gltfPrimitive, topology);
1285
1424
  const parsedPPBRMaterial = parsePBRMaterial(device, gltfPrimitive.material, geometry.attributes, {
@@ -1288,7 +1427,7 @@ function createNodeForGLTFPrimitive({ device, gltfPrimitive, primitiveIndex, glt
1288
1427
  });
1289
1428
  const modelNode = createGLTFModel(device, {
1290
1429
  id,
1291
- geometry: createGeometry(id, gltfPrimitive, topology),
1430
+ geometry,
1292
1431
  material: gltfPrimitive.material ? gltfMaterialIdToMaterialMap.get(gltfPrimitive.material.id) || null : null,
1293
1432
  parsedPPBRMaterial,
1294
1433
  modelOptions: options.modelOptions,
@@ -1298,18 +1437,32 @@ function createNodeForGLTFPrimitive({ device, gltfPrimitive, primitiveIndex, glt
1298
1437
  return modelNode;
1299
1438
  }
1300
1439
  function getVertexCount(attributes) {
1301
- throw new Error("getVertexCount not implemented");
1440
+ let vertexCount = Infinity;
1441
+ for (const attribute of Object.values(attributes)) {
1442
+ if (attribute) {
1443
+ const { value, size, components } = attribute;
1444
+ const attributeSize = size ?? components;
1445
+ if ((value == null ? void 0 : value.length) !== void 0 && attributeSize >= 1) {
1446
+ vertexCount = Math.min(vertexCount, value.length / attributeSize);
1447
+ }
1448
+ }
1449
+ }
1450
+ if (!Number.isFinite(vertexCount)) {
1451
+ throw new Error("Could not determine vertex count from attributes");
1452
+ }
1453
+ return vertexCount;
1302
1454
  }
1303
1455
  function createGeometry(id, gltfPrimitive, topology) {
1456
+ var _a;
1304
1457
  const attributes = {};
1305
1458
  for (const [attributeName, attribute] of Object.entries(gltfPrimitive.attributes)) {
1306
- const { components, size, value } = attribute;
1307
- attributes[attributeName] = { size: size ?? components, value };
1459
+ const { components, size, value, normalized } = attribute;
1460
+ attributes[attributeName] = { size: size ?? components, value, normalized };
1308
1461
  }
1309
1462
  return new import_engine4.Geometry({
1310
1463
  id,
1311
1464
  topology,
1312
- indices: gltfPrimitive.indices.value,
1465
+ indices: (_a = gltfPrimitive.indices) == null ? void 0 : _a.value,
1313
1466
  attributes
1314
1467
  });
1315
1468
  }
@@ -1318,11 +1471,11 @@ function getGLTFMaterialId(gltfMaterial, materialIndex) {
1318
1471
  }
1319
1472
 
1320
1473
  // dist/gltf/gltf-animator.js
1321
- var import_core6 = require("@luma.gl/core");
1474
+ var import_core7 = require("@luma.gl/core");
1322
1475
 
1323
1476
  // dist/gltf/animations/interpolate.js
1324
- var import_core4 = require("@luma.gl/core");
1325
- var import_core5 = require("@math.gl/core");
1477
+ var import_core5 = require("@luma.gl/core");
1478
+ var import_core6 = require("@math.gl/core");
1326
1479
  function updateTargetPath(target, path, newValue) {
1327
1480
  switch (path) {
1328
1481
  case "translation":
@@ -1332,27 +1485,38 @@ function updateTargetPath(target, path, newValue) {
1332
1485
  case "scale":
1333
1486
  return target.setScale(newValue).updateMatrix();
1334
1487
  default:
1335
- import_core4.log.warn(`Bad animation path ${path}`)();
1488
+ import_core5.log.warn(`Bad animation path ${path}`)();
1336
1489
  return null;
1337
1490
  }
1338
1491
  }
1339
1492
  function interpolate(time, { input, interpolation, output }, target, path) {
1493
+ const value = evaluateSampler(time, { input, interpolation, output }, path);
1494
+ if (value) {
1495
+ updateTargetPath(target, path, value);
1496
+ }
1497
+ }
1498
+ function evaluateSampler(time, { input, interpolation, output }, path) {
1340
1499
  const maxTime = input[input.length - 1];
1500
+ if (!Number.isFinite(maxTime) || maxTime <= 0) {
1501
+ return output[0] || null;
1502
+ }
1341
1503
  const animationTime = time % maxTime;
1342
1504
  const nextIndex = input.findIndex((t) => t >= animationTime);
1505
+ if (nextIndex < 0) {
1506
+ return output[output.length - 1] || null;
1507
+ }
1343
1508
  const previousIndex = Math.max(0, nextIndex - 1);
1344
1509
  const previousTime = input[previousIndex];
1345
1510
  const nextTime = input[nextIndex];
1346
1511
  switch (interpolation) {
1347
1512
  case "STEP":
1348
- stepInterpolate(target, path, output[previousIndex]);
1349
- break;
1513
+ return output[previousIndex];
1350
1514
  case "LINEAR":
1351
1515
  if (nextTime > previousTime) {
1352
1516
  const ratio = (animationTime - previousTime) / (nextTime - previousTime);
1353
- linearInterpolate(target, path, output[previousIndex], output[nextIndex], ratio);
1517
+ return linearInterpolate(path, output[previousIndex], output[nextIndex], ratio);
1354
1518
  }
1355
- break;
1519
+ return output[previousIndex] || null;
1356
1520
  case "CUBICSPLINE":
1357
1521
  if (nextTime > previousTime) {
1358
1522
  const ratio = (animationTime - previousTime) / (nextTime - previousTime);
@@ -1361,36 +1525,32 @@ function interpolate(time, { input, interpolation, output }, target, path) {
1361
1525
  const outTangent0 = output[3 * previousIndex + 2];
1362
1526
  const inTangent1 = output[3 * nextIndex + 0];
1363
1527
  const p1 = output[3 * nextIndex + 1];
1364
- cubicsplineInterpolate(target, path, { p0, outTangent0, inTangent1, p1, tDiff, ratio });
1528
+ return cubicsplineInterpolate({ p0, outTangent0, inTangent1, p1, tDiff, ratio });
1365
1529
  }
1366
- break;
1530
+ return output[3 * previousIndex + 1] || null;
1367
1531
  default:
1368
- import_core4.log.warn(`Interpolation ${interpolation} not supported`)();
1369
- break;
1532
+ import_core5.log.warn(`Interpolation ${interpolation} not supported`)();
1533
+ return null;
1370
1534
  }
1371
1535
  }
1372
- function linearInterpolate(target, path, start, stop, ratio) {
1536
+ function linearInterpolate(path, start, stop, ratio) {
1373
1537
  if (path === "rotation") {
1374
- updateTargetPath(target, path, new import_core5.Quaternion().slerp({ start, target: stop, ratio }));
1375
- } else {
1376
- const newVal = [];
1377
- for (let i = 0; i < start.length; i++) {
1378
- newVal[i] = ratio * stop[i] + (1 - ratio) * start[i];
1379
- }
1380
- updateTargetPath(target, path, newVal);
1538
+ return new import_core6.Quaternion().slerp({ start, target: stop, ratio });
1539
+ }
1540
+ const newVal = [];
1541
+ for (let i = 0; i < start.length; i++) {
1542
+ newVal[i] = ratio * stop[i] + (1 - ratio) * start[i];
1381
1543
  }
1544
+ return newVal;
1382
1545
  }
1383
- function cubicsplineInterpolate(target, path, { p0, outTangent0, inTangent1, p1, tDiff, ratio: t }) {
1546
+ function cubicsplineInterpolate({ p0, outTangent0, inTangent1, p1, tDiff, ratio: t }) {
1384
1547
  const newVal = [];
1385
1548
  for (let i = 0; i < p0.length; i++) {
1386
1549
  const m0 = outTangent0[i] * tDiff;
1387
1550
  const m1 = inTangent1[i] * tDiff;
1388
1551
  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;
1389
1552
  }
1390
- updateTargetPath(target, path, newVal);
1391
- }
1392
- function stepInterpolate(target, path, value) {
1393
- updateTargetPath(target, path, value);
1553
+ return newVal;
1394
1554
  }
1395
1555
 
1396
1556
  // dist/gltf/gltf-animator.js
@@ -1399,18 +1559,26 @@ var GLTFSingleAnimator = class {
1399
1559
  animation;
1400
1560
  /** Target scenegraph lookup table. */
1401
1561
  gltfNodeIdToNodeMap;
1562
+ /** Materials aligned with the source glTF materials array. */
1563
+ materials;
1402
1564
  /** Playback start time in seconds. */
1403
1565
  startTime = 0;
1404
1566
  /** Whether playback is currently enabled. */
1405
1567
  playing = true;
1406
1568
  /** Playback speed multiplier. */
1407
1569
  speed = 1;
1570
+ /** Mutable runtime texture-transform state for animated material slots. */
1571
+ materialTextureTransformState = /* @__PURE__ */ new Map();
1408
1572
  /** Creates a single-animation controller. */
1409
1573
  constructor(props) {
1410
1574
  this.animation = props.animation;
1411
1575
  this.gltfNodeIdToNodeMap = props.gltfNodeIdToNodeMap;
1576
+ this.materials = props.materials || [];
1412
1577
  this.animation.name ||= "unnamed";
1413
1578
  Object.assign(this, props);
1579
+ if (this.animation.channels.some((channel) => channel.type !== "node") && !this.materials.length) {
1580
+ throw new Error(`Animation ${this.animation.name} targets materials, but GLTFAnimator was created without a materials array`);
1581
+ }
1414
1582
  }
1415
1583
  /** Advances the animation to the supplied wall-clock time in milliseconds. */
1416
1584
  setTime(timeMs) {
@@ -1419,12 +1587,28 @@ var GLTFSingleAnimator = class {
1419
1587
  }
1420
1588
  const absTime = timeMs / 1e3;
1421
1589
  const time = (absTime - this.startTime) * this.speed;
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}`);
1590
+ this.animation.channels.forEach((channel) => {
1591
+ if (channel.type === "node") {
1592
+ const { sampler, targetNodeId, path } = channel;
1593
+ const targetNode = this.gltfNodeIdToNodeMap.get(targetNodeId);
1594
+ if (!targetNode) {
1595
+ throw new Error(`Cannot find animation target node ${targetNodeId}`);
1596
+ }
1597
+ interpolate(time, sampler, targetNode, path);
1598
+ return;
1599
+ }
1600
+ const material = this.materials[channel.targetMaterialIndex];
1601
+ if (!material) {
1602
+ throw new Error(`Cannot find animation target material ${channel.targetMaterialIndex} for ${channel.pointer}`);
1603
+ }
1604
+ const value = evaluateSampler(time, channel.sampler);
1605
+ if (value) {
1606
+ if (channel.type === "material") {
1607
+ applyMaterialAnimationValue(material, channel, value);
1608
+ } else {
1609
+ applyTextureTransformAnimationValue(material, channel, value, this.materialTextureTransformState);
1610
+ }
1426
1611
  }
1427
- interpolate(time, sampler, targetNode, path);
1428
1612
  });
1429
1613
  }
1430
1614
  };
@@ -1437,13 +1621,14 @@ var GLTFAnimator = class {
1437
1621
  const name = animation.name || `Animation-${index}`;
1438
1622
  return new GLTFSingleAnimator({
1439
1623
  gltfNodeIdToNodeMap: props.gltfNodeIdToNodeMap,
1624
+ materials: props.materials,
1440
1625
  animation: { name, channels: animation.channels }
1441
1626
  });
1442
1627
  });
1443
1628
  }
1444
1629
  /** @deprecated Use .setTime(). Will be removed (deck.gl is using this) */
1445
1630
  animate(time) {
1446
- import_core6.log.warn("GLTFAnimator#animate is deprecated. Use GLTFAnimator#setTime instead")();
1631
+ import_core7.log.warn("GLTFAnimator#animate is deprecated. Use GLTFAnimator#setTime instead")();
1447
1632
  this.setTime(time);
1448
1633
  }
1449
1634
  /** Advances every animation to the supplied wall-clock time in milliseconds. */
@@ -1455,114 +1640,71 @@ var GLTFAnimator = class {
1455
1640
  return this.animations;
1456
1641
  }
1457
1642
  };
1458
-
1459
- // dist/parsers/parse-gltf-animations.js
1460
- var import_core7 = require("@luma.gl/core");
1461
-
1462
- // dist/webgl-to-webgpu/convert-webgl-attribute.js
1463
- var ATTRIBUTE_TYPE_TO_COMPONENTS = {
1464
- SCALAR: 1,
1465
- VEC2: 2,
1466
- VEC3: 3,
1467
- VEC4: 4,
1468
- MAT2: 4,
1469
- MAT3: 9,
1470
- MAT4: 16
1471
- };
1472
- var ATTRIBUTE_COMPONENT_TYPE_TO_ARRAY = {
1473
- 5120: Int8Array,
1474
- 5121: Uint8Array,
1475
- 5122: Int16Array,
1476
- 5123: Uint16Array,
1477
- 5125: Uint32Array,
1478
- 5126: Float32Array
1479
- };
1480
- function accessorToTypedArray(accessor) {
1481
- var _a;
1482
- const ArrayType = ATTRIBUTE_COMPONENT_TYPE_TO_ARRAY[accessor.componentType];
1483
- const components = ATTRIBUTE_TYPE_TO_COMPONENTS[accessor.type];
1484
- const length = components * accessor.count;
1485
- const { buffer, byteOffset = 0 } = ((_a = accessor.bufferView) == null ? void 0 : _a.data) ?? {};
1486
- const typedArray = new ArrayType(buffer, byteOffset + (accessor.byteOffset || 0), length);
1487
- return { typedArray, components };
1643
+ function applyMaterialAnimationValue(material, channel, value) {
1644
+ const pbrMaterial3 = channel.component !== void 0 ? {
1645
+ [channel.property]: updateMaterialArrayComponent(getCurrentMaterialValue(material, channel.property), channel.component, value[0])
1646
+ } : {
1647
+ [channel.property]: value.length === 1 ? value[0] : value
1648
+ };
1649
+ material.setProps({ pbrMaterial: pbrMaterial3 });
1488
1650
  }
1489
-
1490
- // dist/parsers/parse-gltf-animations.js
1491
- function parseGLTFAnimations(gltf) {
1492
- const gltfAnimations = gltf.animations || [];
1493
- const accessorCache1D = /* @__PURE__ */ new Map();
1494
- const accessorCache2D = /* @__PURE__ */ new Map();
1495
- return gltfAnimations.flatMap((animation, index) => {
1496
- const name = animation.name || `Animation-${index}`;
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}`);
1651
+ function getCurrentMaterialValue(material, property) {
1652
+ var _a;
1653
+ const uniformValues = material.shaderInputs.getUniformValues();
1654
+ const currentValue = (_a = uniformValues["pbrMaterial"]) == null ? void 0 : _a[property];
1655
+ return Array.isArray(currentValue) ? [...currentValue] : [];
1656
+ }
1657
+ function updateMaterialArrayComponent(currentValue, component, nextValue) {
1658
+ const updatedValue = [...currentValue];
1659
+ updatedValue[component] = nextValue;
1660
+ return updatedValue;
1661
+ }
1662
+ function applyTextureTransformAnimationValue(material, channel, value, materialTextureTransformState) {
1663
+ const slotDefinition = getTextureTransformSlotDefinition(channel.textureSlot);
1664
+ const currentTransform = getCurrentTextureTransform(materialTextureTransformState, material, channel);
1665
+ switch (channel.path) {
1666
+ case "offset":
1667
+ if (channel.component !== void 0) {
1668
+ currentTransform.offset[channel.component] = value[0];
1669
+ } else {
1670
+ currentTransform.offset = [value[0], value[1]];
1506
1671
  }
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);
1672
+ break;
1673
+ case "rotation":
1674
+ currentTransform.rotation = value[0];
1675
+ break;
1676
+ case "scale":
1677
+ if (channel.component !== void 0) {
1678
+ currentTransform.scale[channel.component] = value[0];
1679
+ } else {
1680
+ currentTransform.scale = [value[0], value[1]];
1520
1681
  }
1521
- return {
1522
- sampler: parsedSampler,
1523
- targetNodeId: targetNode.id,
1524
- path
1525
- };
1526
- });
1527
- return channels.length ? [{ name, channels }] : [];
1528
- });
1529
- }
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)));
1682
+ break;
1556
1683
  }
1557
- accessorCache.set(accessor, result);
1558
- return result;
1684
+ material.setProps({
1685
+ pbrMaterial: {
1686
+ [slotDefinition.uvTransformUniform]: getTextureTransformDeltaMatrix(channel.baseTransform, currentTransform)
1687
+ }
1688
+ });
1559
1689
  }
1560
- function assert(condition, message) {
1561
- if (!condition) {
1562
- throw new Error(message);
1690
+ function getCurrentTextureTransform(materialTextureTransformState, material, channel) {
1691
+ const materialState = materialTextureTransformState.get(material) || {};
1692
+ let textureTransformState = materialState[channel.textureSlot];
1693
+ if (!textureTransformState) {
1694
+ textureTransformState = {
1695
+ offset: [...channel.baseTransform.offset],
1696
+ rotation: channel.baseTransform.rotation,
1697
+ scale: [...channel.baseTransform.scale]
1698
+ };
1699
+ materialState[channel.textureSlot] = textureTransformState;
1700
+ materialTextureTransformState.set(material, materialState);
1563
1701
  }
1702
+ return textureTransformState;
1564
1703
  }
1565
1704
 
1705
+ // dist/parsers/parse-gltf-animations.js
1706
+ var import_core8 = require("@luma.gl/core");
1707
+
1566
1708
  // dist/gltf/gltf-extension-support.js
1567
1709
  var UNKNOWN_EXTENSION_SUPPORT = {
1568
1710
  supportLevel: "none",
@@ -1658,8 +1800,8 @@ var GLTF_EXTENSION_SUPPORT_REGISTRY = {
1658
1800
  comment: "Node-visibility animations and toggles are not mapped onto runtime scenegraph state."
1659
1801
  },
1660
1802
  KHR_animation_pointer: {
1661
- supportLevel: "none",
1662
- comment: "Animation pointers are not mapped onto runtime scenegraph updates."
1803
+ supportLevel: "parsed-and-wired",
1804
+ comment: "Selected node TRS, material factor, and KHR_texture_transform offset/rotation/scale pointers are wired to runtime updates; unsupported targets are skipped."
1663
1805
  },
1664
1806
  KHR_materials_diffuse_transmission: {
1665
1807
  supportLevel: "none",
@@ -1702,7 +1844,7 @@ function getGLTFExtensionSupport(gltf) {
1702
1844
  extensionName,
1703
1845
  {
1704
1846
  extensionName,
1705
- supported: extensionSupportDefinition.supportLevel === "built-in",
1847
+ supported: extensionSupportDefinition.supportLevel === "built-in" || extensionSupportDefinition.supportLevel === "parsed-and-wired",
1706
1848
  supportLevel: extensionSupportDefinition.supportLevel,
1707
1849
  comment: extensionSupportDefinition.comment
1708
1850
  }
@@ -1710,6 +1852,9 @@ function getGLTFExtensionSupport(gltf) {
1710
1852
  });
1711
1853
  return new Map(extensionSupportEntries);
1712
1854
  }
1855
+ function getRegisteredGLTFExtensionSupport(extensionName) {
1856
+ return GLTF_EXTENSION_SUPPORT_REGISTRY[extensionName] || null;
1857
+ }
1713
1858
  function collectGLTFExtensionNames(gltf) {
1714
1859
  var _a;
1715
1860
  const gltfWithRemovedExtensions = gltf;
@@ -1718,10 +1863,10 @@ function collectGLTFExtensionNames(gltf) {
1718
1863
  addExtensionNames(extensionNames, gltf.extensionsRequired);
1719
1864
  addExtensionNames(extensionNames, gltfWithRemovedExtensions.extensionsRemoved);
1720
1865
  addExtensionNames(extensionNames, Object.keys(gltf.extensions || {}));
1721
- if (((_a = gltfWithRemovedExtensions.lights) == null ? void 0 : _a.length) || gltf.nodes.some((node) => "light" in node)) {
1866
+ if (((_a = gltfWithRemovedExtensions.lights) == null ? void 0 : _a.length) || (gltf.nodes || []).some((node) => "light" in node)) {
1722
1867
  extensionNames.add("KHR_lights_punctual");
1723
1868
  }
1724
- if (gltf.materials.some((material) => {
1869
+ if ((gltf.materials || []).some((material) => {
1725
1870
  var _a2;
1726
1871
  const gltfMaterial = material;
1727
1872
  return gltfMaterial.unlit || ((_a2 = gltfMaterial.extensions) == null ? void 0 : _a2.KHR_materials_unlit);
@@ -1736,12 +1881,358 @@ function addExtensionNames(extensionNames, newExtensionNames = []) {
1736
1881
  }
1737
1882
  }
1738
1883
 
1884
+ // dist/webgl-to-webgpu/convert-webgl-attribute.js
1885
+ var ATTRIBUTE_TYPE_TO_COMPONENTS = {
1886
+ SCALAR: 1,
1887
+ VEC2: 2,
1888
+ VEC3: 3,
1889
+ VEC4: 4,
1890
+ MAT2: 4,
1891
+ MAT3: 9,
1892
+ MAT4: 16
1893
+ };
1894
+ var ATTRIBUTE_COMPONENT_TYPE_TO_ARRAY = {
1895
+ 5120: Int8Array,
1896
+ 5121: Uint8Array,
1897
+ 5122: Int16Array,
1898
+ 5123: Uint16Array,
1899
+ 5125: Uint32Array,
1900
+ 5126: Float32Array
1901
+ };
1902
+ function accessorToTypedArray(accessor) {
1903
+ var _a;
1904
+ const ArrayType = ATTRIBUTE_COMPONENT_TYPE_TO_ARRAY[accessor.componentType];
1905
+ const components = ATTRIBUTE_TYPE_TO_COMPONENTS[accessor.type];
1906
+ const length = components * accessor.count;
1907
+ const { buffer, byteOffset = 0 } = ((_a = accessor.bufferView) == null ? void 0 : _a.data) ?? {};
1908
+ const typedArray = new ArrayType(buffer, byteOffset + (accessor.byteOffset || 0), length);
1909
+ return { typedArray, components };
1910
+ }
1911
+
1912
+ // dist/parsers/parse-gltf-animations.js
1913
+ function parseGLTFAnimations(gltf) {
1914
+ const gltfAnimations = gltf.animations || [];
1915
+ const accessorCache1D = /* @__PURE__ */ new Map();
1916
+ const accessorCache2D = /* @__PURE__ */ new Map();
1917
+ return gltfAnimations.flatMap((animation, index) => {
1918
+ const name = animation.name || `Animation-${index}`;
1919
+ const samplerCache = /* @__PURE__ */ new Map();
1920
+ const channels = animation.channels.flatMap(({ sampler, target }) => {
1921
+ let parsedSampler = samplerCache.get(sampler);
1922
+ if (!parsedSampler) {
1923
+ const gltfSampler = animation.samplers[sampler];
1924
+ if (!gltfSampler) {
1925
+ throw new Error(`Cannot find animation sampler ${sampler}`);
1926
+ }
1927
+ const { input, interpolation = "LINEAR", output } = gltfSampler;
1928
+ parsedSampler = {
1929
+ input: accessorToJsArray1D(gltf.accessors[input], accessorCache1D),
1930
+ interpolation,
1931
+ output: accessorToJsArray2D(gltf.accessors[output], accessorCache2D)
1932
+ };
1933
+ samplerCache.set(sampler, parsedSampler);
1934
+ }
1935
+ const parsedChannel = parseAnimationChannel(gltf, target, parsedSampler);
1936
+ return parsedChannel ? [parsedChannel] : [];
1937
+ });
1938
+ return channels.length ? [{ name, channels }] : [];
1939
+ });
1940
+ }
1941
+ function parseAnimationChannel(gltf, target, sampler) {
1942
+ if (target.path === "pointer") {
1943
+ return parseAnimationPointerChannel(gltf, target, sampler);
1944
+ }
1945
+ const path = getNodeAnimationPath(target.path);
1946
+ if (!path) {
1947
+ return null;
1948
+ }
1949
+ const targetNode = gltf.nodes[target.node ?? 0];
1950
+ if (!targetNode) {
1951
+ throw new Error(`Cannot find animation target ${target.node}`);
1952
+ }
1953
+ return {
1954
+ type: "node",
1955
+ sampler,
1956
+ targetNodeId: targetNode.id,
1957
+ path
1958
+ };
1959
+ }
1960
+ function parseAnimationPointerChannel(gltf, target, sampler) {
1961
+ var _a, _b;
1962
+ const pointer = (_b = (_a = target.extensions) == null ? void 0 : _a["KHR_animation_pointer"]) == null ? void 0 : _b.pointer;
1963
+ if (typeof pointer !== "string" || !pointer.startsWith("/")) {
1964
+ import_core8.log.warn("KHR_animation_pointer channel is missing a valid JSON pointer and will be skipped")();
1965
+ return null;
1966
+ }
1967
+ const pointerSegments = splitJsonPointer(pointer);
1968
+ switch (pointerSegments[0]) {
1969
+ case "nodes":
1970
+ return parseNodePointerAnimationChannel(gltf, pointerSegments, sampler, pointer);
1971
+ case "materials":
1972
+ return parseMaterialPointerAnimationChannel(gltf, pointerSegments, sampler, pointer);
1973
+ default:
1974
+ warnUnsupportedAnimationPointer(pointer, `top-level target "${pointerSegments[0]}" has no runtime animation mapping`);
1975
+ return null;
1976
+ }
1977
+ }
1978
+ function parseNodePointerAnimationChannel(gltf, pointerSegments, sampler, pointer) {
1979
+ if (pointerSegments.length !== 3) {
1980
+ warnUnsupportedAnimationPointer(pointer, "node pointers must use /nodes/{index}/{translation|rotation|scale|weights}");
1981
+ return null;
1982
+ }
1983
+ const nodeIndex = Number(pointerSegments[1]);
1984
+ const targetNode = gltf.nodes[nodeIndex];
1985
+ if (!Number.isInteger(nodeIndex) || !targetNode) {
1986
+ import_core8.log.warn(`KHR_animation_pointer target ${pointer} references a missing node and will be skipped`)();
1987
+ return null;
1988
+ }
1989
+ const path = getNodeAnimationPath(pointerSegments[2]);
1990
+ if (!path) {
1991
+ warnUnsupportedAnimationPointer(pointer, `node property "${pointerSegments[2]}" has no runtime animation mapping`);
1992
+ return null;
1993
+ }
1994
+ if (path === "weights") {
1995
+ import_core8.log.warn(`KHR_animation_pointer target ${pointer} will be skipped because morph weights are not implemented in GLTFAnimator`)();
1996
+ return null;
1997
+ }
1998
+ return {
1999
+ type: "node",
2000
+ sampler,
2001
+ targetNodeId: targetNode.id,
2002
+ path
2003
+ };
2004
+ }
2005
+ function parseMaterialPointerAnimationChannel(gltf, pointerSegments, sampler, pointer) {
2006
+ if (pointerSegments.length < 3) {
2007
+ warnUnsupportedAnimationPointer(pointer, "material pointers must include a material index and target property path");
2008
+ return null;
2009
+ }
2010
+ const materialIndex = Number(pointerSegments[1]);
2011
+ const material = gltf.materials[materialIndex];
2012
+ if (!Number.isInteger(materialIndex) || !material) {
2013
+ import_core8.log.warn(`KHR_animation_pointer target ${pointer} references a missing material and will be skipped`)();
2014
+ return null;
2015
+ }
2016
+ const materialTarget = resolveMaterialAnimationTarget(material, pointerSegments.slice(2));
2017
+ if ("reason" in materialTarget) {
2018
+ warnUnsupportedAnimationPointer(pointer, materialTarget.reason);
2019
+ return null;
2020
+ }
2021
+ return {
2022
+ sampler,
2023
+ pointer,
2024
+ targetMaterialIndex: materialIndex,
2025
+ ...materialTarget
2026
+ };
2027
+ }
2028
+ function getNodeAnimationPath(path) {
2029
+ switch (path) {
2030
+ case "translation":
2031
+ case "rotation":
2032
+ case "scale":
2033
+ case "weights":
2034
+ return path;
2035
+ default:
2036
+ return null;
2037
+ }
2038
+ }
2039
+ function resolveMaterialAnimationTarget(material, pointerSegments) {
2040
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
2041
+ const textureTransformTarget = resolveTextureTransformAnimationTarget(material, pointerSegments);
2042
+ if (!("reason" in textureTransformTarget)) {
2043
+ return textureTransformTarget;
2044
+ }
2045
+ if (textureTransformTarget.reason !== "not-a-texture-transform-target") {
2046
+ return textureTransformTarget;
2047
+ }
2048
+ const pointerPath = pointerSegments.join("/");
2049
+ switch (pointerPath) {
2050
+ case "pbrMetallicRoughness/baseColorFactor":
2051
+ return material["pbrMetallicRoughness"] ? { type: "material", property: "baseColorFactor" } : { reason: getUnsupportedMaterialPointerReason(pointerSegments) };
2052
+ case "pbrMetallicRoughness/metallicFactor":
2053
+ return material["pbrMetallicRoughness"] ? { type: "material", property: "metallicRoughnessValues", component: 0 } : { reason: getUnsupportedMaterialPointerReason(pointerSegments) };
2054
+ case "pbrMetallicRoughness/roughnessFactor":
2055
+ return material["pbrMetallicRoughness"] ? { type: "material", property: "metallicRoughnessValues", component: 1 } : { reason: getUnsupportedMaterialPointerReason(pointerSegments) };
2056
+ case "normalTexture/scale":
2057
+ return material["normalTexture"] ? { type: "material", property: "normalScale" } : { reason: getUnsupportedMaterialPointerReason(pointerSegments) };
2058
+ case "occlusionTexture/strength":
2059
+ return material["occlusionTexture"] ? { type: "material", property: "occlusionStrength" } : { reason: getUnsupportedMaterialPointerReason(pointerSegments) };
2060
+ case "emissiveFactor":
2061
+ return { type: "material", property: "emissiveFactor" };
2062
+ case "alphaCutoff":
2063
+ return { type: "material", property: "alphaCutoff" };
2064
+ case "extensions/KHR_materials_specular/specularFactor":
2065
+ return ((_a = material["extensions"]) == null ? void 0 : _a["KHR_materials_specular"]) ? { type: "material", property: "specularIntensityFactor" } : { reason: getUnsupportedMaterialPointerReason(pointerSegments) };
2066
+ case "extensions/KHR_materials_specular/specularColorFactor":
2067
+ return ((_b = material["extensions"]) == null ? void 0 : _b["KHR_materials_specular"]) ? { type: "material", property: "specularColorFactor" } : { reason: getUnsupportedMaterialPointerReason(pointerSegments) };
2068
+ case "extensions/KHR_materials_ior/ior":
2069
+ return ((_c = material["extensions"]) == null ? void 0 : _c["KHR_materials_ior"]) ? { type: "material", property: "ior" } : { reason: getUnsupportedMaterialPointerReason(pointerSegments) };
2070
+ case "extensions/KHR_materials_transmission/transmissionFactor":
2071
+ return ((_d = material["extensions"]) == null ? void 0 : _d["KHR_materials_transmission"]) ? { type: "material", property: "transmissionFactor" } : { reason: getUnsupportedMaterialPointerReason(pointerSegments) };
2072
+ case "extensions/KHR_materials_volume/thicknessFactor":
2073
+ return ((_e = material["extensions"]) == null ? void 0 : _e["KHR_materials_volume"]) ? { type: "material", property: "thicknessFactor" } : { reason: getUnsupportedMaterialPointerReason(pointerSegments) };
2074
+ case "extensions/KHR_materials_volume/attenuationDistance":
2075
+ return ((_f = material["extensions"]) == null ? void 0 : _f["KHR_materials_volume"]) ? { type: "material", property: "attenuationDistance" } : { reason: getUnsupportedMaterialPointerReason(pointerSegments) };
2076
+ case "extensions/KHR_materials_volume/attenuationColor":
2077
+ return ((_g = material["extensions"]) == null ? void 0 : _g["KHR_materials_volume"]) ? { type: "material", property: "attenuationColor" } : { reason: getUnsupportedMaterialPointerReason(pointerSegments) };
2078
+ case "extensions/KHR_materials_clearcoat/clearcoatFactor":
2079
+ return ((_h = material["extensions"]) == null ? void 0 : _h["KHR_materials_clearcoat"]) ? { type: "material", property: "clearcoatFactor" } : { reason: getUnsupportedMaterialPointerReason(pointerSegments) };
2080
+ case "extensions/KHR_materials_clearcoat/clearcoatRoughnessFactor":
2081
+ return ((_i = material["extensions"]) == null ? void 0 : _i["KHR_materials_clearcoat"]) ? { type: "material", property: "clearcoatRoughnessFactor" } : { reason: getUnsupportedMaterialPointerReason(pointerSegments) };
2082
+ case "extensions/KHR_materials_sheen/sheenColorFactor":
2083
+ return ((_j = material["extensions"]) == null ? void 0 : _j["KHR_materials_sheen"]) ? { type: "material", property: "sheenColorFactor" } : { reason: getUnsupportedMaterialPointerReason(pointerSegments) };
2084
+ case "extensions/KHR_materials_sheen/sheenRoughnessFactor":
2085
+ return ((_k = material["extensions"]) == null ? void 0 : _k["KHR_materials_sheen"]) ? { type: "material", property: "sheenRoughnessFactor" } : { reason: getUnsupportedMaterialPointerReason(pointerSegments) };
2086
+ case "extensions/KHR_materials_iridescence/iridescenceFactor":
2087
+ return ((_l = material["extensions"]) == null ? void 0 : _l["KHR_materials_iridescence"]) ? { type: "material", property: "iridescenceFactor" } : { reason: getUnsupportedMaterialPointerReason(pointerSegments) };
2088
+ case "extensions/KHR_materials_iridescence/iridescenceIor":
2089
+ return ((_m = material["extensions"]) == null ? void 0 : _m["KHR_materials_iridescence"]) ? { type: "material", property: "iridescenceIor" } : { reason: getUnsupportedMaterialPointerReason(pointerSegments) };
2090
+ case "extensions/KHR_materials_iridescence/iridescenceThicknessMinimum":
2091
+ return ((_n = material["extensions"]) == null ? void 0 : _n["KHR_materials_iridescence"]) ? { type: "material", property: "iridescenceThicknessRange", component: 0 } : { reason: getUnsupportedMaterialPointerReason(pointerSegments) };
2092
+ case "extensions/KHR_materials_iridescence/iridescenceThicknessMaximum":
2093
+ return ((_o = material["extensions"]) == null ? void 0 : _o["KHR_materials_iridescence"]) ? { type: "material", property: "iridescenceThicknessRange", component: 1 } : { reason: getUnsupportedMaterialPointerReason(pointerSegments) };
2094
+ case "extensions/KHR_materials_anisotropy/anisotropyStrength":
2095
+ return ((_p = material["extensions"]) == null ? void 0 : _p["KHR_materials_anisotropy"]) ? { type: "material", property: "anisotropyStrength" } : { reason: getUnsupportedMaterialPointerReason(pointerSegments) };
2096
+ case "extensions/KHR_materials_anisotropy/anisotropyRotation":
2097
+ return ((_q = material["extensions"]) == null ? void 0 : _q["KHR_materials_anisotropy"]) ? { type: "material", property: "anisotropyRotation" } : { reason: getUnsupportedMaterialPointerReason(pointerSegments) };
2098
+ case "extensions/KHR_materials_emissive_strength/emissiveStrength":
2099
+ return ((_r = material["extensions"]) == null ? void 0 : _r["KHR_materials_emissive_strength"]) ? { type: "material", property: "emissiveStrength" } : { reason: getUnsupportedMaterialPointerReason(pointerSegments) };
2100
+ default:
2101
+ return { reason: getUnsupportedMaterialPointerReason(pointerSegments) };
2102
+ }
2103
+ }
2104
+ function resolveTextureTransformAnimationTarget(material, pointerSegments) {
2105
+ const extensionIndex = pointerSegments.lastIndexOf("extensions");
2106
+ if (extensionIndex < 0 || pointerSegments[extensionIndex + 1] !== "KHR_texture_transform" || extensionIndex < 1) {
2107
+ return { reason: "not-a-texture-transform-target" };
2108
+ }
2109
+ const textureSlotDefinition = resolveTextureTransformSlot(pointerSegments.slice(0, extensionIndex));
2110
+ if (!textureSlotDefinition) {
2111
+ return {
2112
+ reason: getUnsupportedTextureTransformSlotReason(pointerSegments.slice(0, extensionIndex))
2113
+ };
2114
+ }
2115
+ const textureInfo = getNestedMaterialValue(material, textureSlotDefinition.pathSegments);
2116
+ if (!textureInfo) {
2117
+ return {
2118
+ reason: `texture-transform target "${pointerSegments.slice(0, extensionIndex).join("/")}" does not exist on the referenced material`
2119
+ };
2120
+ }
2121
+ const textureTransformPath = pointerSegments[extensionIndex + 2];
2122
+ if (textureTransformPath === "texCoord") {
2123
+ return {
2124
+ reason: "animated KHR_texture_transform.texCoord is unsupported because texCoord selection is structural, not a runtime float/vector update"
2125
+ };
2126
+ }
2127
+ if (textureTransformPath !== "offset" && textureTransformPath !== "rotation" && textureTransformPath !== "scale") {
2128
+ return {
2129
+ reason: `KHR_texture_transform property "${textureTransformPath}" is not animatable; supported properties are offset, rotation, and scale`
2130
+ };
2131
+ }
2132
+ const componentSegment = pointerSegments[extensionIndex + 3];
2133
+ if (pointerSegments.length > extensionIndex + 4) {
2134
+ return {
2135
+ reason: `KHR_texture_transform.${textureTransformPath} does not support nested property paths`
2136
+ };
2137
+ }
2138
+ let component;
2139
+ if (componentSegment !== void 0) {
2140
+ component = Number(componentSegment);
2141
+ if (textureTransformPath === "rotation") {
2142
+ return {
2143
+ reason: "KHR_texture_transform.rotation does not support component indices"
2144
+ };
2145
+ }
2146
+ if (!Number.isInteger(component) || component < 0 || component > 1) {
2147
+ return {
2148
+ reason: `KHR_texture_transform.${textureTransformPath} component index "${componentSegment}" is invalid; only 0 and 1 are supported`
2149
+ };
2150
+ }
2151
+ }
2152
+ return {
2153
+ type: "textureTransform",
2154
+ textureSlot: textureSlotDefinition.slot,
2155
+ path: textureTransformPath,
2156
+ component,
2157
+ baseTransform: resolveTextureTransform(textureInfo)
2158
+ };
2159
+ }
2160
+ function getNestedMaterialValue(material, pathSegments) {
2161
+ let value = material;
2162
+ for (const pathSegment of pathSegments) {
2163
+ value = value == null ? void 0 : value[pathSegment];
2164
+ if (!value) {
2165
+ return null;
2166
+ }
2167
+ }
2168
+ return value;
2169
+ }
2170
+ function splitJsonPointer(pointer) {
2171
+ return pointer.slice(1).split("/").map((segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~"));
2172
+ }
2173
+ function getUnsupportedMaterialPointerReason(pointerSegments) {
2174
+ const extensionName = getPointerExtensionName(pointerSegments);
2175
+ if (extensionName) {
2176
+ const extensionSupport = getRegisteredGLTFExtensionSupport(extensionName);
2177
+ if ((extensionSupport == null ? void 0 : extensionSupport.supportLevel) === "none") {
2178
+ return `${extensionName} is referenced by this pointer, but ${extensionSupport.comment.charAt(0).toLowerCase()}${extensionSupport.comment.slice(1)}`;
2179
+ }
2180
+ }
2181
+ return `no runtime target exists for material property "${pointerSegments.join("/")}"`;
2182
+ }
2183
+ function getUnsupportedTextureTransformSlotReason(pointerSegments) {
2184
+ const extensionName = getPointerExtensionName(pointerSegments);
2185
+ if (extensionName) {
2186
+ const extensionSupport = getRegisteredGLTFExtensionSupport(extensionName);
2187
+ if ((extensionSupport == null ? void 0 : extensionSupport.supportLevel) === "none") {
2188
+ return `${extensionName} is referenced by this pointer, but ${extensionSupport.comment.charAt(0).toLowerCase()}${extensionSupport.comment.slice(1)}`;
2189
+ }
2190
+ }
2191
+ return `texture-transform target "${pointerSegments.join("/")}" has no runtime texture-slot mapping`;
2192
+ }
2193
+ function getPointerExtensionName(pointerSegments) {
2194
+ const extensionIndex = pointerSegments.indexOf("extensions");
2195
+ const extensionName = pointerSegments[extensionIndex + 1];
2196
+ return extensionIndex >= 0 && extensionName ? extensionName : null;
2197
+ }
2198
+ function warnUnsupportedAnimationPointer(pointer, reason) {
2199
+ import_core8.log.warn(`KHR_animation_pointer target ${pointer} will be skipped because ${reason}`)();
2200
+ }
2201
+ function accessorToJsArray1D(accessor, accessorCache) {
2202
+ if (accessorCache.has(accessor)) {
2203
+ return accessorCache.get(accessor);
2204
+ }
2205
+ const { typedArray: array, components } = accessorToTypedArray(accessor);
2206
+ assert(components === 1, "accessorToJsArray1D must have exactly 1 component");
2207
+ const result = Array.from(array);
2208
+ accessorCache.set(accessor, result);
2209
+ return result;
2210
+ }
2211
+ function accessorToJsArray2D(accessor, accessorCache) {
2212
+ if (accessorCache.has(accessor)) {
2213
+ return accessorCache.get(accessor);
2214
+ }
2215
+ const { typedArray: array, components } = accessorToTypedArray(accessor);
2216
+ assert(components >= 1, "accessorToJsArray2D must have at least 1 component");
2217
+ const result = [];
2218
+ for (let i = 0; i < array.length; i += components) {
2219
+ result.push(Array.from(array.slice(i, i + components)));
2220
+ }
2221
+ accessorCache.set(accessor, result);
2222
+ return result;
2223
+ }
2224
+ function assert(condition, message) {
2225
+ if (!condition) {
2226
+ throw new Error(message);
2227
+ }
2228
+ }
2229
+
1739
2230
  // dist/gltf/create-scenegraph-from-gltf.js
1740
2231
  function createScenegraphsFromGLTF(device, gltf, options) {
1741
2232
  const { scenes, materials, gltfMeshIdToNodeMap, gltfNodeIdToNodeMap, gltfNodeIndexToNodeMap } = parseGLTF(device, gltf, options);
1742
2233
  const animations = parseGLTFAnimations(gltf);
1743
- const animator = new GLTFAnimator({ animations, gltfNodeIdToNodeMap });
1744
- const lights = parseGLTFLights(gltf);
2234
+ const animator = new GLTFAnimator({ animations, gltfNodeIdToNodeMap, materials });
2235
+ const lights = parseGLTFLights(gltf, { useByteColors: (options == null ? void 0 : options.useByteColors) ?? true });
1745
2236
  const extensionSupport = getGLTFExtensionSupport(gltf);
1746
2237
  const sceneBounds = scenes.map((scene) => getScenegraphBounds(scene.getBounds()));
1747
2238
  const modelBounds = getCombinedScenegraphBounds(sceneBounds);