From 5e87250f6794759c469006f325f68482d8c8e9d5 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Thu, 27 May 2021 16:31:30 -0700 Subject: [PATCH] Fix viewport icon rendering on high DPI devices (#1006) * Clarify ViewportWorldToScreen being in widget space and add DeviceScalingFactor * -Fix viewport icons being draw wrong on high DPI displays -Fix loading viewport icons from absolute paths, which * Address review feedback, fix build --- .../ViewportInteraction.h | 1 + .../Source/ViewportInteraction.cpp | 7 ++- .../Viewport/ViewportMessages.h | 7 ++- Code/Sandbox/Editor/RenderViewport.h | 1 + .../Viewport/RenderViewportWidget.h | 1 + .../Source/Viewport/RenderViewportWidget.cpp | 8 +++- ...tomViewportDisplayIconsSystemComponent.cpp | 46 +++++++++++++------ .../AtomViewportDisplayIconsSystemComponent.h | 2 +- 8 files changed, 53 insertions(+), 20 deletions(-) diff --git a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ViewportInteraction.h b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ViewportInteraction.h index 884562d7e8..28971dc779 100644 --- a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ViewportInteraction.h +++ b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ViewportInteraction.h @@ -41,6 +41,7 @@ namespace AzManipulatorTestFramework AZStd::optional ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition, float depth) override; AZStd::optional ViewportScreenToWorldRay( const AzFramework::ScreenPoint& screenPosition) override; + float DeviceScalingFactor() override; private: // ViewportInteractionRequestBus ... bool GridSnappingEnabled(); diff --git a/Code/Framework/AzManipulatorTestFramework/Source/ViewportInteraction.cpp b/Code/Framework/AzManipulatorTestFramework/Source/ViewportInteraction.cpp index ebef9dea30..7d32187a74 100644 --- a/Code/Framework/AzManipulatorTestFramework/Source/ViewportInteraction.cpp +++ b/Code/Framework/AzManipulatorTestFramework/Source/ViewportInteraction.cpp @@ -127,4 +127,9 @@ namespace AzManipulatorTestFramework { return {}; } -} // namespace AzManipulatorTestFramework + + float ViewportInteraction::DeviceScalingFactor() + { + return 1.0f; + } +}// namespace AzManipulatorTestFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h index 8e91dc945d..91eee18cb7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h @@ -165,15 +165,18 @@ namespace AzToolsFramework virtual bool AngleSnappingEnabled() = 0; /// Return the angle snapping/step size. virtual float AngleStep() = 0; - /// Transform a point in world space to screen space coordinates. + /// Transform a point in world space to screen space coordinates in Qt Widget space. + /// Multiply by DeviceScalingFactor to get the position in viewport pixel space. virtual AzFramework::ScreenPoint ViewportWorldToScreen(const AZ::Vector3& worldPosition) = 0; - /// Transform a point in screen space coordinates to a vector in world space based on clip space depth. + /// Transform a point from Qt widget screen space to world space based on the given clip space depth. /// Depth specifies a relative camera depth to project in the range of [0.f, 1.f]. /// Returns the world space position if successful. virtual AZStd::optional ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition, float depth) = 0; /// Casts a point in screen space to a ray in world space originating from the viewport camera frustum's near plane. /// Returns a ray containing the ray's origin and a direction normal, if successful. virtual AZStd::optional ViewportScreenToWorldRay(const AzFramework::ScreenPoint& screenPosition) = 0; + /// Gets the DPI scaling factor that translates Qt widget space into viewport pixel space. + virtual float DeviceScalingFactor() = 0; protected: ~ViewportInteractionRequests() = default; diff --git a/Code/Sandbox/Editor/RenderViewport.h b/Code/Sandbox/Editor/RenderViewport.h index d70dd59b98..b45c92b1c8 100644 --- a/Code/Sandbox/Editor/RenderViewport.h +++ b/Code/Sandbox/Editor/RenderViewport.h @@ -200,6 +200,7 @@ public: { return {}; } + float DeviceScalingFactor() override { return 1.0f; } // AzToolsFramework::ViewportFreezeRequestBus bool IsViewportInputFrozen() override; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h index a41c8221f0..f2c120dbcd 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h @@ -98,6 +98,7 @@ namespace AtomToolsFramework AZStd::optional ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition, float depth) override; AZStd::optional ViewportScreenToWorldRay( const AzFramework::ScreenPoint& screenPosition) override; + float DeviceScalingFactor() override; //! Set interface for providing viewport specific settings (e.g. snapping properties). void SetViewportSettings(const AzToolsFramework::ViewportInteraction::ViewportSettings* viewportSettings); diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp index cee178c724..35edb3af5b 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp @@ -313,8 +313,7 @@ namespace AtomToolsFramework // Scale the size by the DPI of the platform to // get the proper size in pixels. const QSize uiWindowSize = size(); - const qreal deficePixelRatio = devicePixelRatioF(); - const QSize windowSize = uiWindowSize * deficePixelRatio; + const QSize windowSize = uiWindowSize * devicePixelRatioF(); const AzFramework::NativeWindowHandle windowId = reinterpret_cast(winId()); AzFramework::WindowNotificationBus::Event(windowId, &AzFramework::WindowNotifications::OnWindowResized, windowSize.width(), windowSize.height()); @@ -465,6 +464,11 @@ namespace AtomToolsFramework return AzToolsFramework::ViewportInteraction::ProjectedViewportRay{rayOrigin, rayDirection}; } + float RenderViewportWidget::DeviceScalingFactor() + { + return aznumeric_cast(devicePixelRatioF()); + } + AzFramework::ScreenPoint RenderViewportWidget::ViewportCursorScreenPosition() { return AzToolsFramework::ViewportInteraction::ScreenPointFromQPoint(m_mousePosition.toPoint()); diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp index dce83c3072..5fe8c50350 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -145,12 +146,24 @@ namespace AZ::Render } // Initialize our shader - auto viewportSize = viewportContext->GetViewportSize(); + AZ::Vector2 viewportSize; + { + AzFramework::WindowSize viewportWindowSize = viewportContext->GetViewportSize(); + viewportSize = AZ::Vector2{aznumeric_cast(viewportWindowSize.m_width), aznumeric_cast(viewportWindowSize.m_height)}; + } AZ::Data::Instance drawSrg = dynamicDraw->NewDrawSrg(); - drawSrg->SetConstant(m_viewportSizeIndex, AZ::Vector2(aznumeric_cast(viewportSize.m_width), aznumeric_cast(viewportSize.m_height))); + drawSrg->SetConstant(m_viewportSizeIndex,viewportSize); drawSrg->SetImageView(m_textureParameterIndex, image->GetImageView()); drawSrg->Compile(); + // Scale icons by screen DPI + float scalingFactor = 1.0f; + { + using ViewportRequestBus = AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus; + ViewportRequestBus::EventResult( + scalingFactor, drawParameters.m_viewport, &ViewportRequestBus::Events::DeviceScalingFactor); + } + AZ::Vector3 screenPosition; if (drawParameters.m_positionSpace == CoordinateSpace::ScreenSpace) { @@ -158,9 +171,11 @@ namespace AZ::Render } else if (drawParameters.m_positionSpace == CoordinateSpace::WorldSpace) { - using ViewportRequestBus = AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus; - AzFramework::ScreenPoint position; - ViewportRequestBus::EventResult(position, drawParameters.m_viewport, &ViewportRequestBus::Events::ViewportWorldToScreen, drawParameters.m_position); + // Calculate our screen space position using the viewport size + // We want this instead of RenderViewportWidget::WorldToScreen which works in QWidget virtual coordinate space + AzFramework::ScreenPoint position = AzFramework::WorldToScreen( + drawParameters.m_position, viewportContext->GetCameraViewMatrix(), viewportContext->GetCameraProjectionMatrix(), + viewportSize); screenPosition.SetX(aznumeric_cast(position.m_x)); screenPosition.SetY(aznumeric_cast(position.m_y)); } @@ -179,8 +194,8 @@ namespace AZ::Render { Vertex vertex; screenPosition.StoreToFloat3(vertex.m_position); - vertex.m_position[0] += offsetX * drawParameters.m_size.GetX(); - vertex.m_position[1] += offsetY * drawParameters.m_size.GetY(); + vertex.m_position[0] += offsetX * drawParameters.m_size.GetX() * scalingFactor; + vertex.m_position[1] += offsetY * drawParameters.m_size.GetY() * scalingFactor; vertex.m_color = drawParameters.m_color.ToU32(); vertex.m_uv[0] = u; vertex.m_uv[1] = v; @@ -197,8 +212,15 @@ namespace AZ::Render dynamicDraw->DrawIndexed(&vertices, vertices.size(), &indices, indices.size(), RHI::IndexFormat::Uint16, drawSrg); } - QString AtomViewportDisplayIconsSystemComponent::FindAssetPath(const QString& sourceRelativePath) const + QString AtomViewportDisplayIconsSystemComponent::FindAssetPath(const QString& path) const { + // If we get an absolute path, just use it. + QFileInfo pathInfo(path); + if (pathInfo.isAbsolute()) + { + return path; + } + bool found = false; AZStd::vector scanFolders; AzToolsFramework::AssetSystemRequestBus::BroadcastResult( @@ -212,9 +234,9 @@ namespace AZ::Render for (const auto& folder : scanFolders) { QDir dir(folder.data()); - if (dir.exists(sourceRelativePath)) + if (dir.exists(path)) { - return dir.absoluteFilePath(sourceRelativePath); + return dir.absoluteFilePath(path); } } @@ -256,10 +278,6 @@ namespace AZ::Render AzToolsFramework::EditorViewportIconDisplayInterface::IconId AtomViewportDisplayIconsSystemComponent::GetOrLoadIconForPath( AZStd::string_view path) { - AZ_Error( - "AtomViewportDisplayIconsSystemComponent", AzFramework::StringFunc::Path::IsRelative(path.data()), - "GetOrLoadIconForPath assumes that it will always be given a relative path, but got '%s'", path.data()); - // Check our cache to see if the image is already loaded auto existingEntryIt = AZStd::find_if(m_iconData.begin(), m_iconData.end(), [&path](const auto& iconData) { diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h index 0c7366f23b..b44957b51d 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h @@ -61,7 +61,7 @@ namespace AZ static constexpr QSize MinimumRenderedSvgSize = QSize(128, 128); static constexpr QImage::Format QtImageFormat = QImage::Format_RGBA8888; - QString FindAssetPath(const QString& sourceRelativePath) const; + QString FindAssetPath(const QString& path) const; QImage RenderSvgToImage(const QString& svgPath) const; AZ::Data::Instance ConvertToAtomImage(AZ::Uuid assetId, QImage image) const;