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