@kitware/vtk.js 24.14.3 → 24.16.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.
@@ -61,7 +61,6 @@ function vtkCamera(publicAPI, model) {
61
61
  model.position[2] = z; // recompute the focal distance
62
62
 
63
63
  publicAPI.computeDistance();
64
- publicAPI.computeCameraLightTransform();
65
64
  publicAPI.modified();
66
65
  };
67
66
 
@@ -75,7 +74,6 @@ function vtkCamera(publicAPI, model) {
75
74
  model.focalPoint[2] = z; // recompute the focal distance
76
75
 
77
76
  publicAPI.computeDistance();
78
- publicAPI.computeCameraLightTransform();
79
77
  publicAPI.modified();
80
78
  };
81
79
 
@@ -97,7 +95,6 @@ function vtkCamera(publicAPI, model) {
97
95
  model.focalPoint[0] = model.position[0] + vec[0] * model.distance;
98
96
  model.focalPoint[1] = model.position[1] + vec[1] * model.distance;
99
97
  model.focalPoint[2] = model.position[2] + vec[2] * model.distance;
100
- publicAPI.computeCameraLightTransform();
101
98
  publicAPI.modified();
102
99
  }; //----------------------------------------------------------------------------
103
100
  // This method must be called when the focal point or camera position changes
