@luma.gl/engine 9.2.6 → 9.3.0-alpha.2

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 (71) hide show
  1. package/dist/animation-loop/animation-loop.d.ts +3 -1
  2. package/dist/animation-loop/animation-loop.d.ts.map +1 -1
  3. package/dist/animation-loop/animation-loop.js +10 -4
  4. package/dist/animation-loop/animation-loop.js.map +1 -1
  5. package/dist/compute/computation.d.ts.map +1 -1
  6. package/dist/compute/computation.js +3 -2
  7. package/dist/compute/computation.js.map +1 -1
  8. package/dist/compute/swap.d.ts +2 -0
  9. package/dist/compute/swap.d.ts.map +1 -1
  10. package/dist/compute/swap.js +10 -5
  11. package/dist/compute/swap.js.map +1 -1
  12. package/dist/dist.dev.js +554 -358
  13. package/dist/dist.min.js +59 -50
  14. package/dist/dynamic-texture/dynamic-texture.d.ts +95 -0
  15. package/dist/dynamic-texture/dynamic-texture.d.ts.map +1 -0
  16. package/dist/dynamic-texture/dynamic-texture.js +356 -0
  17. package/dist/dynamic-texture/dynamic-texture.js.map +1 -0
  18. package/dist/dynamic-texture/texture-data.d.ts +137 -0
  19. package/dist/dynamic-texture/texture-data.d.ts.map +1 -0
  20. package/dist/dynamic-texture/texture-data.js +183 -0
  21. package/dist/dynamic-texture/texture-data.js.map +1 -0
  22. package/dist/factories/pipeline-factory.d.ts.map +1 -1
  23. package/dist/factories/pipeline-factory.js +3 -3
  24. package/dist/factories/pipeline-factory.js.map +1 -1
  25. package/dist/factories/shader-factory.d.ts.map +1 -1
  26. package/dist/factories/shader-factory.js +3 -2
  27. package/dist/factories/shader-factory.js.map +1 -1
  28. package/dist/index.cjs +566 -370
  29. package/dist/index.cjs.map +4 -4
  30. package/dist/index.d.ts +8 -3
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +4 -1
  33. package/dist/index.js.map +1 -1
  34. package/dist/model/model.d.ts +31 -10
  35. package/dist/model/model.d.ts.map +1 -1
  36. package/dist/model/model.js +34 -14
  37. package/dist/model/model.js.map +1 -1
  38. package/dist/models/billboard-texture-model.d.ts +8 -5
  39. package/dist/models/billboard-texture-model.d.ts.map +1 -1
  40. package/dist/models/billboard-texture-model.js +70 -18
  41. package/dist/models/billboard-texture-model.js.map +1 -1
  42. package/dist/passes/get-fragment-shader.js +15 -11
  43. package/dist/passes/get-fragment-shader.js.map +1 -1
  44. package/dist/passes/shader-pass-renderer.d.ts +5 -5
  45. package/dist/passes/shader-pass-renderer.d.ts.map +1 -1
  46. package/dist/passes/shader-pass-renderer.js +13 -12
  47. package/dist/passes/shader-pass-renderer.js.map +1 -1
  48. package/dist/types.d.ts +7 -0
  49. package/dist/types.d.ts.map +1 -0
  50. package/dist/types.js +5 -0
  51. package/dist/types.js.map +1 -0
  52. package/package.json +4 -4
  53. package/src/animation-loop/animation-loop.ts +11 -4
  54. package/src/compute/computation.ts +3 -2
  55. package/src/compute/swap.ts +13 -7
  56. package/src/dynamic-texture/dynamic-texture.ts +451 -0
  57. package/src/dynamic-texture/texture-data.ts +301 -0
  58. package/src/factories/pipeline-factory.ts +4 -3
  59. package/src/factories/shader-factory.ts +4 -2
  60. package/src/index.ts +9 -4
  61. package/src/model/model.ts +37 -18
  62. package/src/models/billboard-texture-model.ts +81 -22
  63. package/src/passes/get-fragment-shader.ts +15 -11
  64. package/src/passes/shader-pass-renderer.ts +22 -16
  65. package/src/types.ts +11 -0
  66. package/dist/async-texture/async-texture.d.ts +0 -166
  67. package/dist/async-texture/async-texture.d.ts.map +0 -1
  68. package/dist/async-texture/async-texture.js +0 -386
  69. package/dist/async-texture/async-texture.js.map +0 -1
  70. package/src/async-texture/async-texture.ts +0 -551
  71. /package/src/{async-texture/texture-setters.ts.disabled → dynamic-texture/texture-data.ts.disabled} +0 -0
