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