@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/index.cjs CHANGED
@@ -35,6 +35,7 @@ __export(dist_exports, {
35
35
  ConeGeometry: () => ConeGeometry,
36
36
  CubeGeometry: () => CubeGeometry,
37
37
  CylinderGeometry: () => CylinderGeometry,
38
+ DirectionalLightModel: () => DirectionalLightModel,
38
39
  DynamicTexture: () => DynamicTexture,
39
40
  GPUGeometry: () => GPUGeometry,
40
41
  Geometry: () => Geometry,
@@ -42,16 +43,18 @@ __export(dist_exports, {
42
43
  IcoSphereGeometry: () => IcoSphereGeometry,
43
44
  KeyFrames: () => KeyFrames,
44
45
  LegacyPickingManager: () => LegacyPickingManager,
46
+ Material: () => Material,
47
+ MaterialFactory: () => MaterialFactory,
45
48
  Model: () => Model,
46
49
  ModelNode: () => ModelNode,
47
50
  PickingManager: () => PickingManager,
48
- PipelineFactory: () => PipelineFactory,
49
51
  PlaneGeometry: () => PlaneGeometry,
52
+ PointLightModel: () => PointLightModel,
50
53
  ScenegraphNode: () => ScenegraphNode,
51
- ShaderFactory: () => ShaderFactory,
52
54
  ShaderInputs: () => ShaderInputs,
53
55
  ShaderPassRenderer: () => ShaderPassRenderer,
54
56
  SphereGeometry: () => SphereGeometry,
57
+ SpotLightModel: () => SpotLightModel,
55
58
  Swap: () => Swap,
56
59
  SwapBuffers: () => SwapBuffers,
57
60
  SwapFramebuffers: () => SwapFramebuffers,
@@ -59,14 +62,19 @@ __export(dist_exports, {
59
62
  Timeline: () => Timeline,
60
63
  TruncatedConeGeometry: () => TruncatedConeGeometry,
61
64
  cancelAnimationFramePolyfill: () => cancelAnimationFramePolyfill,
62
- colorPicking: () => picking2,
63
- indexPicking: () => picking,
65
+ colorPicking: () => picking,
66
+ indexPicking: () => picking2,
67
+ legacyColorPicking: () => legacyColorPicking,
64
68
  loadImage: () => loadImage,
65
69
  loadImageBitmap: () => loadImageBitmap,
66
70
  makeAnimationLoop: () => makeAnimationLoop,
67
71
  makeRandomGenerator: () => makeRandomGenerator,
72
+ picking: () => picking3,
68
73
  requestAnimationFramePolyfill: () => requestAnimationFramePolyfill,
69
- setPathPrefix: () => setPathPrefix
74
+ resolvePickingBackend: () => resolvePickingBackend,
75
+ resolvePickingMode: () => resolvePickingMode,
76
+ setPathPrefix: () => setPathPrefix,
77
+ supportsIndexPicking: () => supportsIndexPicking
70
78
  });
71
79
  module.exports = __toCommonJS(dist_exports);
72
80
 
@@ -350,6 +358,9 @@ var _AnimationLoop = class {
350
358
  this._initialized = true;
351
359
  await this._initDevice();
352
360
  this._initialize();
361
+ if (!this._running) {
362
+ return null;
363
+ }
353
364
  await this.props.onInitialize(this._getAnimationProps());
354
365
  }
355
366
  if (!this._running) {
@@ -744,7 +755,7 @@ function clearError(device) {
744
755
  }
745
756
 
746
757
  // dist/model/model.js
747
- var import_core10 = require("@luma.gl/core");
758
+ var import_core8 = require("@luma.gl/core");
748
759
  var import_shadertools2 = require("@luma.gl/shadertools");
749
760
 
750
761
  // dist/geometry/gpu-geometry.js
@@ -848,293 +859,19 @@ function getAttributeBuffersFromGeometry(device, geometry) {
848
859
  id: `${attributeName}-buffer`
849
860
  });
850
861
  const { value, size, normalized } = attribute;
851
- bufferLayout.push({ name, format: (0, import_core3.getVertexFormatFromAttribute)(value, size, normalized) });
862
+ if (size === void 0) {
863
+ throw new Error(`Attribute ${attributeName} is missing a size`);
864
+ }
865
+ bufferLayout.push({
866
+ name,
867
+ format: import_core3.vertexFormatDecoder.getVertexFormatFromAttribute(value, size, normalized)
868
+ });
852
869
  }
853
870
  }
854
871
  const vertexCount = geometry._calculateVertexCount(geometry.attributes, geometry.indices);
855
872
  return { attributes, bufferLayout, vertexCount };
856
873
  }
857
874
 
858
- // dist/factories/pipeline-factory.js
859
- var import_core4 = require("@luma.gl/core");
860
- var _PipelineFactory = class {
861
- /** Get the singleton default pipeline factory for the specified device */
862
- static getDefaultPipelineFactory(device) {
863
- const moduleData = device.getModuleData("@luma.gl/engine");
864
- moduleData.defaultPipelineFactory ||= new _PipelineFactory(device);
865
- return moduleData.defaultPipelineFactory;
866
- }
867
- device;
868
- _hashCounter = 0;
869
- _hashes = {};
870
- _renderPipelineCache = {};
871
- _computePipelineCache = {};
872
- _sharedRenderPipelineCache = {};
873
- get [Symbol.toStringTag]() {
874
- return "PipelineFactory";
875
- }
876
- toString() {
877
- return `PipelineFactory(${this.device.id})`;
878
- }
879
- constructor(device) {
880
- this.device = device;
881
- }
882
- /** Return a RenderPipeline matching supplied props. Reuses an equivalent pipeline if already created. */
883
- createRenderPipeline(props) {
884
- var _a;
885
- if (!this.device.props._cachePipelines) {
886
- return this.device.createRenderPipeline(props);
887
- }
888
- const allProps = { ...import_core4.RenderPipeline.defaultProps, ...props };
889
- const cache = this._renderPipelineCache;
890
- const hash = this._hashRenderPipeline(allProps);
891
- let pipeline = (_a = cache[hash]) == null ? void 0 : _a.resource;
892
- if (!pipeline) {
893
- const sharedRenderPipeline = this.device.type === "webgl" && this.device.props._sharePipelines ? this.createSharedRenderPipeline(allProps) : void 0;
894
- pipeline = this.device.createRenderPipeline({
895
- ...allProps,
896
- id: allProps.id ? `${allProps.id}-cached` : uid("unnamed-cached"),
897
- _sharedRenderPipeline: sharedRenderPipeline
898
- });
899
- pipeline.hash = hash;
900
- cache[hash] = { resource: pipeline, useCount: 1 };
901
- if (this.device.props.debugFactories) {
902
- import_core4.log.log(3, `${this}: ${pipeline} created, count=${cache[hash].useCount}`)();
903
- }
904
- } else {
905
- cache[hash].useCount++;
906
- if (this.device.props.debugFactories) {
907
- import_core4.log.log(3, `${this}: ${cache[hash].resource} reused, count=${cache[hash].useCount}, (id=${props.id})`)();
908
- }
909
- }
910
- return pipeline;
911
- }
912
- /** Return a ComputePipeline matching supplied props. Reuses an equivalent pipeline if already created. */
913
- createComputePipeline(props) {
914
- var _a;
915
- if (!this.device.props._cachePipelines) {
916
- return this.device.createComputePipeline(props);
917
- }
918
- const allProps = { ...import_core4.ComputePipeline.defaultProps, ...props };
919
- const cache = this._computePipelineCache;
920
- const hash = this._hashComputePipeline(allProps);
921
- let pipeline = (_a = cache[hash]) == null ? void 0 : _a.resource;
922
- if (!pipeline) {
923
- pipeline = this.device.createComputePipeline({
924
- ...allProps,
925
- id: allProps.id ? `${allProps.id}-cached` : void 0
926
- });
927
- pipeline.hash = hash;
928
- cache[hash] = { resource: pipeline, useCount: 1 };
929
- if (this.device.props.debugFactories) {
930
- import_core4.log.log(3, `${this}: ${pipeline} created, count=${cache[hash].useCount}`)();
931
- }
932
- } else {
933
- cache[hash].useCount++;
934
- if (this.device.props.debugFactories) {
935
- import_core4.log.log(3, `${this}: ${cache[hash].resource} reused, count=${cache[hash].useCount}, (id=${props.id})`)();
936
- }
937
- }
938
- return pipeline;
939
- }
940
- release(pipeline) {
941
- if (!this.device.props._cachePipelines) {
942
- pipeline.destroy();
943
- return;
944
- }
945
- const cache = this._getCache(pipeline);
946
- const hash = pipeline.hash;
947
- cache[hash].useCount--;
948
- if (cache[hash].useCount === 0) {
949
- this._destroyPipeline(pipeline);
950
- if (this.device.props.debugFactories) {
951
- import_core4.log.log(3, `${this}: ${pipeline} released and destroyed`)();
952
- }
953
- } else if (cache[hash].useCount < 0) {
954
- import_core4.log.error(`${this}: ${pipeline} released, useCount < 0, resetting`)();
955
- cache[hash].useCount = 0;
956
- } else if (this.device.props.debugFactories) {
957
- import_core4.log.log(3, `${this}: ${pipeline} released, count=${cache[hash].useCount}`)();
958
- }
959
- }
960
- createSharedRenderPipeline(props) {
961
- const sharedPipelineHash = this._hashSharedRenderPipeline(props);
962
- let sharedCacheItem = this._sharedRenderPipelineCache[sharedPipelineHash];
963
- if (!sharedCacheItem) {
964
- const sharedRenderPipeline = this.device._createSharedRenderPipelineWebGL(props);
965
- sharedCacheItem = { resource: sharedRenderPipeline, useCount: 0 };
966
- this._sharedRenderPipelineCache[sharedPipelineHash] = sharedCacheItem;
967
- }
968
- sharedCacheItem.useCount++;
969
- return sharedCacheItem.resource;
970
- }
971
- releaseSharedRenderPipeline(pipeline) {
972
- if (!pipeline.sharedRenderPipeline) {
973
- return;
974
- }
975
- const sharedPipelineHash = this._hashSharedRenderPipeline(pipeline.sharedRenderPipeline.props);
976
- const sharedCacheItem = this._sharedRenderPipelineCache[sharedPipelineHash];
977
- if (!sharedCacheItem) {
978
- return;
979
- }
980
- sharedCacheItem.useCount--;
981
- if (sharedCacheItem.useCount === 0) {
982
- sharedCacheItem.resource.destroy();
983
- delete this._sharedRenderPipelineCache[sharedPipelineHash];
984
- }
985
- }
986
- // PRIVATE
987
- /** Destroy a cached pipeline, removing it from the cache if configured to do so. */
988
- _destroyPipeline(pipeline) {
989
- const cache = this._getCache(pipeline);
990
- if (!this.device.props._destroyPipelines) {
991
- return false;
992
- }
993
- delete cache[pipeline.hash];
994
- pipeline.destroy();
995
- if (pipeline instanceof import_core4.RenderPipeline) {
996
- this.releaseSharedRenderPipeline(pipeline);
997
- }
998
- return true;
999
- }
1000
- /** Get the appropriate cache for the type of pipeline */
1001
- _getCache(pipeline) {
1002
- let cache;
1003
- if (pipeline instanceof import_core4.ComputePipeline) {
1004
- cache = this._computePipelineCache;
1005
- }
1006
- if (pipeline instanceof import_core4.RenderPipeline) {
1007
- cache = this._renderPipelineCache;
1008
- }
1009
- if (!cache) {
1010
- throw new Error(`${this}`);
1011
- }
1012
- if (!cache[pipeline.hash]) {
1013
- throw new Error(`${this}: ${pipeline} matched incorrect entry`);
1014
- }
1015
- return cache;
1016
- }
1017
- /** Calculate a hash based on all the inputs for a compute pipeline */
1018
- _hashComputePipeline(props) {
1019
- const { type } = this.device;
1020
- const shaderHash = this._getHash(props.shader.source);
1021
- return `${type}/C/${shaderHash}`;
1022
- }
1023
- /** Calculate a hash based on all the inputs for a render pipeline */
1024
- _hashRenderPipeline(props) {
1025
- const vsHash = props.vs ? this._getHash(props.vs.source) : 0;
1026
- const fsHash = props.fs ? this._getHash(props.fs.source) : 0;
1027
- const varyingHash = this._getWebGLVaryingHash(props);
1028
- const bufferLayoutHash = this._getHash(JSON.stringify(props.bufferLayout));
1029
- const { type } = this.device;
1030
- switch (type) {
1031
- case "webgl":
1032
- const webglParameterHash = this._getHash(JSON.stringify(props.parameters));
1033
- return `${type}/R/${vsHash}/${fsHash}V${varyingHash}T${props.topology}P${webglParameterHash}BL${bufferLayoutHash}`;
1034
- case "webgpu":
1035
- default:
1036
- const parameterHash = this._getHash(JSON.stringify(props.parameters));
1037
- return `${type}/R/${vsHash}/${fsHash}V${varyingHash}T${props.topology}P${parameterHash}BL${bufferLayoutHash}`;
1038
- }
1039
- }
1040
- _hashSharedRenderPipeline(props) {
1041
- const vsHash = props.vs ? this._getHash(props.vs.source) : 0;
1042
- const fsHash = props.fs ? this._getHash(props.fs.source) : 0;
1043
- const varyingHash = this._getWebGLVaryingHash(props);
1044
- return `webgl/S/${vsHash}/${fsHash}V${varyingHash}`;
1045
- }
1046
- _getHash(key) {
1047
- if (this._hashes[key] === void 0) {
1048
- this._hashes[key] = this._hashCounter++;
1049
- }
1050
- return this._hashes[key];
1051
- }
1052
- _getWebGLVaryingHash(props) {
1053
- const { varyings = [], bufferMode = null } = props;
1054
- return this._getHash(JSON.stringify({ varyings, bufferMode }));
1055
- }
1056
- };
1057
- var PipelineFactory = _PipelineFactory;
1058
- __publicField(PipelineFactory, "defaultProps", { ...import_core4.RenderPipeline.defaultProps });
1059
-
1060
- // dist/factories/shader-factory.js
1061
- var import_core5 = require("@luma.gl/core");
1062
- var _ShaderFactory = class {
1063
- /** Returns the default ShaderFactory for the given {@link Device}, creating one if necessary. */
1064
- static getDefaultShaderFactory(device) {
1065
- const moduleData = device.getModuleData("@luma.gl/engine");
1066
- moduleData.defaultShaderFactory ||= new _ShaderFactory(device);
1067
- return moduleData.defaultShaderFactory;
1068
- }
1069
- device;
1070
- _cache = {};
1071
- get [Symbol.toStringTag]() {
1072
- return "ShaderFactory";
1073
- }
1074
- toString() {
1075
- return `${this[Symbol.toStringTag]}(${this.device.id})`;
1076
- }
1077
- /** @internal */
1078
- constructor(device) {
1079
- this.device = device;
1080
- }
1081
- /** Requests a {@link Shader} from the cache, creating a new Shader only if necessary. */
1082
- createShader(props) {
1083
- if (!this.device.props._cacheShaders) {
1084
- return this.device.createShader(props);
1085
- }
1086
- const key = this._hashShader(props);
1087
- let cacheEntry = this._cache[key];
1088
- if (!cacheEntry) {
1089
- const resource = this.device.createShader({
1090
- ...props,
1091
- id: props.id ? `${props.id}-cached` : void 0
1092
- });
1093
- this._cache[key] = cacheEntry = { resource, useCount: 1 };
1094
- if (this.device.props.debugFactories) {
1095
- import_core5.log.log(3, `${this}: Created new shader ${resource.id}`)();
1096
- }
1097
- } else {
1098
- cacheEntry.useCount++;
1099
- if (this.device.props.debugFactories) {
1100
- import_core5.log.log(3, `${this}: Reusing shader ${cacheEntry.resource.id} count=${cacheEntry.useCount}`)();
1101
- }
1102
- }
1103
- return cacheEntry.resource;
1104
- }
1105
- /** Releases a previously-requested {@link Shader}, destroying it if no users remain. */
1106
- release(shader) {
1107
- if (!this.device.props._cacheShaders) {
1108
- shader.destroy();
1109
- return;
1110
- }
1111
- const key = this._hashShader(shader);
1112
- const cacheEntry = this._cache[key];
1113
- if (cacheEntry) {
1114
- cacheEntry.useCount--;
1115
- if (cacheEntry.useCount === 0) {
1116
- if (this.device.props._destroyShaders) {
1117
- delete this._cache[key];
1118
- cacheEntry.resource.destroy();
1119
- if (this.device.props.debugFactories) {
1120
- import_core5.log.log(3, `${this}: Releasing shader ${shader.id}, destroyed`)();
1121
- }
1122
- }
1123
- } else if (cacheEntry.useCount < 0) {
1124
- throw new Error(`ShaderFactory: Shader ${shader.id} released too many times`);
1125
- } else if (this.device.props.debugFactories) {
1126
- import_core5.log.log(3, `${this}: Releasing shader ${shader.id} count=${cacheEntry.useCount}`)();
1127
- }
1128
- }
1129
- }
1130
- // PRIVATE
1131
- _hashShader(value) {
1132
- return `${value.stage}:${value.source}`;
1133
- }
1134
- };
1135
- var ShaderFactory = _ShaderFactory;
1136
- __publicField(ShaderFactory, "defaultProps", { ...import_core5.Shader.defaultProps });
1137
-
1138
875
  // dist/debug/debug-shader-layout.js
1139
876
  function getDebugTableForShaderLayout(layout, name) {
1140
877
  var _a;
@@ -1235,7 +972,7 @@ function deepEqual(a, b, depth) {
1235
972
  }
1236
973
 
1237
974
  // dist/utils/buffer-layout-helper.js
1238
- var import_core6 = require("@luma.gl/core");
975
+ var import_core4 = require("@luma.gl/core");
1239
976
  var BufferLayoutHelper = class {
1240
977
  bufferLayouts;
1241
978
  constructor(bufferLayouts) {
@@ -1264,7 +1001,7 @@ var BufferLayoutHelper = class {
1264
1001
  getBufferIndex(bufferName) {
1265
1002
  const bufferIndex = this.bufferLayouts.findIndex((layout) => layout.name === bufferName);
1266
1003
  if (bufferIndex === -1) {
1267
- import_core6.log.warn(`BufferLayout: Missing buffer for "${bufferName}".`)();
1004
+ import_core4.log.warn(`BufferLayout: Missing buffer for "${bufferName}".`)();
1268
1005
  }
1269
1006
  return bufferIndex;
1270
1007
  }
@@ -1294,8 +1031,52 @@ function sortedBufferLayoutByShaderSourceLocations(shaderLayout, bufferLayout) {
1294
1031
  return sortedLayout;
1295
1032
  }
1296
1033
 
1034
+ // dist/utils/shader-module-utils.js
1035
+ function mergeShaderModuleBindingsIntoLayout(shaderLayout, modules) {
1036
+ if (!shaderLayout || !modules.some((module2) => {
1037
+ var _a;
1038
+ return (_a = module2.bindingLayout) == null ? void 0 : _a.length;
1039
+ })) {
1040
+ return shaderLayout;
1041
+ }
1042
+ const mergedLayout = {
1043
+ ...shaderLayout,
1044
+ bindings: shaderLayout.bindings.map((binding) => ({ ...binding }))
1045
+ };
1046
+ if ("attributes" in (shaderLayout || {})) {
1047
+ mergedLayout.attributes = (shaderLayout == null ? void 0 : shaderLayout.attributes) || [];
1048
+ }
1049
+ for (const module2 of modules) {
1050
+ for (const bindingLayout of module2.bindingLayout || []) {
1051
+ for (const relatedBindingName of getRelatedBindingNames(bindingLayout.name)) {
1052
+ const binding = mergedLayout.bindings.find((candidate) => candidate.name === relatedBindingName);
1053
+ if ((binding == null ? void 0 : binding.group) === 0) {
1054
+ binding.group = bindingLayout.group;
1055
+ }
1056
+ }
1057
+ }
1058
+ }
1059
+ return mergedLayout;
1060
+ }
1061
+ function shaderModuleHasUniforms(module2) {
1062
+ return Boolean(module2.uniformTypes && !isObjectEmpty(module2.uniformTypes));
1063
+ }
1064
+ function getRelatedBindingNames(bindingName) {
1065
+ const bindingNames = /* @__PURE__ */ new Set([bindingName, `${bindingName}Uniforms`]);
1066
+ if (!bindingName.endsWith("Uniforms")) {
1067
+ bindingNames.add(`${bindingName}Sampler`);
1068
+ }
1069
+ return [...bindingNames];
1070
+ }
1071
+ function isObjectEmpty(obj) {
1072
+ for (const key in obj) {
1073
+ return false;
1074
+ }
1075
+ return true;
1076
+ }
1077
+
1297
1078
  // dist/shader-inputs.js
1298
- var import_core7 = require("@luma.gl/core");
1079
+ var import_core5 = require("@luma.gl/core");
1299
1080
  var import_shadertools = require("@luma.gl/shadertools");
1300
1081
 
1301
1082
  // dist/model/split-uniforms-and-bindings.js
@@ -1303,11 +1084,11 @@ var import_types = require("@math.gl/types");
1303
1084
  function isUniformValue(value) {
1304
1085
  return (0, import_types.isNumericArray)(value) || typeof value === "number" || typeof value === "boolean";
1305
1086
  }
1306
- function splitUniformsAndBindings(uniforms) {
1087
+ function splitUniformsAndBindings(uniforms, uniformTypes2 = {}) {
1307
1088
  const result = { bindings: {}, uniforms: {} };
1308
1089
  Object.keys(uniforms).forEach((name) => {
1309
1090
  const uniform = uniforms[name];
1310
- if (isUniformValue(uniform)) {
1091
+ if (Object.prototype.hasOwnProperty.call(uniformTypes2, name) || isUniformValue(uniform)) {
1311
1092
  result.uniforms[name] = uniform;
1312
1093
  } else {
1313
1094
  result.bindings[name] = uniform;
@@ -1339,18 +1120,21 @@ var ShaderInputs = class {
1339
1120
  */
1340
1121
  constructor(modules, options) {
1341
1122
  Object.assign(this.options, options);
1342
- const resolvedModules = (0, import_shadertools.getShaderModuleDependencies)(Object.values(modules).filter((module2) => module2.dependencies));
1123
+ const resolvedModules = (0, import_shadertools.getShaderModuleDependencies)(Object.values(modules).filter(isShaderInputsModuleWithDependencies));
1343
1124
  for (const resolvedModule of resolvedModules) {
1344
1125
  modules[resolvedModule.name] = resolvedModule;
1345
1126
  }
1346
- import_core7.log.log(1, "Creating ShaderInputs with modules", Object.keys(modules))();
1127
+ import_core5.log.log(1, "Creating ShaderInputs with modules", Object.keys(modules))();
1347
1128
  this.modules = modules;
1348
1129
  this.moduleUniforms = {};
1349
1130
  this.moduleBindings = {};
1350
1131
  for (const [name, module2] of Object.entries(modules)) {
1132
+ if (!module2) {
1133
+ continue;
1134
+ }
1351
1135
  this._addModule(module2);
1352
1136
  if (module2.name && name !== module2.name && !this.options.disableWarnings) {
1353
- import_core7.log.warn(`Module name: ${name} vs ${module2.name}`)();
1137
+ import_core5.log.warn(`Module name: ${name} vs ${module2.name}`)();
1354
1138
  }
1355
1139
  }
1356
1140
  }
@@ -1368,15 +1152,15 @@ var ShaderInputs = class {
1368
1152
  const module2 = this.modules[moduleName];
1369
1153
  if (!module2) {
1370
1154
  if (!this.options.disableWarnings) {
1371
- import_core7.log.warn(`Module ${name} not found`)();
1155
+ import_core5.log.warn(`Module ${name} not found`)();
1372
1156
  }
1373
1157
  continue;
1374
1158
  }
1375
1159
  const oldUniforms = this.moduleUniforms[moduleName];
1376
1160
  const oldBindings = this.moduleBindings[moduleName];
1377
1161
  const uniformsAndBindings = ((_a = module2.getUniforms) == null ? void 0 : _a.call(module2, moduleProps, oldUniforms)) || moduleProps;
1378
- const { uniforms, bindings } = splitUniformsAndBindings(uniformsAndBindings);
1379
- this.moduleUniforms[moduleName] = { ...oldUniforms, ...uniforms };
1162
+ const { uniforms, bindings } = splitUniformsAndBindings(uniformsAndBindings, module2.uniformTypes);
1163
+ this.moduleUniforms[moduleName] = mergeModuleUniforms(oldUniforms, uniforms, module2.uniformTypes);
1380
1164
  this.moduleBindings[moduleName] = { ...oldBindings, ...bindings };
1381
1165
  }
1382
1166
  }
@@ -1416,16 +1200,87 @@ var ShaderInputs = class {
1416
1200
  }
1417
1201
  _addModule(module2) {
1418
1202
  const moduleName = module2.name;
1419
- this.moduleUniforms[moduleName] = module2.defaultUniforms || {};
1203
+ this.moduleUniforms[moduleName] = mergeModuleUniforms({}, module2.defaultUniforms || {}, module2.uniformTypes);
1420
1204
  this.moduleBindings[moduleName] = {};
1421
1205
  }
1422
1206
  };
1207
+ function mergeModuleUniforms(currentUniforms = {}, nextUniforms = {}, uniformTypes2 = {}) {
1208
+ const mergedUniforms = { ...currentUniforms };
1209
+ for (const [key, value] of Object.entries(nextUniforms)) {
1210
+ if (value === void 0) {
1211
+ continue;
1212
+ }
1213
+ mergedUniforms[key] = mergeModuleUniformValue(currentUniforms[key], value, uniformTypes2[key]);
1214
+ }
1215
+ return mergedUniforms;
1216
+ }
1217
+ function mergeModuleUniformValue(currentValue, nextValue, uniformType) {
1218
+ if (!uniformType || typeof uniformType === "string") {
1219
+ return cloneModuleUniformValue(nextValue);
1220
+ }
1221
+ if (Array.isArray(uniformType)) {
1222
+ if (isPackedUniformArrayValue(nextValue) || !Array.isArray(nextValue)) {
1223
+ return cloneModuleUniformValue(nextValue);
1224
+ }
1225
+ const currentArray = Array.isArray(currentValue) && !isPackedUniformArrayValue(currentValue) ? [...currentValue] : [];
1226
+ const mergedArray = currentArray.slice();
1227
+ for (let index = 0; index < nextValue.length; index++) {
1228
+ const elementValue = nextValue[index];
1229
+ if (elementValue === void 0) {
1230
+ continue;
1231
+ }
1232
+ mergedArray[index] = mergeModuleUniformValue(currentArray[index], elementValue, uniformType[0]);
1233
+ }
1234
+ return mergedArray;
1235
+ }
1236
+ if (!isPlainUniformObject(nextValue)) {
1237
+ return cloneModuleUniformValue(nextValue);
1238
+ }
1239
+ const uniformStruct = uniformType;
1240
+ const currentObject = isPlainUniformObject(currentValue) ? currentValue : {};
1241
+ const mergedObject = { ...currentObject };
1242
+ for (const [key, value] of Object.entries(nextValue)) {
1243
+ if (value === void 0) {
1244
+ continue;
1245
+ }
1246
+ mergedObject[key] = mergeModuleUniformValue(currentObject[key], value, uniformStruct[key]);
1247
+ }
1248
+ return mergedObject;
1249
+ }
1250
+ function cloneModuleUniformValue(value) {
1251
+ if (ArrayBuffer.isView(value)) {
1252
+ return Array.prototype.slice.call(value);
1253
+ }
1254
+ if (Array.isArray(value)) {
1255
+ if (isPackedUniformArrayValue(value)) {
1256
+ return value.slice();
1257
+ }
1258
+ const compositeArray = value;
1259
+ return compositeArray.map((element) => element === void 0 ? void 0 : cloneModuleUniformValue(element));
1260
+ }
1261
+ if (isPlainUniformObject(value)) {
1262
+ return Object.fromEntries(Object.entries(value).map(([key, nestedValue]) => [
1263
+ key,
1264
+ nestedValue === void 0 ? void 0 : cloneModuleUniformValue(nestedValue)
1265
+ ]));
1266
+ }
1267
+ return value;
1268
+ }
1269
+ function isPackedUniformArrayValue(value) {
1270
+ return ArrayBuffer.isView(value) || Array.isArray(value) && (value.length === 0 || typeof value[0] === "number");
1271
+ }
1272
+ function isPlainUniformObject(value) {
1273
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value) && !ArrayBuffer.isView(value);
1274
+ }
1275
+ function isShaderInputsModuleWithDependencies(module2) {
1276
+ return Boolean(module2 == null ? void 0 : module2.dependencies);
1277
+ }
1423
1278
 
1424
1279
  // dist/dynamic-texture/dynamic-texture.js
1425
- var import_core9 = require("@luma.gl/core");
1280
+ var import_core7 = require("@luma.gl/core");
1426
1281
 
1427
1282
  // dist/dynamic-texture/texture-data.js
1428
- var import_core8 = require("@luma.gl/core");
1283
+ var import_core6 = require("@luma.gl/core");
1429
1284
  var TEXTURE_CUBE_FACE_MAP = { "+X": 0, "-X": 1, "+Y": 2, "-Y": 3, "+Z": 4, "-Z": 5 };
1430
1285
  function getFirstMipLevel(layer) {
1431
1286
  if (!layer)
@@ -1479,8 +1334,8 @@ function getTextureSizeFromData(props) {
1479
1334
  }
1480
1335
  }
1481
1336
  function getTextureMipLevelSize(data) {
1482
- if ((0, import_core8.isExternalImage)(data)) {
1483
- return (0, import_core8.getExternalImageSize)(data);
1337
+ if ((0, import_core6.isExternalImage)(data)) {
1338
+ return (0, import_core6.getExternalImageSize)(data);
1484
1339
  }
1485
1340
  if (typeof data === "object" && "width" in data && "height" in data) {
1486
1341
  return { width: data.width, height: data.height };
@@ -1490,6 +1345,9 @@ function getTextureMipLevelSize(data) {
1490
1345
  function isTextureImageData(data) {
1491
1346
  return typeof data === "object" && data !== null && "data" in data && "width" in data && "height" in data;
1492
1347
  }
1348
+ function isTypedArrayMipLevelData(data) {
1349
+ return ArrayBuffer.isView(data);
1350
+ }
1493
1351
  function resolveTextureImageFormat(data) {
1494
1352
  const { textureFormat, format } = data;
1495
1353
  if (textureFormat && format && textureFormat !== format) {
@@ -1512,13 +1370,13 @@ function getTexture1DSubresources(data) {
1512
1370
  function _normalizeTexture2DData(data) {
1513
1371
  return Array.isArray(data) ? data : [data];
1514
1372
  }
1515
- function getTexture2DSubresources(slice, lodData) {
1373
+ function getTexture2DSubresources(slice, lodData, baseLevelSize, textureFormat) {
1516
1374
  const lodArray = _normalizeTexture2DData(lodData);
1517
1375
  const z = slice;
1518
1376
  const subresources = [];
1519
1377
  for (let mipLevel = 0; mipLevel < lodArray.length; mipLevel++) {
1520
1378
  const imageData = lodArray[mipLevel];
1521
- if ((0, import_core8.isExternalImage)(imageData)) {
1379
+ if ((0, import_core6.isExternalImage)(imageData)) {
1522
1380
  subresources.push({
1523
1381
  type: "external-image",
1524
1382
  image: imageData,
@@ -1533,6 +1391,19 @@ function getTexture2DSubresources(slice, lodData) {
1533
1391
  z,
1534
1392
  mipLevel
1535
1393
  });
1394
+ } else if (isTypedArrayMipLevelData(imageData) && baseLevelSize) {
1395
+ subresources.push({
1396
+ type: "texture-data",
1397
+ data: {
1398
+ data: imageData,
1399
+ width: Math.max(1, baseLevelSize.width >> mipLevel),
1400
+ height: Math.max(1, baseLevelSize.height >> mipLevel),
1401
+ ...textureFormat ? { format: textureFormat } : {}
1402
+ },
1403
+ textureFormat,
1404
+ z,
1405
+ mipLevel
1406
+ });
1536
1407
  } else {
1537
1408
  throw new Error("Unsupported 2D mip-level payload");
1538
1409
  }
@@ -1628,7 +1499,12 @@ var _DynamicTexture = class {
1628
1499
  try {
1629
1500
  const propsWithSyncData = await this._loadAllData(originalPropsWithAsyncData);
1630
1501
  this._checkNotDestroyed();
1631
- const subresources = propsWithSyncData.data ? getTextureSubresources(propsWithSyncData) : [];
1502
+ const subresources = propsWithSyncData.data ? getTextureSubresources({
1503
+ ...propsWithSyncData,
1504
+ width: originalPropsWithAsyncData.width,
1505
+ height: originalPropsWithAsyncData.height,
1506
+ format: originalPropsWithAsyncData.format
1507
+ }) : [];
1632
1508
  const userProvidedFormat = "format" in originalPropsWithAsyncData && originalPropsWithAsyncData.format !== void 0;
1633
1509
  const userProvidedUsage = "usage" in originalPropsWithAsyncData && originalPropsWithAsyncData.usage !== void 0;
1634
1510
  const deduceSize = () => {
@@ -1658,11 +1534,11 @@ var _DynamicTexture = class {
1658
1534
  data: void 0
1659
1535
  };
1660
1536
  if (this.device.isTextureFormatCompressed(resolvedFormat) && !userProvidedUsage) {
1661
- baseTextureProps.usage = import_core9.Texture.SAMPLE | import_core9.Texture.COPY_DST;
1537
+ baseTextureProps.usage = import_core7.Texture.SAMPLE | import_core7.Texture.COPY_DST;
1662
1538
  }
1663
1539
  const shouldGenerateMipmaps = this.props.mipmaps && !textureData.hasExplicitMipChain && !this.device.isTextureFormatCompressed(resolvedFormat);
1664
1540
  if (this.device.type === "webgpu" && shouldGenerateMipmaps) {
1665
- 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;
1541
+ 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;
1666
1542
  baseTextureProps.usage |= requiredUsage;
1667
1543
  }
1668
1544
  const maxMips = this.device.getMipLevelCount(baseTextureProps.width, baseTextureProps.height);
@@ -1675,18 +1551,17 @@ var _DynamicTexture = class {
1675
1551
  this._setTextureSubresources(textureData.subresources);
1676
1552
  }
1677
1553
  if (this.props.mipmaps && !textureData.hasExplicitMipChain && !shouldGenerateMipmaps) {
1678
- import_core9.log.warn(`${this} skipping auto-generated mipmaps for compressed texture format`)();
1554
+ import_core7.log.warn(`${this} skipping auto-generated mipmaps for compressed texture format`)();
1679
1555
  }
1680
1556
  if (shouldGenerateMipmaps) {
1681
1557
  this.generateMipmaps();
1682
1558
  }
1683
1559
  this.isReady = true;
1684
1560
  this.resolveReady(this.texture);
1685
- import_core9.log.info(0, `${this} created`)();
1561
+ import_core7.log.info(0, `${this} created`)();
1686
1562
  } catch (e) {
1687
1563
  const err = e instanceof Error ? e : new Error(String(e));
1688
1564
  this.rejectReady(err);
1689
- throw err;
1690
1565
  }
1691
1566
  }
1692
1567
  destroy() {
@@ -1704,16 +1579,57 @@ var _DynamicTexture = class {
1704
1579
  } else if (this.device.type === "webgpu") {
1705
1580
  this.device.generateMipmapsWebGPU(this.texture);
1706
1581
  } else {
1707
- import_core9.log.warn(`${this} mipmaps not supported on ${this.device.type}`);
1582
+ import_core7.log.warn(`${this} mipmaps not supported on ${this.device.type}`);
1708
1583
  }
1709
1584
  }
1710
1585
  /** Set sampler or create one from props */
1711
1586
  setSampler(sampler = {}) {
1712
1587
  this._checkReady();
1713
- const s = sampler instanceof import_core9.Sampler ? sampler : this.device.createSampler(sampler);
1588
+ const s = sampler instanceof import_core7.Sampler ? sampler : this.device.createSampler(sampler);
1714
1589
  this.texture.setSampler(s);
1715
1590
  this._sampler = s;
1716
1591
  }
1592
+ /**
1593
+ * Copies texture contents into a GPU buffer and waits until the copy is complete.
1594
+ * The caller owns the returned buffer and must destroy it when finished.
1595
+ */
1596
+ async readBuffer(options = {}) {
1597
+ if (!this.isReady) {
1598
+ await this.ready;
1599
+ }
1600
+ const width = options.width ?? this.texture.width;
1601
+ const height = options.height ?? this.texture.height;
1602
+ const depthOrArrayLayers = options.depthOrArrayLayers ?? this.texture.depth;
1603
+ const layout = this.texture.computeMemoryLayout({ width, height, depthOrArrayLayers });
1604
+ const buffer = this.device.createBuffer({
1605
+ byteLength: layout.byteLength,
1606
+ usage: import_core7.Buffer.COPY_DST | import_core7.Buffer.MAP_READ
1607
+ });
1608
+ this.texture.readBuffer({
1609
+ ...options,
1610
+ width,
1611
+ height,
1612
+ depthOrArrayLayers
1613
+ }, buffer);
1614
+ const fence = this.device.createFence();
1615
+ await fence.signaled;
1616
+ fence.destroy();
1617
+ return buffer;
1618
+ }
1619
+ /** Reads texture contents back to CPU memory. */
1620
+ async readAsync(options = {}) {
1621
+ if (!this.isReady) {
1622
+ await this.ready;
1623
+ }
1624
+ const width = options.width ?? this.texture.width;
1625
+ const height = options.height ?? this.texture.height;
1626
+ const depthOrArrayLayers = options.depthOrArrayLayers ?? this.texture.depth;
1627
+ const layout = this.texture.computeMemoryLayout({ width, height, depthOrArrayLayers });
1628
+ const buffer = await this.readBuffer(options);
1629
+ const data = await buffer.readAsync(0, layout.byteLength);
1630
+ buffer.destroy();
1631
+ return data.buffer;
1632
+ }
1717
1633
  /**
1718
1634
  * Resize by cloning the underlying immutable texture.
1719
1635
  * Does not copy contents; caller may need to re-upload and/or regenerate mips.
@@ -1728,7 +1644,7 @@ var _DynamicTexture = class {
1728
1644
  this._sampler = this.texture.sampler;
1729
1645
  this._view = this.texture.view;
1730
1646
  prev.destroy();
1731
- import_core9.log.info(`${this} resized`);
1647
+ import_core7.log.info(`${this} resized`);
1732
1648
  return true;
1733
1649
  }
1734
1650
  /** Convert cube face label to texture slice index. Index can be used with `setTexture2DData()`. */
@@ -1830,18 +1746,18 @@ var _DynamicTexture = class {
1830
1746
  }
1831
1747
  _checkNotDestroyed() {
1832
1748
  if (this.destroyed) {
1833
- import_core9.log.warn(`${this} already destroyed`);
1749
+ import_core7.log.warn(`${this} already destroyed`);
1834
1750
  }
1835
1751
  }
1836
1752
  _checkReady() {
1837
1753
  if (!this.isReady) {
1838
- import_core9.log.warn(`${this} Cannot perform this operation before ready`);
1754
+ import_core7.log.warn(`${this} Cannot perform this operation before ready`);
1839
1755
  }
1840
1756
  }
1841
1757
  };
1842
1758
  var DynamicTexture = _DynamicTexture;
1843
1759
  __publicField(DynamicTexture, "defaultProps", {
1844
- ...import_core9.Texture.defaultProps,
1760
+ ...import_core7.Texture.defaultProps,
1845
1761
  dimension: "2d",
1846
1762
  data: null,
1847
1763
  mipmaps: false
@@ -1850,11 +1766,13 @@ function getTextureSubresources(props) {
1850
1766
  if (!props.data) {
1851
1767
  return [];
1852
1768
  }
1769
+ const baseLevelSize = props.width && props.height ? { width: props.width, height: props.height } : void 0;
1770
+ const textureFormat = "format" in props ? props.format : void 0;
1853
1771
  switch (props.dimension) {
1854
1772
  case "1d":
1855
1773
  return getTexture1DSubresources(props.data);
1856
1774
  case "2d":
1857
- return getTexture2DSubresources(0, props.data);
1775
+ return getTexture2DSubresources(0, props.data, baseLevelSize, textureFormat);
1858
1776
  case "3d":
1859
1777
  return getTexture3DSubresources(props.data);
1860
1778
  case "2d-array":
@@ -1981,7 +1899,7 @@ async function awaitAllPromises(x) {
1981
1899
  }
1982
1900
  if (x && typeof x === "object" && x.constructor === Object) {
1983
1901
  const object = x;
1984
- const values = await Promise.all(Object.values(object));
1902
+ const values = await Promise.all(Object.values(object).map(awaitAllPromises));
1985
1903
  const keys = Object.keys(object);
1986
1904
  const resolvedObject = {};
1987
1905
  for (let i = 0; i < keys.length; i++) {
@@ -2050,6 +1968,7 @@ var _Model = class {
2050
1968
  /** ShaderInputs instance */
2051
1969
  // @ts-expect-error Assigned in function called by constructor
2052
1970
  shaderInputs;
1971
+ material = null;
2053
1972
  // @ts-expect-error Assigned in function called by constructor
2054
1973
  _uniformStore;
2055
1974
  _attributeInfos = {};
@@ -2060,6 +1979,7 @@ var _Model = class {
2060
1979
  _destroyed = false;
2061
1980
  /** "Time" of last draw. Monotonically increasing timestamp */
2062
1981
  _lastDrawTimestamp = -1;
1982
+ _bindingTable = [];
2063
1983
  get [Symbol.toStringTag]() {
2064
1984
  return "Model";
2065
1985
  }
@@ -2067,12 +1987,13 @@ var _Model = class {
2067
1987
  return `Model(${this.id})`;
2068
1988
  }
2069
1989
  constructor(device, props) {
2070
- var _a, _b, _c;
1990
+ var _a, _b, _c, _d;
2071
1991
  this.props = { ..._Model.defaultProps, ...props };
2072
1992
  props = this.props;
2073
1993
  this.id = props.id || uid("model");
2074
1994
  this.device = device;
2075
1995
  Object.assign(this.userData, props.userData);
1996
+ this.material = props.material || null;
2076
1997
  const moduleMap = Object.fromEntries(((_a = this.props.modules) == null ? void 0 : _a.map((module2) => [module2.name, module2])) || []);
2077
1998
  const shaderInputs = props.shaderInputs || new ShaderInputs(moduleMap, { disableWarnings: this.props.disableWarnings });
2078
1999
  this.setShaderInputs(shaderInputs);
@@ -2081,16 +2002,19 @@ var _Model = class {
2081
2002
  // @ts-ignore shaderInputs is assigned in setShaderInputs above.
2082
2003
  (((_b = this.props.modules) == null ? void 0 : _b.length) > 0 ? this.props.modules : (_c = this.shaderInputs) == null ? void 0 : _c.getModules()) || []
2083
2004
  );
2005
+ this.props.shaderLayout = mergeShaderModuleBindingsIntoLayout(this.props.shaderLayout, modules) || null;
2084
2006
  const isWebGPU = this.device.type === "webgpu";
2085
2007
  if (isWebGPU && this.props.source) {
2086
- const { source: source3, getUniforms: getUniforms2 } = this.props.shaderAssembler.assembleWGSLShader({
2008
+ const { source: source3, getUniforms: getUniforms2, bindingTable } = this.props.shaderAssembler.assembleWGSLShader({
2087
2009
  platformInfo,
2088
2010
  ...this.props,
2089
2011
  modules
2090
2012
  });
2091
2013
  this.source = source3;
2092
2014
  this._getModuleUniforms = getUniforms2;
2093
- this.props.shaderLayout ||= device.getShaderLayout(this.source);
2015
+ this._bindingTable = bindingTable;
2016
+ const inferredShaderLayout = (_d = device.getShaderLayout) == null ? void 0 : _d.call(device, this.source);
2017
+ this.props.shaderLayout = mergeShaderModuleBindingsIntoLayout(this.props.shaderLayout || inferredShaderLayout || null, modules) || null;
2094
2018
  } else {
2095
2019
  const { vs: vs3, fs: fs3, getUniforms: getUniforms2 } = this.props.shaderAssembler.assembleGLSLShaderPair({
2096
2020
  platformInfo,
@@ -2100,6 +2024,7 @@ var _Model = class {
2100
2024
  this.vs = vs3;
2101
2025
  this.fs = fs3;
2102
2026
  this._getModuleUniforms = getUniforms2;
2027
+ this._bindingTable = [];
2103
2028
  }
2104
2029
  this.vertexCount = this.props.vertexCount;
2105
2030
  this.instanceCount = this.props.instanceCount;
@@ -2109,8 +2034,8 @@ var _Model = class {
2109
2034
  if (props.geometry) {
2110
2035
  this.setGeometry(props.geometry);
2111
2036
  }
2112
- this.pipelineFactory = props.pipelineFactory || PipelineFactory.getDefaultPipelineFactory(this.device);
2113
- this.shaderFactory = props.shaderFactory || ShaderFactory.getDefaultShaderFactory(this.device);
2037
+ this.pipelineFactory = props.pipelineFactory || import_core8.PipelineFactory.getDefaultPipelineFactory(this.device);
2038
+ this.shaderFactory = props.shaderFactory || import_core8.ShaderFactory.getDefaultShaderFactory(this.device);
2114
2039
  this.pipeline = this._updatePipeline();
2115
2040
  this.vertexArray = device.createVertexArray({
2116
2041
  shaderLayout: this.pipeline.shaderLayout,
@@ -2171,6 +2096,10 @@ var _Model = class {
2171
2096
  setNeedsRedraw(reason) {
2172
2097
  this._needsRedraw ||= reason;
2173
2098
  }
2099
+ /** Returns WGSL binding debug rows for the assembled shader. Returns an empty array for GLSL models. */
2100
+ getBindingDebugTable() {
2101
+ return this._bindingTable;
2102
+ }
2174
2103
  /** Update uniforms and pipeline state prior to drawing. */
2175
2104
  predraw() {
2176
2105
  this.updateShaderInputs();
@@ -2184,7 +2113,7 @@ var _Model = class {
2184
2113
  draw(renderPass) {
2185
2114
  const loadingBinding = this._areBindingsLoading();
2186
2115
  if (loadingBinding) {
2187
- import_core10.log.info(LOG_DRAW_PRIORITY, `>>> DRAWING ABORTED ${this.id}: ${loadingBinding} not loaded`)();
2116
+ import_core8.log.info(LOG_DRAW_PRIORITY, `>>> DRAWING ABORTED ${this.id}: ${loadingBinding} not loaded`)();
2188
2117
  return false;
2189
2118
  }
2190
2119
  try {
@@ -2199,6 +2128,7 @@ var _Model = class {
2199
2128
  this._logDrawCallStart();
2200
2129
  this.pipeline = this._updatePipeline();
2201
2130
  const syncBindings = this._getBindings();
2131
+ const syncBindGroups = this._getBindGroups();
2202
2132
  const { indexBuffer } = this.vertexArray;
2203
2133
  const indexCount = indexBuffer ? indexBuffer.byteLength / (indexBuffer.indexType === "uint32" ? 4 : 2) : void 0;
2204
2134
  drawSuccess = this.pipeline.draw({
@@ -2213,6 +2143,8 @@ var _Model = class {
2213
2143
  // and WebGL uniforms must be supplied on every draw instead of being stored
2214
2144
  // on the pipeline instance.
2215
2145
  bindings: syncBindings,
2146
+ bindGroups: syncBindGroups,
2147
+ _bindGroupCacheKeys: this._getBindGroupCacheKeys(),
2216
2148
  uniforms: this.props.uniforms,
2217
2149
  // WebGL shares underlying cached pipelines even for models that have different parameters and topology,
2218
2150
  // so we must provide our unique parameters to each draw
@@ -2313,20 +2245,25 @@ var _Model = class {
2313
2245
  }
2314
2246
  /** Set the shader inputs */
2315
2247
  setShaderInputs(shaderInputs) {
2248
+ var _a;
2316
2249
  this.shaderInputs = shaderInputs;
2317
- this._uniformStore = new import_core10.UniformStore(this.shaderInputs.modules);
2250
+ this._uniformStore = new import_core8.UniformStore(this.shaderInputs.modules);
2318
2251
  for (const [moduleName, module2] of Object.entries(this.shaderInputs.modules)) {
2319
- if (shaderModuleHasUniforms(module2)) {
2252
+ if (shaderModuleHasUniforms(module2) && !((_a = this.material) == null ? void 0 : _a.ownsModule(moduleName))) {
2320
2253
  const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
2321
2254
  this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
2322
2255
  }
2323
2256
  }
2324
2257
  this.setNeedsRedraw("shaderInputs");
2325
2258
  }
2259
+ setMaterial(material) {
2260
+ this.material = material;
2261
+ this.setNeedsRedraw("material");
2262
+ }
2326
2263
  /** Update uniform buffers from the model's shader inputs */
2327
2264
  updateShaderInputs() {
2328
2265
  this._uniformStore.setUniforms(this.shaderInputs.getUniformValues());
2329
- this.setBindings(this.shaderInputs.getBindingValues());
2266
+ this.setBindings(this._getNonMaterialBindings(this.shaderInputs.getBindingValues()));
2330
2267
  this.setNeedsRedraw("shaderInputs");
2331
2268
  }
2332
2269
  /**
@@ -2358,7 +2295,7 @@ var _Model = class {
2358
2295
  setAttributes(buffers, options) {
2359
2296
  const disableWarnings = (options == null ? void 0 : options.disableWarnings) ?? this.props.disableWarnings;
2360
2297
  if (buffers["indices"]) {
2361
- import_core10.log.warn(`Model:${this.id} setAttributes() - indexBuffer should be set using setIndexBuffer()`)();
2298
+ import_core8.log.warn(`Model:${this.id} setAttributes() - indexBuffer should be set using setIndexBuffer()`)();
2362
2299
  }
2363
2300
  this.bufferLayout = sortedBufferLayoutByShaderSourceLocations(this.pipeline.shaderLayout, this.bufferLayout);
2364
2301
  const bufferLayoutHelper = new BufferLayoutHelper(this.bufferLayout);
@@ -2366,7 +2303,7 @@ var _Model = class {
2366
2303
  const bufferLayout = bufferLayoutHelper.getBufferLayout(bufferName);
2367
2304
  if (!bufferLayout) {
2368
2305
  if (!disableWarnings) {
2369
- import_core10.log.warn(`Model(${this.id}): Missing layout for buffer "${bufferName}".`)();
2306
+ import_core8.log.warn(`Model(${this.id}): Missing layout for buffer "${bufferName}".`)();
2370
2307
  }
2371
2308
  continue;
2372
2309
  }
@@ -2381,7 +2318,7 @@ var _Model = class {
2381
2318
  }
2382
2319
  }
2383
2320
  if (!set && !disableWarnings) {
2384
- import_core10.log.warn(`Model(${this.id}): Ignoring buffer "${buffer.id}" for unknown attribute "${bufferName}"`)();
2321
+ import_core8.log.warn(`Model(${this.id}): Ignoring buffer "${buffer.id}" for unknown attribute "${bufferName}"`)();
2385
2322
  }
2386
2323
  }
2387
2324
  this.setNeedsRedraw("attributes");
@@ -2400,7 +2337,7 @@ var _Model = class {
2400
2337
  if (attributeInfo) {
2401
2338
  this.vertexArray.setConstantWebGL(attributeInfo.location, value);
2402
2339
  } else if (!((options == null ? void 0 : options.disableWarnings) ?? this.props.disableWarnings)) {
2403
- import_core10.log.warn(`Model "${this.id}: Ignoring constant supplied for unknown attribute "${attributeName}"`)();
2340
+ import_core8.log.warn(`Model "${this.id}: Ignoring constant supplied for unknown attribute "${attributeName}"`)();
2404
2341
  }
2405
2342
  }
2406
2343
  this.setNeedsRedraw("constants");
@@ -2408,11 +2345,17 @@ var _Model = class {
2408
2345
  // INTERNAL METHODS
2409
2346
  /** Check that bindings are loaded. Returns id of first binding that is still loading. */
2410
2347
  _areBindingsLoading() {
2348
+ var _a;
2411
2349
  for (const binding of Object.values(this.bindings)) {
2412
2350
  if (binding instanceof DynamicTexture && !binding.isReady) {
2413
2351
  return binding.id;
2414
2352
  }
2415
2353
  }
2354
+ for (const binding of Object.values(((_a = this.material) == null ? void 0 : _a.bindings) || {})) {
2355
+ if (binding instanceof DynamicTexture && !binding.isReady) {
2356
+ return binding.id;
2357
+ }
2358
+ }
2416
2359
  return false;
2417
2360
  }
2418
2361
  /** Extracts texture view from loaded async textures. Returns null if any textures have not yet been loaded. */
@@ -2429,24 +2372,46 @@ var _Model = class {
2429
2372
  }
2430
2373
  return validBindings;
2431
2374
  }
2375
+ _getBindGroups() {
2376
+ var _a;
2377
+ const shaderLayout = ((_a = this.pipeline) == null ? void 0 : _a.shaderLayout) || this.props.shaderLayout || { bindings: [] };
2378
+ const bindGroups = shaderLayout.bindings.length ? (0, import_core8.normalizeBindingsByGroup)(shaderLayout, this._getBindings()) : { 0: this._getBindings() };
2379
+ if (!this.material) {
2380
+ return bindGroups;
2381
+ }
2382
+ for (const [groupKey, groupBindings] of Object.entries(this.material.getBindingsByGroup())) {
2383
+ const group = Number(groupKey);
2384
+ bindGroups[group] = {
2385
+ ...bindGroups[group] || {},
2386
+ ...groupBindings
2387
+ };
2388
+ }
2389
+ return bindGroups;
2390
+ }
2391
+ _getBindGroupCacheKeys() {
2392
+ var _a;
2393
+ const bindGroupCacheKey = (_a = this.material) == null ? void 0 : _a.getBindGroupCacheKey(3);
2394
+ return bindGroupCacheKey ? { 3: bindGroupCacheKey } : {};
2395
+ }
2432
2396
  /** Get the timestamp of the latest updated bound GPU memory resource (buffer/texture). */
2433
2397
  _getBindingsUpdateTimestamp() {
2398
+ var _a;
2434
2399
  let timestamp = 0;
2435
2400
  for (const binding of Object.values(this.bindings)) {
2436
- if (binding instanceof import_core10.TextureView) {
2401
+ if (binding instanceof import_core8.TextureView) {
2437
2402
  timestamp = Math.max(timestamp, binding.texture.updateTimestamp);
2438
- } else if (binding instanceof import_core10.Buffer || binding instanceof import_core10.Texture) {
2403
+ } else if (binding instanceof import_core8.Buffer || binding instanceof import_core8.Texture) {
2439
2404
  timestamp = Math.max(timestamp, binding.updateTimestamp);
2440
2405
  } else if (binding instanceof DynamicTexture) {
2441
2406
  timestamp = binding.texture ? Math.max(timestamp, binding.texture.updateTimestamp) : (
2442
2407
  // The texture will become available in the future
2443
2408
  Infinity
2444
2409
  );
2445
- } else if (!(binding instanceof import_core10.Sampler)) {
2410
+ } else if (!(binding instanceof import_core8.Sampler)) {
2446
2411
  timestamp = Math.max(timestamp, binding.buffer.updateTimestamp);
2447
2412
  }
2448
2413
  }
2449
- return timestamp;
2414
+ return Math.max(timestamp, ((_a = this.material) == null ? void 0 : _a.getBindingsUpdateTimestamp()) || 0);
2450
2415
  }
2451
2416
  /**
2452
2417
  * Updates the optional geometry attributes
@@ -2477,7 +2442,7 @@ var _Model = class {
2477
2442
  let prevShaderVs = null;
2478
2443
  let prevShaderFs = null;
2479
2444
  if (this.pipeline) {
2480
- import_core10.log.log(1, `Model ${this.id}: Recreating pipeline because "${this._pipelineNeedsUpdate}".`)();
2445
+ import_core8.log.log(1, `Model ${this.id}: Recreating pipeline because "${this._pipelineNeedsUpdate}".`)();
2481
2446
  prevShaderVs = this.pipeline.vs;
2482
2447
  prevShaderFs = this.pipeline.fs;
2483
2448
  }
@@ -2501,16 +2466,15 @@ var _Model = class {
2501
2466
  }
2502
2467
  this.pipeline = this.pipelineFactory.createRenderPipeline({
2503
2468
  ...this.props,
2469
+ bindings: void 0,
2504
2470
  bufferLayout: this.bufferLayout,
2505
2471
  topology: this.topology,
2506
2472
  parameters: this.parameters,
2507
- // TODO - why set bindings here when we reset them every frame?
2508
- // Should we expose a BindGroup abstraction?
2509
- bindings: this._getBindings(),
2473
+ bindGroups: this._getBindGroups(),
2510
2474
  vs: vs3,
2511
2475
  fs: fs3
2512
2476
  });
2513
- this._attributeInfos = (0, import_core10.getAttributeInfosFromLayouts)(this.pipeline.shaderLayout, this.bufferLayout);
2477
+ this._attributeInfos = (0, import_core8.getAttributeInfosFromLayouts)(this.pipeline.shaderLayout, this.bufferLayout);
2514
2478
  if (prevShaderVs)
2515
2479
  this.shaderFactory.release(prevShaderVs);
2516
2480
  if (prevShaderFs && prevShaderFs !== prevShaderVs) {
@@ -2523,24 +2487,24 @@ var _Model = class {
2523
2487
  _lastLogTime = 0;
2524
2488
  _logOpen = false;
2525
2489
  _logDrawCallStart() {
2526
- const logDrawTimeout = import_core10.log.level > 3 ? 0 : LOG_DRAW_TIMEOUT;
2527
- if (import_core10.log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
2490
+ const logDrawTimeout = import_core8.log.level > 3 ? 0 : LOG_DRAW_TIMEOUT;
2491
+ if (import_core8.log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
2528
2492
  return;
2529
2493
  }
2530
2494
  this._lastLogTime = Date.now();
2531
2495
  this._logOpen = true;
2532
- import_core10.log.group(LOG_DRAW_PRIORITY, `>>> DRAWING MODEL ${this.id}`, { collapsed: import_core10.log.level <= 2 })();
2496
+ import_core8.log.group(LOG_DRAW_PRIORITY, `>>> DRAWING MODEL ${this.id}`, { collapsed: import_core8.log.level <= 2 })();
2533
2497
  }
2534
2498
  _logDrawCallEnd() {
2535
2499
  if (this._logOpen) {
2536
2500
  const shaderLayoutTable = getDebugTableForShaderLayout(this.pipeline.shaderLayout, this.id);
2537
- import_core10.log.table(LOG_DRAW_PRIORITY, shaderLayoutTable)();
2501
+ import_core8.log.table(LOG_DRAW_PRIORITY, shaderLayoutTable)();
2538
2502
  const uniformTable = this.shaderInputs.getDebugTable();
2539
- import_core10.log.table(LOG_DRAW_PRIORITY, uniformTable)();
2503
+ import_core8.log.table(LOG_DRAW_PRIORITY, uniformTable)();
2540
2504
  const attributeTable = this._getAttributeDebugTable();
2541
- import_core10.log.table(LOG_DRAW_PRIORITY, this._attributeInfos)();
2542
- import_core10.log.table(LOG_DRAW_PRIORITY, attributeTable)();
2543
- import_core10.log.groupEnd(LOG_DRAW_PRIORITY)();
2505
+ import_core8.log.table(LOG_DRAW_PRIORITY, this._attributeInfos)();
2506
+ import_core8.log.table(LOG_DRAW_PRIORITY, attributeTable)();
2507
+ import_core8.log.groupEnd(LOG_DRAW_PRIORITY)();
2544
2508
  this._logOpen = false;
2545
2509
  }
2546
2510
  }
@@ -2579,14 +2543,26 @@ var _Model = class {
2579
2543
  }
2580
2544
  // TODO - fix typing of luma data types
2581
2545
  _getBufferOrConstantValues(attribute, dataType) {
2582
- const TypedArrayConstructor = (0, import_core10.getTypedArrayConstructor)(dataType);
2583
- const typedArray = attribute instanceof import_core10.Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
2546
+ const TypedArrayConstructor = import_core8.dataTypeDecoder.getTypedArrayConstructor(dataType);
2547
+ const typedArray = attribute instanceof import_core8.Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
2584
2548
  return typedArray.toString();
2585
2549
  }
2550
+ _getNonMaterialBindings(bindings) {
2551
+ if (!this.material) {
2552
+ return bindings;
2553
+ }
2554
+ const filteredBindings = {};
2555
+ for (const [name, binding] of Object.entries(bindings)) {
2556
+ if (!this.material.ownsBinding(name)) {
2557
+ filteredBindings[name] = binding;
2558
+ }
2559
+ }
2560
+ return filteredBindings;
2561
+ }
2586
2562
  };
2587
2563
  var Model = _Model;
2588
2564
  __publicField(Model, "defaultProps", {
2589
- ...import_core10.RenderPipeline.defaultProps,
2565
+ ...import_core8.RenderPipeline.defaultProps,
2590
2566
  source: void 0,
2591
2567
  vs: null,
2592
2568
  fs: null,
@@ -2606,6 +2582,7 @@ __publicField(Model, "defaultProps", {
2606
2582
  instanceCount: 0,
2607
2583
  vertexCount: 0,
2608
2584
  shaderInputs: void 0,
2585
+ material: void 0,
2609
2586
  pipelineFactory: void 0,
2610
2587
  shaderFactory: void 0,
2611
2588
  transformFeedback: void 0,
@@ -2613,9 +2590,6 @@ __publicField(Model, "defaultProps", {
2613
2590
  debugShaders: void 0,
2614
2591
  disableWarnings: void 0
2615
2592
  });
2616
- function shaderModuleHasUniforms(module2) {
2617
- return Boolean(module2.uniformTypes && !isObjectEmpty(module2.uniformTypes));
2618
- }
2619
2593
  function getPlatformInfo(device) {
2620
2594
  return {
2621
2595
  type: device.type,
@@ -2626,101 +2600,322 @@ function getPlatformInfo(device) {
2626
2600
  features: device.features
2627
2601
  };
2628
2602
  }
2629
- function isObjectEmpty(obj) {
2630
- for (const key in obj) {
2631
- return false;
2632
- }
2633
- return true;
2634
- }
2635
2603
 
2636
- // dist/compute/buffer-transform.js
2637
- var import_core11 = require("@luma.gl/core");
2638
- var import_shadertools3 = require("@luma.gl/shadertools");
2639
- var _BufferTransform = class {
2604
+ // dist/material/material.js
2605
+ var import_core9 = require("@luma.gl/core");
2606
+
2607
+ // dist/material/material-factory.js
2608
+ var MATERIAL_BIND_GROUP = 3;
2609
+ var MaterialFactory = class {
2610
+ /** Device that creates materials for this schema. */
2640
2611
  device;
2641
- model;
2642
- transformFeedback;
2643
- static isSupported(device) {
2644
- var _a;
2645
- return ((_a = device == null ? void 0 : device.info) == null ? void 0 : _a.type) === "webgl";
2646
- }
2647
- constructor(device, props = _BufferTransform.defaultProps) {
2648
- if (!_BufferTransform.isSupported(device)) {
2649
- throw new Error("BufferTransform not yet implemented on WebGPU");
2650
- }
2612
+ /** Shader modules that define the material schema. */
2613
+ modules;
2614
+ _materialBindingNames;
2615
+ _materialModuleNames;
2616
+ constructor(device, props = {}) {
2651
2617
  this.device = device;
2652
- this.model = new Model(this.device, {
2653
- id: props.id || "buffer-transform-model",
2654
- fs: props.fs || (0, import_shadertools3.getPassthroughFS)(),
2655
- topology: props.topology || "point-list",
2656
- varyings: props.outputs || props.varyings,
2657
- ...props
2658
- });
2659
- this.transformFeedback = this.device.createTransformFeedback({
2660
- layout: this.model.pipeline.shaderLayout,
2661
- // @ts-expect-error TODO
2662
- buffers: props.feedbackBuffers
2618
+ this.modules = props.modules || [];
2619
+ const shaderInputs = new ShaderInputs(Object.fromEntries(this.modules.map((module2) => [module2.name, module2])));
2620
+ this._materialBindingNames = getMaterialBindingNames(shaderInputs);
2621
+ this._materialModuleNames = getMaterialModuleNames(shaderInputs);
2622
+ }
2623
+ /** Creates one typed material instance for this factory's schema. */
2624
+ createMaterial(props = {}) {
2625
+ return new Material(this.device, {
2626
+ ...props,
2627
+ factory: this
2663
2628
  });
2664
- this.model.setTransformFeedback(this.transformFeedback);
2665
- Object.seal(this);
2666
2629
  }
2667
- /** Destroy owned resources. */
2668
- destroy() {
2669
- if (this.model) {
2670
- this.model.destroy();
2671
- }
2672
- }
2673
- /** @deprecated Use {@link destroy}. */
2674
- delete() {
2675
- this.destroy();
2630
+ /** Returns the logical material-owned resource binding names. */
2631
+ getBindingNames() {
2632
+ return Array.from(this._materialBindingNames);
2676
2633
  }
2677
- /** Run one transform loop. */
2678
- run(options) {
2679
- if (options == null ? void 0 : options.inputBuffers) {
2680
- this.model.setAttributes(options.inputBuffers);
2681
- }
2682
- if (options == null ? void 0 : options.outputBuffers) {
2683
- this.transformFeedback.setBuffers(options.outputBuffers);
2634
+ /** Returns `true` when the supplied binding belongs to this material schema. */
2635
+ ownsBinding(bindingName) {
2636
+ if (this._materialBindingNames.has(bindingName)) {
2637
+ return true;
2684
2638
  }
2685
- const renderPass = this.device.beginRenderPass(options);
2686
- this.model.draw(renderPass);
2687
- renderPass.end();
2639
+ const aliasedModuleName = getModuleNameFromUniformBinding(bindingName);
2640
+ return aliasedModuleName ? this._materialModuleNames.has(aliasedModuleName) : false;
2688
2641
  }
2689
- // DEPRECATED METHODS
2690
- /** @deprecated App knows what buffers it is passing in - Returns the {@link Buffer} or {@link BufferRange} for given varying name. */
2691
- getBuffer(varyingName) {
2692
- return this.transformFeedback.getBuffer(varyingName);
2642
+ /** Returns `true` when the supplied shader module is owned by this material schema. */
2643
+ ownsModule(moduleName) {
2644
+ return this._materialModuleNames.has(moduleName);
2693
2645
  }
2694
- /** @deprecated App knows what buffers it is passing in - Reads the {@link Buffer} or {@link BufferRange} for given varying name. */
2695
- readAsync(varyingName) {
2696
- const result = this.getBuffer(varyingName);
2697
- if (!result) {
2698
- throw new Error("BufferTransform#getBuffer");
2646
+ /** Packages resolved material bindings into bind group `3`. */
2647
+ getBindingsByGroup(bindings) {
2648
+ return Object.keys(bindings).length > 0 ? { [MATERIAL_BIND_GROUP]: bindings } : {};
2649
+ }
2650
+ };
2651
+ function getModuleNameFromUniformBinding(bindingName) {
2652
+ return bindingName.endsWith("Uniforms") ? bindingName.slice(0, -"Uniforms".length) : null;
2653
+ }
2654
+ function getMaterialBindingNames(shaderInputs) {
2655
+ const bindingNames = /* @__PURE__ */ new Set();
2656
+ for (const module2 of Object.values(shaderInputs.modules)) {
2657
+ for (const binding of module2.bindingLayout || []) {
2658
+ if (binding.group === MATERIAL_BIND_GROUP) {
2659
+ bindingNames.add(binding.name);
2660
+ }
2699
2661
  }
2700
- if (result instanceof import_core11.Buffer) {
2701
- return result.readAsync();
2662
+ }
2663
+ return bindingNames;
2664
+ }
2665
+ function getMaterialModuleNames(shaderInputs) {
2666
+ var _a;
2667
+ const moduleNames = /* @__PURE__ */ new Set();
2668
+ for (const module2 of Object.values(shaderInputs.modules)) {
2669
+ if (module2.name && ((_a = module2.bindingLayout) == null ? void 0 : _a.some((binding) => binding.group === MATERIAL_BIND_GROUP && binding.name === module2.name))) {
2670
+ moduleNames.add(module2.name);
2702
2671
  }
2703
- const { buffer, byteOffset = 0, byteLength = buffer.byteLength } = result;
2704
- return buffer.readAsync(byteOffset, byteLength);
2705
2672
  }
2706
- };
2707
- var BufferTransform = _BufferTransform;
2708
- __publicField(BufferTransform, "defaultProps", {
2709
- ...Model.defaultProps,
2710
- outputs: void 0,
2711
- feedbackBuffers: void 0
2712
- });
2673
+ return moduleNames;
2674
+ }
2713
2675
 
2714
- // dist/compute/texture-transform.js
2715
- var import_shadertools4 = require("@luma.gl/shadertools");
2716
- var FS_OUTPUT_VARIABLE = "transform_output";
2717
- var TextureTransform = class {
2676
+ // dist/material/material.js
2677
+ var Material = class {
2678
+ /** Application-provided identifier. */
2679
+ id;
2680
+ /** Device that owns the material resources. */
2718
2681
  device;
2719
- model;
2720
- sampler;
2721
- currentIndex = 0;
2722
- samplerTextureMap = null;
2723
- bindings = [];
2682
+ /** Factory that defines the material schema. */
2683
+ factory;
2684
+ /** Shader inputs for the material-owned modules. */
2685
+ shaderInputs;
2686
+ /** Internal binding store including uniform buffers and resource bindings. */
2687
+ bindings = {};
2688
+ _uniformStore;
2689
+ _bindGroupCacheToken = {};
2690
+ constructor(device, props = {}) {
2691
+ var _a, _b;
2692
+ this.id = props.id || uid("material");
2693
+ this.device = device;
2694
+ this.factory = props.factory || new MaterialFactory(device, {
2695
+ modules: props.modules || ((_a = props.shaderInputs) == null ? void 0 : _a.getModules()) || []
2696
+ });
2697
+ const moduleMap = Object.fromEntries((((_b = props.shaderInputs) == null ? void 0 : _b.getModules()) || this.factory.modules).map((module2) => [
2698
+ module2.name,
2699
+ module2
2700
+ ]));
2701
+ this.shaderInputs = props.shaderInputs || new ShaderInputs(moduleMap);
2702
+ this._uniformStore = new import_core9.UniformStore(this.shaderInputs.modules);
2703
+ for (const [moduleName, module2] of Object.entries(this.shaderInputs.modules)) {
2704
+ if (this.ownsModule(moduleName) && shaderModuleHasUniforms(module2)) {
2705
+ const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
2706
+ this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
2707
+ }
2708
+ }
2709
+ this.updateShaderInputs();
2710
+ if (props.bindings) {
2711
+ this._replaceOwnedBindings(props.bindings);
2712
+ }
2713
+ }
2714
+ /** Destroys managed uniform-buffer resources owned by this material. */
2715
+ destroy() {
2716
+ this._uniformStore.destroy();
2717
+ }
2718
+ /** Creates a new material variant with optional structural and uniform overrides. */
2719
+ clone(props = {}) {
2720
+ const material = this.factory.createMaterial({
2721
+ id: props.id,
2722
+ shaderInputs: props.shaderInputs,
2723
+ bindings: {
2724
+ ...this.getResourceBindings(),
2725
+ ...props.bindings
2726
+ }
2727
+ });
2728
+ if (!props.shaderInputs) {
2729
+ material.setProps(this.shaderInputs.getUniformValues());
2730
+ }
2731
+ if (props.moduleProps) {
2732
+ material.setProps(props.moduleProps);
2733
+ }
2734
+ return material;
2735
+ }
2736
+ /** Returns `true` if this material owns the supplied binding name. */
2737
+ ownsBinding(bindingName) {
2738
+ return this.factory.ownsBinding(bindingName);
2739
+ }
2740
+ /** Returns `true` if this material owns the supplied shader module. */
2741
+ ownsModule(moduleName) {
2742
+ return this.factory.ownsModule(moduleName);
2743
+ }
2744
+ /** Updates material uniform/module props in place without changing material identity. */
2745
+ setProps(props) {
2746
+ this.shaderInputs.setProps(props);
2747
+ this.updateShaderInputs();
2748
+ }
2749
+ /** Updates managed uniform buffers and shader-input-owned bindings. */
2750
+ updateShaderInputs() {
2751
+ this._uniformStore.setUniforms(this.shaderInputs.getUniformValues());
2752
+ const didChange = this._setOwnedBindings(this.shaderInputs.getBindingValues());
2753
+ if (didChange) {
2754
+ this._bindGroupCacheToken = {};
2755
+ }
2756
+ }
2757
+ /** Returns the material-owned resource bindings without internal uniform buffers. */
2758
+ getResourceBindings() {
2759
+ const resourceBindings = {};
2760
+ for (const [name, binding] of Object.entries(this.bindings)) {
2761
+ if (!getModuleNameFromUniformBinding(name)) {
2762
+ resourceBindings[name] = binding;
2763
+ }
2764
+ }
2765
+ return resourceBindings;
2766
+ }
2767
+ /** Returns the resolved bindings, including internal uniform buffers and ready textures. */
2768
+ getBindings() {
2769
+ const validBindings = {};
2770
+ const validBindingsMap = validBindings;
2771
+ for (const [name, binding] of Object.entries(this.bindings)) {
2772
+ if (binding instanceof DynamicTexture) {
2773
+ if (binding.isReady) {
2774
+ validBindingsMap[name] = binding.texture;
2775
+ }
2776
+ } else {
2777
+ validBindingsMap[name] = binding;
2778
+ }
2779
+ }
2780
+ return validBindings;
2781
+ }
2782
+ /** Packages resolved material bindings into logical bind group `3`. */
2783
+ getBindingsByGroup() {
2784
+ return this.factory.getBindingsByGroup(this.getBindings());
2785
+ }
2786
+ /** Returns the stable bind-group cache token for the requested bind group. */
2787
+ getBindGroupCacheKey(group) {
2788
+ return group === MATERIAL_BIND_GROUP ? this._bindGroupCacheToken : null;
2789
+ }
2790
+ /** Returns the latest update timestamp across material-owned resources. */
2791
+ getBindingsUpdateTimestamp() {
2792
+ let timestamp = 0;
2793
+ for (const binding of Object.values(this.bindings)) {
2794
+ if (binding instanceof import_core9.TextureView) {
2795
+ timestamp = Math.max(timestamp, binding.texture.updateTimestamp);
2796
+ } else if (binding instanceof import_core9.Buffer || binding instanceof import_core9.Texture) {
2797
+ timestamp = Math.max(timestamp, binding.updateTimestamp);
2798
+ } else if (binding instanceof DynamicTexture) {
2799
+ timestamp = binding.texture ? Math.max(timestamp, binding.texture.updateTimestamp) : Infinity;
2800
+ } else if (!(binding instanceof import_core9.Sampler)) {
2801
+ timestamp = Math.max(timestamp, binding.buffer.updateTimestamp);
2802
+ }
2803
+ }
2804
+ return timestamp;
2805
+ }
2806
+ /** Replaces owned resource bindings and invalidates the material cache identity when needed. */
2807
+ _replaceOwnedBindings(bindings) {
2808
+ const didChange = this._setOwnedBindings(bindings);
2809
+ if (didChange) {
2810
+ this._bindGroupCacheToken = {};
2811
+ }
2812
+ }
2813
+ _setOwnedBindings(bindings) {
2814
+ let didChange = false;
2815
+ for (const [name, binding] of Object.entries(bindings)) {
2816
+ if (binding === void 0) {
2817
+ continue;
2818
+ }
2819
+ if (!this.ownsBinding(name)) {
2820
+ continue;
2821
+ }
2822
+ if (this.bindings[name] !== binding) {
2823
+ this.bindings[name] = binding;
2824
+ didChange = true;
2825
+ }
2826
+ }
2827
+ return didChange;
2828
+ }
2829
+ };
2830
+
2831
+ // dist/compute/buffer-transform.js
2832
+ var import_core10 = require("@luma.gl/core");
2833
+ var import_shadertools3 = require("@luma.gl/shadertools");
2834
+ var _BufferTransform = class {
2835
+ device;
2836
+ model;
2837
+ transformFeedback;
2838
+ static isSupported(device) {
2839
+ var _a;
2840
+ return ((_a = device == null ? void 0 : device.info) == null ? void 0 : _a.type) === "webgl";
2841
+ }
2842
+ constructor(device, props = _BufferTransform.defaultProps) {
2843
+ if (!_BufferTransform.isSupported(device)) {
2844
+ throw new Error("BufferTransform not yet implemented on WebGPU");
2845
+ }
2846
+ this.device = device;
2847
+ this.model = new Model(this.device, {
2848
+ id: props.id || "buffer-transform-model",
2849
+ fs: props.fs || (0, import_shadertools3.getPassthroughFS)(),
2850
+ topology: props.topology || "point-list",
2851
+ varyings: props.outputs || props.varyings,
2852
+ ...props
2853
+ });
2854
+ this.transformFeedback = this.device.createTransformFeedback({
2855
+ layout: this.model.pipeline.shaderLayout,
2856
+ // @ts-expect-error TODO
2857
+ buffers: props.feedbackBuffers
2858
+ });
2859
+ this.model.setTransformFeedback(this.transformFeedback);
2860
+ Object.seal(this);
2861
+ }
2862
+ /** Destroy owned resources. */
2863
+ destroy() {
2864
+ if (this.model) {
2865
+ this.model.destroy();
2866
+ }
2867
+ }
2868
+ /** @deprecated Use {@link destroy}. */
2869
+ delete() {
2870
+ this.destroy();
2871
+ }
2872
+ /** Run one transform loop. */
2873
+ run(options) {
2874
+ if (options == null ? void 0 : options.inputBuffers) {
2875
+ this.model.setAttributes(options.inputBuffers);
2876
+ }
2877
+ if (options == null ? void 0 : options.outputBuffers) {
2878
+ this.transformFeedback.setBuffers(options.outputBuffers);
2879
+ }
2880
+ const renderPass = this.device.beginRenderPass(options);
2881
+ this.model.draw(renderPass);
2882
+ renderPass.end();
2883
+ }
2884
+ // DEPRECATED METHODS
2885
+ /** @deprecated App knows what buffers it is passing in - Returns the {@link Buffer} or {@link BufferRange} for given varying name. */
2886
+ getBuffer(varyingName) {
2887
+ return this.transformFeedback.getBuffer(varyingName);
2888
+ }
2889
+ /** @deprecated App knows what buffers it is passing in - Reads the {@link Buffer} or {@link BufferRange} for given varying name. */
2890
+ readAsync(varyingName) {
2891
+ const result = this.getBuffer(varyingName);
2892
+ if (!result) {
2893
+ throw new Error("BufferTransform#getBuffer");
2894
+ }
2895
+ if (result instanceof import_core10.Buffer) {
2896
+ return result.readAsync();
2897
+ }
2898
+ const { buffer, byteOffset = 0, byteLength = buffer.byteLength } = result;
2899
+ return buffer.readAsync(byteOffset, byteLength);
2900
+ }
2901
+ };
2902
+ var BufferTransform = _BufferTransform;
2903
+ __publicField(BufferTransform, "defaultProps", {
2904
+ ...Model.defaultProps,
2905
+ outputs: void 0,
2906
+ feedbackBuffers: void 0
2907
+ });
2908
+
2909
+ // dist/compute/texture-transform.js
2910
+ var import_shadertools4 = require("@luma.gl/shadertools");
2911
+ var FS_OUTPUT_VARIABLE = "transform_output";
2912
+ var TextureTransform = class {
2913
+ device;
2914
+ model;
2915
+ sampler;
2916
+ currentIndex = 0;
2917
+ samplerTextureMap = null;
2918
+ bindings = [];
2724
2919
  // each element is an object : {sourceTextures, targetTexture, framebuffer}
2725
2920
  resources = {};
2726
2921
  // resources to be deleted
@@ -3096,45 +3291,676 @@ var BackgroundTextureModel = class extends ClipSpace {
3096
3291
  }
3097
3292
  };
3098
3293
 
3099
- // dist/scenegraph/scenegraph-node.js
3100
- var import_core12 = require("@math.gl/core");
3101
- function assert(condition, message) {
3102
- if (!condition) {
3103
- throw new Error(message);
3104
- }
3105
- }
3106
- var ScenegraphNode = class {
3107
- id;
3108
- matrix = new import_core12.Matrix4();
3109
- display = true;
3110
- position = new import_core12.Vector3();
3111
- rotation = new import_core12.Vector3();
3112
- scale = new import_core12.Vector3(1, 1, 1);
3113
- userData = {};
3114
- props = {};
3294
+ // dist/geometries/sphere-geometry.js
3295
+ var SphereGeometry = class extends Geometry {
3115
3296
  constructor(props = {}) {
3116
- const { id } = props;
3117
- this.id = id || uid(this.constructor.name);
3118
- this._setScenegraphNodeProps(props);
3119
- }
3120
- getBounds() {
3121
- return null;
3122
- }
3123
- destroy() {
3124
- }
3125
- /** @deprecated use .destroy() */
3126
- delete() {
3127
- this.destroy();
3297
+ const { id = uid("sphere-geometry") } = props;
3298
+ const { indices, attributes } = tesselateSphere(props);
3299
+ super({
3300
+ ...props,
3301
+ id,
3302
+ topology: "triangle-list",
3303
+ indices,
3304
+ attributes: { ...attributes, ...props.attributes }
3305
+ });
3128
3306
  }
3129
- setProps(props) {
3130
- this._setScenegraphNodeProps(props);
3131
- return this;
3307
+ };
3308
+ function tesselateSphere(props) {
3309
+ const { nlat = 10, nlong = 10 } = props;
3310
+ const startLat = 0;
3311
+ const endLat = Math.PI;
3312
+ const latRange = endLat - startLat;
3313
+ const startLong = 0;
3314
+ const endLong = 2 * Math.PI;
3315
+ const longRange = endLong - startLong;
3316
+ const numVertices = (nlat + 1) * (nlong + 1);
3317
+ const radius = (n1, n2, n3, u, v) => props.radius || 1;
3318
+ const positions = new Float32Array(numVertices * 3);
3319
+ const normals = new Float32Array(numVertices * 3);
3320
+ const texCoords = new Float32Array(numVertices * 2);
3321
+ const IndexType = numVertices > 65535 ? Uint32Array : Uint16Array;
3322
+ const indices = new IndexType(nlat * nlong * 6);
3323
+ for (let y = 0; y <= nlat; y++) {
3324
+ for (let x = 0; x <= nlong; x++) {
3325
+ const u = x / nlong;
3326
+ const v = y / nlat;
3327
+ const index = x + y * (nlong + 1);
3328
+ const i2 = index * 2;
3329
+ const i3 = index * 3;
3330
+ const theta = longRange * u;
3331
+ const phi = latRange * v;
3332
+ const sinTheta = Math.sin(theta);
3333
+ const cosTheta = Math.cos(theta);
3334
+ const sinPhi = Math.sin(phi);
3335
+ const cosPhi = Math.cos(phi);
3336
+ const ux = cosTheta * sinPhi;
3337
+ const uy = cosPhi;
3338
+ const uz = sinTheta * sinPhi;
3339
+ const r = radius(ux, uy, uz, u, v);
3340
+ positions[i3 + 0] = r * ux;
3341
+ positions[i3 + 1] = r * uy;
3342
+ positions[i3 + 2] = r * uz;
3343
+ normals[i3 + 0] = ux;
3344
+ normals[i3 + 1] = uy;
3345
+ normals[i3 + 2] = uz;
3346
+ texCoords[i2 + 0] = u;
3347
+ texCoords[i2 + 1] = 1 - v;
3348
+ }
3132
3349
  }
3133
- toString() {
3134
- return `{type: ScenegraphNode, id: ${this.id})}`;
3350
+ const numVertsAround = nlong + 1;
3351
+ for (let x = 0; x < nlong; x++) {
3352
+ for (let y = 0; y < nlat; y++) {
3353
+ const index = (x * nlat + y) * 6;
3354
+ indices[index + 0] = y * numVertsAround + x;
3355
+ indices[index + 1] = y * numVertsAround + x + 1;
3356
+ indices[index + 2] = (y + 1) * numVertsAround + x;
3357
+ indices[index + 3] = (y + 1) * numVertsAround + x;
3358
+ indices[index + 4] = y * numVertsAround + x + 1;
3359
+ indices[index + 5] = (y + 1) * numVertsAround + x + 1;
3360
+ }
3135
3361
  }
3136
- setPosition(position) {
3137
- assert(position.length === 3, "setPosition requires vector argument");
3362
+ return {
3363
+ indices: { size: 1, value: indices },
3364
+ attributes: {
3365
+ POSITION: { size: 3, value: positions },
3366
+ NORMAL: { size: 3, value: normals },
3367
+ TEXCOORD_0: { size: 2, value: texCoords }
3368
+ }
3369
+ };
3370
+ }
3371
+
3372
+ // dist/models/light-model-utils.js
3373
+ var import_core11 = require("@math.gl/core");
3374
+ var DEFAULT_POINT_LIGHT_RADIUS_FACTOR = 0.02;
3375
+ var DEFAULT_SPOT_LIGHT_LENGTH_FACTOR = 0.12;
3376
+ var DEFAULT_DIRECTIONAL_LIGHT_LENGTH_FACTOR = 0.15;
3377
+ var DEFAULT_DIRECTIONAL_LIGHT_RADIUS_FACTOR = 0.2;
3378
+ var DEFAULT_DIRECTION_FALLBACK = [0, 1, 0];
3379
+ var DEFAULT_LIGHT_COLOR = [255, 255, 255];
3380
+ var DEFAULT_MARKER_SCALE = 1;
3381
+ var DIRECTIONAL_ANCHOR_DISTANCE_FACTOR = 0.35;
3382
+ var LIGHT_COLOR_FACTOR = 255;
3383
+ var MIN_SCENE_SCALE = 1;
3384
+ var SPOTLIGHT_OUTER_CONE_EPSILON = 0.01;
3385
+ var LIGHT_MARKER_PARAMETERS = {
3386
+ depthCompare: "less-equal",
3387
+ depthWriteEnabled: false,
3388
+ cullMode: "none"
3389
+ };
3390
+ var INSTANCE_BUFFER_LAYOUT = [
3391
+ { name: "instancePosition", format: "float32x3", stepMode: "instance" },
3392
+ { name: "instanceDirection", format: "float32x3", stepMode: "instance" },
3393
+ { name: "instanceScale", format: "float32x3", stepMode: "instance" },
3394
+ { name: "instanceColor", format: "float32x4", stepMode: "instance" }
3395
+ ];
3396
+ var lightMarker = {
3397
+ name: "lightMarker",
3398
+ props: {},
3399
+ uniforms: {},
3400
+ uniformTypes: {
3401
+ viewProjectionMatrix: "mat4x4<f32>"
3402
+ }
3403
+ };
3404
+ var CENTERED_LOCAL_POSITION_WGSL = "inputs.positions * inputs.instanceScale";
3405
+ 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)";
3406
+ var CENTERED_LOCAL_POSITION_GLSL = "positions * instanceScale";
3407
+ var APEX_LOCAL_POSITION_GLSL = "vec3(positions.x * instanceScale.x, (positions.y - 0.5) * instanceScale.y, positions.z * instanceScale.z)";
3408
+ var BaseLightModel = class extends Model {
3409
+ lightModelProps;
3410
+ _instanceData;
3411
+ _managedBuffers;
3412
+ buildInstanceData;
3413
+ sizePropNames;
3414
+ constructor(device, props, options) {
3415
+ const instanceData = options.buildInstanceData(props);
3416
+ const managedBuffers = createManagedInstanceBuffers(device, props.id || options.idPrefix, instanceData);
3417
+ const shaderInputs = new ShaderInputs({ lightMarker });
3418
+ shaderInputs.setProps({
3419
+ lightMarker: { viewProjectionMatrix: createViewProjectionMatrix(props) }
3420
+ });
3421
+ const { source: source3, vs: vs3, fs: fs3 } = getLightMarkerShaders(options.anchorMode);
3422
+ const modelProps = props;
3423
+ super(device, {
3424
+ ...modelProps,
3425
+ id: props.id || options.idPrefix,
3426
+ source: source3,
3427
+ vs: vs3,
3428
+ fs: fs3,
3429
+ geometry: options.geometry,
3430
+ shaderInputs,
3431
+ bufferLayout: [...INSTANCE_BUFFER_LAYOUT],
3432
+ attributes: managedBuffers,
3433
+ instanceCount: instanceData.instanceCount,
3434
+ parameters: mergeLightMarkerParameters(props.parameters)
3435
+ });
3436
+ this.lightModelProps = props;
3437
+ this._instanceData = instanceData;
3438
+ this._managedBuffers = managedBuffers;
3439
+ this.buildInstanceData = options.buildInstanceData;
3440
+ this.sizePropNames = options.sizePropNames;
3441
+ }
3442
+ destroy() {
3443
+ super.destroy();
3444
+ destroyManagedInstanceBuffers(this._managedBuffers);
3445
+ this._managedBuffers = {};
3446
+ }
3447
+ draw(renderPass) {
3448
+ if (this.instanceCount === 0) {
3449
+ return true;
3450
+ }
3451
+ return super.draw(renderPass);
3452
+ }
3453
+ setProps(props) {
3454
+ this.lightModelProps = { ...this.lightModelProps, ...props };
3455
+ if (props.parameters) {
3456
+ this.setParameters(mergeLightMarkerParameters(this.lightModelProps.parameters));
3457
+ }
3458
+ if ("viewMatrix" in props || "projectionMatrix" in props) {
3459
+ this.shaderInputs.setProps({
3460
+ lightMarker: { viewProjectionMatrix: createViewProjectionMatrix(this.lightModelProps) }
3461
+ });
3462
+ this.setNeedsRedraw("lightMarker camera");
3463
+ }
3464
+ if (shouldRebuildInstanceData(props, this.sizePropNames)) {
3465
+ this.rebuildInstanceData();
3466
+ }
3467
+ }
3468
+ rebuildInstanceData() {
3469
+ const nextInstanceData = this.buildInstanceData(this.lightModelProps);
3470
+ const nextManagedBuffers = createManagedInstanceBuffers(this.device, this.id, nextInstanceData);
3471
+ this.setAttributes(nextManagedBuffers);
3472
+ this.setInstanceCount(nextInstanceData.instanceCount);
3473
+ destroyManagedInstanceBuffers(this._managedBuffers);
3474
+ this._managedBuffers = nextManagedBuffers;
3475
+ this._instanceData = nextInstanceData;
3476
+ }
3477
+ };
3478
+ function buildPointLightInstanceData(props) {
3479
+ const pointLights = getPointLights(props.lights);
3480
+ const context = getLightMarkerContext(props);
3481
+ const pointLightRadius = props.pointLightRadius ?? DEFAULT_POINT_LIGHT_RADIUS_FACTOR * context.sceneScale * context.markerScale;
3482
+ return createLightMarkerInstanceData(pointLights.length, (light, _index) => ({
3483
+ color: getDisplayColor(light),
3484
+ direction: DEFAULT_DIRECTION_FALLBACK,
3485
+ position: light.position,
3486
+ scale: [pointLightRadius, pointLightRadius, pointLightRadius]
3487
+ }), pointLights);
3488
+ }
3489
+ function buildSpotLightInstanceData(props) {
3490
+ const spotLights = getSpotLights(props.lights);
3491
+ const context = getLightMarkerContext(props);
3492
+ const spotLightLength = props.spotLightLength ?? DEFAULT_SPOT_LIGHT_LENGTH_FACTOR * context.sceneScale * context.markerScale;
3493
+ return createLightMarkerInstanceData(spotLights.length, (light, _index) => {
3494
+ const outerConeAngle = clamp(light.outerConeAngle ?? Math.PI / 4, 0, Math.PI / 2 - SPOTLIGHT_OUTER_CONE_EPSILON);
3495
+ const radius = Math.tan(outerConeAngle) * spotLightLength;
3496
+ return {
3497
+ color: getDisplayColor(light),
3498
+ direction: normalizeDirection(light.direction),
3499
+ position: light.position,
3500
+ scale: [radius, spotLightLength, radius]
3501
+ };
3502
+ }, spotLights);
3503
+ }
3504
+ function buildDirectionalLightInstanceData(props) {
3505
+ const directionalLights = getDirectionalLights(props.lights);
3506
+ const context = getLightMarkerContext(props);
3507
+ const directionalLightLength = props.directionalLightLength ?? DEFAULT_DIRECTIONAL_LIGHT_LENGTH_FACTOR * context.sceneScale * context.markerScale;
3508
+ const directionalLightRadius = directionalLightLength * DEFAULT_DIRECTIONAL_LIGHT_RADIUS_FACTOR;
3509
+ return createLightMarkerInstanceData(directionalLights.length, (light, _index) => {
3510
+ const direction = normalizeDirection(light.direction);
3511
+ const position = [
3512
+ context.sceneCenter[0] - direction[0] * context.sceneScale * DIRECTIONAL_ANCHOR_DISTANCE_FACTOR,
3513
+ context.sceneCenter[1] - direction[1] * context.sceneScale * DIRECTIONAL_ANCHOR_DISTANCE_FACTOR,
3514
+ context.sceneCenter[2] - direction[2] * context.sceneScale * DIRECTIONAL_ANCHOR_DISTANCE_FACTOR
3515
+ ];
3516
+ return {
3517
+ color: getDisplayColor(light),
3518
+ direction,
3519
+ position,
3520
+ scale: [directionalLightRadius, directionalLightLength, directionalLightRadius]
3521
+ };
3522
+ }, directionalLights);
3523
+ }
3524
+ function getPointLights(lights) {
3525
+ return lights.filter((light) => light.type === "point");
3526
+ }
3527
+ function getSpotLights(lights) {
3528
+ return lights.filter((light) => light.type === "spot");
3529
+ }
3530
+ function getDirectionalLights(lights) {
3531
+ return lights.filter((light) => light.type === "directional");
3532
+ }
3533
+ function getLightMarkerContext(props) {
3534
+ const bounds = getSceneBounds(props.lights, props.bounds);
3535
+ const sceneCenter = [
3536
+ (bounds[0][0] + bounds[1][0]) / 2,
3537
+ (bounds[0][1] + bounds[1][1]) / 2,
3538
+ (bounds[0][2] + bounds[1][2]) / 2
3539
+ ];
3540
+ const sceneScale = Math.max(Math.hypot(bounds[1][0] - bounds[0][0], bounds[1][1] - bounds[0][1], bounds[1][2] - bounds[0][2]), MIN_SCENE_SCALE);
3541
+ return {
3542
+ bounds,
3543
+ markerScale: Math.max(props.markerScale ?? DEFAULT_MARKER_SCALE, 0),
3544
+ sceneCenter,
3545
+ sceneScale
3546
+ };
3547
+ }
3548
+ function getDisplayColor(light) {
3549
+ const color = light.color || DEFAULT_LIGHT_COLOR;
3550
+ const intensity = Math.max(light.intensity ?? 1, 0);
3551
+ const brightness = clamp(0.35 + 0.3 * Math.log10(intensity + 1), 0.35, 1);
3552
+ return [
3553
+ clamp(color[0] / LIGHT_COLOR_FACTOR, 0, 1) * brightness,
3554
+ clamp(color[1] / LIGHT_COLOR_FACTOR, 0, 1) * brightness,
3555
+ clamp(color[2] / LIGHT_COLOR_FACTOR, 0, 1) * brightness,
3556
+ 1
3557
+ ];
3558
+ }
3559
+ function normalizeDirection(direction) {
3560
+ const [x, y, z] = direction || DEFAULT_DIRECTION_FALLBACK;
3561
+ const length = Math.hypot(x, y, z);
3562
+ if (length === 0) {
3563
+ return [...DEFAULT_DIRECTION_FALLBACK];
3564
+ }
3565
+ return [x / length, y / length, z / length];
3566
+ }
3567
+ function createLightMarkerInstanceData(instanceCount, getInstance, lights = []) {
3568
+ const instancePositions = new Float32Array(instanceCount * 3);
3569
+ const instanceDirections = new Float32Array(instanceCount * 3);
3570
+ const instanceScales = new Float32Array(instanceCount * 3);
3571
+ const instanceColors = new Float32Array(instanceCount * 4);
3572
+ for (const [index, light] of lights.entries()) {
3573
+ const instance = getInstance(light, index);
3574
+ instancePositions.set(instance.position, index * 3);
3575
+ instanceDirections.set(instance.direction, index * 3);
3576
+ instanceScales.set(instance.scale, index * 3);
3577
+ instanceColors.set(instance.color, index * 4);
3578
+ }
3579
+ return {
3580
+ instanceCount,
3581
+ instancePositions,
3582
+ instanceDirections,
3583
+ instanceScales,
3584
+ instanceColors
3585
+ };
3586
+ }
3587
+ function getSceneBounds(lights, bounds) {
3588
+ if (bounds) {
3589
+ return cloneBounds(bounds);
3590
+ }
3591
+ const positions = [
3592
+ ...getPointLights(lights).map((light) => light.position),
3593
+ ...getSpotLights(lights).map((light) => light.position)
3594
+ ];
3595
+ if (positions.length === 0) {
3596
+ return [
3597
+ [-0.5, -0.5, -0.5],
3598
+ [0.5, 0.5, 0.5]
3599
+ ];
3600
+ }
3601
+ const minBounds = [...positions[0]];
3602
+ const maxBounds = [...positions[0]];
3603
+ for (const position of positions.slice(1)) {
3604
+ minBounds[0] = Math.min(minBounds[0], position[0]);
3605
+ minBounds[1] = Math.min(minBounds[1], position[1]);
3606
+ minBounds[2] = Math.min(minBounds[2], position[2]);
3607
+ maxBounds[0] = Math.max(maxBounds[0], position[0]);
3608
+ maxBounds[1] = Math.max(maxBounds[1], position[1]);
3609
+ maxBounds[2] = Math.max(maxBounds[2], position[2]);
3610
+ }
3611
+ return [minBounds, maxBounds];
3612
+ }
3613
+ function cloneBounds(bounds) {
3614
+ return [[...bounds[0]], [...bounds[1]]];
3615
+ }
3616
+ function createManagedInstanceBuffers(device, idPrefix, instanceData) {
3617
+ return {
3618
+ instancePosition: device.createBuffer({
3619
+ id: `${idPrefix}-instance-position`,
3620
+ data: getBufferDataOrPlaceholder(instanceData.instancePositions, 3)
3621
+ }),
3622
+ instanceDirection: device.createBuffer({
3623
+ id: `${idPrefix}-instance-direction`,
3624
+ data: getBufferDataOrPlaceholder(instanceData.instanceDirections, 3)
3625
+ }),
3626
+ instanceScale: device.createBuffer({
3627
+ id: `${idPrefix}-instance-scale`,
3628
+ data: getBufferDataOrPlaceholder(instanceData.instanceScales, 3)
3629
+ }),
3630
+ instanceColor: device.createBuffer({
3631
+ id: `${idPrefix}-instance-color`,
3632
+ data: getBufferDataOrPlaceholder(instanceData.instanceColors, 4)
3633
+ })
3634
+ };
3635
+ }
3636
+ function getBufferDataOrPlaceholder(data, size) {
3637
+ return data.length > 0 ? data : new Float32Array(size);
3638
+ }
3639
+ function destroyManagedInstanceBuffers(managedBuffers) {
3640
+ for (const buffer of Object.values(managedBuffers)) {
3641
+ buffer == null ? void 0 : buffer.destroy();
3642
+ }
3643
+ }
3644
+ function createViewProjectionMatrix(props) {
3645
+ return new import_core11.Matrix4(props.projectionMatrix).multiplyRight(props.viewMatrix);
3646
+ }
3647
+ function shouldRebuildInstanceData(props, sizePropNames) {
3648
+ if ("lights" in props || "bounds" in props || "markerScale" in props) {
3649
+ return true;
3650
+ }
3651
+ return sizePropNames.some((sizePropName) => sizePropName in props);
3652
+ }
3653
+ function mergeLightMarkerParameters(parameters) {
3654
+ return {
3655
+ ...LIGHT_MARKER_PARAMETERS,
3656
+ ...parameters || {}
3657
+ };
3658
+ }
3659
+ function getLightMarkerShaders(anchorMode) {
3660
+ const localPositionWGSL = anchorMode === "apex" ? APEX_LOCAL_POSITION_WGSL : CENTERED_LOCAL_POSITION_WGSL;
3661
+ const localPositionGLSL = anchorMode === "apex" ? APEX_LOCAL_POSITION_GLSL : CENTERED_LOCAL_POSITION_GLSL;
3662
+ return {
3663
+ source: `struct lightMarkerUniforms {
3664
+ viewProjectionMatrix: mat4x4<f32>,
3665
+ };
3666
+
3667
+ @binding(0) @group(0) var<uniform> lightMarker : lightMarkerUniforms;
3668
+
3669
+ struct VertexInputs {
3670
+ @location(0) positions : vec3<f32>,
3671
+ @location(1) instancePosition : vec3<f32>,
3672
+ @location(2) instanceDirection : vec3<f32>,
3673
+ @location(3) instanceScale : vec3<f32>,
3674
+ @location(4) instanceColor : vec4<f32>,
3675
+ };
3676
+
3677
+ struct FragmentInputs {
3678
+ @builtin(position) Position : vec4<f32>,
3679
+ @location(0) color : vec4<f32>,
3680
+ };
3681
+
3682
+ fn lightMarker_rotate(localPosition: vec3<f32>, direction: vec3<f32>) -> vec3<f32> {
3683
+ let forward = normalize(direction);
3684
+ var helperAxis = vec3<f32>(0.0, 1.0, 0.0);
3685
+ if (abs(forward.y) > 0.999) {
3686
+ helperAxis = vec3<f32>(1.0, 0.0, 0.0);
3687
+ }
3688
+
3689
+ let tangent = normalize(cross(helperAxis, forward));
3690
+ let bitangent = cross(forward, tangent);
3691
+ return tangent * localPosition.x + forward * localPosition.y + bitangent * localPosition.z;
3692
+ }
3693
+
3694
+ @vertex
3695
+ fn vertexMain(inputs: VertexInputs) -> FragmentInputs {
3696
+ var outputs : FragmentInputs;
3697
+ let localPosition = ${localPositionWGSL};
3698
+ let worldPosition = inputs.instancePosition + lightMarker_rotate(localPosition, inputs.instanceDirection);
3699
+ outputs.Position = lightMarker.viewProjectionMatrix * vec4<f32>(worldPosition, 1.0);
3700
+ outputs.color = inputs.instanceColor;
3701
+ return outputs;
3702
+ }
3703
+
3704
+ @fragment
3705
+ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4<f32> {
3706
+ return inputs.color;
3707
+ }
3708
+ `,
3709
+ vs: `#version 300 es
3710
+
3711
+ in vec3 positions;
3712
+ in vec3 instancePosition;
3713
+ in vec3 instanceDirection;
3714
+ in vec3 instanceScale;
3715
+ in vec4 instanceColor;
3716
+
3717
+ uniform lightMarkerUniforms {
3718
+ mat4 viewProjectionMatrix;
3719
+ } lightMarker;
3720
+
3721
+ out vec4 vColor;
3722
+
3723
+ vec3 lightMarker_rotate(vec3 localPosition, vec3 direction) {
3724
+ vec3 forward = normalize(direction);
3725
+ vec3 helperAxis = abs(forward.y) > 0.999 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);
3726
+ vec3 tangent = normalize(cross(helperAxis, forward));
3727
+ vec3 bitangent = cross(forward, tangent);
3728
+ return tangent * localPosition.x + forward * localPosition.y + bitangent * localPosition.z;
3729
+ }
3730
+
3731
+ void main(void) {
3732
+ vec3 localPosition = ${localPositionGLSL};
3733
+ vec3 worldPosition = instancePosition + lightMarker_rotate(localPosition, instanceDirection);
3734
+ gl_Position = lightMarker.viewProjectionMatrix * vec4(worldPosition, 1.0);
3735
+ vColor = instanceColor;
3736
+ }
3737
+ `,
3738
+ fs: `#version 300 es
3739
+ precision highp float;
3740
+
3741
+ in vec4 vColor;
3742
+ out vec4 fragColor;
3743
+
3744
+ void main(void) {
3745
+ fragColor = vColor;
3746
+ }
3747
+ `
3748
+ };
3749
+ }
3750
+ function clamp(value, minValue, maxValue) {
3751
+ return Math.min(maxValue, Math.max(minValue, value));
3752
+ }
3753
+
3754
+ // dist/models/point-light-model.js
3755
+ var POINT_LIGHT_GEOMETRY = new SphereGeometry({
3756
+ nlat: 8,
3757
+ nlong: 12,
3758
+ radius: 1
3759
+ });
3760
+ var PointLightModel = class extends BaseLightModel {
3761
+ constructor(device, props) {
3762
+ super(device, props, {
3763
+ anchorMode: "centered",
3764
+ buildInstanceData: buildPointLightInstanceData,
3765
+ geometry: POINT_LIGHT_GEOMETRY,
3766
+ idPrefix: "point-light-model",
3767
+ sizePropNames: ["pointLightRadius"]
3768
+ });
3769
+ }
3770
+ };
3771
+
3772
+ // dist/geometries/truncated-cone-geometry.js
3773
+ var INDEX_OFFSETS = {
3774
+ x: [2, 0, 1],
3775
+ y: [0, 1, 2],
3776
+ z: [1, 2, 0]
3777
+ };
3778
+ var TruncatedConeGeometry = class extends Geometry {
3779
+ constructor(props = {}) {
3780
+ const { id = uid("truncated-code-geometry") } = props;
3781
+ const { indices, attributes } = tesselateTruncatedCone(props);
3782
+ super({
3783
+ ...props,
3784
+ id,
3785
+ topology: "triangle-list",
3786
+ indices,
3787
+ attributes: {
3788
+ POSITION: { size: 3, value: attributes.POSITION },
3789
+ NORMAL: { size: 3, value: attributes.NORMAL },
3790
+ TEXCOORD_0: { size: 2, value: attributes.TEXCOORD_0 },
3791
+ ...props.attributes
3792
+ }
3793
+ });
3794
+ }
3795
+ };
3796
+ function tesselateTruncatedCone(props = {}) {
3797
+ const { bottomRadius = 0, topRadius = 0, height = 1, nradial = 10, nvertical = 10, verticalAxis = "y", topCap = false, bottomCap = false } = props;
3798
+ const extra = (topCap ? 2 : 0) + (bottomCap ? 2 : 0);
3799
+ const numVertices = (nradial + 1) * (nvertical + 1 + extra);
3800
+ const slant = Math.atan2(bottomRadius - topRadius, height);
3801
+ const msin = Math.sin;
3802
+ const mcos = Math.cos;
3803
+ const mpi = Math.PI;
3804
+ const cosSlant = mcos(slant);
3805
+ const sinSlant = msin(slant);
3806
+ const start = topCap ? -2 : 0;
3807
+ const end = nvertical + (bottomCap ? 2 : 0);
3808
+ const vertsAroundEdge = nradial + 1;
3809
+ const indices = new Uint16Array(nradial * (nvertical + extra) * 6);
3810
+ const indexOffset = INDEX_OFFSETS[verticalAxis];
3811
+ const positions = new Float32Array(numVertices * 3);
3812
+ const normals = new Float32Array(numVertices * 3);
3813
+ const texCoords = new Float32Array(numVertices * 2);
3814
+ let i3 = 0;
3815
+ let i2 = 0;
3816
+ for (let i = start; i <= end; i++) {
3817
+ let v = i / nvertical;
3818
+ let y = height * v;
3819
+ let ringRadius;
3820
+ if (i < 0) {
3821
+ y = 0;
3822
+ v = 1;
3823
+ ringRadius = bottomRadius;
3824
+ } else if (i > nvertical) {
3825
+ y = height;
3826
+ v = 1;
3827
+ ringRadius = topRadius;
3828
+ } else {
3829
+ ringRadius = bottomRadius + (topRadius - bottomRadius) * (i / nvertical);
3830
+ }
3831
+ if (i === -2 || i === nvertical + 2) {
3832
+ ringRadius = 0;
3833
+ v = 0;
3834
+ }
3835
+ y -= height / 2;
3836
+ for (let j = 0; j < vertsAroundEdge; j++) {
3837
+ const sin = msin(j * mpi * 2 / nradial);
3838
+ const cos = mcos(j * mpi * 2 / nradial);
3839
+ positions[i3 + indexOffset[0]] = sin * ringRadius;
3840
+ positions[i3 + indexOffset[1]] = y;
3841
+ positions[i3 + indexOffset[2]] = cos * ringRadius;
3842
+ normals[i3 + indexOffset[0]] = i < 0 || i > nvertical ? 0 : sin * cosSlant;
3843
+ normals[i3 + indexOffset[1]] = i < 0 ? -1 : i > nvertical ? 1 : sinSlant;
3844
+ normals[i3 + indexOffset[2]] = i < 0 || i > nvertical ? 0 : cos * cosSlant;
3845
+ texCoords[i2 + 0] = j / nradial;
3846
+ texCoords[i2 + 1] = v;
3847
+ i2 += 2;
3848
+ i3 += 3;
3849
+ }
3850
+ }
3851
+ for (let i = 0; i < nvertical + extra; i++) {
3852
+ for (let j = 0; j < nradial; j++) {
3853
+ const index = (i * nradial + j) * 6;
3854
+ indices[index + 0] = vertsAroundEdge * (i + 0) + 0 + j;
3855
+ indices[index + 1] = vertsAroundEdge * (i + 0) + 1 + j;
3856
+ indices[index + 2] = vertsAroundEdge * (i + 1) + 1 + j;
3857
+ indices[index + 3] = vertsAroundEdge * (i + 0) + 0 + j;
3858
+ indices[index + 4] = vertsAroundEdge * (i + 1) + 1 + j;
3859
+ indices[index + 5] = vertsAroundEdge * (i + 1) + 0 + j;
3860
+ }
3861
+ }
3862
+ return {
3863
+ indices,
3864
+ attributes: {
3865
+ POSITION: positions,
3866
+ NORMAL: normals,
3867
+ TEXCOORD_0: texCoords
3868
+ }
3869
+ };
3870
+ }
3871
+
3872
+ // dist/geometries/cone-geometry.js
3873
+ var ConeGeometry = class extends TruncatedConeGeometry {
3874
+ constructor(props = {}) {
3875
+ const { id = uid("cone-geometry"), radius = 1, cap = true } = props;
3876
+ super({
3877
+ ...props,
3878
+ id,
3879
+ topRadius: 0,
3880
+ topCap: Boolean(cap),
3881
+ bottomCap: Boolean(cap),
3882
+ bottomRadius: radius
3883
+ });
3884
+ }
3885
+ };
3886
+
3887
+ // dist/models/spot-light-model.js
3888
+ var SPOT_LIGHT_GEOMETRY = new ConeGeometry({
3889
+ cap: true,
3890
+ nradial: 16,
3891
+ nvertical: 1,
3892
+ radius: 1
3893
+ });
3894
+ var SpotLightModel = class extends BaseLightModel {
3895
+ constructor(device, props) {
3896
+ super(device, props, {
3897
+ anchorMode: "apex",
3898
+ buildInstanceData: buildSpotLightInstanceData,
3899
+ geometry: SPOT_LIGHT_GEOMETRY,
3900
+ idPrefix: "spot-light-model",
3901
+ sizePropNames: ["spotLightLength"]
3902
+ });
3903
+ }
3904
+ };
3905
+
3906
+ // dist/models/directional-light-model.js
3907
+ var DIRECTIONAL_LIGHT_GEOMETRY = new ConeGeometry({
3908
+ cap: true,
3909
+ nradial: 12,
3910
+ nvertical: 1,
3911
+ radius: 1
3912
+ });
3913
+ var DirectionalLightModel = class extends BaseLightModel {
3914
+ constructor(device, props) {
3915
+ super(device, props, {
3916
+ anchorMode: "apex",
3917
+ buildInstanceData: buildDirectionalLightInstanceData,
3918
+ geometry: DIRECTIONAL_LIGHT_GEOMETRY,
3919
+ idPrefix: "directional-light-model",
3920
+ sizePropNames: ["directionalLightLength"]
3921
+ });
3922
+ }
3923
+ };
3924
+
3925
+ // dist/scenegraph/scenegraph-node.js
3926
+ var import_core12 = require("@math.gl/core");
3927
+ function assert(condition, message) {
3928
+ if (!condition) {
3929
+ throw new Error(message);
3930
+ }
3931
+ }
3932
+ var ScenegraphNode = class {
3933
+ id;
3934
+ matrix = new import_core12.Matrix4();
3935
+ display = true;
3936
+ position = new import_core12.Vector3();
3937
+ rotation = new import_core12.Vector3();
3938
+ scale = new import_core12.Vector3(1, 1, 1);
3939
+ userData = {};
3940
+ props = {};
3941
+ constructor(props = {}) {
3942
+ const { id } = props;
3943
+ this.id = id || uid(this.constructor.name);
3944
+ this._setScenegraphNodeProps(props);
3945
+ }
3946
+ getBounds() {
3947
+ return null;
3948
+ }
3949
+ destroy() {
3950
+ }
3951
+ /** @deprecated use .destroy() */
3952
+ delete() {
3953
+ this.destroy();
3954
+ }
3955
+ setProps(props) {
3956
+ this._setScenegraphNodeProps(props);
3957
+ return this;
3958
+ }
3959
+ toString() {
3960
+ return `{type: ScenegraphNode, id: ${this.id})}`;
3961
+ }
3962
+ setPosition(position) {
3963
+ assert(position.length === 3, "setPosition requires vector argument");
3138
3964
  this.position = position;
3139
3965
  return this;
3140
3966
  }
@@ -3374,121 +4200,6 @@ var ModelNode = class extends ScenegraphNode {
3374
4200
  }
3375
4201
  };
3376
4202
 
3377
- // dist/geometries/truncated-cone-geometry.js
3378
- var INDEX_OFFSETS = {
3379
- x: [2, 0, 1],
3380
- y: [0, 1, 2],
3381
- z: [1, 2, 0]
3382
- };
3383
- var TruncatedConeGeometry = class extends Geometry {
3384
- constructor(props = {}) {
3385
- const { id = uid("truncated-code-geometry") } = props;
3386
- const { indices, attributes } = tesselateTruncatedCone(props);
3387
- super({
3388
- ...props,
3389
- id,
3390
- topology: "triangle-list",
3391
- indices,
3392
- attributes: {
3393
- POSITION: { size: 3, value: attributes.POSITION },
3394
- NORMAL: { size: 3, value: attributes.NORMAL },
3395
- TEXCOORD_0: { size: 2, value: attributes.TEXCOORD_0 },
3396
- ...props.attributes
3397
- }
3398
- });
3399
- }
3400
- };
3401
- function tesselateTruncatedCone(props = {}) {
3402
- const { bottomRadius = 0, topRadius = 0, height = 1, nradial = 10, nvertical = 10, verticalAxis = "y", topCap = false, bottomCap = false } = props;
3403
- const extra = (topCap ? 2 : 0) + (bottomCap ? 2 : 0);
3404
- const numVertices = (nradial + 1) * (nvertical + 1 + extra);
3405
- const slant = Math.atan2(bottomRadius - topRadius, height);
3406
- const msin = Math.sin;
3407
- const mcos = Math.cos;
3408
- const mpi = Math.PI;
3409
- const cosSlant = mcos(slant);
3410
- const sinSlant = msin(slant);
3411
- const start = topCap ? -2 : 0;
3412
- const end = nvertical + (bottomCap ? 2 : 0);
3413
- const vertsAroundEdge = nradial + 1;
3414
- const indices = new Uint16Array(nradial * (nvertical + extra) * 6);
3415
- const indexOffset = INDEX_OFFSETS[verticalAxis];
3416
- const positions = new Float32Array(numVertices * 3);
3417
- const normals = new Float32Array(numVertices * 3);
3418
- const texCoords = new Float32Array(numVertices * 2);
3419
- let i3 = 0;
3420
- let i2 = 0;
3421
- for (let i = start; i <= end; i++) {
3422
- let v = i / nvertical;
3423
- let y = height * v;
3424
- let ringRadius;
3425
- if (i < 0) {
3426
- y = 0;
3427
- v = 1;
3428
- ringRadius = bottomRadius;
3429
- } else if (i > nvertical) {
3430
- y = height;
3431
- v = 1;
3432
- ringRadius = topRadius;
3433
- } else {
3434
- ringRadius = bottomRadius + (topRadius - bottomRadius) * (i / nvertical);
3435
- }
3436
- if (i === -2 || i === nvertical + 2) {
3437
- ringRadius = 0;
3438
- v = 0;
3439
- }
3440
- y -= height / 2;
3441
- for (let j = 0; j < vertsAroundEdge; j++) {
3442
- const sin = msin(j * mpi * 2 / nradial);
3443
- const cos = mcos(j * mpi * 2 / nradial);
3444
- positions[i3 + indexOffset[0]] = sin * ringRadius;
3445
- positions[i3 + indexOffset[1]] = y;
3446
- positions[i3 + indexOffset[2]] = cos * ringRadius;
3447
- normals[i3 + indexOffset[0]] = i < 0 || i > nvertical ? 0 : sin * cosSlant;
3448
- normals[i3 + indexOffset[1]] = i < 0 ? -1 : i > nvertical ? 1 : sinSlant;
3449
- normals[i3 + indexOffset[2]] = i < 0 || i > nvertical ? 0 : cos * cosSlant;
3450
- texCoords[i2 + 0] = j / nradial;
3451
- texCoords[i2 + 1] = v;
3452
- i2 += 2;
3453
- i3 += 3;
3454
- }
3455
- }
3456
- for (let i = 0; i < nvertical + extra; i++) {
3457
- for (let j = 0; j < nradial; j++) {
3458
- const index = (i * nradial + j) * 6;
3459
- indices[index + 0] = vertsAroundEdge * (i + 0) + 0 + j;
3460
- indices[index + 1] = vertsAroundEdge * (i + 0) + 1 + j;
3461
- indices[index + 2] = vertsAroundEdge * (i + 1) + 1 + j;
3462
- indices[index + 3] = vertsAroundEdge * (i + 0) + 0 + j;
3463
- indices[index + 4] = vertsAroundEdge * (i + 1) + 1 + j;
3464
- indices[index + 5] = vertsAroundEdge * (i + 1) + 0 + j;
3465
- }
3466
- }
3467
- return {
3468
- indices,
3469
- attributes: {
3470
- POSITION: positions,
3471
- NORMAL: normals,
3472
- TEXCOORD_0: texCoords
3473
- }
3474
- };
3475
- }
3476
-
3477
- // dist/geometries/cone-geometry.js
3478
- var ConeGeometry = class extends TruncatedConeGeometry {
3479
- constructor(props = {}) {
3480
- const { id = uid("cone-geometry"), radius = 1, cap = true } = props;
3481
- super({
3482
- ...props,
3483
- id,
3484
- topRadius: 0,
3485
- topCap: Boolean(cap),
3486
- bottomCap: Boolean(cap),
3487
- bottomRadius: radius
3488
- });
3489
- }
3490
- };
3491
-
3492
4203
  // dist/geometries/cube-geometry.js
3493
4204
  var CubeGeometry = class extends Geometry {
3494
4205
  constructor(props = {}) {
@@ -4378,84 +5089,6 @@ function tesselatePlane(props) {
4378
5089
  return unpack ? unpackIndexedGeometry(geometry) : geometry;
4379
5090
  }
4380
5091
 
4381
- // dist/geometries/sphere-geometry.js
4382
- var SphereGeometry = class extends Geometry {
4383
- constructor(props = {}) {
4384
- const { id = uid("sphere-geometry") } = props;
4385
- const { indices, attributes } = tesselateSphere(props);
4386
- super({
4387
- ...props,
4388
- id,
4389
- topology: "triangle-list",
4390
- indices,
4391
- attributes: { ...attributes, ...props.attributes }
4392
- });
4393
- }
4394
- };
4395
- function tesselateSphere(props) {
4396
- const { nlat = 10, nlong = 10 } = props;
4397
- const startLat = 0;
4398
- const endLat = Math.PI;
4399
- const latRange = endLat - startLat;
4400
- const startLong = 0;
4401
- const endLong = 2 * Math.PI;
4402
- const longRange = endLong - startLong;
4403
- const numVertices = (nlat + 1) * (nlong + 1);
4404
- const radius = (n1, n2, n3, u, v) => props.radius || 1;
4405
- const positions = new Float32Array(numVertices * 3);
4406
- const normals = new Float32Array(numVertices * 3);
4407
- const texCoords = new Float32Array(numVertices * 2);
4408
- const IndexType = numVertices > 65535 ? Uint32Array : Uint16Array;
4409
- const indices = new IndexType(nlat * nlong * 6);
4410
- for (let y = 0; y <= nlat; y++) {
4411
- for (let x = 0; x <= nlong; x++) {
4412
- const u = x / nlong;
4413
- const v = y / nlat;
4414
- const index = x + y * (nlong + 1);
4415
- const i2 = index * 2;
4416
- const i3 = index * 3;
4417
- const theta = longRange * u;
4418
- const phi = latRange * v;
4419
- const sinTheta = Math.sin(theta);
4420
- const cosTheta = Math.cos(theta);
4421
- const sinPhi = Math.sin(phi);
4422
- const cosPhi = Math.cos(phi);
4423
- const ux = cosTheta * sinPhi;
4424
- const uy = cosPhi;
4425
- const uz = sinTheta * sinPhi;
4426
- const r = radius(ux, uy, uz, u, v);
4427
- positions[i3 + 0] = r * ux;
4428
- positions[i3 + 1] = r * uy;
4429
- positions[i3 + 2] = r * uz;
4430
- normals[i3 + 0] = ux;
4431
- normals[i3 + 1] = uy;
4432
- normals[i3 + 2] = uz;
4433
- texCoords[i2 + 0] = u;
4434
- texCoords[i2 + 1] = 1 - v;
4435
- }
4436
- }
4437
- const numVertsAround = nlong + 1;
4438
- for (let x = 0; x < nlong; x++) {
4439
- for (let y = 0; y < nlat; y++) {
4440
- const index = (x * nlat + y) * 6;
4441
- indices[index + 0] = y * numVertsAround + x;
4442
- indices[index + 1] = y * numVertsAround + x + 1;
4443
- indices[index + 2] = (y + 1) * numVertsAround + x;
4444
- indices[index + 3] = (y + 1) * numVertsAround + x;
4445
- indices[index + 4] = y * numVertsAround + x + 1;
4446
- indices[index + 5] = (y + 1) * numVertsAround + x + 1;
4447
- }
4448
- }
4449
- return {
4450
- indices: { size: 1, value: indices },
4451
- attributes: {
4452
- POSITION: { size: 3, value: positions },
4453
- NORMAL: { size: 3, value: normals },
4454
- TEXCOORD_0: { size: 2, value: texCoords }
4455
- }
4456
- };
4457
- }
4458
-
4459
5092
  // dist/application-utils/random.js
4460
5093
  function makeRandomGenerator() {
4461
5094
  let s = 1;
@@ -4865,7 +5498,7 @@ var _Computation = class {
4865
5498
  props;
4866
5499
  _destroyed = false;
4867
5500
  constructor(device, props) {
4868
- var _a, _b, _c;
5501
+ var _a, _b, _c, _d;
4869
5502
  if (device.type !== "webgpu") {
4870
5503
  throw new Error("Computation is only supported in WebGPU");
4871
5504
  }
@@ -4877,11 +5510,11 @@ var _Computation = class {
4877
5510
  const moduleMap = Object.fromEntries(((_a = this.props.modules) == null ? void 0 : _a.map((module2) => [module2.name, module2])) || []);
4878
5511
  this.shaderInputs = props.shaderInputs || new ShaderInputs(moduleMap);
4879
5512
  this.setShaderInputs(this.shaderInputs);
4880
- this.props.shaderLayout ||= device.getShaderLayout(this.props.source);
4881
5513
  const platformInfo = getPlatformInfo2(device);
4882
5514
  const modules = (((_b = this.props.modules) == null ? void 0 : _b.length) > 0 ? this.props.modules : (_c = this.shaderInputs) == null ? void 0 : _c.getModules()) || [];
4883
- this.pipelineFactory = props.pipelineFactory || PipelineFactory.getDefaultPipelineFactory(this.device);
4884
- this.shaderFactory = props.shaderFactory || ShaderFactory.getDefaultShaderFactory(this.device);
5515
+ this.props.shaderLayout = mergeShaderModuleBindingsIntoLayout(this.props.shaderLayout, modules) || null;
5516
+ this.pipelineFactory = props.pipelineFactory || import_core17.PipelineFactory.getDefaultPipelineFactory(this.device);
5517
+ this.shaderFactory = props.shaderFactory || import_core17.ShaderFactory.getDefaultShaderFactory(this.device);
4885
5518
  const { source: source3, getUniforms: getUniforms2 } = this.props.shaderAssembler.assembleWGSLShader({
4886
5519
  platformInfo,
4887
5520
  ...this.props,
@@ -4889,6 +5522,8 @@ var _Computation = class {
4889
5522
  });
4890
5523
  this.source = source3;
4891
5524
  this._getModuleUniforms = getUniforms2;
5525
+ const inferredShaderLayout = (_d = device.getShaderLayout) == null ? void 0 : _d.call(device, this.source);
5526
+ this.props.shaderLayout = mergeShaderModuleBindingsIntoLayout(this.props.shaderLayout || inferredShaderLayout || null, modules) || null;
4892
5527
  this.pipeline = this._updatePipeline();
4893
5528
  if (props.bindings) {
4894
5529
  this.setBindings(props.bindings);
@@ -4913,7 +5548,7 @@ var _Computation = class {
4913
5548
  this.pipeline = this._updatePipeline();
4914
5549
  this.pipeline.setBindings(this.bindings);
4915
5550
  computePass.setPipeline(this.pipeline);
4916
- computePass.setBindings([]);
5551
+ computePass.setBindings({});
4917
5552
  computePass.dispatch(x, y, z);
4918
5553
  } finally {
4919
5554
  this._logDrawCallEnd();
@@ -4936,9 +5571,11 @@ var _Computation = class {
4936
5571
  setShaderInputs(shaderInputs) {
4937
5572
  this.shaderInputs = shaderInputs;
4938
5573
  this._uniformStore = new import_core17.UniformStore(this.shaderInputs.modules);
4939
- for (const moduleName of Object.keys(this.shaderInputs.modules)) {
4940
- const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
4941
- this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
5574
+ for (const [moduleName, module2] of Object.entries(this.shaderInputs.modules)) {
5575
+ if (shaderModuleHasUniforms(module2)) {
5576
+ const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
5577
+ this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
5578
+ }
4942
5579
  }
4943
5580
  }
4944
5581
  /**
@@ -5015,7 +5652,7 @@ var _Computation = class {
5015
5652
  _drawCount = 0;
5016
5653
  // TODO - fix typing of luma data types
5017
5654
  _getBufferOrConstantValues(attribute, dataType) {
5018
- const TypedArrayConstructor = (0, import_core17.getTypedArrayConstructor)(dataType);
5655
+ const TypedArrayConstructor = import_core17.dataTypeDecoder.getTypedArrayConstructor(dataType);
5019
5656
  const typedArray = attribute instanceof import_core17.Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
5020
5657
  return typedArray.toString();
5021
5658
  }
@@ -5047,6 +5684,9 @@ function getPlatformInfo2(device) {
5047
5684
  };
5048
5685
  }
5049
5686
 
5687
+ // dist/modules/picking/picking-manager.js
5688
+ var import_core18 = require("@luma.gl/core");
5689
+
5050
5690
  // dist/modules/picking/picking-uniforms.js
5051
5691
  var DEFAULT_HIGHLIGHT_COLOR = [0, 1, 1, 1];
5052
5692
  var INVALID_INDEX = -1;
@@ -5079,15 +5719,17 @@ uniform pickingUniforms {
5079
5719
  var WGSL_UNIFORMS = (
5080
5720
  /* wgsl */
5081
5721
  `struct pickingUniforms {
5082
- isActive: int32;
5083
- indexMode: int32;
5084
- batchIndex: int32;
5085
-
5086
- isHighlightActive: int32;
5087
- highlightedBatchIndex: int32;
5088
- highlightedObjectIndex: int32;
5089
- highlightColor: vec4<f32>;
5090
- } picking;
5722
+ isActive: i32,
5723
+ indexMode: i32,
5724
+ batchIndex: i32,
5725
+
5726
+ isHighlightActive: i32,
5727
+ highlightedBatchIndex: i32,
5728
+ highlightedObjectIndex: i32,
5729
+ highlightColor: vec4<f32>,
5730
+ };
5731
+
5732
+ @group(0) @binding(auto) var<uniform> picking: pickingUniforms;
5091
5733
  `
5092
5734
  );
5093
5735
  function getUniforms(props = {}, prevUniforms) {
@@ -5099,25 +5741,36 @@ function getUniforms(props = {}, prevUniforms) {
5099
5741
  case "instance":
5100
5742
  uniforms.indexMode = 0;
5101
5743
  break;
5102
- case "custom":
5744
+ case "attribute":
5103
5745
  uniforms.indexMode = 1;
5104
5746
  break;
5105
5747
  case void 0:
5106
5748
  break;
5107
5749
  }
5108
- switch (props.highlightedObjectIndex) {
5750
+ if (typeof props.batchIndex === "number") {
5751
+ uniforms.batchIndex = props.batchIndex;
5752
+ }
5753
+ switch (props.highlightedObjectIndex) {
5754
+ case void 0:
5755
+ break;
5756
+ case null:
5757
+ uniforms.isHighlightActive = false;
5758
+ uniforms.highlightedObjectIndex = INVALID_INDEX;
5759
+ break;
5760
+ default:
5761
+ uniforms.isHighlightActive = true;
5762
+ uniforms.highlightedObjectIndex = props.highlightedObjectIndex;
5763
+ }
5764
+ switch (props.highlightedBatchIndex) {
5109
5765
  case void 0:
5110
5766
  break;
5111
5767
  case null:
5112
5768
  uniforms.isHighlightActive = false;
5113
- uniforms.highlightedObjectIndex = INVALID_INDEX;
5769
+ uniforms.highlightedBatchIndex = INVALID_INDEX;
5114
5770
  break;
5115
5771
  default:
5116
5772
  uniforms.isHighlightActive = true;
5117
- uniforms.highlightedObjectIndex = props.highlightedObjectIndex;
5118
- }
5119
- if (typeof props.highlightedBatchIndex === "number") {
5120
- uniforms.highlightedBatchIndex = props.highlightedBatchIndex;
5773
+ uniforms.highlightedBatchIndex = props.highlightedBatchIndex;
5121
5774
  }
5122
5775
  if (props.highlightColor) {
5123
5776
  uniforms.highlightColor = props.highlightColor;
@@ -5133,7 +5786,7 @@ var pickingUniforms = {
5133
5786
  isActive: false,
5134
5787
  indexMode: 0,
5135
5788
  batchIndex: 0,
5136
- isHighlightActive: true,
5789
+ isHighlightActive: false,
5137
5790
  highlightedBatchIndex: INVALID_INDEX,
5138
5791
  highlightedObjectIndex: INVALID_INDEX,
5139
5792
  highlightColor: DEFAULT_HIGHLIGHT_COLOR
@@ -5142,9 +5795,42 @@ var pickingUniforms = {
5142
5795
  };
5143
5796
 
5144
5797
  // dist/modules/picking/picking-manager.js
5798
+ var INDEX_PICKING_ATTACHMENT_INDEX = 1;
5799
+ var INDEX_PICKING_CLEAR_COLOR = new Int32Array([INVALID_INDEX, INVALID_INDEX, 0, 0]);
5800
+ function resolvePickingMode(deviceType, mode = "color", indexPickingSupported = deviceType === "webgpu") {
5801
+ if (mode === "auto") {
5802
+ return indexPickingSupported ? "index" : "color";
5803
+ }
5804
+ if (mode === "index" && !indexPickingSupported) {
5805
+ throw new Error(`Picking mode "${mode}" requires WebGPU or a WebGL device that supports renderable rg32sint textures.`);
5806
+ }
5807
+ return mode;
5808
+ }
5809
+ function supportsIndexPicking(device) {
5810
+ return device.type === "webgpu" || device.type === "webgl" && device.isTextureFormatRenderable("rg32sint");
5811
+ }
5812
+ var resolvePickingBackend = resolvePickingMode;
5813
+ function decodeIndexPickInfo(pixelData) {
5814
+ return {
5815
+ objectIndex: pixelData[0] === INVALID_INDEX ? null : pixelData[0],
5816
+ batchIndex: pixelData[1] === INVALID_INDEX ? null : pixelData[1]
5817
+ };
5818
+ }
5819
+ function decodeColorPickInfo(pixelData) {
5820
+ const encodedObjectIndex = pixelData[0] + pixelData[1] * 256 + pixelData[2] * 65536;
5821
+ if (encodedObjectIndex === 0) {
5822
+ return { objectIndex: null, batchIndex: null };
5823
+ }
5824
+ const batchIndex = pixelData[3] > 0 ? pixelData[3] - 1 : 0;
5825
+ return {
5826
+ objectIndex: encodedObjectIndex - 1,
5827
+ batchIndex
5828
+ };
5829
+ }
5145
5830
  var _PickingManager = class {
5146
5831
  device;
5147
5832
  props;
5833
+ mode;
5148
5834
  /** Info from latest pick operation */
5149
5835
  pickInfo = { batchIndex: null, objectIndex: null };
5150
5836
  /** Framebuffer used for picking */
@@ -5152,6 +5838,10 @@ var _PickingManager = class {
5152
5838
  constructor(device, props) {
5153
5839
  this.device = device;
5154
5840
  this.props = { ..._PickingManager.defaultProps, ...props };
5841
+ const requestedMode = props.mode ?? props.backend ?? _PickingManager.defaultProps.mode;
5842
+ this.props.mode = requestedMode;
5843
+ this.props.backend = requestedMode;
5844
+ this.mode = resolvePickingMode(this.device.type, requestedMode, supportsIndexPicking(this.device));
5155
5845
  }
5156
5846
  destroy() {
5157
5847
  var _a;
@@ -5160,58 +5850,44 @@ var _PickingManager = class {
5160
5850
  // TODO - Ask for a cached framebuffer? a Framebuffer factory?
5161
5851
  getFramebuffer() {
5162
5852
  if (!this.framebuffer) {
5163
- this.framebuffer = this.device.createFramebuffer({
5164
- colorAttachments: ["rgba8unorm", "rg32sint"],
5165
- depthStencilAttachment: "depth24plus"
5166
- });
5853
+ this.framebuffer = this.mode === "index" ? this.createIndexFramebuffer() : this.createColorFramebuffer();
5167
5854
  }
5168
5855
  return this.framebuffer;
5169
5856
  }
5170
5857
  /** Clear highlighted / picked object */
5171
5858
  clearPickState() {
5172
- this.props.shaderInputs.setProps({ picking: { highlightedObjectIndex: null } });
5859
+ this.setPickingProps({ highlightedBatchIndex: null, highlightedObjectIndex: null });
5173
5860
  }
5174
5861
  /** Prepare for rendering picking colors */
5175
5862
  beginRenderPass() {
5176
- var _a;
5177
5863
  const framebuffer = this.getFramebuffer();
5178
5864
  framebuffer.resize(this.device.getDefaultCanvasContext().getDevicePixelSize());
5179
- (_a = this.props.shaderInputs) == null ? void 0 : _a.setProps({ picking: { isActive: true } });
5180
- const pickingPass = this.device.beginRenderPass({
5865
+ this.setPickingProps({ isActive: true });
5866
+ return this.mode === "index" ? this.device.beginRenderPass({
5181
5867
  framebuffer,
5182
- clearColors: [new Float32Array([0, 0, 0, 0]), new Int32Array([-1, -1, 0, 0])],
5868
+ clearColors: [new Float32Array([0, 0, 0, 0]), INDEX_PICKING_CLEAR_COLOR],
5869
+ clearDepth: 1
5870
+ }) : this.device.beginRenderPass({
5871
+ framebuffer,
5872
+ clearColor: [0, 0, 0, 0],
5183
5873
  clearDepth: 1
5184
5874
  });
5185
- return pickingPass;
5186
5875
  }
5187
5876
  async updatePickInfo(mousePosition) {
5188
- var _a;
5189
5877
  const framebuffer = this.getFramebuffer();
5190
- const [pickX, pickY] = this.getPickPosition(mousePosition);
5191
- const pixelData = this.device.readPixelsToArrayWebGL(framebuffer, {
5192
- sourceX: pickX,
5193
- sourceY: pickY,
5194
- sourceWidth: 1,
5195
- sourceHeight: 1,
5196
- sourceAttachment: 1
5197
- });
5198
- if (!pixelData) {
5878
+ const pickPosition = this.getPickPosition(mousePosition);
5879
+ const pickInfo = await this.readPickInfo(framebuffer, pickPosition);
5880
+ if (!pickInfo) {
5199
5881
  return null;
5200
5882
  }
5201
- const pickInfo = {
5202
- objectIndex: pixelData[0] === INVALID_INDEX ? null : pixelData[0],
5203
- batchIndex: pixelData[1] === INVALID_INDEX ? null : pixelData[1]
5204
- };
5205
- if (pickInfo.objectIndex !== this.pickInfo.objectIndex || pickInfo.batchIndex !== this.pickInfo.batchIndex) {
5883
+ if (this.hasPickInfoChanged(pickInfo)) {
5206
5884
  this.pickInfo = pickInfo;
5207
5885
  this.props.onObjectPicked(pickInfo);
5208
5886
  }
5209
- (_a = this.props.shaderInputs) == null ? void 0 : _a.setProps({
5210
- picking: {
5211
- isActive: false,
5212
- highlightedBatchIndex: pickInfo.batchIndex,
5213
- highlightedObjectIndex: pickInfo.objectIndex
5214
- }
5887
+ this.setPickingProps({
5888
+ isActive: false,
5889
+ highlightedBatchIndex: pickInfo.batchIndex,
5890
+ highlightedObjectIndex: pickInfo.objectIndex
5215
5891
  });
5216
5892
  return this.pickInfo;
5217
5893
  }
@@ -5220,43 +5896,204 @@ var _PickingManager = class {
5220
5896
  * use the center pixel location in device pixel range
5221
5897
  */
5222
5898
  getPickPosition(mousePosition) {
5223
- const devicePixels = this.device.getDefaultCanvasContext().cssToDevicePixels(mousePosition);
5899
+ const yInvert = this.device.type !== "webgpu";
5900
+ const devicePixels = this.device.getDefaultCanvasContext().cssToDevicePixels(mousePosition, yInvert);
5224
5901
  const pickX = devicePixels.x + Math.floor(devicePixels.width / 2);
5225
5902
  const pickY = devicePixels.y + Math.floor(devicePixels.height / 2);
5226
5903
  return [pickX, pickY];
5227
5904
  }
5905
+ createIndexFramebuffer() {
5906
+ const colorTexture = this.device.createTexture({
5907
+ format: "rgba8unorm",
5908
+ width: 1,
5909
+ height: 1,
5910
+ usage: import_core18.Texture.RENDER_ATTACHMENT
5911
+ });
5912
+ const pickingTexture = this.device.createTexture({
5913
+ format: "rg32sint",
5914
+ width: 1,
5915
+ height: 1,
5916
+ usage: import_core18.Texture.RENDER_ATTACHMENT | import_core18.Texture.COPY_SRC
5917
+ });
5918
+ return this.device.createFramebuffer({
5919
+ colorAttachments: [colorTexture, pickingTexture],
5920
+ depthStencilAttachment: "depth24plus"
5921
+ });
5922
+ }
5923
+ createColorFramebuffer() {
5924
+ const pickingTexture = this.device.createTexture({
5925
+ format: "rgba8unorm",
5926
+ width: 1,
5927
+ height: 1,
5928
+ usage: import_core18.Texture.RENDER_ATTACHMENT | import_core18.Texture.COPY_SRC
5929
+ });
5930
+ return this.device.createFramebuffer({
5931
+ colorAttachments: [pickingTexture],
5932
+ depthStencilAttachment: "depth24plus"
5933
+ });
5934
+ }
5935
+ setPickingProps(props) {
5936
+ var _a;
5937
+ (_a = this.props.shaderInputs) == null ? void 0 : _a.setProps({ picking: props });
5938
+ }
5939
+ async readPickInfo(framebuffer, pickPosition) {
5940
+ return this.mode === "index" ? this.readIndexPickInfo(framebuffer, pickPosition) : this.readColorPickInfo(framebuffer, pickPosition);
5941
+ }
5942
+ async readIndexPickInfo(framebuffer, [pickX, pickY]) {
5943
+ var _a;
5944
+ if (this.device.type === "webgpu") {
5945
+ const pickTexture = (_a = framebuffer.colorAttachments[INDEX_PICKING_ATTACHMENT_INDEX]) == null ? void 0 : _a.texture;
5946
+ if (!pickTexture) {
5947
+ return null;
5948
+ }
5949
+ const layout = pickTexture.computeMemoryLayout({ width: 1, height: 1 });
5950
+ const readBuffer = this.device.createBuffer({
5951
+ byteLength: layout.byteLength,
5952
+ usage: import_core18.Buffer.COPY_DST | import_core18.Buffer.MAP_READ
5953
+ });
5954
+ try {
5955
+ pickTexture.readBuffer({
5956
+ x: pickX,
5957
+ y: pickY,
5958
+ width: 1,
5959
+ height: 1
5960
+ }, readBuffer);
5961
+ const pickDataView = await readBuffer.readAsync(0, layout.byteLength);
5962
+ return decodeIndexPickInfo(new Int32Array(pickDataView.buffer, pickDataView.byteOffset, 2));
5963
+ } finally {
5964
+ readBuffer.destroy();
5965
+ }
5966
+ }
5967
+ const pixelData = this.device.readPixelsToArrayWebGL(framebuffer, {
5968
+ sourceX: pickX,
5969
+ sourceY: pickY,
5970
+ sourceWidth: 1,
5971
+ sourceHeight: 1,
5972
+ sourceAttachment: INDEX_PICKING_ATTACHMENT_INDEX
5973
+ });
5974
+ return pixelData ? decodeIndexPickInfo(new Int32Array(pixelData.buffer, pixelData.byteOffset, 2)) : null;
5975
+ }
5976
+ async readColorPickInfo(framebuffer, [pickX, pickY]) {
5977
+ var _a;
5978
+ if (this.device.type === "webgpu") {
5979
+ const pickTexture = (_a = framebuffer.colorAttachments[0]) == null ? void 0 : _a.texture;
5980
+ if (!pickTexture) {
5981
+ return null;
5982
+ }
5983
+ const layout = pickTexture.computeMemoryLayout({ width: 1, height: 1 });
5984
+ const readBuffer = this.device.createBuffer({
5985
+ byteLength: layout.byteLength,
5986
+ usage: import_core18.Buffer.COPY_DST | import_core18.Buffer.MAP_READ
5987
+ });
5988
+ try {
5989
+ pickTexture.readBuffer({
5990
+ x: pickX,
5991
+ y: pickY,
5992
+ width: 1,
5993
+ height: 1
5994
+ }, readBuffer);
5995
+ const pickDataView = await readBuffer.readAsync(0, layout.byteLength);
5996
+ return decodeColorPickInfo(new Uint8Array(pickDataView.buffer, pickDataView.byteOffset, 4));
5997
+ } finally {
5998
+ readBuffer.destroy();
5999
+ }
6000
+ }
6001
+ const pixelData = this.device.readPixelsToArrayWebGL(framebuffer, {
6002
+ sourceX: pickX,
6003
+ sourceY: pickY,
6004
+ sourceWidth: 1,
6005
+ sourceHeight: 1,
6006
+ sourceAttachment: 0
6007
+ });
6008
+ return pixelData ? decodeColorPickInfo(new Uint8Array(pixelData.buffer, pixelData.byteOffset, 4)) : null;
6009
+ }
6010
+ hasPickInfoChanged(pickInfo) {
6011
+ return pickInfo.objectIndex !== this.pickInfo.objectIndex || pickInfo.batchIndex !== this.pickInfo.batchIndex;
6012
+ }
5228
6013
  };
5229
6014
  var PickingManager = _PickingManager;
5230
6015
  __publicField(PickingManager, "defaultProps", {
5231
6016
  shaderInputs: void 0,
5232
6017
  onObjectPicked: () => {
5233
- }
6018
+ },
6019
+ mode: "color",
6020
+ backend: "color"
5234
6021
  });
5235
6022
 
5236
- // dist/modules/picking/index-picking.js
6023
+ // dist/modules/picking/color-picking.js
5237
6024
  var source = (
5238
6025
  /* wgsl */
5239
6026
  `${WGSL_UNIFORMS}
5240
6027
 
5241
- const INDEX_PICKING_MODE_INSTANCE = 0;
5242
- const INDEX_PICKING_MODE_CUSTOM = 1;
5243
- const INDEX_PICKING_INVALID_INDEX = ${INVALID_INDEX}; // 2^32 - 1
6028
+ const COLOR_PICKING_INVALID_INDEX = ${INVALID_INDEX};
6029
+ const COLOR_PICKING_MAX_OBJECT_INDEX = 16777214;
6030
+ const COLOR_PICKING_MAX_BATCH_INDEX = 254;
5244
6031
 
5245
- /**
5246
- * Vertex shaders should call this function to set the object index.
5247
- * If using instance or vertex mode, argument will be ignored, 0 can be supplied.
5248
- */
5249
- fn picking_setObjectIndex(objectIndex: int32) {
5250
- switch (picking.indexMode) {
5251
- case INDEX_PICKING_MODE_INSTANCE, default: {
5252
- picking_objectIndex = instance_index;
5253
- };
5254
- case INDEX_PICKING_MODE_CUSTOM: {
5255
- picking_objectIndex = objectIndex;
5256
- };
6032
+ fn picking_setObjectIndex(objectIndex: i32) -> i32 {
6033
+ return objectIndex;
6034
+ }
6035
+
6036
+ fn picking_isObjectHighlighted(objectIndex: i32) -> bool {
6037
+ return
6038
+ picking.isHighlightActive != 0 &&
6039
+ picking.highlightedBatchIndex == picking.batchIndex &&
6040
+ picking.highlightedObjectIndex == objectIndex;
6041
+ }
6042
+
6043
+ fn picking_filterHighlightColor(color: vec4<f32>, objectIndex: i32) -> vec4<f32> {
6044
+ if (picking.isActive != 0 || !picking_isObjectHighlighted(objectIndex)) {
6045
+ return color;
6046
+ }
6047
+
6048
+ let highLightAlpha = picking.highlightColor.a;
6049
+ let blendedAlpha = highLightAlpha + color.a * (1.0 - highLightAlpha);
6050
+ if (blendedAlpha == 0.0) {
6051
+ return vec4<f32>(color.rgb, 0.0);
6052
+ }
6053
+
6054
+ let highLightRatio = highLightAlpha / blendedAlpha;
6055
+ let blendedRGB = mix(color.rgb, picking.highlightColor.rgb, highLightRatio);
6056
+ return vec4<f32>(blendedRGB, blendedAlpha);
6057
+ }
6058
+
6059
+ fn picking_canEncodePickInfo(objectIndex: i32) -> bool {
6060
+ return
6061
+ objectIndex != COLOR_PICKING_INVALID_INDEX &&
6062
+ objectIndex >= 0 &&
6063
+ objectIndex <= COLOR_PICKING_MAX_OBJECT_INDEX &&
6064
+ picking.batchIndex >= 0 &&
6065
+ picking.batchIndex <= COLOR_PICKING_MAX_BATCH_INDEX;
6066
+ }
6067
+
6068
+ fn picking_getPickingColor(objectIndex: i32) -> vec4<f32> {
6069
+ if (!picking_canEncodePickInfo(objectIndex)) {
6070
+ return vec4<f32>(0.0, 0.0, 0.0, 0.0);
5257
6071
  }
6072
+
6073
+ let encodedObjectIndex = objectIndex + 1;
6074
+ let red = encodedObjectIndex % 256;
6075
+ let green = (encodedObjectIndex / 256) % 256;
6076
+ let blue = (encodedObjectIndex / 65536) % 256;
6077
+ let alpha = picking.batchIndex + 1;
6078
+
6079
+ return vec4<f32>(
6080
+ f32(red) / 255.0,
6081
+ f32(green) / 255.0,
6082
+ f32(blue) / 255.0,
6083
+ f32(alpha) / 255.0
6084
+ );
5258
6085
  }
5259
6086
 
6087
+ fn picking_filterPickingColor(color: vec4<f32>, objectIndex: i32) -> vec4<f32> {
6088
+ if (picking.isActive != 0) {
6089
+ if (!picking_canEncodePickInfo(objectIndex)) {
6090
+ discard;
6091
+ }
6092
+ return picking_getPickingColor(objectIndex);
6093
+ }
6094
+
6095
+ return color;
6096
+ }
5260
6097
  `
5261
6098
  );
5262
6099
  var vs = (
@@ -5266,14 +6103,10 @@ var vs = (
5266
6103
  const int INDEX_PICKING_MODE_INSTANCE = 0;
5267
6104
  const int INDEX_PICKING_MODE_CUSTOM = 1;
5268
6105
 
5269
- const int INDEX_PICKING_INVALID_INDEX = ${INVALID_INDEX}; // 2^32 - 1
6106
+ const int COLOR_PICKING_INVALID_INDEX = ${INVALID_INDEX};
5270
6107
 
5271
6108
  flat out int picking_objectIndex;
5272
6109
 
5273
- /**
5274
- * Vertex shaders should call this function to set the object index.
5275
- * If using instance or vertex mode, argument will be ignored, 0 can be supplied.
5276
- */
5277
6110
  void picking_setObjectIndex(int objectIndex) {
5278
6111
  switch (picking.indexMode) {
5279
6112
  case INDEX_PICKING_MODE_INSTANCE:
@@ -5290,36 +6123,29 @@ var fs = (
5290
6123
  /* glsl */
5291
6124
  `${GLSL_UNIFORMS}
5292
6125
 
5293
- const int INDEX_PICKING_INVALID_INDEX = ${INVALID_INDEX}; // 2^32 - 1
6126
+ const int COLOR_PICKING_INVALID_INDEX = ${INVALID_INDEX};
6127
+ const int COLOR_PICKING_MAX_OBJECT_INDEX = 16777214;
6128
+ const int COLOR_PICKING_MAX_BATCH_INDEX = 254;
5294
6129
 
5295
6130
  flat in int picking_objectIndex;
5296
6131
 
5297
- /**
5298
- * Check if this vertex is highlighted (part of the selected batch and object)
5299
- */
5300
6132
  bool picking_isFragmentHighlighted() {
5301
- return
6133
+ return
5302
6134
  bool(picking.isHighlightActive) &&
5303
6135
  picking.highlightedBatchIndex == picking.batchIndex &&
5304
6136
  picking.highlightedObjectIndex == picking_objectIndex
5305
6137
  ;
5306
6138
  }
5307
6139
 
5308
- /**
5309
- * Returns highlight color if this item is selected.
5310
- */
5311
6140
  vec4 picking_filterHighlightColor(vec4 color) {
5312
- // If we are still picking, we don't highlight
5313
6141
  if (bool(picking.isActive)) {
5314
6142
  return color;
5315
6143
  }
5316
6144
 
5317
- // If we are not highlighted, return color as is
5318
6145
  if (!picking_isFragmentHighlighted()) {
5319
6146
  return color;
5320
6147
  }
5321
-
5322
- // Blend in highlight color based on its alpha value
6148
+
5323
6149
  float highLightAlpha = picking.highlightColor.a;
5324
6150
  float blendedAlpha = highLightAlpha + color.a * (1.0 - highLightAlpha);
5325
6151
  float highLightRatio = highLightAlpha / blendedAlpha;
@@ -5328,28 +6154,40 @@ vec4 picking_filterHighlightColor(vec4 color) {
5328
6154
  return vec4(blendedRGB, blendedAlpha);
5329
6155
  }
5330
6156
 
5331
- /*
5332
- * Returns picking color if picking enabled else unmodified argument.
5333
- */
5334
- ivec4 picking_getPickingColor() {
5335
- // Assumes that colorAttachment0 is rg32int
5336
- // TODO? - we could render indices into a second color attachment and not mess with fragColor
5337
- return ivec4(picking_objectIndex, picking.batchIndex, 0u, 0u);
6157
+ bool picking_canEncodePickInfo(int objectIndex) {
6158
+ return
6159
+ objectIndex != COLOR_PICKING_INVALID_INDEX &&
6160
+ objectIndex >= 0 &&
6161
+ objectIndex <= COLOR_PICKING_MAX_OBJECT_INDEX &&
6162
+ picking.batchIndex >= 0 &&
6163
+ picking.batchIndex <= COLOR_PICKING_MAX_BATCH_INDEX;
6164
+ }
6165
+
6166
+ vec4 picking_getPickingColor() {
6167
+ if (!picking_canEncodePickInfo(picking_objectIndex)) {
6168
+ return vec4(0.0);
6169
+ }
6170
+
6171
+ int encodedObjectIndex = picking_objectIndex + 1;
6172
+ int red = encodedObjectIndex % 256;
6173
+ int green = (encodedObjectIndex / 256) % 256;
6174
+ int blue = (encodedObjectIndex / 65536) % 256;
6175
+ int alpha = picking.batchIndex + 1;
6176
+
6177
+ return vec4(float(red), float(green), float(blue), float(alpha)) / 255.0;
5338
6178
  }
5339
6179
 
5340
6180
  vec4 picking_filterPickingColor(vec4 color) {
5341
6181
  if (bool(picking.isActive)) {
5342
- if (picking_objectIndex == INDEX_PICKING_INVALID_INDEX) {
6182
+ if (!picking_canEncodePickInfo(picking_objectIndex)) {
5343
6183
  discard;
5344
6184
  }
6185
+ return picking_getPickingColor();
5345
6186
  }
6187
+
5346
6188
  return color;
5347
6189
  }
5348
6190
 
5349
- /*
5350
- * Returns picking color if picking is enabled if not
5351
- * highlight color if this item is selected, otherwise unmodified argument.
5352
- */
5353
6191
  vec4 picking_filterColor(vec4 color) {
5354
6192
  vec4 outColor = color;
5355
6193
  outColor = picking_filterHighlightColor(outColor);
@@ -5366,87 +6204,81 @@ var picking = {
5366
6204
  fs
5367
6205
  };
5368
6206
 
5369
- // dist/modules/picking/color-picking.js
6207
+ // dist/modules/picking/index-picking.js
5370
6208
  var source2 = (
5371
6209
  /* wgsl */
5372
6210
  `${WGSL_UNIFORMS}
5373
- `
5374
- );
5375
- var vs2 = (
5376
- /* glsl */
5377
- `${GLSL_UNIFORMS}
5378
- out vec4 picking_vRGBcolor_Avalid;
5379
-
5380
- // Normalize unsigned byte color to 0-1 range
5381
- vec3 picking_normalizeColor(vec3 color) {
5382
- return picking.useFloatColors > 0.5 ? color : color / 255.0;
5383
- }
5384
-
5385
- // Normalize unsigned byte color to 0-1 range
5386
- vec4 picking_normalizeColor(vec4 color) {
5387
- return picking.useFloatColors > 0.5 ? color : color / 255.0;
5388
- }
5389
6211
 
5390
- bool picking_isColorZero(vec3 color) {
5391
- return dot(color, vec3(1.0)) < 0.00001;
5392
- }
6212
+ const INDEX_PICKING_MODE_INSTANCE = 0;
6213
+ const INDEX_PICKING_MODE_CUSTOM = 1;
6214
+ const INDEX_PICKING_INVALID_INDEX = ${INVALID_INDEX}; // 2^32 - 1
5393
6215
 
5394
- bool picking_isColorValid(vec3 color) {
5395
- return dot(color, vec3(1.0)) > 0.00001;
6216
+ /**
6217
+ * WGSL shaders need to carry the returned object index through their own stage outputs.
6218
+ */
6219
+ fn picking_setObjectIndex(objectIndex: i32) -> i32 {
6220
+ return objectIndex;
5396
6221
  }
5397
6222
 
5398
- // Check if this vertex is highlighted
5399
- bool isVertexHighlighted(vec3 vertexColor) {
5400
- vec3 highlightedObjectColor = picking_normalizeColor(picking.highlightedObjectColor);
6223
+ fn picking_isObjectHighlighted(objectIndex: i32) -> bool {
5401
6224
  return
5402
- bool(picking.isHighlightActive) && picking_isColorZero(abs(vertexColor - highlightedObjectColor));
6225
+ picking.isHighlightActive != 0 &&
6226
+ picking.highlightedBatchIndex == picking.batchIndex &&
6227
+ picking.highlightedObjectIndex == objectIndex;
5403
6228
  }
5404
6229
 
5405
- // Set the current picking color
5406
- void picking_setPickingColor(vec3 pickingColor) {
5407
- pickingColor = picking_normalizeColor(pickingColor);
5408
-
5409
- if (bool(picking.isActive)) {
5410
- // Use alpha as the validity flag. If pickingColor is [0, 0, 0] fragment is non-pickable
5411
- picking_vRGBcolor_Avalid.a = float(picking_isColorValid(pickingColor));
5412
-
5413
- if (!bool(picking.isAttribute)) {
5414
- // Stores the picking color so that the fragment shader can render it during picking
5415
- picking_vRGBcolor_Avalid.rgb = pickingColor;
5416
- }
5417
- } else {
5418
- // Do the comparison with selected item color in vertex shader as it should mean fewer compares
5419
- picking_vRGBcolor_Avalid.a = float(isVertexHighlighted(pickingColor));
6230
+ fn picking_filterHighlightColor(color: vec4<f32>, objectIndex: i32) -> vec4<f32> {
6231
+ if (picking.isActive != 0 || !picking_isObjectHighlighted(objectIndex)) {
6232
+ return color;
5420
6233
  }
5421
- }
5422
6234
 
5423
- void picking_setObjectIndex(uint objectIndex) {
5424
- if (bool(picking.isActive)) {
5425
- uint index = objectIndex;
5426
- if (picking.indexMode == PICKING_INDEX_MODE_INSTANCE) {
5427
- index = uint(gl_InstanceID);
5428
- }
5429
- picking_vRGBcolor_Avalid.r = float(index % 255) / 255.0;
5430
- picking_vRGBcolor_Avalid.g = float((index / 255) % 255) / 255.0;
5431
- picking_vRGBcolor_Avalid.b = float((index / 255 / 255) %255) / 255.0;
6235
+ let highLightAlpha = picking.highlightColor.a;
6236
+ let blendedAlpha = highLightAlpha + color.a * (1.0 - highLightAlpha);
6237
+ if (blendedAlpha == 0.0) {
6238
+ return vec4<f32>(color.rgb, 0.0);
5432
6239
  }
6240
+
6241
+ let highLightRatio = highLightAlpha / blendedAlpha;
6242
+ let blendedRGB = mix(color.rgb, picking.highlightColor.rgb, highLightRatio);
6243
+ return vec4<f32>(blendedRGB, blendedAlpha);
5433
6244
  }
5434
6245
 
5435
- void picking_setPickingAttribute(float value) {
5436
- if (bool(picking.isAttribute)) {
5437
- picking_vRGBcolor_Avalid.r = value;
6246
+ fn picking_filterPickingColor(color: vec4<f32>, objectIndex: i32) -> vec4<f32> {
6247
+ if (picking.isActive != 0 && objectIndex == INDEX_PICKING_INVALID_INDEX) {
6248
+ discard;
5438
6249
  }
6250
+ return color;
5439
6251
  }
5440
6252
 
5441
- void picking_setPickingAttribute(vec2 value) {
5442
- if (bool(picking.isAttribute)) {
5443
- picking_vRGBcolor_Avalid.rg = value;
5444
- }
6253
+ fn picking_getPickingColor(objectIndex: i32) -> vec2<i32> {
6254
+ return vec2<i32>(objectIndex, picking.batchIndex);
5445
6255
  }
5446
6256
 
5447
- void picking_setPickingAttribute(vec3 value) {
5448
- if (bool(picking.isAttribute)) {
5449
- picking_vRGBcolor_Avalid.rgb = value;
6257
+ `
6258
+ );
6259
+ var vs2 = (
6260
+ /* glsl */
6261
+ `${GLSL_UNIFORMS}
6262
+
6263
+ const int INDEX_PICKING_MODE_INSTANCE = 0;
6264
+ const int INDEX_PICKING_MODE_CUSTOM = 1;
6265
+
6266
+ const int INDEX_PICKING_INVALID_INDEX = ${INVALID_INDEX}; // 2^32 - 1
6267
+
6268
+ flat out int picking_objectIndex;
6269
+
6270
+ /**
6271
+ * Vertex shaders should call this function to set the object index.
6272
+ * If using instance or vertex mode, argument will be ignored, 0 can be supplied.
6273
+ */
6274
+ void picking_setObjectIndex(int objectIndex) {
6275
+ switch (picking.indexMode) {
6276
+ case INDEX_PICKING_MODE_INSTANCE:
6277
+ picking_objectIndex = gl_InstanceID;
6278
+ break;
6279
+ case INDEX_PICKING_MODE_CUSTOM:
6280
+ picking_objectIndex = objectIndex;
6281
+ break;
5450
6282
  }
5451
6283
  }
5452
6284
  `
@@ -5455,41 +6287,58 @@ var fs2 = (
5455
6287
  /* glsl */
5456
6288
  `${GLSL_UNIFORMS}
5457
6289
 
5458
- in vec4 picking_vRGBcolor_Avalid;
6290
+ const int INDEX_PICKING_INVALID_INDEX = ${INVALID_INDEX}; // 2^32 - 1
6291
+
6292
+ flat in int picking_objectIndex;
5459
6293
 
5460
- /*
6294
+ /**
6295
+ * Check if this vertex is highlighted (part of the selected batch and object)
6296
+ */
6297
+ bool picking_isFragmentHighlighted() {
6298
+ return
6299
+ bool(picking.isHighlightActive) &&
6300
+ picking.highlightedBatchIndex == picking.batchIndex &&
6301
+ picking.highlightedObjectIndex == picking_objectIndex
6302
+ ;
6303
+ }
6304
+
6305
+ /**
5461
6306
  * Returns highlight color if this item is selected.
5462
6307
  */
5463
6308
  vec4 picking_filterHighlightColor(vec4 color) {
5464
6309
  // If we are still picking, we don't highlight
5465
- if (picking.isActive > 0.5) {
6310
+ if (bool(picking.isActive)) {
5466
6311
  return color;
5467
6312
  }
5468
6313
 
5469
- bool selected = bool(picking_vRGBcolor_Avalid.a);
5470
-
5471
- if (selected) {
5472
- // Blend in highlight color based on its alpha value
5473
- float highLightAlpha = picking.highlightColor.a;
5474
- float blendedAlpha = highLightAlpha + color.a * (1.0 - highLightAlpha);
5475
- float highLightRatio = highLightAlpha / blendedAlpha;
5476
-
5477
- vec3 blendedRGB = mix(color.rgb, picking.highlightColor.rgb, highLightRatio);
5478
- return vec4(blendedRGB, blendedAlpha);
5479
- } else {
6314
+ // If we are not highlighted, return color as is
6315
+ if (!picking_isFragmentHighlighted()) {
5480
6316
  return color;
5481
6317
  }
6318
+
6319
+ // Blend in highlight color based on its alpha value
6320
+ float highLightAlpha = picking.highlightColor.a;
6321
+ float blendedAlpha = highLightAlpha + color.a * (1.0 - highLightAlpha);
6322
+ float highLightRatio = highLightAlpha / blendedAlpha;
6323
+
6324
+ vec3 blendedRGB = mix(color.rgb, picking.highlightColor.rgb, highLightRatio);
6325
+ return vec4(blendedRGB, blendedAlpha);
5482
6326
  }
5483
6327
 
5484
6328
  /*
5485
6329
  * Returns picking color if picking enabled else unmodified argument.
5486
6330
  */
6331
+ ivec4 picking_getPickingColor() {
6332
+ // Assumes that colorAttachment0 is rg32int
6333
+ // TODO? - we could render indices into a second color attachment and not mess with fragColor
6334
+ return ivec4(picking_objectIndex, picking.batchIndex, 0u, 0u);
6335
+ }
6336
+
5487
6337
  vec4 picking_filterPickingColor(vec4 color) {
5488
6338
  if (bool(picking.isActive)) {
5489
- if (picking_vRGBcolor_Avalid.a == 0.0) {
6339
+ if (picking_objectIndex == INDEX_PICKING_INVALID_INDEX) {
5490
6340
  discard;
5491
6341
  }
5492
- return picking_vRGBcolor_Avalid;
5493
6342
  }
5494
6343
  return color;
5495
6344
  }
@@ -5499,8 +6348,10 @@ vec4 picking_filterPickingColor(vec4 color) {
5499
6348
  * highlight color if this item is selected, otherwise unmodified argument.
5500
6349
  */
5501
6350
  vec4 picking_filterColor(vec4 color) {
5502
- vec4 highlightColor = picking_filterHighlightColor(color);
5503
- return picking_filterPickingColor(highlightColor);
6351
+ vec4 outColor = color;
6352
+ outColor = picking_filterHighlightColor(outColor);
6353
+ outColor = picking_filterPickingColor(outColor);
6354
+ return outColor;
5504
6355
  }
5505
6356
  `
5506
6357
  );
@@ -5512,6 +6363,15 @@ var picking2 = {
5512
6363
  fs: fs2
5513
6364
  };
5514
6365
 
6366
+ // dist/modules/picking/picking.js
6367
+ var picking3 = {
6368
+ ...pickingUniforms,
6369
+ name: "picking",
6370
+ source: picking2.source,
6371
+ vs: picking.vs,
6372
+ fs: picking.fs
6373
+ };
6374
+
5515
6375
  // dist/modules/picking/legacy-picking-manager.js
5516
6376
  var LegacyPickingManager = class {
5517
6377
  device;
@@ -5580,6 +6440,10 @@ var LegacyPickingManager = class {
5580
6440
  }
5581
6441
  };
5582
6442
 
6443
+ // dist/modules/picking/legacy-color-picking.js
6444
+ var import_shadertools7 = require("@luma.gl/shadertools");
6445
+ var legacyColorPicking = import_shadertools7.picking;
6446
+
5583
6447
  // dist/index.js
5584
6448
  var AsyncTexture = DynamicTexture;
5585
6449
  //# sourceMappingURL=index.cjs.map