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

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 (80) 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 +1251 -574
  13. package/dist/dist.min.js +216 -48
  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 +389 -0
  17. package/dist/dynamic-texture/dynamic-texture.js.map +1 -0
  18. package/dist/dynamic-texture/mipmaps.d.ts +6 -0
  19. package/dist/dynamic-texture/mipmaps.d.ts.map +1 -0
  20. package/dist/dynamic-texture/mipmaps.js +441 -0
  21. package/dist/dynamic-texture/mipmaps.js.map +1 -0
  22. package/dist/dynamic-texture/texture-data.d.ts +137 -0
  23. package/dist/dynamic-texture/texture-data.d.ts.map +1 -0
  24. package/dist/dynamic-texture/texture-data.js +183 -0
  25. package/dist/dynamic-texture/texture-data.js.map +1 -0
  26. package/dist/factories/pipeline-factory.d.ts.map +1 -1
  27. package/dist/factories/pipeline-factory.js +3 -3
  28. package/dist/factories/pipeline-factory.js.map +1 -1
  29. package/dist/factories/shader-factory.d.ts.map +1 -1
  30. package/dist/factories/shader-factory.js +3 -2
  31. package/dist/factories/shader-factory.js.map +1 -1
  32. package/dist/index.cjs +1243 -583
  33. package/dist/index.cjs.map +4 -4
  34. package/dist/index.d.ts +8 -3
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +4 -1
  37. package/dist/index.js.map +1 -1
  38. package/dist/model/model.d.ts +31 -10
  39. package/dist/model/model.d.ts.map +1 -1
  40. package/dist/model/model.js +34 -14
  41. package/dist/model/model.js.map +1 -1
  42. package/dist/models/billboard-texture-model.d.ts +8 -5
  43. package/dist/models/billboard-texture-model.d.ts.map +1 -1
  44. package/dist/models/billboard-texture-model.js +70 -18
  45. package/dist/models/billboard-texture-model.js.map +1 -1
  46. package/dist/passes/get-fragment-shader.js +15 -11
  47. package/dist/passes/get-fragment-shader.js.map +1 -1
  48. package/dist/passes/shader-pass-renderer.d.ts +5 -5
  49. package/dist/passes/shader-pass-renderer.d.ts.map +1 -1
  50. package/dist/passes/shader-pass-renderer.js +13 -12
  51. package/dist/passes/shader-pass-renderer.js.map +1 -1
  52. package/dist/types.d.ts +7 -0
  53. package/dist/types.d.ts.map +1 -0
  54. package/dist/types.js +5 -0
  55. package/dist/types.js.map +1 -0
  56. package/dist/utils/buffer-layout-order.d.ts.map +1 -1
  57. package/dist/utils/buffer-layout-order.js +12 -2
  58. package/dist/utils/buffer-layout-order.js.map +1 -1
  59. package/package.json +6 -6
  60. package/src/animation-loop/animation-loop.ts +11 -4
  61. package/src/compute/computation.ts +3 -2
  62. package/src/compute/swap.ts +13 -7
  63. package/src/dynamic-texture/dynamic-texture.ts +499 -0
  64. package/src/dynamic-texture/mipmaps.ts +517 -0
  65. package/src/dynamic-texture/texture-data.ts +301 -0
  66. package/src/factories/pipeline-factory.ts +4 -3
  67. package/src/factories/shader-factory.ts +4 -2
  68. package/src/index.ts +9 -4
  69. package/src/model/model.ts +37 -18
  70. package/src/models/billboard-texture-model.ts +81 -22
  71. package/src/passes/get-fragment-shader.ts +15 -11
  72. package/src/passes/shader-pass-renderer.ts +22 -16
  73. package/src/types.ts +11 -0
  74. package/src/utils/buffer-layout-order.ts +18 -2
  75. package/dist/async-texture/async-texture.d.ts +0 -166
  76. package/dist/async-texture/async-texture.d.ts.map +0 -1
  77. package/dist/async-texture/async-texture.js +0 -386
  78. package/dist/async-texture/async-texture.js.map +0 -1
  79. package/src/async-texture/async-texture.ts +0 -551
  80. /package/src/{async-texture/texture-setters.ts.disabled → dynamic-texture/texture-data.ts.disabled} +0 -0
