Merge branch 'main' into non-uniform-scale-blast

This commit is contained in:
greerdv
2021-05-20 22:05:22 +01:00
119 changed files with 15045 additions and 877 deletions
@@ -193,13 +193,12 @@ namespace AzFramework
bool handling = false;
for (auto& cameraInput : m_activeCameraInputs)
{
cameraInput->HandleEvents(event, cursorDelta, scrollDelta);
handling = !cameraInput->Idle() || handling;
handling = cameraInput->HandleEvents(event, cursorDelta, scrollDelta) || handling;
}
for (auto& cameraInput : m_idleCameraInputs)
{
cameraInput->HandleEvents(event, cursorDelta, scrollDelta);
handling = cameraInput->HandleEvents(event, cursorDelta, scrollDelta) || handling;
}
return handling;
@@ -262,17 +261,26 @@ namespace AzFramework
{
m_activeCameraInputs[i]->Reset();
m_idleCameraInputs.push_back(m_activeCameraInputs[i]);
m_activeCameraInputs[i] = m_activeCameraInputs[m_activeCameraInputs.size() - 1];
using AZStd::swap;
swap(m_activeCameraInputs[i], m_activeCameraInputs[m_activeCameraInputs.size() - 1]);
m_activeCameraInputs.pop_back();
}
}
void Cameras::Clear()
{
Reset();
AZ_Assert(m_activeCameraInputs.empty(), "Active Camera Inputs is not empty");
m_idleCameraInputs.clear();
}
RotateCameraInput::RotateCameraInput(const InputChannelId rotateChannelId)
: m_rotateChannelId(rotateChannelId)
{
}
void RotateCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
bool RotateCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
{
const ClickDetector::ClickEvent clickEvent = [&event, this] {
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
@@ -304,6 +312,11 @@ namespace AzFramework
// noop
break;
}
// note - must also check !ending to ensure the mouse up (release) event
// is not consumed and can be propagated to other systems.
// (don't swallow mouse up events)
return !Idle() && !Ending();
}
Camera RotateCameraInput::StepCamera(
@@ -330,7 +343,7 @@ namespace AzFramework
{
}
void PanCameraInput::HandleEvents(
bool PanCameraInput::HandleEvents(
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
{
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
@@ -347,6 +360,8 @@ namespace AzFramework
}
}
}
return !Idle();
}
Camera PanCameraInput::StepCamera(
@@ -411,7 +426,7 @@ namespace AzFramework
{
}
void TranslateCameraInput::HandleEvents(
bool TranslateCameraInput::HandleEvents(
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
{
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
@@ -429,7 +444,8 @@ namespace AzFramework
m_boost = true;
}
}
else if (input->m_state == InputChannel::State::Ended)
// ensure we don't process end events in the idle state
else if (input->m_state == InputChannel::State::Ended && !Idle())
{
m_translation &= ~(translationFromKey(input->m_channelId));
if (m_translation == TranslationType::Nil)
@@ -442,6 +458,8 @@ namespace AzFramework
}
}
}
return !Idle();
}
Camera TranslateCameraInput::StepCamera(
@@ -503,7 +521,7 @@ namespace AzFramework
m_boost = false;
}
void OrbitCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta)
bool OrbitCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta)
{
if (const auto* input = AZStd::get_if<DiscreteInputEvent>(&event))
{
@@ -522,8 +540,10 @@ namespace AzFramework
if (Active())
{
m_orbitCameras.HandleEvents(event, cursorDelta, scrollDelta);
return m_orbitCameras.HandleEvents(event, cursorDelta, scrollDelta);
}
return !Idle();
}
Camera OrbitCameraInput::StepCamera(
@@ -533,7 +553,7 @@ namespace AzFramework
if (Beginning())
{
const auto hasLookAt = [&nextCamera, &targetCamera, lookAtFn = m_lookAtFn] {
const auto hasLookAt = [&nextCamera, &targetCamera, &lookAtFn = m_lookAtFn] {
if (lookAtFn)
{
if (const auto lookAt = lookAtFn())
@@ -585,13 +605,15 @@ namespace AzFramework
return nextCamera;
}
void OrbitDollyScrollCameraInput::HandleEvents(
bool OrbitDollyScrollCameraInput::HandleEvents(
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
{
if (const auto* scroll = AZStd::get_if<ScrollEvent>(&event))
{
BeginActivation();
}
return !Idle();
}
Camera OrbitDollyScrollCameraInput::StepCamera(
@@ -609,7 +631,7 @@ namespace AzFramework
{
}
void OrbitDollyCursorMoveCameraInput::HandleEvents(
bool OrbitDollyCursorMoveCameraInput::HandleEvents(
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
{
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
@@ -626,6 +648,8 @@ namespace AzFramework
}
}
}
return !Idle();
}
Camera OrbitDollyCursorMoveCameraInput::StepCamera(
@@ -637,13 +661,15 @@ namespace AzFramework
return nextCamera;
}
void ScrollTranslationCameraInput::HandleEvents(
bool ScrollTranslationCameraInput::HandleEvents(
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
{
if (const auto* scroll = AZStd::get_if<ScrollEvent>(&event))
{
BeginActivation();
}
return !Idle();
}
Camera ScrollTranslationCameraInput::StepCamera(
@@ -149,7 +149,7 @@ namespace AzFramework
ResetImpl();
}
virtual void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) = 0;
virtual bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) = 0;
virtual Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) = 0;
virtual bool Exclusive() const
@@ -171,16 +171,29 @@ namespace AzFramework
class Cameras
{
public:
void AddCamera(AZStd::shared_ptr<CameraInput> cameraInput);
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta);
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime);
void AddCamera(AZStd::shared_ptr<CameraInput> cameraInput);
//! Reset the state of all cameras.
void Reset();
//! Remove all cameras that were added.
void Clear();
//! Is one of the cameras in the active camera inputs marked as 'exclusive'.
//! @note This implies no other sibling cameras can begin while the exclusive camera is running.
bool Exclusive() const;
private:
AZStd::vector<AZStd::shared_ptr<CameraInput>> m_activeCameraInputs;
AZStd::vector<AZStd::shared_ptr<CameraInput>> m_idleCameraInputs;
};
inline bool Cameras::Exclusive() const
{
return AZStd::any_of(
m_activeCameraInputs.begin(), m_activeCameraInputs.end(), [](const auto& cameraInput) { return cameraInput->Exclusive(); });
}
class CameraSystem
{
public:
@@ -200,7 +213,7 @@ namespace AzFramework
explicit RotateCameraInput(InputChannelId rotateChannelId);
// CameraInput overrides ...
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
private:
@@ -241,7 +254,7 @@ namespace AzFramework
PanCameraInput(InputChannelId panChannelId, PanAxesFn panAxesFn);
// CameraInput overrides ...
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
private:
@@ -282,7 +295,7 @@ namespace AzFramework
explicit TranslateCameraInput(TranslationAxesFn translationAxesFn);
// CameraInput overrides ...
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
bool 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;
@@ -352,7 +365,7 @@ namespace AzFramework
{
public:
// CameraInput overrides ...
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
};
@@ -362,7 +375,7 @@ namespace AzFramework
explicit OrbitDollyCursorMoveCameraInput(InputChannelId dollyChannelId);
// CameraInput overrides ...
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
private:
@@ -373,7 +386,7 @@ namespace AzFramework
{
public:
// CameraInput overrides ...
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
};
@@ -383,7 +396,7 @@ namespace AzFramework
using LookAtFn = AZStd::function<AZStd::optional<AZ::Vector3>()>;
// CameraInput overrides ...
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
bool Exclusive() const override;
@@ -17,6 +17,17 @@ namespace AzFramework
{
ClickDetector::ClickOutcome ClickDetector::DetectClick(const ClickEvent clickEvent, const ScreenVector& cursorDelta)
{
const auto previousDetectionState = m_detectionState;
if (previousDetectionState == 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;
}
}
if (clickEvent == ClickEvent::Down)
{
const auto now = std::chrono::steady_clock::now();
@@ -52,15 +63,9 @@ namespace AzFramework
return clickOutcome;
}
if (m_detectionState == DetectionState::WaitingForMove)
if (previousDetectionState == DetectionState::WaitingForMove && m_detectionState == DetectionState::Moved)
{
// 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::Move;
}
return ClickOutcome::Nil;
@@ -50,7 +50,11 @@ namespace AzFramework
//! Called from any type of 'handle event' function.
ClickOutcome DetectClick(ClickEvent clickEvent, const ScreenVector& cursorDelta);
//! Override the default double click interval.
//! @note Default is 400ms - system default.
void SetDoubleClickInterval(float doubleClickInterval);
//! Override the dead zone before a 'move' outcome will be triggered.
void SetDeadZone(float deadZone);
private:
//! Internal state of ClickDetector based on incoming events.
@@ -72,4 +76,9 @@ namespace AzFramework
{
m_doubleClickInterval = doubleClickInterval;
}
inline void ClickDetector::SetDeadZone(const float deadZone)
{
m_deadZone = deadZone;
}
} // namespace AzFramework
@@ -84,6 +84,7 @@ namespace AzToolsFramework
AZStd::vector<AZ::Entity*> entities;
AZStd::vector<AZStd::unique_ptr<Instance>> instances;
AZStd::unordered_map<Instance*, PrefabDom> nestedInstanceLinkPatchesMap;
// Retrieve all entities affected and identify Instances
if (!RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonRootEntityOwningInstance->get(), entities, instances))
@@ -96,6 +97,16 @@ namespace AzToolsFramework
// target templates of the other instances.
for (auto& nestedInstance : instances)
{
auto linkRef = m_prefabSystemComponentInterface->FindLink(nestedInstance->GetLinkId());
if (linkRef.has_value())
{
PrefabDom oldLinkPatches;
oldLinkPatches.CopyFrom(linkRef->get().GetLinkDom(), oldLinkPatches.GetAllocator());
nestedInstanceLinkPatchesMap.emplace(nestedInstance.get(), AZStd::move(oldLinkPatches));
}
RemoveLink(nestedInstance, commonRootEntityOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch());
}
@@ -122,6 +133,9 @@ namespace AzToolsFramework
AZ::EntityId containerEntityId = instanceToCreate->get().GetContainerEntityId();
// Apply the correct transform to the container for the new instance, and store the patch for use when creating the link.
PrefabDom patch = ApplyContainerTransformAndGeneratePatch(containerEntityId, commonRootEntityId, topLevelEntities);
// Parent the non-container top level entities to the container entity.
// Parenting the top level container entities will be done during the creation of links.
for (AZ::Entity* topLevelEntity : topLevelEntities)
@@ -141,35 +155,55 @@ namespace AzToolsFramework
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.");
AZ::EntityId parentId;
AZ::TransformBus::EventResult(
parentId, nestedInstanceContainerEntity->get().GetId(), &AZ::TransformBus::Events::GetParentId);
AZ::EntityId nestedInstanceContainerEntityId = nestedInstanceContainerEntity->get().GetId();
PrefabDom previousPatch;
auto entityIterator = AZStd::find_if(
entities.begin(), entities.end(), [parentId](AZ::Entity* entity) { return entity->GetId() == parentId; });
// If the previous parent entity of the nested instance is not part of the entities of the newly created prefab,
// then set the parent of the nested prefab as the container entity of the newly created prefab.
if (entityIterator == entities.end())
// Retrieve the previous patch if it exists
if (nestedInstanceLinkPatchesMap.contains(nestedInstance.get()))
{
parentId = containerEntityId;
previousPatch = AZStd::move(nestedInstanceLinkPatchesMap[nestedInstance.get()]);
}
// 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(), parentId, false);
CreateLink(*nestedInstance, instanceToCreate->get().GetTemplateId(), undoBatch.GetUndoBatch(), AZStd::move(previousPatch), false);
// If this nested instance's container is a top level entity in the new prefab, re-parent it and apply the change.
if (AZStd::find(topLevelEntities.begin(), topLevelEntities.end(), &nestedInstanceContainerEntity->get()) != topLevelEntities.end())
{
Prefab::PrefabDom containerEntityDomBefore;
m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomBefore, *nestedInstanceContainerEntity);
AZ::TransformBus::Event(nestedInstanceContainerEntityId, &AZ::TransformBus::Events::SetParent, containerEntityId);
PrefabDom containerEntityDomAfter;
m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomAfter, *nestedInstanceContainerEntity);
PrefabDom reparentPatch;
m_instanceToTemplateInterface->GeneratePatch(reparentPatch, containerEntityDomBefore, containerEntityDomAfter);
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(reparentPatch, nestedInstanceContainerEntityId);
// Update the cache - this prevents these changes from being stored in the regular undo/redo nodes as a separate step
m_prefabUndoCache.Store(nestedInstanceContainerEntityId, AZStd::move(containerEntityDomAfter));
// Save these changes as patches to the link
PrefabUndoLinkUpdate* linkUpdate = aznew PrefabUndoLinkUpdate(AZStd::to_string(static_cast<AZ::u64>(nestedInstanceContainerEntityId)));
linkUpdate->SetParent(undoBatch.GetUndoBatch());
linkUpdate->Capture(reparentPatch, nestedInstance->GetLinkId());
linkUpdate->Redo();
}
});
// 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);
instanceToCreate->get(), commonRootEntityOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch(),
AZStd::move(patch));
for (AZ::Entity* topLevelEntity : topLevelEntities)
{
@@ -199,6 +233,40 @@ namespace AzToolsFramework
return AZ::Success();
}
PrefabDom PrefabPublicHandler::ApplyContainerTransformAndGeneratePatch(AZ::EntityId containerEntityId, AZ::EntityId parentEntityId, const EntityList& childEntities)
{
AZ::Entity* containerEntity = GetEntityById(containerEntityId);
AZ_Assert(containerEntity, "Invalid container entity passed to ApplyContainerTransformAndGeneratePatch.");
// Generate the transform for the container entity out of the top level entities, and set it
// This step needs to be done before anything is parented to the container, else children position will be wrong
Prefab::PrefabDom containerEntityDomBefore;
m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomBefore, *containerEntity);
AZ::Vector3 containerEntityTranslation(AZ::Vector3::CreateZero());
AZ::Quaternion containerEntityRotation(AZ::Quaternion::CreateZero());
// Set container entity to be child of common root
AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetParent, parentEntityId);
// Set the transform (translation, rotation) of the container entity
GenerateContainerEntityTransform(childEntities, containerEntityTranslation, containerEntityRotation);
AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetLocalTranslation, containerEntityTranslation);
AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, containerEntityRotation);
PrefabDom containerEntityDomAfter;
m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomAfter, *containerEntity);
PrefabDom patch;
m_instanceToTemplateInterface->GeneratePatch(patch, containerEntityDomBefore, containerEntityDomAfter);
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, containerEntityId);
// Update the cache - this prevents these changes from being stored in the regular undo/redo nodes
m_prefabUndoCache.Store(containerEntityId, AZStd::move(containerEntityDomAfter));
return AZStd::move(patch);
}
PrefabOperationResult PrefabPublicHandler::InstantiatePrefab(
AZStd::string_view filePath, AZ::EntityId parent, const AZ::Vector3& position)
{
@@ -249,10 +317,10 @@ namespace AzToolsFramework
// Initialize Undo Batch object
ScopedUndoBatch undoBatch("Instantiate Prefab");
// Instantiate the Prefab
PrefabDom instanceToParentUnderDomBeforeCreate;
m_instanceToTemplateInterface->GenerateDomForInstance(instanceToParentUnderDomBeforeCreate, instanceToParentUnder->get());
// Instantiate the Prefab
auto instanceToCreate = prefabEditorEntityOwnershipInterface->InstantiatePrefab(relativePath, instanceToParentUnder);
if (!instanceToCreate)
@@ -264,11 +332,32 @@ namespace AzToolsFramework
PrefabUndoHelpers::UpdatePrefabInstance(
instanceToParentUnder->get(), "Update prefab instance", instanceToParentUnderDomBeforeCreate, undoBatch.GetUndoBatch());
CreateLink({}, instanceToCreate->get(), instanceToParentUnder->get().GetTemplateId(), undoBatch.GetUndoBatch(), parent);
// Create Link with correct container patches
AZ::EntityId containerEntityId = instanceToCreate->get().GetContainerEntityId();
AZ::Entity* containerEntity = GetEntityById(containerEntityId);
AZ_Assert(containerEntity, "Invalid container entity detected in InstantiatePrefab.");
// Apply position
Prefab::PrefabDom containerEntityDomBefore;
m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomBefore, *containerEntity);
// Set container entity's parent
AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetParent, parent);
// Set the position of the container entity
AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetWorldTranslation, position);
PrefabDom containerEntityDomAfter;
m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomAfter, *containerEntity);
// Generate patch to be stored in the link
PrefabDom patch;
m_instanceToTemplateInterface->GeneratePatch(patch, containerEntityDomBefore, containerEntityDomAfter);
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, containerEntityId);
CreateLink(instanceToCreate->get(), instanceToParentUnder->get().GetTemplateId(), undoBatch.GetUndoBatch(), AZStd::move(patch));
// Update the cache - this prevents these changes from being stored in the regular undo/redo nodes
m_prefabUndoCache.Store(containerEntityId, AZStd::move(containerEntityDomAfter));
}
return AZ::Success();
@@ -335,33 +424,9 @@ namespace AzToolsFramework
}
void PrefabPublicHandler::CreateLink(
const EntityList& topLevelEntities, Instance& sourceInstance, TemplateId targetTemplateId,
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId commonRootEntityId, const bool isUndoRedoSupportNeeded)
Instance& sourceInstance, TemplateId targetTemplateId,
UndoSystem::URSequencePoint* undoBatch, PrefabDom patch, const bool isUndoRedoSupportNeeded)
{
AZ::EntityId containerEntityId = sourceInstance.GetContainerEntityId();
AZ::Entity* containerEntity = GetEntityById(containerEntityId);
Prefab::PrefabDom containerEntityDomBefore;
m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomBefore, *containerEntity);
AZ::Vector3 containerEntityTranslation(AZ::Vector3::CreateZero());
AZ::Quaternion containerEntityRotation(AZ::Quaternion::CreateZero());
// Set the transform (translation, rotation) of the container entity
GenerateContainerEntityTransform(topLevelEntities, containerEntityTranslation, containerEntityRotation);
// Set container entity to be child of common root
AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetParent, commonRootEntityId);
AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetLocalTranslation, containerEntityTranslation);
AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, containerEntityRotation);
PrefabDom containerEntityDomAfter;
m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomAfter, *containerEntity);
PrefabDom patch;
m_instanceToTemplateInterface->GeneratePatch(patch, containerEntityDomBefore, containerEntityDomAfter);
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, containerEntityId);
LinkId linkId;
if (isUndoRedoSupportNeeded)
{
@@ -377,9 +442,6 @@ namespace AzToolsFramework
}
sourceInstance.SetLinkId(linkId);
// Update the cache - this prevents these changes from being stored in the regular undo/redo nodes
m_prefabUndoCache.Store(containerEntityId, AZStd::move(containerEntityDomAfter));
}
void PrefabPublicHandler::RemoveLink(
@@ -68,20 +68,32 @@ namespace AzToolsFramework
InstanceOptionalReference GetOwnerInstanceByEntityId(AZ::EntityId entityId) const;
bool EntitiesBelongToSameInstance(const EntityIdList& entityIds) const;
/**
* Applies the correct transform changes to the container entity based on the parent and child entities provided, and returns an appropriate patch.
* The container will be parented to parentId, moved to the average transform of the future direct children and its cache will be updated.
* This helper function won't support undo/redo, update the templates or create any links. All that needs to be done by the caller.
*
* \param containerEntityId The container to apply the changes to.
* \param parentEntityId The id of the entity the container should be parented to.
* \param childEntities A list of entities that will subsequently be parented to this container.
* \return The PrefabDom containing the patches that should be stored in the parent link.
*/
PrefabDom ApplyContainerTransformAndGeneratePatch(
AZ::EntityId containerEntityId, AZ::EntityId parentEntityId, const EntityList& childEntities);
/**
* Creates a link between the templates of an instance and its parent.
*
* \param topLevelEntities The list of entities that are immediate children to the container entity of the instance.
* \param sourceInstance The instance that corresponds to the source template of the link.
* \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 patch The patch to store in the newly created link dom.
* \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, const bool isUndoRedoSupportNeeded = true);
Instance& sourceInstance, TemplateId targetTemplateId, UndoSystem::URSequencePoint* undoBatch,
PrefabDom patch, const bool isUndoRedoSupportNeeded = true);
/**
* Removes the link between template of the sourceInstance and the template corresponding to targetTemplateId.
@@ -18,6 +18,7 @@
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <AzFramework/Viewport/CameraState.h>
#include <AzFramework/Viewport/ViewportId.h>
#include <AzFramework/Viewport/ClickDetector.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Viewport/ViewportTypes.h>
@@ -304,4 +305,24 @@ namespace AzToolsFramework
return entityContextId;
}
//! Maps a mouse interaction event to a ClickDetector event.
//! @note Function only cares about up or down events, all other events are mapped to Nil (ignored).
inline AzFramework::ClickDetector::ClickEvent ClickDetectorEventFromViewportInteraction(
const ViewportInteraction::MouseInteractionEvent& 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;
}
} // namespace AzToolsFramework
@@ -14,6 +14,7 @@
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <QApplication>
@@ -27,8 +28,11 @@ namespace AzToolsFramework
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left() &&
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down)
m_cursorState.SetCurrentPosition(mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates);
const auto selectClickEvent = ClickDetectorEventFromViewportInteraction(mouseInteraction);
const auto clickOutcome = m_clickDetector.DetectClick(selectClickEvent, m_cursorState.CursorDelta());
if (clickOutcome == AzFramework::ClickDetector::ClickOutcome::Move)
{
if (m_leftMouseDown)
{
@@ -58,8 +62,7 @@ namespace AzToolsFramework
}
}
if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left() &&
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Up)
if (clickOutcome == AzFramework::ClickDetector::ClickOutcome::Release)
{
if (m_leftMouseUp)
{
@@ -77,6 +80,8 @@ namespace AzToolsFramework
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
m_cursorState.Update();
if (m_boxSelectRegion)
{
debugDisplay.DepthTestOff();
@@ -14,6 +14,8 @@
#include <AzCore/std/functional.h>
#include <AzCore/std/optional.h>
#include <AzFramework/Viewport/ClickDetector.h>
#include <AzFramework/Viewport/CursorState.h>
#include <AzToolsFramework/Viewport/ViewportTypes.h>
#include <QRect>
@@ -26,49 +28,49 @@ namespace AzFramework
namespace AzToolsFramework
{
/// Utility to provide box select (click and drag) support for viewport types.
/// Users can override the mouse event callbacks and display scene function to customize behavior.
//! Utility to provide box select (click and drag) support for viewport types.
//! Users can override the mouse event callbacks and display scene function to customize behavior.
class EditorBoxSelect
{
public:
EditorBoxSelect() = default;
/// Return if a box select action is currently taking place.
//! Return if a box select action is currently taking place.
bool Active() const { return m_boxSelectRegion.has_value(); }
/// Update the box select for various mouse events.
/// Call HandleMouseInteraction from type/system implementing MouseViewportRequests interface.
//! Update the box select for various mouse events.
//! Call HandleMouseInteraction from type/system implementing MouseViewportRequests interface.
void HandleMouseInteraction(
const ViewportInteraction::MouseInteractionEvent& mouseInteraction);
/// Responsible for drawing the 2d box representing the selection in screen space.
//! Responsible for drawing the 2d box representing the selection in screen space.
void Display2d(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay);
/// Custom drawing behavior to happen during a box select.
//! Custom drawing behavior to happen during a box select.
void DisplayScene(
const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay);
/// Set the left mouse down callback.
//! Set the left mouse down callback.
void InstallLeftMouseDown(
const AZStd::function<void(const ViewportInteraction::MouseInteractionEvent& mouseInteraction)>& leftMouseDown);
/// Set the mouse move callback.
//! Set the mouse move callback.
void InstallMouseMove(
const AZStd::function<void(const ViewportInteraction::MouseInteractionEvent& mouseInteraction)>& mouseMove);
/// Set the left mouse up callback.
//! Set the left mouse up callback.
void InstallLeftMouseUp(
const AZStd::function<void()>& leftMouseUp);
/// Set the display scene callback.
//! Set the display scene callback.
void InstallDisplayScene(
const AZStd::function<void(
const AzFramework::ViewportInfo& viewportInfo,
AzFramework::DebugDisplayRequests& debugDisplay)>& displayScene);
/// Return the box select region.
/// If a box selection is being made, return the current rectangle representing the area.
/// If there is currently no active box select, then the Maybe type will be empty (there will be no region/area).
//! Return the box select region.
//! If a box selection is being made, return the current rectangle representing the area.
//! If there is currently no active box select, then the Maybe type will be empty (there will be no region/area).
const AZStd::optional<QRect>& BoxRegion() const { return m_boxSelectRegion; }
/// Return the active modifiers from the previous frame.
//! Return the active modifiers from the previous frame.
ViewportInteraction::KeyboardModifiers PreviousModifiers() const { return m_previousModifiers; }
private:
@@ -79,7 +81,9 @@ namespace AzToolsFramework
AZStd::function<void(
const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay)> m_displayScene;
AZStd::optional<QRect> m_boxSelectRegion; ///< Maybe/optional value to store box select region while active.
ViewportInteraction::KeyboardModifiers m_previousModifiers; ///< Modifier keys active on the previous frame.
AZStd::optional<QRect> m_boxSelectRegion; //!< Maybe/optional value to store box select region while active.
ViewportInteraction::KeyboardModifiers m_previousModifiers; //!< Modifier keys active on the previous frame.
AzFramework::ClickDetector m_clickDetector; //!< Utility type to detect if a mouse click or move has occurred.
AzFramework::CursorState m_cursorState; //!< Utility type to track the current cursor position (and movement/delta).
};
} // namespace AzToolsFramework
@@ -1782,22 +1782,7 @@ 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;
}();
const auto selectClickEvent = ClickDetectorEventFromViewportInteraction(mouseInteraction);
m_cursorState.SetCurrentPosition(mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates);
const auto clickOutcome = m_clickDetector.DetectClick(selectClickEvent, m_cursorState.CursorDelta());
+90
View File
@@ -0,0 +1,90 @@
/*
* 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 <AzCore/std/smart_ptr/make_shared.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Viewport/CameraInput.h>
#include <AzFramework/Windowing/WindowBus.h>
namespace UnitTest
{
class CameraInputFixture : public AllocatorsTestFixture
{
public:
AzFramework::Camera m_camera;
AzFramework::Camera m_targetCamera;
AZStd::shared_ptr<AzFramework::CameraSystem> m_cameraSystem;
bool HandleEventAndUpdate(const AzFramework::InputEvent& event)
{
constexpr float deltaTime = 0.01666f; // 60fps
const bool consumed = m_cameraSystem->HandleEvents(event);
m_camera = m_cameraSystem->StepCamera(m_targetCamera, deltaTime);
return consumed;
}
void SetUp() override
{
AllocatorsTestFixture::SetUp();
AzFramework::ReloadCameraKeyBindings();
m_cameraSystem = AZStd::make_shared<AzFramework::CameraSystem>();
auto firstPersonRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::InputDeviceMouse::Button::Right);
auto firstPersonTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(AzFramework::LookTranslation);
auto orbitCamera = AZStd::make_shared<AzFramework::OrbitCameraInput>();
auto orbitRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::InputDeviceMouse::Button::Left);
auto orbitTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(AzFramework::OrbitTranslation);
orbitCamera->m_orbitCameras.AddCamera(orbitRotateCamera);
orbitCamera->m_orbitCameras.AddCamera(orbitTranslateCamera);
m_cameraSystem->m_cameras.AddCamera(firstPersonRotateCamera);
m_cameraSystem->m_cameras.AddCamera(firstPersonTranslateCamera);
m_cameraSystem->m_cameras.AddCamera(orbitCamera);
}
void TearDown() override
{
m_cameraSystem->m_cameras.Clear();
m_cameraSystem.reset();
AllocatorsTestFixture::TearDown();
}
};
TEST_F(CameraInputFixture, BeginEndOrbitCameraConsumesCorrectEvents)
{
// set initial mouse position
const bool consumed1 = HandleEventAndUpdate(AzFramework::CursorEvent{AzFramework::ScreenPoint(5, 5)});
// begin orbit camera
const bool consumed2 = HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{AzFramework::InputDeviceKeyboard::Key::ModifierAltL, AzFramework::InputChannel::State::Began});
// begin listening for orbit rotate (click detector) - event is not consumed
const bool consumed3 = HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Began});
// begin orbit rotate (mouse has moved sufficient distance to initiate)
const bool consumed4 = HandleEventAndUpdate(AzFramework::CursorEvent{AzFramework::ScreenPoint(10, 10)});
// end orbit (mouse up) - event is not consumed
const bool consumed5 = HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Ended});
const auto allConsumed = AZStd::vector<bool>{consumed1, consumed2, consumed3, consumed4, consumed5};
using ::testing::ElementsAre;
EXPECT_THAT(allConsumed, ElementsAre(false, true, false, true, false));
}
} // namespace UnitTest
@@ -139,4 +139,21 @@ namespace UnitTest
EXPECT_THAT(secondaryDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // ignored double click
EXPECT_THAT(secondaryUpOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // click not registered
}
// if the click detector registers a mouse down event, but then all intermediate calls are ignored
// (another system may start intercepting events and swallowing them) then when we do receive a mouse
// up event we should ensure we take into account the current delta - if the delta is large, then the
// outcome will be release
TEST_F(ClickDetectorFixture, ClickIsNotRegisteredAfterIgnoringMouseMovesBeforeMouseUpWithLargeDelta)
{
using ::testing::Eq;
const ClickDetector::ClickOutcome downOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
const ClickDetector::ClickOutcome upOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(50, 50));
EXPECT_THAT(downOutcome, Eq(ClickDetector::ClickOutcome::Nil));
EXPECT_THAT(upOutcome, Eq(ClickDetector::ClickOutcome::Release));
}
} // namespace UnitTest
@@ -17,6 +17,7 @@ set(FILES
BinToTextEncode.cpp
ComponentAddRemove.cpp
ComponentAdapterTests.cpp
CameraInputTests.cpp
ClickDetectorTests.cpp
CursorStateTests.cpp
EntityContext.cpp