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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/dist/animation-loop/animation-loop.d.ts.map +1 -1
  2. package/dist/animation-loop/animation-loop.js +3 -0
  3. package/dist/animation-loop/animation-loop.js.map +1 -1
  4. package/dist/compute/computation.d.ts +3 -7
  5. package/dist/compute/computation.d.ts.map +1 -1
  6. package/dist/compute/computation.js +14 -12
  7. package/dist/compute/computation.js.map +1 -1
  8. package/dist/dist.dev.js +1751 -831
  9. package/dist/dist.min.js +296 -148
  10. package/dist/dynamic-texture/dynamic-texture.d.ts +9 -2
  11. package/dist/dynamic-texture/dynamic-texture.d.ts.map +1 -1
  12. package/dist/dynamic-texture/dynamic-texture.js +54 -5
  13. package/dist/dynamic-texture/dynamic-texture.js.map +1 -1
  14. package/dist/dynamic-texture/texture-data.d.ts +4 -1
  15. package/dist/dynamic-texture/texture-data.d.ts.map +1 -1
  16. package/dist/dynamic-texture/texture-data.js +19 -2
  17. package/dist/dynamic-texture/texture-data.js.map +1 -1
  18. package/dist/geometry/gpu-geometry.d.ts.map +1 -1
  19. package/dist/geometry/gpu-geometry.js +8 -3
  20. package/dist/geometry/gpu-geometry.js.map +1 -1
  21. package/dist/index.cjs +1711 -847
  22. package/dist/index.cjs.map +4 -4
  23. package/dist/index.d.ts +12 -3
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +8 -3
  26. package/dist/index.js.map +1 -1
  27. package/dist/material/material-factory.d.ts +73 -0
  28. package/dist/material/material-factory.d.ts.map +1 -0
  29. package/dist/material/material-factory.js +111 -0
  30. package/dist/material/material-factory.js.map +1 -0
  31. package/dist/material/material.d.ts +84 -0
  32. package/dist/material/material.d.ts.map +1 -0
  33. package/dist/material/material.js +176 -0
  34. package/dist/material/material.js.map +1 -0
  35. package/dist/model/model.d.ts +14 -6
  36. package/dist/model/model.d.ts.map +1 -1
  37. package/dist/model/model.js +69 -25
  38. package/dist/model/model.js.map +1 -1
  39. package/dist/model/split-uniforms-and-bindings.d.ts +4 -3
  40. package/dist/model/split-uniforms-and-bindings.d.ts.map +1 -1
  41. package/dist/model/split-uniforms-and-bindings.js +2 -2
  42. package/dist/model/split-uniforms-and-bindings.js.map +1 -1
  43. package/dist/models/directional-light-model.d.ts +7 -0
  44. package/dist/models/directional-light-model.d.ts.map +1 -0
  45. package/dist/models/directional-light-model.js +23 -0
  46. package/dist/models/directional-light-model.js.map +1 -0
  47. package/dist/models/light-model-utils.d.ts +69 -0
  48. package/dist/models/light-model-utils.d.ts.map +1 -0
  49. package/dist/models/light-model-utils.js +395 -0
  50. package/dist/models/light-model-utils.js.map +1 -0
  51. package/dist/models/point-light-model.d.ts +7 -0
  52. package/dist/models/point-light-model.d.ts.map +1 -0
  53. package/dist/models/point-light-model.js +22 -0
  54. package/dist/models/point-light-model.js.map +1 -0
  55. package/dist/models/spot-light-model.d.ts +7 -0
  56. package/dist/models/spot-light-model.d.ts.map +1 -0
  57. package/dist/models/spot-light-model.js +23 -0
  58. package/dist/models/spot-light-model.js.map +1 -0
  59. package/dist/modules/picking/color-picking.d.ts +5 -9
  60. package/dist/modules/picking/color-picking.d.ts.map +1 -1
  61. package/dist/modules/picking/color-picking.js +122 -115
  62. package/dist/modules/picking/color-picking.js.map +1 -1
  63. package/dist/modules/picking/index-picking.d.ts +2 -2
  64. package/dist/modules/picking/index-picking.d.ts.map +1 -1
  65. package/dist/modules/picking/index-picking.js +36 -10
  66. package/dist/modules/picking/index-picking.js.map +1 -1
  67. package/dist/modules/picking/legacy-color-picking.d.ts +26 -0
  68. package/dist/modules/picking/legacy-color-picking.d.ts.map +1 -0
  69. package/dist/modules/picking/legacy-color-picking.js +7 -0
  70. package/dist/modules/picking/legacy-color-picking.js.map +1 -0
  71. package/dist/modules/picking/picking-manager.d.ts +29 -3
  72. package/dist/modules/picking/picking-manager.d.ts.map +1 -1
  73. package/dist/modules/picking/picking-manager.js +188 -41
  74. package/dist/modules/picking/picking-manager.js.map +1 -1
  75. package/dist/modules/picking/picking-uniforms.d.ts +12 -11
  76. package/dist/modules/picking/picking-uniforms.d.ts.map +1 -1
  77. package/dist/modules/picking/picking-uniforms.js +26 -13
  78. package/dist/modules/picking/picking-uniforms.js.map +1 -1
  79. package/dist/modules/picking/picking.d.ts +25 -0
  80. package/dist/modules/picking/picking.d.ts.map +1 -0
  81. package/dist/modules/picking/picking.js +18 -0
  82. package/dist/modules/picking/picking.js.map +1 -0
  83. package/dist/shader-inputs.d.ts +9 -7
  84. package/dist/shader-inputs.d.ts.map +1 -1
  85. package/dist/shader-inputs.js +84 -4
  86. package/dist/shader-inputs.js.map +1 -1
  87. package/dist/utils/shader-module-utils.d.ts +7 -0
  88. package/dist/utils/shader-module-utils.d.ts.map +1 -0
  89. package/dist/utils/shader-module-utils.js +46 -0
  90. package/dist/utils/shader-module-utils.js.map +1 -0
  91. package/package.json +4 -4
  92. package/src/animation-loop/animation-loop.ts +3 -0
  93. package/src/compute/computation.ts +31 -17
  94. package/src/dynamic-texture/dynamic-texture.ts +79 -7
  95. package/src/dynamic-texture/texture-data.ts +25 -4
  96. package/src/geometry/gpu-geometry.ts +8 -3
  97. package/src/index.ts +29 -4
  98. package/src/material/material-factory.ts +157 -0
  99. package/src/material/material.ts +254 -0
  100. package/src/model/model.ts +108 -40
  101. package/src/model/split-uniforms-and-bindings.ts +8 -6
  102. package/src/models/directional-light-model.ts +32 -0
  103. package/src/models/light-model-utils.ts +587 -0
  104. package/src/models/point-light-model.ts +31 -0
  105. package/src/models/spot-light-model.ts +32 -0
  106. package/src/modules/picking/color-picking.ts +123 -122
  107. package/src/modules/picking/index-picking.ts +36 -10
  108. package/src/modules/picking/legacy-color-picking.ts +8 -0
  109. package/src/modules/picking/picking-manager.ts +252 -50
  110. package/src/modules/picking/picking-uniforms.ts +38 -23
  111. package/src/modules/picking/picking.ts +22 -0
  112. package/src/shader-inputs.ts +165 -15
  113. package/src/utils/shader-module-utils.ts +65 -0
  114. package/dist/factories/pipeline-factory.d.ts +0 -39
  115. package/dist/factories/pipeline-factory.d.ts.map +0 -1
  116. package/dist/factories/pipeline-factory.js +0 -216
  117. package/dist/factories/pipeline-factory.js.map +0 -1
  118. package/dist/factories/shader-factory.d.ts +0 -19
  119. package/dist/factories/shader-factory.d.ts.map +0 -1
  120. package/dist/factories/shader-factory.js +0 -83
  121. package/dist/factories/shader-factory.js.map +0 -1
  122. package/dist/types.d.ts +0 -7
  123. package/dist/types.d.ts.map +0 -1
  124. package/dist/types.js +0 -5
  125. package/dist/types.js.map +0 -1
  126. package/src/factories/pipeline-factory.ts +0 -266
  127. package/src/factories/shader-factory.ts +0 -101
  128. package/src/types.ts +0 -11
