Merge remote-tracking branch 'upstream/main' into nvsickle/DebugInfoDisplay

This commit is contained in:
nvsickle
2021-05-14 10:27:33 -07:00
698 changed files with 4528 additions and 41223 deletions
-1
View File
@@ -1 +0,0 @@
*.xml
-1
View File
@@ -1 +0,0 @@
*.xml
@@ -269,13 +269,13 @@ namespace AZ
{
for (auto& [assetId, dependentAsset] : m_dependencies)
{
if (dependentAsset->IsReady())
if (dependentAsset->IsReady() || dependentAsset->IsError())
{
HandleReadyAsset(dependentAsset);
}
}
}
if (auto asset = m_rootAsset.GetStrongReference(); asset.IsReady())
if (auto asset = m_rootAsset.GetStrongReference(); asset.IsReady() || asset.IsError())
{
HandleReadyAsset(asset);
}
@@ -496,10 +496,10 @@ namespace AZ
m_waitingCount -= 1;
disconnectEbus = true;
if (m_waitingAssets.empty())
{
allReady = true;
}
}
if (m_waitingAssets.empty())
{
allReady = true;
}
}
@@ -510,8 +510,15 @@ namespace AZ
}
}
if (allReady && m_initComplete)
// If there are no assets left to be loaded, trigger the final AssetContainer notification (ready or canceled).
// We guard against prematurely sending it (m_initComplete) because it's possible for assets to get removed from our waiting
// list *while* we're still building up the list, so the list would appear to be empty too soon.
// We also guard against sending it multiple times (m_finalNotificationSent), because in some error conditions, it may be
// possible to try to remove the same asset multiple times, which if it's the last asset, it could trigger multiple
// notifications.
if (allReady && m_initComplete && !m_finalNotificationSent)
{
m_finalNotificationSent = true;
if (m_rootAsset)
{
AssetManagerBus::Broadcast(&AssetManagerBus::Events::OnAssetContainerReady, this);
@@ -137,6 +137,7 @@ namespace AZ
AZStd::atomic_int m_invalidDependencies{ 0 };
AZStd::unordered_set<AZ::Data::AssetId> m_unloadedDependencies;
AZStd::atomic_bool m_initComplete{ false };
AZStd::atomic_bool m_finalNotificationSent{false};
mutable AZStd::recursive_mutex m_preloadMutex;
// AssetId -> List of assets it is still waiting on
@@ -1304,7 +1304,7 @@ namespace AZ
// Add all auto loadable non-asset gems to the list of gem modules to load
if (!moduleLoadData.m_autoLoad)
{
break;
continue;
}
for (AZ::OSString& dynamicLibraryPath : moduleLoadData.m_dynamicLibraryPaths)
{
@@ -351,6 +351,7 @@ namespace UnitTest
public AZ::Data::AssetCatalog
{
static inline const AZ::Uuid TestAssetId{"{E970B177-5F45-44EB-A2C4-9F29D9A0B2A2}"};
static inline const AZ::Uuid MissingAssetId{"{11111111-1111-1111-1111-111111111111}"};
static inline constexpr AZStd::string_view TestAssetPath = "test";
void SetUp() override
@@ -431,24 +432,40 @@ namespace UnitTest
// AssetCatalogRequestBus implementation
// Minimalist mocks to provide our desired asset path or asset id
AZStd::string GetAssetPathById([[maybe_unused]] const AZ::Data::AssetId& id) override
AZStd::string GetAssetPathById(const AZ::Data::AssetId& id) override
{
return TestAssetPath;
if (id == TestAssetId)
{
return TestAssetPath;
}
return "";
}
AZ::Data::AssetId GetAssetIdByPath(
[[maybe_unused]] const char* path, [[maybe_unused]] const AZ::Data::AssetType& typeToRegister,
const char* path, [[maybe_unused]] const AZ::Data::AssetType& typeToRegister,
[[maybe_unused]] bool autoRegisterIfNotFound) override
{
return TestAssetId;
if (path == TestAssetPath)
{
return TestAssetId;
}
return AZ::Data::AssetId();
}
// Return the mocked-out information for our test asset
AZ::Data::AssetInfo GetAssetInfoById([[maybe_unused]] const AZ::Data::AssetId& id) override
AZ::Data::AssetInfo GetAssetInfoById(const AZ::Data::AssetId& id) override
{
AZ::Data::AssetInfo assetInfo;
assetInfo.m_assetId = TestAssetId;
assetInfo.m_assetType = AZ::AzTypeInfo<EmptyAsset>::Uuid();
assetInfo.m_relativePath = TestAssetPath;
if (id == TestAssetId)
{
assetInfo.m_assetId = TestAssetId;
assetInfo.m_assetType = AZ::AzTypeInfo<EmptyAsset>::Uuid();
assetInfo.m_relativePath = TestAssetPath;
}
return assetInfo;
}
@@ -456,15 +473,20 @@ namespace UnitTest
// Set the mocked-out asset load to have a 0-byte length so that the load skips I/O and immediately returns success
AZ::Data::AssetStreamInfo GetStreamInfoForLoad(
[[maybe_unused]] const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) override
const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) override
{
EXPECT_TRUE(type == AZ::AzTypeInfo<EmptyAsset>::Uuid());
AZ::Data::AssetStreamInfo info;
info.m_dataOffset = 0;
info.m_streamName = TestAssetPath;
info.m_dataLen = 0;
info.m_streamFlags = AZ::IO::OpenMode::ModeRead;
if (id == TestAssetId)
{
info.m_streamName = TestAssetPath;
}
return info;
}
@@ -489,4 +511,27 @@ namespace UnitTest
EXPECT_TRUE(testAsset.IsReady());
}
// This test verifies that even if the asset loading returns immediately with an error, all of the loading code works
// successfully. The test itself loads a missing asset twice - the first time is a non-immediate error, where the error
// isn't reported until the DispatchEvents() call. The second time is an immediate error, because now the asset is already
// registered in an Error state. If the test fails, it will likely get caught in the shutdown of the test class, if any
// assets still exist at the point that the asset handler is unregistered. If they're present, then handling of the immediate
// error didn't work, as it left around extra references to the asset that haven't been cleaned up.
TEST_F(AssetManagerStreamerImmediateCompletionTests, ImmediateAssetError_WorksSuccessfully)
{
AZ::Data::AssetLoadParameters loadParams;
// Attempt to load a missing asset the first time. It will get an error, but not until the DispatchEvents() call happens.
auto testAsset1 = AssetManager::Instance().GetAsset<EmptyAsset>(MissingAssetId, AZ::Data::AssetLoadBehavior::Default, loadParams);
AZ::Data::AssetManager::Instance().DispatchEvents();
EXPECT_TRUE(testAsset1.IsError());
// While the reference to the missing asset still exists, try to get it again. This will cause a more immediate error in
// the AssetContainer code, which should still get handled correctly. In the failure condition, it will instead leave the
// AssetContainer in a state where it never sends the final OnAssetContainerReady/Canceled message.
auto testAsset2 = AssetManager::Instance().GetAsset<EmptyAsset>(MissingAssetId, AZ::Data::AssetLoadBehavior::Default, loadParams);
AZ::Data::AssetManager::Instance().DispatchEvents();
EXPECT_TRUE(testAsset2.IsError());
}
} // namespace UnitTest
@@ -24,7 +24,7 @@ namespace AZ::IO
ePakPriorityPakOnly = 2
};
// variables that control behavior of Archive/StreamEngine subsystems
// variables that control behavior of the Archive subsystem
struct ArchiveVars
{
#if defined(_RELEASE)
@@ -37,29 +37,6 @@ namespace AzFramework
void NonUniformScaleComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC_CE("NonUniformScaleService"));
incompatible.push_back(AZ_CRC_CE("DebugDrawObbService"));
incompatible.push_back(AZ_CRC_CE("DebugDrawService"));
incompatible.push_back(AZ_CRC_CE("EMotionFXActorService"));
incompatible.push_back(AZ_CRC_CE("EMotionFXSimpleMotionService"));
incompatible.push_back(AZ_CRC_CE("GradientTransformService"));
incompatible.push_back(AZ_CRC_CE("LegacyMeshService"));
incompatible.push_back(AZ_CRC_CE("LookAtService"));
incompatible.push_back(AZ_CRC_CE("SequenceService"));
incompatible.push_back(AZ_CRC_CE("ClothMeshService"));
incompatible.push_back(AZ_CRC_CE("PhysXJointService"));
incompatible.push_back(AZ_CRC_CE("PhysXCharacterControllerService"));
incompatible.push_back(AZ_CRC_CE("PhysXRagdollService"));
incompatible.push_back(AZ_CRC_CE("WhiteBoxService"));
incompatible.push_back(AZ_CRC_CE("NavigationAreaService"));
incompatible.push_back(AZ_CRC_CE("GeometryService"));
incompatible.push_back(AZ_CRC_CE("CapsuleShapeService"));
incompatible.push_back(AZ_CRC_CE("CompoundShapeService"));
incompatible.push_back(AZ_CRC_CE("CylinderShapeService"));
incompatible.push_back(AZ_CRC_CE("DiskShapeService"));
incompatible.push_back(AZ_CRC_CE("SphereShapeService"));
incompatible.push_back(AZ_CRC_CE("SplineService"));
incompatible.push_back(AZ_CRC_CE("TubeShapeService"));
}
void NonUniformScaleComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
@@ -881,7 +881,7 @@ namespace AzFramework
serializeContext->ClassDeprecate("NetBindable", "{80206665-D429-4703-B42E-94434F82F381}");
serializeContext->Class<TransformComponent, AZ::Component>()
->Version(4, &TransformComponentVersionConverter)
->Version(5, &TransformComponentVersionConverter)
->Field("Parent", &TransformComponent::m_parentId)
->Field("Transform", &TransformComponent::m_worldTM)
->Field("LocalTransform", &TransformComponent::m_localTM)
@@ -99,6 +99,11 @@ namespace AzPhysics
classElement.RemoveElementByName(AZ_CRC_CE("Property Visibility Flags"));
}
if (classElement.GetVersion() <= 4)
{
classElement.RemoveElementByName(AZ_CRC_CE("Simulated"));
}
return true;
}
}
@@ -110,7 +115,7 @@ namespace AzPhysics
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<RigidBodyConfiguration, AzPhysics::SimulatedBodyConfiguration>()
->Version(4, &Internal::RigidBodyVersionConverter)
->Version(5, &Internal::RigidBodyVersionConverter)
->Field("Initial linear velocity", &RigidBodyConfiguration::m_initialLinearVelocity)
->Field("Initial angular velocity", &RigidBodyConfiguration::m_initialAngularVelocity)
->Field("Linear damping", &RigidBodyConfiguration::m_linearDamping)
@@ -119,7 +124,6 @@ namespace AzPhysics
->Field("Start Asleep", &RigidBodyConfiguration::m_startAsleep)
->Field("Interpolate Motion", &RigidBodyConfiguration::m_interpolateMotion)
->Field("Gravity Enabled", &RigidBodyConfiguration::m_gravityEnabled)
->Field("Simulated", &RigidBodyConfiguration::m_simulated)
->Field("Kinematic", &RigidBodyConfiguration::m_kinematic)
->Field("CCD Enabled", &RigidBodyConfiguration::m_ccdEnabled)
->Field("Compute Mass", &RigidBodyConfiguration::m_computeMass)
@@ -57,7 +57,6 @@ namespace AzPhysics
bool m_startAsleep = false;
bool m_interpolateMotion = false;
bool m_gravityEnabled = true;
bool m_simulated = true;
bool m_kinematic = false;
bool m_ccdEnabled = false; //!< Whether continuous collision detection is enabled.
float m_ccdMinAdvanceCoefficient = 0.15f; //!< Coefficient affecting how granularly time is subdivided in CCD.
@@ -88,13 +88,13 @@ namespace AzPhysics
//! Remove a simulated body from the Scene.z
//! @param sceneHandle A handle to the scene to remove the requested simulated body.
//! @param bodyHandle A handle to the simulated body being removed.
virtual void RemoveSimulatedBody(SceneHandle sceneHandle, SimulatedBodyHandle bodyHandle) = 0;
//! @param bodyHandle A handle to the simulated body being removed. This will be set to AzPhysics::InvalidSimulatedBodyHandle as they're no longer valid.
virtual void RemoveSimulatedBody(SceneHandle sceneHandle, SimulatedBodyHandle& bodyHandle) = 0;
//! Remove a list of simulated bodies from the Scene.
//! @param sceneHandle A handle to the scene to remove the simulated bodies from.
//! @param bodyHandles A list of simulated body handles to be removed.
virtual void RemoveSimulatedBodies(SceneHandle sceneHandle, const SimulatedBodyHandleList& bodyHandles) = 0;
//! @param bodyHandles A list of simulated body handles to be removed. All handles will be set to AzPhysics::InvalidSimulatedBodyHandle as they're no longer valid.
virtual void RemoveSimulatedBodies(SceneHandle sceneHandle, SimulatedBodyHandleList& bodyHandles) = 0;
//! Enable / Disable simulation of the requested body. By default all bodies added are enabled.
//! Disabling simulation the body will no longer be affected by any forces, collisions, or found with scene queries.
@@ -286,12 +286,12 @@ namespace AzPhysics
virtual SimulatedBodyList GetSimulatedBodiesFromHandle(const SimulatedBodyHandleList& bodyHandles) = 0;
//! Remove a simulated body from the Scene.
//! @param bodyHandle A handle to the simulated body being removed.
virtual void RemoveSimulatedBody(SimulatedBodyHandle bodyHandle) = 0;
//! @param bodyHandle A handle to the simulated body being removed. This will be set to AzPhysics::InvalidSimulatedBodyHandle as they're no longer valid.
virtual void RemoveSimulatedBody(SimulatedBodyHandle& bodyHandle) = 0;
//! Remove a list of simulated bodies from the Scene.
//! @param bodyHandles A list of simulated body handles to be removed.
virtual void RemoveSimulatedBodies(const SimulatedBodyHandleList& bodyHandles) = 0;
//! @param bodyHandles A list of simulated body handles to be removed. All handles will be set to AzPhysics::InvalidSimulatedBodyHandle as they're no longer valid.
virtual void RemoveSimulatedBodies(SimulatedBodyHandleList& bodyHandles) = 0;
//! Enable / Disable simulation of the requested body. By default all bodies added are enabled.
//! Disabling simulation the body will no longer be affected by any forces, collisions, or found with scene queries.
@@ -62,7 +62,7 @@ namespace AzPhysics
virtual void SetLinearVelocity(const AZ::Vector3& velocity) = 0;
virtual AZ::Vector3 GetAngularVelocity() const = 0;
virtual void SetAngularVelocity(const AZ::Vector3& angularVelocity) = 0;
virtual AZ::Vector3 GetLinearVelocityAtWorldPoint(const AZ::Vector3& worldPoint) = 0;
virtual AZ::Vector3 GetLinearVelocityAtWorldPoint(const AZ::Vector3& worldPoint) const = 0;
virtual void ApplyLinearImpulse(const AZ::Vector3& impulse) = 0;
virtual void ApplyLinearImpulseAtWorldPoint(const AZ::Vector3& impulse, const AZ::Vector3& worldPoint) = 0;
virtual void ApplyAngularImpulse(const AZ::Vector3& angularImpulse) = 0;
@@ -979,7 +979,7 @@ namespace AzFramework
};
serializeContext->Class<ScriptComponent, AZ::Component>()
->Version(3, converter)
->Version(4, converter)
->Field("ContextID", &ScriptComponent::m_contextId)
->Field("Properties", &ScriptComponent::m_properties)
->Field("Script", &ScriptComponent::m_script)
@@ -15,6 +15,7 @@
#include <AzCore/Console/IConsole.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Math/Plane.h>
#include <AzCore/std/numeric.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Windowing/WindowBus.h>
@@ -156,35 +157,25 @@ namespace AzFramework
camera.m_lookAt = transform.GetTranslation() + (camera.Rotation().GetBasisY() * -camera.m_lookDist);
}
static ScreenVector CursorDelta(const AZStd::optional<ScreenPoint>& currentPosition, const AZStd::optional<ScreenPoint>& lastPosition)
{
return currentPosition.has_value() && lastPosition.has_value() ? currentPosition.value() - lastPosition.value()
: ScreenVector(0, 0);
}
bool CameraSystem::HandleEvents(const InputEvent& event)
{
if (const auto& cursor = AZStd::get_if<CursorEvent>(&event))
{
m_currentCursorPosition = cursor->m_position;
m_cursorState.SetCurrentPosition(cursor->m_position);
}
else if (const auto& scroll = AZStd::get_if<ScrollEvent>(&event))
{
m_scrollDelta = scroll->m_delta;
}
return m_cameras.HandleEvents(event, CursorDelta(m_currentCursorPosition, m_lastCursorPosition), m_scrollDelta);
return m_cameras.HandleEvents(event, m_cursorState.CursorDelta(), m_scrollDelta);
}
Camera CameraSystem::StepCamera(const Camera& targetCamera, const float deltaTime)
{
const auto cursorDelta = CursorDelta(m_currentCursorPosition, m_lastCursorPosition);
if (m_currentCursorPosition.has_value())
{
m_lastCursorPosition = m_currentCursorPosition;
}
const auto nextCamera = m_cameras.StepCamera(targetCamera, m_cursorState.CursorDelta(), m_scrollDelta, deltaTime);
const auto nextCamera = m_cameras.StepCamera(targetCamera, cursorDelta, m_scrollDelta, deltaTime);
m_cursorState.Update();
m_scrollDelta = 0.0f;
@@ -236,12 +227,12 @@ namespace AzFramework
}
}
// accumulate
Camera nextCamera = targetCamera;
for (auto& cameraInput : m_activeCameraInputs)
{
nextCamera = cameraInput->StepCamera(nextCamera, cursorDelta, scrollDelta, deltaTime);
}
const Camera nextCamera = AZStd::accumulate(
AZStd::begin(m_activeCameraInputs), AZStd::end(m_activeCameraInputs), targetCamera,
[cursorDelta, scrollDelta, deltaTime](Camera acc, auto& camera) {
acc = camera->StepCamera(acc, cursorDelta, scrollDelta, deltaTime);
return acc;
});
for (int i = 0; i < m_activeCameraInputs.size();)
{
@@ -275,34 +266,42 @@ namespace AzFramework
}
}
RotateCameraInput::RotateCameraInput(const InputChannelId rotateChannelId)
: m_rotateChannelId(rotateChannelId)
{
}
void RotateCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
{
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_channelId == m_rotateChannelId)
const ClickDetector::ClickEvent clickEvent = [&event, this] {
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_state == InputChannel::State::Began)
if (input->m_channelId == m_rotateChannelId)
{
m_tryingToBegin = true;
m_moveAccumulator = 0.0f;
}
else if (input->m_state == InputChannel::State::Ended)
{
m_tryingToBegin = false;
EndActivation();
if (input->m_state == InputChannel::State::Began)
{
return ClickDetector::ClickEvent::Down;
}
else if (input->m_state == InputChannel::State::Ended)
{
return ClickDetector::ClickEvent::Up;
}
}
}
}
return ClickDetector::ClickEvent::Nil;
}();
if (m_tryingToBegin)
switch (const auto outcome = m_clickDetector.DetectClick(clickEvent, cursorDelta); outcome)
{
// only allow the action to begin if the mouse has been moved a small amount
m_moveAccumulator += ScreenVectorLength(cursorDelta);
if (m_moveAccumulator > ed_cameraSystemLookDeadzone)
{
BeginActivation();
m_tryingToBegin = false;
}
case ClickDetector::ClickOutcome::Move:
BeginActivation();
break;
case ClickDetector::ClickOutcome::Release:
EndActivation();
break;
default:
// noop
break;
}
}
@@ -324,6 +323,12 @@ namespace AzFramework
return nextCamera;
}
PanCameraInput::PanCameraInput(const InputChannelId panChannelId, PanAxesFn panAxesFn)
: m_panAxesFn(AZStd::move(panAxesFn))
, m_panChannelId(panChannelId)
{
}
void PanCameraInput::HandleEvents(
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
{
@@ -400,6 +405,11 @@ namespace AzFramework
return TranslationType::Nil;
}
TranslateCameraInput::TranslateCameraInput(TranslationAxesFn translationAxesFn)
: m_translationAxesFn(AZStd::move(translationAxesFn))
{
}
void TranslateCameraInput::HandleEvents(
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
{
@@ -574,6 +584,11 @@ namespace AzFramework
return nextCamera;
}
OrbitDollyCursorMoveCameraInput::OrbitDollyCursorMoveCameraInput(const InputChannelId dollyChannelId)
: m_dollyChannelId(dollyChannelId)
{
}
void OrbitDollyCursorMoveCameraInput::HandleEvents(
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
{
@@ -17,6 +17,8 @@
#include <AzCore/std/containers/variant.h>
#include <AzCore/std/optional.h>
#include <AzFramework/Input/Channels/InputChannel.h>
#include <AzFramework/Viewport/ClickDetector.h>
#include <AzFramework/Viewport/CursorState.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
#include <AzFramework/Viewport/ViewportId.h>
@@ -188,26 +190,21 @@ namespace AzFramework
Cameras m_cameras;
private:
CursorState m_cursorState;
float m_scrollDelta = 0.0f;
AZStd::optional<ScreenPoint> m_lastCursorPosition;
AZStd::optional<ScreenPoint> m_currentCursorPosition;
};
class RotateCameraInput : public CameraInput
{
public:
explicit RotateCameraInput(const InputChannelId rotateChannelId)
: m_rotateChannelId(rotateChannelId)
{
}
explicit RotateCameraInput(InputChannelId rotateChannelId);
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
private:
InputChannelId m_rotateChannelId;
float m_moveAccumulator = 0.0f;
bool m_tryingToBegin = false;
ClickDetector m_clickDetector;
};
struct PanAxes
@@ -240,11 +237,8 @@ namespace AzFramework
class PanCameraInput : public CameraInput
{
public:
PanCameraInput(const InputChannelId panChannelId, PanAxesFn panAxesFn)
: m_panAxesFn(AZStd::move(panAxesFn))
, m_panChannelId(panChannelId)
{
}
PanCameraInput(InputChannelId panChannelId, PanAxesFn panAxesFn);
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
@@ -283,10 +277,8 @@ namespace AzFramework
class TranslateCameraInput : public CameraInput
{
public:
explicit TranslateCameraInput(TranslationAxesFn translationAxesFn)
: m_translationAxesFn(AZStd::move(translationAxesFn))
{
}
explicit TranslateCameraInput(TranslationAxesFn translationAxesFn);
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
void ResetImpl() override;
@@ -363,8 +355,7 @@ namespace AzFramework
class OrbitDollyCursorMoveCameraInput : public CameraInput
{
public:
explicit OrbitDollyCursorMoveCameraInput(const InputChannelId dollyChannelId)
: m_dollyChannelId(dollyChannelId) {}
explicit OrbitDollyCursorMoveCameraInput(InputChannelId dollyChannelId);
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
@@ -0,0 +1,68 @@
/*
* 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 <AzFramework/Viewport/ClickDetector.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
namespace AzFramework
{
ClickDetector::ClickOutcome ClickDetector::DetectClick(const ClickEvent clickEvent, const ScreenVector& cursorDelta)
{
if (clickEvent == ClickEvent::Down)
{
const auto now = std::chrono::steady_clock::now();
if (m_tryBeginTime)
{
const std::chrono::duration<float> diff = now - m_tryBeginTime.value();
if (diff.count() < m_doubleClickInterval)
{
return ClickOutcome::Nil;
}
}
m_detectionState = DetectionState::WaitingForMove;
m_moveAccumulator = 0.0f;
m_tryBeginTime = now;
}
else if (clickEvent == ClickEvent::Up)
{
const auto clickOutcome = [detectionState = m_detectionState] {
if (detectionState == DetectionState::WaitingForMove)
{
return ClickOutcome::Click;
}
if (detectionState == DetectionState::Moved)
{
return ClickOutcome::Release;
}
return ClickOutcome::Nil;
}();
m_detectionState = DetectionState::Nil;
return clickOutcome;
}
if (m_detectionState == DetectionState::WaitingForMove)
{
// only allow the action to begin if the mouse has been moved a small amount
m_moveAccumulator += ScreenVectorLength(cursorDelta);
if (m_moveAccumulator > m_deadZone)
{
m_detectionState = DetectionState::Moved;
return ClickOutcome::Move;
}
}
return ClickOutcome::Nil;
}
} // namespace AzFramework
@@ -0,0 +1,75 @@
/*
* 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
#include <AzCore/std/optional.h>
#include <chrono>
namespace AzFramework
{
struct ScreenVector;
//! Utility class to help detect different types of mouse click (mouse down and up with
//! no movement), mouse move (down and initial move after some threshold) and mouse release
//! (mouse down with movement and then mouse up).
class ClickDetector
{
//! Alias for recording time of mouse down events
using Time = std::chrono::time_point<std::chrono::steady_clock>;
public:
//! Internal representation of click event (map from external event for this when
//! calling DetectClick).
enum class ClickEvent
{
Nil,
Down,
Up
};
//! The type of mouse click.
enum class ClickOutcome
{
Nil, //!< Not recognized.
Move, //!< Initial move after mouse down.
Click, //!< Mouse down and up with no intermediate movement.
Release //!< Mouse down with movement and then mouse up.
};
//! Called from any type of 'handle event' function.
ClickOutcome DetectClick(ClickEvent clickEvent, const ScreenVector& cursorDelta);
void SetDoubleClickInterval(float doubleClickInterval);
private:
//! Internal state of ClickDetector based on incoming events.
enum class DetectionState
{
Nil, //!< Initial state
WaitingForMove, //! Mouse down has happened but mouse hasn't yet moved.
Moved //! Mouse has moved, no longer will be counted as a click.
};
float m_moveAccumulator = 0.0f; //!< How far the mouse has moved after mouse down.
float m_deadZone = 2.0f; //!< How far to move before a click is cancelled (when Move will fire).
float m_doubleClickInterval = 0.4f; //!< Default double click interval, can be overridden.
DetectionState m_detectionState; //!< Internal state of ClickDetector.
AZStd::optional<Time> m_tryBeginTime; //!< Mouse down time (happens each mouse down, helps with double click handling).
};
inline void ClickDetector::SetDoubleClickInterval(const float doubleClickInterval)
{
m_doubleClickInterval = doubleClickInterval;
}
} // namespace AzFramework
@@ -0,0 +1,56 @@
/*
* 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
#include <AzFramework/Viewport/ScreenGeometry.h>
#include <AzCore/std/optional.h>
namespace AzFramework
{
//! Utility type to wrap a current and last cursor position.
struct CursorState
{
//! Returns the delta between the current and last cursor position.
[[nodiscard]] ScreenVector CursorDelta() const;
//! Call this in a 'handle event' call to update the most recent cursor position.
void SetCurrentPosition(const ScreenPoint& currentPosition);
//! Call this in an 'update' call to copy the current cursor position to the last
//! cursor position.
void Update();
private:
AZStd::optional<ScreenPoint> m_lastCursorPosition;
AZStd::optional<ScreenPoint> m_currentCursorPosition;
};
inline void CursorState::SetCurrentPosition(const ScreenPoint& currentPosition)
{
m_currentCursorPosition = currentPosition;
}
inline ScreenVector CursorState::CursorDelta() const
{
return m_currentCursorPosition.has_value() && m_lastCursorPosition.has_value()
? m_currentCursorPosition.value() - m_lastCursorPosition.value()
: ScreenVector(0, 0);
}
inline void CursorState::Update()
{
if (m_currentCursorPosition.has_value())
{
m_lastCursorPosition = m_currentCursorPosition;
}
}
} // namespace AzFramework
@@ -103,6 +103,9 @@ set(FILES
Viewport/CameraState.cpp
Viewport/CameraInput.h
Viewport/CameraInput.cpp
Viewport/ClickDetector.h
Viewport/ClickDetector.cpp
Viewport/CursorState.h
Viewport/DisplayContextRequestBus.h
Entity/BehaviorEntity.cpp
Entity/BehaviorEntity.h
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="22px" height="20px" viewBox="0 0 22 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Icon / Toolbar / Play Console / Simulate Physics</title>
<defs>
<filter id="filter-1">
<feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 1.000000 0"></feColorMatrix>
</filter>
<path d="M15.6428742,11.9827626 C14.6770188,10.7687802 14.4956657,9.03975584 15.318317,7.61488202 C16.3923878,5.75453685 18.7712019,5.11713552 20.6315471,6.1912063 C22.4918923,7.26527709 23.1292936,9.64409122 22.0552228,11.5044364 C21.013572,13.3086286 18.7447543,13.9625898 16.9118187,13.0207075 C16.3649862,12.6909949 16.1306352,12.5503038 15.6428742,11.9827626 Z M21.8449657,10.7460039 C22.0595758,10.1342007 22.2373043,8.55906064 21.8449657,9.05833735 C21.4526271,9.55761407 21.2918858,9.96701497 20.8130083,10.4818331 C19.9042528,11.4587922 18.2692551,11.5405181 17.2685207,11.5405181 C16.2677863,11.5405181 17.2685207,12.7159692 18.7447536,12.7159692 C20.2209864,12.7159692 21.4894947,11.7593684 21.8449657,10.7460039 Z M16.197388,14.0090117 C16.3975463,13.8229455 16.701528,13.7946404 16.9039947,13.9824218 C17.1064613,14.1702033 17.0967404,14.4628636 16.9305845,14.6890285 C16.3625338,15.4622373 15.7176919,17.2794303 15.043117,20.0013354 L13.9731652,20.0013354 C13.74245,13.5862059 13.0475403,9.88991503 12.1237705,9.88991503 C11.4442663,9.88991503 10.8362307,11.6219 10.2533824,15.1128617 C10.1857467,15.5179652 9.53567637,19.2951617 9.47653253,20.0013354 L8.32801304,20.0013354 C8.32801304,11.4928776 6.37313749,5.95119082 2.36188186,4.12720757 C2.11050727,4.01290346 1.65264126,3.52516756 2.0367453,2.87964604 C2.42084934,2.23412452 3.17248781,2.63112648 3.42386239,2.7454306 C7.09988702,4.41697884 8.58435154,8.916133 9.2170288,15.2523916 C9.23745765,15.1271094 9.25478369,15.0215631 9.26703535,14.9481819 C9.97359325,10.7162629 10.1786779,8.42129315 12.1237705,8.42129315 C14.0688632,8.42129315 14.4491297,12.5275507 14.8251327,17.6881742 C15.0978485,16.4034552 15.5446067,14.6158341 16.197388,14.0090117 Z M2.0241711,20.6412764 L22.0595758,20.6412764 L20.6246231,21.7592943 L3.70859025,21.7592943 L2.0241711,20.6412764 Z" id="path-2"></path>
</defs>
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Play-Console-v2" transform="translate(-169.000000, -8.000000)">
<g id="Play-Console" transform="translate(100.000000, 0.000000)">
<g id="Icon-/-Toolbar-/-Play-Console-/-Simulate-Physics" transform="translate(68.000000, 6.000000)" filter="url(#filter-1)">
<g>
<mask id="mask-3" fill="white">
<use xlink:href="#path-2"></use>
</mask>
<use id="Shape" fill="#FFFFFF" fill-rule="nonzero" xlink:href="#path-2"></use>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

@@ -372,6 +372,7 @@
<file>img/UI20/toolbar/Select.svg</file>
<file>img/UI20/toolbar/select_object.svg</file>
<file>img/UI20/toolbar/Select_terrain.svg</file>
<file>img/UI20/toolbar/Simulate_Physics.svg</file>
<file>img/UI20/toolbar/Simulate_Physics_on_selected_objects.svg</file>
<file>img/UI20/toolbar/Terrain.svg</file>
<file>img/UI20/toolbar/Terrain_Texture.svg</file>
+2 -3
View File
@@ -17,7 +17,7 @@
#include <array>
AZ_PUSH_DISABLE_WARNING(4389 4800, "-Wunknown-warning-option"); // 'int' : forcing value to bool 'true' or 'false' (performance warning).
#undef strdup // platform.h in CryCommon changes this define which is required by googletest
#undef strdup // This define is required by googletest
#include <gtest/gtest.h>
#include <gmock/gmock.h>
AZ_POP_DISABLE_WARNING;
@@ -477,8 +477,7 @@ int main(int argc, char** argv)
} \
} while (0); // safe multi-line macro - creates a single statement
// Avoid accidentally being managed by CryMemory, or problems with new/delete when
// AZ allocators are not ready or properly un/initialized.
// Avoid problems with new/delete when AZ allocators are not ready or properly un/initialized.
#define AZ_TEST_CLASS_ALLOCATOR(Class_) \
void* operator new (size_t size) \
{ \
@@ -282,14 +282,6 @@ namespace AzToolsFramework
containerEntity->AddComponent(aznew Prefab::EditorPrefabComponent());
HandleEntitiesAdded({containerEntity});
HandleEntitiesAdded(entities);
// Update the template of the instance since we modified the entities of the instance by calling HandleEntitiesAdded.
Prefab::PrefabDom serializedInstance;
if (Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(addedInstance, serializedInstance))
{
m_prefabSystemComponent->UpdatePrefabTemplate(addedInstance.GetTemplateId(), serializedInstance);
}
return addedInstance;
}
@@ -270,7 +270,7 @@ namespace AzToolsFramework
return parentInstance;
}
void InstanceToTemplatePropagator::AddPatchesToLink(PrefabDom& patches, Link& link)
void InstanceToTemplatePropagator::AddPatchesToLink(const PrefabDom& patches, Link& link)
{
PrefabDom& linkDom = link.GetLinkDom();
PrefabDomValueReference linkPatchesReference =
@@ -279,7 +279,14 @@ namespace AzToolsFramework
// This logic only covers addition of patches. If patches already exists, the given list of patches must be appended to them.
if (!linkPatchesReference.has_value())
{
linkDom.AddMember(rapidjson::StringRef(PrefabDomUtils::PatchesName), patches, linkDom.GetAllocator());
/*
If the original allocator the patches were created with gets destroyed, then the patches would become garbage in the
linkDom. Since we cannot guarantee the lifecycle of the patch allocators, we are doing a copy of the patches here to
associate them with the linkDom's allocator.
*/
PrefabDom patchesCopy;
patchesCopy.CopyFrom(patches, linkDom.GetAllocator());
linkDom.AddMember(rapidjson::StringRef(PrefabDomUtils::PatchesName), patchesCopy, linkDom.GetAllocator());
}
}
}
@@ -41,7 +41,7 @@ namespace AzToolsFramework
void ApplyPatchesToInstance(const AZ::EntityId& entityId, PrefabDom& patches, const Instance& instanceToAddPatches) override;
void AddPatchesToLink(PrefabDom& patches, Link& link);
void AddPatchesToLink(const PrefabDom& patches, Link& link);
private:
@@ -27,6 +27,7 @@ namespace AzToolsFramework
using PrefabDomList = AZStd::vector<PrefabDom>;
using PrefabDomReference = AZStd::optional<AZStd::reference_wrapper<PrefabDom>>;
using PrefabDomConstReference = AZStd::optional<AZStd::reference_wrapper<const PrefabDom>>;
using PrefabDomValueReference = AZStd::optional<AZStd::reference_wrapper<PrefabDomValue>>;
using PrefabDomValueConstReference = AZStd::optional<AZStd::reference_wrapper<const PrefabDomValue>>;
@@ -122,30 +122,49 @@ namespace AzToolsFramework
AZ::EntityId containerEntityId = instanceToCreate->get().GetContainerEntityId();
// Parent the entities to the container entity. Parenting the container entities of the instances passed to createPrefab
// will be done during the creation of links below.
for (AZ::Entity* topLevelEntity : entities)
{
AZ::TransformBus::Event(topLevelEntity->GetId(), &AZ::TransformBus::Events::SetParent, containerEntityId);
}
// Update the template of the instance since the entities are modified since the template creation.
Prefab::PrefabDom serializedInstance;
if (Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(instanceToCreate->get(), serializedInstance))
{
m_prefabSystemComponentInterface->UpdatePrefabTemplate(instanceToCreate->get().GetTemplateId(), serializedInstance);
}
instanceToCreate->get().GetNestedInstances([&](AZStd::unique_ptr<Instance>& nestedInstance) {
AZ_Assert(nestedInstance, "Invalid nested instance found in the new prefab created.");
EntityOptionalReference nestedInstanceContainerEntity = nestedInstance->GetContainerEntity();
AZ_Assert(
nestedInstanceContainerEntity, "Invalid container entity found for the nested instance used in prefab creation.");
// These link creations shouldn't be undone because that would put the template in a non-usable state if a user
// chooses to instantiate the template after undoing the creation.
CreateLink(
{&nestedInstanceContainerEntity->get()}, *nestedInstance, instanceToCreate->get().GetTemplateId(),
undoBatch.GetUndoBatch(), containerEntityId);
undoBatch.GetUndoBatch(), containerEntityId, false);
});
// Create a link between the templates of the newly created instance and the instance it's being parented under.
CreateLink(
topLevelEntities, instanceToCreate->get(), commonRootEntityOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch(),
commonRootEntityId);
topLevelEntities, instanceToCreate->get(), commonRootEntityOwningInstance->get().GetTemplateId(),
undoBatch.GetUndoBatch(), commonRootEntityId);
// Change top level entities to be parented to the container entity
// Mark them as dirty so this change is correctly applied to the template
for (AZ::Entity* topLevelEntity : topLevelEntities)
{
AZ::EntityId topLevelEntityId = topLevelEntity->GetId();
if (topLevelEntityId.IsValid())
{
m_prefabUndoCache.UpdateCache(topLevelEntityId);
undoBatch.MarkEntityDirty(topLevelEntityId);
AZ::TransformBus::Event(topLevelEntityId, &AZ::TransformBus::Events::SetParent, containerEntityId);
m_prefabUndoCache.UpdateCache(topLevelEntity->GetId());
// Parenting entities would mark entities as dirty. But we want to unmark the top level entities as dirty because
// if we don't, the template created would be updated and cause issues with undo operation followed by instantiation.
ToolsApplicationRequests::Bus::Broadcast(
&ToolsApplicationRequests::Bus::Events::RemoveDirtyEntity, topLevelEntity->GetId());
}
}
@@ -296,7 +315,7 @@ namespace AzToolsFramework
void PrefabPublicHandler::CreateLink(
const EntityList& topLevelEntities, Instance& sourceInstance, TemplateId targetTemplateId,
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId commonRootEntityId)
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId commonRootEntityId, const bool isUndoRedoSupportNeeded)
{
AZ::EntityId containerEntityId = sourceInstance.GetContainerEntityId();
AZ::Entity* containerEntity = GetEntityById(containerEntityId);
@@ -322,9 +341,19 @@ namespace AzToolsFramework
m_instanceToTemplateInterface->GeneratePatch(patch, containerEntityDomBefore, containerEntityDomAfter);
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, containerEntityId);
LinkId linkId = PrefabUndoHelpers::CreateLink(
sourceInstance.GetTemplateId(), targetTemplateId, patch, sourceInstance.GetInstanceAlias(),
undoBatch);
LinkId linkId;
if (isUndoRedoSupportNeeded)
{
linkId = PrefabUndoHelpers::CreateLink(
sourceInstance.GetTemplateId(), targetTemplateId, AZStd::move(patch), sourceInstance.GetInstanceAlias(), undoBatch);
}
else
{
linkId = m_prefabSystemComponentInterface->CreateLink(
targetTemplateId, sourceInstance.GetTemplateId(), sourceInstance.GetInstanceAlias(), patch,
InvalidLinkId);
m_prefabSystemComponentInterface->PropagateTemplateChanges(targetTemplateId);
}
sourceInstance.SetLinkId(linkId);
@@ -357,7 +386,7 @@ namespace AzToolsFramework
patchesCopyForUndoSupport.CopyFrom(nestedInstanceLinkPatches->get(), patchesCopyForUndoSupport.GetAllocator());
PrefabUndoHelpers::RemoveLink(
sourceInstance->GetTemplateId(), targetTemplateId, sourceInstance->GetInstanceAlias(), sourceInstance->GetLinkId(),
patchesCopyForUndoSupport, undoBatch);
AZStd::move(patchesCopyForUndoSupport), undoBatch);
}
PrefabOperationResult PrefabPublicHandler::SavePrefab(AZ::IO::Path filePath)
@@ -607,11 +636,18 @@ namespace AzToolsFramework
if (!EntitiesBelongToSameInstance(entityIds))
{
return AZ::Failure(AZStd::string("DeleteEntitiesAndAllDescendantsInInstance - Deletion Error. Cannot delete multiple "
"entities belonging to different instances with one operation."));
return AZ::Failure(AZStd::string("Cannot delete multiple entities belonging to different instances with one operation."));
}
InstanceOptionalReference instance = GetOwnerInstanceByEntityId(entityIds[0]);
AZ::EntityId firstEntityIdToDelete = entityIds[0];
InstanceOptionalReference commonOwningInstance = GetOwnerInstanceByEntityId(firstEntityIdToDelete);
// If the first entity id is a container entity id, then we need to mark its parent as the common owning instance because you
// cannot delete an instance from itself.
if (commonOwningInstance->get().GetContainerEntityId() == firstEntityIdToDelete)
{
commonOwningInstance = commonOwningInstance->get().GetParentInstance();
}
// Retrieve entityList from entityIds
EntityList inputEntityList = EntityIdListToEntityList(entityIds);
@@ -651,14 +687,14 @@ namespace AzToolsFramework
AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::AzToolsFramework, "Internal::DeleteEntities:UndoCaptureAndPurgeEntities");
Prefab::PrefabDom instanceDomBefore;
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBefore, instance->get());
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBefore, commonOwningInstance->get());
if (deleteDescendants)
{
AZStd::vector<AZ::Entity*> entities;
AZStd::vector<AZStd::unique_ptr<Instance>> instances;
bool success = RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, instance->get(), entities, instances);
bool success = RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonOwningInstance->get(), entities, instances);
if (!success)
{
@@ -672,6 +708,7 @@ namespace AzToolsFramework
for (auto& nestedInstance : instances)
{
RemoveLink(nestedInstance, commonOwningInstance->get().GetTemplateId(), currentUndoBatch);
nestedInstance.reset();
}
}
@@ -683,22 +720,22 @@ namespace AzToolsFramework
// If this is the container entity, it actually represents the instance so get its owner
if (owningInstance->get().GetContainerEntityId() == entityId)
{
auto instancePtr = instance->get().DetachNestedInstance(owningInstance->get().GetInstanceAlias());
instancePtr.reset();
auto instancePtr = commonOwningInstance->get().DetachNestedInstance(owningInstance->get().GetInstanceAlias());
RemoveLink(instancePtr, commonOwningInstance->get().GetTemplateId(), currentUndoBatch);
}
else
{
instance->get().DetachEntity(entityId);
commonOwningInstance->get().DetachEntity(entityId);
AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationRequests::DeleteEntity, entityId);
}
}
}
Prefab::PrefabDom instanceDomAfter;
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomAfter, instance->get());
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomAfter, commonOwningInstance->get());
PrefabUndoInstance* command = aznew PrefabUndoInstance("Instance deletion");
command->Capture(instanceDomBefore, instanceDomAfter, instance->get().GetTemplateId());
command->Capture(instanceDomBefore, instanceDomAfter, commonOwningInstance->get().GetTemplateId());
command->SetParent(selCommand);
}
@@ -77,10 +77,11 @@ namespace AzToolsFramework
* \param targetInstance The id of the target template.
* \param undoBatch The undo batch to set as parent for this create link action.
* \param commonRootEntityId The id of the entity that the source instance should be parented under.
* \param isUndoRedoSupportNeeded The flag indicating whether the link should be created with undo/redo support or not.
*/
void CreateLink(
const EntityList& topLevelEntities, Instance& sourceInstance, TemplateId targetTemplateId,
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId commonRootEntityId);
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId commonRootEntityId, const bool isUndoRedoSupportNeeded = true);
/**
* Removes the link between template of the sourceInstance and the template corresponding to targetTemplateId.
@@ -583,7 +583,7 @@ namespace AzToolsFramework
const TemplateId& linkTargetId,
const TemplateId& linkSourceId,
const InstanceAlias& instanceAlias,
const PrefabDomReference linkPatch,
const PrefabDomConstReference linkPatches,
const LinkId& linkId)
{
if (linkTargetId == InvalidTemplateId)
@@ -667,9 +667,9 @@ namespace AzToolsFramework
rapidjson::StringRef(PrefabDomUtils::SourceName), rapidjson::StringRef(sourceTemplate.GetFilePath().c_str()),
newLink.GetLinkDom().GetAllocator());
if (linkPatch && linkPatch->get().IsArray() && !(linkPatch->get().Empty()))
if (linkPatches && linkPatches->get().IsArray() && !(linkPatches->get().Empty()))
{
m_instanceToTemplatePropagator.AddPatchesToLink(linkPatch.value(), newLink);
m_instanceToTemplatePropagator.AddPatchesToLink(linkPatches.value(), newLink);
}
//update the target template dom to have the proper values for the source template dom
@@ -156,7 +156,7 @@ namespace AzToolsFramework
const TemplateId& linkTargetId,
const TemplateId& linkSourceId,
const InstanceAlias& instanceAlias,
const PrefabDomReference linkPatch,
const PrefabDomConstReference linkPatches,
const LinkId& linkId = InvalidLinkId) override;
/**
@@ -43,9 +43,9 @@ namespace AzToolsFramework
PrefabDomValue::MemberIterator& instanceIterator, InstanceOptionalReference instance) = 0;
//creates a new Link
virtual LinkId CreateLink(const TemplateId& linkTargetId, const TemplateId& linkSourceId,
const InstanceAlias& instanceAlias, const PrefabDomReference linkPatch,
const LinkId& linkId = InvalidLinkId) = 0;
virtual LinkId CreateLink(
const TemplateId& linkTargetId, const TemplateId& linkSourceId, const InstanceAlias& instanceAlias,
const PrefabDomConstReference linkPatches, const LinkId& linkId = InvalidLinkId) = 0;
virtual void RemoveLink(const LinkId& linkId) = 0;
@@ -124,7 +124,7 @@ namespace AzToolsFramework
const TemplateId& targetId,
const TemplateId& sourceId,
const InstanceAlias& instanceAlias,
PrefabDomReference linkPatches,
PrefabDom linkPatches,
const LinkId linkId)
{
m_targetId = targetId;
@@ -132,10 +132,7 @@ namespace AzToolsFramework
m_instanceAlias = instanceAlias;
m_linkId = linkId;
if (linkPatches.has_value())
{
m_linkPatches = AZStd::move(linkPatches->get());
}
m_linkPatches = AZStd::move(linkPatches);
//if linkId is invalid, set as ADD
if (m_linkId == InvalidLinkId)
@@ -228,7 +225,7 @@ namespace AzToolsFramework
if (link.has_value())
{
m_linkDomPrevious = AZStd::move(link->get().GetLinkDom());
m_linkDomPrevious.CopyFrom(link->get().GetLinkDom(), m_linkDomPrevious.GetAllocator());
}
//get source templateDom
@@ -275,7 +272,7 @@ namespace AzToolsFramework
if (patchesIter == m_linkDomNext.MemberEnd())
{
m_linkDomNext.AddMember(
rapidjson::GenericStringRef(PrefabDomUtils::PatchesName), patchLinkCopy, m_linkDomNext.GetAllocator());
rapidjson::GenericStringRef(PrefabDomUtils::PatchesName), AZStd::move(patchLinkCopy), m_linkDomNext.GetAllocator());
}
else
{
@@ -303,9 +300,7 @@ namespace AzToolsFramework
return;
}
PrefabDom moveLink;
moveLink.CopyFrom(linkDom, linkDom.GetAllocator());
link->get().GetLinkDom() = AZStd::move(moveLink);
link->get().SetLinkDom(linkDom);
//propagate the link changes
link->get().UpdateTarget();
@@ -101,7 +101,7 @@ namespace AzToolsFramework
const TemplateId& targetId,
const TemplateId& sourceId,
const InstanceAlias& instanceAlias,
PrefabDomReference linkPatches = PrefabDomReference(),
PrefabDom linkPatches = PrefabDom(),
const LinkId linkId = InvalidLinkId);
void Undo() override;
@@ -34,11 +34,11 @@ namespace AzToolsFramework
}
LinkId CreateLink(
TemplateId sourceTemplateId, TemplateId targetTemplateId, PrefabDomReference patch,
TemplateId sourceTemplateId, TemplateId targetTemplateId, PrefabDom patch,
const InstanceAlias& instanceAlias, UndoSystem::URSequencePoint* undoBatch)
{
auto linkAddUndo = aznew PrefabUndoInstanceLink("Create Link");
linkAddUndo->Capture(targetTemplateId, sourceTemplateId, instanceAlias, patch, InvalidLinkId);
linkAddUndo->Capture(targetTemplateId, sourceTemplateId, instanceAlias, AZStd::move(patch), InvalidLinkId);
linkAddUndo->SetParent(undoBatch);
linkAddUndo->Redo();
@@ -47,10 +47,10 @@ namespace AzToolsFramework
void RemoveLink(
TemplateId sourceTemplateId, TemplateId targetTemplateId, const InstanceAlias& instanceAlias, LinkId linkId,
PrefabDomReference linkPatches, UndoSystem::URSequencePoint* undoBatch)
PrefabDom linkPatches, UndoSystem::URSequencePoint* undoBatch)
{
auto linkRemoveUndo = aznew PrefabUndoInstanceLink("Remove Link");
linkRemoveUndo->Capture(targetTemplateId, sourceTemplateId, instanceAlias, linkPatches, linkId);
linkRemoveUndo->Capture(targetTemplateId, sourceTemplateId, instanceAlias, AZStd::move(linkPatches), linkId);
linkRemoveUndo->SetParent(undoBatch);
linkRemoveUndo->Redo();
}
@@ -22,11 +22,11 @@ namespace AzToolsFramework
const Instance& instance, AZStd::string_view undoMessage, const PrefabDom& instanceDomBeforeUpdate,
UndoSystem::URSequencePoint* undoBatch);
LinkId CreateLink(
TemplateId sourceTemplateId, TemplateId targetTemplateId, PrefabDomReference patch,
TemplateId sourceTemplateId, TemplateId targetTemplateId, PrefabDom patch,
const InstanceAlias& instanceAlias, UndoSystem::URSequencePoint* undoBatch);
void RemoveLink(
TemplateId sourceTemplateId, TemplateId targetTemplateId, const InstanceAlias& instanceAlias, LinkId linkId,
PrefabDomReference linkPatches, UndoSystem::URSequencePoint* undoBatch);
PrefabDom linkPatches, UndoSystem::URSequencePoint* undoBatch);
}
} // namespace Prefab
} // namespace AzToolsFramework
@@ -63,29 +63,6 @@ namespace AzToolsFramework
void EditorNonUniformScaleComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC_CE("NonUniformScaleService"));
incompatible.push_back(AZ_CRC_CE("DebugDrawObbService"));
incompatible.push_back(AZ_CRC_CE("DebugDrawService"));
incompatible.push_back(AZ_CRC_CE("EMotionFXActorService"));
incompatible.push_back(AZ_CRC_CE("EMotionFXSimpleMotionService"));
incompatible.push_back(AZ_CRC_CE("GradientTransformService"));
incompatible.push_back(AZ_CRC_CE("LegacyMeshService"));
incompatible.push_back(AZ_CRC_CE("LookAtService"));
incompatible.push_back(AZ_CRC_CE("SequenceService"));
incompatible.push_back(AZ_CRC_CE("ClothMeshService"));
incompatible.push_back(AZ_CRC_CE("PhysXJointService"));
incompatible.push_back(AZ_CRC_CE("PhysXCharacterControllerService"));
incompatible.push_back(AZ_CRC_CE("PhysXRagdollService"));
incompatible.push_back(AZ_CRC_CE("WhiteBoxService"));
incompatible.push_back(AZ_CRC_CE("NavigationAreaService"));
incompatible.push_back(AZ_CRC_CE("GeometryService"));
incompatible.push_back(AZ_CRC_CE("CapsuleShapeService"));
incompatible.push_back(AZ_CRC_CE("CompoundShapeService"));
incompatible.push_back(AZ_CRC_CE("CylinderShapeService"));
incompatible.push_back(AZ_CRC_CE("DiskShapeService"));
incompatible.push_back(AZ_CRC_CE("SphereShapeService"));
incompatible.push_back(AZ_CRC_CE("SplineService"));
incompatible.push_back(AZ_CRC_CE("TubeShapeService"));
}
void EditorNonUniformScaleComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
@@ -403,9 +403,12 @@ namespace AzToolsFramework
AzToolsFramework::EntityIdList selectedEntityIds;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
selectedEntityIds, &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
&AzToolsFramework::ToolsApplicationRequests::DeleteEntitiesAndAllDescendants, selectedEntityIds);
PrefabOperationResult deleteSelectedResult =
s_prefabPublicInterface->DeleteEntitiesAndAllDescendantsInInstance(selectedEntityIds);
if (!deleteSelectedResult.IsSuccess())
{
WarnUserOfError("Delete selected entities error", deleteSelectedResult.GetError());
}
}
void PrefabIntegrationManager::GenerateSuggestedFilenameFromEntities(const EntityIdList& entityIds, AZStd::string& outName)
@@ -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);
}
@@ -2526,7 +2544,7 @@ namespace AzToolsFramework
// create the cluster for changing transform mode
ViewportUi::ViewportUiRequestBus::EventResult(
m_transformModeClusterId, ViewportUi::DefaultViewportId,
&ViewportUi::ViewportUiRequestBus::Events::CreateCluster);
&ViewportUi::ViewportUiRequestBus::Events::CreateCluster, ViewportUi::Alignment::TopLeft);
// create and register the buttons (strings correspond to icons even if the values appear different)
m_translateButtonId = RegisterClusterButton(m_transformModeClusterId, "Move");
@@ -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,
@@ -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,
@@ -41,6 +41,28 @@ namespace AzToolsFramework::ViewportUi::Internal
}
}
static Qt::Alignment GetQtAlignment(Alignment align)
{
switch (align)
{
case Alignment::TopRight:
return Qt::AlignTop | Qt::AlignRight;
case Alignment::TopLeft:
return Qt::AlignTop | Qt::AlignLeft;
case Alignment::BottomRight:
return Qt::AlignBottom | Qt::AlignRight;
case Alignment::BottomLeft:
return Qt::AlignBottom | Qt::AlignLeft;
case Alignment::Top:
return Qt::AlignTop;
case Alignment::Bottom:
return Qt::AlignBottom;
}
AZ_Assert(false, "ViewportUI", "Unhandled ViewportUI Alignment %d", static_cast<int>(align));
return Qt::AlignTop;
}
ViewportUiDisplay::ViewportUiDisplay(QWidget* parent, QWidget* renderOverlay)
: m_renderOverlay(renderOverlay)
, m_uiMainWindow(parent)
@@ -56,7 +78,7 @@ namespace AzToolsFramework::ViewportUi::Internal
UnparentWidgets(m_viewportUiElements);
}
void ViewportUiDisplay::AddCluster(AZStd::shared_ptr<ButtonGroup> buttonGroup)
void ViewportUiDisplay::AddCluster(AZStd::shared_ptr<ButtonGroup> buttonGroup, const Alignment align)
{
if (!buttonGroup.get())
{
@@ -66,7 +88,7 @@ namespace AzToolsFramework::ViewportUi::Internal
auto viewportUiCluster = AZStd::make_shared<ViewportUiCluster>(buttonGroup);
auto id = AddViewportUiElement(viewportUiCluster);
buttonGroup->SetViewportUiElementId(id);
PositionViewportUiElementAnchored(id, Qt::AlignTop | Qt::AlignLeft);
PositionViewportUiElementAnchored(id, GetQtAlignment(align));
}
void ViewportUiDisplay::AddClusterButton(
@@ -94,7 +116,7 @@ namespace AzToolsFramework::ViewportUi::Internal
}
}
void ViewportUiDisplay::AddSwitcher(AZStd::shared_ptr<ButtonGroup> buttonGroup)
void ViewportUiDisplay::AddSwitcher(AZStd::shared_ptr<ButtonGroup> buttonGroup, const Alignment align)
{
if (!buttonGroup.get())
{
@@ -104,7 +126,7 @@ namespace AzToolsFramework::ViewportUi::Internal
auto viewportUiSwitcher = AZStd::make_shared<ViewportUiSwitcher>(buttonGroup);
auto id = AddViewportUiElement(viewportUiSwitcher);
buttonGroup->SetViewportUiElementId(id);
PositionViewportUiElementAnchored(id, Qt::AlignTop | Qt::AlignLeft);
PositionViewportUiElementAnchored(id, GetQtAlignment(align));
}
void ViewportUiDisplay::AddSwitcherButton(const ViewportUiElementId clusterId, Button* button)
@@ -56,12 +56,12 @@ namespace AzToolsFramework::ViewportUi::Internal
ViewportUiDisplay(QWidget* parent, QWidget* renderOverlay);
~ViewportUiDisplay();
void AddCluster(AZStd::shared_ptr<ButtonGroup> buttonGroup);
void AddCluster(AZStd::shared_ptr<ButtonGroup> buttonGroup, Alignment align);
void AddClusterButton(ViewportUiElementId clusterId, Button* button);
void RemoveClusterButton(ViewportUiElementId clusterId, ButtonId buttonId);
void UpdateCluster(const ViewportUiElementId clusterId);
void AddSwitcher(AZStd::shared_ptr<ButtonGroup> buttonGroup);
void AddSwitcher(AZStd::shared_ptr<ButtonGroup> buttonGroup, Alignment align);
void AddSwitcherButton(ViewportUiElementId switcherId, Button* button);
void RemoveSwitcherButton(ViewportUiElementId switcherId, ButtonId buttonId);
void UpdateSwitcher(ViewportUiElementId switcherId);
@@ -30,18 +30,18 @@ namespace AzToolsFramework::ViewportUi
ViewportUiRequestBus::Handler::BusDisconnect();
}
const ClusterId ViewportUiManager::CreateCluster()
const ClusterId ViewportUiManager::CreateCluster(const Alignment align)
{
auto buttonGroup = AZStd::make_shared<Internal::ButtonGroup>();
m_viewportUi->AddCluster(buttonGroup);
m_viewportUi->AddCluster(buttonGroup, align);
return RegisterNewCluster(buttonGroup);
}
const SwitcherId ViewportUiManager::CreateSwitcher()
const SwitcherId ViewportUiManager::CreateSwitcher(const Alignment align)
{
auto buttonGroup = AZStd::make_shared<Internal::ButtonGroup>();
m_viewportUi->AddSwitcher(buttonGroup);
m_viewportUi->AddSwitcher(buttonGroup, align);
return RegisterNewSwitcher(buttonGroup);
}
@@ -31,8 +31,8 @@ namespace AzToolsFramework::ViewportUi
~ViewportUiManager() = default;
// ViewportUiRequestBus ...
const ClusterId CreateCluster() override;
const SwitcherId CreateSwitcher() override;
const ClusterId CreateCluster(Alignment align) override;
const SwitcherId CreateSwitcher(Alignment align) override;
void SetClusterActiveButton(ClusterId clusterId, ButtonId buttonId) override;
void SetSwitcherActiveButton(SwitcherId switcherId, ButtonId buttonId) override;
const ButtonId CreateClusterButton(ClusterId clusterId, const AZStd::string& icon) override;
@@ -41,15 +41,26 @@ namespace AzToolsFramework::ViewportUi
String
};
//! Used to anchor widgets to a specific side of the viewport.
enum class Alignment
{
TopRight,
TopLeft,
BottomRight,
BottomLeft,
Top,
Bottom
};
//! Viewport requests to interact with the Viewport UI. Viewport UI refers to the entire UI overlay (one per viewport).
//! Each widget on the Viewport UI is referred to as an element.
class ViewportUiRequests
{
public:
//! Creates and registers a cluster with the Viewport UI system.
virtual const ClusterId CreateCluster() = 0;
virtual const ClusterId CreateCluster(Alignment align) = 0;
//! Creates and registers a switcher with the Viewport UI system.
virtual const SwitcherId CreateSwitcher() = 0;
virtual const SwitcherId CreateSwitcher(Alignment align) = 0;
//! Sets the active button of the cluster. This is the button which will display as highlighted.
virtual void SetClusterActiveButton(ClusterId clusterId, ButtonId buttonId) = 0;
//! Sets the active button of the switcher. This is the button which has a text label.
@@ -120,7 +120,7 @@ namespace UnitTest
//create an undo node to apply the patch and prep for undo
PrefabUndoInstanceLink undoInstanceLinkNode("Undo Link Patch");
undoInstanceLinkNode.Capture(rootTemplateId, nestedTemplateId, aliases[0], patch, InvalidLinkId);
undoInstanceLinkNode.Capture(rootTemplateId, nestedTemplateId, aliases[0], AZStd::move(patch), InvalidLinkId);
undoInstanceLinkNode.Redo();
m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
@@ -196,7 +196,7 @@ namespace UnitTest
//create an undo node to apply the patch and prep for undo
PrefabUndoInstanceLink undoInstanceLinkNode("Undo Link Patch");
undoInstanceLinkNode.Capture(rootTemplateId, nestedTemplateId, aliases[0], linkPatch, InvalidLinkId);
undoInstanceLinkNode.Capture(rootTemplateId, nestedTemplateId, aliases[0], AZStd::move(linkPatch), InvalidLinkId);
undoInstanceLinkNode.Redo();
m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
@@ -72,7 +72,7 @@ namespace UnitTest
TEST_F(ViewportUiDisplayTestFixture, RemoveViewportUiElementRemovesElementFromViewportUi)
{
ViewportUiDisplay viewportUi(m_parentWidget, m_mockRenderOverlay);
viewportUi.AddCluster(m_buttonGroup);
viewportUi.AddCluster(m_buttonGroup, AzToolsFramework::ViewportUi::Alignment::TopLeft);
auto widget = viewportUi.GetViewportUiElement(m_buttonGroup->GetViewportUiElementId());
EXPECT_TRUE(widget.get() != nullptr);
@@ -89,7 +89,7 @@ namespace UnitTest
ViewportUiDisplay viewportUi(m_parentWidget, m_mockRenderOverlay);
viewportUi.InitializeUiOverlay();
viewportUi.AddCluster(m_buttonGroup);
viewportUi.AddCluster(m_buttonGroup, AzToolsFramework::ViewportUi::Alignment::TopLeft);
viewportUi.Update();
viewportUi.ShowViewportUiElement(m_buttonGroup->GetViewportUiElementId());
@@ -102,7 +102,7 @@ namespace UnitTest
ViewportUiDisplay viewportUi(m_parentWidget, m_mockRenderOverlay);
viewportUi.InitializeUiOverlay();
viewportUi.AddCluster(m_buttonGroup);
viewportUi.AddCluster(m_buttonGroup, AzToolsFramework::ViewportUi::Alignment::TopLeft);
viewportUi.HideViewportUiElement(m_buttonGroup->GetViewportUiElementId());
EXPECT_FALSE(viewportUi.IsViewportUiElementVisible(m_buttonGroup->GetViewportUiElementId()));
@@ -112,7 +112,7 @@ namespace UnitTest
{
ViewportUiDisplay viewportUi(m_parentWidget, m_mockRenderOverlay);
viewportUi.InitializeUiOverlay();
viewportUi.AddCluster(m_buttonGroup);
viewportUi.AddCluster(m_buttonGroup, AzToolsFramework::ViewportUi::Alignment::TopLeft);
viewportUi.Update();
auto widget = viewportUi.GetViewportUiElement(m_buttonGroup->GetViewportUiElementId());
@@ -129,7 +129,7 @@ namespace UnitTest
auto buttonGroup = AZStd::make_shared<ButtonGroup>();
buttonGroup->AddButton("");
viewportUi.AddCluster(buttonGroup);
viewportUi.AddCluster(buttonGroup, AzToolsFramework::ViewportUi::Alignment::TopLeft);
viewportUi.Update();
EXPECT_TRUE(viewportUi.GetUiMainWindow()->isVisible());
@@ -101,7 +101,7 @@ namespace UnitTest
TEST_F(ViewportUiManagerTestFixture, CreateClusterAddsNewClusterAndReturnsId)
{
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster();
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster(AzToolsFramework::ViewportUi::Alignment::TopLeft);
auto clusterEntry = m_viewportManagerWrapper.GetViewportManager()->GetClusterMap().find(clusterId);
EXPECT_TRUE(clusterEntry != m_viewportManagerWrapper.GetViewportManager()->GetClusterMap().end());
@@ -110,7 +110,7 @@ namespace UnitTest
TEST_F(ViewportUiManagerTestFixture, CreateClusterButtonAddsNewButtonAndReturnsId)
{
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster();
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster(AzToolsFramework::ViewportUi::Alignment::TopLeft);
auto buttonId = m_viewportManagerWrapper.GetViewportManager()->CreateClusterButton(clusterId, "");
auto clusterEntry = m_viewportManagerWrapper.GetViewportManager()->GetClusterMap().find(clusterId);
@@ -120,7 +120,7 @@ namespace UnitTest
TEST_F(ViewportUiManagerTestFixture, SetClusterActiveButtonSetsButtonStateToActive)
{
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster();
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster(AzToolsFramework::ViewportUi::Alignment::TopLeft);
auto buttonId = m_viewportManagerWrapper.GetViewportManager()->CreateClusterButton(clusterId, "");
auto clusterEntry = m_viewportManagerWrapper.GetViewportManager()->GetClusterMap().find(clusterId);
@@ -133,7 +133,7 @@ namespace UnitTest
TEST_F(ViewportUiManagerTestFixture, RegisterClusterEventHandlerConnectsHandlerToClusterEvent)
{
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster();
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster(AzToolsFramework::ViewportUi::Alignment::TopLeft);
auto buttonId = m_viewportManagerWrapper.GetViewportManager()->CreateClusterButton(clusterId, "");
// create a handler which will be triggered by the cluster
@@ -159,7 +159,7 @@ namespace UnitTest
TEST_F(ViewportUiManagerTestFixture, RemoveClusterRemovesClusterFromViewportUi)
{
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster();
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster(AzToolsFramework::ViewportUi::Alignment::TopLeft);
m_viewportManagerWrapper.GetViewportManager()->RemoveCluster(clusterId);
auto clusterEntry = m_viewportManagerWrapper.GetViewportManager()->GetClusterMap().find(clusterId);
@@ -171,7 +171,7 @@ namespace UnitTest
{
m_viewportManagerWrapper.GetMockRenderOverlay()->setVisible(true);
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster();
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster(AzToolsFramework::ViewportUi::Alignment::TopLeft);
auto buttonId = m_viewportManagerWrapper.GetViewportManager()->CreateClusterButton(clusterId, "");
m_viewportManagerWrapper.GetViewportManager()->Update();
+142
View File
@@ -0,0 +1,142 @@
/*
* 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/UnitTest/TestTypes.h>
#include <AzFramework/Viewport/ClickDetector.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
namespace AzFramework
{
std::ostream& operator<<(std::ostream& os, const ClickDetector::ClickOutcome clickOutcome)
{
switch (clickOutcome)
{
case ClickDetector::ClickOutcome::Click:
os << "ClickOutcome::Click";
break;
case ClickDetector::ClickOutcome::Move:
os << "ClickOutcome::Move";
break;
case ClickDetector::ClickOutcome::Release:
os << "ClickOutcome::Release";
break;
case ClickDetector::ClickOutcome::Nil:
os << "ClickOutcome::Nil";
break;
}
return os;
}
} // namespace AzFramework
namespace UnitTest
{
using AzFramework::ClickDetector;
using AzFramework::ScreenVector;
class ClickDetectorFixture : public ::testing::Test
{
public:
ClickDetector m_clickDetector;
};
TEST_F(ClickDetectorFixture, ClickIsDetectedWithNoMouseMovementOnMouseUp)
{
using ::testing::Eq;
const ClickDetector::ClickOutcome initialDownOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
const ClickDetector::ClickOutcome initialUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
EXPECT_THAT(initialUpOutcome, Eq(ClickDetector::ClickOutcome::Click));
}
TEST_F(ClickDetectorFixture, MoveIsDetectedWithMouseMovementAfterMouseDown)
{
using ::testing::Eq;
const ClickDetector::ClickOutcome initialDownOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
const ClickDetector::ClickOutcome initialMoveOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(10, 10));
EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
EXPECT_THAT(initialMoveOutcome, Eq(ClickDetector::ClickOutcome::Move));
}
TEST_F(ClickDetectorFixture, ReleaseIsDetectedAfterMouseMovementOnMouseUp)
{
using ::testing::Eq;
const ClickDetector::ClickOutcome initialDownOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
// move
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(10, 10));
const ClickDetector::ClickOutcome initialUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
EXPECT_THAT(initialUpOutcome, Eq(ClickDetector::ClickOutcome::Release));
}
TEST_F(ClickDetectorFixture, MoveIsReturnedOnlyAfterFirstMouseMove)
{
using ::testing::Eq;
const ClickDetector::ClickOutcome initialDownOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
const ClickDetector::ClickOutcome initialMoveOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(10, 10));
const ClickDetector::ClickOutcome secondaryMoveOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(10, 10));
EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
EXPECT_THAT(initialMoveOutcome, Eq(ClickDetector::ClickOutcome::Move));
EXPECT_THAT(secondaryMoveOutcome, Eq(ClickDetector::ClickOutcome::Nil));
}
TEST_F(ClickDetectorFixture, ClickIsNotRegisteredAfterDoubleClick)
{
using ::testing::Eq;
const ClickDetector::ClickOutcome initialDownOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
const ClickDetector::ClickOutcome initialUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
const ClickDetector::ClickOutcome secondaryDownOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
const ClickDetector::ClickOutcome secondaryUpOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
EXPECT_THAT(initialUpOutcome, Eq(ClickDetector::ClickOutcome::Click));
EXPECT_THAT(secondaryDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // double click
EXPECT_THAT(secondaryUpOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // click not registered
}
TEST_F(ClickDetectorFixture, ClickIsNotRegisteredAfterIgnoredDoubleClick)
{
using ::testing::Eq;
const ClickDetector::ClickOutcome initialDownOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
const ClickDetector::ClickOutcome initialUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
const ClickDetector::ClickOutcome secondaryDownOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(0, 0));
const ClickDetector::ClickOutcome secondaryUpOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
EXPECT_THAT(initialUpOutcome, Eq(ClickDetector::ClickOutcome::Click));
EXPECT_THAT(secondaryDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // ignored double click
EXPECT_THAT(secondaryUpOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // click not registered
}
} // namespace UnitTest
+54
View File
@@ -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/UnitTest/TestTypes.h>
#include <AzFramework/Viewport/CursorState.h>
namespace UnitTest
{
using AzFramework::CursorState;
using AzFramework::ScreenVector;
using AzFramework::ScreenPoint;
class CursorStateFixture : public ::testing::Test
{
public:
CursorState m_cursorState;
};
TEST_F(CursorStateFixture, CursorStateHasZeroDeltaInitially)
{
using ::testing::Eq;
EXPECT_THAT(m_cursorState.CursorDelta(), Eq(ScreenVector(0, 0)));
}
TEST_F(CursorStateFixture, CursorStateReturnsZeroDeltaAfterSingleMoveAndUpdate)
{
using ::testing::Eq;
m_cursorState.SetCurrentPosition(ScreenPoint(10, 10));
m_cursorState.Update();
EXPECT_THAT(m_cursorState.CursorDelta(), Eq(ScreenVector(0, 0)));
}
TEST_F(CursorStateFixture, CursorStateReturnsDeltaAfterSecondMoveAndUpdate)
{
using ::testing::Eq;
m_cursorState.SetCurrentPosition(ScreenPoint(10, 10));
m_cursorState.Update();
m_cursorState.SetCurrentPosition(ScreenPoint(15, 22));
EXPECT_THAT(m_cursorState.CursorDelta(), Eq(ScreenVector(5, 12)));
}
} // namespace UnitTest
@@ -17,6 +17,8 @@ set(FILES
BinToTextEncode.cpp
ComponentAddRemove.cpp
ComponentAdapterTests.cpp
ClickDetectorTests.cpp
CursorStateTests.cpp
EntityContext.cpp
EntityTestbed.h
FileFunc.cpp