@kitware/vtk.js 22.0.0 → 22.1.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.
@@ -119,7 +119,7 @@ declare function buildFromRadian(): Transform;
119
119
  * @example
120
120
  * ```js
121
121
  * let point = [2,5,12];
122
- * vtkMatrixBuilder.buildfromDegree().translate(1,0,2).rotateZ(45).apply(point);
122
+ * vtkMatrixBuilder.buildFromDegree().translate(1,0,2).rotateZ(45).apply(point);
123
123
  * ```
124
124
  *
125
125
  * The vtkMatrixBuilder class has two functions, `vtkMatrixBuilder.buildFromDegree()` and
@@ -10,15 +10,16 @@ export interface IStyle {
10
10
  fontSizeScale?: (res: number) => number;
11
11
  edgeThickness?: number;
12
12
  edgeColor?: string;
13
+ resolution?: number;
13
14
  }
14
15
 
15
- export interface IFaceProperty {
16
+ export interface IFaceProperty extends IStyle {
16
17
  text?: string;
17
18
  faceRotation?: number;
18
19
  }
19
20
 
20
21
  /**
21
- *
22
+ *
22
23
  */
23
24
  export interface IAnnotatedCubeActorInitialValues extends IActorInitialValues {
24
25
  }
@@ -18,7 +18,7 @@ var vtkWarningMacro = macro.vtkWarningMacro,
18
18
  var deviceInputMap = {
19
19
  'xr-standard': [Input.Trigger, Input.Grip, Input.TrackPad, Input.Thumbstick, Input.A, Input.B]
20
20
  };
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'];
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'];
22
22
 
23
23
  function preventDefault(event) {
24
24
  if (event.cancelable) {
@@ -326,6 +326,8 @@ function vtkRenderWindowInteractor(publicAPI, model) {
326
326
  animationRequesters.add(requestor);
327
327
 
328
328
  if (animationRequesters.size === 1 && !model.xrAnimation) {
329
+ model._animationStartTime = Date.now();
330
+ model._animationFrameCount = 0;
329
331
  model.lastFrameTime = 0.1;
330
332
  model.lastFrameStart = Date.now();
331
333
  model.animationRequest = requestAnimationFrame(publicAPI.handleAnimation);
@@ -381,7 +383,7 @@ function vtkRenderWindowInteractor(publicAPI, model) {
381
383
  publicAPI.updateXRGamepads = function (xrSession, xrFrame, xrRefSpace) {
382
384
  // watch for when buttons change state and fire events
383
385
  xrSession.inputSources.forEach(function (inputSource) {
384
- var pose = xrFrame.getPose(inputSource.gripSpace, xrRefSpace);
386
+ var gripPose = inputSource.gripSpace == null ? null : xrFrame.getPose(inputSource.gripSpace, xrRefSpace);
385
387
  var gp = inputSource.gamepad;
386
388
  var hand = inputSource.handedness;
387
389
 
@@ -402,11 +404,11 @@ function vtkRenderWindowInteractor(publicAPI, model) {
402
404
  model.lastGamepadValues[gp.index][hand].buttons[b] = false;
403
405
  }
404
406
 
405
- if (model.lastGamepadValues[gp.index][hand].buttons[b] !== gp.buttons[b].pressed) {
407
+ if (model.lastGamepadValues[gp.index][hand].buttons[b] !== gp.buttons[b].pressed && gripPose != null) {
406
408
  publicAPI.button3DEvent({
407
409
  gamepad: gp,
408
- position: pose.transform.position,
409
- orientation: pose.transform.orientation,
410
+ position: gripPose.transform.position,
411
+ orientation: gripPose.transform.orientation,
410
412
  pressed: gp.buttons[b].pressed,
411
413
  device: inputSource.handedness === 'left' ? Device.LeftController : Device.RightController,
412
414
  input: deviceInputMap[gp.mapping] && deviceInputMap[gp.mapping][b] ? deviceInputMap[gp.mapping][b] : Input.Trigger
@@ -414,11 +416,11 @@ function vtkRenderWindowInteractor(publicAPI, model) {
414
416
  model.lastGamepadValues[gp.index][hand].buttons[b] = gp.buttons[b].pressed;
415
417
  }
416
418
 
417
- if (model.lastGamepadValues[gp.index][hand].buttons[b]) {
419
+ if (model.lastGamepadValues[gp.index][hand].buttons[b] && gripPose != null) {
418
420
  publicAPI.move3DEvent({
419
421
  gamepad: gp,
420
- position: pose.transform.position,
421
- orientation: pose.transform.orientation,
422
+ position: gripPose.transform.position,
423
+ orientation: gripPose.transform.orientation,
422
424
  device: inputSource.handedness === 'left' ? Device.LeftController : Device.RightController
423
425
  });
424
426
  }
@@ -450,6 +452,14 @@ function vtkRenderWindowInteractor(publicAPI, model) {
450
452
 
451
453
  publicAPI.handleAnimation = function () {
452
454
  var currTime = Date.now();
455
+ model._animationFrameCount++;
456
+
457
+ if (currTime - model._animationStartTime > 1000.0 && model._animationFrameCount > 1) {
458
+ model.recentAnimationFrameRate = 1000.0 * (model._animationFrameCount - 1) / (currTime - model._animationStartTime);
459
+ publicAPI.animationFrameRateUpdateEvent();
460
+ model._animationStartTime = currTime;
461
+ model._animationFrameCount = 1;
462
+ }
453
463
 
454
464
  if (model.FrameTime === -1.0) {
455
465
  model.lastFrameTime = 0.1;
@@ -498,7 +508,7 @@ function vtkRenderWindowInteractor(publicAPI, model) {
498
508
  model.wheelTimeoutID = setTimeout(function () {
499
509
  publicAPI.endMouseWheelEvent();
500
510
  model.wheelTimeoutID = 0;
501
- }, 200);
511
+ }, 800);
502
512
  };
503
513
 
504
514
  publicAPI.handleMouseEnter = function (event) {
@@ -660,10 +670,14 @@ function vtkRenderWindowInteractor(publicAPI, model) {
660
670
  };
661
671
 
662
672
  publicAPI.getFirstRenderer = function () {
663
- return model.view.getRenderable().getRenderersByReference()[0];
673
+ var _model$view, _model$view$getRender, _model$view$getRender2;
674
+
675
+ return (_model$view = model.view) === null || _model$view === void 0 ? void 0 : (_model$view$getRender = _model$view.getRenderable()) === null || _model$view$getRender === void 0 ? void 0 : (_model$view$getRender2 = _model$view$getRender.getRenderersByReference()) === null || _model$view$getRender2 === void 0 ? void 0 : _model$view$getRender2[0];
664
676
  };
665
677
 
666
678
  publicAPI.findPokedRenderer = function () {
679
+ var _model$view2, _model$view2$getRende;
680
+
667
681
  var x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
668
682
  var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
669
683
 
@@ -673,7 +687,12 @@ function vtkRenderWindowInteractor(publicAPI, model) {
673
687
  // the first one is the one we want to manipulate the camera on.
674
688
 
675
689
 
676
- var rc = model.view.getRenderable().getRenderers();
690
+ var rc = (_model$view2 = model.view) === null || _model$view2 === void 0 ? void 0 : (_model$view2$getRende = _model$view2.getRenderable()) === null || _model$view2$getRende === void 0 ? void 0 : _model$view2$getRende.getRenderers();
691
+
692
+ if (!rc) {
693
+ return null;
694
+ }
695
+
677
696
  rc.sort(function (a, b) {
678
697
  return a.getLayer() - b.getLayer();
679
698
  });
@@ -915,6 +934,8 @@ function vtkRenderWindowInteractor(publicAPI, model) {
915
934
 
916
935
  publicAPI.handleVisibilityChange = function () {
917
936
  model.lastFrameStart = Date.now();
937
+ model._animationStartTime = Date.now();
938
+ model._animationFrameCount = 0;
918
939
  };
919
940
 
920
941
  publicAPI.setCurrentRenderer = function (r) {
@@ -966,6 +987,7 @@ var DEFAULT_VALUES = {
966
987
  currentGesture: 'Start',
967
988
  animationRequest: null,
968
989
  lastFrameTime: 0.1,
990
+ recentAnimationFrameRate: 10.0,
969
991
  wheelTimeoutID: 0,
970
992
  moveTimeoutID: 0,
971
993
  lastGamepadValues: {}
@@ -981,7 +1003,7 @@ function extend(publicAPI, model) {
981
1003
  return macro.event(publicAPI, model, eventName);
982
1004
  }); // Create get-only macros
983
1005
 
984
- macro.get(publicAPI, model, ['initialized', 'container', 'interactorStyle', 'lastFrameTime', 'view']); // Create get-set macros
1006
+ macro.get(publicAPI, model, ['initialized', 'container', 'interactorStyle', 'lastFrameTime', 'recentAnimationFrameRate', 'view']); // Create get-set macros
985
1007
 
986
1008
  macro.setGet(publicAPI, model, ['lightFollowCamera', 'enabled', 'enableRender', 'recognizeGestures', 'desiredUpdateRate', 'stillUpdateRate', 'picker']); // For more macro methods, see "Sources/macros.js"
987
1009
  // Object specific methods
@@ -8,6 +8,7 @@ import vtkWebGPUMapperHelper from './MapperHelper.js';
8
8
  import vtkWebGPURenderEncoder from './RenderEncoder.js';
9
9
  import vtkWebGPUShaderCache from './ShaderCache.js';
10
10
  import vtkWebGPUTexture from './Texture.js';
11
+ import vtkWebGPUFullScreenQuad from './FullScreenQuad.js';
11
12
  import vtkWebGPUVolumePassFSQ from './VolumePassFSQ.js';
12
13
  import { f as distance2BetweenPoints } from '../../Common/Core/Math/index.js';
13
14
 
@@ -32,6 +33,7 @@ var BufferUsage = vtkWebGPUBufferManager.BufferUsage,
32
33
 
33
34
  var cubeFaceTriangles = [[0, 4, 6], [0, 6, 2], [1, 3, 7], [1, 7, 5], [0, 5, 4], [0, 1, 5], [2, 6, 7], [2, 7, 3], [0, 3, 1], [0, 2, 3], [4, 5, 7], [4, 7, 6]];
34
35
  var DepthBoundsFS = "\n//VTK::Renderer::Dec\n\n//VTK::Select::Dec\n\n//VTK::VolumePass::Dec\n\n//VTK::TCoord::Dec\n\n//VTK::RenderEncoder::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::IOStructs::Dec\n\n[[stage(fragment)]]\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output : fragmentOutput;\n\n //VTK::Select::Impl\n\n //VTK::TCoord::Impl\n\n //VTK::VolumePass::Impl\n\n // use the maximum (closest) of the current value and the zbuffer\n // the blend func will then take the min to find the farthest stop value\n var stopval: f32 = max(input.fragPos.z, textureLoad(opaquePassDepthTexture, vec2<i32>(i32(input.fragPos.x), i32(input.fragPos.y)), 0));\n\n //VTK::RenderEncoder::Impl\n return output;\n}\n";
36
+ var volumeCopyFragTemplate = "\n//VTK::Renderer::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::TCoord::Dec\n\n//VTK::RenderEncoder::Dec\n\n//VTK::IOStructs::Dec\n\n[[stage(fragment)]]\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output: fragmentOutput;\n\n var computedColor: vec4<f32> = textureSample(volumePassSmallColorTexture,\n volumePassSmallColorTextureSampler, input.tcoordVS);\n\n //VTK::RenderEncoder::Impl\n return output;\n}\n";
35
37
  /* eslint-disable no-undef */
36
38
 
37
39
  /* eslint-disable no-bitwise */
@@ -39,7 +41,41 @@ var DepthBoundsFS = "\n//VTK::Renderer::Dec\n\n//VTK::Select::Dec\n\n//VTK::Volu
39
41
 
40
42
  function vtkWebGPUVolumePass(publicAPI, model) {
41
43
  // Set our className
42
- model.classHierarchy.push('vtkWebGPUVolumePass');
44
+ model.classHierarchy.push('vtkWebGPUVolumePass'); // create the required textures, encoders, FSQ etc
45
+
46
+ publicAPI.initialize = function (viewNode) {
47
+ if (!model._mergeEncoder) {
48
+ publicAPI.createMergeEncoder(viewNode);
49
+ }
50
+
51
+ if (!model._clearEncoder) {
52
+ publicAPI.createClearEncoder(viewNode);
53
+ }
54
+
55
+ if (!model._copyEncoder) {
56
+ publicAPI.createCopyEncoder(viewNode);
57
+ }
58
+
59
+ if (!model._depthRangeEncoder) {
60
+ publicAPI.createDepthRangeEncoder(viewNode);
61
+ }
62
+
63
+ if (!model.fullScreenQuad) {
64
+ model.fullScreenQuad = vtkWebGPUVolumePassFSQ.newInstance();
65
+ model.fullScreenQuad.setDevice(viewNode.getDevice());
66
+ model.fullScreenQuad.setTextureViews(_toConsumableArray(model._depthRangeEncoder.getColorTextureViews()));
67
+ }
68
+
69
+ if (!model._volumeCopyQuadQuad) {
70
+ model._volumeCopyQuad = vtkWebGPUFullScreenQuad.newInstance();
71
+
72
+ model._volumeCopyQuad.setPipelineHash('volpassfsq');
73
+
74
+ model._volumeCopyQuad.setDevice(viewNode.getDevice());
75
+
76
+ model._volumeCopyQuad.setFragmentShaderTemplate(volumeCopyFragTemplate);
77
+ }
78
+ };
43
79
 
44
80
  publicAPI.traverse = function (renNode, viewNode) {
45
81
  if (model.deleted) {
@@ -47,29 +83,25 @@ function vtkWebGPUVolumePass(publicAPI, model) {
47
83
  } // we just render our delegates in order
48
84
 
49
85
 
50
- model.currentParent = viewNode; // then perform the ray casting using the depth bounds texture
51
-
52
- if (!model.finalEncoder) {
53
- publicAPI.createFinalEncoder(viewNode);
54
- } // first render the boxes to generate a min max depth
55
- // map for all the volumes
86
+ model.currentParent = viewNode; // create stuff we need
56
87
 
88
+ publicAPI.initialize(viewNode); // determine if we are rendering a small size
57
89
 
58
- publicAPI.renderDepthBounds(renNode, viewNode);
90
+ publicAPI.computeTiming(viewNode); // first render the boxes to generate a min max depth
91
+ // map for all the volumes
59
92
 
60
- if (!model.fullScreenQuad) {
61
- model.fullScreenQuad = vtkWebGPUVolumePassFSQ.newInstance();
62
- model.fullScreenQuad.setDevice(viewNode.getDevice());
63
- model.fullScreenQuad.setTextureViews(_toConsumableArray(model.depthRangeEncoder.getColorTextureViews()));
64
- }
93
+ publicAPI.renderDepthBounds(renNode, viewNode); // always mark true
65
94
 
66
- var device = viewNode.getDevice(); // -4 because we use know we use textures for min, max, ofun and tfun
95
+ model._firstGroup = true;
96
+ var device = viewNode.getDevice(); // determine how many volumes we can render at a time. We subtract
97
+ // 4 because we use know we use textures for min, max, ofun and tfun
67
98
 
68
- var maxVolumes = device.getHandle().limits.maxSampledTexturesPerShaderStage - 4;
69
- var cameraPos = renNode.getRenderable().getActiveCamera().getPosition();
99
+ var maxVolumes = device.getHandle().limits.maxSampledTexturesPerShaderStage - 4; // if we have to make multiple passes then break the volumes up into groups
100
+ // rendered from farthest to closest
70
101
 
71
102
  if (model.volumes.length > maxVolumes) {
72
- // sort from back to front based on volume centroid
103
+ var cameraPos = renNode.getRenderable().getActiveCamera().getPosition(); // sort from back to front based on volume centroid
104
+
73
105
  var distances = [];
74
106
 
75
107
  for (var v = 0; v < model.volumes.length; v++) {
@@ -94,26 +126,116 @@ function vtkWebGPUVolumePass(publicAPI, model) {
94
126
  volumesToRender.push(model.volumes[volumeOrder[_v]]);
95
127
 
96
128
  if (volumesToRender.length >= chunkSize) {
97
- publicAPI.finalPass(viewNode, renNode, volumesToRender);
129
+ publicAPI.rayCastPass(viewNode, renNode, volumesToRender);
98
130
  volumesToRender = [];
99
131
  chunkSize = maxVolumes;
132
+ model._firstGroup = false;
100
133
  }
101
134
  }
102
135
  } else {
103
- publicAPI.finalPass(viewNode, renNode, model.volumes);
136
+ // if not rendering in chunks then just draw all of them at once
137
+ publicAPI.rayCastPass(viewNode, renNode, model.volumes);
138
+ } // copy back to the original color buffer
139
+
140
+
141
+ if (model.small) {
142
+ model._volumeCopyQuad.setTextureViews([model._smallColorTextureView]);
143
+ } else {
144
+ model._volumeCopyQuad.setTextureViews([model._largeColorTextureView]);
145
+ } // final composite
146
+
147
+
148
+ model._copyEncoder.setColorTextureView(0, model.colorTextureView);
149
+
150
+ model._copyEncoder.attachTextureViews();
151
+
152
+ renNode.setRenderEncoder(model._copyEncoder);
153
+
154
+ model._copyEncoder.begin(viewNode.getCommandEncoder());
155
+
156
+ renNode.scissorAndViewport(model._copyEncoder);
157
+
158
+ model._volumeCopyQuad.setWebGPURenderer(renNode);
159
+
160
+ model._volumeCopyQuad.render(model._copyEncoder, viewNode.getDevice());
161
+
162
+ model._copyEncoder.end();
163
+ }; // unsubscribe from our listeners
164
+
165
+
166
+ publicAPI.delete = macro.chain(function () {
167
+ if (model._animationRateSubscription) {
168
+ model._animationRateSubscription.unsubscribe();
169
+
170
+ model._animationRateSubscription = null;
171
+ }
172
+ }, publicAPI.delete);
173
+
174
+ publicAPI.computeTiming = function (viewNode) {
175
+ model.small = false;
176
+ var rwi = viewNode.getRenderable().getInteractor();
177
+
178
+ if (rwi.isAnimating() && model._lastScale > 1.5) {
179
+ model.small = true;
180
+ }
181
+
182
+ if (model.small) {
183
+ model._activeColorTextureView = model._smallColorTextureView;
184
+ } else {
185
+ model._largeColorTexture.resize(viewNode.getCanvas().width, viewNode.getCanvas().height);
186
+
187
+ model._activeColorTextureView = model._largeColorTextureView;
188
+ }
189
+
190
+ if (!model._animationRateSubscription) {
191
+ // when the animation frame rate changes recompute the scale factor
192
+ model._animationRateSubscription = rwi.onAnimationFrameRateUpdate(function () {
193
+ var frate = rwi.getRecentAnimationFrameRate();
194
+ var targetScale = model._lastScale * rwi.getDesiredUpdateRate() / frate; // Do we need a resize? The values below are to provide some stickyness
195
+ // so that we are not changing texture size every second. Instead the targhet scale
196
+ // has to be a certain amount larger or smaller than the current value to force a
197
+ // change in texture size
198
+
199
+ if (targetScale > 1.4 * model._lastScale || targetScale < 0.7 * model._lastScale) {
200
+ model._lastScale = targetScale; // clamp scale to some reasonable values.
201
+ // Below 1.5 we will just be using full resolution as that is close enough
202
+ // Above 400 seems like a lot so we limit to that 1/20th per axis
203
+
204
+ if (model._lastScale > 400) {
205
+ model._lastScale = 400;
206
+ }
207
+
208
+ if (model._lastScale < 1.5) {
209
+ model._lastScale = 1.5;
210
+ } else {
211
+ var targetSmallWidth = 64 * Math.ceil(viewNode.getCanvas().width / (Math.sqrt(model._lastScale) * 64));
212
+ var targetSmallHeight = Math.ceil(targetSmallWidth * viewNode.getCanvas().height / viewNode.getCanvas().width);
213
+
214
+ model._smallColorTexture.resize(targetSmallWidth, targetSmallHeight);
215
+ }
216
+ }
217
+ });
104
218
  }
105
219
  };
106
220
 
107
- publicAPI.finalPass = function (viewNode, renNode, volumes) {
108
- model.finalEncoder.setColorTextureView(0, model.colorTextureView);
109
- model.finalEncoder.attachTextureViews();
110
- renNode.setRenderEncoder(model.finalEncoder);
111
- model.finalEncoder.begin(viewNode.getCommandEncoder());
112
- renNode.scissorAndViewport(model.finalEncoder);
221
+ publicAPI.rayCastPass = function (viewNode, renNode, volumes) {
222
+ var encoder = model._firstGroup ? model._clearEncoder : model._mergeEncoder;
223
+ encoder.setColorTextureView(0, model._activeColorTextureView);
224
+ encoder.attachTextureViews();
225
+ renNode.setRenderEncoder(encoder);
226
+ encoder.begin(viewNode.getCommandEncoder());
227
+
228
+ var width = model._activeColorTextureView.getTexture().getWidth();
229
+
230
+ var height = model._activeColorTextureView.getTexture().getHeight();
231
+
232
+ encoder.getHandle().setViewport(0, 0, width, height, 0.0, 1.0); // set scissor
233
+
234
+ encoder.getHandle().setScissorRect(0, 0, width, height);
113
235
  model.fullScreenQuad.setWebGPURenderer(renNode);
114
236
  model.fullScreenQuad.setVolumes(volumes);
115
- model.fullScreenQuad.render(model.finalEncoder, viewNode.getDevice());
116
- model.finalEncoder.end();
237
+ model.fullScreenQuad.render(encoder, viewNode.getDevice());
238
+ encoder.end();
117
239
  };
118
240
 
119
241
  publicAPI.renderDepthBounds = function (renNode, viewNode) {
@@ -211,55 +333,32 @@ function vtkWebGPUVolumePass(publicAPI, model) {
211
333
  publicAPI.drawDepthRange = function (renNode, viewNode) {
212
334
  var device = viewNode.getDevice(); // copy current depth buffer to
213
335
 
214
- if (!model.depthRangeEncoder) {
215
- publicAPI.createDepthRangeEncoder();
216
- model.depthRangeTexture = vtkWebGPUTexture.newInstance();
217
- model.depthRangeTexture.create(device, {
218
- width: viewNode.getCanvas().width,
219
- height: viewNode.getCanvas().height,
220
- format: 'r16float',
221
- usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
222
- });
223
- var maxView = model.depthRangeTexture.createView();
224
- maxView.setName('maxTexture');
225
- model.depthRangeEncoder.setColorTextureView(0, maxView);
226
- model.depthRangeTexture2 = vtkWebGPUTexture.newInstance();
227
- model.depthRangeTexture2.create(device, {
228
- width: viewNode.getCanvas().width,
229
- height: viewNode.getCanvas().height,
230
- format: 'r16float',
231
- usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
232
- });
233
- var minView = model.depthRangeTexture2.createView();
234
- minView.setName('minTexture');
235
- model.depthRangeEncoder.setColorTextureView(1, minView);
336
+ model._depthRangeTexture.resizeToMatch(model.colorTextureView.getTexture());
236
337
 
237
- model._mapper.setDevice(viewNode.getDevice());
338
+ model._depthRangeTexture2.resizeToMatch(model.colorTextureView.getTexture());
238
339
 
239
- model._mapper.setTextureViews([model.depthTextureView]);
240
- } else {
241
- model.depthRangeTexture.resizeToMatch(model.colorTextureView.getTexture());
242
- model.depthRangeTexture2.resizeToMatch(model.colorTextureView.getTexture());
243
- }
340
+ model._depthRangeEncoder.attachTextureViews();
244
341
 
245
- model.depthRangeEncoder.attachTextureViews();
246
342
  publicAPI.setCurrentOperation('volumeDepthRangePass');
247
- renNode.setRenderEncoder(model.depthRangeEncoder);
343
+ renNode.setRenderEncoder(model._depthRangeEncoder);
248
344
  renNode.volumeDepthRangePass(true);
249
345
 
250
346
  model._mapper.setWebGPURenderer(renNode);
251
347
 
252
- model._mapper.build(model.depthRangeEncoder, device);
348
+ model._mapper.build(model._depthRangeEncoder, device);
253
349
 
254
350
  model._mapper.registerToDraw();
255
351
 
256
352
  renNode.volumeDepthRangePass(false);
257
353
  };
258
354
 
259
- publicAPI.createDepthRangeEncoder = function () {
260
- model.depthRangeEncoder = vtkWebGPURenderEncoder.newInstance();
261
- model.depthRangeEncoder.setPipelineHash('volr');
262
- model.depthRangeEncoder.setReplaceShaderCodeFunction(function (pipeline) {
355
+ publicAPI.createDepthRangeEncoder = function (viewNode) {
356
+ var device = viewNode.getDevice();
357
+ model._depthRangeEncoder = vtkWebGPURenderEncoder.newInstance();
358
+
359
+ model._depthRangeEncoder.setPipelineHash('volr');
360
+
361
+ model._depthRangeEncoder.setReplaceShaderCodeFunction(function (pipeline) {
263
362
  var fDesc = pipeline.getShaderDescription('fragment');
264
363
  fDesc.addOutput('vec4<f32>', 'outColor1');
265
364
  fDesc.addOutput('vec4<f32>', 'outColor2');
@@ -267,7 +366,8 @@ function vtkWebGPUVolumePass(publicAPI, model) {
267
366
  code = vtkWebGPUShaderCache.substitute(code, '//VTK::RenderEncoder::Impl', ['output.outColor1 = vec4<f32>(input.fragPos.z, 0.0, 0.0, 0.0);', 'output.outColor2 = vec4<f32>(stopval, 0.0, 0.0, 0.0);']).result;
268
367
  fDesc.setCode(code);
269
368
  });
270
- model.depthRangeEncoder.setDescription({
369
+
370
+ model._depthRangeEncoder.setDescription({
271
371
  colorAttachments: [{
272
372
  view: null,
273
373
  loadValue: [0.0, 0.0, 0.0, 0.0],
@@ -278,7 +378,8 @@ function vtkWebGPUVolumePass(publicAPI, model) {
278
378
  storeOp: 'store'
279
379
  }]
280
380
  });
281
- model.depthRangeEncoder.setPipelineSettings({
381
+
382
+ model._depthRangeEncoder.setPipelineSettings({
282
383
  primitive: {
283
384
  cullMode: 'none'
284
385
  },
@@ -313,27 +414,184 @@ function vtkWebGPUVolumePass(publicAPI, model) {
313
414
  }
314
415
  }]
315
416
  }
417
+ }); // and the textures it needs
418
+
419
+
420
+ model._depthRangeTexture = vtkWebGPUTexture.newInstance();
421
+
422
+ model._depthRangeTexture.create(device, {
423
+ width: viewNode.getCanvas().width,
424
+ height: viewNode.getCanvas().height,
425
+ format: 'r16float',
426
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
427
+ });
428
+
429
+ var maxView = model._depthRangeTexture.createView();
430
+
431
+ maxView.setName('maxTexture');
432
+
433
+ model._depthRangeEncoder.setColorTextureView(0, maxView);
434
+
435
+ model._depthRangeTexture2 = vtkWebGPUTexture.newInstance();
436
+
437
+ model._depthRangeTexture2.create(device, {
438
+ width: viewNode.getCanvas().width,
439
+ height: viewNode.getCanvas().height,
440
+ format: 'r16float',
441
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
442
+ });
443
+
444
+ var minView = model._depthRangeTexture2.createView();
445
+
446
+ minView.setName('minTexture');
447
+
448
+ model._depthRangeEncoder.setColorTextureView(1, minView);
449
+
450
+ model._mapper.setDevice(viewNode.getDevice());
451
+
452
+ model._mapper.setTextureViews([model.depthTextureView]);
453
+ };
454
+
455
+ publicAPI.createClearEncoder = function (viewNode) {
456
+ model._smallColorTexture = vtkWebGPUTexture.newInstance(); // multiple of 64 pixels to help with texture alignment/size issues
457
+ // as webgpu textures have to be multiples of 256 bytes wide
458
+ // for data transfers (just in case)
459
+
460
+ var targetSmallWidth = 64 * Math.ceil(0.7 * viewNode.getCanvas().width / 64);
461
+
462
+ model._smallColorTexture.create(viewNode.getDevice(), {
463
+ width: targetSmallWidth,
464
+ height: Math.ceil(targetSmallWidth * viewNode.getCanvas().height / viewNode.getCanvas().width),
465
+ format: 'bgra8unorm',
466
+
467
+ /* eslint-disable no-undef */
468
+
469
+ /* eslint-disable no-bitwise */
470
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC
471
+ });
472
+
473
+ model._smallColorTextureView = model._smallColorTexture.createView();
474
+
475
+ model._smallColorTextureView.setName('volumePassSmallColorTexture');
476
+
477
+ model._smallColorTextureView.addSampler(viewNode.getDevice(), {
478
+ minFilter: 'linear',
479
+ magFilter: 'linear'
480
+ });
481
+
482
+ model._largeColorTexture = vtkWebGPUTexture.newInstance();
483
+
484
+ model._largeColorTexture.create(viewNode.getDevice(), {
485
+ width: viewNode.getCanvas().width,
486
+ height: viewNode.getCanvas().height,
487
+ format: 'bgra8unorm',
488
+
489
+ /* eslint-disable no-undef */
490
+
491
+ /* eslint-disable no-bitwise */
492
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC
493
+ });
494
+
495
+ model._largeColorTextureView = model._largeColorTexture.createView();
496
+
497
+ model._largeColorTextureView.setName('volumePassSmallColorTexture');
498
+
499
+ model._largeColorTextureView.addSampler(viewNode.getDevice(), {
500
+ minFilter: 'linear',
501
+ magFilter: 'linear'
502
+ });
503
+
504
+ model._clearEncoder = vtkWebGPURenderEncoder.newInstance();
505
+
506
+ model._clearEncoder.setDescription({
507
+ colorAttachments: [{
508
+ view: null,
509
+ loadValue: [0.0, 0.0, 0.0, 0.0],
510
+ storeOp: 'store'
511
+ }]
512
+ });
513
+
514
+ model._clearEncoder.setPipelineHash('volpf');
515
+
516
+ model._clearEncoder.setPipelineSettings({
517
+ primitive: {
518
+ cullMode: 'none'
519
+ },
520
+ fragment: {
521
+ targets: [{
522
+ format: 'bgra8unorm',
523
+ blend: {
524
+ color: {
525
+ srcFactor: 'src-alpha',
526
+ dstFactor: 'one-minus-src-alpha'
527
+ },
528
+ alpha: {
529
+ srcfactor: 'one',
530
+ dstFactor: 'one-minus-src-alpha'
531
+ }
532
+ }
533
+ }]
534
+ }
316
535
  });
317
536
  };
318
537
 
319
- publicAPI.createFinalEncoder = function (viewNode) {
320
- model.finalEncoder = vtkWebGPURenderEncoder.newInstance();
321
- model.finalEncoder.setDescription({
538
+ publicAPI.createCopyEncoder = function (viewNode) {
539
+ model._copyEncoder = vtkWebGPURenderEncoder.newInstance();
540
+
541
+ model._copyEncoder.setDescription({
322
542
  colorAttachments: [{
323
543
  view: null,
324
544
  loadValue: 'load',
325
545
  storeOp: 'store'
326
546
  }]
327
547
  });
328
- model.finalEncoder.setReplaceShaderCodeFunction(function (pipeline) {
548
+
549
+ model._copyEncoder.setPipelineHash('volcopypf');
550
+
551
+ model._copyEncoder.setPipelineSettings({
552
+ primitive: {
553
+ cullMode: 'none'
554
+ },
555
+ fragment: {
556
+ targets: [{
557
+ format: 'bgra8unorm',
558
+ blend: {
559
+ color: {
560
+ srcFactor: 'one',
561
+ dstFactor: 'one-minus-src-alpha'
562
+ },
563
+ alpha: {
564
+ srcfactor: 'one',
565
+ dstFactor: 'one-minus-src-alpha'
566
+ }
567
+ }
568
+ }]
569
+ }
570
+ });
571
+ };
572
+
573
+ publicAPI.createMergeEncoder = function (viewNode) {
574
+ model._mergeEncoder = vtkWebGPURenderEncoder.newInstance();
575
+
576
+ model._mergeEncoder.setDescription({
577
+ colorAttachments: [{
578
+ view: null,
579
+ loadValue: 'load',
580
+ storeOp: 'store'
581
+ }]
582
+ });
583
+
584
+ model._mergeEncoder.setReplaceShaderCodeFunction(function (pipeline) {
329
585
  var fDesc = pipeline.getShaderDescription('fragment');
330
586
  fDesc.addOutput('vec4<f32>', 'outColor');
331
587
  var code = fDesc.getCode();
332
- code = vtkWebGPUShaderCache.substitute(code, '//VTK::RenderEncoder::Impl', ['output.outColor = vec4<f32>(computedColor.rgb*computedColor.a, computedColor.a);']).result;
588
+ code = vtkWebGPUShaderCache.substitute(code, '//VTK::RenderEncoder::Impl', ['output.outColor = vec4<f32>(computedColor.rgb, computedColor.a);']).result;
333
589
  fDesc.setCode(code);
334
590
  });
335
- model.finalEncoder.setPipelineHash('volpf');
336
- model.finalEncoder.setPipelineSettings({
591
+
592
+ model._mergeEncoder.setPipelineHash('volpf');
593
+
594
+ model._mergeEncoder.setPipelineSettings({
337
595
  primitive: {
338
596
  cullMode: 'none'
339
597
  },
@@ -342,7 +600,7 @@ function vtkWebGPUVolumePass(publicAPI, model) {
342
600
  format: 'bgra8unorm',
343
601
  blend: {
344
602
  color: {
345
- srcFactor: 'one',
603
+ srcFactor: 'src-alpha',
346
604
  dstFactor: 'one-minus-src-alpha'
347
605
  },
348
606
  alpha: {
@@ -387,6 +645,7 @@ function extend(publicAPI, model) {
387
645
  Object.assign(model, DEFAULT_VALUES, initialValues); // Build VTK API
388
646
 
389
647
  vtkRenderPass.extend(publicAPI, model, initialValues);
648
+ model._lastScale = 2.0;
390
649
  model._mapper = vtkWebGPUMapperHelper.newInstance();
391
650
 
392
651
  model._mapper.setFragmentShaderTemplate(DepthBoundsFS);
@@ -9,7 +9,7 @@ import vtkWebGPUSampler from './Sampler.js';
9
9
  import vtkWebGPUTypes from './Types.js';
10
10
  import { BlendMode } from '../Core/VolumeMapper/Constants.js';
11
11
 
12
- var volFragTemplate = "\n//VTK::Renderer::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::TCoord::Dec\n\n//VTK::Volume::TraverseDec\n\n//VTK::RenderEncoder::Dec\n\n//VTK::IOStructs::Dec\n\nfn getTextureValue(vTex: texture_3d<f32>, tpos: vec4<f32>) -> f32\n{\n // todo multicomponent support\n return textureSampleLevel(vTex, clampSampler, tpos.xyz, 0.0).r;\n}\n\nfn getGradient(vTex: texture_3d<f32>, tpos: vec4<f32>, vNum: i32, scalar: f32) -> vec4<f32>\n{\n var result: vec4<f32>;\n\n var tstep: vec4<f32> = volumeSSBO.values[vNum].tstep;\n result.x = getTextureValue(vTex, tpos + vec4<f32>(tstep.x, 0.0, 0.0, 1.0)) - scalar;\n result.y = getTextureValue(vTex, tpos + vec4<f32>(0.0, tstep.y, 0.0, 1.0)) - scalar;\n result.z = getTextureValue(vTex, tpos + vec4<f32>(0.0, 0.0, tstep.z, 1.0)) - scalar;\n\n // divide by spacing\n result = result / volumeSSBO.values[vNum].spacing;\n\n var grad: f32 = length(result.xyz);\n\n // // rotate to View Coords, needed for lighting and shading\n // result.xyz =\n // result.x * vPlaneNormal0 +\n // result.y * vPlaneNormal2 +\n // result.z * vPlaneNormal4;\n\n if (grad > 0.0)\n {\n result = result * (1.0 / grad);\n }\n\n result.w = grad;\n\n return result;\n}\n\nfn processVolume(vTex: texture_3d<f32>, vNum: i32, cNum: i32, posSC: vec4<f32>, tfunRows: f32) -> vec4<f32>\n{\n var outColor: vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 0.0);\n\n // convert to tcoords and reject if outside the volume\n var tpos: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*posSC;\n if (tpos.x < 0.0 || tpos.y < 0.0 || tpos.z < 0.0 ||\n tpos.x > 1.0 || tpos.y > 1.0 || tpos.z > 1.0) { return outColor; }\n\n var scalar: f32 = getTextureValue(vTex, tpos);\n\n var coord: vec2<f32> =\n vec2<f32>(scalar * componentSSBO.values[cNum].cScale + componentSSBO.values[cNum].cShift,\n (0.5 + 2.0 * f32(vNum)) / tfunRows);\n var color: vec4<f32> = textureSampleLevel(tfunTexture, clampSampler, coord, 0.0);\n\n var gofactor: f32 = 1.0;\n if (componentSSBO.values[cNum].gomin < 1.0)\n {\n var normal: vec4<f32> = getGradient(vTex, tpos, vNum, scalar);\n gofactor = clamp(normal.a*componentSSBO.values[cNum].goScale + componentSSBO.values[cNum].goShift,\n componentSSBO.values[cNum].gomin, componentSSBO.values[cNum].gomax);\n }\n\n coord.x = (scalar * componentSSBO.values[cNum].oScale + componentSSBO.values[cNum].oShift);\n var opacity: f32 = textureSampleLevel(ofunTexture, clampSampler, coord, 0.0).r;\n\n outColor = vec4<f32>(color.rgb, gofactor * opacity);\n\n//VTK::Volume::Process\n\n return outColor;\n}\n\n// adjust the start and end point of a raycast such that it intersects the unit cube.\n// This function is used to take a raycast starting point and step vector\n// and numSteps and return the startijng and ending steps for intersecting the\n// unit cube. Recall for a 3D texture, the unit cube is the range of texture coordsinates\n// that have valid values. So this funtion can be used to take a ray in texture coordinates\n// and bound it to intersecting the texture.\n//\nfn adjustBounds(tpos: vec4<f32>, tstep: vec4<f32>, numSteps: f32) -> vec2<f32>\n{\n var result: vec2<f32> = vec2<f32>(0.0, numSteps);\n var tpos2: vec4<f32> = tpos + tstep*numSteps;\n\n // move tpos to the start of the volume\n var adjust: f32 =\n min(\n max(tpos.x/tstep.x, (tpos.x - 1.0)/tstep.x),\n min(\n max((tpos.y - 1.0)/tstep.y, tpos.y/tstep.y),\n max((tpos.z - 1.0)/tstep.z, tpos.z/tstep.z)));\n if (adjust < 0.0)\n {\n result.x = result.x - adjust;\n }\n\n // adjust length to the end\n adjust =\n max(\n min(tpos2.x/tstep.x, (tpos2.x - 1.0)/tstep.x),\n max(\n min((tpos2.y - 1.0)/tstep.y, tpos2.y/tstep.y),\n min((tpos2.z - 1.0)/tstep.z, tpos2.z/tstep.z)));\n if (adjust > 0.0)\n {\n result.y = result.y - adjust;\n }\n\n return result;\n}\n\nfn getSimpleColor(scalar: f32, vNum: i32, cNum: i32) -> vec4<f32>\n{\n // how many rows (tfuns) do we have in our tfunTexture\n var tfunRows: f32 = f32(textureDimensions(tfunTexture).y);\n\n var coord: vec2<f32> =\n vec2<f32>(scalar * componentSSBO.values[cNum].cScale + componentSSBO.values[cNum].cShift,\n (0.5 + 2.0 * f32(vNum)) / tfunRows);\n var color: vec4<f32> = textureSampleLevel(tfunTexture, clampSampler, coord, 0.0);\n coord.x = (scalar * componentSSBO.values[cNum].oScale + componentSSBO.values[cNum].oShift);\n var opacity: f32 = textureSampleLevel(ofunTexture, clampSampler, coord, 0.0).r;\n return vec4<f32>(color.rgb, opacity);\n}\n\nfn traverseMax(vTex: texture_3d<f32>, vNum: i32, cNum: i32, rayLengthSC: f32, minPosSC: vec4<f32>, rayStepSC: vec4<f32>)\n{\n // convert to tcoords and reject if outside the volume\n var numSteps: f32 = rayLengthSC/mapperUBO.SampleDistance;\n var tpos: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*minPosSC;\n var tpos2: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*(minPosSC + rayStepSC);\n var tstep: vec4<f32> = tpos2 - tpos;\n\n var rayBounds: vec2<f32> = adjustBounds(tpos, tstep, numSteps);\n\n // did we hit anything\n if (rayBounds.x >= rayBounds.y)\n {\n traverseVals[vNum] = vec4<f32>(0.0,0.0,0.0,0.0);\n return;\n }\n\n tpos = tpos + tstep*rayBounds.x;\n var curDist: f32 = rayBounds.x;\n var maxVal: f32 = -1.0e37;\n loop\n {\n var scalar: f32 = getTextureValue(vTex, tpos);\n if (scalar > maxVal)\n {\n maxVal = scalar;\n }\n\n // increment position\n curDist = curDist + 1.0;\n tpos = tpos + tstep;\n\n // check if we have reached a terminating condition\n if (curDist > rayBounds.y) { break; }\n }\n\n // process to get the color and opacity\n traverseVals[vNum] = getSimpleColor(maxVal, vNum, cNum);\n}\n\nfn traverseMin(vTex: texture_3d<f32>, vNum: i32, cNum: i32, rayLengthSC: f32, minPosSC: vec4<f32>, rayStepSC: vec4<f32>)\n{\n // convert to tcoords and reject if outside the volume\n var numSteps: f32 = rayLengthSC/mapperUBO.SampleDistance;\n var tpos: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*minPosSC;\n var tpos2: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*(minPosSC + rayStepSC);\n var tstep: vec4<f32> = tpos2 - tpos;\n\n var rayBounds: vec2<f32> = adjustBounds(tpos, tstep, numSteps);\n\n // did we hit anything\n if (rayBounds.x >= rayBounds.y)\n {\n traverseVals[vNum] = vec4<f32>(0.0,0.0,0.0,0.0);\n return;\n }\n\n tpos = tpos + tstep*rayBounds.x;\n var curDist: f32 = rayBounds.x;\n var minVal: f32 = 1.0e37;\n loop\n {\n var scalar: f32 = getTextureValue(vTex, tpos);\n if (scalar < minVal)\n {\n minVal = scalar;\n }\n\n // increment position\n curDist = curDist + 1.0;\n tpos = tpos + tstep;\n\n // check if we have reached a terminating condition\n if (curDist > rayBounds.y) { break; }\n }\n\n // process to get the color and opacity\n traverseVals[vNum] = getSimpleColor(minVal, vNum, cNum);\n}\n\nfn traverseAverage(vTex: texture_3d<f32>, vNum: i32, cNum: i32, rayLengthSC: f32, minPosSC: vec4<f32>, rayStepSC: vec4<f32>)\n{\n // convert to tcoords and reject if outside the volume\n var numSteps: f32 = rayLengthSC/mapperUBO.SampleDistance;\n var tpos: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*minPosSC;\n var tpos2: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*(minPosSC + rayStepSC);\n var tstep: vec4<f32> = tpos2 - tpos;\n\n var rayBounds: vec2<f32> = adjustBounds(tpos, tstep, numSteps);\n\n // did we hit anything\n if (rayBounds.x >= rayBounds.y)\n {\n traverseVals[vNum] = vec4<f32>(0.0,0.0,0.0,0.0);\n return;\n }\n\n let ipRange: vec4<f32> = volumeSSBO.values[vNum].ipScalarRange;\n tpos = tpos + tstep*rayBounds.x;\n var curDist: f32 = rayBounds.x;\n var avgVal: f32 = 0.0;\n var sampleCount: f32 = 0.0;\n loop\n {\n var sample: f32 = getTextureValue(vTex, tpos);\n // right now leave filtering off until WebGL changes get merged\n // if (ipRange.z == 0.0 || sample >= ipRange.x && sample <= ipRange.y)\n // {\n avgVal = avgVal + sample;\n sampleCount = sampleCount + 1.0;\n // }\n\n // increment position\n curDist = curDist + 1.0;\n tpos = tpos + tstep;\n\n // check if we have reached a terminating condition\n if (curDist > rayBounds.y) { break; }\n }\n\n if (sampleCount <= 0.0)\n {\n traverseVals[vNum] = vec4<f32>(0.0,0.0,0.0,0.0);\n }\n\n // process to get the color and opacity\n traverseVals[vNum] = getSimpleColor(avgVal/sampleCount, vNum, cNum);\n}\n\nfn traverseAdditive(vTex: texture_3d<f32>, vNum: i32, cNum: i32, rayLengthSC: f32, minPosSC: vec4<f32>, rayStepSC: vec4<f32>)\n{\n // convert to tcoords and reject if outside the volume\n var numSteps: f32 = rayLengthSC/mapperUBO.SampleDistance;\n var tpos: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*minPosSC;\n var tpos2: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*(minPosSC + rayStepSC);\n var tstep: vec4<f32> = tpos2 - tpos;\n\n var rayBounds: vec2<f32> = adjustBounds(tpos, tstep, numSteps);\n\n // did we hit anything\n if (rayBounds.x >= rayBounds.y)\n {\n traverseVals[vNum] = vec4<f32>(0.0,0.0,0.0,0.0);\n return;\n }\n\n let ipRange: vec4<f32> = volumeSSBO.values[vNum].ipScalarRange;\n tpos = tpos + tstep*rayBounds.x;\n var curDist: f32 = rayBounds.x;\n var sumVal: f32 = 0.0;\n loop\n {\n var sample: f32 = getTextureValue(vTex, tpos);\n // right now leave filtering off until WebGL changes get merged\n // if (ipRange.z == 0.0 || sample >= ipRange.x && sample <= ipRange.y)\n // {\n sumVal = sumVal + sample;\n // }\n\n // increment position\n curDist = curDist + 1.0;\n tpos = tpos + tstep;\n\n // check if we have reached a terminating condition\n if (curDist > rayBounds.y) { break; }\n }\n\n // process to get the color and opacity\n traverseVals[vNum] = getSimpleColor(sumVal, vNum, cNum);\n}\n\nfn composite(rayLengthSC: f32, minPosSC: vec4<f32>, rayStepSC: vec4<f32>) -> vec4<f32>\n{\n // initial ray position is at the beginning\n var rayPosSC: vec4<f32> = minPosSC;\n\n // how many rows (tfuns) do we have in our tfunTexture\n var tfunRows: f32 = f32(textureDimensions(tfunTexture).y);\n\n var curDist: f32 = 0.0;\n var computedColor: vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 0.0);\n var sampleColor: vec4<f32>;\n//VTK::Volume::TraverseCalls\n\n loop\n {\n // for each volume, sample and accumulate color\n//VTK::Volume::CompositeCalls\n\n // increment position\n curDist = curDist + mapperUBO.SampleDistance;\n rayPosSC = rayPosSC + rayStepSC;\n\n // check if we have reached a terminating condition\n if (curDist > rayLengthSC) { break; }\n if (computedColor.a > 0.98) { break; }\n }\n return computedColor;\n}\n\n[[stage(fragment)]]\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output: fragmentOutput;\n\n var rayMax: f32 = textureSampleLevel(maxTexture, clampSampler, input.tcoordVS, 0.0).r;\n var rayMin: f32 = textureSampleLevel(minTexture, clampSampler, input.tcoordVS, 0.0).r;\n\n // discard empty rays\n if (rayMax <= rayMin) { discard; }\n else\n {\n var winDimsI32: vec2<i32> = textureDimensions(minTexture);\n var winDims: vec2<f32> = vec2<f32>(f32(winDimsI32.x), f32(winDimsI32.y));\n\n // compute start and end ray positions in view coordinates\n var minPosSC: vec4<f32> = rendererUBO.PCSCMatrix*vec4<f32>(2.0*input.fragPos.x/winDims.x - 1.0, 1.0 - 2.0 * input.fragPos.y/winDims.y, rayMax, 1.0);\n minPosSC = minPosSC * (1.0 / minPosSC.w);\n var maxPosSC: vec4<f32> = rendererUBO.PCSCMatrix*vec4<f32>(2.0*input.fragPos.x/winDims.x - 1.0, 1.0 - 2.0 * input.fragPos.y/winDims.y, rayMin, 1.0);\n maxPosSC = maxPosSC * (1.0 / maxPosSC.w);\n\n var rayLengthSC: f32 = distance(minPosSC.xyz, maxPosSC.xyz);\n var rayStepSC: vec4<f32> = (maxPosSC - minPosSC)*(mapperUBO.SampleDistance/rayLengthSC);\n rayStepSC.w = 0.0;\n\n var computedColor: vec4<f32>;\n\n//VTK::Volume::Loop\n\n//VTK::RenderEncoder::Impl\n }\n\n return output;\n}\n";
12
+ var volFragTemplate = "\n//VTK::Renderer::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::TCoord::Dec\n\n//VTK::Volume::TraverseDec\n\n//VTK::RenderEncoder::Dec\n\n//VTK::IOStructs::Dec\n\nfn getTextureValue(vTex: texture_3d<f32>, tpos: vec4<f32>) -> f32\n{\n // todo multicomponent support\n return textureSampleLevel(vTex, clampSampler, tpos.xyz, 0.0).r;\n}\n\nfn getGradient(vTex: texture_3d<f32>, tpos: vec4<f32>, vNum: i32, scalar: f32) -> vec4<f32>\n{\n var result: vec4<f32>;\n\n var tstep: vec4<f32> = volumeSSBO.values[vNum].tstep;\n result.x = getTextureValue(vTex, tpos + vec4<f32>(tstep.x, 0.0, 0.0, 1.0)) - scalar;\n result.y = getTextureValue(vTex, tpos + vec4<f32>(0.0, tstep.y, 0.0, 1.0)) - scalar;\n result.z = getTextureValue(vTex, tpos + vec4<f32>(0.0, 0.0, tstep.z, 1.0)) - scalar;\n\n // divide by spacing\n result = result / volumeSSBO.values[vNum].spacing;\n\n var grad: f32 = length(result.xyz);\n\n // // rotate to View Coords, needed for lighting and shading\n // result.xyz =\n // result.x * vPlaneNormal0 +\n // result.y * vPlaneNormal2 +\n // result.z * vPlaneNormal4;\n\n if (grad > 0.0)\n {\n result = result * (1.0 / grad);\n }\n\n result.w = grad;\n\n return result;\n}\n\nfn processVolume(vTex: texture_3d<f32>, vNum: i32, cNum: i32, posSC: vec4<f32>, tfunRows: f32) -> vec4<f32>\n{\n var outColor: vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 0.0);\n\n // convert to tcoords and reject if outside the volume\n var tpos: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*posSC;\n if (tpos.x < 0.0 || tpos.y < 0.0 || tpos.z < 0.0 ||\n tpos.x > 1.0 || tpos.y > 1.0 || tpos.z > 1.0) { return outColor; }\n\n var scalar: f32 = getTextureValue(vTex, tpos);\n\n var coord: vec2<f32> =\n vec2<f32>(scalar * componentSSBO.values[cNum].cScale + componentSSBO.values[cNum].cShift,\n (0.5 + 2.0 * f32(vNum)) / tfunRows);\n var color: vec4<f32> = textureSampleLevel(tfunTexture, clampSampler, coord, 0.0);\n\n var gofactor: f32 = 1.0;\n if (componentSSBO.values[cNum].gomin < 1.0)\n {\n var normal: vec4<f32> = getGradient(vTex, tpos, vNum, scalar);\n gofactor = clamp(normal.a*componentSSBO.values[cNum].goScale + componentSSBO.values[cNum].goShift,\n componentSSBO.values[cNum].gomin, componentSSBO.values[cNum].gomax);\n }\n\n coord.x = (scalar * componentSSBO.values[cNum].oScale + componentSSBO.values[cNum].oShift);\n var opacity: f32 = textureSampleLevel(ofunTexture, clampSampler, coord, 0.0).r;\n\n outColor = vec4<f32>(color.rgb, gofactor * opacity);\n\n//VTK::Volume::Process\n\n return outColor;\n}\n\n// adjust the start and end point of a raycast such that it intersects the unit cube.\n// This function is used to take a raycast starting point and step vector\n// and numSteps and return the startijng and ending steps for intersecting the\n// unit cube. Recall for a 3D texture, the unit cube is the range of texture coordsinates\n// that have valid values. So this funtion can be used to take a ray in texture coordinates\n// and bound it to intersecting the texture.\n//\nfn adjustBounds(tpos: vec4<f32>, tstep: vec4<f32>, numSteps: f32) -> vec2<f32>\n{\n var result: vec2<f32> = vec2<f32>(0.0, numSteps);\n var tpos2: vec4<f32> = tpos + tstep*numSteps;\n\n // move tpos to the start of the volume\n var adjust: f32 =\n min(\n max(tpos.x/tstep.x, (tpos.x - 1.0)/tstep.x),\n min(\n max((tpos.y - 1.0)/tstep.y, tpos.y/tstep.y),\n max((tpos.z - 1.0)/tstep.z, tpos.z/tstep.z)));\n if (adjust < 0.0)\n {\n result.x = result.x - adjust;\n }\n\n // adjust length to the end\n adjust =\n max(\n min(tpos2.x/tstep.x, (tpos2.x - 1.0)/tstep.x),\n max(\n min((tpos2.y - 1.0)/tstep.y, tpos2.y/tstep.y),\n min((tpos2.z - 1.0)/tstep.z, tpos2.z/tstep.z)));\n if (adjust > 0.0)\n {\n result.y = result.y - adjust;\n }\n\n return result;\n}\n\nfn getSimpleColor(scalar: f32, vNum: i32, cNum: i32) -> vec4<f32>\n{\n // how many rows (tfuns) do we have in our tfunTexture\n var tfunRows: f32 = f32(textureDimensions(tfunTexture).y);\n\n var coord: vec2<f32> =\n vec2<f32>(scalar * componentSSBO.values[cNum].cScale + componentSSBO.values[cNum].cShift,\n (0.5 + 2.0 * f32(vNum)) / tfunRows);\n var color: vec4<f32> = textureSampleLevel(tfunTexture, clampSampler, coord, 0.0);\n coord.x = (scalar * componentSSBO.values[cNum].oScale + componentSSBO.values[cNum].oShift);\n var opacity: f32 = textureSampleLevel(ofunTexture, clampSampler, coord, 0.0).r;\n return vec4<f32>(color.rgb, opacity);\n}\n\nfn traverseMax(vTex: texture_3d<f32>, vNum: i32, cNum: i32, rayLengthSC: f32, minPosSC: vec4<f32>, rayStepSC: vec4<f32>)\n{\n // convert to tcoords and reject if outside the volume\n var numSteps: f32 = rayLengthSC/mapperUBO.SampleDistance;\n var tpos: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*minPosSC;\n var tpos2: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*(minPosSC + rayStepSC);\n var tstep: vec4<f32> = tpos2 - tpos;\n\n var rayBounds: vec2<f32> = adjustBounds(tpos, tstep, numSteps);\n\n // did we hit anything\n if (rayBounds.x >= rayBounds.y)\n {\n traverseVals[vNum] = vec4<f32>(0.0,0.0,0.0,0.0);\n return;\n }\n\n tpos = tpos + tstep*rayBounds.x;\n var curDist: f32 = rayBounds.x;\n var maxVal: f32 = -1.0e37;\n loop\n {\n var scalar: f32 = getTextureValue(vTex, tpos);\n if (scalar > maxVal)\n {\n maxVal = scalar;\n }\n\n // increment position\n curDist = curDist + 1.0;\n tpos = tpos + tstep;\n\n // check if we have reached a terminating condition\n if (curDist > rayBounds.y) { break; }\n }\n\n // process to get the color and opacity\n traverseVals[vNum] = getSimpleColor(maxVal, vNum, cNum);\n}\n\nfn traverseMin(vTex: texture_3d<f32>, vNum: i32, cNum: i32, rayLengthSC: f32, minPosSC: vec4<f32>, rayStepSC: vec4<f32>)\n{\n // convert to tcoords and reject if outside the volume\n var numSteps: f32 = rayLengthSC/mapperUBO.SampleDistance;\n var tpos: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*minPosSC;\n var tpos2: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*(minPosSC + rayStepSC);\n var tstep: vec4<f32> = tpos2 - tpos;\n\n var rayBounds: vec2<f32> = adjustBounds(tpos, tstep, numSteps);\n\n // did we hit anything\n if (rayBounds.x >= rayBounds.y)\n {\n traverseVals[vNum] = vec4<f32>(0.0,0.0,0.0,0.0);\n return;\n }\n\n tpos = tpos + tstep*rayBounds.x;\n var curDist: f32 = rayBounds.x;\n var minVal: f32 = 1.0e37;\n loop\n {\n var scalar: f32 = getTextureValue(vTex, tpos);\n if (scalar < minVal)\n {\n minVal = scalar;\n }\n\n // increment position\n curDist = curDist + 1.0;\n tpos = tpos + tstep;\n\n // check if we have reached a terminating condition\n if (curDist > rayBounds.y) { break; }\n }\n\n // process to get the color and opacity\n traverseVals[vNum] = getSimpleColor(minVal, vNum, cNum);\n}\n\nfn traverseAverage(vTex: texture_3d<f32>, vNum: i32, cNum: i32, rayLengthSC: f32, minPosSC: vec4<f32>, rayStepSC: vec4<f32>)\n{\n // convert to tcoords and reject if outside the volume\n var numSteps: f32 = rayLengthSC/mapperUBO.SampleDistance;\n var tpos: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*minPosSC;\n var tpos2: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*(minPosSC + rayStepSC);\n var tstep: vec4<f32> = tpos2 - tpos;\n\n var rayBounds: vec2<f32> = adjustBounds(tpos, tstep, numSteps);\n\n // did we hit anything\n if (rayBounds.x >= rayBounds.y)\n {\n traverseVals[vNum] = vec4<f32>(0.0,0.0,0.0,0.0);\n return;\n }\n\n let ipRange: vec4<f32> = volumeSSBO.values[vNum].ipScalarRange;\n tpos = tpos + tstep*rayBounds.x;\n var curDist: f32 = rayBounds.x;\n var avgVal: f32 = 0.0;\n var sampleCount: f32 = 0.0;\n loop\n {\n var sample: f32 = getTextureValue(vTex, tpos);\n // right now leave filtering off until WebGL changes get merged\n // if (ipRange.z == 0.0 || sample >= ipRange.x && sample <= ipRange.y)\n // {\n avgVal = avgVal + sample;\n sampleCount = sampleCount + 1.0;\n // }\n\n // increment position\n curDist = curDist + 1.0;\n tpos = tpos + tstep;\n\n // check if we have reached a terminating condition\n if (curDist > rayBounds.y) { break; }\n }\n\n if (sampleCount <= 0.0)\n {\n traverseVals[vNum] = vec4<f32>(0.0,0.0,0.0,0.0);\n }\n\n // process to get the color and opacity\n traverseVals[vNum] = getSimpleColor(avgVal/sampleCount, vNum, cNum);\n}\n\nfn traverseAdditive(vTex: texture_3d<f32>, vNum: i32, cNum: i32, rayLengthSC: f32, minPosSC: vec4<f32>, rayStepSC: vec4<f32>)\n{\n // convert to tcoords and reject if outside the volume\n var numSteps: f32 = rayLengthSC/mapperUBO.SampleDistance;\n var tpos: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*minPosSC;\n var tpos2: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*(minPosSC + rayStepSC);\n var tstep: vec4<f32> = tpos2 - tpos;\n\n var rayBounds: vec2<f32> = adjustBounds(tpos, tstep, numSteps);\n\n // did we hit anything\n if (rayBounds.x >= rayBounds.y)\n {\n traverseVals[vNum] = vec4<f32>(0.0,0.0,0.0,0.0);\n return;\n }\n\n let ipRange: vec4<f32> = volumeSSBO.values[vNum].ipScalarRange;\n tpos = tpos + tstep*rayBounds.x;\n var curDist: f32 = rayBounds.x;\n var sumVal: f32 = 0.0;\n loop\n {\n var sample: f32 = getTextureValue(vTex, tpos);\n // right now leave filtering off until WebGL changes get merged\n // if (ipRange.z == 0.0 || sample >= ipRange.x && sample <= ipRange.y)\n // {\n sumVal = sumVal + sample;\n // }\n\n // increment position\n curDist = curDist + 1.0;\n tpos = tpos + tstep;\n\n // check if we have reached a terminating condition\n if (curDist > rayBounds.y) { break; }\n }\n\n // process to get the color and opacity\n traverseVals[vNum] = getSimpleColor(sumVal, vNum, cNum);\n}\n\nfn composite(rayLengthSC: f32, minPosSC: vec4<f32>, rayStepSC: vec4<f32>) -> vec4<f32>\n{\n // initial ray position is at the beginning\n var rayPosSC: vec4<f32> = minPosSC;\n\n // how many rows (tfuns) do we have in our tfunTexture\n var tfunRows: f32 = f32(textureDimensions(tfunTexture).y);\n\n var curDist: f32 = 0.0;\n var computedColor: vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 0.0);\n var sampleColor: vec4<f32>;\n//VTK::Volume::TraverseCalls\n\n loop\n {\n // for each volume, sample and accumulate color\n//VTK::Volume::CompositeCalls\n\n // increment position\n curDist = curDist + mapperUBO.SampleDistance;\n rayPosSC = rayPosSC + rayStepSC;\n\n // check if we have reached a terminating condition\n if (curDist > rayLengthSC) { break; }\n if (computedColor.a > 0.98) { break; }\n }\n return computedColor;\n}\n\n[[stage(fragment)]]\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output: fragmentOutput;\n\n var rayMax: f32 = textureSampleLevel(maxTexture, clampSampler, input.tcoordVS, 0.0).r;\n var rayMin: f32 = textureSampleLevel(minTexture, clampSampler, input.tcoordVS, 0.0).r;\n\n // discard empty rays\n if (rayMax <= rayMin) { discard; }\n else\n {\n // compute start and end ray positions in view coordinates\n var minPosSC: vec4<f32> = rendererUBO.PCSCMatrix*vec4<f32>(2.0 * input.tcoordVS.x - 1.0, 1.0 - 2.0 * input.tcoordVS.y, rayMax, 1.0);\n minPosSC = minPosSC * (1.0 / minPosSC.w);\n var maxPosSC: vec4<f32> = rendererUBO.PCSCMatrix*vec4<f32>(2.0 * input.tcoordVS.x - 1.0, 1.0 - 2.0 * input.tcoordVS.y, rayMin, 1.0);\n maxPosSC = maxPosSC * (1.0 / maxPosSC.w);\n\n var rayLengthSC: f32 = distance(minPosSC.xyz, maxPosSC.xyz);\n var rayStepSC: vec4<f32> = (maxPosSC - minPosSC)*(mapperUBO.SampleDistance/rayLengthSC);\n rayStepSC.w = 0.0;\n\n var computedColor: vec4<f32>;\n\n//VTK::Volume::Loop\n\n//VTK::RenderEncoder::Impl\n }\n\n return output;\n}\n";
13
13
  var tmpMat4 = new Float64Array(16);
14
14
  var tmp2Mat4 = new Float64Array(16); // ----------------------------------------------------------------------------
15
15
  // vtkWebGPUVolumePassFSQ methods
package/macros.js CHANGED
@@ -702,12 +702,7 @@ function algo(publicAPI, model, numberOfInputs, numberOfOutputs) {
702
702
  return model.inputConnection[port];
703
703
  }
704
704
 
705
- function addInputConnection(outputPort) {
706
- if (model.deleted) {
707
- vtkErrorMacro('instance deleted - cannot call any method');
708
- return;
709
- }
710
-
705
+ function getPortToFill() {
711
706
  var portToFill = model.numberOfInputs;
712
707
 
713
708
  while (portToFill && !model.inputData[portToFill - 1] && !model.inputConnection[portToFill - 1]) {
@@ -718,26 +713,25 @@ function algo(publicAPI, model, numberOfInputs, numberOfOutputs) {
718
713
  model.numberOfInputs++;
719
714
  }
720
715
 
721
- setInputConnection(outputPort, portToFill);
716
+ return portToFill;
722
717
  }
723
718
 
724
- function addInputData(dataset) {
719
+ function addInputConnection(outputPort) {
725
720
  if (model.deleted) {
726
721
  vtkErrorMacro('instance deleted - cannot call any method');
727
722
  return;
728
723
  }
729
724
 
730
- var portToFill = model.numberOfInputs;
731
-
732
- while (portToFill && !model.inputData[portToFill - 1] && !model.inputConnection[portToFill - 1]) {
733
- portToFill--;
734
- }
725
+ setInputConnection(outputPort, getPortToFill());
726
+ }
735
727
 
736
- if (portToFill === model.numberOfInputs) {
737
- model.numberOfInputs++;
728
+ function addInputData(dataset) {
729
+ if (model.deleted) {
730
+ vtkErrorMacro('instance deleted - cannot call any method');
731
+ return;
738
732
  }
739
733
 
740
- setInputData(dataset, portToFill);
734
+ setInputData(dataset, getPortToFill());
741
735
  }
742
736
 
743
737
  function getOutputData() {
@@ -757,15 +751,11 @@ function algo(publicAPI, model, numberOfInputs, numberOfOutputs) {
757
751
 
758
752
  publicAPI.shouldUpdate = function () {
759
753
  var localMTime = publicAPI.getMTime();
760
- var count = numberOfOutputs;
761
754
  var minOutputMTime = Infinity;
755
+ var count = numberOfOutputs;
762
756
 
763
757
  while (count--) {
764
- if (!model.output[count]) {
765
- return true;
766
- }
767
-
768
- if (model.output[count].isDeleted()) {
758
+ if (!model.output[count] || model.output[count].isDeleted()) {
769
759
  return true;
770
760
  }
771
761
 
@@ -783,15 +773,9 @@ function algo(publicAPI, model, numberOfInputs, numberOfOutputs) {
783
773
  count = model.numberOfInputs;
784
774
 
785
775
  while (count--) {
786
- if (model.inputConnection[count] && model.inputConnection[count].filter.shouldUpdate()) {
787
- return true;
788
- }
789
- }
776
+ var _model$inputConnectio, _publicAPI$getInputDa;
790
777
 
791
- count = model.numberOfInputs;
792
-
793
- while (count--) {
794
- if (publicAPI.getInputData(count) && publicAPI.getInputData(count).getMTime() > minOutputMTime) {
778
+ if ((_model$inputConnectio = model.inputConnection[count]) !== null && _model$inputConnectio !== void 0 && _model$inputConnectio.filter.shouldUpdate() || ((_publicAPI$getInputDa = publicAPI.getInputData(count)) === null || _publicAPI$getInputDa === void 0 ? void 0 : _publicAPI$getInputDa.getMTime()) > minOutputMTime) {
795
779
  return true;
796
780
  }
797
781
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kitware/vtk.js",
3
- "version": "22.0.0",
3
+ "version": "22.1.1",
4
4
  "description": "Visualization Toolkit for the Web",
5
5
  "keywords": [
6
6
  "3d",
@@ -115,7 +115,6 @@
115
115
  "semantic-release": "18.0.1",
116
116
  "string-replace-loader": "3.1.0",
117
117
  "style-loader": "3.3.1",
118
- "tap-markdown": "1.2.1",
119
118
  "tap-spec": "5.0.0",
120
119
  "tape": "5.4.0",
121
120
  "tape-catch": "1.0.6",