package/dist/dist.dev.js CHANGED
@@ -70,6 +70,7 @@ var __exports__ = (() => {
70
70
  ConeGeometry: () => ConeGeometry,
71
71
  CubeGeometry: () => CubeGeometry,
72
72
  CylinderGeometry: () => CylinderGeometry,
73
+ DynamicTexture: () => DynamicTexture,
73
74
  GPUGeometry: () => GPUGeometry,
74
75
  Geometry: () => Geometry,
75
76
  GroupNode: () => GroupNode,
@@ -492,7 +493,7 @@ var __exports__ = (() => {
492
493
  gpuTime;
493
494
  frameRate;
494
495
  display;
495
- needsRedraw = "initialized";
496
+ _needsRedraw = "initialized";
496
497
  _initialized = false;
497
498
  _running = false;
498
499
  _animationFrameId = null;
@@ -534,9 +535,15 @@ var __exports__ = (() => {
534
535
  }
535
536
  /** Flags this animation loop as needing redraw */
536
537
  setNeedsRedraw(reason) {
537
- this.needsRedraw = this.needsRedraw || reason;
538
+ this._needsRedraw = this._needsRedraw || reason;
538
539
  return this;
539
540
  }
541
+ /** Query redraw status. Clears the flag. */
542
+ needsRedraw() {
543
+ const reason = this._needsRedraw;
544
+ this._needsRedraw = false;
545
+ return reason;
546
+ }
540
547
  setProps(props) {
541
548
  if ("autoResizeViewport" in props) {
542
549
  this.props.autoResizeViewport = props.autoResizeViewport || false;
@@ -678,7 +685,7 @@ var __exports__ = (() => {
678
685
  this.device?.submit();
679
686
  }
680
687
  _clearNeedsRedraw() {
681
- this.needsRedraw = false;
688
+ this._needsRedraw = false;
682
689
  }
683
690
  _setupFrame() {
684
691
  this._resizeViewport();
@@ -736,7 +743,7 @@ var __exports__ = (() => {
736
743
  this.animationProps.width = width;
737
744
  this.animationProps.height = height;
738
745
  this.animationProps.aspect = aspect;
739
- this.animationProps.needsRedraw = this.needsRedraw;
746
+ this.animationProps.needsRedraw = this._needsRedraw;
740
747
  this.animationProps.engineTime = Date.now() - this.animationProps.startTime;
741
748
  if (this.timeline) {
742
749
  this.timeline.update(this.animationProps.engineTime);
@@ -892,7 +899,7 @@ var __exports__ = (() => {
892
899
  }
893
900
 
894
901
  // src/model/model.ts
895
- var import_core9 = __toESM(require_core(), 1);
902
+ var import_core10 = __toESM(require_core(), 1);
896
903
  var import_shadertools2 = __toESM(require_shadertools(), 1);
897
904
 
898
905
  // src/geometry/gpu-geometry.ts
@@ -1007,8 +1014,9 @@ var __exports__ = (() => {
1007
1014
  var _PipelineFactory = class {
1008
1015
  /** Get the singleton default pipeline factory for the specified device */
1009
1016
  static getDefaultPipelineFactory(device) {
1010
- device._lumaData["defaultPipelineFactory"] = device._lumaData["defaultPipelineFactory"] || new _PipelineFactory(device);
1011
- return device._lumaData["defaultPipelineFactory"];
1017
+ const moduleData = device.getModuleData("@luma.gl/engine");
1018
+ moduleData.defaultPipelineFactory ||= new _PipelineFactory(device);
1019
+ return moduleData.defaultPipelineFactory;
1012
1020
  }
1013
1021
  device;
1014
1022
  cachingEnabled;
@@ -1177,8 +1185,9 @@ var __exports__ = (() => {
1177
1185
  var _ShaderFactory = class {
1178
1186
  /** Returns the default ShaderFactory for the given {@link Device}, creating one if necessary. */
1179
1187
  static getDefaultShaderFactory(device) {
1180
- device._lumaData["defaultShaderFactory"] ||= new _ShaderFactory(device);
1181
- return device._lumaData["defaultShaderFactory"];
1188
+ const moduleData = device.getModuleData("@luma.gl/engine");
1189
+ moduleData.defaultShaderFactory ||= new _ShaderFactory(device);
1190
+ return moduleData.defaultShaderFactory;
1182
1191
  }
1183
1192
  device;
1184
1193
  cachingEnabled;
@@ -1554,49 +1563,160 @@ var __exports__ = (() => {
1554
1563
  }
1555
1564
  };
1556
1565
 
1557
- // src/async-texture/async-texture.ts
1558
- var import_core8 = __toESM(require_core(), 1);
1566
+ // src/dynamic-texture/dynamic-texture.ts
1567
+ var import_core9 = __toESM(require_core(), 1);
1559
1568
 
1560
- // src/application-utils/load-file.ts
1561
- var pathPrefix = "";
1562
- function setPathPrefix(prefix) {
1563
- pathPrefix = prefix;
1569
+ // src/dynamic-texture/texture-data.ts
1570
+ var import_core8 = __toESM(require_core(), 1);
1571
+ var TEXTURE_CUBE_FACE_MAP = { "+X": 0, "-X": 1, "+Y": 2, "-Y": 3, "+Z": 4, "-Z": 5 };
1572
+ function getFirstMipLevel(layer) {
1573
+ if (!layer)
1574
+ return null;
1575
+ return Array.isArray(layer) ? layer[0] ?? null : layer;
1564
1576
  }
1565
- async function loadImageBitmap(url, opts) {
1566
- const image = new Image();
1567
- image.crossOrigin = opts?.crossOrigin || "anonymous";
1568
- image.src = url.startsWith("http") ? url : pathPrefix + url;
1569
- await image.decode();
1570
- return opts ? await createImageBitmap(image, opts) : await createImageBitmap(image);
1577
+ function getTextureSizeFromData(props) {
1578
+ const { dimension, data } = props;
1579
+ if (!data) {
1580
+ return null;
1581
+ }
1582
+ switch (dimension) {
1583
+ case "1d": {
1584
+ const mipLevel = getFirstMipLevel(data);
1585
+ if (!mipLevel)
1586
+ return null;
1587
+ const { width } = getTextureMipLevelSize(mipLevel);
1588
+ return { width, height: 1 };
1589
+ }
1590
+ case "2d": {
1591
+ const mipLevel = getFirstMipLevel(data);
1592
+ return mipLevel ? getTextureMipLevelSize(mipLevel) : null;
1593
+ }
1594
+ case "3d":
1595
+ case "2d-array": {
1596
+ if (!Array.isArray(data) || data.length === 0)
1597
+ return null;
1598
+ const mipLevel = getFirstMipLevel(data[0]);
1599
+ return mipLevel ? getTextureMipLevelSize(mipLevel) : null;
1600
+ }
1601
+ case "cube": {
1602
+ const face = Object.keys(data)[0] ?? null;
1603
+ if (!face)
1604
+ return null;
1605
+ const faceData = data[face];
1606
+ const mipLevel = getFirstMipLevel(faceData);
1607
+ return mipLevel ? getTextureMipLevelSize(mipLevel) : null;
1608
+ }
1609
+ case "cube-array": {
1610
+ if (!Array.isArray(data) || data.length === 0)
1611
+ return null;
1612
+ const firstCube = data[0];
1613
+ const face = Object.keys(firstCube)[0] ?? null;
1614
+ if (!face)
1615
+ return null;
1616
+ const mipLevel = getFirstMipLevel(firstCube[face]);
1617
+ return mipLevel ? getTextureMipLevelSize(mipLevel) : null;
1618
+ }
1619
+ default:
1620
+ return null;
1621
+ }
1571
1622
  }
1572
- async function loadImage(url, opts) {
1573
- return await new Promise((resolve, reject) => {
1574
- try {
1575
- const image = new Image();
1576
- image.onload = () => resolve(image);
1577
- image.onerror = () => reject(new Error(`Could not load image ${url}.`));
1578
- image.crossOrigin = opts?.crossOrigin || "anonymous";
1579
- image.src = url.startsWith("http") ? url : pathPrefix + url;
1580
- } catch (error) {
1581
- reject(error);
1623
+ function getTextureMipLevelSize(data) {
1624
+ if ((0, import_core8.isExternalImage)(data)) {
1625
+ return (0, import_core8.getExternalImageSize)(data);
1626
+ }
1627
+ if (typeof data === "object" && "width" in data && "height" in data) {
1628
+ return { width: data.width, height: data.height };
1629
+ }
1630
+ throw new Error("Unsupported mip-level data");
1631
+ }
1632
+ function isTextureImageData(data) {
1633
+ return typeof data === "object" && data !== null && "data" in data && "width" in data && "height" in data;
1634
+ }
1635
+ function getCubeFaceIndex(face) {
1636
+ const idx = TEXTURE_CUBE_FACE_MAP[face];
1637
+ if (idx === void 0)
1638
+ throw new Error(`Invalid cube face: ${face}`);
1639
+ return idx;
1640
+ }
1641
+ function getCubeArrayFaceIndex(cubeIndex, face) {
1642
+ return 6 * cubeIndex + getCubeFaceIndex(face);
1643
+ }
1644
+ function getTexture1DSubresources(data) {
1645
+ throw new Error("setTexture1DData not supported in WebGL.");
1646
+ }
1647
+ function _normalizeTexture2DData(data) {
1648
+ return Array.isArray(data) ? data : [data];
1649
+ }
1650
+ function getTexture2DSubresources(slice, lodData) {
1651
+ const lodArray = _normalizeTexture2DData(lodData);
1652
+ const z = slice;
1653
+ const subresources = [];
1654
+ for (let mipLevel = 0; mipLevel < lodArray.length; mipLevel++) {
1655
+ const imageData = lodArray[mipLevel];
1656
+ if ((0, import_core8.isExternalImage)(imageData)) {
1657
+ subresources.push({
1658
+ type: "external-image",
1659
+ image: imageData,
1660
+ z,
1661
+ mipLevel
1662
+ });
1663
+ } else if (isTextureImageData(imageData)) {
1664
+ subresources.push({
1665
+ type: "texture-data",
1666
+ data: imageData,
1667
+ z,
1668
+ mipLevel
1669
+ });
1670
+ } else {
1671
+ throw new Error("Unsupported 2D mip-level payload");
1672
+ }
1673
+ }
1674
+ return subresources;
1675
+ }
1676
+ function getTexture3DSubresources(data) {
1677
+ const subresources = [];
1678
+ for (let depth = 0; depth < data.length; depth++) {
1679
+ subresources.push(...getTexture2DSubresources(depth, data[depth]));
1680
+ }
1681
+ return subresources;
1682
+ }
1683
+ function getTextureArraySubresources(data) {
1684
+ const subresources = [];
1685
+ for (let layer = 0; layer < data.length; layer++) {
1686
+ subresources.push(...getTexture2DSubresources(layer, data[layer]));
1687
+ }
1688
+ return subresources;
1689
+ }
1690
+ function getTextureCubeSubresources(data) {
1691
+ const subresources = [];
1692
+ for (const [face, faceData] of Object.entries(data)) {
1693
+ const faceDepth = getCubeFaceIndex(face);
1694
+ subresources.push(...getTexture2DSubresources(faceDepth, faceData));
1695
+ }
1696
+ return subresources;
1697
+ }
1698
+ function getTextureCubeArraySubresources(data) {
1699
+ const subresources = [];
1700
+ data.forEach((cubeData, cubeIndex) => {
1701
+ for (const [face, faceData] of Object.entries(cubeData)) {
1702
+ const faceDepth = getCubeArrayFaceIndex(cubeIndex, face);
1703
+ getTexture2DSubresources(faceDepth, faceData);
1582
1704
  }
1583
1705
  });
1706
+ return subresources;
1584
1707
  }
1585
1708
 
1586
- // src/async-texture/async-texture.ts
1587
- var TextureCubeFaces = ["+X", "-X", "+Y", "-Y", "+Z", "-Z"];
1588
- var CubeFaces = ["+X", "-X", "+Y", "-Y", "+Z", "-Z"];
1589
- var _AsyncTexture = class {
1709
+ // src/dynamic-texture/dynamic-texture.ts
1710
+ var _DynamicTexture = class {
1590
1711
  device;
1591
1712
  id;
1713
+ /** Props with defaults resolved (except `data` which is processed separately) */
1592
1714
  props;
1593
- // TODO - should we type these as possibly `null`? It will make usage harder?
1594
- // @ts-expect-error
1595
- texture;
1596
- // @ts-expect-error
1597
- sampler;
1598
- // @ts-expect-error
1599
- view;
1715
+ /** Created resources */
1716
+ _texture = null;
1717
+ _sampler = null;
1718
+ _view = null;
1719
+ /** Ready when GPU texture has been created and data (if any) uploaded */
1600
1720
  ready;
1601
1721
  isReady = false;
1602
1722
  destroyed = false;
@@ -1604,271 +1724,251 @@ var __exports__ = (() => {
1604
1724
  };
1605
1725
  rejectReady = () => {
1606
1726
  };
1727
+ get texture() {
1728
+ if (!this._texture)
1729
+ throw new Error("Texture not initialized yet");
1730
+ return this._texture;
1731
+ }
1732
+ get sampler() {
1733
+ if (!this._sampler)
1734
+ throw new Error("Sampler not initialized yet");
1735
+ return this._sampler;
1736
+ }
1737
+ get view() {
1738
+ if (!this._view)
1739
+ throw new Error("View not initialized yet");
1740
+ return this._view;
1741
+ }
1607
1742
  get [Symbol.toStringTag]() {
1608
- return "AsyncTexture";
1743
+ return "DynamicTexture";
1609
1744
  }
1610
1745
  toString() {
1611
- return `AsyncTexture:"${this.id}"(${this.isReady ? "ready" : "loading"})`;
1746
+ return `DynamicTexture:"${this.id}":${this.texture.width}x${this.texture.height}px:(${this.isReady ? "ready" : "loading..."})`;
1612
1747
  }
1613
1748
  constructor(device, props) {
1614
1749
  this.device = device;
1615
- const id = uid("async-texture");
1616
- this.props = { ..._AsyncTexture.defaultProps, id, ...props };
1750
+ const id = uid("dynamic-texture");
1751
+ const originalPropsWithAsyncData = props;
1752
+ this.props = { ..._DynamicTexture.defaultProps, id, ...props, data: null };
1617
1753
  this.id = this.props.id;
1618
- props = { ...props };
1619
- if (typeof props?.data === "string" && props.dimension === "2d") {
1620
- props.data = loadImageBitmap(props.data);
1621
- }
1622
- if (props.mipmaps) {
1623
- props.mipLevels = "auto";
1624
- }
1625
1754
  this.ready = new Promise((resolve, reject) => {
1626
- this.resolveReady = () => {
1627
- this.isReady = true;
1628
- resolve();
1629
- };
1755
+ this.resolveReady = resolve;
1630
1756
  this.rejectReady = reject;
1631
1757
  });
1632
- this.initAsync(props);
1758
+ this.initAsync(originalPropsWithAsyncData);
1633
1759
  }
1634
- async initAsync(props) {
1635
- const asyncData = props.data;
1636
- const data = await awaitAllPromises(asyncData).then(
1637
- void 0,
1638
- this.rejectReady
1639
- );
1640
- if (this.destroyed) {
1641
- return;
1642
- }
1643
- const size = this.props.width && this.props.height ? { width: this.props.width, height: this.props.height } : this.getTextureDataSize(data);
1644
- if (!size) {
1645
- throw new Error("Texture size could not be determined");
1646
- }
1647
- const syncProps = { ...size, ...props, data: void 0, mipLevels: 1 };
1648
- const maxMips = this.device.getMipLevelCount(syncProps.width, syncProps.height);
1649
- syncProps.mipLevels = this.props.mipLevels === "auto" ? maxMips : Math.min(maxMips, this.props.mipLevels);
1650
- this.texture = this.device.createTexture(syncProps);
1651
- this.sampler = this.texture.sampler;
1652
- this.view = this.texture.view;
1653
- if (props.data) {
1654
- switch (this.props.dimension) {
1655
- case "1d":
1656
- this._setTexture1DData(this.texture, data);
1657
- break;
1658
- case "2d":
1659
- this._setTexture2DData(data);
1660
- break;
1661
- case "3d":
1662
- this._setTexture3DData(this.texture, data);
1663
- break;
1664
- case "2d-array":
1665
- this._setTextureArrayData(this.texture, data);
1666
- break;
1667
- case "cube":
1668
- this._setTextureCubeData(this.texture, data);
1669
- break;
1670
- case "cube-array":
1671
- this._setTextureCubeArrayData(this.texture, data);
1672
- break;
1760
+ /** @note Fire and forget; caller can await `ready` */
1761
+ async initAsync(originalPropsWithAsyncData) {
1762
+ try {
1763
+ const propsWithSyncData = await this._loadAllData(originalPropsWithAsyncData);
1764
+ this._checkNotDestroyed();
1765
+ const deduceSize = () => {
1766
+ if (this.props.width && this.props.height) {
1767
+ return { width: this.props.width, height: this.props.height };
1768
+ }
1769
+ const size2 = getTextureSizeFromData(propsWithSyncData);
1770
+ if (size2) {
1771
+ return size2;
1772
+ }
1773
+ return { width: this.props.width || 1, height: this.props.height || 1 };
1774
+ };
1775
+ const size = deduceSize();
1776
+ if (!size || size.width <= 0 || size.height <= 0) {
1777
+ throw new Error(`${this} size could not be determined or was zero`);
1673
1778
  }
1779
+ const baseTextureProps = {
1780
+ ...this.props,
1781
+ ...size,
1782
+ mipLevels: 1,
1783
+ // temporary; updated below
1784
+ data: void 0
1785
+ };
1786
+ const maxMips = this.device.getMipLevelCount(baseTextureProps.width, baseTextureProps.height);
1787
+ const desired = this.props.mipLevels === "auto" ? maxMips : Math.max(1, Math.min(maxMips, this.props.mipLevels ?? 1));
1788
+ const finalTextureProps = { ...baseTextureProps, mipLevels: desired };
1789
+ this._texture = this.device.createTexture(finalTextureProps);
1790
+ this._sampler = this.texture.sampler;
1791
+ this._view = this.texture.view;
1792
+ if (propsWithSyncData.data) {
1793
+ switch (propsWithSyncData.dimension) {
1794
+ case "1d":
1795
+ this.setTexture1DData(propsWithSyncData.data);
1796
+ break;
1797
+ case "2d":
1798
+ this.setTexture2DData(propsWithSyncData.data);
1799
+ break;
1800
+ case "3d":
1801
+ this.setTexture3DData(propsWithSyncData.data);
1802
+ break;
1803
+ case "2d-array":
1804
+ this.setTextureArrayData(propsWithSyncData.data);
1805
+ break;
1806
+ case "cube":
1807
+ this.setTextureCubeData(propsWithSyncData.data);
1808
+ break;
1809
+ case "cube-array":
1810
+ this.setTextureCubeArrayData(propsWithSyncData.data);
1811
+ break;
1812
+ default: {
1813
+ throw new Error(`Unhandled dimension ${propsWithSyncData.dimension}`);
1814
+ }
1815
+ }
1816
+ }
1817
+ if (this.props.mipmaps) {
1818
+ this.generateMipmaps();
1819
+ }
1820
+ this.isReady = true;
1821
+ this.resolveReady(this.texture);
1822
+ import_core9.log.info(0, `${this} created`)();
1823
+ } catch (e) {
1824
+ const err = e instanceof Error ? e : new Error(String(e));
1825
+ this.rejectReady(err);
1826
+ throw err;
1674
1827
  }
1675
- if (this.props.mipmaps) {
1676
- this.generateMipmaps();
1677
- }
1678
- import_core8.log.info(1, `${this} loaded`);
1679
- this.resolveReady();
1680
1828
  }
1681
1829
  destroy() {
1682
- if (this.texture) {
1683
- this.texture.destroy();
1684
- this.texture = null;
1830
+ if (this._texture) {
1831
+ this._texture.destroy();
1832
+ this._texture = null;
1833
+ this._sampler = null;
1834
+ this._view = null;
1685
1835
  }
1686
1836
  this.destroyed = true;
1687
1837
  }
1688
1838
  generateMipmaps() {
1689
- this.texture.generateMipmapsWebGL();
1839
+ if (this.device.type === "webgl") {
1840
+ this.texture.generateMipmapsWebGL();
1841
+ } else {
1842
+ import_core9.log.warn(
1843
+ "Mipmap generation not yet implemented on WebGPU: your texture data will not be correctly initialized"
1844
+ );
1845
+ }
1690
1846
  }
1691
- /** Set sampler or create and set new Sampler from SamplerProps */
1847
+ /** Set sampler or create one from props */
1692
1848
  setSampler(sampler = {}) {
1693
- this.texture.setSampler(
1694
- sampler instanceof import_core8.Sampler ? sampler : this.device.createSampler(sampler)
1695
- );
1849
+ this._checkReady();
1850
+ const s = sampler instanceof import_core9.Sampler ? sampler : this.device.createSampler(sampler);
1851
+ this.texture.setSampler(s);
1852
+ this._sampler = s;
1696
1853
  }
1697
1854
  /**
1698
- * Textures are immutable and cannot be resized after creation,
1699
- * but we can create a similar texture with the same parameters but a new size.
1700
- * @note Does not copy contents of the texture
1701
- * @note Mipmaps may need to be regenerated after resizing / setting new data
1702
- * @todo Abort pending promise and create a texture with the new size?
1855
+ * Resize by cloning the underlying immutable texture.
1856
+ * Does not copy contents; caller may need to re-upload and/or regenerate mips.
1703
1857
  */
1704
1858
  resize(size) {
1705
- if (!this.isReady) {
1706
- throw new Error("Cannot resize texture before it is ready");
1707
- }
1859
+ this._checkReady();
1708
1860
  if (size.width === this.texture.width && size.height === this.texture.height) {
1709
1861
  return false;
1710
1862
  }
1711
- if (this.texture) {
1712
- const texture = this.texture;
1713
- this.texture = texture.clone(size);
1714
- texture.destroy();
1715
- }
1863
+ const prev = this.texture;
1864
+ this._texture = prev.clone(size);
1865
+ this._sampler = this.texture.sampler;
1866
+ this._view = this.texture.view;
1867
+ prev.destroy();
1868
+ import_core9.log.info(`${this} resized`);
1716
1869
  return true;
1717
1870
  }
1718
- /** Check if texture data is a typed array */
1719
- isTextureLevelData(data) {
1720
- const typedArray = data?.data;
1721
- return ArrayBuffer.isView(typedArray);
1722
- }
1723
- /** Get the size of the texture described by the provided TextureData */
1724
- getTextureDataSize(data) {
1725
- if (!data) {
1726
- return null;
1727
- }
1728
- if (ArrayBuffer.isView(data)) {
1729
- return null;
1730
- }
1731
- if (Array.isArray(data)) {
1732
- return this.getTextureDataSize(data[0]);
1733
- }
1734
- if (this.device.isExternalImage(data)) {
1735
- return this.device.getExternalImageSize(data);
1736
- }
1737
- if (data && typeof data === "object" && data.constructor === Object) {
1738
- const textureDataArray = Object.values(data);
1739
- const untypedData = textureDataArray[0];
1740
- return { width: untypedData.width, height: untypedData.height };
1741
- }
1742
- throw new Error("texture size deduction failed");
1743
- }
1744
- /** Convert luma.gl cubemap face constants to depth index */
1745
- getCubeFaceDepth(face) {
1746
- switch (face) {
1747
- case "+X":
1748
- return 0;
1749
- case "-X":
1750
- return 1;
1751
- case "+Y":
1752
- return 2;
1753
- case "-Y":
1754
- return 3;
1755
- case "+Z":
1756
- return 4;
1757
- case "-Z":
1758
- return 5;
1759
- default:
1760
- throw new Error(face);
1761
- }
1762
- }
1763
- // EXPERIMENTAL
1764
- setTextureData(data) {
1765
- }
1766
- /** Experimental: Set multiple mip levels */
1767
- _setTexture1DData(texture, data) {
1768
- throw new Error("setTexture1DData not supported in WebGL.");
1769
- }
1770
- /** Experimental: Set multiple mip levels */
1771
- _setTexture2DData(lodData, depth = 0) {
1772
- if (!this.texture) {
1773
- throw new Error("Texture not initialized");
1774
- }
1775
- const lodArray = this._normalizeTextureData(lodData);
1776
- if (lodArray.length > 1 && this.props.mipmaps !== false) {
1777
- import_core8.log.warn(`Texture ${this.id} mipmap and multiple LODs.`)();
1778
- }
1779
- for (let mipLevel = 0; mipLevel < lodArray.length; mipLevel++) {
1780
- const imageData = lodArray[mipLevel];
1781
- if (this.device.isExternalImage(imageData)) {
1782
- this.texture.copyExternalImage({ image: imageData, depth, mipLevel, flipY: true });
1783
- } else {
1784
- this.texture.copyImageData({ data: imageData.data, mipLevel });
1871
+ /** Convert cube face label to texture slice index. Index can be used with `setTexture2DData()`. */
1872
+ getCubeFaceIndex(face) {
1873
+ const index = TEXTURE_CUBE_FACE_MAP[face];
1874
+ if (index === void 0)
1875
+ throw new Error(`Invalid cube face: ${face}`);
1876
+ return index;
1877
+ }
1878
+ /** Convert cube face label to texture slice index. Index can be used with `setTexture2DData()`. */
1879
+ getCubeArrayFaceIndex(cubeIndex, face) {
1880
+ return 6 * cubeIndex + this.getCubeFaceIndex(face);
1881
+ }
1882
+ /** @note experimental: Set multiple mip levels (1D) */
1883
+ setTexture1DData(data) {
1884
+ this._checkReady();
1885
+ if (this.texture.props.dimension !== "1d") {
1886
+ throw new Error(`${this} is not 1d`);
1887
+ }
1888
+ const subresources = getTexture1DSubresources(data);
1889
+ this._setTextureSubresources(subresources);
1890
+ }
1891
+ /** @note experimental: Set multiple mip levels (2D), optionally at `z`, slice (depth/array level) index */
1892
+ setTexture2DData(lodData, z = 0) {
1893
+ this._checkReady();
1894
+ if (this.texture.props.dimension !== "2d") {
1895
+ throw new Error(`${this} is not 2d`);
1896
+ }
1897
+ const subresources = getTexture2DSubresources(z, lodData);
1898
+ this._setTextureSubresources(subresources);
1899
+ }
1900
+ /** 3D: multiple depth slices, each may carry multiple mip levels */
1901
+ setTexture3DData(data) {
1902
+ if (this.texture.props.dimension !== "3d") {
1903
+ throw new Error(`${this} is not 3d`);
1904
+ }
1905
+ const subresources = getTexture3DSubresources(data);
1906
+ this._setTextureSubresources(subresources);
1907
+ }
1908
+ /** 2D array: multiple layers, each may carry multiple mip levels */
1909
+ setTextureArrayData(data) {
1910
+ if (this.texture.props.dimension !== "2d-array") {
1911
+ throw new Error(`${this} is not 2d-array`);
1912
+ }
1913
+ const subresources = getTextureArraySubresources(data);
1914
+ this._setTextureSubresources(subresources);
1915
+ }
1916
+ /** Cube: 6 faces, each may carry multiple mip levels */
1917
+ setTextureCubeData(data) {
1918
+ if (this.texture.props.dimension !== "cube") {
1919
+ throw new Error(`${this} is not cube`);
1920
+ }
1921
+ const subresources = getTextureCubeSubresources(data);
1922
+ this._setTextureSubresources(subresources);
1923
+ }
1924
+ /** Cube array: multiple cubes (faces×layers), each face may carry multiple mips */
1925
+ setTextureCubeArrayData(data) {
1926
+ if (this.texture.props.dimension !== "cube-array") {
1927
+ throw new Error(`${this} is not cube-array`);
1928
+ }
1929
+ const subresources = getTextureCubeArraySubresources(data);
1930
+ this._setTextureSubresources(subresources);
1931
+ }
1932
+ /** Sets multiple mip levels on different `z` slices (depth/array index) */
1933
+ _setTextureSubresources(subresources) {
1934
+ for (const subresource of subresources) {
1935
+ const { z, mipLevel } = subresource;
1936
+ switch (subresource.type) {
1937
+ case "external-image":
1938
+ const { image, flipY } = subresource;
1939
+ this.texture.copyExternalImage({ image, z, mipLevel, flipY });
1940
+ break;
1941
+ case "texture-data":
1942
+ const { data } = subresource;
1943
+ this.texture.copyImageData({ data: data.data, z, mipLevel });
1944
+ break;
1945
+ default:
1946
+ throw new Error("Unsupported 2D mip-level payload");
1785
1947
  }
1786
1948
  }
1787
1949
  }
1788
- /**
1789
- * Experimental: Sets 3D texture data: multiple depth slices, multiple mip levels
1790
- * @param data
1791
- */
1792
- _setTexture3DData(texture, data) {
1793
- if (this.texture?.props.dimension !== "3d") {
1794
- throw new Error(this.id);
1795
- }
1796
- for (let depth = 0; depth < data.length; depth++) {
1797
- this._setTexture2DData(data[depth], depth);
1798
- }
1799
- }
1800
- /**
1801
- * Experimental: Set Cube texture data, multiple faces, multiple mip levels
1802
- * @todo - could support TextureCubeArray with depth
1803
- * @param data
1804
- * @param index
1805
- */
1806
- _setTextureCubeData(texture, data) {
1807
- if (this.texture?.props.dimension !== "cube") {
1808
- throw new Error(this.id);
1809
- }
1810
- for (const [face, faceData] of Object.entries(data)) {
1811
- const faceDepth = CubeFaces.indexOf(face);
1812
- this._setTexture2DData(faceData, faceDepth);
1813
- }
1814
- }
1815
- /**
1816
- * Experimental: Sets texture array data, multiple levels, multiple depth slices
1817
- * @param data
1818
- */
1819
- _setTextureArrayData(texture, data) {
1820
- if (this.texture?.props.dimension !== "2d-array") {
1821
- throw new Error(this.id);
1822
- }
1823
- for (let depth = 0; depth < data.length; depth++) {
1824
- this._setTexture2DData(data[depth], depth);
1825
- }
1826
- }
1827
- /**
1828
- * Experimental: Sets texture cube array, multiple faces, multiple levels, multiple mip levels
1829
- * @param data
1830
- */
1831
- _setTextureCubeArrayData(texture, data) {
1832
- throw new Error("setTextureCubeArrayData not supported in WebGL2.");
1950
+ // ------------------ helpers ------------------
1951
+ /** Recursively resolve all promises in data structures */
1952
+ async _loadAllData(props) {
1953
+ const syncData = await awaitAllPromises(props.data);
1954
+ const dimension = props.dimension ?? "2d";
1955
+ return { dimension, data: syncData ?? null };
1833
1956
  }
1834
- /** Experimental */
1835
- _setTextureCubeFaceData(texture, lodData, face, depth = 0) {
1836
- if (Array.isArray(lodData) && lodData.length > 1 && this.props.mipmaps !== false) {
1837
- import_core8.log.warn(`${this.id} has mipmap and multiple LODs.`)();
1957
+ _checkNotDestroyed() {
1958
+ if (this.destroyed) {
1959
+ import_core9.log.warn(`${this} already destroyed`);
1838
1960
  }
1839
- const faceDepth = TextureCubeFaces.indexOf(face);
1840
- this._setTexture2DData(lodData, faceDepth);
1841
1961
  }
1842
- /**
1843
- * Normalize TextureData to an array of TextureImageData / ExternalImages
1844
- * @param data
1845
- * @param options
1846
- * @returns array of TextureImageData / ExternalImages
1847
- */
1848
- _normalizeTextureData(data) {
1849
- const options = this.texture;
1850
- let mipLevelArray;
1851
- if (ArrayBuffer.isView(data)) {
1852
- mipLevelArray = [
1853
- {
1854
- // ts-expect-error does data really need to be Uint8ClampedArray?
1855
- data,
1856
- width: options.width,
1857
- height: options.height
1858
- // depth: options.depth
1859
- }
1860
- ];
1861
- } else if (!Array.isArray(data)) {
1862
- mipLevelArray = [data];
1863
- } else {
1864
- mipLevelArray = data;
1962
+ _checkReady() {
1963
+ if (!this.isReady) {
1964
+ import_core9.log.warn(`${this} Cannot perform this operation before ready`);
1865
1965
  }
1866
- return mipLevelArray;
1867
1966
  }
1868
1967
  };
1869
- var AsyncTexture = _AsyncTexture;
1870
- __publicField(AsyncTexture, "defaultProps", {
1871
- ...import_core8.Texture.defaultProps,
1968
+ var DynamicTexture = _DynamicTexture;
1969
+ __publicField(DynamicTexture, "defaultProps", {
1970
+ ...import_core9.Texture.defaultProps,
1971
+ dimension: "2d",
1872
1972
  data: null,
1873
1973
  mipmaps: false
1874
1974
  });
@@ -1894,16 +1994,24 @@ var __exports__ = (() => {
1894
1994
  var LOG_DRAW_PRIORITY = 2;
1895
1995
  var LOG_DRAW_TIMEOUT = 1e4;
1896
1996
  var _Model = class {
1997
+ /** Device that created this model */
1897
1998
  device;
1999
+ /** Application provided identifier */
1898
2000
  id;
2001
+ /** WGSL shader source when using unified shader */
1899
2002
  // @ts-expect-error assigned in function called from constructor
1900
2003
  source;
2004
+ /** GLSL vertex shader source */
1901
2005
  // @ts-expect-error assigned in function called from constructor
1902
2006
  vs;
2007
+ /** GLSL fragment shader source */
1903
2008
  // @ts-expect-error assigned in function called from constructor
1904
2009
  fs;
2010
+ /** Factory used to create render pipelines */
1905
2011
  pipelineFactory;
2012
+ /** Factory used to create shaders */
1906
2013
  shaderFactory;
2014
+ /** User-supplied per-model data */
1907
2015
  userData = {};
1908
2016
  // Fixed properties (change can trigger pipeline rebuild)
1909
2017
  /** The render pipeline GPU parameters, depth testing etc */
@@ -1981,7 +2089,7 @@ var __exports__ = (() => {
1981
2089
  });
1982
2090
  this.source = source3;
1983
2091
  this._getModuleUniforms = getUniforms2;
1984
- this.props.shaderLayout ||= (0, import_shadertools2.getShaderLayoutFromWGSL)(this.source);
2092
+ this.props.shaderLayout ||= device.getShaderLayout(this.source);
1985
2093
  } else {
1986
2094
  const { vs: vs3, fs: fs3, getUniforms: getUniforms2 } = this.props.shaderAssembler.assembleGLSLShaderPair({
1987
2095
  platformInfo,
@@ -2034,7 +2142,6 @@ var __exports__ = (() => {
2034
2142
  if (props.transformFeedback) {
2035
2143
  this.transformFeedback = props.transformFeedback;
2036
2144
  }
2037
- Object.seal(this);
2038
2145
  }
2039
2146
  destroy() {
2040
2147
  if (!this._destroyed) {
@@ -2062,14 +2169,20 @@ var __exports__ = (() => {
2062
2169
  setNeedsRedraw(reason) {
2063
2170
  this._needsRedraw ||= reason;
2064
2171
  }
2172
+ /** Update uniforms and pipeline state prior to drawing. */
2065
2173
  predraw() {
2066
2174
  this.updateShaderInputs();
2067
2175
  this.pipeline = this._updatePipeline();
2068
2176
  }
2177
+ /**
2178
+ * Issue one draw call.
2179
+ * @param renderPass - render pass to draw into
2180
+ * @returns `true` if the draw call was executed, `false` if resources were not ready.
2181
+ */
2069
2182
  draw(renderPass) {
2070
2183
  const loadingBinding = this._areBindingsLoading();
2071
2184
  if (loadingBinding) {
2072
- import_core9.log.info(LOG_DRAW_PRIORITY, `>>> DRAWING ABORTED ${this.id}: ${loadingBinding} not loaded`)();
2185
+ import_core10.log.info(LOG_DRAW_PRIORITY, `>>> DRAWING ABORTED ${this.id}: ${loadingBinding} not loaded`)();
2073
2186
  return false;
2074
2187
  }
2075
2188
  try {
@@ -2199,7 +2312,7 @@ var __exports__ = (() => {
2199
2312
  /** Set the shader inputs */
2200
2313
  setShaderInputs(shaderInputs) {
2201
2314
  this.shaderInputs = shaderInputs;
2202
- this._uniformStore = new import_core9.UniformStore(this.shaderInputs.modules);
2315
+ this._uniformStore = new import_core10.UniformStore(this.shaderInputs.modules);
2203
2316
  for (const [moduleName, module] of Object.entries(this.shaderInputs.modules)) {
2204
2317
  if (shaderModuleHasUniforms(module)) {
2205
2318
  const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
@@ -2243,7 +2356,7 @@ var __exports__ = (() => {
2243
2356
  setAttributes(buffers, options) {
2244
2357
  const disableWarnings = options?.disableWarnings ?? this.props.disableWarnings;
2245
2358
  if (buffers["indices"]) {
2246
- import_core9.log.warn(
2359
+ import_core10.log.warn(
2247
2360
  `Model:${this.id} setAttributes() - indexBuffer should be set using setIndexBuffer()`
2248
2361
  )();
2249
2362
  }
@@ -2256,7 +2369,7 @@ var __exports__ = (() => {
2256
2369
  const bufferLayout = bufferLayoutHelper.getBufferLayout(bufferName);
2257
2370
  if (!bufferLayout) {
2258
2371
  if (!disableWarnings) {
2259
- import_core9.log.warn(`Model(${this.id}): Missing layout for buffer "${bufferName}".`)();
2372
+ import_core10.log.warn(`Model(${this.id}): Missing layout for buffer "${bufferName}".`)();
2260
2373
  }
2261
2374
  continue;
2262
2375
  }
@@ -2271,7 +2384,7 @@ var __exports__ = (() => {
2271
2384
  }
2272
2385
  }
2273
2386
  if (!set && !disableWarnings) {
2274
- import_core9.log.warn(
2387
+ import_core10.log.warn(
2275
2388
  `Model(${this.id}): Ignoring buffer "${buffer.id}" for unknown attribute "${bufferName}"`
2276
2389
  )();
2277
2390
  }
@@ -2292,7 +2405,7 @@ var __exports__ = (() => {
2292
2405
  if (attributeInfo) {
2293
2406
  this.vertexArray.setConstantWebGL(attributeInfo.location, value);
2294
2407
  } else if (!(options?.disableWarnings ?? this.props.disableWarnings)) {
2295
- import_core9.log.warn(
2408
+ import_core10.log.warn(
2296
2409
  `Model "${this.id}: Ignoring constant supplied for unknown attribute "${attributeName}"`
2297
2410
  )();
2298
2411
  }
@@ -2303,7 +2416,7 @@ var __exports__ = (() => {
2303
2416
  /** Check that bindings are loaded. Returns id of first binding that is still loading. */
2304
2417
  _areBindingsLoading() {
2305
2418
  for (const binding of Object.values(this.bindings)) {
2306
- if (binding instanceof AsyncTexture && !binding.isReady) {
2419
+ if (binding instanceof DynamicTexture && !binding.isReady) {
2307
2420
  return binding.id;
2308
2421
  }
2309
2422
  }
@@ -2313,7 +2426,7 @@ var __exports__ = (() => {
2313
2426
  _getBindings() {
2314
2427
  const validBindings = {};
2315
2428
  for (const [name, binding] of Object.entries(this.bindings)) {
2316
- if (binding instanceof AsyncTexture) {
2429
+ if (binding instanceof DynamicTexture) {
2317
2430
  if (binding.isReady) {
2318
2431
  validBindings[name] = binding.texture;
2319
2432
  }
@@ -2327,16 +2440,16 @@ var __exports__ = (() => {
2327
2440
  _getBindingsUpdateTimestamp() {
2328
2441
  let timestamp = 0;
2329
2442
  for (const binding of Object.values(this.bindings)) {
2330
- if (binding instanceof import_core9.TextureView) {
2443
+ if (binding instanceof import_core10.TextureView) {
2331
2444
  timestamp = Math.max(timestamp, binding.texture.updateTimestamp);
2332
- } else if (binding instanceof import_core9.Buffer || binding instanceof import_core9.Texture) {
2445
+ } else if (binding instanceof import_core10.Buffer || binding instanceof import_core10.Texture) {
2333
2446
  timestamp = Math.max(timestamp, binding.updateTimestamp);
2334
- } else if (binding instanceof AsyncTexture) {
2447
+ } else if (binding instanceof DynamicTexture) {
2335
2448
  timestamp = binding.texture ? Math.max(timestamp, binding.texture.updateTimestamp) : (
2336
2449
  // The texture will become available in the future
2337
2450
  Infinity
2338
2451
  );
2339
- } else if (!(binding instanceof import_core9.Sampler)) {
2452
+ } else if (!(binding instanceof import_core10.Sampler)) {
2340
2453
  timestamp = Math.max(timestamp, binding.buffer.updateTimestamp);
2341
2454
  }
2342
2455
  }
@@ -2371,7 +2484,7 @@ var __exports__ = (() => {
2371
2484
  let prevShaderVs = null;
2372
2485
  let prevShaderFs = null;
2373
2486
  if (this.pipeline) {
2374
- import_core9.log.log(
2487
+ import_core10.log.log(
2375
2488
  1,
2376
2489
  `Model ${this.id}: Recreating pipeline because "${this._pipelineNeedsUpdate}".`
2377
2490
  )();
@@ -2407,7 +2520,7 @@ var __exports__ = (() => {
2407
2520
  vs: vs3,
2408
2521
  fs: fs3
2409
2522
  });
2410
- this._attributeInfos = (0, import_core9.getAttributeInfosFromLayouts)(
2523
+ this._attributeInfos = (0, import_core10.getAttributeInfosFromLayouts)(
2411
2524
  this.pipeline.shaderLayout,
2412
2525
  this.bufferLayout
2413
2526
  );
@@ -2422,24 +2535,24 @@ var __exports__ = (() => {
2422
2535
  _lastLogTime = 0;
2423
2536
  _logOpen = false;
2424
2537
  _logDrawCallStart() {
2425
- const logDrawTimeout = import_core9.log.level > 3 ? 0 : LOG_DRAW_TIMEOUT;
2426
- if (import_core9.log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
2538
+ const logDrawTimeout = import_core10.log.level > 3 ? 0 : LOG_DRAW_TIMEOUT;
2539
+ if (import_core10.log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
2427
2540
  return;
2428
2541
  }
2429
2542
  this._lastLogTime = Date.now();
2430
2543
  this._logOpen = true;
2431
- import_core9.log.group(LOG_DRAW_PRIORITY, `>>> DRAWING MODEL ${this.id}`, { collapsed: import_core9.log.level <= 2 })();
2544
+ import_core10.log.group(LOG_DRAW_PRIORITY, `>>> DRAWING MODEL ${this.id}`, { collapsed: import_core10.log.level <= 2 })();
2432
2545
  }
2433
2546
  _logDrawCallEnd() {
2434
2547
  if (this._logOpen) {
2435
2548
  const shaderLayoutTable = getDebugTableForShaderLayout(this.pipeline.shaderLayout, this.id);
2436
- import_core9.log.table(LOG_DRAW_PRIORITY, shaderLayoutTable)();
2549
+ import_core10.log.table(LOG_DRAW_PRIORITY, shaderLayoutTable)();
2437
2550
  const uniformTable = this.shaderInputs.getDebugTable();
2438
- import_core9.log.table(LOG_DRAW_PRIORITY, uniformTable)();
2551
+ import_core10.log.table(LOG_DRAW_PRIORITY, uniformTable)();
2439
2552
  const attributeTable = this._getAttributeDebugTable();
2440
- import_core9.log.table(LOG_DRAW_PRIORITY, this._attributeInfos)();
2441
- import_core9.log.table(LOG_DRAW_PRIORITY, attributeTable)();
2442
- import_core9.log.groupEnd(LOG_DRAW_PRIORITY)();
2553
+ import_core10.log.table(LOG_DRAW_PRIORITY, this._attributeInfos)();
2554
+ import_core10.log.table(LOG_DRAW_PRIORITY, attributeTable)();
2555
+ import_core10.log.groupEnd(LOG_DRAW_PRIORITY)();
2443
2556
  this._logOpen = false;
2444
2557
  }
2445
2558
  }
@@ -2478,14 +2591,14 @@ var __exports__ = (() => {
2478
2591
  }
2479
2592
  // TODO - fix typing of luma data types
2480
2593
  _getBufferOrConstantValues(attribute, dataType) {
2481
- const TypedArrayConstructor = (0, import_core9.getTypedArrayConstructor)(dataType);
2482
- const typedArray = attribute instanceof import_core9.Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
2594
+ const TypedArrayConstructor = (0, import_core10.getTypedArrayConstructor)(dataType);
2595
+ const typedArray = attribute instanceof import_core10.Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
2483
2596
  return typedArray.toString();
2484
2597
  }
2485
2598
  };
2486
2599
  var Model = _Model;
2487
2600
  __publicField(Model, "defaultProps", {
2488
- ...import_core9.RenderPipeline.defaultProps,
2601
+ ...import_core10.RenderPipeline.defaultProps,
2489
2602
  source: void 0,
2490
2603
  vs: null,
2491
2604
  fs: null,
@@ -2531,7 +2644,7 @@ var __exports__ = (() => {
2531
2644
  }
2532
2645
 
2533
2646
  // src/compute/buffer-transform.ts
2534
- var import_core10 = __toESM(require_core(), 1);
2647
+ var import_core11 = __toESM(require_core(), 1);
2535
2648
  var import_shadertools3 = __toESM(require_shadertools(), 1);
2536
2649
  var _BufferTransform = class {
2537
2650
  device;
@@ -2593,7 +2706,7 @@ var __exports__ = (() => {
2593
2706
  if (!result) {
2594
2707
  throw new Error("BufferTransform#getBuffer");
2595
2708
  }
2596
- if (result instanceof import_core10.Buffer) {
2709
+ if (result instanceof import_core11.Buffer) {
2597
2710
  return result.readAsync();
2598
2711
  }
2599
2712
  const { buffer, byteOffset = 0, byteLength = buffer.byteLength } = result;
@@ -2871,22 +2984,31 @@ ${props.source}` };
2871
2984
  };
2872
2985
 
2873
2986
  // src/models/billboard-texture-model.ts
2987
+ var backgroundModule = {
2988
+ name: "background",
2989
+ uniformTypes: {
2990
+ scale: "vec2<f32>"
2991
+ }
2992
+ };
2874
2993
  var BACKGROUND_FS_WGSL = (
2875
2994
  /* wgsl */
2876
2995
  `@group(0) @binding(0) var backgroundTexture: texture_2d<f32>;
2877
2996
  @group(0) @binding(1) var backgroundTextureSampler: sampler;
2997
+ struct backgroundUniforms {
2998
+ scale: vec2<f32>,
2999
+ };
3000
+ @group(0) @binding(2) var<uniform> background: backgroundUniforms;
2878
3001
 
2879
3002
  fn billboardTexture_getTextureUV(coordinates: vec2<f32>) -> vec2<f32> {
2880
- let iTexSize: vec2<u32> = textureDimensions(backgroundTexture, 0);
2881
- let texSize: vec2<f32> = vec2<f32>(f32(iTexSize.x), f32(iTexSize.y));
2882
- var position: vec2<f32> = coordinates.xy / texSize;
2883
- return position;
2884
- }
3003
+ let scale: vec2<f32> = background.scale;
3004
+ var position: vec2<f32> = (coordinates - vec2<f32>(0.5, 0.5)) / scale + vec2<f32>(0.5, 0.5);
3005
+ return position;
3006
+ }
2885
3007
 
2886
3008
  @fragment
2887
3009
  fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4<f32> {
2888
- let position: vec2<f32> = billboardTexture_getTextureUV(inputs.coordinate);
2889
- return textureSample(backgroundTexture, backgroundTextureSampler, position);
3010
+ let position: vec2<f32> = billboardTexture_getTextureUV(inputs.coordinate);
3011
+ return textureSample(backgroundTexture, backgroundTextureSampler, position);
2890
3012
  }
2891
3013
  `
2892
3014
  );
@@ -2896,27 +3018,33 @@ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4<f32> {
2896
3018
  precision highp float;
2897
3019
 
2898
3020
  uniform sampler2D backgroundTexture;
3021
+
3022
+ uniform backgroundUniforms {
3023
+ vec2 scale;
3024
+ } background;
3025
+
3026
+ in vec2 coordinate;
2899
3027
  out vec4 fragColor;
2900
3028
 
2901
- vec2 billboardTexture_getTextureUV() {
2902
- ivec2 iTexSize = textureSize(backgroundTexture, 0);
2903
- vec2 texSize = vec2(float(iTexSize.x), float(iTexSize.y));
2904
- vec2 position = gl_FragCoord.xy / texSize;
3029
+ vec2 billboardTexture_getTextureUV(vec2 coord) {
3030
+ vec2 position = (coord - 0.5) / background.scale + 0.5;
2905
3031
  return position;
2906
3032
  }
2907
3033
 
2908
3034
  void main(void) {
2909
- vec2 position = billboardTexture_getTextureUV();
3035
+ vec2 position = billboardTexture_getTextureUV(coordinate);
2910
3036
  fragColor = texture(backgroundTexture, position);
2911
3037
  }
2912
3038
  `
2913
3039
  );
2914
3040
  var BackgroundTextureModel = class extends ClipSpace {
3041
+ backgroundTexture = null;
2915
3042
  constructor(device, props) {
2916
3043
  super(device, {
2917
3044
  id: props.id || "background-texture-model",
2918
3045
  source: BACKGROUND_FS_WGSL,
2919
3046
  fs: BACKGROUND_FS,
3047
+ modules: [backgroundModule],
2920
3048
  parameters: {
2921
3049
  depthWriteEnabled: false,
2922
3050
  ...props.blend ? {
@@ -2933,17 +3061,47 @@ void main(void) {
2933
3061
  if (!props.backgroundTexture) {
2934
3062
  throw new Error("BackgroundTextureModel requires a backgroundTexture prop");
2935
3063
  }
2936
- this.setTexture(props.backgroundTexture);
3064
+ this.setProps(props);
2937
3065
  }
2938
- setTexture(backgroundTexture) {
2939
- this.setBindings({
2940
- backgroundTexture
2941
- });
3066
+ /** Update the background texture */
3067
+ setProps(props) {
3068
+ const { backgroundTexture } = props;
3069
+ if (backgroundTexture) {
3070
+ this.setBindings({ backgroundTexture });
3071
+ if (backgroundTexture.isReady) {
3072
+ const texture = backgroundTexture instanceof DynamicTexture ? backgroundTexture.texture : backgroundTexture;
3073
+ this.backgroundTexture = texture;
3074
+ this.updateScale(texture);
3075
+ } else {
3076
+ backgroundTexture.ready.then((texture) => {
3077
+ this.backgroundTexture = texture;
3078
+ this.updateScale(texture);
3079
+ });
3080
+ }
3081
+ }
2942
3082
  }
2943
3083
  predraw() {
2944
- this.shaderInputs.setProps({});
2945
3084
  super.predraw();
2946
3085
  }
3086
+ updateScale(texture) {
3087
+ if (!texture) {
3088
+ this.shaderInputs.setProps({ background: { scale: [1, 1] } });
3089
+ return;
3090
+ }
3091
+ const [screenWidth, screenHeight] = this.device.getCanvasContext().getDrawingBufferSize();
3092
+ const textureWidth = texture.width;
3093
+ const textureHeight = texture.height;
3094
+ const screenAspect = screenWidth / screenHeight;
3095
+ const textureAspect = textureWidth / textureHeight;
3096
+ let scaleX = 1;
3097
+ let scaleY = 1;
3098
+ if (screenAspect > textureAspect) {
3099
+ scaleY = screenAspect / textureAspect;
3100
+ } else {
3101
+ scaleX = textureAspect / screenAspect;
3102
+ }
3103
+ this.shaderInputs.setProps({ background: { scale: [scaleX, scaleY] } });
3104
+ }
2947
3105
  };
2948
3106
 
2949
3107
  // ../../node_modules/@math.gl/core/dist/lib/common.js
@@ -5125,13 +5283,13 @@ void main(void) {
5125
5283
  };
5126
5284
 
5127
5285
  // src/scenegraph/group-node.ts
5128
- var import_core13 = __toESM(require_core(), 1);
5286
+ var import_core14 = __toESM(require_core(), 1);
5129
5287
  var GroupNode = class extends ScenegraphNode {
5130
5288
  children;
5131
5289
  constructor(props = {}) {
5132
5290
  props = Array.isArray(props) ? { children: props } : props;
5133
5291
  const { children = [] } = props;
5134
- import_core13.log.assert(
5292
+ import_core14.log.assert(
5135
5293
  children.every((child) => child instanceof ScenegraphNode),
5136
5294
  "every child must an instance of ScenegraphNode"
5137
5295
  );
@@ -6345,17 +6503,45 @@ void main(void) {
6345
6503
  return n - Math.floor(n);
6346
6504
  }
6347
6505
 
6506
+ // src/application-utils/load-file.ts
6507
+ var pathPrefix = "";
6508
+ function setPathPrefix(prefix) {
6509
+ pathPrefix = prefix;
6510
+ }
6511
+ async function loadImageBitmap(url, opts) {
6512
+ const image = new Image();
6513
+ image.crossOrigin = opts?.crossOrigin || "anonymous";
6514
+ image.src = url.startsWith("http") ? url : pathPrefix + url;
6515
+ await image.decode();
6516
+ return opts ? await createImageBitmap(image, opts) : await createImageBitmap(image);
6517
+ }
6518
+ async function loadImage(url, opts) {
6519
+ return await new Promise((resolve, reject) => {
6520
+ try {
6521
+ const image = new Image();
6522
+ image.onload = () => resolve(image);
6523
+ image.onerror = () => reject(new Error(`Could not load image ${url}.`));
6524
+ image.crossOrigin = opts?.crossOrigin || "anonymous";
6525
+ image.src = url.startsWith("http") ? url : pathPrefix + url;
6526
+ } catch (error) {
6527
+ reject(error);
6528
+ }
6529
+ });
6530
+ }
6531
+
6348
6532
  // src/passes/shader-pass-renderer.ts
6349
6533
  var import_shadertools5 = __toESM(require_shadertools(), 1);
6350
6534
 
6351
6535
  // src/compute/swap.ts
6352
- var import_core15 = __toESM(require_core(), 1);
6536
+ var import_core16 = __toESM(require_core(), 1);
6353
6537
  var Swap = class {
6538
+ id;
6354
6539
  /** The current resource - usually the source for renders or computations */
6355
6540
  current;
6356
6541
  /** The next resource - usually the target/destination for transforms / computations */
6357
6542
  next;
6358
6543
  constructor(props) {
6544
+ this.id = props.id || "swap";
6359
6545
  this.current = props.current;
6360
6546
  this.next = props.next;
6361
6547
  }
@@ -6374,21 +6560,24 @@ void main(void) {
6374
6560
  var SwapFramebuffers = class extends Swap {
6375
6561
  constructor(device, props) {
6376
6562
  props = { ...props };
6563
+ const { width = 1, height = 1 } = props;
6377
6564
  let colorAttachments = props.colorAttachments?.map(
6378
6565
  (colorAttachment) => typeof colorAttachment !== "string" ? colorAttachment : device.createTexture({
6566
+ id: `${props.id}-texture-0`,
6379
6567
  format: colorAttachment,
6380
- usage: import_core15.Texture.SAMPLE | import_core15.Texture.RENDER | import_core15.Texture.COPY_SRC | import_core15.Texture.COPY_DST,
6381
- width: 1,
6382
- height: 1
6568
+ usage: import_core16.Texture.SAMPLE | import_core16.Texture.RENDER | import_core16.Texture.COPY_SRC | import_core16.Texture.COPY_DST,
6569
+ width,
6570
+ height
6383
6571
  })
6384
6572
  );
6385
6573
  const current = device.createFramebuffer({ ...props, colorAttachments });
6386
6574
  colorAttachments = props.colorAttachments?.map(
6387
6575
  (colorAttachment) => typeof colorAttachment !== "string" ? colorAttachment : device.createTexture({
6576
+ id: `${props.id}-texture-1`,
6388
6577
  format: colorAttachment,
6389
- usage: import_core15.Texture.TEXTURE | import_core15.Texture.COPY_SRC | import_core15.Texture.COPY_DST | import_core15.Texture.RENDER_ATTACHMENT,
6390
- width: 1,
6391
- height: 1
6578
+ usage: import_core16.Texture.SAMPLE | import_core16.Texture.RENDER | import_core16.Texture.COPY_SRC | import_core16.Texture.COPY_DST,
6579
+ width,
6580
+ height
6392
6581
  })
6393
6582
  );
6394
6583
  const next = device.createFramebuffer({ ...props, colorAttachments });
@@ -6453,19 +6642,23 @@ void main(void) {
6453
6642
  `// Binding 0:1 is reserved for shader passes
6454
6643
  // @group(0) @binding(0) var<uniform> brightnessContrast : brightnessContrastUniforms;
6455
6644
  @group(0) @binding(1) var texture: texture_2d<f32>;
6456
- @group(0) @binding(2) var sampler: sampler;
6645
+ @group(0) @binding(2) var textureSampler: sampler;
6457
6646
 
6458
- struct FragmentInputs {
6459
- @location(0) fragUV: vec2f,
6460
- @location(1) fragPosition: vec4f,
6461
- @location(2) fragCoordinate: vec4f
6462
- };
6647
+ // This needs to be aligned with
6648
+ // struct FragmentInputs {
6649
+ // @location(0) fragUV: vec2f,
6650
+ // @location(1) fragPosition: vec4f,
6651
+ // @location(2) fragCoordinate: vec4f
6652
+ // };
6463
6653
 
6464
6654
  @fragment
6465
6655
  fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
6466
- let texSize = textureDimensions(texture, 0);
6467
- var fragColor = textureSample(texture, sampler, fragUV);
6468
- fragColor = ${func}(gl_FragColor, texSize, texCoord);
6656
+ let fragUV = inputs.uv;
6657
+ let fragCoordinate = inputs.coordinate;
6658
+ let texSize = vec2f(textureDimensions(texture, 0));
6659
+
6660
+ var fragColor = textureSample(texture, textureSampler, fragUV);
6661
+ fragColor = ${func}(fragColor, texSize, fragCoordinate);
6469
6662
  return fragColor;
6470
6663
  }
6471
6664
  `
@@ -6487,9 +6680,9 @@ struct FragmentInputs = {
6487
6680
 
6488
6681
  @fragment
6489
6682
  fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
6490
- let texSize = textureDimensions(texture, 0);
6683
+ let texSize = vec2f(textureDimensions(texture, 0));
6491
6684
  var fragColor = textureSample(texture, sampler, fragUV);
6492
- fragColor = ${func}(gl_FragColor, texSize, texCoord);
6685
+ fragColor = ${func}(fragColor, texSize, texCoord);
6493
6686
  return fragColor;
6494
6687
  }
6495
6688
  `
@@ -6588,12 +6781,10 @@ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4<f32> {
6588
6781
 
6589
6782
  uniform sampler2D sourceTexture;
6590
6783
  in vec2 uv;
6591
- in vec2 coordinate;
6592
6784
  out vec4 fragColor;
6593
6785
 
6594
6786
  void main() {
6595
- vec2 texCoord = coordinate;
6596
- fragColor = texture(sourceTexture, coordinate);
6787
+ fragColor = texture(sourceTexture, uv);
6597
6788
  }
6598
6789
  `
6599
6790
  )
@@ -6607,9 +6798,11 @@ void main() {
6607
6798
  }
6608
6799
  this.swapFramebuffers.destroy();
6609
6800
  this.clipSpace.destroy();
6801
+ this.textureModel.destroy();
6610
6802
  }
6611
- resize(width, height) {
6612
- this.swapFramebuffers.resize({ width, height });
6803
+ resize(size) {
6804
+ size ||= this.device.getCanvasContext().getDrawingBufferSize();
6805
+ this.swapFramebuffers.resize({ width: size[0], height: size[1] });
6613
6806
  }
6614
6807
  renderToScreen(options) {
6615
6808
  const outputTexture = this.renderToTexture(options);
@@ -6620,7 +6813,7 @@ void main() {
6620
6813
  const renderPass = this.device.beginRenderPass({
6621
6814
  id: "shader-pass-renderer-to-screen",
6622
6815
  framebuffer,
6623
- clearColor: [0, 0, 0, 1],
6816
+ // clearColor: [1, 1, 0, 1],
6624
6817
  clearDepth: 1
6625
6818
  });
6626
6819
  this.clipSpace.setBindings({ sourceTexture: outputTexture });
@@ -6636,14 +6829,14 @@ void main() {
6636
6829
  if (!sourceTexture.isReady) {
6637
6830
  return null;
6638
6831
  }
6639
- this.textureModel.destroy();
6640
- this.textureModel = new BackgroundTextureModel(this.device, {
6641
- backgroundTexture: sourceTexture
6642
- });
6832
+ if (this.passRenderers.length === 0) {
6833
+ return sourceTexture.texture;
6834
+ }
6835
+ this.textureModel.setProps({ backgroundTexture: sourceTexture });
6643
6836
  const clearTexturePass = this.device.beginRenderPass({
6644
6837
  id: "shader-pass-renderer-clear-texture",
6645
6838
  framebuffer: this.swapFramebuffers.current,
6646
- clearColor: [0, 0, 0, 1]
6839
+ clearColor: [1, 0, 0, 1]
6647
6840
  });
6648
6841
  this.textureModel.draw(clearTexturePass);
6649
6842
  clearTexturePass.end();
@@ -6730,7 +6923,7 @@ void main() {
6730
6923
  };
6731
6924
 
6732
6925
  // src/compute/computation.ts
6733
- var import_core16 = __toESM(require_core(), 1);
6926
+ var import_core17 = __toESM(require_core(), 1);
6734
6927
  var import_shadertools6 = __toESM(require_shadertools(), 1);
6735
6928
  var LOG_DRAW_PRIORITY2 = 2;
6736
6929
  var LOG_DRAW_TIMEOUT2 = 1e4;
@@ -6771,7 +6964,7 @@ void main() {
6771
6964
  );
6772
6965
  this.shaderInputs = props.shaderInputs || new ShaderInputs(moduleMap);
6773
6966
  this.setShaderInputs(this.shaderInputs);
6774
- this.props.shaderLayout ||= (0, import_shadertools6.getShaderLayoutFromWGSL)(this.props.source);
6967
+ this.props.shaderLayout ||= device.getShaderLayout(this.props.source);
6775
6968
  const platformInfo = getPlatformInfo2(device);
6776
6969
  const modules = (this.props.modules?.length > 0 ? this.props.modules : this.shaderInputs?.getModules()) || [];
6777
6970
  this.pipelineFactory = props.pipelineFactory || PipelineFactory.getDefaultPipelineFactory(this.device);
@@ -6829,7 +7022,7 @@ void main() {
6829
7022
  }
6830
7023
  setShaderInputs(shaderInputs) {
6831
7024
  this.shaderInputs = shaderInputs;
6832
- this._uniformStore = new import_core16.UniformStore(this.shaderInputs.modules);
7025
+ this._uniformStore = new import_core17.UniformStore(this.shaderInputs.modules);
6833
7026
  for (const moduleName of Object.keys(this.shaderInputs.modules)) {
6834
7027
  const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
6835
7028
  this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
@@ -6866,7 +7059,7 @@ void main() {
6866
7059
  if (this._pipelineNeedsUpdate) {
6867
7060
  let prevShader = null;
6868
7061
  if (this.pipeline) {
6869
- import_core16.log.log(
7062
+ import_core17.log.log(
6870
7063
  1,
6871
7064
  `Model ${this.id}: Recreating pipeline because "${this._pipelineNeedsUpdate}".`
6872
7065
  )();
@@ -6893,33 +7086,33 @@ void main() {
6893
7086
  _lastLogTime = 0;
6894
7087
  _logOpen = false;
6895
7088
  _logDrawCallStart() {
6896
- const logDrawTimeout = import_core16.log.level > 3 ? 0 : LOG_DRAW_TIMEOUT2;
6897
- if (import_core16.log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
7089
+ const logDrawTimeout = import_core17.log.level > 3 ? 0 : LOG_DRAW_TIMEOUT2;
7090
+ if (import_core17.log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
6898
7091
  return;
6899
7092
  }
6900
7093
  this._lastLogTime = Date.now();
6901
7094
  this._logOpen = true;
6902
- import_core16.log.group(LOG_DRAW_PRIORITY2, `>>> DRAWING MODEL ${this.id}`, { collapsed: import_core16.log.level <= 2 })();
7095
+ import_core17.log.group(LOG_DRAW_PRIORITY2, `>>> DRAWING MODEL ${this.id}`, { collapsed: import_core17.log.level <= 2 })();
6903
7096
  }
6904
7097
  _logDrawCallEnd() {
6905
7098
  if (this._logOpen) {
6906
7099
  const uniformTable = this.shaderInputs.getDebugTable();
6907
- import_core16.log.table(LOG_DRAW_PRIORITY2, uniformTable)();
6908
- import_core16.log.groupEnd(LOG_DRAW_PRIORITY2)();
7100
+ import_core17.log.table(LOG_DRAW_PRIORITY2, uniformTable)();
7101
+ import_core17.log.groupEnd(LOG_DRAW_PRIORITY2)();
6909
7102
  this._logOpen = false;
6910
7103
  }
6911
7104
  }
6912
7105
  _drawCount = 0;
6913
7106
  // TODO - fix typing of luma data types
6914
7107
  _getBufferOrConstantValues(attribute, dataType) {
6915
- const TypedArrayConstructor = (0, import_core16.getTypedArrayConstructor)(dataType);
6916
- const typedArray = attribute instanceof import_core16.Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
7108
+ const TypedArrayConstructor = (0, import_core17.getTypedArrayConstructor)(dataType);
7109
+ const typedArray = attribute instanceof import_core17.Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
6917
7110
  return typedArray.toString();
6918
7111
  }
6919
7112
  };
6920
7113
  var Computation = _Computation;
6921
7114
  __publicField(Computation, "defaultProps", {
6922
- ...import_core16.ComputePipeline.defaultProps,
7115
+ ...import_core17.ComputePipeline.defaultProps,
6923
7116
  id: "unnamed",
6924
7117
  handle: void 0,
6925
7118
  userData: {},
@@ -7480,6 +7673,9 @@ vec4 picking_filterColor(vec4 color) {
7480
7673
  return [pickX, pickY];
7481
7674
  }
7482
7675
  };
7676
+
7677
+ // src/index.ts
7678
+ var AsyncTexture = DynamicTexture;
7483
7679
  return __toCommonJS(bundle_exports);
7484
7680
  })();
7485
7681
  return __exports__;