@kitware/vtk.js 33.3.2 → 34.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/BREAKING_CHANGES.md +10 -0
  2. package/IO/Geometry/GLTFImporter/Decoder.js +21 -35
  3. package/IO/Geometry/GLTFImporter/ORMTexture.worker.js +42 -0
  4. package/IO/Geometry/GLTFImporter/Parser.js +6 -15
  5. package/IO/Geometry/GLTFImporter/Reader.js +10 -7
  6. package/IO/Geometry/GLTFImporter/Utils.js +27 -9
  7. package/Interaction/Manipulators/KeyboardCameraManipulator.d.ts +113 -0
  8. package/Rendering/Core/Actor.d.ts +5 -20
  9. package/Rendering/Core/Actor.js +5 -68
  10. package/Rendering/Core/ImageCPRMapper.d.ts +1 -20
  11. package/Rendering/Core/ImageCPRMapper.js +1 -2
  12. package/Rendering/Core/ImageProperty.d.ts +20 -1
  13. package/Rendering/Core/ImageProperty.js +7 -5
  14. package/Rendering/Core/ImageResliceMapper.d.ts +1 -20
  15. package/Rendering/Core/ImageResliceMapper.js +1 -2
  16. package/Rendering/Core/ImageSlice.d.ts +7 -23
  17. package/Rendering/Core/ImageSlice.js +9 -68
  18. package/Rendering/Core/Prop3D.d.ts +39 -2
  19. package/Rendering/Core/Prop3D.js +81 -2
  20. package/Rendering/Core/RenderWindowInteractor.d.ts +6 -0
  21. package/Rendering/Core/RenderWindowInteractor.js +7 -5
  22. package/Rendering/Core/Volume.d.ts +5 -20
  23. package/Rendering/Core/Volume.js +2 -70
  24. package/Rendering/Core/VolumeMapper/Constants.d.ts +0 -7
  25. package/Rendering/Core/VolumeMapper/Constants.js +2 -8
  26. package/Rendering/Core/VolumeMapper.d.ts +16 -173
  27. package/Rendering/Core/VolumeMapper.js +16 -51
  28. package/Rendering/Core/VolumeProperty/Constants.d.ts +12 -3
  29. package/Rendering/Core/VolumeProperty/Constants.js +11 -4
  30. package/Rendering/Core/VolumeProperty.d.ts +140 -5
  31. package/Rendering/Core/VolumeProperty.js +54 -7
  32. package/Rendering/OpenGL/Framebuffer.js +7 -1
  33. package/Rendering/OpenGL/ImageCPRMapper.js +72 -27
  34. package/Rendering/OpenGL/ImageMapper.js +71 -33
  35. package/Rendering/OpenGL/ImageResliceMapper.js +306 -183
  36. package/Rendering/OpenGL/OrderIndependentTranslucentPass.js +20 -3
  37. package/Rendering/OpenGL/PolyDataMapper.js +8 -9
  38. package/Rendering/OpenGL/RenderWindow/resourceSharingHelper.d.ts +3 -3
  39. package/Rendering/OpenGL/RenderWindow/resourceSharingHelper.js +8 -5
  40. package/Rendering/OpenGL/SurfaceLIC/LineIntegralConvolution2D/pingpong.js +7 -1
  41. package/Rendering/OpenGL/SurfaceLIC/SurfaceLICInterface.js +20 -3
  42. package/Rendering/OpenGL/Texture.d.ts +110 -62
  43. package/Rendering/OpenGL/Texture.js +145 -37
  44. package/Rendering/OpenGL/VolumeMapper.js +763 -783
  45. package/Rendering/OpenGL/glsl/vtkVolumeFS.glsl.js +1 -1
  46. package/Rendering/WebGPU/CellArrayMapper.js +17 -17
  47. package/Rendering/WebGPU/PolyDataMapper.js +15 -35
  48. package/Rendering/WebGPU/Renderer.js +1 -1
  49. package/Rendering/WebGPU/Texture.js +12 -13
  50. package/Rendering/WebGPU/TextureManager.js +7 -12
  51. package/Rendering/WebGPU/VolumePassFSQ.js +2 -2
  52. package/_virtual/rollup-plugin-worker-loader__module_Sources/IO/Geometry/GLTFImporter/ORMTexture.worker.js +296 -0
  53. package/index.d.ts +1 -0
  54. package/macros.js +1 -1
  55. package/macros2.js +8 -3
  56. package/package.json +1 -1
@@ -13,7 +13,7 @@ import vtkReplacementShaderMapper from './ReplacementShaderMapper.js';
13
13
  import vtkShaderProgram from './ShaderProgram.js';
14
14
  import vtkTransform from '../../Common/Transform/Transform.js';
15
15
  import vtkViewNode from '../SceneGraph/ViewNode.js';
16
- import { getImageDataHash, getTransferFunctionHash } from './RenderWindow/resourceSharingHelper.js';
16
+ import { getImageDataHash, getTransferFunctionsHash } from './RenderWindow/resourceSharingHelper.js';
17
17
  import { v as vtkImageResliceMapperVS } from './glsl/vtkImageResliceMapperVS.glsl.js';
18
18
  import { v as vtkImageResliceMapperFS } from './glsl/vtkImageResliceMapperFS.glsl.js';
19
19
  import { Filter } from './Texture/Constants.js';
