Add focus camera and custom camera input (#4493)

* add focus camera and custom camera input

Signed-off-by: hultonha <hultonha@amazon.co.uk>

* very minor tidy before publishing PR

Signed-off-by: hultonha <hultonha@amazon.co.uk>

* small updates after PR feedback

Signed-off-by: hultonha <hultonha@amazon.co.uk>
This commit is contained in:
hultonha
2021-10-06 14:52:59 +01:00
committed by GitHub
parent 5cee9b43b7
commit a40af98394
8 changed files with 198 additions and 20 deletions
@@ -623,15 +623,24 @@ namespace AzFramework
{
Camera nextCamera = targetCamera;
const auto pivotDirection = targetCamera.m_offset.GetNormalized();
nextCamera.m_offset -= pivotDirection * delta;
const auto pivotDot = targetCamera.m_offset.Dot(nextCamera.m_offset);
const auto distance = nextCamera.m_offset.GetLength() * AZ::GetSign(pivotDot);
const auto minDistance = 0.01f;
if (distance < minDistance || pivotDot < 0.0f)
// handle case where pivot and offset may be the same to begin with
// choose negative y-axis for offset to default to moving the camera backwards from the pivot (standard centered pivot behavior)
const auto pivotDirection = [&targetCamera]
{
nextCamera.m_offset = pivotDirection * minDistance;
if (const auto offsetLength = targetCamera.m_offset.GetLength(); AZ::IsCloseMag(offsetLength, 0.0f))
{
return -AZ::Vector3::CreateAxisY();
}
else
{
return targetCamera.m_offset / offsetLength;
}
}();
nextCamera.m_offset -= pivotDirection * delta;
if (pivotDirection.Dot(nextCamera.m_offset) < 0.0f)
{
nextCamera.m_offset = pivotDirection * 0.001f;
}
return nextCamera;
@@ -771,6 +780,73 @@ namespace AzFramework
return camera;
}
FocusCameraInput::FocusCameraInput(const InputChannelId& focusChannelId, FocusOffsetFn offsetFn)
: m_focusChannelId(focusChannelId)
, m_offsetFn(offsetFn)
{
}
bool FocusCameraInput::HandleEvents(
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
{
if (const auto* input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_channelId == m_focusChannelId && input->m_state == InputChannel::State::Began)
{
BeginActivation();
}
}
return !Idle();
}
Camera FocusCameraInput::StepCamera(
const Camera& targetCamera,
[[maybe_unused]] const ScreenVector& cursorDelta,
[[maybe_unused]] float scrollDelta,
[[maybe_unused]] float deltaTime)
{
if (Beginning())
{
// as the camera starts, record the camera we would like to end up as
m_nextCamera.m_offset = m_offsetFn(m_pivotFn().GetDistance(targetCamera.Translation()));
const auto angles =
EulerAngles(AZ::Matrix3x3::CreateFromMatrix3x4(AZ::Matrix3x4::CreateLookAt(targetCamera.Translation(), m_pivotFn())));
m_nextCamera.m_pitch = angles.GetX();
m_nextCamera.m_yaw = angles.GetZ();
m_nextCamera.m_pivot = targetCamera.m_pivot;
}
// end the behavior when the camera is in alignment
if (AZ::IsCloseMag(targetCamera.m_pitch, m_nextCamera.m_pitch) && AZ::IsCloseMag(targetCamera.m_yaw, m_nextCamera.m_yaw))
{
EndActivation();
}
return m_nextCamera;
}
void FocusCameraInput::SetPivotFn(PivotFn pivotFn)
{
m_pivotFn = AZStd::move(pivotFn);
}
void FocusCameraInput::SetFocusInputChannelId(const InputChannelId& focusChannelId)
{
m_focusChannelId = focusChannelId;
}
bool CustomCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, const float scrollDelta)
{
return m_handleEventsFn(*this, event, cursorDelta, scrollDelta);
}
Camera CustomCameraInput::StepCamera(
const Camera& targetCamera, const ScreenVector& cursorDelta, const float scrollDelta, const float deltaTime)
{
return m_stepCameraFn(*this, targetCamera, cursorDelta, scrollDelta, deltaTime);
}
InputEvent BuildInputEvent(const InputChannel& inputChannel, const WindowSize& windowSize)
{
const auto& inputChannelId = inputChannel.GetInputChannelId();
@@ -617,6 +617,60 @@ namespace AzFramework
return true;
}
//! Callback to use for FocusCameraInput when a free look camera is being used.
//! @note This is when offset is zero.
inline AZ::Vector3 FocusLook(float)
{
return AZ::Vector3::CreateZero();
}
//! Callback to use for FocusCameraInput when a pivot camera is being used.
//! @note This is when offset is non zero.
inline AZ::Vector3 FocusPivot(const float length)
{
return AZ::Vector3::CreateAxisY(-length);
}
using FocusOffsetFn = AZStd::function<AZ::Vector3(float)>;
//! A focus behavior to align the camera view to the position returned by the pivot function.
//! @note This only alters the camera orientation, the translation is unaffected.
class FocusCameraInput : public CameraInput
{
public:
using PivotFn = AZStd::function<AZ::Vector3()>;
FocusCameraInput(const InputChannelId& focusChannelId, FocusOffsetFn offsetFn);
// CameraInput overrides ...
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
//! Override the default behavior for how a pivot point is calculated.
void SetPivotFn(PivotFn pivotFn);
void SetFocusInputChannelId(const InputChannelId& focusChannelId);
private:
InputChannelId m_focusChannelId; //!< Input channel to begin the focus camera input.
Camera m_nextCamera;
PivotFn m_pivotFn;
FocusOffsetFn m_offsetFn;
};
//! Provides a CameraInput type that can be implemented without needing to create a new type deriving from CameraInput.
//! This can be very useful for specific use cases that are less generally applicable.
class CustomCameraInput : public CameraInput
{
public:
// CameraInput overrides ...
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
AZStd::function<bool(CameraInput&, const InputEvent&, const ScreenVector&, float)> m_handleEventsFn;
AZStd::function<Camera(CameraInput&, const Camera&, const ScreenVector&, float, float)> m_stepCameraFn;
};
//! Map from a generic InputChannel event to a camera specific InputEvent.
InputEvent BuildInputEvent(const InputChannel& inputChannel, const WindowSize& windowSize);
} // namespace AzFramework