//============================================================================ // Copyright (c) Kitware, Inc. // All rights reserved. // See LICENSE.txt for details. // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notice for more information. //============================================================================ #include namespace vtkm { namespace rendering { vtkm::Matrix Camera::Camera3DStruct::CreateViewMatrix() const { return MatrixHelpers::ViewMatrix(this->Position, this->LookAt, this->ViewUp); } vtkm::Matrix Camera::Camera3DStruct::CreateProjectionMatrix( vtkm::Id width, vtkm::Id height, vtkm::Float32 nearPlane, vtkm::Float32 farPlane) const { vtkm::Matrix matrix; vtkm::MatrixIdentity(matrix); vtkm::Float32 AspectRatio = vtkm::Float32(width) / vtkm::Float32(height); vtkm::Float32 fovRad = this->FieldOfView * vtkm::Pi_180f(); fovRad = vtkm::Tan(fovRad * 0.5f); vtkm::Float32 size = nearPlane * fovRad; vtkm::Float32 left = -size * AspectRatio; vtkm::Float32 right = size * AspectRatio; vtkm::Float32 bottom = -size; vtkm::Float32 top = size; matrix(0, 0) = 2.f * nearPlane / (right - left); matrix(1, 1) = 2.f * nearPlane / (top - bottom); matrix(0, 2) = (right + left) / (right - left); matrix(1, 2) = (top + bottom) / (top - bottom); matrix(2, 2) = -(farPlane + nearPlane) / (farPlane - nearPlane); matrix(3, 2) = -1.f; matrix(2, 3) = -(2.f * farPlane * nearPlane) / (farPlane - nearPlane); matrix(3, 3) = 0.f; vtkm::Matrix T, Z; T = vtkm::Transform3DTranslate(this->XPan, this->YPan, 0.f); Z = vtkm::Transform3DScale(this->Zoom, this->Zoom, 1.f); matrix = vtkm::MatrixMultiply(Z, vtkm::MatrixMultiply(T, matrix)); return matrix; } //--------------------------------------------------------------------------- vtkm::Matrix Camera::Camera2DStruct::CreateViewMatrix() const { vtkm::Vec3f_32 lookAt((this->Left + this->Right) / 2.f, (this->Top + this->Bottom) / 2.f, 0.f); vtkm::Vec3f_32 position = lookAt; position[2] = 1.f; vtkm::Vec3f_32 up(0, 1, 0); vtkm::Matrix V = MatrixHelpers::ViewMatrix(position, lookAt, up); vtkm::Matrix scaleMatrix = MatrixHelpers::CreateScale(this->XScale, 1, 1); V = vtkm::MatrixMultiply(scaleMatrix, V); return V; } vtkm::Matrix Camera::Camera2DStruct::CreateProjectionMatrix( vtkm::Float32 size, vtkm::Float32 znear, vtkm::Float32 zfar, vtkm::Float32 aspect) const { vtkm::Matrix matrix(0.f); vtkm::Float32 left = -size / 2.f * aspect; vtkm::Float32 right = size / 2.f * aspect; vtkm::Float32 bottom = -size / 2.f; vtkm::Float32 top = size / 2.f; matrix(0, 0) = 2.f / (right - left); matrix(1, 1) = 2.f / (top - bottom); matrix(2, 2) = -2.f / (zfar - znear); matrix(0, 3) = -(right + left) / (right - left); matrix(1, 3) = -(top + bottom) / (top - bottom); matrix(2, 3) = -(zfar + znear) / (zfar - znear); matrix(3, 3) = 1.f; vtkm::Matrix T, Z; T = vtkm::Transform3DTranslate(this->XPan, this->YPan, 0.f); Z = vtkm::Transform3DScale(this->Zoom, this->Zoom, 1.f); matrix = vtkm::MatrixMultiply(Z, vtkm::MatrixMultiply(T, matrix)); return matrix; } //--------------------------------------------------------------------------- vtkm::Matrix Camera::CreateViewMatrix() const { if (this->ModeType == Camera::Mode::ThreeD) { return this->Camera3D.CreateViewMatrix(); } else { return this->Camera2D.CreateViewMatrix(); } } vtkm::Matrix Camera::CreateProjectionMatrix(vtkm::Id screenWidth, vtkm::Id screenHeight) const { if (this->ModeType == Camera::Mode::ThreeD) { return this->Camera3D.CreateProjectionMatrix( screenWidth, screenHeight, this->NearPlane, this->FarPlane); } else { vtkm::Float32 size = vtkm::Abs(this->Camera2D.Top - this->Camera2D.Bottom); vtkm::Float32 left, right, bottom, top; this->GetRealViewport(screenWidth, screenHeight, left, right, bottom, top); vtkm::Float32 aspect = (static_cast(screenWidth) * (right - left)) / (static_cast(screenHeight) * (top - bottom)); return this->Camera2D.CreateProjectionMatrix(size, this->NearPlane, this->FarPlane, aspect); } } void Camera::GetRealViewport(vtkm::Id screenWidth, vtkm::Id screenHeight, vtkm::Float32& left, vtkm::Float32& right, vtkm::Float32& bottom, vtkm::Float32& top) const { if (this->ModeType == Camera::Mode::ThreeD) { left = this->ViewportLeft; right = this->ViewportRight; bottom = this->ViewportBottom; top = this->ViewportTop; } else { vtkm::Float32 maxvw = (this->ViewportRight - this->ViewportLeft) * static_cast(screenWidth); vtkm::Float32 maxvh = (this->ViewportTop - this->ViewportBottom) * static_cast(screenHeight); vtkm::Float32 waspect = maxvw / maxvh; vtkm::Float32 daspect = (this->Camera2D.Right - this->Camera2D.Left) / (this->Camera2D.Top - this->Camera2D.Bottom); daspect *= this->Camera2D.XScale; //needed as center is a constant value #if defined(VTKM_MSVC) #pragma warning(push) #pragma warning(disable : 4127) // conditional expression is constant #endif const bool center = true; // if false, anchor to bottom-left if (waspect > daspect) { vtkm::Float32 new_w = (this->ViewportRight - this->ViewportLeft) * daspect / waspect; if (center) { left = (this->ViewportLeft + this->ViewportRight) / 2.f - new_w / 2.f; right = (this->ViewportLeft + this->ViewportRight) / 2.f + new_w / 2.f; } else { left = this->ViewportLeft; right = this->ViewportLeft + new_w; } bottom = this->ViewportBottom; top = this->ViewportTop; } else { vtkm::Float32 new_h = (this->ViewportTop - this->ViewportBottom) * waspect / daspect; if (center) { bottom = (this->ViewportBottom + this->ViewportTop) / 2.f - new_h / 2.f; top = (this->ViewportBottom + this->ViewportTop) / 2.f + new_h / 2.f; } else { bottom = this->ViewportBottom; top = this->ViewportBottom + new_h; } left = this->ViewportLeft; right = this->ViewportRight; } #if defined(VTKM_MSVC) #pragma warning(pop) #endif } } void Camera::Pan(vtkm::Float32 dx, vtkm::Float32 dy) { this->Camera3D.XPan += dx; this->Camera3D.YPan += dy; this->Camera2D.XPan += dx; this->Camera2D.YPan += dy; } void Camera::Zoom(vtkm::Float32 zoom) { vtkm::Float32 factor = vtkm::Pow(4.0f, zoom); this->Camera3D.Zoom *= factor; this->Camera3D.XPan *= factor; this->Camera3D.YPan *= factor; this->Camera2D.Zoom *= factor; this->Camera2D.XPan *= factor; this->Camera2D.YPan *= factor; } void Camera::TrackballRotate(vtkm::Float32 startX, vtkm::Float32 startY, vtkm::Float32 endX, vtkm::Float32 endY) { vtkm::Matrix rotate = MatrixHelpers::TrackballMatrix(startX, startY, endX, endY); //Translate matrix vtkm::Matrix translate = vtkm::Transform3DTranslate(-this->Camera3D.LookAt); //Translate matrix vtkm::Matrix inverseTranslate = vtkm::Transform3DTranslate(this->Camera3D.LookAt); vtkm::Matrix view = this->CreateViewMatrix(); view(0, 3) = 0; view(1, 3) = 0; view(2, 3) = 0; vtkm::Matrix inverseView = vtkm::MatrixTranspose(view); //fullTransform = inverseTranslate * inverseView * rotate * view * translate vtkm::Matrix fullTransform; fullTransform = vtkm::MatrixMultiply( inverseTranslate, vtkm::MatrixMultiply(inverseView, vtkm::MatrixMultiply(rotate, vtkm::MatrixMultiply(view, translate)))); this->Camera3D.Position = vtkm::Transform3DPoint(fullTransform, this->Camera3D.Position); this->Camera3D.LookAt = vtkm::Transform3DPoint(fullTransform, this->Camera3D.LookAt); this->Camera3D.ViewUp = vtkm::Transform3DVector(fullTransform, this->Camera3D.ViewUp); } namespace { void PadRange(vtkm::Range& range, vtkm::Float64 padding) { if (range.IsNonEmpty()) { vtkm::Float64 padAmount = padding * range.Length(); range.Min -= padAmount; range.Max += padAmount; } else { range.Include(0); } } } // anonymous namespace void Camera::ResetToBounds(const vtkm::Bounds& dataBounds, const vtkm::Float64 XDataViewPadding, const vtkm::Float64 YDataViewPadding, const vtkm::Float64 ZDataViewPadding) { // Save camera mode Mode saveMode = this->GetMode(); // Pad view around data extents vtkm::Bounds db = dataBounds; PadRange(db.X, XDataViewPadding); PadRange(db.Y, YDataViewPadding); PadRange(db.Z, ZDataViewPadding); // Reset for 3D camera vtkm::Vec3f_32 directionOfProjection = this->GetPosition() - this->GetLookAt(); vtkm::Normalize(directionOfProjection); vtkm::Vec3f_32 center = db.Center(); this->SetLookAt(center); vtkm::Vec3f_32 totalExtent; totalExtent[0] = vtkm::Float32(db.X.Length()); totalExtent[1] = vtkm::Float32(db.Y.Length()); totalExtent[2] = vtkm::Float32(db.Z.Length()); vtkm::Float32 diagonalLength = vtkm::Magnitude(totalExtent); if (diagonalLength < XDataViewPadding) { diagonalLength = static_cast(XDataViewPadding); } this->SetPosition(center + directionOfProjection * diagonalLength * 1.0f); this->SetFieldOfView(60.0f); this->SetClippingRange(0.1f * diagonalLength, diagonalLength * 10.0f); // Reset for 2D camera this->SetViewRange2D(db); // Reset pan and zoom this->Camera3D.XPan = 0; this->Camera3D.YPan = 0; this->Camera3D.Zoom = 1; this->Camera2D.XPan = 0; this->Camera2D.YPan = 0; this->Camera2D.Zoom = 1; // Restore camera mode this->SetMode(saveMode); } // Enable the ability to pad the data extents in the final view void Camera::ResetToBounds(const vtkm::Bounds& dataBounds, const vtkm::Float64 dataViewPadding) { Camera::ResetToBounds(dataBounds, dataViewPadding, dataViewPadding, dataViewPadding); } void Camera::ResetToBounds(const vtkm::Bounds& dataBounds) { Camera::ResetToBounds(dataBounds, 0); } void Camera::Roll(vtkm::Float32 angleDegrees) { vtkm::Vec3f_32 directionOfProjection = this->GetLookAt() - this->GetPosition(); vtkm::Matrix rotateTransform = vtkm::Transform3DRotate(angleDegrees, directionOfProjection); this->SetViewUp(vtkm::Transform3DVector(rotateTransform, this->GetViewUp())); } void Camera::Azimuth(vtkm::Float32 angleDegrees) { // Translate to the focal point (LookAt), rotate about view up, and // translate back again. vtkm::Matrix transform = vtkm::Transform3DTranslate(this->GetLookAt()); transform = vtkm::MatrixMultiply(transform, vtkm::Transform3DRotate(angleDegrees, this->GetViewUp())); transform = vtkm::MatrixMultiply(transform, vtkm::Transform3DTranslate(-this->GetLookAt())); this->SetPosition(vtkm::Transform3DPoint(transform, this->GetPosition())); } void Camera::Elevation(vtkm::Float32 angleDegrees) { vtkm::Vec3f_32 axisOfRotation = vtkm::Cross(this->GetPosition() - this->GetLookAt(), this->GetViewUp()); // Translate to the focal point (LookAt), rotate about the defined axis, // and translate back again. vtkm::Matrix transform = vtkm::Transform3DTranslate(this->GetLookAt()); transform = vtkm::MatrixMultiply(transform, vtkm::Transform3DRotate(angleDegrees, axisOfRotation)); transform = vtkm::MatrixMultiply(transform, vtkm::Transform3DTranslate(-this->GetLookAt())); this->SetPosition(vtkm::Transform3DPoint(transform, this->GetPosition())); } void Camera::Dolly(vtkm::Float32 value) { if (value <= vtkm::Epsilon32()) { return; } vtkm::Vec3f_32 lookAtToPos = this->GetPosition() - this->GetLookAt(); this->SetPosition(this->GetLookAt() + (1.0f / value) * lookAtToPos); } void Camera::Print() const { if (this->ModeType == Camera::Mode::ThreeD) { std::cout << "Camera: 3D" << std::endl; std::cout << " LookAt: " << Camera3D.LookAt << std::endl; std::cout << " Pos : " << Camera3D.Position << std::endl; std::cout << " Up : " << Camera3D.ViewUp << std::endl; std::cout << " FOV : " << GetFieldOfView() << std::endl; std::cout << " Clip : " << GetClippingRange() << std::endl; std::cout << " XyZ : " << Camera3D.XPan << " " << Camera3D.YPan << " " << Camera3D.Zoom << std::endl; vtkm::Matrix pm, vm; pm = CreateProjectionMatrix(512, 512); vm = CreateViewMatrix(); std::cout << " PM: " << std::endl; std::cout << pm[0][0] << " " << pm[0][1] << " " << pm[0][2] << " " << pm[0][3] << std::endl; std::cout << pm[1][0] << " " << pm[1][1] << " " << pm[1][2] << " " << pm[1][3] << std::endl; std::cout << pm[2][0] << " " << pm[2][1] << " " << pm[2][2] << " " << pm[2][3] << std::endl; std::cout << pm[3][0] << " " << pm[3][1] << " " << pm[3][2] << " " << pm[3][3] << std::endl; std::cout << " VM: " << std::endl; std::cout << vm[0][0] << " " << vm[0][1] << " " << vm[0][2] << " " << vm[0][3] << std::endl; std::cout << vm[1][0] << " " << vm[1][1] << " " << vm[1][2] << " " << vm[1][3] << std::endl; std::cout << vm[2][0] << " " << vm[2][1] << " " << vm[2][2] << " " << vm[2][3] << std::endl; std::cout << vm[3][0] << " " << vm[3][1] << " " << vm[3][2] << " " << vm[3][3] << std::endl; } else if (this->ModeType == Camera::Mode::TwoD) { std::cout << "Camera: 2D" << std::endl; std::cout << " LRBT: " << Camera2D.Left << " " << Camera2D.Right << " " << Camera2D.Bottom << " " << Camera2D.Top << std::endl; std::cout << " XY : " << Camera2D.XPan << " " << Camera2D.YPan << std::endl; std::cout << " SZ : " << Camera2D.XScale << " " << Camera2D.Zoom << std::endl; vtkm::Matrix pm, vm; pm = CreateProjectionMatrix(512, 512); vm = CreateViewMatrix(); std::cout << " PM: " << std::endl; std::cout << pm[0][0] << " " << pm[0][1] << " " << pm[0][2] << " " << pm[0][3] << std::endl; std::cout << pm[1][0] << " " << pm[1][1] << " " << pm[1][2] << " " << pm[1][3] << std::endl; std::cout << pm[2][0] << " " << pm[2][1] << " " << pm[2][2] << " " << pm[2][3] << std::endl; std::cout << pm[3][0] << " " << pm[3][1] << " " << pm[3][2] << " " << pm[3][3] << std::endl; std::cout << " VM: " << std::endl; std::cout << vm[0][0] << " " << vm[0][1] << " " << vm[0][2] << " " << vm[0][3] << std::endl; std::cout << vm[1][0] << " " << vm[1][1] << " " << vm[1][2] << " " << vm[1][3] << std::endl; std::cout << vm[2][0] << " " << vm[2][1] << " " << vm[2][2] << " " << vm[2][3] << std::endl; std::cout << vm[3][0] << " " << vm[3][1] << " " << vm[3][2] << " " << vm[3][3] << std::endl; } } } } // namespace vtkm::rendering