@kitware/vtk.js 32.5.0 → 33.0.0-beta.1

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.
@@ -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,56 +238,63 @@ 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
- if (reBuildTex) {
189
- model.openGLTexture = vtkOpenGLTexture.newInstance();
190
- model.openGLTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
191
- // Build the image scalar texture
192
- const dims = image.getDimensions();
193
- // Use norm16 for the 3D texture if the extension is available
194
- model.openGLTexture.setOglNorm16Ext(model.context.getExtension('EXT_texture_norm16'));
195
- model.openGLTexture.resetFormatAndType();
196
- model.openGLTexture.create3DFilterableFromDataArray(dims[0], dims[1], dims[2], scalars);
197
- model._openGLRenderWindow.setGraphicsResourceForObject(scalars, model.openGLTexture, toString);
198
- if (scalars !== model._scalars) {
199
- model._openGLRenderWindow.registerGraphicsResourceUser(scalars, publicAPI);
200
- model._openGLRenderWindow.unregisterGraphicsResourceUser(model._scalars, publicAPI);
201
- }
202
- model._scalars = scalars;
203
- } else {
204
- model.openGLTexture = tex.oglObject;
205
- }
206
- const ppty = actor.getProperty();
207
- const iComps = ppty.getIndependentComponents();
208
- const numIComps = iComps ? numComp : 1;
248
+ model.currentValidInputs.forEach((_ref3, component) => {
249
+ let {
250
+ imageData
251
+ } = _ref3;
252
+ // rebuild the scalarTexture if the data has changed
253
+ const scalars = imageData.getPointData().getScalars();
254
+ const tex = model._openGLRenderWindow.getGraphicsResourceForObject(scalars);
255
+ const scalarsHash = getImageDataHash(imageData, scalars);
256
+ const reBuildTex = !tex?.oglObject?.getHandle() || tex?.hash !== scalarsHash;
257
+ if (reBuildTex) {
258
+ const newScalarTexture = vtkOpenGLTexture.newInstance();
259
+ newScalarTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
260
+ // Build the textures
261
+ const dims = imageData.getDimensions();
262
+ // Use norm16 for scalar texture if the extension is available
263
+ newScalarTexture.setOglNorm16Ext(model.context.getExtension('EXT_texture_norm16'));
264
+ newScalarTexture.resetFormatAndType();
265
+ newScalarTexture.create3DFilterableFromDataArray(dims[0], dims[1], dims[2], scalars);
266
+ model._openGLRenderWindow.setGraphicsResourceForObject(scalars, newScalarTexture, scalarsHash);
267
+ model.scalarTextures[component] = newScalarTexture;
268
+ } else {
269
+ model.scalarTextures[component] = tex.oglObject;
270
+ }
271
+ replaceGraphicsResource(model._openGLRenderWindow, model._scalarTexturesCore[component], scalars);
272
+ model._scalarTexturesCore[component] = scalars;
273
+ });
274
+ const firstValidInput = model.currentValidInputs[0];
275
+ const actorProperties = actor.getProperties();
276
+ const firstActorProperty = actorProperties[firstValidInput.inputIndex];
277
+ const iComps = firstActorProperty.getIndependentComponents();
278
+ const numIComps = iComps ? model.numberOfComponents : 1;
209
279
  const textureHeight = iComps ? 2 * numIComps : 1;
