source: trunk/packages/vizservers/vtkvis/Volume.cpp @ 3778

Last change on this file since 3778 was 3761, checked in by ldelgass, 11 years ago

Fix for handling ugrids with no cells as point clouds in vtkvis volumes.

  • Property svn:eol-style set to native
File size: 8.4 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 <vtkVolumeProperty.h>
14#include <vtkGPUVolumeRayCastMapper.h>
15#include <vtkVolumeTextureMapper3D.h>
16#include <vtkUnstructuredGrid.h>
17#include <vtkPolyData.h>
18#include <vtkCellType.h>
19#include <vtkUnstructuredGridVolumeMapper.h>
20#include <vtkUnstructuredGridVolumeRayCastMapper.h>
21#include <vtkProjectedTetrahedraMapper.h>
22#include <vtkDataSetTriangleFilter.h>
23#include <vtkGaussianSplatter.h>
24
25#include "Volume.h"
26#include "Trace.h"
27
28using namespace VtkVis;
29
30Volume::Volume() :
31    GraphicsObject(),
32    _colorMap(NULL)
33{
34}
35
36Volume::~Volume()
37{
38#ifdef WANT_TRACE
39    if (_dataSet != NULL)
40        TRACE("Deleting Volume for %s", _dataSet->getName().c_str());
41    else
42        TRACE("Deleting Volume with NULL DataSet");
43#endif
44}
45
46/**
47 * \brief Create and initialize a VTK Prop to render the Volume
48 */
49void Volume::initProp()
50{
51    if (_prop == NULL) {
52        _prop = vtkSmartPointer<vtkVolume>::New();
53        getVolume()->GetProperty()->SetInterpolationTypeToLinear();
54    }
55}
56
57/**
58 * \brief Get the voxel dimensions (if the volume is not a
59 * uniform grid, unit spacing is returned)
60 */
61void Volume::getSpacing(double spacing[3])
62{
63    spacing[0] = spacing[1] = spacing[2] = 1.0;
64    if (_dataSet == NULL)
65        return;
66    vtkDataSet *ds = _dataSet->getVtkDataSet();
67    if (ds != NULL && vtkImageData::SafeDownCast(ds) != NULL) {
68        vtkImageData::SafeDownCast(ds)->GetSpacing(spacing);
69    } else if (ds != NULL && vtkVolumeMapper::SafeDownCast(_volumeMapper) != NULL) {
70        vtkImageData *imgData = vtkVolumeMapper::SafeDownCast(_volumeMapper)->GetInput();
71        if (imgData != NULL) {
72            imgData->GetSpacing(spacing);
73        }
74    }
75}
76
77/**
78 * \brief Get the average voxel dimension (if the volume is not a
79 * uniform grid, unit spacing is returned)
80 */
81double Volume::getAverageSpacing()
82{
83    double spacing[3];
84    getSpacing(spacing);
85    TRACE("Spacing: %g %g %g", spacing[0], spacing[1], spacing[2]);
86    return (spacing[0] + spacing[1] + spacing[2]) * 0.333;
87}
88
89/**
90 * \brief Internal method to set up pipeline after a state change
91 */
92void Volume::update()
93{
94    if (_dataSet == NULL)
95        return;
96
97    vtkDataSet *ds = _dataSet->getVtkDataSet();
98
99    if (_dataSet->is2D()) {
100        USER_ERROR("Volume rendering requires a 3D data set");
101        _dataSet = NULL;
102        return;
103    }
104
105    if (vtkImageData::SafeDownCast(ds) != NULL) {
106        // Image data required for these mappers
107#ifdef USE_GPU_RAYCAST_MAPPER
108        _volumeMapper = vtkSmartPointer<vtkGPUVolumeRayCastMapper>::New();
109        vtkGPUVolumeRayCastMapper::SafeDownCast(_volumeMapper)->AutoAdjustSampleDistancesOff();
110#else
111        _volumeMapper = vtkSmartPointer<vtkVolumeTextureMapper3D>::New();
112#endif
113#ifdef USE_VTK6
114#ifdef USE_GPU_RAYCAST_MAPPER
115        vtkGPUVolumeRayCastMapper::SafeDownCast(_volumeMapper)->SetInputData(ds);
116#else
117        vtkVolumeTextureMapper3D::SafeDownCast(_volumeMapper)->SetInputData(ds);
118#endif
119#else
120        _volumeMapper->SetInput(ds);
121#endif
122        vtkVolumeMapper::SafeDownCast(_volumeMapper)->SetBlendModeToComposite();
123    } else if (_dataSet->isCloud()) {
124        // DataSet is a 3D point cloud
125        vtkSmartPointer<vtkGaussianSplatter> splatter = vtkSmartPointer<vtkGaussianSplatter>::New();
126#ifdef USE_VTK6
127        splatter->SetInputData(ds);
128#else
129        splatter->SetInput(ds);
130#endif
131        int dims[3];
132        dims[0] = dims[1] = dims[2] = 64;
133        TRACE("Generating volume with dims (%d,%d,%d) from point cloud",
134              dims[0], dims[1], dims[2]);
135        splatter->SetSampleDimensions(dims);
136        splatter->Update();
137        TRACE("Done generating volume");
138#ifdef USE_GPU_RAYCAST_MAPPER
139        _volumeMapper = vtkSmartPointer<vtkGPUVolumeRayCastMapper>::New();
140        vtkGPUVolumeRayCastMapper::SafeDownCast(_volumeMapper)->AutoAdjustSampleDistancesOff();
141#else
142        _volumeMapper = vtkSmartPointer<vtkVolumeTextureMapper3D>::New();
143#endif
144        _volumeMapper->SetInputConnection(splatter->GetOutputPort());
145        vtkVolumeMapper::SafeDownCast(_volumeMapper)->SetBlendModeToComposite();
146    } else if (vtkUnstructuredGrid::SafeDownCast(ds) != NULL) {
147        // Unstructured grid with cells (not a cloud)
148        vtkUnstructuredGrid *ugrid = vtkUnstructuredGrid::SafeDownCast(ds);
149        // DataSet is unstructured grid
150        // Only works if cells are all tetrahedra
151        _volumeMapper = vtkSmartPointer<vtkProjectedTetrahedraMapper>::New();
152        // Software raycast rendering - requires all tetrahedra
153        //_volumeMapper = vtkSmartPointer<vtkUnstructuredGridVolumeRayCastMapper>::New();
154        if (ugrid->GetCellType(0) == VTK_TETRA &&
155            ugrid->IsHomogeneous()) {
156#ifdef USE_VTK6
157            vtkProjectedTetrahedraMapper::SafeDownCast(_volumeMapper)->SetInputData(ds);
158#else
159            _volumeMapper->SetInput(ds);
160#endif
161        } else {
162            // Decompose to tetrahedra
163            vtkSmartPointer<vtkDataSetTriangleFilter> filter =
164                vtkSmartPointer<vtkDataSetTriangleFilter>::New();
165#ifdef USE_VTK6
166            filter->SetInputData(ugrid);
167#else
168            filter->SetInput(ugrid);
169#endif
170            filter->TetrahedraOnlyOn();
171            TRACE("Decomposing grid to tets");
172            filter->Update();
173            TRACE("Decomposing done");
174            _volumeMapper->SetInputConnection(filter->GetOutputPort());
175        }
176
177        vtkUnstructuredGridVolumeMapper::SafeDownCast(_volumeMapper)->
178            SetBlendModeToComposite();
179    } else {
180        ERROR("Unsupported DataSet type: %s", _dataSet->getVtkType());
181        _dataSet = NULL;
182        return;
183    }
184
185    TRACE("Using mapper type: %s", _volumeMapper->GetClassName());
186
187    initProp();
188
189    if (ds->GetPointData() == NULL ||
190        ds->GetPointData()->GetScalars() == NULL) {
191        WARN("No scalar point data in dataset %s", _dataSet->getName().c_str());
192    }
193
194    if (_colorMap == NULL) {
195        setColorMap(ColorMap::getVolumeDefault());
196    }
197
198    setSampleDistance(getAverageSpacing());
199
200    getVolume()->SetMapper(_volumeMapper);
201    _volumeMapper->Update();
202}
203
204void Volume::updateRanges(Renderer *renderer)
205{
206    GraphicsObject::updateRanges(renderer);
207
208    if (getVolume() != NULL) {
209        getVolume()->GetProperty()->SetColor(_colorMap->getColorTransferFunction(_dataRange));
210        getVolume()->GetProperty()->SetScalarOpacity(_colorMap->getOpacityTransferFunction(_dataRange));
211    }
212}
213
214void Volume::updateColorMap()
215{
216    setColorMap(_colorMap);
217}
218
219/**
220 * \brief Assign a color map (transfer function) to use in rendering the Volume
221 */
222void Volume::setColorMap(ColorMap *cmap)
223{
224    if (cmap == NULL)
225        return;
226
227    _colorMap = cmap;
228
229    if (getVolume() != NULL) {
230        getVolume()->GetProperty()->SetColor(_colorMap->getColorTransferFunction(_dataRange));
231        getVolume()->GetProperty()->SetScalarOpacity(_colorMap->getOpacityTransferFunction(_dataRange));
232    }
233}
234
235/**
236 * \brief Return the ColorMap assigned to this Volume
237 */
238ColorMap *Volume::getColorMap()
239{
240    return _colorMap;
241}
242
243/**
244 * \brief Set opacity scaling used to render the Volume
245 */
246void Volume::setOpacity(double opacity)
247{
248    _opacity = opacity;
249    // FIXME: There isn't really a good opacity scaling option that works
250    // across the different mappers/algorithms.  This only works with the
251    // 3D texture mapper, not the GPU raycast mapper
252    if (getVolume() != NULL) {
253        if (opacity < 1.0e-6)
254            opacity = 1.0e-6;
255        getVolume()->GetProperty()->SetScalarOpacityUnitDistance(1.0/opacity);
256    }
257}
258
259/**
260 * \brief Set a group of world coordinate planes to clip rendering
261 *
262 * Passing NULL for planes will remove all cliping planes
263 */
264void Volume::setClippingPlanes(vtkPlaneCollection *planes)
265{
266    if (_volumeMapper != NULL) {
267        _volumeMapper->SetClippingPlanes(planes);
268    }
269}
270
271/**
272 * \brief Set distance between samples along rays
273 */
274void Volume::setSampleDistance(float d)
275{
276    TRACE("Sample distance: %g", d);
277    if (_volumeMapper != NULL &&
278#ifdef USE_GPU_RAYCAST_MAPPER
279        vtkGPUVolumeRayCastMapper::SafeDownCast(_volumeMapper) != NULL) {
280        vtkGPUVolumeRayCastMapper::SafeDownCast(_volumeMapper)->SetSampleDistance(d);
281#else
282        vtkVolumeTextureMapper3D::SafeDownCast(_volumeMapper) != NULL) {
283        vtkVolumeTextureMapper3D::SafeDownCast(_volumeMapper)->SetSampleDistance(d);
284#endif
285    }
286}
Note: See TracBrowser for help on using the repository browser.