1 /*=========================================================================
2
3 Program: Visualization Toolkit
4 Module: vtkMultiVolume.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 "vtkMultiVolume.h"
16 #include "vtkBoundingBox.h"
17 #include "vtkGPUVolumeRayCastMapper.h"
18 #include "vtkImageData.h"
19 #include "vtkMath.h"
20 #include "vtkMatrix4x4.h"
21 #include "vtkObjectFactory.h"
22 #include "vtkRenderer.h"
23 #include "vtkVector.h"
24 #include "vtkVectorOperators.h"
25 #include "vtkVolumeProperty.h"
26
vtkMultiVolume()27 vtkMultiVolume::vtkMultiVolume()
28 : TexToBBox(vtkSmartPointer<vtkMatrix4x4>::New())
29 {
30 vtkMath::UninitializeBounds(this->Bounds);
31 vtkMath::UninitializeBounds(this->DataBounds.data());
32 }
33
~vtkMultiVolume()34 vtkMultiVolume::~vtkMultiVolume()
35 {
36 for (auto& pair : this->Volumes)
37 {
38 vtkVolume* vol = pair.second;
39 if (vol)
40 {
41 vol->UnRegister(this);
42 }
43 }
44 }
45
46 vtkStandardNewMacro(vtkMultiVolume);
47
SetVolume(vtkVolume * vol,int port)48 void vtkMultiVolume::SetVolume(vtkVolume* vol, int port)
49 {
50 auto currentVol = this->FindVolume(port);
51 if (currentVol != vol)
52 {
53 if (currentVol)
54 {
55 currentVol->UnRegister(this);
56 this->Volumes.erase(port);
57 }
58
59 if (vol)
60 {
61 this->Volumes[port] = vol;
62 vol->Register(this);
63 }
64
65 this->Modified();
66 }
67 }
68
GetVolume(int port)69 vtkVolume* vtkMultiVolume::GetVolume(int port)
70 {
71 auto vol = this->FindVolume(port);
72 if (!vol)
73 {
74 vtkErrorMacro(<< "Failed to query vtkVolume instance for port " << port);
75 return nullptr;
76 }
77
78 return vol;
79 }
80
FindVolume(int port)81 vtkVolume* vtkMultiVolume::FindVolume(int port)
82 {
83 vtkVolume* vol = nullptr;
84 const auto it = this->Volumes.find(port);
85 if (it != this->Volumes.end())
86 {
87 vol = it->second;
88 }
89 return vol;
90 }
91
GetBounds()92 double* vtkMultiVolume::GetBounds()
93 {
94 if (!this->VolumesChanged() && vtkMath::AreBoundsInitialized(this->Bounds))
95 {
96 return this->Bounds;
97 }
98
99 vtkMath::UninitializeBounds(this->Bounds);
100
101 // Transform the bounds of each input to world coordinates and compute the
102 // total bounds (T_total = T_vol * T_tex (vtkImageData))
103 for (auto& item : this->Volumes)
104 {
105 // Transform to world coordinates (ensure the matrix is
106 // up-to-date).
107 const int port = item.first;
108 auto mapper = vtkGPUVolumeRayCastMapper::SafeDownCast(this->Mapper);
109 if (!mapper)
110 {
111 vtkErrorMacro(<< "vtkMultiVolume is currently only supported by"
112 " vtkGPUVolumeRayCastMapper.");
113 return this->Bounds;
114 }
115 double* bnd = mapper->GetBoundsFromPort(port);
116
117 vtkVolume* vol = item.second;
118 vol->ComputeMatrix();
119 auto rBoundsWorld = this->ComputeAABounds(bnd, vol->GetMatrix());
120
121 if (vtkMath::AreBoundsInitialized(this->Bounds))
122 {
123 // Expand current bounds
124 for (size_t i = 0; i < 3; i++)
125 {
126 const size_t c = i * 2;
127 this->Bounds[c] = std::min(rBoundsWorld[c], this->Bounds[c]);
128 this->Bounds[c + 1] = std::max(rBoundsWorld[c + 1], this->Bounds[c + 1]);
129 }
130 }
131 else
132 {
133 // Init bounds
134 this->Bounds[0] = rBoundsWorld[0];
135 this->Bounds[1] = rBoundsWorld[1];
136 this->Bounds[2] = rBoundsWorld[2];
137 this->Bounds[3] = rBoundsWorld[3];
138 this->Bounds[4] = rBoundsWorld[4];
139 this->Bounds[5] = rBoundsWorld[5];
140 }
141 }
142
143 vtkVector3d minPoint(this->Bounds[0], this->Bounds[2], this->Bounds[4]);
144 vtkVector3d maxPoint(this->Bounds[1], this->Bounds[3], this->Bounds[5]);
145
146 // The bounding-box coordinate system is axis-aligned with the world
147 // coordinate system, so only the translation vector is needed for
148 // the bboxDatasetToWorld transformation (unlike other volume-matrices,
149 // this one does not include any scaling or rotation, those are only
150 // defined in the contained volumes).
151
152 // T_bboxToWorld = T_translation
153 // Translation vector is its actual position in world coordinates.
154 // (Min-point as origin).
155 this->Matrix->Identity();
156 this->Matrix->SetElement(0, 3, minPoint[0]);
157 this->Matrix->SetElement(1, 3, minPoint[1]);
158 this->Matrix->SetElement(2, 3, minPoint[2]);
159
160 // Compute bbox dimensions (world)
161 auto scale = maxPoint - minPoint;
162
163 // T_texToBbox = T_scaling
164 TexToBBox->Identity();
165 TexToBBox->SetElement(0, 0, scale[0]);
166 TexToBBox->SetElement(1, 1, scale[1]);
167 TexToBBox->SetElement(2, 2, scale[2]);
168
169 // Transform bounds back to data-coords (range [0, scale]). Data-coords
170 // is what the mapper expects.
171 auto minPointData = minPoint - minPoint;
172 auto maxPointData = maxPoint - minPoint;
173 this->DataBounds[0] = minPointData[0];
174 this->DataBounds[2] = minPointData[1];
175 this->DataBounds[4] = minPointData[2];
176 this->DataBounds[1] = maxPointData[0];
177 this->DataBounds[3] = maxPointData[1];
178 this->DataBounds[5] = maxPointData[2];
179
180 this->DataGeometry[0] = minPointData[0];
181 this->DataGeometry[1] = minPointData[1];
182 this->DataGeometry[2] = minPointData[2];
183 this->DataGeometry[3] = maxPointData[0];
184 this->DataGeometry[4] = minPointData[1];
185 this->DataGeometry[5] = minPointData[2];
186
187 this->DataGeometry[6] = minPointData[0];
188 this->DataGeometry[7] = maxPointData[1];
189 this->DataGeometry[8] = minPointData[2];
190 this->DataGeometry[9] = maxPointData[0];
191 this->DataGeometry[10] = maxPointData[1];
192 this->DataGeometry[11] = minPointData[2];
193
194 this->DataGeometry[12] = minPointData[0];
195 this->DataGeometry[13] = minPointData[1];
196 this->DataGeometry[14] = maxPointData[2];
197 this->DataGeometry[15] = maxPointData[0];
198 this->DataGeometry[16] = minPointData[1];
199 this->DataGeometry[17] = maxPointData[2];
200
201 this->DataGeometry[18] = minPointData[0];
202 this->DataGeometry[19] = maxPointData[1];
203 this->DataGeometry[20] = maxPointData[2];
204 this->DataGeometry[21] = maxPointData[0];
205 this->DataGeometry[22] = maxPointData[1];
206 this->DataGeometry[23] = maxPointData[2];
207
208 this->BoundsComputeTime.Modified();
209 return this->Bounds;
210 }
211
ComputeAABounds(double bounds[6],vtkMatrix4x4 * T) const212 std::array<double, 6> vtkMultiVolume::ComputeAABounds(double bounds[6], vtkMatrix4x4* T) const
213 {
214 using Point = vtkVector4d;
215 using PointVec = std::vector<Point>;
216
217 // Create all corner poiints of the bounding box
218 vtkVector3d dim(bounds[1] - bounds[0], bounds[3] - bounds[2], bounds[5] - bounds[4]);
219
220 Point minPoint(bounds[0], bounds[2], bounds[4], 1.0);
221 PointVec pointsDataCoords;
222 pointsDataCoords.reserve(8);
223 pointsDataCoords.push_back(minPoint);
224 pointsDataCoords.push_back(minPoint + Point(dim[0], 0., 0., 0.));
225 pointsDataCoords.push_back(minPoint + Point(dim[0], dim[1], 0., 0.));
226 pointsDataCoords.push_back(minPoint + Point(0., dim[1], 0., 0.));
227 pointsDataCoords.push_back(minPoint + Point(0., 0., dim[2], 0.));
228 pointsDataCoords.push_back(minPoint + Point(dim[0], 0., dim[2], 0.));
229 pointsDataCoords.push_back(Point(bounds[1], bounds[3], bounds[5], 0.));
230 pointsDataCoords.push_back(minPoint + Point(0., dim[1], dim[2], 0.));
231
232 // Transform all points from data to world coordinates
233 vtkBoundingBox bBoxWorld;
234 for (const Point& pData : pointsDataCoords)
235 {
236 Point pWorld;
237 T->MultiplyPoint(pData.GetData(), pWorld.GetData());
238 bBoxWorld.AddPoint(pWorld.GetX(), pWorld.GetY(), pWorld.GetZ());
239 }
240 std::array<double, 6> boundsWorld;
241 bBoxWorld.GetBounds(boundsWorld.data());
242
243 return boundsWorld;
244 }
245
VolumesChanged()246 bool vtkMultiVolume::VolumesChanged()
247 {
248 auto mapper = vtkGPUVolumeRayCastMapper::SafeDownCast(this->Mapper);
249 if (!mapper)
250 {
251 vtkErrorMacro(<< "vtkMultiVolume is currently only supported by"
252 " vtkGPUVolumeRayCastMapper.");
253 return false;
254 }
255
256 for (auto& item : this->Volumes)
257 {
258 auto vol = item.second;
259 vol->ComputeMatrix();
260 const bool moved = this->BoundsComputeTime < vol->GetMatrix()->GetMTime();
261 auto data = mapper->GetTransformedInput(item.first);
262 const bool changed = data ? this->BoundsComputeTime < data->GetMTime() : true;
263
264 if (moved || changed)
265 return true;
266 }
267
268 return false;
269 }
270
GetMTime()271 vtkMTimeType vtkMultiVolume::GetMTime()
272 {
273 auto mTime = this->Superclass::GetMTime();
274
275 mTime = this->BoundsComputeTime > mTime ? this->BoundsComputeTime.GetMTime() : mTime;
276
277 return mTime;
278 }
279
PrintSelf(ostream & os,vtkIndent indent)280 void vtkMultiVolume::PrintSelf(ostream& os, vtkIndent indent)
281 {
282 Superclass::PrintSelf(os, indent);
283 os << indent << "Num. volumes: " << this->Volumes.size() << "\n";
284 os << indent << "BoundsComputeTime: " << this->BoundsComputeTime.GetMTime() << "\n";
285 os << indent << "Texture-To-Data: \n";
286 this->TexToBBox->PrintSelf(os, indent);
287 os << indent << "Data-To-World: \n ";
288 this->Matrix->PrintSelf(os, indent);
289 }
290
ShallowCopy(vtkProp * prop)291 void vtkMultiVolume::ShallowCopy(vtkProp* prop)
292 {
293 auto multiVol = vtkMultiVolume::SafeDownCast(prop);
294 if (multiVol)
295 {
296 for (auto& item : multiVol->Volumes)
297 {
298 this->SetVolume(item.second, item.first);
299 }
300 this->DataBounds = multiVol->DataBounds;
301 this->DataGeometry = multiVol->DataGeometry;
302 this->BoundsComputeTime = multiVol->BoundsComputeTime;
303 this->TexToBBox->DeepCopy(multiVol->TexToBBox);
304 return;
305 }
306 Superclass::ShallowCopy(prop);
307 }
308
RenderVolumetricGeometry(vtkViewport * vp)309 int vtkMultiVolume::RenderVolumetricGeometry(vtkViewport* vp)
310 {
311 this->Update();
312
313 if (!this->Mapper)
314 {
315 vtkErrorMacro(<< "Invalid Mapper!\n");
316 return 0;
317 }
318
319 if (!this->Mapper->GetDataObjectInput())
320 {
321 return 0;
322 }
323
324 this->Mapper->Render(static_cast<vtkRenderer*>(vp), this);
325 this->EstimatedRenderTime += this->Mapper->GetTimeToDraw();
326
327 return 1;
328 }
329
SetProperty(vtkVolumeProperty * vtkNotUsed (property))330 void vtkMultiVolume::SetProperty(vtkVolumeProperty* vtkNotUsed(property))
331 {
332 vtkWarningMacro(<< "This vtkVolumeProperty will not be used during"
333 << " rendering. Volume properties should be specified through registered"
334 << " vtkVolume instances.");
335 }
336
GetProperty()337 vtkVolumeProperty* vtkMultiVolume::GetProperty()
338 {
339 auto volume = this->FindVolume(0);
340 if (volume)
341 return volume->GetProperty();
342
343 return nullptr;
344 }
345