@kitware/vtk.js 27.5.0 → 28.0.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 +4 -0
- package/Common/DataModel/Line.d.ts +24 -2
- package/Common/DataModel/Line.js +17 -1
- package/Common/DataModel/PolyLine.d.ts +36 -2
- package/Common/DataModel/PolyLine.js +80 -10
- package/Common/Transform/Transform.d.ts +177 -0
- package/Common/Transform/Transform.js +81 -3
- package/Proxy/Core/View2DProxy.js +22 -12
- package/Rendering/Core/AbstractMapper3D.d.ts +1 -3
- package/Rendering/Core/AbstractMapper3D.js +21 -45
- package/Rendering/Core/ImageCPRMapper.d.ts +380 -0
- package/Rendering/Core/ImageCPRMapper.js +361 -0
- package/Rendering/Core/ImageProperty/Constants.js +2 -2
- package/Rendering/Core/ImageProperty.js +2 -2
- package/Rendering/OpenGL/ImageCPRMapper.js +919 -0
- package/Rendering/OpenGL/ImageResliceMapper.js +2 -2
- package/Rendering/OpenGL/Profiles/All.js +1 -0
- package/Rendering/OpenGL/Profiles/Volume.js +1 -0
- package/Rendering/OpenGL/glsl/vtkVolumeFS.glsl.js +1 -1
- package/Rendering/Profiles/All.js +1 -0
- package/Rendering/Profiles/Volume.js +1 -0
- package/Widgets/Core/WidgetManager.js +1 -1
- package/Widgets/Manipulators/AbstractManipulator.d.ts +2 -2
- package/Widgets/Manipulators/CPRManipulator.js +138 -0
- package/Widgets/Manipulators/LineManipulator.js +3 -1
- package/Widgets/Manipulators/PlaneManipulator.js +3 -1
- package/Widgets/Manipulators/TrackballManipulator.js +3 -1
- package/Widgets/Widgets3D/AngleWidget/behavior.js +5 -2
- package/Widgets/Widgets3D/DistanceWidget/behavior.js +5 -2
- package/Widgets/Widgets3D/ImageCroppingWidget/behavior.js +3 -3
- package/Widgets/Widgets3D/ImplicitPlaneWidget.js +3 -1
- package/Widgets/Widgets3D/LabelWidget/behavior.js +5 -2
- package/Widgets/Widgets3D/LineWidget/behavior.js +4 -2
- package/Widgets/Widgets3D/PaintWidget/behavior.js +2 -1
- package/Widgets/Widgets3D/PolyLineWidget/behavior.js +2 -1
- package/Widgets/Widgets3D/ResliceCursorWidget/Constants.js +2 -1
- package/Widgets/Widgets3D/ResliceCursorWidget/behavior.js +72 -34
- package/Widgets/Widgets3D/ResliceCursorWidget/cprBehavior.js +92 -0
- package/Widgets/Widgets3D/ResliceCursorWidget/helpers.js +64 -18
- package/Widgets/Widgets3D/ResliceCursorWidget/state.js +30 -16
- package/Widgets/Widgets3D/ResliceCursorWidget.js +43 -20
- package/Widgets/Widgets3D/ShapeWidget/behavior.js +4 -2
- package/Widgets/Widgets3D/SphereWidget/behavior.js +1 -1
- package/Widgets/Widgets3D/SplineWidget/behavior.js +3 -1
- package/index.d.ts +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,919 @@
|
|
|
1
|
+
import _defineProperty from '@babel/runtime/helpers/defineProperty';
|
|
2
|
+
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
|
|
3
|
+
import macro from '../../macros.js';
|
|
4
|
+
import { mat4, vec3 } from 'gl-matrix';
|
|
5
|
+
import vtkViewNode from '../SceneGraph/ViewNode.js';
|
|
6
|
+
import vtkHelper from './Helper.js';
|
|
7
|
+
import vtkReplacementShaderMapper from './ReplacementShaderMapper.js';
|
|
8
|
+
import vtkShaderProgram from './ShaderProgram.js';
|
|
9
|
+
import vtkOpenGLTexture from './Texture.js';
|
|
10
|
+
import vtkDataArray from '../../Common/Core/DataArray.js';
|
|
11
|
+
import { VtkDataTypes } from '../../Common/Core/DataArray/Constants.js';
|
|
12
|
+
import { Representation } from '../Core/Property/Constants.js';
|
|
13
|
+
import { Filter } from './Texture/Constants.js';
|
|
14
|
+
import { InterpolationType } from '../Core/ImageProperty/Constants.js';
|
|
15
|
+
import { v as vtkPolyDataVS } from './glsl/vtkPolyDataVS.glsl.js';
|
|
16
|
+
import { v as vtkPolyDataFS } from './glsl/vtkPolyDataFS.glsl.js';
|
|
17
|
+
import { registerOverride } from './ViewNodeFactory.js';
|
|
18
|
+
|
|
19
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
20
|
+
|
|
21
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
22
|
+
var vtkErrorMacro = macro.vtkErrorMacro; // ----------------------------------------------------------------------------
|
|
23
|
+
// helper methods
|
|
24
|
+
// ----------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
function computeFnToString(property, fn, numberOfComponents) {
|
|
27
|
+
var pwfun = fn.apply(property);
|
|
28
|
+
|
|
29
|
+
if (pwfun) {
|
|
30
|
+
var iComps = property.getIndependentComponents();
|
|
31
|
+
return "".concat(property.getMTime(), "-").concat(iComps, "-").concat(numberOfComponents);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return '0';
|
|
35
|
+
} // ----------------------------------------------------------------------------
|
|
36
|
+
// vtkOpenGLImageCPRMapper methods
|
|
37
|
+
// ----------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
41
|
+
// Set our className
|
|
42
|
+
model.classHierarchy.push('vtkOpenGLImageCPRMapper');
|
|
43
|
+
|
|
44
|
+
publicAPI.buildPass = function (prepass) {
|
|
45
|
+
if (prepass) {
|
|
46
|
+
model.currentRenderPass = null;
|
|
47
|
+
model.openGLImageSlice = publicAPI.getFirstAncestorOfType('vtkOpenGLImageSlice');
|
|
48
|
+
model._openGLRenderer = publicAPI.getFirstAncestorOfType('vtkOpenGLRenderer');
|
|
49
|
+
model._openGLRenderWindow = model._openGLRenderer.getParent();
|
|
50
|
+
model.context = model._openGLRenderWindow.getContext();
|
|
51
|
+
model.openGLCamera = model._openGLRenderer.getViewNodeFor(model._openGLRenderer.getRenderable().getActiveCamera());
|
|
52
|
+
model.tris.setOpenGLRenderWindow(model._openGLRenderWindow);
|
|
53
|
+
model.volumeTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
|
|
54
|
+
model.colorTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
|
|
55
|
+
model.pwfTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
publicAPI.opaquePass = function (prepass, renderPass) {
|
|
60
|
+
if (prepass) {
|
|
61
|
+
model.currentRenderPass = renderPass;
|
|
62
|
+
publicAPI.render();
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
publicAPI.opaqueZBufferPass = function (prepass) {
|
|
67
|
+
if (prepass) {
|
|
68
|
+
model.haveSeenDepthRequest = true;
|
|
69
|
+
model.renderDepth = true;
|
|
70
|
+
publicAPI.render();
|
|
71
|
+
model.renderDepth = false;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
publicAPI.getCoincidentParameters = function (ren, actor) {
|
|
76
|
+
if (model.renderable.getResolveCoincidentTopology()) {
|
|
77
|
+
return model.renderable.getCoincidentTopologyPolygonOffsetParameters();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return null;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
publicAPI.render = function () {
|
|
84
|
+
var prop = model.openGLImageSlice.getRenderable();
|
|
85
|
+
|
|
86
|
+
var ren = model._openGLRenderer.getRenderable();
|
|
87
|
+
|
|
88
|
+
publicAPI.renderPiece(ren, prop);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
publicAPI.renderPiece = function (ren, prop) {
|
|
92
|
+
publicAPI.invokeEvent({
|
|
93
|
+
type: 'StartEvent'
|
|
94
|
+
});
|
|
95
|
+
model.renderable.update();
|
|
96
|
+
publicAPI.invokeEvent({
|
|
97
|
+
type: 'EndEvent'
|
|
98
|
+
}); // Check if the ImageCPRMapper has everything it needs to render
|
|
99
|
+
|
|
100
|
+
if (!model.renderable.preRenderCheck()) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
model.currentImageDataInput = model.renderable.getInputData(0);
|
|
105
|
+
model.currentCenterlineInput = model.renderable.getOrientedCenterline();
|
|
106
|
+
publicAPI.renderPieceStart(ren, prop);
|
|
107
|
+
publicAPI.renderPieceDraw(ren, prop);
|
|
108
|
+
publicAPI.renderPieceFinish(ren, prop);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
publicAPI.renderPieceStart = function (ren, actor) {
|
|
112
|
+
// make sure the BOs are up to date
|
|
113
|
+
publicAPI.updateBufferObjects(ren, actor);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
publicAPI.renderPieceDraw = function (ren, actor) {
|
|
117
|
+
var gl = model.context; // activate the texture
|
|
118
|
+
|
|
119
|
+
model.volumeTexture.activate();
|
|
120
|
+
model.colorTexture.activate();
|
|
121
|
+
model.pwfTexture.activate(); // draw polygons
|
|
122
|
+
|
|
123
|
+
if (model.tris.getCABO().getElementCount()) {
|
|
124
|
+
// First we do the triangles, update the shader, set uniforms, etc.
|
|
125
|
+
publicAPI.updateShaders(model.tris, ren, actor);
|
|
126
|
+
gl.drawArrays(gl.TRIANGLES, 0, model.tris.getCABO().getElementCount());
|
|
127
|
+
model.tris.getVAO().release();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
model.volumeTexture.deactivate();
|
|
131
|
+
model.colorTexture.deactivate();
|
|
132
|
+
model.pwfTexture.deactivate();
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
publicAPI.renderPieceFinish = function (ren, actor) {};
|
|
136
|
+
|
|
137
|
+
publicAPI.updateBufferObjects = function (ren, actor) {
|
|
138
|
+
// Rebuild buffers if needed
|
|
139
|
+
if (publicAPI.getNeedToRebuildBufferObjects(ren, actor)) {
|
|
140
|
+
publicAPI.buildBufferObjects(ren, actor);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
publicAPI.getNeedToRebuildBufferObjects = function (ren, actor) {
|
|
145
|
+
// first do a coarse check
|
|
146
|
+
// Note that the actor's mtime includes it's properties mtime
|
|
147
|
+
var vmtime = model.VBOBuildTime.getMTime();
|
|
148
|
+
|
|
149
|
+
if (vmtime < publicAPI.getMTime() || vmtime < model.renderable.getMTime() || vmtime < actor.getMTime() || vmtime < model.currentImageDataInput.getMTime() || vmtime < model.currentCenterlineInput.getMTime()) {
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return false;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
publicAPI.buildBufferObjects = function (ren, actor) {
|
|
157
|
+
var image = model.currentImageDataInput;
|
|
158
|
+
var centerline = model.currentCenterlineInput;
|
|
159
|
+
var actorProperty = actor.getProperty(); // Set interpolation on the texture based on property setting
|
|
160
|
+
|
|
161
|
+
if (actorProperty.getInterpolationType() === InterpolationType.NEAREST) {
|
|
162
|
+
model.volumeTexture.setMinificationFilter(Filter.NEAREST);
|
|
163
|
+
model.volumeTexture.setMagnificationFilter(Filter.NEAREST);
|
|
164
|
+
model.colorTexture.setMinificationFilter(Filter.NEAREST);
|
|
165
|
+
model.colorTexture.setMagnificationFilter(Filter.NEAREST);
|
|
166
|
+
model.pwfTexture.setMinificationFilter(Filter.NEAREST);
|
|
167
|
+
model.pwfTexture.setMagnificationFilter(Filter.NEAREST);
|
|
168
|
+
} else {
|
|
169
|
+
model.volumeTexture.setMinificationFilter(Filter.LINEAR);
|
|
170
|
+
model.volumeTexture.setMagnificationFilter(Filter.LINEAR);
|
|
171
|
+
model.colorTexture.setMinificationFilter(Filter.LINEAR);
|
|
172
|
+
model.colorTexture.setMagnificationFilter(Filter.LINEAR);
|
|
173
|
+
model.pwfTexture.setMinificationFilter(Filter.LINEAR);
|
|
174
|
+
model.pwfTexture.setMagnificationFilter(Filter.LINEAR);
|
|
175
|
+
} // Rebuild the volumeTexture if the data has changed
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
var imageTime = image.getMTime();
|
|
179
|
+
|
|
180
|
+
if (model.volumeTextureTime !== imageTime) {
|
|
181
|
+
// Build the textures
|
|
182
|
+
var dims = image.getDimensions();
|
|
183
|
+
|
|
184
|
+
var _scalars = image.getPointData().getScalars();
|
|
185
|
+
|
|
186
|
+
if (!_scalars) {
|
|
187
|
+
return;
|
|
188
|
+
} // Use norm16 for scalar texture if the extension is available
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
model.volumeTexture.setOglNorm16Ext(model.context.getExtension('EXT_texture_norm16'));
|
|
192
|
+
model.volumeTexture.releaseGraphicsResources(model._openGLRenderWindow);
|
|
193
|
+
model.volumeTexture.resetFormatAndType();
|
|
194
|
+
model.volumeTexture.create3DFilterableFromRaw(dims[0], dims[1], dims[2], _scalars.getNumberOfComponents(), _scalars.getDataType(), _scalars.getData(), model.renderable.getPreferSizeOverAccuracy());
|
|
195
|
+
model.volumeTextureTime = imageTime;
|
|
196
|
+
} // Rebuild the color texture if needed
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
var scalars = image.getPointData() && image.getPointData().getScalars();
|
|
200
|
+
|
|
201
|
+
if (!scalars) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
var numComp = scalars.getNumberOfComponents();
|
|
206
|
+
var ppty = actor.getProperty();
|
|
207
|
+
var iComps = ppty.getIndependentComponents();
|
|
208
|
+
var numIComps = iComps ? numComp : 1;
|
|
209
|
+
var textureHeight = iComps ? 2 * numIComps : 1;
|
|
210
|
+
var cfunToString = computeFnToString(ppty, ppty.getRGBTransferFunction, numIComps);
|
|
211
|
+
|
|
212
|
+
if (model.colorTextureString !== cfunToString) {
|
|
213
|
+
var cWidth = 1024;
|
|
214
|
+
var cSize = cWidth * textureHeight * 3;
|
|
215
|
+
var cTable = new Uint8Array(cSize);
|
|
216
|
+
var cfun = ppty.getRGBTransferFunction();
|
|
217
|
+
|
|
218
|
+
if (cfun) {
|
|
219
|
+
var tmpTable = new Float32Array(cWidth * 3);
|
|
220
|
+
|
|
221
|
+
for (var c = 0; c < numIComps; c++) {
|
|
222
|
+
cfun = ppty.getRGBTransferFunction(c);
|
|
223
|
+
var cRange = cfun.getRange();
|
|
224
|
+
cfun.getTable(cRange[0], cRange[1], cWidth, tmpTable, 1);
|
|
225
|
+
|
|
226
|
+
if (iComps) {
|
|
227
|
+
for (var i = 0; i < cWidth * 3; i++) {
|
|
228
|
+
cTable[c * cWidth * 6 + i] = 255.0 * tmpTable[i];
|
|
229
|
+
cTable[c * cWidth * 6 + i + cWidth * 3] = 255.0 * tmpTable[i];
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
for (var _i = 0; _i < cWidth * 3; _i++) {
|
|
233
|
+
cTable[c * cWidth * 6 + _i] = 255.0 * tmpTable[_i];
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
model.colorTexture.releaseGraphicsResources(model._openGLRenderWindow);
|
|
239
|
+
model.colorTexture.resetFormatAndType();
|
|
240
|
+
model.colorTexture.create2DFromRaw(cWidth, textureHeight, 3, VtkDataTypes.UNSIGNED_CHAR, cTable);
|
|
241
|
+
} else {
|
|
242
|
+
for (var _i2 = 0; _i2 < cWidth * 3; ++_i2) {
|
|
243
|
+
cTable[_i2] = 255.0 * _i2 / ((cWidth - 1) * 3);
|
|
244
|
+
cTable[_i2 + 1] = 255.0 * _i2 / ((cWidth - 1) * 3);
|
|
245
|
+
cTable[_i2 + 2] = 255.0 * _i2 / ((cWidth - 1) * 3);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
model.colorTexture.create2DFromRaw(cWidth, 1, 3, VtkDataTypes.UNSIGNED_CHAR, cTable);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
model.colorTextureString = cfunToString;
|
|
252
|
+
} // Build piecewise function buffer. This buffer is used either
|
|
253
|
+
// for component weighting or opacity, depending on whether we're
|
|
254
|
+
// rendering components independently or not.
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
var pwfunToString = computeFnToString(ppty, ppty.getPiecewiseFunction, numIComps);
|
|
258
|
+
|
|
259
|
+
if (model.pwfTextureString !== pwfunToString) {
|
|
260
|
+
var pwfWidth = 1024;
|
|
261
|
+
var pwfSize = pwfWidth * textureHeight;
|
|
262
|
+
var pwfTable = new Uint8Array(pwfSize);
|
|
263
|
+
var pwfun = ppty.getPiecewiseFunction(); // support case where pwfun is added/removed
|
|
264
|
+
|
|
265
|
+
model.pwfTexture.releaseGraphicsResources(model._openGLRenderWindow);
|
|
266
|
+
model.pwfTexture.resetFormatAndType();
|
|
267
|
+
|
|
268
|
+
if (pwfun) {
|
|
269
|
+
var pwfFloatTable = new Float32Array(pwfSize);
|
|
270
|
+
|
|
271
|
+
var _tmpTable = new Float32Array(pwfWidth);
|
|
272
|
+
|
|
273
|
+
for (var _c = 0; _c < numIComps; ++_c) {
|
|
274
|
+
pwfun = ppty.getPiecewiseFunction(_c);
|
|
275
|
+
|
|
276
|
+
if (pwfun === null) {
|
|
277
|
+
// Piecewise constant max if no function supplied for this component
|
|
278
|
+
pwfFloatTable.fill(1.0);
|
|
279
|
+
} else {
|
|
280
|
+
var pwfRange = pwfun.getRange();
|
|
281
|
+
pwfun.getTable(pwfRange[0], pwfRange[1], pwfWidth, _tmpTable, 1); // adjust for sample distance etc
|
|
282
|
+
|
|
283
|
+
if (iComps) {
|
|
284
|
+
for (var _i3 = 0; _i3 < pwfWidth; _i3++) {
|
|
285
|
+
pwfFloatTable[_c * pwfWidth * 2 + _i3] = _tmpTable[_i3];
|
|
286
|
+
pwfFloatTable[_c * pwfWidth * 2 + _i3 + pwfWidth] = _tmpTable[_i3];
|
|
287
|
+
}
|
|
288
|
+
} else {
|
|
289
|
+
for (var _i4 = 0; _i4 < pwfWidth; _i4++) {
|
|
290
|
+
pwfFloatTable[_c * pwfWidth * 2 + _i4] = _tmpTable[_i4];
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
model.pwfTexture.create2DFromRaw(pwfWidth, textureHeight, 1, VtkDataTypes.FLOAT, pwfFloatTable);
|
|
297
|
+
} else {
|
|
298
|
+
// default is opaque
|
|
299
|
+
pwfTable.fill(255.0);
|
|
300
|
+
model.pwfTexture.create2DFromRaw(pwfWidth, 1, 1, VtkDataTypes.UNSIGNED_CHAR, pwfTable);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
model.pwfTextureString = pwfunToString;
|
|
304
|
+
} // Rebuild the image vertices if needed
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
if (model.VBOBuildTime.getMTime() < model.renderable.getMTime() || model.VBOBuildTime.getMTime() < centerline.getMTime()) {
|
|
308
|
+
var nPoints = centerline.getNumberOfPoints();
|
|
309
|
+
var nLines = nPoints <= 1 ? 0 : nPoints - 1;
|
|
310
|
+
var distances = centerline.getDistancesToFirstPoint();
|
|
311
|
+
var totalHeight = model.renderable.getHeight();
|
|
312
|
+
var nPts = 4 * nLines; // Create the array of point: 4 points per segment
|
|
313
|
+
|
|
314
|
+
var ptsArray = new Float32Array(3 * nPts);
|
|
315
|
+
var widthMC = model.renderable.getWidth();
|
|
316
|
+
|
|
317
|
+
for (var lineIdx = 0, offset = 0; lineIdx < nLines; ++lineIdx) {
|
|
318
|
+
// Use model coordinates
|
|
319
|
+
// See "setCameraShaderParameters" to see how MCPCMatrix is built
|
|
320
|
+
// Top left
|
|
321
|
+
ptsArray.set([0, totalHeight - distances[lineIdx], 0], offset);
|
|
322
|
+
offset += 3; // Top right
|
|
323
|
+
|
|
324
|
+
ptsArray.set([widthMC, totalHeight - distances[lineIdx], 0], offset);
|
|
325
|
+
offset += 3; // Bottom right
|
|
326
|
+
|
|
327
|
+
ptsArray.set([widthMC, totalHeight - distances[lineIdx + 1], 0], offset);
|
|
328
|
+
offset += 3; // Bottom left
|
|
329
|
+
|
|
330
|
+
ptsArray.set([0, totalHeight - distances[lineIdx + 1], 0], offset);
|
|
331
|
+
offset += 3;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
var points = vtkDataArray.newInstance({
|
|
335
|
+
numberOfComponents: 3,
|
|
336
|
+
values: ptsArray
|
|
337
|
+
});
|
|
338
|
+
points.setName('points'); // Create the array of cells: a quad per segment
|
|
339
|
+
|
|
340
|
+
var cellArray = new Uint16Array(5 * nLines);
|
|
341
|
+
|
|
342
|
+
for (var _lineIdx = 0, _offset = 0, ptIdx = 0; _lineIdx < nLines; ++_lineIdx) {
|
|
343
|
+
cellArray.set([4, ptIdx + 3, ptIdx + 2, ptIdx + 1, ptIdx], _offset);
|
|
344
|
+
_offset += 5;
|
|
345
|
+
ptIdx += 4;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
var cells = vtkDataArray.newInstance({
|
|
349
|
+
numberOfComponents: 1,
|
|
350
|
+
values: cellArray
|
|
351
|
+
}); // Create the array of centerline positions (VBO custom attribute)
|
|
352
|
+
|
|
353
|
+
var pointsDataArray = centerline.getPoints();
|
|
354
|
+
var centerlinePositionArray = new Float32Array(3 * nPts);
|
|
355
|
+
var pa = new Array(3);
|
|
356
|
+
var pb = new Array(3);
|
|
357
|
+
|
|
358
|
+
for (var _lineIdx2 = 0, _offset2 = 0; _lineIdx2 < nLines; ++_lineIdx2) {
|
|
359
|
+
pointsDataArray.getPoint(_lineIdx2, pa);
|
|
360
|
+
pointsDataArray.getPoint(_lineIdx2 + 1, pb); // Top left
|
|
361
|
+
|
|
362
|
+
centerlinePositionArray.set(pa, _offset2);
|
|
363
|
+
_offset2 += 3; // Top right
|
|
364
|
+
|
|
365
|
+
centerlinePositionArray.set(pa, _offset2);
|
|
366
|
+
_offset2 += 3; // Bottom right
|
|
367
|
+
|
|
368
|
+
centerlinePositionArray.set(pb, _offset2);
|
|
369
|
+
_offset2 += 3; // Bottom left
|
|
370
|
+
|
|
371
|
+
centerlinePositionArray.set(pb, _offset2);
|
|
372
|
+
_offset2 += 3;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
var centerlinePosition = vtkDataArray.newInstance({
|
|
376
|
+
numberOfComponents: 3,
|
|
377
|
+
values: centerlinePositionArray,
|
|
378
|
+
name: 'centerlinePosition'
|
|
379
|
+
}); // Create the array of quad index:
|
|
380
|
+
// 0 ____ 1
|
|
381
|
+
// | |
|
|
382
|
+
// |____|
|
|
383
|
+
// 2 3
|
|
384
|
+
|
|
385
|
+
var quadIndexArray = new Float32Array(nPts);
|
|
386
|
+
|
|
387
|
+
for (var _lineIdx3 = 0, _offset3 = 0; _lineIdx3 < nLines; ++_lineIdx3) {
|
|
388
|
+
quadIndexArray.set([0, // Top left
|
|
389
|
+
1, // Top right
|
|
390
|
+
3, // Bottom right
|
|
391
|
+
2 // Bottom left
|
|
392
|
+
], _offset3);
|
|
393
|
+
_offset3 += 4;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
var quadIndex = vtkDataArray.newInstance({
|
|
397
|
+
numberOfComponents: 1,
|
|
398
|
+
values: quadIndexArray,
|
|
399
|
+
name: 'quadIndex'
|
|
400
|
+
});
|
|
401
|
+
var customAttributes = [centerlinePosition, quadIndex];
|
|
402
|
+
|
|
403
|
+
if (!model.renderable.getUseUniformOrientation()) {
|
|
404
|
+
// For each {quad / centerline segment}, two vectors in directionDataArray give the orientation of the centerline
|
|
405
|
+
// Send these two vectors to each vertex and use flat interpolation to get them as is in the fragment shader
|
|
406
|
+
// The interpolation will occur in the fragment shader (slerp)
|
|
407
|
+
var directions = model.renderable.getCenterlineTangentDirections();
|
|
408
|
+
var centerlineTopDirectionArray = new Float32Array(3 * nPts);
|
|
409
|
+
var centerlineBotDirectionArray = new Float32Array(3 * nPts);
|
|
410
|
+
|
|
411
|
+
for (var _lineIdx4 = 0, _offset4 = 0; _lineIdx4 < nLines; ++_lineIdx4) {
|
|
412
|
+
var baseDirectionIdx = 3 * _lineIdx4; // Every vertex of each quad/segment have the same topDir and botDir
|
|
413
|
+
// Top left, Top right, Bottom right, Bottom left
|
|
414
|
+
|
|
415
|
+
for (var _i5 = 0; _i5 < 4; ++_i5) {
|
|
416
|
+
// Top array
|
|
417
|
+
centerlineTopDirectionArray[_offset4 + 0] = directions[baseDirectionIdx + 0];
|
|
418
|
+
centerlineTopDirectionArray[_offset4 + 1] = directions[baseDirectionIdx + 1];
|
|
419
|
+
centerlineTopDirectionArray[_offset4 + 2] = directions[baseDirectionIdx + 2]; // Bot array
|
|
420
|
+
|
|
421
|
+
centerlineBotDirectionArray[_offset4 + 0] = directions[baseDirectionIdx + 3];
|
|
422
|
+
centerlineBotDirectionArray[_offset4 + 1] = directions[baseDirectionIdx + 4];
|
|
423
|
+
centerlineBotDirectionArray[_offset4 + 2] = directions[baseDirectionIdx + 5];
|
|
424
|
+
_offset4 += 3;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
var centerlineTopDirection = vtkDataArray.newInstance({
|
|
429
|
+
numberOfComponents: 3,
|
|
430
|
+
values: centerlineTopDirectionArray,
|
|
431
|
+
name: 'centerlineTopDirection'
|
|
432
|
+
});
|
|
433
|
+
var centerlineBotDirection = vtkDataArray.newInstance({
|
|
434
|
+
numberOfComponents: 3,
|
|
435
|
+
values: centerlineBotDirectionArray,
|
|
436
|
+
name: 'centerlineBotDirection'
|
|
437
|
+
});
|
|
438
|
+
customAttributes.push(centerlineTopDirection, centerlineBotDirection);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
model.tris.getCABO().createVBO(cells, 'polys', Representation.SURFACE, {
|
|
442
|
+
points: points,
|
|
443
|
+
customAttributes: customAttributes
|
|
444
|
+
});
|
|
445
|
+
model.VBOBuildTime.modified();
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
publicAPI.getNeedToRebuildShaders = function (cellBO, ren, actor) {
|
|
450
|
+
// has something changed that would require us to recreate the shader?
|
|
451
|
+
// candidates are
|
|
452
|
+
// property modified (representation interpolation and lighting)
|
|
453
|
+
// input modified
|
|
454
|
+
// light complexity changed
|
|
455
|
+
// render pass shader replacement changed
|
|
456
|
+
var tNumComp = model.volumeTexture.getComponents();
|
|
457
|
+
var iComp = actor.getProperty().getIndependentComponents();
|
|
458
|
+
|
|
459
|
+
if (model.lastHaveSeenDepthRequest !== model.haveSeenDepthRequest || cellBO.getProgram() === 0 || model.lastTextureComponents !== tNumComp || model.lastIndependentComponents !== iComp) {
|
|
460
|
+
model.lastHaveSeenDepthRequest = model.haveSeenDepthRequest;
|
|
461
|
+
model.lastTextureComponents = tNumComp;
|
|
462
|
+
model.lastIndependentComponents = iComp;
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return false;
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
publicAPI.buildShaders = function (shaders, ren, actor) {
|
|
470
|
+
publicAPI.getShaderTemplate(shaders, ren, actor);
|
|
471
|
+
publicAPI.replaceShaderValues(shaders, ren, actor);
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
publicAPI.replaceShaderValues = function (shaders, ren, actor) {
|
|
475
|
+
var VSSource = shaders.Vertex;
|
|
476
|
+
var FSSource = shaders.Fragment; // Vertex shader main replacements
|
|
477
|
+
|
|
478
|
+
VSSource = vtkShaderProgram.substitute(VSSource, '//VTK::Camera::Dec', ['uniform mat4 MCPCMatrix;']).result;
|
|
479
|
+
VSSource = vtkShaderProgram.substitute(VSSource, '//VTK::PositionVC::Impl', [' gl_Position = MCPCMatrix * vertexMC;']).result;
|
|
480
|
+
var vsColorDec = ['attribute vec3 centerlinePosition;', 'attribute float quadIndex;', 'uniform float width;', 'out vec2 quadOffsetVSOutput;', 'out vec3 centerlinePosVSOutput;'];
|
|
481
|
+
var isDirectionUniform = model.renderable.getUseUniformOrientation();
|
|
482
|
+
|
|
483
|
+
if (isDirectionUniform) {
|
|
484
|
+
vsColorDec.push('out vec3 centerlineDirVSOutput;', 'uniform vec3 centerlineDirection;');
|
|
485
|
+
} else {
|
|
486
|
+
vsColorDec.push('out vec3 centerlineTopDirVSOutput;', 'out vec3 centerlineBotDirVSOutput;', 'out float centerlineAngleVSOutput;', 'attribute vec3 centerlineTopDirection;', 'attribute vec3 centerlineBotDirection;');
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
VSSource = vtkShaderProgram.substitute(VSSource, '//VTK::Color::Dec', vsColorDec).result;
|
|
490
|
+
var vsColorImpl = [// quadOffsetVSOutput.x: left = -0.5* width; right = 0.5 * width
|
|
491
|
+
// quadOffsetVSOutput.y: bottom = 0.0; top = 1.0;
|
|
492
|
+
'quadOffsetVSOutput = vec2(width * (mod(quadIndex, 2.0) == 0.0 ? -0.5 : 0.5), quadIndex > 1.0 ? 0.0 : 1.0);', 'centerlinePosVSOutput = centerlinePosition;'];
|
|
493
|
+
|
|
494
|
+
if (isDirectionUniform) {
|
|
495
|
+
vsColorImpl.push('centerlineDirVSOutput = centerlineDirection;');
|
|
496
|
+
} else {
|
|
497
|
+
vsColorImpl.push( // When u and v are unit vectors: uvAngle = 2 * atan2(|| u - v ||, || u + v ||)
|
|
498
|
+
// When u != -v: || u + v || > 0
|
|
499
|
+
// When x > 0: atan2(y, x) = atan(y/x)
|
|
500
|
+
// Thus: dirAngle = 2 * atan(|| topDir - botDir || / || topDir + botDir ||)
|
|
501
|
+
// This is more stable and should not be to slow compared to acos(dot(u, v))
|
|
502
|
+
'vec3 sumVec = centerlineTopDirection + centerlineBotDirection;', 'float sumLen2 = dot(sumVec, sumVec);', 'float diffLen2 = 4.0 - sumLen2;', 'if (diffLen2 < 0.001) {', ' // vectors are too close to each other, use lerp', ' centerlineAngleVSOutput = -1.0; // use negative angle as a flag for lerp', ' centerlineTopDirVSOutput = centerlineTopDirection;', ' centerlineBotDirVSOutput = centerlineBotDirection;', '} else if (sumLen2 == 0.0) {', " // vector are opposite to each other, don't make a choice for the user", ' // use slerp without direction, it will display the centerline color on each row of pixel', ' centerlineAngleVSOutput = 0.0;', ' centerlineTopDirVSOutput = vec3(0.0);', ' centerlineBotDirVSOutput = vec3(0.0);', '} else {', ' // use slerp', ' centerlineAngleVSOutput = 2.0 * atan(sqrt(diffLen2/sumLen2));', ' float sinAngle = sin(centerlineAngleVSOutput);', ' centerlineTopDirVSOutput = centerlineTopDirection / sinAngle;', ' centerlineBotDirVSOutput = centerlineBotDirection / sinAngle;', '}');
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
VSSource = vtkShaderProgram.substitute(VSSource, '//VTK::Color::Impl', vsColorImpl).result; // Fragment shader main replacements
|
|
506
|
+
|
|
507
|
+
var tNumComp = model.volumeTexture.getComponents();
|
|
508
|
+
var iComps = actor.getProperty().getIndependentComponents();
|
|
509
|
+
var tcoordFSDec = [// used to compute texture coordinates of the sample
|
|
510
|
+
'uniform mat4 MCTCMatrix; // Model coordinates to texture coordinates', 'in vec2 quadOffsetVSOutput;', 'in vec3 centerlinePosVSOutput;', // volume texture
|
|
511
|
+
'uniform highp sampler3D volumeTexture;', // color and pwf textures
|
|
512
|
+
'uniform sampler2D colorTexture1;', 'uniform sampler2D pwfTexture1;', // opacity
|
|
513
|
+
'uniform float opacity;', // background color (out of volume samples)
|
|
514
|
+
'uniform vec4 backgroundColor;', // color shift and scale
|
|
515
|
+
"uniform float cshift0;", "uniform float cscale0;", // weighting shift and scale
|
|
516
|
+
"uniform float pwfshift0;", "uniform float pwfscale0;"];
|
|
517
|
+
|
|
518
|
+
if (isDirectionUniform) {
|
|
519
|
+
tcoordFSDec.push('in vec3 centerlineDirVSOutput;');
|
|
520
|
+
} else {
|
|
521
|
+
tcoordFSDec.push('in vec3 centerlineTopDirVSOutput;', 'in vec3 centerlineBotDirVSOutput;', 'in float centerlineAngleVSOutput;');
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
if (iComps) {
|
|
525
|
+
for (var comp = 1; comp < tNumComp; comp++) {
|
|
526
|
+
tcoordFSDec = tcoordFSDec.concat([// color shift and scale
|
|
527
|
+
"uniform float cshift".concat(comp, ";"), "uniform float cscale".concat(comp, ";"), // weighting shift and scale
|
|
528
|
+
"uniform float pwfshift".concat(comp, ";"), "uniform float pwfscale".concat(comp, ";")]);
|
|
529
|
+
} // the heights defined below are the locations
|
|
530
|
+
// for the up to four components of the tfuns
|
|
531
|
+
// the tfuns have a height of 2XnumComps pixels so the
|
|
532
|
+
// values are computed to hit the middle of the two rows
|
|
533
|
+
// for that component
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
switch (tNumComp) {
|
|
537
|
+
case 1:
|
|
538
|
+
tcoordFSDec = tcoordFSDec.concat(['uniform float mix0;', '#define height0 0.5']);
|
|
539
|
+
break;
|
|
540
|
+
|
|
541
|
+
case 2:
|
|
542
|
+
tcoordFSDec = tcoordFSDec.concat(['uniform float mix0;', 'uniform float mix1;', '#define height0 0.25', '#define height1 0.75']);
|
|
543
|
+
break;
|
|
544
|
+
|
|
545
|
+
case 3:
|
|
546
|
+
tcoordFSDec = tcoordFSDec.concat(['uniform float mix0;', 'uniform float mix1;', 'uniform float mix2;', '#define height0 0.17', '#define height1 0.5', '#define height2 0.83']);
|
|
547
|
+
break;
|
|
548
|
+
|
|
549
|
+
case 4:
|
|
550
|
+
tcoordFSDec = tcoordFSDec.concat(['uniform float mix0;', 'uniform float mix1;', 'uniform float mix2;', 'uniform float mix3;', '#define height0 0.125', '#define height1 0.375', '#define height2 0.625', '#define height3 0.875']);
|
|
551
|
+
break;
|
|
552
|
+
|
|
553
|
+
default:
|
|
554
|
+
vtkErrorMacro('Unsupported number of independent coordinates.');
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::TCoord::Dec', tcoordFSDec).result;
|
|
559
|
+
var tcoordFSImpl = [];
|
|
560
|
+
|
|
561
|
+
if (isDirectionUniform) {
|
|
562
|
+
tcoordFSImpl.push('vec3 interpolatedCenterlineDir = centerlineDirVSOutput;');
|
|
563
|
+
} else {
|
|
564
|
+
// Slerp or lerp between centerlineTopDirVSOutput and centerlineBotDirVSOutput
|
|
565
|
+
// We use quadOffsetVSOutput.y: bottom = 0.0; top = 1.0;
|
|
566
|
+
tcoordFSImpl.push('vec3 interpolatedCenterlineDir;', 'if (centerlineAngleVSOutput < 0.0) {', ' // Lerp', ' interpolatedCenterlineDir = quadOffsetVSOutput.y * centerlineTopDirVSOutput + (1.0 - quadOffsetVSOutput.y) * centerlineBotDirVSOutput;', '} else {', ' // Slerp', ' float topInterpolationAngle = quadOffsetVSOutput.y * centerlineAngleVSOutput;', ' float botInterpolationAngle = centerlineAngleVSOutput - topInterpolationAngle;', ' interpolatedCenterlineDir = sin(topInterpolationAngle) * centerlineTopDirVSOutput + sin(botInterpolationAngle) * centerlineBotDirVSOutput;', '}', '// Slerp should give a normalized vector but when sin(angle) is small, rounding error occurs', '// Normalize for both lerp and slerp', 'interpolatedCenterlineDir = normalize(interpolatedCenterlineDir);');
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
tcoordFSImpl.push('vec3 volumePosMC = centerlinePosVSOutput + quadOffsetVSOutput.x * interpolatedCenterlineDir;', 'vec3 volumePosTC = (MCTCMatrix * vec4(volumePosMC, 1.0)).xyz;', 'if (any(lessThan(volumePosTC, vec3(0.0))) || any(greaterThan(volumePosTC, vec3(1.0))))', '{', ' // set the background color and exit', ' gl_FragData[0] = backgroundColor;', ' return;', '}', 'vec4 tvalue = texture(volumeTexture, volumePosTC);');
|
|
570
|
+
|
|
571
|
+
if (iComps) {
|
|
572
|
+
var rgba = ['r', 'g', 'b', 'a'];
|
|
573
|
+
|
|
574
|
+
for (var _comp = 0; _comp < tNumComp; ++_comp) {
|
|
575
|
+
tcoordFSImpl = tcoordFSImpl.concat(["vec3 tcolor".concat(_comp, " = mix").concat(_comp, " * texture2D(colorTexture1, vec2(tvalue.").concat(rgba[_comp], " * cscale").concat(_comp, " + cshift").concat(_comp, ", height").concat(_comp, ")).rgb;"), "float compWeight".concat(_comp, " = mix").concat(_comp, " * texture2D(pwfTexture1, vec2(tvalue.").concat(rgba[_comp], " * pwfscale").concat(_comp, " + pwfshift").concat(_comp, ", height").concat(_comp, ")).r;")]);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
switch (tNumComp) {
|
|
579
|
+
case 1:
|
|
580
|
+
tcoordFSImpl = tcoordFSImpl.concat(['gl_FragData[0] = vec4(tcolor0.rgb, compWeight0 * opacity);']);
|
|
581
|
+
break;
|
|
582
|
+
|
|
583
|
+
case 2:
|
|
584
|
+
tcoordFSImpl = tcoordFSImpl.concat(['float weightSum = compWeight0 + compWeight1;', 'gl_FragData[0] = vec4(vec3((tcolor0.rgb * (compWeight0 / weightSum)) + (tcolor1.rgb * (compWeight1 / weightSum))), opacity);']);
|
|
585
|
+
break;
|
|
586
|
+
|
|
587
|
+
case 3:
|
|
588
|
+
tcoordFSImpl = tcoordFSImpl.concat(['float weightSum = compWeight0 + compWeight1 + compWeight2;', 'gl_FragData[0] = vec4(vec3((tcolor0.rgb * (compWeight0 / weightSum)) + (tcolor1.rgb * (compWeight1 / weightSum)) + (tcolor2.rgb * (compWeight2 / weightSum))), opacity);']);
|
|
589
|
+
break;
|
|
590
|
+
|
|
591
|
+
case 4:
|
|
592
|
+
tcoordFSImpl = tcoordFSImpl.concat(['float weightSum = compWeight0 + compWeight1 + compWeight2 + compWeight3;', 'gl_FragData[0] = vec4(vec3((tcolor0.rgb * (compWeight0 / weightSum)) + (tcolor1.rgb * (compWeight1 / weightSum)) + (tcolor2.rgb * (compWeight2 / weightSum)) + (tcolor3.rgb * (compWeight3 / weightSum))), opacity);']);
|
|
593
|
+
break;
|
|
594
|
+
|
|
595
|
+
default:
|
|
596
|
+
vtkErrorMacro('Unsupported number of independent coordinates.');
|
|
597
|
+
}
|
|
598
|
+
} else {
|
|
599
|
+
// dependent components
|
|
600
|
+
switch (tNumComp) {
|
|
601
|
+
case 1:
|
|
602
|
+
tcoordFSImpl = tcoordFSImpl.concat(['// Dependent components', 'float intensity = tvalue.r;', 'vec3 tcolor = texture2D(colorTexture1, vec2(intensity * cscale0 + cshift0, 0.5)).rgb;', 'float scalarOpacity = texture2D(pwfTexture1, vec2(intensity * pwfscale0 + pwfshift0, 0.5)).r;', 'gl_FragData[0] = vec4(tcolor, scalarOpacity * opacity);']);
|
|
603
|
+
break;
|
|
604
|
+
|
|
605
|
+
case 2:
|
|
606
|
+
tcoordFSImpl = tcoordFSImpl.concat(['float intensity = tvalue.r*cscale0 + cshift0;', 'gl_FragData[0] = vec4(texture2D(colorTexture1, vec2(intensity, 0.5)).rgb, pwfscale0*tvalue.g + pwfshift0);']);
|
|
607
|
+
break;
|
|
608
|
+
|
|
609
|
+
case 3:
|
|
610
|
+
tcoordFSImpl = tcoordFSImpl.concat(['vec4 tcolor = cscale0*tvalue + cshift0;', 'gl_FragData[0] = vec4(texture2D(colorTexture1, vec2(tcolor.r,0.5)).r,', ' texture2D(colorTexture1, vec2(tcolor.g,0.5)).r,', ' texture2D(colorTexture1, vec2(tcolor.b,0.5)).r, opacity);']);
|
|
611
|
+
break;
|
|
612
|
+
|
|
613
|
+
default:
|
|
614
|
+
tcoordFSImpl = tcoordFSImpl.concat(['vec4 tcolor = cscale0*tvalue + cshift0;', 'gl_FragData[0] = vec4(texture2D(colorTexture1, vec2(tcolor.r,0.5)).r,', ' texture2D(colorTexture1, vec2(tcolor.g,0.5)).r,', ' texture2D(colorTexture1, vec2(tcolor.b,0.5)).r, tcolor.a);']);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::TCoord::Impl', tcoordFSImpl).result; // Picking shader replacements
|
|
619
|
+
|
|
620
|
+
if (model.haveSeenDepthRequest) {
|
|
621
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::ZBuffer::Dec', 'uniform int depthRequest;').result;
|
|
622
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::ZBuffer::Impl', ['if (depthRequest == 1) {', 'float iz = floor(gl_FragCoord.z*65535.0 + 0.1);', 'float rf = floor(iz/256.0)/255.0;', 'float gf = mod(iz,256.0)/255.0;', 'gl_FragData[0] = vec4(rf, gf, 0.0, 1.0); }']).result;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
shaders.Vertex = VSSource;
|
|
626
|
+
shaders.Fragment = FSSource;
|
|
627
|
+
publicAPI.replaceShaderClip(shaders, ren, actor);
|
|
628
|
+
publicAPI.replaceShaderCoincidentOffset(shaders, ren, actor);
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
publicAPI.replaceShaderClip = function (shaders, ren, actor) {
|
|
632
|
+
var VSSource = shaders.Vertex;
|
|
633
|
+
var FSSource = shaders.Fragment;
|
|
634
|
+
|
|
635
|
+
if (model.renderable.getNumberOfClippingPlanes()) {
|
|
636
|
+
var numClipPlanes = model.renderable.getNumberOfClippingPlanes();
|
|
637
|
+
|
|
638
|
+
if (numClipPlanes > 6) {
|
|
639
|
+
macro.vtkErrorMacro('OpenGL has a limit of 6 clipping planes');
|
|
640
|
+
numClipPlanes = 6;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
VSSource = vtkShaderProgram.substitute(VSSource, '//VTK::Clip::Dec', ['uniform int numClipPlanes;', 'uniform vec4 clipPlanes[6];', 'varying float clipDistancesVSOutput[6];']).result;
|
|
644
|
+
VSSource = vtkShaderProgram.substitute(VSSource, '//VTK::Clip::Impl', ['for (int planeNum = 0; planeNum < 6; planeNum++)', ' {', ' if (planeNum >= numClipPlanes)', ' {', ' break;', ' }', ' clipDistancesVSOutput[planeNum] = dot(clipPlanes[planeNum], vertexMC);', ' }']).result;
|
|
645
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::Clip::Dec', ['uniform int numClipPlanes;', 'varying float clipDistancesVSOutput[6];']).result;
|
|
646
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::Clip::Impl', ['for (int planeNum = 0; planeNum < 6; planeNum++)', ' {', ' if (planeNum >= numClipPlanes)', ' {', ' break;', ' }', ' if (clipDistancesVSOutput[planeNum] < 0.0) discard;', ' }']).result;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
shaders.Vertex = VSSource;
|
|
650
|
+
shaders.Fragment = FSSource;
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
publicAPI.getShaderTemplate = function (shaders, ren, actor) {
|
|
654
|
+
shaders.Vertex = vtkPolyDataVS;
|
|
655
|
+
shaders.Fragment = vtkPolyDataFS;
|
|
656
|
+
shaders.Geometry = '';
|
|
657
|
+
};
|
|
658
|
+
|
|
659
|
+
publicAPI.setMapperShaderParameters = function (cellBO, ren, actor) {
|
|
660
|
+
var _cellBO$getProgram;
|
|
661
|
+
|
|
662
|
+
if (cellBO.getCABO().getElementCount() && (model.VBOBuildTime.getMTime() > cellBO.getAttributeUpdateTime().getMTime() || cellBO.getShaderSourceTime().getMTime() > cellBO.getAttributeUpdateTime().getMTime())) {
|
|
663
|
+
if (cellBO.getProgram().isAttributeUsed('vertexMC')) {
|
|
664
|
+
if (!cellBO.getVAO().addAttributeArray(cellBO.getProgram(), cellBO.getCABO(), 'vertexMC', cellBO.getCABO().getVertexOffset(), cellBO.getCABO().getStride(), model.context.FLOAT, 3, model.context.FALSE)) {
|
|
665
|
+
vtkErrorMacro('Error setting vertexMC in shader VAO.');
|
|
666
|
+
}
|
|
667
|
+
} // Custom data of the CABO (centerlinePosition, centerlineTopDirection,
|
|
668
|
+
// centerlineBotDirection, quadIndex and user defined custom data)
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
cellBO.getCABO().getCustomData().forEach(function (data) {
|
|
672
|
+
if (data && cellBO.getProgram().isAttributeUsed(data.name) && !cellBO.getVAO().addAttributeArray(cellBO.getProgram(), cellBO.getCABO(), data.name, data.offset, cellBO.getCABO().getStride(), model.context.FLOAT, data.components, model.context.FALSE)) {
|
|
673
|
+
vtkErrorMacro("Error setting ".concat(data.name, " in shader VAO."));
|
|
674
|
+
}
|
|
675
|
+
});
|
|
676
|
+
cellBO.getAttributeUpdateTime().modified();
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
var texUnit = model.volumeTexture.getTextureUnit();
|
|
680
|
+
cellBO.getProgram().setUniformi('volumeTexture', texUnit);
|
|
681
|
+
cellBO.getProgram().setUniformf('width', model.renderable.getWidth());
|
|
682
|
+
|
|
683
|
+
(_cellBO$getProgram = cellBO.getProgram()).setUniform4f.apply(_cellBO$getProgram, ['backgroundColor'].concat(_toConsumableArray(model.renderable.getBackgroundColor())));
|
|
684
|
+
|
|
685
|
+
if (cellBO.getProgram().isUniformUsed('centerlineDirection')) {
|
|
686
|
+
var uniformDirection = model.renderable.getUniformDirection();
|
|
687
|
+
cellBO.getProgram().setUniform3fArray('centerlineDirection', uniformDirection);
|
|
688
|
+
} // Model coordinates to image space
|
|
689
|
+
// getWorldToIndex is badly named and is in fact modelToIndex
|
|
690
|
+
// MCIC -> Model coordinates to index coordinates
|
|
691
|
+
// MCTC -> Model coordinates to texture coordinates
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
var image = model.currentImageDataInput;
|
|
695
|
+
var MCICMatrix = image.getWorldToIndex();
|
|
696
|
+
var ICTCMatrix = mat4.fromScaling(new Float32Array(16), vec3.inverse([], image.getDimensions()));
|
|
697
|
+
var MCTCMatrix = mat4.mul(ICTCMatrix, ICTCMatrix, MCICMatrix);
|
|
698
|
+
cellBO.getProgram().setUniformMatrix('MCTCMatrix', MCTCMatrix);
|
|
699
|
+
|
|
700
|
+
if (model.haveSeenDepthRequest) {
|
|
701
|
+
cellBO.getProgram().setUniformi('depthRequest', model.renderDepth ? 1 : 0);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
if (model.renderable.getNumberOfClippingPlanes()) {
|
|
705
|
+
// add all the clipping planes
|
|
706
|
+
var numClipPlanes = model.renderable.getNumberOfClippingPlanes();
|
|
707
|
+
|
|
708
|
+
if (numClipPlanes > 6) {
|
|
709
|
+
macro.vtkErrorMacro('OpenGL has a limit of 6 clipping planes');
|
|
710
|
+
numClipPlanes = 6;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
var shiftScaleEnabled = cellBO.getCABO().getCoordShiftAndScaleEnabled();
|
|
714
|
+
var inverseShiftScaleMatrix = shiftScaleEnabled ? cellBO.getCABO().getInverseShiftAndScaleMatrix() : null;
|
|
715
|
+
var mat = inverseShiftScaleMatrix ? mat4.copy(model.imagematinv, actor.getMatrix()) : actor.getMatrix();
|
|
716
|
+
|
|
717
|
+
if (inverseShiftScaleMatrix) {
|
|
718
|
+
mat4.transpose(mat, mat);
|
|
719
|
+
mat4.multiply(mat, mat, inverseShiftScaleMatrix);
|
|
720
|
+
mat4.transpose(mat, mat);
|
|
721
|
+
} // transform crop plane normal with transpose(inverse(worldToIndex))
|
|
722
|
+
|
|
723
|
+
|
|
724
|
+
mat4.transpose(model.imagemat, model.currentImageDataInput.getIndexToWorld());
|
|
725
|
+
mat4.multiply(model.imagematinv, mat, model.imagemat);
|
|
726
|
+
var planeEquations = [];
|
|
727
|
+
|
|
728
|
+
for (var i = 0; i < numClipPlanes; i++) {
|
|
729
|
+
var planeEquation = [];
|
|
730
|
+
model.renderable.getClippingPlaneInDataCoords(model.imagematinv, i, planeEquation);
|
|
731
|
+
|
|
732
|
+
for (var j = 0; j < 4; j++) {
|
|
733
|
+
planeEquations.push(planeEquation[j]);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
cellBO.getProgram().setUniformi('numClipPlanes', numClipPlanes);
|
|
738
|
+
cellBO.getProgram().setUniform4fv('clipPlanes', planeEquations);
|
|
739
|
+
} // handle coincident
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
if (cellBO.getProgram().isUniformUsed('coffset')) {
|
|
743
|
+
var cp = publicAPI.getCoincidentParameters(ren, actor);
|
|
744
|
+
cellBO.getProgram().setUniformf('coffset', cp.offset); // cfactor isn't always used when coffset is.
|
|
745
|
+
|
|
746
|
+
if (cellBO.getProgram().isUniformUsed('cfactor')) {
|
|
747
|
+
cellBO.getProgram().setUniformf('cfactor', cp.factor);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
|
|
752
|
+
publicAPI.setCameraShaderParameters = function (cellBO, ren, actor) {
|
|
753
|
+
var MCWCMatrix = model.openGLImageSlice.getKeyMatrices().mcwc;
|
|
754
|
+
var WCPCMatrix = model.openGLCamera.getKeyMatrices(ren).wcpc;
|
|
755
|
+
mat4.multiply(model.imagemat, WCPCMatrix, MCWCMatrix);
|
|
756
|
+
|
|
757
|
+
if (cellBO.getCABO().getCoordShiftAndScaleEnabled()) {
|
|
758
|
+
var inverseShiftScaleMat = cellBO.getCABO().getInverseShiftAndScaleMatrix();
|
|
759
|
+
mat4.multiply(model.imagemat, model.imagemat, inverseShiftScaleMat);
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
cellBO.getProgram().setUniformMatrix('MCPCMatrix', model.imagemat);
|
|
763
|
+
};
|
|
764
|
+
|
|
765
|
+
publicAPI.setPropertyShaderParameters = function (cellBO, ren, actor) {
|
|
766
|
+
var program = cellBO.getProgram();
|
|
767
|
+
var ppty = actor.getProperty();
|
|
768
|
+
var opacity = ppty.getOpacity();
|
|
769
|
+
program.setUniformf('opacity', opacity); // Component mix
|
|
770
|
+
// Independent components: Mixed according to component weights
|
|
771
|
+
// Dependent components: Mixed using the following logic:
|
|
772
|
+
// - 2 comps => LA
|
|
773
|
+
// - 3 comps => RGB + opacity from pwf
|
|
774
|
+
// - 4 comps => RGBA
|
|
775
|
+
|
|
776
|
+
var numComp = model.volumeTexture.getComponents();
|
|
777
|
+
var iComps = ppty.getIndependentComponents();
|
|
778
|
+
|
|
779
|
+
if (iComps) {
|
|
780
|
+
for (var i = 0; i < numComp; ++i) {
|
|
781
|
+
program.setUniformf("mix".concat(i), ppty.getComponentWeight(i));
|
|
782
|
+
}
|
|
783
|
+
} // Color opacity map
|
|
784
|
+
|
|
785
|
+
|
|
786
|
+
var volInfo = model.volumeTexture.getVolumeInfo(); // three levels of shift scale combined into one
|
|
787
|
+
// for performance in the fragment shader
|
|
788
|
+
|
|
789
|
+
for (var _i6 = 0; _i6 < numComp; _i6++) {
|
|
790
|
+
var cw = ppty.getColorWindow();
|
|
791
|
+
var cl = ppty.getColorLevel();
|
|
792
|
+
var target = iComps ? _i6 : 0;
|
|
793
|
+
var cfun = ppty.getRGBTransferFunction(target);
|
|
794
|
+
|
|
795
|
+
if (cfun && ppty.getUseLookupTableScalarRange()) {
|
|
796
|
+
var cRange = cfun.getRange();
|
|
797
|
+
cw = cRange[1] - cRange[0];
|
|
798
|
+
cl = 0.5 * (cRange[1] + cRange[0]);
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
var scale = volInfo.scale[_i6] / cw;
|
|
802
|
+
var shift = (volInfo.offset[_i6] - cl) / cw + 0.5;
|
|
803
|
+
program.setUniformf("cshift".concat(_i6), shift);
|
|
804
|
+
program.setUniformf("cscale".concat(_i6), scale);
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
var texColorUnit = model.colorTexture.getTextureUnit(); // TODO
|
|
808
|
+
|
|
809
|
+
program.setUniformi('colorTexture1', texColorUnit); // pwf shift/scale
|
|
810
|
+
|
|
811
|
+
for (var _i7 = 0; _i7 < numComp; _i7++) {
|
|
812
|
+
var pwfScale = 1.0;
|
|
813
|
+
var pwfShift = 0.0;
|
|
814
|
+
|
|
815
|
+
var _target = iComps ? _i7 : 0;
|
|
816
|
+
|
|
817
|
+
var pwfun = ppty.getPiecewiseFunction(_target);
|
|
818
|
+
|
|
819
|
+
if (pwfun) {
|
|
820
|
+
var pwfRange = pwfun.getRange();
|
|
821
|
+
var length = pwfRange[1] - pwfRange[0];
|
|
822
|
+
var mid = 0.5 * (pwfRange[0] + pwfRange[1]);
|
|
823
|
+
pwfScale = volInfo.scale[_i7] / length;
|
|
824
|
+
pwfShift = (volInfo.offset[_i7] - mid) / length + 0.5;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
program.setUniformf("pwfshift".concat(_i7), pwfShift);
|
|
828
|
+
program.setUniformf("pwfscale".concat(_i7), pwfScale);
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
var texOpacityUnit = model.pwfTexture.getTextureUnit(); // TODO
|
|
832
|
+
|
|
833
|
+
program.setUniformi('pwfTexture1', texOpacityUnit);
|
|
834
|
+
};
|
|
835
|
+
|
|
836
|
+
publicAPI.updateShaders = function (cellBO, ren, actor) {
|
|
837
|
+
// has something changed that would require us to recreate the shader?
|
|
838
|
+
if (publicAPI.getNeedToRebuildShaders(cellBO, ren, actor)) {
|
|
839
|
+
var shaders = {
|
|
840
|
+
Vertex: null,
|
|
841
|
+
Fragment: null,
|
|
842
|
+
Geometry: null
|
|
843
|
+
};
|
|
844
|
+
publicAPI.buildShaders(shaders, ren, actor); // compile and bind the program if needed
|
|
845
|
+
|
|
846
|
+
var newShader = model._openGLRenderWindow.getShaderCache().readyShaderProgramArray(shaders.Vertex, shaders.Fragment, shaders.Geometry); // if the shader changed reinitialize the VAO
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
if (newShader !== cellBO.getProgram()) {
|
|
850
|
+
cellBO.setProgram(newShader); // reset the VAO as the shader has changed
|
|
851
|
+
|
|
852
|
+
cellBO.getVAO().releaseGraphicsResources();
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
cellBO.getShaderSourceTime().modified();
|
|
856
|
+
} else {
|
|
857
|
+
model._openGLRenderWindow.getShaderCache().readyShaderProgram(cellBO.getProgram());
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
cellBO.getVAO().bind();
|
|
861
|
+
publicAPI.setMapperShaderParameters(cellBO, ren, actor);
|
|
862
|
+
publicAPI.setCameraShaderParameters(cellBO, ren, actor);
|
|
863
|
+
publicAPI.setPropertyShaderParameters(cellBO, ren, actor);
|
|
864
|
+
};
|
|
865
|
+
} // ----------------------------------------------------------------------------
|
|
866
|
+
// Object factory
|
|
867
|
+
// ----------------------------------------------------------------------------
|
|
868
|
+
|
|
869
|
+
|
|
870
|
+
var DEFAULT_VALUES = {
|
|
871
|
+
currentRenderPass: null,
|
|
872
|
+
volumeTexture: null,
|
|
873
|
+
volumeTextureTime: 0,
|
|
874
|
+
colorTexture: null,
|
|
875
|
+
colorTextureString: null,
|
|
876
|
+
pwfTexture: null,
|
|
877
|
+
pwfTextureString: null,
|
|
878
|
+
tris: null,
|
|
879
|
+
lastHaveSeenDepthRequest: false,
|
|
880
|
+
haveSeenDepthRequest: false,
|
|
881
|
+
lastTextureComponents: 0,
|
|
882
|
+
lastIndependentComponents: 0,
|
|
883
|
+
imagemat: null,
|
|
884
|
+
imagematinv: null
|
|
885
|
+
}; // ----------------------------------------------------------------------------
|
|
886
|
+
|
|
887
|
+
function extend(publicAPI, model) {
|
|
888
|
+
var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
889
|
+
Object.assign(model, DEFAULT_VALUES, initialValues); // Inheritance
|
|
890
|
+
|
|
891
|
+
vtkViewNode.extend(publicAPI, model, initialValues);
|
|
892
|
+
vtkReplacementShaderMapper.implementReplaceShaderCoincidentOffset(publicAPI, model, initialValues); // Two inputs: one for the ImageData/Texture and one for the PolyData (centerline)
|
|
893
|
+
|
|
894
|
+
macro.algo(publicAPI, model, 2, 0);
|
|
895
|
+
model.tris = vtkHelper.newInstance();
|
|
896
|
+
model.volumeTexture = vtkOpenGLTexture.newInstance();
|
|
897
|
+
model.colorTexture = vtkOpenGLTexture.newInstance();
|
|
898
|
+
model.pwfTexture = vtkOpenGLTexture.newInstance();
|
|
899
|
+
model.imagemat = mat4.identity(new Float64Array(16));
|
|
900
|
+
model.imagematinv = mat4.identity(new Float64Array(16));
|
|
901
|
+
model.VBOBuildTime = {};
|
|
902
|
+
macro.obj(model.VBOBuildTime, {
|
|
903
|
+
mtime: 0
|
|
904
|
+
}); // Object methods
|
|
905
|
+
|
|
906
|
+
vtkOpenGLImageCPRMapper(publicAPI, model);
|
|
907
|
+
} // ----------------------------------------------------------------------------
|
|
908
|
+
|
|
909
|
+
var newInstance = macro.newInstance(extend, 'vtkOpenGLImageCPRMapper');
|
|
910
|
+
var STATIC = {}; // ----------------------------------------------------------------------------
|
|
911
|
+
|
|
912
|
+
var index = _objectSpread({
|
|
913
|
+
newInstance: newInstance,
|
|
914
|
+
extend: extend
|
|
915
|
+
}, STATIC); // Register ourself to OpenGL backend if imported
|
|
916
|
+
|
|
917
|
+
registerOverride('vtkImageCPRMapper', newInstance);
|
|
918
|
+
|
|
919
|
+
export { STATIC, index as default, extend, newInstance };
|