Improve selection in the viewport (#720)
* improve selection in the viewport * remove debug code * updates following review feedback - update API comments from /// to //! from - add [[nodiscard]] attribute to member function - move constructor implementations to .cpp files * use lambda instead of ternary operator * fix unit test failure caused by typo
This commit is contained in:
committed by
GitHub
parent
4aff32e719
commit
795aa114e6
+28
-8
@@ -237,16 +237,15 @@ namespace AzToolsFramework
|
||||
mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Alt();
|
||||
}
|
||||
|
||||
static bool IndividualSelect(const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
|
||||
static bool IndividualSelect(const AzFramework::ClickDetector::ClickOutcome clickOutcome)
|
||||
{
|
||||
return mouseInteraction.m_mouseInteraction.m_mouseButtons.Left() &&
|
||||
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down;
|
||||
return clickOutcome == AzFramework::ClickDetector::ClickOutcome::Click;
|
||||
}
|
||||
|
||||
static bool AdditiveIndividualSelect(const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
|
||||
static bool AdditiveIndividualSelect(
|
||||
const AzFramework::ClickDetector::ClickOutcome clickOutcome, const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
|
||||
{
|
||||
return mouseInteraction.m_mouseInteraction.m_mouseButtons.Left() &&
|
||||
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down &&
|
||||
return clickOutcome == AzFramework::ClickDetector::ClickOutcome::Click &&
|
||||
mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Ctrl() &&
|
||||
!mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Alt();
|
||||
}
|
||||
@@ -1783,6 +1782,25 @@ namespace AzToolsFramework
|
||||
|
||||
m_cachedEntityIdUnderCursor = m_editorHelpers->HandleMouseInteraction(cameraState, mouseInteraction);
|
||||
|
||||
const AzFramework::ClickDetector::ClickEvent selectClickEvent = [&mouseInteraction] {
|
||||
if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left())
|
||||
{
|
||||
if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down)
|
||||
{
|
||||
return AzFramework::ClickDetector::ClickEvent::Down;
|
||||
}
|
||||
|
||||
if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Up)
|
||||
{
|
||||
return AzFramework::ClickDetector::ClickEvent::Up;
|
||||
}
|
||||
}
|
||||
return AzFramework::ClickDetector::ClickEvent::Nil;
|
||||
}();
|
||||
|
||||
m_cursorState.SetCurrentPosition(mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates);
|
||||
const auto clickOutcome = m_clickDetector.DetectClick(selectClickEvent, m_cursorState.CursorDelta());
|
||||
|
||||
// for entities selected with no bounds of their own (just TransformComponent)
|
||||
// check selection against the selection indicator aabb
|
||||
for (AZ::EntityId entityId : m_selectedEntityIds)
|
||||
@@ -1841,7 +1859,7 @@ namespace AzToolsFramework
|
||||
if (!m_selectedEntityIds.empty())
|
||||
{
|
||||
// select/deselect (add/remove) entities with ctrl held
|
||||
if (Input::AdditiveIndividualSelect(mouseInteraction))
|
||||
if (Input::AdditiveIndividualSelect(clickOutcome, mouseInteraction))
|
||||
{
|
||||
if (SelectDeselect(entityIdUnderCursor))
|
||||
{
|
||||
@@ -2023,7 +2041,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
// standard toggle selection
|
||||
if (Input::IndividualSelect(mouseInteraction))
|
||||
if (Input::IndividualSelect(clickOutcome))
|
||||
{
|
||||
SelectDeselect(entityIdUnderCursor);
|
||||
}
|
||||
@@ -3267,6 +3285,8 @@ namespace AzToolsFramework
|
||||
const auto modifiers = ViewportInteraction::KeyboardModifiers(
|
||||
ViewportInteraction::TranslateKeyboardModifiers(QApplication::queryKeyboardModifiers()));
|
||||
|
||||
m_cursorState.Update();
|
||||
|
||||
HandleAccents(
|
||||
!m_selectedEntityIds.empty(), m_cachedEntityIdUnderCursor,
|
||||
modifiers.Ctrl(), m_hoveredEntityId,
|
||||
|
||||
+80
-76
@@ -17,6 +17,8 @@
|
||||
#include <AzCore/std/optional.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzFramework/Components/CameraBus.h>
|
||||
#include <AzFramework/Viewport/ClickDetector.h>
|
||||
#include <AzFramework/Viewport/CursorState.h>
|
||||
#include <AzToolsFramework/API/EditorCameraBus.h>
|
||||
#include <AzToolsFramework/Commands/EntityManipulatorCommand.h>
|
||||
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
|
||||
@@ -35,37 +37,37 @@ namespace AzToolsFramework
|
||||
{
|
||||
class EditorVisibleEntityDataCache;
|
||||
|
||||
using EntityIdSet = AZStd::unordered_set<AZ::EntityId>; ///< Alias for unordered_set of EntityIds.
|
||||
using EntityIdSet = AZStd::unordered_set<AZ::EntityId>; //!< Alias for unordered_set of EntityIds.
|
||||
|
||||
/// Entity related data required by manipulators during action.
|
||||
//! Entity related data required by manipulators during action.
|
||||
struct EntityIdManipulatorLookup
|
||||
{
|
||||
AZ::Transform m_initial; /// Transform of Entity at mouse down on manipulator.
|
||||
AZ::Transform m_initial; //!< Transform of Entity at mouse down on manipulator.
|
||||
};
|
||||
|
||||
/// Alias for a mapping between EntityIds and Entity related data required by manipulators.
|
||||
//! Alias for a mapping between EntityIds and Entity related data required by manipulators.
|
||||
using EntityIdManipulatorLookups = AZStd::unordered_map<AZ::EntityId, EntityIdManipulatorLookup>;
|
||||
|
||||
/// Generic wrapper to handle specific manipulators controlling 1-* entities.
|
||||
//! Generic wrapper to handle specific manipulators controlling 1-* entities.
|
||||
struct EntityIdManipulators
|
||||
{
|
||||
EntityIdManipulatorLookups m_lookups; ///< Mapping between the EntityId and the transform of the Entity at
|
||||
///< the point a manipulator started adjusting it.
|
||||
AZStd::unique_ptr<Manipulators> m_manipulators; ///< The aggregate manipulator currently in use.
|
||||
EntityIdManipulatorLookups m_lookups; //!< Mapping between the EntityId and the transform of the Entity at
|
||||
//!< the point a manipulator started adjusting it.
|
||||
AZStd::unique_ptr<Manipulators> m_manipulators; //!< The aggregate manipulator currently in use.
|
||||
};
|
||||
|
||||
/// Store translation and orientation only (no scale).
|
||||
//! Store translation and orientation only (no scale).
|
||||
struct Frame
|
||||
{
|
||||
AZ::Vector3 m_translation = AZ::Vector3::CreateZero(); ///< Position of frame.
|
||||
AZ::Quaternion m_orientation = AZ::Quaternion::CreateIdentity(); ///< Orientation of frame.
|
||||
AZ::Vector3 m_translation = AZ::Vector3::CreateZero(); //!< Position of frame.
|
||||
AZ::Quaternion m_orientation = AZ::Quaternion::CreateIdentity(); //!< Orientation of frame.
|
||||
};
|
||||
|
||||
/// Temporary manipulator frame used during selection.
|
||||
//! Temporary manipulator frame used during selection.
|
||||
struct OptionalFrame
|
||||
{
|
||||
/// What part of the transform did we pick (when using ditto on
|
||||
/// the manipulator). This will depend on the transform mode we're in.
|
||||
//! What part of the transform did we pick (when using ditto on
|
||||
//! the manipulator). This will depend on the transform mode we're in.
|
||||
struct PickType
|
||||
{
|
||||
enum : AZ::u8
|
||||
@@ -83,29 +85,29 @@ namespace AzToolsFramework
|
||||
bool PickedTranslation() const;
|
||||
bool PickedOrientation() const;
|
||||
|
||||
/// Clear all state associated with the frame.
|
||||
//! Clear all state associated with the frame.
|
||||
void Reset();
|
||||
/// Clear only picked translation state.
|
||||
//! Clear only picked translation state.
|
||||
void ResetPickedTranslation();
|
||||
/// Clear only picked orientation state.
|
||||
//! Clear only picked orientation state.
|
||||
void ResetPickedOrientation();
|
||||
|
||||
AZ::EntityId m_pickedEntityIdOverride; ///< 'Picked' Entity - frame and parent space relative to this if active.
|
||||
AZStd::optional<AZ::Vector3> m_translationOverride; ///< Translation override, if set, reset when selection is empty.
|
||||
AZStd::optional<AZ::Quaternion> m_orientationOverride; ///< Orientation override, if set, reset when selection is empty.
|
||||
AZ::u8 m_pickTypes = PickType::None; ///< What mode(s) were we in when picking an EntityId override.
|
||||
AZ::EntityId m_pickedEntityIdOverride; //!< 'Picked' Entity - frame and parent space relative to this if active.
|
||||
AZStd::optional<AZ::Vector3> m_translationOverride; //!< Translation override, if set, reset when selection is empty.
|
||||
AZStd::optional<AZ::Quaternion> m_orientationOverride; //!< Orientation override, if set, reset when selection is empty.
|
||||
AZ::u8 m_pickTypes = PickType::None; //!< What mode(s) were we in when picking an EntityId override.
|
||||
};
|
||||
|
||||
/// What frame/space is the manipulator currently operating in.
|
||||
//! What frame/space is the manipulator currently operating in.
|
||||
enum class ReferenceFrame
|
||||
{
|
||||
Local, /// The local space of the individual entity.
|
||||
Parent, /// The parent space of the individual entity (world space if no parent exists).
|
||||
World, /// World space (space aligned to world axes - identity).
|
||||
Local, //!< The local space of the individual entity.
|
||||
Parent, //!< The parent space of the individual entity (world space if no parent exists).
|
||||
World, //!< World space (space aligned to world axes - identity).
|
||||
};
|
||||
|
||||
/// Entity selection/interaction handling.
|
||||
/// Provide a suite of functionality for manipulating entities, primarily through their TransformComponent.
|
||||
//! Entity selection/interaction handling.
|
||||
//! Provide a suite of functionality for manipulating entities, primarily through their TransformComponent.
|
||||
class EditorTransformComponentSelection
|
||||
: public ViewportInteraction::ViewportSelectionRequests
|
||||
, private EditorEventsBus::Handler
|
||||
@@ -127,15 +129,15 @@ namespace AzToolsFramework
|
||||
EditorTransformComponentSelection& operator=(const EditorTransformComponentSelection&) = delete;
|
||||
virtual ~EditorTransformComponentSelection();
|
||||
|
||||
/// Register entity manipulators with the ManipulatorManager.
|
||||
/// After being registered, the entity manipulators will draw and check for input.
|
||||
//! Register entity manipulators with the ManipulatorManager.
|
||||
//! After being registered, the entity manipulators will draw and check for input.
|
||||
void RegisterManipulator();
|
||||
/// Unregister entity manipulators with the ManipulatorManager.
|
||||
/// No longer draw or respond to input.
|
||||
//! Unregister entity manipulators with the ManipulatorManager.
|
||||
//! No longer draw or respond to input.
|
||||
void UnregisterManipulator();
|
||||
|
||||
/// ViewportInteraction::ViewportSelectionRequests
|
||||
/// Intercept all viewport mouse events and respond to inputs.
|
||||
//! ViewportInteraction::ViewportSelectionRequests
|
||||
//! Intercept all viewport mouse events and respond to inputs.
|
||||
bool HandleMouseInteraction(
|
||||
const ViewportInteraction::MouseInteractionEvent& mouseInteraction) override;
|
||||
void DisplayViewportSelection(
|
||||
@@ -145,9 +147,9 @@ namespace AzToolsFramework
|
||||
const AzFramework::ViewportInfo& viewportInfo,
|
||||
AzFramework::DebugDisplayRequests& debugDisplay) override;
|
||||
|
||||
/// Add an entity to the current selection
|
||||
//! Add an entity to the current selection
|
||||
void AddEntityToSelection(AZ::EntityId entityId);
|
||||
/// Remove an entity from the current selection
|
||||
//! Remove an entity from the current selection
|
||||
void RemoveEntityFromSelection(AZ::EntityId entityId);
|
||||
|
||||
private:
|
||||
@@ -161,8 +163,8 @@ namespace AzToolsFramework
|
||||
|
||||
void ClearManipulatorTranslationOverride();
|
||||
void ClearManipulatorOrientationOverride();
|
||||
/// Handle an event triggered by the user to clear any manipulator overrides.
|
||||
/// Delegate to either translation or orientation reset/clear depending on the state we're in.
|
||||
//! Handle an event triggered by the user to clear any manipulator overrides.
|
||||
//! Delegate to either translation or orientation reset/clear depending on the state we're in.
|
||||
void DelegateClearManipulatorOverride();
|
||||
|
||||
void ToggleCenterPivotSelection();
|
||||
@@ -251,63 +253,65 @@ namespace AzToolsFramework
|
||||
void SetEntityLocalScale(AZ::EntityId entityId, const AZ::Vector3& localScale);
|
||||
void SetEntityLocalRotation(AZ::EntityId entityId, const AZ::Vector3& localRotation);
|
||||
|
||||
AZ::EntityId m_hoveredEntityId; ///< What EntityId is the mouse currently hovering over (if any).
|
||||
AZ::EntityId m_cachedEntityIdUnderCursor; ///< Store the EntityId on each mouse move for use in Display.
|
||||
AZ::EntityId m_editorCameraComponentEntityId; ///< The EditorCameraComponent EntityId if it is set.
|
||||
EntityIdSet m_selectedEntityIds; ///< Represents the current entities in the selection.
|
||||
AZ::EntityId m_hoveredEntityId; //!< What EntityId is the mouse currently hovering over (if any).
|
||||
AZ::EntityId m_cachedEntityIdUnderCursor; //!< Store the EntityId on each mouse move for use in Display.
|
||||
AZ::EntityId m_editorCameraComponentEntityId; //!< The EditorCameraComponent EntityId if it is set.
|
||||
EntityIdSet m_selectedEntityIds; //!< Represents the current entities in the selection.
|
||||
|
||||
const EditorVisibleEntityDataCache* m_entityDataCache = nullptr; ///< A cache of packed EntityData that can be
|
||||
///< iterated over efficiently without the need
|
||||
///< to make individual EBus calls.
|
||||
AZStd::unique_ptr<EditorHelpers> m_editorHelpers; ///< Editor visualization of entities (icons, shapes, debug visuals etc).
|
||||
EntityIdManipulators m_entityIdManipulators; ///< Mapping from a Manipulator to potentially many EntityIds.
|
||||
const EditorVisibleEntityDataCache* m_entityDataCache = nullptr; //!< A cache of packed EntityData that can be
|
||||
//!< iterated over efficiently without the need
|
||||
//!< to make individual EBus calls.
|
||||
AZStd::unique_ptr<EditorHelpers> m_editorHelpers; //!< Editor visualization of entities (icons, shapes, debug visuals etc).
|
||||
EntityIdManipulators m_entityIdManipulators; //!< Mapping from a Manipulator to potentially many EntityIds.
|
||||
|
||||
EditorBoxSelect m_boxSelect; ///< Type responsible for handling box select.
|
||||
AZStd::unique_ptr<EntityManipulatorCommand> m_manipulatorMoveCommand; ///< Track adjustments to manipulator translation and orientation (during mouse press/move).
|
||||
AZStd::vector<AZStd::unique_ptr<QAction>> m_actions; ///< What actions are tied to this handler.
|
||||
ViewportInteraction::KeyboardModifiers m_previousModifiers; ///< What modifiers were held last frame.
|
||||
EditorContextMenu m_contextMenu; ///< Viewport right click context menu.
|
||||
OptionalFrame m_pivotOverrideFrame; ///< Has a pivot override been set.
|
||||
Mode m_mode = Mode::Translation; ///< Manipulator mode - default to translation.
|
||||
Pivot m_pivotMode = Pivot::Object; ///< Entity pivot mode - default to object (authored root).
|
||||
ReferenceFrame m_referenceFrame = ReferenceFrame::Parent; ///< What reference frame is the Manipulator currently operating in.
|
||||
Frame m_axisPreview; ///< Axes of entity at the time of mouse down to indicate delta of translation.
|
||||
bool m_triedToRefresh = false; ///< Did a refresh event occur to recalculate the current Manipulator transform.
|
||||
bool m_didSetSelectedEntities = false; ///< Was EditorTransformComponentSelection responsible for the most recent entity selection change.
|
||||
bool m_selectedEntityIdsAndManipulatorsDirty = false; ///< Do the active manipulators need to recalculated after a modification (lock/visibility etc).
|
||||
bool m_transformChangedInternally = false; ///< Was an OnTransformChanged event triggered internally or not.
|
||||
ViewportUi::ClusterId m_transformModeClusterId; ///< Id of the Viewport UI cluster for changing transform mode.
|
||||
ViewportUi::ButtonId m_translateButtonId; ///< Id of the Viewport UI button for translate mode.
|
||||
ViewportUi::ButtonId m_rotateButtonId; ///< Id of the Viewport UI button for rotate mode.
|
||||
ViewportUi::ButtonId m_scaleButtonId; ///< Id of the Viewport UI button for scale mode.
|
||||
AZ::Event<ViewportUi::ButtonId>::Handler m_transformModeSelectionHandler; ///< Event handler for the Viewport UI cluster.
|
||||
EditorBoxSelect m_boxSelect; //!< Type responsible for handling box select.
|
||||
AZStd::unique_ptr<EntityManipulatorCommand> m_manipulatorMoveCommand; //!< Track adjustments to manipulator translation and orientation (during mouse press/move).
|
||||
AZStd::vector<AZStd::unique_ptr<QAction>> m_actions; //!< What actions are tied to this handler.
|
||||
ViewportInteraction::KeyboardModifiers m_previousModifiers; //!< What modifiers were held last frame.
|
||||
EditorContextMenu m_contextMenu; //!< Viewport right click context menu.
|
||||
OptionalFrame m_pivotOverrideFrame; //!< Has a pivot override been set.
|
||||
Mode m_mode = Mode::Translation; //!< Manipulator mode - default to translation.
|
||||
Pivot m_pivotMode = Pivot::Object; //!< Entity pivot mode - default to object (authored root).
|
||||
ReferenceFrame m_referenceFrame = ReferenceFrame::Parent; //!< What reference frame is the Manipulator currently operating in.
|
||||
Frame m_axisPreview; //!< Axes of entity at the time of mouse down to indicate delta of translation.
|
||||
bool m_triedToRefresh = false; //!< Did a refresh event occur to recalculate the current Manipulator transform.
|
||||
bool m_didSetSelectedEntities = false; //!< Was EditorTransformComponentSelection responsible for the most recent entity selection change.
|
||||
bool m_selectedEntityIdsAndManipulatorsDirty = false; //!< Do the active manipulators need to recalculated after a modification (lock/visibility etc).
|
||||
bool m_transformChangedInternally = false; //!< Was an OnTransformChanged event triggered internally or not.
|
||||
ViewportUi::ClusterId m_transformModeClusterId; //!< Id of the Viewport UI cluster for changing transform mode.
|
||||
ViewportUi::ButtonId m_translateButtonId; //!< Id of the Viewport UI button for translate mode.
|
||||
ViewportUi::ButtonId m_rotateButtonId; //!< Id of the Viewport UI button for rotate mode.
|
||||
ViewportUi::ButtonId m_scaleButtonId; //!< Id of the Viewport UI button for scale mode.
|
||||
AZ::Event<ViewportUi::ButtonId>::Handler m_transformModeSelectionHandler; //!< Event handler for the Viewport UI cluster.
|
||||
AzFramework::ClickDetector m_clickDetector; //!< Detect different types of mouse click.
|
||||
AzFramework::CursorState m_cursorState; //!< Track the mouse position and delta movement each frame.
|
||||
};
|
||||
|
||||
/// The ETCS (EntityTransformComponentSelection) namespace contains functions and data used exclusively by
|
||||
/// the EditorTransformComponentSelection type. Functions in this namespace are exposed to facilitate testing
|
||||
/// and should not be used outside of EditorTransformComponentSelection or EditorTransformComponentSelectionTests.
|
||||
//! The ETCS (EntityTransformComponentSelection) namespace contains functions and data used exclusively by
|
||||
//! the EditorTransformComponentSelection type. Functions in this namespace are exposed to facilitate testing
|
||||
//! and should not be used outside of EditorTransformComponentSelection or EditorTransformComponentSelectionTests.
|
||||
namespace ETCS
|
||||
{
|
||||
/// The result from calculating the entity (transform component) orientation.
|
||||
/// Does the entity have a parent or not, and what orientation should the manipulator have when
|
||||
/// displayed at the object pivot (determined by the entity hierarchy and what modifiers are held).
|
||||
//! The result from calculating the entity (transform component) orientation.
|
||||
//! Does the entity have a parent or not, and what orientation should the manipulator have when
|
||||
//! displayed at the object pivot (determined by the entity hierarchy and what modifiers are held).
|
||||
struct PivotOrientationResult
|
||||
{
|
||||
AZ::Quaternion m_worldOrientation;
|
||||
AZ::EntityId m_parentId;
|
||||
};
|
||||
|
||||
/// Calculate the orientation for an individual entity based on the incoming reference frame.
|
||||
/// Note: If the entity is in a hierarchy the Parent reference frame will return the orientation of the parent.
|
||||
//! Calculate the orientation for an individual entity based on the incoming reference frame.
|
||||
//! Note: If the entity is in a hierarchy the Parent reference frame will return the orientation of the parent.
|
||||
PivotOrientationResult CalculatePivotOrientation(AZ::EntityId entityId, ReferenceFrame referenceFrame);
|
||||
|
||||
/// Calculate the orientation for a group of entities based on the incoming reference frame.
|
||||
//! Calculate the orientation for a group of entities based on the incoming reference frame.
|
||||
template<typename EntityIdMap>
|
||||
PivotOrientationResult CalculatePivotOrientationForEntityIds(
|
||||
const EntityIdMap& entityIdMap, const ReferenceFrame referenceFrame);
|
||||
|
||||
/// Calculate the orientation for a group of entities based on the incoming
|
||||
/// reference frame with possible pivot override.
|
||||
//! Calculate the orientation for a group of entities based on the incoming
|
||||
//! reference frame with possible pivot override.
|
||||
template<typename EntityIdMap>
|
||||
PivotOrientationResult CalculateSelectionPivotOrientation(
|
||||
const EntityIdMap& entityIdMap, const OptionalFrame& pivotOverrideFrame,
|
||||
|
||||
Reference in New Issue
Block a user