@kitware/vtk.js 34.0.0-beta.1 → 34.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -71,6 +71,8 @@ struct PBRData {
71
71
  specular: vec3<f32>,
72
72
  }
73
73
 
74
+ const pi: f32 = 3.14159265359;
75
+
74
76
  // Dot product with the max already in it
75
77
  fn mdot(a: vec3<f32>, b: vec3<f32>) -> f32 {
76
78
  return max(0.0, dot(a, b));
@@ -89,7 +91,6 @@ fn cdot(a: vec3<f32>, b: vec3<f32>) -> f32 {
89
91
 
90
92
  // Lambertian diffuse model
91
93
  fn lambertDiffuse(base: vec3<f32>, N: vec3<f32>, L: vec3<f32>) -> vec3<f32> {
92
- var pi: f32 = 3.14159265359;
93
94
  var NdotL: f32 = mdot(N, L);
94
95
  NdotL = pow(NdotL, 1.5);
95
96
  return (base/pi)*NdotL;
@@ -135,12 +136,10 @@ fn schlickFresnelRGB(V: vec3<f32>, N: vec3<f32>, F0: vec3<f32>) -> vec3<f32> {
135
136
  // https://learnopengl.com/PBR/Theory
136
137
  // Trowbridge-Reitz GGX functions: normal, halfway, roughness^2
137
138
  fn trGGX(N: vec3<f32>, H: vec3<f32>, a: f32) -> f32 {
138
- var pi: f32 = 3.14159265359;
139
-
140
139
  var a2: f32 = a*a;
141
140
  var NdotH = mdot(N, H);
142
141
  var NdotH2 = NdotH*NdotH;
143
-
142
+
144
143
  var denom: f32 = NdotH2 * (a2 - 1.0) + 1.0;
145
144
 
146
145
  return a2 / max((pi*denom*denom), 0.000001);
@@ -176,7 +175,7 @@ fn cookTorrance(D: f32, F: f32, G: f32, N: vec3<f32>, V: vec3<f32>, L: vec3<f32>
176
175
  }
177
176
 
178
177
  // Different lighting calculations for different light sources
179
- fn calcDirectionalLight(N: vec3<f32>, V: vec3<f32>, ior: f32, roughness: f32, metallic: f32, direction: vec3<f32>, color: vec3<f32>, base: vec3<f32>) -> PBRData {
178
+ fn calcDirectionalLight(N: vec3<f32>, V: vec3<f32>, ior: f32, roughness: f32, metallic: f32, direction: vec3<f32>, color: vec3<f32>, base: vec3<f32>) -> PBRData {
180
179
  var L: vec3<f32> = normalize(direction); // Light Vector
181
180
  var H: vec3<f32> = normalize(L + V); // Halfway Vector
182
181
 
@@ -195,10 +194,10 @@ fn calcDirectionalLight(N: vec3<f32>, V: vec3<f32>, ior: f32, roughness: f32, me
195
194
  var specular: vec3<f32> = brdf*incoming*angle;
196
195
  // Oren-Nayar gives a clay-like effect when fully rough which some people may not want, so it might be better to give a separate
197
196
  // control property for the diffuse vs specular roughness
198
- var diffuse: vec3<f32> = incoming*fujiiOrenNayar(base, roughness, N, L, V);
197
+ var diffuse: vec3<f32> = incoming*fujiiOrenNayar(base, roughness, N, L, V);
199
198
  // Stores the specular and diffuse separately to allow for finer post processing
200
199
  var out = PBRData(diffuse, specular);
201
-
200
+
202
201
  return out; // Returns angle along with color of light so the final color can be multiplied by angle as well (creates black areas)
203
202
  }
204
203
 
@@ -226,7 +225,7 @@ fn calcPointLight(N: vec3<f32>, V: vec3<f32>, fragPos: vec3<f32>, ior: f32, roug
226
225
  // Stores the specular and diffuse separately to allow for finer post processing
227
226
  // Could also be done (propably more properly) with a struct
228
227
  var out = PBRData(diffuse, specular);
229
-
228
+
230
229
  return out; // Returns angle along with color of light so the final color can be multiplied by angle as well (creates black areas)
231
230
  }
232
231
 
@@ -244,7 +243,7 @@ fn calcSpotLight(N: vec3<f32>, V: vec3<f32>, fragPos: vec3<f32>, ior: f32, rough
244
243
  var G: f32 = smithSurfaceRoughness(N, V, L, k); // Geometry
245
244
 
246
245
  var brdf: f32 = cookTorrance(D, 1.0, G, N, V, L);
247
-
246
+
248
247
  // Cones.x is the inner phi and cones.y is the outer phi
249
248
  var theta: f32 = mdot(normalize(direction), L);
250
249
  var epsilon: f32 = cones.x - cones.y;
@@ -263,7 +262,7 @@ fn calcSpotLight(N: vec3<f32>, V: vec3<f32>, fragPos: vec3<f32>, ior: f32, rough
263
262
  // Stores the specular and diffuse separately to allow for finer post processing
264
263
  // Could also be done (propably more properly) with a struct
265
264
  var out = PBRData(diffuse, specular);
266
-
265
+
267
266
  return out; // Returns angle along with color of light so the final color can be multiplied by angle as well (creates black areas)
268
267
  }
269
268
 
@@ -271,7 +270,6 @@ fn calcSpotLight(N: vec3<f32>, V: vec3<f32>, fragPos: vec3<f32>, ior: f32, rough
271
270
  // Takes in a vector and converts it to an equivalent coordinate in a rectilinear texture. Should be replaced with cubemaps at some point
272
271
  fn vecToRectCoord(dir: vec3<f32>) -> vec2<f32> {
273
272
  var tau: f32 = 6.28318530718;
274
- var pi: f32 = 3.14159265359;
275
273
  var out: vec2<f32> = vec2<f32>(0.0);
276
274
 
277
275
  out.x = atan2(dir.z, dir.x) / tau;
@@ -385,56 +383,59 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
385
383
  }
386
384
  };
387
385
  publicAPI.updateUBO = () => {
388
- // make sure the data is up to date
389
386
  const actor = model.WebGPUActor.getRenderable();
390
387
  const ppty = actor.getProperty();
391
388
  const utime = model.UBO.getSendTime();
392
- if (publicAPI.getMTime() > utime || ppty.getMTime() > utime || model.renderable.getMTime() > utime) {
393
- // Matricies
394
- const keyMats = model.WebGPUActor.getKeyMatrices(model.WebGPURenderer);
395
- model.UBO.setArray('BCWCMatrix', keyMats.bcwc);
396
- model.UBO.setArray('BCSCMatrix', keyMats.bcsc);
397
- model.UBO.setArray('MCWCNormals', keyMats.normalMatrix);
398
- if (model.is2D) {
399
- model.UBO.setValue('ZValue', model.WebGPUActor.getRenderable().getProperty().getDisplayLocation() === DisplayLocation.FOREGROUND ? 1.0 : 0.0);
400
- const aColor = ppty.getColorByReference();
401
- model.UBO.setValue('AmbientIntensity', 1.0);
402
- model.UBO.setArray('DiffuseColor', [aColor[0], aColor[1], aColor[2], 1.0]);
403
- model.UBO.setValue('DiffuseIntensity', 0.0);
404
- model.UBO.setValue('SpecularIntensity', 0.0);
405
- } else {
406
- // Base Colors
407
- let aColor = ppty.getAmbientColorByReference();
408
- model.UBO.setValue('AmbientIntensity', ppty.getAmbient());
409
- model.UBO.setArray('AmbientColor', [aColor[0], aColor[1], aColor[2], 1.0]);
410
- model.UBO.setValue('DiffuseIntensity', ppty.getDiffuse());
411
- aColor = ppty.getDiffuseColorByReference();
412
- model.UBO.setArray('DiffuseColor', [aColor[0], aColor[1], aColor[2], 1.0]);
413
- // Roughness
414
- model.UBO.setValue('Roughness', ppty.getRoughness());
415
- model.UBO.setValue('BaseIOR', ppty.getBaseIOR());
416
- // Metallic
417
- model.UBO.setValue('Metallic', ppty.getMetallic());
418
- // Normal
419
- model.UBO.setValue('NormalStrength', ppty.getNormalStrength());
420
- // Emission
421
- model.UBO.setValue('Emission', ppty.getEmission());
422
- // Specular
423
- model.UBO.setValue('SpecularIntensity', ppty.getSpecular());
424
- aColor = ppty.getSpecularColorByReference();
425
- model.UBO.setArray('SpecularColor', [aColor[0], aColor[1], aColor[2], 1.0]);
426
- }
427
- // Edge and line rendering
428
- const aColor = ppty.getEdgeColorByReference?.();
429
- if (aColor) {
430
- model.UBO.setArray('EdgeColor', [aColor[0], aColor[1], aColor[2], 1.0]);
389
+ if (publicAPI.getMTime() <= utime && ppty.getMTime() <= utime && model.renderable.getMTime() <= utime) {
390
+ return;
391
+ }
392
+
393
+ // --- Matrix Updates ---
394
+ const keyMats = model.WebGPUActor.getKeyMatrices(model.WebGPURenderer);
395
+ model.UBO.setArray('BCWCMatrix', keyMats.bcwc);
396
+ model.UBO.setArray('BCSCMatrix', keyMats.bcsc);
397
+ model.UBO.setArray('MCWCNormals', keyMats.normalMatrix);
398
+
399
+ // --- 2D or 3D ---
400
+ if (model.is2D) {
401
+ const displayLoc = ppty.getDisplayLocation?.() ?? DisplayLocation.BACKGROUND;
402
+ model.UBO.setValue('ZValue', displayLoc === DisplayLocation.FOREGROUND ? 1.0 : 0.0);
403
+ const aColor = ppty.getColorByReference();
404
+ model.UBO.setValue('AmbientIntensity', 1.0);
405
+ model.UBO.setArray('DiffuseColor', [...aColor, 1.0]);
406
+ model.UBO.setValue('DiffuseIntensity', 0.0);
407
+ model.UBO.setValue('SpecularIntensity', 0.0);
408
+ } else {
409
+ // Base Colors
410
+ model.UBO.setValue('AmbientIntensity', ppty.getAmbient());
411
+ model.UBO.setArray('AmbientColor', [...ppty.getAmbientColorByReference(), 1.0]);
412
+ model.UBO.setValue('DiffuseIntensity', ppty.getDiffuse());
413
+ model.UBO.setArray('DiffuseColor', [...ppty.getDiffuseColorByReference(), 1.0]);
414
+ // Roughness
415
+ model.UBO.setValue('Roughness', ppty.getRoughness());
416
+ model.UBO.setValue('BaseIOR', ppty.getBaseIOR());
417
+ // Metallic
418
+ model.UBO.setValue('Metallic', ppty.getMetallic());
419
+ // Normal
420
+ model.UBO.setValue('NormalStrength', ppty.getNormalStrength());
421
+ // Emission
422
+ model.UBO.setValue('Emission', ppty.getEmission());
423
+ // Specular
424
+ model.UBO.setValue('SpecularIntensity', ppty.getSpecular());
425
+ if (ppty.getSpecularColorByReference()) {
426
+ model.UBO.setArray('SpecularColor', [...ppty.getSpecularColorByReference(), 1.0]);
431
427
  }
432
- model.UBO.setValue('LineWidth', ppty.getLineWidth());
433
- model.UBO.setValue('Opacity', ppty.getOpacity());
434
- model.UBO.setValue('PropID', model.WebGPUActor.getPropID());
435
- const device = model.WebGPURenderWindow.getDevice();
436
- model.UBO.sendIfNeeded(device);
437
428
  }
429
+
430
+ // --- Edge and Misc ---
431
+ const edgeColor = ppty.getEdgeColorByReference?.();
432
+ if (edgeColor) model.UBO.setArray('EdgeColor', [...edgeColor, 1.0]);
433
+ model.UBO.setValue('LineWidth', ppty.getLineWidth());
434
+ model.UBO.setValue('Opacity', ppty.getOpacity());
435
+ model.UBO.setValue('PropID', model.WebGPUActor.getPropID());
436
+
437
+ // Only send if needed
438
+ model.UBO.sendIfNeeded(model.WebGPURenderWindow.getDevice());
438
439
  };
439
440
  publicAPI.haveWideLines = () => {
440
441
  const actor = model.WebGPUActor.getRenderable();
@@ -523,8 +524,6 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
523
524
  // Code that runs if the fragment shader includes normals
524
525
  if (code.includes('var normal:') && model.useRendererMatrix && !isEdges(hash) && !model.is2D && !hash.includes('sel')) {
525
526
  const lightingCode = [
526
- // Constants
527
- ' var pi: f32 = 3.14159265359;',
528
527
  // Vectors needed for light calculations
529
528
  ' var fragPos: vec3<f32> = vec3<f32>(input.vertexVC.xyz);', ' var V: vec3<f32> = mix(normalize(-fragPos), vec3<f32>(0, 0, 1), f32(rendererUBO.cameraParallel)); // View Vector',
530
529
  // Values needed for light calculations
@@ -630,17 +629,17 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
630
629
  } else {
631
630
  if (roughnessTexture?.getImageLoaded()) {
632
631
  if (checkDims(roughnessTexture)) {
633
- usedTextures.push('_roughnessMap = textureSample(RoughnessTexture, RoughnessTextureSampler, input.tcoordVS);');
632
+ usedTextures.push('_roughnessMap = textureSample(RoughnessTexture, RoughnessTextureSampler, input.tcoordVS).ggga;');
634
633
  }
635
634
  }
636
635
  if (metallicTexture?.getImageLoaded()) {
637
636
  if (checkDims(metallicTexture)) {
638
- usedTextures.push('_metallicMap = textureSample(MetallicTexture, MetallicTextureSampler, input.tcoordVS);');
637
+ usedTextures.push('_metallicMap = textureSample(MetallicTexture, MetallicTextureSampler, input.tcoordVS).bbba;');
639
638
  }
640
639
  }
641
640
  if (ambientOcclusionTexture?.getImageLoaded()) {
642
641
  if (checkDims(ambientOcclusionTexture)) {
643
- usedTextures.push('_ambientOcclusionMap = textureSample(AmbientOcclusionTexture, AmbientOcclusionTextureSampler, input.tcoordVS);');
642
+ usedTextures.push('_ambientOcclusionMap = textureSample(AmbientOcclusionTexture, AmbientOcclusionTextureSampler, input.tcoordVS).rrra;');
644
643
  }
645
644
  }
646
645
  }
@@ -722,19 +721,18 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
722
721
  }
723
722
  const vertexInput = model.vertexInput;
724
723
  const points = pd.getPoints();
725
- let indexBuffer;
726
724
 
727
- // get the flat mapping indexBuffer for the cells
725
+ // --- Index Buffer ---
726
+ let indexBuffer = null;
728
727
  if (cells) {
729
- const buffRequest = {
728
+ indexBuffer = device.getBufferManager().getBuffer({
730
729
  hash: `R${representation}P${primType}${cells.getMTime()}`,
731
730
  usage: BufferUsage.Index,
732
731
  cells,
733
732
  numberOfPoints: points.getNumberOfPoints(),
734
733
  primitiveType: primType,
735
734
  representation
736
- };
737
- indexBuffer = device.getBufferManager().getBuffer(buffRequest);
735
+ });
738
736
  vertexInput.setIndexBuffer(indexBuffer);
739
737
  } else {
740
738
  vertexInput.setIndexBuffer(null);
@@ -749,26 +747,23 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
749
747
  // - format
750
748
  // - usage
751
749
  // - packExtra - covered by format
752
-
753
- // points
750
+ // --- Points Buffer ---
754
751
  if (points) {
755
752
  const shift = model.WebGPUActor.getBufferShift(model.WebGPURenderer);
756
- const buffRequest = {
757
- hash: `${points.getMTime()}I${indexBuffer.getMTime()}${shift.join()}float32x4`,
753
+ vertexInput.addBuffer(device.getBufferManager().getBuffer({
754
+ hash: `${points.getMTime()}I${indexBuffer?.getMTime?.() ?? 0}${shift.join()}float32x4`,
758
755
  usage: BufferUsage.PointArray,
759
756
  format: 'float32x4',
760
757
  dataArray: points,
761
758
  indexBuffer,
762
759
  shift,
763
760
  packExtra: true
764
- };
765
- const buff = device.getBufferManager().getBuffer(buffRequest);
766
- vertexInput.addBuffer(buff, ['vertexBC']);
761
+ }), ['vertexBC']);
767
762
  } else {
768
763
  vertexInput.removeBufferIfPresent('vertexBC');
769
764
  }
770
765
 
771
- // normals, only used for surface rendering
766
+ // --- Normals ---
772
767
  const usage = publicAPI.getUsage(representation, primType);
773
768
  model._usesCellNormals = false;
774
769
  if (!model.is2D && (
@@ -788,16 +783,14 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
788
783
  buffRequest.hash = `${normals.getMTime()}I${indexBuffer.getMTime()}snorm8x4`;
789
784
  buffRequest.dataArray = normals;
790
785
  buffRequest.usage = BufferUsage.PointArray;
791
- const buff = device.getBufferManager().getBuffer(buffRequest);
792
- vertexInput.addBuffer(buff, ['normalMC']);
786
+ vertexInput.addBuffer(device.getBufferManager().getBuffer(buffRequest), ['normalMC']);
793
787
  } else if (primType === PrimitiveTypes.Triangles) {
794
788
  model._usesCellNormals = true;
795
789
  buffRequest.hash = `PFN${points.getMTime()}I${indexBuffer.getMTime()}snorm8x4`;
796
790
  buffRequest.dataArray = points;
797
791
  buffRequest.cells = cells;
798
792
  buffRequest.usage = BufferUsage.NormalsFromPoints;
799
- const buff = device.getBufferManager().getBuffer(buffRequest);
800
- vertexInput.addBuffer(buff, ['normalMC']);
793
+ vertexInput.addBuffer(device.getBufferManager().getBuffer(buffRequest), ['normalMC']);
801
794
  } else {
802
795
  vertexInput.removeBufferIfPresent('normalMC');
803
796
  }
@@ -805,18 +798,15 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
805
798
  vertexInput.removeBufferIfPresent('normalMC');
806
799
  }
807
800
 
808
- // deal with colors but only if modified
801
+ // --- Colors ---
809
802
  let haveColors = false;
810
803
  if (model.renderable.getScalarVisibility()) {
811
804
  const c = model.renderable.getColorMapColors();
812
805
  if (c && !edges) {
813
806
  const scalarMode = model.renderable.getScalarMode();
814
- let haveCellScalars = false;
815
807
  // We must figure out how the scalars should be mapped to the polydata.
816
- if ((scalarMode === ScalarMode.USE_CELL_DATA || scalarMode === ScalarMode.USE_CELL_FIELD_DATA || scalarMode === ScalarMode.USE_FIELD_DATA || !pd.getPointData().getScalars()) && scalarMode !== ScalarMode.USE_POINT_FIELD_DATA && c) {
817
- haveCellScalars = true;
818
- }
819
- const buffRequest = {
808
+ const haveCellScalars = (scalarMode === ScalarMode.USE_CELL_DATA || scalarMode === ScalarMode.USE_CELL_FIELD_DATA || scalarMode === ScalarMode.USE_FIELD_DATA || !pd.getPointData().getScalars()) && scalarMode !== ScalarMode.USE_POINT_FIELD_DATA && c;
809
+ vertexInput.addBuffer(device.getBufferManager().getBuffer({
820
810
  usage: BufferUsage.PointArray,
821
811
  format: 'unorm8x4',
822
812
  hash: `${haveCellScalars}${c.getMTime()}I${indexBuffer.getMTime()}unorm8x4`,
@@ -824,15 +814,13 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
824
814
  indexBuffer,
825
815
  cellData: haveCellScalars,
826
816
  cellOffset: 0
827
- };
828
- const buff = device.getBufferManager().getBuffer(buffRequest);
829
- vertexInput.addBuffer(buff, ['colorVI']);
817
+ }), ['colorVI']);
830
818
  haveColors = true;
831
819
  }
832
820
  }
833
- if (!haveColors) {
834
- vertexInput.removeBufferIfPresent('colorVI');
835
- }
821
+ if (!haveColors) vertexInput.removeBufferIfPresent('colorVI');
822
+
823
+ // --- Texture Coordinates ---
836
824
  let tcoords = null;
837
825
  if (model.renderable.getInterpolateScalarsBeforeMapping?.() && model.renderable.getColorCoordinates()) {
838
826
  tcoords = model.renderable.getColorCoordinates();
@@ -840,134 +828,86 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
840
828
  tcoords = pd.getPointData().getTCoords();
841
829
  }
842
830
  if (tcoords && !edges) {
843
- const buff = device.getBufferManager().getBufferForPointArray(tcoords, vertexInput.getIndexBuffer());
844
- vertexInput.addBuffer(buff, ['tcoord']);
831
+ vertexInput.addBuffer(device.getBufferManager().getBufferForPointArray(tcoords, vertexInput.getIndexBuffer()), ['tcoord']);
845
832
  } else {
846
833
  vertexInput.removeBufferIfPresent('tcoord');
847
834
  }
848
835
  };
849
836
  publicAPI.updateTextures = () => {
850
- // we keep track of new and used textures so
851
- // that we can clean up any unused textures so we don't hold onto them
837
+ // Track textures in-use and new
852
838
  const usedTextures = [];
853
839
  const newTextures = [];
854
840
 
855
- // do we have a scalar color texture
841
+ // Add scalar color texture if available
856
842
  const idata = model.renderable.getColorTextureMap?.();
843
+ if (idata && !model.colorTexture) {
844
+ model.colorTexture = vtkTexture.newInstance({
845
+ label: 'polyDataColor'
846
+ });
847
+ }
857
848
  if (idata) {
858
- if (!model.colorTexture) {
859
- model.colorTexture = vtkTexture.newInstance({
860
- label: 'polyDataColor'
861
- });
862
- }
863
849
  model.colorTexture.setInputData(idata);
864
- newTextures.push(['Diffuse', model.colorTexture]);
850
+ newTextures.push(['DiffuseTexture', model.colorTexture]);
865
851
  }
866
-
867
- // actor textures?
868
852
  const actor = model.WebGPUActor.getRenderable();
869
853
  const renderer = model.WebGPURenderer.getRenderable();
870
-
871
- // Reusing the old code for new and old textures, just loading in from properties instead of actor.getTextures()
872
- const textures = [];
873
-
874
- // Feels like there should be a better way than individually adding all
875
- if (actor.getProperty().getDiffuseTexture?.()) {
876
- const pair = ['Diffuse', actor.getProperty().getDiffuseTexture()];
877
- textures.push(pair);
878
- }
879
- if (actor.getTextures()[0]) {
880
- const pair = ['Diffuse', actor.getTextures()[0]];
881
- textures.push(pair);
882
- }
883
- if (model.colorTexture) {
884
- const pair = ['Diffuse', model.colorTexture];
885
- textures.push(pair);
886
- }
887
- if (actor.getProperty().getORMTexture?.()) {
888
- const pair = ['ORM', actor.getProperty().getORMTexture()];
889
- textures.push(pair);
890
- }
891
- if (actor.getProperty().getRMTexture?.()) {
892
- const pair = ['RM', actor.getProperty().getRMTexture()];
893
- textures.push(pair);
894
- }
895
- if (actor.getProperty().getRoughnessTexture?.()) {
896
- const pair = ['Roughness', actor.getProperty().getRoughnessTexture()];
897
- textures.push(pair);
898
- }
899
- if (actor.getProperty().getMetallicTexture?.()) {
900
- const pair = ['Metallic', actor.getProperty().getMetallicTexture()];
901
- textures.push(pair);
902
- }
903
- if (actor.getProperty().getNormalTexture?.()) {
904
- const pair = ['Normal', actor.getProperty().getNormalTexture()];
905
- textures.push(pair);
906
- }
907
- if (actor.getProperty().getAmbientOcclusionTexture?.()) {
908
- const pair = ['AmbientOcclusion', actor.getProperty().getAmbientOcclusionTexture()];
909
- textures.push(pair);
910
- }
911
- if (actor.getProperty().getEmissionTexture?.()) {
912
- const pair = ['Emission', actor.getProperty().getEmissionTexture()];
913
- textures.push(pair);
914
- }
915
- if (renderer.getEnvironmentTexture?.()) {
916
- const pair = ['Environment', renderer.getEnvironmentTexture()];
917
- textures.push(pair);
918
- }
919
- for (let i = 0; i < textures.length; i++) {
920
- if (textures[i][1].getInputData() || textures[i][1].getJsImageData() || textures[i][1].getCanvas()) {
921
- newTextures.push(textures[i]);
854
+ const textures = [['DiffuseTexture', actor.getProperty().getDiffuseTexture?.()], ['DiffuseTexture', actor.getTextures()[0]], ['DiffuseTexture', model.colorTexture], ['ORMTexture', actor.getProperty().getORMTexture?.()], ['RMTexture', actor.getProperty().getRMTexture?.()], ['RoughnessTexture', actor.getProperty().getRoughnessTexture?.()], ['MetallicTexture', actor.getProperty().getMetallicTexture?.()], ['NormalTexture', actor.getProperty().getNormalTexture?.()], ['AmbientOcclusionTexture', actor.getProperty().getAmbientOcclusionTexture?.()], ['EmissionTexture', actor.getProperty().getEmissionTexture?.()], ['EnvironmentTexture', renderer.getEnvironmentTexture?.()]];
855
+ textures.forEach(_ref => {
856
+ let [name, tex] = _ref;
857
+ if (!tex) return;
858
+ if (tex.getInputData() || tex.getJsImageData() || tex.getCanvas() || tex.getImageBitmap()) {
859
+ newTextures.push([name, tex]);
922
860
  }
923
- if (textures[i][1].getImage() && textures[i][1].getImageLoaded()) {
924
- newTextures.push(textures[i]);
861
+ if (tex.getImage() && tex.getImageLoaded()) {
862
+ newTextures.push([name, tex]);
925
863
  }
926
- }
927
- for (let i = 0; i < newTextures.length; i++) {
928
- const srcTexture = newTextures[i][1];
929
- const textureName = newTextures[i][0];
930
- const newTex = model.device.getTextureManager().getTextureForVTKTexture(srcTexture); // Generates hash
931
- if (newTex.getReady()) {
932
- // is this a new texture
933
- let found = false;
934
- for (let t = 0; t < model.textures.length; t++) {
935
- if (model.textures[t] === newTex) {
936
- found = true;
937
- usedTextures[t] = true;
938
- }
864
+ });
865
+
866
+ // Add textures to manager only if not present
867
+ newTextures.forEach(_ref2 => {
868
+ let [textureName, srcTexture] = _ref2;
869
+ const newTex = model.device.getTextureManager().getTextureForVTKTexture(srcTexture, textureName);
870
+ if (!newTex.getReady()) return;
871
+ let found = false;
872
+ for (let t = 0; t < model.textures.length; ++t) {
873
+ if (model.textures[t] === newTex) {
874
+ found = true;
875
+ usedTextures[t] = true;
876
+ break;
939
877
  }
940
- if (!found) {
941
- usedTextures[model.textures.length] = true;
942
- const tview = newTex.createView(`${textureName}Texture`);
943
- model.textures.push(newTex);
944
- model.textureViews.push(tview);
945
- const interpolate = srcTexture.getInterpolate() ? 'linear' : 'nearest';
946
- let addressMode = null;
947
- if (!addressMode && srcTexture.getEdgeClamp() && srcTexture.getRepeat()) addressMode = 'mirror-repeat';
948
- if (!addressMode && srcTexture.getEdgeClamp()) addressMode = 'clamp-to-edge';
949
- if (!addressMode && srcTexture.getRepeat()) addressMode = 'repeat';
950
- if (textureName !== 'Environment') {
951
- tview.addSampler(model.device, {
952
- addressModeU: addressMode,
953
- addressModeV: addressMode,
954
- addressModeW: addressMode,
955
- minFilter: interpolate,
956
- magFilter: interpolate
957
- });
958
- } else {
959
- tview.addSampler(model.device, {
960
- addressModeU: 'repeat',
961
- addressModeV: 'clamp-to-edge',
962
- addressModeW: 'repeat',
963
- minFilter: interpolate,
964
- magFilter: interpolate,
965
- mipmapFilter: 'linear'
966
- });
967
- }
878
+ }
879
+ if (!found) {
880
+ usedTextures[model.textures.length] = true;
881
+ const tview = newTex.createView(textureName);
882
+ model.textures.push(newTex);
883
+ model.textureViews.push(tview);
884
+
885
+ // Sampler setup
886
+ const interpolate = srcTexture.getInterpolate() ? 'linear' : 'nearest';
887
+ let addressMode = null;
888
+ if (srcTexture.getEdgeClamp() && srcTexture.getRepeat()) addressMode = 'mirror-repeat';else if (srcTexture.getEdgeClamp()) addressMode = 'clamp-to-edge';else if (srcTexture.getRepeat()) addressMode = 'repeat';
889
+
890
+ // Handle environment texture separately
891
+ let options = {
892
+ addressModeU: addressMode,
893
+ addressModeV: addressMode,
894
+ addressModeW: addressMode,
895
+ minFilter: interpolate,
896
+ magFilter: interpolate
897
+ };
898
+ if (textureName === 'EnvironmentTexture') {
899
+ options = {
900
+ addressModeU: 'repeat',
901
+ addressModeV: 'clamp-to-edge',
902
+ addressModeW: 'repeat',
903
+ minFilter: interpolate,
904
+ magFilter: interpolate,
905
+ mipmapFilter: 'linear'
906
+ };
968
907
  }
908
+ tview.addSampler(model.device, options);
969
909
  }
970
- }
910
+ });
971
911
 
972
912
  // remove unused textures
973
913
  for (let i = model.textures.length - 1; i >= 0; i--) {
@@ -38,7 +38,8 @@ function vtkWebGPUPolyDataMapper(publicAPI, model) {
38
38
  // and they handle the rendering of that cell array
39
39
  const cellMappers = [];
40
40
  let cellOffset = 0;
41
- for (let i = PrimitiveTypes.Points; i <= PrimitiveTypes.Triangles; i++) {
41
+ // Handle all primitive types including strips
42
+ for (let i = PrimitiveTypes.Points; i <= PrimitiveTypes.TriangleStrips; i++) {
42
43
  if (prims[i].getNumberOfValues() > 0) {
43
44
  if (!model.primitives[i]) {
44
45
  model.primitives[i] = publicAPI.createCellArrayMapper();
@@ -55,22 +56,41 @@ function vtkWebGPUPolyDataMapper(publicAPI, model) {
55
56
  model.primitives[i] = null;
56
57
  }
57
58
  }
59
+
60
+ // Handle edge visibility for both triangles and triangle strips
58
61
  if (model.WebGPUActor.getRenderable().getProperty().getEdgeVisibility()) {
59
- for (let i = PrimitiveTypes.TriangleEdges; i <= PrimitiveTypes.TriangleStripEdges; i++) {
60
- if (prims[i - 2].getNumberOfValues() > 0) {
61
- if (!model.primitives[i]) {
62
- model.primitives[i] = publicAPI.createCellArrayMapper();
63
- }
64
- const cellMapper = model.primitives[i];
65
- cellMapper.setCellArray(prims[i - 2]);
66
- cellMapper.setCurrentInput(poly);
67
- cellMapper.setCellOffset(model.primitives[i - 2].getCellOffset());
68
- cellMapper.setPrimitiveType(i);
69
- cellMapper.setRenderable(model.renderable);
70
- cellMappers.push(cellMapper);
71
- } else {
72
- model.primitives[i] = null;
62
+ // Handle triangle edges
63
+ if (prims[PrimitiveTypes.Triangles].getNumberOfValues() > 0) {
64
+ const i = PrimitiveTypes.TriangleEdges;
65
+ if (!model.primitives[i]) {
66
+ model.primitives[i] = publicAPI.createCellArrayMapper();
67
+ }
68
+ const cellMapper = model.primitives[i];
69
+ cellMapper.setCellArray(prims[PrimitiveTypes.Triangles]);
70
+ cellMapper.setCurrentInput(poly);
71
+ cellMapper.setCellOffset(model.primitives[PrimitiveTypes.Triangles].getCellOffset());
72
+ cellMapper.setPrimitiveType(i);
73
+ cellMapper.setRenderable(model.renderable);
74
+ cellMappers.push(cellMapper);
75
+ } else {
76
+ model.primitives[PrimitiveTypes.TriangleEdges] = null;
77
+ }
78
+
79
+ // Handle triangle strip edges
80
+ if (prims[PrimitiveTypes.TriangleStrips].getNumberOfValues() > 0) {
81
+ const i = PrimitiveTypes.TriangleStripEdges;
82
+ if (!model.primitives[i]) {
83
+ model.primitives[i] = publicAPI.createCellArrayMapper();
73
84
  }
85
+ const cellMapper = model.primitives[i];
86
+ cellMapper.setCellArray(prims[PrimitiveTypes.TriangleStrips]);
87
+ cellMapper.setCurrentInput(poly);
88
+ cellMapper.setCellOffset(model.primitives[PrimitiveTypes.TriangleStrips].getCellOffset());
89
+ cellMapper.setPrimitiveType(i);
90
+ cellMapper.setRenderable(model.renderable);
91
+ cellMappers.push(cellMapper);
92
+ } else {
93
+ model.primitives[PrimitiveTypes.TriangleStripEdges] = null;
74
94
  }
75
95
  }
76
96
  publicAPI.prepareNodes();
@@ -315,7 +315,7 @@ function vtkWebGPURenderer(publicAPI, model) {
315
315
  ubo.addEntry('FSQMatrix', 'mat4x4<f32>');
316
316
  ubo.addEntry('BackgroundColor', 'vec4<f32>');
317
317
  model.clearFSQ.setUBO(ubo);
318
- const environmentTextureHash = device.getTextureManager().getTextureForVTKTexture(model.backgroundTex);
318
+ const environmentTextureHash = device.getTextureManager().getTextureForVTKTexture(model.backgroundTex, 'EnvironmentTexture');
319
319
  if (environmentTextureHash.getReady()) {
320
320
  const tview = environmentTextureHash.createView(`EnvironmentTexture`);
321
321
  model.clearFSQ.setTextureViews([tview]);