source: vtkvis/trunk/Volume.cpp @ 5835

Last change on this file since 5835 was 5835, checked in by ldelgass, 8 years ago

Require VTK >= 6.0.0

  • Property svn:eol-style set to native
File size: 12.3 KB
Line 
1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 * Copyright (C) 2004-2012  HUBzero Foundation, LLC
4 *
5 * Author: Leif Delgass <ldelgass@purdue.edu>
6 */
7
8#include <cassert>
9
10#include <vtkDataSet.h>
11#include <vtkPointData.h>
12#include <vtkImageData.h>
13#include <vtkProbeFilter.h>
14#include <vtkVolumeProperty.h>
15#include <vtkGPUVolumeRayCastMapper.h>
16#include <vtkVolumeTextureMapper3D.h>
17#include <vtkRectilinearGrid.h>
18#include <vtkStructuredGrid.h>
19#include <vtkUnstructuredGrid.h>
20#include <vtkPolyData.h>
21#include <vtkCellType.h>
22#include <vtkUnstructuredGridVolumeMapper.h>
23#include <vtkUnstructuredGridVolumeRayCastMapper.h>
24#include <vtkProjectedTetrahedraMapper.h>
25#include <vtkDataSetTriangleFilter.h>
26#include <vtkGaussianSplatter.h>
27
28#include "Volume.h"
29#include "Trace.h"
30
31using namespace VtkVis;
32
33Volume::Volume() :
34    GraphicsObject(),
35    _useUgridMapper(false),
36    _colorMap(NULL)
37{
38}
39
40Volume::~Volume()
41{
42#ifdef WANT_TRACE
43    if (_dataSet != NULL)
44        TRACE("Deleting Volume for %s", _dataSet->getName().c_str());
45    else
46        TRACE("Deleting Volume with NULL DataSet");
47#endif
48}
49
50/**
51 * \brief Create and initialize a VTK Prop to render the Volume
52 */
53void Volume::initProp()
54{
55    if (_prop == NULL) {
56        _prop = vtkSmartPointer<vtkVolume>::New();
57        getVolume()->GetProperty()->SetInterpolationTypeToLinear();
58    }
59}
60
61/**
62 * \brief Get the voxel dimensions (if the volume is not a
63 * uniform grid, unit spacing is returned)
64 */
65void Volume::getSpacing(double spacing[3])
66{
67    spacing[0] = spacing[1] = spacing[2] = 1.0;
68    if (_dataSet == NULL)
69        return;
70    vtkDataSet *ds = _dataSet->getVtkDataSet();
71    if (ds != NULL && vtkImageData::SafeDownCast(ds) != NULL) {
72        vtkImageData::SafeDownCast(ds)->GetSpacing(spacing);
73    } else if (ds != NULL && vtkVolumeMapper::SafeDownCast(_volumeMapper) != NULL) {
74        vtkImageData *imgData = vtkVolumeMapper::SafeDownCast(_volumeMapper)->GetInput();
75        if (imgData != NULL) {
76            imgData->GetSpacing(spacing);
77        }
78    }
79}
80
81/**
82 * \brief Get the average voxel dimension (if the volume is not a
83 * uniform grid, unit spacing is returned)
84 */
85double Volume::getAverageSpacing()
86{
87    double spacing[3];
88    getSpacing(spacing);
89    TRACE("Spacing: %g %g %g", spacing[0], spacing[1], spacing[2]);
90    return (spacing[0] + spacing[1] + spacing[2]) * 0.333;
91}
92
93/**
94 * \brief Internal method to set up pipeline after a state change
95 */
96void Volume::update()
97{
98    if (_dataSet == NULL)
99        return;
100
101    vtkDataSet *ds = _dataSet->getVtkDataSet();
102
103    if (_dataSet->is2D()) {
104        USER_ERROR("Volume rendering requires a 3D data set");
105        _dataSet = NULL;
106        return;
107    }
108
109    if (ds->GetPointData() == NULL ||
110        ds->GetPointData()->GetScalars() == NULL) {
111        USER_ERROR("No scalar field was found in the data set");
112        return;
113    }
114
115    if (vtkImageData::SafeDownCast(ds) != NULL) {
116        // Image data required for these mappers
117#ifdef USE_GPU_RAYCAST_MAPPER
118        _volumeMapper = vtkSmartPointer<vtkGPUVolumeRayCastMapper>::New();
119        vtkGPUVolumeRayCastMapper::SafeDownCast(_volumeMapper)->AutoAdjustSampleDistancesOff();
120#else
121        _volumeMapper = vtkSmartPointer<vtkVolumeTextureMapper3D>::New();
122#endif
123#ifdef USE_GPU_RAYCAST_MAPPER
124        vtkGPUVolumeRayCastMapper::SafeDownCast(_volumeMapper)->SetInputData(ds);
125#else
126        vtkVolumeTextureMapper3D::SafeDownCast(_volumeMapper)->SetInputData(ds);
127#endif
128        vtkVolumeMapper::SafeDownCast(_volumeMapper)->SetBlendModeToComposite();
129    } else if (_dataSet->isCloud()) {
130        // DataSet is a 3D point cloud
131        vtkSmartPointer<vtkGaussianSplatter> splatter = vtkSmartPointer<vtkGaussianSplatter>::New();
132        splatter->SetInputData(ds);
133        int dims[3];
134        dims[0] = dims[1] = dims[2] = 64;
135        if (vtkStructuredGrid::SafeDownCast(ds) != NULL) {
136            vtkStructuredGrid::SafeDownCast(ds)->GetDimensions(dims);
137        } else if (vtkRectilinearGrid::SafeDownCast(ds) != NULL) {
138            vtkRectilinearGrid::SafeDownCast(ds)->GetDimensions(dims);
139        }
140        TRACE("Generating volume with dims (%d,%d,%d) from %d points",
141              dims[0], dims[1], dims[2], ds->GetNumberOfPoints());
142        splatter->SetSampleDimensions(dims);
143        splatter->Update();
144        TRACE("Done generating volume");
145#ifdef USE_GPU_RAYCAST_MAPPER
146        _volumeMapper = vtkSmartPointer<vtkGPUVolumeRayCastMapper>::New();
147        vtkGPUVolumeRayCastMapper::SafeDownCast(_volumeMapper)->AutoAdjustSampleDistancesOff();
148#else
149        _volumeMapper = vtkSmartPointer<vtkVolumeTextureMapper3D>::New();
150#endif
151        _volumeMapper->SetInputConnection(splatter->GetOutputPort());
152        vtkVolumeMapper::SafeDownCast(_volumeMapper)->SetBlendModeToComposite();
153    } else if (vtkUnstructuredGrid::SafeDownCast(ds) == NULL || !_useUgridMapper) {
154        // (Slow) Resample using ProbeFilter
155        double bounds[6];
156        ds->GetBounds(bounds);
157        double xLen = bounds[1] - bounds[0];
158        double yLen = bounds[3] - bounds[2];
159        double zLen = bounds[5] - bounds[4];
160
161        int dims[3];
162        dims[0] = dims[1] = dims[2] = 64;
163        if (xLen == 0.0) dims[0] = 1;
164        if (yLen == 0.0) dims[1] = 1;
165        if (zLen == 0.0) dims[2] = 1;
166        if (vtkStructuredGrid::SafeDownCast(ds) != NULL) {
167            vtkStructuredGrid::SafeDownCast(ds)->GetDimensions(dims);
168        } else if (vtkRectilinearGrid::SafeDownCast(ds) != NULL) {
169            vtkRectilinearGrid::SafeDownCast(ds)->GetDimensions(dims);
170        }
171        TRACE("Generating volume with dims (%d,%d,%d) from %d points",
172              dims[0], dims[1], dims[2], ds->GetNumberOfPoints());
173
174        double xSpacing = (dims[0] == 1 ? 0.0 : xLen/((double)(dims[0]-1)));
175        double ySpacing = (dims[1] == 1 ? 0.0 : yLen/((double)(dims[1]-1)));
176        double zSpacing = (dims[2] == 1 ? 0.0 : zLen/((double)(dims[2]-1)));
177
178        vtkSmartPointer<vtkImageData> imageData = vtkSmartPointer<vtkImageData>::New();
179        imageData->SetDimensions(dims[0], dims[1], dims[2]);
180        imageData->SetOrigin(bounds[0], bounds[2], bounds[4]);
181        imageData->SetSpacing(xSpacing, ySpacing, zSpacing);
182
183        vtkSmartPointer<vtkProbeFilter> probe = vtkSmartPointer<vtkProbeFilter>::New();
184        probe->SetInputData(imageData);
185        probe->SetSourceData(ds);
186        probe->Update();
187
188        TRACE("Done generating volume");
189
190#ifdef USE_GPU_RAYCAST_MAPPER
191        _volumeMapper = vtkSmartPointer<vtkGPUVolumeRayCastMapper>::New();
192        vtkGPUVolumeRayCastMapper::SafeDownCast(_volumeMapper)->AutoAdjustSampleDistancesOff();
193#else
194        _volumeMapper = vtkSmartPointer<vtkVolumeTextureMapper3D>::New();
195#endif
196        _volumeMapper->SetInputConnection(probe->GetOutputPort());
197        vtkVolumeMapper::SafeDownCast(_volumeMapper)->SetBlendModeToComposite();
198    } else {
199        // Unstructured grid with cells (not a cloud)
200        vtkUnstructuredGrid *ugrid = vtkUnstructuredGrid::SafeDownCast(ds);
201        assert(ugrid != NULL);
202        // DataSet is unstructured grid
203        // Only works if cells are all tetrahedra
204        //_volumeMapper = vtkSmartPointer<vtkProjectedTetrahedraMapper>::New();
205        // Software raycast rendering - requires all tetrahedra
206        _volumeMapper = vtkSmartPointer<vtkUnstructuredGridVolumeRayCastMapper>::New();
207        if (ugrid->GetCellType(0) == VTK_TETRA &&
208            ugrid->IsHomogeneous()) {
209            vtkProjectedTetrahedraMapper::SafeDownCast(_volumeMapper)->SetInputData(ds);
210        } else {
211            // Decompose to tetrahedra
212            vtkSmartPointer<vtkDataSetTriangleFilter> filter =
213                vtkSmartPointer<vtkDataSetTriangleFilter>::New();
214            filter->SetInputData(ugrid);
215            filter->TetrahedraOnlyOn();
216            TRACE("Decomposing grid to tets");
217            filter->Update();
218            TRACE("Decomposing done");
219            _volumeMapper->SetInputConnection(filter->GetOutputPort());
220        }
221
222        vtkUnstructuredGridVolumeMapper::SafeDownCast(_volumeMapper)->
223            SetBlendModeToComposite();
224    }
225
226    TRACE("Using mapper type: %s", _volumeMapper->GetClassName());
227
228    initProp();
229
230    if (_colorMap == NULL) {
231        setColorMap(ColorMap::getVolumeDefault());
232    }
233
234    setSampleDistance(getAverageSpacing());
235
236    getVolume()->SetMapper(_volumeMapper);
237    _volumeMapper->Update();
238}
239
240void Volume::updateRanges(Renderer *renderer)
241{
242    GraphicsObject::updateRanges(renderer);
243
244    if (getVolume() != NULL) {
245        getVolume()->GetProperty()->SetColor(_colorMap->getColorTransferFunction(_dataRange));
246#ifdef USE_GPU_RAYCAST_MAPPER
247        getVolume()->GetProperty()->SetScalarOpacity(_colorMap->getOpacityTransferFunction(_dataRange, _opacity));
248#else
249        getVolume()->GetProperty()->SetScalarOpacity(_colorMap->getOpacityTransferFunction(_dataRange));
250#endif
251    }
252}
253
254void Volume::updateColorMap()
255{
256    setColorMap(_colorMap);
257}
258
259/**
260 * \brief Assign a color map (transfer function) to use in rendering the Volume
261 */
262void Volume::setColorMap(ColorMap *cmap)
263{
264    if (cmap == NULL)
265        return;
266
267    _colorMap = cmap;
268
269    if (getVolume() != NULL) {
270        getVolume()->GetProperty()->SetColor(_colorMap->getColorTransferFunction(_dataRange));
271#ifdef USE_GPU_RAYCAST_MAPPER
272        getVolume()->GetProperty()->SetScalarOpacity(_colorMap->getOpacityTransferFunction(_dataRange, _opacity));
273#else
274        getVolume()->GetProperty()->SetScalarOpacity(_colorMap->getOpacityTransferFunction(_dataRange));
275#endif
276    }
277}
278
279/**
280 * \brief Return the ColorMap assigned to this Volume
281 */
282ColorMap *Volume::getColorMap()
283{
284    return _colorMap;
285}
286
287/**
288 * \brief Set opacity scaling used to render the Volume
289 */
290void Volume::setOpacity(double opacity)
291{
292    _opacity = opacity;
293    // FIXME: There isn't really a good opacity scaling option that works
294    // across the different mappers/algorithms.  This only works with the
295    // 3D texture mapper, not the GPU raycast mapper
296    if (getVolume() != NULL) {
297#ifdef USE_GPU_RAYCAST_MAPPER
298        getVolume()->GetProperty()->SetScalarOpacity(_colorMap->getOpacityTransferFunction(_dataRange, opacity));
299#else
300        if (opacity < 1.0e-6)
301            opacity = 1.0e-6;
302        getVolume()->GetProperty()->SetScalarOpacityUnitDistance(1.0/opacity);
303#endif
304    }
305}
306
307/**
308 * \brief Set a group of world coordinate planes to clip rendering
309 *
310 * Passing NULL for planes will remove all cliping planes
311 */
312void Volume::setClippingPlanes(vtkPlaneCollection *planes)
313{
314    if (_volumeMapper != NULL) {
315        _volumeMapper->SetClippingPlanes(planes);
316    }
317}
318
319/**
320 * \brief Set distance between samples along rays
321 */
322void Volume::setSampleDistance(float d)
323{
324    TRACE("Sample distance: %g", d);
325    if (_volumeMapper != NULL &&
326#ifdef USE_GPU_RAYCAST_MAPPER
327        vtkGPUVolumeRayCastMapper::SafeDownCast(_volumeMapper) != NULL) {
328        vtkGPUVolumeRayCastMapper::SafeDownCast(_volumeMapper)->SetSampleDistance(d);
329#else
330        vtkVolumeTextureMapper3D::SafeDownCast(_volumeMapper) != NULL) {
331        vtkVolumeTextureMapper3D::SafeDownCast(_volumeMapper)->SetSampleDistance(d);
332#endif
333    }
334}
335
336/**
337 * \brief Set Volume renderer blending mode
338 */
339void Volume::setBlendMode(BlendMode mode)
340{
341    if (_volumeMapper == NULL)
342        return;
343
344    vtkVolumeMapper *mapper = vtkVolumeMapper::SafeDownCast(_volumeMapper);
345    if (mapper == NULL) {
346        vtkUnstructuredGridVolumeMapper *ugmapper = vtkUnstructuredGridVolumeMapper::SafeDownCast(_volumeMapper);
347        if (ugmapper == NULL) {
348            TRACE("Mapper does not support BlendMode");
349            return;
350        }
351        switch (mode) {
352        case BLEND_COMPOSITE:
353            ugmapper->SetBlendModeToComposite();
354            break;
355        case BLEND_MAX_INTENSITY:
356            ugmapper->SetBlendModeToMaximumIntensity();
357            break;
358        case BLEND_MIN_INTENSITY:
359        case BLEND_ADDITIVE:
360        default:
361            ERROR("Unknown BlendMode");
362        }
363    } else {
364        switch (mode) {
365        case BLEND_COMPOSITE:
366            mapper->SetBlendModeToComposite();
367            break;
368        case BLEND_MAX_INTENSITY:
369            mapper->SetBlendModeToMaximumIntensity();
370            break;
371        case BLEND_MIN_INTENSITY:
372            mapper->SetBlendModeToMinimumIntensity();
373            break;
374        case BLEND_ADDITIVE:
375            mapper->SetBlendModeToAdditive();
376            break;
377        default:
378            ERROR("Unknown BlendMode");
379        }
380    }
381}
Note: See TracBrowser for help on using the repository browser.