1 /*=========================================================================
2
3 Program: Visualization Toolkit
4 Module: vtkGPUVolumeRayCastMapper.cxx
5
6 Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7 All rights reserved.
8 See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9
10 This software is distributed WITHOUT ANY WARRANTY; without even
11 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12 PURPOSE. See the above copyright notice for more information.
13
14 =========================================================================*/
15 #include "vtkGPUVolumeRayCastMapper.h"
16
17 #include "vtkObjectFactory.h"
18 #include "vtkImageData.h"
19 #include "vtkPointData.h"
20 #include "vtkCellData.h"
21 #include "vtkDataArray.h"
22 #include "vtkTimerLog.h"
23 #include "vtkImageResample.h"
24 #include "vtkVolume.h"
25 #include "vtkVolumeProperty.h"
26 #include "vtkRenderer.h"
27 #include "vtkRenderWindow.h"
28 #include <cassert>
29 #include "vtkCommand.h" // for VolumeMapperRender{Start|End|Progress}Event
30 #include "vtkCamera.h"
31 #include "vtkRendererCollection.h"
32 #include "vtkMultiThreader.h"
33 #include "vtkGPUInfoList.h"
34 #include "vtkGPUInfo.h"
35
36 // Return NULL if no override is supplied.
37 vtkAbstractObjectFactoryNewMacro(vtkGPUVolumeRayCastMapper)
38 vtkCxxSetObjectMacro(vtkGPUVolumeRayCastMapper, MaskInput, vtkImageData);
39 vtkCxxSetObjectMacro(vtkGPUVolumeRayCastMapper, TransformedInput, vtkImageData);
40
vtkGPUVolumeRayCastMapper()41 vtkGPUVolumeRayCastMapper::vtkGPUVolumeRayCastMapper()
42 {
43 this->AutoAdjustSampleDistances = 1;
44 this->ImageSampleDistance = 1.0;
45 this->MinimumImageSampleDistance = 1.0;
46 this->MaximumImageSampleDistance = 10.0;
47 this->SampleDistance = 1.0;
48 this->SmallVolumeRender = 0;
49 this->BigTimeToDraw = 0.0;
50 this->SmallTimeToDraw = 0.0;
51 this->FinalColorWindow = 1.0;
52 this->FinalColorLevel = 0.5;
53 this->GeneratingCanonicalView = 0;
54 this->CanonicalViewImageData = NULL;
55
56 this->MaskInput = NULL;
57 this->MaskBlendFactor = 1.0f;
58 this->MaskType
59 = vtkGPUVolumeRayCastMapper::LabelMapMaskType;
60
61 this->AMRMode=0;
62 this->ClippedCroppingRegionPlanes[0]=VTK_DOUBLE_MAX;
63 this->ClippedCroppingRegionPlanes[1]=VTK_DOUBLE_MIN;
64 this->ClippedCroppingRegionPlanes[2]=VTK_DOUBLE_MAX;
65 this->ClippedCroppingRegionPlanes[3]=VTK_DOUBLE_MIN;
66 this->ClippedCroppingRegionPlanes[4]=VTK_DOUBLE_MAX;
67 this->ClippedCroppingRegionPlanes[5]=VTK_DOUBLE_MIN;
68
69 this->MaxMemoryInBytes=0;
70 vtkGPUInfoList *l=vtkGPUInfoList::New();
71 l->Probe();
72 if(l->GetNumberOfGPUs()>0)
73 {
74 vtkGPUInfo *info=l->GetGPUInfo(0);
75 this->MaxMemoryInBytes=info->GetDedicatedVideoMemory();
76 if(this->MaxMemoryInBytes==0)
77 {
78 this->MaxMemoryInBytes=info->GetDedicatedSystemMemory();
79 }
80 // we ignore info->GetSharedSystemMemory(); as this is very slow.
81 }
82 l->Delete();
83
84 if(this->MaxMemoryInBytes==0) // use some default value: 128MB.
85 {
86 this->MaxMemoryInBytes=128*1024*1024;
87 }
88
89 this->MaxMemoryFraction = 0.75;
90
91 this->ReportProgress=true;
92
93 this->TransformedInput = NULL;
94 this->LastInput = NULL;
95 }
96
97 // ----------------------------------------------------------------------------
~vtkGPUVolumeRayCastMapper()98 vtkGPUVolumeRayCastMapper::~vtkGPUVolumeRayCastMapper()
99 {
100 this->SetMaskInput(NULL);
101 this->SetTransformedInput(NULL);
102 this->LastInput = NULL;
103 }
104
105 // ----------------------------------------------------------------------------
106 // The render method that is called from the volume. If this is a canonical
107 // view render, a specialized version of this method will be called instead.
108 // Otherwise we will
109 // - Invoke a start event
110 // - Start timing
111 // - Check that everything is OK for rendering
112 // - Render
113 // - Stop the timer and record results
114 // - Invoke an end event
115 // ----------------------------------------------------------------------------
Render(vtkRenderer * ren,vtkVolume * vol)116 void vtkGPUVolumeRayCastMapper::Render( vtkRenderer *ren, vtkVolume *vol )
117 {
118 // Catch renders that are happening due to a canonical view render and
119 // handle them separately.
120 if (this->GeneratingCanonicalView )
121 {
122 this->CanonicalViewRender(ren, vol);
123 return;
124 }
125
126 // Invoke a VolumeMapperRenderStartEvent
127 this->InvokeEvent(vtkCommand::VolumeMapperRenderStartEvent,0);
128
129 // Start the timer to time the length of this render
130 vtkTimerLog *timer = vtkTimerLog::New();
131 timer->StartTimer();
132
133 // Make sure everything about this render is OK.
134 // This is where the input is updated.
135 if ( this->ValidateRender(ren, vol ) )
136 {
137 // Everything is OK - so go ahead and really do the render
138 this->GPURender( ren, vol);
139 }
140
141 // Stop the timer
142 timer->StopTimer();
143 double t = timer->GetElapsedTime();
144
145 // cout << "Render Timer " << t << " seconds, " << 1.0/t << " frames per second" << endl;
146
147 this->TimeToDraw = t;
148 timer->Delete();
149
150 if ( vol->GetAllocatedRenderTime() < 1.0 )
151 {
152 this->SmallTimeToDraw = t;
153 }
154 else
155 {
156 this->BigTimeToDraw = t;
157 }
158
159 // Invoke a VolumeMapperRenderEndEvent
160 this->InvokeEvent(vtkCommand::VolumeMapperRenderEndEvent,0);
161 }
162
163 // ----------------------------------------------------------------------------
164 // Special version for rendering a canonical view - we don't do things like
165 // invoke start or end events, and we don't capture the render time.
166 // ----------------------------------------------------------------------------
CanonicalViewRender(vtkRenderer * ren,vtkVolume * vol)167 void vtkGPUVolumeRayCastMapper::CanonicalViewRender(vtkRenderer *ren,
168 vtkVolume *vol )
169 {
170 // Make sure everything about this render is OK
171 if ( this->ValidateRender(ren, vol ) )
172 {
173 // Everything is OK - so go ahead and really do the render
174 this->GPURender( ren, vol);
175 }
176 }
177
178 // ----------------------------------------------------------------------------
179 // This method us used by the render method to validate everything before
180 // attempting to render. This method returns 0 if something is not right -
181 // such as missing input, a null renderer or a null volume, no scalars, etc.
182 // In some cases it will produce a vtkErrorMacro message, and in others
183 // (for example, in the case of cropping planes that define a region with
184 // a volume or 0 or less) it will fail silently. If everything is OK, it will
185 // return with a value of 1.
186 // ----------------------------------------------------------------------------
ValidateRender(vtkRenderer * ren,vtkVolume * vol)187 int vtkGPUVolumeRayCastMapper::ValidateRender(vtkRenderer *ren,
188 vtkVolume *vol)
189 {
190 // Check that we have everything we need to render.
191 int goodSoFar = 1;
192
193 // Check for a renderer - we MUST have one
194 if ( !ren )
195 {
196 goodSoFar = 0;
197 vtkErrorMacro("Renderer cannot be null.");
198 }
199
200 // Check for the volume - we MUST have one
201 if ( goodSoFar && !vol )
202 {
203 goodSoFar = 0;
204 vtkErrorMacro("Volume cannot be null.");
205 }
206
207 // Don't need to check if we have a volume property
208 // since the volume will create one if we don't. Also
209 // don't need to check for the scalar opacity function
210 // or the RGB transfer function since the property will
211 // create them if they do not yet exist.
212
213 // However we must currently check that the number of
214 // color channels is 3
215 // TODO: lift this restriction - should work with
216 // gray functions as well. Right now turning off test
217 // because otherwise 4 component rendering isn't working.
218 // Will revisit.
219 if ( goodSoFar && vol->GetProperty()->GetColorChannels() != 3 )
220 {
221 // goodSoFar = 0;
222 // vtkErrorMacro("Must have a color transfer function.");
223 }
224
225 // Check the cropping planes. If they are invalid, just silently
226 // fail. This will happen when an interactive widget is dragged
227 // such that it defines 0 or negative volume - this can happen
228 // and should just not render the volume.
229 // Check the cropping planes
230 if( goodSoFar && this->Cropping &&
231 (this->CroppingRegionPlanes[0]>=this->CroppingRegionPlanes[1] ||
232 this->CroppingRegionPlanes[2]>=this->CroppingRegionPlanes[3] ||
233 this->CroppingRegionPlanes[4]>=this->CroppingRegionPlanes[5] ))
234 {
235 // No error message here - we want to be silent
236 goodSoFar = 0;
237 }
238
239 // Check that we have input data
240 vtkImageData *input=this->GetInput();
241
242 if(goodSoFar && input==0)
243 {
244 vtkErrorMacro("Input is NULL but is required");
245 goodSoFar = 0;
246 }
247
248 if(goodSoFar)
249 {
250 this->GetInputAlgorithm()->Update();
251 }
252
253 // If we have a timestamp change or data change then create a new clone.
254 if(goodSoFar && (input != this->LastInput ||
255 input->GetMTime() > this->TransformedInput->GetMTime()))
256 {
257 this->LastInput = input;
258
259 vtkImageData* clone;
260 if(!this->TransformedInput)
261 {
262 clone = vtkImageData::New();
263 this->SetTransformedInput(clone);
264 clone->Delete();
265 }
266 else
267 {
268 clone = this->TransformedInput;
269 }
270
271 clone->ShallowCopy(input);
272
273 // @TODO: This is the workaround to deal with GPUVolumeRayCastMapper
274 // not able to handle extents starting from non zero values.
275 // There is not a easy fix in the GPU volume ray cast mapper hence
276 // this fix has been introduced.
277
278 // Get the current extents.
279 int extents[6], real_extents[6];
280 clone->GetExtent(extents);
281 clone->GetExtent(real_extents);
282
283 // Get the current origin and spacing.
284 double origin[3], spacing[3];
285 clone->GetOrigin(origin);
286 clone->GetSpacing(spacing);
287
288 for (int cc=0; cc < 3; cc++)
289 {
290 // Transform the origin and the extents.
291 origin[cc] = origin[cc] + extents[2*cc]*spacing[cc];
292 extents[2*cc+1] -= extents[2*cc];
293 extents[2*cc] -= extents[2*cc];
294 }
295
296 clone->SetOrigin(origin);
297 clone->SetExtent(extents);
298 }
299
300 // Update the date then make sure we have scalars. Note
301 // that we must have point or cell scalars because field
302 // scalars are not supported.
303 vtkDataArray *scalars = NULL;
304 if ( goodSoFar )
305 {
306 // Now make sure we can find scalars
307 scalars=this->GetScalars(this->TransformedInput,this->ScalarMode,
308 this->ArrayAccessMode,
309 this->ArrayId,
310 this->ArrayName,
311 this->CellFlag);
312
313 // We couldn't find scalars
314 if ( !scalars )
315 {
316 vtkErrorMacro("No scalars found on input.");
317 goodSoFar = 0;
318 }
319 // Even if we found scalars, if they are field data scalars that isn't good
320 else if ( this->CellFlag == 2 )
321 {
322 vtkErrorMacro("Only point or cell scalar support - found field scalars instead.");
323 goodSoFar = 0;
324 }
325 }
326
327 // Make sure the scalar type is actually supported. This mappers supports
328 // almost all standard scalar types.
329 if ( goodSoFar )
330 {
331 switch(scalars->GetDataType())
332 {
333 case VTK_CHAR:
334 vtkErrorMacro(<< "scalar of type VTK_CHAR is not supported "
335 << "because this type is platform dependent. "
336 << "Use VTK_SIGNED_CHAR or VTK_UNSIGNED_CHAR instead.");
337 goodSoFar = 0;
338 break;
339 case VTK_BIT:
340 vtkErrorMacro("scalar of type VTK_BIT is not supported by this mapper.");
341 goodSoFar = 0;
342 break;
343 case VTK_ID_TYPE:
344 vtkErrorMacro("scalar of type VTK_ID_TYPE is not supported by this mapper.");
345 goodSoFar = 0;
346 break;
347 case VTK_STRING:
348 vtkErrorMacro("scalar of type VTK_STRING is not supported by this mapper.");
349 goodSoFar = 0;
350 break;
351 default:
352 // Don't need to do anything here
353 break;
354 }
355 }
356
357 // Check on the blending type - we support composite and min / max intensity
358 if ( goodSoFar )
359 {
360 if(this->BlendMode!=vtkVolumeMapper::COMPOSITE_BLEND &&
361 this->BlendMode!=vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND &&
362 this->BlendMode!=vtkVolumeMapper::MINIMUM_INTENSITY_BLEND &&
363 this->BlendMode!=vtkVolumeMapper::ADDITIVE_BLEND)
364 {
365 goodSoFar = 0;
366 vtkErrorMacro(<< "Selected blend mode not supported. "
367 << "Only Composite, MIP, MinIP and additive modes "
368 << "are supported by the current implementation.");
369 }
370 }
371
372 // This mapper supports 1 component data, or 4 component if it is not independent
373 // component (i.e. the four components define RGBA)
374 int numberOfComponents = 0;
375 if ( goodSoFar )
376 {
377 numberOfComponents = scalars->GetNumberOfComponents();
378 if( !(numberOfComponents == 1 ||
379 numberOfComponents == 4) )
380 {
381 goodSoFar = 0;
382 vtkErrorMacro(<< "Only one component scalars, or four "
383 << "component with non-independent components, "
384 << "are supported by this mapper.");
385 }
386 }
387
388 // If this is four component data, then it better be unsigned char (RGBA).
389 if( goodSoFar &&
390 numberOfComponents == 4 &&
391 scalars->GetDataType() != VTK_UNSIGNED_CHAR)
392 {
393 goodSoFar = 0;
394 vtkErrorMacro("Only unsigned char is supported for 4-component scalars!");
395 }
396
397 if(goodSoFar && numberOfComponents!=1 &&
398 this->BlendMode==vtkVolumeMapper::ADDITIVE_BLEND)
399 {
400 goodSoFar=0;
401 vtkErrorMacro("Additive mode only works with 1-component scalars!");
402 }
403
404 // return our status
405 return goodSoFar;
406 }
407
408 // ----------------------------------------------------------------------------
409 // Description:
410 // Called by the AMR Volume Mapper.
411 // Set the flag that tells if the scalars are on point data (0) or
412 // cell data (1).
SetCellFlag(int cellFlag)413 void vtkGPUVolumeRayCastMapper::SetCellFlag(int cellFlag)
414 {
415 this->CellFlag=cellFlag;
416 }
417
418 // ----------------------------------------------------------------------------
CreateCanonicalView(vtkRenderer * ren,vtkVolume * volume,vtkImageData * image,int vtkNotUsed (blend_mode),double viewDirection[3],double viewUp[3])419 void vtkGPUVolumeRayCastMapper::CreateCanonicalView(
420 vtkRenderer *ren,
421 vtkVolume *volume,
422 vtkImageData *image,
423 int vtkNotUsed(blend_mode),
424 double viewDirection[3],
425 double viewUp[3])
426 {
427 this->GeneratingCanonicalView = 1;
428 int oldSwap = ren->GetRenderWindow()->GetSwapBuffers();
429 ren->GetRenderWindow()->SwapBuffersOff();
430
431 int dim[3];
432 image->GetDimensions(dim);
433 int *size = ren->GetRenderWindow()->GetSize();
434
435 vtkImageData *bigImage = vtkImageData::New();
436 bigImage->SetDimensions(size[0], size[1], 1);
437 bigImage->AllocateScalars(VTK_UNSIGNED_CHAR, 3);
438
439 this->CanonicalViewImageData = bigImage;
440
441 double scale[2];
442 scale[0] = dim[0] / static_cast<double>(size[0]);
443 scale[1] = dim[1] / static_cast<double>(size[1]);
444
445 // Save the visibility flags of the renderers and set all to false except
446 // for the ren.
447 vtkRendererCollection *renderers=ren->GetRenderWindow()->GetRenderers();
448 int numberOfRenderers=renderers->GetNumberOfItems();
449
450 bool *rendererVisibilities=new bool[numberOfRenderers];
451 renderers->InitTraversal();
452 int i=0;
453 while(i<numberOfRenderers)
454 {
455 vtkRenderer *r=renderers->GetNextItem();
456 rendererVisibilities[i]=r->GetDraw()==1;
457 if(r!=ren)
458 {
459 r->SetDraw(false);
460 }
461 ++i;
462 }
463
464 // Save the visibility flags of the props and set all to false except
465 // for the volume.
466
467 vtkPropCollection *props=ren->GetViewProps();
468 int numberOfProps=props->GetNumberOfItems();
469
470 bool *propVisibilities=new bool[numberOfProps];
471 props->InitTraversal();
472 i=0;
473 while(i<numberOfProps)
474 {
475 vtkProp *p=props->GetNextProp();
476 propVisibilities[i]=p->GetVisibility()==1;
477 if(p!=volume)
478 {
479 p->SetVisibility(false);
480 }
481 ++i;
482 }
483
484 vtkCamera *savedCamera=ren->GetActiveCamera();
485 savedCamera->Modified();
486 vtkCamera *canonicalViewCamera=vtkCamera::New();
487
488 // Code from vtkFixedPointVolumeRayCastMapper:
489 double *center=volume->GetCenter();
490 double bounds[6];
491 volume->GetBounds(bounds);
492 double d=sqrt((bounds[1]-bounds[0])*(bounds[1]-bounds[0]) +
493 (bounds[3]-bounds[2])*(bounds[3]-bounds[2]) +
494 (bounds[5]-bounds[4])*(bounds[5]-bounds[4]));
495
496 // For now use x distance - need to change this
497 d=bounds[1]-bounds[0];
498
499 // Set up the camera in parallel
500 canonicalViewCamera->SetFocalPoint(center);
501 canonicalViewCamera->ParallelProjectionOn();
502 canonicalViewCamera->SetPosition(center[0] - d*viewDirection[0],
503 center[1] - d*viewDirection[1],
504 center[2] - d*viewDirection[2]);
505 canonicalViewCamera->SetViewUp(viewUp);
506 canonicalViewCamera->SetParallelScale(d/2);
507
508 ren->SetActiveCamera(canonicalViewCamera);
509 ren->GetRenderWindow()->Render();
510
511 ren->SetActiveCamera(savedCamera);
512 canonicalViewCamera->Delete();
513
514 // Shrink to image to the desired size
515 vtkImageResample *resample = vtkImageResample::New();
516 resample->SetInputData( bigImage );
517 resample->SetAxisMagnificationFactor(0,scale[0]);
518 resample->SetAxisMagnificationFactor(1,scale[1]);
519 resample->SetAxisMagnificationFactor(2,1);
520 resample->UpdateWholeExtent();
521
522 // Copy the pixels over
523 image->DeepCopy(resample->GetOutput());
524
525 bigImage->Delete();
526 resample->Delete();
527
528 // Restore the visibility flags of the props
529 props->InitTraversal();
530 i=0;
531 while(i<numberOfProps)
532 {
533 vtkProp *p=props->GetNextProp();
534 p->SetVisibility(propVisibilities[i]);
535 ++i;
536 }
537
538 delete[] propVisibilities;
539
540 // Restore the visibility flags of the renderers
541 renderers->InitTraversal();
542 i=0;
543 while(i<numberOfRenderers)
544 {
545 vtkRenderer *r=renderers->GetNextItem();
546 r->SetDraw(rendererVisibilities[i]);
547 ++i;
548 }
549
550 delete[] rendererVisibilities;
551
552 ren->GetRenderWindow()->SetSwapBuffers(oldSwap);
553 this->CanonicalViewImageData = NULL;
554 this->GeneratingCanonicalView = 0;
555 }
556
557 // ----------------------------------------------------------------------------
558 // Print method for vtkGPUVolumeRayCastMapper
PrintSelf(ostream & os,vtkIndent indent)559 void vtkGPUVolumeRayCastMapper::PrintSelf(ostream& os, vtkIndent indent)
560 {
561 this->Superclass::PrintSelf(os,indent);
562
563 os << indent << "AutoAdjustSampleDistances: "
564 << this->AutoAdjustSampleDistances << endl;
565 os << indent << "MinimumImageSampleDistance: "
566 << this->MinimumImageSampleDistance << endl;
567 os << indent << "MaximumImageSampleDistance: "
568 << this->MaximumImageSampleDistance << endl;
569 os << indent << "ImageSampleDistance: " << this->ImageSampleDistance << endl;
570 os << indent << "SampleDistance: " << this->SampleDistance << endl;
571 os << indent << "FinalColorWindow: " << this->FinalColorWindow << endl;
572 os << indent << "FinalColorLevel: " << this->FinalColorLevel << endl;
573 os << indent << "MaskInput: " << this->MaskInput << endl;
574 os << indent << "MaskType: " << this->MaskType << endl;
575 os << indent << "MaskBlendFactor: " << this->MaskBlendFactor << endl;
576 os << indent << "MaxMemoryInBytes: " << this->MaxMemoryInBytes << endl;
577 os << indent << "MaxMemoryFraction: " << this->MaxMemoryFraction << endl;
578 os << indent << "ReportProgress: " << this->ReportProgress << endl;
579 }
580
581 // ----------------------------------------------------------------------------
582 // Description:
583 // Compute the cropping planes clipped by the bounds of the volume.
584 // The result is put into this->ClippedCroppingRegionPlanes.
585 // NOTE: IT WILL BE MOVED UP TO vtkVolumeMapper after bullet proof usage
586 // in this mapper. Other subclasses will use the ClippedCroppingRegionsPlanes
587 // members instead of CroppingRegionPlanes.
588 // \pre volume_exists: this->GetInput()!=0
589 // \pre valid_cropping: this->Cropping &&
590 // this->CroppingRegionPlanes[0]<this->CroppingRegionPlanes[1] &&
591 // this->CroppingRegionPlanes[2]<this->CroppingRegionPlanes[3] &&
592 // this->CroppingRegionPlanes[4]<this->CroppingRegionPlanes[5])
ClipCroppingRegionPlanes()593 void vtkGPUVolumeRayCastMapper::ClipCroppingRegionPlanes()
594 {
595 assert("pre: volume_exists" && this->GetInput()!=0);
596 assert("pre: valid_cropping" && this->Cropping &&
597 this->CroppingRegionPlanes[0]<this->CroppingRegionPlanes[1] &&
598 this->CroppingRegionPlanes[2]<this->CroppingRegionPlanes[3] &&
599 this->CroppingRegionPlanes[4]<this->CroppingRegionPlanes[5]);
600
601 // vtkVolumeMapper::Render() will have something like:
602 // if(this->Cropping && (this->CroppingRegionPlanes[0]>=this->CroppingRegionPlanes[1] ||
603 // this->CroppingRegionPlanes[2]>=this->CroppingRegionPlanes[3] ||
604 // this->CroppingRegionPlanes[4]>=this->CroppingRegionPlanes[5]))
605 // {
606 // // silentely stop because the cropping is not valid.
607 // return;
608 // }
609
610 double volBounds[6];
611 this->GetInput()->GetBounds(volBounds);
612
613 int i=0;
614 while(i<6)
615 {
616 // max of the mins
617 if(this->CroppingRegionPlanes[i]<volBounds[i])
618 {
619 this->ClippedCroppingRegionPlanes[i]=volBounds[i];
620 }
621 else
622 {
623 this->ClippedCroppingRegionPlanes[i]=this->CroppingRegionPlanes[i];
624 }
625 ++i;
626 // min of the maxs
627 if(this->CroppingRegionPlanes[i]>volBounds[i])
628 {
629 this->ClippedCroppingRegionPlanes[i]=volBounds[i];
630 }
631 else
632 {
633 this->ClippedCroppingRegionPlanes[i]=this->CroppingRegionPlanes[i];
634 }
635 ++i;
636 }
637 }
638
639 //----------------------------------------------------------------------------
SetMaskTypeToBinary()640 void vtkGPUVolumeRayCastMapper::SetMaskTypeToBinary()
641 {
642 this->MaskType = vtkGPUVolumeRayCastMapper::BinaryMaskType;
643 }
644
645 //----------------------------------------------------------------------------
SetMaskTypeToLabelMap()646 void vtkGPUVolumeRayCastMapper::SetMaskTypeToLabelMap()
647 {
648 this->MaskType = vtkGPUVolumeRayCastMapper::LabelMapMaskType;
649 }
650