package/dist/index.cjs CHANGED
@@ -35,6 +35,7 @@ __export(dist_exports, {
35
35
  ConeGeometry: () => ConeGeometry,
36
36
  CubeGeometry: () => CubeGeometry,
37
37
  CylinderGeometry: () => CylinderGeometry,
38
+ DynamicTexture: () => DynamicTexture,
38
39
  GPUGeometry: () => GPUGeometry,
39
40
  Geometry: () => Geometry,
40
41
  GroupNode: () => GroupNode,
@@ -265,7 +266,7 @@ var _AnimationLoop = class {
265
266
  gpuTime;
266
267
  frameRate;
267
268
  display;
268
- needsRedraw = "initialized";
269
+ _needsRedraw = "initialized";
269
270
  _initialized = false;
270
271
  _running = false;
271
272
  _animationFrameId = null;
@@ -307,9 +308,15 @@ var _AnimationLoop = class {
307
308
  }
308
309
  /** Flags this animation loop as needing redraw */
309
310
  setNeedsRedraw(reason) {
310
- this.needsRedraw = this.needsRedraw || reason;
311
+ this._needsRedraw = this._needsRedraw || reason;
311
312
  return this;
312
313
  }
314
+ /** Query redraw status. Clears the flag. */
315
+ needsRedraw() {
316
+ const reason = this._needsRedraw;
317
+ this._needsRedraw = false;
318
+ return reason;
319
+ }
313
320
  setProps(props) {
314
321
  if ("autoResizeViewport" in props) {
315
322
  this.props.autoResizeViewport = props.autoResizeViewport || false;
@@ -453,7 +460,7 @@ var _AnimationLoop = class {
453
460
  (_a = this.device) == null ? void 0 : _a.submit();
454
461
  }
455
462
  _clearNeedsRedraw() {
456
- this.needsRedraw = false;
463
+ this._needsRedraw = false;
457
464
  }
458
465
  _setupFrame() {
459
466
  this._resizeViewport();
@@ -512,7 +519,7 @@ var _AnimationLoop = class {
512
519
  this.animationProps.width = width;
513
520
  this.animationProps.height = height;
514
521
  this.animationProps.aspect = aspect;
515
- this.animationProps.needsRedraw = this.needsRedraw;
522
+ this.animationProps.needsRedraw = this._needsRedraw;
516
523
  this.animationProps.engineTime = Date.now() - this.animationProps.startTime;
517
524
  if (this.timeline) {
518
525
  this.timeline.update(this.animationProps.engineTime);
@@ -670,8 +677,8 @@ function clearError(device) {
670
677
  }
671
678
 
672
679
  // dist/model/model.js
673
- var import_core9 = require("@luma.gl/core");
674
- var import_shadertools2 = require("@luma.gl/shadertools");
680
+ var import_core12 = require("@luma.gl/core");
681
+ var import_shadertools3 = require("@luma.gl/shadertools");
675
682
 
676
683
  // dist/geometry/gpu-geometry.js
677
684
  var import_core3 = require("@luma.gl/core");
@@ -786,8 +793,9 @@ var import_core4 = require("@luma.gl/core");
786
793
  var _PipelineFactory = class {
787
794
  /** Get the singleton default pipeline factory for the specified device */
788
795
  static getDefaultPipelineFactory(device) {
789
- device._lumaData["defaultPipelineFactory"] = device._lumaData["defaultPipelineFactory"] || new _PipelineFactory(device);
790
- return device._lumaData["defaultPipelineFactory"];
796
+ const moduleData = device.getModuleData("@luma.gl/engine");
797
+ moduleData.defaultPipelineFactory ||= new _PipelineFactory(device);
798
+ return moduleData.defaultPipelineFactory;
791
799
  }
792
800
  device;
793
801
  cachingEnabled;
@@ -952,8 +960,9 @@ var import_core5 = require("@luma.gl/core");
952
960
  var _ShaderFactory = class {
953
961
  /** Returns the default ShaderFactory for the given {@link Device}, creating one if necessary. */
954
962
  static getDefaultShaderFactory(device) {
955
- device._lumaData["defaultShaderFactory"] ||= new _ShaderFactory(device);
956
- return device._lumaData["defaultShaderFactory"];
963
+ const moduleData = device.getModuleData("@luma.gl/engine");
964
+ moduleData.defaultShaderFactory ||= new _ShaderFactory(device);
965
+ return moduleData.defaultShaderFactory;
957
966
  }
958
967
  device;
959
968
  cachingEnabled;
@@ -1166,14 +1175,24 @@ var BufferLayoutHelper = class {
1166
1175
  };
1167
1176
 
1168
1177
  // dist/utils/buffer-layout-order.js
1178
+ function getMinLocation(attributeNames, shaderLayoutMap) {
1179
+ let minLocation = Infinity;
1180
+ for (const name of attributeNames) {
1181
+ const location = shaderLayoutMap[name];
1182
+ if (location !== void 0) {
1183
+ minLocation = Math.min(minLocation, location);
1184
+ }
1185
+ }
1186
+ return minLocation;
1187
+ }
1169
1188
  function sortedBufferLayoutByShaderSourceLocations(shaderLayout, bufferLayout) {
1170
1189
  const shaderLayoutMap = Object.fromEntries(shaderLayout.attributes.map((attr) => [attr.name, attr.location]));
1171
1190
  const sortedLayout = bufferLayout.slice();
1172
1191
  sortedLayout.sort((a, b) => {
1173
1192
  const attributeNamesA = a.attributes ? a.attributes.map((attr) => attr.attribute) : [a.name];
1174
1193
  const attributeNamesB = b.attributes ? b.attributes.map((attr) => attr.attribute) : [b.name];
1175
- const minLocationA = Math.min(...attributeNamesA.map((name) => shaderLayoutMap[name]));
1176
- const minLocationB = Math.min(...attributeNamesB.map((name) => shaderLayoutMap[name]));
1194
+ const minLocationA = getMinLocation(attributeNamesA, shaderLayoutMap);
1195
+ const minLocationB = getMinLocation(attributeNamesB, shaderLayoutMap);
1177
1196
  return minLocationA - minLocationB;
1178
1197
  });
1179
1198
  return sortedLayout;
@@ -1306,49 +1325,795 @@ var ShaderInputs = class {
1306
1325
  }
1307
1326
  };
1308
1327
 
1309
- // dist/async-texture/async-texture.js
1310
- var import_core8 = require("@luma.gl/core");
1328
+ // dist/dynamic-texture/dynamic-texture.js
1329
+ var import_core11 = require("@luma.gl/core");
1311
1330
 
1312
- // dist/application-utils/load-file.js
1313
- var pathPrefix = "";
1314
- function setPathPrefix(prefix) {
1315
- pathPrefix = prefix;
1331
+ // dist/dynamic-texture/texture-data.js
1332
+ var import_core8 = require("@luma.gl/core");
1333
+ var TEXTURE_CUBE_FACE_MAP = { "+X": 0, "-X": 1, "+Y": 2, "-Y": 3, "+Z": 4, "-Z": 5 };
1334
+ function getFirstMipLevel(layer) {
1335
+ if (!layer)
1336
+ return null;
1337
+ return Array.isArray(layer) ? layer[0] ?? null : layer;
1316
1338
  }
1317
- async function loadImageBitmap(url, opts) {
1318
- const image = new Image();
1319
- image.crossOrigin = (opts == null ? void 0 : opts.crossOrigin) || "anonymous";
1320
- image.src = url.startsWith("http") ? url : pathPrefix + url;
1321
- await image.decode();
1322
- return opts ? await createImageBitmap(image, opts) : await createImageBitmap(image);
1339
+ function getTextureSizeFromData(props) {
1340
+ const { dimension, data } = props;
1341
+ if (!data) {
1342
+ return null;
1343
+ }
1344
+ switch (dimension) {
1345
+ case "1d": {
1346
+ const mipLevel = getFirstMipLevel(data);
1347
+ if (!mipLevel)
1348
+ return null;
1349
+ const { width } = getTextureMipLevelSize(mipLevel);
1350
+ return { width, height: 1 };
1351
+ }
1352
+ case "2d": {
1353
+ const mipLevel = getFirstMipLevel(data);
1354
+ return mipLevel ? getTextureMipLevelSize(mipLevel) : null;
1355
+ }
1356
+ case "3d":
1357
+ case "2d-array": {
1358
+ if (!Array.isArray(data) || data.length === 0)
1359
+ return null;
1360
+ const mipLevel = getFirstMipLevel(data[0]);
1361
+ return mipLevel ? getTextureMipLevelSize(mipLevel) : null;
1362
+ }
1363
+ case "cube": {
1364
+ const face = Object.keys(data)[0] ?? null;
1365
+ if (!face)
1366
+ return null;
1367
+ const faceData = data[face];
1368
+ const mipLevel = getFirstMipLevel(faceData);
1369
+ return mipLevel ? getTextureMipLevelSize(mipLevel) : null;
1370
+ }
1371
+ case "cube-array": {
1372
+ if (!Array.isArray(data) || data.length === 0)
1373
+ return null;
1374
+ const firstCube = data[0];
1375
+ const face = Object.keys(firstCube)[0] ?? null;
1376
+ if (!face)
1377
+ return null;
1378
+ const mipLevel = getFirstMipLevel(firstCube[face]);
1379
+ return mipLevel ? getTextureMipLevelSize(mipLevel) : null;
1380
+ }
1381
+ default:
1382
+ return null;
1383
+ }
1323
1384
  }
1324
- async function loadImage(url, opts) {
1325
- return await new Promise((resolve, reject) => {
1385
+ function getTextureMipLevelSize(data) {
1386
+ if ((0, import_core8.isExternalImage)(data)) {
1387
+ return (0, import_core8.getExternalImageSize)(data);
1388
+ }
1389
+ if (typeof data === "object" && "width" in data && "height" in data) {
1390
+ return { width: data.width, height: data.height };
1391
+ }
1392
+ throw new Error("Unsupported mip-level data");
1393
+ }
1394
+ function isTextureImageData(data) {
1395
+ return typeof data === "object" && data !== null && "data" in data && "width" in data && "height" in data;
1396
+ }
1397
+ function getCubeFaceIndex(face) {
1398
+ const idx = TEXTURE_CUBE_FACE_MAP[face];
1399
+ if (idx === void 0)
1400
+ throw new Error(`Invalid cube face: ${face}`);
1401
+ return idx;
1402
+ }
1403
+ function getCubeArrayFaceIndex(cubeIndex, face) {
1404
+ return 6 * cubeIndex + getCubeFaceIndex(face);
1405
+ }
1406
+ function getTexture1DSubresources(data) {
1407
+ throw new Error("setTexture1DData not supported in WebGL.");
1408
+ }
1409
+ function _normalizeTexture2DData(data) {
1410
+ return Array.isArray(data) ? data : [data];
1411
+ }
1412
+ function getTexture2DSubresources(slice, lodData) {
1413
+ const lodArray = _normalizeTexture2DData(lodData);
1414
+ const z = slice;
1415
+ const subresources = [];
1416
+ for (let mipLevel = 0; mipLevel < lodArray.length; mipLevel++) {
1417
+ const imageData = lodArray[mipLevel];
1418
+ if ((0, import_core8.isExternalImage)(imageData)) {
1419
+ subresources.push({
1420
+ type: "external-image",
1421
+ image: imageData,
1422
+ z,
1423
+ mipLevel
1424
+ });
1425
+ } else if (isTextureImageData(imageData)) {
1426
+ subresources.push({
1427
+ type: "texture-data",
1428
+ data: imageData,
1429
+ z,
1430
+ mipLevel
1431
+ });
1432
+ } else {
1433
+ throw new Error("Unsupported 2D mip-level payload");
1434
+ }
1435
+ }
1436
+ return subresources;
1437
+ }
1438
+ function getTexture3DSubresources(data) {
1439
+ const subresources = [];
1440
+ for (let depth = 0; depth < data.length; depth++) {
1441
+ subresources.push(...getTexture2DSubresources(depth, data[depth]));
1442
+ }
1443
+ return subresources;
1444
+ }
1445
+ function getTextureArraySubresources(data) {
1446
+ const subresources = [];
1447
+ for (let layer = 0; layer < data.length; layer++) {
1448
+ subresources.push(...getTexture2DSubresources(layer, data[layer]));
1449
+ }
1450
+ return subresources;
1451
+ }
1452
+ function getTextureCubeSubresources(data) {
1453
+ const subresources = [];
1454
+ for (const [face, faceData] of Object.entries(data)) {
1455
+ const faceDepth = getCubeFaceIndex(face);
1456
+ subresources.push(...getTexture2DSubresources(faceDepth, faceData));
1457
+ }
1458
+ return subresources;
1459
+ }
1460
+ function getTextureCubeArraySubresources(data) {
1461
+ const subresources = [];
1462
+ data.forEach((cubeData, cubeIndex) => {
1463
+ for (const [face, faceData] of Object.entries(cubeData)) {
1464
+ const faceDepth = getCubeArrayFaceIndex(cubeIndex, face);
1465
+ subresources.push(...getTexture2DSubresources(faceDepth, faceData));
1466
+ }
1467
+ });
1468
+ return subresources;
1469
+ }
1470
+
1471
+ // dist/dynamic-texture/mipmaps.js
1472
+ var import_core10 = require("@luma.gl/core");
1473
+
1474
+ // dist/compute/computation.js
1475
+ var import_core9 = require("@luma.gl/core");
1476
+ var import_shadertools2 = require("@luma.gl/shadertools");
1477
+ var import_types2 = require("@math.gl/types");
1478
+ var LOG_DRAW_PRIORITY = 2;
1479
+ var LOG_DRAW_TIMEOUT = 1e4;
1480
+ var _Computation = class {
1481
+ device;
1482
+ id;
1483
+ pipelineFactory;
1484
+ shaderFactory;
1485
+ userData = {};
1486
+ /** Bindings (textures, samplers, uniform buffers) */
1487
+ bindings = {};
1488
+ /** The underlying GPU pipeline. */
1489
+ pipeline;
1490
+ /** Assembled compute shader source */
1491
+ source;
1492
+ /** the underlying compiled compute shader */
1493
+ // @ts-ignore Set in function called from constructor
1494
+ shader;
1495
+ /** ShaderInputs instance */
1496
+ shaderInputs;
1497
+ // @ts-ignore Set in function called from constructor
1498
+ _uniformStore;
1499
+ _pipelineNeedsUpdate = "newly created";
1500
+ _getModuleUniforms;
1501
+ props;
1502
+ _destroyed = false;
1503
+ constructor(device, props) {
1504
+ var _a, _b, _c;
1505
+ if (device.type !== "webgpu") {
1506
+ throw new Error("Computation is only supported in WebGPU");
1507
+ }
1508
+ this.props = { ..._Computation.defaultProps, ...props };
1509
+ props = this.props;
1510
+ this.id = props.id || uid("model");
1511
+ this.device = device;
1512
+ Object.assign(this.userData, props.userData);
1513
+ const moduleMap = Object.fromEntries(((_a = this.props.modules) == null ? void 0 : _a.map((module2) => [module2.name, module2])) || []);
1514
+ this.shaderInputs = props.shaderInputs || new ShaderInputs(moduleMap);
1515
+ this.setShaderInputs(this.shaderInputs);
1516
+ this.props.shaderLayout ||= device.getShaderLayout(this.props.source);
1517
+ const platformInfo = getPlatformInfo(device);
1518
+ const modules = (((_b = this.props.modules) == null ? void 0 : _b.length) > 0 ? this.props.modules : (_c = this.shaderInputs) == null ? void 0 : _c.getModules()) || [];
1519
+ this.pipelineFactory = props.pipelineFactory || PipelineFactory.getDefaultPipelineFactory(this.device);
1520
+ this.shaderFactory = props.shaderFactory || ShaderFactory.getDefaultShaderFactory(this.device);
1521
+ const { source: source3, getUniforms: getUniforms2 } = this.props.shaderAssembler.assembleWGSLShader({
1522
+ platformInfo,
1523
+ ...this.props,
1524
+ modules
1525
+ });
1526
+ this.source = source3;
1527
+ this._getModuleUniforms = getUniforms2;
1528
+ this.pipeline = this._updatePipeline();
1529
+ if (props.bindings) {
1530
+ this.setBindings(props.bindings);
1531
+ }
1532
+ Object.seal(this);
1533
+ }
1534
+ destroy() {
1535
+ if (this._destroyed)
1536
+ return;
1537
+ this.pipelineFactory.release(this.pipeline);
1538
+ this.shaderFactory.release(this.shader);
1539
+ this._uniformStore.destroy();
1540
+ this._destroyed = true;
1541
+ }
1542
+ // Draw call
1543
+ predraw() {
1544
+ this.updateShaderInputs();
1545
+ }
1546
+ dispatch(computePass, x, y, z) {
1326
1547
  try {
1327
- const image = new Image();
1328
- image.onload = () => resolve(image);
1329
- image.onerror = () => reject(new Error(`Could not load image ${url}.`));
1330
- image.crossOrigin = (opts == null ? void 0 : opts.crossOrigin) || "anonymous";
1331
- image.src = url.startsWith("http") ? url : pathPrefix + url;
1332
- } catch (error) {
1333
- reject(error);
1548
+ this._logDrawCallStart();
1549
+ this.pipeline = this._updatePipeline();
1550
+ this.pipeline.setBindings(this.bindings);
1551
+ computePass.setPipeline(this.pipeline);
1552
+ computePass.setBindings([]);
1553
+ computePass.dispatch(x, y, z);
1554
+ } finally {
1555
+ this._logDrawCallEnd();
1556
+ }
1557
+ }
1558
+ // Update fixed fields (can trigger pipeline rebuild)
1559
+ // Update dynamic fields
1560
+ /**
1561
+ * Updates the vertex count (used in draw calls)
1562
+ * @note Any attributes with stepMode=vertex need to be at least this big
1563
+ */
1564
+ setVertexCount(vertexCount) {
1565
+ }
1566
+ /**
1567
+ * Updates the instance count (used in draw calls)
1568
+ * @note Any attributes with stepMode=instance need to be at least this big
1569
+ */
1570
+ setInstanceCount(instanceCount) {
1571
+ }
1572
+ setShaderInputs(shaderInputs) {
1573
+ this.shaderInputs = shaderInputs;
1574
+ this._uniformStore = new import_core9.UniformStore(this.shaderInputs.modules);
1575
+ for (const moduleName of Object.keys(this.shaderInputs.modules)) {
1576
+ const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
1577
+ this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
1578
+ }
1579
+ }
1580
+ /**
1581
+ * Updates shader module settings (which results in uniforms being set)
1582
+ */
1583
+ setShaderModuleProps(props) {
1584
+ const uniforms = this._getModuleUniforms(props);
1585
+ const keys = Object.keys(uniforms).filter((k) => {
1586
+ const uniform = uniforms[k];
1587
+ return !(0, import_types2.isNumericArray)(uniform) && typeof uniform !== "number" && typeof uniform !== "boolean";
1588
+ });
1589
+ const bindings = {};
1590
+ for (const k of keys) {
1591
+ bindings[k] = uniforms[k];
1592
+ delete uniforms[k];
1593
+ }
1594
+ }
1595
+ updateShaderInputs() {
1596
+ this._uniformStore.setUniforms(this.shaderInputs.getUniformValues());
1597
+ }
1598
+ /**
1599
+ * Sets bindings (textures, samplers, uniform buffers)
1600
+ */
1601
+ setBindings(bindings) {
1602
+ Object.assign(this.bindings, bindings);
1603
+ }
1604
+ _setPipelineNeedsUpdate(reason) {
1605
+ this._pipelineNeedsUpdate = this._pipelineNeedsUpdate || reason;
1606
+ }
1607
+ _updatePipeline() {
1608
+ if (this._pipelineNeedsUpdate) {
1609
+ let prevShader = null;
1610
+ if (this.pipeline) {
1611
+ import_core9.log.log(1, `Model ${this.id}: Recreating pipeline because "${this._pipelineNeedsUpdate}".`)();
1612
+ prevShader = this.shader;
1613
+ }
1614
+ this._pipelineNeedsUpdate = false;
1615
+ this.shader = this.shaderFactory.createShader({
1616
+ id: `${this.id}-fragment`,
1617
+ stage: "compute",
1618
+ source: this.source,
1619
+ debugShaders: this.props.debugShaders
1620
+ });
1621
+ this.pipeline = this.pipelineFactory.createComputePipeline({
1622
+ ...this.props,
1623
+ shader: this.shader
1624
+ });
1625
+ if (prevShader) {
1626
+ this.shaderFactory.release(prevShader);
1627
+ }
1334
1628
  }
1629
+ return this.pipeline;
1630
+ }
1631
+ /** Throttle draw call logging */
1632
+ _lastLogTime = 0;
1633
+ _logOpen = false;
1634
+ _logDrawCallStart() {
1635
+ const logDrawTimeout = import_core9.log.level > 3 ? 0 : LOG_DRAW_TIMEOUT;
1636
+ if (import_core9.log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
1637
+ return;
1638
+ }
1639
+ this._lastLogTime = Date.now();
1640
+ this._logOpen = true;
1641
+ import_core9.log.group(LOG_DRAW_PRIORITY, `>>> DRAWING MODEL ${this.id}`, { collapsed: import_core9.log.level <= 2 })();
1642
+ }
1643
+ _logDrawCallEnd() {
1644
+ if (this._logOpen) {
1645
+ const uniformTable = this.shaderInputs.getDebugTable();
1646
+ import_core9.log.table(LOG_DRAW_PRIORITY, uniformTable)();
1647
+ import_core9.log.groupEnd(LOG_DRAW_PRIORITY)();
1648
+ this._logOpen = false;
1649
+ }
1650
+ }
1651
+ _drawCount = 0;
1652
+ // TODO - fix typing of luma data types
1653
+ _getBufferOrConstantValues(attribute, dataType) {
1654
+ const TypedArrayConstructor = (0, import_core9.getTypedArrayConstructor)(dataType);
1655
+ const typedArray = attribute instanceof import_core9.Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
1656
+ return typedArray.toString();
1657
+ }
1658
+ };
1659
+ var Computation = _Computation;
1660
+ __publicField(Computation, "defaultProps", {
1661
+ ...import_core9.ComputePipeline.defaultProps,
1662
+ id: "unnamed",
1663
+ handle: void 0,
1664
+ userData: {},
1665
+ source: "",
1666
+ modules: [],
1667
+ defines: {},
1668
+ bindings: void 0,
1669
+ shaderInputs: void 0,
1670
+ pipelineFactory: void 0,
1671
+ shaderFactory: void 0,
1672
+ shaderAssembler: import_shadertools2.ShaderAssembler.getDefaultShaderAssembler(),
1673
+ debugShaders: void 0
1674
+ });
1675
+ function getPlatformInfo(device) {
1676
+ return {
1677
+ type: device.type,
1678
+ shaderLanguage: device.info.shadingLanguage,
1679
+ shaderLanguageVersion: device.info.shadingLanguageVersion,
1680
+ gpu: device.info.gpu,
1681
+ // HACK - we pretend that the DeviceFeatures is a Set, it has a similar API
1682
+ features: device.features
1683
+ };
1684
+ }
1685
+
1686
+ // dist/dynamic-texture/mipmaps.js
1687
+ var RENDER_DIMENSIONS = [
1688
+ "2d",
1689
+ "2d-array",
1690
+ "cube",
1691
+ "cube-array"
1692
+ ];
1693
+ var WORKGROUP_SIZE = {
1694
+ x: 4,
1695
+ y: 4,
1696
+ z: 4
1697
+ };
1698
+ function generateMipmap(device, texture) {
1699
+ if (texture.mipLevels <= 1) {
1700
+ return;
1701
+ }
1702
+ if (device.type !== "webgpu") {
1703
+ throw new Error(`Cannot generate mipmaps on device type "${device.type}". Use generateMipmapsWebGL for WebGL devices.`);
1704
+ }
1705
+ if (texture.dimension === "3d") {
1706
+ generateMipmaps3D(device, texture);
1707
+ return;
1708
+ }
1709
+ if (RENDER_DIMENSIONS.includes(texture.dimension)) {
1710
+ generateMipmapsRender(device, texture);
1711
+ return;
1712
+ }
1713
+ throw new Error(`Cannot generate mipmaps for texture dimension "${texture.dimension}" with WebGPU.`);
1714
+ }
1715
+ function generateMipmapsRender(device, texture) {
1716
+ validateFormatCapabilities(device, texture, ["render", "filter"], "render");
1717
+ const colorAttachmentFormat = getColorAttachmentFormat(texture.format, "render", texture.dimension);
1718
+ const viewDimension = texture.dimension;
1719
+ const shader = getRenderMipmapWGSL(viewDimension);
1720
+ const sampler = device.createSampler({ minFilter: "linear", magFilter: "linear" });
1721
+ const uniformValues = new Uint32Array(1);
1722
+ const uniformsBuffer = device.createBuffer({
1723
+ byteLength: 16,
1724
+ usage: import_core10.Buffer.UNIFORM | import_core10.Buffer.COPY_DST
1725
+ });
1726
+ const model = new Model(device, {
1727
+ source: shader,
1728
+ colorAttachmentFormats: [colorAttachmentFormat],
1729
+ topology: "triangle-list",
1730
+ vertexCount: 3,
1731
+ shaderLayout: {
1732
+ attributes: [],
1733
+ bindings: [
1734
+ { type: "sampler", name: "sourceSampler", group: 0, location: 0 },
1735
+ {
1736
+ type: "texture",
1737
+ name: "sourceTexture",
1738
+ group: 0,
1739
+ location: 1,
1740
+ viewDimension,
1741
+ sampleType: "float"
1742
+ },
1743
+ { type: "uniform", name: "uniforms", group: 0, location: 2 }
1744
+ ]
1745
+ },
1746
+ bindings: {
1747
+ sourceSampler: sampler,
1748
+ sourceTexture: texture,
1749
+ uniforms: uniformsBuffer
1750
+ }
1751
+ });
1752
+ let sourceWidth = texture.width;
1753
+ let sourceHeight = texture.height;
1754
+ const layerCount = texture.dimension === "2d" ? 1 : texture.depth;
1755
+ try {
1756
+ for (let baseMipLevel = 1; baseMipLevel < texture.mipLevels; ++baseMipLevel) {
1757
+ validateFormatCapabilities(device, texture, ["render", "filter"], "render");
1758
+ const sourceMipLevel = baseMipLevel - 1;
1759
+ const destinationWidth = Math.max(1, sourceWidth >> 1);
1760
+ const destinationHeight = Math.max(1, sourceHeight >> 1);
1761
+ const sourceView = texture.createView({
1762
+ dimension: viewDimension,
1763
+ baseMipLevel: sourceMipLevel,
1764
+ mipLevelCount: 1,
1765
+ baseArrayLayer: 0,
1766
+ arrayLayerCount: texture.depth
1767
+ });
1768
+ model.setBindings({ sourceTexture: sourceView });
1769
+ for (let baseArrayLayer = 0; baseArrayLayer < layerCount; ++baseArrayLayer) {
1770
+ uniformValues[0] = baseArrayLayer;
1771
+ uniformsBuffer.write(uniformValues);
1772
+ const destinationView = texture.createView({
1773
+ dimension: "2d",
1774
+ baseMipLevel,
1775
+ mipLevelCount: 1,
1776
+ baseArrayLayer,
1777
+ arrayLayerCount: 1
1778
+ });
1779
+ const framebuffer = device.createFramebuffer({
1780
+ colorAttachments: [destinationView]
1781
+ });
1782
+ const renderPass = device.beginRenderPass({
1783
+ id: `mipmap-generation:${texture.format}:${baseMipLevel}:${baseArrayLayer}`,
1784
+ framebuffer
1785
+ });
1786
+ renderPass.setParameters({
1787
+ viewport: [0, 0, destinationWidth, destinationHeight, 0, 1],
1788
+ scissorRect: [0, 0, destinationWidth, destinationHeight]
1789
+ });
1790
+ model.draw(renderPass);
1791
+ renderPass.end();
1792
+ device.submit();
1793
+ destinationView.destroy();
1794
+ framebuffer.destroy();
1795
+ }
1796
+ sourceView.destroy();
1797
+ sourceWidth = destinationWidth;
1798
+ sourceHeight = destinationHeight;
1799
+ }
1800
+ } finally {
1801
+ model.destroy();
1802
+ sampler.destroy();
1803
+ uniformsBuffer.destroy();
1804
+ }
1805
+ }
1806
+ function getColorAttachmentFormat(format, path, dimension) {
1807
+ if (import_core10.textureFormatDecoder.isColor(format)) {
1808
+ return format;
1809
+ }
1810
+ throw new Error(`Cannot run ${path} mipmap generation for ${dimension} texture with format "${format}". Only color textures can be used for this operation. Required capabilities: color. Actual capabilities: color=false.`);
1811
+ }
1812
+ function generateMipmaps3D(device, texture) {
1813
+ validateFormatCapabilities(device, texture, ["filter", "store"], "compute");
1814
+ const format = getColorAttachmentFormat(texture.format, "compute", texture.dimension);
1815
+ const shaderSource = get3DComputeMipmapWGSL(format);
1816
+ const uniformsBuffer = device.createBuffer({
1817
+ byteLength: 32,
1818
+ usage: import_core10.Buffer.UNIFORM | import_core10.Buffer.COPY_DST
1335
1819
  });
1820
+ const uniformValues = new Uint32Array(8);
1821
+ let sourceWidth = texture.width;
1822
+ let sourceHeight = texture.height;
1823
+ let sourceDepth = texture.depth;
1824
+ try {
1825
+ for (let destinationMipLevel = 1; destinationMipLevel < texture.mipLevels; ++destinationMipLevel) {
1826
+ validateFormatCapabilities(device, texture, ["filter", "store"], "compute");
1827
+ const destinationWidth = Math.max(1, sourceWidth >> 1);
1828
+ const destinationHeight = Math.max(1, sourceHeight >> 1);
1829
+ const destinationDepth = Math.max(1, sourceDepth >> 1);
1830
+ uniformValues[0] = sourceWidth;
1831
+ uniformValues[1] = sourceHeight;
1832
+ uniformValues[2] = sourceDepth;
1833
+ uniformValues[3] = destinationWidth;
1834
+ uniformValues[4] = destinationHeight;
1835
+ uniformValues[5] = destinationDepth;
1836
+ uniformValues[6] = 0;
1837
+ uniformsBuffer.write(uniformValues);
1838
+ const sourceView = texture.createView({
1839
+ dimension: "3d",
1840
+ baseMipLevel: destinationMipLevel - 1,
1841
+ mipLevelCount: 1,
1842
+ baseArrayLayer: 0,
1843
+ arrayLayerCount: 1
1844
+ });
1845
+ const destinationView = texture.createView({
1846
+ dimension: "3d",
1847
+ baseMipLevel: destinationMipLevel,
1848
+ mipLevelCount: 1,
1849
+ baseArrayLayer: 0,
1850
+ arrayLayerCount: 1
1851
+ });
1852
+ const computation = new Computation(device, {
1853
+ source: shaderSource,
1854
+ shaderLayout: {
1855
+ bindings: [
1856
+ {
1857
+ type: "texture",
1858
+ name: "sourceTexture",
1859
+ group: 0,
1860
+ location: 0,
1861
+ viewDimension: "3d",
1862
+ sampleType: "float"
1863
+ },
1864
+ {
1865
+ type: "storage",
1866
+ name: "destinationTexture",
1867
+ group: 0,
1868
+ location: 1,
1869
+ format,
1870
+ viewDimension: "3d",
1871
+ access: "write-only"
1872
+ },
1873
+ { type: "uniform", name: "uniforms", group: 0, location: 2 }
1874
+ ]
1875
+ },
1876
+ bindings: {
1877
+ sourceTexture: sourceView,
1878
+ destinationTexture: destinationView,
1879
+ uniforms: uniformsBuffer
1880
+ }
1881
+ });
1882
+ const workgroupsX = Math.ceil(destinationWidth / WORKGROUP_SIZE.x);
1883
+ const workgroupsY = Math.ceil(destinationHeight / WORKGROUP_SIZE.y);
1884
+ const workgroupsZ = Math.ceil(destinationDepth / WORKGROUP_SIZE.z);
1885
+ const computePass = device.beginComputePass({});
1886
+ computation.dispatch(computePass, workgroupsX, workgroupsY, workgroupsZ);
1887
+ computePass.end();
1888
+ device.submit();
1889
+ computation.destroy();
1890
+ sourceView.destroy();
1891
+ destinationView.destroy();
1892
+ sourceWidth = destinationWidth;
1893
+ sourceHeight = destinationHeight;
1894
+ sourceDepth = destinationDepth;
1895
+ }
1896
+ } finally {
1897
+ uniformsBuffer.destroy();
1898
+ }
1899
+ }
1900
+ function validateFormatCapabilities(device, texture, requiredCapabilities, path) {
1901
+ const { format, dimension } = texture;
1902
+ const capabilities = device.getTextureFormatCapabilities(format);
1903
+ const missingCapabilities = requiredCapabilities.filter((capability) => !capabilities[capability]);
1904
+ if (missingCapabilities.length > 0) {
1905
+ const required = requiredCapabilities.join(" + ");
1906
+ const actual = requiredCapabilities.map((capability) => `${capability}=${capabilities[capability]}`).join(", ");
1907
+ throw new Error(`Cannot run ${path} mipmap generation for ${dimension} texture with format "${format}". Required capabilities: ${required}. Actual capabilities: ${actual}.`);
1908
+ }
1909
+ }
1910
+ function getSourceTextureType(dimension) {
1911
+ switch (dimension) {
1912
+ case "2d":
1913
+ return "texture_2d<f32>";
1914
+ case "2d-array":
1915
+ return "texture_2d_array<f32>";
1916
+ case "cube":
1917
+ return "texture_cube<f32>";
1918
+ case "cube-array":
1919
+ return "texture_cube_array<f32>";
1920
+ default:
1921
+ throw new Error(`Unsupported render dimension "${dimension}" for mipmap generation.`);
1922
+ }
1923
+ }
1924
+ function getRenderMipmapWGSL(dimension) {
1925
+ const sourceSnippet = getRenderMipmapSampleSnippet(dimension);
1926
+ return `
1927
+ struct MipmapUniforms {
1928
+ sourceLayer: u32,
1929
+ };
1930
+
1931
+ fn _touchUniform(uniforms: MipmapUniforms) {
1932
+ let unusedSourceLayer = uniforms.sourceLayer;
1933
+ }
1934
+
1935
+ const faceMat = array(
1936
+ mat3x3f(
1937
+ 0.0, 0.0, -2.0,
1938
+ 0.0, -2.0, 0.0,
1939
+ 1.0, 1.0, 1.0
1940
+ ), // pos-x
1941
+ mat3x3f(
1942
+ 0.0, 0.0, 2.0,
1943
+ 0.0, -2.0, 0.0,
1944
+ -1.0, 1.0, -1.0
1945
+ ), // neg-x
1946
+ mat3x3f(
1947
+ 2.0, 0.0, 0.0,
1948
+ 0.0, 0.0, 2.0,
1949
+ -1.0, 1.0, -1.0
1950
+ ), // pos-y
1951
+ mat3x3f(
1952
+ 2.0, 0.0, 0.0,
1953
+ 0.0, 0.0, -2.0,
1954
+ -1.0, -1.0, 1.0
1955
+ ), // neg-y
1956
+ mat3x3f(
1957
+ 2.0, 0.0, 0.0,
1958
+ 0.0, -2.0, 0.0,
1959
+ -1.0, 1.0, 1.0
1960
+ ), // pos-z
1961
+ mat3x3f(
1962
+ -2.0, 0.0, 0.0,
1963
+ 0.0, -2.0, 0.0,
1964
+ 1.0, 1.0, -1.0
1965
+ ) // neg-z
1966
+ );
1967
+
1968
+ struct FragmentInputs {
1969
+ @builtin(position) position: vec4f,
1970
+ @location(0) texcoord: vec2f
1971
+ };
1972
+
1973
+ struct VertexOutput {
1974
+ @builtin(position) position: vec4f,
1975
+ @location(0) texcoord: vec2f
1976
+ };
1977
+
1978
+ @group(0) @binding(0) var sourceSampler: sampler;
1979
+ @group(0) @binding(1) var sourceTexture: ${getSourceTextureType(dimension)};
1980
+ @group(0) @binding(2) var<uniform> uniforms: MipmapUniforms;
1981
+
1982
+ @vertex
1983
+ fn vertexMain(
1984
+ @builtin(vertex_index) vertexIndex: u32
1985
+ ) -> VertexOutput {
1986
+ const positions = array(
1987
+ vec2f(-1.0, -1.0),
1988
+ vec2f(-1.0, 3.0),
1989
+ vec2f( 3.0, -1.0)
1990
+ );
1991
+
1992
+ let xy = positions[vertexIndex];
1993
+ return VertexOutput(
1994
+ vec4f(xy, 0.0, 1.0),
1995
+ xy * vec2f(0.5, -0.5) + vec2f(0.5)
1996
+ );
1997
+ }
1998
+
1999
+ @fragment
2000
+ fn fragmentMain(fsInput: VertexOutput) -> @location(0) vec4f {
2001
+ _touchUniform(uniforms);
2002
+ return ${sourceSnippet};
2003
+ }
2004
+ `;
2005
+ }
2006
+ function getRenderMipmapSampleSnippet(dimension) {
2007
+ const layer = "uniforms.sourceLayer";
2008
+ switch (dimension) {
2009
+ case "2d":
2010
+ return "textureSampleLevel(sourceTexture, sourceSampler, fsInput.texcoord, 0.0)";
2011
+ case "2d-array":
2012
+ return `textureSampleLevel(sourceTexture, sourceSampler, fsInput.texcoord, i32(${layer}), 0.0)`;
2013
+ case "cube":
2014
+ return `textureSampleLevel(sourceTexture, sourceSampler, faceMat[i32(${layer})] * vec3f(fract(fsInput.texcoord), 1.0), 0.0)`;
2015
+ case "cube-array":
2016
+ return `textureSampleLevel(sourceTexture, sourceSampler, faceMat[i32(${layer} % 6u)] * vec3f(fract(fsInput.texcoord), 1.0), i32(${layer} / 6u), 0.0)`;
2017
+ default:
2018
+ throw new Error(`Unsupported render dimension "${dimension}" for mipmap generation.`);
2019
+ }
2020
+ }
2021
+ function get3DComputeMipmapWGSL(format) {
2022
+ return `
2023
+ struct MipmapUniforms {
2024
+ sourceWidth: u32,
2025
+ sourceHeight: u32,
2026
+ sourceDepth: u32,
2027
+ destinationWidth: u32,
2028
+ destinationHeight: u32,
2029
+ destinationDepth: u32,
2030
+ padding: u32,
2031
+ };
2032
+
2033
+ @group(0) @binding(0) var sourceTexture: texture_3d<f32>;
2034
+ @group(0) @binding(1) var destinationTexture: texture_storage_3d<${format}, write>;
2035
+ @group(0) @binding(2) var<uniform> uniforms: MipmapUniforms;
2036
+
2037
+ @compute @workgroup_size(${WORKGROUP_SIZE.x}, ${WORKGROUP_SIZE.y}, ${WORKGROUP_SIZE.z})
2038
+ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
2039
+ if (
2040
+ id.x >= uniforms.destinationWidth ||
2041
+ id.y >= uniforms.destinationHeight ||
2042
+ id.z >= uniforms.destinationDepth
2043
+ ) {
2044
+ return;
2045
+ }
2046
+
2047
+ let sourceBase = id * 2u;
2048
+ let sourceX0 = min(sourceBase.x, uniforms.sourceWidth - 1u);
2049
+ let sourceY0 = min(sourceBase.y, uniforms.sourceHeight - 1u);
2050
+ let sourceZ0 = min(sourceBase.z, uniforms.sourceDepth - 1u);
2051
+
2052
+ let sourceX1 = min(sourceBase.x + 1u, uniforms.sourceWidth - 1u);
2053
+ let sourceY1 = min(sourceBase.y + 1u, uniforms.sourceHeight - 1u);
2054
+ let sourceZ1 = min(sourceBase.z + 1u, uniforms.sourceDepth - 1u);
2055
+
2056
+ var sum = textureLoad(
2057
+ sourceTexture,
2058
+ vec3<i32>(i32(sourceX0), i32(sourceY0), i32(sourceZ0)),
2059
+ 0
2060
+ );
2061
+ sum += textureLoad(
2062
+ sourceTexture,
2063
+ vec3<i32>(i32(sourceX1), i32(sourceY0), i32(sourceZ0)),
2064
+ 0
2065
+ );
2066
+ sum += textureLoad(
2067
+ sourceTexture,
2068
+ vec3<i32>(i32(sourceX0), i32(sourceY1), i32(sourceZ0)),
2069
+ 0
2070
+ );
2071
+ sum += textureLoad(
2072
+ sourceTexture,
2073
+ vec3<i32>(i32(sourceX1), i32(sourceY1), i32(sourceZ0)),
2074
+ 0
2075
+ );
2076
+ sum += textureLoad(
2077
+ sourceTexture,
2078
+ vec3<i32>(i32(sourceX0), i32(sourceY0), i32(sourceZ1)),
2079
+ 0
2080
+ );
2081
+ sum += textureLoad(
2082
+ sourceTexture,
2083
+ vec3<i32>(i32(sourceX1), i32(sourceY0), i32(sourceZ1)),
2084
+ 0
2085
+ );
2086
+ sum += textureLoad(
2087
+ sourceTexture,
2088
+ vec3<i32>(i32(sourceX0), i32(sourceY1), i32(sourceZ1)),
2089
+ 0
2090
+ );
2091
+ sum += textureLoad(
2092
+ sourceTexture,
2093
+ vec3<i32>(i32(sourceX1), i32(sourceY1), i32(sourceZ1)),
2094
+ 0
2095
+ );
2096
+
2097
+ textureStore(
2098
+ destinationTexture,
2099
+ vec3<i32>(i32(id.x), i32(id.y), i32(id.z)),
2100
+ vec4<f32>(sum.xyz / 8.0, sum.w / 8.0)
2101
+ );
2102
+ }
2103
+ `;
1336
2104
  }
1337
2105
 
1338
- // dist/async-texture/async-texture.js
1339
- var TextureCubeFaces = ["+X", "-X", "+Y", "-Y", "+Z", "-Z"];
1340
- var CubeFaces = ["+X", "-X", "+Y", "-Y", "+Z", "-Z"];
1341
- var _AsyncTexture = class {
2106
+ // dist/dynamic-texture/dynamic-texture.js
2107
+ var _DynamicTexture = class {
1342
2108
  device;
1343
2109
  id;
2110
+ /** Props with defaults resolved (except `data` which is processed separately) */
1344
2111
  props;
1345
- // TODO - should we type these as possibly `null`? It will make usage harder?
1346
- // @ts-expect-error
1347
- texture;
1348
- // @ts-expect-error
1349
- sampler;
1350
- // @ts-expect-error
1351
- view;
2112
+ /** Created resources */
2113
+ _texture = null;
2114
+ _sampler = null;
2115
+ _view = null;
2116
+ /** Ready when GPU texture has been created and data (if any) uploaded */
1352
2117
  ready;
1353
2118
  isReady = false;
1354
2119
  destroyed = false;
@@ -1356,272 +2121,283 @@ var _AsyncTexture = class {
1356
2121
  };
1357
2122
  rejectReady = () => {
1358
2123
  };
2124
+ get texture() {
2125
+ if (!this._texture)
2126
+ throw new Error("Texture not initialized yet");
2127
+ return this._texture;
2128
+ }
2129
+ get sampler() {
2130
+ if (!this._sampler)
2131
+ throw new Error("Sampler not initialized yet");
2132
+ return this._sampler;
2133
+ }
2134
+ get view() {
2135
+ if (!this._view)
2136
+ throw new Error("View not initialized yet");
2137
+ return this._view;
2138
+ }
1359
2139
  get [Symbol.toStringTag]() {
1360
- return "AsyncTexture";
2140
+ return "DynamicTexture";
1361
2141
  }
1362
2142
  toString() {
1363
- return `AsyncTexture:"${this.id}"(${this.isReady ? "ready" : "loading"})`;
2143
+ return `DynamicTexture:"${this.id}":${this.texture.width}x${this.texture.height}px:(${this.isReady ? "ready" : "loading..."})`;
1364
2144
  }
1365
2145
  constructor(device, props) {
1366
2146
  this.device = device;
1367
- const id = uid("async-texture");
1368
- this.props = { ..._AsyncTexture.defaultProps, id, ...props };
2147
+ const id = uid("dynamic-texture");
2148
+ const originalPropsWithAsyncData = props;
2149
+ this.props = { ..._DynamicTexture.defaultProps, id, ...props, data: null };
1369
2150
  this.id = this.props.id;
1370
- props = { ...props };
1371
- if (typeof (props == null ? void 0 : props.data) === "string" && props.dimension === "2d") {
1372
- props.data = loadImageBitmap(props.data);
1373
- }
1374
- if (props.mipmaps) {
1375
- props.mipLevels = "auto";
1376
- }
1377
2151
  this.ready = new Promise((resolve, reject) => {
1378
- this.resolveReady = () => {
1379
- this.isReady = true;
1380
- resolve();
1381
- };
2152
+ this.resolveReady = resolve;
1382
2153
  this.rejectReady = reject;
1383
2154
  });
1384
- this.initAsync(props);
2155
+ this.initAsync(originalPropsWithAsyncData);
1385
2156
  }
1386
- async initAsync(props) {
1387
- const asyncData = props.data;
1388
- const data = await awaitAllPromises(asyncData).then(void 0, this.rejectReady);
1389
- if (this.destroyed) {
1390
- return;
1391
- }
1392
- const size = this.props.width && this.props.height ? { width: this.props.width, height: this.props.height } : this.getTextureDataSize(data);
1393
- if (!size) {
1394
- throw new Error("Texture size could not be determined");
1395
- }
1396
- const syncProps = { ...size, ...props, data: void 0, mipLevels: 1 };
1397
- const maxMips = this.device.getMipLevelCount(syncProps.width, syncProps.height);
1398
- syncProps.mipLevels = this.props.mipLevels === "auto" ? maxMips : Math.min(maxMips, this.props.mipLevels);
1399
- this.texture = this.device.createTexture(syncProps);
1400
- this.sampler = this.texture.sampler;
1401
- this.view = this.texture.view;
1402
- if (props.data) {
1403
- switch (this.props.dimension) {
1404
- case "1d":
1405
- this._setTexture1DData(this.texture, data);
1406
- break;
1407
- case "2d":
1408
- this._setTexture2DData(data);
1409
- break;
1410
- case "3d":
1411
- this._setTexture3DData(this.texture, data);
1412
- break;
1413
- case "2d-array":
1414
- this._setTextureArrayData(this.texture, data);
1415
- break;
1416
- case "cube":
1417
- this._setTextureCubeData(this.texture, data);
1418
- break;
1419
- case "cube-array":
1420
- this._setTextureCubeArrayData(this.texture, data);
1421
- break;
2157
+ /** @note Fire and forget; caller can await `ready` */
2158
+ async initAsync(originalPropsWithAsyncData) {
2159
+ try {
2160
+ const propsWithSyncData = await this._loadAllData(originalPropsWithAsyncData);
2161
+ this._checkNotDestroyed();
2162
+ const deduceSize = () => {
2163
+ if (this.props.width && this.props.height) {
2164
+ return { width: this.props.width, height: this.props.height };
2165
+ }
2166
+ const size2 = getTextureSizeFromData(propsWithSyncData);
2167
+ if (size2) {
2168
+ return size2;
2169
+ }
2170
+ return { width: this.props.width || 1, height: this.props.height || 1 };
2171
+ };
2172
+ const size = deduceSize();
2173
+ if (!size || size.width <= 0 || size.height <= 0) {
2174
+ throw new Error(`${this} size could not be determined or was zero`);
1422
2175
  }
2176
+ const baseTextureProps = {
2177
+ ...this.props,
2178
+ ...size,
2179
+ mipLevels: 1,
2180
+ // temporary; updated below
2181
+ data: void 0
2182
+ };
2183
+ if (this.device.type === "webgpu" && this.props.mipmaps) {
2184
+ const requiredUsage = this.props.dimension === "3d" ? import_core11.Texture.SAMPLE | import_core11.Texture.STORAGE | import_core11.Texture.COPY_DST | import_core11.Texture.COPY_SRC : import_core11.Texture.SAMPLE | import_core11.Texture.RENDER | import_core11.Texture.COPY_DST | import_core11.Texture.COPY_SRC;
2185
+ baseTextureProps.usage |= requiredUsage;
2186
+ }
2187
+ const maxMips = this.device.getMipLevelCount(baseTextureProps.width, baseTextureProps.height);
2188
+ const desired = this.props.mipLevels === "auto" ? maxMips : Math.max(1, Math.min(maxMips, this.props.mipLevels ?? 1));
2189
+ const finalTextureProps = { ...baseTextureProps, mipLevels: desired };
2190
+ this._texture = this.device.createTexture(finalTextureProps);
2191
+ this._sampler = this.texture.sampler;
2192
+ this._view = this.texture.view;
2193
+ if (propsWithSyncData.data) {
2194
+ switch (propsWithSyncData.dimension) {
2195
+ case "1d":
2196
+ this.setTexture1DData(propsWithSyncData.data);
2197
+ break;
2198
+ case "2d":
2199
+ this.setTexture2DData(propsWithSyncData.data);
2200
+ break;
2201
+ case "3d":
2202
+ this.setTexture3DData(propsWithSyncData.data);
2203
+ break;
2204
+ case "2d-array":
2205
+ this.setTextureArrayData(propsWithSyncData.data);
2206
+ break;
2207
+ case "cube":
2208
+ this.setTextureCubeData(propsWithSyncData.data);
2209
+ break;
2210
+ case "cube-array":
2211
+ this.setTextureCubeArrayData(propsWithSyncData.data);
2212
+ break;
2213
+ default: {
2214
+ throw new Error(`Unhandled dimension ${propsWithSyncData.dimension}`);
2215
+ }
2216
+ }
2217
+ }
2218
+ if (this.props.mipmaps) {
2219
+ this.generateMipmaps();
2220
+ }
2221
+ this.isReady = true;
2222
+ this.resolveReady(this.texture);
2223
+ import_core11.log.info(0, `${this} created`)();
2224
+ } catch (e) {
2225
+ const err = e instanceof Error ? e : new Error(String(e));
2226
+ this.rejectReady(err);
2227
+ throw err;
1423
2228
  }
1424
- if (this.props.mipmaps) {
1425
- this.generateMipmaps();
1426
- }
1427
- import_core8.log.info(1, `${this} loaded`);
1428
- this.resolveReady();
1429
2229
  }
1430
2230
  destroy() {
1431
- if (this.texture) {
1432
- this.texture.destroy();
1433
- this.texture = null;
2231
+ if (this._texture) {
2232
+ this._texture.destroy();
2233
+ this._texture = null;
2234
+ this._sampler = null;
2235
+ this._view = null;
1434
2236
  }
1435
2237
  this.destroyed = true;
1436
2238
  }
1437
2239
  generateMipmaps() {
1438
- this.texture.generateMipmapsWebGL();
2240
+ if (this.device.type === "webgl") {
2241
+ this.texture.generateMipmapsWebGL();
2242
+ } else if (this.device.type === "webgpu") {
2243
+ generateMipmap(this.device, this.texture);
2244
+ } else {
2245
+ import_core11.log.warn(`${this} mipmaps not supported on ${this.device.type}`);
2246
+ }
1439
2247
  }
1440
- /** Set sampler or create and set new Sampler from SamplerProps */
2248
+ /** Set sampler or create one from props */
1441
2249
  setSampler(sampler = {}) {
1442
- this.texture.setSampler(sampler instanceof import_core8.Sampler ? sampler : this.device.createSampler(sampler));
2250
+ this._checkReady();
2251
+ const s = sampler instanceof import_core11.Sampler ? sampler : this.device.createSampler(sampler);
2252
+ this.texture.setSampler(s);
2253
+ this._sampler = s;
1443
2254
  }
1444
2255
  /**
1445
- * Textures are immutable and cannot be resized after creation,
1446
- * but we can create a similar texture with the same parameters but a new size.
1447
- * @note Does not copy contents of the texture
1448
- * @note Mipmaps may need to be regenerated after resizing / setting new data
1449
- * @todo Abort pending promise and create a texture with the new size?
2256
+ * Resize by cloning the underlying immutable texture.
2257
+ * Does not copy contents; caller may need to re-upload and/or regenerate mips.
1450
2258
  */
1451
2259
  resize(size) {
1452
- if (!this.isReady) {
1453
- throw new Error("Cannot resize texture before it is ready");
1454
- }
2260
+ this._checkReady();
1455
2261
  if (size.width === this.texture.width && size.height === this.texture.height) {
1456
2262
  return false;
1457
2263
  }
1458
- if (this.texture) {
1459
- const texture = this.texture;
1460
- this.texture = texture.clone(size);
1461
- texture.destroy();
1462
- }
2264
+ const prev = this.texture;
2265
+ this._texture = prev.clone(size);
2266
+ this._sampler = this.texture.sampler;
2267
+ this._view = this.texture.view;
2268
+ prev.destroy();
2269
+ import_core11.log.info(`${this} resized`);
1463
2270
  return true;
1464
2271
  }
1465
- /** Check if texture data is a typed array */
1466
- isTextureLevelData(data) {
1467
- const typedArray = data == null ? void 0 : data.data;
1468
- return ArrayBuffer.isView(typedArray);
1469
- }
1470
- /** Get the size of the texture described by the provided TextureData */
1471
- getTextureDataSize(data) {
1472
- if (!data) {
1473
- return null;
1474
- }
1475
- if (ArrayBuffer.isView(data)) {
1476
- return null;
1477
- }
1478
- if (Array.isArray(data)) {
1479
- return this.getTextureDataSize(data[0]);
1480
- }
1481
- if (this.device.isExternalImage(data)) {
1482
- return this.device.getExternalImageSize(data);
1483
- }
1484
- if (data && typeof data === "object" && data.constructor === Object) {
1485
- const textureDataArray = Object.values(data);
1486
- const untypedData = textureDataArray[0];
1487
- return { width: untypedData.width, height: untypedData.height };
1488
- }
1489
- throw new Error("texture size deduction failed");
1490
- }
1491
- /** Convert luma.gl cubemap face constants to depth index */
1492
- getCubeFaceDepth(face) {
1493
- switch (face) {
1494
- case "+X":
1495
- return 0;
1496
- case "-X":
1497
- return 1;
1498
- case "+Y":
1499
- return 2;
1500
- case "-Y":
1501
- return 3;
1502
- case "+Z":
1503
- return 4;
1504
- case "-Z":
1505
- return 5;
1506
- default:
1507
- throw new Error(face);
1508
- }
1509
- }
1510
- // EXPERIMENTAL
1511
- setTextureData(data) {
1512
- }
1513
- /** Experimental: Set multiple mip levels */
1514
- _setTexture1DData(texture, data) {
1515
- throw new Error("setTexture1DData not supported in WebGL.");
1516
- }
1517
- /** Experimental: Set multiple mip levels */
1518
- _setTexture2DData(lodData, depth = 0) {
1519
- if (!this.texture) {
1520
- throw new Error("Texture not initialized");
1521
- }
1522
- const lodArray = this._normalizeTextureData(lodData);
1523
- if (lodArray.length > 1 && this.props.mipmaps !== false) {
1524
- import_core8.log.warn(`Texture ${this.id} mipmap and multiple LODs.`)();
1525
- }
1526
- for (let mipLevel = 0; mipLevel < lodArray.length; mipLevel++) {
1527
- const imageData = lodArray[mipLevel];
1528
- if (this.device.isExternalImage(imageData)) {
1529
- this.texture.copyExternalImage({ image: imageData, depth, mipLevel, flipY: true });
1530
- } else {
1531
- this.texture.copyImageData({ data: imageData.data, mipLevel });
2272
+ /** Convert cube face label to texture slice index. Index can be used with `setTexture2DData()`. */
2273
+ getCubeFaceIndex(face) {
2274
+ const index = TEXTURE_CUBE_FACE_MAP[face];
2275
+ if (index === void 0)
2276
+ throw new Error(`Invalid cube face: ${face}`);
2277
+ return index;
2278
+ }
2279
+ /** Convert cube face label to texture slice index. Index can be used with `setTexture2DData()`. */
2280
+ getCubeArrayFaceIndex(cubeIndex, face) {
2281
+ return 6 * cubeIndex + this.getCubeFaceIndex(face);
2282
+ }
2283
+ /** @note experimental: Set multiple mip levels (1D) */
2284
+ setTexture1DData(data) {
2285
+ this._checkReady();
2286
+ if (this.texture.props.dimension !== "1d") {
2287
+ throw new Error(`${this} is not 1d`);
2288
+ }
2289
+ const subresources = getTexture1DSubresources(data);
2290
+ this._setTextureSubresources(subresources);
2291
+ }
2292
+ /** @note experimental: Set multiple mip levels (2D), optionally at `z`, slice (depth/array level) index */
2293
+ setTexture2DData(lodData, z = 0) {
2294
+ this._checkReady();
2295
+ if (this.texture.props.dimension !== "2d") {
2296
+ throw new Error(`${this} is not 2d`);
2297
+ }
2298
+ const subresources = getTexture2DSubresources(z, lodData);
2299
+ this._setTextureSubresources(subresources);
2300
+ }
2301
+ /** 3D: multiple depth slices, each may carry multiple mip levels */
2302
+ setTexture3DData(data) {
2303
+ if (this.texture.props.dimension !== "3d") {
2304
+ throw new Error(`${this} is not 3d`);
2305
+ }
2306
+ const subresources = getTexture3DSubresources(data);
2307
+ this._setTextureSubresources(subresources);
2308
+ }
2309
+ /** 2D array: multiple layers, each may carry multiple mip levels */
2310
+ setTextureArrayData(data) {
2311
+ if (this.texture.props.dimension !== "2d-array") {
2312
+ throw new Error(`${this} is not 2d-array`);
2313
+ }
2314
+ const subresources = getTextureArraySubresources(data);
2315
+ this._setTextureSubresources(subresources);
2316
+ }
2317
+ /** Cube: 6 faces, each may carry multiple mip levels */
2318
+ setTextureCubeData(data) {
2319
+ if (this.texture.props.dimension !== "cube") {
2320
+ throw new Error(`${this} is not cube`);
2321
+ }
2322
+ const subresources = getTextureCubeSubresources(data);
2323
+ this._setTextureSubresources(subresources);
2324
+ }
2325
+ /** Cube array: multiple cubes (faces×layers), each face may carry multiple mips */
2326
+ setTextureCubeArrayData(data) {
2327
+ if (this.texture.props.dimension !== "cube-array") {
2328
+ throw new Error(`${this} is not cube-array`);
2329
+ }
2330
+ const subresources = getTextureCubeArraySubresources(data);
2331
+ this._setTextureSubresources(subresources);
2332
+ }
2333
+ /** Sets multiple mip levels on different `z` slices (depth/array index) */
2334
+ _setTextureSubresources(subresources) {
2335
+ for (const subresource of subresources) {
2336
+ const { z, mipLevel } = subresource;
2337
+ switch (subresource.type) {
2338
+ case "external-image":
2339
+ const { image, flipY } = subresource;
2340
+ this.texture.copyExternalImage({ image, z, mipLevel, flipY });
2341
+ break;
2342
+ case "texture-data":
2343
+ const { data } = subresource;
2344
+ this.texture.writeData(getAlignedUploadData(this.texture, data), {
2345
+ x: 0,
2346
+ y: 0,
2347
+ z,
2348
+ width: data.width,
2349
+ height: data.height,
2350
+ depthOrArrayLayers: 1,
2351
+ mipLevel
2352
+ });
2353
+ break;
2354
+ default:
2355
+ throw new Error("Unsupported 2D mip-level payload");
1532
2356
  }
1533
2357
  }
1534
2358
  }
1535
- /**
1536
- * Experimental: Sets 3D texture data: multiple depth slices, multiple mip levels
1537
- * @param data
1538
- */
1539
- _setTexture3DData(texture, data) {
1540
- var _a;
1541
- if (((_a = this.texture) == null ? void 0 : _a.props.dimension) !== "3d") {
1542
- throw new Error(this.id);
1543
- }
1544
- for (let depth = 0; depth < data.length; depth++) {
1545
- this._setTexture2DData(data[depth], depth);
1546
- }
2359
+ // ------------------ helpers ------------------
2360
+ /** Recursively resolve all promises in data structures */
2361
+ async _loadAllData(props) {
2362
+ const syncData = await awaitAllPromises(props.data);
2363
+ const dimension = props.dimension ?? "2d";
2364
+ return { dimension, data: syncData ?? null };
1547
2365
  }
1548
- /**
1549
- * Experimental: Set Cube texture data, multiple faces, multiple mip levels
1550
- * @todo - could support TextureCubeArray with depth
1551
- * @param data
1552
- * @param index
1553
- */
1554
- _setTextureCubeData(texture, data) {
1555
- var _a;
1556
- if (((_a = this.texture) == null ? void 0 : _a.props.dimension) !== "cube") {
1557
- throw new Error(this.id);
1558
- }
1559
- for (const [face, faceData] of Object.entries(data)) {
1560
- const faceDepth = CubeFaces.indexOf(face);
1561
- this._setTexture2DData(faceData, faceDepth);
1562
- }
1563
- }
1564
- /**
1565
- * Experimental: Sets texture array data, multiple levels, multiple depth slices
1566
- * @param data
1567
- */
1568
- _setTextureArrayData(texture, data) {
1569
- var _a;
1570
- if (((_a = this.texture) == null ? void 0 : _a.props.dimension) !== "2d-array") {
1571
- throw new Error(this.id);
1572
- }
1573
- for (let depth = 0; depth < data.length; depth++) {
1574
- this._setTexture2DData(data[depth], depth);
1575
- }
1576
- }
1577
- /**
1578
- * Experimental: Sets texture cube array, multiple faces, multiple levels, multiple mip levels
1579
- * @param data
1580
- */
1581
- _setTextureCubeArrayData(texture, data) {
1582
- throw new Error("setTextureCubeArrayData not supported in WebGL2.");
1583
- }
1584
- /** Experimental */
1585
- _setTextureCubeFaceData(texture, lodData, face, depth = 0) {
1586
- if (Array.isArray(lodData) && lodData.length > 1 && this.props.mipmaps !== false) {
1587
- import_core8.log.warn(`${this.id} has mipmap and multiple LODs.`)();
2366
+ _checkNotDestroyed() {
2367
+ if (this.destroyed) {
2368
+ import_core11.log.warn(`${this} already destroyed`);
1588
2369
  }
1589
- const faceDepth = TextureCubeFaces.indexOf(face);
1590
- this._setTexture2DData(lodData, faceDepth);
1591
2370
  }
1592
- /**
1593
- * Normalize TextureData to an array of TextureImageData / ExternalImages
1594
- * @param data
1595
- * @param options
1596
- * @returns array of TextureImageData / ExternalImages
1597
- */
1598
- _normalizeTextureData(data) {
1599
- const options = this.texture;
1600
- let mipLevelArray;
1601
- if (ArrayBuffer.isView(data)) {
1602
- mipLevelArray = [
1603
- {
1604
- // ts-expect-error does data really need to be Uint8ClampedArray?
1605
- data,
1606
- width: options.width,
1607
- height: options.height
1608
- // depth: options.depth
1609
- }
1610
- ];
1611
- } else if (!Array.isArray(data)) {
1612
- mipLevelArray = [data];
1613
- } else {
1614
- mipLevelArray = data;
2371
+ _checkReady() {
2372
+ if (!this.isReady) {
2373
+ import_core11.log.warn(`${this} Cannot perform this operation before ready`);
1615
2374
  }
1616
- return mipLevelArray;
1617
2375
  }
1618
2376
  };
1619
- var AsyncTexture = _AsyncTexture;
1620
- __publicField(AsyncTexture, "defaultProps", {
1621
- ...import_core8.Texture.defaultProps,
2377
+ var DynamicTexture = _DynamicTexture;
2378
+ __publicField(DynamicTexture, "defaultProps", {
2379
+ ...import_core11.Texture.defaultProps,
2380
+ dimension: "2d",
1622
2381
  data: null,
1623
2382
  mipmaps: false
1624
2383
  });
2384
+ function getAlignedUploadData(texture, data) {
2385
+ const { width, height, data: uploadData } = data;
2386
+ const { bytesPerPixel } = texture.device.getTextureFormatInfo(texture.format);
2387
+ const bytesPerRow = width * bytesPerPixel;
2388
+ const alignedBytesPerRow = Math.ceil(bytesPerRow / texture.byteAlignment) * texture.byteAlignment;
2389
+ if (alignedBytesPerRow === bytesPerRow) {
2390
+ return uploadData;
2391
+ }
2392
+ const sourceBytes = new Uint8Array(uploadData.buffer, uploadData.byteOffset, uploadData.byteLength);
2393
+ const paddedBytes = new Uint8Array(alignedBytesPerRow * height);
2394
+ for (let row = 0; row < height; row++) {
2395
+ const sourceOffset = row * bytesPerRow;
2396
+ const destinationOffset = row * alignedBytesPerRow;
2397
+ paddedBytes.set(sourceBytes.subarray(sourceOffset, sourceOffset + bytesPerRow), destinationOffset);
2398
+ }
2399
+ return paddedBytes;
2400
+ }
1625
2401
  async function awaitAllPromises(x) {
1626
2402
  x = await x;
1627
2403
  if (Array.isArray(x)) {
@@ -1641,19 +2417,27 @@ async function awaitAllPromises(x) {
1641
2417
  }
1642
2418
 
1643
2419
  // dist/model/model.js
1644
- var LOG_DRAW_PRIORITY = 2;
1645
- var LOG_DRAW_TIMEOUT = 1e4;
2420
+ var LOG_DRAW_PRIORITY2 = 2;
2421
+ var LOG_DRAW_TIMEOUT2 = 1e4;
1646
2422
  var _Model = class {
2423
+ /** Device that created this model */
1647
2424
  device;
2425
+ /** Application provided identifier */
1648
2426
  id;
2427
+ /** WGSL shader source when using unified shader */
1649
2428
  // @ts-expect-error assigned in function called from constructor
1650
2429
  source;
2430
+ /** GLSL vertex shader source */
1651
2431
  // @ts-expect-error assigned in function called from constructor
1652
2432
  vs;
2433
+ /** GLSL fragment shader source */
1653
2434
  // @ts-expect-error assigned in function called from constructor
1654
2435
  fs;
2436
+ /** Factory used to create render pipelines */
1655
2437
  pipelineFactory;
2438
+ /** Factory used to create shaders */
1656
2439
  shaderFactory;
2440
+ /** User-supplied per-model data */
1657
2441
  userData = {};
1658
2442
  // Fixed properties (change can trigger pipeline rebuild)
1659
2443
  /** The render pipeline GPU parameters, depth testing etc */
@@ -1716,7 +2500,7 @@ var _Model = class {
1716
2500
  const moduleMap = Object.fromEntries(((_a = this.props.modules) == null ? void 0 : _a.map((module2) => [module2.name, module2])) || []);
1717
2501
  const shaderInputs = props.shaderInputs || new ShaderInputs(moduleMap, { disableWarnings: this.props.disableWarnings });
1718
2502
  this.setShaderInputs(shaderInputs);
1719
- const platformInfo = getPlatformInfo(device);
2503
+ const platformInfo = getPlatformInfo2(device);
1720
2504
  const modules = (
1721
2505
  // @ts-ignore shaderInputs is assigned in setShaderInputs above.
1722
2506
  (((_b = this.props.modules) == null ? void 0 : _b.length) > 0 ? this.props.modules : (_c = this.shaderInputs) == null ? void 0 : _c.getModules()) || []
@@ -1730,7 +2514,7 @@ var _Model = class {
1730
2514
  });
1731
2515
  this.source = source3;
1732
2516
  this._getModuleUniforms = getUniforms2;
1733
- this.props.shaderLayout ||= (0, import_shadertools2.getShaderLayoutFromWGSL)(this.source);
2517
+ this.props.shaderLayout ||= device.getShaderLayout(this.source);
1734
2518
  } else {
1735
2519
  const { vs: vs3, fs: fs3, getUniforms: getUniforms2 } = this.props.shaderAssembler.assembleGLSLShaderPair({
1736
2520
  platformInfo,
@@ -1783,7 +2567,6 @@ var _Model = class {
1783
2567
  if (props.transformFeedback) {
1784
2568
  this.transformFeedback = props.transformFeedback;
1785
2569
  }
1786
- Object.seal(this);
1787
2570
  }
1788
2571
  destroy() {
1789
2572
  var _a;
@@ -1812,14 +2595,20 @@ var _Model = class {
1812
2595
  setNeedsRedraw(reason) {
1813
2596
  this._needsRedraw ||= reason;
1814
2597
  }
2598
+ /** Update uniforms and pipeline state prior to drawing. */
1815
2599
  predraw() {
1816
2600
  this.updateShaderInputs();
1817
2601
  this.pipeline = this._updatePipeline();
1818
2602
  }
2603
+ /**
2604
+ * Issue one draw call.
2605
+ * @param renderPass - render pass to draw into
2606
+ * @returns `true` if the draw call was executed, `false` if resources were not ready.
2607
+ */
1819
2608
  draw(renderPass) {
1820
2609
  const loadingBinding = this._areBindingsLoading();
1821
2610
  if (loadingBinding) {
1822
- import_core9.log.info(LOG_DRAW_PRIORITY, `>>> DRAWING ABORTED ${this.id}: ${loadingBinding} not loaded`)();
2611
+ import_core12.log.info(LOG_DRAW_PRIORITY2, `>>> DRAWING ABORTED ${this.id}: ${loadingBinding} not loaded`)();
1823
2612
  return false;
1824
2613
  }
1825
2614
  try {
@@ -1947,7 +2736,7 @@ var _Model = class {
1947
2736
  /** Set the shader inputs */
1948
2737
  setShaderInputs(shaderInputs) {
1949
2738
  this.shaderInputs = shaderInputs;
1950
- this._uniformStore = new import_core9.UniformStore(this.shaderInputs.modules);
2739
+ this._uniformStore = new import_core12.UniformStore(this.shaderInputs.modules);
1951
2740
  for (const [moduleName, module2] of Object.entries(this.shaderInputs.modules)) {
1952
2741
  if (shaderModuleHasUniforms(module2)) {
1953
2742
  const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
@@ -1991,7 +2780,7 @@ var _Model = class {
1991
2780
  setAttributes(buffers, options) {
1992
2781
  const disableWarnings = (options == null ? void 0 : options.disableWarnings) ?? this.props.disableWarnings;
1993
2782
  if (buffers["indices"]) {
1994
- import_core9.log.warn(`Model:${this.id} setAttributes() - indexBuffer should be set using setIndexBuffer()`)();
2783
+ import_core12.log.warn(`Model:${this.id} setAttributes() - indexBuffer should be set using setIndexBuffer()`)();
1995
2784
  }
1996
2785
  this.bufferLayout = sortedBufferLayoutByShaderSourceLocations(this.pipeline.shaderLayout, this.bufferLayout);
1997
2786
  const bufferLayoutHelper = new BufferLayoutHelper(this.bufferLayout);
@@ -1999,7 +2788,7 @@ var _Model = class {
1999
2788
  const bufferLayout = bufferLayoutHelper.getBufferLayout(bufferName);
2000
2789
  if (!bufferLayout) {
2001
2790
  if (!disableWarnings) {
2002
- import_core9.log.warn(`Model(${this.id}): Missing layout for buffer "${bufferName}".`)();
2791
+ import_core12.log.warn(`Model(${this.id}): Missing layout for buffer "${bufferName}".`)();
2003
2792
  }
2004
2793
  continue;
2005
2794
  }
@@ -2014,7 +2803,7 @@ var _Model = class {
2014
2803
  }
2015
2804
  }
2016
2805
  if (!set && !disableWarnings) {
2017
- import_core9.log.warn(`Model(${this.id}): Ignoring buffer "${buffer.id}" for unknown attribute "${bufferName}"`)();
2806
+ import_core12.log.warn(`Model(${this.id}): Ignoring buffer "${buffer.id}" for unknown attribute "${bufferName}"`)();
2018
2807
  }
2019
2808
  }
2020
2809
  this.setNeedsRedraw("attributes");
@@ -2033,7 +2822,7 @@ var _Model = class {
2033
2822
  if (attributeInfo) {
2034
2823
  this.vertexArray.setConstantWebGL(attributeInfo.location, value);
2035
2824
  } else if (!((options == null ? void 0 : options.disableWarnings) ?? this.props.disableWarnings)) {
2036
- import_core9.log.warn(`Model "${this.id}: Ignoring constant supplied for unknown attribute "${attributeName}"`)();
2825
+ import_core12.log.warn(`Model "${this.id}: Ignoring constant supplied for unknown attribute "${attributeName}"`)();
2037
2826
  }
2038
2827
  }
2039
2828
  this.setNeedsRedraw("constants");
@@ -2042,7 +2831,7 @@ var _Model = class {
2042
2831
  /** Check that bindings are loaded. Returns id of first binding that is still loading. */
2043
2832
  _areBindingsLoading() {
2044
2833
  for (const binding of Object.values(this.bindings)) {
2045
- if (binding instanceof AsyncTexture && !binding.isReady) {
2834
+ if (binding instanceof DynamicTexture && !binding.isReady) {
2046
2835
  return binding.id;
2047
2836
  }
2048
2837
  }
@@ -2052,7 +2841,7 @@ var _Model = class {
2052
2841
  _getBindings() {
2053
2842
  const validBindings = {};
2054
2843
  for (const [name, binding] of Object.entries(this.bindings)) {
2055
- if (binding instanceof AsyncTexture) {
2844
+ if (binding instanceof DynamicTexture) {
2056
2845
  if (binding.isReady) {
2057
2846
  validBindings[name] = binding.texture;
2058
2847
  }
@@ -2066,16 +2855,16 @@ var _Model = class {
2066
2855
  _getBindingsUpdateTimestamp() {
2067
2856
  let timestamp = 0;
2068
2857
  for (const binding of Object.values(this.bindings)) {
2069
- if (binding instanceof import_core9.TextureView) {
2858
+ if (binding instanceof import_core12.TextureView) {
2070
2859
  timestamp = Math.max(timestamp, binding.texture.updateTimestamp);
2071
- } else if (binding instanceof import_core9.Buffer || binding instanceof import_core9.Texture) {
2860
+ } else if (binding instanceof import_core12.Buffer || binding instanceof import_core12.Texture) {
2072
2861
  timestamp = Math.max(timestamp, binding.updateTimestamp);
2073
- } else if (binding instanceof AsyncTexture) {
2862
+ } else if (binding instanceof DynamicTexture) {
2074
2863
  timestamp = binding.texture ? Math.max(timestamp, binding.texture.updateTimestamp) : (
2075
2864
  // The texture will become available in the future
2076
2865
  Infinity
2077
2866
  );
2078
- } else if (!(binding instanceof import_core9.Sampler)) {
2867
+ } else if (!(binding instanceof import_core12.Sampler)) {
2079
2868
  timestamp = Math.max(timestamp, binding.buffer.updateTimestamp);
2080
2869
  }
2081
2870
  }
@@ -2110,7 +2899,7 @@ var _Model = class {
2110
2899
  let prevShaderVs = null;
2111
2900
  let prevShaderFs = null;
2112
2901
  if (this.pipeline) {
2113
- import_core9.log.log(1, `Model ${this.id}: Recreating pipeline because "${this._pipelineNeedsUpdate}".`)();
2902
+ import_core12.log.log(1, `Model ${this.id}: Recreating pipeline because "${this._pipelineNeedsUpdate}".`)();
2114
2903
  prevShaderVs = this.pipeline.vs;
2115
2904
  prevShaderFs = this.pipeline.fs;
2116
2905
  }
@@ -2143,7 +2932,7 @@ var _Model = class {
2143
2932
  vs: vs3,
2144
2933
  fs: fs3
2145
2934
  });
2146
- this._attributeInfos = (0, import_core9.getAttributeInfosFromLayouts)(this.pipeline.shaderLayout, this.bufferLayout);
2935
+ this._attributeInfos = (0, import_core12.getAttributeInfosFromLayouts)(this.pipeline.shaderLayout, this.bufferLayout);
2147
2936
  if (prevShaderVs)
2148
2937
  this.shaderFactory.release(prevShaderVs);
2149
2938
  if (prevShaderFs)
@@ -2155,24 +2944,24 @@ var _Model = class {
2155
2944
  _lastLogTime = 0;
2156
2945
  _logOpen = false;
2157
2946
  _logDrawCallStart() {
2158
- const logDrawTimeout = import_core9.log.level > 3 ? 0 : LOG_DRAW_TIMEOUT;
2159
- if (import_core9.log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
2947
+ const logDrawTimeout = import_core12.log.level > 3 ? 0 : LOG_DRAW_TIMEOUT2;
2948
+ if (import_core12.log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
2160
2949
  return;
2161
2950
  }
2162
2951
  this._lastLogTime = Date.now();
2163
2952
  this._logOpen = true;
2164
- import_core9.log.group(LOG_DRAW_PRIORITY, `>>> DRAWING MODEL ${this.id}`, { collapsed: import_core9.log.level <= 2 })();
2953
+ import_core12.log.group(LOG_DRAW_PRIORITY2, `>>> DRAWING MODEL ${this.id}`, { collapsed: import_core12.log.level <= 2 })();
2165
2954
  }
2166
2955
  _logDrawCallEnd() {
2167
2956
  if (this._logOpen) {
2168
2957
  const shaderLayoutTable = getDebugTableForShaderLayout(this.pipeline.shaderLayout, this.id);
2169
- import_core9.log.table(LOG_DRAW_PRIORITY, shaderLayoutTable)();
2958
+ import_core12.log.table(LOG_DRAW_PRIORITY2, shaderLayoutTable)();
2170
2959
  const uniformTable = this.shaderInputs.getDebugTable();
2171
- import_core9.log.table(LOG_DRAW_PRIORITY, uniformTable)();
2960
+ import_core12.log.table(LOG_DRAW_PRIORITY2, uniformTable)();
2172
2961
  const attributeTable = this._getAttributeDebugTable();
2173
- import_core9.log.table(LOG_DRAW_PRIORITY, this._attributeInfos)();
2174
- import_core9.log.table(LOG_DRAW_PRIORITY, attributeTable)();
2175
- import_core9.log.groupEnd(LOG_DRAW_PRIORITY)();
2962
+ import_core12.log.table(LOG_DRAW_PRIORITY2, this._attributeInfos)();
2963
+ import_core12.log.table(LOG_DRAW_PRIORITY2, attributeTable)();
2964
+ import_core12.log.groupEnd(LOG_DRAW_PRIORITY2)();
2176
2965
  this._logOpen = false;
2177
2966
  }
2178
2967
  }
@@ -2211,14 +3000,14 @@ var _Model = class {
2211
3000
  }
2212
3001
  // TODO - fix typing of luma data types
2213
3002
  _getBufferOrConstantValues(attribute, dataType) {
2214
- const TypedArrayConstructor = (0, import_core9.getTypedArrayConstructor)(dataType);
2215
- const typedArray = attribute instanceof import_core9.Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
3003
+ const TypedArrayConstructor = (0, import_core12.getTypedArrayConstructor)(dataType);
3004
+ const typedArray = attribute instanceof import_core12.Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
2216
3005
  return typedArray.toString();
2217
3006
  }
2218
3007
  };
2219
3008
  var Model = _Model;
2220
3009
  __publicField(Model, "defaultProps", {
2221
- ...import_core9.RenderPipeline.defaultProps,
3010
+ ...import_core12.RenderPipeline.defaultProps,
2222
3011
  source: void 0,
2223
3012
  vs: null,
2224
3013
  fs: null,
@@ -2239,14 +3028,14 @@ __publicField(Model, "defaultProps", {
2239
3028
  pipelineFactory: void 0,
2240
3029
  shaderFactory: void 0,
2241
3030
  transformFeedback: void 0,
2242
- shaderAssembler: import_shadertools2.ShaderAssembler.getDefaultShaderAssembler(),
3031
+ shaderAssembler: import_shadertools3.ShaderAssembler.getDefaultShaderAssembler(),
2243
3032
  debugShaders: void 0,
2244
3033
  disableWarnings: void 0
2245
3034
  });
2246
3035
  function shaderModuleHasUniforms(module2) {
2247
3036
  return Boolean(module2.uniformTypes && !isObjectEmpty(module2.uniformTypes));
2248
3037
  }
2249
- function getPlatformInfo(device) {
3038
+ function getPlatformInfo2(device) {
2250
3039
  return {
2251
3040
  type: device.type,
2252
3041
  shaderLanguage: device.info.shadingLanguage,
@@ -2264,8 +3053,8 @@ function isObjectEmpty(obj) {
2264
3053
  }
2265
3054
 
2266
3055
  // dist/compute/buffer-transform.js
2267
- var import_core10 = require("@luma.gl/core");
2268
- var import_shadertools3 = require("@luma.gl/shadertools");
3056
+ var import_core13 = require("@luma.gl/core");
3057
+ var import_shadertools4 = require("@luma.gl/shadertools");
2269
3058
  var _BufferTransform = class {
2270
3059
  device;
2271
3060
  model;
@@ -2281,7 +3070,7 @@ var _BufferTransform = class {
2281
3070
  this.device = device;
2282
3071
  this.model = new Model(this.device, {
2283
3072
  id: props.id || "buffer-transform-model",
2284
- fs: props.fs || (0, import_shadertools3.getPassthroughFS)(),
3073
+ fs: props.fs || (0, import_shadertools4.getPassthroughFS)(),
2285
3074
  topology: props.topology || "point-list",
2286
3075
  varyings: props.outputs || props.varyings,
2287
3076
  ...props
@@ -2327,7 +3116,7 @@ var _BufferTransform = class {
2327
3116
  if (!result) {
2328
3117
  throw new Error("BufferTransform#getBuffer");
2329
3118
  }
2330
- if (result instanceof import_core10.Buffer) {
3119
+ if (result instanceof import_core13.Buffer) {
2331
3120
  return result.readAsync();
2332
3121
  }
2333
3122
  const { buffer, byteOffset = 0, byteLength = buffer.byteLength } = result;
@@ -2342,7 +3131,7 @@ __publicField(BufferTransform, "defaultProps", {
2342
3131
  });
2343
3132
 
2344
3133
  // dist/compute/texture-transform.js
2345
- var import_shadertools4 = require("@luma.gl/shadertools");
3134
+ var import_shadertools5 = require("@luma.gl/shadertools");
2346
3135
  var FS_OUTPUT_VARIABLE = "transform_output";
2347
3136
  var TextureTransform = class {
2348
3137
  device;
@@ -2365,7 +3154,7 @@ var TextureTransform = class {
2365
3154
  });
2366
3155
  this.model = new Model(this.device, {
2367
3156
  id: props.id || uid("texture-transform-model"),
2368
- fs: props.fs || (0, import_shadertools4.getPassthroughFS)({
3157
+ fs: props.fs || (0, import_shadertools5.getPassthroughFS)({
2369
3158
  input: props.targetTextureVarying,
2370
3159
  inputChannels: props.targetTextureChannels,
2371
3160
  output: FS_OUTPUT_VARIABLE
@@ -2604,22 +3393,31 @@ ${props.source}` };
2604
3393
  };
2605
3394
 
2606
3395
  // dist/models/billboard-texture-model.js
3396
+ var backgroundModule = {
3397
+ name: "background",
3398
+ uniformTypes: {
3399
+ scale: "vec2<f32>"
3400
+ }
3401
+ };
2607
3402
  var BACKGROUND_FS_WGSL = (
2608
3403
  /* wgsl */
2609
3404
  `@group(0) @binding(0) var backgroundTexture: texture_2d<f32>;
2610
3405
  @group(0) @binding(1) var backgroundTextureSampler: sampler;
3406
+ struct backgroundUniforms {
3407
+ scale: vec2<f32>,
3408
+ };
3409
+ @group(0) @binding(2) var<uniform> background: backgroundUniforms;
2611
3410
 
2612
3411
  fn billboardTexture_getTextureUV(coordinates: vec2<f32>) -> vec2<f32> {
2613
- let iTexSize: vec2<u32> = textureDimensions(backgroundTexture, 0);
2614
- let texSize: vec2<f32> = vec2<f32>(f32(iTexSize.x), f32(iTexSize.y));
2615
- var position: vec2<f32> = coordinates.xy / texSize;
2616
- return position;
2617
- }
3412
+ let scale: vec2<f32> = background.scale;
3413
+ var position: vec2<f32> = (coordinates - vec2<f32>(0.5, 0.5)) / scale + vec2<f32>(0.5, 0.5);
3414
+ return position;
3415
+ }
2618
3416
 
2619
3417
  @fragment
2620
3418
  fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4<f32> {
2621
- let position: vec2<f32> = billboardTexture_getTextureUV(inputs.coordinate);
2622
- return textureSample(backgroundTexture, backgroundTextureSampler, position);
3419
+ let position: vec2<f32> = billboardTexture_getTextureUV(inputs.coordinate);
3420
+ return textureSample(backgroundTexture, backgroundTextureSampler, position);
2623
3421
  }
2624
3422
  `
2625
3423
  );
@@ -2629,27 +3427,33 @@ var BACKGROUND_FS = (
2629
3427
  precision highp float;
2630
3428
 
2631
3429
  uniform sampler2D backgroundTexture;
3430
+
3431
+ uniform backgroundUniforms {
3432
+ vec2 scale;
3433
+ } background;
3434
+
3435
+ in vec2 coordinate;
2632
3436
  out vec4 fragColor;
2633
3437
 
2634
- vec2 billboardTexture_getTextureUV() {
2635
- ivec2 iTexSize = textureSize(backgroundTexture, 0);
2636
- vec2 texSize = vec2(float(iTexSize.x), float(iTexSize.y));
2637
- vec2 position = gl_FragCoord.xy / texSize;
3438
+ vec2 billboardTexture_getTextureUV(vec2 coord) {
3439
+ vec2 position = (coord - 0.5) / background.scale + 0.5;
2638
3440
  return position;
2639
3441
  }
2640
3442
 
2641
3443
  void main(void) {
2642
- vec2 position = billboardTexture_getTextureUV();
3444
+ vec2 position = billboardTexture_getTextureUV(coordinate);
2643
3445
  fragColor = texture(backgroundTexture, position);
2644
3446
  }
2645
3447
  `
2646
3448
  );
2647
3449
  var BackgroundTextureModel = class extends ClipSpace {
3450
+ backgroundTexture = null;
2648
3451
  constructor(device, props) {
2649
3452
  super(device, {
2650
3453
  id: props.id || "background-texture-model",
2651
3454
  source: BACKGROUND_FS_WGSL,
2652
3455
  fs: BACKGROUND_FS,
3456
+ modules: [backgroundModule],
2653
3457
  parameters: {
2654
3458
  depthWriteEnabled: false,
2655
3459
  ...props.blend ? {
@@ -2666,28 +3470,58 @@ var BackgroundTextureModel = class extends ClipSpace {
2666
3470
  if (!props.backgroundTexture) {
2667
3471
  throw new Error("BackgroundTextureModel requires a backgroundTexture prop");
2668
3472
  }
2669
- this.setTexture(props.backgroundTexture);
3473
+ this.setProps(props);
2670
3474
  }
2671
- setTexture(backgroundTexture) {
2672
- this.setBindings({
2673
- backgroundTexture
2674
- });
3475
+ /** Update the background texture */
3476
+ setProps(props) {
3477
+ const { backgroundTexture } = props;
3478
+ if (backgroundTexture) {
3479
+ this.setBindings({ backgroundTexture });
3480
+ if (backgroundTexture.isReady) {
3481
+ const texture = backgroundTexture instanceof DynamicTexture ? backgroundTexture.texture : backgroundTexture;
3482
+ this.backgroundTexture = texture;
3483
+ this.updateScale(texture);
3484
+ } else {
3485
+ backgroundTexture.ready.then((texture) => {
3486
+ this.backgroundTexture = texture;
3487
+ this.updateScale(texture);
3488
+ });
3489
+ }
3490
+ }
2675
3491
  }
2676
3492
  predraw() {
2677
- this.shaderInputs.setProps({});
2678
3493
  super.predraw();
2679
3494
  }
3495
+ updateScale(texture) {
3496
+ if (!texture) {
3497
+ this.shaderInputs.setProps({ background: { scale: [1, 1] } });
3498
+ return;
3499
+ }
3500
+ const [screenWidth, screenHeight] = this.device.getCanvasContext().getDrawingBufferSize();
3501
+ const textureWidth = texture.width;
3502
+ const textureHeight = texture.height;
3503
+ const screenAspect = screenWidth / screenHeight;
3504
+ const textureAspect = textureWidth / textureHeight;
3505
+ let scaleX = 1;
3506
+ let scaleY = 1;
3507
+ if (screenAspect > textureAspect) {
3508
+ scaleY = screenAspect / textureAspect;
3509
+ } else {
3510
+ scaleX = textureAspect / screenAspect;
3511
+ }
3512
+ this.shaderInputs.setProps({ background: { scale: [scaleX, scaleY] } });
3513
+ }
2680
3514
  };
2681
3515
 
2682
3516
  // dist/scenegraph/scenegraph-node.js
2683
- var import_core11 = require("@math.gl/core");
3517
+ var import_core14 = require("@math.gl/core");
2684
3518
  var ScenegraphNode = class {
2685
3519
  id;
2686
- matrix = new import_core11.Matrix4();
3520
+ matrix = new import_core14.Matrix4();
2687
3521
  display = true;
2688
- position = new import_core11.Vector3();
2689
- rotation = new import_core11.Vector3();
2690
- scale = new import_core11.Vector3(1, 1, 1);
3522
+ position = new import_core14.Vector3();
3523
+ rotation = new import_core14.Vector3();
3524
+ scale = new import_core14.Vector3(1, 1, 1);
2691
3525
  userData = {};
2692
3526
  props = {};
2693
3527
  constructor(props = {}) {
@@ -2772,7 +3606,7 @@ var ScenegraphNode = class {
2772
3606
  }
2773
3607
  getCoordinateUniforms(viewMatrix, modelMatrix) {
2774
3608
  modelMatrix = modelMatrix || this.matrix;
2775
- const worldMatrix = new import_core11.Matrix4(viewMatrix).multiplyRight(modelMatrix);
3609
+ const worldMatrix = new import_core14.Matrix4(viewMatrix).multiplyRight(modelMatrix);
2776
3610
  const worldInverse = worldMatrix.invert();
2777
3611
  const worldInverseTranspose = worldInverse.transpose();
2778
3612
  return {
@@ -2824,14 +3658,14 @@ var ScenegraphNode = class {
2824
3658
  };
2825
3659
 
2826
3660
  // dist/scenegraph/group-node.js
2827
- var import_core12 = require("@math.gl/core");
2828
- var import_core13 = require("@luma.gl/core");
3661
+ var import_core15 = require("@math.gl/core");
3662
+ var import_core16 = require("@luma.gl/core");
2829
3663
  var GroupNode = class extends ScenegraphNode {
2830
3664
  children;
2831
3665
  constructor(props = {}) {
2832
3666
  props = Array.isArray(props) ? { children: props } : props;
2833
3667
  const { children = [] } = props;
2834
- import_core13.log.assert(children.every((child) => child instanceof ScenegraphNode), "every child must an instance of ScenegraphNode");
3668
+ import_core16.log.assert(children.every((child) => child instanceof ScenegraphNode), "every child must an instance of ScenegraphNode");
2835
3669
  super(props);
2836
3670
  this.children = children;
2837
3671
  }
@@ -2846,12 +3680,12 @@ var GroupNode = class extends ScenegraphNode {
2846
3680
  return;
2847
3681
  }
2848
3682
  const [min, max] = bounds;
2849
- const center = new import_core12.Vector3(min).add(max).divide([2, 2, 2]);
3683
+ const center = new import_core15.Vector3(min).add(max).divide([2, 2, 2]);
2850
3684
  worldMatrix.transformAsPoint(center, center);
2851
- const halfSize = new import_core12.Vector3(max).subtract(min).divide([2, 2, 2]);
3685
+ const halfSize = new import_core15.Vector3(max).subtract(min).divide([2, 2, 2]);
2852
3686
  worldMatrix.transformAsVector(halfSize, halfSize);
2853
3687
  for (let v = 0; v < 8; v++) {
2854
- const position = new import_core12.Vector3(v & 1 ? -1 : 1, v & 2 ? -1 : 1, v & 4 ? -1 : 1).multiply(halfSize).add(center);
3688
+ const position = new import_core15.Vector3(v & 1 ? -1 : 1, v & 2 ? -1 : 1, v & 4 ? -1 : 1).multiply(halfSize).add(center);
2855
3689
  for (let i = 0; i < 3; i++) {
2856
3690
  result[0][i] = Math.min(result[0][i], position[i]);
2857
3691
  result[1][i] = Math.max(result[1][i], position[i]);
@@ -2891,8 +3725,8 @@ var GroupNode = class extends ScenegraphNode {
2891
3725
  this.children = [];
2892
3726
  return this;
2893
3727
  }
2894
- traverse(visitor, { worldMatrix = new import_core12.Matrix4() } = {}) {
2895
- const modelMatrix = new import_core12.Matrix4(worldMatrix).multiplyRight(this.matrix);
3728
+ traverse(visitor, { worldMatrix = new import_core15.Matrix4() } = {}) {
3729
+ const modelMatrix = new import_core15.Matrix4(worldMatrix).multiplyRight(this.matrix);
2896
3730
  for (const child of this.children) {
2897
3731
  if (child instanceof GroupNode) {
2898
3732
  child.traverse(visitor, { worldMatrix: modelMatrix });
@@ -3674,7 +4508,7 @@ var CylinderGeometry = class extends TruncatedConeGeometry {
3674
4508
  };
3675
4509
 
3676
4510
  // dist/geometries/ico-sphere-geometry.js
3677
- var import_core14 = require("@math.gl/core");
4511
+ var import_core17 = require("@math.gl/core");
3678
4512
  var ICO_POSITIONS = [-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 1, 0, -1, 0, 1, 0, 0];
3679
4513
  var ICO_INDICES = [3, 4, 5, 3, 5, 1, 3, 1, 0, 3, 0, 4, 4, 0, 2, 4, 2, 5, 2, 0, 1, 5, 2, 1];
3680
4514
  var IcoSphereGeometry = class extends Geometry {
@@ -3772,7 +4606,7 @@ function tesselateIcosaHedron(props) {
3772
4606
  const u3 = 1 - phi3 / PI2;
3773
4607
  const vec1 = [x3 - x2, y3 - y2, z3 - z2];
3774
4608
  const vec2 = [x1 - x2, y1 - y2, z1 - z2];
3775
- const normal = new import_core14.Vector3(vec1).cross(vec2).normalize();
4609
+ const normal = new import_core17.Vector3(vec1).cross(vec2).normalize();
3776
4610
  let newIndex;
3777
4611
  if ((u1 === 0 || u2 === 0 || u3 === 0) && (u1 === 0 || u1 > 0.5) && (u2 === 0 || u2 > 0.5) && (u3 === 0 || u3 > 0.5)) {
3778
4612
  positions.push(positions[in1 + 0], positions[in1 + 1], positions[in1 + 2]);
@@ -4032,17 +4866,45 @@ function fract(n) {
4032
4866
  return n - Math.floor(n);
4033
4867
  }
4034
4868
 
4869
+ // dist/application-utils/load-file.js
4870
+ var pathPrefix = "";
4871
+ function setPathPrefix(prefix) {
4872
+ pathPrefix = prefix;
4873
+ }
4874
+ async function loadImageBitmap(url, opts) {
4875
+ const image = new Image();
4876
+ image.crossOrigin = (opts == null ? void 0 : opts.crossOrigin) || "anonymous";
4877
+ image.src = url.startsWith("http") ? url : pathPrefix + url;
4878
+ await image.decode();
4879
+ return opts ? await createImageBitmap(image, opts) : await createImageBitmap(image);
4880
+ }
4881
+ async function loadImage(url, opts) {
4882
+ return await new Promise((resolve, reject) => {
4883
+ try {
4884
+ const image = new Image();
4885
+ image.onload = () => resolve(image);
4886
+ image.onerror = () => reject(new Error(`Could not load image ${url}.`));
4887
+ image.crossOrigin = (opts == null ? void 0 : opts.crossOrigin) || "anonymous";
4888
+ image.src = url.startsWith("http") ? url : pathPrefix + url;
4889
+ } catch (error) {
4890
+ reject(error);
4891
+ }
4892
+ });
4893
+ }
4894
+
4035
4895
  // dist/passes/shader-pass-renderer.js
4036
- var import_shadertools5 = require("@luma.gl/shadertools");
4896
+ var import_shadertools6 = require("@luma.gl/shadertools");
4037
4897
 
4038
4898
  // dist/compute/swap.js
4039
- var import_core15 = require("@luma.gl/core");
4899
+ var import_core18 = require("@luma.gl/core");
4040
4900
  var Swap = class {
4901
+ id;
4041
4902
  /** The current resource - usually the source for renders or computations */
4042
4903
  current;
4043
4904
  /** The next resource - usually the target/destination for transforms / computations */
4044
4905
  next;
4045
4906
  constructor(props) {
4907
+ this.id = props.id || "swap";
4046
4908
  this.current = props.current;
4047
4909
  this.next = props.next;
4048
4910
  }
@@ -4063,18 +4925,21 @@ var SwapFramebuffers = class extends Swap {
4063
4925
  constructor(device, props) {
4064
4926
  var _a, _b;
4065
4927
  props = { ...props };
4928
+ const { width = 1, height = 1 } = props;
4066
4929
  let colorAttachments = (_a = props.colorAttachments) == null ? void 0 : _a.map((colorAttachment) => typeof colorAttachment !== "string" ? colorAttachment : device.createTexture({
4930
+ id: `${props.id}-texture-0`,
4067
4931
  format: colorAttachment,
4068
- usage: import_core15.Texture.SAMPLE | import_core15.Texture.RENDER | import_core15.Texture.COPY_SRC | import_core15.Texture.COPY_DST,
4069
- width: 1,
4070
- height: 1
4932
+ usage: import_core18.Texture.SAMPLE | import_core18.Texture.RENDER | import_core18.Texture.COPY_SRC | import_core18.Texture.COPY_DST,
4933
+ width,
4934
+ height
4071
4935
  }));
4072
4936
  const current = device.createFramebuffer({ ...props, colorAttachments });
4073
4937
  colorAttachments = (_b = props.colorAttachments) == null ? void 0 : _b.map((colorAttachment) => typeof colorAttachment !== "string" ? colorAttachment : device.createTexture({
4938
+ id: `${props.id}-texture-1`,
4074
4939
  format: colorAttachment,
4075
- usage: import_core15.Texture.TEXTURE | import_core15.Texture.COPY_SRC | import_core15.Texture.COPY_DST | import_core15.Texture.RENDER_ATTACHMENT,
4076
- width: 1,
4077
- height: 1
4940
+ usage: import_core18.Texture.SAMPLE | import_core18.Texture.RENDER | import_core18.Texture.COPY_SRC | import_core18.Texture.COPY_DST,
4941
+ width,
4942
+ height
4078
4943
  }));
4079
4944
  const next = device.createFramebuffer({ ...props, colorAttachments });
4080
4945
  super({ current, next });
@@ -4138,19 +5003,23 @@ function getFilterShaderWGSL(func) {
4138
5003
  `// Binding 0:1 is reserved for shader passes
4139
5004
  // @group(0) @binding(0) var<uniform> brightnessContrast : brightnessContrastUniforms;
4140
5005
  @group(0) @binding(1) var texture: texture_2d<f32>;
4141
- @group(0) @binding(2) var sampler: sampler;
5006
+ @group(0) @binding(2) var textureSampler: sampler;
4142
5007
 
4143
- struct FragmentInputs {
4144
- @location(0) fragUV: vec2f,
4145
- @location(1) fragPosition: vec4f,
4146
- @location(2) fragCoordinate: vec4f
4147
- };
5008
+ // This needs to be aligned with
5009
+ // struct FragmentInputs {
5010
+ // @location(0) fragUV: vec2f,
5011
+ // @location(1) fragPosition: vec4f,
5012
+ // @location(2) fragCoordinate: vec4f
5013
+ // };
4148
5014
 
4149
5015
  @fragment
4150
5016
  fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
4151
- let texSize = textureDimensions(texture, 0);
4152
- var fragColor = textureSample(texture, sampler, fragUV);
4153
- fragColor = ${func}(gl_FragColor, texSize, texCoord);
5017
+ let fragUV = inputs.uv;
5018
+ let fragCoordinate = inputs.coordinate;
5019
+ let texSize = vec2f(textureDimensions(texture, 0));
5020
+
5021
+ var fragColor = textureSample(texture, textureSampler, fragUV);
5022
+ fragColor = ${func}(fragColor, texSize, fragCoordinate);
4154
5023
  return fragColor;
4155
5024
  }
4156
5025
  `
@@ -4172,9 +5041,9 @@ struct FragmentInputs = {
4172
5041
 
4173
5042
  @fragment
4174
5043
  fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
4175
- let texSize = textureDimensions(texture, 0);
5044
+ let texSize = vec2f(textureDimensions(texture, 0));
4176
5045
  var fragColor = textureSample(texture, sampler, fragUV);
4177
- fragColor = ${func}(gl_FragColor, texSize, texCoord);
5046
+ fragColor = ${func}(fragColor, texSize, texCoord);
4178
5047
  return fragColor;
4179
5048
  }
4180
5049
  `
@@ -4239,7 +5108,7 @@ var ShaderPassRenderer = class {
4239
5108
  textureModel;
4240
5109
  constructor(device, props) {
4241
5110
  this.device = device;
4242
- props.shaderPasses.map((shaderPass) => (0, import_shadertools5.initializeShaderModule)(shaderPass));
5111
+ props.shaderPasses.map((shaderPass) => (0, import_shadertools6.initializeShaderModule)(shaderPass));
4243
5112
  const modules = props.shaderPasses.reduce((object, shaderPass) => ({ ...object, [shaderPass.name]: shaderPass }), {});
4244
5113
  this.shaderInputs = props.shaderInputs || new ShaderInputs(modules);
4245
5114
  const size = device.getCanvasContext().getDrawingBufferSize();
@@ -4270,12 +5139,10 @@ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4<f32> {
4270
5139
 
4271
5140
  uniform sampler2D sourceTexture;
4272
5141
  in vec2 uv;
4273
- in vec2 coordinate;
4274
5142
  out vec4 fragColor;
4275
5143
 
4276
5144
  void main() {
4277
- vec2 texCoord = coordinate;
4278
- fragColor = texture(sourceTexture, coordinate);
5145
+ fragColor = texture(sourceTexture, uv);
4279
5146
  }
4280
5147
  `
4281
5148
  )
@@ -4289,9 +5156,11 @@ void main() {
4289
5156
  }
4290
5157
  this.swapFramebuffers.destroy();
4291
5158
  this.clipSpace.destroy();
5159
+ this.textureModel.destroy();
4292
5160
  }
4293
- resize(width, height) {
4294
- this.swapFramebuffers.resize({ width, height });
5161
+ resize(size) {
5162
+ size ||= this.device.getCanvasContext().getDrawingBufferSize();
5163
+ this.swapFramebuffers.resize({ width: size[0], height: size[1] });
4295
5164
  }
4296
5165
  renderToScreen(options) {
4297
5166
  const outputTexture = this.renderToTexture(options);
@@ -4302,7 +5171,7 @@ void main() {
4302
5171
  const renderPass = this.device.beginRenderPass({
4303
5172
  id: "shader-pass-renderer-to-screen",
4304
5173
  framebuffer,
4305
- clearColor: [0, 0, 0, 1],
5174
+ // clearColor: [1, 1, 0, 1],
4306
5175
  clearDepth: 1
4307
5176
  });
4308
5177
  this.clipSpace.setBindings({ sourceTexture: outputTexture });
@@ -4318,14 +5187,14 @@ void main() {
4318
5187
  if (!sourceTexture.isReady) {
4319
5188
  return null;
4320
5189
  }
4321
- this.textureModel.destroy();
4322
- this.textureModel = new BackgroundTextureModel(this.device, {
4323
- backgroundTexture: sourceTexture
4324
- });
5190
+ if (this.passRenderers.length === 0) {
5191
+ return sourceTexture.texture;
5192
+ }
5193
+ this.textureModel.setProps({ backgroundTexture: sourceTexture });
4325
5194
  const clearTexturePass = this.device.beginRenderPass({
4326
5195
  id: "shader-pass-renderer-clear-texture",
4327
5196
  framebuffer: this.swapFramebuffers.current,
4328
- clearColor: [0, 0, 0, 1]
5197
+ clearColor: [1, 0, 0, 1]
4329
5198
  });
4330
5199
  this.textureModel.draw(clearTexturePass);
4331
5200
  clearTexturePass.end();
@@ -4411,218 +5280,6 @@ var SubPassRenderer = class {
4411
5280
  }
4412
5281
  };
4413
5282
 
4414
- // dist/compute/computation.js
4415
- var import_core16 = require("@luma.gl/core");
4416
- var import_shadertools6 = require("@luma.gl/shadertools");
4417
- var import_types2 = require("@math.gl/types");
4418
- var LOG_DRAW_PRIORITY2 = 2;
4419
- var LOG_DRAW_TIMEOUT2 = 1e4;
4420
- var _Computation = class {
4421
- device;
4422
- id;
4423
- pipelineFactory;
4424
- shaderFactory;
4425
- userData = {};
4426
- /** Bindings (textures, samplers, uniform buffers) */
4427
- bindings = {};
4428
- /** The underlying GPU pipeline. */
4429
- pipeline;
4430
- /** Assembled compute shader source */
4431
- source;
4432
- /** the underlying compiled compute shader */
4433
- // @ts-ignore Set in function called from constructor
4434
- shader;
4435
- /** ShaderInputs instance */
4436
- shaderInputs;
4437
- // @ts-ignore Set in function called from constructor
4438
- _uniformStore;
4439
- _pipelineNeedsUpdate = "newly created";
4440
- _getModuleUniforms;
4441
- props;
4442
- _destroyed = false;
4443
- constructor(device, props) {
4444
- var _a, _b, _c;
4445
- if (device.type !== "webgpu") {
4446
- throw new Error("Computation is only supported in WebGPU");
4447
- }
4448
- this.props = { ..._Computation.defaultProps, ...props };
4449
- props = this.props;
4450
- this.id = props.id || uid("model");
4451
- this.device = device;
4452
- Object.assign(this.userData, props.userData);
4453
- const moduleMap = Object.fromEntries(((_a = this.props.modules) == null ? void 0 : _a.map((module2) => [module2.name, module2])) || []);
4454
- this.shaderInputs = props.shaderInputs || new ShaderInputs(moduleMap);
4455
- this.setShaderInputs(this.shaderInputs);
4456
- this.props.shaderLayout ||= (0, import_shadertools6.getShaderLayoutFromWGSL)(this.props.source);
4457
- const platformInfo = getPlatformInfo2(device);
4458
- const modules = (((_b = this.props.modules) == null ? void 0 : _b.length) > 0 ? this.props.modules : (_c = this.shaderInputs) == null ? void 0 : _c.getModules()) || [];
4459
- this.pipelineFactory = props.pipelineFactory || PipelineFactory.getDefaultPipelineFactory(this.device);
4460
- this.shaderFactory = props.shaderFactory || ShaderFactory.getDefaultShaderFactory(this.device);
4461
- const { source: source3, getUniforms: getUniforms2 } = this.props.shaderAssembler.assembleWGSLShader({
4462
- platformInfo,
4463
- ...this.props,
4464
- modules
4465
- });
4466
- this.source = source3;
4467
- this._getModuleUniforms = getUniforms2;
4468
- this.pipeline = this._updatePipeline();
4469
- if (props.bindings) {
4470
- this.setBindings(props.bindings);
4471
- }
4472
- Object.seal(this);
4473
- }
4474
- destroy() {
4475
- if (this._destroyed)
4476
- return;
4477
- this.pipelineFactory.release(this.pipeline);
4478
- this.shaderFactory.release(this.shader);
4479
- this._uniformStore.destroy();
4480
- this._destroyed = true;
4481
- }
4482
- // Draw call
4483
- predraw() {
4484
- this.updateShaderInputs();
4485
- }
4486
- dispatch(computePass, x, y, z) {
4487
- try {
4488
- this._logDrawCallStart();
4489
- this.pipeline = this._updatePipeline();
4490
- this.pipeline.setBindings(this.bindings);
4491
- computePass.setPipeline(this.pipeline);
4492
- computePass.setBindings([]);
4493
- computePass.dispatch(x, y, z);
4494
- } finally {
4495
- this._logDrawCallEnd();
4496
- }
4497
- }
4498
- // Update fixed fields (can trigger pipeline rebuild)
4499
- // Update dynamic fields
4500
- /**
4501
- * Updates the vertex count (used in draw calls)
4502
- * @note Any attributes with stepMode=vertex need to be at least this big
4503
- */
4504
- setVertexCount(vertexCount) {
4505
- }
4506
- /**
4507
- * Updates the instance count (used in draw calls)
4508
- * @note Any attributes with stepMode=instance need to be at least this big
4509
- */
4510
- setInstanceCount(instanceCount) {
4511
- }
4512
- setShaderInputs(shaderInputs) {
4513
- this.shaderInputs = shaderInputs;
4514
- this._uniformStore = new import_core16.UniformStore(this.shaderInputs.modules);
4515
- for (const moduleName of Object.keys(this.shaderInputs.modules)) {
4516
- const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
4517
- this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
4518
- }
4519
- }
4520
- /**
4521
- * Updates shader module settings (which results in uniforms being set)
4522
- */
4523
- setShaderModuleProps(props) {
4524
- const uniforms = this._getModuleUniforms(props);
4525
- const keys = Object.keys(uniforms).filter((k) => {
4526
- const uniform = uniforms[k];
4527
- return !(0, import_types2.isNumericArray)(uniform) && typeof uniform !== "number" && typeof uniform !== "boolean";
4528
- });
4529
- const bindings = {};
4530
- for (const k of keys) {
4531
- bindings[k] = uniforms[k];
4532
- delete uniforms[k];
4533
- }
4534
- }
4535
- updateShaderInputs() {
4536
- this._uniformStore.setUniforms(this.shaderInputs.getUniformValues());
4537
- }
4538
- /**
4539
- * Sets bindings (textures, samplers, uniform buffers)
4540
- */
4541
- setBindings(bindings) {
4542
- Object.assign(this.bindings, bindings);
4543
- }
4544
- _setPipelineNeedsUpdate(reason) {
4545
- this._pipelineNeedsUpdate = this._pipelineNeedsUpdate || reason;
4546
- }
4547
- _updatePipeline() {
4548
- if (this._pipelineNeedsUpdate) {
4549
- let prevShader = null;
4550
- if (this.pipeline) {
4551
- import_core16.log.log(1, `Model ${this.id}: Recreating pipeline because "${this._pipelineNeedsUpdate}".`)();
4552
- prevShader = this.shader;
4553
- }
4554
- this._pipelineNeedsUpdate = false;
4555
- this.shader = this.shaderFactory.createShader({
4556
- id: `${this.id}-fragment`,
4557
- stage: "compute",
4558
- source: this.source,
4559
- debugShaders: this.props.debugShaders
4560
- });
4561
- this.pipeline = this.pipelineFactory.createComputePipeline({
4562
- ...this.props,
4563
- shader: this.shader
4564
- });
4565
- if (prevShader) {
4566
- this.shaderFactory.release(prevShader);
4567
- }
4568
- }
4569
- return this.pipeline;
4570
- }
4571
- /** Throttle draw call logging */
4572
- _lastLogTime = 0;
4573
- _logOpen = false;
4574
- _logDrawCallStart() {
4575
- const logDrawTimeout = import_core16.log.level > 3 ? 0 : LOG_DRAW_TIMEOUT2;
4576
- if (import_core16.log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
4577
- return;
4578
- }
4579
- this._lastLogTime = Date.now();
4580
- this._logOpen = true;
4581
- import_core16.log.group(LOG_DRAW_PRIORITY2, `>>> DRAWING MODEL ${this.id}`, { collapsed: import_core16.log.level <= 2 })();
4582
- }
4583
- _logDrawCallEnd() {
4584
- if (this._logOpen) {
4585
- const uniformTable = this.shaderInputs.getDebugTable();
4586
- import_core16.log.table(LOG_DRAW_PRIORITY2, uniformTable)();
4587
- import_core16.log.groupEnd(LOG_DRAW_PRIORITY2)();
4588
- this._logOpen = false;
4589
- }
4590
- }
4591
- _drawCount = 0;
4592
- // TODO - fix typing of luma data types
4593
- _getBufferOrConstantValues(attribute, dataType) {
4594
- const TypedArrayConstructor = (0, import_core16.getTypedArrayConstructor)(dataType);
4595
- const typedArray = attribute instanceof import_core16.Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
4596
- return typedArray.toString();
4597
- }
4598
- };
4599
- var Computation = _Computation;
4600
- __publicField(Computation, "defaultProps", {
4601
- ...import_core16.ComputePipeline.defaultProps,
4602
- id: "unnamed",
4603
- handle: void 0,
4604
- userData: {},
4605
- source: "",
4606
- modules: [],
4607
- defines: {},
4608
- bindings: void 0,
4609
- shaderInputs: void 0,
4610
- pipelineFactory: void 0,
4611
- shaderFactory: void 0,
4612
- shaderAssembler: import_shadertools6.ShaderAssembler.getDefaultShaderAssembler(),
4613
- debugShaders: void 0
4614
- });
4615
- function getPlatformInfo2(device) {
4616
- return {
4617
- type: device.type,
4618
- shaderLanguage: device.info.shadingLanguage,
4619
- shaderLanguageVersion: device.info.shadingLanguageVersion,
4620
- gpu: device.info.gpu,
4621
- // HACK - we pretend that the DeviceFeatures is a Set, it has a similar API
4622
- features: device.features
4623
- };
4624
- }
4625
-
4626
5283
  // dist/modules/picking/picking-uniforms.js
4627
5284
  var DEFAULT_HIGHLIGHT_COLOR = [0, 1, 1, 1];
4628
5285
  var INVALID_INDEX = -1;
@@ -5161,4 +5818,7 @@ var LegacyPickingManager = class {
5161
5818
  return [pickX, pickY];
5162
5819
  }
5163
5820
  };
5821
+
5822
+ // dist/index.js
5823
+ var AsyncTexture = DynamicTexture;
5164
5824
  //# sourceMappingURL=index.cjs.map