Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,445 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Atom/Component/DebugCamera/ArcBallControllerComponent.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h>
#include <AzFramework/Input/Devices/Touch/InputDeviceTouch.h>
#include <AzFramework/Windowing/WindowBus.h>
#include <DebugCameraUtils.h>
namespace AZ
{
namespace Debug
{
ArcBallControllerComponent::ArcBallControllerComponent()
: AzFramework::InputChannelEventListener(AzFramework::InputChannelEventListener::GetPriorityDefault())
{}
void ArcBallControllerComponent::Reflect(AZ::ReflectContext* reflection)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection))
{
serializeContext->Class<ArcBallControllerComponent, CameraControllerComponent, AZ::Component>()
->Version(1);
}
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(reflection))
{
behaviorContext->EBus<ArcBallControllerRequestBus>("ArcBallControllerRequestBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Category, "Camera")
->Attribute(AZ::Script::Attributes::Module, "render")
->Event("SetCenter", &ArcBallControllerRequestBus::Events::SetCenter)
->Event("SetPan", &ArcBallControllerRequestBus::Events::SetPan)
->Event("SetDistance", &ArcBallControllerRequestBus::Events::SetDistance)
->Event("SetMinDistance", &ArcBallControllerRequestBus::Events::SetMinDistance)
->Event("SetMaxDistance", &ArcBallControllerRequestBus::Events::SetMaxDistance)
->Event("SetHeading", &ArcBallControllerRequestBus::Events::SetHeading)
->Event("SetPitch", &ArcBallControllerRequestBus::Events::SetPitch)
->Event("SetPanningSensitivity", &ArcBallControllerRequestBus::Events::SetPanningSensitivity)
->Event("SetZoomingSensitivity", &ArcBallControllerRequestBus::Events::SetZoomingSensitivity)
->Event("GetCenter", &ArcBallControllerRequestBus::Events::GetCenter)
->Event("GetPan", &ArcBallControllerRequestBus::Events::GetPan)
->Event("GetDistance", &ArcBallControllerRequestBus::Events::GetDistance)
->Event("GetMinDistance", &ArcBallControllerRequestBus::Events::GetMinDistance)
->Event("GetMaxDistance", &ArcBallControllerRequestBus::Events::GetMaxDistance)
->Event("GetHeading", &ArcBallControllerRequestBus::Events::GetHeading)
->Event("GetPitch", &ArcBallControllerRequestBus::Events::GetPitch)
->Event("GetPanningSensitivity", &ArcBallControllerRequestBus::Events::GetPanningSensitivity)
->Event("GetZoomingSensitivity", &ArcBallControllerRequestBus::Events::GetZoomingSensitivity)
;
}
}
void ArcBallControllerComponent::OnEnabled()
{
// Reset parameters with initial values
m_arcballActive = false;
m_panningActive = false;
m_center = AZ::Vector3::CreateZero();
m_panningOffset = AZ::Vector3::CreateZero();
m_panningOffsetDelta = AZ::Vector3::CreateZero();
m_distance = 5.0f;
m_minDistance = 0.1f;
m_maxDistance = 10.0f;
m_currentHeading = 0.0f;
m_currentPitch = 0.0f;
m_panningSensitivity = 1.0f;
m_zoomingSensitivity = 1.0f;
AzFramework::NativeWindowHandle windowHandle = nullptr;
AzFramework::WindowSystemRequestBus::BroadcastResult(
windowHandle,
&AzFramework::WindowSystemRequestBus::Events::GetDefaultWindowHandle);
AzFramework::WindowSize windowSize;
AzFramework::WindowRequestBus::EventResult(
windowSize,
windowHandle,
&AzFramework::WindowRequestBus::Events::GetClientAreaSize);
m_windowWidth = windowSize.m_width;
m_windowHeight = windowSize.m_height;
ArcBallControllerRequestBus::Handler::BusConnect(GetEntityId());
AzFramework::InputChannelEventListener::Connect();
AZ::TickBus::Handler::BusConnect();
}
void ArcBallControllerComponent::OnDisabled()
{
TickBus::Handler::BusDisconnect();
AzFramework::InputChannelEventListener::Disconnect();
ArcBallControllerRequestBus::Handler::BusDisconnect();
}
void ArcBallControllerComponent::OnTick(float deltaTime, AZ::ScriptTimePoint time)
{
AZ_UNUSED(time);
if (m_distance < m_minDistance)
{
m_distance = m_minDistance;
}
else if (m_distance > m_maxDistance)
{
m_distance = m_maxDistance;
}
// The coordinate system is right-handed and Z-up. So heading is a rotation around the Z axis.
// After that rotation we rotate around the (rotated by heading) X axis for pitch.
AZ::Quaternion orientation = AZ::Quaternion::CreateRotationZ(m_currentHeading)
* AZ::Quaternion::CreateRotationX(m_currentPitch);
m_panningOffsetDelta *= deltaTime;
m_panningOffset += (orientation.TransformVector(m_panningOffsetDelta));
AZ::Vector3 position = (m_center + m_panningOffset) + (orientation.TransformVector(AZ::Vector3(0, -m_distance, 0)));
AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(orientation, position);
AZ::TransformBus::Event(
GetEntityId(), &AZ::TransformBus::Events::SetLocalTM, transform);
}
bool ArcBallControllerComponent::OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel)
{
static const float PixelToDegree = 1.0 / 360.0f;
uint32_t handledChannels = ArcBallControllerChannel_None;
const AzFramework::InputChannelId& inputChannelId = inputChannel.GetInputChannelId();
switch (inputChannel.GetState())
{
case AzFramework::InputChannel::State::Began:
case AzFramework::InputChannel::State::Updated: // update the camera rotation
{
//Keyboard & Mouse
if (inputChannelId == AzFramework::InputDeviceMouse::Button::Right)
{
m_arcballActive = true;
handledChannels |= ArcBallControllerChannel_Orientation;
}
else if (inputChannelId == AzFramework::InputDeviceMouse::Button::Middle)
{
m_panningActive = true;
handledChannels |= ArcBallControllerChannel_Pan;
}
if (m_arcballActive)
{
if (inputChannelId == AzFramework::InputDeviceMouse::Movement::X)
{
// modify yaw angle
m_currentHeading -= inputChannel.GetValue() * PixelToDegree;
m_currentHeading = NormalizeAngle(m_currentHeading);
}
else if (inputChannelId == AzFramework::InputDeviceMouse::Movement::Y)
{
// modify pitch angle
m_currentPitch -= inputChannel.GetValue() * PixelToDegree;
m_currentPitch = AZStd::max(m_currentPitch, -AZ::Constants::HalfPi);
m_currentPitch = AZStd::min(m_currentPitch, AZ::Constants::HalfPi);
}
}
else if (m_panningActive)
{
if (inputChannelId == AzFramework::InputDeviceMouse::Movement::X)
{
m_panningOffsetDelta.SetX(-inputChannel.GetValue() * m_panningSensitivity);
}
else if (inputChannelId == AzFramework::InputDeviceMouse::Movement::Y)
{
m_panningOffsetDelta.SetZ(inputChannel.GetValue() * m_panningSensitivity);
}
}
if (inputChannelId == AzFramework::InputDeviceMouse::Movement::Z)
{
const float MouseWheelDeltaScale = 1.0f / 120.0f; // based on WHEEL_DELTA in WinUser.h
m_distance -= inputChannel.GetValue() * MouseWheelDeltaScale * m_zoomingSensitivity;
m_zoomingActive = true;
handledChannels |= ArcBallControllerChannel_Distance;
}
// Gamepad
if (inputChannelId == AzFramework::InputDeviceGamepad::Trigger::L2)
{
m_arcballActive = true;
handledChannels |= ArcBallControllerChannel_Orientation;
}
else if (inputChannelId == AzFramework::InputDeviceGamepad::Button::L1)
{
m_panningActive = true;
handledChannels |= ArcBallControllerChannel_Pan;
}
if (m_arcballActive)
{
if (inputChannelId == AzFramework::InputDeviceGamepad::ThumbStickAxis1D::RX)
{
// modify yaw angle
m_currentHeading -= inputChannel.GetValue() * PixelToDegree;
m_currentHeading = NormalizeAngle(m_currentHeading);
}
else if (inputChannelId == AzFramework::InputDeviceGamepad::ThumbStickAxis1D::RY)
{
// modify pitch angle
m_currentPitch += inputChannel.GetValue() * PixelToDegree;
m_currentPitch = AZStd::max(m_currentPitch, -AZ::Constants::HalfPi);
m_currentPitch = AZStd::min(m_currentPitch, AZ::Constants::HalfPi);
}
}
else if (m_panningActive)
{
if (inputChannelId == AzFramework::InputDeviceGamepad::ThumbStickAxis1D::RX)
{
m_panningOffsetDelta.SetX(-inputChannel.GetValue() * 10.0f * m_panningSensitivity);
}
else if (inputChannelId == AzFramework::InputDeviceGamepad::ThumbStickAxis1D::RY)
{
m_panningOffsetDelta.SetZ(inputChannel.GetValue() * 10.0f * m_panningSensitivity);
}
}
if (inputChannelId == AzFramework::InputDeviceGamepad::ThumbStickAxis1D::LY)
{
m_distance -= inputChannel.GetValue() * m_zoomingSensitivity;
m_zoomingActive = true;
handledChannels |= ArcBallControllerChannel_Distance;
}
// Touch controls works depending which side of the screen you start the touch event.
// Left side controls the heading and pitch. Right side controls the panning.
// Only one touch control can be active at the same time.
if (inputChannelId == AzFramework::InputDeviceTouch::Touch::Index0)
{
auto* positionData = inputChannel.GetCustomData<AzFramework::InputChannel::PositionData2D>();
AZ::Vector2 screenPos = positionData->m_normalizedPosition;
auto deltaInPixels = screenPos - m_lastTouchPosition;
deltaInPixels *= AZ::Vector2(static_cast<float>(m_windowWidth), static_cast<float>(m_windowHeight));
if (inputChannel.GetState() == AzFramework::InputChannel::State::Began)
{
m_panningActive = screenPos.GetX() > 0.5f ? true : false;
m_arcballActive = !m_panningActive;
}
else if(m_panningActive)
{
m_panningOffsetDelta.SetX(-deltaInPixels.GetX() * m_panningSensitivity);
m_panningOffsetDelta.SetZ(deltaInPixels.GetY() * m_panningSensitivity);
}
else if(m_arcballActive)
{
// modify yaw angle
m_currentHeading -= deltaInPixels.GetX() * PixelToDegree;
m_currentHeading = NormalizeAngle(m_currentHeading);
// modify pitch angle
m_currentPitch -= deltaInPixels.GetY() * PixelToDegree;
m_currentPitch = AZStd::max(m_currentPitch, -AZ::Constants::HalfPi);
m_currentPitch = AZStd::min(m_currentPitch, AZ::Constants::HalfPi);
}
m_lastTouchPosition = screenPos;
if (m_panningActive)
{
handledChannels |= ArcBallControllerChannel_Pan;
}
else if (m_arcballActive)
{
handledChannels |= ArcBallControllerChannel_Orientation;
}
}
if (handledChannels && AzFramework::InputChannel::State::Began == inputChannel.GetState())
{
CameraControllerNotificationBus::Broadcast(&CameraControllerNotifications::OnCameraMoveBegan, RTTI_GetType(), handledChannels);
}
break;
}
case AzFramework::InputChannel::State::Ended: // update the released input state
{
uint32_t handledChannels2 = ArcBallControllerChannel_None;
if (inputChannelId == AzFramework::InputDeviceMouse::Button::Right)
{
m_arcballActive = false;
handledChannels2 |= ArcBallControllerChannel_Orientation;
}
else if (inputChannelId == AzFramework::InputDeviceMouse::Button::Middle)
{
m_panningActive = false;
handledChannels2 |= ArcBallControllerChannel_Pan;
}
else if (inputChannelId == AzFramework::InputDeviceGamepad::Trigger::L2)
{
m_arcballActive = false;
handledChannels2 |= ArcBallControllerChannel_Orientation;
}
else if (inputChannelId == AzFramework::InputDeviceGamepad::Button::L1)
{
m_panningActive = false;
handledChannels2 |= ArcBallControllerChannel_Pan;
}
else if (inputChannelId == AzFramework::InputDeviceTouch::Touch::Index0)
{
if (m_panningActive)
{
handledChannels2 |= ArcBallControllerChannel_Pan;
}
else if (m_arcballActive)
{
handledChannels2 |= ArcBallControllerChannel_Orientation;
}
m_panningActive = false;
m_arcballActive = false;
}
else if (inputChannelId == AzFramework::InputDeviceMouse::Movement::Z ||
inputChannelId == AzFramework::InputDeviceGamepad::ThumbStickAxis1D::LY)
{
m_zoomingActive = false;
handledChannels2 |= ArcBallControllerChannel_Distance;
}
if (handledChannels2)
{
CameraControllerNotificationBus::Broadcast(&CameraControllerNotifications::OnCameraMoveEnded, RTTI_GetType(), handledChannels2);
}
}
default:
{
break;
}
}
return false;
}
void ArcBallControllerComponent::SetCenter(AZ::Vector3 center)
{
m_center = center;
}
void ArcBallControllerComponent::SetPan(AZ::Vector3 pan)
{
m_panningOffset = pan;
}
void ArcBallControllerComponent::SetDistance(float distance)
{
m_distance = distance;
}
void ArcBallControllerComponent::SetMinDistance(float minDistance)
{
m_minDistance = minDistance;
m_distance = AZ::GetMax(m_distance, m_minDistance);
}
void ArcBallControllerComponent::SetMaxDistance(float maxDistance)
{
m_maxDistance = maxDistance;
m_distance = AZ::GetMin(m_distance, m_maxDistance);
}
void ArcBallControllerComponent::SetHeading(float heading)
{
m_currentHeading = heading;
}
void ArcBallControllerComponent::SetPitch(float pitch)
{
m_currentPitch = pitch;
}
void ArcBallControllerComponent::SetPanningSensitivity(float panningSensitivity)
{
m_panningSensitivity = AZ::GetMax(panningSensitivity, 0.0f);
}
void ArcBallControllerComponent::SetZoomingSensitivity(float zoomingSensitivity)
{
m_zoomingSensitivity = AZ::GetMax(zoomingSensitivity, 0.0f);
}
AZ::Vector3 ArcBallControllerComponent::GetCenter()
{
return m_center;
}
AZ::Vector3 ArcBallControllerComponent::GetPan()
{
return m_panningOffset;
}
float ArcBallControllerComponent::GetDistance()
{
return m_distance;
}
float ArcBallControllerComponent::GetMinDistance()
{
return m_minDistance;
}
float ArcBallControllerComponent::GetMaxDistance()
{
return m_maxDistance;
}
float ArcBallControllerComponent::GetHeading()
{
return m_currentHeading;
}
float ArcBallControllerComponent::GetPitch()
{
return m_currentPitch;
}
float ArcBallControllerComponent::GetPanningSensitivity()
{
return m_panningSensitivity;
}
float ArcBallControllerComponent::GetZoomingSensitivity()
{
return m_zoomingSensitivity;
}
} // namespace Debug
} // namespace AZ
@@ -0,0 +1,272 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Atom/Component/DebugCamera/CameraComponent.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/IO/IOUtils.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Math/MatrixUtils.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/RPI.Public/RPISystemInterface.h>
#include <Atom/RPI.Public/AuxGeom/AuxGeomFeatureProcessorInterface.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Public/WindowContext.h>
namespace AZ
{
namespace Debug
{
void CameraComponentConfig::Reflect(AZ::ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<CameraComponentConfig, AZ::ComponentConfig>()
->Version(1)
->Field("FovY", &CameraComponentConfig::m_fovY)
->Field("DepthNear", &CameraComponentConfig::m_depthNear)
->Field("DepthFar", &CameraComponentConfig::m_depthFar)
->Field("AspectRatioOverride", &CameraComponentConfig::m_aspectRatioOverride)
;
}
}
void CameraComponent::Reflect(AZ::ReflectContext* context)
{
CameraComponentConfig::Reflect(context);
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<CameraComponent, AZ::Component>()
->Version(1)
->Field("Config", &CameraComponent::m_componentConfig)
;
}
}
void CameraComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC("TransformService", 0x8ee22c50));
}
void CameraComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("CameraService", 0x1dd1caa4));
}
void CameraComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("CameraService", 0x1dd1caa4));
}
void CameraComponent::Activate()
{
AZ::Name viewName = GetEntity() ?
AZ::Name(AZStd::string::format("Camera View (entity: \"%s\")", GetEntity()->GetName().c_str())) :
AZ::Name("Camera view (unknown entity)");
m_view = RPI::View::CreateView(viewName, RPI::View::UsageCamera);
m_auxGeomFeatureProcessor = RPI::Scene::GetFeatureProcessorForEntity<RPI::AuxGeomFeatureProcessorInterface>(GetEntityId());
if (m_auxGeomFeatureProcessor)
{
m_auxGeomFeatureProcessor->GetOrCreateDrawQueueForView(m_view.get());
}
//Get transform at start
Transform transform;
TransformBus::BroadcastResult(transform, &TransformBus::Events::GetWorldTM);
OnTransformChanged(transform, transform);
TransformNotificationBus::Handler::BusConnect(GetEntityId());
RPI::ViewProviderBus::Handler::BusConnect(GetEntityId());
Camera::CameraRequestBus::Handler::BusConnect(GetEntityId());
Camera::CameraNotificationBus::Broadcast(&Camera::CameraNotificationBus::Events::OnCameraAdded, GetEntityId());
}
void CameraComponent::Deactivate()
{
Camera::CameraNotificationBus::Broadcast(&Camera::CameraNotificationBus::Events::OnCameraRemoved, GetEntityId());
Camera::CameraRequestBus::Handler::BusDisconnect();
RPI::ViewProviderBus::Handler::BusDisconnect();
TransformNotificationBus::Handler::BusDisconnect();
RPI::WindowContextNotificationBus::Handler::BusDisconnect();
if (m_auxGeomFeatureProcessor)
{
m_auxGeomFeatureProcessor->ReleaseDrawQueueForView(m_view.get());
}
m_view = nullptr;
m_auxGeomFeatureProcessor = nullptr;
}
RPI::ViewPtr CameraComponent::GetView() const
{
return m_view;
}
bool CameraComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
{
auto config = azrtti_cast<const CameraComponentConfig*>(baseConfig);
if (config != nullptr)
{
m_componentConfig = *config;
if (config->m_target != nullptr)
{
RPI::WindowContextNotificationBus::Handler::BusConnect(m_componentConfig.m_target->GetWindowHandle());
}
UpdateAspectRatio();
return true;
}
return false;
}
bool CameraComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const
{
auto config = azrtti_cast<CameraComponentConfig*>(outBaseConfig);
if (config != nullptr)
{
*config = m_componentConfig;
return true;
}
return false;
}
void CameraComponent::OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world)
{
AZ_UNUSED(local);
m_view->SetCameraTransform(AZ::Matrix3x4::CreateFromTransform(world));
UpdateViewToClipMatrix();
}
float CameraComponent::GetFovDegrees()
{
return RadToDeg(m_componentConfig.m_fovY);
}
float CameraComponent::GetFovRadians()
{
return m_componentConfig.m_fovY;
}
float CameraComponent::GetNearClipDistance()
{
return m_componentConfig.m_depthNear;
}
float CameraComponent::GetFarClipDistance()
{
return m_componentConfig.m_depthFar;
}
float CameraComponent::GetFrustumWidth()
{
return m_componentConfig.m_depthFar * tanf(m_componentConfig.m_fovY / 2) * m_aspectRatio * 2;
}
float CameraComponent::GetFrustumHeight()
{
return m_componentConfig.m_depthFar * tanf(m_componentConfig.m_fovY / 2) * 2;
}
void CameraComponent::SetFovDegrees(float fov)
{
m_componentConfig.m_fovY = DegToRad(fov);
UpdateViewToClipMatrix();
}
void CameraComponent::SetFovRadians(float fov)
{
m_componentConfig.m_fovY = fov;
UpdateViewToClipMatrix();
}
void CameraComponent::SetNearClipDistance(float nearClipDistance)
{
m_componentConfig.m_depthNear = nearClipDistance;
UpdateViewToClipMatrix();
}
void CameraComponent::SetFarClipDistance(float farClipDistance)
{
m_componentConfig.m_depthFar = farClipDistance;
UpdateViewToClipMatrix();
}
void CameraComponent::SetFrustumWidth(float width)
{
AZ_Assert(m_componentConfig.m_depthFar > 0.f, "Depth Far has to be positive.");
AZ_Assert(m_aspectRatio > 0.f, "Aspect ratio must be positive.");
const float height = width / m_aspectRatio;
m_componentConfig.m_fovY = atanf(height / 2 / m_componentConfig.m_depthFar) * 2;
UpdateViewToClipMatrix();
}
void CameraComponent::SetFrustumHeight(float height)
{
AZ_Assert(m_componentConfig.m_depthFar > 0.f, "Depth Far has to be positive.");
m_componentConfig.m_fovY = atanf(height / 2 / m_componentConfig.m_depthFar) * 2;
UpdateViewToClipMatrix();
}
void CameraComponent::MakeActiveView()
{
// do nothing
}
void CameraComponent::OnViewportResized(uint32_t width, uint32_t height)
{
AZ_UNUSED(width)
AZ_UNUSED(height)
UpdateAspectRatio();
UpdateViewToClipMatrix();
}
void CameraComponent::UpdateAspectRatio()
{
if (m_componentConfig.m_aspectRatioOverride > 0.0f)
{
m_aspectRatio = m_componentConfig.m_aspectRatioOverride;
}
else if (m_componentConfig.m_target)
{
const auto& viewport = m_componentConfig.m_target->GetViewport();
m_aspectRatio = viewport.m_maxX / viewport.m_maxY;
}
}
void CameraComponent::UpdateViewToClipMatrix()
{
// Note: This is projection assumes a setup for reversed depth
AZ::Matrix4x4 viewToClipMatrix;
MakePerspectiveFovMatrixRH(viewToClipMatrix,
m_componentConfig.m_fovY,
m_aspectRatio,
m_componentConfig.m_depthNear,
m_componentConfig.m_depthFar, true);
m_view->SetViewToClipMatrix(viewToClipMatrix);
}
} // namespace Debug
} // namespace AZ
@@ -0,0 +1,96 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Atom/Component/DebugCamera/CameraControllerComponent.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace AZ
{
namespace Debug
{
void CameraControllerComponent::Reflect(AZ::ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<CameraControllerComponent, AZ::Component>()
->Version(1);
}
}
void CameraControllerComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC("TransformService", 0x8ee22c50));
required.push_back(AZ_CRC("CameraService", 0x1dd1caa4));
}
void CameraControllerComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("CameraControllerService", 0xc35788f9));
}
void CameraControllerComponent::Enable(TypeId typeId)
{
// Enable this controller if type id matches, otherwise disable this controller
if (typeId == RTTI_GetType())
{
if (!m_enabled)
{
m_enabled = true;
OnEnabled();
}
}
else
{
if (m_enabled)
{
m_enabled = false;
OnDisabled();
}
}
}
void CameraControllerComponent::Disable()
{
if (m_enabled)
{
m_enabled = false;
OnDisabled();
}
}
void CameraControllerComponent::Reset()
{
if (m_enabled)
{
OnDisabled();
OnEnabled();
}
}
void CameraControllerComponent::Activate()
{
CameraControllerRequestBus::Handler::BusConnect(GetEntityId());
}
void CameraControllerComponent::Deactivate()
{
if (m_enabled)
{
m_enabled = false;
OnDisabled();
}
CameraControllerRequestBus::Handler::BusDisconnect();
}
} // namespace Debug
} // namespace AZ
@@ -0,0 +1,43 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <DebugCameraUtils.h>
#include <AzCore/Math/MathUtils.h>
namespace AZ
{
namespace Debug
{
void ApplyMomentum(float& oldValue, float& newValue, float deltaTime)
{
float blendedValue;
blendedValue = AZ::Lerp(newValue, oldValue, deltaTime);
oldValue = blendedValue;
newValue = blendedValue;
}
float NormalizeAngle(float angle)
{
if (angle > AZ::Constants::Pi)
{
angle -= AZ::Constants::TwoPi;
}
else if (angle < -AZ::Constants::Pi)
{
angle += AZ::Constants::TwoPi;
}
return angle;
}
} // namespace Debug
} // namespace AZ
@@ -0,0 +1,22 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
namespace AZ
{
namespace Debug
{
void ApplyMomentum(float& oldValue, float& newValue, float deltaTime);
float NormalizeAngle(float angle);
} // namespace Debug
} // namespace AZ
@@ -0,0 +1,54 @@
/*
* All or portions of this file Copyright(c) Amazon.com, Inc.or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution(the "License").All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file.Do not
* remove or modify any license notices.This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/Module/Module.h>
#include <Atom/Component/DebugCamera/ArcBallControllerComponent.h>
#include <Atom/Component/DebugCamera/CameraComponent.h>
#include <Atom/Component/DebugCamera/NoClipControllerComponent.h>
namespace AZ
{
namespace Debug
{
class CameraModule
: public AZ::Module
{
public:
AZ_RTTI(DebugCameraModule, "{C4F5D301-5C7F-42C2-8326-08F685B2D7A3}", AZ::Module);
CameraModule()
{
m_descriptors.insert(m_descriptors.end(), {
ArcBallControllerComponent::CreateDescriptor(),
CameraComponent::CreateDescriptor(),
NoClipControllerComponent::CreateDescriptor(),
});
}
/**
* Add required SystemComponents to the SystemEntity.
*/
AZ::ComponentTypeList GetRequiredSystemComponents() const override
{
AZ::ComponentTypeList required;
return required;
}
};
} // namespace Debug
} // namespace AZ
// DO NOT MODIFY THIS LINE UNLESS YOU RENAME THE GEM
// The first parameter should be GemName_GemIdLower
// The second should be the fully qualified name of the class above
AZ_DECLARE_MODULE_CLASS(Gem_Atom_Component_DebugCamera, AZ::Debug::CameraModule)
@@ -0,0 +1,474 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Atom/Component/DebugCamera/NoClipControllerComponent.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h>
#include <AzFramework/Input/Devices/Touch/InputDeviceTouch.h>
#include <AzFramework/Components/CameraBus.h>
#include <DebugCameraUtils.h>
namespace AZ
{
namespace Debug
{
const AzFramework::InputChannelId NoClipControllerComponent::TouchEvent::InvalidTouchChannelId = AzFramework::InputChannelId("InvalidChannel");
static constexpr float MaxFov = 160.0f * Constants::Pi / 180.0f;
static constexpr float MinFov = 1.0f * Constants::Pi / 180.0f;
static constexpr float DefaultFov = Constants::QuarterPi;
void NoClipControllerProperties::Reflect(AZ::ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<NoClipControllerProperties>()
->Version(2)
->Field("Mouse Sensitivity X", &NoClipControllerProperties::m_mouseSensitivityX)
->Field("Mouse Sensitivity Y", &NoClipControllerProperties::m_mouseSensitivityY)
->Field("Move Speed", &NoClipControllerProperties::m_moveSpeed)
->Field("Panning Speed", &NoClipControllerProperties::m_panningSpeed)
->Field("Touch Sensitivity", &NoClipControllerProperties::m_touchSensitivity);
}
}
NoClipControllerComponent::NoClipControllerComponent()
: AzFramework::InputChannelEventListener(AzFramework::InputChannelEventListener::GetPriorityDefault())
{}
void NoClipControllerComponent::Reflect(AZ::ReflectContext* reflection)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection))
{
serializeContext->Class<NoClipControllerComponent, CameraControllerComponent, AZ::Component>()
->Version(1)
->Field("Properties", &NoClipControllerComponent::m_properties);
}
}
void NoClipControllerComponent::OnEnabled()
{
// Reset parameters
m_mouseLookEnabled = false;
m_inputStates = 0;
m_currentHeading = 0.0f;
m_currentPitch = 0.0f;
m_currentFov = DefaultFov;
m_lastForward = 0.0f;
m_lastStrafe = 0.0f;
m_lastAscent = 0.0f;
NoClipControllerRequestBus::Handler::BusConnect(GetEntityId());
AzFramework::InputChannelEventListener::Connect();
AZ::TickBus::Handler::BusConnect();
}
void NoClipControllerComponent::OnDisabled()
{
TickBus::Handler::BusDisconnect();
AzFramework::InputChannelEventListener::Disconnect();
NoClipControllerRequestBus::Handler::BusDisconnect();
// Reset the Fov to default.
Camera::CameraRequestBus::Event(GetEntityId(), &Camera::CameraRequestBus::Events::SetFovRadians, DefaultFov);
}
void NoClipControllerComponent::OnTick(float deltaTime, AZ::ScriptTimePoint time)
{
AZ_UNUSED(time);
static const float normalSpeed = 3.0f;
static const float sprintSpeed = 10.0f;
float speedFactor = m_inputStates[CameraKeys::FastMode] ? sprintSpeed : normalSpeed;
float forward = m_properties.m_moveSpeed * speedFactor * (
(m_inputStates[CameraKeys::Forward] ? deltaTime : 0.0f) +
(m_inputStates[CameraKeys::Back] ? -deltaTime : 0.0f));
float strafe = m_properties.m_panningSpeed * speedFactor * (
(m_inputStates[CameraKeys::Right] ? deltaTime : 0.0f) +
(m_inputStates[CameraKeys::Left] ? -deltaTime : 0.0f));
float ascent = m_properties.m_panningSpeed * speedFactor * (
(m_inputStates[CameraKeys::Up] ? deltaTime : 0.0f) +
(m_inputStates[CameraKeys::Down] ? -deltaTime : 0.0f));
ApplyMomentum(m_lastForward, forward, deltaTime);
ApplyMomentum(m_lastStrafe, strafe, deltaTime);
ApplyMomentum(m_lastAscent, ascent, deltaTime);
AZ::Vector3 worldPosition;
AZ::TransformBus::EventResult(
worldPosition, GetEntityId(), &AZ::TransformBus::Events::GetWorldTranslation);
// The coordinate system is right-handed and Z-up. So heading is a rotation around the Z axis.
// After that rotation we rotate around the (rotated by heading) X axis for pitch.
AZ::Quaternion orientation = AZ::Quaternion::CreateRotationZ(m_currentHeading)
* AZ::Quaternion::CreateRotationX(m_currentPitch);
AZ::Vector3 position = orientation.TransformVector(AZ::Vector3(strafe, forward, ascent)) + worldPosition;
AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(orientation, position);
AZ::TransformBus::Event(
GetEntityId(), &AZ::TransformBus::Events::SetWorldTM, transform);
Camera::CameraRequestBus::Event(GetEntityId(), &Camera::CameraRequestBus::Events::SetFovRadians, m_currentFov);
}
bool NoClipControllerComponent::OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel)
{
static const auto KeyCount = static_cast<uint32_t>(CameraKeys::Count);
static const float PixelToDegree = 1.0 / 360.0f;
static const AzFramework::InputChannelId CameraInputMap[KeyCount] =
{
AzFramework::InputDeviceKeyboard::Key::AlphanumericW, // Forward
AzFramework::InputDeviceKeyboard::Key::AlphanumericS, // Back
AzFramework::InputDeviceKeyboard::Key::AlphanumericA, // Left
AzFramework::InputDeviceKeyboard::Key::AlphanumericD, // Right
AzFramework::InputDeviceKeyboard::Key::AlphanumericQ, // Up
AzFramework::InputDeviceKeyboard::Key::AlphanumericE, // Down
AzFramework::InputDeviceKeyboard::Key::ModifierShiftL, // FastMode
};
static const AzFramework::InputChannelId CameraGamepadInputMap[KeyCount] =
{
AzFramework::InputDeviceGamepad::Button::DU, // Forward
AzFramework::InputDeviceGamepad::Button::DD, // Back
AzFramework::InputDeviceGamepad::Button::DL, // Left
AzFramework::InputDeviceGamepad::Button::DR, // Right
AzFramework::InputDeviceGamepad::Button::R1, // Up
AzFramework::InputDeviceGamepad::Button::L1, // Down
AzFramework::InputDeviceGamepad::Trigger::R2, // FastMode
};
uint32_t handledChannels = NoClipControllerChannel_None;
const AzFramework::InputChannelId& inputChannelId = inputChannel.GetInputChannelId();
switch (inputChannel.GetState())
{
case AzFramework::InputChannel::State::Began:
case AzFramework::InputChannel::State::Updated: // update the camera rotation
{
//Keyboard & Mouse
if (m_mouseLookEnabled && inputChannelId == AzFramework::InputDeviceMouse::Movement::X)
{
// modify yaw angle
m_currentHeading -= inputChannel.GetValue() * m_properties.m_mouseSensitivityX * PixelToDegree;
m_currentHeading = NormalizeAngle(m_currentHeading);
}
else if (m_mouseLookEnabled && inputChannelId == AzFramework::InputDeviceMouse::Movement::Y)
{
// modify pitch angle
m_currentPitch -= inputChannel.GetValue() * m_properties.m_mouseSensitivityY * PixelToDegree;
m_currentPitch = AZStd::max(m_currentPitch, -AZ::Constants::HalfPi);
m_currentPitch = AZStd::min(m_currentPitch, AZ::Constants::HalfPi);
}
else if (inputChannelId == AzFramework::InputDeviceMouse::Movement::Z)
{
// modify field of view
m_currentFov = GetClamp(m_currentFov - inputChannel.GetValue() * 0.0005f * m_currentFov, MinFov, MaxFov);
handledChannels |= NoClipControllerChannel_Fov;
}
else if (inputChannelId == AzFramework::InputDeviceMouse::Button::Right)
{
m_mouseLookEnabled = true;
handledChannels |= NoClipControllerChannel_Orientation;
}
else
{
for (uint32_t i = 0; i < KeyCount; ++i)
{
if (inputChannelId == CameraInputMap[i])
{
m_inputStates[i] = true;
if (i != CameraKeys::FastMode)
{
handledChannels |= NoClipControllerChannel_Position;
}
break;
}
}
}
// Gamepad
if (inputChannelId == AzFramework::InputDeviceGamepad::Trigger::L2)
{
m_mouseLookEnabled = true;
handledChannels |= NoClipControllerChannel_Orientation;
}
else if (m_mouseLookEnabled)
{
if (inputChannelId == AzFramework::InputDeviceGamepad::ThumbStickAxis1D::RX)
{
// modify yaw angle
m_currentHeading -= inputChannel.GetValue() * m_properties.m_mouseSensitivityX * PixelToDegree;
m_currentHeading = NormalizeAngle(m_currentHeading);
}
else if (inputChannelId == AzFramework::InputDeviceGamepad::ThumbStickAxis1D::RY)
{
// modify pitch angle
m_currentPitch += inputChannel.GetValue() * m_properties.m_mouseSensitivityY * PixelToDegree;
m_currentPitch = AZStd::max(m_currentPitch, -AZ::Constants::HalfPi);
m_currentPitch = AZStd::min(m_currentPitch, AZ::Constants::HalfPi);
}
for (uint32_t i = 0; i < KeyCount; ++i)
{
if (inputChannelId == CameraGamepadInputMap[i])
{
m_inputStates[i] = true;
if (i != CameraKeys::FastMode)
{
handledChannels |= NoClipControllerChannel_Position;
}
break;
}
}
}
// Touch controls works like two virtual joysticks.
// The left "joystick" controls forward/backward/left and right movements.
// The right "joystick" controls the camera heading and pitch.
// There's no control to move up and down.
if (inputChannelId == AzFramework::InputDeviceTouch::Touch::Index0 || inputChannelId == AzFramework::InputDeviceTouch::Touch::Index1)
{
auto* positionData = inputChannel.GetCustomData<AzFramework::InputChannel::PositionData2D>();
AZ::Vector2 screenPos = positionData->m_normalizedPosition;
const float deadZone = 0.07f;
if (inputChannelId == m_mouseLookTouch.m_channelId)
{
// modify yaw angle
AZ::Vector2 deltaPos = screenPos - m_mouseLookTouch.m_initialPos;
AZ::Vector2 inputValue = AZ::Vector2(fabsf(deltaPos.GetX()) > deadZone ? 1.0f : 0, fabsf(deltaPos.GetY()) > deadZone ? 1.0f : 0);
m_currentHeading -= inputValue.GetX() * AZ::GetSign(deltaPos.GetX()) * m_properties.m_touchSensitivity * m_properties.m_mouseSensitivityX * PixelToDegree;
m_currentHeading = NormalizeAngle(m_currentHeading);
// modify pitch angle
m_currentPitch -= inputValue.GetY() * AZ::GetSign(deltaPos.GetY()) * m_properties.m_touchSensitivity * m_properties.m_mouseSensitivityY * PixelToDegree;
m_currentPitch = AZStd::max(m_currentPitch, -AZ::Constants::HalfPi);
m_currentPitch = AZStd::min(m_currentPitch, AZ::Constants::HalfPi);
handledChannels |= NoClipControllerChannel_Orientation;
}
else if (inputChannelId == m_movementTouch.m_channelId)
{
AZ::Vector2 deltaPos = screenPos - m_movementTouch.m_initialPos;
m_inputStates[Forward] = deltaPos.GetY() < -deadZone ? true : false;
m_inputStates[Back] = deltaPos.GetY() > deadZone ? true : false;
m_inputStates[Left] = deltaPos.GetX() < -deadZone ? true : false;
m_inputStates[Right] = deltaPos.GetX() > deadZone ? true : false;
handledChannels |= NoClipControllerChannel_Position;
}
else
{
bool isMouseLook = (screenPos.GetX() > 0.5);
auto& touchEvent = isMouseLook ? m_mouseLookTouch : m_movementTouch;
if (touchEvent.m_channelId == TouchEvent::InvalidTouchChannelId)
{
touchEvent.m_channelId = inputChannelId;
touchEvent.m_initialPos = screenPos;
}
if (isMouseLook)
{
handledChannels |= NoClipControllerChannel_Orientation;
}
else
{
handledChannels |= NoClipControllerChannel_Position;
}
}
}
if (handledChannels && AzFramework::InputChannel::State::Began == inputChannel.GetState())
{
CameraControllerNotificationBus::Broadcast(&CameraControllerNotifications::OnCameraMoveBegan, RTTI_GetType(), handledChannels);
}
break;
}
case AzFramework::InputChannel::State::Ended: // update the released input state
{
if (inputChannelId == AzFramework::InputDeviceMouse::Button::Right)
{
m_mouseLookEnabled = false;
handledChannels |= NoClipControllerChannel_Orientation;
}
else if (inputChannelId == AzFramework::InputDeviceMouse::Movement::Z)
{
handledChannels |= NoClipControllerChannel_Fov;
}
else if (inputChannelId == AzFramework::InputDeviceGamepad::Trigger::L2)
{
m_mouseLookEnabled = false;
handledChannels |= NoClipControllerChannel_Orientation;
// On gamepads, Trigger::L2 also indicates positional movement, see above.
handledChannels |= NoClipControllerChannel_Position;
}
else if (inputChannelId == m_movementTouch.m_channelId)
{
m_movementTouch.m_channelId = TouchEvent::InvalidTouchChannelId;
for (uint32_t i = 0; i < KeyCount; ++i)
{
m_inputStates[i] = false;
if (i != CameraKeys::FastMode)
{
handledChannels |= NoClipControllerChannel_Position;
}
}
}
else if (inputChannelId == m_mouseLookTouch.m_channelId)
{
m_mouseLookTouch.m_channelId = TouchEvent::InvalidTouchChannelId;
handledChannels |= NoClipControllerChannel_Orientation;
}
else
{
for (uint32_t i = 0; i < KeyCount; ++i)
{
if (inputChannelId == CameraInputMap[i] || inputChannelId == CameraGamepadInputMap[i])
{
m_inputStates[i] = false;
if (i != CameraKeys::FastMode)
{
handledChannels |= NoClipControllerChannel_Position;
}
break;
}
}
}
if (handledChannels)
{
CameraControllerNotificationBus::Broadcast(&CameraControllerNotifications::OnCameraMoveEnded, RTTI_GetType(), handledChannels);
}
break;
}
default:
{
break;
}
}
return false;
}
void NoClipControllerComponent::SetMouseSensitivityX(float mouseSensitivityX)
{
m_properties.m_mouseSensitivityX = mouseSensitivityX;
}
void NoClipControllerComponent::SetMouseSensitivityY(float mouseSensitivityY)
{
m_properties.m_mouseSensitivityY = mouseSensitivityY;
}
void NoClipControllerComponent::SetMoveSpeed(float moveSpeed)
{
m_properties.m_moveSpeed = moveSpeed;
}
void NoClipControllerComponent::SetPanningSpeed(float panningSpeed)
{
m_properties.m_panningSpeed = panningSpeed;
}
void NoClipControllerComponent::SetControllerProperties(const NoClipControllerProperties& properties)
{
m_properties = properties;
}
void NoClipControllerComponent::SetTouchSensitivity([[maybe_unused]] float touchSensitivity)
{
m_properties.m_touchSensitivity;
}
void NoClipControllerComponent::SetPosition(AZ::Vector3 position)
{
AZ::TransformBus::Event(GetEntityId(), &AZ::TransformBus::Events::SetWorldTranslation, position);
}
void NoClipControllerComponent::SetHeading(float heading)
{
m_currentHeading = heading;
m_currentHeading = NormalizeAngle(m_currentHeading);
}
void NoClipControllerComponent::SetPitch(float pitch)
{
m_currentPitch = pitch;
m_currentPitch = AZStd::max(m_currentPitch, -AZ::Constants::HalfPi);
m_currentPitch = AZStd::min(m_currentPitch, AZ::Constants::HalfPi);
}
void NoClipControllerComponent::SetFov(float fov)
{
m_currentFov = GetClamp(fov, MinFov, MaxFov);
}
float NoClipControllerComponent::GetMouseSensitivityX()
{
return m_properties.m_mouseSensitivityX;
}
float NoClipControllerComponent::GetMouseSensitivityY()
{
return m_properties.m_mouseSensitivityY;
}
float NoClipControllerComponent::GetMoveSpeed()
{
return m_properties.m_moveSpeed;
}
float NoClipControllerComponent::GetPanningSpeed()
{
return m_properties.m_panningSpeed;
}
float NoClipControllerComponent::GetTouchSensitivity()
{
return m_properties.m_touchSensitivity;
}
NoClipControllerProperties NoClipControllerComponent::GetControllerProperties()
{
return m_properties;
}
AZ::Vector3 NoClipControllerComponent::GetPosition()
{
AZ::Vector3 position;
AZ::TransformBus::EventResult(position, GetEntityId(), &AZ::TransformBus::Events::GetWorldTranslation);
return position;
}
float NoClipControllerComponent::GetHeading()
{
return m_currentHeading;
}
float NoClipControllerComponent::GetPitch()
{
return m_currentPitch;
}
float NoClipControllerComponent::GetFov()
{
return m_currentFov;
}
} // namespace Debug
} // namespace AZ