package/dist/dist.dev.js CHANGED
@@ -70,6 +70,7 @@ var __exports__ = (() => {
70
70
  ConeGeometry: () => ConeGeometry,
71
71
  CubeGeometry: () => CubeGeometry,
72
72
  CylinderGeometry: () => CylinderGeometry,
73
+ DirectionalLightModel: () => DirectionalLightModel,
73
74
  DynamicTexture: () => DynamicTexture,
74
75
  GPUGeometry: () => GPUGeometry,
75
76
  Geometry: () => Geometry,
@@ -77,16 +78,18 @@ var __exports__ = (() => {
77
78
  IcoSphereGeometry: () => IcoSphereGeometry,
78
79
  KeyFrames: () => KeyFrames,
79
80
  LegacyPickingManager: () => LegacyPickingManager,
81
+ Material: () => Material,
82
+ MaterialFactory: () => MaterialFactory,
80
83
  Model: () => Model,
81
84
  ModelNode: () => ModelNode,
82
85
  PickingManager: () => PickingManager,
83
- PipelineFactory: () => PipelineFactory,
84
86
  PlaneGeometry: () => PlaneGeometry,
87
+ PointLightModel: () => PointLightModel,
85
88
  ScenegraphNode: () => ScenegraphNode,
86
- ShaderFactory: () => ShaderFactory,
87
89
  ShaderInputs: () => ShaderInputs,
88
90
  ShaderPassRenderer: () => ShaderPassRenderer,
89
91
  SphereGeometry: () => SphereGeometry,
92
+ SpotLightModel: () => SpotLightModel,
90
93
  Swap: () => Swap,
91
94
  SwapBuffers: () => SwapBuffers,
92
95
  SwapFramebuffers: () => SwapFramebuffers,
@@ -94,14 +97,19 @@ var __exports__ = (() => {
94
97
  Timeline: () => Timeline,
95
98
  TruncatedConeGeometry: () => TruncatedConeGeometry,
96
99
  cancelAnimationFramePolyfill: () => cancelAnimationFramePolyfill,
97
- colorPicking: () => picking2,
98
- indexPicking: () => picking,
100
+ colorPicking: () => picking,
101
+ indexPicking: () => picking2,
102
+ legacyColorPicking: () => legacyColorPicking,
99
103
  loadImage: () => loadImage,
100
104
  loadImageBitmap: () => loadImageBitmap,
101
105
  makeAnimationLoop: () => makeAnimationLoop,
102
106
  makeRandomGenerator: () => makeRandomGenerator,
107
+ picking: () => picking3,
103
108
  requestAnimationFramePolyfill: () => requestAnimationFramePolyfill,
104
- setPathPrefix: () => setPathPrefix
109
+ resolvePickingBackend: () => resolvePickingBackend,
110
+ resolvePickingMode: () => resolvePickingMode,
111
+ setPathPrefix: () => setPathPrefix,
112
+ supportsIndexPicking: () => supportsIndexPicking
105
113
  });
106
114
  __reExport(bundle_exports, __toESM(require_core(), 1));
107
115
 
@@ -579,6 +587,9 @@ var __exports__ = (() => {
579
587
  this._initialized = true;
580
588
  await this._initDevice();
581
589
  this._initialize();
590
+ if (!this._running) {
591
+ return null;
592
+ }
582
593
  await this.props.onInitialize(this._getAnimationProps());
583
594
  }
584
595
  if (!this._running) {
@@ -966,7 +977,7 @@ var __exports__ = (() => {
966
977
  }
967
978
 
968
979
  // src/model/model.ts
969
- var import_core10 = __toESM(require_core(), 1);
980
+ var import_core8 = __toESM(require_core(), 1);
970
981
  var import_shadertools2 = __toESM(require_shadertools(), 1);
971
982
 
972
983
  // src/geometry/gpu-geometry.ts
@@ -1069,300 +1080,19 @@ var __exports__ = (() => {
1069
1080
  id: `${attributeName}-buffer`
1070
1081
  });
1071
1082
  const { value, size, normalized } = attribute;
1072
- bufferLayout.push({ name, format: (0, import_core3.getVertexFormatFromAttribute)(value, size, normalized) });
1083
+ if (size === void 0) {
1084
+ throw new Error(`Attribute ${attributeName} is missing a size`);
1085
+ }
1086
+ bufferLayout.push({
1087
+ name,
1088
+ format: import_core3.vertexFormatDecoder.getVertexFormatFromAttribute(value, size, normalized)
1089
+ });
1073
1090
  }
1074
1091
  }
1075
1092
  const vertexCount = geometry._calculateVertexCount(geometry.attributes, geometry.indices);
1076
1093
  return { attributes, bufferLayout, vertexCount };
1077
1094
  }
1078
1095
 
1079
- // src/factories/pipeline-factory.ts
1080
- var import_core4 = __toESM(require_core(), 1);
1081
- var _PipelineFactory = class {
1082
- /** Get the singleton default pipeline factory for the specified device */
1083
- static getDefaultPipelineFactory(device) {
1084
- const moduleData = device.getModuleData("@luma.gl/engine");
1085
- moduleData.defaultPipelineFactory ||= new _PipelineFactory(device);
1086
- return moduleData.defaultPipelineFactory;
1087
- }
1088
- device;
1089
- _hashCounter = 0;
1090
- _hashes = {};
1091
- _renderPipelineCache = {};
1092
- _computePipelineCache = {};
1093
- _sharedRenderPipelineCache = {};
1094
- get [Symbol.toStringTag]() {
1095
- return "PipelineFactory";
1096
- }
1097
- toString() {
1098
- return `PipelineFactory(${this.device.id})`;
1099
- }
1100
- constructor(device) {
1101
- this.device = device;
1102
- }
1103
- /** Return a RenderPipeline matching supplied props. Reuses an equivalent pipeline if already created. */
1104
- createRenderPipeline(props) {
1105
- if (!this.device.props._cachePipelines) {
1106
- return this.device.createRenderPipeline(props);
1107
- }
1108
- const allProps = { ...import_core4.RenderPipeline.defaultProps, ...props };
1109
- const cache = this._renderPipelineCache;
1110
- const hash = this._hashRenderPipeline(allProps);
1111
- let pipeline = cache[hash]?.resource;
1112
- if (!pipeline) {
1113
- const sharedRenderPipeline = this.device.type === "webgl" && this.device.props._sharePipelines ? this.createSharedRenderPipeline(allProps) : void 0;
1114
- pipeline = this.device.createRenderPipeline({
1115
- ...allProps,
1116
- id: allProps.id ? `${allProps.id}-cached` : uid("unnamed-cached"),
1117
- _sharedRenderPipeline: sharedRenderPipeline
1118
- });
1119
- pipeline.hash = hash;
1120
- cache[hash] = { resource: pipeline, useCount: 1 };
1121
- if (this.device.props.debugFactories) {
1122
- import_core4.log.log(3, `${this}: ${pipeline} created, count=${cache[hash].useCount}`)();
1123
- }
1124
- } else {
1125
- cache[hash].useCount++;
1126
- if (this.device.props.debugFactories) {
1127
- import_core4.log.log(
1128
- 3,
1129
- `${this}: ${cache[hash].resource} reused, count=${cache[hash].useCount}, (id=${props.id})`
1130
- )();
1131
- }
1132
- }
1133
- return pipeline;
1134
- }
1135
- /** Return a ComputePipeline matching supplied props. Reuses an equivalent pipeline if already created. */
1136
- createComputePipeline(props) {
1137
- if (!this.device.props._cachePipelines) {
1138
- return this.device.createComputePipeline(props);
1139
- }
1140
- const allProps = { ...import_core4.ComputePipeline.defaultProps, ...props };
1141
- const cache = this._computePipelineCache;
1142
- const hash = this._hashComputePipeline(allProps);
1143
- let pipeline = cache[hash]?.resource;
1144
- if (!pipeline) {
1145
- pipeline = this.device.createComputePipeline({
1146
- ...allProps,
1147
- id: allProps.id ? `${allProps.id}-cached` : void 0
1148
- });
1149
- pipeline.hash = hash;
1150
- cache[hash] = { resource: pipeline, useCount: 1 };
1151
- if (this.device.props.debugFactories) {
1152
- import_core4.log.log(3, `${this}: ${pipeline} created, count=${cache[hash].useCount}`)();
1153
- }
1154
- } else {
1155
- cache[hash].useCount++;
1156
- if (this.device.props.debugFactories) {
1157
- import_core4.log.log(
1158
- 3,
1159
- `${this}: ${cache[hash].resource} reused, count=${cache[hash].useCount}, (id=${props.id})`
1160
- )();
1161
- }
1162
- }
1163
- return pipeline;
1164
- }
1165
- release(pipeline) {
1166
- if (!this.device.props._cachePipelines) {
1167
- pipeline.destroy();
1168
- return;
1169
- }
1170
- const cache = this._getCache(pipeline);
1171
- const hash = pipeline.hash;
1172
- cache[hash].useCount--;
1173
- if (cache[hash].useCount === 0) {
1174
- this._destroyPipeline(pipeline);
1175
- if (this.device.props.debugFactories) {
1176
- import_core4.log.log(3, `${this}: ${pipeline} released and destroyed`)();
1177
- }
1178
- } else if (cache[hash].useCount < 0) {
1179
- import_core4.log.error(`${this}: ${pipeline} released, useCount < 0, resetting`)();
1180
- cache[hash].useCount = 0;
1181
- } else if (this.device.props.debugFactories) {
1182
- import_core4.log.log(3, `${this}: ${pipeline} released, count=${cache[hash].useCount}`)();
1183
- }
1184
- }
1185
- createSharedRenderPipeline(props) {
1186
- const sharedPipelineHash = this._hashSharedRenderPipeline(props);
1187
- let sharedCacheItem = this._sharedRenderPipelineCache[sharedPipelineHash];
1188
- if (!sharedCacheItem) {
1189
- const sharedRenderPipeline = this.device._createSharedRenderPipelineWebGL(props);
1190
- sharedCacheItem = { resource: sharedRenderPipeline, useCount: 0 };
1191
- this._sharedRenderPipelineCache[sharedPipelineHash] = sharedCacheItem;
1192
- }
1193
- sharedCacheItem.useCount++;
1194
- return sharedCacheItem.resource;
1195
- }
1196
- releaseSharedRenderPipeline(pipeline) {
1197
- if (!pipeline.sharedRenderPipeline) {
1198
- return;
1199
- }
1200
- const sharedPipelineHash = this._hashSharedRenderPipeline(pipeline.sharedRenderPipeline.props);
1201
- const sharedCacheItem = this._sharedRenderPipelineCache[sharedPipelineHash];
1202
- if (!sharedCacheItem) {
1203
- return;
1204
- }
1205
- sharedCacheItem.useCount--;
1206
- if (sharedCacheItem.useCount === 0) {
1207
- sharedCacheItem.resource.destroy();
1208
- delete this._sharedRenderPipelineCache[sharedPipelineHash];
1209
- }
1210
- }
1211
- // PRIVATE
1212
- /** Destroy a cached pipeline, removing it from the cache if configured to do so. */
1213
- _destroyPipeline(pipeline) {
1214
- const cache = this._getCache(pipeline);
1215
- if (!this.device.props._destroyPipelines) {
1216
- return false;
1217
- }
1218
- delete cache[pipeline.hash];
1219
- pipeline.destroy();
1220
- if (pipeline instanceof import_core4.RenderPipeline) {
1221
- this.releaseSharedRenderPipeline(pipeline);
1222
- }
1223
- return true;
1224
- }
1225
- /** Get the appropriate cache for the type of pipeline */
1226
- _getCache(pipeline) {
1227
- let cache;
1228
- if (pipeline instanceof import_core4.ComputePipeline) {
1229
- cache = this._computePipelineCache;
1230
- }
1231
- if (pipeline instanceof import_core4.RenderPipeline) {
1232
- cache = this._renderPipelineCache;
1233
- }
1234
- if (!cache) {
1235
- throw new Error(`${this}`);
1236
- }
1237
- if (!cache[pipeline.hash]) {
1238
- throw new Error(`${this}: ${pipeline} matched incorrect entry`);
1239
- }
1240
- return cache;
1241
- }
1242
- /** Calculate a hash based on all the inputs for a compute pipeline */
1243
- _hashComputePipeline(props) {
1244
- const { type } = this.device;
1245
- const shaderHash = this._getHash(props.shader.source);
1246
- return `${type}/C/${shaderHash}`;
1247
- }
1248
- /** Calculate a hash based on all the inputs for a render pipeline */
1249
- _hashRenderPipeline(props) {
1250
- const vsHash = props.vs ? this._getHash(props.vs.source) : 0;
1251
- const fsHash = props.fs ? this._getHash(props.fs.source) : 0;
1252
- const varyingHash = this._getWebGLVaryingHash(props);
1253
- const bufferLayoutHash = this._getHash(JSON.stringify(props.bufferLayout));
1254
- const { type } = this.device;
1255
- switch (type) {
1256
- case "webgl":
1257
- const webglParameterHash = this._getHash(JSON.stringify(props.parameters));
1258
- return `${type}/R/${vsHash}/${fsHash}V${varyingHash}T${props.topology}P${webglParameterHash}BL${bufferLayoutHash}`;
1259
- case "webgpu":
1260
- default:
1261
- const parameterHash = this._getHash(JSON.stringify(props.parameters));
1262
- return `${type}/R/${vsHash}/${fsHash}V${varyingHash}T${props.topology}P${parameterHash}BL${bufferLayoutHash}`;
1263
- }
1264
- }
1265
- _hashSharedRenderPipeline(props) {
1266
- const vsHash = props.vs ? this._getHash(props.vs.source) : 0;
1267
- const fsHash = props.fs ? this._getHash(props.fs.source) : 0;
1268
- const varyingHash = this._getWebGLVaryingHash(props);
1269
- return `webgl/S/${vsHash}/${fsHash}V${varyingHash}`;
1270
- }
1271
- _getHash(key) {
1272
- if (this._hashes[key] === void 0) {
1273
- this._hashes[key] = this._hashCounter++;
1274
- }
1275
- return this._hashes[key];
1276
- }
1277
- _getWebGLVaryingHash(props) {
1278
- const { varyings = [], bufferMode = null } = props;
1279
- return this._getHash(JSON.stringify({ varyings, bufferMode }));
1280
- }
1281
- };
1282
- var PipelineFactory = _PipelineFactory;
1283
- __publicField(PipelineFactory, "defaultProps", { ...import_core4.RenderPipeline.defaultProps });
1284
-
1285
- // src/factories/shader-factory.ts
1286
- var import_core5 = __toESM(require_core(), 1);
1287
- var _ShaderFactory = class {
1288
- /** Returns the default ShaderFactory for the given {@link Device}, creating one if necessary. */
1289
- static getDefaultShaderFactory(device) {
1290
- const moduleData = device.getModuleData("@luma.gl/engine");
1291
- moduleData.defaultShaderFactory ||= new _ShaderFactory(device);
1292
- return moduleData.defaultShaderFactory;
1293
- }
1294
- device;
1295
- _cache = {};
1296
- get [Symbol.toStringTag]() {
1297
- return "ShaderFactory";
1298
- }
1299
- toString() {
1300
- return `${this[Symbol.toStringTag]}(${this.device.id})`;
1301
- }
1302
- /** @internal */
1303
- constructor(device) {
1304
- this.device = device;
1305
- }
1306
- /** Requests a {@link Shader} from the cache, creating a new Shader only if necessary. */
1307
- createShader(props) {
1308
- if (!this.device.props._cacheShaders) {
1309
- return this.device.createShader(props);
1310
- }
1311
- const key = this._hashShader(props);
1312
- let cacheEntry = this._cache[key];
1313
- if (!cacheEntry) {
1314
- const resource = this.device.createShader({
1315
- ...props,
1316
- id: props.id ? `${props.id}-cached` : void 0
1317
- });
1318
- this._cache[key] = cacheEntry = { resource, useCount: 1 };
1319
- if (this.device.props.debugFactories) {
1320
- import_core5.log.log(3, `${this}: Created new shader ${resource.id}`)();
1321
- }
1322
- } else {
1323
- cacheEntry.useCount++;
1324
- if (this.device.props.debugFactories) {
1325
- import_core5.log.log(
1326
- 3,
1327
- `${this}: Reusing shader ${cacheEntry.resource.id} count=${cacheEntry.useCount}`
1328
- )();
1329
- }
1330
- }
1331
- return cacheEntry.resource;
1332
- }
1333
- /** Releases a previously-requested {@link Shader}, destroying it if no users remain. */
1334
- release(shader) {
1335
- if (!this.device.props._cacheShaders) {
1336
- shader.destroy();
1337
- return;
1338
- }
1339
- const key = this._hashShader(shader);
1340
- const cacheEntry = this._cache[key];
1341
- if (cacheEntry) {
1342
- cacheEntry.useCount--;
1343
- if (cacheEntry.useCount === 0) {
1344
- if (this.device.props._destroyShaders) {
1345
- delete this._cache[key];
1346
- cacheEntry.resource.destroy();
1347
- if (this.device.props.debugFactories) {
1348
- import_core5.log.log(3, `${this}: Releasing shader ${shader.id}, destroyed`)();
1349
- }
1350
- }
1351
- } else if (cacheEntry.useCount < 0) {
1352
- throw new Error(`ShaderFactory: Shader ${shader.id} released too many times`);
1353
- } else if (this.device.props.debugFactories) {
1354
- import_core5.log.log(3, `${this}: Releasing shader ${shader.id} count=${cacheEntry.useCount}`)();
1355
- }
1356
- }
1357
- }
1358
- // PRIVATE
1359
- _hashShader(value) {
1360
- return `${value.stage}:${value.source}`;
1361
- }
1362
- };
1363
- var ShaderFactory = _ShaderFactory;
1364
- __publicField(ShaderFactory, "defaultProps", { ...import_core5.Shader.defaultProps });
1365
-
1366
1096
  // src/debug/debug-shader-layout.ts
1367
1097
  function getDebugTableForShaderLayout(layout, name) {
1368
1098
  const table = {};
@@ -1469,7 +1199,7 @@ var __exports__ = (() => {
1469
1199
  }
1470
1200
 
1471
1201
  // src/utils/buffer-layout-helper.ts
1472
- var import_core6 = __toESM(require_core(), 1);
1202
+ var import_core4 = __toESM(require_core(), 1);
1473
1203
  var BufferLayoutHelper = class {
1474
1204
  bufferLayouts;
1475
1205
  constructor(bufferLayouts) {
@@ -1497,7 +1227,7 @@ var __exports__ = (() => {
1497
1227
  getBufferIndex(bufferName) {
1498
1228
  const bufferIndex = this.bufferLayouts.findIndex((layout) => layout.name === bufferName);
1499
1229
  if (bufferIndex === -1) {
1500
- import_core6.log.warn(`BufferLayout: Missing buffer for "${bufferName}".`)();
1230
+ import_core4.log.warn(`BufferLayout: Missing buffer for "${bufferName}".`)();
1501
1231
  }
1502
1232
  return bufferIndex;
1503
1233
  }
@@ -1529,8 +1259,51 @@ var __exports__ = (() => {
1529
1259
  return sortedLayout;
1530
1260
  }
1531
1261
 
1262
+ // src/utils/shader-module-utils.ts
1263
+ function mergeShaderModuleBindingsIntoLayout(shaderLayout, modules) {
1264
+ if (!shaderLayout || !modules.some((module) => module.bindingLayout?.length)) {
1265
+ return shaderLayout;
1266
+ }
1267
+ const mergedLayout = {
1268
+ ...shaderLayout,
1269
+ bindings: shaderLayout.bindings.map((binding) => ({ ...binding }))
1270
+ };
1271
+ if ("attributes" in (shaderLayout || {})) {
1272
+ mergedLayout.attributes = shaderLayout?.attributes || [];
1273
+ }
1274
+ for (const module of modules) {
1275
+ for (const bindingLayout of module.bindingLayout || []) {
1276
+ for (const relatedBindingName of getRelatedBindingNames(bindingLayout.name)) {
1277
+ const binding = mergedLayout.bindings.find(
1278
+ (candidate) => candidate.name === relatedBindingName
1279
+ );
1280
+ if (binding?.group === 0) {
1281
+ binding.group = bindingLayout.group;
1282
+ }
1283
+ }
1284
+ }
1285
+ }
1286
+ return mergedLayout;
1287
+ }
1288
+ function shaderModuleHasUniforms(module) {
1289
+ return Boolean(module.uniformTypes && !isObjectEmpty(module.uniformTypes));
1290
+ }
1291
+ function getRelatedBindingNames(bindingName) {
1292
+ const bindingNames = /* @__PURE__ */ new Set([bindingName, `${bindingName}Uniforms`]);
1293
+ if (!bindingName.endsWith("Uniforms")) {
1294
+ bindingNames.add(`${bindingName}Sampler`);
1295
+ }
1296
+ return [...bindingNames];
1297
+ }
1298
+ function isObjectEmpty(obj) {
1299
+ for (const key in obj) {
1300
+ return false;
1301
+ }
1302
+ return true;
1303
+ }
1304
+
1532
1305
  // src/shader-inputs.ts
1533
- var import_core7 = __toESM(require_core(), 1);
1306
+ var import_core5 = __toESM(require_core(), 1);
1534
1307
  var import_shadertools = __toESM(require_shadertools(), 1);
1535
1308
 
1536
1309
  // ../../node_modules/@math.gl/types/dist/is-array.js
@@ -1551,11 +1324,11 @@ var __exports__ = (() => {
1551
1324
  function isUniformValue(value) {
1552
1325
  return isNumericArray(value) || typeof value === "number" || typeof value === "boolean";
1553
1326
  }
1554
- function splitUniformsAndBindings(uniforms) {
1327
+ function splitUniformsAndBindings(uniforms, uniformTypes2 = {}) {
1555
1328
  const result = { bindings: {}, uniforms: {} };
1556
1329
  Object.keys(uniforms).forEach((name) => {
1557
1330
  const uniform = uniforms[name];
1558
- if (isUniformValue(uniform)) {
1331
+ if (Object.prototype.hasOwnProperty.call(uniformTypes2, name) || isUniformValue(uniform)) {
1559
1332
  result.uniforms[name] = uniform;
1560
1333
  } else {
1561
1334
  result.bindings[name] = uniform;
@@ -1588,19 +1361,22 @@ var __exports__ = (() => {
1588
1361
  constructor(modules, options) {
1589
1362
  Object.assign(this.options, options);
1590
1363
  const resolvedModules = (0, import_shadertools.getShaderModuleDependencies)(
1591
- Object.values(modules).filter((module) => module.dependencies)
1364
+ Object.values(modules).filter(isShaderInputsModuleWithDependencies)
1592
1365
  );
1593
1366
  for (const resolvedModule of resolvedModules) {
1594
1367
  modules[resolvedModule.name] = resolvedModule;
1595
1368
  }
1596
- import_core7.log.log(1, "Creating ShaderInputs with modules", Object.keys(modules))();
1369
+ import_core5.log.log(1, "Creating ShaderInputs with modules", Object.keys(modules))();
1597
1370
  this.modules = modules;
1598
1371
  this.moduleUniforms = {};
1599
1372
  this.moduleBindings = {};
1600
1373
  for (const [name, module] of Object.entries(modules)) {
1374
+ if (!module) {
1375
+ continue;
1376
+ }
1601
1377
  this._addModule(module);
1602
1378
  if (module.name && name !== module.name && !this.options.disableWarnings) {
1603
- import_core7.log.warn(`Module name: ${name} vs ${module.name}`)();
1379
+ import_core5.log.warn(`Module name: ${name} vs ${module.name}`)();
1604
1380
  }
1605
1381
  }
1606
1382
  }
@@ -1617,15 +1393,22 @@ var __exports__ = (() => {
1617
1393
  const module = this.modules[moduleName];
1618
1394
  if (!module) {
1619
1395
  if (!this.options.disableWarnings) {
1620
- import_core7.log.warn(`Module ${name} not found`)();
1396
+ import_core5.log.warn(`Module ${name} not found`)();
1621
1397
  }
1622
1398
  continue;
1623
1399
  }
1624
1400
  const oldUniforms = this.moduleUniforms[moduleName];
1625
1401
  const oldBindings = this.moduleBindings[moduleName];
1626
1402
  const uniformsAndBindings = module.getUniforms?.(moduleProps, oldUniforms) || moduleProps;
1627
- const { uniforms, bindings } = splitUniformsAndBindings(uniformsAndBindings);
1628
- this.moduleUniforms[moduleName] = { ...oldUniforms, ...uniforms };
1403
+ const { uniforms, bindings } = splitUniformsAndBindings(
1404
+ uniformsAndBindings,
1405
+ module.uniformTypes
1406
+ );
1407
+ this.moduleUniforms[moduleName] = mergeModuleUniforms(
1408
+ oldUniforms,
1409
+ uniforms,
1410
+ module.uniformTypes
1411
+ );
1629
1412
  this.moduleBindings[moduleName] = { ...oldBindings, ...bindings };
1630
1413
  }
1631
1414
  }
@@ -1664,16 +1447,99 @@ var __exports__ = (() => {
1664
1447
  }
1665
1448
  _addModule(module) {
1666
1449
  const moduleName = module.name;
1667
- this.moduleUniforms[moduleName] = module.defaultUniforms || {};
1450
+ this.moduleUniforms[moduleName] = mergeModuleUniforms(
1451
+ {},
1452
+ module.defaultUniforms || {},
1453
+ module.uniformTypes
1454
+ );
1668
1455
  this.moduleBindings[moduleName] = {};
1669
1456
  }
1670
1457
  };
1458
+ function mergeModuleUniforms(currentUniforms = {}, nextUniforms = {}, uniformTypes2 = {}) {
1459
+ const mergedUniforms = { ...currentUniforms };
1460
+ for (const [key, value] of Object.entries(nextUniforms)) {
1461
+ if (value === void 0) {
1462
+ continue;
1463
+ }
1464
+ mergedUniforms[key] = mergeModuleUniformValue(currentUniforms[key], value, uniformTypes2[key]);
1465
+ }
1466
+ return mergedUniforms;
1467
+ }
1468
+ function mergeModuleUniformValue(currentValue, nextValue, uniformType) {
1469
+ if (!uniformType || typeof uniformType === "string") {
1470
+ return cloneModuleUniformValue(nextValue);
1471
+ }
1472
+ if (Array.isArray(uniformType)) {
1473
+ if (isPackedUniformArrayValue(nextValue) || !Array.isArray(nextValue)) {
1474
+ return cloneModuleUniformValue(nextValue);
1475
+ }
1476
+ const currentArray = Array.isArray(currentValue) && !isPackedUniformArrayValue(currentValue) ? [...currentValue] : [];
1477
+ const mergedArray = currentArray.slice();
1478
+ for (let index = 0; index < nextValue.length; index++) {
1479
+ const elementValue = nextValue[index];
1480
+ if (elementValue === void 0) {
1481
+ continue;
1482
+ }
1483
+ mergedArray[index] = mergeModuleUniformValue(
1484
+ currentArray[index],
1485
+ elementValue,
1486
+ uniformType[0]
1487
+ );
1488
+ }
1489
+ return mergedArray;
1490
+ }
1491
+ if (!isPlainUniformObject(nextValue)) {
1492
+ return cloneModuleUniformValue(nextValue);
1493
+ }
1494
+ const uniformStruct = uniformType;
1495
+ const currentObject = isPlainUniformObject(currentValue) ? currentValue : {};
1496
+ const mergedObject = { ...currentObject };
1497
+ for (const [key, value] of Object.entries(nextValue)) {
1498
+ if (value === void 0) {
1499
+ continue;
1500
+ }
1501
+ mergedObject[key] = mergeModuleUniformValue(currentObject[key], value, uniformStruct[key]);
1502
+ }
1503
+ return mergedObject;
1504
+ }
1505
+ function cloneModuleUniformValue(value) {
1506
+ if (ArrayBuffer.isView(value)) {
1507
+ return Array.prototype.slice.call(value);
1508
+ }
1509
+ if (Array.isArray(value)) {
1510
+ if (isPackedUniformArrayValue(value)) {
1511
+ return value.slice();
1512
+ }
1513
+ const compositeArray = value;
1514
+ return compositeArray.map(
1515
+ (element) => element === void 0 ? void 0 : cloneModuleUniformValue(element)
1516
+ );
1517
+ }
1518
+ if (isPlainUniformObject(value)) {
1519
+ return Object.fromEntries(
1520
+ Object.entries(value).map(([key, nestedValue]) => [
1521
+ key,
1522
+ nestedValue === void 0 ? void 0 : cloneModuleUniformValue(nestedValue)
1523
+ ])
1524
+ );
1525
+ }
1526
+ return value;
1527
+ }
1528
+ function isPackedUniformArrayValue(value) {
1529
+ return ArrayBuffer.isView(value) || Array.isArray(value) && (value.length === 0 || typeof value[0] === "number");
1530
+ }
1531
+ function isPlainUniformObject(value) {
1532
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value) && !ArrayBuffer.isView(value);
1533
+ }
1534
+ function isShaderInputsModuleWithDependencies(module) {
1535
+ return Boolean(module?.dependencies);
1536
+ }
1671
1537
 
1672
1538
  // src/dynamic-texture/dynamic-texture.ts
1673
- var import_core9 = __toESM(require_core(), 1);
1539
+ var import_core7 = __toESM(require_core(), 1);
1674
1540
 
1675
1541
  // src/dynamic-texture/texture-data.ts
1676
- var import_core8 = __toESM(require_core(), 1);
1542
+ var import_core6 = __toESM(require_core(), 1);
1677
1543
  var TEXTURE_CUBE_FACE_MAP = { "+X": 0, "-X": 1, "+Y": 2, "-Y": 3, "+Z": 4, "-Z": 5 };
1678
1544
  function getFirstMipLevel(layer) {
1679
1545
  if (!layer)
@@ -1727,8 +1593,8 @@ var __exports__ = (() => {
1727
1593
  }
1728
1594
  }
1729
1595
  function getTextureMipLevelSize(data) {
1730
- if ((0, import_core8.isExternalImage)(data)) {
1731
- return (0, import_core8.getExternalImageSize)(data);
1596
+ if ((0, import_core6.isExternalImage)(data)) {
1597
+ return (0, import_core6.getExternalImageSize)(data);
1732
1598
  }
1733
1599
  if (typeof data === "object" && "width" in data && "height" in data) {
1734
1600
  return { width: data.width, height: data.height };
@@ -1738,6 +1604,9 @@ var __exports__ = (() => {
1738
1604
  function isTextureImageData(data) {
1739
1605
  return typeof data === "object" && data !== null && "data" in data && "width" in data && "height" in data;
1740
1606
  }
1607
+ function isTypedArrayMipLevelData(data) {
1608
+ return ArrayBuffer.isView(data);
1609
+ }
1741
1610
  function resolveTextureImageFormat(data) {
1742
1611
  const { textureFormat, format } = data;
1743
1612
  if (textureFormat && format && textureFormat !== format) {
@@ -1762,13 +1631,13 @@ var __exports__ = (() => {
1762
1631
  function _normalizeTexture2DData(data) {
1763
1632
  return Array.isArray(data) ? data : [data];
1764
1633
  }
1765
- function getTexture2DSubresources(slice, lodData) {
1634
+ function getTexture2DSubresources(slice, lodData, baseLevelSize, textureFormat) {
1766
1635
  const lodArray = _normalizeTexture2DData(lodData);
1767
1636
  const z = slice;
1768
1637
  const subresources = [];
1769
1638
  for (let mipLevel = 0; mipLevel < lodArray.length; mipLevel++) {
1770
1639
  const imageData = lodArray[mipLevel];
1771
- if ((0, import_core8.isExternalImage)(imageData)) {
1640
+ if ((0, import_core6.isExternalImage)(imageData)) {
1772
1641
  subresources.push({
1773
1642
  type: "external-image",
1774
1643
  image: imageData,
@@ -1783,6 +1652,19 @@ var __exports__ = (() => {
1783
1652
  z,
1784
1653
  mipLevel
1785
1654
  });
1655
+ } else if (isTypedArrayMipLevelData(imageData) && baseLevelSize) {
1656
+ subresources.push({
1657
+ type: "texture-data",
1658
+ data: {
1659
+ data: imageData,
1660
+ width: Math.max(1, baseLevelSize.width >> mipLevel),
1661
+ height: Math.max(1, baseLevelSize.height >> mipLevel),
1662
+ ...textureFormat ? { format: textureFormat } : {}
1663
+ },
1664
+ textureFormat,
1665
+ z,
1666
+ mipLevel
1667
+ });
1786
1668
  } else {
1787
1669
  throw new Error("Unsupported 2D mip-level payload");
1788
1670
  }
@@ -1878,7 +1760,12 @@ var __exports__ = (() => {
1878
1760
  try {
1879
1761
  const propsWithSyncData = await this._loadAllData(originalPropsWithAsyncData);
1880
1762
  this._checkNotDestroyed();
1881
- const subresources = propsWithSyncData.data ? getTextureSubresources(propsWithSyncData) : [];
1763
+ const subresources = propsWithSyncData.data ? getTextureSubresources({
1764
+ ...propsWithSyncData,
1765
+ width: originalPropsWithAsyncData.width,
1766
+ height: originalPropsWithAsyncData.height,
1767
+ format: originalPropsWithAsyncData.format
1768
+ }) : [];
1882
1769
  const userProvidedFormat = "format" in originalPropsWithAsyncData && originalPropsWithAsyncData.format !== void 0;
1883
1770
  const userProvidedUsage = "usage" in originalPropsWithAsyncData && originalPropsWithAsyncData.usage !== void 0;
1884
1771
  const deduceSize = () => {
@@ -1908,11 +1795,11 @@ var __exports__ = (() => {
1908
1795
  data: void 0
1909
1796
  };
1910
1797
  if (this.device.isTextureFormatCompressed(resolvedFormat) && !userProvidedUsage) {
1911
- baseTextureProps.usage = import_core9.Texture.SAMPLE | import_core9.Texture.COPY_DST;
1798
+ baseTextureProps.usage = import_core7.Texture.SAMPLE | import_core7.Texture.COPY_DST;
1912
1799
  }
1913
1800
  const shouldGenerateMipmaps = this.props.mipmaps && !textureData.hasExplicitMipChain && !this.device.isTextureFormatCompressed(resolvedFormat);
1914
1801
  if (this.device.type === "webgpu" && shouldGenerateMipmaps) {
1915
- const requiredUsage = this.props.dimension === "3d" ? import_core9.Texture.SAMPLE | import_core9.Texture.STORAGE | import_core9.Texture.COPY_DST | import_core9.Texture.COPY_SRC : import_core9.Texture.SAMPLE | import_core9.Texture.RENDER | import_core9.Texture.COPY_DST | import_core9.Texture.COPY_SRC;
1802
+ const requiredUsage = this.props.dimension === "3d" ? import_core7.Texture.SAMPLE | import_core7.Texture.STORAGE | import_core7.Texture.COPY_DST | import_core7.Texture.COPY_SRC : import_core7.Texture.SAMPLE | import_core7.Texture.RENDER | import_core7.Texture.COPY_DST | import_core7.Texture.COPY_SRC;
1916
1803
  baseTextureProps.usage |= requiredUsage;
1917
1804
  }
1918
1805
  const maxMips = this.device.getMipLevelCount(baseTextureProps.width, baseTextureProps.height);
@@ -1925,18 +1812,17 @@ var __exports__ = (() => {
1925
1812
  this._setTextureSubresources(textureData.subresources);
1926
1813
  }
1927
1814
  if (this.props.mipmaps && !textureData.hasExplicitMipChain && !shouldGenerateMipmaps) {
1928
- import_core9.log.warn(`${this} skipping auto-generated mipmaps for compressed texture format`)();
1815
+ import_core7.log.warn(`${this} skipping auto-generated mipmaps for compressed texture format`)();
1929
1816
  }
1930
1817
  if (shouldGenerateMipmaps) {
1931
1818
  this.generateMipmaps();
1932
1819
  }
1933
1820
  this.isReady = true;
1934
1821
  this.resolveReady(this.texture);
1935
- import_core9.log.info(0, `${this} created`)();
1822
+ import_core7.log.info(0, `${this} created`)();
1936
1823
  } catch (e) {
1937
1824
  const err = e instanceof Error ? e : new Error(String(e));
1938
1825
  this.rejectReady(err);
1939
- throw err;
1940
1826
  }
1941
1827
  }
1942
1828
  destroy() {
@@ -1954,16 +1840,60 @@ var __exports__ = (() => {
1954
1840
  } else if (this.device.type === "webgpu") {
1955
1841
  this.device.generateMipmapsWebGPU(this.texture);
1956
1842
  } else {
1957
- import_core9.log.warn(`${this} mipmaps not supported on ${this.device.type}`);
1843
+ import_core7.log.warn(`${this} mipmaps not supported on ${this.device.type}`);
1958
1844
  }
1959
1845
  }
1960
1846
  /** Set sampler or create one from props */
1961
1847
  setSampler(sampler = {}) {
1962
1848
  this._checkReady();
1963
- const s = sampler instanceof import_core9.Sampler ? sampler : this.device.createSampler(sampler);
1849
+ const s = sampler instanceof import_core7.Sampler ? sampler : this.device.createSampler(sampler);
1964
1850
  this.texture.setSampler(s);
1965
1851
  this._sampler = s;
1966
1852
  }
1853
+ /**
1854
+ * Copies texture contents into a GPU buffer and waits until the copy is complete.
1855
+ * The caller owns the returned buffer and must destroy it when finished.
1856
+ */
1857
+ async readBuffer(options = {}) {
1858
+ if (!this.isReady) {
1859
+ await this.ready;
1860
+ }
1861
+ const width = options.width ?? this.texture.width;
1862
+ const height = options.height ?? this.texture.height;
1863
+ const depthOrArrayLayers = options.depthOrArrayLayers ?? this.texture.depth;
1864
+ const layout = this.texture.computeMemoryLayout({ width, height, depthOrArrayLayers });
1865
+ const buffer = this.device.createBuffer({
1866
+ byteLength: layout.byteLength,
1867
+ usage: import_core7.Buffer.COPY_DST | import_core7.Buffer.MAP_READ
1868
+ });
1869
+ this.texture.readBuffer(
1870
+ {
1871
+ ...options,
1872
+ width,
1873
+ height,
1874
+ depthOrArrayLayers
1875
+ },
1876
+ buffer
1877
+ );
1878
+ const fence = this.device.createFence();
1879
+ await fence.signaled;
1880
+ fence.destroy();
1881
+ return buffer;
1882
+ }
1883
+ /** Reads texture contents back to CPU memory. */
1884
+ async readAsync(options = {}) {
1885
+ if (!this.isReady) {
1886
+ await this.ready;
1887
+ }
1888
+ const width = options.width ?? this.texture.width;
1889
+ const height = options.height ?? this.texture.height;
1890
+ const depthOrArrayLayers = options.depthOrArrayLayers ?? this.texture.depth;
1891
+ const layout = this.texture.computeMemoryLayout({ width, height, depthOrArrayLayers });
1892
+ const buffer = await this.readBuffer(options);
1893
+ const data = await buffer.readAsync(0, layout.byteLength);
1894
+ buffer.destroy();
1895
+ return data.buffer;
1896
+ }
1967
1897
  /**
1968
1898
  * Resize by cloning the underlying immutable texture.
1969
1899
  * Does not copy contents; caller may need to re-upload and/or regenerate mips.
@@ -1978,7 +1908,7 @@ var __exports__ = (() => {
1978
1908
  this._sampler = this.texture.sampler;
1979
1909
  this._view = this.texture.view;
1980
1910
  prev.destroy();
1981
- import_core9.log.info(`${this} resized`);
1911
+ import_core7.log.info(`${this} resized`);
1982
1912
  return true;
1983
1913
  }
1984
1914
  /** Convert cube face label to texture slice index. Index can be used with `setTexture2DData()`. */
@@ -2082,18 +2012,18 @@ var __exports__ = (() => {
2082
2012
  }
2083
2013
  _checkNotDestroyed() {
2084
2014
  if (this.destroyed) {
2085
- import_core9.log.warn(`${this} already destroyed`);
2015
+ import_core7.log.warn(`${this} already destroyed`);
2086
2016
  }
2087
2017
  }
2088
2018
  _checkReady() {
2089
2019
  if (!this.isReady) {
2090
- import_core9.log.warn(`${this} Cannot perform this operation before ready`);
2020
+ import_core7.log.warn(`${this} Cannot perform this operation before ready`);
2091
2021
  }
2092
2022
  }
2093
2023
  };
2094
2024
  var DynamicTexture = _DynamicTexture;
2095
2025
  __publicField(DynamicTexture, "defaultProps", {
2096
- ...import_core9.Texture.defaultProps,
2026
+ ...import_core7.Texture.defaultProps,
2097
2027
  dimension: "2d",
2098
2028
  data: null,
2099
2029
  mipmaps: false
@@ -2102,11 +2032,13 @@ var __exports__ = (() => {
2102
2032
  if (!props.data) {
2103
2033
  return [];
2104
2034
  }
2035
+ const baseLevelSize = props.width && props.height ? { width: props.width, height: props.height } : void 0;
2036
+ const textureFormat = "format" in props ? props.format : void 0;
2105
2037
  switch (props.dimension) {
2106
2038
  case "1d":
2107
2039
  return getTexture1DSubresources(props.data);
2108
2040
  case "2d":
2109
- return getTexture2DSubresources(0, props.data);
2041
+ return getTexture2DSubresources(0, props.data, baseLevelSize, textureFormat);
2110
2042
  case "3d":
2111
2043
  return getTexture3DSubresources(props.data);
2112
2044
  case "2d-array":
@@ -2239,7 +2171,7 @@ var __exports__ = (() => {
2239
2171
  }
2240
2172
  if (x && typeof x === "object" && x.constructor === Object) {
2241
2173
  const object = x;
2242
- const values = await Promise.all(Object.values(object));
2174
+ const values = await Promise.all(Object.values(object).map(awaitAllPromises));
2243
2175
  const keys = Object.keys(object);
2244
2176
  const resolvedObject = {};
2245
2177
  for (let i = 0; i < keys.length; i++) {
@@ -2308,6 +2240,7 @@ var __exports__ = (() => {
2308
2240
  /** ShaderInputs instance */
2309
2241
  // @ts-expect-error Assigned in function called by constructor
2310
2242
  shaderInputs;
2243
+ material = null;
2311
2244
  // @ts-expect-error Assigned in function called by constructor
2312
2245
  _uniformStore;
2313
2246
  _attributeInfos = {};
@@ -2318,6 +2251,7 @@ var __exports__ = (() => {
2318
2251
  _destroyed = false;
2319
2252
  /** "Time" of last draw. Monotonically increasing timestamp */
2320
2253
  _lastDrawTimestamp = -1;
2254
+ _bindingTable = [];
2321
2255
  get [Symbol.toStringTag]() {
2322
2256
  return "Model";
2323
2257
  }
@@ -2330,6 +2264,7 @@ var __exports__ = (() => {
2330
2264
  this.id = props.id || uid("model");
2331
2265
  this.device = device;
2332
2266
  Object.assign(this.userData, props.userData);
2267
+ this.material = props.material || null;
2333
2268
  const moduleMap = Object.fromEntries(
2334
2269
  this.props.modules?.map((module) => [module.name, module]) || []
2335
2270
  );
@@ -2340,16 +2275,22 @@ var __exports__ = (() => {
2340
2275
  // @ts-ignore shaderInputs is assigned in setShaderInputs above.
2341
2276
  (this.props.modules?.length > 0 ? this.props.modules : this.shaderInputs?.getModules()) || []
2342
2277
  );
2278
+ this.props.shaderLayout = mergeShaderModuleBindingsIntoLayout(this.props.shaderLayout, modules) || null;
2343
2279
  const isWebGPU = this.device.type === "webgpu";
2344
2280
  if (isWebGPU && this.props.source) {
2345
- const { source: source3, getUniforms: getUniforms2 } = this.props.shaderAssembler.assembleWGSLShader({
2281
+ const { source: source3, getUniforms: getUniforms2, bindingTable } = this.props.shaderAssembler.assembleWGSLShader({
2346
2282
  platformInfo,
2347
2283
  ...this.props,
2348
2284
  modules
2349
2285
  });
2350
2286
  this.source = source3;
2351
2287
  this._getModuleUniforms = getUniforms2;
2352
- this.props.shaderLayout ||= device.getShaderLayout(this.source);
2288
+ this._bindingTable = bindingTable;
2289
+ const inferredShaderLayout = device.getShaderLayout?.(this.source);
2290
+ this.props.shaderLayout = mergeShaderModuleBindingsIntoLayout(
2291
+ this.props.shaderLayout || inferredShaderLayout || null,
2292
+ modules
2293
+ ) || null;
2353
2294
  } else {
2354
2295
  const { vs: vs3, fs: fs3, getUniforms: getUniforms2 } = this.props.shaderAssembler.assembleGLSLShaderPair({
2355
2296
  platformInfo,
@@ -2359,6 +2300,7 @@ var __exports__ = (() => {
2359
2300
  this.vs = vs3;
2360
2301
  this.fs = fs3;
2361
2302
  this._getModuleUniforms = getUniforms2;
2303
+ this._bindingTable = [];
2362
2304
  }
2363
2305
  this.vertexCount = this.props.vertexCount;
2364
2306
  this.instanceCount = this.props.instanceCount;
@@ -2368,8 +2310,8 @@ var __exports__ = (() => {
2368
2310
  if (props.geometry) {
2369
2311
  this.setGeometry(props.geometry);
2370
2312
  }
2371
- this.pipelineFactory = props.pipelineFactory || PipelineFactory.getDefaultPipelineFactory(this.device);
2372
- this.shaderFactory = props.shaderFactory || ShaderFactory.getDefaultShaderFactory(this.device);
2313
+ this.pipelineFactory = props.pipelineFactory || import_core8.PipelineFactory.getDefaultPipelineFactory(this.device);
2314
+ this.shaderFactory = props.shaderFactory || import_core8.ShaderFactory.getDefaultShaderFactory(this.device);
2373
2315
  this.pipeline = this._updatePipeline();
2374
2316
  this.vertexArray = device.createVertexArray({
2375
2317
  shaderLayout: this.pipeline.shaderLayout,
@@ -2429,6 +2371,10 @@ var __exports__ = (() => {
2429
2371
  setNeedsRedraw(reason) {
2430
2372
  this._needsRedraw ||= reason;
2431
2373
  }
2374
+ /** Returns WGSL binding debug rows for the assembled shader. Returns an empty array for GLSL models. */
2375
+ getBindingDebugTable() {
2376
+ return this._bindingTable;
2377
+ }
2432
2378
  /** Update uniforms and pipeline state prior to drawing. */
2433
2379
  predraw() {
2434
2380
  this.updateShaderInputs();
@@ -2442,7 +2388,7 @@ var __exports__ = (() => {
2442
2388
  draw(renderPass) {
2443
2389
  const loadingBinding = this._areBindingsLoading();
2444
2390
  if (loadingBinding) {
2445
- import_core10.log.info(LOG_DRAW_PRIORITY, `>>> DRAWING ABORTED ${this.id}: ${loadingBinding} not loaded`)();
2391
+ import_core8.log.info(LOG_DRAW_PRIORITY, `>>> DRAWING ABORTED ${this.id}: ${loadingBinding} not loaded`)();
2446
2392
  return false;
2447
2393
  }
2448
2394
  try {
@@ -2457,6 +2403,7 @@ var __exports__ = (() => {
2457
2403
  this._logDrawCallStart();
2458
2404
  this.pipeline = this._updatePipeline();
2459
2405
  const syncBindings = this._getBindings();
2406
+ const syncBindGroups = this._getBindGroups();
2460
2407
  const { indexBuffer } = this.vertexArray;
2461
2408
  const indexCount = indexBuffer ? indexBuffer.byteLength / (indexBuffer.indexType === "uint32" ? 4 : 2) : void 0;
2462
2409
  drawSuccess = this.pipeline.draw({
@@ -2471,6 +2418,8 @@ var __exports__ = (() => {
2471
2418
  // and WebGL uniforms must be supplied on every draw instead of being stored
2472
2419
  // on the pipeline instance.
2473
2420
  bindings: syncBindings,
2421
+ bindGroups: syncBindGroups,
2422
+ _bindGroupCacheKeys: this._getBindGroupCacheKeys(),
2474
2423
  uniforms: this.props.uniforms,
2475
2424
  // WebGL shares underlying cached pipelines even for models that have different parameters and topology,
2476
2425
  // so we must provide our unique parameters to each draw
@@ -2574,19 +2523,23 @@ var __exports__ = (() => {
2574
2523
  /** Set the shader inputs */
2575
2524
  setShaderInputs(shaderInputs) {
2576
2525
  this.shaderInputs = shaderInputs;
2577
- this._uniformStore = new import_core10.UniformStore(this.shaderInputs.modules);
2526
+ this._uniformStore = new import_core8.UniformStore(this.shaderInputs.modules);
2578
2527
  for (const [moduleName, module] of Object.entries(this.shaderInputs.modules)) {
2579
- if (shaderModuleHasUniforms(module)) {
2528
+ if (shaderModuleHasUniforms(module) && !this.material?.ownsModule(moduleName)) {
2580
2529
  const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
2581
2530
  this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
2582
2531
  }
2583
2532
  }
2584
2533
  this.setNeedsRedraw("shaderInputs");
2585
2534
  }
2535
+ setMaterial(material) {
2536
+ this.material = material;
2537
+ this.setNeedsRedraw("material");
2538
+ }
2586
2539
  /** Update uniform buffers from the model's shader inputs */
2587
2540
  updateShaderInputs() {
2588
2541
  this._uniformStore.setUniforms(this.shaderInputs.getUniformValues());
2589
- this.setBindings(this.shaderInputs.getBindingValues());
2542
+ this.setBindings(this._getNonMaterialBindings(this.shaderInputs.getBindingValues()));
2590
2543
  this.setNeedsRedraw("shaderInputs");
2591
2544
  }
2592
2545
  /**
@@ -2618,7 +2571,7 @@ var __exports__ = (() => {
2618
2571
  setAttributes(buffers, options) {
2619
2572
  const disableWarnings = options?.disableWarnings ?? this.props.disableWarnings;
2620
2573
  if (buffers["indices"]) {
2621
- import_core10.log.warn(
2574
+ import_core8.log.warn(
2622
2575
  `Model:${this.id} setAttributes() - indexBuffer should be set using setIndexBuffer()`
2623
2576
  )();
2624
2577
  }
@@ -2631,7 +2584,7 @@ var __exports__ = (() => {
2631
2584
  const bufferLayout = bufferLayoutHelper.getBufferLayout(bufferName);
2632
2585
  if (!bufferLayout) {
2633
2586
  if (!disableWarnings) {
2634
- import_core10.log.warn(`Model(${this.id}): Missing layout for buffer "${bufferName}".`)();
2587
+ import_core8.log.warn(`Model(${this.id}): Missing layout for buffer "${bufferName}".`)();
2635
2588
  }
2636
2589
  continue;
2637
2590
  }
@@ -2646,7 +2599,7 @@ var __exports__ = (() => {
2646
2599
  }
2647
2600
  }
2648
2601
  if (!set && !disableWarnings) {
2649
- import_core10.log.warn(
2602
+ import_core8.log.warn(
2650
2603
  `Model(${this.id}): Ignoring buffer "${buffer.id}" for unknown attribute "${bufferName}"`
2651
2604
  )();
2652
2605
  }
@@ -2667,7 +2620,7 @@ var __exports__ = (() => {
2667
2620
  if (attributeInfo) {
2668
2621
  this.vertexArray.setConstantWebGL(attributeInfo.location, value);
2669
2622
  } else if (!(options?.disableWarnings ?? this.props.disableWarnings)) {
2670
- import_core10.log.warn(
2623
+ import_core8.log.warn(
2671
2624
  `Model "${this.id}: Ignoring constant supplied for unknown attribute "${attributeName}"`
2672
2625
  )();
2673
2626
  }
@@ -2682,6 +2635,11 @@ var __exports__ = (() => {
2682
2635
  return binding.id;
2683
2636
  }
2684
2637
  }
2638
+ for (const binding of Object.values(this.material?.bindings || {})) {
2639
+ if (binding instanceof DynamicTexture && !binding.isReady) {
2640
+ return binding.id;
2641
+ }
2642
+ }
2685
2643
  return false;
2686
2644
  }
2687
2645
  /** Extracts texture view from loaded async textures. Returns null if any textures have not yet been loaded. */
@@ -2698,24 +2656,43 @@ var __exports__ = (() => {
2698
2656
  }
2699
2657
  return validBindings;
2700
2658
  }
2659
+ _getBindGroups() {
2660
+ const shaderLayout = this.pipeline?.shaderLayout || this.props.shaderLayout || { bindings: [] };
2661
+ const bindGroups = shaderLayout.bindings.length ? (0, import_core8.normalizeBindingsByGroup)(shaderLayout, this._getBindings()) : { 0: this._getBindings() };
2662
+ if (!this.material) {
2663
+ return bindGroups;
2664
+ }
2665
+ for (const [groupKey, groupBindings] of Object.entries(this.material.getBindingsByGroup())) {
2666
+ const group = Number(groupKey);
2667
+ bindGroups[group] = {
2668
+ ...bindGroups[group] || {},
2669
+ ...groupBindings
2670
+ };
2671
+ }
2672
+ return bindGroups;
2673
+ }
2674
+ _getBindGroupCacheKeys() {
2675
+ const bindGroupCacheKey = this.material?.getBindGroupCacheKey(3);
2676
+ return bindGroupCacheKey ? { 3: bindGroupCacheKey } : {};
2677
+ }
2701
2678
  /** Get the timestamp of the latest updated bound GPU memory resource (buffer/texture). */
2702
2679
  _getBindingsUpdateTimestamp() {
2703
2680
  let timestamp = 0;
2704
2681
  for (const binding of Object.values(this.bindings)) {
2705
- if (binding instanceof import_core10.TextureView) {
2682
+ if (binding instanceof import_core8.TextureView) {
2706
2683
  timestamp = Math.max(timestamp, binding.texture.updateTimestamp);
2707
- } else if (binding instanceof import_core10.Buffer || binding instanceof import_core10.Texture) {
2684
+ } else if (binding instanceof import_core8.Buffer || binding instanceof import_core8.Texture) {
2708
2685
  timestamp = Math.max(timestamp, binding.updateTimestamp);
2709
2686
  } else if (binding instanceof DynamicTexture) {
2710
2687
  timestamp = binding.texture ? Math.max(timestamp, binding.texture.updateTimestamp) : (
2711
2688
  // The texture will become available in the future
2712
2689
  Infinity
2713
2690
  );
2714
- } else if (!(binding instanceof import_core10.Sampler)) {
2691
+ } else if (!(binding instanceof import_core8.Sampler)) {
2715
2692
  timestamp = Math.max(timestamp, binding.buffer.updateTimestamp);
2716
2693
  }
2717
2694
  }
2718
- return timestamp;
2695
+ return Math.max(timestamp, this.material?.getBindingsUpdateTimestamp() || 0);
2719
2696
  }
2720
2697
  /**
2721
2698
  * Updates the optional geometry attributes
@@ -2746,7 +2723,7 @@ var __exports__ = (() => {
2746
2723
  let prevShaderVs = null;
2747
2724
  let prevShaderFs = null;
2748
2725
  if (this.pipeline) {
2749
- import_core10.log.log(
2726
+ import_core8.log.log(
2750
2727
  1,
2751
2728
  `Model ${this.id}: Recreating pipeline because "${this._pipelineNeedsUpdate}".`
2752
2729
  )();
@@ -2773,16 +2750,15 @@ var __exports__ = (() => {
2773
2750
  }
2774
2751
  this.pipeline = this.pipelineFactory.createRenderPipeline({
2775
2752
  ...this.props,
2753
+ bindings: void 0,
2776
2754
  bufferLayout: this.bufferLayout,
2777
2755
  topology: this.topology,
2778
2756
  parameters: this.parameters,
2779
- // TODO - why set bindings here when we reset them every frame?
2780
- // Should we expose a BindGroup abstraction?
2781
- bindings: this._getBindings(),
2757
+ bindGroups: this._getBindGroups(),
2782
2758
  vs: vs3,
2783
2759
  fs: fs3
2784
2760
  });
2785
- this._attributeInfos = (0, import_core10.getAttributeInfosFromLayouts)(
2761
+ this._attributeInfos = (0, import_core8.getAttributeInfosFromLayouts)(
2786
2762
  this.pipeline.shaderLayout,
2787
2763
  this.bufferLayout
2788
2764
  );
@@ -2798,24 +2774,24 @@ var __exports__ = (() => {
2798
2774
  _lastLogTime = 0;
2799
2775
  _logOpen = false;
2800
2776
  _logDrawCallStart() {
2801
- const logDrawTimeout = import_core10.log.level > 3 ? 0 : LOG_DRAW_TIMEOUT;
2802
- if (import_core10.log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
2777
+ const logDrawTimeout = import_core8.log.level > 3 ? 0 : LOG_DRAW_TIMEOUT;
2778
+ if (import_core8.log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
2803
2779
  return;
2804
2780
  }
2805
2781
  this._lastLogTime = Date.now();
2806
2782
  this._logOpen = true;
2807
- import_core10.log.group(LOG_DRAW_PRIORITY, `>>> DRAWING MODEL ${this.id}`, { collapsed: import_core10.log.level <= 2 })();
2783
+ import_core8.log.group(LOG_DRAW_PRIORITY, `>>> DRAWING MODEL ${this.id}`, { collapsed: import_core8.log.level <= 2 })();
2808
2784
  }
2809
2785
  _logDrawCallEnd() {
2810
2786
  if (this._logOpen) {
2811
2787
  const shaderLayoutTable = getDebugTableForShaderLayout(this.pipeline.shaderLayout, this.id);
2812
- import_core10.log.table(LOG_DRAW_PRIORITY, shaderLayoutTable)();
2788
+ import_core8.log.table(LOG_DRAW_PRIORITY, shaderLayoutTable)();
2813
2789
  const uniformTable = this.shaderInputs.getDebugTable();
2814
- import_core10.log.table(LOG_DRAW_PRIORITY, uniformTable)();
2790
+ import_core8.log.table(LOG_DRAW_PRIORITY, uniformTable)();
2815
2791
  const attributeTable = this._getAttributeDebugTable();
2816
- import_core10.log.table(LOG_DRAW_PRIORITY, this._attributeInfos)();
2817
- import_core10.log.table(LOG_DRAW_PRIORITY, attributeTable)();
2818
- import_core10.log.groupEnd(LOG_DRAW_PRIORITY)();
2792
+ import_core8.log.table(LOG_DRAW_PRIORITY, this._attributeInfos)();
2793
+ import_core8.log.table(LOG_DRAW_PRIORITY, attributeTable)();
2794
+ import_core8.log.groupEnd(LOG_DRAW_PRIORITY)();
2819
2795
  this._logOpen = false;
2820
2796
  }
2821
2797
  }
@@ -2854,14 +2830,26 @@ var __exports__ = (() => {
2854
2830
  }
2855
2831
  // TODO - fix typing of luma data types
2856
2832
  _getBufferOrConstantValues(attribute, dataType) {
2857
- const TypedArrayConstructor = (0, import_core10.getTypedArrayConstructor)(dataType);
2858
- const typedArray = attribute instanceof import_core10.Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
2833
+ const TypedArrayConstructor = import_core8.dataTypeDecoder.getTypedArrayConstructor(dataType);
2834
+ const typedArray = attribute instanceof import_core8.Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
2859
2835
  return typedArray.toString();
2860
2836
  }
2837
+ _getNonMaterialBindings(bindings) {
2838
+ if (!this.material) {
2839
+ return bindings;
2840
+ }
2841
+ const filteredBindings = {};
2842
+ for (const [name, binding] of Object.entries(bindings)) {
2843
+ if (!this.material.ownsBinding(name)) {
2844
+ filteredBindings[name] = binding;
2845
+ }
2846
+ }
2847
+ return filteredBindings;
2848
+ }
2861
2849
  };
2862
2850
  var Model = _Model;
2863
2851
  __publicField(Model, "defaultProps", {
2864
- ...import_core10.RenderPipeline.defaultProps,
2852
+ ...import_core8.RenderPipeline.defaultProps,
2865
2853
  source: void 0,
2866
2854
  vs: null,
2867
2855
  fs: null,
@@ -2881,6 +2869,7 @@ var __exports__ = (() => {
2881
2869
  instanceCount: 0,
2882
2870
  vertexCount: 0,
2883
2871
  shaderInputs: void 0,
2872
+ material: void 0,
2884
2873
  pipelineFactory: void 0,
2885
2874
  shaderFactory: void 0,
2886
2875
  transformFeedback: void 0,
@@ -2888,9 +2877,6 @@ var __exports__ = (() => {
2888
2877
  debugShaders: void 0,
2889
2878
  disableWarnings: void 0
2890
2879
  });
2891
- function shaderModuleHasUniforms(module) {
2892
- return Boolean(module.uniformTypes && !isObjectEmpty(module.uniformTypes));
2893
- }
2894
2880
  function getPlatformInfo(device) {
2895
2881
  return {
2896
2882
  type: device.type,
@@ -2901,85 +2887,310 @@ var __exports__ = (() => {
2901
2887
  features: device.features
2902
2888
  };
2903
2889
  }
2904
- function isObjectEmpty(obj) {
2905
- for (const key in obj) {
2906
- return false;
2907
- }
2908
- return true;
2909
- }
2910
2890
 
2911
- // src/compute/buffer-transform.ts
2912
- var import_core11 = __toESM(require_core(), 1);
2913
- var import_shadertools3 = __toESM(require_shadertools(), 1);
2914
- var _BufferTransform = class {
2891
+ // src/material/material.ts
2892
+ var import_core9 = __toESM(require_core(), 1);
2893
+
2894
+ // src/material/material-factory.ts
2895
+ var MATERIAL_BIND_GROUP = 3;
2896
+ var MaterialFactory = class {
2897
+ /** Device that creates materials for this schema. */
2915
2898
  device;
2916
- model;
2917
- transformFeedback;
2918
- static isSupported(device) {
2919
- return device?.info?.type === "webgl";
2920
- }
2921
- constructor(device, props = _BufferTransform.defaultProps) {
2922
- if (!_BufferTransform.isSupported(device)) {
2923
- throw new Error("BufferTransform not yet implemented on WebGPU");
2924
- }
2899
+ /** Shader modules that define the material schema. */
2900
+ modules;
2901
+ _materialBindingNames;
2902
+ _materialModuleNames;
2903
+ constructor(device, props = {}) {
2925
2904
  this.device = device;
2926
- this.model = new Model(this.device, {
2927
- id: props.id || "buffer-transform-model",
2928
- fs: props.fs || (0, import_shadertools3.getPassthroughFS)(),
2929
- topology: props.topology || "point-list",
2930
- varyings: props.outputs || props.varyings,
2931
- ...props
2932
- });
2933
- this.transformFeedback = this.device.createTransformFeedback({
2934
- layout: this.model.pipeline.shaderLayout,
2935
- // @ts-expect-error TODO
2936
- buffers: props.feedbackBuffers
2937
- });
2938
- this.model.setTransformFeedback(this.transformFeedback);
2939
- Object.seal(this);
2905
+ this.modules = props.modules || [];
2906
+ const shaderInputs = new ShaderInputs(
2907
+ Object.fromEntries(this.modules.map((module) => [module.name, module]))
2908
+ );
2909
+ this._materialBindingNames = getMaterialBindingNames(shaderInputs);
2910
+ this._materialModuleNames = getMaterialModuleNames(shaderInputs);
2940
2911
  }
2941
- /** Destroy owned resources. */
2942
- destroy() {
2943
- if (this.model) {
2944
- this.model.destroy();
2945
- }
2912
+ /** Creates one typed material instance for this factory's schema. */
2913
+ createMaterial(props = {}) {
2914
+ return new Material(this.device, {
2915
+ ...props,
2916
+ factory: this
2917
+ });
2946
2918
  }
2947
- /** @deprecated Use {@link destroy}. */
2948
- delete() {
2949
- this.destroy();
2919
+ /** Returns the logical material-owned resource binding names. */
2920
+ getBindingNames() {
2921
+ return Array.from(this._materialBindingNames);
2950
2922
  }
2951
- /** Run one transform loop. */
2952
- run(options) {
2953
- if (options?.inputBuffers) {
2954
- this.model.setAttributes(options.inputBuffers);
2955
- }
2956
- if (options?.outputBuffers) {
2957
- this.transformFeedback.setBuffers(options.outputBuffers);
2923
+ /** Returns `true` when the supplied binding belongs to this material schema. */
2924
+ ownsBinding(bindingName) {
2925
+ if (this._materialBindingNames.has(bindingName)) {
2926
+ return true;
2958
2927
  }
2959
- const renderPass = this.device.beginRenderPass(options);
2960
- this.model.draw(renderPass);
2961
- renderPass.end();
2928
+ const aliasedModuleName = getModuleNameFromUniformBinding(bindingName);
2929
+ return aliasedModuleName ? this._materialModuleNames.has(aliasedModuleName) : false;
2962
2930
  }
2963
- // DEPRECATED METHODS
2964
- /** @deprecated App knows what buffers it is passing in - Returns the {@link Buffer} or {@link BufferRange} for given varying name. */
2965
- getBuffer(varyingName) {
2966
- return this.transformFeedback.getBuffer(varyingName);
2931
+ /** Returns `true` when the supplied shader module is owned by this material schema. */
2932
+ ownsModule(moduleName) {
2933
+ return this._materialModuleNames.has(moduleName);
2967
2934
  }
2968
- /** @deprecated App knows what buffers it is passing in - Reads the {@link Buffer} or {@link BufferRange} for given varying name. */
2969
- readAsync(varyingName) {
2970
- const result = this.getBuffer(varyingName);
2971
- if (!result) {
2972
- throw new Error("BufferTransform#getBuffer");
2935
+ /** Packages resolved material bindings into bind group `3`. */
2936
+ getBindingsByGroup(bindings) {
2937
+ return Object.keys(bindings).length > 0 ? { [MATERIAL_BIND_GROUP]: bindings } : {};
2938
+ }
2939
+ };
2940
+ function getModuleNameFromUniformBinding(bindingName) {
2941
+ return bindingName.endsWith("Uniforms") ? bindingName.slice(0, -"Uniforms".length) : null;
2942
+ }
2943
+ function getMaterialBindingNames(shaderInputs) {
2944
+ const bindingNames = /* @__PURE__ */ new Set();
2945
+ for (const module of Object.values(shaderInputs.modules)) {
2946
+ for (const binding of module.bindingLayout || []) {
2947
+ if (binding.group === MATERIAL_BIND_GROUP) {
2948
+ bindingNames.add(binding.name);
2949
+ }
2973
2950
  }
2974
- if (result instanceof import_core11.Buffer) {
2975
- return result.readAsync();
2951
+ }
2952
+ return bindingNames;
2953
+ }
2954
+ function getMaterialModuleNames(shaderInputs) {
2955
+ const moduleNames = /* @__PURE__ */ new Set();
2956
+ for (const module of Object.values(shaderInputs.modules)) {
2957
+ if (module.name && module.bindingLayout?.some(
2958
+ (binding) => binding.group === MATERIAL_BIND_GROUP && binding.name === module.name
2959
+ )) {
2960
+ moduleNames.add(module.name);
2976
2961
  }
2977
- const { buffer, byteOffset = 0, byteLength = buffer.byteLength } = result;
2978
- return buffer.readAsync(byteOffset, byteLength);
2979
2962
  }
2980
- };
2981
- var BufferTransform = _BufferTransform;
2982
- __publicField(BufferTransform, "defaultProps", {
2963
+ return moduleNames;
2964
+ }
2965
+
2966
+ // src/material/material.ts
2967
+ var Material = class {
2968
+ /** Application-provided identifier. */
2969
+ id;
2970
+ /** Device that owns the material resources. */
2971
+ device;
2972
+ /** Factory that defines the material schema. */
2973
+ factory;
2974
+ /** Shader inputs for the material-owned modules. */
2975
+ shaderInputs;
2976
+ /** Internal binding store including uniform buffers and resource bindings. */
2977
+ bindings = {};
2978
+ _uniformStore;
2979
+ _bindGroupCacheToken = {};
2980
+ constructor(device, props = {}) {
2981
+ this.id = props.id || uid("material");
2982
+ this.device = device;
2983
+ this.factory = props.factory || new MaterialFactory(device, {
2984
+ modules: props.modules || props.shaderInputs?.getModules() || []
2985
+ });
2986
+ const moduleMap = Object.fromEntries(
2987
+ (props.shaderInputs?.getModules() || this.factory.modules).map((module) => [
2988
+ module.name,
2989
+ module
2990
+ ])
2991
+ );
2992
+ this.shaderInputs = props.shaderInputs || new ShaderInputs(moduleMap);
2993
+ this._uniformStore = new import_core9.UniformStore(this.shaderInputs.modules);
2994
+ for (const [moduleName, module] of Object.entries(this.shaderInputs.modules)) {
2995
+ if (this.ownsModule(moduleName) && shaderModuleHasUniforms(module)) {
2996
+ const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
2997
+ this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
2998
+ }
2999
+ }
3000
+ this.updateShaderInputs();
3001
+ if (props.bindings) {
3002
+ this._replaceOwnedBindings(props.bindings);
3003
+ }
3004
+ }
3005
+ /** Destroys managed uniform-buffer resources owned by this material. */
3006
+ destroy() {
3007
+ this._uniformStore.destroy();
3008
+ }
3009
+ /** Creates a new material variant with optional structural and uniform overrides. */
3010
+ clone(props = {}) {
3011
+ const material = this.factory.createMaterial({
3012
+ id: props.id,
3013
+ shaderInputs: props.shaderInputs,
3014
+ bindings: {
3015
+ ...this.getResourceBindings(),
3016
+ ...props.bindings
3017
+ }
3018
+ });
3019
+ if (!props.shaderInputs) {
3020
+ material.setProps(this.shaderInputs.getUniformValues());
3021
+ }
3022
+ if (props.moduleProps) {
3023
+ material.setProps(props.moduleProps);
3024
+ }
3025
+ return material;
3026
+ }
3027
+ /** Returns `true` if this material owns the supplied binding name. */
3028
+ ownsBinding(bindingName) {
3029
+ return this.factory.ownsBinding(bindingName);
3030
+ }
3031
+ /** Returns `true` if this material owns the supplied shader module. */
3032
+ ownsModule(moduleName) {
3033
+ return this.factory.ownsModule(moduleName);
3034
+ }
3035
+ /** Updates material uniform/module props in place without changing material identity. */
3036
+ setProps(props) {
3037
+ this.shaderInputs.setProps(props);
3038
+ this.updateShaderInputs();
3039
+ }
3040
+ /** Updates managed uniform buffers and shader-input-owned bindings. */
3041
+ updateShaderInputs() {
3042
+ this._uniformStore.setUniforms(this.shaderInputs.getUniformValues());
3043
+ const didChange = this._setOwnedBindings(this.shaderInputs.getBindingValues());
3044
+ if (didChange) {
3045
+ this._bindGroupCacheToken = {};
3046
+ }
3047
+ }
3048
+ /** Returns the material-owned resource bindings without internal uniform buffers. */
3049
+ getResourceBindings() {
3050
+ const resourceBindings = {};
3051
+ for (const [name, binding] of Object.entries(this.bindings)) {
3052
+ if (!getModuleNameFromUniformBinding(name)) {
3053
+ resourceBindings[name] = binding;
3054
+ }
3055
+ }
3056
+ return resourceBindings;
3057
+ }
3058
+ /** Returns the resolved bindings, including internal uniform buffers and ready textures. */
3059
+ getBindings() {
3060
+ const validBindings = {};
3061
+ const validBindingsMap = validBindings;
3062
+ for (const [name, binding] of Object.entries(this.bindings)) {
3063
+ if (binding instanceof DynamicTexture) {
3064
+ if (binding.isReady) {
3065
+ validBindingsMap[name] = binding.texture;
3066
+ }
3067
+ } else {
3068
+ validBindingsMap[name] = binding;
3069
+ }
3070
+ }
3071
+ return validBindings;
3072
+ }
3073
+ /** Packages resolved material bindings into logical bind group `3`. */
3074
+ getBindingsByGroup() {
3075
+ return this.factory.getBindingsByGroup(this.getBindings());
3076
+ }
3077
+ /** Returns the stable bind-group cache token for the requested bind group. */
3078
+ getBindGroupCacheKey(group) {
3079
+ return group === MATERIAL_BIND_GROUP ? this._bindGroupCacheToken : null;
3080
+ }
3081
+ /** Returns the latest update timestamp across material-owned resources. */
3082
+ getBindingsUpdateTimestamp() {
3083
+ let timestamp = 0;
3084
+ for (const binding of Object.values(this.bindings)) {
3085
+ if (binding instanceof import_core9.TextureView) {
3086
+ timestamp = Math.max(timestamp, binding.texture.updateTimestamp);
3087
+ } else if (binding instanceof import_core9.Buffer || binding instanceof import_core9.Texture) {
3088
+ timestamp = Math.max(timestamp, binding.updateTimestamp);
3089
+ } else if (binding instanceof DynamicTexture) {
3090
+ timestamp = binding.texture ? Math.max(timestamp, binding.texture.updateTimestamp) : Infinity;
3091
+ } else if (!(binding instanceof import_core9.Sampler)) {
3092
+ timestamp = Math.max(timestamp, binding.buffer.updateTimestamp);
3093
+ }
3094
+ }
3095
+ return timestamp;
3096
+ }
3097
+ /** Replaces owned resource bindings and invalidates the material cache identity when needed. */
3098
+ _replaceOwnedBindings(bindings) {
3099
+ const didChange = this._setOwnedBindings(bindings);
3100
+ if (didChange) {
3101
+ this._bindGroupCacheToken = {};
3102
+ }
3103
+ }
3104
+ _setOwnedBindings(bindings) {
3105
+ let didChange = false;
3106
+ for (const [name, binding] of Object.entries(bindings)) {
3107
+ if (binding === void 0) {
3108
+ continue;
3109
+ }
3110
+ if (!this.ownsBinding(name)) {
3111
+ continue;
3112
+ }
3113
+ if (this.bindings[name] !== binding) {
3114
+ this.bindings[name] = binding;
3115
+ didChange = true;
3116
+ }
3117
+ }
3118
+ return didChange;
3119
+ }
3120
+ };
3121
+
3122
+ // src/compute/buffer-transform.ts
3123
+ var import_core10 = __toESM(require_core(), 1);
3124
+ var import_shadertools3 = __toESM(require_shadertools(), 1);
3125
+ var _BufferTransform = class {
3126
+ device;
3127
+ model;
3128
+ transformFeedback;
3129
+ static isSupported(device) {
3130
+ return device?.info?.type === "webgl";
3131
+ }
3132
+ constructor(device, props = _BufferTransform.defaultProps) {
3133
+ if (!_BufferTransform.isSupported(device)) {
3134
+ throw new Error("BufferTransform not yet implemented on WebGPU");
3135
+ }
3136
+ this.device = device;
3137
+ this.model = new Model(this.device, {
3138
+ id: props.id || "buffer-transform-model",
3139
+ fs: props.fs || (0, import_shadertools3.getPassthroughFS)(),
3140
+ topology: props.topology || "point-list",
3141
+ varyings: props.outputs || props.varyings,
3142
+ ...props
3143
+ });
3144
+ this.transformFeedback = this.device.createTransformFeedback({
3145
+ layout: this.model.pipeline.shaderLayout,
3146
+ // @ts-expect-error TODO
3147
+ buffers: props.feedbackBuffers
3148
+ });
3149
+ this.model.setTransformFeedback(this.transformFeedback);
3150
+ Object.seal(this);
3151
+ }
3152
+ /** Destroy owned resources. */
3153
+ destroy() {
3154
+ if (this.model) {
3155
+ this.model.destroy();
3156
+ }
3157
+ }
3158
+ /** @deprecated Use {@link destroy}. */
3159
+ delete() {
3160
+ this.destroy();
3161
+ }
3162
+ /** Run one transform loop. */
3163
+ run(options) {
3164
+ if (options?.inputBuffers) {
3165
+ this.model.setAttributes(options.inputBuffers);
3166
+ }
3167
+ if (options?.outputBuffers) {
3168
+ this.transformFeedback.setBuffers(options.outputBuffers);
3169
+ }
3170
+ const renderPass = this.device.beginRenderPass(options);
3171
+ this.model.draw(renderPass);
3172
+ renderPass.end();
3173
+ }
3174
+ // DEPRECATED METHODS
3175
+ /** @deprecated App knows what buffers it is passing in - Returns the {@link Buffer} or {@link BufferRange} for given varying name. */
3176
+ getBuffer(varyingName) {
3177
+ return this.transformFeedback.getBuffer(varyingName);
3178
+ }
3179
+ /** @deprecated App knows what buffers it is passing in - Reads the {@link Buffer} or {@link BufferRange} for given varying name. */
3180
+ readAsync(varyingName) {
3181
+ const result = this.getBuffer(varyingName);
3182
+ if (!result) {
3183
+ throw new Error("BufferTransform#getBuffer");
3184
+ }
3185
+ if (result instanceof import_core10.Buffer) {
3186
+ return result.readAsync();
3187
+ }
3188
+ const { buffer, byteOffset = 0, byteLength = buffer.byteLength } = result;
3189
+ return buffer.readAsync(byteOffset, byteLength);
3190
+ }
3191
+ };
3192
+ var BufferTransform = _BufferTransform;
3193
+ __publicField(BufferTransform, "defaultProps", {
2983
3194
  ...Model.defaultProps,
2984
3195
  outputs: void 0,
2985
3196
  feedbackBuffers: void 0
@@ -3371,6 +3582,84 @@ void main(void) {
3371
3582
  }
3372
3583
  };
3373
3584
 
3585
+ // src/geometries/sphere-geometry.ts
3586
+ var SphereGeometry = class extends Geometry {
3587
+ constructor(props = {}) {
3588
+ const { id = uid("sphere-geometry") } = props;
3589
+ const { indices, attributes } = tesselateSphere(props);
3590
+ super({
3591
+ ...props,
3592
+ id,
3593
+ topology: "triangle-list",
3594
+ indices,
3595
+ attributes: { ...attributes, ...props.attributes }
3596
+ });
3597
+ }
3598
+ };
3599
+ function tesselateSphere(props) {
3600
+ const { nlat = 10, nlong = 10 } = props;
3601
+ const startLat = 0;
3602
+ const endLat = Math.PI;
3603
+ const latRange = endLat - startLat;
3604
+ const startLong = 0;
3605
+ const endLong = 2 * Math.PI;
3606
+ const longRange = endLong - startLong;
3607
+ const numVertices = (nlat + 1) * (nlong + 1);
3608
+ const radius = (n1, n2, n3, u, v) => props.radius || 1;
3609
+ const positions = new Float32Array(numVertices * 3);
3610
+ const normals = new Float32Array(numVertices * 3);
3611
+ const texCoords = new Float32Array(numVertices * 2);
3612
+ const IndexType = numVertices > 65535 ? Uint32Array : Uint16Array;
3613
+ const indices = new IndexType(nlat * nlong * 6);
3614
+ for (let y = 0; y <= nlat; y++) {
3615
+ for (let x = 0; x <= nlong; x++) {
3616
+ const u = x / nlong;
3617
+ const v = y / nlat;
3618
+ const index = x + y * (nlong + 1);
3619
+ const i2 = index * 2;
3620
+ const i3 = index * 3;
3621
+ const theta = longRange * u;
3622
+ const phi = latRange * v;
3623
+ const sinTheta = Math.sin(theta);
3624
+ const cosTheta = Math.cos(theta);
3625
+ const sinPhi = Math.sin(phi);
3626
+ const cosPhi = Math.cos(phi);
3627
+ const ux = cosTheta * sinPhi;
3628
+ const uy = cosPhi;
3629
+ const uz = sinTheta * sinPhi;
3630
+ const r = radius(ux, uy, uz, u, v);
3631
+ positions[i3 + 0] = r * ux;
3632
+ positions[i3 + 1] = r * uy;
3633
+ positions[i3 + 2] = r * uz;
3634
+ normals[i3 + 0] = ux;
3635
+ normals[i3 + 1] = uy;
3636
+ normals[i3 + 2] = uz;
3637
+ texCoords[i2 + 0] = u;
3638
+ texCoords[i2 + 1] = 1 - v;
3639
+ }
3640
+ }
3641
+ const numVertsAround = nlong + 1;
3642
+ for (let x = 0; x < nlong; x++) {
3643
+ for (let y = 0; y < nlat; y++) {
3644
+ const index = (x * nlat + y) * 6;
3645
+ indices[index + 0] = y * numVertsAround + x;
3646
+ indices[index + 1] = y * numVertsAround + x + 1;
3647
+ indices[index + 2] = (y + 1) * numVertsAround + x;
3648
+ indices[index + 3] = (y + 1) * numVertsAround + x;
3649
+ indices[index + 4] = y * numVertsAround + x + 1;
3650
+ indices[index + 5] = (y + 1) * numVertsAround + x + 1;
3651
+ }
3652
+ }
3653
+ return {
3654
+ indices: { size: 1, value: indices },
3655
+ attributes: {
3656
+ POSITION: { size: 3, value: positions },
3657
+ NORMAL: { size: 3, value: normals },
3658
+ TEXCOORD_0: { size: 2, value: texCoords }
3659
+ }
3660
+ };
3661
+ }
3662
+
3374
3663
  // ../../node_modules/@math.gl/core/dist/lib/common.js
3375
3664
  var RADIANS_TO_DEGREES = 1 / Math.PI * 180;
3376
3665
  var DEGREES_TO_RADIANS = 1 / 180 * Math.PI;
@@ -5406,28 +5695,616 @@ void main(void) {
5406
5695
  return result;
5407
5696
  }
5408
5697
 
5409
- // src/scenegraph/scenegraph-node.ts
5410
- function assert2(condition, message) {
5411
- if (!condition) {
5412
- throw new Error(message);
5413
- }
5414
- }
5415
- var ScenegraphNode = class {
5416
- id;
5417
- matrix = new Matrix4();
5418
- display = true;
5419
- position = new Vector3();
5420
- rotation = new Vector3();
5421
- scale = new Vector3(1, 1, 1);
5422
- userData = {};
5423
- props = {};
5424
- constructor(props = {}) {
5425
- const { id } = props;
5426
- this.id = id || uid(this.constructor.name);
5427
- this._setScenegraphNodeProps(props);
5698
+ // src/models/light-model-utils.ts
5699
+ var DEFAULT_POINT_LIGHT_RADIUS_FACTOR = 0.02;
5700
+ var DEFAULT_SPOT_LIGHT_LENGTH_FACTOR = 0.12;
5701
+ var DEFAULT_DIRECTIONAL_LIGHT_LENGTH_FACTOR = 0.15;
5702
+ var DEFAULT_DIRECTIONAL_LIGHT_RADIUS_FACTOR = 0.2;
5703
+ var DEFAULT_DIRECTION_FALLBACK = [0, 1, 0];
5704
+ var DEFAULT_LIGHT_COLOR = [255, 255, 255];
5705
+ var DEFAULT_MARKER_SCALE = 1;
5706
+ var DIRECTIONAL_ANCHOR_DISTANCE_FACTOR = 0.35;
5707
+ var LIGHT_COLOR_FACTOR = 255;
5708
+ var MIN_SCENE_SCALE = 1;
5709
+ var SPOTLIGHT_OUTER_CONE_EPSILON = 0.01;
5710
+ var LIGHT_MARKER_PARAMETERS = {
5711
+ depthCompare: "less-equal",
5712
+ depthWriteEnabled: false,
5713
+ cullMode: "none"
5714
+ };
5715
+ var INSTANCE_BUFFER_LAYOUT = [
5716
+ { name: "instancePosition", format: "float32x3", stepMode: "instance" },
5717
+ { name: "instanceDirection", format: "float32x3", stepMode: "instance" },
5718
+ { name: "instanceScale", format: "float32x3", stepMode: "instance" },
5719
+ { name: "instanceColor", format: "float32x4", stepMode: "instance" }
5720
+ ];
5721
+ var lightMarker = {
5722
+ name: "lightMarker",
5723
+ props: {},
5724
+ uniforms: {},
5725
+ uniformTypes: {
5726
+ viewProjectionMatrix: "mat4x4<f32>"
5428
5727
  }
5429
- getBounds() {
5430
- return null;
5728
+ };
5729
+ var CENTERED_LOCAL_POSITION_WGSL = "inputs.positions * inputs.instanceScale";
5730
+ var APEX_LOCAL_POSITION_WGSL = "vec3<f32>(inputs.positions.x * inputs.instanceScale.x, (inputs.positions.y - 0.5) * inputs.instanceScale.y, inputs.positions.z * inputs.instanceScale.z)";
5731
+ var CENTERED_LOCAL_POSITION_GLSL = "positions * instanceScale";
5732
+ var APEX_LOCAL_POSITION_GLSL = "vec3(positions.x * instanceScale.x, (positions.y - 0.5) * instanceScale.y, positions.z * instanceScale.z)";
5733
+ var BaseLightModel = class extends Model {
5734
+ lightModelProps;
5735
+ _instanceData;
5736
+ _managedBuffers;
5737
+ buildInstanceData;
5738
+ sizePropNames;
5739
+ constructor(device, props, options) {
5740
+ const instanceData = options.buildInstanceData(props);
5741
+ const managedBuffers = createManagedInstanceBuffers(
5742
+ device,
5743
+ props.id || options.idPrefix,
5744
+ instanceData
5745
+ );
5746
+ const shaderInputs = new ShaderInputs({ lightMarker });
5747
+ shaderInputs.setProps({
5748
+ lightMarker: { viewProjectionMatrix: createViewProjectionMatrix(props) }
5749
+ });
5750
+ const { source: source3, vs: vs3, fs: fs3 } = getLightMarkerShaders(options.anchorMode);
5751
+ const modelProps = props;
5752
+ super(device, {
5753
+ ...modelProps,
5754
+ id: props.id || options.idPrefix,
5755
+ source: source3,
5756
+ vs: vs3,
5757
+ fs: fs3,
5758
+ geometry: options.geometry,
5759
+ shaderInputs,
5760
+ bufferLayout: [...INSTANCE_BUFFER_LAYOUT],
5761
+ attributes: managedBuffers,
5762
+ instanceCount: instanceData.instanceCount,
5763
+ parameters: mergeLightMarkerParameters(props.parameters)
5764
+ });
5765
+ this.lightModelProps = props;
5766
+ this._instanceData = instanceData;
5767
+ this._managedBuffers = managedBuffers;
5768
+ this.buildInstanceData = options.buildInstanceData;
5769
+ this.sizePropNames = options.sizePropNames;
5770
+ }
5771
+ destroy() {
5772
+ super.destroy();
5773
+ destroyManagedInstanceBuffers(this._managedBuffers);
5774
+ this._managedBuffers = {};
5775
+ }
5776
+ draw(renderPass) {
5777
+ if (this.instanceCount === 0) {
5778
+ return true;
5779
+ }
5780
+ return super.draw(renderPass);
5781
+ }
5782
+ setProps(props) {
5783
+ this.lightModelProps = { ...this.lightModelProps, ...props };
5784
+ if (props.parameters) {
5785
+ this.setParameters(mergeLightMarkerParameters(this.lightModelProps.parameters));
5786
+ }
5787
+ if ("viewMatrix" in props || "projectionMatrix" in props) {
5788
+ this.shaderInputs.setProps({
5789
+ lightMarker: { viewProjectionMatrix: createViewProjectionMatrix(this.lightModelProps) }
5790
+ });
5791
+ this.setNeedsRedraw("lightMarker camera");
5792
+ }
5793
+ if (shouldRebuildInstanceData(props, this.sizePropNames)) {
5794
+ this.rebuildInstanceData();
5795
+ }
5796
+ }
5797
+ rebuildInstanceData() {
5798
+ const nextInstanceData = this.buildInstanceData(this.lightModelProps);
5799
+ const nextManagedBuffers = createManagedInstanceBuffers(this.device, this.id, nextInstanceData);
5800
+ this.setAttributes(nextManagedBuffers);
5801
+ this.setInstanceCount(nextInstanceData.instanceCount);
5802
+ destroyManagedInstanceBuffers(this._managedBuffers);
5803
+ this._managedBuffers = nextManagedBuffers;
5804
+ this._instanceData = nextInstanceData;
5805
+ }
5806
+ };
5807
+ function buildPointLightInstanceData(props) {
5808
+ const pointLights = getPointLights(props.lights);
5809
+ const context = getLightMarkerContext(props);
5810
+ const pointLightRadius = props.pointLightRadius ?? DEFAULT_POINT_LIGHT_RADIUS_FACTOR * context.sceneScale * context.markerScale;
5811
+ return createLightMarkerInstanceData(
5812
+ pointLights.length,
5813
+ (light, _index) => ({
5814
+ color: getDisplayColor(light),
5815
+ direction: DEFAULT_DIRECTION_FALLBACK,
5816
+ position: light.position,
5817
+ scale: [pointLightRadius, pointLightRadius, pointLightRadius]
5818
+ }),
5819
+ pointLights
5820
+ );
5821
+ }
5822
+ function buildSpotLightInstanceData(props) {
5823
+ const spotLights = getSpotLights(props.lights);
5824
+ const context = getLightMarkerContext(props);
5825
+ const spotLightLength = props.spotLightLength ?? DEFAULT_SPOT_LIGHT_LENGTH_FACTOR * context.sceneScale * context.markerScale;
5826
+ return createLightMarkerInstanceData(
5827
+ spotLights.length,
5828
+ (light, _index) => {
5829
+ const outerConeAngle = clamp(
5830
+ light.outerConeAngle ?? Math.PI / 4,
5831
+ 0,
5832
+ Math.PI / 2 - SPOTLIGHT_OUTER_CONE_EPSILON
5833
+ );
5834
+ const radius = Math.tan(outerConeAngle) * spotLightLength;
5835
+ return {
5836
+ color: getDisplayColor(light),
5837
+ direction: normalizeDirection(light.direction),
5838
+ position: light.position,
5839
+ scale: [radius, spotLightLength, radius]
5840
+ };
5841
+ },
5842
+ spotLights
5843
+ );
5844
+ }
5845
+ function buildDirectionalLightInstanceData(props) {
5846
+ const directionalLights = getDirectionalLights(props.lights);
5847
+ const context = getLightMarkerContext(props);
5848
+ const directionalLightLength = props.directionalLightLength ?? DEFAULT_DIRECTIONAL_LIGHT_LENGTH_FACTOR * context.sceneScale * context.markerScale;
5849
+ const directionalLightRadius = directionalLightLength * DEFAULT_DIRECTIONAL_LIGHT_RADIUS_FACTOR;
5850
+ return createLightMarkerInstanceData(
5851
+ directionalLights.length,
5852
+ (light, _index) => {
5853
+ const direction = normalizeDirection(light.direction);
5854
+ const position = [
5855
+ context.sceneCenter[0] - direction[0] * context.sceneScale * DIRECTIONAL_ANCHOR_DISTANCE_FACTOR,
5856
+ context.sceneCenter[1] - direction[1] * context.sceneScale * DIRECTIONAL_ANCHOR_DISTANCE_FACTOR,
5857
+ context.sceneCenter[2] - direction[2] * context.sceneScale * DIRECTIONAL_ANCHOR_DISTANCE_FACTOR
5858
+ ];
5859
+ return {
5860
+ color: getDisplayColor(light),
5861
+ direction,
5862
+ position,
5863
+ scale: [directionalLightRadius, directionalLightLength, directionalLightRadius]
5864
+ };
5865
+ },
5866
+ directionalLights
5867
+ );
5868
+ }
5869
+ function getPointLights(lights) {
5870
+ return lights.filter((light) => light.type === "point");
5871
+ }
5872
+ function getSpotLights(lights) {
5873
+ return lights.filter((light) => light.type === "spot");
5874
+ }
5875
+ function getDirectionalLights(lights) {
5876
+ return lights.filter((light) => light.type === "directional");
5877
+ }
5878
+ function getLightMarkerContext(props) {
5879
+ const bounds = getSceneBounds(props.lights, props.bounds);
5880
+ const sceneCenter = [
5881
+ (bounds[0][0] + bounds[1][0]) / 2,
5882
+ (bounds[0][1] + bounds[1][1]) / 2,
5883
+ (bounds[0][2] + bounds[1][2]) / 2
5884
+ ];
5885
+ const sceneScale = Math.max(
5886
+ Math.hypot(
5887
+ bounds[1][0] - bounds[0][0],
5888
+ bounds[1][1] - bounds[0][1],
5889
+ bounds[1][2] - bounds[0][2]
5890
+ ),
5891
+ MIN_SCENE_SCALE
5892
+ );
5893
+ return {
5894
+ bounds,
5895
+ markerScale: Math.max(props.markerScale ?? DEFAULT_MARKER_SCALE, 0),
5896
+ sceneCenter,
5897
+ sceneScale
5898
+ };
5899
+ }
5900
+ function getDisplayColor(light) {
5901
+ const color = light.color || DEFAULT_LIGHT_COLOR;
5902
+ const intensity = Math.max(light.intensity ?? 1, 0);
5903
+ const brightness = clamp(0.35 + 0.3 * Math.log10(intensity + 1), 0.35, 1);
5904
+ return [
5905
+ clamp(color[0] / LIGHT_COLOR_FACTOR, 0, 1) * brightness,
5906
+ clamp(color[1] / LIGHT_COLOR_FACTOR, 0, 1) * brightness,
5907
+ clamp(color[2] / LIGHT_COLOR_FACTOR, 0, 1) * brightness,
5908
+ 1
5909
+ ];
5910
+ }
5911
+ function normalizeDirection(direction) {
5912
+ const [x, y, z] = direction || DEFAULT_DIRECTION_FALLBACK;
5913
+ const length = Math.hypot(x, y, z);
5914
+ if (length === 0) {
5915
+ return [...DEFAULT_DIRECTION_FALLBACK];
5916
+ }
5917
+ return [x / length, y / length, z / length];
5918
+ }
5919
+ function createLightMarkerInstanceData(instanceCount, getInstance, lights = []) {
5920
+ const instancePositions = new Float32Array(instanceCount * 3);
5921
+ const instanceDirections = new Float32Array(instanceCount * 3);
5922
+ const instanceScales = new Float32Array(instanceCount * 3);
5923
+ const instanceColors = new Float32Array(instanceCount * 4);
5924
+ for (const [index, light] of lights.entries()) {
5925
+ const instance = getInstance(light, index);
5926
+ instancePositions.set(instance.position, index * 3);
5927
+ instanceDirections.set(instance.direction, index * 3);
5928
+ instanceScales.set(instance.scale, index * 3);
5929
+ instanceColors.set(instance.color, index * 4);
5930
+ }
5931
+ return {
5932
+ instanceCount,
5933
+ instancePositions,
5934
+ instanceDirections,
5935
+ instanceScales,
5936
+ instanceColors
5937
+ };
5938
+ }
5939
+ function getSceneBounds(lights, bounds) {
5940
+ if (bounds) {
5941
+ return cloneBounds(bounds);
5942
+ }
5943
+ const positions = [
5944
+ ...getPointLights(lights).map((light) => light.position),
5945
+ ...getSpotLights(lights).map((light) => light.position)
5946
+ ];
5947
+ if (positions.length === 0) {
5948
+ return [
5949
+ [-0.5, -0.5, -0.5],
5950
+ [0.5, 0.5, 0.5]
5951
+ ];
5952
+ }
5953
+ const minBounds = [...positions[0]];
5954
+ const maxBounds = [...positions[0]];
5955
+ for (const position of positions.slice(1)) {
5956
+ minBounds[0] = Math.min(minBounds[0], position[0]);
5957
+ minBounds[1] = Math.min(minBounds[1], position[1]);
5958
+ minBounds[2] = Math.min(minBounds[2], position[2]);
5959
+ maxBounds[0] = Math.max(maxBounds[0], position[0]);
5960
+ maxBounds[1] = Math.max(maxBounds[1], position[1]);
5961
+ maxBounds[2] = Math.max(maxBounds[2], position[2]);
5962
+ }
5963
+ return [minBounds, maxBounds];
5964
+ }
5965
+ function cloneBounds(bounds) {
5966
+ return [[...bounds[0]], [...bounds[1]]];
5967
+ }
5968
+ function createManagedInstanceBuffers(device, idPrefix, instanceData) {
5969
+ return {
5970
+ instancePosition: device.createBuffer({
5971
+ id: `${idPrefix}-instance-position`,
5972
+ data: getBufferDataOrPlaceholder(instanceData.instancePositions, 3)
5973
+ }),
5974
+ instanceDirection: device.createBuffer({
5975
+ id: `${idPrefix}-instance-direction`,
5976
+ data: getBufferDataOrPlaceholder(instanceData.instanceDirections, 3)
5977
+ }),
5978
+ instanceScale: device.createBuffer({
5979
+ id: `${idPrefix}-instance-scale`,
5980
+ data: getBufferDataOrPlaceholder(instanceData.instanceScales, 3)
5981
+ }),
5982
+ instanceColor: device.createBuffer({
5983
+ id: `${idPrefix}-instance-color`,
5984
+ data: getBufferDataOrPlaceholder(instanceData.instanceColors, 4)
5985
+ })
5986
+ };
5987
+ }
5988
+ function getBufferDataOrPlaceholder(data, size) {
5989
+ return data.length > 0 ? data : new Float32Array(size);
5990
+ }
5991
+ function destroyManagedInstanceBuffers(managedBuffers) {
5992
+ for (const buffer of Object.values(managedBuffers)) {
5993
+ buffer?.destroy();
5994
+ }
5995
+ }
5996
+ function createViewProjectionMatrix(props) {
5997
+ return new Matrix4(props.projectionMatrix).multiplyRight(props.viewMatrix);
5998
+ }
5999
+ function shouldRebuildInstanceData(props, sizePropNames) {
6000
+ if ("lights" in props || "bounds" in props || "markerScale" in props) {
6001
+ return true;
6002
+ }
6003
+ return sizePropNames.some((sizePropName) => sizePropName in props);
6004
+ }
6005
+ function mergeLightMarkerParameters(parameters) {
6006
+ return {
6007
+ ...LIGHT_MARKER_PARAMETERS,
6008
+ ...parameters || {}
6009
+ };
6010
+ }
6011
+ function getLightMarkerShaders(anchorMode) {
6012
+ const localPositionWGSL = anchorMode === "apex" ? APEX_LOCAL_POSITION_WGSL : CENTERED_LOCAL_POSITION_WGSL;
6013
+ const localPositionGLSL = anchorMode === "apex" ? APEX_LOCAL_POSITION_GLSL : CENTERED_LOCAL_POSITION_GLSL;
6014
+ return {
6015
+ source: `struct lightMarkerUniforms {
6016
+ viewProjectionMatrix: mat4x4<f32>,
6017
+ };
6018
+
6019
+ @binding(0) @group(0) var<uniform> lightMarker : lightMarkerUniforms;
6020
+
6021
+ struct VertexInputs {
6022
+ @location(0) positions : vec3<f32>,
6023
+ @location(1) instancePosition : vec3<f32>,
6024
+ @location(2) instanceDirection : vec3<f32>,
6025
+ @location(3) instanceScale : vec3<f32>,
6026
+ @location(4) instanceColor : vec4<f32>,
6027
+ };
6028
+
6029
+ struct FragmentInputs {
6030
+ @builtin(position) Position : vec4<f32>,
6031
+ @location(0) color : vec4<f32>,
6032
+ };
6033
+
6034
+ fn lightMarker_rotate(localPosition: vec3<f32>, direction: vec3<f32>) -> vec3<f32> {
6035
+ let forward = normalize(direction);
6036
+ var helperAxis = vec3<f32>(0.0, 1.0, 0.0);
6037
+ if (abs(forward.y) > 0.999) {
6038
+ helperAxis = vec3<f32>(1.0, 0.0, 0.0);
6039
+ }
6040
+
6041
+ let tangent = normalize(cross(helperAxis, forward));
6042
+ let bitangent = cross(forward, tangent);
6043
+ return tangent * localPosition.x + forward * localPosition.y + bitangent * localPosition.z;
6044
+ }
6045
+
6046
+ @vertex
6047
+ fn vertexMain(inputs: VertexInputs) -> FragmentInputs {
6048
+ var outputs : FragmentInputs;
6049
+ let localPosition = ${localPositionWGSL};
6050
+ let worldPosition = inputs.instancePosition + lightMarker_rotate(localPosition, inputs.instanceDirection);
6051
+ outputs.Position = lightMarker.viewProjectionMatrix * vec4<f32>(worldPosition, 1.0);
6052
+ outputs.color = inputs.instanceColor;
6053
+ return outputs;
6054
+ }
6055
+
6056
+ @fragment
6057
+ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4<f32> {
6058
+ return inputs.color;
6059
+ }
6060
+ `,
6061
+ vs: `#version 300 es
6062
+
6063
+ in vec3 positions;
6064
+ in vec3 instancePosition;
6065
+ in vec3 instanceDirection;
6066
+ in vec3 instanceScale;
6067
+ in vec4 instanceColor;
6068
+
6069
+ uniform lightMarkerUniforms {
6070
+ mat4 viewProjectionMatrix;
6071
+ } lightMarker;
6072
+
6073
+ out vec4 vColor;
6074
+
6075
+ vec3 lightMarker_rotate(vec3 localPosition, vec3 direction) {
6076
+ vec3 forward = normalize(direction);
6077
+ vec3 helperAxis = abs(forward.y) > 0.999 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);
6078
+ vec3 tangent = normalize(cross(helperAxis, forward));
6079
+ vec3 bitangent = cross(forward, tangent);
6080
+ return tangent * localPosition.x + forward * localPosition.y + bitangent * localPosition.z;
6081
+ }
6082
+
6083
+ void main(void) {
6084
+ vec3 localPosition = ${localPositionGLSL};
6085
+ vec3 worldPosition = instancePosition + lightMarker_rotate(localPosition, instanceDirection);
6086
+ gl_Position = lightMarker.viewProjectionMatrix * vec4(worldPosition, 1.0);
6087
+ vColor = instanceColor;
6088
+ }
6089
+ `,
6090
+ fs: `#version 300 es
6091
+ precision highp float;
6092
+
6093
+ in vec4 vColor;
6094
+ out vec4 fragColor;
6095
+
6096
+ void main(void) {
6097
+ fragColor = vColor;
6098
+ }
6099
+ `
6100
+ };
6101
+ }
6102
+ function clamp(value, minValue, maxValue) {
6103
+ return Math.min(maxValue, Math.max(minValue, value));
6104
+ }
6105
+
6106
+ // src/models/point-light-model.ts
6107
+ var POINT_LIGHT_GEOMETRY = new SphereGeometry({
6108
+ nlat: 8,
6109
+ nlong: 12,
6110
+ radius: 1
6111
+ });
6112
+ var PointLightModel = class extends BaseLightModel {
6113
+ constructor(device, props) {
6114
+ super(device, props, {
6115
+ anchorMode: "centered",
6116
+ buildInstanceData: buildPointLightInstanceData,
6117
+ geometry: POINT_LIGHT_GEOMETRY,
6118
+ idPrefix: "point-light-model",
6119
+ sizePropNames: ["pointLightRadius"]
6120
+ });
6121
+ }
6122
+ };
6123
+
6124
+ // src/geometries/truncated-cone-geometry.ts
6125
+ var INDEX_OFFSETS = {
6126
+ x: [2, 0, 1],
6127
+ y: [0, 1, 2],
6128
+ z: [1, 2, 0]
6129
+ };
6130
+ var TruncatedConeGeometry = class extends Geometry {
6131
+ constructor(props = {}) {
6132
+ const { id = uid("truncated-code-geometry") } = props;
6133
+ const { indices, attributes } = tesselateTruncatedCone(props);
6134
+ super({
6135
+ ...props,
6136
+ id,
6137
+ topology: "triangle-list",
6138
+ indices,
6139
+ attributes: {
6140
+ POSITION: { size: 3, value: attributes.POSITION },
6141
+ NORMAL: { size: 3, value: attributes.NORMAL },
6142
+ TEXCOORD_0: { size: 2, value: attributes.TEXCOORD_0 },
6143
+ ...props.attributes
6144
+ }
6145
+ });
6146
+ }
6147
+ };
6148
+ function tesselateTruncatedCone(props = {}) {
6149
+ const {
6150
+ bottomRadius = 0,
6151
+ topRadius = 0,
6152
+ height = 1,
6153
+ nradial = 10,
6154
+ nvertical = 10,
6155
+ verticalAxis = "y",
6156
+ topCap = false,
6157
+ bottomCap = false
6158
+ } = props;
6159
+ const extra = (topCap ? 2 : 0) + (bottomCap ? 2 : 0);
6160
+ const numVertices = (nradial + 1) * (nvertical + 1 + extra);
6161
+ const slant = Math.atan2(bottomRadius - topRadius, height);
6162
+ const msin = Math.sin;
6163
+ const mcos = Math.cos;
6164
+ const mpi = Math.PI;
6165
+ const cosSlant = mcos(slant);
6166
+ const sinSlant = msin(slant);
6167
+ const start = topCap ? -2 : 0;
6168
+ const end = nvertical + (bottomCap ? 2 : 0);
6169
+ const vertsAroundEdge = nradial + 1;
6170
+ const indices = new Uint16Array(nradial * (nvertical + extra) * 6);
6171
+ const indexOffset = INDEX_OFFSETS[verticalAxis];
6172
+ const positions = new Float32Array(numVertices * 3);
6173
+ const normals = new Float32Array(numVertices * 3);
6174
+ const texCoords = new Float32Array(numVertices * 2);
6175
+ let i3 = 0;
6176
+ let i2 = 0;
6177
+ for (let i = start; i <= end; i++) {
6178
+ let v = i / nvertical;
6179
+ let y = height * v;
6180
+ let ringRadius;
6181
+ if (i < 0) {
6182
+ y = 0;
6183
+ v = 1;
6184
+ ringRadius = bottomRadius;
6185
+ } else if (i > nvertical) {
6186
+ y = height;
6187
+ v = 1;
6188
+ ringRadius = topRadius;
6189
+ } else {
6190
+ ringRadius = bottomRadius + (topRadius - bottomRadius) * (i / nvertical);
6191
+ }
6192
+ if (i === -2 || i === nvertical + 2) {
6193
+ ringRadius = 0;
6194
+ v = 0;
6195
+ }
6196
+ y -= height / 2;
6197
+ for (let j = 0; j < vertsAroundEdge; j++) {
6198
+ const sin = msin(j * mpi * 2 / nradial);
6199
+ const cos = mcos(j * mpi * 2 / nradial);
6200
+ positions[i3 + indexOffset[0]] = sin * ringRadius;
6201
+ positions[i3 + indexOffset[1]] = y;
6202
+ positions[i3 + indexOffset[2]] = cos * ringRadius;
6203
+ normals[i3 + indexOffset[0]] = i < 0 || i > nvertical ? 0 : sin * cosSlant;
6204
+ normals[i3 + indexOffset[1]] = i < 0 ? -1 : i > nvertical ? 1 : sinSlant;
6205
+ normals[i3 + indexOffset[2]] = i < 0 || i > nvertical ? 0 : cos * cosSlant;
6206
+ texCoords[i2 + 0] = j / nradial;
6207
+ texCoords[i2 + 1] = v;
6208
+ i2 += 2;
6209
+ i3 += 3;
6210
+ }
6211
+ }
6212
+ for (let i = 0; i < nvertical + extra; i++) {
6213
+ for (let j = 0; j < nradial; j++) {
6214
+ const index = (i * nradial + j) * 6;
6215
+ indices[index + 0] = vertsAroundEdge * (i + 0) + 0 + j;
6216
+ indices[index + 1] = vertsAroundEdge * (i + 0) + 1 + j;
6217
+ indices[index + 2] = vertsAroundEdge * (i + 1) + 1 + j;
6218
+ indices[index + 3] = vertsAroundEdge * (i + 0) + 0 + j;
6219
+ indices[index + 4] = vertsAroundEdge * (i + 1) + 1 + j;
6220
+ indices[index + 5] = vertsAroundEdge * (i + 1) + 0 + j;
6221
+ }
6222
+ }
6223
+ return {
6224
+ indices,
6225
+ attributes: {
6226
+ POSITION: positions,
6227
+ NORMAL: normals,
6228
+ TEXCOORD_0: texCoords
6229
+ }
6230
+ };
6231
+ }
6232
+
6233
+ // src/geometries/cone-geometry.ts
6234
+ var ConeGeometry = class extends TruncatedConeGeometry {
6235
+ constructor(props = {}) {
6236
+ const { id = uid("cone-geometry"), radius = 1, cap = true } = props;
6237
+ super({
6238
+ ...props,
6239
+ id,
6240
+ topRadius: 0,
6241
+ topCap: Boolean(cap),
6242
+ bottomCap: Boolean(cap),
6243
+ bottomRadius: radius
6244
+ });
6245
+ }
6246
+ };
6247
+
6248
+ // src/models/spot-light-model.ts
6249
+ var SPOT_LIGHT_GEOMETRY = new ConeGeometry({
6250
+ cap: true,
6251
+ nradial: 16,
6252
+ nvertical: 1,
6253
+ radius: 1
6254
+ });
6255
+ var SpotLightModel = class extends BaseLightModel {
6256
+ constructor(device, props) {
6257
+ super(device, props, {
6258
+ anchorMode: "apex",
6259
+ buildInstanceData: buildSpotLightInstanceData,
6260
+ geometry: SPOT_LIGHT_GEOMETRY,
6261
+ idPrefix: "spot-light-model",
6262
+ sizePropNames: ["spotLightLength"]
6263
+ });
6264
+ }
6265
+ };
6266
+
6267
+ // src/models/directional-light-model.ts
6268
+ var DIRECTIONAL_LIGHT_GEOMETRY = new ConeGeometry({
6269
+ cap: true,
6270
+ nradial: 12,
6271
+ nvertical: 1,
6272
+ radius: 1
6273
+ });
6274
+ var DirectionalLightModel = class extends BaseLightModel {
6275
+ constructor(device, props) {
6276
+ super(device, props, {
6277
+ anchorMode: "apex",
6278
+ buildInstanceData: buildDirectionalLightInstanceData,
6279
+ geometry: DIRECTIONAL_LIGHT_GEOMETRY,
6280
+ idPrefix: "directional-light-model",
6281
+ sizePropNames: ["directionalLightLength"]
6282
+ });
6283
+ }
6284
+ };
6285
+
6286
+ // src/scenegraph/scenegraph-node.ts
6287
+ function assert2(condition, message) {
6288
+ if (!condition) {
6289
+ throw new Error(message);
6290
+ }
6291
+ }
6292
+ var ScenegraphNode = class {
6293
+ id;
6294
+ matrix = new Matrix4();
6295
+ display = true;
6296
+ position = new Vector3();
6297
+ rotation = new Vector3();
6298
+ scale = new Vector3(1, 1, 1);
6299
+ userData = {};
6300
+ props = {};
6301
+ constructor(props = {}) {
6302
+ const { id } = props;
6303
+ this.id = id || uid(this.constructor.name);
6304
+ this._setScenegraphNodeProps(props);
6305
+ }
6306
+ getBounds() {
6307
+ return null;
5431
6308
  }
5432
6309
  destroy() {
5433
6310
  }
@@ -5685,130 +6562,6 @@ void main(void) {
5685
6562
  }
5686
6563
  };
5687
6564
 
5688
- // src/geometries/truncated-cone-geometry.ts
5689
- var INDEX_OFFSETS = {
5690
- x: [2, 0, 1],
5691
- y: [0, 1, 2],
5692
- z: [1, 2, 0]
5693
- };
5694
- var TruncatedConeGeometry = class extends Geometry {
5695
- constructor(props = {}) {
5696
- const { id = uid("truncated-code-geometry") } = props;
5697
- const { indices, attributes } = tesselateTruncatedCone(props);
5698
- super({
5699
- ...props,
5700
- id,
5701
- topology: "triangle-list",
5702
- indices,
5703
- attributes: {
5704
- POSITION: { size: 3, value: attributes.POSITION },
5705
- NORMAL: { size: 3, value: attributes.NORMAL },
5706
- TEXCOORD_0: { size: 2, value: attributes.TEXCOORD_0 },
5707
- ...props.attributes
5708
- }
5709
- });
5710
- }
5711
- };
5712
- function tesselateTruncatedCone(props = {}) {
5713
- const {
5714
- bottomRadius = 0,
5715
- topRadius = 0,
5716
- height = 1,
5717
- nradial = 10,
5718
- nvertical = 10,
5719
- verticalAxis = "y",
5720
- topCap = false,
5721
- bottomCap = false
5722
- } = props;
5723
- const extra = (topCap ? 2 : 0) + (bottomCap ? 2 : 0);
5724
- const numVertices = (nradial + 1) * (nvertical + 1 + extra);
5725
- const slant = Math.atan2(bottomRadius - topRadius, height);
5726
- const msin = Math.sin;
5727
- const mcos = Math.cos;
5728
- const mpi = Math.PI;
5729
- const cosSlant = mcos(slant);
5730
- const sinSlant = msin(slant);
5731
- const start = topCap ? -2 : 0;
5732
- const end = nvertical + (bottomCap ? 2 : 0);
5733
- const vertsAroundEdge = nradial + 1;
5734
- const indices = new Uint16Array(nradial * (nvertical + extra) * 6);
5735
- const indexOffset = INDEX_OFFSETS[verticalAxis];
5736
- const positions = new Float32Array(numVertices * 3);
5737
- const normals = new Float32Array(numVertices * 3);
5738
- const texCoords = new Float32Array(numVertices * 2);
5739
- let i3 = 0;
5740
- let i2 = 0;
5741
- for (let i = start; i <= end; i++) {
5742
- let v = i / nvertical;
5743
- let y = height * v;
5744
- let ringRadius;
5745
- if (i < 0) {
5746
- y = 0;
5747
- v = 1;
5748
- ringRadius = bottomRadius;
5749
- } else if (i > nvertical) {
5750
- y = height;
5751
- v = 1;
5752
- ringRadius = topRadius;
5753
- } else {
5754
- ringRadius = bottomRadius + (topRadius - bottomRadius) * (i / nvertical);
5755
- }
5756
- if (i === -2 || i === nvertical + 2) {
5757
- ringRadius = 0;
5758
- v = 0;
5759
- }
5760
- y -= height / 2;
5761
- for (let j = 0; j < vertsAroundEdge; j++) {
5762
- const sin = msin(j * mpi * 2 / nradial);
5763
- const cos = mcos(j * mpi * 2 / nradial);
5764
- positions[i3 + indexOffset[0]] = sin * ringRadius;
5765
- positions[i3 + indexOffset[1]] = y;
5766
- positions[i3 + indexOffset[2]] = cos * ringRadius;
5767
- normals[i3 + indexOffset[0]] = i < 0 || i > nvertical ? 0 : sin * cosSlant;
5768
- normals[i3 + indexOffset[1]] = i < 0 ? -1 : i > nvertical ? 1 : sinSlant;
5769
- normals[i3 + indexOffset[2]] = i < 0 || i > nvertical ? 0 : cos * cosSlant;
5770
- texCoords[i2 + 0] = j / nradial;
5771
- texCoords[i2 + 1] = v;
5772
- i2 += 2;
5773
- i3 += 3;
5774
- }
5775
- }
5776
- for (let i = 0; i < nvertical + extra; i++) {
5777
- for (let j = 0; j < nradial; j++) {
5778
- const index = (i * nradial + j) * 6;
5779
- indices[index + 0] = vertsAroundEdge * (i + 0) + 0 + j;
5780
- indices[index + 1] = vertsAroundEdge * (i + 0) + 1 + j;
5781
- indices[index + 2] = vertsAroundEdge * (i + 1) + 1 + j;
5782
- indices[index + 3] = vertsAroundEdge * (i + 0) + 0 + j;
5783
- indices[index + 4] = vertsAroundEdge * (i + 1) + 1 + j;
5784
- indices[index + 5] = vertsAroundEdge * (i + 1) + 0 + j;
5785
- }
5786
- }
5787
- return {
5788
- indices,
5789
- attributes: {
5790
- POSITION: positions,
5791
- NORMAL: normals,
5792
- TEXCOORD_0: texCoords
5793
- }
5794
- };
5795
- }
5796
-
5797
- // src/geometries/cone-geometry.ts
5798
- var ConeGeometry = class extends TruncatedConeGeometry {
5799
- constructor(props = {}) {
5800
- const { id = uid("cone-geometry"), radius = 1, cap = true } = props;
5801
- super({
5802
- ...props,
5803
- id,
5804
- topRadius: 0,
5805
- topCap: Boolean(cap),
5806
- bottomCap: Boolean(cap),
5807
- bottomRadius: radius
5808
- });
5809
- }
5810
- };
5811
-
5812
6565
  // src/geometries/cube-geometry.ts
5813
6566
  var CubeGeometry = class extends Geometry {
5814
6567
  constructor(props = {}) {
@@ -6699,84 +7452,6 @@ void main(void) {
6699
7452
  return unpack ? unpackIndexedGeometry(geometry) : geometry;
6700
7453
  }
6701
7454
 
6702
- // src/geometries/sphere-geometry.ts
6703
- var SphereGeometry = class extends Geometry {
6704
- constructor(props = {}) {
6705
- const { id = uid("sphere-geometry") } = props;
6706
- const { indices, attributes } = tesselateSphere(props);
6707
- super({
6708
- ...props,
6709
- id,
6710
- topology: "triangle-list",
6711
- indices,
6712
- attributes: { ...attributes, ...props.attributes }
6713
- });
6714
- }
6715
- };
6716
- function tesselateSphere(props) {
6717
- const { nlat = 10, nlong = 10 } = props;
6718
- const startLat = 0;
6719
- const endLat = Math.PI;
6720
- const latRange = endLat - startLat;
6721
- const startLong = 0;
6722
- const endLong = 2 * Math.PI;
6723
- const longRange = endLong - startLong;
6724
- const numVertices = (nlat + 1) * (nlong + 1);
6725
- const radius = (n1, n2, n3, u, v) => props.radius || 1;
6726
- const positions = new Float32Array(numVertices * 3);
6727
- const normals = new Float32Array(numVertices * 3);
6728
- const texCoords = new Float32Array(numVertices * 2);
6729
- const IndexType = numVertices > 65535 ? Uint32Array : Uint16Array;
6730
- const indices = new IndexType(nlat * nlong * 6);
6731
- for (let y = 0; y <= nlat; y++) {
6732
- for (let x = 0; x <= nlong; x++) {
6733
- const u = x / nlong;
6734
- const v = y / nlat;
6735
- const index = x + y * (nlong + 1);
6736
- const i2 = index * 2;
6737
- const i3 = index * 3;
6738
- const theta = longRange * u;
6739
- const phi = latRange * v;
6740
- const sinTheta = Math.sin(theta);
6741
- const cosTheta = Math.cos(theta);
6742
- const sinPhi = Math.sin(phi);
6743
- const cosPhi = Math.cos(phi);
6744
- const ux = cosTheta * sinPhi;
6745
- const uy = cosPhi;
6746
- const uz = sinTheta * sinPhi;
6747
- const r = radius(ux, uy, uz, u, v);
6748
- positions[i3 + 0] = r * ux;
6749
- positions[i3 + 1] = r * uy;
6750
- positions[i3 + 2] = r * uz;
6751
- normals[i3 + 0] = ux;
6752
- normals[i3 + 1] = uy;
6753
- normals[i3 + 2] = uz;
6754
- texCoords[i2 + 0] = u;
6755
- texCoords[i2 + 1] = 1 - v;
6756
- }
6757
- }
6758
- const numVertsAround = nlong + 1;
6759
- for (let x = 0; x < nlong; x++) {
6760
- for (let y = 0; y < nlat; y++) {
6761
- const index = (x * nlat + y) * 6;
6762
- indices[index + 0] = y * numVertsAround + x;
6763
- indices[index + 1] = y * numVertsAround + x + 1;
6764
- indices[index + 2] = (y + 1) * numVertsAround + x;
6765
- indices[index + 3] = (y + 1) * numVertsAround + x;
6766
- indices[index + 4] = y * numVertsAround + x + 1;
6767
- indices[index + 5] = (y + 1) * numVertsAround + x + 1;
6768
- }
6769
- }
6770
- return {
6771
- indices: { size: 1, value: indices },
6772
- attributes: {
6773
- POSITION: { size: 3, value: positions },
6774
- NORMAL: { size: 3, value: normals },
6775
- TEXCOORD_0: { size: 2, value: texCoords }
6776
- }
6777
- };
6778
- }
6779
-
6780
7455
  // src/application-utils/random.ts
6781
7456
  function makeRandomGenerator() {
6782
7457
  let s = 1;
@@ -7203,11 +7878,11 @@ void main() {
7203
7878
  );
7204
7879
  this.shaderInputs = props.shaderInputs || new ShaderInputs(moduleMap);
7205
7880
  this.setShaderInputs(this.shaderInputs);
7206
- this.props.shaderLayout ||= device.getShaderLayout(this.props.source);
7207
7881
  const platformInfo = getPlatformInfo2(device);
7208
7882
  const modules = (this.props.modules?.length > 0 ? this.props.modules : this.shaderInputs?.getModules()) || [];
7209
- this.pipelineFactory = props.pipelineFactory || PipelineFactory.getDefaultPipelineFactory(this.device);
7210
- this.shaderFactory = props.shaderFactory || ShaderFactory.getDefaultShaderFactory(this.device);
7883
+ this.props.shaderLayout = mergeShaderModuleBindingsIntoLayout(this.props.shaderLayout, modules) || null;
7884
+ this.pipelineFactory = props.pipelineFactory || import_core17.PipelineFactory.getDefaultPipelineFactory(this.device);
7885
+ this.shaderFactory = props.shaderFactory || import_core17.ShaderFactory.getDefaultShaderFactory(this.device);
7211
7886
  const { source: source3, getUniforms: getUniforms2 } = this.props.shaderAssembler.assembleWGSLShader({
7212
7887
  platformInfo,
7213
7888
  ...this.props,
@@ -7215,6 +7890,11 @@ void main() {
7215
7890
  });
7216
7891
  this.source = source3;
7217
7892
  this._getModuleUniforms = getUniforms2;
7893
+ const inferredShaderLayout = device.getShaderLayout?.(this.source);
7894
+ this.props.shaderLayout = mergeShaderModuleBindingsIntoLayout(
7895
+ this.props.shaderLayout || inferredShaderLayout || null,
7896
+ modules
7897
+ ) || null;
7218
7898
  this.pipeline = this._updatePipeline();
7219
7899
  if (props.bindings) {
7220
7900
  this.setBindings(props.bindings);
@@ -7239,7 +7919,7 @@ void main() {
7239
7919
  this.pipeline = this._updatePipeline();
7240
7920
  this.pipeline.setBindings(this.bindings);
7241
7921
  computePass.setPipeline(this.pipeline);
7242
- computePass.setBindings([]);
7922
+ computePass.setBindings({});
7243
7923
  computePass.dispatch(x, y, z);
7244
7924
  } finally {
7245
7925
  this._logDrawCallEnd();
@@ -7262,9 +7942,11 @@ void main() {
7262
7942
  setShaderInputs(shaderInputs) {
7263
7943
  this.shaderInputs = shaderInputs;
7264
7944
  this._uniformStore = new import_core17.UniformStore(this.shaderInputs.modules);
7265
- for (const moduleName of Object.keys(this.shaderInputs.modules)) {
7266
- const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
7267
- this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
7945
+ for (const [moduleName, module] of Object.entries(this.shaderInputs.modules)) {
7946
+ if (shaderModuleHasUniforms(module)) {
7947
+ const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
7948
+ this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
7949
+ }
7268
7950
  }
7269
7951
  }
7270
7952
  /**
@@ -7344,7 +8026,7 @@ void main() {
7344
8026
  _drawCount = 0;
7345
8027
  // TODO - fix typing of luma data types
7346
8028
  _getBufferOrConstantValues(attribute, dataType) {
7347
- const TypedArrayConstructor = (0, import_core17.getTypedArrayConstructor)(dataType);
8029
+ const TypedArrayConstructor = import_core17.dataTypeDecoder.getTypedArrayConstructor(dataType);
7348
8030
  const typedArray = attribute instanceof import_core17.Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
7349
8031
  return typedArray.toString();
7350
8032
  }
@@ -7376,6 +8058,9 @@ void main() {
7376
8058
  };
7377
8059
  }
7378
8060
 
8061
+ // src/modules/picking/picking-manager.ts
8062
+ var import_core18 = __toESM(require_core(), 1);
8063
+
7379
8064
  // src/modules/picking/picking-uniforms.ts
7380
8065
  var DEFAULT_HIGHLIGHT_COLOR = [0, 1, 1, 1];
7381
8066
  var INVALID_INDEX = -1;
@@ -7408,15 +8093,17 @@ uniform pickingUniforms {
7408
8093
  var WGSL_UNIFORMS = (
7409
8094
  /* wgsl */
7410
8095
  `struct pickingUniforms {
7411
- isActive: int32;
7412
- indexMode: int32;
7413
- batchIndex: int32;
7414
-
7415
- isHighlightActive: int32;
7416
- highlightedBatchIndex: int32;
7417
- highlightedObjectIndex: int32;
7418
- highlightColor: vec4<f32>;
7419
- } picking;
8096
+ isActive: i32,
8097
+ indexMode: i32,
8098
+ batchIndex: i32,
8099
+
8100
+ isHighlightActive: i32,
8101
+ highlightedBatchIndex: i32,
8102
+ highlightedObjectIndex: i32,
8103
+ highlightColor: vec4<f32>,
8104
+ };
8105
+
8106
+ @group(0) @binding(auto) var<uniform> picking: pickingUniforms;
7420
8107
  `
7421
8108
  );
7422
8109
  function getUniforms(props = {}, prevUniforms) {
@@ -7428,25 +8115,36 @@ uniform pickingUniforms {
7428
8115
  case "instance":
7429
8116
  uniforms.indexMode = 0;
7430
8117
  break;
7431
- case "custom":
8118
+ case "attribute":
7432
8119
  uniforms.indexMode = 1;
7433
8120
  break;
7434
8121
  case void 0:
7435
8122
  break;
7436
8123
  }
7437
- switch (props.highlightedObjectIndex) {
8124
+ if (typeof props.batchIndex === "number") {
8125
+ uniforms.batchIndex = props.batchIndex;
8126
+ }
8127
+ switch (props.highlightedObjectIndex) {
8128
+ case void 0:
8129
+ break;
8130
+ case null:
8131
+ uniforms.isHighlightActive = false;
8132
+ uniforms.highlightedObjectIndex = INVALID_INDEX;
8133
+ break;
8134
+ default:
8135
+ uniforms.isHighlightActive = true;
8136
+ uniforms.highlightedObjectIndex = props.highlightedObjectIndex;
8137
+ }
8138
+ switch (props.highlightedBatchIndex) {
7438
8139
  case void 0:
7439
8140
  break;
7440
8141
  case null:
7441
8142
  uniforms.isHighlightActive = false;
7442
- uniforms.highlightedObjectIndex = INVALID_INDEX;
8143
+ uniforms.highlightedBatchIndex = INVALID_INDEX;
7443
8144
  break;
7444
8145
  default:
7445
8146
  uniforms.isHighlightActive = true;
7446
- uniforms.highlightedObjectIndex = props.highlightedObjectIndex;
7447
- }
7448
- if (typeof props.highlightedBatchIndex === "number") {
7449
- uniforms.highlightedBatchIndex = props.highlightedBatchIndex;
8147
+ uniforms.highlightedBatchIndex = props.highlightedBatchIndex;
7450
8148
  }
7451
8149
  if (props.highlightColor) {
7452
8150
  uniforms.highlightColor = props.highlightColor;
@@ -7462,7 +8160,7 @@ uniform pickingUniforms {
7462
8160
  isActive: false,
7463
8161
  indexMode: 0,
7464
8162
  batchIndex: 0,
7465
- isHighlightActive: true,
8163
+ isHighlightActive: false,
7466
8164
  highlightedBatchIndex: INVALID_INDEX,
7467
8165
  highlightedObjectIndex: INVALID_INDEX,
7468
8166
  highlightColor: DEFAULT_HIGHLIGHT_COLOR
@@ -7471,9 +8169,44 @@ uniform pickingUniforms {
7471
8169
  };
7472
8170
 
7473
8171
  // src/modules/picking/picking-manager.ts
8172
+ var INDEX_PICKING_ATTACHMENT_INDEX = 1;
8173
+ var INDEX_PICKING_CLEAR_COLOR = new Int32Array([INVALID_INDEX, INVALID_INDEX, 0, 0]);
8174
+ function resolvePickingMode(deviceType, mode = "color", indexPickingSupported = deviceType === "webgpu") {
8175
+ if (mode === "auto") {
8176
+ return indexPickingSupported ? "index" : "color";
8177
+ }
8178
+ if (mode === "index" && !indexPickingSupported) {
8179
+ throw new Error(
8180
+ `Picking mode "${mode}" requires WebGPU or a WebGL device that supports renderable rg32sint textures.`
8181
+ );
8182
+ }
8183
+ return mode;
8184
+ }
8185
+ function supportsIndexPicking(device) {
8186
+ return device.type === "webgpu" || device.type === "webgl" && device.isTextureFormatRenderable("rg32sint");
8187
+ }
8188
+ var resolvePickingBackend = resolvePickingMode;
8189
+ function decodeIndexPickInfo(pixelData) {
8190
+ return {
8191
+ objectIndex: pixelData[0] === INVALID_INDEX ? null : pixelData[0],
8192
+ batchIndex: pixelData[1] === INVALID_INDEX ? null : pixelData[1]
8193
+ };
8194
+ }
8195
+ function decodeColorPickInfo(pixelData) {
8196
+ const encodedObjectIndex = pixelData[0] + pixelData[1] * 256 + pixelData[2] * 65536;
8197
+ if (encodedObjectIndex === 0) {
8198
+ return { objectIndex: null, batchIndex: null };
8199
+ }
8200
+ const batchIndex = pixelData[3] > 0 ? pixelData[3] - 1 : 0;
8201
+ return {
8202
+ objectIndex: encodedObjectIndex - 1,
8203
+ batchIndex
8204
+ };
8205
+ }
7474
8206
  var _PickingManager = class {
7475
8207
  device;
7476
8208
  props;
8209
+ mode;
7477
8210
  /** Info from latest pick operation */
7478
8211
  pickInfo = { batchIndex: null, objectIndex: null };
7479
8212
  /** Framebuffer used for picking */
@@ -7481,6 +8214,14 @@ uniform pickingUniforms {
7481
8214
  constructor(device, props) {
7482
8215
  this.device = device;
7483
8216
  this.props = { ..._PickingManager.defaultProps, ...props };
8217
+ const requestedMode = props.mode ?? props.backend ?? _PickingManager.defaultProps.mode;
8218
+ this.props.mode = requestedMode;
8219
+ this.props.backend = requestedMode;
8220
+ this.mode = resolvePickingMode(
8221
+ this.device.type,
8222
+ requestedMode,
8223
+ supportsIndexPicking(this.device)
8224
+ );
7484
8225
  }
7485
8226
  destroy() {
7486
8227
  this.framebuffer?.destroy();
@@ -7488,56 +8229,44 @@ uniform pickingUniforms {
7488
8229
  // TODO - Ask for a cached framebuffer? a Framebuffer factory?
7489
8230
  getFramebuffer() {
7490
8231
  if (!this.framebuffer) {
7491
- this.framebuffer = this.device.createFramebuffer({
7492
- colorAttachments: ["rgba8unorm", "rg32sint"],
7493
- depthStencilAttachment: "depth24plus"
7494
- });
8232
+ this.framebuffer = this.mode === "index" ? this.createIndexFramebuffer() : this.createColorFramebuffer();
7495
8233
  }
7496
8234
  return this.framebuffer;
7497
8235
  }
7498
8236
  /** Clear highlighted / picked object */
7499
8237
  clearPickState() {
7500
- this.props.shaderInputs.setProps({ picking: { highlightedObjectIndex: null } });
8238
+ this.setPickingProps({ highlightedBatchIndex: null, highlightedObjectIndex: null });
7501
8239
  }
7502
8240
  /** Prepare for rendering picking colors */
7503
8241
  beginRenderPass() {
7504
8242
  const framebuffer = this.getFramebuffer();
7505
8243
  framebuffer.resize(this.device.getDefaultCanvasContext().getDevicePixelSize());
7506
- this.props.shaderInputs?.setProps({ picking: { isActive: true } });
7507
- const pickingPass = this.device.beginRenderPass({
8244
+ this.setPickingProps({ isActive: true });
8245
+ return this.mode === "index" ? this.device.beginRenderPass({
8246
+ framebuffer,
8247
+ clearColors: [new Float32Array([0, 0, 0, 0]), INDEX_PICKING_CLEAR_COLOR],
8248
+ clearDepth: 1
8249
+ }) : this.device.beginRenderPass({
7508
8250
  framebuffer,
7509
- clearColors: [new Float32Array([0, 0, 0, 0]), new Int32Array([-1, -1, 0, 0])],
8251
+ clearColor: [0, 0, 0, 0],
7510
8252
  clearDepth: 1
7511
8253
  });
7512
- return pickingPass;
7513
8254
  }
7514
8255
  async updatePickInfo(mousePosition) {
7515
8256
  const framebuffer = this.getFramebuffer();
7516
- const [pickX, pickY] = this.getPickPosition(mousePosition);
7517
- const pixelData = this.device.readPixelsToArrayWebGL(framebuffer, {
7518
- sourceX: pickX,
7519
- sourceY: pickY,
7520
- sourceWidth: 1,
7521
- sourceHeight: 1,
7522
- sourceAttachment: 1
7523
- });
7524
- if (!pixelData) {
8257
+ const pickPosition = this.getPickPosition(mousePosition);
8258
+ const pickInfo = await this.readPickInfo(framebuffer, pickPosition);
8259
+ if (!pickInfo) {
7525
8260
  return null;
7526
8261
  }
7527
- const pickInfo = {
7528
- objectIndex: pixelData[0] === INVALID_INDEX ? null : pixelData[0],
7529
- batchIndex: pixelData[1] === INVALID_INDEX ? null : pixelData[1]
7530
- };
7531
- if (pickInfo.objectIndex !== this.pickInfo.objectIndex || pickInfo.batchIndex !== this.pickInfo.batchIndex) {
8262
+ if (this.hasPickInfoChanged(pickInfo)) {
7532
8263
  this.pickInfo = pickInfo;
7533
8264
  this.props.onObjectPicked(pickInfo);
7534
8265
  }
7535
- this.props.shaderInputs?.setProps({
7536
- picking: {
7537
- isActive: false,
7538
- highlightedBatchIndex: pickInfo.batchIndex,
7539
- highlightedObjectIndex: pickInfo.objectIndex
7540
- }
8266
+ this.setPickingProps({
8267
+ isActive: false,
8268
+ highlightedBatchIndex: pickInfo.batchIndex,
8269
+ highlightedObjectIndex: pickInfo.objectIndex
7541
8270
  });
7542
8271
  return this.pickInfo;
7543
8272
  }
@@ -7546,43 +8275,207 @@ uniform pickingUniforms {
7546
8275
  * use the center pixel location in device pixel range
7547
8276
  */
7548
8277
  getPickPosition(mousePosition) {
7549
- const devicePixels = this.device.getDefaultCanvasContext().cssToDevicePixels(mousePosition);
8278
+ const yInvert = this.device.type !== "webgpu";
8279
+ const devicePixels = this.device.getDefaultCanvasContext().cssToDevicePixels(mousePosition, yInvert);
7550
8280
  const pickX = devicePixels.x + Math.floor(devicePixels.width / 2);
7551
8281
  const pickY = devicePixels.y + Math.floor(devicePixels.height / 2);
7552
8282
  return [pickX, pickY];
7553
8283
  }
8284
+ createIndexFramebuffer() {
8285
+ const colorTexture = this.device.createTexture({
8286
+ format: "rgba8unorm",
8287
+ width: 1,
8288
+ height: 1,
8289
+ usage: import_core18.Texture.RENDER_ATTACHMENT
8290
+ });
8291
+ const pickingTexture = this.device.createTexture({
8292
+ format: "rg32sint",
8293
+ width: 1,
8294
+ height: 1,
8295
+ usage: import_core18.Texture.RENDER_ATTACHMENT | import_core18.Texture.COPY_SRC
8296
+ });
8297
+ return this.device.createFramebuffer({
8298
+ colorAttachments: [colorTexture, pickingTexture],
8299
+ depthStencilAttachment: "depth24plus"
8300
+ });
8301
+ }
8302
+ createColorFramebuffer() {
8303
+ const pickingTexture = this.device.createTexture({
8304
+ format: "rgba8unorm",
8305
+ width: 1,
8306
+ height: 1,
8307
+ usage: import_core18.Texture.RENDER_ATTACHMENT | import_core18.Texture.COPY_SRC
8308
+ });
8309
+ return this.device.createFramebuffer({
8310
+ colorAttachments: [pickingTexture],
8311
+ depthStencilAttachment: "depth24plus"
8312
+ });
8313
+ }
8314
+ setPickingProps(props) {
8315
+ this.props.shaderInputs?.setProps({ picking: props });
8316
+ }
8317
+ async readPickInfo(framebuffer, pickPosition) {
8318
+ return this.mode === "index" ? this.readIndexPickInfo(framebuffer, pickPosition) : this.readColorPickInfo(framebuffer, pickPosition);
8319
+ }
8320
+ async readIndexPickInfo(framebuffer, [pickX, pickY]) {
8321
+ if (this.device.type === "webgpu") {
8322
+ const pickTexture = framebuffer.colorAttachments[INDEX_PICKING_ATTACHMENT_INDEX]?.texture;
8323
+ if (!pickTexture) {
8324
+ return null;
8325
+ }
8326
+ const layout = pickTexture.computeMemoryLayout({ width: 1, height: 1 });
8327
+ const readBuffer = this.device.createBuffer({
8328
+ byteLength: layout.byteLength,
8329
+ usage: import_core18.Buffer.COPY_DST | import_core18.Buffer.MAP_READ
8330
+ });
8331
+ try {
8332
+ pickTexture.readBuffer(
8333
+ {
8334
+ x: pickX,
8335
+ y: pickY,
8336
+ width: 1,
8337
+ height: 1
8338
+ },
8339
+ readBuffer
8340
+ );
8341
+ const pickDataView = await readBuffer.readAsync(0, layout.byteLength);
8342
+ return decodeIndexPickInfo(new Int32Array(pickDataView.buffer, pickDataView.byteOffset, 2));
8343
+ } finally {
8344
+ readBuffer.destroy();
8345
+ }
8346
+ }
8347
+ const pixelData = this.device.readPixelsToArrayWebGL(framebuffer, {
8348
+ sourceX: pickX,
8349
+ sourceY: pickY,
8350
+ sourceWidth: 1,
8351
+ sourceHeight: 1,
8352
+ sourceAttachment: INDEX_PICKING_ATTACHMENT_INDEX
8353
+ });
8354
+ return pixelData ? decodeIndexPickInfo(new Int32Array(pixelData.buffer, pixelData.byteOffset, 2)) : null;
8355
+ }
8356
+ async readColorPickInfo(framebuffer, [pickX, pickY]) {
8357
+ if (this.device.type === "webgpu") {
8358
+ const pickTexture = framebuffer.colorAttachments[0]?.texture;
8359
+ if (!pickTexture) {
8360
+ return null;
8361
+ }
8362
+ const layout = pickTexture.computeMemoryLayout({ width: 1, height: 1 });
8363
+ const readBuffer = this.device.createBuffer({
8364
+ byteLength: layout.byteLength,
8365
+ usage: import_core18.Buffer.COPY_DST | import_core18.Buffer.MAP_READ
8366
+ });
8367
+ try {
8368
+ pickTexture.readBuffer(
8369
+ {
8370
+ x: pickX,
8371
+ y: pickY,
8372
+ width: 1,
8373
+ height: 1
8374
+ },
8375
+ readBuffer
8376
+ );
8377
+ const pickDataView = await readBuffer.readAsync(0, layout.byteLength);
8378
+ return decodeColorPickInfo(new Uint8Array(pickDataView.buffer, pickDataView.byteOffset, 4));
8379
+ } finally {
8380
+ readBuffer.destroy();
8381
+ }
8382
+ }
8383
+ const pixelData = this.device.readPixelsToArrayWebGL(framebuffer, {
8384
+ sourceX: pickX,
8385
+ sourceY: pickY,
8386
+ sourceWidth: 1,
8387
+ sourceHeight: 1,
8388
+ sourceAttachment: 0
8389
+ });
8390
+ return pixelData ? decodeColorPickInfo(new Uint8Array(pixelData.buffer, pixelData.byteOffset, 4)) : null;
8391
+ }
8392
+ hasPickInfoChanged(pickInfo) {
8393
+ return pickInfo.objectIndex !== this.pickInfo.objectIndex || pickInfo.batchIndex !== this.pickInfo.batchIndex;
8394
+ }
7554
8395
  };
7555
8396
  var PickingManager = _PickingManager;
7556
8397
  __publicField(PickingManager, "defaultProps", {
7557
8398
  shaderInputs: void 0,
7558
8399
  onObjectPicked: () => {
7559
- }
8400
+ },
8401
+ mode: "color",
8402
+ backend: "color"
7560
8403
  });
7561
8404
 
7562
- // src/modules/picking/index-picking.ts
8405
+ // src/modules/picking/color-picking.ts
7563
8406
  var source = (
7564
8407
  /* wgsl */
7565
8408
  `${WGSL_UNIFORMS}
7566
8409
 
7567
- const INDEX_PICKING_MODE_INSTANCE = 0;
7568
- const INDEX_PICKING_MODE_CUSTOM = 1;
7569
- const INDEX_PICKING_INVALID_INDEX = ${INVALID_INDEX}; // 2^32 - 1
8410
+ const COLOR_PICKING_INVALID_INDEX = ${INVALID_INDEX};
8411
+ const COLOR_PICKING_MAX_OBJECT_INDEX = 16777214;
8412
+ const COLOR_PICKING_MAX_BATCH_INDEX = 254;
7570
8413
 
7571
- /**
7572
- * Vertex shaders should call this function to set the object index.
7573
- * If using instance or vertex mode, argument will be ignored, 0 can be supplied.
7574
- */
7575
- fn picking_setObjectIndex(objectIndex: int32) {
7576
- switch (picking.indexMode) {
7577
- case INDEX_PICKING_MODE_INSTANCE, default: {
7578
- picking_objectIndex = instance_index;
7579
- };
7580
- case INDEX_PICKING_MODE_CUSTOM: {
7581
- picking_objectIndex = objectIndex;
7582
- };
8414
+ fn picking_setObjectIndex(objectIndex: i32) -> i32 {
8415
+ return objectIndex;
8416
+ }
8417
+
8418
+ fn picking_isObjectHighlighted(objectIndex: i32) -> bool {
8419
+ return
8420
+ picking.isHighlightActive != 0 &&
8421
+ picking.highlightedBatchIndex == picking.batchIndex &&
8422
+ picking.highlightedObjectIndex == objectIndex;
8423
+ }
8424
+
8425
+ fn picking_filterHighlightColor(color: vec4<f32>, objectIndex: i32) -> vec4<f32> {
8426
+ if (picking.isActive != 0 || !picking_isObjectHighlighted(objectIndex)) {
8427
+ return color;
8428
+ }
8429
+
8430
+ let highLightAlpha = picking.highlightColor.a;
8431
+ let blendedAlpha = highLightAlpha + color.a * (1.0 - highLightAlpha);
8432
+ if (blendedAlpha == 0.0) {
8433
+ return vec4<f32>(color.rgb, 0.0);
7583
8434
  }
8435
+
8436
+ let highLightRatio = highLightAlpha / blendedAlpha;
8437
+ let blendedRGB = mix(color.rgb, picking.highlightColor.rgb, highLightRatio);
8438
+ return vec4<f32>(blendedRGB, blendedAlpha);
8439
+ }
8440
+
8441
+ fn picking_canEncodePickInfo(objectIndex: i32) -> bool {
8442
+ return
8443
+ objectIndex != COLOR_PICKING_INVALID_INDEX &&
8444
+ objectIndex >= 0 &&
8445
+ objectIndex <= COLOR_PICKING_MAX_OBJECT_INDEX &&
8446
+ picking.batchIndex >= 0 &&
8447
+ picking.batchIndex <= COLOR_PICKING_MAX_BATCH_INDEX;
8448
+ }
8449
+
8450
+ fn picking_getPickingColor(objectIndex: i32) -> vec4<f32> {
8451
+ if (!picking_canEncodePickInfo(objectIndex)) {
8452
+ return vec4<f32>(0.0, 0.0, 0.0, 0.0);
8453
+ }
8454
+
8455
+ let encodedObjectIndex = objectIndex + 1;
8456
+ let red = encodedObjectIndex % 256;
8457
+ let green = (encodedObjectIndex / 256) % 256;
8458
+ let blue = (encodedObjectIndex / 65536) % 256;
8459
+ let alpha = picking.batchIndex + 1;
8460
+
8461
+ return vec4<f32>(
8462
+ f32(red) / 255.0,
8463
+ f32(green) / 255.0,
8464
+ f32(blue) / 255.0,
8465
+ f32(alpha) / 255.0
8466
+ );
7584
8467
  }
7585
8468
 
8469
+ fn picking_filterPickingColor(color: vec4<f32>, objectIndex: i32) -> vec4<f32> {
8470
+ if (picking.isActive != 0) {
8471
+ if (!picking_canEncodePickInfo(objectIndex)) {
8472
+ discard;
8473
+ }
8474
+ return picking_getPickingColor(objectIndex);
8475
+ }
8476
+
8477
+ return color;
8478
+ }
7586
8479
  `
7587
8480
  );
7588
8481
  var vs = (
@@ -7592,14 +8485,10 @@ fn picking_setObjectIndex(objectIndex: int32) {
7592
8485
  const int INDEX_PICKING_MODE_INSTANCE = 0;
7593
8486
  const int INDEX_PICKING_MODE_CUSTOM = 1;
7594
8487
 
7595
- const int INDEX_PICKING_INVALID_INDEX = ${INVALID_INDEX}; // 2^32 - 1
8488
+ const int COLOR_PICKING_INVALID_INDEX = ${INVALID_INDEX};
7596
8489
 
7597
8490
  flat out int picking_objectIndex;
7598
8491
 
7599
- /**
7600
- * Vertex shaders should call this function to set the object index.
7601
- * If using instance or vertex mode, argument will be ignored, 0 can be supplied.
7602
- */
7603
8492
  void picking_setObjectIndex(int objectIndex) {
7604
8493
  switch (picking.indexMode) {
7605
8494
  case INDEX_PICKING_MODE_INSTANCE:
@@ -7616,36 +8505,29 @@ void picking_setObjectIndex(int objectIndex) {
7616
8505
  /* glsl */
7617
8506
  `${GLSL_UNIFORMS}
7618
8507
 
7619
- const int INDEX_PICKING_INVALID_INDEX = ${INVALID_INDEX}; // 2^32 - 1
8508
+ const int COLOR_PICKING_INVALID_INDEX = ${INVALID_INDEX};
8509
+ const int COLOR_PICKING_MAX_OBJECT_INDEX = 16777214;
8510
+ const int COLOR_PICKING_MAX_BATCH_INDEX = 254;
7620
8511
 
7621
8512
  flat in int picking_objectIndex;
7622
8513
 
7623
- /**
7624
- * Check if this vertex is highlighted (part of the selected batch and object)
7625
- */
7626
8514
  bool picking_isFragmentHighlighted() {
7627
- return
8515
+ return
7628
8516
  bool(picking.isHighlightActive) &&
7629
8517
  picking.highlightedBatchIndex == picking.batchIndex &&
7630
8518
  picking.highlightedObjectIndex == picking_objectIndex
7631
8519
  ;
7632
8520
  }
7633
8521
 
7634
- /**
7635
- * Returns highlight color if this item is selected.
7636
- */
7637
8522
  vec4 picking_filterHighlightColor(vec4 color) {
7638
- // If we are still picking, we don't highlight
7639
8523
  if (bool(picking.isActive)) {
7640
8524
  return color;
7641
8525
  }
7642
8526
 
7643
- // If we are not highlighted, return color as is
7644
8527
  if (!picking_isFragmentHighlighted()) {
7645
8528
  return color;
7646
8529
  }
7647
-
7648
- // Blend in highlight color based on its alpha value
8530
+
7649
8531
  float highLightAlpha = picking.highlightColor.a;
7650
8532
  float blendedAlpha = highLightAlpha + color.a * (1.0 - highLightAlpha);
7651
8533
  float highLightRatio = highLightAlpha / blendedAlpha;
@@ -7654,28 +8536,40 @@ vec4 picking_filterHighlightColor(vec4 color) {
7654
8536
  return vec4(blendedRGB, blendedAlpha);
7655
8537
  }
7656
8538
 
7657
- /*
7658
- * Returns picking color if picking enabled else unmodified argument.
7659
- */
7660
- ivec4 picking_getPickingColor() {
7661
- // Assumes that colorAttachment0 is rg32int
7662
- // TODO? - we could render indices into a second color attachment and not mess with fragColor
7663
- return ivec4(picking_objectIndex, picking.batchIndex, 0u, 0u);
8539
+ bool picking_canEncodePickInfo(int objectIndex) {
8540
+ return
8541
+ objectIndex != COLOR_PICKING_INVALID_INDEX &&
8542
+ objectIndex >= 0 &&
8543
+ objectIndex <= COLOR_PICKING_MAX_OBJECT_INDEX &&
8544
+ picking.batchIndex >= 0 &&
8545
+ picking.batchIndex <= COLOR_PICKING_MAX_BATCH_INDEX;
8546
+ }
8547
+
8548
+ vec4 picking_getPickingColor() {
8549
+ if (!picking_canEncodePickInfo(picking_objectIndex)) {
8550
+ return vec4(0.0);
8551
+ }
8552
+
8553
+ int encodedObjectIndex = picking_objectIndex + 1;
8554
+ int red = encodedObjectIndex % 256;
8555
+ int green = (encodedObjectIndex / 256) % 256;
8556
+ int blue = (encodedObjectIndex / 65536) % 256;
8557
+ int alpha = picking.batchIndex + 1;
8558
+
8559
+ return vec4(float(red), float(green), float(blue), float(alpha)) / 255.0;
7664
8560
  }
7665
8561
 
7666
8562
  vec4 picking_filterPickingColor(vec4 color) {
7667
8563
  if (bool(picking.isActive)) {
7668
- if (picking_objectIndex == INDEX_PICKING_INVALID_INDEX) {
8564
+ if (!picking_canEncodePickInfo(picking_objectIndex)) {
7669
8565
  discard;
7670
8566
  }
8567
+ return picking_getPickingColor();
7671
8568
  }
8569
+
7672
8570
  return color;
7673
8571
  }
7674
8572
 
7675
- /*
7676
- * Returns picking color if picking is enabled if not
7677
- * highlight color if this item is selected, otherwise unmodified argument.
7678
- */
7679
8573
  vec4 picking_filterColor(vec4 color) {
7680
8574
  vec4 outColor = color;
7681
8575
  outColor = picking_filterHighlightColor(outColor);
@@ -7692,87 +8586,81 @@ vec4 picking_filterColor(vec4 color) {
7692
8586
  fs
7693
8587
  };
7694
8588
 
7695
- // src/modules/picking/color-picking.ts
8589
+ // src/modules/picking/index-picking.ts
7696
8590
  var source2 = (
7697
8591
  /* wgsl */
7698
8592
  `${WGSL_UNIFORMS}
7699
- `
7700
- );
7701
- var vs2 = (
7702
- /* glsl */
7703
- `${GLSL_UNIFORMS}
7704
- out vec4 picking_vRGBcolor_Avalid;
7705
-
7706
- // Normalize unsigned byte color to 0-1 range
7707
- vec3 picking_normalizeColor(vec3 color) {
7708
- return picking.useFloatColors > 0.5 ? color : color / 255.0;
7709
- }
7710
-
7711
- // Normalize unsigned byte color to 0-1 range
7712
- vec4 picking_normalizeColor(vec4 color) {
7713
- return picking.useFloatColors > 0.5 ? color : color / 255.0;
7714
- }
7715
8593
 
7716
- bool picking_isColorZero(vec3 color) {
7717
- return dot(color, vec3(1.0)) < 0.00001;
7718
- }
8594
+ const INDEX_PICKING_MODE_INSTANCE = 0;
8595
+ const INDEX_PICKING_MODE_CUSTOM = 1;
8596
+ const INDEX_PICKING_INVALID_INDEX = ${INVALID_INDEX}; // 2^32 - 1
7719
8597
 
7720
- bool picking_isColorValid(vec3 color) {
7721
- return dot(color, vec3(1.0)) > 0.00001;
8598
+ /**
8599
+ * WGSL shaders need to carry the returned object index through their own stage outputs.
8600
+ */
8601
+ fn picking_setObjectIndex(objectIndex: i32) -> i32 {
8602
+ return objectIndex;
7722
8603
  }
7723
8604
 
7724
- // Check if this vertex is highlighted
7725
- bool isVertexHighlighted(vec3 vertexColor) {
7726
- vec3 highlightedObjectColor = picking_normalizeColor(picking.highlightedObjectColor);
8605
+ fn picking_isObjectHighlighted(objectIndex: i32) -> bool {
7727
8606
  return
7728
- bool(picking.isHighlightActive) && picking_isColorZero(abs(vertexColor - highlightedObjectColor));
8607
+ picking.isHighlightActive != 0 &&
8608
+ picking.highlightedBatchIndex == picking.batchIndex &&
8609
+ picking.highlightedObjectIndex == objectIndex;
7729
8610
  }
7730
8611
 
7731
- // Set the current picking color
7732
- void picking_setPickingColor(vec3 pickingColor) {
7733
- pickingColor = picking_normalizeColor(pickingColor);
7734
-
7735
- if (bool(picking.isActive)) {
7736
- // Use alpha as the validity flag. If pickingColor is [0, 0, 0] fragment is non-pickable
7737
- picking_vRGBcolor_Avalid.a = float(picking_isColorValid(pickingColor));
7738
-
7739
- if (!bool(picking.isAttribute)) {
7740
- // Stores the picking color so that the fragment shader can render it during picking
7741
- picking_vRGBcolor_Avalid.rgb = pickingColor;
7742
- }
7743
- } else {
7744
- // Do the comparison with selected item color in vertex shader as it should mean fewer compares
7745
- picking_vRGBcolor_Avalid.a = float(isVertexHighlighted(pickingColor));
8612
+ fn picking_filterHighlightColor(color: vec4<f32>, objectIndex: i32) -> vec4<f32> {
8613
+ if (picking.isActive != 0 || !picking_isObjectHighlighted(objectIndex)) {
8614
+ return color;
7746
8615
  }
7747
- }
7748
8616
 
7749
- void picking_setObjectIndex(uint objectIndex) {
7750
- if (bool(picking.isActive)) {
7751
- uint index = objectIndex;
7752
- if (picking.indexMode == PICKING_INDEX_MODE_INSTANCE) {
7753
- index = uint(gl_InstanceID);
7754
- }
7755
- picking_vRGBcolor_Avalid.r = float(index % 255) / 255.0;
7756
- picking_vRGBcolor_Avalid.g = float((index / 255) % 255) / 255.0;
7757
- picking_vRGBcolor_Avalid.b = float((index / 255 / 255) %255) / 255.0;
8617
+ let highLightAlpha = picking.highlightColor.a;
8618
+ let blendedAlpha = highLightAlpha + color.a * (1.0 - highLightAlpha);
8619
+ if (blendedAlpha == 0.0) {
8620
+ return vec4<f32>(color.rgb, 0.0);
7758
8621
  }
8622
+
8623
+ let highLightRatio = highLightAlpha / blendedAlpha;
8624
+ let blendedRGB = mix(color.rgb, picking.highlightColor.rgb, highLightRatio);
8625
+ return vec4<f32>(blendedRGB, blendedAlpha);
7759
8626
  }
7760
8627
 
7761
- void picking_setPickingAttribute(float value) {
7762
- if (bool(picking.isAttribute)) {
7763
- picking_vRGBcolor_Avalid.r = value;
8628
+ fn picking_filterPickingColor(color: vec4<f32>, objectIndex: i32) -> vec4<f32> {
8629
+ if (picking.isActive != 0 && objectIndex == INDEX_PICKING_INVALID_INDEX) {
8630
+ discard;
7764
8631
  }
8632
+ return color;
7765
8633
  }
7766
8634
 
7767
- void picking_setPickingAttribute(vec2 value) {
7768
- if (bool(picking.isAttribute)) {
7769
- picking_vRGBcolor_Avalid.rg = value;
7770
- }
8635
+ fn picking_getPickingColor(objectIndex: i32) -> vec2<i32> {
8636
+ return vec2<i32>(objectIndex, picking.batchIndex);
7771
8637
  }
7772
8638
 
7773
- void picking_setPickingAttribute(vec3 value) {
7774
- if (bool(picking.isAttribute)) {
7775
- picking_vRGBcolor_Avalid.rgb = value;
8639
+ `
8640
+ );
8641
+ var vs2 = (
8642
+ /* glsl */
8643
+ `${GLSL_UNIFORMS}
8644
+
8645
+ const int INDEX_PICKING_MODE_INSTANCE = 0;
8646
+ const int INDEX_PICKING_MODE_CUSTOM = 1;
8647
+
8648
+ const int INDEX_PICKING_INVALID_INDEX = ${INVALID_INDEX}; // 2^32 - 1
8649
+
8650
+ flat out int picking_objectIndex;
8651
+
8652
+ /**
8653
+ * Vertex shaders should call this function to set the object index.
8654
+ * If using instance or vertex mode, argument will be ignored, 0 can be supplied.
8655
+ */
8656
+ void picking_setObjectIndex(int objectIndex) {
8657
+ switch (picking.indexMode) {
8658
+ case INDEX_PICKING_MODE_INSTANCE:
8659
+ picking_objectIndex = gl_InstanceID;
8660
+ break;
8661
+ case INDEX_PICKING_MODE_CUSTOM:
8662
+ picking_objectIndex = objectIndex;
8663
+ break;
7776
8664
  }
7777
8665
  }
7778
8666
  `
@@ -7781,41 +8669,58 @@ void picking_setPickingAttribute(vec3 value) {
7781
8669
  /* glsl */
7782
8670
  `${GLSL_UNIFORMS}
7783
8671
 
7784
- in vec4 picking_vRGBcolor_Avalid;
8672
+ const int INDEX_PICKING_INVALID_INDEX = ${INVALID_INDEX}; // 2^32 - 1
8673
+
8674
+ flat in int picking_objectIndex;
7785
8675
 
7786
- /*
8676
+ /**
8677
+ * Check if this vertex is highlighted (part of the selected batch and object)
8678
+ */
8679
+ bool picking_isFragmentHighlighted() {
8680
+ return
8681
+ bool(picking.isHighlightActive) &&
8682
+ picking.highlightedBatchIndex == picking.batchIndex &&
8683
+ picking.highlightedObjectIndex == picking_objectIndex
8684
+ ;
8685
+ }
8686
+
8687
+ /**
7787
8688
  * Returns highlight color if this item is selected.
7788
8689
  */
7789
8690
  vec4 picking_filterHighlightColor(vec4 color) {
7790
8691
  // If we are still picking, we don't highlight
7791
- if (picking.isActive > 0.5) {
8692
+ if (bool(picking.isActive)) {
7792
8693
  return color;
7793
8694
  }
7794
8695
 
7795
- bool selected = bool(picking_vRGBcolor_Avalid.a);
7796
-
7797
- if (selected) {
7798
- // Blend in highlight color based on its alpha value
7799
- float highLightAlpha = picking.highlightColor.a;
7800
- float blendedAlpha = highLightAlpha + color.a * (1.0 - highLightAlpha);
7801
- float highLightRatio = highLightAlpha / blendedAlpha;
7802
-
7803
- vec3 blendedRGB = mix(color.rgb, picking.highlightColor.rgb, highLightRatio);
7804
- return vec4(blendedRGB, blendedAlpha);
7805
- } else {
8696
+ // If we are not highlighted, return color as is
8697
+ if (!picking_isFragmentHighlighted()) {
7806
8698
  return color;
7807
8699
  }
8700
+
8701
+ // Blend in highlight color based on its alpha value
8702
+ float highLightAlpha = picking.highlightColor.a;
8703
+ float blendedAlpha = highLightAlpha + color.a * (1.0 - highLightAlpha);
8704
+ float highLightRatio = highLightAlpha / blendedAlpha;
8705
+
8706
+ vec3 blendedRGB = mix(color.rgb, picking.highlightColor.rgb, highLightRatio);
8707
+ return vec4(blendedRGB, blendedAlpha);
7808
8708
  }
7809
8709
 
7810
8710
  /*
7811
8711
  * Returns picking color if picking enabled else unmodified argument.
7812
8712
  */
8713
+ ivec4 picking_getPickingColor() {
8714
+ // Assumes that colorAttachment0 is rg32int
8715
+ // TODO? - we could render indices into a second color attachment and not mess with fragColor
8716
+ return ivec4(picking_objectIndex, picking.batchIndex, 0u, 0u);
8717
+ }
8718
+
7813
8719
  vec4 picking_filterPickingColor(vec4 color) {
7814
8720
  if (bool(picking.isActive)) {
7815
- if (picking_vRGBcolor_Avalid.a == 0.0) {
8721
+ if (picking_objectIndex == INDEX_PICKING_INVALID_INDEX) {
7816
8722
  discard;
7817
8723
  }
7818
- return picking_vRGBcolor_Avalid;
7819
8724
  }
7820
8725
  return color;
7821
8726
  }
@@ -7825,8 +8730,10 @@ vec4 picking_filterPickingColor(vec4 color) {
7825
8730
  * highlight color if this item is selected, otherwise unmodified argument.
7826
8731
  */
7827
8732
  vec4 picking_filterColor(vec4 color) {
7828
- vec4 highlightColor = picking_filterHighlightColor(color);
7829
- return picking_filterPickingColor(highlightColor);
8733
+ vec4 outColor = color;
8734
+ outColor = picking_filterHighlightColor(outColor);
8735
+ outColor = picking_filterPickingColor(outColor);
8736
+ return outColor;
7830
8737
  }
7831
8738
  `
7832
8739
  );
@@ -7838,6 +8745,15 @@ vec4 picking_filterColor(vec4 color) {
7838
8745
  fs: fs2
7839
8746
  };
7840
8747
 
8748
+ // src/modules/picking/picking.ts
8749
+ var picking3 = {
8750
+ ...pickingUniforms,
8751
+ name: "picking",
8752
+ source: picking2.source,
8753
+ vs: picking.vs,
8754
+ fs: picking.fs
8755
+ };
8756
+
7841
8757
  // src/modules/picking/legacy-picking-manager.ts
7842
8758
  var LegacyPickingManager = class {
7843
8759
  device;
@@ -7907,6 +8823,10 @@ vec4 picking_filterColor(vec4 color) {
7907
8823
  }
7908
8824
  };
7909
8825
 
8826
+ // src/modules/picking/legacy-color-picking.ts
8827
+ var import_shadertools7 = __toESM(require_shadertools(), 1);
8828
+ var legacyColorPicking = import_shadertools7.picking;
8829
+
7910
8830
  // src/index.ts
7911
8831
  var AsyncTexture = DynamicTexture;
7912
8832
  return __toCommonJS(bundle_exports);