@@ -173,6 +173,16 @@ export interface vtkRenderWindowInteractor extends vtkObject {
173
173
  *
174
174
  * @param {IRenderWindowInteractorEvent} callData
175
175
  */
176
+ invokePointerEnter(callData: IRenderWindowInteractorEvent): void;
177
+
178
+ /**
179
+ *
180
+ */
181
+ invokePointerLeave(callData: IRenderWindowInteractorEvent): void;
182
+
183
+ /**
184
+ *
185
+ */
176
186
  invokeMouseEnter(callData: IRenderWindowInteractorEvent): void;
177
187
 
178
188
  /**
@@ -392,6 +402,18 @@ export interface vtkRenderWindowInteractor extends vtkObject {
392
402
  * @param {InteractorEventCallback} cb The callback to be called.
393
403
  * @param {Number} [priority] The priority of the event.
394
404
  */
405
+ onPointerEnter(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;
406
+
407
+ /**
408
+ *
409
+ * @param cb The callback to be called
410
+ */
411
+ onPointerLeave(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;
412
+
413
+ /**
414
+ *
415
+ * @param cb The callback to be called
416
+ */
395
417
  onMouseEnter(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;
396
418
 
397
419
  /**
@@ -949,11 +971,35 @@ export interface vtkRenderWindowInteractor extends vtkObject {
949
971
  */
950
972
  handleKeyUp(event: KeyboardEvent): void;
951
973
 
974
+ /**
975
+ *
976
+ * @param {PointerEvent} event
977
+ */
978
+ handlePointerDown(event: PointerEvent): void;
979
+
980
+ /**
981
+ *
982
+ * @param {PointerEvent} event
983
+ */
984
+ handlePointerUp(event: PointerEvent): void;
985
+
986
+ /**
987
+ *
988
+ * @param {PointerEvent} event
989
+ */
990
+ handlePointerCancel(event: PointerEvent): void;
991
+
992
+ /**
993
+ *
994
+ * @param {PointerEvent} event
995
+ */
996
+ handlePointerMove(event: PointerEvent): void;
997
+
952
998
  /**
953
999
  *
954
- * @param {MouseEvent} event
1000
+ * @param {PointerEvent} event
955
1001
  */
956
- handleMouseDown(event: MouseEvent): void;
1002
+ handleMouseDown(event: PointerEvent): void;
957
1003
 
958
1004
  /**
959
1005
  *
@@ -1011,9 +1057,9 @@ export interface vtkRenderWindowInteractor extends vtkObject {
1011
1057
 
1012
1058
  /**
1013
1059
  *
1014
- * @param {MouseEvent} event
1060
+ * @param {PointerEvent} event
1015
1061
  */
1016
- handleMouseMove(event: MouseEvent): void;
1062
+ handleMouseMove(event: PointerEvent): void;
1017
1063
 
1018
1064
  /**
1019
1065
  *
@@ -1028,39 +1074,39 @@ export interface vtkRenderWindowInteractor extends vtkObject {
1028
1074
 
1029
1075
  /**
1030
1076
  *
1031
- * @param {MouseEvent} event
1077
+ * @param {PointerEvent} event
1032
1078
  */
1033
- handleMouseEnter(event: MouseEvent): void;
1079
+ handlePointerEnter(event: PointerEvent): void;
1034
1080
 
1035
1081
  /**
1036
1082
  *
1037
- * @param {MouseEvent} event
1083
+ * @param {PointerEvent} event
1038
1084
  */
1039
- handleMouseLeave(event: MouseEvent): void;
1085
+ handlePointerLeave(event: PointerEvent): void;
1040
1086
 
1041
1087
  /**
1042
1088
  *
1043
- * @param {MouseEvent} event
1089
+ * @param {PointerEvent} event
1044
1090
  */
1045
- handleMouseUp(event: MouseEvent): void;
1091
+ handleMouseUp(event: PointerEvent): void;
1046
1092
 
1047
1093
  /**
1048
1094
  *
1049
- * @param {TouchEvent} event
1095
+ * @param {PointerEvent} event
1050
1096
  */
1051
- handleTouchStart(event: TouchEvent): void;
1097
+ handleTouchStart(event: PointerEvent): void;
1052
1098
 
1053
1099
  /**
1054
1100
  *
1055
- * @param {TouchEvent} event
1101
+ * @param {PointerEvent} event
1056
1102
  */
1057
- handleTouchMove(event: TouchEvent): void;
1103
+ handleTouchMove(event: PointerEvent): void;
1058
1104
 
1059
1105
  /**
1060
1106
  *
1061
- * @param {TouchEvent} event
1107
+ * @param {PointerEvent} event
1062
1108
  */
1063
- handleTouchEnd(event: TouchEvent): void;
1109
+ handleTouchEnd(event: PointerEvent): void;
1064
1110
 
1065
1111
  /**
1066
1112
  *
@@ -1093,7 +1139,7 @@ export interface vtkRenderWindowInteractor extends vtkObject {
1093
1139
  * @param event
1094
1140
  * @param positions
1095
1141
  */
1096
- recognizeGesture(event: 'TouchStart' | 'TouchMouve' | 'TouchEnd', positions: IPosition): void;
1142
+ recognizeGesture(event: 'TouchStart' | 'TouchMove' | 'TouchEnd', positions: IPosition): void;
1097
1143
 
1098
1144
  /**
1099
1145
  *
@@ -1,3 +1,4 @@
1
+ import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
1
2
  import _defineProperty from '@babel/runtime/helpers/defineProperty';
2
3
  import macro from '../../macros.js';
3
4
  import { A as degreesFromRadians } from '../../Common/Core/Math/index.js';
@@ -15,10 +16,11 @@ var vtkWarningMacro = macro.vtkWarningMacro,
15
16
  // Global methods
16
17
  // ----------------------------------------------------------------------------
17
18
 
19
+ var EMPTY_MOUSE_EVENT = new MouseEvent('');
18
20
  var deviceInputMap = {
19
21
  'xr-standard': [Input.Trigger, Input.Grip, Input.TrackPad, Input.Thumbstick, Input.A, Input.B]
20
22
  };
21
- var handledEvents = ['StartAnimation', 'Animation', 'EndAnimation', 'MouseEnter', 'MouseLeave', 'StartMouseMove', 'MouseMove', 'EndMouseMove', 'LeftButtonPress', 'LeftButtonRelease', 'MiddleButtonPress', 'MiddleButtonRelease', 'RightButtonPress', 'RightButtonRelease', 'KeyPress', 'KeyDown', 'KeyUp', 'StartMouseWheel', 'MouseWheel', 'EndMouseWheel', 'StartPinch', 'Pinch', 'EndPinch', 'StartPan', 'Pan', 'EndPan', 'StartRotate', 'Rotate', 'EndRotate', 'Button3D', 'Move3D', 'StartPointerLock', 'EndPointerLock', 'StartInteraction', 'Interaction', 'EndInteraction', 'AnimationFrameRateUpdate'];
23
+ var handledEvents = ['StartAnimation', 'Animation', 'EndAnimation', 'PointerEnter', 'PointerLeave', 'MouseEnter', 'MouseLeave', 'StartMouseMove', 'MouseMove', 'EndMouseMove', 'LeftButtonPress', 'LeftButtonRelease', 'MiddleButtonPress', 'MiddleButtonRelease', 'RightButtonPress', 'RightButtonRelease', 'KeyPress', 'KeyDown', 'KeyUp', 'StartMouseWheel', 'MouseWheel', 'EndMouseWheel', 'StartPinch', 'Pinch', 'EndPinch', 'StartPan', 'Pan', 'EndPan', 'StartRotate', 'Rotate', 'EndRotate', 'Button3D', 'Move3D', 'StartPointerLock', 'EndPointerLock', 'StartInteraction', 'Interaction', 'EndInteraction', 'AnimationFrameRateUpdate'];
22
24
 
23
25
  function preventDefault(event) {
24
26
  if (event.cancelable) {
@@ -27,6 +29,16 @@ function preventDefault(event) {
27
29
  }
28
30
 
29
31
  return false;
32
+ }
33
+
34
+ function pointerCacheToPositions(cache) {
35
+ var positions = Object.create(null);
36
+ cache.forEach(function (_ref) {
37
+ var pointerId = _ref.pointerId,
38
+ position = _ref.position;
39
+ positions[pointerId] = position;
40
+ });
41
+ return positions;
30
42
  } // ----------------------------------------------------------------------------
31
43
  // vtkRenderWindowInteractor methods
32
44
  // ----------------------------------------------------------------------------
@@ -36,9 +48,9 @@ function vtkRenderWindowInteractor(publicAPI, model) {
36
48
  // Set our className
37
49
  model.classHierarchy.push('vtkRenderWindowInteractor'); // Initialize list of requesters
38
50
 
39
- var animationRequesters = new Set(); // track active event listeners to handle simultaneous button tracking
51
+ var animationRequesters = new Set(); // map from pointerId to { pointerId: number, position: [x, y] }
40
52
 
41
- var activeListenerCount = 0; // Public API methods
53
+ var pointerCache = new Map(); // Public API methods
42
54
  //----------------------------------------------------------------------
43
55
 
44
56
  publicAPI.start = function () {
@@ -127,20 +139,13 @@ function vtkRenderWindowInteractor(publicAPI, model) {
127
139
  x: scaleX * (source.clientX - bounds.left),
128
140
  y: scaleY * (bounds.height - source.clientY + bounds.top),
129
141
  z: 0
130
- };
131
- updateCurrentRenderer(position.x, position.y);
132
- return position;
133
- }
134
-
135
- function getTouchEventPositionsFor(touches) {
136
- var positions = {};
142
+ }; // if multitouch, do not update the current renderer
137
143
 
138
- for (var i = 0; i < touches.length; i++) {
139
- var touch = touches[i];
140
- positions[touch.identifier] = getScreenEventPositionFor(touch);
144
+ if (pointerCache.size <= 1 || !model.currentRenderer) {
145
+ updateCurrentRenderer(position.x, position.y);
141
146
  }
142
147
 
143
- return positions;
148
+ return position;
144
149
  }
145
150
 
146
151
  function getModifierKeysFor(event) {
@@ -162,40 +167,8 @@ function vtkRenderWindowInteractor(publicAPI, model) {
162
167
  return keys;
163
168
  }
164
169
 
165
- function interactionRegistration(addListeners) {
166
- var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
167
- var rootElm = document;
168
- var method = addListeners ? 'addEventListener' : 'removeEventListener';
169
- var invMethod = addListeners ? 'removeEventListener' : 'addEventListener';
170
-
171
- if (!force && !addListeners && activeListenerCount > 0) {
172
- --activeListenerCount;
173
- } // only add/remove listeners when there are no registered listeners
174
-
175
-
176
- if (!activeListenerCount || force) {
177
- activeListenerCount = 0;
178
-
179
- if (model.container) {
180
- model.container[invMethod]('mousemove', publicAPI.handleMouseMove);
181
- }
182
-
183
- rootElm[method]('mouseup', publicAPI.handleMouseUp);
184
- rootElm[method]('mousemove', publicAPI.handleMouseMove);
185
- rootElm[method]('touchend', publicAPI.handleTouchEnd, false);
186
- rootElm[method]('touchcancel', publicAPI.handleTouchEnd, {
187
- passive: false,
188
- capture: false
189
- });
190
- rootElm[method]('touchmove', publicAPI.handleTouchMove, {
191
- passive: false,
192
- capture: false
193
- });
194
- }
195
-
196
- if (!force && addListeners) {
197
- ++activeListenerCount;
198
- }
170
+ function getDeviceTypeFor(event) {
171
+ return event.pointerType || '';
199
172
  }
200
173
 
201
174
  publicAPI.bindEvents = function (container) {
@@ -204,40 +177,50 @@ function vtkRenderWindowInteractor(publicAPI, model) {
204
177
 
205
178
  container.addEventListener('wheel', publicAPI.handleWheel);
206
179
  container.addEventListener('DOMMouseScroll', publicAPI.handleWheel);
207
- container.addEventListener('mouseenter', publicAPI.handleMouseEnter);
208
- container.addEventListener('mouseleave', publicAPI.handleMouseLeave);
209
- container.addEventListener('mousemove', publicAPI.handleMouseMove);
210
- container.addEventListener('mousedown', publicAPI.handleMouseDown);
180
+ container.addEventListener('pointerenter', publicAPI.handlePointerEnter);
181
+ container.addEventListener('pointerleave', publicAPI.handlePointerLeave);
182
+ container.addEventListener('pointermove', publicAPI.handlePointerMove, {
183
+ passive: false
184
+ });
185
+ container.addEventListener('pointerdown', publicAPI.handlePointerDown, {
186
+ passive: false
187
+ });
188
+ container.addEventListener('pointerup', publicAPI.handlePointerUp);
189
+ container.addEventListener('pointercancel', publicAPI.handlePointerCancel);
211
190
  document.addEventListener('keypress', publicAPI.handleKeyPress);
212
191
  document.addEventListener('keydown', publicAPI.handleKeyDown);
213
192
  document.addEventListener('keyup', publicAPI.handleKeyUp);
214
- document.addEventListener('pointerlockchange', publicAPI.handlePointerLockChange);
215
- container.addEventListener('touchstart', publicAPI.handleTouchStart, {
216
- passive: false,
217
- capture: false
218
- });
193
+ document.addEventListener('pointerlockchange', publicAPI.handlePointerLockChange); // using touchAction is more performant than preventDefault
194
+ // in a touchstart handler.
195
+
196
+ container.style.touchAction = 'none';
197
+ container.style.userSelect = 'none'; // disables tap highlight for when cursor is pointer
198
+
199
+ container.style.webkitTapHighlightColor = 'rgba(0,0,0,0)';
219
200
  };
220
201
 
221
202
  publicAPI.unbindEvents = function () {
222
- // force unbinding listeners
223
- interactionRegistration(false, true);
224
- model.container.removeEventListener('contextmenu', preventDefault); // model.container.removeEventListener('click', preventDefault); // Avoid stopping event propagation
225
-
226
- model.container.removeEventListener('wheel', publicAPI.handleWheel);
227
- model.container.removeEventListener('DOMMouseScroll', publicAPI.handleWheel);
228
- model.container.removeEventListener('mouseenter', publicAPI.handleMouseEnter);
229
- model.container.removeEventListener('mouseleave', publicAPI.handleMouseLeave);
230
- model.container.removeEventListener('mousemove', publicAPI.handleMouseMove);
231
- model.container.removeEventListener('mousedown', publicAPI.handleMouseDown);
203
+ var container = model.container;
204
+ container.removeEventListener('contextmenu', preventDefault); // model.container.removeEventListener('click', preventDefault); // Avoid stopping event propagation
205
+
206
+ container.removeEventListener('wheel', publicAPI.handleWheel);
207
+ container.removeEventListener('DOMMouseScroll', publicAPI.handleWheel);
208
+ container.removeEventListener('pointerenter', publicAPI.handlePointerEnter);
209
+ container.removeEventListener('pointerleave', publicAPI.handlePointerLeave);
210
+ container.removeEventListener('pointermove', publicAPI.handlePointerMove, {
211
+ passive: false
212
+ });
213
+ container.removeEventListener('pointerdown', publicAPI.handlePointerDown, {
214
+ passive: false
215
+ });
216
+ container.removeEventListener('pointerup', publicAPI.handlePointerUp);
217
+ container.removeEventListener('pointercancel', publicAPI.handlePointerCancel);
232
218
  document.removeEventListener('keypress', publicAPI.handleKeyPress);
233
219
  document.removeEventListener('keydown', publicAPI.handleKeyDown);
234
220
  document.removeEventListener('keyup', publicAPI.handleKeyUp);
235
221
  document.removeEventListener('pointerlockchange', publicAPI.handlePointerLockChange);
236
- model.container.removeEventListener('touchstart', publicAPI.handleTouchStart, {
237
- passive: false,
238
- capture: false
239
- });
240
222
  model.container = null;
223
+ pointerCache.clear();
241
224
  };
242
225
 
243
226
  publicAPI.handleKeyPress = function (event) {
@@ -255,19 +238,131 @@ function vtkRenderWindowInteractor(publicAPI, model) {
255
238
  publicAPI.keyUpEvent(data);
256
239
  };
257
240
 
258
- publicAPI.handleMouseDown = function (event) {
259
- if (event.button > 2) {
241
+ publicAPI.handlePointerEnter = function (event) {
242
+ var callData = _objectSpread(_objectSpread({}, getModifierKeysFor(event)), {}, {
243
+ position: getScreenEventPositionFor(event),
244
+ deviceType: getDeviceTypeFor(event)
245
+ });
246
+
247
+ publicAPI.pointerEnterEvent(callData);
248
+
249
+ if (callData.deviceType === 'mouse') {
250
+ publicAPI.mouseEnterEvent(callData);
251
+ }
252
+ };
253
+
254
+ publicAPI.handlePointerLeave = function (event) {
255
+ var callData = _objectSpread(_objectSpread({}, getModifierKeysFor(event)), {}, {
256
+ position: getScreenEventPositionFor(event),
257
+ deviceType: getDeviceTypeFor(event)
258
+ });
259
+
260
+ publicAPI.pointerLeaveEvent(callData);
261
+
262
+ if (callData.deviceType === 'mouse') {
263
+ publicAPI.mouseLeaveEvent(callData);
264
+ }
265
+ };
266
+
267
+ publicAPI.handlePointerDown = function (event) {
268
+ if (event.button > 2 || publicAPI.isPointerLocked()) {
260
269
  // ignore events from extra mouse buttons such as `back` and `forward`
261
270
  return;
262
271
  }
263
272
 
264
- interactionRegistration(true);
265
273
  preventDefault(event);
266
274
 
267
- var callData = _objectSpread(_objectSpread({}, getModifierKeysFor(event)), {}, {
275
+ if (event.target.hasPointerCapture(event.pointerId)) {
276
+ event.target.releasePointerCapture(event.pointerId);
277
+ }
278
+
279
+ model.container.setPointerCapture(event.pointerId);
280
+
281
+ if (pointerCache.has(event.pointerId)) {
282
+ vtkWarningMacro('[RenderWindowInteractor] duplicate pointerId detected');
283
+ }
284
+
285
+ pointerCache.set(event.pointerId, {
286
+ pointerId: event.pointerId,
268
287
  position: getScreenEventPositionFor(event)
269
288
  });
270
289
 
290
+ switch (event.pointerType) {
291
+ case 'pen':
292
+ case 'touch':
293
+ publicAPI.handleTouchStart(event);
294
+ break;
295
+
296
+ case 'mouse':
297
+ default:
298
+ publicAPI.handleMouseDown(event);
299
+ break;
300
+ }
301
+ };
302
+
303
+ publicAPI.handlePointerUp = function (event) {
304
+ if (pointerCache.has(event.pointerId)) {
305
+ preventDefault(event);
306
+ pointerCache.delete(event.pointerId);
307
+ model.container.releasePointerCapture(event.pointerId);
308
+
309
+ switch (event.pointerType) {
310
+ case 'pen':
311
+ case 'touch':
312
+ publicAPI.handleTouchEnd(event);
313
+ break;
314
+
315
+ case 'mouse':
316
+ default:
317
+ publicAPI.handleMouseUp(event);
318
+ break;
319
+ }
320
+ }
321
+ };
322
+
323
+ publicAPI.handlePointerCancel = function (event) {
324
+ if (pointerCache.has(event.pointerId)) {
325
+ pointerCache.delete(event.pointerId);
326
+
327
+ switch (event.pointerType) {
328
+ case 'pen':
329
+ case 'touch':
330
+ publicAPI.handleTouchEnd(event);
331
+ break;
332
+
333
+ case 'mouse':
334
+ default:
335
+ publicAPI.handleMouseUp(event);
336
+ break;
337
+ }
338
+ }
339
+ };
340
+
341
+ publicAPI.handlePointerMove = function (event) {
342
+ if (pointerCache.has(event.pointerId)) {
343
+ var pointer = pointerCache.get(event.pointerId);
344
+ pointer.position = getScreenEventPositionFor(event);
345
+
346
+ switch (event.pointerType) {
347
+ case 'pen':
348
+ case 'touch':
349
+ publicAPI.handleTouchMove(event);
350
+ break;
351
+
352
+ case 'mouse':
353
+ default:
354
+ publicAPI.handleMouseMove(event);
355
+ break;
356
+ }
357
+ }
358
+ };
359
+
360
+ publicAPI.handleMouseDown = function (event) {
361
+ var callData = _objectSpread(_objectSpread({}, getModifierKeysFor(event)), {}, {
362
+ position: getScreenEventPositionFor(event),
363
+ deviceType: getDeviceTypeFor(event)
364
+ });
365
+
271
366
  switch (event.button) {
272
367
  case 0:
273
368
  publicAPI.leftButtonPressEvent(callData);
@@ -457,10 +552,9 @@ function vtkRenderWindowInteractor(publicAPI, model) {
457
552
  };
458
553
 
459
554
  publicAPI.handleMouseMove = function (event) {
460
- // Do not consume event for move
461
- // preventDefault(event);
462
555
  var callData = _objectSpread(_objectSpread({}, getModifierKeysFor(event)), {}, {
463
- position: getScreenEventPositionFor(event)
556
+ position: getScreenEventPositionFor(event),
557
+ deviceType: getDeviceTypeFor(event)
464
558
  });
465
559
 
466
560
  if (model.moveTimeoutID === 0) {
@@ -522,7 +616,8 @@ function vtkRenderWindowInteractor(publicAPI, model) {
522
616
  */
523
617
 
524
618
  var callData = _objectSpread(_objectSpread(_objectSpread({}, normalizeWheel(event)), getModifierKeysFor(event)), {}, {
525
- position: getScreenEventPositionFor(event)
619
+ position: getScreenEventPositionFor(event),
620
+ deviceType: getDeviceTypeFor(event)
526
621
  });
527
622
 
528
623
  if (model.wheelTimeoutID === 0) {
@@ -540,28 +635,10 @@ function vtkRenderWindowInteractor(publicAPI, model) {
540
635
  }, 200);
541
636
  };
542
637
 
543
- publicAPI.handleMouseEnter = function (event) {
544
- var callData = _objectSpread(_objectSpread({}, getModifierKeysFor(event)), {}, {
545
- position: getScreenEventPositionFor(event)
546
- });
547
-
548
- publicAPI.mouseEnterEvent(callData);
549
- };
550
-
551
- publicAPI.handleMouseLeave = function (event) {
552
- var callData = _objectSpread(_objectSpread({}, getModifierKeysFor(event)), {}, {
553
- position: getScreenEventPositionFor(event)
554
- });
555
-
556
- publicAPI.mouseLeaveEvent(callData);
557
- };
558
-
559
638
  publicAPI.handleMouseUp = function (event) {
560
- interactionRegistration(false);
561
- preventDefault(event);
562
-
563
639
  var callData = _objectSpread(_objectSpread({}, getModifierKeysFor(event)), {}, {
564
- position: getScreenEventPositionFor(event)
640
+ position: getScreenEventPositionFor(event),
641
+ deviceType: getDeviceTypeFor(event)
565
642
  });
566
643
 
567
644
  switch (event.button) {
@@ -584,107 +661,85 @@ function vtkRenderWindowInteractor(publicAPI, model) {
584
661
  };
585
662
 
586
663
  publicAPI.handleTouchStart = function (event) {
587
- interactionRegistration(true);
588
- preventDefault(event); // If multitouch
589
-
590
- if (model.recognizeGestures && event.touches.length > 1) {
591
- var positions = getTouchEventPositionsFor(event.touches); // did we just transition to multitouch?
592
-
593
- if (event.touches.length === 2) {
594
- var touch = event.touches[0];
595
- var callData = {
596
- position: getScreenEventPositionFor(touch),
597
- shiftKey: false,
598
- altKey: false,
599
- controlKey: false
600
- };
664
+ var pointers = _toConsumableArray(pointerCache.values()); // If multitouch
665
+
666
+
667
+ if (model.recognizeGestures && pointers.length > 1) {
668
+ var positions = pointerCacheToPositions(pointerCache); // did we just transition to multitouch?
669
+
670
+ if (pointers.length === 2) {
671
+ var callData = _objectSpread(_objectSpread({}, getModifierKeysFor(EMPTY_MOUSE_EVENT)), {}, {
672
+ position: pointers[0].position,
673
+ deviceType: getDeviceTypeFor(event)
674
+ });
675
+
601
676
  publicAPI.leftButtonReleaseEvent(callData);
602
677
  } // handle the gesture
603
678
 
604
679
 
605
680
  publicAPI.recognizeGesture('TouchStart', positions);
606
681
  } else {
607
- var _touch = event.touches[0];
608
- var _callData = {
609
- position: getScreenEventPositionFor(_touch),
610
- shiftKey: false,
611
- altKey: false,
612
- controlKey: false
613
- };
682
+ var _callData = _objectSpread(_objectSpread({}, getModifierKeysFor(EMPTY_MOUSE_EVENT)), {}, {
683
+ position: getScreenEventPositionFor(event),
684
+ deviceType: getDeviceTypeFor(event)
685
+ });
686
+
614
687
  publicAPI.leftButtonPressEvent(_callData);
615
688
  }
616
689
  };
617
690
 
618
691
  publicAPI.handleTouchMove = function (event) {
619
- preventDefault(event);
692
+ var pointers = _toConsumableArray(pointerCache.values());
620
693
 
621
- if (model.recognizeGestures && event.touches.length > 1) {
622
- var positions = getTouchEventPositionsFor(event.touches);
694
+ if (model.recognizeGestures && pointers.length > 1) {
695
+ var positions = pointerCacheToPositions(pointerCache);
623
696
  publicAPI.recognizeGesture('TouchMove', positions);
624
697
  } else {
625
- var touch = event.touches[0];
626
- var callData = {
627
- position: getScreenEventPositionFor(touch),
628
- shiftKey: false,
629
- altKey: false,
630
- controlKey: false
631
- };
698
+ var callData = _objectSpread(_objectSpread({}, getModifierKeysFor(EMPTY_MOUSE_EVENT)), {}, {
699
+ position: pointers[0].position,
700
+ deviceType: getDeviceTypeFor(event)
701
+ });
702
+
632
703
  publicAPI.mouseMoveEvent(callData);
633
704
  }
634
705
  };
635
706
 
636
707
  publicAPI.handleTouchEnd = function (event) {
637
- preventDefault(event);
708
+ var pointers = _toConsumableArray(pointerCache.values());
638
709
 
639
710
  if (model.recognizeGestures) {
640
711
  // No more fingers down
641
- if (event.touches.length === 0) {
642
- // If just one finger released, consider as left button
643
- if (event.changedTouches.length === 1) {
644
- var touch = event.changedTouches[0];
645
- var callData = {
646
- position: getScreenEventPositionFor(touch),
647
- shiftKey: false,
648
- altKey: false,
649
- controlKey: false
650
- };
651
- publicAPI.leftButtonReleaseEvent(callData);
652
- interactionRegistration(false);
653
- } else {
654
- // If more than one finger released, recognize touchend
655
- var positions = getTouchEventPositionsFor(event.changedTouches);
656
- publicAPI.recognizeGesture('TouchEnd', positions);
657
- interactionRegistration(false);
658
- }
659
- } else if (event.touches.length === 1) {
712
+ if (pointers.length === 0) {
713
+ var callData = _objectSpread(_objectSpread({}, getModifierKeysFor(EMPTY_MOUSE_EVENT)), {}, {
714
+ position: pointers[0].position,
715
+ deviceType: getDeviceTypeFor(event)
716
+ });
717
+
718
+ publicAPI.leftButtonReleaseEvent(callData);
719
+ } else if (pointers.length === 1) {
660
720
  // If one finger left, end touch and start button press
661
- var _positions = getTouchEventPositionsFor(event.changedTouches);
662
-
663
- publicAPI.recognizeGesture('TouchEnd', _positions);
664
- var _touch2 = event.touches[0];
665
- var _callData2 = {
666
- position: getScreenEventPositionFor(_touch2),
667
- shiftKey: false,
668
- altKey: false,
669
- controlKey: false
670
- };
721
+ var positions = pointerCacheToPositions(pointerCache);
722
+ publicAPI.recognizeGesture('TouchEnd', positions);
723
+
724
+ var _callData2 = _objectSpread(_objectSpread({}, getModifierKeysFor(EMPTY_MOUSE_EVENT)), {}, {
725
+ position: pointers[0].position,
726
+ deviceType: getDeviceTypeFor(event)
727
+ });
728
+
671
729
  publicAPI.leftButtonPressEvent(_callData2);
672
730
  } else {
673
731
  // If more than one finger left, keep touch move
674
- var _positions2 = getTouchEventPositionsFor(event.touches);
732
+ var _positions = pointerCacheToPositions(pointerCache);
675
733
 
676
- publicAPI.recognizeGesture('TouchMove', _positions2);
734
+ publicAPI.recognizeGesture('TouchMove', _positions);
677
735
  }
678
736
  } else {
679
- var _touch3 = event.changedTouches[0];
680
- var _callData3 = {
681
- position: getScreenEventPositionFor(_touch3),
682
- shiftKey: false,
683
- altKey: false,
684
- controlKey: false
685
- };
737
+ var _callData3 = _objectSpread(_objectSpread({}, getModifierKeysFor(EMPTY_MOUSE_EVENT)), {}, {
738
+ position: pointers[0].position,
739
+ deviceType: getDeviceTypeFor(event)
740
+ });
741
+
686
742
  publicAPI.leftButtonReleaseEvent(_callData3);
687
- interactionRegistration(false);
688
743
  }
689
744
  };
690
745
 
@@ -92,15 +92,16 @@ var DEFAULT_VALUES = {
92
92
  ipScalarRange: [-1000000.0, 1000000.0],
93
93
  filterMode: FilterMode.OFF,
94
94
  // ignored by WebGL so no behavior change
95
- preferSizeOverAccuracy: false // Whether to use halfFloat representation of float, when it is inaccurate
96
-
95
+ preferSizeOverAccuracy: false,
96
+ // Whether to use halfFloat representation of float, when it is inaccurate
97
+ computeNormalFromOpacity: false
97
98
  }; // ----------------------------------------------------------------------------
98
99
 
99
100
  function extend(publicAPI, model) {
100
101
  var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
101
102
  Object.assign(model, DEFAULT_VALUES, initialValues);
102
103
  vtkAbstractMapper.extend(publicAPI, model, initialValues);
103
- macro.setGet(publicAPI, model, ['sampleDistance', 'imageSampleDistance', 'maximumSamplesPerRay', 'autoAdjustSampleDistances', 'blendMode', 'filterMode', 'preferSizeOverAccuracy']);
104
+ macro.setGet(publicAPI, model, ['sampleDistance', 'imageSampleDistance', 'maximumSamplesPerRay', 'autoAdjustSampleDistances', 'blendMode', 'filterMode', 'preferSizeOverAccuracy', 'computeNormalFromOpacity']);
104
105
  macro.setGetArray(publicAPI, model, ['ipScalarRange'], 2);
105
106
  macro.event(publicAPI, model, 'lightingActivated'); // Object methods
106
107
 
@@ -153,6 +153,11 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
153
153
 
154
154
  if (model.gopacity) {
155
155
  FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::GradientOpacityOn', '#define vtkGradientOpacityOn').result;
156
+ } // set normal from density
157
+
158
+
159
+ if (model.renderable.getComputeNormalFromOpacity()) {
160
+ FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::vtkComputeNormalFromOpacity', "#define vtkComputeNormalFromOpacity").result;
156
161
  } // if we have a ztexture then declare it and use it
157
162
 
158
163
 
@@ -1,3 +1,3 @@
1
- var vtkVolumeFS = "//VTK::System::Dec\n\n/*=========================================================================\n\n Program: Visualization Toolkit\n Module: vtkVolumeFS.glsl\n\n Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen\n All rights reserved.\n See Copyright.txt or http://www.kitware.com/Copyright.htm for details.\n\n This software is distributed WITHOUT ANY WARRANTY; without even\n the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR\n PURPOSE. See the above copyright notice for more information.\n\n=========================================================================*/\n// Template for the volume mappers fragment shader\n\n// the output of this shader\n//VTK::Output::Dec\n\nvarying vec3 vertexVCVSOutput;\n\n// first declare the settings from the mapper\n// that impact the code paths in here\n\n// always set vtkNumComponents 1,2,3,4\n//VTK::NumComponents\n\n// possibly define vtkTrilinearOn\n//VTK::TrilinearOn\n\n// possibly define vtkIndependentComponents\n//VTK::IndependentComponentsOn\n\n// possibly define any \"proportional\" components\n//VTK::vtkProportionalComponents\n\n// Define the blend mode to use\n#define vtkBlendMode //VTK::BlendMode\n\n// Possibly define vtkImageLabelOutlineOn\n//VTK::ImageLabelOutlineOn\n\n#ifdef vtkImageLabelOutlineOn\nuniform int outlineThickness;\nuniform float vpWidth;\nuniform float vpHeight;\nuniform float vpOffsetX;\nuniform float vpOffsetY;\nuniform mat4 PCWCMatrix;\nuniform mat4 vWCtoIDX;\n#endif\n\n// define vtkLightComplexity\n//VTK::LightComplexity\n#if vtkLightComplexity > 0\nuniform float vSpecularPower;\nuniform float vAmbient;\nuniform float vDiffuse;\nuniform float vSpecular;\n//VTK::Light::Dec\n#endif\n\n// possibly define vtkGradientOpacityOn\n//VTK::GradientOpacityOn\n#ifdef vtkGradientOpacityOn\nuniform float goscale0;\nuniform float goshift0;\nuniform float gomin0;\nuniform float gomax0;\n#if defined(vtkIndependentComponentsOn) && (vtkNumComponents > 1)\nuniform float goscale1;\nuniform float goshift1;\nuniform float gomin1;\nuniform float gomax1;\n#if vtkNumComponents >= 3\nuniform float goscale2;\nuniform float goshift2;\nuniform float gomin2;\nuniform float gomax2;\n#endif\n#if vtkNumComponents >= 4\nuniform float goscale3;\nuniform float goshift3;\nuniform float gomin3;\nuniform float gomax3;\n#endif\n#endif\n#endif\n\n// if you want to see the raw tiled\n// data in webgl1 uncomment the following line\n// #define debugtile\n\n// camera values\nuniform float camThick;\nuniform float camNear;\nuniform float camFar;\nuniform int cameraParallel;\n\n// values describing the volume geometry\nuniform vec3 vOriginVC;\nuniform vec3 vSpacing;\nuniform ivec3 volumeDimensions; // 3d texture dimensions\nuniform vec3 vPlaneNormal0;\nuniform float vPlaneDistance0;\nuniform vec3 vPlaneNormal1;\nuniform float vPlaneDistance1;\nuniform vec3 vPlaneNormal2;\nuniform float vPlaneDistance2;\nuniform vec3 vPlaneNormal3;\nuniform float vPlaneDistance3;\nuniform vec3 vPlaneNormal4;\nuniform float vPlaneDistance4;\nuniform vec3 vPlaneNormal5;\nuniform float vPlaneDistance5;\n\n//VTK::ClipPlane::Dec\n\n// opacity and color textures\nuniform sampler2D otexture;\nuniform float oshift0;\nuniform float oscale0;\nuniform sampler2D ctexture;\nuniform float cshift0;\nuniform float cscale0;\n\n// jitter texture\nuniform sampler2D jtexture;\n\n// some 3D texture values\nuniform float sampleDistance;\nuniform vec3 vVCToIJK;\n\n// the heights defined below are the locations\n// for the up to four components of the tfuns\n// the tfuns have a height of 2XnumComps pixels so the\n// values are computed to hit the middle of the two rows\n// for that component\n#ifdef vtkIndependentComponentsOn\n#if vtkNumComponents == 2\nuniform float mix0;\nuniform float mix1;\n#define height0 0.25\n#define height1 0.75\n#endif\n#if vtkNumComponents == 3\nuniform float mix0;\nuniform float mix1;\nuniform float mix2;\n#define height0 0.17\n#define height1 0.5\n#define height2 0.83\n#endif\n#if vtkNumComponents == 4\nuniform float mix0;\nuniform float mix1;\nuniform float mix2;\nuniform float mix3;\n#define height0 0.125\n#define height1 0.375\n#define height2 0.625\n#define height3 0.875\n#endif\n#endif\n\n#if vtkNumComponents >= 2\nuniform float oshift1;\nuniform float oscale1;\nuniform float cshift1;\nuniform float cscale1;\n#endif\n#if vtkNumComponents >= 3\nuniform float oshift2;\nuniform float oscale2;\nuniform float cshift2;\nuniform float cscale2;\n#endif\n#if vtkNumComponents >= 4\nuniform float oshift3;\nuniform float oscale3;\nuniform float cshift3;\nuniform float cscale3;\n#endif\n\nuniform vec4 ipScalarRangeMin;\nuniform vec4 ipScalarRangeMax;\n\n// declaration for intermixed geometry\n//VTK::ZBuffer::Dec\n\n//=======================================================================\n// global and custom variables (a temporary section before photorealistics rendering module is complete)\nvec3 rayDirVC;\n\n//=======================================================================\n// Webgl2 specific version of functions\n#if __VERSION__ == 300\n\nuniform highp sampler3D texture1;\n\nvec4 getTextureValue(vec3 pos)\n{\n vec4 tmp = texture(texture1, pos);\n#if vtkNumComponents == 1\n tmp.a = tmp.r;\n#endif\n#if vtkNumComponents == 2\n tmp.a = tmp.g;\n#endif\n#if vtkNumComponents == 3\n tmp.a = length(tmp.rgb);\n#endif\n return tmp;\n}\n\n//=======================================================================\n// WebGL1 specific version of functions\n#else\n\nuniform sampler2D texture1;\n\nuniform float texWidth;\nuniform float texHeight;\nuniform int xreps;\nuniform int xstride;\nuniform int ystride;\n\n// if computing trilinear values from multiple z slices\n#ifdef vtkTrilinearOn\nvec4 getTextureValue(vec3 ijk)\n{\n float zoff = 1.0/float(volumeDimensions.z);\n vec4 val1 = getOneTextureValue(ijk);\n vec4 val2 = getOneTextureValue(vec3(ijk.xy, ijk.z + zoff));\n\n float indexZ = float(volumeDimensions)*ijk.z;\n float zmix = indexZ - floor(indexZ);\n\n return mix(val1, val2, zmix);\n}\n\nvec4 getOneTextureValue(vec3 ijk)\n#else // nearest or fast linear\nvec4 getTextureValue(vec3 ijk)\n#endif\n{\n vec3 tdims = vec3(volumeDimensions);\n\n#ifdef debugtile\n vec2 tpos = vec2(ijk.x, ijk.y);\n vec4 tmp = texture2D(texture1, tpos);\n tmp.a = 1.0;\n\n#else\n int z = int(ijk.z * tdims.z);\n int yz = z / xreps;\n int xz = z - yz*xreps;\n\n int tileWidth = volumeDimensions.x/xstride;\n int tileHeight = volumeDimensions.y/ystride;\n\n xz *= tileWidth;\n yz *= tileHeight;\n\n float ni = float(xz) + (ijk.x*float(tileWidth));\n float nj = float(yz) + (ijk.y*float(tileHeight));\n\n vec2 tpos = vec2(ni/texWidth, nj/texHeight);\n\n vec4 tmp = texture2D(texture1, tpos);\n\n#if vtkNumComponents == 1\n tmp.a = tmp.r;\n#endif\n#if vtkNumComponents == 2\n tmp.g = tmp.a;\n#endif\n#if vtkNumComponents == 3\n tmp.a = length(tmp.rgb);\n#endif\n#endif\n\n return tmp;\n}\n\n// End of Webgl1 specific code\n//=======================================================================\n#endif\n\n//=======================================================================\n// compute the normal and gradient magnitude for a position\nvec4 computeNormal(vec3 pos, float scalar, vec3 tstep)\n{\n vec4 result;\n\n result.x = getTextureValue(pos + vec3(tstep.x, 0.0, 0.0)).a - scalar;\n result.y = getTextureValue(pos + vec3(0.0, tstep.y, 0.0)).a - scalar;\n result.z = getTextureValue(pos + vec3(0.0, 0.0, tstep.z)).a - scalar;\n\n // divide by spacing\n result.xyz /= vSpacing;\n\n result.w = length(result.xyz);\n\n // rotate to View Coords\n result.xyz =\n result.x * vPlaneNormal0 +\n result.y * vPlaneNormal2 +\n result.z * vPlaneNormal4;\n\n if (result.w > 0.0)\n {\n result.xyz /= result.w;\n }\n return result;\n}\n\n#ifdef vtkImageLabelOutlineOn\nvec3 fragCoordToIndexSpace(vec4 fragCoord) {\n vec4 pcPos = vec4(\n (fragCoord.x / vpWidth - vpOffsetX - 0.5) * 2.0,\n (fragCoord.y / vpHeight - vpOffsetY - 0.5) * 2.0,\n (fragCoord.z - 0.5) * 2.0,\n 1.0);\n\n vec4 worldCoord = PCWCMatrix * pcPos;\n vec4 vertex = (worldCoord/worldCoord.w);\n\n vec3 index = (vWCtoIDX * vertex).xyz;\n\n // half voxel fix for labelmapOutline \n return (index + vec3(0.5)) / vec3(volumeDimensions);\n}\n#endif\n\n//=======================================================================\n// compute the normals and gradient magnitudes for a position\n// for independent components\nmat4 computeMat4Normal(vec3 pos, vec4 tValue, vec3 tstep)\n{\n mat4 result;\n vec4 distX = getTextureValue(pos + vec3(tstep.x, 0.0, 0.0)) - tValue;\n vec4 distY = getTextureValue(pos + vec3(0.0, tstep.y, 0.0)) - tValue;\n vec4 distZ = getTextureValue(pos + vec3(0.0, 0.0, tstep.z)) - tValue;\n\n // divide by spacing\n distX /= vSpacing.x;\n distY /= vSpacing.y;\n distZ /= vSpacing.z;\n\n mat3 rot;\n rot[0] = vPlaneNormal0;\n rot[1] = vPlaneNormal2;\n rot[2] = vPlaneNormal4;\n\n#if !defined(vtkComponent0Proportional)\n result[0].xyz = vec3(distX.r, distY.r, distZ.r);\n result[0].a = length(result[0].xyz);\n result[0].xyz *= rot;\n if (result[0].w > 0.0)\n {\n result[0].xyz /= result[0].w;\n }\n#endif\n\n// optionally compute the 2nd component\n#if vtkNumComponents >= 2 && !defined(vtkComponent1Proportional)\n result[1].xyz = vec3(distX.g, distY.g, distZ.g);\n result[1].a = length(result[1].xyz);\n result[1].xyz *= rot;\n if (result[1].w > 0.0)\n {\n result[1].xyz /= result[1].w;\n }\n#endif\n\n// optionally compute the 3rd component\n#if vtkNumComponents >= 3 && !defined(vtkComponent2Proportional)\n result[2].xyz = vec3(distX.b, distY.b, distZ.b);\n result[2].a = length(result[2].xyz);\n result[2].xyz *= rot;\n if (result[2].w > 0.0)\n {\n result[2].xyz /= result[2].w;\n }\n#endif\n\n// optionally compute the 4th component\n#if vtkNumComponents >= 4 && !defined(vtkComponent3Proportional)\n result[3].xyz = vec3(distX.a, distY.a, distZ.a);\n result[3].a = length(result[3].xyz);\n result[3].xyz *= rot;\n if (result[3].w > 0.0)\n {\n result[3].xyz /= result[3].w;\n }\n#endif\n\n return result;\n}\n\n//=======================================================================\n// Given a normal compute the gradient opacity factors\n//\nfloat computeGradientOpacityFactor(\n vec4 normal, float goscale, float goshift, float gomin, float gomax)\n{\n#if defined(vtkGradientOpacityOn)\n return clamp(normal.a*goscale + goshift, gomin, gomax);\n#else\n return 1.0;\n#endif\n}\n\n\n//=======================================================================\n// surface light contribution\n\n#if vtkLightComplexity == 3\n// convert vector position from idx to vc\nvec3 IStoVC(vec3 posIS){\n vec3 posVC = posIS / vVCToIJK;\n return posVC.x * vPlaneNormal0 + posVC.y * vPlaneNormal2 + posVC.z * vPlaneNormal4 + vOriginVC;\n}\n#endif\n\n#if vtkLightComplexity > 0\nvoid applyLighting(inout vec3 tColor, vec4 normal)\n{\n vec3 diffuse = vec3(0.0, 0.0, 0.0);\n vec3 specular = vec3(0.0, 0.0, 0.0);\n float df, sf = 0.0;\n for (int i = 0; i < lightNum; i++){\n df = abs(dot(normal.rgb, -lightDirectionVC[i]));\n diffuse += df * lightColor[i];\n sf = pow( abs(dot(lightHalfAngleVC[i],normal.rgb)), vSpecularPower);\n specular += sf * lightColor[i];\n }\n tColor.rgb = tColor.rgb*(diffuse*vDiffuse + vAmbient) + specular*vSpecular;\n}\n #if vtkLightComplexity < 3\n void applyLightingDirectional(inout vec3 tColor, vec4 normal)\n {\n // everything in VC\n vec3 diffuse = vec3(0.0);\n vec3 specular = vec3(0.0);\n vec3 vertLightDirection;\n for (int i = 0; i < lightNum; i++){\n float ndotL,ndotH;\n vertLightDirection = lightDirectionVC[i];\n ndotL = dot(normal.xyz, vertLightDirection);\n if (ndotL < 0.0 && twoSidedLighting)\n {\n ndotL = -ndotL;\n }\n if (ndotL > 0.0)\n {\n diffuse += ndotL * lightColor[i];\n //specular\n ndotH = dot(normal.xyz, normalize(rayDirVC + vertLightDirection));\n if (ndotH < 0.0 && twoSidedLighting)\n {\n ndotH = -ndotH;\n }\n if (ndotH > 0.0)\n {\n specular += pow(ndotH, vSpecularPower) * lightColor[i];\n }\n }\n }\n tColor.rgb = tColor.rgb*(diffuse*vDiffuse + vAmbient) + specular*vSpecular;\n }\n #else\n void applyLightingPositional(inout vec3 tColor, vec4 normal, vec3 posVC)\n {\n // everything in VC\n vec3 diffuse = vec3(0.0);\n vec3 specular = vec3(0.0);\n vec3 vertLightDirection;\n for (int i = 0; i < lightNum; i++){\n float distance,attenuation,ndotL,ndotH;\n vec3 lightDir;\n if (lightPositional[i] == 1){\n lightDir = lightDirectionVC[i];\n vertLightDirection = lightPositionVC[i] - posVC; // or reverse...?\n distance = length(vertLightDirection);\n vertLightDirection = normalize(vertLightDirection);\n attenuation = 1.0 / (lightAttenuation[i].x\n + lightAttenuation[i].y * distance\n + lightAttenuation[i].z * distance * distance);\n // per OpenGL standard cone angle is 90 or less for a spot light`,\n if (lightConeAngle[i] <= 90.0){\n float coneDot = dot(normalize(posVC - lightPositionVC[i]), lightDir);\n if (coneDot >= cos(radians(lightConeAngle[i]))){ // if inside cone\n attenuation = attenuation * pow(coneDot, lightExponent[i]);\n }\n else {\n attenuation = 0.0;\n }\n }\n ndotL = dot(normal.xyz, vertLightDirection);\n if (ndotL < 0.0 && twoSidedLighting)\n {\n ndotL = -ndotL;\n }\n if (ndotL > 0.0)\n {\n diffuse += ndotL * attenuation * lightColor[i];\n //specular\n ndotH = dot(normal.xyz, normalize(rayDirVC + vertLightDirection));\n if (ndotH < 0.0 && twoSidedLighting)\n {\n ndotH = -ndotH;\n }\n if (ndotH > 0.0)\n {\n specular += pow(ndotH, vSpecularPower) * attenuation * lightColor[i];\n }\n }\n } else {\n vertLightDirection = lightDirectionVC[i];\n ndotL = dot(normal.xyz, vertLightDirection);\n if (ndotL < 0.0 && twoSidedLighting)\n {\n ndotL = -ndotL;\n }\n if (ndotL > 0.0)\n {\n diffuse += ndotL * lightColor[i];\n //specular\n ndotH = dot(normal.xyz, normalize(rayDirVC + vertLightDirection));\n if (ndotH < 0.0 && twoSidedLighting)\n {\n ndotH = -ndotH;\n }\n if (ndotH > 0.0)\n {\n specular += pow(ndotH, vSpecularPower) * lightColor[i];\n }\n }\n }\n }\n tColor.rgb = tColor.rgb * (diffuse * vDiffuse + vAmbient) + specular*vSpecular;\n }\n #endif \n#endif\n\n//=======================================================================\n// Given a texture value compute the color and opacity\n//\nvec4 getColorForValue(vec4 tValue, vec3 posIS, vec3 tstep)\n{\n#ifdef vtkImageLabelOutlineOn\n vec3 centerPosIS = fragCoordToIndexSpace(gl_FragCoord); // pos in texture space\n vec4 centerValue = getTextureValue(centerPosIS);\n bool pixelOnBorder = false;\n vec4 tColor = texture2D(ctexture, vec2(centerValue.r * cscale0 + cshift0, 0.5));\n\n // Get alpha of segment from opacity function.\n tColor.a = texture2D(otexture, vec2(centerValue.r * oscale0 + oshift0, 0.5)).r;\n\n // Only perform outline check on fragments rendering voxels that aren't invisible.\n // Saves a bunch of needless checks on the background.\n // TODO define epsilon when building shader?\n if (float(tColor.a) > 0.01) {\n for (int i = -outlineThickness; i <= outlineThickness; i++) {\n for (int j = -outlineThickness; j <= outlineThickness; j++) {\n if (i == 0 || j == 0) {\n continue;\n }\n\n vec4 neighborPixelCoord = vec4(gl_FragCoord.x + float(i),\n gl_FragCoord.y + float(j),\n gl_FragCoord.z, gl_FragCoord.w);\n\n vec3 neighborPosIS = fragCoordToIndexSpace(neighborPixelCoord);\n vec4 value = getTextureValue(neighborPosIS);\n\n // If any of my neighbours are not the same value as I\n // am, this means I am on the border of the segment.\n // We can break the loops\n if (any(notEqual(value, centerValue))) {\n pixelOnBorder = true;\n break;\n }\n }\n\n if (pixelOnBorder == true) {\n break;\n }\n }\n\n // If I am on the border, I am displayed at full opacity\n if (pixelOnBorder == true) {\n tColor.a = 1.0;\n }\n }\n\n#else\n // compute the normal and gradient magnitude if needed\n // We compute it as a vec4 if possible otherwise a mat4\n //\n vec4 goFactor = vec4(1.0,1.0,1.0,1.0);\n\n // compute the normal vectors as needed\n #if (vtkLightComplexity > 0) || defined(vtkGradientOpacityOn)\n #if defined(vtkIndependentComponentsOn) && (vtkNumComponents > 1)\n mat4 normalMat = computeMat4Normal(posIS, tValue, tstep);\n #if !defined(vtkComponent0Proportional)\n vec4 normal0 = normalMat[0];\n #endif\n #if !defined(vtkComponent1Proportional)\n vec4 normal1 = normalMat[1];\n #endif\n #if vtkNumComponents > 2\n #if !defined(vtkComponent2Proportional)\n vec4 normal2 = normalMat[2];\n #endif\n #if vtkNumComponents > 3\n #if !defined(vtkComponent3Proportional)\n vec4 normal3 = normalMat[3];\n #endif\n #endif\n #endif\n #else\n vec4 normal0 = computeNormal(posIS, tValue.a, tstep);\n #endif\n #endif\n\n // compute gradient opacity factors as needed\n #if defined(vtkGradientOpacityOn)\n #if !defined(vtkComponent0Proportional)\n goFactor.x =\n computeGradientOpacityFactor(normal0, goscale0, goshift0, gomin0, gomax0);\n #endif\n #if defined(vtkIndependentComponentsOn) && (vtkNumComponents > 1)\n #if !defined(vtkComponent1Proportional)\n goFactor.y =\n computeGradientOpacityFactor(normal1, goscale1, goshift1, gomin1, gomax1);\n #endif\n #if vtkNumComponents > 2\n #if !defined(vtkComponent2Proportional)\n goFactor.z =\n computeGradientOpacityFactor(normal2, goscale2, goshift2, gomin2, gomax2);\n #endif\n #if vtkNumComponents > 3\n #if !defined(vtkComponent3Proportional)\n goFactor.w =\n computeGradientOpacityFactor(normal3, goscale3, goshift3, gomin3, gomax3);\n #endif\n #endif\n #endif\n #endif\n #endif\n\n // single component is always independent\n #if vtkNumComponents == 1\n vec4 tColor = texture2D(ctexture, vec2(tValue.r * cscale0 + cshift0, 0.5));\n tColor.a = goFactor.x*texture2D(otexture, vec2(tValue.r * oscale0 + oshift0, 0.5)).r;\n #endif\n\n #if defined(vtkIndependentComponentsOn) && vtkNumComponents >= 2\n vec4 tColor = mix0*texture2D(ctexture, vec2(tValue.r * cscale0 + cshift0, height0));\n #if !defined(vtkComponent0Proportional)\n tColor.a = goFactor.x*mix0*texture2D(otexture, vec2(tValue.r * oscale0 + oshift0, height0)).r;\n #else\n float pwfValue = texture2D(otexture, vec2(tValue.r * oscale0 + oshift0, height0)).r;\n tColor *= pwfValue;\n tColor.a *= mix(pwfValue, 1.0, (1.0 - mix0));\n #endif\n\n vec3 tColor1 = mix1*texture2D(ctexture, vec2(tValue.g * cscale1 + cshift1, height1)).rgb;\n #if !defined(vtkComponent1Proportional)\n tColor.a += goFactor.y*mix1*texture2D(otexture, vec2(tValue.g * oscale1 + oshift1, height1)).r;\n #else\n float pwfValue = texture2D(otexture, vec2(tValue.g * oscale1 + oshift1, height1)).r;\n tColor1 *= pwfValue;\n tColor.a *= mix(pwfValue, 1.0, (1.0 - mix1));\n #endif\n\n #if vtkNumComponents >= 3\n vec3 tColor2 = mix2*texture2D(ctexture, vec2(tValue.b * cscale2 + cshift2, height2)).rgb;\n #if !defined(vtkComponent2Proportional)\n tColor.a += goFactor.z*mix2*texture2D(otexture, vec2(tValue.b * oscale2 + oshift2, height2)).r;\n #else\n float pwfValue = texture2D(otexture, vec2(tValue.b * oscale2 + oshift2, height2)).r;\n tColor2 *= pwfValue;\n tColor.a *= mix(pwfValue, 1.0, (1.0 - mix2));\n #endif\n\n #if vtkNumComponents >= 4\n vec3 tColor3 = mix3*texture2D(ctexture, vec2(tValue.a * cscale3 + cshift3, height3)).rgb;\n #if !defined(vtkComponent3Proportional)\n tColor.a += goFactor.w*mix3*texture2D(otexture, vec2(tValue.a * oscale3 + oshift3, height3)).r;\n #else\n float pwfValue = texture2D(otexture, vec2(tValue.a * oscale3 + oshift3, height3)).r;\n tColor3 *= pwfValue;\n tColor.a *= mix(pwfValue, 1.0, (1.0 - mix3));\n #endif\n #endif\n #endif\n #else // then not independent\n\n #if vtkNumComponents == 2\n float lum = tValue.r * cscale0 + cshift0;\n float alpha = goFactor.x*texture2D(otexture, vec2(tValue.a * oscale1 + oshift1, 0.5)).r;\n vec4 tColor = vec4(lum, lum, lum, alpha);\n #endif\n #if vtkNumComponents == 3\n vec4 tColor;\n tColor.r = tValue.r * cscale0 + cshift0;\n tColor.g = tValue.g * cscale1 + cshift1;\n tColor.b = tValue.b * cscale2 + cshift2;\n tColor.a = goFactor.x*texture2D(otexture, vec2(tValue.a * oscale0 + oshift0, 0.5)).r;\n #endif\n #if vtkNumComponents == 4\n vec4 tColor;\n tColor.r = tValue.r * cscale0 + cshift0;\n tColor.g = tValue.g * cscale1 + cshift1;\n tColor.b = tValue.b * cscale2 + cshift2;\n tColor.a = goFactor.x*texture2D(otexture, vec2(tValue.a * oscale3 + oshift3, 0.5)).r;\n #endif\n #endif // dependent\n\n // apply lighting if requested as appropriate\n #if vtkLightComplexity > 0\n #if !defined(vtkComponent0Proportional)\n #if vtkLightComplexity < 3\n applyLightingDirectional(tColor.rgb, vec4(normalize(normal0.xyz),normal0.w));\n #else\n applyLightingPositional(tColor.rgb, vec4(normalize(normal0.xyz),normal0.w), IStoVC(posIS));\n #endif\n #endif\n #if defined(vtkIndependentComponentsOn) && vtkNumComponents >= 2\n #if !defined(vtkComponent1Proportional)\n applyLighting(tColor1, normal1);\n #endif\n #if vtkNumComponents >= 3\n #if !defined(vtkComponent2Proportional)\n applyLighting(tColor2, normal2);\n #endif\n #if vtkNumComponents >= 4\n #if !defined(vtkComponent3Proportional)\n applyLighting(tColor3, normal3);\n #endif\n #endif\n #endif\n #endif\n #endif\n\n// perform final independent blend as needed\n#if defined(vtkIndependentComponentsOn) && vtkNumComponents >= 2\n tColor.rgb += tColor1;\n#if vtkNumComponents >= 3\n tColor.rgb += tColor2;\n#if vtkNumComponents >= 4\n tColor.rgb += tColor3;\n#endif\n#endif\n#endif\n\n#endif\n\n\n\n\n\n\n\nreturn tColor;\n}\n\nbool valueWithinScalarRange(vec4 val, vec4 min, vec4 max) {\n bool withinRange = false;\n #if vtkNumComponents == 1\n if (val.r >= min.r && val.r <= max.r) {\n withinRange = true;\n }\n #endif\n #if defined(vtkIndependentComponentsOn) && vtkNumComponents == 2\n if (val.r >= min.r && val.r <= max.r &&\n val.g >= min.g && val.g <= max.g) {\n withinRange = true;\n }\n #endif\n #if defined(vtkIndependentComponentsOn) && vtkNumComponents >= 3\n if (all(greaterThanEqual(val, ipScalarRangeMin)) &&\n all(lessThanEqual(val, ipScalarRangeMax))) {\n withinRange = true;\n }\n #endif\n return withinRange;\n}\n\n//=======================================================================\n// Apply the specified blend mode operation along the ray's path.\n//\nvoid applyBlend(vec3 posIS, vec3 endIS, float sampleDistanceIS, vec3 tdims)\n{\n vec3 tstep = 1.0/tdims;\n\n // start slightly inside and apply some jitter\n vec3 delta = endIS - posIS;\n vec3 stepIS = normalize(delta)*sampleDistanceIS;\n float raySteps = length(delta)/sampleDistanceIS;\n\n // avoid 0.0 jitter\n float jitter = 0.01 + 0.99*texture2D(jtexture, gl_FragCoord.xy/32.0).r;\n float stepsTraveled = jitter;\n\n // local vars for the loop\n vec4 color = vec4(0.0, 0.0, 0.0, 0.0);\n vec4 tValue;\n vec4 tColor;\n\n // if we have less than one step then pick the middle point\n // as our value\n // if (raySteps <= 1.0)\n // {\n // posIS = (posIS + endIS)*0.5;\n // }\n\n // Perform initial step at the volume boundary\n // compute the scalar\n tValue = getTextureValue(posIS);\n\n #if vtkBlendMode == 0 // COMPOSITE_BLEND\n // now map through opacity and color\n tColor = getColorForValue(tValue, posIS, tstep);\n\n // handle very thin volumes\n if (raySteps <= 1.0)\n {\n tColor.a = 1.0 - pow(1.0 - tColor.a, raySteps);\n gl_FragData[0] = tColor;\n return;\n }\n\n tColor.a = 1.0 - pow(1.0 - tColor.a, jitter);\n color = vec4(tColor.rgb*tColor.a, tColor.a);\n posIS += (jitter*stepIS);\n\n for (int i = 0; i < //VTK::MaximumSamplesValue ; ++i)\n {\n if (stepsTraveled + 1.0 >= raySteps) { break; }\n\n // compute the scalar\n tValue = getTextureValue(posIS);\n\n // now map through opacity and color\n tColor = getColorForValue(tValue, posIS, tstep);\n\n float mix = (1.0 - color.a);\n\n // this line should not be needed but nvidia seems to not handle\n // the break correctly on windows/chrome 58 angle\n //mix = mix * sign(max(raySteps - stepsTraveled - 1.0, 0.0));\n\n color = color + vec4(tColor.rgb*tColor.a, tColor.a)*mix;\n stepsTraveled++;\n posIS += stepIS;\n if (color.a > 0.99) { color.a = 1.0; break; }\n }\n\n if (color.a < 0.99 && (raySteps - stepsTraveled) > 0.0)\n {\n posIS = endIS;\n\n // compute the scalar\n tValue = getTextureValue(posIS);\n\n // now map through opacity and color\n tColor = getColorForValue(tValue, posIS, tstep);\n tColor.a = 1.0 - pow(1.0 - tColor.a, raySteps - stepsTraveled);\n\n float mix = (1.0 - color.a);\n color = color + vec4(tColor.rgb*tColor.a, tColor.a)*mix;\n }\n\n gl_FragData[0] = vec4(color.rgb/color.a, color.a);\n #endif\n #if vtkBlendMode == 1 || vtkBlendMode == 2\n // MAXIMUM_INTENSITY_BLEND || MINIMUM_INTENSITY_BLEND\n // Find maximum/minimum intensity along the ray.\n\n // Define the operation we will use (min or max)\n #if vtkBlendMode == 1\n #define OP max\n #else\n #define OP min\n #endif\n\n // If the clipping range is shorter than the sample distance\n // we can skip the sampling loop along the ray.\n if (raySteps <= 1.0)\n {\n gl_FragData[0] = getColorForValue(tValue, posIS, tstep);\n return;\n }\n\n vec4 value = tValue;\n posIS += (jitter*stepIS);\n\n // Sample along the ray until MaximumSamplesValue,\n // ending slightly inside the total distance\n for (int i = 0; i < //VTK::MaximumSamplesValue ; ++i)\n {\n // If we have reached the last step, break\n if (stepsTraveled + 1.0 >= raySteps) { break; }\n\n // compute the scalar\n tValue = getTextureValue(posIS);\n\n // Update the maximum value if necessary\n value = OP(tValue, value);\n\n // Otherwise, continue along the ray\n stepsTraveled++;\n posIS += stepIS;\n }\n\n // Perform the last step along the ray using the\n // residual distance\n posIS = endIS;\n tValue = getTextureValue(posIS);\n value = OP(tValue, value);\n\n // Now map through opacity and color\n gl_FragData[0] = getColorForValue(value, posIS, tstep);\n #endif\n #if vtkBlendMode == 3 || vtkBlendMode == 4 //AVERAGE_INTENSITY_BLEND || ADDITIVE_BLEND\n vec4 sum = vec4(0.);\n\n if (valueWithinScalarRange(tValue, ipScalarRangeMin, ipScalarRangeMax)) {\n sum += tValue;\n }\n\n if (raySteps <= 1.0) {\n gl_FragData[0] = getColorForValue(sum, posIS, tstep);\n return;\n }\n\n posIS += (jitter*stepIS);\n\n // Sample along the ray until MaximumSamplesValue,\n // ending slightly inside the total distance\n for (int i = 0; i < //VTK::MaximumSamplesValue ; ++i)\n {\n // If we have reached the last step, break\n if (stepsTraveled + 1.0 >= raySteps) { break; }\n\n // compute the scalar\n tValue = getTextureValue(posIS);\n\n // One can control the scalar range by setting the AverageIPScalarRange to disregard scalar values, not in the range of interest, from the average computation.\n // Notes:\n // - We are comparing all values in the texture to see if any of them\n // are outside of the scalar range. In the future we might want to allow\n // scalar ranges for each component.\n if (valueWithinScalarRange(tValue, ipScalarRangeMin, ipScalarRangeMax)) {\n // Sum the values across each step in the path\n sum += tValue;\n }\n stepsTraveled++;\n posIS += stepIS;\n }\n\n // Perform the last step along the ray using the\n // residual distance\n posIS = endIS;\n\n // compute the scalar\n tValue = getTextureValue(posIS);\n\n // One can control the scalar range by setting the IPScalarRange to disregard scalar values, not in the range of interest, from the average computation\n if (valueWithinScalarRange(tValue, ipScalarRangeMin, ipScalarRangeMax)) {\n sum += tValue;\n\n stepsTraveled++;\n }\n\n #if vtkBlendMode == 3 // Average\n sum /= vec4(stepsTraveled, stepsTraveled, stepsTraveled, 1.0);\n #endif\n\n gl_FragData[0] = getColorForValue(sum, posIS, tstep);\n #endif\n}\n\n//=======================================================================\n// Compute a new start and end point for a given ray based\n// on the provided bounded clipping plane (aka a rectangle)\nvoid getRayPointIntersectionBounds(\n vec3 rayPos, vec3 rayDir,\n vec3 planeDir, float planeDist,\n inout vec2 tbounds, vec3 vPlaneX, vec3 vPlaneY,\n float vSize1, float vSize2)\n{\n float result = dot(rayDir, planeDir);\n if (abs(result) < 1e-6)\n {\n return;\n }\n result = -1.0 * (dot(rayPos, planeDir) + planeDist) / result;\n vec3 xposVC = rayPos + rayDir*result;\n vec3 vxpos = xposVC - vOriginVC;\n vec2 vpos = vec2(\n dot(vxpos, vPlaneX),\n dot(vxpos, vPlaneY));\n\n // on some apple nvidia systems this does not work\n // if (vpos.x < 0.0 || vpos.x > vSize1 ||\n // vpos.y < 0.0 || vpos.y > vSize2)\n // even just\n // if (vpos.x < 0.0 || vpos.y < 0.0)\n // fails\n // so instead we compute a value that represents in and out\n //and then compute the return using this value\n float xcheck = max(0.0, vpos.x * (vpos.x - vSize1)); // 0 means in bounds\n float check = sign(max(xcheck, vpos.y * (vpos.y - vSize2))); // 0 means in bounds, 1 = out\n\n tbounds = mix(\n vec2(min(tbounds.x, result), max(tbounds.y, result)), // in value\n tbounds, // out value\n check); // 0 in 1 out\n}\n\n//=======================================================================\n// given a\n// - ray direction (rayDir)\n// - starting point (vertexVCVSOutput)\n// - bounding planes of the volume\n// - optionally depth buffer values\n// - far clipping plane\n// compute the start/end distances of the ray we need to cast\nvec2 computeRayDistances(vec3 rayDir, vec3 tdims)\n{\n vec2 dists = vec2(100.0*camFar, -1.0);\n\n vec3 vSize = vSpacing*tdims;\n\n // all this is in View Coordinates\n getRayPointIntersectionBounds(vertexVCVSOutput, rayDir,\n vPlaneNormal0, vPlaneDistance0, dists, vPlaneNormal2, vPlaneNormal4,\n vSize.y, vSize.z);\n getRayPointIntersectionBounds(vertexVCVSOutput, rayDir,\n vPlaneNormal1, vPlaneDistance1, dists, vPlaneNormal2, vPlaneNormal4,\n vSize.y, vSize.z);\n getRayPointIntersectionBounds(vertexVCVSOutput, rayDir,\n vPlaneNormal2, vPlaneDistance2, dists, vPlaneNormal0, vPlaneNormal4,\n vSize.x, vSize.z);\n getRayPointIntersectionBounds(vertexVCVSOutput, rayDir,\n vPlaneNormal3, vPlaneDistance3, dists, vPlaneNormal0, vPlaneNormal4,\n vSize.x, vSize.z);\n getRayPointIntersectionBounds(vertexVCVSOutput, rayDir,\n vPlaneNormal4, vPlaneDistance4, dists, vPlaneNormal0, vPlaneNormal2,\n vSize.x, vSize.y);\n getRayPointIntersectionBounds(vertexVCVSOutput, rayDir,\n vPlaneNormal5, vPlaneDistance5, dists, vPlaneNormal0, vPlaneNormal2,\n vSize.x, vSize.y);\n\n //VTK::ClipPlane::Impl\n\n // do not go behind front clipping plane\n dists.x = max(0.0,dists.x);\n\n // do not go PAST far clipping plane\n float farDist = -camThick/rayDir.z;\n dists.y = min(farDist,dists.y);\n\n // Do not go past the zbuffer value if set\n // This is used for intermixing opaque geometry\n //VTK::ZBuffer::Impl\n\n return dists;\n}\n\n//=======================================================================\n// Compute the index space starting position (pos) and end\n// position\n//\nvoid computeIndexSpaceValues(out vec3 pos, out vec3 endPos, out float sampleDistanceIS, vec3 rayDir, vec2 dists)\n{\n // compute starting and ending values in volume space\n pos = vertexVCVSOutput + dists.x*rayDir;\n pos = pos - vOriginVC;\n // convert to volume basis and origin\n pos = vec3(\n dot(pos, vPlaneNormal0),\n dot(pos, vPlaneNormal2),\n dot(pos, vPlaneNormal4));\n\n endPos = vertexVCVSOutput + dists.y*rayDir;\n endPos = endPos - vOriginVC;\n endPos = vec3(\n dot(endPos, vPlaneNormal0),\n dot(endPos, vPlaneNormal2),\n dot(endPos, vPlaneNormal4));\n\n float delta = length(endPos - pos);\n\n pos *= vVCToIJK;\n endPos *= vVCToIJK;\n\n float delta2 = length(endPos - pos);\n sampleDistanceIS = sampleDistance*delta2/delta;\n}\n\nvoid main()\n{\n\n if (cameraParallel == 1)\n {\n // Camera is parallel, so the rayDir is just the direction of the camera.\n rayDirVC = vec3(0.0, 0.0, -1.0);\n } else {\n // camera is at 0,0,0 so rayDir for perspective is just the vc coord\n rayDirVC = normalize(vertexVCVSOutput);\n }\n\n vec3 tdims = vec3(volumeDimensions);\n\n // compute the start and end points for the ray\n vec2 rayStartEndDistancesVC = computeRayDistances(rayDirVC, tdims);\n\n // do we need to composite? aka does the ray have any length\n // If not, bail out early\n if (rayStartEndDistancesVC.y <= rayStartEndDistancesVC.x)\n {\n discard;\n }\n\n // IS = Index Space\n vec3 posIS;\n vec3 endIS;\n float sampleDistanceIS;\n computeIndexSpaceValues(posIS, endIS, sampleDistanceIS, rayDirVC, rayStartEndDistancesVC);\n\n // Perform the blending operation along the ray\n applyBlend(posIS, endIS, sampleDistanceIS, tdims);\n}\n";
1
+ var vtkVolumeFS = "//VTK::System::Dec\n\n/*=========================================================================\n\n Program: Visualization Toolkit\n Module: vtkVolumeFS.glsl\n\n Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen\n All rights reserved.\n See Copyright.txt or http://www.kitware.com/Copyright.htm for details.\n\n This software is distributed WITHOUT ANY WARRANTY; without even\n the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR\n PURPOSE. See the above copyright notice for more information.\n\n=========================================================================*/\n// Template for the volume mappers fragment shader\n\n// the output of this shader\n//VTK::Output::Dec\n\nvarying vec3 vertexVCVSOutput;\n\n// first declare the settings from the mapper\n// that impact the code paths in here\n\n// always set vtkNumComponents 1,2,3,4\n//VTK::NumComponents\n\n// possibly define vtkTrilinearOn\n//VTK::TrilinearOn\n\n// possibly define vtkIndependentComponents\n//VTK::IndependentComponentsOn\n\n// possibly define any \"proportional\" components\n//VTK::vtkProportionalComponents\n\n// Define the blend mode to use\n#define vtkBlendMode //VTK::BlendMode\n\n// Possibly define vtkImageLabelOutlineOn\n//VTK::ImageLabelOutlineOn\n\n#ifdef vtkImageLabelOutlineOn\nuniform int outlineThickness;\nuniform float vpWidth;\nuniform float vpHeight;\nuniform float vpOffsetX;\nuniform float vpOffsetY;\nuniform mat4 PCWCMatrix;\nuniform mat4 vWCtoIDX;\n#endif\n\n// define vtkLightComplexity\n//VTK::LightComplexity\n#if vtkLightComplexity > 0\nuniform float vSpecularPower;\nuniform float vAmbient;\nuniform float vDiffuse;\nuniform float vSpecular;\n//VTK::Light::Dec\n#endif\n\n// define vtkComputeNormalFromOpacity\n//VTK::vtkComputeNormalFromOpacity\n\n// possibly define vtkGradientOpacityOn\n//VTK::GradientOpacityOn\n#ifdef vtkGradientOpacityOn\nuniform float goscale0;\nuniform float goshift0;\nuniform float gomin0;\nuniform float gomax0;\n#if defined(vtkIndependentComponentsOn) && (vtkNumComponents > 1)\nuniform float goscale1;\nuniform float goshift1;\nuniform float gomin1;\nuniform float gomax1;\n#if vtkNumComponents >= 3\nuniform float goscale2;\nuniform float goshift2;\nuniform float gomin2;\nuniform float gomax2;\n#endif\n#if vtkNumComponents >= 4\nuniform float goscale3;\nuniform float goshift3;\nuniform float gomin3;\nuniform float gomax3;\n#endif\n#endif\n#endif\n\n// if you want to see the raw tiled\n// data in webgl1 uncomment the following line\n// #define debugtile\n\n// camera values\nuniform float camThick;\nuniform float camNear;\nuniform float camFar;\nuniform int cameraParallel;\n\n// values describing the volume geometry\nuniform vec3 vOriginVC;\nuniform vec3 vSpacing;\nuniform ivec3 volumeDimensions; // 3d texture dimensions\nuniform vec3 vPlaneNormal0;\nuniform float vPlaneDistance0;\nuniform vec3 vPlaneNormal1;\nuniform float vPlaneDistance1;\nuniform vec3 vPlaneNormal2;\nuniform float vPlaneDistance2;\nuniform vec3 vPlaneNormal3;\nuniform float vPlaneDistance3;\nuniform vec3 vPlaneNormal4;\nuniform float vPlaneDistance4;\nuniform vec3 vPlaneNormal5;\nuniform float vPlaneDistance5;\n\n//VTK::ClipPlane::Dec\n\n// opacity and color textures\nuniform sampler2D otexture;\nuniform float oshift0;\nuniform float oscale0;\nuniform sampler2D ctexture;\nuniform float cshift0;\nuniform float cscale0;\n\n// jitter texture\nuniform sampler2D jtexture;\n\n// some 3D texture values\nuniform float sampleDistance;\nuniform vec3 vVCToIJK;\n\n// the heights defined below are the locations\n// for the up to four components of the tfuns\n// the tfuns have a height of 2XnumComps pixels so the\n// values are computed to hit the middle of the two rows\n// for that component\n#ifdef vtkIndependentComponentsOn\n#if vtkNumComponents == 2\nuniform float mix0;\nuniform float mix1;\n#define height0 0.25\n#define height1 0.75\n#endif\n#if vtkNumComponents == 3\nuniform float mix0;\nuniform float mix1;\nuniform float mix2;\n#define height0 0.17\n#define height1 0.5\n#define height2 0.83\n#endif\n#if vtkNumComponents == 4\nuniform float mix0;\nuniform float mix1;\nuniform float mix2;\nuniform float mix3;\n#define height0 0.125\n#define height1 0.375\n#define height2 0.625\n#define height3 0.875\n#endif\n#endif\n\n#if vtkNumComponents >= 2\nuniform float oshift1;\nuniform float oscale1;\nuniform float cshift1;\nuniform float cscale1;\n#endif\n#if vtkNumComponents >= 3\nuniform float oshift2;\nuniform float oscale2;\nuniform float cshift2;\nuniform float cscale2;\n#endif\n#if vtkNumComponents >= 4\nuniform float oshift3;\nuniform float oscale3;\nuniform float cshift3;\nuniform float cscale3;\n#endif\n\nuniform vec4 ipScalarRangeMin;\nuniform vec4 ipScalarRangeMax;\n\n// declaration for intermixed geometry\n//VTK::ZBuffer::Dec\n\n//=======================================================================\n// global and custom variables (a temporary section before photorealistics rendering module is complete)\nvec3 rayDirVC;\n\n//=======================================================================\n// Webgl2 specific version of functions\n#if __VERSION__ == 300\n\nuniform highp sampler3D texture1;\n\nvec4 getTextureValue(vec3 pos)\n{\n vec4 tmp = texture(texture1, pos);\n#if vtkNumComponents == 1\n tmp.a = tmp.r;\n#endif\n#if vtkNumComponents == 2\n tmp.a = tmp.g;\n#endif\n#if vtkNumComponents == 3\n tmp.a = length(tmp.rgb);\n#endif\n return tmp;\n}\n\n//=======================================================================\n// WebGL1 specific version of functions\n#else\n\nuniform sampler2D texture1;\n\nuniform float texWidth;\nuniform float texHeight;\nuniform int xreps;\nuniform int xstride;\nuniform int ystride;\n\n// if computing trilinear values from multiple z slices\n#ifdef vtkTrilinearOn\nvec4 getTextureValue(vec3 ijk)\n{\n float zoff = 1.0/float(volumeDimensions.z);\n vec4 val1 = getOneTextureValue(ijk);\n vec4 val2 = getOneTextureValue(vec3(ijk.xy, ijk.z + zoff));\n\n float indexZ = float(volumeDimensions)*ijk.z;\n float zmix = indexZ - floor(indexZ);\n\n return mix(val1, val2, zmix);\n}\n\nvec4 getOneTextureValue(vec3 ijk)\n#else // nearest or fast linear\nvec4 getTextureValue(vec3 ijk)\n#endif\n{\n vec3 tdims = vec3(volumeDimensions);\n\n#ifdef debugtile\n vec2 tpos = vec2(ijk.x, ijk.y);\n vec4 tmp = texture2D(texture1, tpos);\n tmp.a = 1.0;\n\n#else\n int z = int(ijk.z * tdims.z);\n int yz = z / xreps;\n int xz = z - yz*xreps;\n\n int tileWidth = volumeDimensions.x/xstride;\n int tileHeight = volumeDimensions.y/ystride;\n\n xz *= tileWidth;\n yz *= tileHeight;\n\n float ni = float(xz) + (ijk.x*float(tileWidth));\n float nj = float(yz) + (ijk.y*float(tileHeight));\n\n vec2 tpos = vec2(ni/texWidth, nj/texHeight);\n\n vec4 tmp = texture2D(texture1, tpos);\n\n#if vtkNumComponents == 1\n tmp.a = tmp.r;\n#endif\n#if vtkNumComponents == 2\n tmp.g = tmp.a;\n#endif\n#if vtkNumComponents == 3\n tmp.a = length(tmp.rgb);\n#endif\n#endif\n\n return tmp;\n}\n\n// End of Webgl1 specific code\n//=======================================================================\n#endif\n\n//=======================================================================\n// Given a normal compute the gradient opacity factors\nfloat computeGradientOpacityFactor(\n float normalMag, float goscale, float goshift, float gomin, float gomax)\n{\n#if defined(vtkGradientOpacityOn)\n return clamp(normalMag * goscale + goshift, gomin, gomax);\n#else\n return 1.0;\n#endif\n}\n\n//=======================================================================\n//Rotate gradients to view coordinate\n#if (vtkLightComplexity > 0) || (defined vtkGradientOpacityOn)\nvoid rotateToViewCoord(inout vec3 normalIDX){\n normalIDX.xyz =\n normalIDX.x * vPlaneNormal0 +\n normalIDX.y * vPlaneNormal2 +\n normalIDX.z * vPlaneNormal4;\n}\n#endif\n//=======================================================================\n// compute the normal and gradient magnitude for a position, uses forward difference\n#if vtkLightComplexity > 0\n #ifdef vtkComputeNormalFromOpacity\n #ifdef vtkGradientOpacityOn\n vec4 computeNormalForDensity(vec3 pos, float scalar, vec3 tstep, out mat3 scalarInterp, out vec3 secondaryGradientMag)\n {\n vec4 result;\n scalarInterp[0][0] = getTextureValue(pos + vec3(tstep.x, 0.0, 0.0)).a;\n scalarInterp[0][1] = getTextureValue(pos + vec3(0.0, tstep.y, 0.0)).a;\n scalarInterp[0][2] = getTextureValue(pos + vec3(0.0, 0.0, tstep.z)).a;\n // look up scalar values for computing secondary gradient\n scalarInterp[1][0] = getTextureValue(pos + vec3(2.0*tstep.x, 0.0, 0.0)).a;\n scalarInterp[1][1] = getTextureValue(pos + vec3(0.0, 2.0*tstep.y, 0.0)).a;\n scalarInterp[1][2] = getTextureValue(pos + vec3(0.0, 0.0, 2.0*tstep.z)).a;\n scalarInterp[2][0] = getTextureValue(pos + vec3(tstep.x, tstep.y, 0.0)).a;\n scalarInterp[2][1] = getTextureValue(pos + vec3(tstep.x, 0.0, tstep.z)).a;\n scalarInterp[2][2] = getTextureValue(pos + vec3(0.0, tstep.y, tstep.z)).a;\n result.x = scalarInterp[0][0] - scalar;\n result.y = scalarInterp[0][1] - scalar;\n result.z = scalarInterp[0][2] - scalar;\n // divide by spacing\n result.xyz /= vSpacing;\n result.w = length(result.xyz);\n rotateToViewCoord(result.xyz);\n secondaryGradientMag.x = length(vec3(scalarInterp[1][0] - scalarInterp[0][0],\n scalarInterp[2][0] - scalarInterp[0][0],\n scalarInterp[2][1] - scalarInterp[0][0]) / vSpacing);\n secondaryGradientMag.y = length(vec3(scalarInterp[2][0] - scalarInterp[0][1],\n scalarInterp[1][1] - scalarInterp[0][1],\n scalarInterp[2][2] - scalarInterp[0][1]) / vSpacing);\n secondaryGradientMag.z = length(vec3(scalarInterp[2][1] - scalarInterp[0][2],\n scalarInterp[2][2] - scalarInterp[0][2],\n scalarInterp[1][2] - scalarInterp[0][2]) / vSpacing);\n if (length(result.xyz) > 0.0) {\n return vec4(normalize(result.xyz),result.w);\n } else {\n return vec4(0.0);\n }\n }\n\n vec4 computeDensityNormal(float scalar, float gradientMag, mat3 scalarInterp, vec3 secondaryGradientMag)\n {\n vec4 opacityG;\n vec3 opacityInterp = vec3(0.0);\n float opacity = texture2D(otexture, vec2(scalar * oscale0 + oshift0, 0.5)).r;\n if (gradientMag >= 0.0){\n opacity *= computeGradientOpacityFactor(gradientMag, goscale0, goshift0, gomin0, gomax0);\n }\n opacityInterp.x = texture2D(otexture, vec2(scalarInterp[0][0] * oscale0 + oshift0, 0.5)).r; \n if (secondaryGradientMag.x >= 0.0){\n // opacityInterp.x *= computeGradientOpacityFactor(secondaryGradientMag.x, goscale0, goshift0, gomin0, gomax0);\n }\n \n opacityInterp.y = texture2D(otexture, vec2(scalarInterp[0][1] * oscale0 + oshift0, 0.5)).r;\n if (secondaryGradientMag.y >= 0.0){\n // opacityInterp.y *= computeGradientOpacityFactor(secondaryGradientMag.y, goscale0, goshift0, gomin0, gomax0);\n }\n\n opacityInterp.z = texture2D(otexture, vec2(scalarInterp[0][2] * oscale0 + oshift0, 0.5)).r;\n if (secondaryGradientMag.z >= 0.0){\n // opacityInterp.z *= computeGradientOpacityFactor(secondaryGradientMag.z, goscale0, goshift0, gomin0, gomax0);\n }\n\n opacityG.xyz = opacityInterp - vec3(opacity,opacity,opacity);\n // divide by spacing\n opacityG.xyz /= vSpacing;\n opacityG.w = length(opacityG.xyz);\n rotateToViewCoord(opacityG.xyz);\n if (length(opacityG.xyz) > 0.0) { \n return vec4(normalize(opacityG.xyz),opacityG.w);\n } else {\n return vec4(0.0);\n }\n } \n\n #else\n //if gradient opacity not on but using density gradient\n vec4 computeDensityNormal(float scalar, vec3 scalarInterp) \n { \n vec4 opacityG; \n float opacity = texture2D(otexture, vec2(scalar * oscale0 + oshift0, 0.5)).r; \n opacityG.x = texture2D(otexture, vec2(scalarInterp.x * oscale0 + oshift0, 0.5)).r - opacity; \n opacityG.y = texture2D(otexture, vec2(scalarInterp.y * oscale0 + oshift0, 0.5)).r - opacity; \n opacityG.z = texture2D(otexture, vec2(scalarInterp.z * oscale0 + oshift0, 0.5)).r - opacity; \n // divide by spacing \n opacityG.xyz /= vSpacing; \n opacityG.w = length(opacityG.xyz); \n // rotate to View Coords \n rotateToViewCoord(opacityG.xyz);\n if (length(opacityG.xyz) > 0.0) { \n return vec4(normalize(opacityG.xyz),opacityG.w); \n } else { \n return vec4(0.0); \n } \n } \n vec4 computeNormalForDensity(vec3 pos, float scalar, vec3 tstep, out vec3 scalarInterp) \n { \n vec4 result; \n scalarInterp.x = getTextureValue(pos + vec3(tstep.x, 0.0, 0.0)).a; \n scalarInterp.y = getTextureValue(pos + vec3(0.0, tstep.y, 0.0)).a; \n scalarInterp.z = getTextureValue(pos + vec3(0.0, 0.0, tstep.z)).a; \n result.x = scalarInterp.x - scalar; \n result.y = scalarInterp.y - scalar; \n result.z = scalarInterp.z - scalar; \n // divide by spacing\n result.xyz /= vSpacing;\n result.w = length(result.xyz); \n // rotate to View Coords \n rotateToViewCoord(result.xyz); \n if (length(result.xyz) > 0.0) { \n return vec4(normalize(result.xyz),result.w); \n } else { \n return vec4(0.0); \n } \n } \n #endif\n #endif\n // compute scalar density\n vec4 computeNormal(vec3 pos, float scalar, vec3 tstep) \n { \n vec4 result; \n result.x = getTextureValue(pos + vec3(tstep.x, 0.0, 0.0)).a - scalar; \n result.y = getTextureValue(pos + vec3(0.0, tstep.y, 0.0)).a - scalar; \n result.z = getTextureValue(pos + vec3(0.0, 0.0, tstep.z)).a - scalar; \n // divide by spacing \n result.xyz /= vSpacing; \n result.w = length(result.xyz); \n // rotate to View Coords \n rotateToViewCoord(result.xyz);\n return vec4(normalize(result.xyz),result.w); \n } \n#endif\n\n#ifdef vtkImageLabelOutlineOn\nvec3 fragCoordToIndexSpace(vec4 fragCoord) {\n vec4 pcPos = vec4(\n (fragCoord.x / vpWidth - vpOffsetX - 0.5) * 2.0,\n (fragCoord.y / vpHeight - vpOffsetY - 0.5) * 2.0,\n (fragCoord.z - 0.5) * 2.0,\n 1.0);\n\n vec4 worldCoord = PCWCMatrix * pcPos;\n vec4 vertex = (worldCoord/worldCoord.w);\n\n vec3 index = (vWCtoIDX * vertex).xyz;\n\n // half voxel fix for labelmapOutline \n return (index + vec3(0.5)) / vec3(volumeDimensions);\n}\n#endif\n\n//=======================================================================\n// compute the normals and gradient magnitudes for a position\n// for independent components\nmat4 computeMat4Normal(vec3 pos, vec4 tValue, vec3 tstep)\n{\n mat4 result;\n vec4 distX = getTextureValue(pos + vec3(tstep.x, 0.0, 0.0)) - tValue;\n vec4 distY = getTextureValue(pos + vec3(0.0, tstep.y, 0.0)) - tValue;\n vec4 distZ = getTextureValue(pos + vec3(0.0, 0.0, tstep.z)) - tValue;\n\n // divide by spacing\n distX /= vSpacing.x;\n distY /= vSpacing.y;\n distZ /= vSpacing.z;\n\n mat3 rot;\n rot[0] = vPlaneNormal0;\n rot[1] = vPlaneNormal2;\n rot[2] = vPlaneNormal4;\n\n#if !defined(vtkComponent0Proportional)\n result[0].xyz = vec3(distX.r, distY.r, distZ.r);\n result[0].a = length(result[0].xyz);\n result[0].xyz *= rot;\n if (result[0].w > 0.0)\n {\n result[0].xyz /= result[0].w;\n }\n#endif\n\n// optionally compute the 2nd component\n#if vtkNumComponents >= 2 && !defined(vtkComponent1Proportional)\n result[1].xyz = vec3(distX.g, distY.g, distZ.g);\n result[1].a = length(result[1].xyz);\n result[1].xyz *= rot;\n if (result[1].w > 0.0)\n {\n result[1].xyz /= result[1].w;\n }\n#endif\n\n// optionally compute the 3rd component\n#if vtkNumComponents >= 3 && !defined(vtkComponent2Proportional)\n result[2].xyz = vec3(distX.b, distY.b, distZ.b);\n result[2].a = length(result[2].xyz);\n result[2].xyz *= rot;\n if (result[2].w > 0.0)\n {\n result[2].xyz /= result[2].w;\n }\n#endif\n\n// optionally compute the 4th component\n#if vtkNumComponents >= 4 && !defined(vtkComponent3Proportional)\n result[3].xyz = vec3(distX.a, distY.a, distZ.a);\n result[3].a = length(result[3].xyz);\n result[3].xyz *= rot;\n if (result[3].w > 0.0)\n {\n result[3].xyz /= result[3].w;\n }\n#endif\n\n return result;\n}\n\n//=======================================================================\n// surface light contribution\n\n#if vtkLightComplexity == 3\n// convert vector position from idx to vc\nvec3 IStoVC(vec3 posIS){\n vec3 posVC = posIS / vVCToIJK;\n return posVC.x * vPlaneNormal0 + posVC.y * vPlaneNormal2 + posVC.z * vPlaneNormal4 + vOriginVC;\n}\n#endif\n\n#if vtkLightComplexity > 0\n void applyLighting(inout vec3 tColor, vec4 normal)\n {\n vec3 diffuse = vec3(0.0, 0.0, 0.0);\n vec3 specular = vec3(0.0, 0.0, 0.0);\n float df, sf = 0.0;\n for (int i = 0; i < lightNum; i++){\n df = abs(dot(normal.rgb, -lightDirectionVC[i]));\n diffuse += df * lightColor[i];\n sf = pow( abs(dot(lightHalfAngleVC[i],normal.rgb)), vSpecularPower);\n specular += sf * lightColor[i];\n }\n tColor.rgb = tColor.rgb*(diffuse*vDiffuse + vAmbient) + specular*vSpecular;\n }\n #if vtkLightComplexity < 3\n void applyLightingDirectional(inout vec3 tColor, vec4 normal)\n {\n // everything in VC\n vec3 diffuse = vec3(0.0);\n vec3 specular = vec3(0.0);\n vec3 vertLightDirection;\n for (int i = 0; i < lightNum; i++){\n float ndotL,vdotR;\n vertLightDirection = lightDirectionVC[i];\n ndotL = dot(normal.xyz, vertLightDirection);\n if (ndotL < 0.0 && twoSidedLighting)\n {\n ndotL = -ndotL;\n }\n if (ndotL > 0.0)\n {\n diffuse += ndotL * lightColor[i];\n //specular\n vdotR = dot(-rayDirVC, normalize(2.0 * ndotL * -normal.xyz + vertLightDirection));\n if (vdotR > 0.0)\n {\n specular += pow(vdotR, vSpecularPower) * lightColor[i];\n }\n }\n } \n tColor.rgb = tColor.rgb*(diffuse*vDiffuse + vAmbient) + specular*vSpecular; \n }\n #else\n void applyLightingPositional(inout vec3 tColor, vec4 normal, vec3 posVC)\n {\n // everything in VC\n vec3 diffuse = vec3(0.0);\n vec3 specular = vec3(0.0);\n vec3 vertLightDirection;\n for (int i = 0; i < lightNum; i++){\n float distance,attenuation,ndotL,vdotR;\n vec3 lightDir;\n if (lightPositional[i] == 1){\n lightDir = lightDirectionVC[i];\n vertLightDirection = posVC - lightPositionVC[i]; \n distance = length(vertLightDirection);\n vertLightDirection = normalize(vertLightDirection);\n attenuation = 1.0 / (lightAttenuation[i].x\n + lightAttenuation[i].y * distance\n + lightAttenuation[i].z * distance * distance);\n // per OpenGL standard cone angle is 90 or less for a spot light\n if (lightConeAngle[i] <= 90.0){\n float coneDot = dot(vertLightDirection, lightDir);\n if (coneDot >= cos(radians(lightConeAngle[i]))){ // if inside cone\n attenuation = attenuation * pow(coneDot, lightExponent[i]);\n }\n else {\n attenuation = 0.0;\n }\n }\n ndotL = dot(normal.xyz, vertLightDirection);\n if (ndotL < 0.0 && twoSidedLighting)\n {\n ndotL = -ndotL;\n }\n if (ndotL > 0.0)\n {\n diffuse += ndotL * attenuation * lightColor[i];\n //specular\n vdotR = dot(-rayDirVC, normalize(2.0 * ndotL * -normal.xyz + vertLightDirection));\n if (vdotR > 0.0)\n {\n specular += pow(vdotR, vSpecularPower) * attenuation * lightColor[i];\n }\n }\n } else {\n vertLightDirection = lightDirectionVC[i];\n ndotL = dot(normal.xyz, vertLightDirection);\n if (ndotL < 0.0 && twoSidedLighting)\n {\n ndotL = -ndotL;\n }\n if (ndotL > 0.0)\n {\n diffuse += ndotL * lightColor[i];\n //specular\n vdotR = dot(-rayDirVC, normalize(2.0 * ndotL * -normal.xyz + vertLightDirection));\n if (vdotR > 0.0)\n {\n specular += pow(vdotR, vSpecularPower) * lightColor[i];\n }\n }\n }\n }\n tColor.rgb = tColor.rgb * (diffuse * vDiffuse + vAmbient) + specular*vSpecular;\n }\n #endif \n#endif\n\n//=======================================================================\n// Given a texture value compute the color and opacity\n//\nvec4 getColorForValue(vec4 tValue, vec3 posIS, vec3 tstep)\n{\n#ifdef vtkImageLabelOutlineOn\n vec3 centerPosIS = fragCoordToIndexSpace(gl_FragCoord); // pos in texture space\n vec4 centerValue = getTextureValue(centerPosIS);\n bool pixelOnBorder = false;\n vec4 tColor = texture2D(ctexture, vec2(centerValue.r * cscale0 + cshift0, 0.5));\n\n // Get alpha of segment from opacity function.\n tColor.a = texture2D(otexture, vec2(centerValue.r * oscale0 + oshift0, 0.5)).r;\n\n // Only perform outline check on fragments rendering voxels that aren't invisible.\n // Saves a bunch of needless checks on the background.\n // TODO define epsilon when building shader?\n if (float(tColor.a) > 0.01) {\n for (int i = -outlineThickness; i <= outlineThickness; i++) {\n for (int j = -outlineThickness; j <= outlineThickness; j++) {\n if (i == 0 || j == 0) {\n continue;\n }\n\n vec4 neighborPixelCoord = vec4(gl_FragCoord.x + float(i),\n gl_FragCoord.y + float(j),\n gl_FragCoord.z, gl_FragCoord.w);\n\n vec3 neighborPosIS = fragCoordToIndexSpace(neighborPixelCoord);\n vec4 value = getTextureValue(neighborPosIS);\n\n // If any of my neighbours are not the same value as I\n // am, this means I am on the border of the segment.\n // We can break the loops\n if (any(notEqual(value, centerValue))) {\n pixelOnBorder = true;\n break;\n }\n }\n\n if (pixelOnBorder == true) {\n break;\n }\n }\n\n // If I am on the border, I am displayed at full opacity\n if (pixelOnBorder == true) {\n tColor.a = 1.0;\n }\n }\n\n#else\n // compute the normal and gradient magnitude if needed\n // We compute it as a vec4 if possible otherwise a mat4\n //\n vec4 goFactor = vec4(1.0,1.0,1.0,1.0);\n\n // compute the normal vectors as needed\n #if (vtkLightComplexity > 0) || defined(vtkGradientOpacityOn)\n #if defined(vtkIndependentComponentsOn) && (vtkNumComponents > 1)\n mat4 normalMat = computeMat4Normal(posIS, tValue, tstep);\n #if !defined(vtkComponent0Proportional)\n vec4 normal0 = normalMat[0];\n #endif\n #if !defined(vtkComponent1Proportional)\n vec4 normal1 = normalMat[1];\n #endif\n #if vtkNumComponents > 2\n #if !defined(vtkComponent2Proportional)\n vec4 normal2 = normalMat[2];\n #endif\n #if vtkNumComponents > 3\n #if !defined(vtkComponent3Proportional)\n vec4 normal3 = normalMat[3];\n #endif\n #endif\n #endif\n #else\n #ifdef vtkComputeNormalFromOpacity\n #ifdef vtkGradientOpacityOn\n mat3 scalarInterp; \n vec3 secondaryGradientMag; \n vec4 normalOpacity = vec4(0.0); \n vec4 normal0 = computeNormalForDensity(posIS, tValue.a, tstep, scalarInterp, secondaryGradientMag); \n normalOpacity = computeDensityNormal(tValue.a, normal0.w, scalarInterp,secondaryGradientMag); \n if (length(normalOpacity) == 0.0){ \n normalOpacity = normal0; \n } \n #else\n vec3 scalarInterp; \n vec4 normal0 = computeNormalForDensity(posIS, tValue.a, tstep, scalarInterp); \n vec4 normalOpacity; \n if (length(normal0)>0.0){ \n normalOpacity = computeDensityNormal(tValue.a,scalarInterp); \n if (length(normalOpacity)==0.0){ \n normalOpacity = normal0; \n } \n } \n #endif\n #else \n vec4 normal0 = computeNormal(posIS, tValue.a, tstep); \n #endif\n #endif\n #endif\n\n // compute gradient opacity factors as needed\n #if defined(vtkGradientOpacityOn)\n #if !defined(vtkComponent0Proportional)\n goFactor.x =\n computeGradientOpacityFactor(normal0.a, goscale0, goshift0, gomin0, gomax0);\n #endif\n #if defined(vtkIndependentComponentsOn) && (vtkNumComponents > 1)\n #if !defined(vtkComponent1Proportional)\n goFactor.y =\n computeGradientOpacityFactor(normal1.a, goscale1, goshift1, gomin1, gomax1);\n #endif\n #if vtkNumComponents > 2\n #if !defined(vtkComponent2Proportional)\n goFactor.z =\n computeGradientOpacityFactor(normal2.a, goscale2, goshift2, gomin2, gomax2);\n #endif\n #if vtkNumComponents > 3\n #if !defined(vtkComponent3Proportional)\n goFactor.w =\n computeGradientOpacityFactor(normal3.a, goscale3, goshift3, gomin3, gomax3);\n #endif\n #endif\n #endif\n #endif\n #endif\n\n // single component is always independent\n #if vtkNumComponents == 1\n vec4 tColor = texture2D(ctexture, vec2(tValue.r * cscale0 + cshift0, 0.5));\n tColor.a = goFactor.x*texture2D(otexture, vec2(tValue.r * oscale0 + oshift0, 0.5)).r;\n #endif\n\n #if defined(vtkIndependentComponentsOn) && vtkNumComponents >= 2\n vec4 tColor = mix0*texture2D(ctexture, vec2(tValue.r * cscale0 + cshift0, height0));\n #if !defined(vtkComponent0Proportional)\n tColor.a = goFactor.x*mix0*texture2D(otexture, vec2(tValue.r * oscale0 + oshift0, height0)).r;\n #else\n float pwfValue = texture2D(otexture, vec2(tValue.r * oscale0 + oshift0, height0)).r;\n tColor *= pwfValue;\n tColor.a *= mix(pwfValue, 1.0, (1.0 - mix0));\n #endif\n\n vec3 tColor1 = mix1*texture2D(ctexture, vec2(tValue.g * cscale1 + cshift1, height1)).rgb;\n #if !defined(vtkComponent1Proportional)\n tColor.a += goFactor.y*mix1*texture2D(otexture, vec2(tValue.g * oscale1 + oshift1, height1)).r;\n #else\n float pwfValue = texture2D(otexture, vec2(tValue.g * oscale1 + oshift1, height1)).r;\n tColor1 *= pwfValue;\n tColor.a *= mix(pwfValue, 1.0, (1.0 - mix1));\n #endif\n\n #if vtkNumComponents >= 3\n vec3 tColor2 = mix2*texture2D(ctexture, vec2(tValue.b * cscale2 + cshift2, height2)).rgb;\n #if !defined(vtkComponent2Proportional)\n tColor.a += goFactor.z*mix2*texture2D(otexture, vec2(tValue.b * oscale2 + oshift2, height2)).r;\n #else\n float pwfValue = texture2D(otexture, vec2(tValue.b * oscale2 + oshift2, height2)).r;\n tColor2 *= pwfValue;\n tColor.a *= mix(pwfValue, 1.0, (1.0 - mix2));\n #endif\n\n #if vtkNumComponents >= 4\n vec3 tColor3 = mix3*texture2D(ctexture, vec2(tValue.a * cscale3 + cshift3, height3)).rgb;\n #if !defined(vtkComponent3Proportional)\n tColor.a += goFactor.w*mix3*texture2D(otexture, vec2(tValue.a * oscale3 + oshift3, height3)).r;\n #else\n float pwfValue = texture2D(otexture, vec2(tValue.a * oscale3 + oshift3, height3)).r;\n tColor3 *= pwfValue;\n tColor.a *= mix(pwfValue, 1.0, (1.0 - mix3));\n #endif\n #endif\n #endif\n #else // then not independent\n\n #if vtkNumComponents == 2\n float lum = tValue.r * cscale0 + cshift0;\n float alpha = goFactor.x*texture2D(otexture, vec2(tValue.a * oscale1 + oshift1, 0.5)).r;\n vec4 tColor = vec4(lum, lum, lum, alpha);\n #endif\n #if vtkNumComponents == 3\n vec4 tColor;\n tColor.r = tValue.r * cscale0 + cshift0;\n tColor.g = tValue.g * cscale1 + cshift1;\n tColor.b = tValue.b * cscale2 + cshift2;\n tColor.a = goFactor.x*texture2D(otexture, vec2(tValue.a * oscale0 + oshift0, 0.5)).r;\n #endif\n #if vtkNumComponents == 4\n vec4 tColor;\n tColor.r = tValue.r * cscale0 + cshift0;\n tColor.g = tValue.g * cscale1 + cshift1;\n tColor.b = tValue.b * cscale2 + cshift2;\n tColor.a = goFactor.x*texture2D(otexture, vec2(tValue.a * oscale3 + oshift3, 0.5)).r;\n #endif\n #endif // dependent\n\n // apply lighting if requested as appropriate\n #if vtkLightComplexity > 0\n #if !defined(vtkComponent0Proportional)\n #if vtkLightComplexity < 3\n #ifdef vtkComputeNormalFromOpacity\n applyLightingDirectional(tColor.rgb, normalOpacity);\n #else\n applyLightingDirectional(tColor.rgb, normal0);\n #endif\n #else\n #ifdef vtkComputeNormalFromOpacity\n applyLightingPositional(tColor.rgb, normalOpacity, IStoVC(posIS)); \n #else\n applyLightingPositional(tColor.rgb, normal0, IStoVC(posIS)); \n #endif\n #endif\n #endif\n #if defined(vtkIndependentComponentsOn) && vtkNumComponents >= 2\n #if !defined(vtkComponent1Proportional)\n applyLighting(tColor1, normal1);\n #endif\n #if vtkNumComponents >= 3\n #if !defined(vtkComponent2Proportional)\n applyLighting(tColor2, normal2);\n #endif\n #if vtkNumComponents >= 4\n #if !defined(vtkComponent3Proportional)\n applyLighting(tColor3, normal3);\n #endif\n #endif\n #endif\n #endif\n #endif\n\n// perform final independent blend as needed\n#if defined(vtkIndependentComponentsOn) && vtkNumComponents >= 2\n tColor.rgb += tColor1;\n#if vtkNumComponents >= 3\n tColor.rgb += tColor2;\n#if vtkNumComponents >= 4\n tColor.rgb += tColor3;\n#endif\n#endif\n#endif\n\n#endif\n\n\n\n\n\n\n\nreturn tColor;\n}\n\nbool valueWithinScalarRange(vec4 val, vec4 min, vec4 max) {\n bool withinRange = false;\n #if vtkNumComponents == 1\n if (val.r >= min.r && val.r <= max.r) {\n withinRange = true;\n }\n #endif\n #if defined(vtkIndependentComponentsOn) && vtkNumComponents == 2\n if (val.r >= min.r && val.r <= max.r &&\n val.g >= min.g && val.g <= max.g) {\n withinRange = true;\n }\n #endif\n #if defined(vtkIndependentComponentsOn) && vtkNumComponents >= 3\n if (all(greaterThanEqual(val, ipScalarRangeMin)) &&\n all(lessThanEqual(val, ipScalarRangeMax))) {\n withinRange = true;\n }\n #endif\n return withinRange;\n}\n\n//=======================================================================\n// Apply the specified blend mode operation along the ray's path.\n//\nvoid applyBlend(vec3 posIS, vec3 endIS, float sampleDistanceIS, vec3 tdims)\n{\n vec3 tstep = 1.0/tdims;\n\n // start slightly inside and apply some jitter\n vec3 delta = endIS - posIS;\n vec3 stepIS = normalize(delta)*sampleDistanceIS;\n float raySteps = length(delta)/sampleDistanceIS;\n\n // avoid 0.0 jitter\n float jitter = 0.01 + 0.99*texture2D(jtexture, gl_FragCoord.xy/32.0).r;\n float stepsTraveled = jitter;\n\n // local vars for the loop\n vec4 color = vec4(0.0, 0.0, 0.0, 0.0);\n vec4 tValue;\n vec4 tColor;\n\n // if we have less than one step then pick the middle point\n // as our value\n // if (raySteps <= 1.0)\n // {\n // posIS = (posIS + endIS)*0.5;\n // }\n\n // Perform initial step at the volume boundary\n // compute the scalar\n tValue = getTextureValue(posIS);\n\n #if vtkBlendMode == 0 // COMPOSITE_BLEND\n // now map through opacity and color\n tColor = getColorForValue(tValue, posIS, tstep);\n\n // handle very thin volumes\n if (raySteps <= 1.0)\n {\n tColor.a = 1.0 - pow(1.0 - tColor.a, raySteps);\n gl_FragData[0] = tColor;\n return;\n }\n\n tColor.a = 1.0 - pow(1.0 - tColor.a, jitter);\n color = vec4(tColor.rgb*tColor.a, tColor.a);\n posIS += (jitter*stepIS);\n\n for (int i = 0; i < //VTK::MaximumSamplesValue ; ++i)\n {\n if (stepsTraveled + 1.0 >= raySteps) { break; }\n\n // compute the scalar\n tValue = getTextureValue(posIS);\n\n // now map through opacity and color\n tColor = getColorForValue(tValue, posIS, tstep);\n\n float mix = (1.0 - color.a);\n\n // this line should not be needed but nvidia seems to not handle\n // the break correctly on windows/chrome 58 angle\n //mix = mix * sign(max(raySteps - stepsTraveled - 1.0, 0.0));\n\n color = color + vec4(tColor.rgb*tColor.a, tColor.a)*mix;\n stepsTraveled++;\n posIS += stepIS;\n if (color.a > 0.99) { color.a = 1.0; break; }\n }\n\n if (color.a < 0.99 && (raySteps - stepsTraveled) > 0.0)\n {\n posIS = endIS;\n\n // compute the scalar\n tValue = getTextureValue(posIS);\n\n // now map through opacity and color\n tColor = getColorForValue(tValue, posIS, tstep);\n tColor.a = 1.0 - pow(1.0 - tColor.a, raySteps - stepsTraveled);\n\n float mix = (1.0 - color.a);\n color = color + vec4(tColor.rgb*tColor.a, tColor.a)*mix;\n }\n\n gl_FragData[0] = vec4(color.rgb/color.a, color.a);\n #endif\n #if vtkBlendMode == 1 || vtkBlendMode == 2\n // MAXIMUM_INTENSITY_BLEND || MINIMUM_INTENSITY_BLEND\n // Find maximum/minimum intensity along the ray.\n\n // Define the operation we will use (min or max)\n #if vtkBlendMode == 1\n #define OP max\n #else\n #define OP min\n #endif\n\n // If the clipping range is shorter than the sample distance\n // we can skip the sampling loop along the ray.\n if (raySteps <= 1.0)\n {\n gl_FragData[0] = getColorForValue(tValue, posIS, tstep);\n return;\n }\n\n vec4 value = tValue;\n posIS += (jitter*stepIS);\n\n // Sample along the ray until MaximumSamplesValue,\n // ending slightly inside the total distance\n for (int i = 0; i < //VTK::MaximumSamplesValue ; ++i)\n {\n // If we have reached the last step, break\n if (stepsTraveled + 1.0 >= raySteps) { break; }\n\n // compute the scalar\n tValue = getTextureValue(posIS);\n\n // Update the maximum value if necessary\n value = OP(tValue, value);\n\n // Otherwise, continue along the ray\n stepsTraveled++;\n posIS += stepIS;\n }\n\n // Perform the last step along the ray using the\n // residual distance\n posIS = endIS;\n tValue = getTextureValue(posIS);\n value = OP(tValue, value);\n\n // Now map through opacity and color\n gl_FragData[0] = getColorForValue(value, posIS, tstep);\n #endif\n #if vtkBlendMode == 3 || vtkBlendMode == 4 //AVERAGE_INTENSITY_BLEND || ADDITIVE_BLEND\n vec4 sum = vec4(0.);\n\n if (valueWithinScalarRange(tValue, ipScalarRangeMin, ipScalarRangeMax)) {\n sum += tValue;\n }\n\n if (raySteps <= 1.0) {\n gl_FragData[0] = getColorForValue(sum, posIS, tstep);\n return;\n }\n\n posIS += (jitter*stepIS);\n\n // Sample along the ray until MaximumSamplesValue,\n // ending slightly inside the total distance\n for (int i = 0; i < //VTK::MaximumSamplesValue ; ++i)\n {\n // If we have reached the last step, break\n if (stepsTraveled + 1.0 >= raySteps) { break; }\n\n // compute the scalar\n tValue = getTextureValue(posIS);\n\n // One can control the scalar range by setting the AverageIPScalarRange to disregard scalar values, not in the range of interest, from the average computation.\n // Notes:\n // - We are comparing all values in the texture to see if any of them\n // are outside of the scalar range. In the future we might want to allow\n // scalar ranges for each component.\n if (valueWithinScalarRange(tValue, ipScalarRangeMin, ipScalarRangeMax)) {\n // Sum the values across each step in the path\n sum += tValue;\n }\n stepsTraveled++;\n posIS += stepIS;\n }\n\n // Perform the last step along the ray using the\n // residual distance\n posIS = endIS;\n\n // compute the scalar\n tValue = getTextureValue(posIS);\n\n // One can control the scalar range by setting the IPScalarRange to disregard scalar values, not in the range of interest, from the average computation\n if (valueWithinScalarRange(tValue, ipScalarRangeMin, ipScalarRangeMax)) {\n sum += tValue;\n\n stepsTraveled++;\n }\n\n #if vtkBlendMode == 3 // Average\n sum /= vec4(stepsTraveled, stepsTraveled, stepsTraveled, 1.0);\n #endif\n\n gl_FragData[0] = getColorForValue(sum, posIS, tstep);\n #endif\n}\n\n//=======================================================================\n// Compute a new start and end point for a given ray based\n// on the provided bounded clipping plane (aka a rectangle)\nvoid getRayPointIntersectionBounds(\n vec3 rayPos, vec3 rayDir,\n vec3 planeDir, float planeDist,\n inout vec2 tbounds, vec3 vPlaneX, vec3 vPlaneY,\n float vSize1, float vSize2)\n{\n float result = dot(rayDir, planeDir);\n if (abs(result) < 1e-6)\n {\n return;\n }\n result = -1.0 * (dot(rayPos, planeDir) + planeDist) / result;\n vec3 xposVC = rayPos + rayDir*result;\n vec3 vxpos = xposVC - vOriginVC;\n vec2 vpos = vec2(\n dot(vxpos, vPlaneX),\n dot(vxpos, vPlaneY));\n\n // on some apple nvidia systems this does not work\n // if (vpos.x < 0.0 || vpos.x > vSize1 ||\n // vpos.y < 0.0 || vpos.y > vSize2)\n // even just\n // if (vpos.x < 0.0 || vpos.y < 0.0)\n // fails\n // so instead we compute a value that represents in and out\n //and then compute the return using this value\n float xcheck = max(0.0, vpos.x * (vpos.x - vSize1)); // 0 means in bounds\n float check = sign(max(xcheck, vpos.y * (vpos.y - vSize2))); // 0 means in bounds, 1 = out\n\n tbounds = mix(\n vec2(min(tbounds.x, result), max(tbounds.y, result)), // in value\n tbounds, // out value\n check); // 0 in 1 out\n}\n\n//=======================================================================\n// given a\n// - ray direction (rayDir)\n// - starting point (vertexVCVSOutput)\n// - bounding planes of the volume\n// - optionally depth buffer values\n// - far clipping plane\n// compute the start/end distances of the ray we need to cast\nvec2 computeRayDistances(vec3 rayDir, vec3 tdims)\n{\n vec2 dists = vec2(100.0*camFar, -1.0);\n\n vec3 vSize = vSpacing*tdims;\n\n // all this is in View Coordinates\n getRayPointIntersectionBounds(vertexVCVSOutput, rayDir,\n vPlaneNormal0, vPlaneDistance0, dists, vPlaneNormal2, vPlaneNormal4,\n vSize.y, vSize.z);\n getRayPointIntersectionBounds(vertexVCVSOutput, rayDir,\n vPlaneNormal1, vPlaneDistance1, dists, vPlaneNormal2, vPlaneNormal4,\n vSize.y, vSize.z);\n getRayPointIntersectionBounds(vertexVCVSOutput, rayDir,\n vPlaneNormal2, vPlaneDistance2, dists, vPlaneNormal0, vPlaneNormal4,\n vSize.x, vSize.z);\n getRayPointIntersectionBounds(vertexVCVSOutput, rayDir,\n vPlaneNormal3, vPlaneDistance3, dists, vPlaneNormal0, vPlaneNormal4,\n vSize.x, vSize.z);\n getRayPointIntersectionBounds(vertexVCVSOutput, rayDir,\n vPlaneNormal4, vPlaneDistance4, dists, vPlaneNormal0, vPlaneNormal2,\n vSize.x, vSize.y);\n getRayPointIntersectionBounds(vertexVCVSOutput, rayDir,\n vPlaneNormal5, vPlaneDistance5, dists, vPlaneNormal0, vPlaneNormal2,\n vSize.x, vSize.y);\n\n //VTK::ClipPlane::Impl\n\n // do not go behind front clipping plane\n dists.x = max(0.0,dists.x);\n\n // do not go PAST far clipping plane\n float farDist = -camThick/rayDir.z;\n dists.y = min(farDist,dists.y);\n\n // Do not go past the zbuffer value if set\n // This is used for intermixing opaque geometry\n //VTK::ZBuffer::Impl\n\n return dists;\n}\n\n//=======================================================================\n// Compute the index space starting position (pos) and end\n// position\n//\nvoid computeIndexSpaceValues(out vec3 pos, out vec3 endPos, out float sampleDistanceIS, vec3 rayDir, vec2 dists)\n{\n // compute starting and ending values in volume space\n pos = vertexVCVSOutput + dists.x*rayDir;\n pos = pos - vOriginVC;\n // convert to volume basis and origin\n pos = vec3(\n dot(pos, vPlaneNormal0),\n dot(pos, vPlaneNormal2),\n dot(pos, vPlaneNormal4));\n\n endPos = vertexVCVSOutput + dists.y*rayDir;\n endPos = endPos - vOriginVC;\n endPos = vec3(\n dot(endPos, vPlaneNormal0),\n dot(endPos, vPlaneNormal2),\n dot(endPos, vPlaneNormal4));\n\n float delta = length(endPos - pos);\n\n pos *= vVCToIJK;\n endPos *= vVCToIJK;\n\n float delta2 = length(endPos - pos);\n sampleDistanceIS = sampleDistance*delta2/delta;\n}\n\nvoid main()\n{\n\n if (cameraParallel == 1)\n {\n // Camera is parallel, so the rayDir is just the direction of the camera.\n rayDirVC = vec3(0.0, 0.0, -1.0);\n } else {\n // camera is at 0,0,0 so rayDir for perspective is just the vc coord\n rayDirVC = normalize(vertexVCVSOutput);\n }\n\n vec3 tdims = vec3(volumeDimensions);\n\n // compute the start and end points for the ray\n vec2 rayStartEndDistancesVC = computeRayDistances(rayDirVC, tdims);\n\n // do we need to composite? aka does the ray have any length\n // If not, bail out early\n if (rayStartEndDistancesVC.y <= rayStartEndDistancesVC.x)\n {\n discard;\n }\n\n // IS = Index Space\n vec3 posIS;\n vec3 endIS;\n float sampleDistanceIS;\n computeIndexSpaceValues(posIS, endIS, sampleDistanceIS, rayDirVC, rayStartEndDistancesVC);\n\n // Perform the blending operation along the ray\n applyBlend(posIS, endIS, sampleDistanceIS, tdims);\n}\n";
2
2
 
3
3
  export { vtkVolumeFS as v };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kitware/vtk.js",
3
- "version": "24.14.3",
3
+ "version": "24.16.1",
4
4
  "description": "Visualization Toolkit for the Web",
5
5
  "keywords": [
6
6
  "3d",