210
- const colorTransferFunc = ppty.getRGBTransferFunction();
211
- toString = getTransferFunctionHash(colorTransferFunc, iComps, numIComps);
212
- const cTex = model._openGLRenderWindow.getGraphicsResourceForObject(colorTransferFunc);
213
- const reBuildC = !cTex?.oglObject?.getHandle() || cTex?.hash !== toString;
280
+ const colorTransferFunctions = [];
281
+ for (let component = 0; component < numIComps; ++component) {
282
+ colorTransferFunctions.push(firstActorProperty.getRGBTransferFunction(component));
283
+ }
284
+ const colorFuncHash = getTransferFunctionsHash(colorTransferFunctions, iComps, numIComps);
285
+ const firstColorTransferFunc = firstActorProperty.getRGBTransferFunction();
286
+ const cTex = model._openGLRenderWindow.getGraphicsResourceForObject(firstColorTransferFunc);
287
+ const reBuildC = !cTex?.oglObject?.getHandle() || cTex?.hash !== colorFuncHash;
214
288
  if (reBuildC) {
215
289
  const cWidth = 1024;
216
290
  const cSize = cWidth * textureHeight * 3;
217
291
  const cTable = new Uint8ClampedArray(cSize);
218
- model.colorTexture = vtkOpenGLTexture.newInstance();
219
- model.colorTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
220
- if (colorTransferFunc) {
292
+ const newColorTexture = vtkOpenGLTexture.newInstance();
293
+ newColorTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
294
+ if (firstColorTransferFunc) {
221
295
  const tmpTable = new Float32Array(cWidth * 3);
222
296
  for (let c = 0; c < numIComps; c++) {
223
- const cfun = ppty.getRGBTransferFunction(c);
297
+ const cfun = firstActorProperty.getRGBTransferFunction(c);
224
298
  const cRange = cfun.getRange();
225
299
  cfun.getTable(cRange[0], cRange[1], cWidth, tmpTable, 1);
226
300
  if (iComps) {
@@ -230,52 +304,57 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
230
304
  }
231
305
  } else {
232
306
  for (let i = 0; i < cWidth * 3; i++) {
233
- cTable[c * cWidth * 6 + i] = 255.0 * tmpTable[i];
307
+ cTable[c * cWidth * 3 + i] = 255.0 * tmpTable[i];
234
308
  }
235
309
  }
236
310
  }
237
- model.colorTexture.resetFormatAndType();
238
- model.colorTexture.create2DFromRaw(cWidth, textureHeight, 3, VtkDataTypes.UNSIGNED_CHAR, cTable);
311
+ newColorTexture.resetFormatAndType();
312
+ newColorTexture.create2DFromRaw(cWidth, textureHeight, 3, VtkDataTypes.UNSIGNED_CHAR, cTable);
239
313
  } else {
240
- for (let i = 0; i < cWidth * 3; ++i) {
241
- cTable[i] = 255.0 * i / ((cWidth - 1) * 3);
242
- cTable[i + 1] = 255.0 * i / ((cWidth - 1) * 3);
243
- cTable[i + 2] = 255.0 * i / ((cWidth - 1) * 3);
244
- }
245
- model.colorTexture.resetFormatAndType();
246
- model.colorTexture.create2DFromRaw(cWidth, 1, 3, VtkDataTypes.UNSIGNED_CHAR, cTable);
247
- }
248
- if (colorTransferFunc) {
249
- model._openGLRenderWindow.setGraphicsResourceForObject(colorTransferFunc, model.colorTexture, toString);
250
- if (colorTransferFunc !== model._colorTransferFunc) {
251
- model._openGLRenderWindow.registerGraphicsResourceUser(colorTransferFunc, publicAPI);
252
- model._openGLRenderWindow.unregisterGraphicsResourceUser(model._colorTransferFunc, publicAPI);
314
+ for (let column = 0; column < cWidth * 3; ++column) {
315
+ const opacity = 255.0 * column / ((cWidth - 1) * 3);
316
+ for (let row = 0; row < textureHeight; ++row) {
317
+ // R, G, B
318
+ cTable[row * cWidth * 3 + column + 0] = opacity;
319
+ cTable[row * cWidth * 3 + column + 1] = opacity;
320
+ cTable[row * cWidth * 3 + column + 2] = opacity;
321
+ }
253
322
  }
254
- model._colorTransferFunc = colorTransferFunc;
323
+ newColorTexture.resetFormatAndType();
324
+ newColorTexture.create2DFromRaw(cWidth, 1, 3, VtkDataTypes.UNSIGNED_CHAR, cTable);
325
+ }
326
+ if (firstColorTransferFunc) {
327
+ model._openGLRenderWindow.setGraphicsResourceForObject(firstColorTransferFunc, newColorTexture, colorFuncHash);
255
328
  }
329
+ model.colorTexture = newColorTexture;
256
330
  } else {
257
331
  model.colorTexture = cTex.oglObject;
258
332
  }
333
+ replaceGraphicsResource(model._openGLRenderWindow, model._colorTextureCore, firstColorTransferFunc);
334
+ model._colorTextureCore = firstColorTransferFunc;
259
335
 
260
336
  // Build piecewise function buffer. This buffer is used either
261
337
  // for component weighting or opacity, depending on whether we're
262
338
  // rendering components independently or not.
263
- const pwFunc = ppty.getPiecewiseFunction();
264
- toString = getTransferFunctionHash(pwFunc, iComps, numIComps);
265
- const pwfTex = model._openGLRenderWindow.getGraphicsResourceForObject(pwFunc);
266
- // rebuild opacity tfun?
267
- const reBuildPwf = !pwfTex?.oglObject?.getHandle() || pwfTex?.hash !== toString;
339
+ const opacityFunctions = [];
340
+ for (let component = 0; component < numIComps; ++component) {
341
+ opacityFunctions.push(firstActorProperty.getPiecewiseFunction(component));
342
+ }
343
+ const opacityFuncHash = getTransferFunctionsHash(opacityFunctions, iComps, numIComps);
344
+ const firstPwFunc = firstActorProperty.getPiecewiseFunction();
345
+ const pwfTex = model._openGLRenderWindow.getGraphicsResourceForObject(firstPwFunc);
346
+ const reBuildPwf = !pwfTex?.oglObject?.getHandle() || pwfTex?.hash !== opacityFuncHash;
268
347
  if (reBuildPwf) {
269
348
  const pwfWidth = 1024;
270
349
  const pwfSize = pwfWidth * textureHeight;
271
350
  const pwfTable = new Uint8ClampedArray(pwfSize);
272
- model.pwfTexture = vtkOpenGLTexture.newInstance();
273
- model.pwfTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
274
- if (pwFunc) {
351
+ const newOpacityTexture = vtkOpenGLTexture.newInstance();
352
+ newOpacityTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
353
+ if (firstPwFunc) {
275
354
  const pwfFloatTable = new Float32Array(pwfSize);
276
355
  const tmpTable = new Float32Array(pwfWidth);
277
356
  for (let c = 0; c < numIComps; ++c) {
278
- const pwfun = ppty.getPiecewiseFunction(c);
357
+ const pwfun = firstActorProperty.getPiecewiseFunction(c);
279
358
  if (pwfun === null) {
280
359
  // Piecewise constant max if no function supplied for this component
281
360
  pwfFloatTable.fill(1.0);
@@ -290,30 +369,28 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
290
369
  }
291
370
  } else {
292
371
  for (let i = 0; i < pwfWidth; i++) {
293
- pwfFloatTable[c * pwfWidth * 2 + i] = tmpTable[i];
372
+ pwfFloatTable[i] = tmpTable[i];
294
373
  }
295
374
  }
296
375
  }
297
376
  }
298
- model.pwfTexture.resetFormatAndType();
299
- model.pwfTexture.create2DFromRaw(pwfWidth, textureHeight, 1, VtkDataTypes.FLOAT, pwfFloatTable);
377
+ newOpacityTexture.resetFormatAndType();
378
+ newOpacityTexture.create2DFromRaw(pwfWidth, textureHeight, 1, VtkDataTypes.FLOAT, pwfFloatTable);
300
379
  } else {
301
380
  // default is opaque
302
381
  pwfTable.fill(255.0);
303
- model.pwfTexture.resetFormatAndType();
304
- model.pwfTexture.create2DFromRaw(pwfWidth, 1, 1, VtkDataTypes.UNSIGNED_CHAR, pwfTable);
305
- }
306
- if (pwFunc) {
307
- model._openGLRenderWindow.setGraphicsResourceForObject(pwFunc, model.pwfTexture, toString);
308
- if (pwFunc !== model._pwFunc) {
309
- model._openGLRenderWindow.registerGraphicsResourceUser(pwFunc, publicAPI);
310
- model._openGLRenderWindow.unregisterGraphicsResourceUser(model._pwFunc, publicAPI);
311
- }
312
- model._pwFunc = pwFunc;
382
+ newOpacityTexture.resetFormatAndType();
383
+ newOpacityTexture.create2DFromRaw(pwfWidth, textureHeight, 1, VtkDataTypes.UNSIGNED_CHAR, pwfTable);
384
+ }
385
+ if (firstPwFunc) {
386
+ model._openGLRenderWindow.setGraphicsResourceForObject(firstPwFunc, newOpacityTexture, opacityFuncHash);
313
387
  }
388
+ model.pwfTexture = newOpacityTexture;
314
389
  } else {
315
390
  model.pwfTexture = pwfTex.oglObject;
316
391
  }
