@ -40,16 +40,12 @@
# include <Tests/BoundsTestComponent.h>
namespace AZ
{
std : : ostream & operator < < ( std : : ostream & os , const EntityId entityId )
{
return os < < entityId . ToString ( ) . c_str ( ) ;
}
} // namespace AZ
namespace UnitTest
{
using AzToolsFramework : : ViewportInteraction : : BuildMouseButtons ;
using AzToolsFramework : : ViewportInteraction : : BuildMouseInteraction ;
using AzToolsFramework : : ViewportInteraction : : BuildMousePick ;
AzToolsFramework : : EntityIdList SelectedEntities ( )
{
AzToolsFramework : : EntityIdList selectedEntitiesBefore ;
@ -137,6 +133,18 @@ namespace UnitTest
AzToolsFramework : : EntityIdList m_entityIds ;
} ;
AZ : : EntityId CreateEntityWithBounds ( const char * entityName )
{
AZ : : Entity * entity = nullptr ;
AZ : : EntityId entityId = CreateDefaultEditorEntity ( entityName , & entity ) ;
entity - > Deactivate ( ) ;
entity - > CreateComponent < BoundsTestComponent > ( ) ;
entity - > Activate ( ) ;
return entityId ;
}
class EditorTransformComponentSelectionViewportPickingFixture : public ToolsApplicationFixture
{
public :
@ -146,21 +154,9 @@ namespace UnitTest
// register a simple component implementing BoundsRequestBus and EditorComponentSelectionRequestsBus
app - > RegisterComponentDescriptor ( BoundsTestComponent : : CreateDescriptor ( ) ) ;
auto createEntityWithBoundsFn = [ ] ( const char * entityName )
{
AZ : : Entity * entity = nullptr ;
AZ : : EntityId entityId = CreateDefaultEditorEntity ( entityName , & entity ) ;
entity - > Deactivate ( ) ;
entity - > CreateComponent < BoundsTestComponent > ( ) ;
entity - > Activate ( ) ;
return entityId ;
} ;
m_entityId1 = createEntityWithBoundsFn ( " Entity1 " ) ;
m_entityId2 = createEntityWithBoundsFn ( " Entity2 " ) ;
m_entityId3 = createEntityWithBoundsFn ( " Entity3 " ) ;
m_entityId1 = CreateEntityWithBounds ( " Entity1 " ) ;
m_entityId2 = CreateEntityWithBounds ( " Entity2 " ) ;
m_entityId3 = CreateEntityWithBounds ( " Entity3 " ) ;
}
void PositionEntities ( )
@ -959,6 +955,9 @@ namespace UnitTest
const auto entity2ScreenPosition = AzFramework : : WorldToScreen ( AzToolsFramework : : GetWorldTranslation ( m_entityId2 ) , m_cameraState ) ;
// ensure icons are not enabled to avoid them interfering with bound detection
m_viewportManipulatorInteraction - > GetViewportInteraction ( ) . SetIconsVisible ( false ) ;
// click the entity in the viewport
m_actionDispatcher - > SetStickySelect ( true )
- > CameraState ( m_cameraState )
@ -974,6 +973,137 @@ namespace UnitTest
EXPECT_THAT ( selectedEntities , UnorderedElementsAreArray ( expectedSelectedEntities ) ) ;
}
// entity can be selected using icon
TEST_F ( EditorTransformComponentSelectionViewportPickingManipulatorTestFixture , CursorOverEntityIconReturnsThatEntityId )
{
const AZ : : EntityId boundlessEntityId = CreateDefaultEditorEntity ( " BoundlessEntity " ) ;
// camera (go to position format) -5.00, -8.00, 5.00, 0.00, 0.00
AzFramework : : SetCameraTransform ( m_cameraState , AZ : : Transform : : CreateTranslation ( AZ : : Vector3 ( - 5.0f , - 8.0f , 5.0f ) ) ) ;
// position entity in the world
AZ : : TransformBus : : Event ( boundlessEntityId , & AZ : : TransformBus : : Events : : SetWorldTranslation , AZ : : Vector3 ( - 5.0f , - 1.0f , 5.0f ) ) ;
const float distanceFromCamera = m_cameraState . m_position . GetDistance ( AzToolsFramework : : GetWorldTranslation ( boundlessEntityId ) ) ;
const auto quaterIconSize = AzToolsFramework : : GetIconSize ( distanceFromCamera ) * 0.25f ;
const auto entity1ScreenPosition =
AzFramework : : WorldToScreen ( AzToolsFramework : : GetWorldTranslation ( boundlessEntityId ) , m_cameraState ) +
AzFramework : : ScreenVectorFromVector2 ( AZ : : Vector2 ( quaterIconSize ) ) ;
AzToolsFramework : : EditorVisibleEntityDataCache editorVisibleEntityDataCache ;
AzToolsFramework : : EditorHelpers editorHelpers ( & editorVisibleEntityDataCache ) ;
const auto viewportId = m_viewportManipulatorInteraction - > GetViewportInteraction ( ) . GetViewportId ( ) ;
const auto mousePick = BuildMousePick ( m_cameraState , entity1ScreenPosition ) ;
const auto mouseInteraction = BuildMouseInteraction (
mousePick , BuildMouseButtons ( AzToolsFramework : : ViewportInteraction : : MouseButton : : None ) ,
AzToolsFramework : : ViewportInteraction : : InteractionId ( AZ : : EntityId ( ) , viewportId ) ,
AzToolsFramework : : ViewportInteraction : : KeyboardModifiers ( ) ) ;
const auto mouseInteractionEvent = AzToolsFramework : : ViewportInteraction : : BuildMouseInteractionEvent (
mouseInteraction , AzToolsFramework : : ViewportInteraction : : MouseEvent : : Move , false ) ;
// mimic mouse move
m_actionDispatcher - > CameraState ( m_cameraState ) - > MousePosition ( entity1ScreenPosition ) ;
// simulate hovering over an icon in the viewport
editorVisibleEntityDataCache . CalculateVisibleEntityDatas ( AzFramework : : ViewportInfo { viewportId } ) ;
auto entityIdUnderCursor = editorHelpers . FindEntityIdUnderCursor ( m_cameraState , mouseInteractionEvent ) ;
using : : testing : : Eq ;
EXPECT_THAT ( entityIdUnderCursor . EntityIdUnderCursor ( ) , Eq ( boundlessEntityId ) ) ;
}
// overlapping icons, nearest is detected
TEST_F ( EditorTransformComponentSelectionViewportPickingManipulatorTestFixture , CursorOverOverlappingEntityIconsReturnsClosestEntityId )
{
const AZ : : EntityId boundlessEntityId1 = CreateDefaultEditorEntity ( " BoundlessEntity1 " ) ;
const AZ : : EntityId boundlessEntityId2 = CreateDefaultEditorEntity ( " BoundlessEntity2 " ) ;
// camera (go to position format) -5.00, -8.00, 5.00, 0.00, 0.00
AzFramework : : SetCameraTransform ( m_cameraState , AZ : : Transform : : CreateTranslation ( AZ : : Vector3 ( - 5.0f , - 8.0f , 5.0f ) ) ) ;
// position entities in the world
AZ : : TransformBus : : Event ( boundlessEntityId1 , & AZ : : TransformBus : : Events : : SetWorldTranslation , AZ : : Vector3 ( - 5.0f , - 1.0f , 5.0f ) ) ;
// note: boundlessEntityId2 is closer to the camera
AZ : : TransformBus : : Event ( boundlessEntityId2 , & AZ : : TransformBus : : Events : : SetWorldTranslation , AZ : : Vector3 ( - 5.0f , - 3.0f , 5.0f ) ) ;
const float distanceFromCamera = m_cameraState . m_position . GetDistance ( AzToolsFramework : : GetWorldTranslation ( boundlessEntityId2 ) ) ;
const auto quaterIconSize = AzToolsFramework : : GetIconSize ( distanceFromCamera ) * 0.25f ;
const auto entity2ScreenPosition =
AzFramework : : WorldToScreen ( AzToolsFramework : : GetWorldTranslation ( boundlessEntityId2 ) , m_cameraState ) +
AzFramework : : ScreenVectorFromVector2 ( AZ : : Vector2 ( quaterIconSize ) ) ;
AzToolsFramework : : EditorVisibleEntityDataCache editorVisibleEntityDataCache ;
AzToolsFramework : : EditorHelpers editorHelpers ( & editorVisibleEntityDataCache ) ;
const auto viewportId = m_viewportManipulatorInteraction - > GetViewportInteraction ( ) . GetViewportId ( ) ;
const auto mousePick = BuildMousePick ( m_cameraState , entity2ScreenPosition ) ;
const auto mouseInteraction = BuildMouseInteraction (
mousePick , BuildMouseButtons ( AzToolsFramework : : ViewportInteraction : : MouseButton : : None ) ,
AzToolsFramework : : ViewportInteraction : : InteractionId ( AZ : : EntityId ( ) , viewportId ) ,
AzToolsFramework : : ViewportInteraction : : KeyboardModifiers ( ) ) ;
const auto mouseInteractionEvent = AzToolsFramework : : ViewportInteraction : : BuildMouseInteractionEvent (
mouseInteraction , AzToolsFramework : : ViewportInteraction : : MouseEvent : : Move , false ) ;
// mimic mouse move
m_actionDispatcher - > CameraState ( m_cameraState ) - > MousePosition ( entity2ScreenPosition ) ;
// simulate hovering over an icon in the viewport
editorVisibleEntityDataCache . CalculateVisibleEntityDatas ( AzFramework : : ViewportInfo { viewportId } ) ;
auto entityIdUnderCursor = editorHelpers . FindEntityIdUnderCursor ( m_cameraState , mouseInteractionEvent ) ;
using : : testing : : Eq ;
EXPECT_THAT ( entityIdUnderCursor . EntityIdUnderCursor ( ) , Eq ( boundlessEntityId2 ) ) ;
}
// if an entity with an icon is behind an entity with a bound, the entity with the icon will be selected
// even if the bound is closer (this is because icons are treated as if they are on the near clip plane)
TEST_F (
EditorTransformComponentSelectionViewportPickingManipulatorTestFixture , FurtherAwayEntityWithIconReturnedWhenBoundEntityIsInFront )
{
const AZ : : EntityId boundEntityId = CreateEntityWithBounds ( " BoundEntity " ) ;
const AZ : : EntityId boundlessEntityId = CreateDefaultEditorEntity ( " BoundlessEntity " ) ;
auto * boundTestComponent = AzToolsFramework : : GetEntityById ( boundEntityId ) - > FindComponent < BoundsTestComponent > ( ) ;
boundTestComponent - > m_localBounds = AZ : : Aabb : : CreateFromMinMax ( AZ : : Vector3 ( - 1.5f , - 0.5f , - 0.5f ) , AZ : : Vector3 ( 1.5f , 0.5 , 0.5f ) ) ;
// camera (go to position format) -5.00, -8.00, 5.00, 0.00, 0.00
AzFramework : : SetCameraTransform ( m_cameraState , AZ : : Transform : : CreateTranslation ( AZ : : Vector3 ( - 5.0f , - 8.0f , 5.0f ) ) ) ;
// position entities in the world
AZ : : TransformBus : : Event ( boundEntityId , & AZ : : TransformBus : : Events : : SetWorldTranslation , AZ : : Vector3 ( - 4.0f , - 3.0f , 5.0f ) ) ;
// note: boundlessEntityId2 is closer to the camera
AZ : : TransformBus : : Event ( boundlessEntityId , & AZ : : TransformBus : : Events : : SetWorldTranslation , AZ : : Vector3 ( - 5.0f , - 1.0f , 5.0f ) ) ;
const float distanceFromCamera = m_cameraState . m_position . GetDistance ( AzToolsFramework : : GetWorldTranslation ( boundlessEntityId ) ) ;
const auto quaterIconSize = AzToolsFramework : : GetIconSize ( distanceFromCamera ) * 0.25f ;
const auto entity2ScreenPosition =
AzFramework : : WorldToScreen ( AzToolsFramework : : GetWorldTranslation ( boundlessEntityId ) , m_cameraState ) +
AzFramework : : ScreenVectorFromVector2 ( AZ : : Vector2 ( quaterIconSize ) ) ;
AzToolsFramework : : EditorVisibleEntityDataCache editorVisibleEntityDataCache ;
AzToolsFramework : : EditorHelpers editorHelpers ( & editorVisibleEntityDataCache ) ;
const auto viewportId = m_viewportManipulatorInteraction - > GetViewportInteraction ( ) . GetViewportId ( ) ;
const auto mousePick = BuildMousePick ( m_cameraState , entity2ScreenPosition ) ;
const auto mouseInteraction = BuildMouseInteraction (
mousePick , BuildMouseButtons ( AzToolsFramework : : ViewportInteraction : : MouseButton : : None ) ,
AzToolsFramework : : ViewportInteraction : : InteractionId ( AZ : : EntityId ( ) , viewportId ) ,
AzToolsFramework : : ViewportInteraction : : KeyboardModifiers ( ) ) ;
const auto mouseInteractionEvent = AzToolsFramework : : ViewportInteraction : : BuildMouseInteractionEvent (
mouseInteraction , AzToolsFramework : : ViewportInteraction : : MouseEvent : : Move , false ) ;
// mimic mouse move
m_actionDispatcher - > CameraState ( m_cameraState ) - > MousePosition ( entity2ScreenPosition ) ;
// simulate hovering over an icon in the viewport
editorVisibleEntityDataCache . CalculateVisibleEntityDatas ( AzFramework : : ViewportInfo { viewportId } ) ;
auto entityIdUnderCursor = editorHelpers . FindEntityIdUnderCursor ( m_cameraState , mouseInteractionEvent ) ;
using : : testing : : Eq ;
EXPECT_THAT ( entityIdUnderCursor . EntityIdUnderCursor ( ) , Eq ( boundlessEntityId ) ) ;
}
class EditorTransformComponentSelectionViewportPickingManipulatorTestFixtureParam
: public EditorTransformComponentSelectionViewportPickingManipulatorTestFixture
, public : : testing : : WithParamInterface < bool >
@ -1709,8 +1839,9 @@ namespace UnitTest
using MouseInteractionResult = AzToolsFramework : : ViewportInteraction : : MouseInteractionResult ;
public :
WheelEventWidget ( QWidget * parent = nullptr )
WheelEventWidget ( const AzFramework : : ViewportId viewportId , QWidget * parent = nullptr )
: QWidget ( parent )
, m_viewportId ( viewportId )
{
}
@ -1719,7 +1850,7 @@ namespace UnitTest
namespace vi = AzToolsFramework : : ViewportInteraction ;
vi : : MouseInteraction mouseInteraction ;
mouseInteraction . m_interactionId . m_cameraId = AZ : : EntityId ( ) ;
mouseInteraction . m_interactionId . m_viewportId = 0 ;
mouseInteraction . m_interactionId . m_viewportId = m_viewportId ;
mouseInteraction . m_mouseButtons = vi : : BuildMouseButtons ( ev - > buttons ( ) ) ;
mouseInteraction . m_mousePick = vi : : MousePick ( ) ;
mouseInteraction . m_keyboardModifiers = vi : : BuildKeyboardModifiers ( ev - > modifiers ( ) ) ;
@ -1731,15 +1862,15 @@ namespace UnitTest
}
MouseInteractionResult m_mouseInteractionResult ;
AzFramework : : ViewportId m_viewportId ;
} ;
TEST_F ( EditorTransformComponentSelection Fixture, MouseScrollWheelSwitchesTransformMode )
TEST_F ( EditorTransformComponentSelection ManipulatorTest Fixture, MouseScrollWheelSwitchesTransformMode )
{
using : : testing : : Eq ;
namespace vi = AzToolsFramework : : ViewportInteraction ;
using AzToolsFramework : : EditorTransformComponentSelectionRequestBus ;
const auto transformMode = [ ] ( )
const auto transformMode = [ ]
{
EditorTransformComponentSelectionRequestBus : : Events : : Mode transformMode ;
EditorTransformComponentSelectionRequestBus : : EventResult (
@ -1752,7 +1883,7 @@ namespace UnitTest
// preconditions
EXPECT_THAT ( transformMode ( ) , EditorTransformComponentSelectionRequestBus : : Events : : Mode : : Translation ) ;
auto wheelEventWidget = WheelEventWidget ( ) ;
auto wheelEventWidget = WheelEventWidget ( m_viewportManipulatorInteraction - > GetViewportInteraction ( ) . GetViewportId ( ) ) ;
// attach the global event filter to the placeholder widget
AzQtComponents : : GlobalEventFilter globalEventFilter ( QApplication : : instance ( ) ) ;
wheelEventWidget . installEventFilter ( & globalEventFilter ) ;
@ -1768,6 +1899,7 @@ namespace UnitTest
// then
// transform mode has changed and mouse event was handled
using : : testing : : Eq ;
EXPECT_THAT ( transformMode ( ) , Eq ( EditorTransformComponentSelectionRequestBus : : Events : : Mode : : Rotation ) ) ;
EXPECT_THAT ( wheelEventWidget . m_mouseInteractionResult , Eq ( vi : : MouseInteractionResult : : Viewport ) ) ;
}
@ -3100,7 +3232,7 @@ namespace UnitTest
const AZ : : Transform finalEntityTransform = AzToolsFramework : : GetWorldTransform ( m_entityIdBox ) ;
const auto viewportRay = AzToolsFramework : : ViewportInteraction : : ViewportScreenToWorldRay ( m_cameraState , initialPositionScreen ) ;
const auto distanceAway = ( finalEntityTransform . GetTranslation ( ) - viewportRay . origin) . GetLength ( ) ;
const auto distanceAway = ( finalEntityTransform . GetTranslation ( ) - viewportRay . m_ origin) . GetLength ( ) ;
// ensure final world positions match
EXPECT_THAT ( finalEntityTransform , IsCloseTolerance ( finalTransformWorld , 0.01f ) ) ;