1 //============================================================================
2 //  Copyright (c) Kitware, Inc.
3 //  All rights reserved.
4 //  See LICENSE.txt for details.
5 //  This software is distributed WITHOUT ANY WARRANTY; without even
6 //  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
7 //  PURPOSE.  See the above copyright notice for more information.
8 //
9 //  Copyright 2015 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
10 //  Copyright 2015 UT-Battelle, LLC.
11 //  Copyright 2015 Los Alamos National Security.
12 //
13 //  Under the terms of Contract DE-NA0003525 with NTESS,
14 //  the U.S. Government retains certain rights in this software.
15 //
16 //  Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
17 //  Laboratory (LANL), the U.S. Government retains certain rights in
18 //  this software.
19 //============================================================================
20 
21 #include <vtkm/rendering/Camera.h>
22 
23 namespace vtkm
24 {
25 namespace rendering
26 {
27 
CreateViewMatrix() const28 vtkm::Matrix<vtkm::Float32, 4, 4> Camera::Camera3DStruct::CreateViewMatrix() const
29 {
30   return MatrixHelpers::ViewMatrix(this->Position, this->LookAt, this->ViewUp);
31 }
32 
CreateProjectionMatrix(vtkm::Id width,vtkm::Id height,vtkm::Float32 nearPlane,vtkm::Float32 farPlane) const33 vtkm::Matrix<vtkm::Float32, 4, 4> Camera::Camera3DStruct::CreateProjectionMatrix(
34   vtkm::Id width,
35   vtkm::Id height,
36   vtkm::Float32 nearPlane,
37   vtkm::Float32 farPlane) const
38 {
39   vtkm::Matrix<vtkm::Float32, 4, 4> matrix;
40   vtkm::MatrixIdentity(matrix);
41 
42   vtkm::Float32 AspectRatio = vtkm::Float32(width) / vtkm::Float32(height);
43   vtkm::Float32 fovRad = this->FieldOfView * vtkm::Pi_180f();
44   fovRad = vtkm::Tan(fovRad * 0.5f);
45   vtkm::Float32 size = nearPlane * fovRad;
46   vtkm::Float32 left = -size * AspectRatio;
47   vtkm::Float32 right = size * AspectRatio;
48   vtkm::Float32 bottom = -size;
49   vtkm::Float32 top = size;
50 
51   matrix(0, 0) = 2.f * nearPlane / (right - left);
52   matrix(1, 1) = 2.f * nearPlane / (top - bottom);
53   matrix(0, 2) = (right + left) / (right - left);
54   matrix(1, 2) = (top + bottom) / (top - bottom);
55   matrix(2, 2) = -(farPlane + nearPlane) / (farPlane - nearPlane);
56   matrix(3, 2) = -1.f;
57   matrix(2, 3) = -(2.f * farPlane * nearPlane) / (farPlane - nearPlane);
58   matrix(3, 3) = 0.f;
59 
60   vtkm::Matrix<vtkm::Float32, 4, 4> T, Z;
61   T = vtkm::Transform3DTranslate(this->XPan, this->YPan, 0.f);
62   Z = vtkm::Transform3DScale(this->Zoom, this->Zoom, 1.f);
63   matrix = vtkm::MatrixMultiply(Z, vtkm::MatrixMultiply(T, matrix));
64   return matrix;
65 }
66 
67 //---------------------------------------------------------------------------
68 
CreateViewMatrix() const69 vtkm::Matrix<vtkm::Float32, 4, 4> Camera::Camera2DStruct::CreateViewMatrix() const
70 {
71   vtkm::Vec<vtkm::Float32, 3> lookAt(
72     (this->Left + this->Right) / 2.f, (this->Top + this->Bottom) / 2.f, 0.f);
73   vtkm::Vec<vtkm::Float32, 3> position = lookAt;
74   position[2] = 1.f;
75   vtkm::Vec<vtkm::Float32, 3> up(0, 1, 0);
76   vtkm::Matrix<vtkm::Float32, 4, 4> V = MatrixHelpers::ViewMatrix(position, lookAt, up);
77   vtkm::Matrix<vtkm::Float32, 4, 4> scaleMatrix = MatrixHelpers::CreateScale(this->XScale, 1, 1);
78   V = vtkm::MatrixMultiply(scaleMatrix, V);
79   return V;
80 }
81 
CreateProjectionMatrix(vtkm::Float32 size,vtkm::Float32 znear,vtkm::Float32 zfar,vtkm::Float32 aspect) const82 vtkm::Matrix<vtkm::Float32, 4, 4> Camera::Camera2DStruct::CreateProjectionMatrix(
83   vtkm::Float32 size,
84   vtkm::Float32 znear,
85   vtkm::Float32 zfar,
86   vtkm::Float32 aspect) const
87 {
88   vtkm::Matrix<vtkm::Float32, 4, 4> matrix(0.f);
89   vtkm::Float32 left = -size / 2.f * aspect;
90   vtkm::Float32 right = size / 2.f * aspect;
91   vtkm::Float32 bottom = -size / 2.f;
92   vtkm::Float32 top = size / 2.f;
93 
94   matrix(0, 0) = 2.f / (right - left);
95   matrix(1, 1) = 2.f / (top - bottom);
96   matrix(2, 2) = -2.f / (zfar - znear);
97   matrix(0, 3) = -(right + left) / (right - left);
98   matrix(1, 3) = -(top + bottom) / (top - bottom);
99   matrix(2, 3) = -(zfar + znear) / (zfar - znear);
100   matrix(3, 3) = 1.f;
101 
102   vtkm::Matrix<vtkm::Float32, 4, 4> T, Z;
103   T = vtkm::Transform3DTranslate(this->XPan, this->YPan, 0.f);
104   Z = vtkm::Transform3DScale(this->Zoom, this->Zoom, 1.f);
105   matrix = vtkm::MatrixMultiply(Z, vtkm::MatrixMultiply(T, matrix));
106   return matrix;
107 }
108 
109 //---------------------------------------------------------------------------
110 
CreateViewMatrix() const111 vtkm::Matrix<vtkm::Float32, 4, 4> Camera::CreateViewMatrix() const
112 {
113   if (this->Mode == Camera::MODE_3D)
114   {
115     return this->Camera3D.CreateViewMatrix();
116   }
117   else
118   {
119     return this->Camera2D.CreateViewMatrix();
120   }
121 }
122 
CreateProjectionMatrix(vtkm::Id screenWidth,vtkm::Id screenHeight) const123 vtkm::Matrix<vtkm::Float32, 4, 4> Camera::CreateProjectionMatrix(vtkm::Id screenWidth,
124                                                                  vtkm::Id screenHeight) const
125 {
126   if (this->Mode == Camera::MODE_3D)
127   {
128     return this->Camera3D.CreateProjectionMatrix(
129       screenWidth, screenHeight, this->NearPlane, this->FarPlane);
130   }
131   else
132   {
133     vtkm::Float32 size = vtkm::Abs(this->Camera2D.Top - this->Camera2D.Bottom);
134     vtkm::Float32 left, right, bottom, top;
135     this->GetRealViewport(screenWidth, screenHeight, left, right, bottom, top);
136     vtkm::Float32 aspect = (static_cast<vtkm::Float32>(screenWidth) * (right - left)) /
137       (static_cast<vtkm::Float32>(screenHeight) * (top - bottom));
138 
139     return this->Camera2D.CreateProjectionMatrix(size, this->NearPlane, this->FarPlane, aspect);
140   }
141 }
142 
GetRealViewport(vtkm::Id screenWidth,vtkm::Id screenHeight,vtkm::Float32 & left,vtkm::Float32 & right,vtkm::Float32 & bottom,vtkm::Float32 & top) const143 void Camera::GetRealViewport(vtkm::Id screenWidth,
144                              vtkm::Id screenHeight,
145                              vtkm::Float32& left,
146                              vtkm::Float32& right,
147                              vtkm::Float32& bottom,
148                              vtkm::Float32& top) const
149 {
150   if (this->Mode == Camera::MODE_3D)
151   {
152     left = this->ViewportLeft;
153     right = this->ViewportRight;
154     bottom = this->ViewportBottom;
155     top = this->ViewportTop;
156   }
157   else
158   {
159     vtkm::Float32 maxvw =
160       (this->ViewportRight - this->ViewportLeft) * static_cast<vtkm::Float32>(screenWidth);
161     vtkm::Float32 maxvh =
162       (this->ViewportTop - this->ViewportBottom) * static_cast<vtkm::Float32>(screenHeight);
163     vtkm::Float32 waspect = maxvw / maxvh;
164     vtkm::Float32 daspect =
165       (this->Camera2D.Right - this->Camera2D.Left) / (this->Camera2D.Top - this->Camera2D.Bottom);
166     daspect *= this->Camera2D.XScale;
167 //cerr << "waspect="<<waspect << "   \tdaspect="<<daspect<<endl;
168 
169 //needed as center is a constant value
170 #if defined(VTKM_MSVC)
171 #pragma warning(push)
172 #pragma warning(disable : 4127) // conditional expression is constant
173 #endif
174 
175     const bool center = true; // if false, anchor to bottom-left
176     if (waspect > daspect)
177     {
178       vtkm::Float32 new_w = (this->ViewportRight - this->ViewportLeft) * daspect / waspect;
179       if (center)
180       {
181         left = (this->ViewportLeft + this->ViewportRight) / 2.f - new_w / 2.f;
182         right = (this->ViewportLeft + this->ViewportRight) / 2.f + new_w / 2.f;
183       }
184       else
185       {
186         left = this->ViewportLeft;
187         right = this->ViewportLeft + new_w;
188       }
189       bottom = this->ViewportBottom;
190       top = this->ViewportTop;
191     }
192     else
193     {
194       vtkm::Float32 new_h = (this->ViewportTop - this->ViewportBottom) * waspect / daspect;
195       if (center)
196       {
197         bottom = (this->ViewportBottom + this->ViewportTop) / 2.f - new_h / 2.f;
198         top = (this->ViewportBottom + this->ViewportTop) / 2.f + new_h / 2.f;
199       }
200       else
201       {
202         bottom = this->ViewportBottom;
203         top = this->ViewportBottom + new_h;
204       }
205       left = this->ViewportLeft;
206       right = this->ViewportRight;
207     }
208 #if defined(VTKM_MSVC)
209 #pragma warning(pop)
210 #endif
211   }
212 }
213 
Pan(vtkm::Float32 dx,vtkm::Float32 dy)214 void Camera::Pan(vtkm::Float32 dx, vtkm::Float32 dy)
215 {
216   this->Camera3D.XPan += dx;
217   this->Camera3D.YPan += dy;
218   this->Camera2D.XPan += dx;
219   this->Camera2D.YPan += dy;
220 }
221 
Zoom(vtkm::Float32 zoom)222 void Camera::Zoom(vtkm::Float32 zoom)
223 {
224   vtkm::Float32 factor = vtkm::Pow(4.0f, zoom);
225   this->Camera3D.Zoom *= factor;
226   this->Camera3D.XPan *= factor;
227   this->Camera3D.YPan *= factor;
228   this->Camera2D.Zoom *= factor;
229   this->Camera2D.XPan *= factor;
230   this->Camera2D.YPan *= factor;
231 }
232 
TrackballRotate(vtkm::Float32 startX,vtkm::Float32 startY,vtkm::Float32 endX,vtkm::Float32 endY)233 void Camera::TrackballRotate(vtkm::Float32 startX,
234                              vtkm::Float32 startY,
235                              vtkm::Float32 endX,
236                              vtkm::Float32 endY)
237 {
238   vtkm::Matrix<vtkm::Float32, 4, 4> rotate =
239     MatrixHelpers::TrackballMatrix(startX, startY, endX, endY);
240 
241   //Translate matrix
242   vtkm::Matrix<vtkm::Float32, 4, 4> translate = vtkm::Transform3DTranslate(-this->Camera3D.LookAt);
243 
244   //Translate matrix
245   vtkm::Matrix<vtkm::Float32, 4, 4> inverseTranslate =
246     vtkm::Transform3DTranslate(this->Camera3D.LookAt);
247 
248   vtkm::Matrix<vtkm::Float32, 4, 4> view = this->CreateViewMatrix();
249   view(0, 3) = 0;
250   view(1, 3) = 0;
251   view(2, 3) = 0;
252 
253   vtkm::Matrix<vtkm::Float32, 4, 4> inverseView = vtkm::MatrixTranspose(view);
254 
255   //fullTransform = inverseTranslate * inverseView * rotate * view * translate
256   vtkm::Matrix<vtkm::Float32, 4, 4> fullTransform;
257   fullTransform = vtkm::MatrixMultiply(
258     inverseTranslate,
259     vtkm::MatrixMultiply(inverseView,
260                          vtkm::MatrixMultiply(rotate, vtkm::MatrixMultiply(view, translate))));
261   this->Camera3D.Position = vtkm::Transform3DPoint(fullTransform, this->Camera3D.Position);
262   this->Camera3D.LookAt = vtkm::Transform3DPoint(fullTransform, this->Camera3D.LookAt);
263   this->Camera3D.ViewUp = vtkm::Transform3DVector(fullTransform, this->Camera3D.ViewUp);
264 }
265 
ResetToBounds(const vtkm::Bounds & dataBounds,const vtkm::Float64 XDataViewPadding,const vtkm::Float64 YDataViewPadding,const vtkm::Float64 ZDataViewPadding)266 void Camera::ResetToBounds(const vtkm::Bounds& dataBounds,
267                            const vtkm::Float64 XDataViewPadding,
268                            const vtkm::Float64 YDataViewPadding,
269                            const vtkm::Float64 ZDataViewPadding)
270 {
271   // Save camera mode
272   ModeEnum saveMode = this->GetMode();
273 
274   // Pad view around data extents
275   vtkm::Bounds db = dataBounds;
276   vtkm::Float64 viewPadAmount = XDataViewPadding * (db.X.Max - db.X.Min);
277   db.X.Max += viewPadAmount;
278   db.X.Min -= viewPadAmount;
279   viewPadAmount = YDataViewPadding * (db.Y.Max - db.Y.Min);
280   db.Y.Max += viewPadAmount;
281   db.Y.Min -= viewPadAmount;
282   viewPadAmount = ZDataViewPadding * (db.Z.Max - db.Z.Min);
283   db.Z.Max += viewPadAmount;
284   db.Z.Min -= viewPadAmount;
285 
286   // Reset for 3D camera
287   vtkm::Vec<vtkm::Float32, 3> directionOfProjection = this->GetPosition() - this->GetLookAt();
288   vtkm::Normalize(directionOfProjection);
289 
290   vtkm::Vec<vtkm::Float32, 3> center = db.Center();
291   this->SetLookAt(center);
292 
293   vtkm::Vec<vtkm::Float32, 3> totalExtent;
294   totalExtent[0] = vtkm::Float32(db.X.Length());
295   totalExtent[1] = vtkm::Float32(db.Y.Length());
296   totalExtent[2] = vtkm::Float32(db.Z.Length());
297   vtkm::Float32 diagonalLength = vtkm::Magnitude(totalExtent);
298   this->SetPosition(center + directionOfProjection * diagonalLength * 1.0f);
299   this->SetFieldOfView(60.0f);
300   this->SetClippingRange(0.1f * diagonalLength, diagonalLength * 10.0f);
301 
302   // Reset for 2D camera
303   this->SetViewRange2D(db);
304 
305   // Reset pan and zoom
306   this->Camera3D.XPan = 0;
307   this->Camera3D.YPan = 0;
308   this->Camera3D.Zoom = 1;
309   this->Camera2D.XPan = 0;
310   this->Camera2D.YPan = 0;
311   this->Camera2D.Zoom = 1;
312 
313   // Restore camera mode
314   this->SetMode(saveMode);
315 }
316 
317 // Enable the ability to pad the data extents in the final view
ResetToBounds(const vtkm::Bounds & dataBounds,const vtkm::Float64 dataViewPadding)318 void Camera::ResetToBounds(const vtkm::Bounds& dataBounds, const vtkm::Float64 dataViewPadding)
319 {
320   Camera::ResetToBounds(dataBounds, dataViewPadding, dataViewPadding, dataViewPadding);
321 }
322 
ResetToBounds(const vtkm::Bounds & dataBounds)323 void Camera::ResetToBounds(const vtkm::Bounds& dataBounds)
324 {
325   Camera::ResetToBounds(dataBounds, 0);
326 }
327 
Roll(vtkm::Float32 angleDegrees)328 void Camera::Roll(vtkm::Float32 angleDegrees)
329 {
330   vtkm::Vec<vtkm::Float32, 3> directionOfProjection = this->GetLookAt() - this->GetPosition();
331   vtkm::Matrix<vtkm::Float32, 4, 4> rotateTransform =
332     vtkm::Transform3DRotate(angleDegrees, directionOfProjection);
333 
334   this->SetViewUp(vtkm::Transform3DVector(rotateTransform, this->GetViewUp()));
335 }
336 
Azimuth(vtkm::Float32 angleDegrees)337 void Camera::Azimuth(vtkm::Float32 angleDegrees)
338 {
339   // Translate to the focal point (LookAt), rotate about view up, and
340   // translate back again.
341   vtkm::Matrix<vtkm::Float32, 4, 4> transform = vtkm::Transform3DTranslate(this->GetLookAt());
342   transform =
343     vtkm::MatrixMultiply(transform, vtkm::Transform3DRotate(angleDegrees, this->GetViewUp()));
344   transform = vtkm::MatrixMultiply(transform, vtkm::Transform3DTranslate(-this->GetLookAt()));
345 
346   this->SetPosition(vtkm::Transform3DPoint(transform, this->GetPosition()));
347 }
348 
Elevation(vtkm::Float32 angleDegrees)349 void Camera::Elevation(vtkm::Float32 angleDegrees)
350 {
351   vtkm::Vec<vtkm::Float32, 3> axisOfRotation =
352     vtkm::Cross(this->GetPosition() - this->GetLookAt(), this->GetViewUp());
353 
354   // Translate to the focal point (LookAt), rotate about the defined axis,
355   // and translate back again.
356   vtkm::Matrix<vtkm::Float32, 4, 4> transform = vtkm::Transform3DTranslate(this->GetLookAt());
357   transform =
358     vtkm::MatrixMultiply(transform, vtkm::Transform3DRotate(angleDegrees, axisOfRotation));
359   transform = vtkm::MatrixMultiply(transform, vtkm::Transform3DTranslate(-this->GetLookAt()));
360 
361   this->SetPosition(vtkm::Transform3DPoint(transform, this->GetPosition()));
362 }
363 
Dolly(vtkm::Float32 value)364 void Camera::Dolly(vtkm::Float32 value)
365 {
366   if (value <= vtkm::Epsilon32())
367   {
368     return;
369   }
370 
371   vtkm::Vec<vtkm::Float32, 3> lookAtToPos = this->GetPosition() - this->GetLookAt();
372 
373   this->SetPosition(this->GetLookAt() + (1.0f / value) * lookAtToPos);
374 }
375 
Print() const376 void Camera::Print() const
377 {
378   if (Mode == MODE_3D)
379   {
380     std::cout << "Camera: 3D" << std::endl;
381     std::cout << "  LookAt: " << Camera3D.LookAt << std::endl;
382     std::cout << "  Pos   : " << Camera3D.Position << std::endl;
383     std::cout << "  Up    : " << Camera3D.ViewUp << std::endl;
384     std::cout << "  FOV   : " << GetFieldOfView() << std::endl;
385     std::cout << "  Clip  : " << GetClippingRange() << std::endl;
386     std::cout << "  XyZ   : " << Camera3D.XPan << " " << Camera3D.YPan << " " << Camera3D.Zoom
387               << std::endl;
388     vtkm::Matrix<vtkm::Float32, 4, 4> pm, vm;
389     pm = CreateProjectionMatrix(512, 512);
390     vm = CreateViewMatrix();
391     std::cout << " PM: " << std::endl;
392     std::cout << pm[0][0] << " " << pm[0][1] << " " << pm[0][2] << " " << pm[0][3] << std::endl;
393     std::cout << pm[1][0] << " " << pm[1][1] << " " << pm[1][2] << " " << pm[1][3] << std::endl;
394     std::cout << pm[2][0] << " " << pm[2][1] << " " << pm[2][2] << " " << pm[2][3] << std::endl;
395     std::cout << pm[3][0] << " " << pm[3][1] << " " << pm[3][2] << " " << pm[3][3] << std::endl;
396     std::cout << " VM: " << std::endl;
397     std::cout << vm[0][0] << " " << vm[0][1] << " " << vm[0][2] << " " << vm[0][3] << std::endl;
398     std::cout << vm[1][0] << " " << vm[1][1] << " " << vm[1][2] << " " << vm[1][3] << std::endl;
399     std::cout << vm[2][0] << " " << vm[2][1] << " " << vm[2][2] << " " << vm[2][3] << std::endl;
400     std::cout << vm[3][0] << " " << vm[3][1] << " " << vm[3][2] << " " << vm[3][3] << std::endl;
401   }
402   else if (Mode == MODE_2D)
403   {
404     std::cout << "Camera: 2D" << std::endl;
405     std::cout << "  LRBT: " << Camera2D.Left << " " << Camera2D.Right << " " << Camera2D.Bottom
406               << " " << Camera2D.Top << std::endl;
407     std::cout << "  XY  : " << Camera2D.XPan << " " << Camera2D.YPan << std::endl;
408     std::cout << "  SZ  : " << Camera2D.XScale << " " << Camera2D.Zoom << std::endl;
409     vtkm::Matrix<vtkm::Float32, 4, 4> pm, vm;
410     pm = CreateProjectionMatrix(512, 512);
411     vm = CreateViewMatrix();
412     std::cout << " PM: " << std::endl;
413     std::cout << pm[0][0] << " " << pm[0][1] << " " << pm[0][2] << " " << pm[0][3] << std::endl;
414     std::cout << pm[1][0] << " " << pm[1][1] << " " << pm[1][2] << " " << pm[1][3] << std::endl;
415     std::cout << pm[2][0] << " " << pm[2][1] << " " << pm[2][2] << " " << pm[2][3] << std::endl;
416     std::cout << pm[3][0] << " " << pm[3][1] << " " << pm[3][2] << " " << pm[3][3] << std::endl;
417     std::cout << " VM: " << std::endl;
418     std::cout << vm[0][0] << " " << vm[0][1] << " " << vm[0][2] << " " << vm[0][3] << std::endl;
419     std::cout << vm[1][0] << " " << vm[1][1] << " " << vm[1][2] << " " << vm[1][3] << std::endl;
420     std::cout << vm[2][0] << " " << vm[2][1] << " " << vm[2][2] << " " << vm[2][3] << std::endl;
421     std::cout << vm[3][0] << " " << vm[3][1] << " " << vm[3][2] << " " << vm[3][3] << std::endl;
422   }
423 }
424 }
425 } // namespace vtkm::rendering
426