@kitware/vtk.js 33.2.0 → 34.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.
- package/BREAKING_CHANGES.md +10 -0
- package/Interaction/Manipulators/KeyboardCameraManipulator.d.ts +113 -0
- package/Rendering/Core/Actor.d.ts +5 -20
- package/Rendering/Core/Actor.js +5 -68
- package/Rendering/Core/ImageCPRMapper.d.ts +1 -20
- package/Rendering/Core/ImageCPRMapper.js +1 -2
- package/Rendering/Core/ImageProperty.d.ts +20 -1
- package/Rendering/Core/ImageProperty.js +7 -5
- package/Rendering/Core/ImageResliceMapper.d.ts +1 -20
- package/Rendering/Core/ImageResliceMapper.js +1 -2
- package/Rendering/Core/ImageSlice.d.ts +7 -23
- package/Rendering/Core/ImageSlice.js +9 -68
- package/Rendering/Core/Prop3D.d.ts +39 -2
- package/Rendering/Core/Prop3D.js +81 -2
- package/Rendering/Core/RenderWindowInteractor.d.ts +6 -0
- package/Rendering/Core/RenderWindowInteractor.js +7 -5
- package/Rendering/Core/Volume.d.ts +5 -20
- package/Rendering/Core/Volume.js +2 -70
- package/Rendering/Core/VolumeMapper/Constants.d.ts +0 -7
- package/Rendering/Core/VolumeMapper/Constants.js +2 -8
- package/Rendering/Core/VolumeMapper.d.ts +16 -173
- package/Rendering/Core/VolumeMapper.js +16 -51
- package/Rendering/Core/VolumeProperty/Constants.d.ts +12 -3
- package/Rendering/Core/VolumeProperty/Constants.js +11 -4
- package/Rendering/Core/VolumeProperty.d.ts +140 -5
- package/Rendering/Core/VolumeProperty.js +54 -7
- package/Rendering/OpenGL/Framebuffer.js +7 -1
- package/Rendering/OpenGL/ImageCPRMapper.js +72 -27
- package/Rendering/OpenGL/ImageMapper.js +71 -33
- package/Rendering/OpenGL/ImageResliceMapper.js +306 -183
- package/Rendering/OpenGL/OrderIndependentTranslucentPass.js +20 -3
- package/Rendering/OpenGL/PolyDataMapper.js +8 -9
- package/Rendering/OpenGL/RenderWindow/resourceSharingHelper.d.ts +3 -3
- package/Rendering/OpenGL/RenderWindow/resourceSharingHelper.js +8 -5
- package/Rendering/OpenGL/SurfaceLIC/LineIntegralConvolution2D/pingpong.js +7 -1
- package/Rendering/OpenGL/SurfaceLIC/SurfaceLICInterface.js +20 -3
- package/Rendering/OpenGL/Texture.d.ts +110 -62
- package/Rendering/OpenGL/Texture.js +145 -37
- package/Rendering/OpenGL/VolumeMapper.js +763 -783
- package/Rendering/OpenGL/glsl/vtkVolumeFS.glsl.js +1 -1
- package/Rendering/WebGPU/VolumePassFSQ.js +2 -2
- package/index.d.ts +1 -0
- package/macros.js +1 -1
- package/macros2.js +8 -3
- package/package.json +1 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { n as newInstance$1, o as obj, e as setGet, h as chain, c as macro } from '../../macros2.js';
|
|
2
2
|
import DeepEqual from 'fast-deep-equal';
|
|
3
3
|
import { mat4, mat3, vec3 } from 'gl-matrix';
|
|
4
|
+
import vtkBoundingBox from '../../Common/DataModel/BoundingBox.js';
|
|
4
5
|
import vtkDataArray from '../../Common/Core/DataArray.js';
|
|
5
6
|
import { VtkDataTypes } from '../../Common/Core/DataArray/Constants.js';
|
|
6
7
|
import vtkHelper from './Helper.js';
|
|
7
|
-
import { u as uninitializeBounds } from '../../Common/Core/Math/index.js';
|
|
8
8
|
import vtkOpenGLFramebuffer from './Framebuffer.js';
|
|
9
9
|
import vtkOpenGLTexture from './Texture.js';
|
|
10
10
|
import vtkReplacementShaderMapper from './ReplacementShaderMapper.js';
|
|
@@ -13,9 +13,9 @@ import vtkVertexArrayObject from './VertexArrayObject.js';
|
|
|
13
13
|
import vtkViewNode from '../SceneGraph/ViewNode.js';
|
|
14
14
|
import { Representation } from '../Core/Property/Constants.js';
|
|
15
15
|
import { Wrap, Filter } from './Texture/Constants.js';
|
|
16
|
-
import {
|
|
16
|
+
import { ColorMixPreset, OpacityMode, InterpolationType } from '../Core/VolumeProperty/Constants.js';
|
|
17
17
|
import { BlendMode } from '../Core/VolumeMapper/Constants.js';
|
|
18
|
-
import {
|
|
18
|
+
import { getTransferFunctionsHash, getImageDataHash } from './RenderWindow/resourceSharingHelper.js';
|
|
19
19
|
import { v as vtkVolumeVS } from './glsl/vtkVolumeVS.glsl.js';
|
|
20
20
|
import { v as vtkVolumeFS } from './glsl/vtkVolumeFS.glsl.js';
|
|
21
21
|
import { registerOverride } from './ViewNodeFactory.js';
|
|
@@ -29,81 +29,14 @@ const {
|
|
|
29
29
|
// helper methods
|
|
30
30
|
// ----------------------------------------------------------------------------
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
#if (vtkLightComplexity > 0) && defined(vtkComputeNormalFromOpacity)
|
|
41
|
-
vec3 scalarInterp0[2];
|
|
42
|
-
vec4 normalLight0 = computeNormalForDensity(posIS, tstep, scalarInterp0, 0);
|
|
43
|
-
scalarInterp0[0] = scalarInterp0[0] * oscale0 + oshift0;
|
|
44
|
-
scalarInterp0[1] = scalarInterp0[1] * oscale0 + oshift0;
|
|
45
|
-
normalLight0 = computeDensityNormal(scalarInterp0, height0, 1.0);
|
|
46
|
-
|
|
47
|
-
vec3 scalarInterp1[2];
|
|
48
|
-
vec4 normalLight1 = computeNormalForDensity(posIS, tstep, scalarInterp1, 1);
|
|
49
|
-
scalarInterp1[0] = scalarInterp1[0] * oscale1 + oshift1;
|
|
50
|
-
scalarInterp1[1] = scalarInterp1[1] * oscale1 + oshift1;
|
|
51
|
-
normalLight1 = computeDensityNormal(scalarInterp1, height1, 1.0);
|
|
52
|
-
#else
|
|
53
|
-
vec4 normalLight0 = normalMat[0];
|
|
54
|
-
vec4 normalLight1 = normalMat[1];
|
|
55
|
-
#endif
|
|
56
|
-
|
|
57
|
-
// compute opacities
|
|
58
|
-
float opacity0 = pwfValue0;
|
|
59
|
-
float opacity1 = pwfValue1;
|
|
60
|
-
#ifdef vtkGradientOpacityOn
|
|
61
|
-
float gof0 = computeGradientOpacityFactor(normalMat[0].a, goscale0, goshift0, gomin0, gomax0);
|
|
62
|
-
opacity0 *= gof0;
|
|
63
|
-
float gof1 = computeGradientOpacityFactor(normalMat[1].a, goscale1, goshift1, gomin1, gomax1);
|
|
64
|
-
opacity1 *= gof1;
|
|
65
|
-
#endif
|
|
66
|
-
float opacitySum = opacity0 + opacity1;
|
|
67
|
-
if (opacitySum <= 0.0) {
|
|
68
|
-
return vec4(0.0);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// mix the colors and opacities
|
|
72
|
-
tColor0 = applyAllLightning(tColor0, opacity0, posIS, normalLight0);
|
|
73
|
-
tColor1 = applyAllLightning(tColor1, opacity1, posIS, normalLight1);
|
|
74
|
-
vec3 mixedColor = (opacity0 * tColor0 + opacity1 * tColor1) / opacitySum;
|
|
75
|
-
return vec4(mixedColor, min(1.0, opacitySum));
|
|
76
|
-
`;
|
|
77
|
-
case ColorMixPreset.COLORIZE:
|
|
78
|
-
return `
|
|
79
|
-
// compute normals
|
|
80
|
-
mat4 normalMat = computeMat4Normal(posIS, tValue, tstep);
|
|
81
|
-
#if (vtkLightComplexity > 0) && defined(vtkComputeNormalFromOpacity)
|
|
82
|
-
vec3 scalarInterp0[2];
|
|
83
|
-
vec4 normalLight0 = computeNormalForDensity(posIS, tstep, scalarInterp0, 0);
|
|
84
|
-
scalarInterp0[0] = scalarInterp0[0] * oscale0 + oshift0;
|
|
85
|
-
scalarInterp0[1] = scalarInterp0[1] * oscale0 + oshift0;
|
|
86
|
-
normalLight0 = computeDensityNormal(scalarInterp0, height0, 1.0);
|
|
87
|
-
#else
|
|
88
|
-
vec4 normalLight0 = normalMat[0];
|
|
89
|
-
#endif
|
|
90
|
-
|
|
91
|
-
// compute opacities
|
|
92
|
-
float opacity0 = pwfValue0;
|
|
93
|
-
#ifdef vtkGradientOpacityOn
|
|
94
|
-
float gof0 = computeGradientOpacityFactor(normalMat[0].a, goscale0, goshift0, gomin0, gomax0);
|
|
95
|
-
opacity0 *= gof0;
|
|
96
|
-
#endif
|
|
97
|
-
|
|
98
|
-
// mix the colors and opacities
|
|
99
|
-
vec3 color = tColor0 * mix(vec3(1.0), tColor1, pwfValue1);
|
|
100
|
-
color = applyAllLightning(color, opacity0, posIS, normalLight0);
|
|
101
|
-
return vec4(color, opacity0);
|
|
102
|
-
`;
|
|
103
|
-
default:
|
|
104
|
-
return null;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
32
|
+
// Some matrices to avoid reallocations when we need them
|
|
33
|
+
const preAllocatedMatrices = {
|
|
34
|
+
idxToView: mat4.identity(new Float64Array(16)),
|
|
35
|
+
vecISToVCMatrix: mat3.identity(new Float64Array(9)),
|
|
36
|
+
modelToView: mat4.identity(new Float64Array(16)),
|
|
37
|
+
projectionToView: mat4.identity(new Float64Array(16)),
|
|
38
|
+
projectionToWorld: mat4.identity(new Float64Array(16))
|
|
39
|
+
};
|
|
107
40
|
|
|
108
41
|
// ----------------------------------------------------------------------------
|
|
109
42
|
// vtkOpenGLVolumeMapper methods
|
|
@@ -112,8 +45,51 @@ function getColorCodeFromPreset(colorMixPreset) {
|
|
|
112
45
|
function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
113
46
|
// Set our className
|
|
114
47
|
model.classHierarchy.push('vtkOpenGLVolumeMapper');
|
|
48
|
+
function getUseIndependentComponents(actorProperty, numComp) {
|
|
49
|
+
const iComps = actorProperty.getIndependentComponents();
|
|
50
|
+
const colorMixPreset = actorProperty.getColorMixPreset();
|
|
51
|
+
return iComps && numComp >= 2 || !!colorMixPreset;
|
|
52
|
+
}
|
|
53
|
+
function isLabelmapOutlineRequired(actorProperty) {
|
|
54
|
+
return actorProperty.getUseLabelOutline() || model.renderable.getBlendMode() === BlendMode.LABELMAP_EDGE_PROJECTION_BLEND;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Associate a reference counter to each graphics resource
|
|
58
|
+
const graphicsResourceReferenceCount = new Map();
|
|
59
|
+
function decreaseGraphicsResourceCount(openGLRenderWindow, coreObject) {
|
|
60
|
+
if (!coreObject) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const oldCount = graphicsResourceReferenceCount.get(coreObject) ?? 0;
|
|
64
|
+
const newCount = oldCount - 1;
|
|
65
|
+
if (newCount <= 0) {
|
|
66
|
+
openGLRenderWindow.unregisterGraphicsResourceUser(coreObject, publicAPI);
|
|
67
|
+
graphicsResourceReferenceCount.delete(coreObject);
|
|
68
|
+
} else {
|
|
69
|
+
graphicsResourceReferenceCount.set(coreObject, newCount);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function increaseGraphicsResourceCount(openGLRenderWindow, coreObject) {
|
|
73
|
+
if (!coreObject) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const oldCount = graphicsResourceReferenceCount.get(coreObject) ?? 0;
|
|
77
|
+
const newCount = oldCount + 1;
|
|
78
|
+
graphicsResourceReferenceCount.set(coreObject, newCount);
|
|
79
|
+
if (oldCount <= 0) {
|
|
80
|
+
openGLRenderWindow.registerGraphicsResourceUser(coreObject, publicAPI);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function replaceGraphicsResource(openGLRenderWindow, oldResourceCoreObject, newResourceCoreObject) {
|
|
84
|
+
if (oldResourceCoreObject === newResourceCoreObject) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
decreaseGraphicsResourceCount(openGLRenderWindow, oldResourceCoreObject);
|
|
88
|
+
increaseGraphicsResourceCount(openGLRenderWindow, newResourceCoreObject);
|
|
89
|
+
}
|
|
115
90
|
function unregisterGraphicsResources(renderWindow) {
|
|
116
|
-
|
|
91
|
+
// Convert to an array using the spread operator as Firefox doesn't support Iterator.forEach()
|
|
92
|
+
[...graphicsResourceReferenceCount.keys()].forEach(coreObject => renderWindow.unregisterGraphicsResourceUser(coreObject, publicAPI));
|
|
117
93
|
}
|
|
118
94
|
publicAPI.buildPass = () => {
|
|
119
95
|
model.zBufferTexture = null;
|
|
@@ -157,216 +133,175 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
157
133
|
shaders.Fragment = vtkVolumeFS;
|
|
158
134
|
shaders.Geometry = '';
|
|
159
135
|
};
|
|
160
|
-
publicAPI.useIndependentComponents = actorProperty => {
|
|
161
|
-
const iComps = actorProperty.getIndependentComponents();
|
|
162
|
-
const image = model.currentInput;
|
|
163
|
-
const numComp = image?.getPointData()?.getScalars()?.getNumberOfComponents();
|
|
164
|
-
const colorMixPreset = actorProperty.getColorMixPreset();
|
|
165
|
-
return iComps && numComp >= 2 || !!colorMixPreset;
|
|
166
|
-
};
|
|
167
136
|
publicAPI.replaceShaderValues = (shaders, ren, actor) => {
|
|
168
|
-
const actorProps = actor.getProperty();
|
|
169
137
|
let FSSource = shaders.Fragment;
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::TrilinearOn', '#define vtkTrilinearOn').result;
|
|
138
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::EnabledColorFunctions', `#define EnableColorForValueFunctionId${model.previousState.colorForValueFunctionId}`).result;
|
|
139
|
+
const enabledLightings = [];
|
|
140
|
+
if (model.previousState.surfaceLightingEnabled) {
|
|
141
|
+
enabledLightings.push('Surface');
|
|
175
142
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::ImageLabelOutlineOn', '#define vtkImageLabelOutlineOn').result;
|
|
143
|
+
if (model.previousState.volumeLightingEnabled) {
|
|
144
|
+
enabledLightings.push('Volume');
|
|
179
145
|
}
|
|
180
|
-
|
|
181
|
-
if (
|
|
182
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::
|
|
146
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::EnabledLightings', enabledLightings.map(lightingType => `#define Enable${lightingType}Lighting`)).result;
|
|
147
|
+
if (model.previousState.multiTexturePerVolumeEnabled) {
|
|
148
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::EnabledMultiTexturePerVolume', '#define EnabledMultiTexturePerVolume').result;
|
|
183
149
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const useIndependentComps = publicAPI.useIndependentComponents(actorProps);
|
|
187
|
-
if (useIndependentComps) {
|
|
188
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::IndependentComponentsOn', '#define UseIndependentComponents').result;
|
|
150
|
+
if (model.previousState.useIndependentComponents) {
|
|
151
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::EnabledIndependentComponents', '#define EnabledIndependentComponents').result;
|
|
189
152
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const proportionalComponents = [];
|
|
193
|
-
const forceNearestComponents = [];
|
|
194
|
-
for (let nc = 0; nc < numComp; nc++) {
|
|
195
|
-
if (actorProps.getOpacityMode(nc) === OpacityMode.PROPORTIONAL) {
|
|
196
|
-
proportionalComponents.push(`#define vtkComponent${nc}Proportional`);
|
|
197
|
-
}
|
|
198
|
-
if (actorProps.getForceNearestInterpolation(nc)) {
|
|
199
|
-
forceNearestComponents.push(`#define vtkComponent${nc}ForceNearest`);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::vtkProportionalComponents', proportionalComponents.join('\n')).result;
|
|
203
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::vtkForceNearestComponents', forceNearestComponents.join('\n')).result;
|
|
204
|
-
const colorMixPreset = actorProps.getColorMixPreset();
|
|
205
|
-
const colorMixCode = getColorCodeFromPreset(colorMixPreset);
|
|
206
|
-
if (colorMixCode) {
|
|
207
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::CustomComponentsColorMixOn', '#define vtkCustomComponentsColorMix').result;
|
|
208
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::CustomComponentsColorMix::Impl', colorMixCode).result;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// WebGL only supports loops over constants
|
|
212
|
-
// and does not support while loops so we
|
|
213
|
-
// have to hard code how many steps/samples to take
|
|
214
|
-
// We do a break so most systems will gracefully
|
|
215
|
-
// early terminate, but it is always possible
|
|
216
|
-
// a system will execute every step regardless
|
|
217
|
-
const ext = model.currentInput.getSpatialExtent();
|
|
218
|
-
const spc = model.currentInput.getSpacing();
|
|
219
|
-
const vsize = new Float64Array(3);
|
|
220
|
-
vec3.set(vsize, (ext[1] - ext[0]) * spc[0], (ext[3] - ext[2]) * spc[1], (ext[5] - ext[4]) * spc[2]);
|
|
221
|
-
const maxSamples = vec3.length(vsize) / publicAPI.getCurrentSampleDistance(ren);
|
|
222
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::MaximumSamplesValue', `${Math.ceil(maxSamples)}`).result;
|
|
223
|
-
|
|
224
|
-
// set light complexity
|
|
225
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::LightComplexity', `#define vtkLightComplexity ${model.lightComplexity}`).result;
|
|
226
|
-
|
|
227
|
-
// set shadow blending flag
|
|
228
|
-
if (model.lightComplexity > 0) {
|
|
229
|
-
if (model.renderable.getVolumetricScatteringBlending() > 0.0) {
|
|
230
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::VolumeShadowOn', `#define VolumeShadowOn`).result;
|
|
231
|
-
}
|
|
232
|
-
if (model.renderable.getVolumetricScatteringBlending() < 1.0) {
|
|
233
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::SurfaceShadowOn', `#define SurfaceShadowOn`).result;
|
|
234
|
-
}
|
|
235
|
-
if (model.renderable.getLocalAmbientOcclusion() && actorProps.getAmbient() > 0.0) {
|
|
236
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::localAmbientOcclusionOn', `#define localAmbientOcclusionOn`).result;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// if using gradient opacity define that
|
|
241
|
-
const numIComps = useIndependentComps ? numComp : 1;
|
|
242
|
-
model.gopacity = false;
|
|
243
|
-
for (let nc = 0; !model.gopacity && nc < numIComps; ++nc) {
|
|
244
|
-
model.gopacity ||= actorProps.getUseGradientOpacity(nc);
|
|
245
|
-
}
|
|
246
|
-
if (model.gopacity) {
|
|
247
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::GradientOpacityOn', '#define vtkGradientOpacityOn').result;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// set normal from density
|
|
251
|
-
if (model.renderable.getComputeNormalFromOpacity()) {
|
|
252
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::vtkComputeNormalFromOpacity', `#define vtkComputeNormalFromOpacity`).result;
|
|
153
|
+
if (model.previousState.gradientOpacityEnabled) {
|
|
154
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::EnabledGradientOpacity', '#define EnabledGradientOpacity').result;
|
|
253
155
|
}
|
|
156
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::vtkProportionalComponents', model.previousState.proportionalComponents.map(component => `#define vtkComponent${component}Proportional`).join('\n')).result;
|
|
157
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::vtkForceNearestComponents', model.previousState.forceNearestComponents.map(component => `#define vtkComponent${component}ForceNearest`).join('\n')).result;
|
|
254
158
|
|
|
255
159
|
// if we have a ztexture then declare it and use it
|
|
256
|
-
if (model.
|
|
160
|
+
if (model.previousState.hasZBufferTexture) {
|
|
257
161
|
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::ZBuffer::Dec', ['uniform sampler2D zBufferTexture;', 'uniform float vpZWidth;', 'uniform float vpZHeight;']).result;
|
|
258
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::ZBuffer::Impl', ['vec4 depthVec = texture2D(zBufferTexture, vec2(gl_FragCoord.x / vpZWidth, gl_FragCoord.y/vpZHeight));', 'float zdepth = (depthVec.r*256.0 + depthVec.g)/257.0;', 'zdepth = zdepth * 2.0 - 1.0;', 'if (cameraParallel == 0) {', 'zdepth = -2.0 * camFar * camNear / (zdepth*(camFar-camNear)-(camFar+camNear)) - camNear;}', 'else {', 'zdepth = (zdepth + 1.0) * 0.5 * (camFar - camNear);}\n', 'zdepth = -zdepth/
|
|
162
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::ZBuffer::Impl', ['vec4 depthVec = texture2D(zBufferTexture, vec2(gl_FragCoord.x / vpZWidth, gl_FragCoord.y/vpZHeight));', 'float zdepth = (depthVec.r*256.0 + depthVec.g)/257.0;', 'zdepth = zdepth * 2.0 - 1.0;', 'if (cameraParallel == 0) {', 'zdepth = -2.0 * camFar * camNear / (zdepth*(camFar-camNear)-(camFar+camNear)) - camNear;}', 'else {', 'zdepth = (zdepth + 1.0) * 0.5 * (camFar - camNear);}\n', 'zdepth = -zdepth/rayDirVC.z;', 'dists.y = min(zdepth,dists.y);']).result;
|
|
259
163
|
}
|
|
260
164
|
|
|
261
165
|
// Set the BlendMode approach
|
|
262
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::BlendMode', `${model.
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
publicAPI.replaceShaderLight = (shaders, ren, actor) => {
|
|
268
|
-
if (model.lightComplexity === 0) {
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
let FSSource = shaders.Fragment;
|
|
272
|
-
// check for shadow maps - not implemented yet, skip
|
|
273
|
-
// const shadowFactor = '';
|
|
274
|
-
|
|
275
|
-
// to-do: single out the case when complexity = 1
|
|
276
|
-
|
|
277
|
-
// only account for lights that are switched on
|
|
278
|
-
let lightNum = 0;
|
|
279
|
-
ren.getLights().forEach(light => {
|
|
280
|
-
if (light.getSwitch()) {
|
|
281
|
-
lightNum += 1;
|
|
282
|
-
}
|
|
283
|
-
});
|
|
284
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::Light::Dec', [`uniform int lightNum;`, `uniform bool twoSidedLighting;`, `uniform vec3 lightColor[${lightNum}];`, `uniform vec3 lightDirectionVC[${lightNum}]; // normalized`, `uniform vec3 lightHalfAngleVC[${lightNum}];`, '//VTK::Light::Dec'], false).result;
|
|
285
|
-
// support any number of lights
|
|
286
|
-
if (model.lightComplexity === 3) {
|
|
287
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::Light::Dec', [`uniform vec3 lightPositionVC[${lightNum}];`, `uniform vec3 lightAttenuation[${lightNum}];`, `uniform float lightConeAngle[${lightNum}];`, `uniform float lightExponent[${lightNum}];`, `uniform int lightPositional[${lightNum}];`], false).result;
|
|
288
|
-
}
|
|
289
|
-
if (model.renderable.getVolumetricScatteringBlending() > 0.0) {
|
|
290
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::VolumeShadow::Dec', [`uniform float volumetricScatteringBlending;`, `uniform float giReach;`, `uniform float volumeShadowSamplingDistFactor;`, `uniform float anisotropy;`, `uniform float anisotropy2;`], false).result;
|
|
291
|
-
}
|
|
292
|
-
if (model.renderable.getLocalAmbientOcclusion() && actor.getProperty().getAmbient() > 0.0) {
|
|
293
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::LAO::Dec', [`uniform int kernelRadius;`, `uniform vec2 kernelSample[${model.renderable.getLAOKernelRadius()}];`, `uniform int kernelSize;`], false).result;
|
|
294
|
-
}
|
|
166
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::BlendMode', `${model.previousState.blendMode}`).result;
|
|
167
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::NumberOfLights', `${model.previousState.numberOfLights}`).result;
|
|
168
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::MaxLaoKernelSize', `${model.previousState.maxLaoKernelSize}`).result;
|
|
169
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::NumberOfComponents', `${model.previousState.numberOfComponents}`).result;
|
|
170
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::MaximumNumberOfSamples', `${model.previousState.maximumNumberOfSamples}`).result;
|
|
295
171
|
shaders.Fragment = FSSource;
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
let FSSource = shaders.Fragment;
|
|
299
|
-
if (model.renderable.getClippingPlanes().length > 0) {
|
|
300
|
-
const clipPlaneSize = model.renderable.getClippingPlanes().length;
|
|
172
|
+
const numberOfClippingPlanes = model.previousState.numberOfClippingPlanes;
|
|
173
|
+
if (numberOfClippingPlanes > 0) {
|
|
301
174
|
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::ClipPlane::Dec', [`uniform vec3 vClipPlaneNormals[6];`, `uniform float vClipPlaneDistances[6];`, `uniform vec3 vClipPlaneOrigins[6];`, `uniform int clip_numPlanes;`, '//VTK::ClipPlane::Dec', '#define vtkClippingPlanesOn'], false).result;
|
|
302
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::ClipPlane::Impl', [`for(int i = 0; i < ${
|
|
175
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::ClipPlane::Impl', [`for(int i = 0; i < ${numberOfClippingPlanes}; i++) {`, ' float rayDirRatio = dot(rayDirVC, vClipPlaneNormals[i]);', ' float equationResult = dot(vertexVCVSOutput, vClipPlaneNormals[i]) + vClipPlaneDistances[i];', ' if (rayDirRatio == 0.0)', ' {', ' if (equationResult < 0.0) dists.x = dists.y;', ' continue;', ' }', ' float result = -1.0 * equationResult / rayDirRatio;', ' if (rayDirRatio < 0.0) dists.y = min(dists.y, result);', ' else dists.x = max(dists.x, result);', '}', '//VTK::ClipPlane::Impl'], false).result;
|
|
303
176
|
}
|
|
304
177
|
shaders.Fragment = FSSource;
|
|
305
178
|
};
|
|
306
|
-
|
|
307
|
-
//
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
179
|
+
publicAPI.getNeedToRebuildShaders = (cellBO, ren, actor) => {
|
|
180
|
+
// These are all the variables that fully determine the behavior of replaceShaderValues
|
|
181
|
+
// and the exact content of the shader
|
|
182
|
+
// See replaceShaderValues method
|
|
183
|
+
const hasZBufferTexture = !!model.zBufferTexture;
|
|
184
|
+
const numberOfValidInputs = model.currentValidInputs.length;
|
|
185
|
+
const numberOfLights = model.numberOfLights;
|
|
186
|
+
const numberOfComponents = model.numberOfComponents;
|
|
187
|
+
const useIndependentComponents = model.useIndependentComponents;
|
|
188
|
+
|
|
189
|
+
// The volume property that is used is always the first one
|
|
190
|
+
const volumeProperties = actor.getProperties();
|
|
191
|
+
const firstValidInput = model.currentValidInputs[0];
|
|
192
|
+
const firstVolumeProperty = volumeProperties[firstValidInput.inputIndex];
|
|
193
|
+
|
|
194
|
+
// There are two modes:
|
|
195
|
+
// - single volume with multiple components
|
|
196
|
+
// - multiple volumes with one component per volume
|
|
197
|
+
const multiTexturePerVolumeEnabled = numberOfValidInputs > 1;
|
|
198
|
+
|
|
199
|
+
// Get maximum number of samples
|
|
200
|
+
const boundsMC = firstValidInput.imageData.getBounds();
|
|
201
|
+
const maximumRayLength = vtkBoundingBox.getDiagonalLength(boundsMC);
|
|
202
|
+
const maximumNumberOfSamples = Math.ceil(maximumRayLength / publicAPI.getCurrentSampleDistance(ren));
|
|
203
|
+
if (maximumNumberOfSamples > model.renderable.getMaximumSamplesPerRay()) {
|
|
204
|
+
vtkWarningMacro(`The number of steps required ${maximumNumberOfSamples} is larger than the ` + `specified maximum number of steps ${model.renderable.getMaximumSamplesPerRay()}.\n` + 'Please either change the volumeMapper sampleDistance or its maximum number of samples.');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Gradient opacity
|
|
208
|
+
const numberOfIndependantComponents = useIndependentComponents ? numberOfComponents : 1;
|
|
209
|
+
let gradientOpacityEnabled = false;
|
|
210
|
+
for (let i = 0; i < numberOfIndependantComponents; ++i) {
|
|
211
|
+
if (firstVolumeProperty.getUseGradientOpacity(i)) {
|
|
212
|
+
gradientOpacityEnabled = true;
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
329
215
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
216
|
+
|
|
217
|
+
// Get the max kernel size from volume properties that use LAO and
|
|
218
|
+
// that are linked to a valid input imageData
|
|
219
|
+
let maxLaoKernelSize = 0;
|
|
220
|
+
const kernelSize = firstVolumeProperty.getLAOKernelSize();
|
|
221
|
+
if (kernelSize > maxLaoKernelSize && firstVolumeProperty.getLocalAmbientOcclusion() && firstVolumeProperty.getAmbient() > 0.0) {
|
|
222
|
+
maxLaoKernelSize = kernelSize;
|
|
223
|
+
}
|
|
224
|
+
const numberOfClippingPlanes = model.renderable.getClippingPlanes().length;
|
|
225
|
+
// These are from the buildShader function in vtkReplacementShaderMapper
|
|
226
|
+
const mapperShaderReplacements = model.renderable.getViewSpecificProperties().OpenGL?.ShaderReplacements;
|
|
227
|
+
const renderPassShaderReplacements = model.currentRenderPass?.getShaderReplacement();
|
|
228
|
+
const blendMode = model.renderable.getBlendMode();
|
|
229
|
+
|
|
230
|
+
// This enables optimizing out some function which avoids huge shader compilation time
|
|
231
|
+
// The result of this computation is used in getColorForValue in the fragment shader
|
|
232
|
+
const colorForValueFunctionId = (() => {
|
|
233
|
+
// If labeloutline and not the edge labelmap, since in the edge labelmap blend
|
|
234
|
+
// we need the underlying data to sample through
|
|
235
|
+
if (blendMode !== BlendMode.LABELMAP_EDGE_PROJECTION_BLEND && isLabelmapOutlineRequired(firstVolumeProperty)) {
|
|
236
|
+
return 5;
|
|
237
|
+
}
|
|
238
|
+
if (useIndependentComponents) {
|
|
239
|
+
switch (firstVolumeProperty.getColorMixPreset()) {
|
|
240
|
+
case ColorMixPreset.ADDITIVE:
|
|
241
|
+
return 1;
|
|
242
|
+
case ColorMixPreset.COLORIZE:
|
|
243
|
+
return 2;
|
|
244
|
+
case ColorMixPreset.CUSTOM:
|
|
245
|
+
return 3;
|
|
246
|
+
default:
|
|
247
|
+
// ColorMixPreset.DEFAULT
|
|
248
|
+
return 4;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return 0;
|
|
252
|
+
})();
|
|
253
|
+
|
|
254
|
+
// Get which types of lighting are enabled
|
|
255
|
+
const surfaceLightingEnabled = firstVolumeProperty.getVolumetricScatteringBlending() < 1.0;
|
|
256
|
+
const volumeLightingEnabled = firstVolumeProperty.getVolumetricScatteringBlending() > 0.0;
|
|
257
|
+
|
|
258
|
+
// Is any volume using ForceNearestInterpolation
|
|
259
|
+
let forceNearestInterpolationEnabled = false;
|
|
260
|
+
for (let component = 0; component < numberOfComponents; ++component) {
|
|
261
|
+
if (firstVolumeProperty.getForceNearestInterpolation(component)) {
|
|
262
|
+
forceNearestInterpolationEnabled = true;
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
333
265
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
const
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
266
|
+
|
|
267
|
+
// Define any proportional components
|
|
268
|
+
const proportionalComponents = [];
|
|
269
|
+
const forceNearestComponents = [];
|
|
270
|
+
for (let component = 0; component < numberOfComponents; component++) {
|
|
271
|
+
if (firstVolumeProperty.getOpacityMode(component) === OpacityMode.PROPORTIONAL) {
|
|
272
|
+
proportionalComponents.push(component);
|
|
273
|
+
}
|
|
274
|
+
if (firstVolumeProperty.getForceNearestInterpolation(component)) {
|
|
275
|
+
forceNearestComponents.push(component);
|
|
276
|
+
}
|
|
344
277
|
}
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
interpolationType: actorProps.getInterpolationType(),
|
|
355
|
-
useLabelOutline: publicAPI.isLabelmapOutlineRequired(actor),
|
|
356
|
-
numComp,
|
|
357
|
-
maxSamples,
|
|
358
|
-
useGradientOpacity: actorProps.getUseGradientOpacity(0),
|
|
359
|
-
blendMode: model.renderable.getBlendMode(),
|
|
278
|
+
const currentState = {
|
|
279
|
+
numberOfComponents,
|
|
280
|
+
useIndependentComponents,
|
|
281
|
+
proportionalComponents,
|
|
282
|
+
forceNearestComponents,
|
|
283
|
+
blendMode,
|
|
284
|
+
numberOfLights,
|
|
285
|
+
numberOfValidInputs,
|
|
286
|
+
maximumNumberOfSamples,
|
|
360
287
|
hasZBufferTexture,
|
|
361
|
-
|
|
362
|
-
|
|
288
|
+
maxLaoKernelSize,
|
|
289
|
+
numberOfClippingPlanes,
|
|
290
|
+
mapperShaderReplacements,
|
|
291
|
+
renderPassShaderReplacements,
|
|
292
|
+
colorForValueFunctionId,
|
|
293
|
+
surfaceLightingEnabled,
|
|
294
|
+
volumeLightingEnabled,
|
|
295
|
+
forceNearestInterpolationEnabled,
|
|
296
|
+
multiTexturePerVolumeEnabled,
|
|
297
|
+
gradientOpacityEnabled
|
|
363
298
|
};
|
|
364
299
|
|
|
365
300
|
// We need to rebuild the shader if one of these variables has changed,
|
|
366
301
|
// since they are used in the shader template replacement step.
|
|
367
302
|
// We also need to rebuild if the shader source time is outdated.
|
|
368
|
-
if (cellBO.getProgram()?.getHandle() === 0 ||
|
|
369
|
-
model.previousState =
|
|
303
|
+
if (cellBO.getProgram()?.getHandle() === 0 || !model.previousState || !DeepEqual(model.previousState, currentState)) {
|
|
304
|
+
model.previousState = currentState;
|
|
370
305
|
return true;
|
|
371
306
|
}
|
|
372
307
|
return false;
|
|
@@ -411,27 +346,49 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
411
346
|
}
|
|
412
347
|
cellBO.getAttributeUpdateTime().modified();
|
|
413
348
|
}
|
|
414
|
-
|
|
415
|
-
program.setUniformf('sampleDistance',
|
|
416
|
-
const
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
//
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
349
|
+
const sampleDistance = publicAPI.getCurrentSampleDistance(ren);
|
|
350
|
+
program.setUniformf('sampleDistance', sampleDistance);
|
|
351
|
+
const volumeShadowSampleDistance = sampleDistance * model.renderable.getVolumeShadowSamplingDistFactor();
|
|
352
|
+
program.setUniformf('volumeShadowSampleDistance', volumeShadowSampleDistance);
|
|
353
|
+
|
|
354
|
+
// Volume textures
|
|
355
|
+
model.scalarTextures.forEach((scalarTexture, component) => {
|
|
356
|
+
program.setUniformi(`volumeTexture[${component}]`, scalarTexture.getTextureUnit());
|
|
357
|
+
});
|
|
358
|
+
const volumeProperties = actor.getProperties();
|
|
359
|
+
const firstValidInput = model.currentValidInputs[0];
|
|
360
|
+
const firstVolumeProperty = volumeProperties[firstValidInput.inputIndex];
|
|
361
|
+
const ipScalarRange = firstVolumeProperty.getIpScalarRange();
|
|
362
|
+
const minVals = new Float32Array(4);
|
|
363
|
+
const maxVals = new Float32Array(4);
|
|
364
|
+
const setMinMaxVal = (component, volInfo, volInfoIndex) => {
|
|
365
|
+
// In some situations, we might not have computed the scale and offset
|
|
366
|
+
// for the data range, or it might not be needed.
|
|
367
|
+
if (volInfo?.dataComputedScale?.length) {
|
|
425
368
|
// convert iprange from 0-1 into data range values
|
|
426
|
-
minVals[
|
|
427
|
-
maxVals[
|
|
369
|
+
minVals[component] = ipScalarRange[0] * volInfo.dataComputedScale[volInfoIndex] + volInfo.dataComputedOffset[volInfoIndex];
|
|
370
|
+
maxVals[component] = ipScalarRange[1] * volInfo.dataComputedScale[volInfoIndex] + volInfo.dataComputedOffset[volInfoIndex];
|
|
428
371
|
// convert data ranges into texture values
|
|
429
|
-
minVals[
|
|
430
|
-
maxVals[
|
|
372
|
+
minVals[component] = (minVals[component] - volInfo.offset[volInfoIndex]) / volInfo.scale[volInfoIndex];
|
|
373
|
+
maxVals[component] = (maxVals[component] - volInfo.offset[volInfoIndex]) / volInfo.scale[volInfoIndex];
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
if (model.previousState.multiTexturePerVolumeEnabled) {
|
|
377
|
+
// Use the first component of all texture infos
|
|
378
|
+
model.scalarTextures.forEach((scalarTexture, component) => {
|
|
379
|
+
const volInfo = scalarTexture.getVolumeInfo();
|
|
380
|
+
setMinMaxVal(component, volInfo, 0);
|
|
381
|
+
});
|
|
382
|
+
} else {
|
|
383
|
+
// Use all components of the first texture info
|
|
384
|
+
const firstVolInfo = model.scalarTextures[0].getVolumeInfo();
|
|
385
|
+
for (let component = 0; component < 4; ++component) {
|
|
386
|
+
setMinMaxVal(component, firstVolInfo, component);
|
|
431
387
|
}
|
|
432
|
-
program.setUniform4f('ipScalarRangeMin', minVals[0], minVals[1], minVals[2], minVals[3]);
|
|
433
|
-
program.setUniform4f('ipScalarRangeMax', maxVals[0], maxVals[1], maxVals[2], maxVals[3]);
|
|
434
388
|
}
|
|
389
|
+
const uniformPrefix = 'volume';
|
|
390
|
+
program.setUniform4f(`${uniformPrefix}.ipScalarRangeMin`, minVals[0], minVals[1], minVals[2], minVals[3]);
|
|
391
|
+
program.setUniform4f(`${uniformPrefix}.ipScalarRangeMax`, maxVals[0], maxVals[1], maxVals[2], maxVals[3]);
|
|
435
392
|
|
|
436
393
|
// if we have a zbuffer texture then set it
|
|
437
394
|
if (model.zBufferTexture !== null) {
|
|
@@ -442,140 +399,174 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
442
399
|
}
|
|
443
400
|
};
|
|
444
401
|
publicAPI.setCameraShaderParameters = (cellBO, ren, actor) => {
|
|
445
|
-
//
|
|
446
|
-
|
|
402
|
+
// These matrices are not cached for their content, but only to avoid reallocations
|
|
403
|
+
const {
|
|
404
|
+
idxToView,
|
|
405
|
+
vecISToVCMatrix,
|
|
406
|
+
modelToView,
|
|
407
|
+
projectionToView,
|
|
408
|
+
projectionToWorld
|
|
409
|
+
} = preAllocatedMatrices;
|
|
410
|
+
|
|
411
|
+
// [WMVP]C == {world, model, view, projection} coordinates
|
|
412
|
+
// E.g., WCPC == world to projection coordinate transformation
|
|
447
413
|
const keyMats = model.openGLCamera.getKeyMatrices(ren);
|
|
448
414
|
const actMats = model.openGLVolume.getKeyMatrices();
|
|
449
|
-
mat4.multiply(
|
|
415
|
+
mat4.multiply(modelToView, keyMats.wcvc, actMats.mcwc);
|
|
450
416
|
const program = cellBO.getProgram();
|
|
451
|
-
const
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
program.setUniformf('
|
|
455
|
-
program.setUniformf('
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
//
|
|
460
|
-
//
|
|
461
|
-
|
|
462
|
-
const
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
// nearZ distance plane. Since the camera is at 0,0,0
|
|
475
|
-
// in VC the ray is just t*pos and
|
|
476
|
-
// t is -nearZ/dir.z
|
|
477
|
-
// intersection becomes pos.x/pos.z
|
|
478
|
-
const t = -crange[0] / pos[2];
|
|
479
|
-
vec3.scale(pos, dir, t);
|
|
417
|
+
const camera = model.openGLCamera.getRenderable();
|
|
418
|
+
const useParallelProjection = camera.getParallelProjection();
|
|
419
|
+
const clippingRange = camera.getClippingRange();
|
|
420
|
+
program.setUniformf('camThick', clippingRange[1] - clippingRange[0]);
|
|
421
|
+
program.setUniformf('camNear', clippingRange[0]);
|
|
422
|
+
program.setUniformf('camFar', clippingRange[1]);
|
|
423
|
+
program.setUniformi('cameraParallel', useParallelProjection);
|
|
424
|
+
|
|
425
|
+
// Compute the viewport bounds of the volume
|
|
426
|
+
// We will only render those fragments
|
|
427
|
+
// First, merge all bounds to get a fusion of all bounds in model coordinates
|
|
428
|
+
const firstValidInput = model.currentValidInputs[0];
|
|
429
|
+
const boundsMC = firstValidInput.imageData.getBounds();
|
|
430
|
+
const cornersMC = vtkBoundingBox.getCorners(boundsMC, []);
|
|
431
|
+
const cornersDC = cornersMC.map(corner => {
|
|
432
|
+
// Convert to view coordinates
|
|
433
|
+
vec3.transformMat4(corner, corner, modelToView);
|
|
434
|
+
if (!useParallelProjection) {
|
|
435
|
+
// Now find the projection of this point onto a
|
|
436
|
+
// nearZ distance plane. Since pos is in view coordinates,
|
|
437
|
+
// scale it until pos.z == nearZ
|
|
438
|
+
const newScale = -clippingRange[0] / (corner[2] * vec3.length(corner));
|
|
439
|
+
vec3.scale(corner, corner, newScale);
|
|
480
440
|
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
program.setUniformf('
|
|
489
|
-
program.setUniformf('
|
|
490
|
-
program.setUniformf('
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
program.
|
|
500
|
-
|
|
501
|
-
model.
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
// apply the image directions
|
|
506
|
-
const i2wmat4 = model.currentInput.getIndexToWorld();
|
|
507
|
-
mat4.multiply(model.idxToView, model.modelToView, i2wmat4);
|
|
508
|
-
mat3.multiply(model.idxNormalMatrix, keyMats.normalMatrix, actMats.normalMatrix);
|
|
509
|
-
mat3.multiply(model.idxNormalMatrix, model.idxNormalMatrix, model.currentInput.getDirectionByReference());
|
|
510
|
-
const maxSamples = vec3.length(vsize) / publicAPI.getCurrentSampleDistance(ren);
|
|
511
|
-
if (maxSamples > model.renderable.getMaximumSamplesPerRay()) {
|
|
512
|
-
vtkWarningMacro(`The number of steps required ${Math.ceil(maxSamples)} is larger than the
|
|
513
|
-
specified maximum number of steps ${model.renderable.getMaximumSamplesPerRay()}.
|
|
514
|
-
Please either change the
|
|
515
|
-
volumeMapper sampleDistance or its maximum number of samples.`);
|
|
516
|
-
}
|
|
517
|
-
const vctoijk = new Float64Array(3);
|
|
518
|
-
vec3.set(vctoijk, 1.0, 1.0, 1.0);
|
|
519
|
-
vec3.divide(vctoijk, vctoijk, vsize);
|
|
520
|
-
program.setUniform3f('vVCToIJK', vctoijk[0], vctoijk[1], vctoijk[2]);
|
|
521
|
-
program.setUniform3i('volumeDimensions', dims[0], dims[1], dims[2]);
|
|
522
|
-
program.setUniform3f('volumeSpacings', spc[0], spc[1], spc[2]);
|
|
523
|
-
if (!model._openGLRenderWindow.getWebgl2()) {
|
|
524
|
-
const volInfo = model.scalarTexture.getVolumeInfo();
|
|
525
|
-
program.setUniformf('texWidth', model.scalarTexture.getWidth());
|
|
526
|
-
program.setUniformf('texHeight', model.scalarTexture.getHeight());
|
|
527
|
-
program.setUniformi('xreps', volInfo.xreps);
|
|
528
|
-
program.setUniformi('xstride', volInfo.xstride);
|
|
529
|
-
program.setUniformi('ystride', volInfo.ystride);
|
|
441
|
+
|
|
442
|
+
// Now convert to display coordinates
|
|
443
|
+
vec3.transformMat4(corner, corner, keyMats.vcpc);
|
|
444
|
+
return corner;
|
|
445
|
+
});
|
|
446
|
+
const boundsDC = vtkBoundingBox.addPoints([...vtkBoundingBox.INIT_BOUNDS], cornersDC);
|
|
447
|
+
program.setUniformf('dcxmin', boundsDC[0]);
|
|
448
|
+
program.setUniformf('dcxmax', boundsDC[1]);
|
|
449
|
+
program.setUniformf('dcymin', boundsDC[2]);
|
|
450
|
+
program.setUniformf('dcymax', boundsDC[3]);
|
|
451
|
+
const size = publicAPI.getRenderTargetSize();
|
|
452
|
+
program.setUniformf('vpWidth', size[0]);
|
|
453
|
+
program.setUniformf('vpHeight', size[1]);
|
|
454
|
+
const offset = publicAPI.getRenderTargetOffset();
|
|
455
|
+
program.setUniformf('vpOffsetX', offset[0] / size[0]);
|
|
456
|
+
program.setUniformf('vpOffsetY', offset[1] / size[1]);
|
|
457
|
+
mat4.invert(projectionToView, keyMats.vcpc);
|
|
458
|
+
program.setUniformMatrix('PCVCMatrix', projectionToView);
|
|
459
|
+
program.setUniformi('twoSidedLighting', ren.getTwoSidedLighting());
|
|
460
|
+
const kernelSample = new Array(2 * model.previousState.maxLaoKernelSize);
|
|
461
|
+
for (let i = 0; i < model.previousState.maxLaoKernelSize; i++) {
|
|
462
|
+
kernelSample[i * 2] = Math.random();
|
|
463
|
+
kernelSample[i * 2 + 1] = Math.random();
|
|
530
464
|
}
|
|
465
|
+
program.setUniform2fv('kernelSample', kernelSample);
|
|
531
466
|
|
|
532
|
-
//
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
vec3.
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
vec3.
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
467
|
+
// Handle lighting values
|
|
468
|
+
if (model.numberOfLights > 0) {
|
|
469
|
+
let lightIndex = 0;
|
|
470
|
+
ren.getLights().forEach(light => {
|
|
471
|
+
if (light.getSwitch() > 0) {
|
|
472
|
+
const lightPrefix = `lights[${lightIndex}]`;
|
|
473
|
+
|
|
474
|
+
// Merge color and intensity
|
|
475
|
+
const color = light.getColor();
|
|
476
|
+
const intensity = light.getIntensity();
|
|
477
|
+
const scaledColor = vec3.scale([], color, intensity);
|
|
478
|
+
program.setUniform3fv(`${lightPrefix}.color`, scaledColor);
|
|
479
|
+
|
|
480
|
+
// Position in view coordinates
|
|
481
|
+
const position = light.getTransformedPosition();
|
|
482
|
+
vec3.transformMat4(position, position, modelToView);
|
|
483
|
+
program.setUniform3fv(`${lightPrefix}.positionVC`, position);
|
|
484
|
+
|
|
485
|
+
// Convert lightDirection in view coordinates and normalize it
|
|
486
|
+
const direction = [...light.getDirection()];
|
|
487
|
+
vec3.transformMat3(direction, direction, keyMats.normalMatrix);
|
|
488
|
+
vec3.normalize(direction, direction);
|
|
489
|
+
program.setUniform3fv(`${lightPrefix}.directionVC`, direction);
|
|
490
|
+
|
|
491
|
+
// Camera direction of projection is (0, 0, -1.0) in view coordinates
|
|
492
|
+
const halfAngle = [-0.5 * direction[0], -0.5 * direction[1], -0.5 * (direction[2] - 1.0)];
|
|
493
|
+
program.setUniform3fv(`${lightPrefix}.halfAngleVC`, halfAngle);
|
|
494
|
+
|
|
495
|
+
// Attenuation
|
|
496
|
+
const attenuation = light.getAttenuationValues();
|
|
497
|
+
program.setUniform3fv(`${lightPrefix}.attenuation`, attenuation);
|
|
498
|
+
|
|
499
|
+
// Exponent
|
|
500
|
+
const exponent = light.getExponent();
|
|
501
|
+
program.setUniformf(`${lightPrefix}.exponent`, exponent);
|
|
502
|
+
|
|
503
|
+
// Cone angle
|
|
504
|
+
const coneAngle = light.getConeAngle();
|
|
505
|
+
program.setUniformf(`${lightPrefix}.coneAngle`, coneAngle);
|
|
506
|
+
|
|
507
|
+
// Positional flag
|
|
508
|
+
const isPositional = light.getPositional();
|
|
509
|
+
program.setUniformi(`${lightPrefix}.isPositional`, isPositional);
|
|
510
|
+
lightIndex++;
|
|
511
|
+
}
|
|
512
|
+
});
|
|
572
513
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
514
|
+
|
|
515
|
+
// Set uniforms for the volume
|
|
516
|
+
const uniformPrefix = 'volume';
|
|
517
|
+
const volumeProperties = actor.getProperties();
|
|
518
|
+
const firstVolumeProperty = volumeProperties[firstValidInput.inputIndex];
|
|
519
|
+
const firstImageData = firstValidInput.imageData;
|
|
520
|
+
const spatialExtent = firstImageData.getSpatialExtent();
|
|
521
|
+
const spacing = firstImageData.getSpacing();
|
|
522
|
+
const dimensions = firstImageData.getDimensions();
|
|
523
|
+
const idxToModel = firstImageData.getIndexToWorld();
|
|
524
|
+
const worldToIndex = firstImageData.getWorldToIndex();
|
|
525
|
+
const imageDirection = firstImageData.getDirectionByReference();
|
|
526
|
+
|
|
527
|
+
// idxToView is equivalent to applying idxToModel then modelToView
|
|
528
|
+
mat4.multiply(idxToView, modelToView, idxToModel);
|
|
529
|
+
|
|
530
|
+
// Set spacing uniform
|
|
531
|
+
program.setUniform3fv(`${uniformPrefix}.spacing`, spacing);
|
|
532
|
+
const inverseSpacing = vec3.inverse([], spacing);
|
|
533
|
+
program.setUniform3fv(`${uniformPrefix}.inverseSpacing`, inverseSpacing);
|
|
534
|
+
|
|
535
|
+
// Set dimensions uniform
|
|
536
|
+
program.setUniform3iv(`${uniformPrefix}.dimensions`, dimensions);
|
|
537
|
+
|
|
538
|
+
// Set inverse dimensions uniform
|
|
539
|
+
program.setUniform3fv(`${uniformPrefix}.inverseDimensions`, vec3.inverse([], dimensions));
|
|
540
|
+
|
|
541
|
+
// Set world to index
|
|
542
|
+
program.setUniformMatrix(`${uniformPrefix}.worldToIndex`, worldToIndex);
|
|
543
|
+
|
|
544
|
+
// Create the vecISToVCMatrix, that transform a point from texture coordinates (IS in the shader) to VC coordinates
|
|
545
|
+
vecISToVCMatrix.fill(0);
|
|
546
|
+
// First apply scaling
|
|
547
|
+
// mat3.fromScaling can't be used, because it uses a vec2 for scaling
|
|
548
|
+
const sizeVC = vec3.multiply(new Float64Array(3), dimensions, spacing);
|
|
549
|
+
vecISToVCMatrix[0] = sizeVC[0];
|
|
550
|
+
vecISToVCMatrix[4] = sizeVC[1];
|
|
551
|
+
vecISToVCMatrix[8] = sizeVC[2];
|
|
552
|
+
// Then apply the image direction matrix
|
|
553
|
+
mat3.multiply(vecISToVCMatrix, imageDirection, vecISToVCMatrix);
|
|
554
|
+
// Then apply the actor matrix
|
|
555
|
+
mat3.multiply(vecISToVCMatrix, actMats.normalMatrix, vecISToVCMatrix);
|
|
556
|
+
// Then apply the camera matrix
|
|
557
|
+
mat3.multiply(vecISToVCMatrix, keyMats.normalMatrix, vecISToVCMatrix);
|
|
558
|
+
program.setUniformMatrix3x3(`${uniformPrefix}.vecISToVCMatrix`, vecISToVCMatrix);
|
|
559
|
+
program.setUniformMatrix3x3(`${uniformPrefix}.vecVCToISMatrix`, mat3.invert(new Float32Array(9), vecISToVCMatrix));
|
|
560
|
+
|
|
561
|
+
// Set originVC uniform that will be used to convert points from IS to VC
|
|
562
|
+
// It will be done in this way: posVC = vecISToVCMatrix * posIS + originVC
|
|
563
|
+
// Or the other way around: posIS = vecVCtoISMatrix * (posVC - originVC)
|
|
564
|
+
const spacialExtentMinIC = vec3.fromValues(spatialExtent[0], spatialExtent[2], spatialExtent[4]);
|
|
565
|
+
const originVC = vec3.transformMat4(new Float64Array(3), spacialExtentMinIC, idxToView);
|
|
566
|
+
program.setUniform3fv(`${uniformPrefix}.originVC`, originVC);
|
|
567
|
+
const diagonalLength = vec3.length(sizeVC);
|
|
568
|
+
program.setUniformf(`${uniformPrefix}.diagonalLength`, diagonalLength);
|
|
569
|
+
if (isLabelmapOutlineRequired(firstVolumeProperty)) {
|
|
579
570
|
const distance = camera.getDistance();
|
|
580
571
|
|
|
581
572
|
// set the clipping range to be model.distance and model.distance + 0.1
|
|
@@ -588,186 +579,151 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
588
579
|
const labelOutlineKeyMats = model.openGLCamera.getKeyMatrices(ren);
|
|
589
580
|
|
|
590
581
|
// Get the projection coordinate to world coordinate transformation matrix.
|
|
591
|
-
mat4.invert(
|
|
582
|
+
mat4.invert(projectionToWorld, labelOutlineKeyMats.wcpc);
|
|
592
583
|
|
|
593
584
|
// reset the clipping range since the keyMats are cached
|
|
594
|
-
camera.setClippingRange(
|
|
585
|
+
camera.setClippingRange(clippingRange[0], clippingRange[1]);
|
|
595
586
|
|
|
596
587
|
// to re compute the matrices for the current camera and cache them
|
|
597
588
|
model.openGLCamera.getKeyMatrices(ren);
|
|
598
|
-
program.setUniformMatrix(
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
program.setUniformf(
|
|
602
|
-
|
|
603
|
-
program.setUniformf(
|
|
604
|
-
program.setUniformf(
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
let lightNum = 0;
|
|
614
|
-
const lightColor = [];
|
|
615
|
-
const lightDir = [];
|
|
616
|
-
const halfAngle = [];
|
|
617
|
-
ren.getLights().forEach(light => {
|
|
618
|
-
const status = light.getSwitch();
|
|
619
|
-
if (status > 0) {
|
|
620
|
-
const dColor = light.getColor();
|
|
621
|
-
const intensity = light.getIntensity();
|
|
622
|
-
lightColor[0 + lightNum * 3] = dColor[0] * intensity;
|
|
623
|
-
lightColor[1 + lightNum * 3] = dColor[1] * intensity;
|
|
624
|
-
lightColor[2 + lightNum * 3] = dColor[2] * intensity;
|
|
625
|
-
const ldir = light.getDirection();
|
|
626
|
-
vec3.set(normal, ldir[0], ldir[1], ldir[2]);
|
|
627
|
-
vec3.transformMat3(normal, normal, keyMats.normalMatrix); // in view coordinat
|
|
628
|
-
vec3.normalize(normal, normal);
|
|
629
|
-
lightDir[0 + lightNum * 3] = normal[0];
|
|
630
|
-
lightDir[1 + lightNum * 3] = normal[1];
|
|
631
|
-
lightDir[2 + lightNum * 3] = normal[2];
|
|
632
|
-
// camera DOP is 0,0,-1.0 in VC
|
|
633
|
-
halfAngle[0 + lightNum * 3] = -0.5 * normal[0];
|
|
634
|
-
halfAngle[1 + lightNum * 3] = -0.5 * normal[1];
|
|
635
|
-
halfAngle[2 + lightNum * 3] = -0.5 * (normal[2] - 1.0);
|
|
636
|
-
lightNum++;
|
|
637
|
-
}
|
|
638
|
-
});
|
|
639
|
-
program.setUniformi('twoSidedLighting', ren.getTwoSidedLighting());
|
|
640
|
-
program.setUniformi('lightNum', lightNum);
|
|
641
|
-
program.setUniform3fv('lightColor', lightColor);
|
|
642
|
-
program.setUniform3fv('lightDirectionVC', lightDir);
|
|
643
|
-
program.setUniform3fv('lightHalfAngleVC', halfAngle);
|
|
644
|
-
if (model.lightComplexity === 3) {
|
|
645
|
-
lightNum = 0;
|
|
646
|
-
const lightPositionVC = [];
|
|
647
|
-
const lightAttenuation = [];
|
|
648
|
-
const lightConeAngle = [];
|
|
649
|
-
const lightExponent = [];
|
|
650
|
-
const lightPositional = [];
|
|
651
|
-
ren.getLights().forEach(light => {
|
|
652
|
-
const status = light.getSwitch();
|
|
653
|
-
if (status > 0) {
|
|
654
|
-
const attenuation = light.getAttenuationValues();
|
|
655
|
-
lightAttenuation[0 + lightNum * 3] = attenuation[0];
|
|
656
|
-
lightAttenuation[1 + lightNum * 3] = attenuation[1];
|
|
657
|
-
lightAttenuation[2 + lightNum * 3] = attenuation[2];
|
|
658
|
-
lightExponent[lightNum] = light.getExponent();
|
|
659
|
-
lightConeAngle[lightNum] = light.getConeAngle();
|
|
660
|
-
lightPositional[lightNum] = light.getPositional();
|
|
661
|
-
const lp = light.getTransformedPosition();
|
|
662
|
-
vec3.transformMat4(lp, lp, model.modelToView);
|
|
663
|
-
lightPositionVC[0 + lightNum * 3] = lp[0];
|
|
664
|
-
lightPositionVC[1 + lightNum * 3] = lp[1];
|
|
665
|
-
lightPositionVC[2 + lightNum * 3] = lp[2];
|
|
666
|
-
lightNum += 1;
|
|
667
|
-
}
|
|
668
|
-
});
|
|
669
|
-
program.setUniform3fv('lightPositionVC', lightPositionVC);
|
|
670
|
-
program.setUniform3fv('lightAttenuation', lightAttenuation);
|
|
671
|
-
program.setUniformfv('lightConeAngle', lightConeAngle);
|
|
672
|
-
program.setUniformfv('lightExponent', lightExponent);
|
|
673
|
-
program.setUniformiv('lightPositional', lightPositional);
|
|
674
|
-
}
|
|
675
|
-
if (model.renderable.getVolumetricScatteringBlending() > 0.0) {
|
|
676
|
-
program.setUniformf('giReach', model.renderable.getGlobalIlluminationReach());
|
|
677
|
-
program.setUniformf('volumetricScatteringBlending', model.renderable.getVolumetricScatteringBlending());
|
|
678
|
-
program.setUniformf('volumeShadowSamplingDistFactor', model.renderable.getVolumeShadowSamplingDistFactor());
|
|
679
|
-
program.setUniformf('anisotropy', model.renderable.getAnisotropy());
|
|
680
|
-
program.setUniformf('anisotropy2', model.renderable.getAnisotropy() ** 2.0);
|
|
681
|
-
}
|
|
682
|
-
if (model.renderable.getLocalAmbientOcclusion() && actor.getProperty().getAmbient() > 0.0) {
|
|
683
|
-
const ks = model.renderable.getLAOKernelSize();
|
|
684
|
-
program.setUniformi('kernelSize', ks);
|
|
685
|
-
const kernelSample = [];
|
|
686
|
-
for (let i = 0; i < ks; i++) {
|
|
687
|
-
kernelSample[i * 2] = Math.random() * 0.5;
|
|
688
|
-
kernelSample[i * 2 + 1] = Math.random() * 0.5;
|
|
689
|
-
}
|
|
690
|
-
program.setUniform2fv('kernelSample', kernelSample);
|
|
691
|
-
program.setUniformi('kernelRadius', model.renderable.getLAOKernelRadius());
|
|
589
|
+
program.setUniformMatrix(`${uniformPrefix}.PCWCMatrix`, projectionToWorld);
|
|
590
|
+
}
|
|
591
|
+
if (firstVolumeProperty.getVolumetricScatteringBlending() > 0.0) {
|
|
592
|
+
program.setUniformf(`${uniformPrefix}.globalIlluminationReach`, firstVolumeProperty.getGlobalIlluminationReach());
|
|
593
|
+
program.setUniformf(`${uniformPrefix}.volumetricScatteringBlending`, firstVolumeProperty.getVolumetricScatteringBlending());
|
|
594
|
+
program.setUniformf(`${uniformPrefix}.anisotropy`, firstVolumeProperty.getAnisotropy());
|
|
595
|
+
program.setUniformf(`${uniformPrefix}.anisotropySquared`, firstVolumeProperty.getAnisotropy() ** 2.0);
|
|
596
|
+
}
|
|
597
|
+
if (firstVolumeProperty.getLocalAmbientOcclusion() && firstVolumeProperty.getAmbient() > 0.0) {
|
|
598
|
+
const kernelSize = firstVolumeProperty.getLAOKernelSize();
|
|
599
|
+
program.setUniformi(`${uniformPrefix}.kernelSize`, kernelSize);
|
|
600
|
+
const kernelRadius = firstVolumeProperty.getLAOKernelRadius();
|
|
601
|
+
program.setUniformi(`${uniformPrefix}.kernelRadius`, kernelRadius);
|
|
602
|
+
} else {
|
|
603
|
+
program.setUniformi(`${uniformPrefix}.kernelSize`, 0);
|
|
692
604
|
}
|
|
693
605
|
};
|
|
694
606
|
publicAPI.setPropertyShaderParameters = (cellBO, ren, actor) => {
|
|
695
607
|
const program = cellBO.getProgram();
|
|
696
|
-
program.setUniformi('ctexture', model.colorTexture.getTextureUnit());
|
|
697
|
-
program.setUniformi('otexture', model.opacityTexture.getTextureUnit());
|
|
698
608
|
program.setUniformi('jtexture', model.jitterTexture.getTextureUnit());
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
609
|
+
const volumeProperties = actor.getProperties();
|
|
610
|
+
|
|
611
|
+
// There is only one label outline thickness texture
|
|
612
|
+
program.setUniformi(`labelOutlineThicknessTexture`, model.labelOutlineThicknessTexture.getTextureUnit());
|
|
613
|
+
program.setUniformi('opacityTexture', model.opacityTexture.getTextureUnit());
|
|
614
|
+
program.setUniformi('colorTexture', model.colorTexture.getTextureUnit());
|
|
615
|
+
const uniformPrefix = 'volume';
|
|
616
|
+
const firstValidInput = model.currentValidInputs[0];
|
|
617
|
+
const firstVolumeProperty = volumeProperties[firstValidInput.inputIndex];
|
|
618
|
+
const numberOfComponents = model.previousState.numberOfComponents;
|
|
619
|
+
const useIndependentComponents = model.previousState.useIndependentComponents;
|
|
702
620
|
|
|
703
621
|
// set the component mix when independent
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
622
|
+
if (useIndependentComponents) {
|
|
623
|
+
const independentComponentMix = new Float32Array(4);
|
|
624
|
+
for (let i = 0; i < numberOfComponents; i++) {
|
|
625
|
+
independentComponentMix[i] = firstVolumeProperty.getComponentWeight(i);
|
|
626
|
+
}
|
|
627
|
+
program.setUniform4fv(`${uniformPrefix}.independentComponentMix`, independentComponentMix);
|
|
628
|
+
const transferFunctionsSampleHeight = new Float32Array(4);
|
|
629
|
+
const pixelHeight = 1 / numberOfComponents;
|
|
630
|
+
for (let i = 0; i < numberOfComponents; ++i) {
|
|
631
|
+
transferFunctionsSampleHeight[i] = (i + 0.5) * pixelHeight;
|
|
709
632
|
}
|
|
633
|
+
program.setUniform4fv(`${uniformPrefix}.transferFunctionsSampleHeight`, transferFunctionsSampleHeight);
|
|
710
634
|
}
|
|
635
|
+
const colorForValueFunctionId = model.colorForValueFunctionId;
|
|
636
|
+
program.setUniformi(`${uniformPrefix}.colorForValueFunctionId`, colorForValueFunctionId);
|
|
637
|
+
const computeNormalFromOpacity = firstVolumeProperty.getComputeNormalFromOpacity();
|
|
638
|
+
program.setUniformi(`${uniformPrefix}.computeNormalFromOpacity`, computeNormalFromOpacity);
|
|
711
639
|
|
|
712
640
|
// three levels of shift scale combined into one
|
|
713
641
|
// for performance in the fragment shader
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
const
|
|
720
|
-
const
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
const
|
|
724
|
-
const
|
|
725
|
-
const
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
642
|
+
const colorTextureScale = new Float32Array(4);
|
|
643
|
+
const colorTextureShift = new Float32Array(4);
|
|
644
|
+
const opacityTextureScale = new Float32Array(4);
|
|
645
|
+
const opacityTextureShift = new Float32Array(4);
|
|
646
|
+
for (let component = 0; component < numberOfComponents; component++) {
|
|
647
|
+
const useMultiTexture = model.previousState.multiTexturePerVolumeEnabled;
|
|
648
|
+
const textureIndex = useMultiTexture ? component : 0;
|
|
649
|
+
const volInfoIndex = useMultiTexture ? 0 : component;
|
|
650
|
+
const scalarTexture = model.scalarTextures[textureIndex];
|
|
651
|
+
const volInfo = scalarTexture.getVolumeInfo();
|
|
652
|
+
const target = useIndependentComponents ? component : 0;
|
|
653
|
+
const sscale = volInfo.scale[volInfoIndex];
|
|
654
|
+
|
|
655
|
+
// Color
|
|
656
|
+
const colorFunction = firstVolumeProperty.getRGBTransferFunction(target);
|
|
657
|
+
const colorRange = colorFunction.getRange();
|
|
658
|
+
colorTextureScale[component] = sscale / (colorRange[1] - colorRange[0]);
|
|
659
|
+
colorTextureShift[component] = (volInfo.offset[volInfoIndex] - colorRange[0]) / (colorRange[1] - colorRange[0]);
|
|
660
|
+
|
|
661
|
+
// Opacity
|
|
662
|
+
const opacityFunction = firstVolumeProperty.getScalarOpacity(target);
|
|
663
|
+
const opacityRange = opacityFunction.getRange();
|
|
664
|
+
opacityTextureScale[component] = sscale / (opacityRange[1] - opacityRange[0]);
|
|
665
|
+
opacityTextureShift[component] = (volInfo.offset[volInfoIndex] - opacityRange[0]) / (opacityRange[1] - opacityRange[0]);
|
|
666
|
+
}
|
|
667
|
+
program.setUniform4fv(`${uniformPrefix}.colorTextureScale`, colorTextureScale);
|
|
668
|
+
program.setUniform4fv(`${uniformPrefix}.colorTextureShift`, colorTextureShift);
|
|
669
|
+
program.setUniform4fv(`${uniformPrefix}.opacityTextureScale`, opacityTextureScale);
|
|
670
|
+
program.setUniform4fv(`${uniformPrefix}.opacityTextureShift`, opacityTextureShift);
|
|
671
|
+
if (model.previousState.gradientOpacityEnabled) {
|
|
672
|
+
const gradientOpacityScale = new Array(4);
|
|
673
|
+
const gradientOpacityShift = new Array(4);
|
|
674
|
+
const gradientOpacityMin = new Array(4);
|
|
675
|
+
const gradientOpacityMax = new Array(4);
|
|
676
|
+
if (useIndependentComponents) {
|
|
677
|
+
for (let component = 0; component < numberOfComponents; ++component) {
|
|
678
|
+
const useMultiTexture = model.previousState.multiTexturePerVolumeEnabled;
|
|
679
|
+
const textureIndex = useMultiTexture ? component : 0;
|
|
680
|
+
const volInfoIndex = useMultiTexture ? 0 : component;
|
|
681
|
+
const scalarTexture = model.scalarTextures[textureIndex];
|
|
682
|
+
const volInfo = scalarTexture.getVolumeInfo();
|
|
683
|
+
const sscale = volInfo.scale[volInfoIndex];
|
|
684
|
+
const useGO = firstVolumeProperty.getUseGradientOpacity(component);
|
|
735
685
|
if (useGO) {
|
|
736
|
-
const
|
|
737
|
-
const
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
program.setUniformf(`goshift${nc}`, -goRange[0] * (gomax - gomin) / (goRange[1] - goRange[0]) + gomin);
|
|
686
|
+
const goOpacityRange = [firstVolumeProperty.getGradientOpacityMinimumOpacity(component), firstVolumeProperty.getGradientOpacityMaximumOpacity(component)];
|
|
687
|
+
const goValueRange = [firstVolumeProperty.getGradientOpacityMinimumValue(component), firstVolumeProperty.getGradientOpacityMaximumValue(component)];
|
|
688
|
+
gradientOpacityMin[component] = goOpacityRange[0];
|
|
689
|
+
gradientOpacityMax[component] = goOpacityRange[1];
|
|
690
|
+
gradientOpacityScale[component] = sscale * (goOpacityRange[1] - goOpacityRange[0]) / (goValueRange[1] - goValueRange[0]);
|
|
691
|
+
gradientOpacityShift[component] = -goValueRange[0] * (goOpacityRange[1] - goOpacityRange[0]) / (goValueRange[1] - goValueRange[0]) + goOpacityRange[0];
|
|
743
692
|
} else {
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
693
|
+
gradientOpacityMin[component] = 1;
|
|
694
|
+
gradientOpacityMax[component] = 1;
|
|
695
|
+
gradientOpacityScale[component] = 0;
|
|
696
|
+
gradientOpacityShift[component] = 1;
|
|
748
697
|
}
|
|
749
698
|
}
|
|
750
699
|
} else {
|
|
751
|
-
const
|
|
752
|
-
const
|
|
753
|
-
const
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
const
|
|
757
|
-
|
|
758
|
-
|
|
700
|
+
const component = numberOfComponents - 1;
|
|
701
|
+
const useMultiTexture = model.previousState.multiTexturePerVolumeEnabled;
|
|
702
|
+
const textureIndex = useMultiTexture ? component : 0;
|
|
703
|
+
const volInfoIndex = useMultiTexture ? 0 : component;
|
|
704
|
+
const scalarTexture = model.scalarTextures[textureIndex];
|
|
705
|
+
const volInfo = scalarTexture.getVolumeInfo();
|
|
706
|
+
const sscale = volInfo.scale[volInfoIndex];
|
|
707
|
+
const goOpacityRange = [firstVolumeProperty.getGradientOpacityMinimumOpacity(0), firstVolumeProperty.getGradientOpacityMaximumOpacity(0)];
|
|
708
|
+
const goValueRange = [firstVolumeProperty.getGradientOpacityMinimumValue(0), firstVolumeProperty.getGradientOpacityMaximumValue(0)];
|
|
709
|
+
gradientOpacityMin[0] = goOpacityRange[0];
|
|
710
|
+
gradientOpacityMax[0] = goOpacityRange[1];
|
|
711
|
+
gradientOpacityScale[0] = sscale * (goOpacityRange[1] - goOpacityRange[0]) / (goValueRange[1] - goValueRange[0]);
|
|
712
|
+
gradientOpacityShift[0] = -goValueRange[0] * (goOpacityRange[1] - goOpacityRange[0]) / (goValueRange[1] - goValueRange[0]) + goOpacityRange[0];
|
|
759
713
|
}
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
program.setUniformf(
|
|
769
|
-
program.setUniformf(
|
|
770
|
-
program.setUniformf(
|
|
714
|
+
program.setUniform4f(`${uniformPrefix}.gradientOpacityScale`, gradientOpacityScale);
|
|
715
|
+
program.setUniform4f(`${uniformPrefix}.gradientOpacityShift`, gradientOpacityShift);
|
|
716
|
+
program.setUniform4f(`${uniformPrefix}.gradientOpacityMin`, gradientOpacityMin);
|
|
717
|
+
program.setUniform4f(`${uniformPrefix}.gradientOpacityMax`, gradientOpacityMax);
|
|
718
|
+
}
|
|
719
|
+
const outlineOpacity = firstVolumeProperty.getLabelOutlineOpacity();
|
|
720
|
+
program.setUniformf(`${uniformPrefix}.outlineOpacity`, outlineOpacity);
|
|
721
|
+
if (model.numberOfLights > 0) {
|
|
722
|
+
program.setUniformf(`${uniformPrefix}.ambient`, firstVolumeProperty.getAmbient());
|
|
723
|
+
program.setUniformf(`${uniformPrefix}.diffuse`, firstVolumeProperty.getDiffuse());
|
|
724
|
+
program.setUniformf(`${uniformPrefix}.specular`, firstVolumeProperty.getSpecular());
|
|
725
|
+
const specularPower = firstVolumeProperty.getSpecularPower();
|
|
726
|
+
program.setUniformf(`${uniformPrefix}.specularPower`, specularPower === 0 ? 1.0 : specularPower);
|
|
771
727
|
}
|
|
772
728
|
};
|
|
773
729
|
publicAPI.getClippingPlaneShaderParameters = (cellBO, ren, actor) => {
|
|
@@ -911,14 +867,22 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
911
867
|
publicAPI.updateBufferObjects(ren, actor);
|
|
912
868
|
|
|
913
869
|
// set interpolation on the texture based on property setting
|
|
914
|
-
const
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
870
|
+
const volumeProperties = actor.getProperties();
|
|
871
|
+
model.currentValidInputs.forEach(_ref => {
|
|
872
|
+
let {
|
|
873
|
+
inputIndex
|
|
874
|
+
} = _ref;
|
|
875
|
+
const volumeProperty = volumeProperties[inputIndex];
|
|
876
|
+
const interpolationType = volumeProperty.getInterpolationType();
|
|
877
|
+
const scalarTexture = model.scalarTextures[inputIndex];
|
|
878
|
+
if (interpolationType === InterpolationType.NEAREST) {
|
|
879
|
+
scalarTexture.setMinificationFilter(Filter.NEAREST);
|
|
880
|
+
scalarTexture.setMagnificationFilter(Filter.NEAREST);
|
|
881
|
+
} else {
|
|
882
|
+
scalarTexture.setMinificationFilter(Filter.LINEAR);
|
|
883
|
+
scalarTexture.setMagnificationFilter(Filter.LINEAR);
|
|
884
|
+
}
|
|
885
|
+
});
|
|
922
886
|
|
|
923
887
|
// if we have a zbuffer texture then activate it
|
|
924
888
|
if (model.zBufferTexture !== null) {
|
|
@@ -929,24 +893,14 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
929
893
|
const gl = model.context;
|
|
930
894
|
|
|
931
895
|
// render the texture
|
|
932
|
-
model.
|
|
933
|
-
|
|
934
|
-
model.labelOutlineThicknessTexture.activate();
|
|
935
|
-
model.colorTexture.activate();
|
|
936
|
-
model.jitterTexture.activate();
|
|
896
|
+
const allTextures = [...model.scalarTextures, model.colorTexture, model.opacityTexture, model.labelOutlineThicknessTexture, model.jitterTexture];
|
|
897
|
+
allTextures.forEach(texture => texture.activate());
|
|
937
898
|
publicAPI.updateShaders(model.tris, ren, actor);
|
|
938
899
|
|
|
939
900
|
// First we do the triangles, update the shader, set uniforms, etc.
|
|
940
|
-
// for (let i = 0; i < 11; ++i) {
|
|
941
|
-
// gl.drawArrays(gl.TRIANGLES, 66 * i, 66);
|
|
942
|
-
// }
|
|
943
901
|
gl.drawArrays(gl.TRIANGLES, 0, model.tris.getCABO().getElementCount());
|
|
944
902
|
model.tris.getVAO().release();
|
|
945
|
-
|
|
946
|
-
model.colorTexture.deactivate();
|
|
947
|
-
model.opacityTexture.deactivate();
|
|
948
|
-
model.labelOutlineThicknessTexture.deactivate();
|
|
949
|
-
model.jitterTexture.deactivate();
|
|
903
|
+
allTextures.forEach(texture => texture.deactivate());
|
|
950
904
|
};
|
|
951
905
|
publicAPI.renderPieceFinish = (ren, actor) => {
|
|
952
906
|
// if we have a zbuffer texture then deactivate it
|
|
@@ -990,69 +944,102 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
990
944
|
publicAPI.invokeEvent({
|
|
991
945
|
type: 'StartEvent'
|
|
992
946
|
});
|
|
947
|
+
|
|
948
|
+
// Get the valid image data inputs
|
|
993
949
|
model.renderable.update();
|
|
994
|
-
|
|
950
|
+
const numberOfInputs = model.renderable.getNumberOfInputPorts();
|
|
951
|
+
model.currentValidInputs = [];
|
|
952
|
+
for (let inputIndex = 0; inputIndex < numberOfInputs; ++inputIndex) {
|
|
953
|
+
const imageData = model.renderable.getInputData(inputIndex);
|
|
954
|
+
if (imageData && !imageData.isDeleted()) {
|
|
955
|
+
model.currentValidInputs.push({
|
|
956
|
+
imageData,
|
|
957
|
+
inputIndex
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
const volumeProperties = actor.getProperties();
|
|
962
|
+
const firstValidInput = model.currentValidInputs[0];
|
|
963
|
+
const firstImageData = firstValidInput.imageData;
|
|
964
|
+
const firstScalars = firstImageData.getPointData().getScalars();
|
|
965
|
+
const firstVolumeProperty = volumeProperties[firstValidInput.inputIndex];
|
|
966
|
+
|
|
967
|
+
// Get the number of lights
|
|
968
|
+
let newNumberOfLights = 0;
|
|
969
|
+
if (firstVolumeProperty.getShade() && model.renderable.getBlendMode() === BlendMode.COMPOSITE_BLEND) {
|
|
970
|
+
ren.getLights().forEach(light => {
|
|
971
|
+
if (light.getSwitch() > 0) {
|
|
972
|
+
newNumberOfLights++;
|
|
973
|
+
}
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
if (newNumberOfLights !== model.numberOfLights) {
|
|
977
|
+
model.numberOfLights = newNumberOfLights;
|
|
978
|
+
publicAPI.modified();
|
|
979
|
+
}
|
|
995
980
|
publicAPI.invokeEvent({
|
|
996
981
|
type: 'EndEvent'
|
|
997
982
|
});
|
|
998
|
-
if (
|
|
999
|
-
vtkErrorMacro('No input!');
|
|
983
|
+
if (model.currentValidInputs.length === 0) {
|
|
1000
984
|
return;
|
|
1001
985
|
}
|
|
986
|
+
|
|
987
|
+
// Number of components
|
|
988
|
+
const numberOfValidInputs = model.currentValidInputs.length;
|
|
989
|
+
const multiTexturePerVolumeEnabled = numberOfValidInputs > 1;
|
|
990
|
+
model.numberOfComponents = multiTexturePerVolumeEnabled ? numberOfValidInputs : firstScalars.getNumberOfComponents();
|
|
991
|
+
model.useIndependentComponents = getUseIndependentComponents(firstVolumeProperty, model.numberOfComponents);
|
|
1002
992
|
publicAPI.renderPieceStart(ren, actor);
|
|
1003
993
|
publicAPI.renderPieceDraw(ren, actor);
|
|
1004
994
|
publicAPI.renderPieceFinish(ren, actor);
|
|
1005
995
|
};
|
|
1006
|
-
publicAPI.computeBounds = (ren, actor) => {
|
|
1007
|
-
if (!publicAPI.getInput()) {
|
|
1008
|
-
uninitializeBounds(model.Bounds);
|
|
1009
|
-
return;
|
|
1010
|
-
}
|
|
1011
|
-
model.bounds = publicAPI.getInput().getBounds();
|
|
1012
|
-
};
|
|
1013
996
|
publicAPI.updateBufferObjects = (ren, actor) => {
|
|
1014
997
|
// Rebuild buffers if needed
|
|
1015
998
|
if (publicAPI.getNeedToRebuildBufferObjects(ren, actor)) {
|
|
1016
999
|
publicAPI.buildBufferObjects(ren, actor);
|
|
1017
1000
|
}
|
|
1018
1001
|
};
|
|
1019
|
-
publicAPI.getNeedToRebuildBufferObjects = (ren, actor) => {
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
};
|
|
1002
|
+
publicAPI.getNeedToRebuildBufferObjects = (ren, actor) => model.VBOBuildTime.getMTime() < publicAPI.getMTime() || model.VBOBuildTime.getMTime() < actor.getMTime() || model.VBOBuildTime.getMTime() < actor.getProperty(model.currentValidInputs[0].inputIndex)?.getMTime() || model.VBOBuildTime.getMTime() < model.renderable.getMTime() || model.currentValidInputs.some(_ref2 => {
|
|
1003
|
+
let {
|
|
1004
|
+
imageData
|
|
1005
|
+
} = _ref2;
|
|
1006
|
+
return model.VBOBuildTime.getMTime() < imageData.getMTime();
|
|
1007
|
+
}) || model.scalarTextures.length !== model.currentValidInputs.length || !model.scalarTextures.every(texture => !!texture?.getHandle()) || !model.colorTexture?.getHandle() || !model.opacityTexture?.getHandle() || !model.labelOutlineThicknessTexture?.getHandle() || !model.jitterTexture?.getHandle();
|
|
1026
1008
|
publicAPI.buildBufferObjects = (ren, actor) => {
|
|
1027
|
-
const image = model.currentInput;
|
|
1028
|
-
if (!image) {
|
|
1029
|
-
return;
|
|
1030
|
-
}
|
|
1031
|
-
const scalars = image.getPointData() && image.getPointData().getScalars();
|
|
1032
|
-
if (!scalars) {
|
|
1033
|
-
return;
|
|
1034
|
-
}
|
|
1035
|
-
const vprop = actor.getProperty();
|
|
1036
1009
|
if (!model.jitterTexture.getHandle()) {
|
|
1037
|
-
const
|
|
1010
|
+
const jitterArray = new Float32Array(32 * 32);
|
|
1038
1011
|
for (let i = 0; i < 32 * 32; ++i) {
|
|
1039
|
-
|
|
1012
|
+
jitterArray[i] = Math.random();
|
|
1040
1013
|
}
|
|
1041
|
-
model.jitterTexture.setMinificationFilter(Filter.
|
|
1042
|
-
model.jitterTexture.setMagnificationFilter(Filter.
|
|
1043
|
-
model.jitterTexture.create2DFromRaw(
|
|
1014
|
+
model.jitterTexture.setMinificationFilter(Filter.NEAREST);
|
|
1015
|
+
model.jitterTexture.setMagnificationFilter(Filter.NEAREST);
|
|
1016
|
+
model.jitterTexture.create2DFromRaw({
|
|
1017
|
+
width: 32,
|
|
1018
|
+
height: 32,
|
|
1019
|
+
numComps: 1,
|
|
1020
|
+
dataType: VtkDataTypes.FLOAT,
|
|
1021
|
+
data: jitterArray
|
|
1022
|
+
});
|
|
1044
1023
|
}
|
|
1045
|
-
const
|
|
1046
|
-
const
|
|
1047
|
-
const
|
|
1048
|
-
const
|
|
1049
|
-
const
|
|
1050
|
-
|
|
1051
|
-
|
|
1024
|
+
const volumeProperties = actor.getProperties();
|
|
1025
|
+
const firstValidInput = model.currentValidInputs[0];
|
|
1026
|
+
const firstVolumeProperty = volumeProperties[firstValidInput.inputIndex];
|
|
1027
|
+
const numberOfComponents = model.numberOfComponents;
|
|
1028
|
+
const useIndependentComps = model.useIndependentComponents;
|
|
1029
|
+
const numIComps = useIndependentComps ? numberOfComponents : 1;
|
|
1030
|
+
|
|
1031
|
+
// rebuild opacity tfun?
|
|
1032
|
+
const opacityFunctions = [];
|
|
1033
|
+
for (let component = 0; component < numIComps; ++component) {
|
|
1034
|
+
opacityFunctions.push(firstVolumeProperty.getScalarOpacity(component));
|
|
1035
|
+
}
|
|
1036
|
+
const opacityFuncHash = getTransferFunctionsHash(opacityFunctions, useIndependentComps, numIComps);
|
|
1037
|
+
const firstScalarOpacityFunc = firstVolumeProperty.getScalarOpacity();
|
|
1038
|
+
const opTex = model._openGLRenderWindow.getGraphicsResourceForObject(firstScalarOpacityFunc);
|
|
1039
|
+
const reBuildOp = !opTex?.oglObject?.getHandle() || opTex.hash !== opacityFuncHash;
|
|
1052
1040
|
if (reBuildOp) {
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
// rebuild opacity tfun?
|
|
1041
|
+
const newOpacityTexture = vtkOpenGLTexture.newInstance();
|
|
1042
|
+
newOpacityTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
|
|
1056
1043
|
let oWidth = model.renderable.getOpacityTextureWidth();
|
|
1057
1044
|
if (oWidth <= 0) {
|
|
1058
1045
|
oWidth = model.context.getParameter(model.context.MAX_TEXTURE_SIZE);
|
|
@@ -1061,8 +1048,8 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
1061
1048
|
const ofTable = new Float32Array(oSize);
|
|
1062
1049
|
const tmpTable = new Float32Array(oWidth);
|
|
1063
1050
|
for (let c = 0; c < numIComps; ++c) {
|
|
1064
|
-
const ofun =
|
|
1065
|
-
const opacityFactor = publicAPI.getCurrentSampleDistance(ren) /
|
|
1051
|
+
const ofun = firstVolumeProperty.getScalarOpacity(c);
|
|
1052
|
+
const opacityFactor = publicAPI.getCurrentSampleDistance(ren) / firstVolumeProperty.getScalarOpacityUnitDistance(c);
|
|
1066
1053
|
const oRange = ofun.getRange();
|
|
1067
1054
|
ofun.getTable(oRange[0], oRange[1], oWidth, tmpTable, 1);
|
|
1068
1055
|
// adjust for sample distance etc
|
|
@@ -1071,43 +1058,57 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
1071
1058
|
ofTable[c * oWidth * 2 + i + oWidth] = ofTable[c * oWidth * 2 + i];
|
|
1072
1059
|
}
|
|
1073
1060
|
}
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1061
|
+
newOpacityTexture.resetFormatAndType();
|
|
1062
|
+
newOpacityTexture.setMinificationFilter(Filter.LINEAR);
|
|
1063
|
+
newOpacityTexture.setMagnificationFilter(Filter.LINEAR);
|
|
1077
1064
|
|
|
1078
1065
|
// use float texture where possible because we really need the resolution
|
|
1079
1066
|
// for this table. Errors in low values of opacity accumulate to
|
|
1080
1067
|
// visible artifacts. High values of opacity quickly terminate without
|
|
1081
1068
|
// artifacts.
|
|
1082
1069
|
if (model._openGLRenderWindow.getWebgl2() || model.context.getExtension('OES_texture_float') && model.context.getExtension('OES_texture_float_linear')) {
|
|
1083
|
-
|
|
1070
|
+
newOpacityTexture.create2DFromRaw({
|
|
1071
|
+
width: oWidth,
|
|
1072
|
+
height: 2 * numIComps,
|
|
1073
|
+
numComps: 1,
|
|
1074
|
+
dataType: VtkDataTypes.FLOAT,
|
|
1075
|
+
data: ofTable
|
|
1076
|
+
});
|
|
1084
1077
|
} else {
|
|
1085
1078
|
const oTable = new Uint8ClampedArray(oSize);
|
|
1086
1079
|
for (let i = 0; i < oSize; ++i) {
|
|
1087
1080
|
oTable[i] = 255.0 * ofTable[i];
|
|
1088
1081
|
}
|
|
1089
|
-
|
|
1082
|
+
newOpacityTexture.create2DFromRaw({
|
|
1083
|
+
width: oWidth,
|
|
1084
|
+
height: 2 * numIComps,
|
|
1085
|
+
numComps: 1,
|
|
1086
|
+
dataType: VtkDataTypes.UNSIGNED_CHAR,
|
|
1087
|
+
data: oTable
|
|
1088
|
+
});
|
|
1090
1089
|
}
|
|
1091
|
-
if (
|
|
1092
|
-
model._openGLRenderWindow.setGraphicsResourceForObject(
|
|
1093
|
-
if (scalarOpacityFunc !== model._scalarOpacityFunc) {
|
|
1094
|
-
model._openGLRenderWindow.registerGraphicsResourceUser(scalarOpacityFunc, publicAPI);
|
|
1095
|
-
model._openGLRenderWindow.unregisterGraphicsResourceUser(model._scalarOpacityFunc, publicAPI);
|
|
1096
|
-
}
|
|
1097
|
-
model._scalarOpacityFunc = scalarOpacityFunc;
|
|
1090
|
+
if (firstScalarOpacityFunc) {
|
|
1091
|
+
model._openGLRenderWindow.setGraphicsResourceForObject(firstScalarOpacityFunc, newOpacityTexture, opacityFuncHash);
|
|
1098
1092
|
}
|
|
1093
|
+
model.opacityTexture = newOpacityTexture;
|
|
1099
1094
|
} else {
|
|
1100
1095
|
model.opacityTexture = opTex.oglObject;
|
|
1101
1096
|
}
|
|
1097
|
+
replaceGraphicsResource(model._openGLRenderWindow, model._opacityTextureCore, firstScalarOpacityFunc);
|
|
1098
|
+
model._opacityTextureCore = firstScalarOpacityFunc;
|
|
1102
1099
|
|
|
1103
1100
|
// rebuild color tfun?
|
|
1104
|
-
const
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1101
|
+
const colorTransferFunctions = [];
|
|
1102
|
+
for (let component = 0; component < numIComps; ++component) {
|
|
1103
|
+
colorTransferFunctions.push(firstVolumeProperty.getRGBTransferFunction(component));
|
|
1104
|
+
}
|
|
1105
|
+
const colorFuncHash = getTransferFunctionsHash(colorTransferFunctions, useIndependentComps, numIComps);
|
|
1106
|
+
const firstColorTransferFunc = firstVolumeProperty.getRGBTransferFunction();
|
|
1107
|
+
const cTex = model._openGLRenderWindow.getGraphicsResourceForObject(firstColorTransferFunc);
|
|
1108
|
+
const reBuildC = !cTex?.oglObject?.getHandle() || cTex?.hash !== colorFuncHash;
|
|
1108
1109
|
if (reBuildC) {
|
|
1109
|
-
|
|
1110
|
-
|
|
1110
|
+
const newColorTexture = vtkOpenGLTexture.newInstance();
|
|
1111
|
+
newColorTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
|
|
1111
1112
|
let cWidth = model.renderable.getColorTextureWidth();
|
|
1112
1113
|
if (cWidth <= 0) {
|
|
1113
1114
|
cWidth = model.context.getParameter(model.context.MAX_TEXTURE_SIZE);
|
|
@@ -1116,7 +1117,7 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
1116
1117
|
const cTable = new Uint8ClampedArray(cSize);
|
|
1117
1118
|
const tmpTable = new Float32Array(cWidth * 3);
|
|
1118
1119
|
for (let c = 0; c < numIComps; ++c) {
|
|
1119
|
-
const cfun =
|
|
1120
|
+
const cfun = firstVolumeProperty.getRGBTransferFunction(c);
|
|
1120
1121
|
const cRange = cfun.getRange();
|
|
1121
1122
|
cfun.getTable(cRange[0], cRange[1], cWidth, tmpTable, 1);
|
|
1122
1123
|
for (let i = 0; i < cWidth * 3; ++i) {
|
|
@@ -1124,57 +1125,122 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
1124
1125
|
cTable[c * cWidth * 6 + i + cWidth * 3] = 255.0 * tmpTable[i];
|
|
1125
1126
|
}
|
|
1126
1127
|
}
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1128
|
+
newColorTexture.resetFormatAndType();
|
|
1129
|
+
newColorTexture.setMinificationFilter(Filter.LINEAR);
|
|
1130
|
+
newColorTexture.setMagnificationFilter(Filter.LINEAR);
|
|
1131
|
+
newColorTexture.create2DFromRaw({
|
|
1132
|
+
width: cWidth,
|
|
1133
|
+
height: 2 * numIComps,
|
|
1134
|
+
numComps: 3,
|
|
1135
|
+
dataType: VtkDataTypes.UNSIGNED_CHAR,
|
|
1136
|
+
data: cTable
|
|
1137
|
+
});
|
|
1138
|
+
model._openGLRenderWindow.setGraphicsResourceForObject(firstColorTransferFunc, newColorTexture, colorFuncHash);
|
|
1139
|
+
model.colorTexture = newColorTexture;
|
|
1139
1140
|
} else {
|
|
1140
1141
|
model.colorTexture = cTex.oglObject;
|
|
1141
1142
|
}
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
const
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1143
|
+
replaceGraphicsResource(model._openGLRenderWindow, model._colorTextureCore, firstColorTransferFunc);
|
|
1144
|
+
model._colorTextureCore = firstColorTransferFunc;
|
|
1145
|
+
|
|
1146
|
+
// rebuild scalarTextures?
|
|
1147
|
+
model.currentValidInputs.forEach((_ref3, component) => {
|
|
1148
|
+
let {
|
|
1149
|
+
imageData,
|
|
1150
|
+
inputIndex
|
|
1151
|
+
} = _ref3;
|
|
1152
|
+
// rebuild the scalarTexture if the data has changed
|
|
1153
|
+
const volumeProperty = volumeProperties[inputIndex];
|
|
1154
|
+
const scalars = imageData.getPointData().getScalars();
|
|
1155
|
+
const tex = model._openGLRenderWindow.getGraphicsResourceForObject(scalars);
|
|
1156
|
+
const scalarsHash = getImageDataHash(imageData, scalars);
|
|
1157
|
+
const reBuildTex = !tex?.oglObject?.getHandle() || tex?.hash !== scalarsHash;
|
|
1158
|
+
const updatedExtents = volumeProperty.getUpdatedExtents();
|
|
1159
|
+
const hasUpdatedExtents = !!updatedExtents.length;
|
|
1160
|
+
if (reBuildTex && !hasUpdatedExtents) {
|
|
1161
|
+
const newScalarTexture = vtkOpenGLTexture.newInstance();
|
|
1162
|
+
newScalarTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
|
|
1163
|
+
// Build the textures
|
|
1164
|
+
const dims = imageData.getDimensions();
|
|
1165
|
+
// Use norm16 for scalar texture if the extension is available
|
|
1166
|
+
newScalarTexture.setOglNorm16Ext(model.context.getExtension('EXT_texture_norm16'));
|
|
1167
|
+
newScalarTexture.resetFormatAndType();
|
|
1168
|
+
newScalarTexture.create3DFilterableFromDataArray({
|
|
1169
|
+
width: dims[0],
|
|
1170
|
+
height: dims[1],
|
|
1171
|
+
depth: dims[2],
|
|
1172
|
+
dataArray: scalars,
|
|
1173
|
+
preferSizeOverAccuracy: volumeProperty.getPreferSizeOverAccuracy()
|
|
1174
|
+
});
|
|
1175
|
+
model._openGLRenderWindow.setGraphicsResourceForObject(scalars, newScalarTexture, scalarsHash);
|
|
1176
|
+
model.scalarTextures[component] = newScalarTexture;
|
|
1177
|
+
} else {
|
|
1178
|
+
model.scalarTextures[component] = tex.oglObject;
|
|
1179
|
+
}
|
|
1180
|
+
if (hasUpdatedExtents) {
|
|
1181
|
+
// If hasUpdatedExtents, then the texture is partially updated.
|
|
1182
|
+
// clear the array to acknowledge the update.
|
|
1183
|
+
volumeProperty.setUpdatedExtents([]);
|
|
1184
|
+
const dims = imageData.getDimensions();
|
|
1185
|
+
model.scalarTextures[component].create3DFilterableFromDataArray({
|
|
1186
|
+
width: dims[0],
|
|
1187
|
+
height: dims[1],
|
|
1188
|
+
depth: dims[2],
|
|
1189
|
+
dataArray: scalars,
|
|
1190
|
+
updatedExtents
|
|
1191
|
+
});
|
|
1192
|
+
}
|
|
1193
|
+
replaceGraphicsResource(model._openGLRenderWindow, model._scalarTexturesCore[component], scalars);
|
|
1194
|
+
model._scalarTexturesCore[component] = scalars;
|
|
1195
|
+
});
|
|
1196
|
+
|
|
1197
|
+
// rebuild label outline thickness texture?
|
|
1198
|
+
const labelOutlineThicknessArray = firstVolumeProperty.getLabelOutlineThickness();
|
|
1199
|
+
const lTex = model._openGLRenderWindow.getGraphicsResourceForObject(labelOutlineThicknessArray);
|
|
1200
|
+
const labelOutlineThicknessHash = labelOutlineThicknessArray.join('-');
|
|
1201
|
+
const reBuildL = !lTex?.oglObject?.getHandle() || lTex?.hash !== labelOutlineThicknessHash;
|
|
1202
|
+
if (reBuildL) {
|
|
1203
|
+
const newLabelOutlineThicknessTexture = vtkOpenGLTexture.newInstance();
|
|
1204
|
+
newLabelOutlineThicknessTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
|
|
1205
|
+
let lWidth = model.renderable.getLabelOutlineTextureWidth();
|
|
1206
|
+
if (lWidth <= 0) {
|
|
1207
|
+
lWidth = model.context.getParameter(model.context.MAX_TEXTURE_SIZE);
|
|
1208
|
+
}
|
|
1209
|
+
const lHeight = 1;
|
|
1210
|
+
const lSize = lWidth * lHeight;
|
|
1211
|
+
const lTable = new Uint8Array(lSize);
|
|
1212
|
+
|
|
1213
|
+
// Assuming labelOutlineThicknessArray contains the thickness for each segment
|
|
1214
|
+
for (let i = 0; i < lWidth; ++i) {
|
|
1215
|
+
// Retrieve the thickness value for the current segment index.
|
|
1216
|
+
// If the value is undefined, use the first element's value as a default, otherwise use the value (even if 0)
|
|
1217
|
+
const thickness = typeof labelOutlineThicknessArray[i] !== 'undefined' ? labelOutlineThicknessArray[i] : labelOutlineThicknessArray[0];
|
|
1218
|
+
lTable[i] = thickness;
|
|
1165
1219
|
}
|
|
1220
|
+
newLabelOutlineThicknessTexture.resetFormatAndType();
|
|
1221
|
+
newLabelOutlineThicknessTexture.setMinificationFilter(Filter.NEAREST);
|
|
1222
|
+
newLabelOutlineThicknessTexture.setMagnificationFilter(Filter.NEAREST);
|
|
1223
|
+
|
|
1224
|
+
// Create a 2D texture (acting as 1D) from the raw data
|
|
1225
|
+
newLabelOutlineThicknessTexture.create2DFromRaw({
|
|
1226
|
+
width: lWidth,
|
|
1227
|
+
height: lHeight,
|
|
1228
|
+
numComps: 1,
|
|
1229
|
+
dataType: VtkDataTypes.UNSIGNED_CHAR,
|
|
1230
|
+
data: lTable
|
|
1231
|
+
});
|
|
1232
|
+
if (labelOutlineThicknessArray) {
|
|
1233
|
+
model._openGLRenderWindow.setGraphicsResourceForObject(labelOutlineThicknessArray, newLabelOutlineThicknessTexture, labelOutlineThicknessHash);
|
|
1234
|
+
}
|
|
1235
|
+
model.labelOutlineThicknessTexture = newLabelOutlineThicknessTexture;
|
|
1166
1236
|
} else {
|
|
1167
|
-
model.
|
|
1168
|
-
}
|
|
1169
|
-
if (hasUpdatedExtents) {
|
|
1170
|
-
// If hasUpdatedExtents, then the texture is partially updated.
|
|
1171
|
-
// clear the array to acknowledge the update.
|
|
1172
|
-
model.renderable.setUpdatedExtents([]);
|
|
1173
|
-
const dims = image.getDimensions();
|
|
1174
|
-
model.scalarTexture.create3DFilterableFromDataArray(dims[0], dims[1], dims[2], scalars, false, updatedExtents);
|
|
1237
|
+
model.labelOutlineThicknessTexture = lTex.oglObject;
|
|
1175
1238
|
}
|
|
1239
|
+
replaceGraphicsResource(model._openGLRenderWindow, model._labelOutlineThicknessTextureCore, labelOutlineThicknessArray);
|
|
1240
|
+
model._labelOutlineThicknessTextureCore = labelOutlineThicknessArray;
|
|
1241
|
+
|
|
1242
|
+
// rebuild the CABO?
|
|
1176
1243
|
if (!model.tris.getCABO().getElementCount()) {
|
|
1177
|
-
// build the CABO
|
|
1178
1244
|
const ptsArray = new Float32Array(12);
|
|
1179
1245
|
for (let i = 0; i < 4; i++) {
|
|
1180
1246
|
ptsArray[i * 3] = i % 2 * 2 - 1.0;
|
|
@@ -1190,33 +1256,6 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
1190
1256
|
cellArray[5] = 0;
|
|
1191
1257
|
cellArray[6] = 3;
|
|
1192
1258
|
cellArray[7] = 2;
|
|
1193
|
-
|
|
1194
|
-
// const dim = 12.0;
|
|
1195
|
-
// const ptsArray = new Float32Array(3 * dim * dim);
|
|
1196
|
-
// for (let i = 0; i < dim; i++) {
|
|
1197
|
-
// for (let j = 0; j < dim; j++) {
|
|
1198
|
-
// const offset = ((i * dim) + j) * 3;
|
|
1199
|
-
// ptsArray[offset] = (2.0 * (i / (dim - 1.0))) - 1.0;
|
|
1200
|
-
// ptsArray[offset + 1] = (2.0 * (j / (dim - 1.0))) - 1.0;
|
|
1201
|
-
// ptsArray[offset + 2] = -1.0;
|
|
1202
|
-
// }
|
|
1203
|
-
// }
|
|
1204
|
-
|
|
1205
|
-
// const cellArray = new Uint16Array(8 * (dim - 1) * (dim - 1));
|
|
1206
|
-
// for (let i = 0; i < dim - 1; i++) {
|
|
1207
|
-
// for (let j = 0; j < dim - 1; j++) {
|
|
1208
|
-
// const offset = 8 * ((i * (dim - 1)) + j);
|
|
1209
|
-
// cellArray[offset] = 3;
|
|
1210
|
-
// cellArray[offset + 1] = (i * dim) + j;
|
|
1211
|
-
// cellArray[offset + 2] = (i * dim) + 1 + j;
|
|
1212
|
-
// cellArray[offset + 3] = ((i + 1) * dim) + 1 + j;
|
|
1213
|
-
// cellArray[offset + 4] = 3;
|
|
1214
|
-
// cellArray[offset + 5] = (i * dim) + j;
|
|
1215
|
-
// cellArray[offset + 6] = ((i + 1) * dim) + 1 + j;
|
|
1216
|
-
// cellArray[offset + 7] = ((i + 1) * dim) + j;
|
|
1217
|
-
// }
|
|
1218
|
-
// }
|
|
1219
|
-
|
|
1220
1259
|
const points = vtkDataArray.newInstance({
|
|
1221
1260
|
numberOfComponents: 3,
|
|
1222
1261
|
values: ptsArray
|
|
@@ -1233,56 +1272,6 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
1233
1272
|
}
|
|
1234
1273
|
model.VBOBuildTime.modified();
|
|
1235
1274
|
};
|
|
1236
|
-
publicAPI.updateLabelOutlineThicknessTexture = volume => {
|
|
1237
|
-
const labelOutlineThicknessArray = volume.getProperty().getLabelOutlineThickness();
|
|
1238
|
-
const lTex = model._openGLRenderWindow.getGraphicsResourceForObject(labelOutlineThicknessArray);
|
|
1239
|
-
|
|
1240
|
-
// compute the join of the labelOutlineThicknessArray so that
|
|
1241
|
-
// we can use it to decide whether to rebuild the labelOutlineThicknessTexture
|
|
1242
|
-
// or not
|
|
1243
|
-
const toString = `${labelOutlineThicknessArray.join('-')}`;
|
|
1244
|
-
const reBuildL = !lTex?.oglObject?.getHandle() || lTex?.hash !== toString;
|
|
1245
|
-
if (reBuildL) {
|
|
1246
|
-
model.labelOutlineThicknessTexture = vtkOpenGLTexture.newInstance();
|
|
1247
|
-
model.labelOutlineThicknessTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
|
|
1248
|
-
let lWidth = model.renderable.getLabelOutlineTextureWidth();
|
|
1249
|
-
if (lWidth <= 0) {
|
|
1250
|
-
lWidth = model.context.getParameter(model.context.MAX_TEXTURE_SIZE);
|
|
1251
|
-
}
|
|
1252
|
-
const lHeight = 1;
|
|
1253
|
-
const lSize = lWidth * lHeight;
|
|
1254
|
-
const lTable = new Uint8Array(lSize);
|
|
1255
|
-
|
|
1256
|
-
// Assuming labelOutlineThicknessArray contains the thickness for each segment
|
|
1257
|
-
for (let i = 0; i < lWidth; ++i) {
|
|
1258
|
-
// Retrieve the thickness value for the current segment index.
|
|
1259
|
-
// If the value is undefined, use the first element's value as a default, otherwise use the value (even if 0)
|
|
1260
|
-
const thickness = typeof labelOutlineThicknessArray[i] !== 'undefined' ? labelOutlineThicknessArray[i] : labelOutlineThicknessArray[0];
|
|
1261
|
-
lTable[i] = thickness;
|
|
1262
|
-
}
|
|
1263
|
-
model.labelOutlineThicknessTexture.resetFormatAndType();
|
|
1264
|
-
model.labelOutlineThicknessTexture.setMinificationFilter(Filter.NEAREST);
|
|
1265
|
-
model.labelOutlineThicknessTexture.setMagnificationFilter(Filter.NEAREST);
|
|
1266
|
-
|
|
1267
|
-
// Create a 2D texture (acting as 1D) from the raw data
|
|
1268
|
-
model.labelOutlineThicknessTexture.create2DFromRaw(lWidth, lHeight, 1, VtkDataTypes.UNSIGNED_CHAR, lTable);
|
|
1269
|
-
if (labelOutlineThicknessArray) {
|
|
1270
|
-
model._openGLRenderWindow.setGraphicsResourceForObject(labelOutlineThicknessArray, model.labelOutlineThicknessTexture, toString);
|
|
1271
|
-
if (labelOutlineThicknessArray !== model._labelOutlineThicknessArray) {
|
|
1272
|
-
model._openGLRenderWindow.registerGraphicsResourceUser(labelOutlineThicknessArray, publicAPI);
|
|
1273
|
-
model._openGLRenderWindow.unregisterGraphicsResourceUser(model._labelOutlineThicknessArray, publicAPI);
|
|
1274
|
-
}
|
|
1275
|
-
model._labelOutlineThicknessArray = labelOutlineThicknessArray;
|
|
1276
|
-
}
|
|
1277
|
-
} else {
|
|
1278
|
-
model.labelOutlineThicknessTexture = lTex.oglObject;
|
|
1279
|
-
}
|
|
1280
|
-
};
|
|
1281
|
-
publicAPI.isLabelmapOutlineRequired = actor => {
|
|
1282
|
-
const prop = actor.getProperty();
|
|
1283
|
-
const renderable = model.renderable;
|
|
1284
|
-
return prop.getUseLabelOutline() || renderable.getBlendMode() === BlendMode.LABELMAP_EDGE_PROJECTION_BLEND;
|
|
1285
|
-
};
|
|
1286
1275
|
}
|
|
1287
1276
|
|
|
1288
1277
|
// ----------------------------------------------------------------------------
|
|
@@ -1292,14 +1281,15 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
1292
1281
|
const DEFAULT_VALUES = {
|
|
1293
1282
|
context: null,
|
|
1294
1283
|
VBOBuildTime: null,
|
|
1295
|
-
|
|
1284
|
+
scalarTextures: [],
|
|
1285
|
+
_scalarTexturesCore: [],
|
|
1296
1286
|
opacityTexture: null,
|
|
1297
|
-
|
|
1287
|
+
_opacityTextureCore: null,
|
|
1298
1288
|
colorTexture: null,
|
|
1299
|
-
|
|
1300
|
-
jitterTexture: null,
|
|
1289
|
+
_colorTextureCore: null,
|
|
1301
1290
|
labelOutlineThicknessTexture: null,
|
|
1302
|
-
|
|
1291
|
+
_labelOutlineThicknessTextureCore: null,
|
|
1292
|
+
jitterTexture: null,
|
|
1303
1293
|
tris: null,
|
|
1304
1294
|
framebuffer: null,
|
|
1305
1295
|
copyShader: null,
|
|
@@ -1308,18 +1298,13 @@ const DEFAULT_VALUES = {
|
|
|
1308
1298
|
targetXYF: 1.0,
|
|
1309
1299
|
zBufferTexture: null,
|
|
1310
1300
|
lastZBufferTexture: null,
|
|
1311
|
-
lightComplexity: 0,
|
|
1312
1301
|
fullViewportTime: 1.0,
|
|
1313
1302
|
idxToView: null,
|
|
1314
|
-
|
|
1303
|
+
vecISToVCMatrix: null,
|
|
1315
1304
|
modelToView: null,
|
|
1316
1305
|
projectionToView: null,
|
|
1317
1306
|
avgWindowArea: 0.0,
|
|
1318
1307
|
avgFrameTime: 0.0
|
|
1319
|
-
// _scalars: null,
|
|
1320
|
-
// _scalarOpacityFunc: null,
|
|
1321
|
-
// _colorTransferFunc: null,
|
|
1322
|
-
// _labelOutlineThicknessArray: null,
|
|
1323
1308
|
};
|
|
1324
1309
|
|
|
1325
1310
|
// ----------------------------------------------------------------------------
|
|
@@ -1340,11 +1325,6 @@ function extend(publicAPI, model) {
|
|
|
1340
1325
|
model.jitterTexture.setWrapS(Wrap.REPEAT);
|
|
1341
1326
|
model.jitterTexture.setWrapT(Wrap.REPEAT);
|
|
1342
1327
|
model.framebuffer = vtkOpenGLFramebuffer.newInstance();
|
|
1343
|
-
model.idxToView = mat4.identity(new Float64Array(16));
|
|
1344
|
-
model.idxNormalMatrix = mat3.identity(new Float64Array(9));
|
|
1345
|
-
model.modelToView = mat4.identity(new Float64Array(16));
|
|
1346
|
-
model.projectionToView = mat4.identity(new Float64Array(16));
|
|
1347
|
-
model.projectionToWorld = mat4.identity(new Float64Array(16));
|
|
1348
1328
|
|
|
1349
1329
|
// Build VTK API
|
|
1350
1330
|
setGet(publicAPI, model, ['context']);
|