API documentation pass for CameraInput types (#1681)

* add api comments for new camera system

Signed-off-by: hultonha <hultonha@amazon.co.uk>
This commit is contained in:
Tom Hulton-Harrop
2021-06-30 18:16:35 +01:00
committed by GitHub
parent 69f2e06134
commit 8d35f31fa6
2 changed files with 94 additions and 44 deletions
@@ -284,6 +284,16 @@ namespace AzFramework
m_idleCameraInputs.clear();
}
bool Cameras::Exclusive() const
{
return AZStd::any_of(
m_activeCameraInputs.begin(), m_activeCameraInputs.end(),
[](const auto& cameraInput)
{
return cameraInput->Exclusive();
});
}
RotateCameraInput::RotateCameraInput(const InputChannelId rotateChannelId)
: m_rotateChannelId(rotateChannelId)
{
@@ -361,7 +371,7 @@ namespace AzFramework
};
nextCamera.m_yaw = clampRotation(nextCamera.m_yaw);
// clamp pitch to be +-90 degrees
// clamp pitch to be +/-90 degrees
nextCamera.m_pitch = AZ::GetClamp(nextCamera.m_pitch, -AZ::Constants::HalfPi, AZ::Constants::HalfPi);
return nextCamera;
@@ -428,7 +438,7 @@ namespace AzFramework
return nextCamera;
}
TranslateCameraInput::TranslationType TranslateCameraInput::translationFromKey(InputChannelId channelId)
TranslateCameraInput::TranslationType TranslateCameraInput::TranslationFromKey(InputChannelId channelId)
{
if (channelId == CameraTranslateForwardId)
{
@@ -484,7 +494,7 @@ namespace AzFramework
{
if (input->m_state == InputChannel::State::Began)
{
m_translation |= translationFromKey(input->m_channelId);
m_translation |= TranslationFromKey(input->m_channelId);
if (m_translation != TranslationType::Nil)
{
BeginActivation();
@@ -498,7 +508,7 @@ namespace AzFramework
// ensure we don't process end events in the idle state
else if (input->m_state == InputChannel::State::Ended && !Idle())
{
m_translation &= ~(translationFromKey(input->m_channelId));
m_translation &= ~(TranslationFromKey(input->m_channelId));
if (m_translation == TranslationType::Nil)
{
EndActivation();
@@ -23,22 +23,26 @@
namespace AzFramework
{
//! Update camera key bindings that can be overridden with AZ console vars (invoke from console to update)
//! Updates camera key bindings that can be overridden with AZ console vars (invoke from console to update).
void ReloadCameraKeyBindings();
//! Return Euler angles (pitch, roll, yaw) for the incoming orientation.
//! Returns Euler angles (pitch, roll, yaw) for the incoming orientation.
//! @note Order of rotation is Z, Y, X.
AZ::Vector3 EulerAngles(const AZ::Matrix3x3& orientation);
//! A simple camera representation using spherical coordinates as input (pitch, yaw and look distance).
//! The cameras transform and view can be obtained through accessor functions that use the internal
//! spherical coordinates to calculate the position and orientation.
struct Camera
{
AZ::Vector3 m_lookAt = AZ::Vector3::CreateZero(); //!< Position of camera when m_lookDist is zero,
//!< or position of m_lookAt when m_lookDist is greater
//!< than zero.
float m_yaw{ 0.0 };
float m_pitch{ 0.0 };
float m_yaw{ 0.0 }; //!< Yaw rotation of camera (stored in radians) usually clamped to 0-360 degrees (0-2Pi radians).
float m_pitch{ 0.0 }; //!< Pitch rotation of the camera (stored in radians) usually clamped to +/-90 degrees (-Pi/2 - Pi/2 radians).
float m_lookDist{ 0.0 }; //!< Zero gives first person free look, otherwise orbit about m_lookAt
//! View camera transform (v in MVP).
//! View camera transform (V in model-view-projection matrix (MVP)).
AZ::Transform View() const;
//! World camera transform.
AZ::Transform Transform() const;
@@ -69,9 +73,10 @@ namespace AzFramework
return Transform().GetTranslation();
}
//! Extracts Euler angles (orientation) and translation from the transform and writes the values to the camera.
void UpdateCameraFromTransform(Camera& camera, const AZ::Transform& transform);
//! Generic motion type
//! Generic motion type.
template<typename MotionTag>
struct MotionEvent
{
@@ -86,35 +91,44 @@ namespace AzFramework
float m_delta;
};
//! Represents an input event which occurs as a discrete change in state (e.g. button down, button up) rather than a continuous stream.
//! @note Such as a key press with a down/up event as opposed to a continuous delta.
struct DiscreteInputEvent
{
InputChannelId m_channelId; //!< Channel type. (e.g. Keyboard key, mouse button or other device input).
InputChannel::State m_state; //!< Channel state. (e.g. Begin/update/end event).
};
//! Represents a type-safe union of input events that are handled by the camera system.
using InputEvent = AZStd::variant<AZStd::monostate, HorizontalMotionEvent, VerticalMotionEvent, ScrollEvent, DiscreteInputEvent>;
//! Base class for all camera behaviors.
//! The core interface consists of:
//! HandleEvents, used to receive and process incoming input events to begin, update and end a behavior.
//! StepCamera, to update the current camera transform (position and orientation).
class CameraInput
{
public:
//! The state of activation the camera input is currently in.
//! State changes of Activation: Idle -> Beginning -> Active -> Ending -> Idle
enum class Activation
{
Idle,
Begin,
Active,
End
Idle, //!< Camera input is not currently active (initial state and transitioned to from Ending).
Beginning, //!< Camera input is just beginning (transitioned to from Idle).
Active, //!< Camera input is currently active and running (transitioned to from Beginning).
Ending //!< Camera input is ending and will return to idle (transitioned to from Active).
};
virtual ~CameraInput() = default;
bool Beginning() const
{
return m_activation == Activation::Begin;
return m_activation == Activation::Beginning;
}
bool Ending() const
{
return m_activation == Activation::End;
return m_activation == Activation::Ending;
}
bool Idle() const
@@ -129,12 +143,12 @@ namespace AzFramework
void BeginActivation()
{
m_activation = Activation::Begin;
m_activation = Activation::Beginning;
}
void EndActivation()
{
m_activation = Activation::End;
m_activation = Activation::Ending;
}
void ContinueActivation()
@@ -153,38 +167,56 @@ namespace AzFramework
ResetImpl();
}
//! Respond to input events to transition a camera input to active, handle input while running, and restore to idle when input ends.
virtual bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) = 0;
//! Use processed input events to update the state of the camera.
//! @note targetCamera is the current target camera at the beginning of an update. The returned camera is the targetCamera + some
//! delta to get to the next camera position and/or orientation.
virtual Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) = 0;
//! It is usually possible for one to many camera inputs to be running at the same time (the default), it is however possible to
//! to make a camera input 'exclusive', so only it can run at a time. This implies that all other behaviors must have stopped before
//! it can begin, and no other camera input can run while it is active.
virtual bool Exclusive() const
{
return false;
}
protected:
//! Handle any state reset that may be required for the camera input (optional).
virtual void ResetImpl()
{
}
private:
Activation m_activation = Activation::Idle;
Activation m_activation = Activation::Idle; //!< Default all camera inputs to the idle state.
};
//! Properties to use to configure behavior across all types of camera.
struct CameraProps
{
AZStd::function<float()> m_rotateSmoothnessFn; //!< Rotate smoothing value (useful approx range 3-6, higher values give sharper feel).
AZStd::function<float()> m_translateSmoothnessFn; //!< Translate smoothing value (useful approx range 3-6, higher values give sharper feel).
AZStd::function<float()>
m_rotateSmoothnessFn; //!< Rotate smoothing value (useful approx range 3-6, higher values give sharper feel).
AZStd::function<float()>
m_translateSmoothnessFn; //!< Translate smoothing value (useful approx range 3-6, higher values give sharper feel).
};
//! An interpolation function to smoothly interpolate all camera properties from currentCamera to targetCamera.
//! The camera returned will be some value between current and target camera.
//! @note The rate of interpolation can be customized with CameraProps.
Camera SmoothCamera(const Camera& currentCamera, const Camera& targetCamera, const CameraProps& cameraProps, float deltaTime);
//! Manages a list of camera inputs.
//! By default all camera inputs are added to the idle list, when a camera activates it will be added to the active list, when it
//! deactivates it will be returned to the idle list.
class Cameras
{
public:
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta);
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime);
//! Add a camera input (behavior) to run in this set of camera inputs.
//! The camera inputs added here will determine the overall behavior of the camera.
void AddCamera(AZStd::shared_ptr<CameraInput> cameraInput);
//! Reset the state of all cameras.
void Reset();
@@ -195,20 +227,11 @@ namespace AzFramework
bool Exclusive() const;
private:
AZStd::vector<AZStd::shared_ptr<CameraInput>> m_activeCameraInputs;
AZStd::vector<AZStd::shared_ptr<CameraInput>> m_idleCameraInputs;
AZStd::vector<AZStd::shared_ptr<CameraInput>> m_activeCameraInputs; //!< Active camera inputs updating the camera (empty initially).
AZStd::vector<AZStd::shared_ptr<CameraInput>>
m_idleCameraInputs; //!< Idle camera inputs not contributing to the update (filled initially).
};
inline bool Cameras::Exclusive() const
{
return AZStd::any_of(
m_activeCameraInputs.begin(), m_activeCameraInputs.end(),
[](const auto& cameraInput)
{
return cameraInput->Exclusive();
});
}
//! Responsible for updating a series of cameras given various inputs.
class CameraSystem
{
@@ -216,13 +239,14 @@ namespace AzFramework
bool HandleEvents(const InputEvent& event);
Camera StepCamera(const Camera& targetCamera, float deltaTime);
Cameras m_cameras;
Cameras m_cameras; //!< Represents a collection of camera inputs that together provide a camera controller.
private:
ScreenVector m_motionDelta; //!< The delta used for look/orbit/pan (rotation + translation) - two dimensional.
float m_scrollDelta = 0.0f; //!< The delta used for dolly/movement (translation) - one dimensional.
};
//! A camera input to handle motion deltas that can rotate or orbit the camera.
class RotateCameraInput : public CameraInput
{
public:
@@ -237,24 +261,28 @@ namespace AzFramework
AZStd::function<bool()> m_invertYawFn;
private:
InputChannelId m_rotateChannelId;
ClickDetector m_clickDetector;
InputChannelId m_rotateChannelId; //!< Input channel to begin the rotate camera input.
ClickDetector m_clickDetector; //!< Used to determine when a sufficient motion delta has occurred to begin the input.
};
//! Axes to use while panning the camera.
struct PanAxes
{
AZ::Vector3 m_horizontalAxis;
AZ::Vector3 m_verticalAxis;
};
//! PanAxes build function that will return a pair of pan axes depending on the camera orientation.
using PanAxesFn = AZStd::function<PanAxes(const Camera& camera)>;
//! PanAxes to use while in 'look' camera behavior (free look).
inline PanAxes LookPan(const Camera& camera)
{
const AZ::Matrix3x3 orientation = camera.Rotation();
return { orientation.GetBasisX(), orientation.GetBasisZ() };
}
//! PanAxes to use while in 'orbit' camera behavior.
inline PanAxes OrbitPan(const Camera& camera)
{
const AZ::Matrix3x3 orientation = camera.Rotation();
@@ -269,6 +297,7 @@ namespace AzFramework
return { basisX, basisY };
}
//! A camera input to handle motion deltas that can pan the camera (translate in two axes).
class PanCameraInput : public CameraInput
{
public:
@@ -283,12 +312,14 @@ namespace AzFramework
AZStd::function<bool()> m_invertPanYFn;
private:
PanAxesFn m_panAxesFn;
InputChannelId m_panChannelId;
PanAxesFn m_panAxesFn; //!< Builder for the particular pan axes (provided in the constructor).
InputChannelId m_panChannelId; //!< Input channel to begin the pan camera input.
};
//! Axes to use while translating the camera.
using TranslationAxesFn = AZStd::function<AZ::Matrix3x3(const Camera& camera)>;
//! TranslationAxes to use while in 'look' camera behavior (free look).
inline AZ::Matrix3x3 LookTranslation(const Camera& camera)
{
const AZ::Matrix3x3 orientation = camera.Rotation();
@@ -300,6 +331,7 @@ namespace AzFramework
return AZ::Matrix3x3::CreateFromColumns(basisX, basisY, basisZ);
}
//! TranslationAxes to use while in 'orbit' camera behavior.
inline AZ::Matrix3x3 OrbitTranslation(const Camera& camera)
{
const AZ::Matrix3x3 orientation = camera.Rotation();
@@ -315,6 +347,7 @@ namespace AzFramework
return AZ::Matrix3x3::CreateFromColumns(basisX, basisY, basisZ);
}
//! A camera input to handle discrete events that can translate the camera (translate in three axes).
class TranslateCameraInput : public CameraInput
{
public:
@@ -329,6 +362,7 @@ namespace AzFramework
AZStd::function<float()> m_boostMultiplierFn;
private:
//! The type of translation the camera input is performing (multiple may be active at once).
enum class TranslationType
{
// clang-format off
@@ -383,13 +417,15 @@ namespace AzFramework
return static_cast<TranslationType>(~static_cast<std::underlying_type_t<TranslationType>>(lhs));
}
static TranslationType translationFromKey(InputChannelId channelId);
//! Converts from a generic input channel id to a concrete translation type (based on the user's key mappings).
static TranslationType TranslationFromKey(InputChannelId channelId);
TranslationType m_translation = TranslationType::Nil;
TranslationAxesFn m_translationAxesFn;
bool m_boost = false;
TranslationType m_translation = TranslationType::Nil; //!< Types of translation the camera input is under.
TranslationAxesFn m_translationAxesFn; //!< Builder for translation axes.
bool m_boost = false; //!< Is the translation speed currently being multiplied/scaled upwards.
};
//! A camera input to handle discrete scroll events that can modify the camera look distance.
class OrbitDollyScrollCameraInput : public CameraInput
{
public:
@@ -402,6 +438,7 @@ namespace AzFramework
AZStd::function<float()> m_scrollSpeedFn;
};
//! A camera input to handle motion deltas that can modify the camera look distance.
class OrbitDollyCursorMoveCameraInput : public CameraInput
{
public:
@@ -417,6 +454,7 @@ namespace AzFramework
InputChannelId m_dollyChannelId;
};
//! A camera input to handle discrete scroll events that can scroll (translate) the camera along its forward axis.
class ScrollTranslationCameraInput : public CameraInput
{
public:
@@ -429,6 +467,8 @@ namespace AzFramework
AZStd::function<float()> m_scrollSpeedFn;
};
//! A camera input that doubles as its own set of camera inputs.
//! It is 'exclusive', so does not overlap with other sibling camera inputs - it runs its own set of camera inputs as 'children'.
class OrbitCameraInput : public CameraInput
{
public:
@@ -439,13 +479,13 @@ namespace AzFramework
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
bool Exclusive() const override;
Cameras m_orbitCameras;
Cameras m_orbitCameras; //!< The camera inputs to run when this camera input is active (only these will run as it is exclusive).
//! Override the default behavior for how a look-at point is calculated.
void SetLookAtFn(const LookAtFn& lookAtFn);
private:
LookAtFn m_lookAtFn;
LookAtFn m_lookAtFn; //!< The look-at behavior to use for this orbit camera (how is the look-at point calculated/retrieved).
};
inline void OrbitCameraInput::SetLookAtFn(const LookAtFn& lookAtFn)