@kitware/vtk.js 33.0.0-beta.3 → 33.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BREAKING_CHANGES.md +0 -3
- package/Common/Core/DataArray.d.ts +17 -0
- package/Common/Core/DataArray.js +36 -0
- package/Common/Core/ScalarsToColors/Constants.js +7 -2
- package/Common/Core/ScalarsToColors.js +3 -1
- package/Rendering/Core/AbstractImageMapper.d.ts +81 -0
- package/Rendering/Core/AbstractImageMapper.js +5 -2
- package/Rendering/Core/AbstractPicker.d.ts +13 -13
- package/Rendering/Core/AbstractPicker.js +1 -1
- package/Rendering/Core/Actor.d.ts +20 -5
- package/Rendering/Core/Actor.js +68 -5
- package/Rendering/Core/Actor2D.d.ts +22 -0
- package/Rendering/Core/Actor2D.js +1 -1
- package/Rendering/Core/CellPicker.js +4 -1
- package/Rendering/Core/ColorTransferFunction.js +26 -35
- package/Rendering/Core/ImageCPRMapper.d.ts +20 -1
- package/Rendering/Core/ImageCPRMapper.js +7 -5
- package/Rendering/Core/ImageResliceMapper.d.ts +20 -2
- package/Rendering/Core/ImageResliceMapper.js +7 -5
- package/Rendering/Core/ImageSlice.d.ts +23 -7
- package/Rendering/Core/ImageSlice.js +68 -9
- package/Rendering/Core/Mapper.js +8 -16
- package/Rendering/Core/Prop3D.d.ts +2 -39
- package/Rendering/Core/Prop3D.js +2 -81
- package/Rendering/Core/ScalarBarActor.js +4 -2
- package/Rendering/Core/Viewport.js +13 -3
- package/Rendering/Core/Volume.d.ts +20 -5
- package/Rendering/Core/Volume.js +70 -2
- package/Rendering/Core/VolumeMapper/Constants.d.ts +7 -0
- package/Rendering/Core/VolumeMapper/Constants.js +8 -2
- package/Rendering/Core/VolumeMapper.d.ts +243 -16
- package/Rendering/Core/VolumeMapper.js +60 -20
- package/Rendering/Core/VolumeProperty/Constants.d.ts +3 -12
- package/Rendering/Core/VolumeProperty/Constants.js +4 -11
- package/Rendering/Core/VolumeProperty.d.ts +4 -120
- package/Rendering/Core/VolumeProperty.js +4 -49
- package/Rendering/Misc/SynchronizableRenderWindow/BehaviorManager/CameraSynchronizer.js +2 -2
- package/Rendering/OpenGL/ImageCPRMapper.js +36 -29
- package/Rendering/OpenGL/ImageMapper.js +55 -31
- package/Rendering/OpenGL/ImageResliceMapper.js +191 -263
- package/Rendering/OpenGL/PolyDataMapper.js +8 -1
- package/Rendering/OpenGL/RenderWindow/resourceSharingHelper.d.ts +3 -3
- package/Rendering/OpenGL/RenderWindow/resourceSharingHelper.js +5 -8
- package/Rendering/OpenGL/Renderer.js +1 -1
- package/Rendering/OpenGL/Texture.d.ts +29 -8
- package/Rendering/OpenGL/Texture.js +154 -23
- package/Rendering/OpenGL/VolumeMapper.js +792 -712
- package/Rendering/OpenGL/glsl/vtkVolumeFS.glsl.js +1 -1
- package/Rendering/SceneGraph/ViewNode.js +12 -2
- package/Rendering/WebGPU/VolumePassFSQ.js +2 -2
- package/Rendering/WebXR/RenderWindowHelper.js +9 -0
- package/Widgets/Core/WidgetManager.d.ts +12 -1
- package/Widgets/Representations/WidgetRepresentation.d.ts +1 -7
- package/Widgets/Widgets3D/ResliceCursorWidget.d.ts +1 -8
- package/index.d.ts +0 -1
- package/macros2.js +1 -1
- package/package.json +11 -11
- package/Interaction/Manipulators/KeyboardCameraManipulator.d.ts +0 -113
|
@@ -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';
|
|
5
4
|
import vtkDataArray from '../../Common/Core/DataArray.js';
|
|
6
5
|
import { VtkDataTypes } from '../../Common/Core/DataArray/Constants.js';
|
|
7
6
|
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 { InterpolationType, OpacityMode, ColorMixPreset } from '../Core/VolumeProperty/Constants.js';
|
|
17
17
|
import { BlendMode } from '../Core/VolumeMapper/Constants.js';
|
|
18
|
-
import {
|
|
18
|
+
import { getTransferFunctionHash, 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,14 +29,81 @@ const {
|
|
|
29
29
|
// helper methods
|
|
30
30
|
// ----------------------------------------------------------------------------
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
function getColorCodeFromPreset(colorMixPreset) {
|
|
33
|
+
switch (colorMixPreset) {
|
|
34
|
+
case ColorMixPreset.CUSTOM:
|
|
35
|
+
return '//VTK::CustomColorMix';
|
|
36
|
+
case ColorMixPreset.ADDITIVE:
|
|
37
|
+
return `
|
|
38
|
+
// compute normals
|
|
39
|
+
mat4 normalMat = computeMat4Normal(posIS, tValue, tstep);
|
|
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
|
+
}
|
|
40
107
|
|
|
41
108
|
// ----------------------------------------------------------------------------
|
|
42
109
|
// vtkOpenGLVolumeMapper methods
|
|
@@ -45,51 +112,8 @@ const preAllocatedMatrices = {
|
|
|
45
112
|
function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
46
113
|
// Set our className
|
|
47
114
|
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
|
-
}
|
|
90
115
|
function unregisterGraphicsResources(renderWindow) {
|
|
91
|
-
|
|
92
|
-
[...graphicsResourceReferenceCount.keys()].forEach(coreObject => renderWindow.unregisterGraphicsResourceUser(coreObject, publicAPI));
|
|
116
|
+
[model._scalars, model._scalarOpacityFunc, model._colorTransferFunc, model._labelOutlineThicknessArray].forEach(coreObject => renderWindow.unregisterGraphicsResourceUser(coreObject, publicAPI));
|
|
93
117
|
}
|
|
94
118
|
publicAPI.buildPass = () => {
|
|
95
119
|
model.zBufferTexture = null;
|
|
@@ -133,175 +157,216 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
133
157
|
shaders.Fragment = vtkVolumeFS;
|
|
134
158
|
shaders.Geometry = '';
|
|
135
159
|
};
|
|
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
|
+
};
|
|
136
167
|
publicAPI.replaceShaderValues = (shaders, ren, actor) => {
|
|
168
|
+
const actorProps = actor.getProperty();
|
|
137
169
|
let FSSource = shaders.Fragment;
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
170
|
+
|
|
171
|
+
// define some values in the shader
|
|
172
|
+
const iType = actorProps.getInterpolationType();
|
|
173
|
+
if (iType === InterpolationType.LINEAR) {
|
|
174
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::TrilinearOn', '#define vtkTrilinearOn').result;
|
|
175
|
+
}
|
|
176
|
+
const vtkImageLabelOutline = publicAPI.isLabelmapOutlineRequired(actor);
|
|
177
|
+
if (vtkImageLabelOutline === true) {
|
|
178
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::ImageLabelOutlineOn', '#define vtkImageLabelOutlineOn').result;
|
|
179
|
+
}
|
|
180
|
+
const LabelEdgeProjection = model.renderable.getBlendMode() === BlendMode.LABELMAP_EDGE_PROJECTION_BLEND;
|
|
181
|
+
if (LabelEdgeProjection) {
|
|
182
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::LabelEdgeProjectionOn', '#define vtkLabelEdgeProjectionOn').result;
|
|
183
|
+
}
|
|
184
|
+
const numComp = model.scalarTexture.getComponents();
|
|
185
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::NumComponents', `#define vtkNumComponents ${numComp}`).result;
|
|
186
|
+
const useIndependentComps = publicAPI.useIndependentComponents(actorProps);
|
|
187
|
+
if (useIndependentComps) {
|
|
188
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::IndependentComponentsOn', '#define UseIndependentComponents').result;
|
|
142
189
|
}
|
|
143
|
-
|
|
144
|
-
|
|
190
|
+
|
|
191
|
+
// Define any proportional components
|
|
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
|
+
}
|
|
145
238
|
}
|
|
146
|
-
|
|
147
|
-
if
|
|
148
|
-
|
|
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);
|
|
149
245
|
}
|
|
150
|
-
if (model.
|
|
151
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::
|
|
246
|
+
if (model.gopacity) {
|
|
247
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::GradientOpacityOn', '#define vtkGradientOpacityOn').result;
|
|
152
248
|
}
|
|
153
|
-
|
|
154
|
-
|
|
249
|
+
|
|
250
|
+
// set normal from density
|
|
251
|
+
if (model.renderable.getComputeNormalFromOpacity()) {
|
|
252
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::vtkComputeNormalFromOpacity', `#define vtkComputeNormalFromOpacity`).result;
|
|
155
253
|
}
|
|
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;
|
|
158
254
|
|
|
159
255
|
// if we have a ztexture then declare it and use it
|
|
160
|
-
if (model.
|
|
256
|
+
if (model.zBufferTexture !== null) {
|
|
161
257
|
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::ZBuffer::Dec', ['uniform sampler2D zBufferTexture;', 'uniform float vpZWidth;', 'uniform float vpZHeight;']).result;
|
|
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/
|
|
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/rayDir.z;', 'dists.y = min(zdepth,dists.y);']).result;
|
|
163
259
|
}
|
|
164
260
|
|
|
165
261
|
// Set the BlendMode approach
|
|
166
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::BlendMode', `${model.
|
|
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;
|
|
171
|
-
shaders.Fragment = FSSource;
|
|
172
|
-
const numberOfClippingPlanes = model.previousState.numberOfClippingPlanes;
|
|
173
|
-
if (numberOfClippingPlanes > 0) {
|
|
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;
|
|
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;
|
|
176
|
-
}
|
|
262
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::BlendMode', `${model.renderable.getBlendMode()}`).result;
|
|
177
263
|
shaders.Fragment = FSSource;
|
|
264
|
+
publicAPI.replaceShaderLight(shaders, ren, actor);
|
|
265
|
+
publicAPI.replaceShaderClippingPlane(shaders, ren, actor);
|
|
178
266
|
};
|
|
179
|
-
publicAPI.
|
|
180
|
-
|
|
181
|
-
|
|
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
|
-
}
|
|
267
|
+
publicAPI.replaceShaderLight = (shaders, ren, actor) => {
|
|
268
|
+
if (model.lightComplexity === 0) {
|
|
269
|
+
return;
|
|
215
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
|
|
216
276
|
|
|
217
|
-
//
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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;
|
|
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;
|
|
237
282
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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
|
+
}
|
|
295
|
+
shaders.Fragment = FSSource;
|
|
296
|
+
};
|
|
297
|
+
publicAPI.replaceShaderClippingPlane = (shaders, ren, actor) => {
|
|
298
|
+
let FSSource = shaders.Fragment;
|
|
299
|
+
if (model.renderable.getClippingPlanes().length > 0) {
|
|
300
|
+
const clipPlaneSize = model.renderable.getClippingPlanes().length;
|
|
301
|
+
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 < ${clipPlaneSize}; i++) {`, ' float rayDirRatio = dot(rayDir, 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
|
+
}
|
|
304
|
+
shaders.Fragment = FSSource;
|
|
305
|
+
};
|
|
306
|
+
const recomputeLightComplexity = (actor, lights) => {
|
|
307
|
+
// do we need lighting?
|
|
308
|
+
let lightComplexity = 0;
|
|
309
|
+
if (actor.getProperty().getShade() && model.renderable.getBlendMode() === BlendMode.COMPOSITE_BLEND) {
|
|
310
|
+
// consider the lighting complexity to determine which case applies
|
|
311
|
+
// simple headlight, Light Kit, the whole feature set of VTK
|
|
312
|
+
lightComplexity = 0;
|
|
313
|
+
model.numberOfLights = 0;
|
|
314
|
+
lights.forEach(light => {
|
|
315
|
+
const status = light.getSwitch();
|
|
316
|
+
if (status > 0) {
|
|
317
|
+
model.numberOfLights++;
|
|
318
|
+
if (lightComplexity === 0) {
|
|
319
|
+
lightComplexity = 1;
|
|
320
|
+
}
|
|
249
321
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
-
}
|
|
322
|
+
if (lightComplexity === 1 && (model.numberOfLights > 1 || light.getIntensity() !== 1.0 || !light.lightTypeIsHeadLight())) {
|
|
323
|
+
lightComplexity = 2;
|
|
324
|
+
}
|
|
325
|
+
if (lightComplexity < 3 && light.getPositional()) {
|
|
326
|
+
lightComplexity = 3;
|
|
327
|
+
}
|
|
328
|
+
});
|
|
265
329
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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
|
-
}
|
|
330
|
+
if (lightComplexity !== model.lightComplexity) {
|
|
331
|
+
model.lightComplexity = lightComplexity;
|
|
332
|
+
publicAPI.modified();
|
|
277
333
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
334
|
+
};
|
|
335
|
+
publicAPI.getNeedToRebuildShaders = (cellBO, ren, actor) => {
|
|
336
|
+
const actorProps = actor.getProperty();
|
|
337
|
+
recomputeLightComplexity(actor, ren.getLights());
|
|
338
|
+
const numComp = model.scalarTexture.getComponents();
|
|
339
|
+
const opacityModes = [];
|
|
340
|
+
const forceNearestInterps = [];
|
|
341
|
+
for (let nc = 0; nc < numComp; nc++) {
|
|
342
|
+
opacityModes.push(actorProps.getOpacityMode(nc));
|
|
343
|
+
forceNearestInterps.push(actorProps.getForceNearestInterpolation(nc));
|
|
344
|
+
}
|
|
345
|
+
const ext = model.currentInput.getSpatialExtent();
|
|
346
|
+
const spc = model.currentInput.getSpacing();
|
|
347
|
+
const vsize = new Float64Array(3);
|
|
348
|
+
vec3.set(vsize, (ext[1] - ext[0]) * spc[0], (ext[3] - ext[2]) * spc[1], (ext[5] - ext[4]) * spc[2]);
|
|
349
|
+
const maxSamples = vec3.length(vsize) / publicAPI.getCurrentSampleDistance(ren);
|
|
350
|
+
const hasZBufferTexture = !!model.zBufferTexture;
|
|
351
|
+
const state = {
|
|
352
|
+
iComps: actorProps.getIndependentComponents(),
|
|
353
|
+
colorMixPreset: actorProps.getColorMixPreset(),
|
|
354
|
+
interpolationType: actorProps.getInterpolationType(),
|
|
355
|
+
useLabelOutline: publicAPI.isLabelmapOutlineRequired(actor),
|
|
356
|
+
numComp,
|
|
357
|
+
maxSamples,
|
|
358
|
+
useGradientOpacity: actorProps.getUseGradientOpacity(0),
|
|
359
|
+
blendMode: model.renderable.getBlendMode(),
|
|
287
360
|
hasZBufferTexture,
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
mapperShaderReplacements,
|
|
291
|
-
renderPassShaderReplacements,
|
|
292
|
-
colorForValueFunctionId,
|
|
293
|
-
surfaceLightingEnabled,
|
|
294
|
-
volumeLightingEnabled,
|
|
295
|
-
forceNearestInterpolationEnabled,
|
|
296
|
-
multiTexturePerVolumeEnabled,
|
|
297
|
-
gradientOpacityEnabled
|
|
361
|
+
opacityModes,
|
|
362
|
+
forceNearestInterps
|
|
298
363
|
};
|
|
299
364
|
|
|
300
365
|
// We need to rebuild the shader if one of these variables has changed,
|
|
301
366
|
// since they are used in the shader template replacement step.
|
|
302
367
|
// We also need to rebuild if the shader source time is outdated.
|
|
303
|
-
if (cellBO.getProgram()?.getHandle() === 0 || !model.previousState || !DeepEqual(model.previousState,
|
|
304
|
-
model.previousState =
|
|
368
|
+
if (cellBO.getProgram()?.getHandle() === 0 || cellBO.getShaderSourceTime().getMTime() < publicAPI.getMTime() || cellBO.getShaderSourceTime().getMTime() < model.renderable.getMTime() || !model.previousState || !DeepEqual(model.previousState, state)) {
|
|
369
|
+
model.previousState = state;
|
|
305
370
|
return true;
|
|
306
371
|
}
|
|
307
372
|
return false;
|
|
@@ -346,49 +411,27 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
346
411
|
}
|
|
347
412
|
cellBO.getAttributeUpdateTime().modified();
|
|
348
413
|
}
|
|
349
|
-
|
|
350
|
-
program.setUniformf('sampleDistance',
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
//
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
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) {
|
|
414
|
+
program.setUniformi('texture1', model.scalarTexture.getTextureUnit());
|
|
415
|
+
program.setUniformf('sampleDistance', publicAPI.getCurrentSampleDistance(ren));
|
|
416
|
+
const volInfo = model.scalarTexture.getVolumeInfo();
|
|
417
|
+
const ipScalarRange = model.renderable.getIpScalarRange();
|
|
418
|
+
|
|
419
|
+
// In some situations, we might not have computed the scale and offset
|
|
420
|
+
// for the data range, or it might not be needed.
|
|
421
|
+
if (volInfo?.dataComputedScale?.length) {
|
|
422
|
+
const minVals = [];
|
|
423
|
+
const maxVals = [];
|
|
424
|
+
for (let i = 0; i < 4; i++) {
|
|
368
425
|
// convert iprange from 0-1 into data range values
|
|
369
|
-
minVals[
|
|
370
|
-
maxVals[
|
|
426
|
+
minVals[i] = ipScalarRange[0] * volInfo.dataComputedScale[i] + volInfo.dataComputedOffset[i];
|
|
427
|
+
maxVals[i] = ipScalarRange[1] * volInfo.dataComputedScale[i] + volInfo.dataComputedOffset[i];
|
|
371
428
|
// convert data ranges into texture values
|
|
372
|
-
minVals[
|
|
373
|
-
maxVals[
|
|
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);
|
|
429
|
+
minVals[i] = (minVals[i] - volInfo.offset[i]) / volInfo.scale[i];
|
|
430
|
+
maxVals[i] = (maxVals[i] - volInfo.offset[i]) / volInfo.scale[i];
|
|
387
431
|
}
|
|
432
|
+
program.setUniform4f('ipScalarRangeMin', minVals[0], minVals[1], minVals[2], minVals[3]);
|
|
433
|
+
program.setUniform4f('ipScalarRangeMax', maxVals[0], maxVals[1], maxVals[2], maxVals[3]);
|
|
388
434
|
}
|
|
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]);
|
|
392
435
|
|
|
393
436
|
// if we have a zbuffer texture then set it
|
|
394
437
|
if (model.zBufferTexture !== null) {
|
|
@@ -399,174 +442,140 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
399
442
|
}
|
|
400
443
|
};
|
|
401
444
|
publicAPI.setCameraShaderParameters = (cellBO, ren, actor) => {
|
|
402
|
-
//
|
|
403
|
-
|
|
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
|
|
445
|
+
// // [WMVP]C == {world, model, view, projection} coordinates
|
|
446
|
+
// // E.g., WCPC == world to projection coordinate transformation
|
|
413
447
|
const keyMats = model.openGLCamera.getKeyMatrices(ren);
|
|
414
448
|
const actMats = model.openGLVolume.getKeyMatrices();
|
|
415
|
-
mat4.multiply(modelToView, keyMats.wcvc, actMats.mcwc);
|
|
449
|
+
mat4.multiply(model.modelToView, keyMats.wcvc, actMats.mcwc);
|
|
416
450
|
const program = cellBO.getProgram();
|
|
417
|
-
const
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
program.setUniformf('
|
|
421
|
-
program.setUniformf('
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
//
|
|
426
|
-
//
|
|
427
|
-
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
451
|
+
const cam = model.openGLCamera.getRenderable();
|
|
452
|
+
const crange = cam.getClippingRange();
|
|
453
|
+
program.setUniformf('camThick', crange[1] - crange[0]);
|
|
454
|
+
program.setUniformf('camNear', crange[0]);
|
|
455
|
+
program.setUniformf('camFar', crange[1]);
|
|
456
|
+
const bounds = model.currentInput.getBounds();
|
|
457
|
+
const dims = model.currentInput.getDimensions();
|
|
458
|
+
|
|
459
|
+
// compute the viewport bounds of the volume
|
|
460
|
+
// we will only render those fragments.
|
|
461
|
+
const pos = new Float64Array(3);
|
|
462
|
+
const dir = new Float64Array(3);
|
|
463
|
+
let dcxmin = 1.0;
|
|
464
|
+
let dcxmax = -1.0;
|
|
465
|
+
let dcymin = 1.0;
|
|
466
|
+
let dcymax = -1.0;
|
|
467
|
+
for (let i = 0; i < 8; ++i) {
|
|
468
|
+
vec3.set(pos, bounds[i % 2], bounds[2 + Math.floor(i / 2) % 2], bounds[4 + Math.floor(i / 4)]);
|
|
469
|
+
vec3.transformMat4(pos, pos, model.modelToView);
|
|
470
|
+
if (!cam.getParallelProjection()) {
|
|
471
|
+
vec3.normalize(dir, pos);
|
|
472
|
+
|
|
473
|
+
// now find the projection of this point onto a
|
|
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);
|
|
440
480
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
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();
|
|
481
|
+
// now convert to DC
|
|
482
|
+
vec3.transformMat4(pos, pos, keyMats.vcpc);
|
|
483
|
+
dcxmin = Math.min(pos[0], dcxmin);
|
|
484
|
+
dcxmax = Math.max(pos[0], dcxmax);
|
|
485
|
+
dcymin = Math.min(pos[1], dcymin);
|
|
486
|
+
dcymax = Math.max(pos[1], dcymax);
|
|
464
487
|
}
|
|
465
|
-
program.
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
// Positional flag
|
|
508
|
-
const isPositional = light.getPositional();
|
|
509
|
-
program.setUniformi(`${lightPrefix}.isPositional`, isPositional);
|
|
510
|
-
lightIndex++;
|
|
511
|
-
}
|
|
512
|
-
});
|
|
488
|
+
program.setUniformf('dcxmin', dcxmin);
|
|
489
|
+
program.setUniformf('dcxmax', dcxmax);
|
|
490
|
+
program.setUniformf('dcymin', dcymin);
|
|
491
|
+
program.setUniformf('dcymax', dcymax);
|
|
492
|
+
if (program.isUniformUsed('cameraParallel')) {
|
|
493
|
+
program.setUniformi('cameraParallel', cam.getParallelProjection());
|
|
494
|
+
}
|
|
495
|
+
const ext = model.currentInput.getSpatialExtent();
|
|
496
|
+
const spc = model.currentInput.getSpacing();
|
|
497
|
+
const vsize = new Float64Array(3);
|
|
498
|
+
vec3.set(vsize, (ext[1] - ext[0]) * spc[0], (ext[3] - ext[2]) * spc[1], (ext[5] - ext[4]) * spc[2]);
|
|
499
|
+
program.setUniform3f('vSpacing', spc[0], spc[1], spc[2]);
|
|
500
|
+
vec3.set(pos, ext[0], ext[2], ext[4]);
|
|
501
|
+
model.currentInput.indexToWorldVec3(pos, pos);
|
|
502
|
+
vec3.transformMat4(pos, pos, model.modelToView);
|
|
503
|
+
program.setUniform3f('vOriginVC', pos[0], pos[1], pos[2]);
|
|
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);
|
|
513
530
|
}
|
|
514
531
|
|
|
515
|
-
//
|
|
516
|
-
|
|
517
|
-
const
|
|
518
|
-
const
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
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)) {
|
|
532
|
+
// map normals through normal matrix
|
|
533
|
+
// then use a point on the plane to compute the distance
|
|
534
|
+
const normal = new Float64Array(3);
|
|
535
|
+
const pos2 = new Float64Array(3);
|
|
536
|
+
for (let i = 0; i < 6; ++i) {
|
|
537
|
+
switch (i) {
|
|
538
|
+
case 1:
|
|
539
|
+
vec3.set(normal, -1.0, 0.0, 0.0);
|
|
540
|
+
vec3.set(pos2, ext[0], ext[2], ext[4]);
|
|
541
|
+
break;
|
|
542
|
+
case 2:
|
|
543
|
+
vec3.set(normal, 0.0, 1.0, 0.0);
|
|
544
|
+
vec3.set(pos2, ext[1], ext[3], ext[5]);
|
|
545
|
+
break;
|
|
546
|
+
case 3:
|
|
547
|
+
vec3.set(normal, 0.0, -1.0, 0.0);
|
|
548
|
+
vec3.set(pos2, ext[0], ext[2], ext[4]);
|
|
549
|
+
break;
|
|
550
|
+
case 4:
|
|
551
|
+
vec3.set(normal, 0.0, 0.0, 1.0);
|
|
552
|
+
vec3.set(pos2, ext[1], ext[3], ext[5]);
|
|
553
|
+
break;
|
|
554
|
+
case 5:
|
|
555
|
+
vec3.set(normal, 0.0, 0.0, -1.0);
|
|
556
|
+
vec3.set(pos2, ext[0], ext[2], ext[4]);
|
|
557
|
+
break;
|
|
558
|
+
case 0:
|
|
559
|
+
default:
|
|
560
|
+
vec3.set(normal, 1.0, 0.0, 0.0);
|
|
561
|
+
vec3.set(pos2, ext[1], ext[3], ext[5]);
|
|
562
|
+
break;
|
|
563
|
+
}
|
|
564
|
+
vec3.transformMat3(normal, normal, model.idxNormalMatrix);
|
|
565
|
+
vec3.transformMat4(pos2, pos2, model.idxToView);
|
|
566
|
+
const dist = -1.0 * vec3.dot(pos2, normal);
|
|
567
|
+
|
|
568
|
+
// we have the plane in view coordinates
|
|
569
|
+
// specify the planes in view coordinates
|
|
570
|
+
program.setUniform3f(`vPlaneNormal${i}`, normal[0], normal[1], normal[2]);
|
|
571
|
+
program.setUniformf(`vPlaneDistance${i}`, dist);
|
|
572
|
+
}
|
|
573
|
+
if (publicAPI.isLabelmapOutlineRequired(actor)) {
|
|
574
|
+
const image = model.currentInput;
|
|
575
|
+
const worldToIndex = image.getWorldToIndex();
|
|
576
|
+
program.setUniformMatrix('vWCtoIDX', worldToIndex);
|
|
577
|
+
const camera = ren.getActiveCamera();
|
|
578
|
+
const [cRange0, cRange1] = camera.getClippingRange();
|
|
570
579
|
const distance = camera.getDistance();
|
|
571
580
|
|
|
572
581
|
// set the clipping range to be model.distance and model.distance + 0.1
|
|
@@ -579,151 +588,186 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
579
588
|
const labelOutlineKeyMats = model.openGLCamera.getKeyMatrices(ren);
|
|
580
589
|
|
|
581
590
|
// Get the projection coordinate to world coordinate transformation matrix.
|
|
582
|
-
mat4.invert(projectionToWorld, labelOutlineKeyMats.wcpc);
|
|
591
|
+
mat4.invert(model.projectionToWorld, labelOutlineKeyMats.wcpc);
|
|
583
592
|
|
|
584
593
|
// reset the clipping range since the keyMats are cached
|
|
585
|
-
camera.setClippingRange(
|
|
594
|
+
camera.setClippingRange(cRange0, cRange1);
|
|
586
595
|
|
|
587
596
|
// to re compute the matrices for the current camera and cache them
|
|
588
597
|
model.openGLCamera.getKeyMatrices(ren);
|
|
589
|
-
program.setUniformMatrix(
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
program.setUniformf(
|
|
593
|
-
|
|
594
|
-
program.setUniformf(
|
|
595
|
-
program.setUniformf(
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
598
|
+
program.setUniformMatrix('PCWCMatrix', model.projectionToWorld);
|
|
599
|
+
const size = publicAPI.getRenderTargetSize();
|
|
600
|
+
program.setUniformf('vpWidth', size[0]);
|
|
601
|
+
program.setUniformf('vpHeight', size[1]);
|
|
602
|
+
const offset = publicAPI.getRenderTargetOffset();
|
|
603
|
+
program.setUniformf('vpOffsetX', offset[0] / size[0]);
|
|
604
|
+
program.setUniformf('vpOffsetY', offset[1] / size[1]);
|
|
605
|
+
}
|
|
606
|
+
mat4.invert(model.projectionToView, keyMats.vcpc);
|
|
607
|
+
program.setUniformMatrix('PCVCMatrix', model.projectionToView);
|
|
608
|
+
|
|
609
|
+
// handle lighting values
|
|
610
|
+
if (model.lightComplexity === 0) {
|
|
611
|
+
return;
|
|
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());
|
|
604
692
|
}
|
|
605
693
|
};
|
|
606
694
|
publicAPI.setPropertyShaderParameters = (cellBO, ren, actor) => {
|
|
607
695
|
const program = cellBO.getProgram();
|
|
696
|
+
program.setUniformi('ctexture', model.colorTexture.getTextureUnit());
|
|
697
|
+
program.setUniformi('otexture', model.opacityTexture.getTextureUnit());
|
|
608
698
|
program.setUniformi('jtexture', model.jitterTexture.getTextureUnit());
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
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;
|
|
699
|
+
program.setUniformi('ttexture', model.labelOutlineThicknessTexture.getTextureUnit());
|
|
700
|
+
const volInfo = model.scalarTexture.getVolumeInfo();
|
|
701
|
+
const vprop = actor.getProperty();
|
|
620
702
|
|
|
621
703
|
// set the component mix when independent
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
704
|
+
const numComp = model.scalarTexture.getComponents();
|
|
705
|
+
const useIndependentComps = publicAPI.useIndependentComponents(vprop);
|
|
706
|
+
if (useIndependentComps) {
|
|
707
|
+
for (let i = 0; i < numComp; i++) {
|
|
708
|
+
program.setUniformf(`mix${i}`, actor.getProperty().getComponentWeight(i));
|
|
626
709
|
}
|
|
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;
|
|
632
|
-
}
|
|
633
|
-
program.setUniform4fv(`${uniformPrefix}.transferFunctionsSampleHeight`, transferFunctionsSampleHeight);
|
|
634
710
|
}
|
|
635
|
-
const colorForValueFunctionId = model.colorForValueFunctionId;
|
|
636
|
-
program.setUniformi(`${uniformPrefix}.colorForValueFunctionId`, colorForValueFunctionId);
|
|
637
|
-
const computeNormalFromOpacity = firstVolumeProperty.getComputeNormalFromOpacity();
|
|
638
|
-
program.setUniformi(`${uniformPrefix}.computeNormalFromOpacity`, computeNormalFromOpacity);
|
|
639
711
|
|
|
640
712
|
// three levels of shift scale combined into one
|
|
641
713
|
// for performance in the fragment shader
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
const
|
|
648
|
-
const
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
const
|
|
652
|
-
const
|
|
653
|
-
const
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
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);
|
|
714
|
+
for (let i = 0; i < numComp; i++) {
|
|
715
|
+
const target = useIndependentComps ? i : 0;
|
|
716
|
+
const sscale = volInfo.scale[i];
|
|
717
|
+
const ofun = vprop.getScalarOpacity(target);
|
|
718
|
+
const oRange = ofun.getRange();
|
|
719
|
+
const oscale = sscale / (oRange[1] - oRange[0]);
|
|
720
|
+
const oshift = (volInfo.offset[i] - oRange[0]) / (oRange[1] - oRange[0]);
|
|
721
|
+
program.setUniformf(`oshift${i}`, oshift);
|
|
722
|
+
program.setUniformf(`oscale${i}`, oscale);
|
|
723
|
+
const cfun = vprop.getRGBTransferFunction(target);
|
|
724
|
+
const cRange = cfun.getRange();
|
|
725
|
+
const cshift = (volInfo.offset[i] - cRange[0]) / (cRange[1] - cRange[0]);
|
|
726
|
+
const cScale = sscale / (cRange[1] - cRange[0]);
|
|
727
|
+
program.setUniformf(`cshift${i}`, cshift);
|
|
728
|
+
program.setUniformf(`cscale${i}`, cScale);
|
|
729
|
+
}
|
|
730
|
+
if (model.gopacity) {
|
|
731
|
+
if (useIndependentComps) {
|
|
732
|
+
for (let nc = 0; nc < numComp; ++nc) {
|
|
733
|
+
const sscale = volInfo.scale[nc];
|
|
734
|
+
const useGO = vprop.getUseGradientOpacity(nc);
|
|
685
735
|
if (useGO) {
|
|
686
|
-
const
|
|
687
|
-
const
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
736
|
+
const gomin = vprop.getGradientOpacityMinimumOpacity(nc);
|
|
737
|
+
const gomax = vprop.getGradientOpacityMaximumOpacity(nc);
|
|
738
|
+
program.setUniformf(`gomin${nc}`, gomin);
|
|
739
|
+
program.setUniformf(`gomax${nc}`, gomax);
|
|
740
|
+
const goRange = [vprop.getGradientOpacityMinimumValue(nc), vprop.getGradientOpacityMaximumValue(nc)];
|
|
741
|
+
program.setUniformf(`goscale${nc}`, sscale * (gomax - gomin) / (goRange[1] - goRange[0]));
|
|
742
|
+
program.setUniformf(`goshift${nc}`, -goRange[0] * (gomax - gomin) / (goRange[1] - goRange[0]) + gomin);
|
|
692
743
|
} else {
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
744
|
+
program.setUniformf(`gomin${nc}`, 1.0);
|
|
745
|
+
program.setUniformf(`gomax${nc}`, 1.0);
|
|
746
|
+
program.setUniformf(`goscale${nc}`, 0.0);
|
|
747
|
+
program.setUniformf(`goshift${nc}`, 1.0);
|
|
697
748
|
}
|
|
698
749
|
}
|
|
699
750
|
} else {
|
|
700
|
-
const
|
|
701
|
-
const
|
|
702
|
-
const
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
const
|
|
706
|
-
|
|
707
|
-
|
|
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];
|
|
751
|
+
const sscale = volInfo.scale[numComp - 1];
|
|
752
|
+
const gomin = vprop.getGradientOpacityMinimumOpacity(0);
|
|
753
|
+
const gomax = vprop.getGradientOpacityMaximumOpacity(0);
|
|
754
|
+
program.setUniformf('gomin0', gomin);
|
|
755
|
+
program.setUniformf('gomax0', gomax);
|
|
756
|
+
const goRange = [vprop.getGradientOpacityMinimumValue(0), vprop.getGradientOpacityMaximumValue(0)];
|
|
757
|
+
program.setUniformf('goscale0', sscale * (gomax - gomin) / (goRange[1] - goRange[0]));
|
|
758
|
+
program.setUniformf('goshift0', -goRange[0] * (gomax - gomin) / (goRange[1] - goRange[0]) + gomin);
|
|
713
759
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
program.setUniformf(
|
|
723
|
-
program.setUniformf(
|
|
724
|
-
program.setUniformf(
|
|
725
|
-
const specularPower = firstVolumeProperty.getSpecularPower();
|
|
726
|
-
program.setUniformf(`${uniformPrefix}.specularPower`, specularPower === 0 ? 1.0 : specularPower);
|
|
760
|
+
}
|
|
761
|
+
const vtkImageLabelOutline = publicAPI.isLabelmapOutlineRequired(actor);
|
|
762
|
+
if (vtkImageLabelOutline === true) {
|
|
763
|
+
const labelOutlineOpacity = actor.getProperty().getLabelOutlineOpacity();
|
|
764
|
+
program.setUniformf('outlineOpacity', labelOutlineOpacity);
|
|
765
|
+
}
|
|
766
|
+
if (model.lightComplexity > 0) {
|
|
767
|
+
program.setUniformf('vAmbient', vprop.getAmbient());
|
|
768
|
+
program.setUniformf('vDiffuse', vprop.getDiffuse());
|
|
769
|
+
program.setUniformf('vSpecular', vprop.getSpecular());
|
|
770
|
+
program.setUniformf('vSpecularPower', vprop.getSpecularPower());
|
|
727
771
|
}
|
|
728
772
|
};
|
|
729
773
|
publicAPI.getClippingPlaneShaderParameters = (cellBO, ren, actor) => {
|
|
@@ -867,22 +911,14 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
867
911
|
publicAPI.updateBufferObjects(ren, actor);
|
|
868
912
|
|
|
869
913
|
// set interpolation on the texture based on property setting
|
|
870
|
-
const
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
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
|
-
});
|
|
914
|
+
const iType = actor.getProperty().getInterpolationType();
|
|
915
|
+
if (iType === InterpolationType.NEAREST) {
|
|
916
|
+
model.scalarTexture.setMinificationFilter(Filter.NEAREST);
|
|
917
|
+
model.scalarTexture.setMagnificationFilter(Filter.NEAREST);
|
|
918
|
+
} else {
|
|
919
|
+
model.scalarTexture.setMinificationFilter(Filter.LINEAR);
|
|
920
|
+
model.scalarTexture.setMagnificationFilter(Filter.LINEAR);
|
|
921
|
+
}
|
|
886
922
|
|
|
887
923
|
// if we have a zbuffer texture then activate it
|
|
888
924
|
if (model.zBufferTexture !== null) {
|
|
@@ -893,14 +929,24 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
893
929
|
const gl = model.context;
|
|
894
930
|
|
|
895
931
|
// render the texture
|
|
896
|
-
|
|
897
|
-
|
|
932
|
+
model.scalarTexture.activate();
|
|
933
|
+
model.opacityTexture.activate();
|
|
934
|
+
model.labelOutlineThicknessTexture.activate();
|
|
935
|
+
model.colorTexture.activate();
|
|
936
|
+
model.jitterTexture.activate();
|
|
898
937
|
publicAPI.updateShaders(model.tris, ren, actor);
|
|
899
938
|
|
|
900
939
|
// 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
|
+
// }
|
|
901
943
|
gl.drawArrays(gl.TRIANGLES, 0, model.tris.getCABO().getElementCount());
|
|
902
944
|
model.tris.getVAO().release();
|
|
903
|
-
|
|
945
|
+
model.scalarTexture.deactivate();
|
|
946
|
+
model.colorTexture.deactivate();
|
|
947
|
+
model.opacityTexture.deactivate();
|
|
948
|
+
model.labelOutlineThicknessTexture.deactivate();
|
|
949
|
+
model.jitterTexture.deactivate();
|
|
904
950
|
};
|
|
905
951
|
publicAPI.renderPieceFinish = (ren, actor) => {
|
|
906
952
|
// if we have a zbuffer texture then deactivate it
|
|
@@ -944,103 +990,79 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
944
990
|
publicAPI.invokeEvent({
|
|
945
991
|
type: 'StartEvent'
|
|
946
992
|
});
|
|
947
|
-
|
|
948
|
-
// Get the valid image data inputs
|
|
949
993
|
model.renderable.update();
|
|
950
|
-
|
|
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
|
-
}
|
|
994
|
+
model.currentInput = model.renderable.getInputData();
|
|
980
995
|
publicAPI.invokeEvent({
|
|
981
996
|
type: 'EndEvent'
|
|
982
997
|
});
|
|
983
|
-
if (model.
|
|
998
|
+
if (!model.currentInput) {
|
|
999
|
+
vtkErrorMacro('No input!');
|
|
984
1000
|
return;
|
|
985
1001
|
}
|
|
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);
|
|
992
1002
|
publicAPI.renderPieceStart(ren, actor);
|
|
993
1003
|
publicAPI.renderPieceDraw(ren, actor);
|
|
994
1004
|
publicAPI.renderPieceFinish(ren, actor);
|
|
995
1005
|
};
|
|
1006
|
+
publicAPI.computeBounds = (ren, actor) => {
|
|
1007
|
+
if (!publicAPI.getInput()) {
|
|
1008
|
+
uninitializeBounds(model.Bounds);
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
model.bounds = publicAPI.getInput().getBounds();
|
|
1012
|
+
};
|
|
996
1013
|
publicAPI.updateBufferObjects = (ren, actor) => {
|
|
997
1014
|
// Rebuild buffers if needed
|
|
998
1015
|
if (publicAPI.getNeedToRebuildBufferObjects(ren, actor)) {
|
|
999
1016
|
publicAPI.buildBufferObjects(ren, actor);
|
|
1000
1017
|
}
|
|
1001
1018
|
};
|
|
1002
|
-
publicAPI.getNeedToRebuildBufferObjects = (ren, actor) =>
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1019
|
+
publicAPI.getNeedToRebuildBufferObjects = (ren, actor) => {
|
|
1020
|
+
// first do a coarse check
|
|
1021
|
+
if (model.VBOBuildTime.getMTime() < publicAPI.getMTime() || model.VBOBuildTime.getMTime() < actor.getMTime() || model.VBOBuildTime.getMTime() < model.renderable.getMTime() || model.VBOBuildTime.getMTime() < actor.getProperty().getMTime() || model.VBOBuildTime.getMTime() < model.currentInput.getMTime() || !model.scalarTexture?.getHandle() || !model.colorTexture?.getHandle() || !model.labelOutlineThicknessTexture?.getHandle()) {
|
|
1022
|
+
return true;
|
|
1023
|
+
}
|
|
1024
|
+
return false;
|
|
1025
|
+
};
|
|
1008
1026
|
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();
|
|
1009
1036
|
if (!model.jitterTexture.getHandle()) {
|
|
1010
|
-
const
|
|
1037
|
+
const oTable = new Uint8Array(32 * 32);
|
|
1011
1038
|
for (let i = 0; i < 32 * 32; ++i) {
|
|
1012
|
-
|
|
1039
|
+
oTable[i] = 255.0 * Math.random();
|
|
1013
1040
|
}
|
|
1014
|
-
model.jitterTexture.setMinificationFilter(Filter.
|
|
1015
|
-
model.jitterTexture.setMagnificationFilter(Filter.
|
|
1016
|
-
model.jitterTexture.create2DFromRaw(32, 32, 1, VtkDataTypes.
|
|
1017
|
-
}
|
|
1018
|
-
const
|
|
1019
|
-
const
|
|
1020
|
-
const
|
|
1021
|
-
const
|
|
1022
|
-
const
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
// rebuild opacity tfun?
|
|
1026
|
-
const opacityFunctions = [];
|
|
1027
|
-
for (let component = 0; component < numIComps; ++component) {
|
|
1028
|
-
opacityFunctions.push(firstVolumeProperty.getScalarOpacity(component));
|
|
1029
|
-
}
|
|
1030
|
-
const opacityFuncHash = getTransferFunctionsHash(opacityFunctions, useIndependentComps, numIComps);
|
|
1031
|
-
const firstScalarOpacityFunc = firstVolumeProperty.getScalarOpacity();
|
|
1032
|
-
const opTex = model._openGLRenderWindow.getGraphicsResourceForObject(firstScalarOpacityFunc);
|
|
1033
|
-
const reBuildOp = !opTex?.oglObject?.getHandle() || opTex.hash !== opacityFuncHash;
|
|
1041
|
+
model.jitterTexture.setMinificationFilter(Filter.LINEAR);
|
|
1042
|
+
model.jitterTexture.setMagnificationFilter(Filter.LINEAR);
|
|
1043
|
+
model.jitterTexture.create2DFromRaw(32, 32, 1, VtkDataTypes.UNSIGNED_CHAR, oTable);
|
|
1044
|
+
}
|
|
1045
|
+
const numComp = scalars.getNumberOfComponents();
|
|
1046
|
+
const useIndependentComps = publicAPI.useIndependentComponents(vprop);
|
|
1047
|
+
const numIComps = useIndependentComps ? numComp : 1;
|
|
1048
|
+
const scalarOpacityFunc = vprop.getScalarOpacity();
|
|
1049
|
+
const opTex = model._openGLRenderWindow.getGraphicsResourceForObject(scalarOpacityFunc);
|
|
1050
|
+
let toString = getTransferFunctionHash(scalarOpacityFunc, useIndependentComps, numIComps);
|
|
1051
|
+
const reBuildOp = !opTex?.oglObject || opTex.hash !== toString;
|
|
1034
1052
|
if (reBuildOp) {
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1053
|
+
model.opacityTexture = vtkOpenGLTexture.newInstance();
|
|
1054
|
+
model.opacityTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
|
|
1055
|
+
// rebuild opacity tfun?
|
|
1056
|
+
let oWidth = model.renderable.getOpacityTextureWidth();
|
|
1057
|
+
if (oWidth <= 0) {
|
|
1058
|
+
oWidth = model.context.getParameter(model.context.MAX_TEXTURE_SIZE);
|
|
1059
|
+
}
|
|
1038
1060
|
const oSize = oWidth * 2 * numIComps;
|
|
1039
1061
|
const ofTable = new Float32Array(oSize);
|
|
1040
1062
|
const tmpTable = new Float32Array(oWidth);
|
|
1041
1063
|
for (let c = 0; c < numIComps; ++c) {
|
|
1042
|
-
const ofun =
|
|
1043
|
-
const opacityFactor = publicAPI.getCurrentSampleDistance(ren) /
|
|
1064
|
+
const ofun = vprop.getScalarOpacity(c);
|
|
1065
|
+
const opacityFactor = publicAPI.getCurrentSampleDistance(ren) / vprop.getScalarOpacityUnitDistance(c);
|
|
1044
1066
|
const oRange = ofun.getRange();
|
|
1045
1067
|
ofun.getTable(oRange[0], oRange[1], oWidth, tmpTable, 1);
|
|
1046
1068
|
// adjust for sample distance etc
|
|
@@ -1049,51 +1071,52 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
1049
1071
|
ofTable[c * oWidth * 2 + i + oWidth] = ofTable[c * oWidth * 2 + i];
|
|
1050
1072
|
}
|
|
1051
1073
|
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1074
|
+
model.opacityTexture.resetFormatAndType();
|
|
1075
|
+
model.opacityTexture.setMinificationFilter(Filter.LINEAR);
|
|
1076
|
+
model.opacityTexture.setMagnificationFilter(Filter.LINEAR);
|
|
1055
1077
|
|
|
1056
1078
|
// use float texture where possible because we really need the resolution
|
|
1057
1079
|
// for this table. Errors in low values of opacity accumulate to
|
|
1058
1080
|
// visible artifacts. High values of opacity quickly terminate without
|
|
1059
1081
|
// artifacts.
|
|
1060
1082
|
if (model._openGLRenderWindow.getWebgl2() || model.context.getExtension('OES_texture_float') && model.context.getExtension('OES_texture_float_linear')) {
|
|
1061
|
-
|
|
1083
|
+
model.opacityTexture.create2DFromRaw(oWidth, 2 * numIComps, 1, VtkDataTypes.FLOAT, ofTable);
|
|
1062
1084
|
} else {
|
|
1063
1085
|
const oTable = new Uint8ClampedArray(oSize);
|
|
1064
1086
|
for (let i = 0; i < oSize; ++i) {
|
|
1065
1087
|
oTable[i] = 255.0 * ofTable[i];
|
|
1066
1088
|
}
|
|
1067
|
-
|
|
1089
|
+
model.opacityTexture.create2DFromRaw(oWidth, 2 * numIComps, 1, VtkDataTypes.UNSIGNED_CHAR, oTable);
|
|
1068
1090
|
}
|
|
1069
|
-
if (
|
|
1070
|
-
model._openGLRenderWindow.setGraphicsResourceForObject(
|
|
1091
|
+
if (scalarOpacityFunc) {
|
|
1092
|
+
model._openGLRenderWindow.setGraphicsResourceForObject(scalarOpacityFunc, model.opacityTexture, toString);
|
|
1093
|
+
if (scalarOpacityFunc !== model._scalarOpacityFunc) {
|
|
1094
|
+
model._openGLRenderWindow.registerGraphicsResourceUser(scalarOpacityFunc, publicAPI);
|
|
1095
|
+
model._openGLRenderWindow.unregisterGraphicsResourceUser(model._scalarOpacityFunc, publicAPI);
|
|
1096
|
+
}
|
|
1097
|
+
model._scalarOpacityFunc = scalarOpacityFunc;
|
|
1071
1098
|
}
|
|
1072
|
-
model.opacityTexture = newOpacityTexture;
|
|
1073
1099
|
} else {
|
|
1074
1100
|
model.opacityTexture = opTex.oglObject;
|
|
1075
1101
|
}
|
|
1076
|
-
replaceGraphicsResource(model._openGLRenderWindow, model._opacityTextureCore, firstScalarOpacityFunc);
|
|
1077
|
-
model._opacityTextureCore = firstScalarOpacityFunc;
|
|
1078
1102
|
|
|
1079
1103
|
// rebuild color tfun?
|
|
1080
|
-
const
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
const colorFuncHash = getTransferFunctionsHash(colorTransferFunctions, useIndependentComps, numIComps);
|
|
1085
|
-
const firstColorTransferFunc = firstVolumeProperty.getRGBTransferFunction();
|
|
1086
|
-
const cTex = model._openGLRenderWindow.getGraphicsResourceForObject(firstColorTransferFunc);
|
|
1087
|
-
const reBuildC = !cTex?.oglObject?.getHandle() || cTex?.hash !== colorFuncHash;
|
|
1104
|
+
const colorTransferFunc = vprop.getRGBTransferFunction();
|
|
1105
|
+
toString = getTransferFunctionHash(colorTransferFunc, useIndependentComps, numIComps);
|
|
1106
|
+
const cTex = model._openGLRenderWindow.getGraphicsResourceForObject(colorTransferFunc);
|
|
1107
|
+
const reBuildC = !cTex?.oglObject?.getHandle() || cTex?.hash !== toString;
|
|
1088
1108
|
if (reBuildC) {
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1109
|
+
model.colorTexture = vtkOpenGLTexture.newInstance();
|
|
1110
|
+
model.colorTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
|
|
1111
|
+
let cWidth = model.renderable.getColorTextureWidth();
|
|
1112
|
+
if (cWidth <= 0) {
|
|
1113
|
+
cWidth = model.context.getParameter(model.context.MAX_TEXTURE_SIZE);
|
|
1114
|
+
}
|
|
1092
1115
|
const cSize = cWidth * 2 * numIComps * 3;
|
|
1093
1116
|
const cTable = new Uint8ClampedArray(cSize);
|
|
1094
1117
|
const tmpTable = new Float32Array(cWidth * 3);
|
|
1095
1118
|
for (let c = 0; c < numIComps; ++c) {
|
|
1096
|
-
const cfun =
|
|
1119
|
+
const cfun = vprop.getRGBTransferFunction(c);
|
|
1097
1120
|
const cRange = cfun.getRange();
|
|
1098
1121
|
cfun.getTable(cRange[0], cRange[1], cWidth, tmpTable, 1);
|
|
1099
1122
|
for (let i = 0; i < cWidth * 3; ++i) {
|
|
@@ -1101,86 +1124,57 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
1101
1124
|
cTable[c * cWidth * 6 + i + cWidth * 3] = 255.0 * tmpTable[i];
|
|
1102
1125
|
}
|
|
1103
1126
|
}
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1127
|
+
model.colorTexture.resetFormatAndType();
|
|
1128
|
+
model.colorTexture.setMinificationFilter(Filter.LINEAR);
|
|
1129
|
+
model.colorTexture.setMagnificationFilter(Filter.LINEAR);
|
|
1130
|
+
model.colorTexture.create2DFromRaw(cWidth, 2 * numIComps, 3, VtkDataTypes.UNSIGNED_CHAR, cTable);
|
|
1131
|
+
if (colorTransferFunc) {
|
|
1132
|
+
model._openGLRenderWindow.setGraphicsResourceForObject(colorTransferFunc, model.colorTexture, toString);
|
|
1133
|
+
if (colorTransferFunc !== model._colorTransferFunc) {
|
|
1134
|
+
model._openGLRenderWindow.registerGraphicsResourceUser(colorTransferFunc, publicAPI);
|
|
1135
|
+
model._openGLRenderWindow.unregisterGraphicsResourceUser(model._colorTransferFunc, publicAPI);
|
|
1136
|
+
}
|
|
1137
|
+
model._colorTransferFunc = colorTransferFunc;
|
|
1138
|
+
}
|
|
1110
1139
|
} else {
|
|
1111
1140
|
model.colorTexture = cTex.oglObject;
|
|
1112
1141
|
}
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
const
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
newScalarTexture.create3DFilterableFromDataArray(dims[0], dims[1], dims[2], scalars, volumeProperty.getPreferSizeOverAccuracy());
|
|
1137
|
-
model._openGLRenderWindow.setGraphicsResourceForObject(scalars, newScalarTexture, scalarsHash);
|
|
1138
|
-
model.scalarTextures[component] = newScalarTexture;
|
|
1139
|
-
} else {
|
|
1140
|
-
model.scalarTextures[component] = tex.oglObject;
|
|
1141
|
-
}
|
|
1142
|
-
replaceGraphicsResource(model._openGLRenderWindow, model._scalarTexturesCore[component], scalars);
|
|
1143
|
-
model._scalarTexturesCore[component] = scalars;
|
|
1144
|
-
});
|
|
1145
|
-
|
|
1146
|
-
// rebuild label outline thickness texture?
|
|
1147
|
-
const labelOutlineThicknessArray = firstVolumeProperty.getLabelOutlineThickness();
|
|
1148
|
-
const lTex = model._openGLRenderWindow.getGraphicsResourceForObject(labelOutlineThicknessArray);
|
|
1149
|
-
const labelOutlineThicknessHash = labelOutlineThicknessArray.join('-');
|
|
1150
|
-
const reBuildL = !lTex?.oglObject?.getHandle() || lTex?.hash !== labelOutlineThicknessHash;
|
|
1151
|
-
if (reBuildL) {
|
|
1152
|
-
const newLabelOutlineThicknessTexture = vtkOpenGLTexture.newInstance();
|
|
1153
|
-
newLabelOutlineThicknessTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
|
|
1154
|
-
const lWidth = 1024;
|
|
1155
|
-
const lHeight = 1;
|
|
1156
|
-
const lSize = lWidth * lHeight;
|
|
1157
|
-
const lTable = new Uint8Array(lSize);
|
|
1158
|
-
|
|
1159
|
-
// Assuming labelOutlineThicknessArray contains the thickness for each segment
|
|
1160
|
-
for (let i = 0; i < lWidth; ++i) {
|
|
1161
|
-
// Retrieve the thickness value for the current segment index.
|
|
1162
|
-
// If the value is undefined, use the first element's value as a default, otherwise use the value (even if 0)
|
|
1163
|
-
const thickness = typeof labelOutlineThicknessArray[i] !== 'undefined' ? labelOutlineThicknessArray[i] : labelOutlineThicknessArray[0];
|
|
1164
|
-
lTable[i] = thickness;
|
|
1165
|
-
}
|
|
1166
|
-
newLabelOutlineThicknessTexture.resetFormatAndType();
|
|
1167
|
-
newLabelOutlineThicknessTexture.setMinificationFilter(Filter.NEAREST);
|
|
1168
|
-
newLabelOutlineThicknessTexture.setMagnificationFilter(Filter.NEAREST);
|
|
1169
|
-
|
|
1170
|
-
// Create a 2D texture (acting as 1D) from the raw data
|
|
1171
|
-
newLabelOutlineThicknessTexture.create2DFromRaw(lWidth, lHeight, 1, VtkDataTypes.UNSIGNED_CHAR, lTable);
|
|
1172
|
-
if (labelOutlineThicknessArray) {
|
|
1173
|
-
model._openGLRenderWindow.setGraphicsResourceForObject(labelOutlineThicknessArray, newLabelOutlineThicknessTexture, labelOutlineThicknessHash);
|
|
1142
|
+
publicAPI.updateLabelOutlineThicknessTexture(actor);
|
|
1143
|
+
const tex = model._openGLRenderWindow.getGraphicsResourceForObject(scalars);
|
|
1144
|
+
// rebuild the scalarTexture if the data has changed
|
|
1145
|
+
toString = getImageDataHash(image, scalars);
|
|
1146
|
+
const reBuildTex = !tex?.oglObject?.getHandle() || tex?.hash !== toString;
|
|
1147
|
+
const updatedExtents = model.renderable.getUpdatedExtents();
|
|
1148
|
+
const hasUpdatedExtents = !!updatedExtents.length;
|
|
1149
|
+
if (reBuildTex && !hasUpdatedExtents) {
|
|
1150
|
+
model.scalarTexture = vtkOpenGLTexture.newInstance();
|
|
1151
|
+
model.scalarTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
|
|
1152
|
+
// Build the textures
|
|
1153
|
+
const dims = image.getDimensions();
|
|
1154
|
+
// Use norm16 for scalar texture if the extension is available
|
|
1155
|
+
model.scalarTexture.setOglNorm16Ext(model.context.getExtension('EXT_texture_norm16'));
|
|
1156
|
+
model.scalarTexture.resetFormatAndType();
|
|
1157
|
+
model.scalarTexture.create3DFilterableFromDataArray(dims[0], dims[1], dims[2], scalars);
|
|
1158
|
+
if (scalars) {
|
|
1159
|
+
model._openGLRenderWindow.setGraphicsResourceForObject(scalars, model.scalarTexture, toString);
|
|
1160
|
+
if (scalars !== model._scalars) {
|
|
1161
|
+
model._openGLRenderWindow.registerGraphicsResourceUser(scalars, publicAPI);
|
|
1162
|
+
model._openGLRenderWindow.unregisterGraphicsResourceUser(model._scalars, publicAPI);
|
|
1163
|
+
}
|
|
1164
|
+
model._scalars = scalars;
|
|
1174
1165
|
}
|
|
1175
|
-
model.labelOutlineThicknessTexture = newLabelOutlineThicknessTexture;
|
|
1176
1166
|
} else {
|
|
1177
|
-
model.
|
|
1167
|
+
model.scalarTexture = tex.oglObject;
|
|
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);
|
|
1178
1175
|
}
|
|
1179
|
-
replaceGraphicsResource(model._openGLRenderWindow, model._labelOutlineThicknessTextureCore, labelOutlineThicknessArray);
|
|
1180
|
-
model._labelOutlineThicknessTextureCore = labelOutlineThicknessArray;
|
|
1181
|
-
|
|
1182
|
-
// rebuild the CABO?
|
|
1183
1176
|
if (!model.tris.getCABO().getElementCount()) {
|
|
1177
|
+
// build the CABO
|
|
1184
1178
|
const ptsArray = new Float32Array(12);
|
|
1185
1179
|
for (let i = 0; i < 4; i++) {
|
|
1186
1180
|
ptsArray[i * 3] = i % 2 * 2 - 1.0;
|
|
@@ -1196,6 +1190,33 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
1196
1190
|
cellArray[5] = 0;
|
|
1197
1191
|
cellArray[6] = 3;
|
|
1198
1192
|
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
|
+
|
|
1199
1220
|
const points = vtkDataArray.newInstance({
|
|
1200
1221
|
numberOfComponents: 3,
|
|
1201
1222
|
values: ptsArray
|
|
@@ -1212,6 +1233,56 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
1212
1233
|
}
|
|
1213
1234
|
model.VBOBuildTime.modified();
|
|
1214
1235
|
};
|
|
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
|
+
};
|
|
1215
1286
|
}
|
|
1216
1287
|
|
|
1217
1288
|
// ----------------------------------------------------------------------------
|
|
@@ -1221,15 +1292,14 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
1221
1292
|
const DEFAULT_VALUES = {
|
|
1222
1293
|
context: null,
|
|
1223
1294
|
VBOBuildTime: null,
|
|
1224
|
-
|
|
1225
|
-
_scalarTexturesCore: [],
|
|
1295
|
+
scalarTexture: null,
|
|
1226
1296
|
opacityTexture: null,
|
|
1227
|
-
|
|
1297
|
+
opacityTextureString: null,
|
|
1228
1298
|
colorTexture: null,
|
|
1229
|
-
|
|
1230
|
-
labelOutlineThicknessTexture: null,
|
|
1231
|
-
_labelOutlineThicknessTextureCore: null,
|
|
1299
|
+
colorTextureString: null,
|
|
1232
1300
|
jitterTexture: null,
|
|
1301
|
+
labelOutlineThicknessTexture: null,
|
|
1302
|
+
labelOutlineThicknessTextureString: null,
|
|
1233
1303
|
tris: null,
|
|
1234
1304
|
framebuffer: null,
|
|
1235
1305
|
copyShader: null,
|
|
@@ -1238,13 +1308,18 @@ const DEFAULT_VALUES = {
|
|
|
1238
1308
|
targetXYF: 1.0,
|
|
1239
1309
|
zBufferTexture: null,
|
|
1240
1310
|
lastZBufferTexture: null,
|
|
1311
|
+
lightComplexity: 0,
|
|
1241
1312
|
fullViewportTime: 1.0,
|
|
1242
1313
|
idxToView: null,
|
|
1243
|
-
|
|
1314
|
+
idxNormalMatrix: null,
|
|
1244
1315
|
modelToView: null,
|
|
1245
1316
|
projectionToView: null,
|
|
1246
1317
|
avgWindowArea: 0.0,
|
|
1247
1318
|
avgFrameTime: 0.0
|
|
1319
|
+
// _scalars: null,
|
|
1320
|
+
// _scalarOpacityFunc: null,
|
|
1321
|
+
// _colorTransferFunc: null,
|
|
1322
|
+
// _labelOutlineThicknessArray: null,
|
|
1248
1323
|
};
|
|
1249
1324
|
|
|
1250
1325
|
// ----------------------------------------------------------------------------
|
|
@@ -1265,6 +1340,11 @@ function extend(publicAPI, model) {
|
|
|
1265
1340
|
model.jitterTexture.setWrapS(Wrap.REPEAT);
|
|
1266
1341
|
model.jitterTexture.setWrapT(Wrap.REPEAT);
|
|
1267
1342
|
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));
|
|
1268
1348
|
|
|
1269
1349
|
// Build VTK API
|
|
1270
1350
|
setGet(publicAPI, model, ['context']);
|