@kitware/vtk.js 32.10.0 → 32.12.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.
- package/Common/Core/DataArray.d.ts +17 -0
- package/Common/Core/DataArray.js +36 -0
- package/Rendering/Core/ImageCPRMapper.d.ts +20 -1
- package/Rendering/Core/ImageCPRMapper.js +7 -5
- package/Rendering/Core/ImageResliceMapper.d.ts +20 -2
- package/Rendering/Core/ImageResliceMapper.js +7 -5
- package/Rendering/Core/VolumeMapper.d.ts +34 -1
- package/Rendering/Core/VolumeMapper.js +7 -5
- package/Rendering/OpenGL/ImageCPRMapper.js +9 -0
- package/Rendering/OpenGL/ImageMapper.js +16 -1
- package/Rendering/OpenGL/ImageResliceMapper.js +14 -3
- package/Rendering/OpenGL/Texture.d.ts +29 -8
- package/Rendering/OpenGL/Texture.js +154 -23
- package/Rendering/OpenGL/VolumeMapper.js +11 -2
- package/Rendering/WebXR/RenderWindowHelper.js +9 -0
- package/package.json +1 -1
|
@@ -83,6 +83,23 @@ export interface vtkDataArray extends vtkObject {
|
|
|
83
83
|
*/
|
|
84
84
|
setRange(rangeValue: vtkRange, componentIndex: number): Range;
|
|
85
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Returns an array of the ranges for each component of the DataArray.
|
|
88
|
+
* Defaults to computing all the ranges if they aren't already computed.
|
|
89
|
+
*
|
|
90
|
+
* If the number of components is greater than 1, the last element in the
|
|
91
|
+
* ranges array is the min,max magnitude of the dataset. This is the same as
|
|
92
|
+
* calling `getRange(-1)`.
|
|
93
|
+
*
|
|
94
|
+
* Passing `getRanges(false)` will return a clone of the ranges that have
|
|
95
|
+
* already been computed. This is useful when you want to avoid recomputing
|
|
96
|
+
* the ranges, which can be expensive.
|
|
97
|
+
*
|
|
98
|
+
* @param {boolean} [computeRanges] (default: true)
|
|
99
|
+
* @returns {vtkRange[]}
|
|
100
|
+
*/
|
|
101
|
+
getRanges(computeRanges: boolean): vtkRange[];
|
|
102
|
+
|
|
86
103
|
/**
|
|
87
104
|
* Set the given tuple at the given index.
|
|
88
105
|
* @param {Number} idx
|
package/Common/Core/DataArray.js
CHANGED
|
@@ -268,6 +268,35 @@ function vtkDataArray(publicAPI, model) {
|
|
|
268
268
|
model.rangeTuple[1] = range.max;
|
|
269
269
|
return model.rangeTuple;
|
|
270
270
|
};
|
|
271
|
+
publicAPI.getRanges = function () {
|
|
272
|
+
let computeRanges = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
|
|
273
|
+
if (!computeRanges) {
|
|
274
|
+
return structuredClone(model.ranges);
|
|
275
|
+
}
|
|
276
|
+
/** @type {import('../../../interfaces').vtkRange[]} */
|
|
277
|
+
const ranges = [];
|
|
278
|
+
for (let i = 0; i < model.numberOfComponents; i++) {
|
|
279
|
+
const [min, max] = publicAPI.getRange(i);
|
|
280
|
+
/** @type {import('../../../interfaces').vtkRange} */
|
|
281
|
+
const range = {
|
|
282
|
+
min,
|
|
283
|
+
max
|
|
284
|
+
};
|
|
285
|
+
ranges.push(range);
|
|
286
|
+
}
|
|
287
|
+
// where the number of components is greater than 1, the last element in
|
|
288
|
+
// the range array is the min,max magnitude of the entire dataset.
|
|
289
|
+
if (model.numberOfComponents > 1) {
|
|
290
|
+
const [min, max] = publicAPI.getRange(-1);
|
|
291
|
+
/** @type {import('../../../interfaces').vtkRange} */
|
|
292
|
+
const range = {
|
|
293
|
+
min,
|
|
294
|
+
max
|
|
295
|
+
};
|
|
296
|
+
ranges.push(range);
|
|
297
|
+
}
|
|
298
|
+
return ranges;
|
|
299
|
+
};
|
|
271
300
|
publicAPI.setTuple = (idx, tuple) => {
|
|
272
301
|
const offset = idx * model.numberOfComponents;
|
|
273
302
|
for (let i = 0; i < model.numberOfComponents; i++) {
|
|
@@ -426,12 +455,19 @@ function vtkDataArray(publicAPI, model) {
|
|
|
426
455
|
}
|
|
427
456
|
return sortedObj;
|
|
428
457
|
};
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* @param {import("./index").vtkDataArray} other
|
|
461
|
+
*/
|
|
429
462
|
publicAPI.deepCopy = other => {
|
|
430
463
|
// Retain current dataType and array reference before shallowCopy call.
|
|
431
464
|
const currentType = publicAPI.getDataType();
|
|
432
465
|
const currentArray = model.values;
|
|
433
466
|
publicAPI.shallowCopy(other);
|
|
434
467
|
|
|
468
|
+
// set the ranges
|
|
469
|
+
model.ranges = structuredClone(other.getRanges());
|
|
470
|
+
|
|
435
471
|
// Avoid array reallocation if size already sufficient
|
|
436
472
|
// and dataTypes match.
|
|
437
473
|
if (currentArray?.length >= other.getNumberOfValues() && currentType === other.getDataType()) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { mat3, mat4, quat, vec3 } from 'gl-matrix';
|
|
2
|
-
import { Nullable } from './../../types';
|
|
2
|
+
import { Extent, Nullable } from './../../types';
|
|
3
3
|
import { vtkOutputPort } from './../../interfaces';
|
|
4
4
|
import vtkAbstractMapper3D, {
|
|
5
5
|
IAbstractMapper3DInitialValues,
|
|
@@ -307,6 +307,25 @@ export interface vtkImageCPRMapper
|
|
|
307
307
|
* @param imageData
|
|
308
308
|
*/
|
|
309
309
|
setImageConnection(imageData: vtkOutputPort): void;
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Tells the mapper to only update the specified extents.
|
|
313
|
+
*
|
|
314
|
+
* If there are zero extents, the mapper updates the entire volume texture.
|
|
315
|
+
* Otherwise, the mapper will only update the texture by the specified extents
|
|
316
|
+
* during the next render call.
|
|
317
|
+
*
|
|
318
|
+
* This array is cleared after a successful render.
|
|
319
|
+
* @param extents
|
|
320
|
+
*/
|
|
321
|
+
setUpdatedExtents(extents: Extent[]): boolean;
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Retrieves the updated extents.
|
|
325
|
+
*
|
|
326
|
+
* This array is cleared after every successful render.
|
|
327
|
+
*/
|
|
328
|
+
getUpdatedExtents(): Extent[];
|
|
310
329
|
}
|
|
311
330
|
|
|
312
331
|
/**
|
|
@@ -270,7 +270,7 @@ function vtkImageCPRMapper(publicAPI, model) {
|
|
|
270
270
|
// Object factory
|
|
271
271
|
// ----------------------------------------------------------------------------
|
|
272
272
|
|
|
273
|
-
const
|
|
273
|
+
const defaultValues = initialValues => ({
|
|
274
274
|
width: 10,
|
|
275
275
|
uniformOrientation: [0, 0, 0, 1],
|
|
276
276
|
useUniformOrientation: false,
|
|
@@ -282,14 +282,16 @@ const DEFAULT_VALUES = {
|
|
|
282
282
|
normalDirection: [0, 0, 1],
|
|
283
283
|
projectionSlabThickness: 1,
|
|
284
284
|
projectionSlabNumberOfSamples: 1,
|
|
285
|
-
projectionMode: ProjectionMode.MAX
|
|
286
|
-
|
|
285
|
+
projectionMode: ProjectionMode.MAX,
|
|
286
|
+
updatedExtents: [],
|
|
287
|
+
...initialValues
|
|
288
|
+
});
|
|
287
289
|
|
|
288
290
|
// ----------------------------------------------------------------------------
|
|
289
291
|
|
|
290
292
|
function extend(publicAPI, model) {
|
|
291
293
|
let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
292
|
-
Object.assign(model,
|
|
294
|
+
Object.assign(model, defaultValues(initialValues));
|
|
293
295
|
|
|
294
296
|
// Inheritance
|
|
295
297
|
vtkAbstractImageMapper.extend(publicAPI, model, initialValues);
|
|
@@ -302,7 +304,7 @@ function extend(publicAPI, model) {
|
|
|
302
304
|
});
|
|
303
305
|
|
|
304
306
|
// Setters and getters
|
|
305
|
-
macro.setGet(publicAPI, model, ['width', 'uniformOrientation', 'useUniformOrientation', 'centerPoint', 'preferSizeOverAccuracy', 'orientationArrayName', 'tangentDirection', 'bitangentDirection', 'normalDirection', 'projectionSlabThickness', 'projectionSlabNumberOfSamples', 'projectionMode']);
|
|
307
|
+
macro.setGet(publicAPI, model, ['width', 'uniformOrientation', 'useUniformOrientation', 'centerPoint', 'preferSizeOverAccuracy', 'orientationArrayName', 'tangentDirection', 'bitangentDirection', 'normalDirection', 'projectionSlabThickness', 'projectionSlabNumberOfSamples', 'projectionMode', 'updatedExtents']);
|
|
306
308
|
CoincidentTopologyHelper.implementCoincidentTopologyMethods(publicAPI, model);
|
|
307
309
|
|
|
308
310
|
// Object methods
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import vtkAbstractImageMapper, {
|
|
2
2
|
IAbstractImageMapperInitialValues,
|
|
3
3
|
} from './AbstractImageMapper';
|
|
4
|
-
import vtkImageData from './../../Common/DataModel/ImageData';
|
|
5
4
|
import vtkPlane from './../../Common/DataModel/Plane';
|
|
6
5
|
import vtkPolyData from './../../Common/DataModel/PolyData';
|
|
7
|
-
import { Bounds,
|
|
6
|
+
import { Bounds, Extent } from './../../types';
|
|
8
7
|
import { SlabTypes } from './ImageResliceMapper/Constants';
|
|
9
8
|
import CoincidentTopologyHelper, {
|
|
10
9
|
StaticCoincidentTopologyMethods,
|
|
@@ -116,6 +115,25 @@ export interface vtkImageResliceMapper
|
|
|
116
115
|
* @param {vtkPolyData} slicePolyData The polydata to slice the volume with. Default: null
|
|
117
116
|
*/
|
|
118
117
|
setSlicePolyData(slicePolyData: vtkPolyData): boolean;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Tells the mapper to only update the specified extents.
|
|
121
|
+
*
|
|
122
|
+
* If there are zero extents, the mapper updates the entire volume texture.
|
|
123
|
+
* Otherwise, the mapper will only update the texture by the specified extents
|
|
124
|
+
* during the next render call.
|
|
125
|
+
*
|
|
126
|
+
* This array is cleared after a successful render.
|
|
127
|
+
* @param extents
|
|
128
|
+
*/
|
|
129
|
+
setUpdatedExtents(extents: Extent[]): boolean;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Retrieves the updated extents.
|
|
133
|
+
*
|
|
134
|
+
* This array is cleared after every successful render.
|
|
135
|
+
*/
|
|
136
|
+
getUpdatedExtents(): Extent[];
|
|
119
137
|
}
|
|
120
138
|
|
|
121
139
|
/**
|
|
@@ -38,23 +38,25 @@ function vtkImageResliceMapper(publicAPI, model) {
|
|
|
38
38
|
// Object factory
|
|
39
39
|
// ----------------------------------------------------------------------------
|
|
40
40
|
|
|
41
|
-
const
|
|
41
|
+
const defaultValues = initialValues => ({
|
|
42
42
|
slabThickness: 0.0,
|
|
43
43
|
slabTrapezoidIntegration: 0,
|
|
44
44
|
slabType: SlabTypes.MEAN,
|
|
45
45
|
slicePlane: null,
|
|
46
|
-
slicePolyData: null
|
|
47
|
-
|
|
46
|
+
slicePolyData: null,
|
|
47
|
+
updatedExtents: [],
|
|
48
|
+
...initialValues
|
|
49
|
+
});
|
|
48
50
|
|
|
49
51
|
// ----------------------------------------------------------------------------
|
|
50
52
|
|
|
51
53
|
function extend(publicAPI, model) {
|
|
52
54
|
let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
53
|
-
Object.assign(model,
|
|
55
|
+
Object.assign(model, defaultValues(initialValues));
|
|
54
56
|
|
|
55
57
|
// Build VTK API
|
|
56
58
|
vtkAbstractImageMapper.extend(publicAPI, model, initialValues);
|
|
57
|
-
macro.setGet(publicAPI, model, ['slabThickness', 'slabTrapezoidIntegration', 'slabType', 'slicePlane', 'slicePolyData']);
|
|
59
|
+
macro.setGet(publicAPI, model, ['slabThickness', 'slabTrapezoidIntegration', 'slabType', 'slicePlane', 'slicePolyData', 'updatedExtents']);
|
|
58
60
|
CoincidentTopologyHelper.implementCoincidentTopologyMethods(publicAPI, model);
|
|
59
61
|
|
|
60
62
|
// Object methods
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import vtkPiecewiseFunction from './../../Common/DataModel/PiecewiseFunction';
|
|
2
|
-
import { Bounds, Range } from './../../types';
|
|
2
|
+
import { Bounds, Range, Extent } from './../../types';
|
|
3
3
|
import vtkAbstractMapper3D, {
|
|
4
4
|
IAbstractMapper3DInitialValues,
|
|
5
5
|
} from './AbstractMapper3D';
|
|
@@ -281,6 +281,39 @@ export interface vtkVolumeMapper extends vtkAbstractMapper3D {
|
|
|
281
281
|
*/
|
|
282
282
|
setLAOKernelRadius(LAOKernelRadius: number): void;
|
|
283
283
|
|
|
284
|
+
/**
|
|
285
|
+
* Set kernel size for local ambient occlusion. It specifies the number of rays that are randomly sampled in the hemisphere.
|
|
286
|
+
* Value is clipped between 1 and 32.
|
|
287
|
+
* @param LAOKernelSize
|
|
288
|
+
*/
|
|
289
|
+
setLAOKernelSize(LAOKernelSize: number): void;
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Set kernel radius for local ambient occlusion. It specifies the number of samples that are considered on each random ray.
|
|
293
|
+
* Value must be greater than or equal to 1.
|
|
294
|
+
* @param LAOKernelRadius
|
|
295
|
+
*/
|
|
296
|
+
setLAOKernelRadius(LAOKernelRadius: number): void;
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Tells the mapper to only update the specified extents.
|
|
300
|
+
*
|
|
301
|
+
* If there are zero extents, the mapper updates the entire volume texture.
|
|
302
|
+
* Otherwise, the mapper will only update the texture by the specified extents
|
|
303
|
+
* during the next render call.
|
|
304
|
+
*
|
|
305
|
+
* This array is cleared after a successful render.
|
|
306
|
+
* @param extents
|
|
307
|
+
*/
|
|
308
|
+
setUpdatedExtents(extents: Extent[]): boolean;
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Retrieves the updated extents.
|
|
312
|
+
*
|
|
313
|
+
* This array is cleared after every successful render.
|
|
314
|
+
*/
|
|
315
|
+
getUpdatedExtents(): Extent[];
|
|
316
|
+
|
|
284
317
|
/**
|
|
285
318
|
*
|
|
286
319
|
*/
|
|
@@ -101,7 +101,7 @@ function vtkVolumeMapper(publicAPI, model) {
|
|
|
101
101
|
// ----------------------------------------------------------------------------
|
|
102
102
|
|
|
103
103
|
// TODO: what values to use for averageIPScalarRange to get GLSL to use max / min values like [-Math.inf, Math.inf]?
|
|
104
|
-
const
|
|
104
|
+
const defaultValues = initialValues => ({
|
|
105
105
|
bounds: [1, -1, 1, -1, 1, -1],
|
|
106
106
|
sampleDistance: 1.0,
|
|
107
107
|
imageSampleDistance: 1.0,
|
|
@@ -124,16 +124,18 @@ const DEFAULT_VALUES = {
|
|
|
124
124
|
// local ambient occlusion
|
|
125
125
|
localAmbientOcclusion: false,
|
|
126
126
|
LAOKernelSize: 15,
|
|
127
|
-
LAOKernelRadius: 7
|
|
128
|
-
|
|
127
|
+
LAOKernelRadius: 7,
|
|
128
|
+
updatedExtents: [],
|
|
129
|
+
...initialValues
|
|
130
|
+
});
|
|
129
131
|
|
|
130
132
|
// ----------------------------------------------------------------------------
|
|
131
133
|
|
|
132
134
|
function extend(publicAPI, model) {
|
|
133
135
|
let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
134
|
-
Object.assign(model,
|
|
136
|
+
Object.assign(model, defaultValues(initialValues));
|
|
135
137
|
vtkAbstractMapper3D.extend(publicAPI, model, initialValues);
|
|
136
|
-
macro.setGet(publicAPI, model, ['sampleDistance', 'imageSampleDistance', 'maximumSamplesPerRay', 'autoAdjustSampleDistances', 'initialInteractionScale', 'interactionSampleDistanceFactor', 'blendMode', 'filterMode', 'preferSizeOverAccuracy', 'computeNormalFromOpacity', 'volumetricScatteringBlending', 'globalIlluminationReach', 'volumeShadowSamplingDistFactor', 'anisotropy', 'localAmbientOcclusion', 'LAOKernelSize', 'LAOKernelRadius']);
|
|
138
|
+
macro.setGet(publicAPI, model, ['sampleDistance', 'imageSampleDistance', 'maximumSamplesPerRay', 'autoAdjustSampleDistances', 'initialInteractionScale', 'interactionSampleDistanceFactor', 'blendMode', 'filterMode', 'preferSizeOverAccuracy', 'computeNormalFromOpacity', 'volumetricScatteringBlending', 'globalIlluminationReach', 'volumeShadowSamplingDistFactor', 'anisotropy', 'localAmbientOcclusion', 'LAOKernelSize', 'LAOKernelRadius', 'updatedExtents']);
|
|
137
139
|
macro.setGetArray(publicAPI, model, ['ipScalarRange'], 2);
|
|
138
140
|
macro.event(publicAPI, model, 'lightingActivated');
|
|
139
141
|
|
|
@@ -156,6 +156,8 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
156
156
|
const cachedScalarsEntry = model._openGLRenderWindow.getGraphicsResourceForObject(scalars);
|
|
157
157
|
const volumeTextureHash = getImageDataHash(image, scalars);
|
|
158
158
|
const reBuildTex = !cachedScalarsEntry?.oglObject?.getHandle() || cachedScalarsEntry?.hash !== volumeTextureHash;
|
|
159
|
+
const updatedExtents = model.renderable.getUpdatedExtents();
|
|
160
|
+
const hasUpdatedExtents = !!updatedExtents.length;
|
|
159
161
|
if (reBuildTex) {
|
|
160
162
|
model.volumeTexture = vtkOpenGLTexture.newInstance();
|
|
161
163
|
model.volumeTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
|
|
@@ -174,6 +176,13 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
174
176
|
} else {
|
|
175
177
|
model.volumeTexture = cachedScalarsEntry.oglObject;
|
|
176
178
|
}
|
|
179
|
+
if (hasUpdatedExtents) {
|
|
180
|
+
// If hasUpdatedExtents, then the texture is partially updated.
|
|
181
|
+
// clear the array to acknowledge the update.
|
|
182
|
+
model.renderable.setUpdatedExtents([]);
|
|
183
|
+
const dims = image.getDimensions();
|
|
184
|
+
model.volumeTexture.create3DFilterableFromDataArray(dims[0], dims[1], dims[2], scalars, false, updatedExtents);
|
|
185
|
+
}
|
|
177
186
|
|
|
178
187
|
// Rebuild the color texture if needed
|
|
179
188
|
const numComp = scalars.getNumberOfComponents();
|
|
@@ -863,10 +863,25 @@ function vtkOpenGLImageMapper(publicAPI, model) {
|
|
|
863
863
|
vtkErrorMacro('Reformat slicing not yet supported.');
|
|
864
864
|
}
|
|
865
865
|
|
|
866
|
+
/**
|
|
867
|
+
*
|
|
868
|
+
* Fetch the ranges of the source volume, `imgScalars`, and use them when
|
|
869
|
+
* creating the texture. Whilst the pre-calculated ranges may not be
|
|
870
|
+
* strictly correct for the slice, it is guaranteed to be within the
|
|
871
|
+
* source volume's range.
|
|
872
|
+
*
|
|
873
|
+
* There is a significant performance improvement by pre-setting the range
|
|
874
|
+
* of the scalars array particularly when scrolling through the source
|
|
875
|
+
* volume as there is no need to calculate the range of the slice scalar.
|
|
876
|
+
*
|
|
877
|
+
* @type{ import("../../../interfaces").vtkRange[] }
|
|
878
|
+
*/
|
|
879
|
+
const ranges = imgScalars.getRanges();
|
|
880
|
+
|
|
866
881
|
// Don't share this resource as `scalars` is created in this function
|
|
867
882
|
// so it is impossible to share
|
|
868
883
|
model.openGLTexture.resetFormatAndType();
|
|
869
|
-
model.openGLTexture.create2DFilterableFromRaw(dims[0], dims[1], numComp, imgScalars.getDataType(), scalars, model.renderable.getPreferSizeOverAccuracy?.());
|
|
884
|
+
model.openGLTexture.create2DFilterableFromRaw(dims[0], dims[1], numComp, imgScalars.getDataType(), scalars, model.renderable.getPreferSizeOverAccuracy?.(), ranges);
|
|
870
885
|
model.openGLTexture.activate();
|
|
871
886
|
model.openGLTexture.sendParameters();
|
|
872
887
|
model.openGLTexture.deactivate();
|
|
@@ -185,15 +185,19 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
|
|
|
185
185
|
let toString = getImageDataHash(image, scalars);
|
|
186
186
|
const tex = model._openGLRenderWindow.getGraphicsResourceForObject(scalars);
|
|
187
187
|
const reBuildTex = !tex?.oglObject?.getHandle() || tex?.hash !== toString;
|
|
188
|
-
|
|
188
|
+
const updatedExtents = model.renderable.getUpdatedExtents();
|
|
189
|
+
const hasUpdatedExtents = !!updatedExtents.length;
|
|
190
|
+
if (reBuildTex && !hasUpdatedExtents) {
|
|
189
191
|
model.openGLTexture = vtkOpenGLTexture.newInstance();
|
|
190
192
|
model.openGLTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
|
|
191
193
|
// Build the image scalar texture
|
|
192
|
-
const dims = image.getDimensions();
|
|
193
194
|
// Use norm16 for the 3D texture if the extension is available
|
|
194
195
|
model.openGLTexture.setOglNorm16Ext(model.context.getExtension('EXT_texture_norm16'));
|
|
195
196
|
model.openGLTexture.resetFormatAndType();
|
|
196
|
-
|
|
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);
|
|
197
201
|
model._openGLRenderWindow.setGraphicsResourceForObject(scalars, model.openGLTexture, toString);
|
|
198
202
|
if (scalars !== model._scalars) {
|
|
199
203
|
model._openGLRenderWindow.registerGraphicsResourceUser(scalars, publicAPI);
|
|
@@ -203,6 +207,13 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
|
|
|
203
207
|
} else {
|
|
204
208
|
model.openGLTexture = tex.oglObject;
|
|
205
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
|
+
}
|
|
206
217
|
const ppty = actor.getProperty();
|
|
207
218
|
const iComps = ppty.getIndependentComponents();
|
|
208
219
|
const numIComps = iComps ? numComp : 1;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Wrap, Filter } from './Texture/Constants';
|
|
2
2
|
import vtkOpenGLRenderWindow from './RenderWindow';
|
|
3
|
-
import { Nullable } from './../../types';
|
|
3
|
+
import { Extent, Nullable } from './../../types';
|
|
4
4
|
import { VtkDataTypes } from './../../Common/Core/DataArray';
|
|
5
5
|
import { vtkViewNode } from './../SceneGraph/ViewNode';
|
|
6
|
-
import { vtkObject } from './../../interfaces';
|
|
6
|
+
import { vtkObject, vtkRange } from './../../interfaces';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Initial values for creating a new instance of vtkOpenGLTexture.
|
|
@@ -244,7 +244,8 @@ export interface vtkOpenGLTexture extends vtkViewNode {
|
|
|
244
244
|
* @param numComps The number of components in the texture.
|
|
245
245
|
* @param dataType The data type of the texture.
|
|
246
246
|
* @param data The raw data for the texture.
|
|
247
|
-
* @param preferSizeOverAccuracy Whether to prefer texture size over accuracy.
|
|
247
|
+
* @param [preferSizeOverAccuracy=false] Whether to prefer texture size over accuracy. Defaults to false.
|
|
248
|
+
* @param [ranges] The precomputed ranges of the data (optional). Provided to prevent computation of the data ranges.
|
|
248
249
|
* @returns {boolean} True if the texture was successfully created, false otherwise.
|
|
249
250
|
*/
|
|
250
251
|
create2DFilterableFromRaw(
|
|
@@ -253,7 +254,8 @@ export interface vtkOpenGLTexture extends vtkViewNode {
|
|
|
253
254
|
numComps: number,
|
|
254
255
|
dataType: VtkDataTypes,
|
|
255
256
|
data: any,
|
|
256
|
-
preferSizeOverAccuracy
|
|
257
|
+
preferSizeOverAccuracy?: boolean,
|
|
258
|
+
ranges?: vtkRange[]
|
|
257
259
|
): boolean;
|
|
258
260
|
|
|
259
261
|
/**
|
|
@@ -273,12 +275,16 @@ export interface vtkOpenGLTexture extends vtkViewNode {
|
|
|
273
275
|
|
|
274
276
|
/**
|
|
275
277
|
* Creates a 3D texture from raw data.
|
|
278
|
+
*
|
|
279
|
+
* updatedExtents is currently incompatible with webgl1, since there's no extent scaling.
|
|
280
|
+
*
|
|
276
281
|
* @param width The width of the texture.
|
|
277
282
|
* @param height The height of the texture.
|
|
278
283
|
* @param depth The depth of the texture.
|
|
279
284
|
* @param numComps The number of components in the texture.
|
|
280
285
|
* @param dataType The data type of the texture.
|
|
281
286
|
* @param data The raw data for the texture.
|
|
287
|
+
* @param updatedExtents Only update the specified extents (default: [])
|
|
282
288
|
* @returns {boolean} True if the texture was successfully created, false otherwise.
|
|
283
289
|
*/
|
|
284
290
|
create3DFromRaw(
|
|
@@ -287,11 +293,15 @@ export interface vtkOpenGLTexture extends vtkViewNode {
|
|
|
287
293
|
depth: number,
|
|
288
294
|
numComps: number,
|
|
289
295
|
dataType: VtkDataTypes,
|
|
290
|
-
data: any
|
|
296
|
+
data: any,
|
|
297
|
+
updatedExtents?: Extent[]
|
|
291
298
|
): boolean;
|
|
292
299
|
|
|
293
300
|
/**
|
|
294
301
|
* Creates a 3D filterable texture from raw data, with a preference for size over accuracy if necessary.
|
|
302
|
+
*
|
|
303
|
+
* updatedExtents is currently incompatible with webgl1, since there's no extent scaling.
|
|
304
|
+
*
|
|
295
305
|
* @param width The width of the texture.
|
|
296
306
|
* @param height The height of the texture.
|
|
297
307
|
* @param depth The depth of the texture.
|
|
@@ -299,7 +309,11 @@ export interface vtkOpenGLTexture extends vtkViewNode {
|
|
|
299
309
|
* @param dataType The data type of the texture.
|
|
300
310
|
* @param values The raw data for the texture.
|
|
301
311
|
* @param preferSizeOverAccuracy Whether to prefer texture size over accuracy.
|
|
302
|
-
* @
|
|
312
|
+
* @param [ranges] The precomputed ranges of the data (optional). Provided to
|
|
313
|
+
* @param updatedExtents Only update the specified extents (default: [])
|
|
314
|
+
* prevent computation of the data ranges.
|
|
315
|
+
* @returns {boolean} True if the texture was successfully created, false
|
|
316
|
+
* otherwise.
|
|
303
317
|
*/
|
|
304
318
|
create3DFilterableFromRaw(
|
|
305
319
|
width: number,
|
|
@@ -308,16 +322,22 @@ export interface vtkOpenGLTexture extends vtkViewNode {
|
|
|
308
322
|
numComps: number,
|
|
309
323
|
dataType: VtkDataTypes,
|
|
310
324
|
values: any,
|
|
311
|
-
preferSizeOverAccuracy: boolean
|
|
325
|
+
preferSizeOverAccuracy: boolean,
|
|
326
|
+
ranges?: vtkRange[],
|
|
327
|
+
updatedExtents?: Extent[]
|
|
312
328
|
): boolean;
|
|
313
329
|
|
|
314
330
|
/**
|
|
315
331
|
* Creates a 3D filterable texture from a data array, with a preference for size over accuracy if necessary.
|
|
332
|
+
*
|
|
333
|
+
* updatedExtents is currently incompatible with webgl1, since there's no extent scaling.
|
|
334
|
+
*
|
|
316
335
|
* @param width The width of the texture.
|
|
317
336
|
* @param height The height of the texture.
|
|
318
337
|
* @param depth The depth of the texture.
|
|
319
338
|
* @param dataArray The data array to use for the texture.
|
|
320
339
|
* @param preferSizeOverAccuracy Whether to prefer texture size over accuracy.
|
|
340
|
+
* @param updatedExtents Only update the specified extents (default: [])
|
|
321
341
|
* @returns {boolean} True if the texture was successfully created, false otherwise.
|
|
322
342
|
*/
|
|
323
343
|
create3DFilterableFromDataArray(
|
|
@@ -325,7 +345,8 @@ export interface vtkOpenGLTexture extends vtkViewNode {
|
|
|
325
345
|
height: number,
|
|
326
346
|
depth: number,
|
|
327
347
|
dataArray: any,
|
|
328
|
-
preferSizeOverAccuracy: boolean
|
|
348
|
+
preferSizeOverAccuracy: boolean,
|
|
349
|
+
updatedExtents?: Extent[]
|
|
329
350
|
): boolean;
|
|
330
351
|
|
|
331
352
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import DeepEqual from 'fast-deep-equal';
|
|
1
2
|
import Constants from './Texture/Constants.js';
|
|
2
3
|
import HalfFloat from '../../Common/Core/HalfFloat.js';
|
|
3
4
|
import { n as newInstance$1, o as obj, s as set, e as setGet, g as get, i as moveToProtected, a as newTypedArray, c as macro } from '../../macros2.js';
|
|
@@ -30,6 +31,16 @@ const {
|
|
|
30
31
|
function vtkOpenGLTexture(publicAPI, model) {
|
|
31
32
|
// Set our className
|
|
32
33
|
model.classHierarchy.push('vtkOpenGLTexture');
|
|
34
|
+
function getTexParams() {
|
|
35
|
+
return {
|
|
36
|
+
internalFormat: model.internalFormat,
|
|
37
|
+
format: model.format,
|
|
38
|
+
openGLDataType: model.openGLDataType,
|
|
39
|
+
width: model.width,
|
|
40
|
+
height: model.height
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
33
44
|
// Renders myself
|
|
34
45
|
publicAPI.render = function () {
|
|
35
46
|
let renWin = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
|
|
@@ -148,6 +159,7 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
148
159
|
if (model.context && model.handle) {
|
|
149
160
|
model.context.deleteTexture(model.handle);
|
|
150
161
|
}
|
|
162
|
+
model._prevTexParams = null;
|
|
151
163
|
model.handle = 0;
|
|
152
164
|
model.numberOfDimensions = 0;
|
|
153
165
|
model.target = 0;
|
|
@@ -209,6 +221,7 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
209
221
|
rwin.activateTexture(publicAPI);
|
|
210
222
|
rwin.deactivateTexture(publicAPI);
|
|
211
223
|
model.context.deleteTexture(model.handle);
|
|
224
|
+
model._prevTexParams = null;
|
|
212
225
|
model.handle = 0;
|
|
213
226
|
model.numberOfDimensions = 0;
|
|
214
227
|
model.target = 0;
|
|
@@ -353,6 +366,7 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
353
366
|
|
|
354
367
|
//----------------------------------------------------------------------------
|
|
355
368
|
publicAPI.resetFormatAndType = () => {
|
|
369
|
+
model._prevTexParams = null;
|
|
356
370
|
model.format = 0;
|
|
357
371
|
model.internalFormat = 0;
|
|
358
372
|
model._forceInternalFormat = false;
|
|
@@ -499,6 +513,86 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
499
513
|
}
|
|
500
514
|
};
|
|
501
515
|
|
|
516
|
+
//----------------------------------------------------------------------------
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Gets the extent's size.
|
|
520
|
+
* @param {Extent} extent
|
|
521
|
+
*/
|
|
522
|
+
function getExtentSize(extent) {
|
|
523
|
+
const [xmin, xmax, ymin, ymax, zmin, zmax] = extent;
|
|
524
|
+
return [xmax - xmin + 1, ymax - ymin + 1, zmax - zmin + 1];
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
//----------------------------------------------------------------------------
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Gets the number of pixels in the extent.
|
|
531
|
+
* @param {Extent} extent
|
|
532
|
+
*/
|
|
533
|
+
function getExtentPixelCount(extent) {
|
|
534
|
+
const [sx, sy, sz] = getExtentSize(extent);
|
|
535
|
+
return sx * sy * sz;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
//----------------------------------------------------------------------------
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Reads a flattened extent from the image data and writes to the given output array.
|
|
542
|
+
*
|
|
543
|
+
* Assumes X varies the fastest and Z varies the slowest.
|
|
544
|
+
*
|
|
545
|
+
* @param {*} data
|
|
546
|
+
* @param {*} dataDims
|
|
547
|
+
* @param {Extent} extent
|
|
548
|
+
* @param {TypedArray} outArray
|
|
549
|
+
* @param {number} outOffset
|
|
550
|
+
* @returns
|
|
551
|
+
*/
|
|
552
|
+
function readExtentIntoArray(data, dataDims, extent, outArray, outOffset) {
|
|
553
|
+
const [xmin, xmax, ymin, ymax, zmin, zmax] = extent;
|
|
554
|
+
const [dx, dy] = dataDims;
|
|
555
|
+
const sxy = dx * dy;
|
|
556
|
+
let writeOffset = outOffset;
|
|
557
|
+
for (let zi = zmin; zi <= zmax; zi++) {
|
|
558
|
+
const zOffset = zi * sxy;
|
|
559
|
+
for (let yi = ymin; yi <= ymax; yi++) {
|
|
560
|
+
const zyOffset = zOffset + yi * dx;
|
|
561
|
+
// explicit alternative to data.subarray,
|
|
562
|
+
// due to potential perf issues on v8
|
|
563
|
+
for (let readOffset = zyOffset + xmin, end = zyOffset + xmax; readOffset <= end; readOffset++, writeOffset++) {
|
|
564
|
+
outArray[writeOffset] = data[readOffset];
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
//----------------------------------------------------------------------------
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Reads several image extents into a contiguous pixel array.
|
|
574
|
+
*
|
|
575
|
+
* @param {*} data
|
|
576
|
+
* @param {Extent[]} extent
|
|
577
|
+
* @param {TypedArrayConstructor} typedArrayConstructor optional typed array constructor
|
|
578
|
+
* @returns
|
|
579
|
+
*/
|
|
580
|
+
function readExtents(data, extents) {
|
|
581
|
+
let typedArrayConstructor = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
|
|
582
|
+
const constructor = typedArrayConstructor || data.constructor;
|
|
583
|
+
const numPixels = extents.reduce((count, extent) => count + getExtentPixelCount(extent), 0);
|
|
584
|
+
const extentPixels = new constructor(numPixels);
|
|
585
|
+
const dataDims = [model.width, model.height, model.depth];
|
|
586
|
+
let writeOffset = 0;
|
|
587
|
+
extents.forEach(extent => {
|
|
588
|
+
readExtentIntoArray(data, dataDims, extent, extentPixels, writeOffset);
|
|
589
|
+
writeOffset += getExtentPixelCount(extent);
|
|
590
|
+
});
|
|
591
|
+
return extentPixels;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
//----------------------------------------------------------------------------
|
|
595
|
+
|
|
502
596
|
/**
|
|
503
597
|
* Updates the data array to match the required data type for OpenGL.
|
|
504
598
|
*
|
|
@@ -508,23 +602,30 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
508
602
|
* @param {string} dataType - The original data type of the input data.
|
|
509
603
|
* @param {Array} data - The input data array that needs to be updated.
|
|
510
604
|
* @param {boolean} [depth=false] - Indicates whether the data is a 3D array.
|
|
605
|
+
* @param {Array<Extent>} imageExtents only consider these image extents (default: [])
|
|
511
606
|
* @returns {Array} The updated data array that matches the OpenGL data type.
|
|
512
607
|
*/
|
|
513
608
|
publicAPI.updateArrayDataTypeForGL = function (dataType, data) {
|
|
514
609
|
let depth = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
610
|
+
let imageExtents = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : [];
|
|
515
611
|
const pixData = [];
|
|
516
612
|
let pixCount = model.width * model.height * model.components;
|
|
517
613
|
if (depth) {
|
|
518
614
|
pixCount *= model.depth;
|
|
519
615
|
}
|
|
616
|
+
const onlyUpdateExtents = !!imageExtents.length;
|
|
520
617
|
|
|
521
618
|
// if the opengl data type is float
|
|
522
619
|
// then the data array must be float
|
|
523
620
|
if (dataType !== VtkDataTypes.FLOAT && model.openGLDataType === model.context.FLOAT) {
|
|
524
621
|
for (let idx = 0; idx < data.length; idx++) {
|
|
525
622
|
if (data[idx]) {
|
|
526
|
-
|
|
527
|
-
|
|
623
|
+
if (onlyUpdateExtents) {
|
|
624
|
+
pixData.push(readExtents(data[idx], imageExtents, Float32Array));
|
|
625
|
+
} else {
|
|
626
|
+
const dataArrayToCopy = data[idx].length > pixCount ? data[idx].subarray(0, pixCount) : data[idx];
|
|
627
|
+
pixData.push(new Float32Array(dataArrayToCopy));
|
|
628
|
+
}
|
|
528
629
|
} else {
|
|
529
630
|
pixData.push(null);
|
|
530
631
|
}
|
|
@@ -536,8 +637,12 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
536
637
|
if (dataType !== VtkDataTypes.UNSIGNED_CHAR && model.openGLDataType === model.context.UNSIGNED_BYTE) {
|
|
537
638
|
for (let idx = 0; idx < data.length; idx++) {
|
|
538
639
|
if (data[idx]) {
|
|
539
|
-
|
|
540
|
-
|
|
640
|
+
if (onlyUpdateExtents) {
|
|
641
|
+
pixData.push(readExtents(data[idx], imageExtents, Uint8Array));
|
|
642
|
+
} else {
|
|
643
|
+
const dataArrayToCopy = data[idx].length > pixCount ? data[idx].subarray(0, pixCount) : data[idx];
|
|
644
|
+
pixData.push(new Uint8Array(dataArrayToCopy));
|
|
645
|
+
}
|
|
541
646
|
} else {
|
|
542
647
|
pixData.push(null);
|
|
543
648
|
}
|
|
@@ -556,9 +661,10 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
556
661
|
if (halfFloat) {
|
|
557
662
|
for (let idx = 0; idx < data.length; idx++) {
|
|
558
663
|
if (data[idx]) {
|
|
559
|
-
const
|
|
560
|
-
const
|
|
561
|
-
|
|
664
|
+
const src = onlyUpdateExtents ? readExtents(data[idx], imageExtents) : data[idx];
|
|
665
|
+
const newArray = new Uint16Array(onlyUpdateExtents ? src.length : pixCount);
|
|
666
|
+
const newArrayLen = newArray.length;
|
|
667
|
+
for (let i = 0; i < newArrayLen; i++) {
|
|
562
668
|
newArray[i] = toHalf(src[i]);
|
|
563
669
|
}
|
|
564
670
|
pixData.push(newArray);
|
|
@@ -571,7 +677,7 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
571
677
|
// The output has to be filled
|
|
572
678
|
if (pixData.length === 0) {
|
|
573
679
|
for (let i = 0; i < data.length; i++) {
|
|
574
|
-
pixData.push(data[i]);
|
|
680
|
+
pixData.push(onlyUpdateExtents && data[i] ? readExtents(data[i], imageExtents) : data[i]);
|
|
575
681
|
}
|
|
576
682
|
}
|
|
577
683
|
return pixData;
|
|
@@ -986,10 +1092,12 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
986
1092
|
}
|
|
987
1093
|
publicAPI.create2DFilterableFromRaw = function (width, height, numberOfComponents, dataType, values) {
|
|
988
1094
|
let preferSizeOverAccuracy = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;
|
|
1095
|
+
let ranges = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : undefined;
|
|
989
1096
|
return publicAPI.create2DFilterableFromDataArray(width, height, vtkDataArray.newInstance({
|
|
990
1097
|
numberOfComponents,
|
|
991
1098
|
dataType,
|
|
992
|
-
values
|
|
1099
|
+
values,
|
|
1100
|
+
ranges
|
|
993
1101
|
}), preferSizeOverAccuracy);
|
|
994
1102
|
};
|
|
995
1103
|
publicAPI.create2DFilterableFromDataArray = function (width, height, dataArray) {
|
|
@@ -1054,7 +1162,8 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
1054
1162
|
};
|
|
1055
1163
|
|
|
1056
1164
|
//----------------------------------------------------------------------------
|
|
1057
|
-
publicAPI.create3DFromRaw = (width, height, depth, numComps, dataType, data)
|
|
1165
|
+
publicAPI.create3DFromRaw = function (width, height, depth, numComps, dataType, data) {
|
|
1166
|
+
let updatedExtents = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : [];
|
|
1058
1167
|
let dataTypeToUse = dataType;
|
|
1059
1168
|
let dataToUse = data;
|
|
1060
1169
|
if (!publicAPI.updateVolumeInfoForGL(dataTypeToUse, numComps) && dataToUse) {
|
|
@@ -1096,25 +1205,42 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
1096
1205
|
model._openGLRenderWindow.activateTexture(publicAPI);
|
|
1097
1206
|
publicAPI.createTexture();
|
|
1098
1207
|
publicAPI.bind();
|
|
1208
|
+
const hasUpdatedExtents = updatedExtents.length > 0;
|
|
1209
|
+
|
|
1210
|
+
// It's possible for the texture parameters to change while
|
|
1211
|
+
// streaming, so check for such a change.
|
|
1212
|
+
const rebuildEntireTexture = !hasUpdatedExtents || !DeepEqual(model._prevTexParams, getTexParams());
|
|
1213
|
+
|
|
1099
1214
|
// Create an array of texture with one texture
|
|
1100
1215
|
const dataArray = [dataToUse];
|
|
1101
1216
|
const is3DArray = true;
|
|
1102
|
-
const pixData = publicAPI.updateArrayDataTypeForGL(dataTypeToUse, dataArray, is3DArray);
|
|
1217
|
+
const pixData = publicAPI.updateArrayDataTypeForGL(dataTypeToUse, dataArray, is3DArray, rebuildEntireTexture ? [] : updatedExtents);
|
|
1103
1218
|
const scaledData = scaleTextureToHighestPowerOfTwo(pixData);
|
|
1104
1219
|
|
|
1105
1220
|
// Source texture data from the PBO.
|
|
1106
1221
|
// model.context.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
|
|
1107
1222
|
model.context.pixelStorei(model.context.UNPACK_ALIGNMENT, 1);
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1223
|
+
if (rebuildEntireTexture) {
|
|
1224
|
+
if (useTexStorage(dataTypeToUse)) {
|
|
1225
|
+
model.context.texStorage3D(model.target, 1, model.internalFormat, model.width, model.height, model.depth);
|
|
1226
|
+
if (scaledData[0] != null) {
|
|
1227
|
+
model.context.texSubImage3D(model.target, 0, 0, 0, 0, model.width, model.height, model.depth, model.format, model.openGLDataType, scaledData[0]);
|
|
1228
|
+
}
|
|
1229
|
+
} else {
|
|
1230
|
+
model.context.texImage3D(model.target, 0, model.internalFormat, model.width, model.height, model.depth, 0, model.format, model.openGLDataType, scaledData[0]);
|
|
1231
|
+
}
|
|
1232
|
+
model._prevTexParams = getTexParams();
|
|
1233
|
+
} else if (hasUpdatedExtents) {
|
|
1234
|
+
const extentPixels = scaledData[0];
|
|
1235
|
+
let readOffset = 0;
|
|
1236
|
+
for (let i = 0; i < updatedExtents.length; i++) {
|
|
1237
|
+
const extent = updatedExtents[i];
|
|
1238
|
+
const extentSize = getExtentSize(extent);
|
|
1239
|
+
const extentPixelCount = getExtentPixelCount(extent);
|
|
1240
|
+
const textureData = new extentPixels.constructor(extentPixels.buffer, readOffset, extentPixelCount);
|
|
1241
|
+
readOffset += textureData.byteLength;
|
|
1242
|
+
model.context.texSubImage3D(model.target, 0, extent[0], extent[2], extent[4], extentSize[0], extentSize[1], extentSize[2], model.format, model.openGLDataType, textureData);
|
|
1115
1243
|
}
|
|
1116
|
-
} else {
|
|
1117
|
-
model.context.texImage3D(model.target, 0, model.internalFormat, model.width, model.height, model.depth, 0, model.format, model.openGLDataType, scaledData[0]);
|
|
1118
1244
|
}
|
|
1119
1245
|
if (model.generateMipmap) {
|
|
1120
1246
|
model.context.generateMipmap(model.target);
|
|
@@ -1129,17 +1255,21 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
1129
1255
|
// Prefer create3DFilterableFromDataArray to enable caching of min and max values
|
|
1130
1256
|
publicAPI.create3DFilterableFromRaw = function (width, height, depth, numberOfComponents, dataType, values) {
|
|
1131
1257
|
let preferSizeOverAccuracy = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false;
|
|
1258
|
+
let ranges = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : undefined;
|
|
1259
|
+
let updatedExtents = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : [];
|
|
1132
1260
|
return publicAPI.create3DFilterableFromDataArray(width, height, depth, vtkDataArray.newInstance({
|
|
1133
1261
|
numberOfComponents,
|
|
1134
1262
|
dataType,
|
|
1135
|
-
values
|
|
1136
|
-
|
|
1263
|
+
values,
|
|
1264
|
+
ranges
|
|
1265
|
+
}), preferSizeOverAccuracy, updatedExtents);
|
|
1137
1266
|
};
|
|
1138
1267
|
|
|
1139
1268
|
//----------------------------------------------------------------------------
|
|
1140
1269
|
// This method create a 3D texture from dimensions and a DataArray
|
|
1141
1270
|
publicAPI.create3DFilterableFromDataArray = function (width, height, depth, dataArray) {
|
|
1142
1271
|
let preferSizeOverAccuracy = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
|
|
1272
|
+
let updatedExtents = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : [];
|
|
1143
1273
|
const {
|
|
1144
1274
|
numComps,
|
|
1145
1275
|
dataType,
|
|
@@ -1174,7 +1304,7 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
1174
1304
|
|
|
1175
1305
|
// WebGL2 path, we have 3d textures etc
|
|
1176
1306
|
if (model._openGLRenderWindow.getWebgl2()) {
|
|
1177
|
-
return publicAPI.create3DFromRaw(width, height, depth, numComps, dataType, data);
|
|
1307
|
+
return publicAPI.create3DFromRaw(width, height, depth, numComps, dataType, data, updatedExtents);
|
|
1178
1308
|
}
|
|
1179
1309
|
const numPixelsIn = width * height * depth;
|
|
1180
1310
|
const scaleOffsetsCopy = structuredClone(scaleOffsets);
|
|
@@ -1343,6 +1473,7 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
1343
1473
|
const DEFAULT_VALUES = {
|
|
1344
1474
|
_openGLRenderWindow: null,
|
|
1345
1475
|
_forceInternalFormat: false,
|
|
1476
|
+
_prevTexParams: null,
|
|
1346
1477
|
context: null,
|
|
1347
1478
|
handle: 0,
|
|
1348
1479
|
sendParametersTime: null,
|
|
@@ -1138,7 +1138,9 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
1138
1138
|
// rebuild the scalarTexture if the data has changed
|
|
1139
1139
|
toString = getImageDataHash(image, scalars);
|
|
1140
1140
|
const reBuildTex = !tex?.oglObject?.getHandle() || tex?.hash !== toString;
|
|
1141
|
-
|
|
1141
|
+
const updatedExtents = model.renderable.getUpdatedExtents();
|
|
1142
|
+
const hasUpdatedExtents = !!updatedExtents.length;
|
|
1143
|
+
if (reBuildTex && !hasUpdatedExtents) {
|
|
1142
1144
|
model.scalarTexture = vtkOpenGLTexture.newInstance();
|
|
1143
1145
|
model.scalarTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
|
|
1144
1146
|
// Build the textures
|
|
@@ -1146,7 +1148,7 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
1146
1148
|
// Use norm16 for scalar texture if the extension is available
|
|
1147
1149
|
model.scalarTexture.setOglNorm16Ext(model.context.getExtension('EXT_texture_norm16'));
|
|
1148
1150
|
model.scalarTexture.resetFormatAndType();
|
|
1149
|
-
model.scalarTexture.create3DFilterableFromDataArray(dims[0], dims[1], dims[2], scalars
|
|
1151
|
+
model.scalarTexture.create3DFilterableFromDataArray(dims[0], dims[1], dims[2], scalars);
|
|
1150
1152
|
if (scalars) {
|
|
1151
1153
|
model._openGLRenderWindow.setGraphicsResourceForObject(scalars, model.scalarTexture, toString);
|
|
1152
1154
|
if (scalars !== model._scalars) {
|
|
@@ -1158,6 +1160,13 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
1158
1160
|
} else {
|
|
1159
1161
|
model.scalarTexture = tex.oglObject;
|
|
1160
1162
|
}
|
|
1163
|
+
if (hasUpdatedExtents) {
|
|
1164
|
+
// If hasUpdatedExtents, then the texture is partially updated.
|
|
1165
|
+
// clear the array to acknowledge the update.
|
|
1166
|
+
model.renderable.setUpdatedExtents([]);
|
|
1167
|
+
const dims = image.getDimensions();
|
|
1168
|
+
model.scalarTexture.create3DFilterableFromDataArray(dims[0], dims[1], dims[2], scalars, false, updatedExtents);
|
|
1169
|
+
}
|
|
1161
1170
|
if (!model.tris.getCABO().getElementCount()) {
|
|
1162
1171
|
// build the CABO
|
|
1163
1172
|
const ptsArray = new Float32Array(12);
|
|
@@ -140,6 +140,15 @@ function vtkWebXRRenderWindowHelper(publicAPI, model) {
|
|
|
140
140
|
model.renderWindow.getRenderable().getInteractor().returnFromXRAnimation();
|
|
141
141
|
const gl = model.renderWindow.get3DContext();
|
|
142
142
|
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
143
|
+
|
|
144
|
+
// Remove controllers ray
|
|
145
|
+
const ren = model.renderWindow.getRenderable().getRenderers()[0];
|
|
146
|
+
model.xrSession.inputSources.forEach(inputSource => {
|
|
147
|
+
if (model.inputSourceToRay[inputSource.handedness]) {
|
|
148
|
+
ren.removeActor(model.inputSourceToRay[inputSource.handedness].actor);
|
|
149
|
+
model.inputSourceToRay[inputSource.handedness].visible = false;
|
|
150
|
+
}
|
|
151
|
+
});
|
|
143
152
|
await model.xrSession.end().catch(error => {
|
|
144
153
|
if (!(error instanceof DOMException)) {
|
|
145
154
|
throw error;
|