Merge branch 'stabilization/2110' into Atom/santorac/MaterialEditorHandlesMissingTextures
This commit is contained in:
@@ -80,6 +80,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
PRIVATE
|
||||
AZ::AzTest
|
||||
AZ::AzTestShared
|
||||
AZ::AzFrameworkTestShared
|
||||
Gem::AtomToolsFramework.Static
|
||||
Gem::Atom_Utils.TestUtils.Static
|
||||
)
|
||||
|
||||
+13
-12
@@ -112,15 +112,16 @@ namespace AtomToolsFramework
|
||||
void UpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event) override;
|
||||
|
||||
// ModularViewportCameraControllerRequestBus overrides ...
|
||||
void InterpolateToTransform(const AZ::Transform& worldFromLocal) override;
|
||||
AZ::Transform GetReferenceFrame() const override;
|
||||
void SetReferenceFrame(const AZ::Transform& worldFromLocal) override;
|
||||
void ClearReferenceFrame() override;
|
||||
bool InterpolateToTransform(const AZ::Transform& worldFromLocal) override;
|
||||
bool IsInterpolating() const override;
|
||||
void StartTrackingTransform(const AZ::Transform& worldFromLocal) override;
|
||||
void StopTrackingTransform() override;
|
||||
bool IsTrackingTransform() const override;
|
||||
|
||||
private:
|
||||
//! Update the reference frame after a change has been made to the camera
|
||||
//! view without updating the internal camera via user input.
|
||||
void RefreshReferenceFrame();
|
||||
//! Combine the current camera transform with any potential roll from the tracked
|
||||
//! transform (this is usually zero).
|
||||
AZ::Transform CombinedCameraTransform() const;
|
||||
|
||||
//! The current mode the camera controller is in.
|
||||
enum class CameraMode
|
||||
@@ -141,21 +142,21 @@ namespace AtomToolsFramework
|
||||
AzFramework::Camera m_camera; //!< The current camera state (pitch/yaw/position/look-distance).
|
||||
AzFramework::Camera m_targetCamera; //!< The target (next) camera state that m_camera is catching up to.
|
||||
AzFramework::Camera m_previousCamera; //!< The state of the camera from the previous frame.
|
||||
AZStd::optional<AzFramework::Camera> m_storedCamera; //!< A potentially stored camera for when a custom reference frame is set.
|
||||
AZStd::optional<AzFramework::Camera> m_storedCamera; //!< A potentially stored camera for when a transform is being tracked.
|
||||
AzFramework::CameraSystem m_cameraSystem; //!< The camera system responsible for managing all CameraInputs.
|
||||
AzFramework::CameraProps m_cameraProps; //!< Camera properties to control rotate and translate smoothness.
|
||||
CameraControllerPriorityFn m_priorityFn; //!< Controls at what priority the camera controller should respond to events.
|
||||
|
||||
CameraAnimation m_cameraAnimation; //!< Camera animation state (used during CameraMode::Animation).
|
||||
CameraMode m_cameraMode = CameraMode::Control; //!< The current mode the camera is operating in.
|
||||
//! An additional reference frame the camera can operate in (identity has no effect).
|
||||
AZ::Transform m_referenceFrameOverride = AZ::Transform::CreateIdentity();
|
||||
//! Flag to prevent circular updates of the camera transform (while the viewport transform is being updated internally).
|
||||
bool m_updatingTransformInternally = false;
|
||||
float m_roll = 0.0f; //!< The current amount of roll to be applied to the camera.
|
||||
float m_targetRoll = 0.0f; //!< The target amount of roll to be applied to the camera (current will move towards this).
|
||||
//! Listen for camera view changes outside of the camera controller.
|
||||
AZ::RPI::ViewportContext::MatrixChangedEvent::Handler m_cameraViewMatrixChangeHandler;
|
||||
//! The current instance of the modular camera viewport context.
|
||||
AZStd::unique_ptr<ModularCameraViewportContext> m_modularCameraViewportContext;
|
||||
//! Flag to prevent circular updates of the camera transform (while the viewport transform is being updated internally).
|
||||
bool m_updatingTransformInternally = false;
|
||||
};
|
||||
|
||||
//! Placeholder implementation for ModularCameraViewportContext (useful for verifying the interface).
|
||||
|
||||
+16
-8
@@ -23,23 +23,31 @@ namespace AtomToolsFramework
|
||||
class ModularViewportCameraControllerRequests : public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
static inline constexpr float InterpolateToTransformDuration = 1.0f;
|
||||
|
||||
using BusIdType = AzFramework::ViewportId;
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
||||
|
||||
//! Begin a smooth transition of the camera to the requested transform.
|
||||
//! @param worldFromLocal The transform of where the camera should end up.
|
||||
virtual void InterpolateToTransform(const AZ::Transform& worldFromLocal) = 0;
|
||||
//! @return Returns true if the call began an interpolation and false otherwise. Calls to InterpolateToTransform
|
||||
//! will have no effect if an interpolation is currently in progress.
|
||||
virtual bool InterpolateToTransform(const AZ::Transform& worldFromLocal) = 0;
|
||||
|
||||
//! Return the current reference frame.
|
||||
//! @note If a reference frame has not been set or a frame has been cleared, this is just the identity.
|
||||
virtual AZ::Transform GetReferenceFrame() const = 0;
|
||||
//! Returns if the camera is currently interpolating to a new transform.
|
||||
virtual bool IsInterpolating() const = 0;
|
||||
|
||||
//! Set a new reference frame other than the identity for the camera controller.
|
||||
virtual void SetReferenceFrame(const AZ::Transform& worldFromLocal) = 0;
|
||||
//! Start tracking a transform.
|
||||
//! Store the current camera transform and move to the next camera transform.
|
||||
virtual void StartTrackingTransform(const AZ::Transform& worldFromLocal) = 0;
|
||||
|
||||
//! Clear the current reference frame to restore the identity.
|
||||
virtual void ClearReferenceFrame() = 0;
|
||||
//! Stop tracking the set transform.
|
||||
//! The previously stored camera transform is restored.
|
||||
virtual void StopTrackingTransform() = 0;
|
||||
|
||||
//! Return if the tracking transform is set.
|
||||
virtual bool IsTrackingTransform() const = 0;
|
||||
|
||||
protected:
|
||||
~ModularViewportCameraControllerRequests() = default;
|
||||
|
||||
+7
-4
@@ -21,6 +21,7 @@
|
||||
#include <AzFramework/Windowing/WindowBus.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <Atom/RPI.Public/AuxGeom/AuxGeomFeatureProcessorInterface.h>
|
||||
#include <AtomToolsFramework/Viewport/ViewportInteractionImpl.h>
|
||||
|
||||
namespace AtomToolsFramework
|
||||
{
|
||||
@@ -30,8 +31,8 @@ namespace AtomToolsFramework
|
||||
//! @see AZ::RPI::ViewportContext for Atom's API for setting up
|
||||
class RenderViewportWidget
|
||||
: public QWidget
|
||||
, public AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus::Handler
|
||||
, public AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Handler
|
||||
, public AzToolsFramework::ViewportInteraction::ViewportInteractionRequests
|
||||
, public AzFramework::WindowRequestBus::Handler
|
||||
, protected AzFramework::InputChannelEventListener
|
||||
, protected AZ::TickBus::Handler
|
||||
@@ -90,11 +91,11 @@ namespace AtomToolsFramework
|
||||
//! Input processing is enabled by default.
|
||||
void SetInputProcessingEnabled(bool enabled);
|
||||
|
||||
// AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus::Handler overrides ...
|
||||
// ViewportInteractionRequests overrides ...
|
||||
AzFramework::CameraState GetCameraState() override;
|
||||
AzFramework::ScreenPoint ViewportWorldToScreen(const AZ::Vector3& worldPosition) override;
|
||||
AZStd::optional<AZ::Vector3> ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition, float depth) override;
|
||||
AZStd::optional<AzToolsFramework::ViewportInteraction::ProjectedViewportRay> ViewportScreenToWorldRay(
|
||||
AZ::Vector3 ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition) override;
|
||||
AzToolsFramework::ViewportInteraction::ProjectedViewportRay ViewportScreenToWorldRay(
|
||||
const AzFramework::ScreenPoint& screenPosition) override;
|
||||
float DeviceScalingFactor() override;
|
||||
|
||||
@@ -149,5 +150,7 @@ namespace AtomToolsFramework
|
||||
AZ::ScriptTimePoint m_time;
|
||||
// Maps our internal Qt events into AzFramework InputChannels for our ViewportControllerList.
|
||||
AzToolsFramework::QtEventToAzInputMapper* m_inputChannelMapper = nullptr;
|
||||
// Implementation of ViewportInteractionRequests (handles viewport picking operations).
|
||||
AZStd::unique_ptr<ViewportInteractionImpl> m_viewportInteractionImpl;
|
||||
};
|
||||
} //namespace AtomToolsFramework
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Atom/RPI.Public/Base.h>
|
||||
#include <Atom/RPI.Public/View.h>
|
||||
#include <AzFramework/Viewport/CameraState.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
|
||||
namespace AtomToolsFramework
|
||||
{
|
||||
//! A concrete implementation of the ViewportInteractionRequestBus.
|
||||
//! Primarily concerned with picking (screen to world and world to screen transformations).
|
||||
class ViewportInteractionImpl : public AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
explicit ViewportInteractionImpl(AZ::RPI::ViewPtr viewPtr);
|
||||
|
||||
void Connect(AzFramework::ViewportId viewportId);
|
||||
void Disconnect();
|
||||
|
||||
// ViewportInteractionRequestBus overrides ...
|
||||
AzFramework::CameraState GetCameraState() override;
|
||||
AzFramework::ScreenPoint ViewportWorldToScreen(const AZ::Vector3& worldPosition) override;
|
||||
AZ::Vector3 ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition) override;
|
||||
AzToolsFramework::ViewportInteraction::ProjectedViewportRay ViewportScreenToWorldRay(
|
||||
const AzFramework::ScreenPoint& screenPosition) override;
|
||||
float DeviceScalingFactor() override;
|
||||
|
||||
AZStd::function<AzFramework::ScreenSize()> m_screenSizeFn; //! Callback to determine the screen size.
|
||||
AZStd::function<float()> m_deviceScalingFactorFn; //! Callback to determine the device scaling factor.
|
||||
|
||||
private:
|
||||
AZ::RPI::ViewPtr m_viewPtr;
|
||||
};
|
||||
} // namespace AtomToolsFramework
|
||||
@@ -79,12 +79,18 @@ namespace AtomToolsFramework
|
||||
m_styleManager.reset(new AzQtComponents::StyleManager(this));
|
||||
m_styleManager->initialize(this, engineRootPath);
|
||||
|
||||
connect(&m_timer, &QTimer::timeout, this, [&]()
|
||||
m_timer.setInterval(1);
|
||||
connect(&m_timer, &QTimer::timeout, this, [this]()
|
||||
{
|
||||
this->PumpSystemEventLoopUntilEmpty();
|
||||
this->Tick();
|
||||
});
|
||||
|
||||
connect(this, &QGuiApplication::applicationStateChanged, this, [this]()
|
||||
{
|
||||
// Limit the update interval when not in focus to reduce power consumption and interference with other applications
|
||||
this->m_timer.setInterval((applicationState() & Qt::ApplicationActive) ? 1 : 32);
|
||||
});
|
||||
}
|
||||
|
||||
AtomToolsApplication ::~AtomToolsApplication()
|
||||
|
||||
@@ -9,11 +9,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Module/Module.h>
|
||||
#include <AzToolsFramework/API/PythonLoader.h>
|
||||
|
||||
namespace AtomToolsFramework
|
||||
{
|
||||
class AtomToolsFrameworkModule
|
||||
: public AZ::Module
|
||||
, public AzToolsFramework::EmbeddedPython::PythonLoader
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(AtomToolsFrameworkModule, "{B58B7CA8-98C9-4DC8-8607-E094989BBBE2}", AZ::Module);
|
||||
|
||||
+41
-29
@@ -12,6 +12,7 @@
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Math/Color.h>
|
||||
#include <AzCore/std/math.h>
|
||||
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
|
||||
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
|
||||
#include <AzFramework/Viewport/ScreenGeometry.h>
|
||||
@@ -175,16 +176,12 @@ namespace AtomToolsFramework
|
||||
// ignore these updates if the camera is being updated internally
|
||||
if (!m_updatingTransformInternally)
|
||||
{
|
||||
if (m_storedCamera.has_value())
|
||||
{
|
||||
// if an external change occurs ensure we update the stored reference frame if one is set
|
||||
RefreshReferenceFrame();
|
||||
return;
|
||||
}
|
||||
|
||||
m_previousCamera = m_targetCamera;
|
||||
UpdateCameraFromTransform(m_targetCamera, m_modularCameraViewportContext->GetCameraTransform());
|
||||
m_camera = m_targetCamera;
|
||||
|
||||
const AZ::Transform transform = m_modularCameraViewportContext->GetCameraTransform();
|
||||
const AZ::Vector3 eulerAngles = AzFramework::EulerAngles(AZ::Matrix3x3::CreateFromTransform(transform));
|
||||
UpdateCameraFromTranslationAndRotation(m_targetCamera, transform.GetTranslation(), eulerAngles);
|
||||
m_targetRoll = eulerAngles.GetY();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -227,7 +224,9 @@ namespace AtomToolsFramework
|
||||
{
|
||||
m_targetCamera = m_cameraSystem.StepCamera(m_targetCamera, event.m_deltaTime.count());
|
||||
m_camera = AzFramework::SmoothCamera(m_camera, m_targetCamera, m_cameraProps, event.m_deltaTime.count());
|
||||
m_modularCameraViewportContext->SetCameraTransform(m_referenceFrameOverride * m_camera.Transform());
|
||||
m_roll = AzFramework::SmoothValue(m_targetRoll, m_roll, m_cameraProps.m_rotateSmoothnessFn(), event.m_deltaTime.count());
|
||||
|
||||
m_modularCameraViewportContext->SetCameraTransform(CombinedCameraTransform());
|
||||
}
|
||||
else if (m_cameraMode == CameraMode::Animation)
|
||||
{
|
||||
@@ -236,7 +235,10 @@ namespace AtomToolsFramework
|
||||
return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f);
|
||||
};
|
||||
|
||||
m_cameraAnimation.m_time = AZ::GetClamp(m_cameraAnimation.m_time + event.m_deltaTime.count(), 0.0f, 1.0f);
|
||||
m_cameraAnimation.m_time = AZ::GetClamp(
|
||||
m_cameraAnimation.m_time +
|
||||
(event.m_deltaTime.count() / ModularViewportCameraControllerRequests::InterpolateToTransformDuration),
|
||||
0.0f, 1.0f);
|
||||
|
||||
const auto& [transformStart, transformEnd, animationTime] = m_cameraAnimation;
|
||||
|
||||
@@ -250,6 +252,7 @@ namespace AtomToolsFramework
|
||||
m_camera.m_yaw = eulerAngles.GetZ();
|
||||
m_camera.m_pivot = current.GetTranslation();
|
||||
m_camera.m_offset = AZ::Vector3::CreateZero();
|
||||
m_targetRoll = eulerAngles.GetY();
|
||||
m_targetCamera = m_camera;
|
||||
|
||||
m_modularCameraViewportContext->SetCameraTransform(current);
|
||||
@@ -257,55 +260,59 @@ namespace AtomToolsFramework
|
||||
if (animationTime >= 1.0f)
|
||||
{
|
||||
m_cameraMode = CameraMode::Control;
|
||||
RefreshReferenceFrame();
|
||||
}
|
||||
}
|
||||
|
||||
m_updatingTransformInternally = false;
|
||||
}
|
||||
|
||||
void ModularViewportCameraControllerInstance::InterpolateToTransform(const AZ::Transform& worldFromLocal)
|
||||
bool ModularViewportCameraControllerInstance::InterpolateToTransform(const AZ::Transform& worldFromLocal)
|
||||
{
|
||||
m_cameraMode = CameraMode::Animation;
|
||||
m_cameraAnimation = CameraAnimation{ m_referenceFrameOverride * m_camera.Transform(), worldFromLocal, 0.0f };
|
||||
if (!IsInterpolating())
|
||||
{
|
||||
m_cameraMode = CameraMode::Animation;
|
||||
m_cameraAnimation = CameraAnimation{ CombinedCameraTransform(), worldFromLocal, 0.0f };
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ::Transform ModularViewportCameraControllerInstance::GetReferenceFrame() const
|
||||
bool ModularViewportCameraControllerInstance::IsInterpolating() const
|
||||
{
|
||||
return m_referenceFrameOverride;
|
||||
return m_cameraMode == CameraMode::Animation;
|
||||
}
|
||||
|
||||
void ModularViewportCameraControllerInstance::SetReferenceFrame(const AZ::Transform& worldFromLocal)
|
||||
void ModularViewportCameraControllerInstance::StartTrackingTransform(const AZ::Transform& worldFromLocal)
|
||||
{
|
||||
if (!m_storedCamera.has_value())
|
||||
{
|
||||
m_storedCamera = m_previousCamera;
|
||||
}
|
||||
|
||||
m_referenceFrameOverride = worldFromLocal;
|
||||
m_targetCamera.m_pitch = 0.0f;
|
||||
m_targetCamera.m_yaw = 0.0f;
|
||||
const auto angles = AzFramework::EulerAngles(AZ::Matrix3x3::CreateFromQuaternion(worldFromLocal.GetRotation()));
|
||||
m_targetCamera.m_pitch = angles.GetX();
|
||||
m_targetCamera.m_yaw = angles.GetZ();
|
||||
m_targetCamera.m_offset = AZ::Vector3::CreateZero();
|
||||
m_targetCamera.m_pivot = AZ::Vector3::CreateZero();
|
||||
m_camera = m_targetCamera;
|
||||
m_targetCamera.m_pivot = worldFromLocal.GetTranslation();
|
||||
m_targetRoll = angles.GetY();
|
||||
}
|
||||
|
||||
void ModularViewportCameraControllerInstance::ClearReferenceFrame()
|
||||
void ModularViewportCameraControllerInstance::StopTrackingTransform()
|
||||
{
|
||||
m_referenceFrameOverride = AZ::Transform::CreateIdentity();
|
||||
|
||||
if (m_storedCamera.has_value())
|
||||
{
|
||||
m_targetCamera = m_storedCamera.value();
|
||||
m_camera = m_targetCamera;
|
||||
m_targetRoll = 0.0f;
|
||||
}
|
||||
|
||||
m_storedCamera.reset();
|
||||
}
|
||||
|
||||
void ModularViewportCameraControllerInstance::RefreshReferenceFrame()
|
||||
bool ModularViewportCameraControllerInstance::IsTrackingTransform() const
|
||||
{
|
||||
m_referenceFrameOverride = m_modularCameraViewportContext->GetCameraTransform() * m_camera.Transform().GetInverse();
|
||||
return m_storedCamera.has_value();
|
||||
}
|
||||
|
||||
AZ::Transform PlaceholderModularCameraViewportContextImpl::GetCameraTransform() const
|
||||
@@ -324,4 +331,9 @@ namespace AtomToolsFramework
|
||||
{
|
||||
handler.Connect(m_viewMatrixChangedEvent);
|
||||
}
|
||||
|
||||
AZ::Transform ModularViewportCameraControllerInstance::CombinedCameraTransform() const
|
||||
{
|
||||
return m_camera.Transform() * AZ::Transform::CreateFromMatrix3x3(AZ::Matrix3x3::CreateRotationY(m_targetRoll));
|
||||
}
|
||||
} // namespace AtomToolsFramework
|
||||
|
||||
@@ -75,7 +75,11 @@ namespace AtomToolsFramework
|
||||
m_defaultCamera = AZ::RPI::View::CreateView(cameraName, AZ::RPI::View::UsageFlags::UsageCamera);
|
||||
AZ::Interface<AZ::RPI::ViewportContextRequestsInterface>::Get()->PushView(m_viewportContext->GetName(), m_defaultCamera);
|
||||
|
||||
AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus::Handler::BusConnect(GetId());
|
||||
m_viewportInteractionImpl = AZStd::make_unique<ViewportInteractionImpl>(m_defaultCamera);
|
||||
m_viewportInteractionImpl->m_deviceScalingFactorFn = [this] { return aznumeric_cast<float>(devicePixelRatioF()); };
|
||||
m_viewportInteractionImpl->m_screenSizeFn = [this] { return AzFramework::ScreenSize(width(), height()); };
|
||||
m_viewportInteractionImpl->Connect(id);
|
||||
|
||||
AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Handler::BusConnect(GetId());
|
||||
AzFramework::InputChannelEventListener::Connect();
|
||||
AZ::TickBus::Handler::BusConnect();
|
||||
@@ -107,7 +111,7 @@ namespace AtomToolsFramework
|
||||
AZ::TickBus::Handler::BusDisconnect();
|
||||
AzFramework::InputChannelEventListener::Disconnect();
|
||||
AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Handler::BusDisconnect();
|
||||
AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus::Handler::BusDisconnect();
|
||||
m_viewportInteractionImpl->Disconnect();
|
||||
}
|
||||
|
||||
void RenderViewportWidget::LockRenderTargetSize(uint32_t width, uint32_t height)
|
||||
@@ -278,77 +282,23 @@ namespace AtomToolsFramework
|
||||
|
||||
AzFramework::CameraState RenderViewportWidget::GetCameraState()
|
||||
{
|
||||
AZ::RPI::ViewPtr currentView = m_viewportContext->GetDefaultView();
|
||||
if (currentView == nullptr)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// Build camera state from Atom camera transforms
|
||||
AzFramework::CameraState cameraState = AzFramework::CreateCameraFromWorldFromViewMatrix(
|
||||
currentView->GetViewToWorldMatrix(),
|
||||
AZ::Vector2{aznumeric_cast<float>(width()), aznumeric_cast<float>(height())}
|
||||
);
|
||||
AzFramework::SetCameraClippingVolumeFromPerspectiveFovMatrixRH(cameraState, currentView->GetViewToClipMatrix());
|
||||
|
||||
// Convert from Z-up
|
||||
AZStd::swap(cameraState.m_forward, cameraState.m_up);
|
||||
cameraState.m_forward = -cameraState.m_forward;
|
||||
|
||||
return cameraState;
|
||||
return m_viewportInteractionImpl->GetCameraState();
|
||||
}
|
||||
|
||||
AzFramework::ScreenPoint RenderViewportWidget::ViewportWorldToScreen(const AZ::Vector3& worldPosition)
|
||||
{
|
||||
if (AZ::RPI::ViewPtr currentView = m_viewportContext->GetDefaultView();
|
||||
currentView == nullptr)
|
||||
{
|
||||
return AzFramework::ScreenPoint(0, 0);
|
||||
}
|
||||
|
||||
return AzFramework::WorldToScreen(worldPosition, GetCameraState());
|
||||
return m_viewportInteractionImpl->ViewportWorldToScreen(worldPosition);
|
||||
}
|
||||
|
||||
AZStd::optional<AZ::Vector3> RenderViewportWidget::ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition, float depth)
|
||||
AZ::Vector3 RenderViewportWidget::ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition)
|
||||
{
|
||||
const auto& cameraProjection = m_viewportContext->GetCameraProjectionMatrix();
|
||||
const auto& cameraView = m_viewportContext->GetCameraViewMatrix();
|
||||
|
||||
const AZ::Vector4 normalizedScreenPosition {
|
||||
screenPosition.m_x * 2.f / width() - 1.0f,
|
||||
(height() - screenPosition.m_y) * 2.f / height() - 1.0f,
|
||||
1.f - depth, // [GFX TODO] [ATOM-1501] Currently we always assume reverse depth
|
||||
1.f
|
||||
};
|
||||
|
||||
AZ::Matrix4x4 worldFromScreen = cameraProjection * cameraView;
|
||||
worldFromScreen.InvertFull();
|
||||
|
||||
const AZ::Vector4 projectedPosition = worldFromScreen * normalizedScreenPosition;
|
||||
if (projectedPosition.GetW() == 0.0f)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return projectedPosition.GetAsVector3() / projectedPosition.GetW();
|
||||
return m_viewportInteractionImpl->ViewportScreenToWorld(screenPosition);
|
||||
}
|
||||
|
||||
AZStd::optional<AzToolsFramework::ViewportInteraction::ProjectedViewportRay> RenderViewportWidget::ViewportScreenToWorldRay(
|
||||
AzToolsFramework::ViewportInteraction::ProjectedViewportRay RenderViewportWidget::ViewportScreenToWorldRay(
|
||||
const AzFramework::ScreenPoint& screenPosition)
|
||||
{
|
||||
auto pos0 = ViewportScreenToWorld(screenPosition, 0.f);
|
||||
auto pos1 = ViewportScreenToWorld(screenPosition, 1.f);
|
||||
if (!pos0.has_value() || !pos1.has_value())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
pos0 = m_viewportContext->GetDefaultView()->GetViewToWorldMatrix().GetTranslation();
|
||||
AZ::Vector3 rayOrigin = pos0.value();
|
||||
AZ::Vector3 rayDirection = pos1.value() - pos0.value();
|
||||
rayDirection.Normalize();
|
||||
|
||||
return AzToolsFramework::ViewportInteraction::ProjectedViewportRay{rayOrigin, rayDirection};
|
||||
return m_viewportInteractionImpl->ViewportScreenToWorldRay(screenPosition);
|
||||
}
|
||||
|
||||
float RenderViewportWidget::DeviceScalingFactor()
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AtomToolsFramework/Viewport/ViewportInteractionImpl.h>
|
||||
#include <AzFramework/Viewport/ViewportScreen.h>
|
||||
|
||||
namespace AtomToolsFramework
|
||||
{
|
||||
ViewportInteractionImpl::ViewportInteractionImpl(AZ::RPI::ViewPtr viewPtr)
|
||||
: m_viewPtr(AZStd::move(viewPtr))
|
||||
{
|
||||
}
|
||||
|
||||
void ViewportInteractionImpl::Connect(const AzFramework::ViewportId viewportId)
|
||||
{
|
||||
AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus::Handler::BusConnect(viewportId);
|
||||
}
|
||||
|
||||
void ViewportInteractionImpl::Disconnect()
|
||||
{
|
||||
AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
AzFramework::CameraState ViewportInteractionImpl::GetCameraState()
|
||||
{
|
||||
// build camera state from atom camera transforms
|
||||
AzFramework::CameraState cameraState =
|
||||
AzFramework::CreateDefaultCamera(m_viewPtr->GetCameraTransform(), AzFramework::Vector2FromScreenSize(m_screenSizeFn()));
|
||||
AzFramework::SetCameraClippingVolumeFromPerspectiveFovMatrixRH(cameraState, m_viewPtr->GetViewToClipMatrix());
|
||||
return cameraState;
|
||||
}
|
||||
|
||||
AzFramework::ScreenPoint ViewportInteractionImpl::ViewportWorldToScreen(const AZ::Vector3& worldPosition)
|
||||
{
|
||||
return AzFramework::WorldToScreen(worldPosition, GetCameraState());
|
||||
}
|
||||
|
||||
AZ::Vector3 ViewportInteractionImpl::ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition)
|
||||
{
|
||||
return AzFramework::ScreenToWorld(screenPosition, GetCameraState());
|
||||
}
|
||||
|
||||
AzToolsFramework::ViewportInteraction::ProjectedViewportRay ViewportInteractionImpl::ViewportScreenToWorldRay(
|
||||
const AzFramework::ScreenPoint& screenPosition)
|
||||
{
|
||||
const AzFramework::CameraState cameraState = GetCameraState();
|
||||
const AZ::Vector3 rayOrigin = AzFramework::ScreenToWorld(screenPosition, cameraState);
|
||||
const AZ::Vector3 rayDirection = (rayOrigin - cameraState.m_position).GetNormalized();
|
||||
return AzToolsFramework::ViewportInteraction::ProjectedViewportRay{ rayOrigin, rayDirection };
|
||||
}
|
||||
|
||||
float ViewportInteractionImpl::DeviceScalingFactor()
|
||||
{
|
||||
return m_deviceScalingFactorFn();
|
||||
}
|
||||
} // namespace AtomToolsFramework
|
||||
@@ -12,16 +12,25 @@
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
class AtomToolsFrameworkTestEnvironment : public AZ::Test::ITestEnvironment
|
||||
{
|
||||
protected:
|
||||
void SetupEnvironment() override
|
||||
{
|
||||
AZ::AllocatorInstance<AZ::SystemAllocator>::Create();
|
||||
}
|
||||
|
||||
void TeardownEnvironment() override
|
||||
{
|
||||
AZ::AllocatorInstance<AZ::SystemAllocator>::Destroy();
|
||||
}
|
||||
};
|
||||
|
||||
class AtomToolsFrameworkTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
if (!AZ::AllocatorInstance<AZ::SystemAllocator>::IsReady())
|
||||
{
|
||||
AZ::AllocatorInstance<AZ::SystemAllocator>::Create(AZ::SystemAllocator::Descriptor());
|
||||
}
|
||||
|
||||
m_assetSystemStub.Activate();
|
||||
|
||||
RegisterSourceAsset("objects/upgrades/materials/supercondor.material");
|
||||
@@ -38,11 +47,6 @@ namespace UnitTest
|
||||
void TearDown() override
|
||||
{
|
||||
m_assetSystemStub.Deactivate();
|
||||
|
||||
if (AZ::AllocatorInstance<AZ::SystemAllocator>::IsReady())
|
||||
{
|
||||
AZ::AllocatorInstance<AZ::SystemAllocator>::Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterSourceAsset(const AZStd::string& path)
|
||||
@@ -73,5 +77,5 @@ namespace UnitTest
|
||||
ASSERT_EQ(AtomToolsFramework::GetExteralReferencePath("d:/project/assets/objects/upgrades/materials/supercondor.material", "d:/project/assets/materials/condor.material", 0), "materials/condor.material");
|
||||
}
|
||||
|
||||
AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV);
|
||||
AZ_UNIT_TEST_HOOK(new AtomToolsFrameworkTestEnvironment);
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Atom/RPI.Public/View.h>
|
||||
#include <AtomToolsFramework/Viewport/RenderViewportWidget.h>
|
||||
#include <AzCore/Math/IntersectSegment.h>
|
||||
#include <AzCore/Math/MatrixUtils.h>
|
||||
#include <AzCore/Name/NameDictionary.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzFramework/Viewport/ScreenGeometry.h>
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
|
||||
#include <Tests/Utils/Printers.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
class ViewportInteractionImplFixture : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
static inline constexpr AzFramework::ViewportId TestViewportId = 1234;
|
||||
static inline constexpr AzFramework::ScreenSize ScreenDimensions = AzFramework::ScreenSize(1280, 720);
|
||||
|
||||
static AzFramework::ScreenPoint ScreenCenter()
|
||||
{
|
||||
const auto halfScreenDimensions = ScreenDimensions * 0.5f;
|
||||
return AzFramework::ScreenPoint(halfScreenDimensions.m_width, halfScreenDimensions.m_height);
|
||||
}
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
AZ::NameDictionary::Create();
|
||||
|
||||
m_view = AZ::RPI::View::CreateView(AZ::Name("TestView"), AZ::RPI::View::UsageCamera);
|
||||
|
||||
const auto aspectRatio = aznumeric_cast<float>(ScreenDimensions.m_width) / aznumeric_cast<float>(ScreenDimensions.m_height);
|
||||
|
||||
AZ::Matrix4x4 viewToClipMatrix;
|
||||
AZ::MakePerspectiveFovMatrixRH(viewToClipMatrix, AZ::DegToRad(60.0f), aspectRatio, 0.1f, 1000.f, true);
|
||||
m_view->SetViewToClipMatrix(viewToClipMatrix);
|
||||
|
||||
m_viewportInteractionImpl = AZStd::make_unique<AtomToolsFramework::ViewportInteractionImpl>(m_view);
|
||||
|
||||
m_viewportInteractionImpl->m_deviceScalingFactorFn = []
|
||||
{
|
||||
return 1.0f;
|
||||
};
|
||||
m_viewportInteractionImpl->m_screenSizeFn = []
|
||||
{
|
||||
return ScreenDimensions;
|
||||
};
|
||||
|
||||
m_viewportInteractionImpl->Connect(TestViewportId);
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_viewportInteractionImpl->Disconnect();
|
||||
m_viewportInteractionImpl.reset();
|
||||
|
||||
m_view.reset();
|
||||
|
||||
AZ::NameDictionary::Destroy();
|
||||
}
|
||||
|
||||
AZ::RPI::ViewPtr m_view;
|
||||
AZStd::unique_ptr<AtomToolsFramework::ViewportInteractionImpl> m_viewportInteractionImpl;
|
||||
};
|
||||
|
||||
// transform a point from screen space to world space, and then from world space back to screen space
|
||||
AzFramework::ScreenPoint ScreenToWorldToScreen(
|
||||
const AzFramework::ScreenPoint& screenPoint,
|
||||
AzToolsFramework::ViewportInteraction::ViewportInteractionRequests& viewportInteractionRequests)
|
||||
{
|
||||
const auto worldResult = viewportInteractionRequests.ViewportScreenToWorld(screenPoint);
|
||||
return viewportInteractionRequests.ViewportWorldToScreen(worldResult);
|
||||
}
|
||||
|
||||
TEST_F(ViewportInteractionImplFixture, ViewportInteractionRequestsMapsFromScreenToWorldAndBack)
|
||||
{
|
||||
using AzFramework::ScreenPoint;
|
||||
|
||||
m_view->SetCameraTransform(AZ::Matrix3x4::CreateFromMatrix3x3AndTranslation(
|
||||
AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(90.0f)), AZ::Vector3(10.0f, 0.0f, 5.0f)));
|
||||
|
||||
{
|
||||
const auto expectedScreenPoint = ScreenPoint{ 600, 450 };
|
||||
const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, *m_viewportInteractionImpl);
|
||||
EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
|
||||
}
|
||||
|
||||
{
|
||||
auto expectedScreenPoint = ScreenCenter();
|
||||
const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, *m_viewportInteractionImpl);
|
||||
EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
|
||||
}
|
||||
|
||||
{
|
||||
const auto expectedScreenPoint = ScreenPoint{ 0, 0 };
|
||||
const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, *m_viewportInteractionImpl);
|
||||
EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
|
||||
}
|
||||
|
||||
{
|
||||
const auto expectedScreenPoint = ScreenPoint{ ScreenDimensions.m_width, ScreenDimensions.m_height };
|
||||
const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, *m_viewportInteractionImpl);
|
||||
EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ViewportInteractionImplFixture, ScreenToWorldReturnsPositionOnNearClipPlaneInWorldSpace)
|
||||
{
|
||||
m_view->SetCameraTransform(AZ::Matrix3x4::CreateFromMatrix3x3AndTranslation(
|
||||
AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(-90.0f)), AZ::Vector3(20.0f, 0.0f, 0.0f)));
|
||||
|
||||
const auto worldResult = m_viewportInteractionImpl->ViewportScreenToWorld(ScreenCenter());
|
||||
EXPECT_THAT(worldResult, IsClose(AZ::Vector3(20.1f, 0.0f, 0.0f)));
|
||||
}
|
||||
|
||||
// note: values produced by reproducing in the editor viewport
|
||||
TEST_F(ViewportInteractionImplFixture, WorldToScreenGivesExpectedScreenCoordinates)
|
||||
{
|
||||
using AzFramework::ScreenPoint;
|
||||
|
||||
{
|
||||
m_view->SetCameraTransform(AZ::Matrix3x4::CreateFromMatrix3x3AndTranslation(
|
||||
AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(160.0f)) * AZ::Matrix3x3::CreateRotationX(AZ::DegToRad(-18.0f)),
|
||||
AZ::Vector3(-21.0f, 2.5f, 6.0f)));
|
||||
|
||||
const auto screenResult = m_viewportInteractionImpl->ViewportWorldToScreen(AZ::Vector3(-21.0f, -1.5f, 5.0f));
|
||||
EXPECT_EQ(screenResult, ScreenPoint(420, 326));
|
||||
}
|
||||
|
||||
{
|
||||
m_view->SetCameraTransform(AZ::Matrix3x4::CreateFromMatrix3x3AndTranslation(
|
||||
AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(175.0f)) * AZ::Matrix3x3::CreateRotationX(AZ::DegToRad(-90.0f)),
|
||||
AZ::Vector3(-10.0f, -11.0f, 2.5f)));
|
||||
|
||||
const auto screenResult = m_viewportInteractionImpl->ViewportWorldToScreen(AZ::Vector3(-10.0f, -10.5f, 0.5f));
|
||||
EXPECT_EQ(screenResult, ScreenPoint(654, 515));
|
||||
}
|
||||
|
||||
{
|
||||
m_view->SetCameraTransform(AZ::Matrix3x4::CreateFromMatrix3x3AndTranslation(
|
||||
AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(70.0f)) * AZ::Matrix3x3::CreateRotationX(AZ::DegToRad(65.0f)),
|
||||
AZ::Vector3(-22.5f, -10.0f, 1.5f)));
|
||||
|
||||
const auto screenResult = m_viewportInteractionImpl->ViewportWorldToScreen(AZ::Vector3(-23.0f, -9.5f, 3.0f));
|
||||
EXPECT_EQ(screenResult, ScreenPoint(754, 340));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ViewportInteractionImplFixture, ScreenToWorldRayGivesGivesExpectedOriginAndDirection)
|
||||
{
|
||||
using AzFramework::ScreenPoint;
|
||||
|
||||
m_view->SetCameraTransform(AZ::Matrix3x4::CreateFromMatrix3x3AndTranslation(
|
||||
AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(34.0f)) * AZ::Matrix3x3::CreateRotationX(AZ::DegToRad(-24.0f)),
|
||||
AZ::Vector3(-9.3f, -9.8f, 4.0f)));
|
||||
|
||||
const auto ray = m_viewportInteractionImpl->ViewportScreenToWorldRay(ScreenPoint(832, 226));
|
||||
|
||||
float unused;
|
||||
auto intersection = AZ::Intersect::IntersectRaySphere(ray.origin, ray.direction, AZ::Vector3(-14.0f, 5.7f, 0.75f), 0.5f, unused);
|
||||
|
||||
EXPECT_EQ(intersection, AZ::Intersect::SphereIsectTypes::ISECT_RAY_SPHERE_ISECT);
|
||||
}
|
||||
} // namespace UnitTest
|
||||
@@ -28,6 +28,7 @@ set(FILES
|
||||
Include/AtomToolsFramework/Util/MaterialPropertyUtil.h
|
||||
Include/AtomToolsFramework/Util/Util.h
|
||||
Include/AtomToolsFramework/Viewport/RenderViewportWidget.h
|
||||
Include/AtomToolsFramework/Viewport/ViewportInteractionImpl.h
|
||||
Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h
|
||||
Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h
|
||||
Include/AtomToolsFramework/Window/AtomToolsMainWindow.h
|
||||
@@ -55,6 +56,7 @@ set(FILES
|
||||
Source/Util/Util.cpp
|
||||
Source/Viewport/RenderViewportWidget.cpp
|
||||
Source/Viewport/ModularViewportCameraController.cpp
|
||||
Source/Viewport/ViewportInteractionImpl.cpp
|
||||
Source/Window/AtomToolsMainWindow.cpp
|
||||
Source/Window/AtomToolsMainWindowSystemComponent.cpp
|
||||
Source/Window/AtomToolsMainWindowSystemComponent.h
|
||||
|
||||
@@ -8,4 +8,5 @@
|
||||
|
||||
set(FILES
|
||||
Tests/AtomToolsFrameworkTest.cpp
|
||||
Tests/ViewportInteractionImplTests.cpp
|
||||
)
|
||||
Reference in New Issue
Block a user