@@ -49,8 +49,43 @@ function safeMatrixMultiply(matrixArray, matrixType, tmpMat) {
49
49
  function vtkOpenGLImageResliceMapper(publicAPI, model) {
50
50
  // Set our className
51
51
  model.classHierarchy.push('vtkOpenGLImageResliceMapper');
52
+
53
+ // Associate a reference counter to each graphics resource
54
+ const graphicsResourceReferenceCount = new Map();
55
+ function decreaseGraphicsResourceCount(openGLRenderWindow, coreObject) {
56
+ if (!coreObject) {
57
+ return;
58
+ }
59
+ const oldCount = graphicsResourceReferenceCount.get(coreObject) ?? 0;
60
+ const newCount = oldCount - 1;
61
+ if (newCount <= 0) {
62
+ openGLRenderWindow.unregisterGraphicsResourceUser(coreObject, publicAPI);
63
+ graphicsResourceReferenceCount.delete(coreObject);
64
+ } else {
65
+ graphicsResourceReferenceCount.set(coreObject, newCount);
66
+ }
67
+ }
68
+ function increaseGraphicsResourceCount(openGLRenderWindow, coreObject) {
69
+ if (!coreObject) {
70
+ return;
71
+ }
72
+ const oldCount = graphicsResourceReferenceCount.get(coreObject) ?? 0;
73
+ const newCount = oldCount + 1;
74
+ graphicsResourceReferenceCount.set(coreObject, newCount);
75
+ if (oldCount <= 0) {
76
+ openGLRenderWindow.registerGraphicsResourceUser(coreObject, publicAPI);
77
+ }
78
+ }
79
+ function replaceGraphicsResource(openGLRenderWindow, oldResourceCoreObject, newResourceCoreObject) {
80
+ if (oldResourceCoreObject === newResourceCoreObject) {
81
+ return;
82
+ }
83
+ decreaseGraphicsResourceCount(openGLRenderWindow, oldResourceCoreObject);
84
+ increaseGraphicsResourceCount(openGLRenderWindow, newResourceCoreObject);
85
+ }
52
86
  function unregisterGraphicsResources(renderWindow) {
53
- [model._scalars, model._colorTransferFunc, model._pwFunc].forEach(coreObject => renderWindow.unregisterGraphicsResourceUser(coreObject, publicAPI));
87
+ // Convert to an array using the spread operator as Firefox doesn't support Iterator.forEach()
88
+ [...graphicsResourceReferenceCount.keys()].forEach(coreObject => renderWindow.unregisterGraphicsResourceUser(coreObject, publicAPI));
54
89
  }
55
90
  publicAPI.buildPass = prepass => {
56
91
  if (prepass) {
@@ -110,11 +145,28 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
110
145
  type: 'StartEvent'
111
146
  });
112
147
  model.renderable.update();
113
- model.currentInput = model.renderable.getInputData();
114
- if (!model.currentInput) {
148
+ const numberOfInputs = model.renderable.getNumberOfInputPorts();
149
+ model.currentValidInputs = [];
150
+ for (let inputIndex = 0; inputIndex < numberOfInputs; ++inputIndex) {
151
+ const imageData = model.renderable.getInputData(inputIndex);
152
+ if (imageData && !imageData.isDeleted()) {
153
+ model.currentValidInputs.push({
154
+ imageData,
155
+ inputIndex
156
+ });
157
+ }
158
+ }
159
+ const numberOfValidInputs = model.currentValidInputs.length;
160
+ if (numberOfValidInputs <= 0) {
115
161
  vtkErrorMacro('No input!');
116
162
  return;
117
163
  }
164
+
165
+ // Number of components
166
+ const firstImageData = model.currentValidInputs[0].imageData;
167
+ const firstScalars = firstImageData.getPointData().getScalars();
168
+ model.multiTexturePerVolumeEnabled = numberOfValidInputs > 1;
169
+ model.numberOfComponents = model.multiTexturePerVolumeEnabled ? numberOfValidInputs : firstScalars.getNumberOfComponents();
118
170
  publicAPI.updateResliceGeometry();
119
171
  publicAPI.renderPieceStart(ren, actor);
120
172
  publicAPI.renderPieceDraw(ren, actor);
@@ -126,17 +178,35 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
126
178
  publicAPI.renderPieceStart = (ren, actor) => {
127
179
  // make sure the BOs are up to date
128
180
  publicAPI.updateBufferObjects(ren, actor);
129
- const iType = actor.getProperty().getInterpolationType();
181
+
182
+ // Update filters for scalar textures
183
+ const actorProperties = actor.getProperties();
184
+ model.currentValidInputs.forEach(_ref => {
185
+ let {
186
+ inputIndex
187
+ } = _ref;
188
+ const actorProperty = actorProperties[inputIndex];
189
+ const interpolationType = actorProperty.getInterpolationType();
190
+ const scalarTexture = model.scalarTextures[inputIndex];
191
+ if (interpolationType === InterpolationType.NEAREST) {
192
+ scalarTexture.setMinificationFilter(Filter.NEAREST);
193
+ scalarTexture.setMagnificationFilter(Filter.NEAREST);
194
+ } else {
195
+ scalarTexture.setMinificationFilter(Filter.LINEAR);
196
+ scalarTexture.setMagnificationFilter(Filter.LINEAR);
197
+ }
198
+ });
199
+
200
+ // Update color and opacity texture filters
201
+ const firstValidInput = model.currentValidInputs[0];
202
+ const firstProperty = actorProperties[firstValidInput.inputIndex];
203
+ const iType = firstProperty.getInterpolationType();
130
204
  if (iType === InterpolationType.NEAREST) {
131
- model.openGLTexture.setMinificationFilter(Filter.NEAREST);
132
- model.openGLTexture.setMagnificationFilter(Filter.NEAREST);
133
205
  model.colorTexture.setMinificationFilter(Filter.NEAREST);
134
206
  model.colorTexture.setMagnificationFilter(Filter.NEAREST);
135
207
  model.pwfTexture.setMinificationFilter(Filter.NEAREST);
136
208
  model.pwfTexture.setMagnificationFilter(Filter.NEAREST);
137
209
  } else {
138
- model.openGLTexture.setMinificationFilter(Filter.LINEAR);
139
- model.openGLTexture.setMagnificationFilter(Filter.LINEAR);
140
210
  model.colorTexture.setMinificationFilter(Filter.LINEAR);
141
211
  model.colorTexture.setMagnificationFilter(Filter.LINEAR);
142
212
  model.pwfTexture.setMinificationFilter(Filter.LINEAR);
@@ -150,9 +220,8 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
150
220
  const gl = model.context;
151
221
 
152
222
  // render the texture
153
- model.openGLTexture.activate();
154
- model.colorTexture.activate();
155
- model.pwfTexture.activate();
223
+ const allTextures = [...model.scalarTextures, model.colorTexture, model.pwfTexture];
224
+ allTextures.forEach(texture => texture.activate());
156
225
 
157
226
  // update shaders if required
158
227
  publicAPI.updateShaders(model.tris, ren, actor);
@@ -160,9 +229,7 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
160
229
  // Finally draw
161
230
  gl.drawArrays(gl.TRIANGLES, 0, model.tris.getCABO().getElementCount());
162
231
  model.tris.getVAO().release();
163
- model.openGLTexture.deactivate();
164
- model.colorTexture.deactivate();
165
- model.pwfTexture.deactivate();
232
+ allTextures.forEach(texture => texture.deactivate());
166
233
  };
167
234
  publicAPI.renderPieceFinish = (ren, actor) => {};
168
235
  publicAPI.updateBufferObjects = (ren, actor) => {
@@ -171,57 +238,74 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
171
238
  publicAPI.buildBufferObjects(ren, actor);
172
239
  }
173
240
  };
174
- publicAPI.getNeedToRebuildBufferObjects = (ren, actor) => model.VBOBuildTime.getMTime() < publicAPI.getMTime() || model.VBOBuildTime.getMTime() < actor.getMTime() || model.VBOBuildTime.getMTime() < model.renderable.getMTime() || model.VBOBuildTime.getMTime() < actor.getProperty().getMTime() || model.VBOBuildTime.getMTime() < model.currentInput.getMTime() || model.VBOBuildTime.getMTime() < model.resliceGeom.getMTime() || !model.openGLTexture?.getHandle() || !model.colorTexture?.getHandle() || !model.pwfTexture?.getHandle();
241
+ publicAPI.getNeedToRebuildBufferObjects = (ren, actor) => model.VBOBuildTime.getMTime() < publicAPI.getMTime() || model.VBOBuildTime.getMTime() < actor.getMTime() || model.VBOBuildTime.getMTime() < model.renderable.getMTime() || model.VBOBuildTime.getMTime() < actor.getProperty(model.currentValidInputs[0].inputIndex)?.getMTime() || model.currentValidInputs.some(_ref2 => {
242
+ let {
243
+ imageData
244
+ } = _ref2;
245
+ return model.VBOBuildTime.getMTime() < imageData.getMTime();
246
+ }) || model.VBOBuildTime.getMTime() < model.resliceGeom.getMTime() || model.scalarTextures.length !== model.currentValidInputs.length || !model.scalarTextures.every(texture => !!texture?.getHandle()) || !model.colorTexture?.getHandle() || !model.pwfTexture?.getHandle();
175
247
  publicAPI.buildBufferObjects = (ren, actor) => {
176
- const image = model.currentInput;
177
- if (!image) {
178
- return;
179
- }
180
- const scalars = image.getPointData()?.getScalars();
181
- if (!scalars) {
182
- return;
183
- }
184
- const numComp = scalars.getNumberOfComponents();
185
- let toString = getImageDataHash(image, scalars);
186
- const tex = model._openGLRenderWindow.getGraphicsResourceForObject(scalars);
187
- const reBuildTex = !tex?.oglObject?.getHandle() || tex?.hash !== toString;
188
- const updatedExtents = model.renderable.getUpdatedExtents();
189
- const hasUpdatedExtents = !!updatedExtents.length;
190
- if (reBuildTex && !hasUpdatedExtents) {
191
- model.openGLTexture = vtkOpenGLTexture.newInstance();
192
- model.openGLTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
193
- // Build the image scalar texture
194
- // Use norm16 for the 3D texture if the extension is available
195
- model.openGLTexture.setOglNorm16Ext(model.context.getExtension('EXT_texture_norm16'));
196
- model.openGLTexture.resetFormatAndType();
197
-
198
- // Build the image scalar texture
199
- const dims = image.getDimensions();
200
- model.openGLTexture.create3DFilterableFromDataArray(dims[0], dims[1], dims[2], scalars, false, updatedExtents);
201
- model._openGLRenderWindow.setGraphicsResourceForObject(scalars, model.openGLTexture, toString);
202
- if (scalars !== model._scalars) {
203
- model._openGLRenderWindow.registerGraphicsResourceUser(scalars, publicAPI);
204
- model._openGLRenderWindow.unregisterGraphicsResourceUser(model._scalars, publicAPI);
205
- }
206
- model._scalars = scalars;
207
- } else {
208
- model.openGLTexture = tex.oglObject;
209
- }
210
- if (hasUpdatedExtents) {
211
- // If hasUpdatedExtents, then the texture is partially updated.
212
- // clear the array to acknowledge the update.
213
- model.renderable.setUpdatedExtents([]);
214
- const dims = image.getDimensions();
215
- model.openGLTexture.create3DFilterableFromDataArray(dims[0], dims[1], dims[2], scalars, false, updatedExtents);
216
- }
217
- const ppty = actor.getProperty();
218
- const iComps = ppty.getIndependentComponents();
219
- const numIComps = iComps ? numComp : 1;
248
+ const actorProperties = actor.getProperties();
249
+ model.currentValidInputs.forEach((_ref3, component) => {
250
+ let {
251
+ imageData
252
+ } = _ref3;
253
+ // rebuild the scalarTexture if the data has changed
254
+ const scalars = imageData.getPointData().getScalars();
255
+ const tex = model._openGLRenderWindow.getGraphicsResourceForObject(scalars);
256
+ const scalarsHash = getImageDataHash(imageData, scalars);
257
+ const reBuildTex = !tex?.oglObject?.getHandle() || tex?.hash !== scalarsHash;
258
+ const actorProperty = actorProperties[component];
259
+ const updatedExtents = actorProperty.getUpdatedExtents();
260
+ const hasUpdatedExtents = !!updatedExtents.length;
261
+ if (reBuildTex && !hasUpdatedExtents) {
262
+ const newScalarTexture = vtkOpenGLTexture.newInstance();
263
+ newScalarTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
264
+ // Build the textures
265
+ const dims = imageData.getDimensions();
266
+ // Use norm16 for scalar texture if the extension is available
267
+ newScalarTexture.setOglNorm16Ext(model.context.getExtension('EXT_texture_norm16'));
268
+ newScalarTexture.resetFormatAndType();
269
+ newScalarTexture.create3DFilterableFromDataArray({
270
+ width: dims[0],
271
+ height: dims[1],
272
+ depth: dims[2],
273
+ dataArray: scalars
274
+ });
275
+ model._openGLRenderWindow.setGraphicsResourceForObject(scalars, newScalarTexture, scalarsHash);
276
+ model.scalarTextures[component] = newScalarTexture;
277
+ } else {
278
+ model.scalarTextures[component] = tex.oglObject;
279
+ }
280
+ if (hasUpdatedExtents) {
281
+ // If hasUpdatedExtents, then the texture is partially updated.
282
+ // clear the array to acknowledge the update.
283
+ actorProperty.setUpdatedExtents([]);
284
+ const dims = imageData.getDimensions();
285
+ model.scalarTextures[component].create3DFilterableFromDataArray({
286
+ width: dims[0],
287
+ height: dims[1],
288
+ depth: dims[2],
289
+ dataArray: scalars,
290
+ updatedExtents
291
+ });
292
+ }
293
+ replaceGraphicsResource(model._openGLRenderWindow, model._scalarTexturesCore[component], scalars);
294
+ model._scalarTexturesCore[component] = scalars;
295
+ });
296
+ const firstValidInput = model.currentValidInputs[0];
297
+ const firstActorProperty = actorProperties[firstValidInput.inputIndex];
298
+ const iComps = firstActorProperty.getIndependentComponents();
299
+ const numIComps = iComps ? model.numberOfComponents : 1;
220
300
  const textureHeight = iComps ? 2 * numIComps : 1;
221
- const colorTransferFunc = ppty.getRGBTransferFunction();
222
- toString = getTransferFunctionHash(colorTransferFunc, iComps, numIComps);
223
- const cTex = model._openGLRenderWindow.getGraphicsResourceForObject(colorTransferFunc);
224
- const reBuildC = !cTex?.oglObject?.getHandle() || cTex?.hash !== toString;
301
+ const colorTransferFunctions = [];
302
+ for (let component = 0; component < numIComps; ++component) {
303
+ colorTransferFunctions.push(firstActorProperty.getRGBTransferFunction(component));
304
+ }
305
+ const colorFuncHash = getTransferFunctionsHash(colorTransferFunctions, iComps, numIComps);
306
+ const firstColorTransferFunc = firstActorProperty.getRGBTransferFunction();
307
+ const cTex = model._openGLRenderWindow.getGraphicsResourceForObject(firstColorTransferFunc);
308
+ const reBuildC = !cTex?.oglObject?.getHandle() || cTex?.hash !== colorFuncHash;
225
309
  if (reBuildC) {
226
310
  let cWidth = model.renderable.getColorTextureWidth();
227
311
  if (cWidth <= 0) {
@@ -229,12 +313,12 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
229
313
  }
230
314
  const cSize = cWidth * textureHeight * 3;
231
315
  const cTable = new Uint8ClampedArray(cSize);
232
- model.colorTexture = vtkOpenGLTexture.newInstance();
233
- model.colorTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
234
- if (colorTransferFunc) {
316
+ const newColorTexture = vtkOpenGLTexture.newInstance();
317
+ newColorTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
318
+ if (firstColorTransferFunc) {
235
319
  const tmpTable = new Float32Array(cWidth * 3);
236
320
  for (let c = 0; c < numIComps; c++) {
237
- const cfun = ppty.getRGBTransferFunction(c);
321
+ const cfun = firstActorProperty.getRGBTransferFunction(c);
238
322
  const cRange = cfun.getRange();
239
323
  cfun.getTable(cRange[0], cRange[1], cWidth, tmpTable, 1);
240
324
  if (iComps) {
@@ -244,41 +328,58 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
244
328
  }
245
329
  } else {
246
330
  for (let i = 0; i < cWidth * 3; i++) {
247
- cTable[c * cWidth * 6 + i] = 255.0 * tmpTable[i];
331
+ cTable[c * cWidth * 3 + i] = 255.0 * tmpTable[i];
248
332
  }
249
333
  }
250
334
  }
251
- model.colorTexture.resetFormatAndType();
252
- model.colorTexture.create2DFromRaw(cWidth, textureHeight, 3, VtkDataTypes.UNSIGNED_CHAR, cTable);
335
+ newColorTexture.resetFormatAndType();
336
+ newColorTexture.create2DFromRaw({
337
+ width: cWidth,
338
+ height: textureHeight,
339
+ numComps: 3,
340
+ dataType: VtkDataTypes.UNSIGNED_CHAR,
341
+ data: cTable
342
+ });
253
343
  } else {
254
- for (let i = 0; i < cWidth * 3; ++i) {
255
- cTable[i] = 255.0 * i / ((cWidth - 1) * 3);
256
- cTable[i + 1] = 255.0 * i / ((cWidth - 1) * 3);
257
- cTable[i + 2] = 255.0 * i / ((cWidth - 1) * 3);
258
- }
259
- model.colorTexture.resetFormatAndType();
260
- model.colorTexture.create2DFromRaw(cWidth, 1, 3, VtkDataTypes.UNSIGNED_CHAR, cTable);
261
- }
262
- if (colorTransferFunc) {
263
- model._openGLRenderWindow.setGraphicsResourceForObject(colorTransferFunc, model.colorTexture, toString);
264
- if (colorTransferFunc !== model._colorTransferFunc) {
265
- model._openGLRenderWindow.registerGraphicsResourceUser(colorTransferFunc, publicAPI);
266
- model._openGLRenderWindow.unregisterGraphicsResourceUser(model._colorTransferFunc, publicAPI);
344
+ for (let column = 0; column < cWidth * 3; ++column) {
345
+ const opacity = 255.0 * column / ((cWidth - 1) * 3);
346
+ for (let row = 0; row < textureHeight; ++row) {
347
+ // R, G, B
348
+ cTable[row * cWidth * 3 + column + 0] = opacity;
349
+ cTable[row * cWidth * 3 + column + 1] = opacity;
350
+ cTable[row * cWidth * 3 + column + 2] = opacity;
351
+ }
267
352
  }
268
- model._colorTransferFunc = colorTransferFunc;
353
+ newColorTexture.resetFormatAndType();
354
+ newColorTexture.create2DFromRaw({
355
+ width: cWidth,
356
+ height: 1,
357
+ numComps: 3,
358
+ dataType: VtkDataTypes.UNSIGNED_CHAR,
359
+ data: cTable
360
+ });
361
+ }
362
+ if (firstColorTransferFunc) {
363
+ model._openGLRenderWindow.setGraphicsResourceForObject(firstColorTransferFunc, newColorTexture, colorFuncHash);
269
364
  }
365
+ model.colorTexture = newColorTexture;
270
366
  } else {
271
367
  model.colorTexture = cTex.oglObject;
272
368
  }
369
+ replaceGraphicsResource(model._openGLRenderWindow, model._colorTextureCore, firstColorTransferFunc);
370
+ model._colorTextureCore = firstColorTransferFunc;
273
371
 
274
372
  // Build piecewise function buffer. This buffer is used either
275
373
  // for component weighting or opacity, depending on whether we're
276
374
  // rendering components independently or not.
277
- const pwFunc = ppty.getPiecewiseFunction();
278
- toString = getTransferFunctionHash(pwFunc, iComps, numIComps);
279
- const pwfTex = model._openGLRenderWindow.getGraphicsResourceForObject(pwFunc);
280
- // rebuild opacity tfun?
281
- const reBuildPwf = !pwfTex?.oglObject?.getHandle() || pwfTex?.hash !== toString;
375
+ const opacityFunctions = [];
376
+ for (let component = 0; component < numIComps; ++component) {
377
+ opacityFunctions.push(firstActorProperty.getPiecewiseFunction(component));
378
+ }
379
+ const opacityFuncHash = getTransferFunctionsHash(opacityFunctions, iComps, numIComps);
380
+ const firstPwFunc = firstActorProperty.getPiecewiseFunction();
381
+ const pwfTex = model._openGLRenderWindow.getGraphicsResourceForObject(firstPwFunc);
382
+ const reBuildPwf = !pwfTex?.oglObject?.getHandle() || pwfTex?.hash !== opacityFuncHash;
282
383
  if (reBuildPwf) {
283
384
  let pwfWidth = model.renderable.getOpacityTextureWidth();
284
385
  if (pwfWidth <= 0) {
@@ -286,13 +387,13 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
286
387
  }
287
388
  const pwfSize = pwfWidth * textureHeight;
288
389
  const pwfTable = new Uint8ClampedArray(pwfSize);
289
- model.pwfTexture = vtkOpenGLTexture.newInstance();
290
- model.pwfTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
291
- if (pwFunc) {
390
+ const newOpacityTexture = vtkOpenGLTexture.newInstance();
391
+ newOpacityTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
392
+ if (firstPwFunc) {
292
393
  const pwfFloatTable = new Float32Array(pwfSize);
293
394
  const tmpTable = new Float32Array(pwfWidth);
294
395
  for (let c = 0; c < numIComps; ++c) {
295
- const pwfun = ppty.getPiecewiseFunction(c);
396
+ const pwfun = firstActorProperty.getPiecewiseFunction(c);
296
397
  if (pwfun === null) {
297
398
  // Piecewise constant max if no function supplied for this component
298
399
  pwfFloatTable.fill(1.0);
@@ -307,30 +408,40 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
307
408
  }
308
409
  } else {
309
410
  for (let i = 0; i < pwfWidth; i++) {
310
- pwfFloatTable[c * pwfWidth * 2 + i] = tmpTable[i];
411
+ pwfFloatTable[i] = tmpTable[i];
311
412
  }
312
413
  }
313
414
  }
314
415
  }
315
- model.pwfTexture.resetFormatAndType();
316
- model.pwfTexture.create2DFromRaw(pwfWidth, textureHeight, 1, VtkDataTypes.FLOAT, pwfFloatTable);
416
+ newOpacityTexture.resetFormatAndType();
417
+ newOpacityTexture.create2DFromRaw({
418
+ width: pwfWidth,
419
+ height: textureHeight,
420
+ numComps: 1,
421
+ dataType: VtkDataTypes.FLOAT,
422
+ data: pwfFloatTable
423
+ });
317
424
  } else {
318
425
  // default is opaque
319
426
  pwfTable.fill(255.0);
320
- model.pwfTexture.resetFormatAndType();
321
- model.pwfTexture.create2DFromRaw(pwfWidth, 1, 1, VtkDataTypes.UNSIGNED_CHAR, pwfTable);
322
- }
323
- if (pwFunc) {
324
- model._openGLRenderWindow.setGraphicsResourceForObject(pwFunc, model.pwfTexture, toString);
325
- if (pwFunc !== model._pwFunc) {
326
- model._openGLRenderWindow.registerGraphicsResourceUser(pwFunc, publicAPI);
327
- model._openGLRenderWindow.unregisterGraphicsResourceUser(model._pwFunc, publicAPI);
328
- }
329
- model._pwFunc = pwFunc;
427
+ newOpacityTexture.resetFormatAndType();
428
+ newOpacityTexture.create2DFromRaw({
429
+ width: pwfWidth,
430
+ height: textureHeight,
431
+ numComps: 1,
432
+ dataType: VtkDataTypes.UNSIGNED_CHAR,
433
+ data: pwfTable
434
+ });
330
435
  }
436
+ if (firstPwFunc) {
437
+ model._openGLRenderWindow.setGraphicsResourceForObject(firstPwFunc, newOpacityTexture, opacityFuncHash);
438
+ }
439
+ model.pwfTexture = newOpacityTexture;
331
440
  } else {
332
441
  model.pwfTexture = pwfTex.oglObject;
333
442
  }
443
+ replaceGraphicsResource(model._openGLRenderWindow, model._pwfTextureCore, firstPwFunc);
444
+ model._pwfTextureCore = firstPwFunc;
334
445
  const vboString = `${model.resliceGeom.getMTime()}A${model.renderable.getSlabThickness()}`;
335
446
  if (!model.tris.getCABO().getElementCount() || model.VBOBuildString !== vboString) {
336
447
  const points = vtkDataArray.newInstance({
@@ -391,11 +502,12 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
391
502
  };
392
503
  publicAPI.setMapperShaderParameters = (cellBO, ren, actor) => {
393
504
  const program = cellBO.getProgram();
505
+ const firstImageData = model.currentValidInputs[0].imageData;
394
506
  if (cellBO.getCABO().getElementCount() && (model.VBOBuildTime.getMTime() > cellBO.getAttributeUpdateTime().getMTime() || cellBO.getShaderSourceTime().getMTime() > cellBO.getAttributeUpdateTime().getMTime())) {
395
507
  // Set the 3D texture
396
- if (program.isUniformUsed('texture1')) {
397
- program.setUniformi('texture1', model.openGLTexture.getTextureUnit());
398
- }
508
+ model.scalarTextures.forEach((scalarTexture, component) => {
509
+ program.setUniformi(`volumeTexture[${component}]`, scalarTexture.getTextureUnit());
510
+ });
399
511
 
400
512
  // Set the plane vertex attributes
401
513
  if (program.isAttributeUsed('vertexWC')) {
@@ -414,7 +526,7 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
414
526
  program.setUniformf('slabThickness', model.renderable.getSlabThickness());
415
527
  }
416
528
  if (program.isUniformUsed('spacing')) {
417
- program.setUniform3fv('spacing', model.currentInput.getSpacing());
529
+ program.setUniform3fv('spacing', firstImageData.getSpacing());
418
530
  }
419
531
  if (program.isUniformUsed('slabType')) {
420
532
  program.setUniformi('slabType', model.renderable.getSlabType());
@@ -430,9 +542,8 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
430
542
 
431
543
  // Set the world->texture matrix
432
544
  if (program.isUniformUsed('WCTCMatrix')) {
433
- const image = model.currentInput;
434
- const dim = image.getDimensions();
435
- mat4.copy(model.tmpMat4, image.getIndexToWorld());
545
+ const dim = firstImageData.getDimensions();
546
+ mat4.copy(model.tmpMat4, firstImageData.getIndexToWorld());
436
547
  mat4.translate(model.tmpMat4, model.tmpMat4, [-0.5, -0.5, -0.5]);
437
548
  mat4.scale(model.tmpMat4, model.tmpMat4, dim);
438
549
  mat4.invert(model.tmpMat4, model.tmpMat4);
@@ -481,8 +592,8 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
481
592
  };
482
593
  publicAPI.setPropertyShaderParameters = (cellBO, ren, actor) => {
483
594
  const program = cellBO.getProgram();
484
- const ppty = actor.getProperty();
485
- const opacity = ppty.getOpacity();
595
+ const firstPpty = actor.getProperty(model.currentValidInputs[0].inputIndex);
596
+ const opacity = firstPpty.getOpacity();
486
597
  program.setUniformf('opacity', opacity);
487
598
 
488
599
  // Component mix
@@ -491,53 +602,56 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
491
602
  // - 2 comps => LA
492
603
  // - 3 comps => RGB + opacity from pwf
493
604
  // - 4 comps => RGBA
494
- const numComp = model.openGLTexture.getComponents();
495
- const iComps = ppty.getIndependentComponents();
605
+ const numComp = model.numberOfComponents;
606
+ const iComps = firstPpty.getIndependentComponents();
496
607
  if (iComps) {
497
608
  for (let i = 0; i < numComp; ++i) {
498
- program.setUniformf(`mix${i}`, ppty.getComponentWeight(i));
609
+ program.setUniformf(`mix${i}`, firstPpty.getComponentWeight(i));
499
610
  }
500
611
  }
501
612
 
502
- // Color opacity map
503
- const volInfo = model.openGLTexture.getVolumeInfo();
504
-
505
613
  // three levels of shift scale combined into one
506
614
  // for performance in the fragment shader
507
- for (let i = 0; i < numComp; i++) {
508
- let cw = ppty.getColorWindow();
509
- let cl = ppty.getColorLevel();
510
- const target = iComps ? i : 0;
511
- const cfun = ppty.getRGBTransferFunction(target);
512
- if (cfun && ppty.getUseLookupTableScalarRange()) {
615
+ for (let component = 0; component < numComp; component++) {
616
+ const useMultiTexture = model.multiTexturePerVolumeEnabled;
617
+ const textureIndex = useMultiTexture ? component : 0;
618
+ const volInfoIndex = useMultiTexture ? 0 : component;
619
+ const scalarTexture = model.scalarTextures[textureIndex];
620
+ const volInfo = scalarTexture.getVolumeInfo();
621
+ const volScale = volInfo.scale[volInfoIndex];
622
+ const volOffset = volInfo.offset[volInfoIndex];
623
+ const target = iComps ? component : 0;
624
+
625
+ // color shift/scale
626
+ let cw = firstPpty.getColorWindow();
627
+ let cl = firstPpty.getColorLevel();
628
+ const cfun = firstPpty.getRGBTransferFunction(target);
629
+ if (cfun && firstPpty.getUseLookupTableScalarRange()) {
513
630
  const cRange = cfun.getRange();
514
631
  cw = cRange[1] - cRange[0];
515
632
  cl = 0.5 * (cRange[1] + cRange[0]);
516
633
  }
517
- const scale = volInfo.scale[i] / cw;
518
- const shift = (volInfo.offset[i] - cl) / cw + 0.5;
519
- program.setUniformf(`cshift${i}`, shift);
520
- program.setUniformf(`cscale${i}`, scale);
521
- }
522
- const texColorUnit = model.colorTexture.getTextureUnit();
523
- program.setUniformi('colorTexture1', texColorUnit);
634
+ const colorScale = volScale / cw;
635
+ const colorShift = (volOffset - cl) / cw + 0.5;
636
+ program.setUniformf(`cshift${component}`, colorShift);
637
+ program.setUniformf(`cscale${component}`, colorScale);
524
638
 
525
- // pwf shift/scale
526
- for (let i = 0; i < numComp; i++) {
639
+ // pwf shift/scale
527
640
  let pwfScale = 1.0;
528
641
  let pwfShift = 0.0;
529
- const target = iComps ? i : 0;
530
- const pwfun = ppty.getPiecewiseFunction(target);
642
+ const pwfun = firstPpty.getPiecewiseFunction(target);
531
643
  if (pwfun) {
532
644
  const pwfRange = pwfun.getRange();
533
645
  const length = pwfRange[1] - pwfRange[0];
534
646
  const mid = 0.5 * (pwfRange[0] + pwfRange[1]);
535
- pwfScale = volInfo.scale[i] / length;
536
- pwfShift = (volInfo.offset[i] - mid) / length + 0.5;
647
+ pwfScale = volScale / length;
648
+ pwfShift = (volOffset - mid) / length + 0.5;
537
649
  }
538
- program.setUniformf(`pwfshift${i}`, pwfShift);
539
- program.setUniformf(`pwfscale${i}`, pwfScale);
650
+ program.setUniformf(`pwfshift${component}`, pwfShift);
651
+ program.setUniformf(`pwfscale${component}`, pwfScale);
540
652
  }
653
+ const texColorUnit = model.colorTexture.getTextureUnit();
654
+ program.setUniformi('colorTexture1', texColorUnit);
541
655
  const texOpacityUnit = model.pwfTexture.getTextureUnit();
542
656
  program.setUniformi('pwfTexture1', texOpacityUnit);
543
657
 
@@ -551,8 +665,7 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
551
665
  // input modified
552
666
  // light complexity changed
553
667
  // render pass shader replacement changed
554
- const tNumComp = model.openGLTexture.getComponents();
555
- const iComp = actor.getProperty().getIndependentComponents();
668
+ const iComp = actor.getProperty(model.currentValidInputs[0].inputIndex).getIndependentComponents();
556
669
  const slabTh = model.renderable.getSlabThickness();
557
670
  const slabType = model.renderable.getSlabType();
558
671
  const slabTrap = model.renderable.getSlabTrapezoidIntegration();
@@ -562,9 +675,10 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
562
675
  if (!model.currentRenderPass && model.lastRenderPassShaderReplacement || model.currentRenderPass && model.currentRenderPass.getShaderReplacement() !== model.lastRenderPassShaderReplacement) {
563
676
  needRebuild = true;
564
677
  }
565
- if (needRebuild || model.lastHaveSeenDepthRequest !== model.haveSeenDepthRequest || cellBO.getProgram()?.getHandle() === 0 || model.lastTextureComponents !== tNumComp || model.lastIndependentComponents !== iComp || model.lastSlabThickness !== slabTh || model.lastSlabType !== slabType || model.lastSlabTrapezoidIntegration !== slabTrap) {
678
+ if (needRebuild || model.lastHaveSeenDepthRequest !== model.haveSeenDepthRequest || model.lastNumberOfComponents !== model.numberOfComponents || model.lastMultiTexturePerVolumeEnabled !== model.multiTexturePerVolumeEnabled || cellBO.getProgram()?.getHandle() === 0 || model.lastIndependentComponents !== iComp || model.lastSlabThickness !== slabTh || model.lastSlabType !== slabType || model.lastSlabTrapezoidIntegration !== slabTrap) {
566
679
  model.lastHaveSeenDepthRequest = model.haveSeenDepthRequest;
567
- model.lastTextureComponents = tNumComp;
680
+ model.lastNumberOfComponents = model.numberOfComponents;
681
+ model.lastMultiTexturePerVolumeEnabled = model.multiTexturePerVolumeEnabled;
568
682
  model.lastIndependentComponents = iComp;
569
683
  model.lastSlabThickness = slabTh;
570
684
  model.lastSlabType = slabType;
@@ -598,9 +712,9 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
598
712
  VSSource = vtkShaderProgram.substitute(VSSource, '//VTK::TCoord::Dec', tcoordVSDec).result;
599
713
  const tcoordVSImpl = ['fragTexCoord = (WCTCMatrix * vertexWC).xyz;'];
600
714
  VSSource = vtkShaderProgram.substitute(VSSource, '//VTK::TCoord::Impl', tcoordVSImpl).result;
601
- const tNumComp = model.openGLTexture.getComponents();
602
- const iComps = actor.getProperty().getIndependentComponents();
603
- let tcoordFSDec = ['in vec3 fragTexCoord;', 'uniform highp sampler3D texture1;', 'uniform mat4 WCTCMatrix;',
715
+ const tNumComp = model.numberOfComponents;
716
+ const iComps = actor.getProperty(model.currentValidInputs[0].inputIndex).getIndependentComponents();
717
+ let tcoordFSDec = ['in vec3 fragTexCoord;', `uniform highp sampler3D volumeTexture[${model.scalarTextures.length}];`, 'uniform mat4 WCTCMatrix;',
604
718
  // color shift and scale
605
719
  'uniform float cshift0;', 'uniform float cscale0;',
606
720
  // pwf shift and scale
@@ -611,6 +725,18 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
611
725
  'uniform float opacity;',
612
726
  // background color
613
727
  'uniform vec4 backgroundColor;'];
728
+
729
+ // Function to sample texture
730
+ tcoordFSDec.push('vec4 rawSampleTexture(vec3 pos) {');
731
+ if (!model.multiTexturePerVolumeEnabled) {
732
+ tcoordFSDec.push('return texture(volumeTexture[0], pos);', '}');
733
+ } else {
734
+ tcoordFSDec.push('vec4 rawSample;');
735
+ for (let component = 0; component < model.scalarTextures.length; ++component) {
736
+ tcoordFSDec.push(`rawSample[${component}] = texture(volumeTexture[${component}], pos)[0];`);
737
+ }
738
+ tcoordFSDec.push('return rawSample;', '}');
739
+ }
614
740
  if (iComps) {
615
741
  for (let comp = 1; comp < tNumComp; comp++) {
616
742
  tcoordFSDec = tcoordFSDec.concat([
@@ -646,14 +772,14 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
646
772
  tcoordFSDec = tcoordFSDec.concat(['vec4 compositeValue(vec4 currVal, vec4 valToComp, int trapezoid)', '{', ' vec4 retVal = vec4(1.0);', ' if (slabType == 0) // min', ' {', ' retVal = min(currVal, valToComp);', ' }', ' else if (slabType == 1) // max', ' {', ' retVal = max(currVal, valToComp);', ' }', ' else if (slabType == 3) // sum', ' {', ' retVal = currVal + (trapezoid > 0 ? 0.5 * valToComp : valToComp); ', ' }', ' else // mean', ' {', ' retVal = currVal + (trapezoid > 0 ? 0.5 * valToComp : valToComp); ', ' }', ' return retVal;', '}']);
647
773
  }
648
774
  FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::TCoord::Dec', tcoordFSDec).result;
649
- let tcoordFSImpl = ['if (any(greaterThan(fragTexCoord, vec3(1.0))) || any(lessThan(fragTexCoord, vec3(0.0))))', '{', ' // set the background color and exit', ' gl_FragData[0] = backgroundColor;', ' return;', '}', 'vec4 tvalue = texture(texture1, fragTexCoord);'];
775
+ let tcoordFSImpl = ['if (any(greaterThan(fragTexCoord, vec3(1.0))) || any(lessThan(fragTexCoord, vec3(0.0))))', '{', ' // set the background color and exit', ' gl_FragData[0] = backgroundColor;', ' return;', '}', 'vec4 tvalue = rawSampleTexture(fragTexCoord);'];
650
776
  if (slabThickness > 0.0) {
651
- tcoordFSImpl = tcoordFSImpl.concat(['// Get the first and last samples', 'int numSlices = 1;', 'float scaling = min(min(spacing.x, spacing.y), spacing.z) * 0.5;', 'vec3 normalxspacing = scaling * normalWCVSOutput;', 'float distTraveled = length(normalxspacing);', 'int trapezoid = 0;', 'while (distTraveled < slabThickness * 0.5)', '{', ' distTraveled += length(normalxspacing);', ' float fnumSlices = float(numSlices);', ' if (distTraveled > slabThickness * 0.5)', ' {', ' // Before stepping outside the slab, sample at the boundaries', ' normalxspacing = normalWCVSOutput * slabThickness * 0.5 / fnumSlices;', ' trapezoid = slabTrapezoid;', ' }', ' vec3 fragTCoordNeg = (WCTCMatrix * vec4(vertexWCVSOutput.xyz - fnumSlices * normalxspacing * vboScaling, 1.0)).xyz;', ' if (!any(greaterThan(fragTCoordNeg, vec3(1.0))) && !any(lessThan(fragTCoordNeg, vec3(0.0))))', ' {', ' vec4 newVal = texture(texture1, fragTCoordNeg);', ' tvalue = compositeValue(tvalue, newVal, trapezoid);', ' numSlices += 1;', ' }', ' vec3 fragTCoordPos = (WCTCMatrix * vec4(vertexWCVSOutput.xyz + fnumSlices * normalxspacing * vboScaling, 1.0)).xyz;', ' if (!any(greaterThan(fragTCoordNeg, vec3(1.0))) && !any(lessThan(fragTCoordNeg, vec3(0.0))))', ' {', ' vec4 newVal = texture(texture1, fragTCoordPos);', ' tvalue = compositeValue(tvalue, newVal, trapezoid);', ' numSlices += 1;', ' }', '}', '// Finally, if slab type is *mean*, divide the sum by the numSlices', 'if (slabType == 2)', '{', ' tvalue = tvalue / float(numSlices);', '}']);
777
+ tcoordFSImpl = tcoordFSImpl.concat(['// Get the first and last samples', 'int numSlices = 1;', 'float scaling = min(min(spacing.x, spacing.y), spacing.z) * 0.5;', 'vec3 normalxspacing = scaling * normalWCVSOutput;', 'float distTraveled = length(normalxspacing);', 'int trapezoid = 0;', 'while (distTraveled < slabThickness * 0.5)', '{', ' distTraveled += length(normalxspacing);', ' float fnumSlices = float(numSlices);', ' if (distTraveled > slabThickness * 0.5)', ' {', ' // Before stepping outside the slab, sample at the boundaries', ' normalxspacing = normalWCVSOutput * slabThickness * 0.5 / fnumSlices;', ' trapezoid = slabTrapezoid;', ' }', ' vec3 fragTCoordNeg = (WCTCMatrix * vec4(vertexWCVSOutput.xyz - fnumSlices * normalxspacing * vboScaling, 1.0)).xyz;', ' if (!any(greaterThan(fragTCoordNeg, vec3(1.0))) && !any(lessThan(fragTCoordNeg, vec3(0.0))))', ' {', ' vec4 newVal = rawSampleTexture(fragTCoordNeg);', ' tvalue = compositeValue(tvalue, newVal, trapezoid);', ' numSlices += 1;', ' }', ' vec3 fragTCoordPos = (WCTCMatrix * vec4(vertexWCVSOutput.xyz + fnumSlices * normalxspacing * vboScaling, 1.0)).xyz;', ' if (!any(greaterThan(fragTCoordNeg, vec3(1.0))) && !any(lessThan(fragTCoordNeg, vec3(0.0))))', ' {', ' vec4 newVal = rawSampleTexture(fragTCoordPos);', ' tvalue = compositeValue(tvalue, newVal, trapezoid);', ' numSlices += 1;', ' }', '}', '// Finally, if slab type is *mean*, divide the sum by the numSlices', 'if (slabType == 2)', '{', ' tvalue = tvalue / float(numSlices);', '}']);
652
778
  }
653
779
  if (iComps) {
654
780
  const rgba = ['r', 'g', 'b', 'a'];
655
781
  for (let comp = 0; comp < tNumComp; ++comp) {
656
- tcoordFSImpl = tcoordFSImpl.concat([`vec3 tcolor${comp} = mix${comp} * texture2D(colorTexture1, vec2(tvalue.${rgba[comp]} * cscale${comp} + cshift${comp}, height${comp})).rgb;`, `float compWeight${comp} = mix${comp} * texture2D(pwfTexture1, vec2(tvalue.${rgba[comp]} * pwfscale${comp} + pwfshift${comp}, height${comp})).r;`]);
782
+ tcoordFSImpl = tcoordFSImpl.concat([`vec3 tcolor${comp} = texture2D(colorTexture1, vec2(tvalue.${rgba[comp]} * cscale${comp} + cshift${comp}, height${comp})).rgb;`, `float compWeight${comp} = mix${comp} * texture2D(pwfTexture1, vec2(tvalue.${rgba[comp]} * pwfscale${comp} + pwfshift${comp}, height${comp})).r;`]);
657
783
  }
658
784
  switch (tNumComp) {
659
785
  case 1:
@@ -739,8 +865,8 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
739
865
  }
740
866
  publicAPI.updateResliceGeometry = () => {
741
867
  let resGeomString = '';
742
- const image = model.currentInput;
743
- const imageBounds = image?.getBounds();
868
+ const firstImageData = model.currentValidInputs[0].imageData;
869
+ const imageBounds = firstImageData?.getBounds();
744
870
  // Orthogonal slicing by default
745
871
  let orthoSlicing = true;
746
872
  let orthoAxis = 2;
@@ -752,11 +878,11 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
752
878
  resGeomString = resGeomString.concat(`Plane${slicePlane.getMTime()}`);
753
879
  // Compute a world-to-image-orientation matrix.
754
880
  const w2io = mat3.create();
755
- if (image) {
756
- resGeomString = resGeomString.concat(`Image${image.getMTime()}`);
881
+ if (firstImageData) {
882
+ resGeomString = resGeomString.concat(`Image${firstImageData.getMTime()}`);
757
883
  // Ignore the translation component since we are
758
884
  // using it on vectors rather than positions.
759
- mat3.set(w2io, ...image.getDirection());
885
+ mat3.set(w2io, ...firstImageData.getDirection());
760
886
  mat3.invert(w2io, w2io);
761
887
  }
762
888
  // Check to see if we can bypass oblique slicing related bounds computation
@@ -769,14 +895,14 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
769
895
  const plane = vtkPlane.newInstance();
770
896
  plane.setNormal(0, 0, 1);
771
897
  let bds = [0, 1, 0, 1, 0, 1];
772
- if (image) {
898
+ if (firstImageData) {
773
899
  bds = imageBounds;
774
900
  }
775
901
  plane.setOrigin(bds[0], bds[2], 0.5 * (bds[5] + bds[4]));
776
902
  model.renderable.setSlicePlane(plane);
777
903
  resGeomString = resGeomString.concat(`Plane${slicePlane?.getMTime()}`);
778
- if (image) {
779
- resGeomString = resGeomString.concat(`Image${image.getMTime()}`);
904
+ if (firstImageData) {
905
+ resGeomString = resGeomString.concat(`Image${firstImageData.getMTime()}`);
780
906
  }
781
907
  }
782
908
  if (!model.resliceGeom || model.resliceGeomUpdateString !== resGeomString) {
@@ -789,7 +915,7 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
789
915
  model.resliceGeom.getPointData().setNormals(slicePD.getPointData().getNormals());
790
916
  } else if (slicePlane) {
791
917
  if (!orthoSlicing) {
792
- model.outlineFilter.setInputData(image);
918
+ model.outlineFilter.setInputData(firstImageData);
793
919
  model.cutter.setInputConnection(model.outlineFilter.getOutputPort());
794
920
  model.cutter.setCutFunction(slicePlane);
795
921
  model.lineToSurfaceFilter.setInputConnection(model.cutter.getOutputPort());
@@ -822,9 +948,9 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
822
948
  // Since the image-local normal is axis-aligned, we
823
949
  // can quickly construct the cutting plane using indexToWorld transforms.
824
950
  const ptsArray = new Float32Array(12);
825
- const indexSpacePlaneOrigin = image.worldToIndex(slicePlane.getOrigin(), [0, 0, 0]);
951
+ const indexSpacePlaneOrigin = firstImageData.worldToIndex(slicePlane.getOrigin(), [0, 0, 0]);
826
952
  const otherAxes = [(orthoAxis + 1) % 3, (orthoAxis + 2) % 3].sort();
827
- const ext = image.getSpatialExtent();
953
+ const ext = firstImageData.getSpatialExtent();
828
954
  let ptIdx = 0;
829
955
  for (let i = 0; i < 2; ++i) {
830
956
  for (let j = 0; j < 2; ++j) {
@@ -834,7 +960,7 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
834
960
  ptIdx += 3;
835
961
  }
836
962
  }
837
- model.transform.setMatrix(image.getIndexToWorld());
963
+ model.transform.setMatrix(firstImageData.getIndexToWorld());
838
964
  model.transform.transformPoints(ptsArray, ptsArray);
839
965
  const cellArray = new Uint16Array(8);
840
966
  cellArray[0] = 3;
@@ -872,11 +998,9 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
872
998
  model.resliceGeom?.modified();
873
999
  }
874
1000
  };
875
- publicAPI.setOpenGLTexture = oglTex => {
876
- if (oglTex) {
877
- model.openGLTexture = oglTex;
878
- model._externalOpenGLTexture = true;
879
- }
1001
+ publicAPI.setScalarTextures = scalarTextures => {
1002
+ model.scalarTextures = [...scalarTextures];
1003
+ model._externalOpenGLTexture = true;
880
1004
  };
881
1005
  publicAPI.delete = chain(() => {
882
1006
  if (model._openGLRenderWindow) {
@@ -895,22 +1019,21 @@ const DEFAULT_VALUES = {
895
1019
  haveSeenDepthRequest: false,
896
1020
  lastHaveSeenDepthRequest: false,
897
1021
  lastIndependentComponents: false,
898
- lastTextureComponents: 0,
1022
+ lastNumberOfComponents: 0,
1023
+ lastMultiTexturePerVolumeEnabled: false,
899
1024
  lastSlabThickness: 0,
900
1025
  lastSlabTrapezoidIntegration: 0,
901
1026
  lastSlabType: -1,
902
- openGLTexture: null,
903
- colorTextureString: null,
904
- pwfTextureString: null,
905
- resliceGeom: null,
906
- resliceGeomUpdateString: null,
907
- tris: null,
1027
+ scalarTextures: [],
1028
+ _scalarTexturesCore: [],
908
1029
  colorTexture: null,
1030
+ _colorTextureCore: null,
909
1031
  pwfTexture: null,
910
- _externalOpenGLTexture: false
911
- // _scalars: null,
912
- // _colorTransferFunc: null,
913
- // _pwFunc: null,
1032
+ _pwfTextureCore: null,
1033
+ _externalOpenGLTexture: false,
1034
+ resliceGeom: null,
1035
+ resliceGeomUpdateString: null,
1036
+ tris: null
914
1037
  };
915
1038
 
916
1039
  // ----------------------------------------------------------------------------
@@ -924,7 +1047,7 @@ function extend(publicAPI, model) {
924
1047
  vtkReplacementShaderMapper.implementReplaceShaderCoincidentOffset(publicAPI, model, initialValues);
925
1048
  vtkReplacementShaderMapper.implementBuildShadersWithReplacements(publicAPI, model, initialValues);
926
1049
  model.tris = vtkHelper.newInstance();
927
- model.openGLTexture = null;
1050
+ model.scalarTextures = [];
928
1051
  model.colorTexture = null;
929
1052
  model.pwfTexture = null;
930
1053
  model.VBOBuildTime = {};
@@ -939,7 +1062,7 @@ function extend(publicAPI, model) {
939
1062
  model.cutter = vtkCutter.newInstance();
940
1063
  model.lineToSurfaceFilter = vtkClosedPolyLineToSurfaceFilter.newInstance();
941
1064
  model.transform = vtkTransform.newInstance();
942
- get(publicAPI, model, ['openGLTexture']);
1065
+ get(publicAPI, model, ['scalarTextures']);
943
1066
 
944
1067
  // Object methods
945
1068
  vtkOpenGLImageResliceMapper(publicAPI, model);