Atom Tools: Extracted the viewport input controller from ME to ATF

These viewport controls will be shared with material canvas and other tools that share a similar environment and viewport configurations.
These classes are currently duplicated between the material editor and prototype projects.
This change generalizes some things that are specific to the material editor and moves the system to a common location.
Event buses were removed and replaced with a normal interface.

Signed-off-by: Guthrie Adams <guthadam@amazon.com>
This commit is contained in:
Guthrie Adams
2022-02-14 03:29:14 -06:00
parent deeb59cb83
commit cd0079c199
25 changed files with 460 additions and 525 deletions
@@ -1,166 +0,0 @@
/*
* 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 <AzCore/Component/TransformBus.h>
#include <AzCore/Math/Vector3.h>
#include <Viewport/InputController/Behavior.h>
#include <Viewport/InputController/MaterialEditorViewportInputController.h>
namespace MaterialEditor
{
Behavior::Behavior()
{
AZ::TickBus::Handler::BusConnect();
}
Behavior::~Behavior()
{
AZ::TickBus::Handler::BusDisconnect();
}
void Behavior::Start()
{
m_x = 0;
m_y = 0;
m_z = 0;
MaterialEditorViewportInputControllerRequestBus::BroadcastResult(
m_cameraEntityId,
&MaterialEditorViewportInputControllerRequestBus::Handler::GetCameraEntityId);
AZ_Assert(m_cameraEntityId.IsValid(), "Failed to find m_cameraEntityId");
MaterialEditorViewportInputControllerRequestBus::BroadcastResult(
m_distanceToTarget,
&MaterialEditorViewportInputControllerRequestBus::Handler::GetDistanceToTarget);
MaterialEditorViewportInputControllerRequestBus::BroadcastResult(
m_targetPosition,
&MaterialEditorViewportInputControllerRequestBus::Handler::GetTargetPosition);
MaterialEditorViewportInputControllerRequestBus::BroadcastResult(
m_radius, &MaterialEditorViewportInputControllerRequestBus::Handler::GetRadius);
}
void Behavior::End()
{
}
void Behavior::MoveX(float value)
{
m_x += value * GetSensitivityX();
}
void Behavior::MoveY(float value)
{
m_y += value * GetSensitivityY();
}
void Behavior::MoveZ(float value)
{
m_z += value * GetSensitivityZ();
}
bool Behavior::HasDelta() const
{
return
AZ::GetAbs(m_x) > std::numeric_limits<float>::min() ||
AZ::GetAbs(m_y) > std::numeric_limits<float>::min() ||
AZ::GetAbs(m_z) > std::numeric_limits<float>::min();
}
void Behavior::TickInternal([[maybe_unused]] float x, [[maybe_unused]] float y, float z)
{
m_distanceToTarget = m_distanceToTarget - z;
bool isCameraCentered = false;
MaterialEditorViewportInputControllerRequestBus::BroadcastResult(
isCameraCentered,
&MaterialEditorViewportInputControllerRequestBus::Handler::IsCameraCentered);
// if camera is looking at the model (locked to the model) we don't want to zoom past the model's center
if (isCameraCentered)
{
m_distanceToTarget = AZ::GetMax(m_distanceToTarget, 0.0f);
}
AZ::Transform transform = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(transform, m_cameraEntityId, &AZ::TransformBus::Events::GetLocalTM);
AZ::Vector3 position = m_targetPosition -
transform.GetRotation().TransformVector(AZ::Vector3::CreateAxisY(m_distanceToTarget));
AZ::TransformBus::Event(m_cameraEntityId, &AZ::TransformBus::Events::SetLocalTranslation, position);
// if camera is not locked to the model, move its focal point so we can free look
if (!isCameraCentered)
{
m_targetPosition += transform.GetRotation().TransformVector(AZ::Vector3::CreateAxisY(z));
MaterialEditorViewportInputControllerRequestBus::Broadcast(
&MaterialEditorViewportInputControllerRequestBus::Handler::SetTargetPosition,
m_targetPosition);
MaterialEditorViewportInputControllerRequestBus::BroadcastResult(
m_distanceToTarget,
&MaterialEditorViewportInputControllerRequestBus::Handler::GetDistanceToTarget);
}
}
float Behavior::GetSensitivityX()
{
return 0;
}
float Behavior::GetSensitivityY()
{
return 0;
}
float Behavior::GetSensitivityZ()
{
// adjust zooming sensitivity by model size, so that large models zoom at the same speed as smaller ones
return 0.001f * AZ::GetMax(0.5f, m_radius);
}
AZ::Quaternion Behavior::LookRotation(AZ::Vector3 forward)
{
forward.Normalize();
AZ::Vector3 right = forward.CrossZAxis();
right.Normalize();
AZ::Vector3 up = right.Cross(forward);
up.Normalize();
AZ::Quaternion rotation = AZ::Quaternion::CreateFromBasis(right, forward, up);
rotation.Normalize();
return rotation;
}
float Behavior::TakeStep(float& value, float t)
{
const float absValue = AZ::GetAbs(value);
float step;
if (absValue < SnapInterval)
{
step = value;
}
else
{
step = AZ::Lerp(0, value, t);
}
value -= step;
return step;
}
void Behavior::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{
// delta x and y values are accumulated in MoveX and MoveY functions (by dragging the mouse)
// in the Tick function we then lerp them down to 0 over short time and apply delta transform to an entity
if (HasDelta())
{
// t is a lerp amount based on time between frames (deltaTime)
// GetMin restricts how much we can lerp in case of low fps (and very high deltaTime)
const float t = AZ::GetMin(deltaTime / LerpTime, 0.5f);
const float x = TakeStep(m_x, t);
const float y = TakeStep(m_y, t);
const float z = TakeStep(m_z, t);
TickInternal(x, y, z);
}
}
} // namespace MaterialEditor
@@ -1,64 +0,0 @@
/*
* 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 <AzCore/Component/EntityId.h>
#include <AzCore/Math/Quaternion.h>
#include <AzCore/Component/TickBus.h>
namespace MaterialEditor
{
//! Performs a single type of action for MaterialEditorViewportInputController based on input
//! See derived behaviors for specific details
class Behavior
: public AZ::TickBus::Handler
{
public:
Behavior();
virtual ~Behavior();
virtual void Start();
virtual void End();
virtual void MoveX(float value);
virtual void MoveY(float value);
virtual void MoveZ(float value);
protected:
bool HasDelta() const;
virtual void TickInternal(float x, float y, float z);
virtual float GetSensitivityX();
virtual float GetSensitivityY();
virtual float GetSensitivityZ();
//! Calculate rotation quaternion towards a forward vector along world up axis
static AZ::Quaternion LookRotation(AZ::Vector3 forward);
//! Lerp a float value to 0 then decrement it by that interval and return it
static float TakeStep(float& value, float t);
//! Time in seconds to approximately complete a transformation
static constexpr float LerpTime = 0.05f;
//! If delta transform less than this, snap instantly
static constexpr float SnapInterval = 0.01f;
//! delta x movement accumulated during current frame
float m_x = 0;
//! delta y movement accumulated during current frame
float m_y = 0;
//! delta scroll wheel accumulated during current frame
float m_z = 0;
//! Model radius
float m_radius = 1.0f;
AZ::EntityId m_cameraEntityId;
AZ::Vector3 m_targetPosition = AZ::Vector3::CreateZero();
float m_distanceToTarget = 0;
private:
// AZ::TickBus::Handler interface overrides...
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
};
} // namespace MaterialEditor
@@ -1,36 +0,0 @@
/*
* 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 <AzCore/Component/TransformBus.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Math/Vector3.h>
#include <Viewport/InputController/DollyCameraBehavior.h>
#include <Viewport/InputController/MaterialEditorViewportInputController.h>
namespace MaterialEditor
{
void DollyCameraBehavior::TickInternal([[maybe_unused]] float x, float y, [[maybe_unused]] float z)
{
m_distanceToTarget = m_distanceToTarget + y;
AZ::Transform transform = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(transform, m_cameraEntityId, &AZ::TransformBus::Events::GetLocalTM);
AZ::Vector3 position = m_targetPosition -
transform.GetRotation().TransformVector(AZ::Vector3::CreateAxisY(m_distanceToTarget));
AZ::TransformBus::Event(m_cameraEntityId, &AZ::TransformBus::Events::SetLocalTranslation, position);
}
float DollyCameraBehavior::GetSensitivityX()
{
return SensitivityX;
}
float DollyCameraBehavior::GetSensitivityY()
{
return SensitivityY;
}
} // namespace MaterialEditor
@@ -1,30 +0,0 @@
/*
* 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 <Viewport/InputController/Behavior.h>
namespace MaterialEditor
{
//! Moves(zooms) camera back and forth towards the target
class DollyCameraBehavior final
: public Behavior
{
public:
DollyCameraBehavior() = default;
virtual ~DollyCameraBehavior() = default;
protected:
void TickInternal(float x, float y, float z) override;
float GetSensitivityX() override;
float GetSensitivityY() override;
private:
static constexpr float SensitivityX = 0;
static constexpr float SensitivityY = 0.005f;
};
} // namespace MaterialEditor
@@ -1,13 +0,0 @@
/*
* 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 <Viewport/InputController/IdleBehavior.h>
namespace MaterialEditor
{
} // namespace MaterialEditor
@@ -1,22 +0,0 @@
/*
* 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 <Viewport/InputController/Behavior.h>
namespace MaterialEditor
{
//! No action taken
class IdleBehavior final
: public Behavior
{
public:
IdleBehavior() = default;
virtual ~IdleBehavior() = default;
};
} // namespace MaterialEditor
@@ -1,352 +0,0 @@
/*
* 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 <QApplication>
#include <QWidget>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Math/Matrix4x4.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Components/CameraBus.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <Atom/Feature/SkyBox/SkyBoxFeatureProcessorInterface.h>
#include <Atom/RPI.Public/RPISystemInterface.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Reflect/Model/ModelAsset.h>
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
#include <Viewport/InputController/DollyCameraBehavior.h>
#include <Viewport/InputController/IdleBehavior.h>
#include <Viewport/InputController/MaterialEditorViewportInputController.h>
#include <Viewport/InputController/MoveCameraBehavior.h>
#include <Viewport/InputController/OrbitCameraBehavior.h>
#include <Viewport/InputController/PanCameraBehavior.h>
#include <Viewport/InputController/RotateEnvironmentBehavior.h>
#include <Viewport/InputController/RotateModelBehavior.h>
namespace MaterialEditor
{
MaterialEditorViewportInputController::MaterialEditorViewportInputController()
: AzFramework::SingleViewportController()
, m_targetPosition(AZ::Vector3::CreateZero())
{
m_behaviorMap[None] = AZStd::make_shared<IdleBehavior>();
m_behaviorMap[Lmb] = AZStd::make_shared<PanCameraBehavior>();
m_behaviorMap[Mmb] = AZStd::make_shared<MoveCameraBehavior>();
m_behaviorMap[Rmb] = AZStd::make_shared<OrbitCameraBehavior>();
m_behaviorMap[Alt ^ Lmb] = AZStd::make_shared<OrbitCameraBehavior>();
m_behaviorMap[Alt ^ Mmb] = AZStd::make_shared<MoveCameraBehavior>();
m_behaviorMap[Alt ^ Rmb] = AZStd::make_shared<DollyCameraBehavior>();
m_behaviorMap[Lmb ^ Rmb] = AZStd::make_shared<DollyCameraBehavior>();
m_behaviorMap[Ctrl ^ Lmb] = AZStd::make_shared<RotateModelBehavior>();
m_behaviorMap[Shift ^ Lmb] = AZStd::make_shared<RotateEnvironmentBehavior>();
}
MaterialEditorViewportInputController::~MaterialEditorViewportInputController()
{
if (m_initialized)
{
MaterialEditorViewportInputControllerRequestBus::Handler::BusDisconnect();
}
}
void MaterialEditorViewportInputController::Init(const AZ::EntityId& cameraEntityId, const AZ::EntityId& targetEntityId, const AZ::EntityId& iblEntityId)
{
if (m_initialized)
{
AZ_Error("MaterialEditorViewportInputController", false, "Controller already initialized.");
return;
}
m_initialized = true;
m_cameraEntityId = cameraEntityId;
m_targetEntityId = targetEntityId;
m_iblEntityId = iblEntityId;
MaterialEditorViewportInputControllerRequestBus::Handler::BusConnect();
}
const AZ::EntityId& MaterialEditorViewportInputController::GetCameraEntityId() const
{
return m_cameraEntityId;
}
const AZ::EntityId& MaterialEditorViewportInputController::GetTargetEntityId() const
{
return m_targetEntityId;
}
const AZ::EntityId& MaterialEditorViewportInputController::GetIblEntityId() const
{
return m_iblEntityId;
}
const AZ::Vector3& MaterialEditorViewportInputController::GetTargetPosition() const
{
return m_targetPosition;
}
void MaterialEditorViewportInputController::SetTargetPosition(const AZ::Vector3& targetPosition)
{
m_targetPosition = targetPosition;
m_isCameraCentered = false;
}
float MaterialEditorViewportInputController::GetDistanceToTarget() const
{
AZ::Vector3 cameraPosition;
AZ::TransformBus::EventResult(cameraPosition, m_cameraEntityId, &AZ::TransformBus::Events::GetLocalTranslation);
return cameraPosition.GetDistance(m_targetPosition);
}
void MaterialEditorViewportInputController::GetExtents(float& distanceMin, float& distanceMax) const
{
distanceMin = m_distanceMin;
distanceMax = m_distanceMax;
}
float MaterialEditorViewportInputController::GetRadius() const
{
return m_radius;
}
void MaterialEditorViewportInputController::UpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event)
{
if (m_keysChanged)
{
if (m_timeToBehaviorSwitchMs > 0)
{
m_timeToBehaviorSwitchMs -= event.m_deltaTime.count();
}
if (m_timeToBehaviorSwitchMs <= 0)
{
EvaluateControlBehavior();
m_keysChanged = false;
}
}
}
bool MaterialEditorViewportInputController::HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event)
{
using namespace AzFramework;
const InputChannelId& inputChannelId = event.m_inputChannel.GetInputChannelId();
const InputChannel::State state = event.m_inputChannel.GetState();
const KeyMask keysOld = m_keys;
bool mouseOver = false;
AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::EventResult(
mouseOver, GetViewportId(),
&AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Events::IsMouseOver);
if (!m_behavior)
{
EvaluateControlBehavior();
}
switch (state)
{
case InputChannel::State::Began:
if (inputChannelId == InputDeviceMouse::Button::Left)
{
m_keys |= Lmb;
}
else if (inputChannelId == InputDeviceMouse::Button::Middle)
{
m_keys |= Mmb;
}
else if (inputChannelId == InputDeviceMouse::Button::Right)
{
m_keys |= Rmb;
}
else if (inputChannelId == InputDeviceKeyboard::Key::ModifierAltL)
{
m_keys |= Alt;
}
else if (inputChannelId == InputDeviceKeyboard::Key::ModifierCtrlL)
{
m_keys |= Ctrl;
}
else if (inputChannelId == InputDeviceKeyboard::Key::ModifierShiftL)
{
m_keys |= Shift;
}
if (inputChannelId == InputDeviceMouse::Movement::X)
{
m_behavior->MoveX(event.m_inputChannel.GetValue());
}
else if (inputChannelId == InputDeviceMouse::Movement::Y)
{
m_behavior->MoveY(event.m_inputChannel.GetValue());
}
else if (inputChannelId == InputDeviceMouse::Movement::Z)
{
if (mouseOver)
{
m_behavior->MoveZ(event.m_inputChannel.GetValue());
}
}
break;
case InputChannel::State::Ended:
if (inputChannelId == InputDeviceMouse::Button::Left)
{
m_keys &= ~Lmb;
}
else if (inputChannelId == InputDeviceMouse::Button::Middle)
{
m_keys &= ~Mmb;
}
else if (inputChannelId == InputDeviceMouse::Button::Right)
{
m_keys &= ~Rmb;
}
else if (inputChannelId == InputDeviceKeyboard::Key::ModifierAltL)
{
m_keys &= ~Alt;
}
else if (inputChannelId == InputDeviceKeyboard::Key::ModifierCtrlL)
{
m_keys &= ~Ctrl;
}
else if (inputChannelId == InputDeviceKeyboard::Key::ModifierShiftL)
{
m_keys &= ~Shift;
}
else if (inputChannelId == InputDeviceKeyboard::Key::AlphanumericZ && (m_keys & Ctrl) == None)
{
// only reset camera if no other widget besides viewport is in focus
const auto focus = QApplication::focusWidget();
if (!focus || focus->objectName() == "Viewport")
{
Reset();
}
}
break;
case InputChannel::State::Updated:
if (inputChannelId == InputDeviceMouse::Movement::X)
{
m_behavior->MoveX(event.m_inputChannel.GetValue());
}
else if (inputChannelId == InputDeviceMouse::Movement::Y)
{
m_behavior->MoveY(event.m_inputChannel.GetValue());
}
else if (inputChannelId == InputDeviceMouse::Movement::Z)
{
if (mouseOver)
{
m_behavior->MoveZ(event.m_inputChannel.GetValue());
}
}
break;
}
if (keysOld != m_keys)
{
m_keysChanged = true;
m_timeToBehaviorSwitchMs = BehaviorSwitchDelayMs;
}
return false;
}
void MaterialEditorViewportInputController::Reset()
{
CalculateExtents();
// reset camera
m_targetPosition = m_modelCenter;
const float distance = m_distanceMin * StartingDistanceMultiplier;
const AZ::Quaternion cameraRotation = AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisZ(), StartingRotationAngle);
AZ::Vector3 cameraPosition(m_targetPosition.GetX(), m_targetPosition.GetY() - distance, m_targetPosition.GetZ());
cameraPosition = cameraRotation.TransformVector(cameraPosition);
AZ::Transform cameraTransform = AZ::Transform::CreateFromQuaternionAndTranslation(cameraRotation, cameraPosition);
AZ::TransformBus::Event(m_cameraEntityId, &AZ::TransformBus::Events::SetLocalTM, cameraTransform);
m_isCameraCentered = true;
// reset model
AZ::Transform modelTransform = AZ::Transform::CreateIdentity();
AZ::TransformBus::Event(m_targetEntityId, &AZ::TransformBus::Events::SetLocalTM, modelTransform);
// reset environment
AZ::Transform iblTransform = AZ::Transform::CreateIdentity();
AZ::TransformBus::Event(m_iblEntityId, &AZ::TransformBus::Events::SetLocalTM, iblTransform);
const AZ::Matrix4x4 rotationMatrix = AZ::Matrix4x4::CreateIdentity();
auto skyBoxFeatureProcessorInterface = AZ::RPI::Scene::GetFeatureProcessorForEntity<AZ::Render::SkyBoxFeatureProcessorInterface>(m_iblEntityId);
skyBoxFeatureProcessorInterface->SetCubemapRotationMatrix(rotationMatrix);
if (m_behavior)
{
m_behavior->End();
m_behavior->Start();
}
}
void MaterialEditorViewportInputController::SetFieldOfView(float value)
{
Camera::CameraRequestBus::Event(m_cameraEntityId, &Camera::CameraRequestBus::Events::SetFovDegrees, value);
}
bool MaterialEditorViewportInputController::IsCameraCentered() const
{
return m_isCameraCentered;
}
void MaterialEditorViewportInputController::CalculateExtents()
{
AZ::TransformBus::EventResult(m_modelCenter, m_targetEntityId, &AZ::TransformBus::Events::GetLocalTranslation);
AZ::Data::AssetId modelAssetId;
AZ::Render::MeshComponentRequestBus::EventResult(modelAssetId, m_targetEntityId,
&AZ::Render::MeshComponentRequestBus::Events::GetModelAssetId);
if (modelAssetId.IsValid())
{
AZ::Data::Asset<AZ::RPI::ModelAsset> modelAsset = AZ::Data::AssetManager::Instance().GetAsset(modelAssetId, azrtti_typeid<AZ::RPI::ModelAsset>(), AZ::Data::AssetLoadBehavior::PreLoad);
modelAsset.BlockUntilLoadComplete();
if (modelAsset.IsReady())
{
const AZ::Aabb& aabb = modelAsset->GetAabb();
aabb.GetAsSphere(m_modelCenter, m_radius);
m_distanceMin = 0.5f * AZ::GetMin(AZ::GetMin(aabb.GetExtents().GetX(), aabb.GetExtents().GetY()), aabb.GetExtents().GetZ()) + DepthNear;
m_distanceMax = m_radius * MaxDistanceMultiplier;
}
}
}
void MaterialEditorViewportInputController::EvaluateControlBehavior()
{
AZStd::shared_ptr<Behavior> nextBehavior;
auto it = m_behaviorMap.find(m_keys);
if (it == m_behaviorMap.end())
{
nextBehavior = m_behaviorMap[None];
}
else
{
nextBehavior = it->second;
}
if (nextBehavior == m_behavior)
{
return;
}
if (m_behavior)
{
m_behavior->End();
}
m_behavior = nextBehavior;
m_behavior->Start();
}
} // namespace MaterialEditor
@@ -1,109 +0,0 @@
/*
* 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 <AzFramework/Input/Events/InputChannelEventListener.h>
#include <AzFramework/Viewport/SingleViewportController.h>
#include <Viewport/InputController/Behavior.h>
#include <Viewport/InputController/MaterialEditorViewportInputControllerBus.h>
namespace MaterialEditor
{
class Behavior;
//! Provides controls for manipulating camera, model, and environment in Material Editor
class MaterialEditorViewportInputController
: public AzFramework::SingleViewportController
, public MaterialEditorViewportInputControllerRequestBus::Handler
{
public:
AZ_TYPE_INFO(MaterialEditorViewportInputController, "{569A0544-7654-4DCE-8156-00A71B408374}");
AZ_CLASS_ALLOCATOR(MaterialEditorViewportInputController, AZ::SystemAllocator, 0)
MaterialEditorViewportInputController();
virtual ~MaterialEditorViewportInputController();
void Init(const AZ::EntityId& cameraEntityId, const AZ::EntityId& targetEntityId, const AZ::EntityId& iblEntityId);
// MaterialEditorViewportInputControllerRequestBus::Handler interface overrides...
const AZ::EntityId& GetCameraEntityId() const override;
const AZ::EntityId& GetTargetEntityId() const override;
const AZ::EntityId& GetIblEntityId() const override;
const AZ::Vector3& GetTargetPosition() const override;
void SetTargetPosition(const AZ::Vector3& targetPosition) override;
float GetDistanceToTarget() const override;
void GetExtents(float& distanceMin, float& distanceMax) const override;
float GetRadius() const override;
void Reset() override;
void SetFieldOfView(float value) override;
bool IsCameraCentered() const override;
// AzFramework::ViewportControllerInstance interface overrides...
bool HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event) override;
void UpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event) override;
private:
using KeyMask = uint32_t;
enum Keys
{
None = 0,
Lmb = 1 << 0,
Mmb = 1 << 1,
Rmb = 1 << 2,
Alt = 1 << 3,
Ctrl = 1 << 4,
Shift = 1 << 5
};
//! Calculate min and max dist and center based on mesh size of target model
void CalculateExtents();
//! Determine which behavior to set based on mouse/keyboard input
void EvaluateControlBehavior();
bool m_initialized = false;
//! Input keys currently pressed
KeyMask m_keys = None;
//! Input key sequence changed
bool m_keysChanged = false;
//! Time remaining before behavior switch
float m_timeToBehaviorSwitchMs = 0;
//! Current behavior of the controller
AZStd::shared_ptr<Behavior> m_behavior;
AZStd::unordered_map<KeyMask, AZStd::shared_ptr<Behavior>> m_behaviorMap;
AZ::EntityId m_cameraEntityId;
//! Target entity is looking at
AZ::EntityId m_targetEntityId;
//! IBL entity for rotating environment lighting
AZ::EntityId m_iblEntityId;
//! Target position camera is pointed towards
AZ::Vector3 m_targetPosition;
//! Center of the model observed
AZ::Vector3 m_modelCenter;
//! Minimum distance from camera to target
float m_distanceMin = 1.0f;
//! Maximum distance from camera to target
float m_distanceMax = 10.0f;
//! Model radius
float m_radius = 1.0f;
//! True if camera is centered on a model
bool m_isCameraCentered = true;
static constexpr float MaxDistanceMultiplier = 2.5f;
static constexpr float StartingDistanceMultiplier = 2.0f;
static constexpr float StartingRotationAngle = AZ::Constants::QuarterPi / 2.0f;
static constexpr float DepthNear = 0.01f;
//! Artificial delay between behavior switching to avoid switching into undesired behaviors with smaller key sequences
//! e.g. pressing RMB+LMB shouldn't switch into RMB behavior (or LMB behavior) first because it's virtually impossible to press both mouse buttons on the same frame
static constexpr float BehaviorSwitchDelayMs = 0.1f;
};
} // namespace MaterialEditor
@@ -1,61 +0,0 @@
/*
* 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 <AzCore/EBus/EBus.h>
#include <AzCore/Math/Vector3.h>
namespace MaterialEditor
{
class MaterialEditorViewportInputControllerRequests
: public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//! Get entityId of viewport camera
virtual const AZ::EntityId& GetCameraEntityId() const = 0;
//! Get entityId of camera target
virtual const AZ::EntityId& GetTargetEntityId() const = 0;
//! Get entityId of scene's IBL entity
virtual const AZ::EntityId& GetIblEntityId() const = 0;
//! Get actual position where the camera is facing
virtual const AZ::Vector3& GetTargetPosition() const = 0;
//! Set camera target position
//! @param targetPosition world space position to point camera at
virtual void SetTargetPosition(const AZ::Vector3& targetPosition) = 0;
//! Get distance between camera and its target
virtual float GetDistanceToTarget() const = 0;
//! Get minimum and maximum camera distance to the model based on mesh size
//! @param distanceMin closest camera can be to the target
//! @param distanceMax furthest camera can be from the target
virtual void GetExtents(float& distanceMin, float& distanceMax) const = 0;
//! Get bounding sphere radius of the active model
virtual float GetRadius() const = 0;
//! Reset camera to default position and rotation
virtual void Reset() = 0;
//! Modify camera's field of view
//! @param value field of view in degrees
virtual void SetFieldOfView(float value) = 0;
//! Check if camera is looking directly at a model
virtual bool IsCameraCentered() const = 0;
};
using MaterialEditorViewportInputControllerRequestBus = AZ::EBus<MaterialEditorViewportInputControllerRequests>;
} // namespace MaterialEditor
@@ -1,56 +0,0 @@
/*
* 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 <AzCore/Component/TransformBus.h>
#include <AzCore/Math/Vector3.h>
#include <Viewport/InputController/MaterialEditorViewportInputControllerBus.h>
#include <Viewport/InputController/MoveCameraBehavior.h>
namespace MaterialEditor
{
void MoveCameraBehavior::End()
{
float distanceToTarget;
MaterialEditorViewportInputControllerRequestBus::BroadcastResult(
distanceToTarget,
&MaterialEditorViewportInputControllerRequestBus::Handler::GetDistanceToTarget);
AZ::Transform transform = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(transform, m_cameraEntityId, &AZ::TransformBus::Events::GetLocalTM);
AZ::Vector3 targetPosition =
transform.GetTranslation() +
transform.GetBasisY() * distanceToTarget;
MaterialEditorViewportInputControllerRequestBus::Broadcast(
&MaterialEditorViewportInputControllerRequestBus::Handler::SetTargetPosition,
targetPosition);
}
void MoveCameraBehavior::TickInternal(float x, float y, float z)
{
AZ::Transform transform = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(transform, m_cameraEntityId, &AZ::TransformBus::Events::GetLocalTM);
AZ::Vector3 up = transform.GetBasisZ();
AZ::Vector3 right = transform.GetBasisX();
AZ::Vector3 position = transform.GetTranslation();
AZ::Vector3 deltaPosition = up * y + right * -x;
position += deltaPosition;
m_targetPosition += deltaPosition;
AZ::TransformBus::Event(m_cameraEntityId, &AZ::TransformBus::Events::SetLocalTranslation, position);
Behavior::TickInternal(x, y, z);
}
float MoveCameraBehavior::GetSensitivityX()
{
return SensitivityX;
}
float MoveCameraBehavior::GetSensitivityY()
{
return SensitivityY;
}
} // namespace MaterialEditor
@@ -1,32 +0,0 @@
/*
* 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 <Viewport/InputController/Behavior.h>
namespace MaterialEditor
{
//! Moves camera along its vertical and horizontal axis
class MoveCameraBehavior final
: public Behavior
{
public:
MoveCameraBehavior() = default;
virtual ~MoveCameraBehavior() = default;
void End() override;
protected:
void TickInternal(float x, float y, float z) override;
float GetSensitivityX() override;
float GetSensitivityY() override;
private:
static constexpr float SensitivityX = 0.01f;
static constexpr float SensitivityY = 0.01f;
};
} // namespace MaterialEditor
@@ -1,59 +0,0 @@
/*
* 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 <AzCore/Component/TransformBus.h>
#include <Viewport/InputController/MaterialEditorViewportInputController.h>
#include <Viewport/InputController/OrbitCameraBehavior.h>
namespace MaterialEditor
{
void OrbitCameraBehavior::TickInternal(float x, float y, float z)
{
Behavior::TickInternal(x, y, z);
// don't align camera until a movement has been made so that accidental right-click doesn't reset camera
if (!m_aligned)
{
Align();
}
AZ::Transform transform = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(transform, m_cameraEntityId, &AZ::TransformBus::Events::GetLocalTM);
AZ::Quaternion rotation = transform.GetRotation();
AZ::Vector3 right = transform.GetBasisX();
rotation =
AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisZ(), -x) *
AZ::Quaternion::CreateFromAxisAngle(right, -y) *
rotation;
rotation.Normalize();
AZ::Vector3 position =
rotation.TransformVector(AZ::Vector3(0, -m_distanceToTarget, 0)) + m_targetPosition;
transform = AZ::Transform::CreateFromQuaternionAndTranslation(rotation, position);
AZ::TransformBus::Event(m_cameraEntityId, &AZ::TransformBus::Events::SetLocalTM, transform);
}
float OrbitCameraBehavior::GetSensitivityX()
{
return SensitivityX;
}
float OrbitCameraBehavior::GetSensitivityY()
{
return SensitivityY;
}
void OrbitCameraBehavior::Align()
{
AZ::Vector3 cameraPosition = AZ::Vector3::CreateZero();
AZ::TransformBus::EventResult(cameraPosition, m_cameraEntityId, &AZ::TransformBus::Events::GetLocalTranslation);
const AZ::Vector3 delta = m_targetPosition - cameraPosition;
AZ::Quaternion targetRotation = LookRotation(delta);
AZ::TransformBus::Event(m_cameraEntityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, targetRotation);
m_aligned = true;
}
} // namespace MaterialEditor
@@ -1,36 +0,0 @@
/*
* 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 <Viewport/InputController/Behavior.h>
namespace MaterialEditor
{
//! Rotates the camera around target position,
//! this can either be model center or any position in world
class OrbitCameraBehavior final
: public Behavior
{
public:
OrbitCameraBehavior() = default;
virtual ~OrbitCameraBehavior() = default;
protected:
void TickInternal(float x, float y, float z) override;
float GetSensitivityX() override;
float GetSensitivityY() override;
private:
void Align();
static constexpr float SensitivityX = 0.005f;
static constexpr float SensitivityY = 0.005f;
bool m_aligned = false;
};
} // namespace MaterialEditor
@@ -1,56 +0,0 @@
/*
* 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 <AzCore/Component/TransformBus.h>
#include <AzCore/Math/Quaternion.h>
#include <AzCore/Math/Vector3.h>
#include <Viewport/InputController/MaterialEditorViewportInputControllerBus.h>
#include <Viewport/InputController/PanCameraBehavior.h>
namespace MaterialEditor
{
void PanCameraBehavior::End()
{
float distanceToTarget;
MaterialEditorViewportInputControllerRequestBus::BroadcastResult(
distanceToTarget,
&MaterialEditorViewportInputControllerRequestBus::Handler::GetDistanceToTarget);
AZ::Transform transform = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(transform, m_cameraEntityId, &AZ::TransformBus::Events::GetLocalTM);
AZ::Vector3 targetPosition =
transform.GetTranslation() +
transform.GetBasisY() * distanceToTarget;
MaterialEditorViewportInputControllerRequestBus::Broadcast(
&MaterialEditorViewportInputControllerRequestBus::Handler::SetTargetPosition,
targetPosition);
}
void PanCameraBehavior::TickInternal(float x, float y, [[maybe_unused]] float z)
{
AZ::Transform transform = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(transform, m_cameraEntityId, &AZ::TransformBus::Events::GetLocalTM);
AZ::Quaternion rotation = transform.GetRotation();
const AZ::Vector3 right = transform.GetBasisX();
rotation =
AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisZ(), -x) *
AZ::Quaternion::CreateFromAxisAngle(right, -y) *
rotation;
rotation.Normalize();
AZ::TransformBus::Event(m_cameraEntityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, rotation);
}
float PanCameraBehavior::GetSensitivityX()
{
return SensitivityX;
}
float PanCameraBehavior::GetSensitivityY()
{
return SensitivityY;
}
} // namespace MaterialEditor
@@ -1,33 +0,0 @@
/*
* 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 <Viewport/InputController/Behavior.h>
namespace MaterialEditor
{
//! Rotates camera around its own axis, allowing to look up/down/left/right
class PanCameraBehavior final
: public Behavior
{
public:
PanCameraBehavior() = default;
virtual ~PanCameraBehavior() = default;
void End() override;
protected:
void TickInternal(float x, float y, float z) override;
float GetSensitivityX() override;
float GetSensitivityY() override;
private:
static constexpr float SensitivityX = 0.005f;
static constexpr float SensitivityY = 0.005f;
};
} // namespace MaterialEditor
@@ -1,51 +0,0 @@
/*
* 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 <AzCore/Component/TransformBus.h>
#include <Atom/Feature/SkyBox/SkyBoxFeatureProcessorInterface.h>
#include <Atom/RPI.Public/RPISystemInterface.h>
#include <Atom/RPI.Public/Scene.h>
#include <Viewport/InputController/MaterialEditorViewportInputController.h>
#include <Viewport/InputController/RotateEnvironmentBehavior.h>
namespace MaterialEditor
{
void RotateEnvironmentBehavior::Start()
{
Behavior::Start();
MaterialEditorViewportInputControllerRequestBus::BroadcastResult(
m_iblEntityId,
&MaterialEditorViewportInputControllerRequestBus::Handler::GetIblEntityId);
AZ_Assert(m_iblEntityId.IsValid(), "Failed to find m_iblEntityId");
m_skyBoxFeatureProcessorInterface = AZ::RPI::Scene::GetFeatureProcessorForEntity<AZ::Render::SkyBoxFeatureProcessorInterface>(m_iblEntityId);
}
void RotateEnvironmentBehavior::TickInternal(float x, float y, float z)
{
Behavior::TickInternal(x, y, z);
m_rotation += x;
AZ::Quaternion rotation = AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisZ(), m_rotation);
AZ::TransformBus::Event(m_iblEntityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, rotation);
const AZ::Matrix4x4 rotationMatrix = AZ::Matrix4x4::CreateFromQuaternion(rotation);
m_skyBoxFeatureProcessorInterface->SetCubemapRotationMatrix(rotationMatrix);
}
float RotateEnvironmentBehavior::GetSensitivityX()
{
return SensitivityX;
}
float RotateEnvironmentBehavior::GetSensitivityY()
{
return SensitivityY;
}
} // namespace MaterialEditor
@@ -1,45 +0,0 @@
/*
* 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 <Viewport/InputController/Behavior.h>
namespace AZ
{
namespace Render
{
class SkyBoxFeatureProcessorInterface;
}
}
namespace MaterialEditor
{
//! Rotates lighting and skybox around vertical axis
class RotateEnvironmentBehavior final
: public Behavior
{
public:
RotateEnvironmentBehavior() = default;
virtual ~RotateEnvironmentBehavior() = default;
void Start() override;
protected:
void TickInternal(float x, float y, float z) override;
float GetSensitivityX() override;
float GetSensitivityY() override;
private:
static constexpr float SensitivityX = 0.01f;
static constexpr float SensitivityY = 0;
AZ::EntityId m_iblEntityId;
AZ::Render::SkyBoxFeatureProcessorInterface* m_skyBoxFeatureProcessorInterface = nullptr;
float m_rotation = 0;
};
} // namespace MaterialEditor
@@ -1,59 +0,0 @@
/*
* 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 <AzCore/Component/TransformBus.h>
#include <Viewport/InputController/MaterialEditorViewportInputController.h>
#include <Viewport/InputController/RotateModelBehavior.h>
namespace MaterialEditor
{
void RotateModelBehavior::Start()
{
Behavior::Start();
MaterialEditorViewportInputControllerRequestBus::BroadcastResult(
m_targetEntityId,
&MaterialEditorViewportInputControllerRequestBus::Handler::GetTargetEntityId);
AZ_Assert(m_targetEntityId.IsValid(), "Failed to find m_targetEntityId");
AZ::EntityId cameraEntityId;
MaterialEditorViewportInputControllerRequestBus::BroadcastResult(
cameraEntityId,
&MaterialEditorViewportInputControllerRequestBus::Handler::GetCameraEntityId);
AZ_Assert(cameraEntityId.IsValid(), "Failed to find cameraEntityId");
AZ::Transform transform = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(transform, cameraEntityId, &AZ::TransformBus::Events::GetLocalTM);
m_cameraRight = transform.GetBasisX();
}
void RotateModelBehavior::TickInternal(float x, float y, float z)
{
Behavior::TickInternal(x, y, z);
AZ::Transform transform = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(transform, m_targetEntityId, &AZ::TransformBus::Events::GetLocalTM);
AZ::Quaternion rotation = transform.GetRotation();
rotation =
AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisZ(), x) *
AZ::Quaternion::CreateFromAxisAngle(m_cameraRight, y) *
rotation;
rotation.Normalize();
AZ::TransformBus::Event(m_targetEntityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, rotation);
}
float RotateModelBehavior::GetSensitivityX()
{
return SensitivityX;
}
float RotateModelBehavior::GetSensitivityY()
{
return SensitivityY;
}
} // namespace MaterialEditor
@@ -1,36 +0,0 @@
/*
* 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 <Viewport/InputController/Behavior.h>
namespace MaterialEditor
{
//! Rotates target model in viewport
class RotateModelBehavior final
: public Behavior
{
public:
RotateModelBehavior() = default;
virtual ~RotateModelBehavior() = default;
void Start() override;
protected:
void TickInternal(float x, float y, float z) override;
float GetSensitivityX() override;
float GetSensitivityY() override;
private:
static constexpr float SensitivityX = 0.01f;
static constexpr float SensitivityY = 0.01f;
AZ::EntityId m_targetEntityId;
AZ::Vector3 m_cameraRight = AZ::Vector3::CreateAxisX();
};
} // namespace MaterialEditor
@@ -394,7 +394,7 @@ namespace MaterialEditor
return m_viewportSettings->m_displayMapperOperationType;
}
inline void MaterialViewportComponent::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
void MaterialViewportComponent::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
if (AZ::Data::Asset<AZ::RPI::AnyAsset> anyAsset = asset)
{
@@ -40,6 +40,12 @@
#include <AtomLyIntegration/CommonFeatures/PostProcess/ExposureControl/ExposureControlBus.h>
#include <AtomLyIntegration/CommonFeatures/PostProcess/ExposureControl/ExposureControlComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/PostProcess/PostFxLayerComponentConstants.h>
#include <AtomToolsFramework/Viewport/ViewportInputBehaviorController/DollyCameraBehavior.h>
#include <AtomToolsFramework/Viewport/ViewportInputBehaviorController/MoveCameraBehavior.h>
#include <AtomToolsFramework/Viewport/ViewportInputBehaviorController/OrbitCameraBehavior.h>
#include <AtomToolsFramework/Viewport/ViewportInputBehaviorController/PanCameraBehavior.h>
#include <AtomToolsFramework/Viewport/ViewportInputBehaviorController/RotateEnvironmentBehavior.h>
#include <AtomToolsFramework/Viewport/ViewportInputBehaviorController/RotateModelBehavior.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Component/Entity.h>
#include <AzFramework/Components/NonUniformScaleComponent.h>
@@ -64,7 +70,6 @@ namespace MaterialEditor
: AtomToolsFramework::RenderViewportWidget(parent)
, m_ui(new Ui::MaterialViewportWidget)
, m_toolId(toolId)
, m_viewportController(AZStd::make_shared<MaterialEditorViewportInputController>())
{
m_ui->setupUi(this);
@@ -117,31 +122,24 @@ namespace MaterialEditor
entityContextId, &AzFramework::GameEntityContextRequestBus::Events::GetGameEntityContextId);
// Configure camera
AzFramework::EntityContextRequestBus::EventResult(
m_cameraEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "Cameraentity");
AZ_Assert(m_cameraEntity != nullptr, "Failed to create camera entity.");
m_cameraEntity =
CreateEntity("Cameraentity", { azrtti_typeid<AzFramework::TransformComponent>(), azrtti_typeid<AZ::Debug::CameraComponent>() });
// Add debug camera and controller components
AZ::Debug::CameraComponentConfig cameraConfig(GetViewportContext()->GetWindowContext());
cameraConfig.m_fovY = AZ::Constants::HalfPi;
cameraConfig.m_depthNear = DepthNear;
m_cameraComponent = m_cameraEntity->CreateComponent(azrtti_typeid<AZ::Debug::CameraComponent>());
m_cameraComponent->SetConfiguration(cameraConfig);
m_cameraEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_cameraEntity->Deactivate();
m_cameraEntity->FindComponent(azrtti_typeid<AZ::Debug::CameraComponent>())->SetConfiguration(cameraConfig);
m_cameraEntity->Activate();
// Connect camera to pipeline's default view after camera entity activated
m_renderPipeline->SetDefaultViewFromEntity(m_cameraEntity->GetId());
// Configure tone mapper
AzFramework::EntityContextRequestBus::EventResult(
m_postProcessEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "postProcessEntity");
AZ_Assert(m_postProcessEntity != nullptr, "Failed to create post process entity.");
m_postProcessEntity->CreateComponent(AZ::Render::PostFxLayerComponentTypeId);
m_postProcessEntity->CreateComponent(AZ::Render::ExposureControlComponentTypeId);
m_postProcessEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_postProcessEntity->Activate();
m_postProcessEntity = CreateEntity(
"PostProcessEntity",
{ AZ::Render::PostFxLayerComponentTypeId, AZ::Render::ExposureControlComponentTypeId,
azrtti_typeid<AzFramework::TransformComponent>() });
// Init directional light processor
m_directionalLightFeatureProcessor = m_scene->GetFeatureProcessor<AZ::Render::DirectionalLightFeatureProcessorInterface>();
@@ -155,33 +153,19 @@ namespace MaterialEditor
m_skyboxFeatureProcessor->SetSkyboxMode(AZ::Render::SkyBoxMode::Cubemap);
// Create IBL
AzFramework::EntityContextRequestBus::EventResult(
m_iblEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "IblEntity");
AZ_Assert(m_iblEntity != nullptr, "Failed to create ibl entity.");
m_iblEntity->CreateComponent(AZ::Render::ImageBasedLightComponentTypeId);
m_iblEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_iblEntity->Activate();
m_iblEntity =
CreateEntity("IblEntity", { AZ::Render::ImageBasedLightComponentTypeId, azrtti_typeid<AzFramework::TransformComponent>() });
// Create model
AzFramework::EntityContextRequestBus::EventResult(
m_modelEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "ViewportModel");
AZ_Assert(m_modelEntity != nullptr, "Failed to create model entity.");
m_modelEntity->CreateComponent(AZ::Render::MeshComponentTypeId);
m_modelEntity->CreateComponent(AZ::Render::MaterialComponentTypeId);
m_modelEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_modelEntity->Activate();
m_modelEntity = CreateEntity(
"ViewportModel",
{ AZ::Render::MeshComponentTypeId, AZ::Render::MaterialComponentTypeId, azrtti_typeid<AzFramework::TransformComponent>() });
// Create shadow catcher
AzFramework::EntityContextRequestBus::EventResult(
m_shadowCatcherEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "ViewportShadowCatcher");
AZ_Assert(m_shadowCatcherEntity != nullptr, "Failed to create shadow catcher entity.");
m_shadowCatcherEntity->CreateComponent(AZ::Render::MeshComponentTypeId);
m_shadowCatcherEntity->CreateComponent(AZ::Render::MaterialComponentTypeId);
m_shadowCatcherEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_shadowCatcherEntity->CreateComponent(azrtti_typeid<AzFramework::NonUniformScaleComponent>());
m_shadowCatcherEntity->Activate();
m_shadowCatcherEntity = CreateEntity(
"ViewportShadowCatcher",
{ AZ::Render::MeshComponentTypeId, AZ::Render::MaterialComponentTypeId, azrtti_typeid<AzFramework::TransformComponent>(),
azrtti_typeid<AzFramework::NonUniformScaleComponent>() });
AZ::NonUniformScaleRequestBus::Event(
m_shadowCatcherEntity->GetId(), &AZ::NonUniformScaleRequests::SetScale, AZ::Vector3{ 100, 100, 1.0 });
@@ -214,21 +198,19 @@ namespace MaterialEditor
}
// Create grid
AzFramework::EntityContextRequestBus::EventResult(
m_gridEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "ViewportGrid");
AZ_Assert(m_gridEntity != nullptr, "Failed to create grid entity.");
m_gridEntity = CreateEntity("ViewportGrid", { AZ::Render::GridComponentTypeId, azrtti_typeid<AzFramework::TransformComponent>() });
AZ::Render::GridComponentConfig gridConfig;
gridConfig.m_gridSize = 4.0f;
gridConfig.m_axisColor = AZ::Color(0.1f, 0.1f, 0.1f, 1.0f);
gridConfig.m_primaryColor = AZ::Color(0.1f, 0.1f, 0.1f, 1.0f);
gridConfig.m_secondaryColor = AZ::Color(0.1f, 0.1f, 0.1f, 1.0f);
auto gridComponent = m_gridEntity->CreateComponent(AZ::Render::GridComponentTypeId);
gridComponent->SetConfiguration(gridConfig);
m_gridEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_gridEntity->Deactivate();
m_gridEntity->FindComponent(AZ::Render::GridComponentTypeId)->SetConfiguration(gridConfig);
m_gridEntity->Activate();
SetupInputController();
OnDocumentOpened(AZ::Uuid::CreateNull());
// Attempt to apply the default lighting preset
@@ -241,8 +223,6 @@ namespace MaterialEditor
MaterialViewportRequestBus::BroadcastResult(modelPreset, &MaterialViewportRequestBus::Events::GetModelPresetSelection);
OnModelPresetSelected(modelPreset);
m_viewportController->Init(m_cameraEntity->GetId(), m_modelEntity->GetId(), m_iblEntity->GetId());
// Apply user settinngs restored since last run
AZStd::intrusive_ptr<MaterialViewportSettings> viewportSettings =
AZ::UserSettings::CreateFind<MaterialViewportSettings>(AZ::Crc32("MaterialViewportSettings"), AZ::UserSettings::CT_GLOBAL);
@@ -257,8 +237,6 @@ namespace MaterialEditor
MaterialViewportNotificationBus::Handler::BusConnect();
AZ::TickBus::Handler::BusConnect();
AZ::TransformNotificationBus::MultiHandler::BusConnect(m_cameraEntity->GetId());
GetControllerList()->Add(m_viewportController);
}
MaterialViewportWidget::~MaterialViewportWidget()
@@ -269,33 +247,12 @@ namespace MaterialEditor
MaterialViewportNotificationBus::Handler::BusDisconnect();
AZ::Data::AssetBus::Handler::BusDisconnect();
AzFramework::EntityContextId entityContextId;
AzFramework::GameEntityContextRequestBus::BroadcastResult(
entityContextId, &AzFramework::GameEntityContextRequestBus::Events::GetGameEntityContextId);
AzFramework::EntityContextRequestBus::Event(
entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_iblEntity);
m_iblEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(
entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_modelEntity);
m_modelEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(
entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_shadowCatcherEntity);
m_shadowCatcherEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(
entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_gridEntity);
m_gridEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(
entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_cameraEntity);
m_cameraEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(
entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_postProcessEntity);
m_postProcessEntity = nullptr;
DestroyEntity(m_iblEntity);
DestroyEntity(m_modelEntity);
DestroyEntity(m_shadowCatcherEntity);
DestroyEntity(m_gridEntity);
DestroyEntity(m_cameraEntity);
DestroyEntity(m_postProcessEntity);
for (DirectionalLightHandle& handle : m_lightHandles)
{
@@ -315,6 +272,74 @@ namespace MaterialEditor
m_scene = nullptr;
}
AZ::Entity* MaterialViewportWidget::CreateEntity(const AZStd::string& name, const AZStd::vector<AZ::Uuid>& componentTypeIds)
{
AzFramework::EntityContextId entityContextId;
AzFramework::GameEntityContextRequestBus::BroadcastResult(
entityContextId, &AzFramework::GameEntityContextRequestBus::Events::GetGameEntityContextId);
AZ::Entity* entity = {};
AzFramework::EntityContextRequestBus::EventResult(
entity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, name.c_str());
AZ_Assert(entity != nullptr, "Failed to create post process entity: %s.", name.c_str());
if (entity)
{
for (const auto& componentTypeId : componentTypeIds)
{
entity->CreateComponent(componentTypeId);
}
entity->Activate();
}
return entity;
}
void MaterialViewportWidget::DestroyEntity(AZ::Entity*& entity)
{
AzFramework::EntityContextId entityContextId;
AzFramework::GameEntityContextRequestBus::BroadcastResult(
entityContextId, &AzFramework::GameEntityContextRequestBus::Events::GetGameEntityContextId);
AzFramework::EntityContextRequestBus::Event(entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, entity);
entity = nullptr;
}
void MaterialViewportWidget::SetupInputController()
{
using namespace AtomToolsFramework;
// Create viewport input controller and regioster its behaviors
m_viewportController.reset(
aznew ViewportInputBehaviorController(m_cameraEntity->GetId(), m_modelEntity->GetId(), m_iblEntity->GetId()));
m_viewportController->AddBehavior(
ViewportInputBehaviorController::Lmb, AZStd::make_shared<PanCameraBehavior>(m_viewportController.get()));
m_viewportController->AddBehavior(
ViewportInputBehaviorController::Mmb, AZStd::make_shared<MoveCameraBehavior>(m_viewportController.get()));
m_viewportController->AddBehavior(
ViewportInputBehaviorController::Rmb, AZStd::make_shared<OrbitCameraBehavior>(m_viewportController.get()));
m_viewportController->AddBehavior(
ViewportInputBehaviorController::Alt ^ ViewportInputBehaviorController::Lmb,
AZStd::make_shared<OrbitCameraBehavior>(m_viewportController.get()));
m_viewportController->AddBehavior(
ViewportInputBehaviorController::Alt ^ ViewportInputBehaviorController::Mmb,
AZStd::make_shared<MoveCameraBehavior>(m_viewportController.get()));
m_viewportController->AddBehavior(
ViewportInputBehaviorController::Alt ^ ViewportInputBehaviorController::Rmb,
AZStd::make_shared<DollyCameraBehavior>(m_viewportController.get()));
m_viewportController->AddBehavior(
ViewportInputBehaviorController::Lmb ^ ViewportInputBehaviorController::Rmb,
AZStd::make_shared<DollyCameraBehavior>(m_viewportController.get()));
m_viewportController->AddBehavior(
ViewportInputBehaviorController::Ctrl ^ ViewportInputBehaviorController::Lmb,
AZStd::make_shared<RotateModelBehavior>(m_viewportController.get()));
m_viewportController->AddBehavior(
ViewportInputBehaviorController::Shift ^ ViewportInputBehaviorController::Lmb,
AZStd::make_shared<RotateEnvironmentBehavior>(m_viewportController.get()));
GetControllerList()->Add(m_viewportController);
}
void MaterialViewportWidget::OnDocumentOpened(const AZ::Uuid& documentId)
{
AZ::Data::Instance<AZ::RPI::Material> materialInstance;
@@ -436,8 +461,7 @@ namespace MaterialEditor
void MaterialViewportWidget::OnFieldOfViewChanged(float fieldOfView)
{
MaterialEditorViewportInputControllerRequestBus::Broadcast(
&MaterialEditorViewportInputControllerRequestBus::Handler::SetFieldOfView, fieldOfView);
m_viewportController->SetFieldOfView(fieldOfView);
}
void MaterialViewportWidget::OnDisplayMapperOperationTypeChanged(AZ::Render::DisplayMapperOperationType operationType)
@@ -451,7 +475,9 @@ namespace MaterialEditor
{
if (m_modelAssetId == asset.GetId())
{
MaterialEditorViewportInputControllerRequestBus::Broadcast(&MaterialEditorViewportInputControllerRequestBus::Handler::Reset);
AZ::Data::Asset<AZ::RPI::ModelAsset> modelAsset = asset;
m_viewportController->SetTargetBounds(modelAsset->GetAabb());
m_viewportController->Reset();
AZ::Data::AssetBus::Handler::BusDisconnect(asset.GetId());
}
}
@@ -15,9 +15,9 @@
#include <AtomCore/Instance/Instance.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h>
#include <AtomToolsFramework/Viewport/RenderViewportWidget.h>
#include <AtomToolsFramework/Viewport/ViewportInputBehaviorController/ViewportInputBehaviorController.h>
#include <AzCore/Component/TransformBus.h>
#include <AzFramework/Windowing/WindowBus.h>
#include <Viewport/InputController/MaterialEditorViewportInputController.h>
#include <Viewport/MaterialViewportNotificationBus.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
@@ -61,6 +61,10 @@ namespace MaterialEditor
~MaterialViewportWidget();
private:
AZ::Entity* CreateEntity(const AZStd::string& name, const AZStd::vector<AZ::Uuid>& componentTypeIds);
void DestroyEntity(AZ::Entity*& entity);
void SetupInputController();
// AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler interface overrides...
void OnDocumentOpened(const AZ::Uuid& documentId) override;
@@ -96,8 +100,6 @@ namespace MaterialEditor
AZ::Render::DisplayMapperFeatureProcessorInterface* m_displayMapperFeatureProcessor = {};
AZ::Entity* m_cameraEntity = {};
AZ::Component* m_cameraComponent = {};
AZ::Entity* m_postProcessEntity = {};
AZ::Entity* m_modelEntity = {};
@@ -114,7 +116,7 @@ namespace MaterialEditor
AZ::Entity* m_iblEntity = {};
AZ::Render::SkyBoxFeatureProcessorInterface* m_skyboxFeatureProcessor = {};
AZStd::shared_ptr<MaterialEditorViewportInputController> m_viewportController;
AZStd::shared_ptr<AtomToolsFramework::ViewportInputBehaviorController> m_viewportController;
QScopedPointer<Ui::MaterialViewportWidget> m_ui;
};
@@ -17,28 +17,9 @@ set(FILES
Source/Viewport/MaterialViewportModule.h
Source/Viewport/MaterialViewportModule.cpp
Source/Viewport/InputController/MaterialEditorViewportInputControllerBus.h
Source/Viewport/MaterialViewportSettings.h
Source/Viewport/MaterialViewportRequestBus.h
Source/Viewport/MaterialViewportNotificationBus.h
Source/Viewport/InputController/MaterialEditorViewportInputController.cpp
Source/Viewport/InputController/MaterialEditorViewportInputController.h
Source/Viewport/InputController/Behavior.cpp
Source/Viewport/InputController/Behavior.h
Source/Viewport/InputController/DollyCameraBehavior.cpp
Source/Viewport/InputController/DollyCameraBehavior.h
Source/Viewport/InputController/IdleBehavior.cpp
Source/Viewport/InputController/IdleBehavior.h
Source/Viewport/InputController/MoveCameraBehavior.cpp
Source/Viewport/InputController/MoveCameraBehavior.h
Source/Viewport/InputController/PanCameraBehavior.cpp
Source/Viewport/InputController/PanCameraBehavior.h
Source/Viewport/InputController/OrbitCameraBehavior.cpp
Source/Viewport/InputController/OrbitCameraBehavior.h
Source/Viewport/InputController/RotateEnvironmentBehavior.cpp
Source/Viewport/InputController/RotateEnvironmentBehavior.h
Source/Viewport/InputController/RotateModelBehavior.cpp
Source/Viewport/InputController/RotateModelBehavior.h
Source/Viewport/MaterialViewportSettings.cpp
Source/Viewport/MaterialViewportComponent.cpp
Source/Viewport/MaterialViewportComponent.h