392
+ replaceGraphicsResource(model._openGLRenderWindow, model._pwfTextureCore, firstPwFunc);
393
+ model._pwfTextureCore = firstPwFunc;
317
394
  const vboString = `${model.resliceGeom.getMTime()}A${model.renderable.getSlabThickness()}`;
318
395
  if (!model.tris.getCABO().getElementCount() || model.VBOBuildString !== vboString) {
319
396
  const points = vtkDataArray.newInstance({
@@ -374,11 +451,12 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
374
451
  };
375
452
  publicAPI.setMapperShaderParameters = (cellBO, ren, actor) => {
376
453
  const program = cellBO.getProgram();
454
+ const firstImageData = model.currentValidInputs[0].imageData;
377
455
  if (cellBO.getCABO().getElementCount() && (model.VBOBuildTime.getMTime() > cellBO.getAttributeUpdateTime().getMTime() || cellBO.getShaderSourceTime().getMTime() > cellBO.getAttributeUpdateTime().getMTime())) {
378
456
  // Set the 3D texture
379
- if (program.isUniformUsed('texture1')) {
380
- program.setUniformi('texture1', model.openGLTexture.getTextureUnit());
381
- }
457
+ model.scalarTextures.forEach((scalarTexture, component) => {
458
+ program.setUniformi(`volumeTexture[${component}]`, scalarTexture.getTextureUnit());
459
+ });
382
460
 
383
461
  // Set the plane vertex attributes
384
462
  if (program.isAttributeUsed('vertexWC')) {
@@ -397,7 +475,7 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
397
475
  program.setUniformf('slabThickness', model.renderable.getSlabThickness());
398
476
  }
399
477
  if (program.isUniformUsed('spacing')) {
400
- program.setUniform3fv('spacing', model.currentInput.getSpacing());
478
+ program.setUniform3fv('spacing', firstImageData.getSpacing());
401
479
  }
402
480
  if (program.isUniformUsed('slabType')) {
403
481
  program.setUniformi('slabType', model.renderable.getSlabType());
@@ -413,9 +491,8 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
413
491
 
414
492
  // Set the world->texture matrix
415
493
  if (program.isUniformUsed('WCTCMatrix')) {
416
- const image = model.currentInput;
417
- const dim = image.getDimensions();
418
- mat4.copy(model.tmpMat4, image.getIndexToWorld());
494
+ const dim = firstImageData.getDimensions();
495
+ mat4.copy(model.tmpMat4, firstImageData.getIndexToWorld());
419
496
  mat4.translate(model.tmpMat4, model.tmpMat4, [-0.5, -0.5, -0.5]);
420
497
  mat4.scale(model.tmpMat4, model.tmpMat4, dim);
421
498
  mat4.invert(model.tmpMat4, model.tmpMat4);
@@ -464,8 +541,8 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
464
541
  };
465
542
  publicAPI.setPropertyShaderParameters = (cellBO, ren, actor) => {
466
543
  const program = cellBO.getProgram();
467
- const ppty = actor.getProperty();
468
- const opacity = ppty.getOpacity();
544
+ const firstPpty = actor.getProperty(model.currentValidInputs[0].inputIndex);
545
+ const opacity = firstPpty.getOpacity();
469
546
  program.setUniformf('opacity', opacity);
470
547
 
471
548
  // Component mix
@@ -474,53 +551,56 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
474
551
  // - 2 comps => LA
475
552
  // - 3 comps => RGB + opacity from pwf
476
553
  // - 4 comps => RGBA
477
- const numComp = model.openGLTexture.getComponents();
478
- const iComps = ppty.getIndependentComponents();
554
+ const numComp = model.numberOfComponents;
555
+ const iComps = firstPpty.getIndependentComponents();
479
556
  if (iComps) {
480
557
  for (let i = 0; i < numComp; ++i) {
481
- program.setUniformf(`mix${i}`, ppty.getComponentWeight(i));
558
+ program.setUniformf(`mix${i}`, firstPpty.getComponentWeight(i));
482
559
  }
483
560
  }
484
561
 
485
- // Color opacity map
486
- const volInfo = model.openGLTexture.getVolumeInfo();
487
-
488
562
  // three levels of shift scale combined into one
489
563
  // for performance in the fragment shader
490
- for (let i = 0; i < numComp; i++) {
491
- let cw = ppty.getColorWindow();
492
- let cl = ppty.getColorLevel();
493
- const target = iComps ? i : 0;
494
- const cfun = ppty.getRGBTransferFunction(target);
495
- if (cfun && ppty.getUseLookupTableScalarRange()) {
564
+ for (let component = 0; component < numComp; component++) {
565
+ const useMultiTexture = model.multiTexturePerVolumeEnabled;
566
+ const textureIndex = useMultiTexture ? component : 0;
567
+ const volInfoIndex = useMultiTexture ? 0 : component;
568
+ const scalarTexture = model.scalarTextures[textureIndex];
569
+ const volInfo = scalarTexture.getVolumeInfo();
570
+ const volScale = volInfo.scale[volInfoIndex];
571
+ const volOffset = volInfo.offset[volInfoIndex];
572
+ const target = iComps ? component : 0;
573
+
574
+ // color shift/scale
575
+ let cw = firstPpty.getColorWindow();
576
+ let cl = firstPpty.getColorLevel();
577
+ const cfun = firstPpty.getRGBTransferFunction(target);
578
+ if (cfun && firstPpty.getUseLookupTableScalarRange()) {
496
579
  const cRange = cfun.getRange();
497
580
  cw = cRange[1] - cRange[0];
498
581
  cl = 0.5 * (cRange[1] + cRange[0]);
499
582
  }
500
- const scale = volInfo.scale[i] / cw;
501
- const shift = (volInfo.offset[i] - cl) / cw + 0.5;
502
- program.setUniformf(`cshift${i}`, shift);
503
- program.setUniformf(`cscale${i}`, scale);
504
- }
505
- const texColorUnit = model.colorTexture.getTextureUnit();
506
- program.setUniformi('colorTexture1', texColorUnit);
583
+ const colorScale = volScale / cw;
584
+ const colorShift = (volOffset - cl) / cw + 0.5;
585
+ program.setUniformf(`cshift${component}`, colorShift);
586
+ program.setUniformf(`cscale${component}`, colorScale);
507
587
 
508
- // pwf shift/scale
509
- for (let i = 0; i < numComp; i++) {
588
+ // pwf shift/scale
510
589
  let pwfScale = 1.0;
511
590
  let pwfShift = 0.0;
512
- const target = iComps ? i : 0;
513
- const pwfun = ppty.getPiecewiseFunction(target);
591
+ const pwfun = firstPpty.getPiecewiseFunction(target);
514
592
  if (pwfun) {
515
593
  const pwfRange = pwfun.getRange();
516
594
  const length = pwfRange[1] - pwfRange[0];
517
595
  const mid = 0.5 * (pwfRange[0] + pwfRange[1]);
518
- pwfScale = volInfo.scale[i] / length;
519
- pwfShift = (volInfo.offset[i] - mid) / length + 0.5;
596
+ pwfScale = volScale / length;
597
+ pwfShift = (volOffset - mid) / length + 0.5;
520
598
  }
521
- program.setUniformf(`pwfshift${i}`, pwfShift);
522
- program.setUniformf(`pwfscale${i}`, pwfScale);
599
+ program.setUniformf(`pwfshift${component}`, pwfShift);
600
+ program.setUniformf(`pwfscale${component}`, pwfScale);
523
601
  }
602
+ const texColorUnit = model.colorTexture.getTextureUnit();
603
+ program.setUniformi('colorTexture1', texColorUnit);
524
604
  const texOpacityUnit = model.pwfTexture.getTextureUnit();
525
605
  program.setUniformi('pwfTexture1', texOpacityUnit);
526
606
 
@@ -534,8 +614,7 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
534
614
  // input modified
535
615
  // light complexity changed
536
616
  // render pass shader replacement changed
537
- const tNumComp = model.openGLTexture.getComponents();
538
- const iComp = actor.getProperty().getIndependentComponents();
617
+ const iComp = actor.getProperty(model.currentValidInputs[0].inputIndex).getIndependentComponents();
539
618
  const slabTh = model.renderable.getSlabThickness();
540
619
  const slabType = model.renderable.getSlabType();
541
620
  const slabTrap = model.renderable.getSlabTrapezoidIntegration();
@@ -545,9 +624,10 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
545
624
  if (!model.currentRenderPass && model.lastRenderPassShaderReplacement || model.currentRenderPass && model.currentRenderPass.getShaderReplacement() !== model.lastRenderPassShaderReplacement) {
546
625
  needRebuild = true;
547
626
  }
548
- 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) {
627
+ 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) {
549
628
  model.lastHaveSeenDepthRequest = model.haveSeenDepthRequest;
550
- model.lastTextureComponents = tNumComp;
629
+ model.lastNumberOfComponents = model.numberOfComponents;
630
+ model.lastMultiTexturePerVolumeEnabled = model.multiTexturePerVolumeEnabled;
551
631
  model.lastIndependentComponents = iComp;
552
632
  model.lastSlabThickness = slabTh;
553
633
  model.lastSlabType = slabType;
@@ -581,9 +661,9 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
581
661
  VSSource = vtkShaderProgram.substitute(VSSource, '//VTK::TCoord::Dec', tcoordVSDec).result;
582
662
  const tcoordVSImpl = ['fragTexCoord = (WCTCMatrix * vertexWC).xyz;'];
583
663
  VSSource = vtkShaderProgram.substitute(VSSource, '//VTK::TCoord::Impl', tcoordVSImpl).result;
584
- const tNumComp = model.openGLTexture.getComponents();
585
- const iComps = actor.getProperty().getIndependentComponents();
586
- let tcoordFSDec = ['in vec3 fragTexCoord;', 'uniform highp sampler3D texture1;', 'uniform mat4 WCTCMatrix;',
664
+ const tNumComp = model.numberOfComponents;
665
+ const iComps = actor.getProperty(model.currentValidInputs[0].inputIndex).getIndependentComponents();
666
+ let tcoordFSDec = ['in vec3 fragTexCoord;', `uniform highp sampler3D volumeTexture[${model.scalarTextures.length}];`, 'uniform mat4 WCTCMatrix;',
587
667
  // color shift and scale
588
668
  'uniform float cshift0;', 'uniform float cscale0;',
589
669
  // pwf shift and scale
@@ -594,6 +674,18 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
594
674
  'uniform float opacity;',
595
675
  // background color
596
676
  'uniform vec4 backgroundColor;'];
677
+
678
+ // Function to sample texture
679
+ tcoordFSDec.push('vec4 rawSampleTexture(vec3 pos) {');
680
+ if (!model.multiTexturePerVolumeEnabled) {
681
+ tcoordFSDec.push('return texture(volumeTexture[0], pos);', '}');
682
+ } else {
683
+ tcoordFSDec.push('vec4 rawSample;');
684
+ for (let component = 0; component < model.scalarTextures.length; ++component) {
685
+ tcoordFSDec.push(`rawSample[${component}] = texture(volumeTexture[${component}], pos)[0];`);
686
+ }
687
+ tcoordFSDec.push('return rawSample;', '}');
688
+ }
597
689
  if (iComps) {
598
690
  for (let comp = 1; comp < tNumComp; comp++) {
599
691
  tcoordFSDec = tcoordFSDec.concat([
@@ -629,14 +721,14 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
629
721
  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;', '}']);
630
722
  }
631
723
  FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::TCoord::Dec', tcoordFSDec).result;
632
- 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);'];
724
+ 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);'];
633
725
  if (slabThickness > 0.0) {
634
- 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);', '}']);
726
+ 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);', '}']);
635
727
  }
636
728
  if (iComps) {
637
729
  const rgba = ['r', 'g', 'b', 'a'];
638
730
  for (let comp = 0; comp < tNumComp; ++comp) {
639
- 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;`]);
731
+ 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;`]);
640
732
  }
641
733
  switch (tNumComp) {
642
734
  case 1:
@@ -702,6 +794,11 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
702
794
  shaders.Geometry = GSSource;
703
795
  shaders.Fragment = FSSource;
704
796
  };
797
+
798
+ /**
799
+ * Returns true if the normal is almost axis aligned.
800
+ * Has a side effect to normalize the vector.
801
+ */
705
802
  function isVectorAxisAligned(n) {
706
803
  vtkMath.normalize(n);
707
804
  const tmpN = [0, 0, 0];
@@ -709,7 +806,7 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
709
806
  vec3.zero(tmpN);
710
807
  tmpN[i] = 1.0;
711
808
  const dotP = vtkMath.dot(n, tmpN);
712
- if (dotP < -0.999 || dotP > 0.999) {
809
+ if (dotP < -0.999999 || dotP > 0.999999) {
713
810
  return [true, i];
714
811
  }
715
812
  }
@@ -717,8 +814,8 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
717
814
  }
718
815
  publicAPI.updateResliceGeometry = () => {
719
816
  let resGeomString = '';
720
- const image = model.currentInput;
721
- const imageBounds = image?.getBounds();
817
+ const firstImageData = model.currentValidInputs[0].imageData;
818
+ const imageBounds = firstImageData?.getBounds();
722
819
  // Orthogonal slicing by default
723
820
  let orthoSlicing = true;
724
821
  let orthoAxis = 2;
@@ -730,11 +827,11 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
730
827
  resGeomString = resGeomString.concat(`Plane${slicePlane.getMTime()}`);
731
828
  // Compute a world-to-image-orientation matrix.
732
829
  const w2io = mat3.create();
733
- if (image) {
734
- resGeomString = resGeomString.concat(`Image${image.getMTime()}`);
830
+ if (firstImageData) {
831
+ resGeomString = resGeomString.concat(`Image${firstImageData.getMTime()}`);
735
832
  // Ignore the translation component since we are
736
833
  // using it on vectors rather than positions.
737
- mat3.set(w2io, ...image.getDirection());
834
+ mat3.set(w2io, ...firstImageData.getDirection());
738
835
  mat3.invert(w2io, w2io);
739
836
  }
740
837
  // Check to see if we can bypass oblique slicing related bounds computation
@@ -747,14 +844,14 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
747
844
  const plane = vtkPlane.newInstance();
748
845
  plane.setNormal(0, 0, 1);
749
846
  let bds = [0, 1, 0, 1, 0, 1];
750
- if (image) {
847
+ if (firstImageData) {
751
848
  bds = imageBounds;
752
849
  }
753
850
  plane.setOrigin(bds[0], bds[2], 0.5 * (bds[5] + bds[4]));
754
851
  model.renderable.setSlicePlane(plane);
755
852
  resGeomString = resGeomString.concat(`Plane${slicePlane?.getMTime()}`);
756
- if (image) {
757
- resGeomString = resGeomString.concat(`Image${image.getMTime()}`);
853
+ if (firstImageData) {
854
+ resGeomString = resGeomString.concat(`Image${firstImageData.getMTime()}`);
758
855
  }
759
856
  }
760
857
  if (!model.resliceGeom || model.resliceGeomUpdateString !== resGeomString) {
@@ -767,7 +864,7 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
767
864
  model.resliceGeom.getPointData().setNormals(slicePD.getPointData().getNormals());
768
865
  } else if (slicePlane) {
769
866
  if (!orthoSlicing) {
770
- model.outlineFilter.setInputData(image);
867
+ model.outlineFilter.setInputData(firstImageData);
771
868
  model.cutter.setInputConnection(model.outlineFilter.getOutputPort());
772
869
  model.cutter.setCutFunction(slicePlane);
773
870
  model.lineToSurfaceFilter.setInputConnection(model.cutter.getOutputPort());
@@ -800,9 +897,9 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
800
897
  // Since the image-local normal is axis-aligned, we
801
898
  // can quickly construct the cutting plane using indexToWorld transforms.
802
899
  const ptsArray = new Float32Array(12);
803
- const indexSpacePlaneOrigin = image.worldToIndex(slicePlane.getOrigin(), [0, 0, 0]);
900
+ const indexSpacePlaneOrigin = firstImageData.worldToIndex(slicePlane.getOrigin(), [0, 0, 0]);
804
901
  const otherAxes = [(orthoAxis + 1) % 3, (orthoAxis + 2) % 3].sort();
805
- const ext = image.getSpatialExtent();
902
+ const ext = firstImageData.getSpatialExtent();
806
903
  let ptIdx = 0;
807
904
  for (let i = 0; i < 2; ++i) {
808
905
  for (let j = 0; j < 2; ++j) {
@@ -812,7 +909,7 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
812
909
  ptIdx += 3;
813
910
  }
814
911
  }
815
- model.transform.setMatrix(image.getIndexToWorld());
912
+ model.transform.setMatrix(firstImageData.getIndexToWorld());
816
913
  model.transform.transformPoints(ptsArray, ptsArray);
817
914
  const cellArray = new Uint16Array(8);
818
915
  cellArray[0] = 3;
@@ -850,11 +947,9 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
850
947
  model.resliceGeom?.modified();
851
948
  }
852
949
  };
853
- publicAPI.setOpenGLTexture = oglTex => {
854
- if (oglTex) {
855
- model.openGLTexture = oglTex;
856
- model._externalOpenGLTexture = true;
857
- }
950
+ publicAPI.setScalarTextures = scalarTextures => {
951
+ model.scalarTextures = [...scalarTextures];
952
+ model._externalOpenGLTexture = true;
858
953
  };
859
954
  publicAPI.delete = chain(() => {
860
955
  if (model._openGLRenderWindow) {
@@ -873,22 +968,21 @@ const DEFAULT_VALUES = {
873
968
  haveSeenDepthRequest: false,
874
969
  lastHaveSeenDepthRequest: false,
875
970
  lastIndependentComponents: false,
876
- lastTextureComponents: 0,
971
+ lastNumberOfComponents: 0,
972
+ lastMultiTexturePerVolumeEnabled: false,
877
973
  lastSlabThickness: 0,
878
974
  lastSlabTrapezoidIntegration: 0,
879
975
  lastSlabType: -1,
880
- openGLTexture: null,
881
- colorTextureString: null,
882
- pwfTextureString: null,
883
- resliceGeom: null,
884
- resliceGeomUpdateString: null,
885
- tris: null,
976
+ scalarTextures: [],
977
+ _scalarTexturesCore: [],
886
978
  colorTexture: null,
979
+ _colorTextureCore: null,
887
980
  pwfTexture: null,
888
- _externalOpenGLTexture: false
889
- // _scalars: null,
890
- // _colorTransferFunc: null,
891
- // _pwFunc: null,
981
+ _pwfTextureCore: null,
982
+ _externalOpenGLTexture: false,
983
+ resliceGeom: null,
984
+ resliceGeomUpdateString: null,
985
+ tris: null
892
986
  };
893
987
 
894
988
  // ----------------------------------------------------------------------------
@@ -902,7 +996,7 @@ function extend(publicAPI, model) {
902
996
  vtkReplacementShaderMapper.implementReplaceShaderCoincidentOffset(publicAPI, model, initialValues);
903
997
  vtkReplacementShaderMapper.implementBuildShadersWithReplacements(publicAPI, model, initialValues);
904
998
  model.tris = vtkHelper.newInstance();
905
- model.openGLTexture = null;
999
+ model.scalarTextures = [];
906
1000
  model.colorTexture = null;
907
1001
  model.pwfTexture = null;
908
1002
  model.VBOBuildTime = {};
@@ -917,7 +1011,7 @@ function extend(publicAPI, model) {
917
1011
  model.cutter = vtkCutter.newInstance();
918
1012
  model.lineToSurfaceFilter = vtkClosedPolyLineToSurfaceFilter.newInstance();
919
1013
  model.transform = vtkTransform.newInstance();
920
- get(publicAPI, model, ['openGLTexture']);
1014
+ get(publicAPI, model, ['scalarTextures']);
921
1015
 
922
1016
  // Object methods
923
1017
  vtkOpenGLImageResliceMapper(publicAPI, model);