source: vtkvis/trunk/Volume.cpp @ 5968

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

Don't include vtkVolumeTextureMapper3D.h if building with raycast mapper.

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