Merge branch 'development' into Atom/guthadam/material_editor_replace_modified_color_with_indicator

monroegm-disable-blank-issue-2
Guthrie Adams 4 years ago
commit d827db741d

@ -141,6 +141,10 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent)
m_ui->m_assetBrowserTreeViewWidget, &AzAssetBrowser::AssetBrowserTreeView::ClearTypeFilter, m_ui->m_searchWidget, m_ui->m_assetBrowserTreeViewWidget, &AzAssetBrowser::AssetBrowserTreeView::ClearTypeFilter, m_ui->m_searchWidget,
&AzAssetBrowser::SearchWidget::ClearTypeFilter); &AzAssetBrowser::SearchWidget::ClearTypeFilter);
connect(
this, &AzAssetBrowserWindow::SizeChangedSignal, m_ui->m_assetBrowserTableViewWidget,
&AzAssetBrowser::AssetBrowserTableView::UpdateSizeSlot);
m_ui->m_assetBrowserTreeViewWidget->SetName("AssetBrowserTreeView_main"); m_ui->m_assetBrowserTreeViewWidget->SetName("AssetBrowserTreeView_main");
} }
@ -164,6 +168,25 @@ QObject* AzAssetBrowserWindow::createListenerForShowAssetEditorEvent(QObject* pa
return listener; return listener;
} }
void AzAssetBrowserWindow::resizeEvent(QResizeEvent* resizeEvent)
{
// leftLayout is the parent of the tableView
// rightLayout is the parent of the preview window.
// Workaround: When docking windows this event keeps holding the old size of the widgets instead of the new one
// but the resizeEvent holds the new size of the whole widget
// So we have to save the proportions somehow
const QWidget* leftLayout = m_ui->m_leftLayout;
const QVBoxLayout* rightLayout = m_ui->m_rightLayout;
const float oldLeftLayoutWidth = aznumeric_cast<float>(leftLayout->geometry().width());
const float oldWidth = aznumeric_cast<float>(leftLayout->geometry().width() + rightLayout->geometry().width());
const float newWidth = oldLeftLayoutWidth * aznumeric_cast<float>(resizeEvent->size().width()) / oldWidth;
emit SizeChangedSignal(aznumeric_cast<int>(newWidth));
QWidget::resizeEvent(resizeEvent);
}
void AzAssetBrowserWindow::OnInitViewToggleButton() void AzAssetBrowserWindow::OnInitViewToggleButton()
{ {
CreateSwitchViewMenu(); CreateSwitchViewMenu();

@ -53,9 +53,17 @@ public:
static QObject* createListenerForShowAssetEditorEvent(QObject* parent); static QObject* createListenerForShowAssetEditorEvent(QObject* parent);
Q_SIGNALS:
void SizeChangedSignal(int newWidth);
protected:
void resizeEvent(QResizeEvent* resizeEvent) override;
private: private:
void OnInitViewToggleButton(); void OnInitViewToggleButton();
void UpdateDisplayInfo(); void UpdateDisplayInfo();
protected slots: protected slots:
void CreateSwitchViewMenu(); void CreateSwitchViewMenu();
void SetExpandedAssetBrowserMode(); void SetExpandedAssetBrowserMode();

@ -2428,7 +2428,7 @@ void EditorViewportWidget::RestoreViewportAfterGameMode()
} }
else else
{ {
AZ_Error("CryLegacy", false, "Not restoring the editor viewport camera is currently unsupported"); AZ_Warning("CryLegacy", false, "Not restoring the editor viewport camera is currently unsupported");
SetViewTM(preGameModeViewTM); SetViewTM(preGameModeViewTM);
} }
} }

@ -649,6 +649,16 @@ namespace AZ
m_stateEvent.Signal(oldState, m_state); m_stateEvent.Signal(oldState, m_state);
} }
void Entity::SetSpawnTicketId(u32 spawnTicketId)
{
m_spawnTicketId = spawnTicketId;
}
u32 Entity::GetSpawnTicketId() const
{
return m_spawnTicketId;
}
void Entity::OnNameChanged() const void Entity::OnNameChanged() const
{ {
EBUS_EVENT_ID(GetId(), EntityBus, OnEntityNameChanged, m_name); EBUS_EVENT_ID(GetId(), EntityBus, OnEntityNameChanged, m_name);

@ -133,6 +133,14 @@ namespace AZ
//! @return The state of the entity. For example, the entity has been initialized, the entity is active, and so on. //! @return The state of the entity. For example, the entity has been initialized, the entity is active, and so on.
State GetState() const { return m_state; } State GetState() const { return m_state; }
//! Gets the ticket id used to spawn the entity.
//! @return the ticket id used to spawn the entity. If entity is not spawned, the id will be 0.
u32 GetSpawnTicketId() const;
//! Sets the ticket id used to spawn the entity. The ticket id in the entity will remain 0 unless it's set using this function.
//! @param spawnTicketId the ticket id used to spawn the entity.
void SetSpawnTicketId(u32 spawnTicketId);
//! Connects an entity state event handler to the entity. //! Connects an entity state event handler to the entity.
//! All state changes will be signaled through this event. //! All state changes will be signaled through this event.
//! @param handler reference to the EntityStateEvent handler to attach to the entities state event. //! @param handler reference to the EntityStateEvent handler to attach to the entities state event.
@ -410,6 +418,8 @@ namespace AZ
//! A user-friendly name for the entity. This makes error messages easier to read. //! A user-friendly name for the entity. This makes error messages easier to read.
AZStd::string m_name; AZStd::string m_name;
u32 m_spawnTicketId = 0;
//! The state of the entity. //! The state of the entity.
State m_state; State m_state;

@ -91,15 +91,6 @@ namespace AzFramework
*/ */
virtual void DestroyGameEntity(const AZ::EntityId& /*id*/) = 0; virtual void DestroyGameEntity(const AZ::EntityId& /*id*/) = 0;
/**
* Destroys an entity only in slice mode (when prefabs are disabled). This request is only added as a stop-gap solution
* to prevent the editor from crashing when prefabs are enabled and must only be called through the BehaviorContext binding
* for 'DestroyGameEntity'. No code should be written to directly call this method. This will be removed soon.
*
* @param id The ID of the entity to destroy.
*/
virtual void DestroyGameEntityOnlyInSliceMode(const AZ::EntityId& /*id*/) = 0;
/** /**
* Destroys an entity and all of its descendants. * Destroys an entity and all of its descendants.
* The entity and its descendants are immediately deactivated and will be * The entity and its descendants are immediately deactivated and will be
@ -108,15 +99,6 @@ namespace AzFramework
*/ */
virtual void DestroyGameEntityAndDescendants(const AZ::EntityId& /*id*/) = 0; virtual void DestroyGameEntityAndDescendants(const AZ::EntityId& /*id*/) = 0;
/**
* Destroys an entity and its descendants only in slice mode (when prefabs are disabled). This request is only added as a stop-gap
* solution to prevent the editor from crashing when prefabs are enabled and must only be called through the BehaviorContext
* binding for 'DestroyGameEntityAndDescendants'.No code should be written to directly call this method. This will be removed soon.
*
* @param id The ID of the entity to destroy.
*/
virtual void DestroyGameEntityAndDescendantsOnlyInSliceMode(const AZ::EntityId& /*id*/) = 0;
/** /**
* Activates the game entity. * Activates the game entity.
* @param id The ID of the entity to activate. * @param id The ID of the entity to activate.

@ -11,9 +11,10 @@
#include <AzCore/Serialization/SerializeContext.h> #include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h> #include <AzCore/Serialization/EditContext.h>
#include <AzCore/RTTI/BehaviorContext.h> #include <AzCore/RTTI/BehaviorContext.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/Entity/EntityContext.h> #include <AzFramework/Entity/EntityContext.h>
#include <AzFramework/Components/TransformComponent.h> #include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/API/ApplicationAPI.h> #include <AzFramework/Spawnable/SpawnableEntitiesInterface.h>
#include "GameEntityContextComponent.h" #include "GameEntityContextComponent.h"
@ -47,9 +48,9 @@ namespace AzFramework
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Event("CreateGameEntity", &GameEntityContextRequestBus::Events::CreateGameEntityForBehaviorContext) ->Event("CreateGameEntity", &GameEntityContextRequestBus::Events::CreateGameEntityForBehaviorContext)
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Event("DestroyGameEntity", &GameEntityContextRequestBus::Events::DestroyGameEntityOnlyInSliceMode) ->Event("DestroyGameEntity", &GameEntityContextRequestBus::Events::DestroyGameEntity)
->Event( ->Event(
"DestroyGameEntityAndDescendants", &GameEntityContextRequestBus::Events::DestroyGameEntityAndDescendantsOnlyInSliceMode) "DestroyGameEntityAndDescendants", &GameEntityContextRequestBus::Events::DestroyGameEntityAndDescendants)
->Event("ActivateGameEntity", &GameEntityContextRequestBus::Events::ActivateGameEntity) ->Event("ActivateGameEntity", &GameEntityContextRequestBus::Events::ActivateGameEntity)
->Event("DeactivateGameEntity", &GameEntityContextRequestBus::Events::DeactivateGameEntity) ->Event("DeactivateGameEntity", &GameEntityContextRequestBus::Events::DeactivateGameEntity)
->Attribute(AZ::ScriptCanvasAttributes::DeactivatesInputEntity, true) ->Attribute(AZ::ScriptCanvasAttributes::DeactivatesInputEntity, true)
@ -249,23 +250,6 @@ namespace AzFramework
DestroyGameEntityInternal(id, false); DestroyGameEntityInternal(id, false);
} }
void GameEntityContextComponent::DestroyGameEntityOnlyInSliceMode(const AZ::EntityId& id)
{
bool isPrefabSystemEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
isPrefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
if (!isPrefabSystemEnabled)
{
DestroyGameEntityInternal(id, false);
}
else
{
AZ_Error(
"GameEntityContextComponent", false,
"Destroying a game entity is temporarily disabled until the Spawnable system can support this.");
}
}
//========================================================================= //=========================================================================
// GameEntityContextComponent::DestroyGameEntityAndDescendantsById // GameEntityContextComponent::DestroyGameEntityAndDescendantsById
//========================================================================= //=========================================================================
@ -274,24 +258,6 @@ namespace AzFramework
DestroyGameEntityInternal(id, true); DestroyGameEntityInternal(id, true);
} }
void GameEntityContextComponent::DestroyGameEntityAndDescendantsOnlyInSliceMode(const AZ::EntityId& id)
{
bool isPrefabSystemEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
isPrefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
if (!isPrefabSystemEnabled)
{
DestroyGameEntityInternal(id, true);
}
else
{
AZ_Error(
"GameEntityContextComponent", false,
"Destroying a game entity and its descendants is temporarily disabled until the Spawnable system can support this.");
}
}
//========================================================================= //=========================================================================
// GameEntityContextComponent::DestroyGameEntityInternal // GameEntityContextComponent::DestroyGameEntityInternal
//========================================================================= //=========================================================================
@ -319,6 +285,28 @@ namespace AzFramework
EBUS_EVENT_RESULT(currentEntity, AZ::ComponentApplicationBus, FindEntity, *entityIdIter); EBUS_EVENT_RESULT(currentEntity, AZ::ComponentApplicationBus, FindEntity, *entityIdIter);
if (currentEntity) if (currentEntity)
{ {
bool isPrefabSystemEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
isPrefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
if (isPrefabSystemEnabled)
{
if (currentEntity->GetSpawnTicketId() > 0)
{
SpawnableEntitiesDefinition* spawnableEntitiesInterface = SpawnableEntitiesInterface::Get();
AZ_Assert(spawnableEntitiesInterface != nullptr, "SpawnableEntitiesInterface is not found.");
spawnableEntitiesInterface->RetrieveEntitySpawnTicket(
currentEntity->GetSpawnTicketId(),
[spawnableEntitiesInterface, currentEntity](EntitySpawnTicket* entitySpawnTicket)
{
if (entitySpawnTicket != nullptr)
{
spawnableEntitiesInterface->DespawnEntity(currentEntity->GetId(), *entitySpawnTicket);
}
});
return;
}
}
if (currentEntity->GetState() == AZ::Entity::State::Active) if (currentEntity->GetState() == AZ::Entity::State::Active)
{ {
// Deactivate the entity, we'll destroy it as soon as it is safe. // Deactivate the entity, we'll destroy it as soon as it is safe.

@ -90,11 +90,6 @@ namespace AzFramework
} }
private: private:
//////////////////////////////////////////////////////////////////////////
// GameEntityContextRequestBus
void DestroyGameEntityOnlyInSliceMode(const AZ::EntityId&) override;
void DestroyGameEntityAndDescendantsOnlyInSliceMode(const AZ::EntityId&) override;
/////////////////////////////////////////////////////////////////////////
AzFramework::EntityVisibilityBoundsUnionSystem m_entityVisibilityBoundsUnionSystem; AzFramework::EntityVisibilityBoundsUnionSystem m_entityVisibilityBoundsUnionSystem;
}; };

@ -283,8 +283,12 @@ namespace AzFramework
: m_payload(rhs.m_payload) : m_payload(rhs.m_payload)
, m_id(rhs.m_id) , m_id(rhs.m_id)
{ {
auto manager = SpawnableEntitiesInterface::Get();
AZ_Assert(manager, "SpawnableEntitiesInterface has no implementation.");
rhs.m_payload = nullptr; rhs.m_payload = nullptr;
rhs.m_id = 0; rhs.m_id = 0;
AZStd::scoped_lock lock(manager->m_entitySpawnTicketMapMutex);
manager->m_entitySpawnTicketMap.insert_or_assign(rhs.m_id, this);
} }
EntitySpawnTicket::EntitySpawnTicket(AZ::Data::Asset<Spawnable> spawnable) EntitySpawnTicket::EntitySpawnTicket(AZ::Data::Asset<Spawnable> spawnable)
@ -294,6 +298,8 @@ namespace AzFramework
AZStd::pair<EntitySpawnTicket::Id, void*> result = manager->CreateTicket(AZStd::move(spawnable)); AZStd::pair<EntitySpawnTicket::Id, void*> result = manager->CreateTicket(AZStd::move(spawnable));
m_id = result.first; m_id = result.first;
m_payload = result.second; m_payload = result.second;
AZStd::scoped_lock lock(manager->m_entitySpawnTicketMapMutex);
manager->m_entitySpawnTicketMap.insert_or_assign(m_id, this);
} }
EntitySpawnTicket::~EntitySpawnTicket() EntitySpawnTicket::~EntitySpawnTicket()
@ -304,6 +310,8 @@ namespace AzFramework
AZ_Assert(manager, "Attempting to destroy an entity spawn ticket while the SpawnableEntitiesInterface has no implementation."); AZ_Assert(manager, "Attempting to destroy an entity spawn ticket while the SpawnableEntitiesInterface has no implementation.");
manager->DestroyTicket(m_payload); manager->DestroyTicket(m_payload);
m_payload = nullptr; m_payload = nullptr;
AZStd::scoped_lock lock(manager->m_entitySpawnTicketMapMutex);
manager->m_entitySpawnTicketMap.erase(m_id);
m_id = 0; m_id = 0;
} }
} }
@ -311,18 +319,24 @@ namespace AzFramework
EntitySpawnTicket& EntitySpawnTicket::operator=(EntitySpawnTicket&& rhs) EntitySpawnTicket& EntitySpawnTicket::operator=(EntitySpawnTicket&& rhs)
{ {
if (this != &rhs) if (this != &rhs)
{
if (m_payload)
{ {
auto manager = SpawnableEntitiesInterface::Get(); auto manager = SpawnableEntitiesInterface::Get();
AZ_Assert(manager, "Attempting to destroy an entity spawn ticket while the SpawnableEntitiesInterface has no implementation."); AZ_Assert(manager, "Attempting to destroy an entity spawn ticket while the SpawnableEntitiesInterface has no implementation.");
if (m_payload)
{
manager->DestroyTicket(m_payload); manager->DestroyTicket(m_payload);
} }
Id previousId = m_id;
m_id = rhs.m_id; m_id = rhs.m_id;
rhs.m_id = 0; rhs.m_id = 0;
m_payload = rhs.m_payload; m_payload = rhs.m_payload;
rhs.m_payload = nullptr; rhs.m_payload = nullptr;
AZStd::scoped_lock lock(manager->m_entitySpawnTicketMapMutex);
manager->m_entitySpawnTicketMap.erase(previousId);
manager->m_entitySpawnTicketMap.insert_or_assign(m_id, this);
} }
return *this; return *this;
} }

@ -154,7 +154,7 @@ namespace AzFramework
public: public:
friend class SpawnableEntitiesDefinition; friend class SpawnableEntitiesDefinition;
using Id = uint64_t; using Id = uint32_t;
EntitySpawnTicket() = default; EntitySpawnTicket() = default;
EntitySpawnTicket(const EntitySpawnTicket&) = delete; EntitySpawnTicket(const EntitySpawnTicket&) = delete;
@ -176,6 +176,7 @@ namespace AzFramework
using EntitySpawnCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableConstEntityContainerView)>; using EntitySpawnCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableConstEntityContainerView)>;
using EntityPreInsertionCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableEntityContainerView)>; using EntityPreInsertionCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableEntityContainerView)>;
using EntityDespawnCallback = AZStd::function<void(EntitySpawnTicket::Id)>; using EntityDespawnCallback = AZStd::function<void(EntitySpawnTicket::Id)>;
using RetrieveEntitySpawnTicketCallback = AZStd::function<void(EntitySpawnTicket*)>;
using ReloadSpawnableCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableConstEntityContainerView)>; using ReloadSpawnableCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableConstEntityContainerView)>;
using ListEntitiesCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableConstEntityContainerView)>; using ListEntitiesCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableConstEntityContainerView)>;
using ListIndicesEntitiesCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableConstIndexEntityContainerView)>; using ListIndicesEntitiesCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableConstIndexEntityContainerView)>;
@ -220,12 +221,21 @@ namespace AzFramework
struct DespawnAllEntitiesOptionalArgs final struct DespawnAllEntitiesOptionalArgs final
{ {
//! Callback that's called when despawning entities has completed. This can be triggered from a different thread than the one that //! Callback that's called when despawning entities has completed. This can be triggered from a different thread than the one that
//! made the function call to despawn. The returned list of entities contains all the newly created entities. //! made the function call to despawn.
EntityDespawnCallback m_completionCallback; EntityDespawnCallback m_completionCallback;
//! The priority at which this call will be executed. //! The priority at which this call will be executed.
SpawnablePriority m_priority { SpawnablePriority_Default }; SpawnablePriority m_priority { SpawnablePriority_Default };
}; };
struct DespawnEntityOptionalArgs final
{
//! Callback that's called when despawning entity has completed. This can be triggered from a different thread than the one that
//! made the function call to despawn.
EntityDespawnCallback m_completionCallback;
//! The priority at which this call will be executed.
SpawnablePriority m_priority{ SpawnablePriority_Default };
};
struct ReloadSpawnableOptionalArgs final struct ReloadSpawnableOptionalArgs final
{ {
//! Callback that's called when respawning entities has completed. This can be triggered from a different thread than the one that //! Callback that's called when respawning entities has completed. This can be triggered from a different thread than the one that
@ -291,9 +301,17 @@ namespace AzFramework
EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices, SpawnEntitiesOptionalArgs optionalArgs = {}) = 0; EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices, SpawnEntitiesOptionalArgs optionalArgs = {}) = 0;
//! Removes all entities in the provided list from the environment. //! Removes all entities in the provided list from the environment.
//! @param ticket The ticket previously used to spawn entities with. //! @param ticket The ticket previously used to spawn entities with.
//! @param priority The priority at which this call will be executed.
//! @param optionalArgs Optional additional arguments, see DespawnAllEntitiesOptionalArgs. //! @param optionalArgs Optional additional arguments, see DespawnAllEntitiesOptionalArgs.
virtual void DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs = {}) = 0; virtual void DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs = {}) = 0;
//! Removes the entity with the provided id from the spawned list of entities.
//! @param entityId the id of entity to despawn.
//! @param ticket The ticket previously used to spawn entities with.
//! @param optionalArgs Optional additional arguments, see DespawnEntityOptionalArgs.
virtual void DespawnEntity(AZ::EntityId entityId, EntitySpawnTicket& ticket, DespawnEntityOptionalArgs optionalArgs = {}) = 0;
//! Gets the EntitySpawnTicket associated with the entitySpawnTicketId.
//! @param entitySpawnTicketId the id of EntitySpawnTicket to get.
//! @param callback The callback to execute upon retrieving the ticket.
virtual void RetrieveEntitySpawnTicket(EntitySpawnTicket::Id entitySpawnTicketId, RetrieveEntitySpawnTicketCallback callback) = 0;
//! Removes all entities in the provided list from the environment and reconstructs the entities from the provided spawnable. //! Removes all entities in the provided list from the environment and reconstructs the entities from the provided spawnable.
//! @param ticket Holds the information on the entities to reload. //! @param ticket Holds the information on the entities to reload.
//! @param priority The priority at which this call will be executed. //! @param priority The priority at which this call will be executed.
@ -361,6 +379,9 @@ namespace AzFramework
{ {
return reinterpret_cast<const T*>(ticket->m_payload); return reinterpret_cast<const T*>(ticket->m_payload);
} }
AZStd::unordered_map<EntitySpawnTicket::Id, EntitySpawnTicket*> m_entitySpawnTicketMap;
AZStd::recursive_mutex m_entitySpawnTicketMapMutex;
}; };
using SpawnableEntitiesInterface = AZ::Interface<SpawnableEntitiesDefinition>; using SpawnableEntitiesInterface = AZ::Interface<SpawnableEntitiesDefinition>;

@ -85,6 +85,35 @@ namespace AzFramework
QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry));
} }
void SpawnableEntitiesManager::DespawnEntity(AZ::EntityId entityId, EntitySpawnTicket& ticket, DespawnEntityOptionalArgs optionalArgs)
{
AZ_Assert(ticket.IsValid(), "Ticket provided to DespawnEntity hasn't been initialized.");
DespawnEntityCommand queueEntry;
queueEntry.m_ticketId = ticket.GetId();
queueEntry.m_entityId = entityId;
queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback);
QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry));
}
void SpawnableEntitiesManager::RetrieveEntitySpawnTicket(EntitySpawnTicket::Id entitySpawnTicketId, RetrieveEntitySpawnTicketCallback callback)
{
if (entitySpawnTicketId == 0)
{
AZ_Assert(false, "Ticket id provided to RetrieveEntitySpawnTicket is invalid.");
return;
}
AZStd::scoped_lock lock(m_entitySpawnTicketMapMutex);
auto entitySpawnTicketIterator = m_entitySpawnTicketMap.find(entitySpawnTicketId);
if (entitySpawnTicketIterator == m_entitySpawnTicketMap.end())
{
AZ_Assert(false, "The EntitySpawnTicket corresponding to id '%lu' cannot be found", entitySpawnTicketId);
return;
}
callback(entitySpawnTicketIterator->second);
}
void SpawnableEntitiesManager::ReloadSpawnable( void SpawnableEntitiesManager::ReloadSpawnable(
EntitySpawnTicket& ticket, AZ::Data::Asset<Spawnable> spawnable, ReloadSpawnableOptionalArgs optionalArgs) EntitySpawnTicket& ticket, AZ::Data::Asset<Spawnable> spawnable, ReloadSpawnableOptionalArgs optionalArgs)
{ {
@ -223,12 +252,13 @@ namespace AzFramework
return queue.m_delayed.empty() ? CommandQueueStatus::NoCommandsLeft : CommandQueueStatus::HasCommandsLeft; return queue.m_delayed.empty() ? CommandQueueStatus::NoCommandsLeft : CommandQueueStatus::HasCommandsLeft;
} }
AZStd::pair<uint64_t, void*> SpawnableEntitiesManager::CreateTicket(AZ::Data::Asset<Spawnable>&& spawnable) AZStd::pair<EntitySpawnTicket::Id, void*> SpawnableEntitiesManager::CreateTicket(AZ::Data::Asset<Spawnable>&& spawnable)
{ {
static AZStd::atomic_uint64_t idCounter { 1 }; static AZStd::atomic_uint32_t idCounter { 1 };
auto result = aznew Ticket(); auto result = aznew Ticket();
result->m_spawnable = AZStd::move(spawnable); result->m_spawnable = AZStd::move(spawnable);
return AZStd::make_pair<EntitySpawnTicket::Id, void*>(idCounter++, result); return AZStd::make_pair<EntitySpawnTicket::Id, void*>(idCounter++, result);
} }
@ -339,6 +369,7 @@ namespace AzFramework
// Add to the game context, now the entities are active // Add to the game context, now the entities are active
for (auto it = ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount; it != ticket.m_spawnedEntities.end(); ++it) for (auto it = ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount; it != ticket.m_spawnedEntities.end(); ++it)
{ {
(*it)->SetSpawnTicketId(request.m_ticketId);
GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it); GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it);
} }
@ -420,6 +451,7 @@ namespace AzFramework
// Add to the game context, now the entities are active // Add to the game context, now the entities are active
for (auto it = ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount; it != ticket.m_spawnedEntities.end(); ++it) for (auto it = ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount; it != ticket.m_spawnedEntities.end(); ++it)
{ {
(*it)->SetSpawnTicketId(request.m_ticketId);
GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it); GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it);
} }
@ -447,8 +479,10 @@ namespace AzFramework
{ {
if (entity != nullptr) if (entity != nullptr)
{ {
// Setting it to 0 is needed to avoid the infite loop between GameEntityContext and SpawnableEntitiesManager.
entity->SetSpawnTicketId(0);
GameEntityContextRequestBus::Broadcast( GameEntityContextRequestBus::Broadcast(
&GameEntityContextRequestBus::Events::DestroyGameEntityAndDescendants, entity->GetId()); &GameEntityContextRequestBus::Events::DestroyGameEntity, entity->GetId());
} }
} }
@ -469,6 +503,40 @@ namespace AzFramework
} }
} }
bool SpawnableEntitiesManager::ProcessRequest(DespawnEntityCommand& request)
{
Ticket& ticket = *request.m_ticket;
if (request.m_requestId == ticket.m_currentRequestId)
{
AZStd::vector<AZ::Entity*>& spawnedEntities = request.m_ticket->m_spawnedEntities;
for (auto entityIterator = spawnedEntities.begin(); entityIterator != spawnedEntities.end(); ++entityIterator)
{
if (*entityIterator != nullptr && (*entityIterator)->GetId() == request.m_entityId)
{
// Setting it to 0 is needed to avoid the infite loop between GameEntityContext and SpawnableEntitiesManager.
(*entityIterator)->SetSpawnTicketId(0);
GameEntityContextRequestBus::Broadcast(
&GameEntityContextRequestBus::Events::DestroyGameEntity, (*entityIterator)->GetId());
AZStd::iter_swap(entityIterator, spawnedEntities.rbegin());
spawnedEntities.pop_back();
break;
}
}
if (request.m_completionCallback)
{
request.m_completionCallback(request.m_ticketId);
}
ticket.m_currentRequestId++;
return true;
}
else
{
return false;
}
}
bool SpawnableEntitiesManager::ProcessRequest(ReloadSpawnableCommand& request) bool SpawnableEntitiesManager::ProcessRequest(ReloadSpawnableCommand& request)
{ {
Ticket& ticket = *request.m_ticket; Ticket& ticket = *request.m_ticket;
@ -482,8 +550,10 @@ namespace AzFramework
{ {
if (entity != nullptr) if (entity != nullptr)
{ {
// Setting it to 0 is needed to avoid the infite loop between GameEntityContext and SpawnableEntitiesManager.
entity->SetSpawnTicketId(0);
GameEntityContextRequestBus::Broadcast( GameEntityContextRequestBus::Broadcast(
&GameEntityContextRequestBus::Events::DestroyGameEntityAndDescendants, entity->GetId()); &GameEntityContextRequestBus::Events::DestroyGameEntity, entity->GetId());
} }
} }
@ -636,8 +706,10 @@ namespace AzFramework
{ {
if (entity != nullptr) if (entity != nullptr)
{ {
// Setting it to 0 is needed to avoid the infite loop between GameEntityContext and SpawnableEntitiesManager.
entity->SetSpawnTicketId(0);
GameEntityContextRequestBus::Broadcast( GameEntityContextRequestBus::Broadcast(
&GameEntityContextRequestBus::Events::DestroyGameEntityAndDescendants, entity->GetId()); &GameEntityContextRequestBus::Events::DestroyGameEntity, entity->GetId());
} }
} }
delete request.m_ticket; delete request.m_ticket;

@ -57,6 +57,8 @@ namespace AzFramework
void SpawnEntities( void SpawnEntities(
EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices, SpawnEntitiesOptionalArgs optionalArgs = {}) override; EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices, SpawnEntitiesOptionalArgs optionalArgs = {}) override;
void DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs = {}) override; void DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs = {}) override;
void DespawnEntity(AZ::EntityId entityId, EntitySpawnTicket& ticket, DespawnEntityOptionalArgs optionalArgs = {}) override;
void RetrieveEntitySpawnTicket(EntitySpawnTicket::Id entitySpawnTicketId, RetrieveEntitySpawnTicketCallback callback) override;
void ReloadSpawnable( void ReloadSpawnable(
EntitySpawnTicket& ticket, AZ::Data::Asset<Spawnable> spawnable, ReloadSpawnableOptionalArgs optionalArgs = {}) override; EntitySpawnTicket& ticket, AZ::Data::Asset<Spawnable> spawnable, ReloadSpawnableOptionalArgs optionalArgs = {}) override;
@ -132,6 +134,14 @@ namespace AzFramework
EntitySpawnTicket::Id m_ticketId; EntitySpawnTicket::Id m_ticketId;
uint32_t m_requestId; uint32_t m_requestId;
}; };
struct DespawnEntityCommand
{
EntityDespawnCallback m_completionCallback;
Ticket* m_ticket;
AZ::EntityId m_entityId;
EntitySpawnTicket::Id m_ticketId;
uint32_t m_requestId;
};
struct ReloadSpawnableCommand struct ReloadSpawnableCommand
{ {
AZ::Data::Asset<Spawnable> m_spawnable; AZ::Data::Asset<Spawnable> m_spawnable;
@ -176,8 +186,16 @@ namespace AzFramework
}; };
using Requests = AZStd::variant< using Requests = AZStd::variant<
SpawnAllEntitiesCommand, SpawnEntitiesCommand, DespawnAllEntitiesCommand, ReloadSpawnableCommand, ListEntitiesCommand, SpawnAllEntitiesCommand,
ListIndicesEntitiesCommand, ClaimEntitiesCommand, BarrierCommand, DestroyTicketCommand>; SpawnEntitiesCommand,
DespawnAllEntitiesCommand,
DespawnEntityCommand,
ReloadSpawnableCommand,
ListEntitiesCommand,
ListIndicesEntitiesCommand,
ClaimEntitiesCommand,
BarrierCommand,
DestroyTicketCommand>;
struct Queue struct Queue
{ {
@ -199,6 +217,7 @@ namespace AzFramework
bool ProcessRequest(SpawnAllEntitiesCommand& request); bool ProcessRequest(SpawnAllEntitiesCommand& request);
bool ProcessRequest(SpawnEntitiesCommand& request); bool ProcessRequest(SpawnEntitiesCommand& request);
bool ProcessRequest(DespawnAllEntitiesCommand& request); bool ProcessRequest(DespawnAllEntitiesCommand& request);
bool ProcessRequest(DespawnEntityCommand& request);
bool ProcessRequest(ReloadSpawnableCommand& request); bool ProcessRequest(ReloadSpawnableCommand& request);
bool ProcessRequest(ListEntitiesCommand& request); bool ProcessRequest(ListEntitiesCommand& request);
bool ProcessRequest(ListIndicesEntitiesCommand& request); bool ProcessRequest(ListIndicesEntitiesCommand& request);

@ -8,11 +8,10 @@
#pragma once #pragma once
#include <AzCore/base.h> #include <AzCore/Casting/numeric_cast.h>
#include <AzCore/Math/Vector2.h> #include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/RTTI/TypeInfoSimple.h> #include <AzCore/RTTI/TypeInfoSimple.h>
#include <AzCore/Casting/numeric_cast.h> #include <AzCore/base.h>
namespace AZ namespace AZ
{ {
@ -27,7 +26,11 @@ namespace AzFramework
AZ_TYPE_INFO(ScreenPoint, "{8472B6C2-527F-44FC-87F8-C226B1A57A97}"); AZ_TYPE_INFO(ScreenPoint, "{8472B6C2-527F-44FC-87F8-C226B1A57A97}");
ScreenPoint() = default; ScreenPoint() = default;
ScreenPoint(int x, int y) : m_x(x), m_y(y) {} ScreenPoint(int x, int y)
: m_x(x)
, m_y(y)
{
}
int m_x; //!< X screen position. int m_x; //!< X screen position.
int m_y; //!< Y screen position. int m_y; //!< Y screen position.
@ -42,7 +45,11 @@ namespace AzFramework
AZ_TYPE_INFO(ScreenVector, "{1EAA2C62-8FDB-4A28-9FE3-1FA4F1418894}"); AZ_TYPE_INFO(ScreenVector, "{1EAA2C62-8FDB-4A28-9FE3-1FA4F1418894}");
ScreenVector() = default; ScreenVector() = default;
ScreenVector(int x, int y) : m_x(x), m_y(y) {} ScreenVector(int x, int y)
: m_x(x)
, m_y(y)
{
}
int m_x; //!< X screen delta. int m_x; //!< X screen delta.
int m_y; //!< Y screen delta. int m_y; //!< Y screen delta.
@ -71,14 +78,14 @@ namespace AzFramework
inline const ScreenPoint operator+(const ScreenPoint& lhs, const ScreenVector& rhs) inline const ScreenPoint operator+(const ScreenPoint& lhs, const ScreenVector& rhs)
{ {
ScreenPoint result{lhs}; ScreenPoint result{ lhs };
result += rhs; result += rhs;
return result; return result;
} }
inline const ScreenPoint operator-(const ScreenPoint& lhs, const ScreenVector& rhs) inline const ScreenPoint operator-(const ScreenPoint& lhs, const ScreenVector& rhs)
{ {
ScreenPoint result{lhs}; ScreenPoint result{ lhs };
result -= rhs; result -= rhs;
return result; return result;
} }
@ -99,14 +106,14 @@ namespace AzFramework
inline const ScreenVector operator+(const ScreenVector& lhs, const ScreenVector& rhs) inline const ScreenVector operator+(const ScreenVector& lhs, const ScreenVector& rhs)
{ {
ScreenVector result{lhs}; ScreenVector result{ lhs };
result += rhs; result += rhs;
return result; return result;
} }
inline const ScreenVector operator-(const ScreenVector& lhs, const ScreenVector& rhs) inline const ScreenVector operator-(const ScreenVector& lhs, const ScreenVector& rhs)
{ {
ScreenVector result{lhs}; ScreenVector result{ lhs };
result -= rhs; result -= rhs;
return result; return result;
} }
@ -131,21 +138,23 @@ namespace AzFramework
return !operator==(lhs, rhs); return !operator==(lhs, rhs);
} }
inline float ScreenVectorLength(const ScreenVector& screenVector) inline ScreenVector& operator*=(ScreenVector& lhs, const float rhs)
{ {
return aznumeric_cast<float>(AZStd::sqrt(screenVector.m_x * screenVector.m_x + screenVector.m_y * screenVector.m_y)); lhs.m_x = aznumeric_cast<int>(AZStd::lround(aznumeric_cast<float>(lhs.m_x) * rhs));
lhs.m_y = aznumeric_cast<int>(AZStd::lround(aznumeric_cast<float>(lhs.m_y) * rhs));
return lhs;
} }
inline ScreenPoint ScreenPointFromNDC(const AZ::Vector3& screenNDC, const AZ::Vector2& viewportSize) inline const ScreenVector operator*(const ScreenVector& lhs, const float rhs)
{ {
return ScreenPoint( ScreenVector result{ lhs };
aznumeric_caster(AZStd::round(screenNDC.GetX() * viewportSize.GetX())), result *= rhs;
aznumeric_caster(AZStd::round((1.0f - screenNDC.GetY()) * viewportSize.GetY()))); return result;
} }
inline AZ::Vector2 NDCFromScreenPoint(const ScreenPoint& screenPoint, const AZ::Vector2& viewportSize) inline float ScreenVectorLength(const ScreenVector& screenVector)
{ {
return AZ::Vector2(aznumeric_cast<float>(screenPoint.m_x), viewportSize.GetY() - aznumeric_cast<float>(screenPoint.m_y)) / viewportSize; return aznumeric_cast<float>(AZStd::sqrt(screenVector.m_x * screenVector.m_x + screenVector.m_y * screenVector.m_y));
} }
//! Return an AZ::Vector2 from a ScreenPoint. //! Return an AZ::Vector2 from a ScreenPoint.

@ -37,15 +37,15 @@ namespace AzFramework
// yaw = AZ::Matrix4x4::CreateRotationZ(AZ::DegToRad(180.0f)); // yaw = AZ::Matrix4x4::CreateRotationZ(AZ::DegToRad(180.0f));
// conversion = pitch * yaw // conversion = pitch * yaw
return AZ::Matrix4x4::CreateFromColumns( return AZ::Matrix4x4::CreateFromColumns(
AZ::Vector4(-1.0f, 0.0f, 0.0f, 0.0f), AZ::Vector4(0.0f, 0.0f, 1.0f, .0f), AZ::Vector4(-1.0f, 0.0f, 0.0f, 0.0f), AZ::Vector4(0.0f, 0.0f, 1.0f, 0.0f), AZ::Vector4(0.0f, 1.0f, 0.0f, 0.0f),
AZ::Vector4(0.0f, 1.0f, 0.0f, 0.0f), AZ::Vector4(0.0f, 0.0f, 0.0f, 1.0f)); AZ::Vector4(0.0f, 0.0f, 0.0f, 1.0f));
} }
AZ::Matrix4x4 CameraTransform(const CameraState& cameraState) AZ::Matrix4x4 CameraTransform(const CameraState& cameraState)
{ {
return AZ::Matrix4x4::CreateFromColumns( return AZ::Matrix4x4::CreateFromColumns(
AZ::Vector3ToVector4(cameraState.m_side), AZ::Vector3ToVector4(cameraState.m_forward), AZ::Vector3ToVector4(cameraState.m_side), AZ::Vector3ToVector4(cameraState.m_forward), AZ::Vector3ToVector4(cameraState.m_up),
AZ::Vector3ToVector4(cameraState.m_up), AZ::Vector3ToVector4(cameraState.m_position, 1.0f)); AZ::Vector3ToVector4(cameraState.m_position, 1.0f));
} }
AZ::Matrix4x4 CameraView(const CameraState& cameraState) AZ::Matrix4x4 CameraView(const CameraState& cameraState)
@ -63,8 +63,7 @@ namespace AzFramework
AZ::Matrix4x4 CameraProjection(const CameraState& cameraState) AZ::Matrix4x4 CameraProjection(const CameraState& cameraState)
{ {
return AZ::Matrix4x4::CreateProjection( return AZ::Matrix4x4::CreateProjection(
cameraState.VerticalFovRadian(), AspectRatio(cameraState.m_viewportSize), cameraState.m_nearClip, cameraState.VerticalFovRadian(), AspectRatio(cameraState.m_viewportSize), cameraState.m_nearClip, cameraState.m_farClip);
cameraState.m_farClip);
} }
AZ::Matrix4x4 InverseCameraProjection(const CameraState& cameraState) AZ::Matrix4x4 InverseCameraProjection(const CameraState& cameraState)
@ -93,12 +92,11 @@ namespace AzFramework
const auto cameraWorldTransform = AZ::Transform::CreateFromMatrix3x3AndTranslation( const auto cameraWorldTransform = AZ::Transform::CreateFromMatrix3x3AndTranslation(
AZ::Matrix3x3::CreateFromMatrix4x4(worldFromView), worldFromView.GetTranslation()); AZ::Matrix3x3::CreateFromMatrix4x4(worldFromView), worldFromView.GetTranslation());
return AZ::ViewFrustumAttributes( return AZ::ViewFrustumAttributes(
cameraWorldTransform, AspectRatio(cameraState.m_viewportSize), cameraState.m_fovOrZoom, cameraWorldTransform, AspectRatio(cameraState.m_viewportSize), cameraState.m_fovOrZoom, cameraState.m_nearClip,
cameraState.m_nearClip, cameraState.m_farClip); cameraState.m_farClip);
} }
AZ::Vector3 WorldToScreenNDC( AZ::Vector3 WorldToScreenNdc(const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection)
const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection)
{ {
// transform the world space position to clip space // transform the world space position to clip space
const auto clipSpacePosition = cameraProjection * cameraView * AZ::Vector3ToVector4(worldPosition, 1.0f); const auto clipSpacePosition = cameraProjection * cameraView * AZ::Vector3ToVector4(worldPosition, 1.0f);
@ -108,25 +106,24 @@ namespace AzFramework
return (AZ::Vector4ToVector3(ndcPosition) + AZ::Vector3::CreateOne()) * 0.5f; return (AZ::Vector4ToVector3(ndcPosition) + AZ::Vector3::CreateOne()) * 0.5f;
} }
ScreenPoint WorldToScreen( ScreenPoint WorldToScreen(
const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection, const AZ::Vector3& worldPosition,
const AZ::Matrix4x4& cameraView,
const AZ::Matrix4x4& cameraProjection,
const AZ::Vector2& viewportSize) const AZ::Vector2& viewportSize)
{ {
const auto ndcNormalizedPosition = WorldToScreenNDC(worldPosition, cameraView, cameraProjection); const auto ndcNormalizedPosition = WorldToScreenNdc(worldPosition, cameraView, cameraProjection);
// scale ndc position by screen dimensions to return screen position // scale ndc position by screen dimensions to return screen position
return ScreenPointFromNDC(ndcNormalizedPosition, viewportSize); return ScreenPointFromNdc(AZ::Vector3ToVector2(ndcNormalizedPosition), viewportSize);
} }
ScreenPoint WorldToScreen(const AZ::Vector3& worldPosition, const CameraState& cameraState) ScreenPoint WorldToScreen(const AZ::Vector3& worldPosition, const CameraState& cameraState)
{ {
return WorldToScreen( return WorldToScreen(worldPosition, CameraView(cameraState), CameraProjection(cameraState), cameraState.m_viewportSize);
worldPosition, CameraView(cameraState), CameraProjection(cameraState), cameraState.m_viewportSize);
} }
AZ::Vector3 ScreenNDCToWorld( AZ::Vector3 ScreenNdcToWorld(
const AZ::Vector2& normalizedScreenPosition, const AZ::Matrix4x4& inverseCameraView, const AZ::Vector2& normalizedScreenPosition, const AZ::Matrix4x4& inverseCameraView, const AZ::Matrix4x4& inverseCameraProjection)
const AZ::Matrix4x4& inverseCameraProjection)
{ {
// convert screen space coordinates from <0, 1> to <-1,1> range // convert screen space coordinates from <0, 1> to <-1,1> range
const auto ndcPosition = normalizedScreenPosition * 2.0f - AZ::Vector2::CreateOne(); const auto ndcPosition = normalizedScreenPosition * 2.0f - AZ::Vector2::CreateOne();
@ -142,18 +139,19 @@ namespace AzFramework
} }
AZ::Vector3 ScreenToWorld( AZ::Vector3 ScreenToWorld(
const ScreenPoint& screenPosition, const AZ::Matrix4x4& inverseCameraView, const ScreenPoint& screenPosition,
const AZ::Matrix4x4& inverseCameraProjection, const AZ::Vector2& viewportSize) const AZ::Matrix4x4& inverseCameraView,
const AZ::Matrix4x4& inverseCameraProjection,
const AZ::Vector2& viewportSize)
{ {
const auto normalizedScreenPosition = NDCFromScreenPoint(screenPosition, viewportSize); const auto normalizedScreenPosition = NdcFromScreenPoint(screenPosition, viewportSize);
return ScreenNDCToWorld(normalizedScreenPosition, inverseCameraView, inverseCameraProjection); return ScreenNdcToWorld(normalizedScreenPosition, inverseCameraView, inverseCameraProjection);
} }
AZ::Vector3 ScreenToWorld(const ScreenPoint& screenPosition, const CameraState& cameraState) AZ::Vector3 ScreenToWorld(const ScreenPoint& screenPosition, const CameraState& cameraState)
{ {
return ScreenToWorld( return ScreenToWorld(
screenPosition, InverseCameraView(cameraState), InverseCameraProjection(cameraState), screenPosition, InverseCameraView(cameraState), InverseCameraProjection(cameraState), cameraState.m_viewportSize);
cameraState.m_viewportSize);
} }
} // namespace AzFramework } // namespace AzFramework

@ -8,26 +8,42 @@
#pragma once #pragma once
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/Math/Vector2.h> #include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
namespace AZ namespace AZ
{ {
class Frustum; class Frustum;
class Matrix4x4; class Matrix4x4;
class Vector3;
struct ViewFrustumAttributes; struct ViewFrustumAttributes;
} // namespace AZ } // namespace AZ
namespace AzFramework namespace AzFramework
{ {
struct CameraState; struct CameraState;
struct ScreenPoint;
struct ViewportInfo; struct ViewportInfo;
//! Projects a position in world space to screen space normalized device coordinates for the given camera. //! Returns a position in screen space (in the range [0-viewportSize.x, 0-viewportSize.y]) from normalized device
AZ::Vector3 WorldToScreenNDC( //! coordinates (in the range 0.0-1.0).
const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection); inline ScreenPoint ScreenPointFromNdc(const AZ::Vector2& screenNdc, const AZ::Vector2& viewportSize)
{
return ScreenPoint(
aznumeric_cast<int>(AZStd::lround(screenNdc.GetX() * viewportSize.GetX())),
aznumeric_cast<int>(AZStd::lround((1.0f - screenNdc.GetY()) * viewportSize.GetY())));
}
//! Returns a position in normalized device coordinates (in the range [0.0-1.0, 0.0-1.0]) from a
//! screen space position (in the range [0-viewportSize.x, 0-viewportSize.y]).
inline AZ::Vector2 NdcFromScreenPoint(const ScreenPoint& screenPoint, const AZ::Vector2& viewportSize)
{
return AZ::Vector2(aznumeric_cast<float>(screenPoint.m_x), viewportSize.GetY() - aznumeric_cast<float>(screenPoint.m_y)) /
viewportSize;
}
//! Projects a position in world space to screen space normalized device coordinates for the given camera.
AZ::Vector3 WorldToScreenNdc(const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection);
//! Projects a position in world space to screen space for the given camera. //! Projects a position in world space to screen space for the given camera.
ScreenPoint WorldToScreen(const AZ::Vector3& worldPosition, const CameraState& cameraState); ScreenPoint WorldToScreen(const AZ::Vector3& worldPosition, const CameraState& cameraState);
@ -35,7 +51,9 @@ namespace AzFramework
//! Overload of WorldToScreen that accepts camera values that can be precomputed if this function //! Overload of WorldToScreen that accepts camera values that can be precomputed if this function
//! is called many times in a loop. //! is called many times in a loop.
ScreenPoint WorldToScreen( ScreenPoint WorldToScreen(
const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection, const AZ::Vector3& worldPosition,
const AZ::Matrix4x4& cameraView,
const AZ::Matrix4x4& cameraProjection,
const AZ::Vector2& viewportSize); const AZ::Vector2& viewportSize);
//! Unprojects a position in screen space pixel coordinates to world space. //! Unprojects a position in screen space pixel coordinates to world space.
@ -45,14 +63,15 @@ namespace AzFramework
//! Overload of ScreenToWorld that accepts camera values that can be precomputed if this function //! Overload of ScreenToWorld that accepts camera values that can be precomputed if this function
//! is called many times in a loop. //! is called many times in a loop.
AZ::Vector3 ScreenToWorld( AZ::Vector3 ScreenToWorld(
const ScreenPoint& screenPosition, const AZ::Matrix4x4& inverseCameraView, const ScreenPoint& screenPosition,
const AZ::Matrix4x4& inverseCameraProjection, const AZ::Vector2& viewportSize); const AZ::Matrix4x4& inverseCameraView,
const AZ::Matrix4x4& inverseCameraProjection,
const AZ::Vector2& viewportSize);
//! Unprojects a position in screen space normalized device coordinates to world space. //! Unprojects a position in screen space normalized device coordinates to world space.
//! Note: The position returned will be on the near clip plane of the camera in world space. //! Note: The position returned will be on the near clip plane of the camera in world space.
AZ::Vector3 ScreenNDCToWorld( AZ::Vector3 ScreenNdcToWorld(
const AZ::Vector2& ndcPosition, const AZ::Matrix4x4& inverseCameraView, const AZ::Vector2& ndcPosition, const AZ::Matrix4x4& inverseCameraView, const AZ::Matrix4x4& inverseCameraProjection);
const AZ::Matrix4x4& inverseCameraProjection);
//! Returns the camera projection for the current camera state. //! Returns the camera projection for the current camera state.
AZ::Matrix4x4 CameraProjection(const CameraState& cameraState); AZ::Matrix4x4 CameraProjection(const CameraState& cameraState);

@ -40,6 +40,11 @@ namespace AzFramework
MOCK_METHOD2(DespawnAllEntities, void(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs)); MOCK_METHOD2(DespawnAllEntities, void(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs));
MOCK_METHOD3(DespawnEntity, void(AZ::EntityId entityId, EntitySpawnTicket& ticket, DespawnEntityOptionalArgs optionalArgs));
MOCK_METHOD2(
RetrieveEntitySpawnTicket, void(EntitySpawnTicket::Id entitySpawnTicketId, RetrieveEntitySpawnTicketCallback callback));
MOCK_METHOD3( MOCK_METHOD3(
ReloadSpawnable, ReloadSpawnable,
void(EntitySpawnTicket& ticket, AZ::Data::Asset<Spawnable> spawnable, ReloadSpawnableOptionalArgs optionalArgs)); void(EntitySpawnTicket& ticket, AZ::Data::Asset<Spawnable> spawnable, ReloadSpawnableOptionalArgs optionalArgs));

@ -151,6 +151,10 @@ namespace AzToolsFramework
m_ui->m_assetBrowserTableViewWidget, &AssetBrowserTableView::ClearTypeFilter, m_ui->m_searchWidget, m_ui->m_assetBrowserTableViewWidget, &AssetBrowserTableView::ClearTypeFilter, m_ui->m_searchWidget,
&SearchWidget::ClearTypeFilter); &SearchWidget::ClearTypeFilter);
connect(
this, &AssetPickerDialog::SizeChangedSignal, m_ui->m_assetBrowserTableViewWidget,
&AssetBrowserTableView::UpdateSizeSlot);
m_ui->m_assetBrowserTableViewWidget->SetName("AssetBrowserTableView_main"); m_ui->m_assetBrowserTableViewWidget->SetName("AssetBrowserTableView_main");
m_tableModel->UpdateTableModelMaps(); m_tableModel->UpdateTableModelMaps();
} }
@ -206,6 +210,12 @@ namespace AzToolsFramework
} }
} }
void AssetPickerDialog::resizeEvent(QResizeEvent* resizeEvent)
{
emit SizeChangedSignal(m_ui->verticalLayout_4->geometry().width());
QDialog::resizeEvent(resizeEvent);
}
void AssetPickerDialog::keyPressEvent(QKeyEvent* e) void AssetPickerDialog::keyPressEvent(QKeyEvent* e)
{ {
// Until search widget is revised, Return key should not close the dialog, // Until search widget is revised, Return key should not close the dialog,

@ -46,6 +46,9 @@ namespace AzToolsFramework
explicit AssetPickerDialog(AssetSelectionModel& selection, QWidget* parent = nullptr); explicit AssetPickerDialog(AssetSelectionModel& selection, QWidget* parent = nullptr);
virtual ~AssetPickerDialog(); virtual ~AssetPickerDialog();
Q_SIGNALS:
void SizeChangedSignal(int newWidth);
protected: protected:
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// QDialog // QDialog
@ -53,6 +56,7 @@ namespace AzToolsFramework
void accept() override; void accept() override;
void reject() override; void reject() override;
void keyPressEvent(QKeyEvent* e) override; void keyPressEvent(QKeyEvent* e) override;
void resizeEvent(QResizeEvent* resizeEvent) override;
private Q_SLOTS: private Q_SLOTS:
void DoubleClickedSlot(const QModelIndex& index); void DoubleClickedSlot(const QModelIndex& index);

@ -117,6 +117,9 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item>
<widget class="AzToolsFramework::AssetBrowser::AssetBrowserTableView" name="m_assetBrowserTableViewWidget"/>
</item>
<item> <item>
<widget class="AzToolsFramework::AssetBrowser::AssetBrowserTreeView" name="m_assetBrowserTreeViewWidget"> <widget class="AzToolsFramework::AssetBrowser::AssetBrowserTreeView" name="m_assetBrowserTreeViewWidget">
<property name="sizePolicy"> <property name="sizePolicy">
@ -142,9 +145,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="AzToolsFramework::AssetBrowser::AssetBrowserTableView" name="m_assetBrowserTableViewWidget"/>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="verticalLayoutWidget"> <widget class="QWidget" name="verticalLayoutWidget">

@ -20,13 +20,17 @@ AZ_PUSH_DISABLE_WARNING(
#include <QCoreApplication> #include <QCoreApplication>
#include <QHeaderView> #include <QHeaderView>
#include <QMenu> #include <QMenu>
#include <QResizeEvent>
#include <QTimer> #include <QTimer>
AZ_POP_DISABLE_WARNING AZ_POP_DISABLE_WARNING
namespace AzToolsFramework namespace AzToolsFramework
{ {
namespace AssetBrowser namespace AssetBrowser
{ {
const float MinHeaderResizeProportion = .25f;
const float MaxHeaderResizeProportion = .75f;
const float DefaultHeaderResizeProportion = .5f;
AssetBrowserTableView::AssetBrowserTableView(QWidget* parent) AssetBrowserTableView::AssetBrowserTableView(QWidget* parent)
: AzQtComponents::TableView(parent) : AzQtComponents::TableView(parent)
, m_delegate(new SearchEntryDelegate(this)) , m_delegate(new SearchEntryDelegate(this))
@ -65,8 +69,10 @@ namespace AzToolsFramework
AzQtComponents::TableView::setModel(model); AzQtComponents::TableView::setModel(model);
connect(m_tableModel, &AssetBrowserTableModel::layoutChanged, this, &AssetBrowserTableView::layoutChangedSlot); connect(m_tableModel, &AssetBrowserTableModel::layoutChanged, this, &AssetBrowserTableView::layoutChangedSlot);
header()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch); header()->setStretchLastSection(true);
header()->setSectionResizeMode(1, QHeaderView::ResizeMode::Stretch); header()->setSectionResizeMode(0, QHeaderView::ResizeMode::Interactive);
header()->setSectionResizeMode(1, QHeaderView::ResizeMode::Interactive);
UpdateSizeSlot(parentWidget()->width());
header()->setSortIndicatorShown(false); header()->setSortIndicatorShown(false);
header()->setSectionsClickable(false); header()->setSectionsClickable(false);
} }
@ -148,8 +154,17 @@ namespace AzToolsFramework
void AssetBrowserTableView::OnAssetBrowserComponentReady() void AssetBrowserTableView::OnAssetBrowserComponentReady()
{ {
UpdateSizeSlot(parentWidget()->width());
}
void AssetBrowserTableView::UpdateSizeSlot(int newWidth)
{
setColumnWidth(0, aznumeric_cast<int>(newWidth * DefaultHeaderResizeProportion));
header()->setMinimumSectionSize(aznumeric_cast<int>(newWidth * MinHeaderResizeProportion));
header()->setMaximumSectionSize(aznumeric_cast<int>(newWidth * MaxHeaderResizeProportion));
} }
void AssetBrowserTableView::OnContextMenu([[maybe_unused]] const QPoint& point) void AssetBrowserTableView::OnContextMenu([[maybe_unused]] const QPoint& point)
{ {
const auto& selectedAssets = GetSelectedAssets(); const auto& selectedAssets = GetSelectedAssets();

@ -59,12 +59,14 @@ namespace AzToolsFramework
void ClearStringFilter(); void ClearStringFilter();
void ClearTypeFilter(); void ClearTypeFilter();
public Q_SLOTS:
void UpdateSizeSlot(int newWidth);
protected Q_SLOTS: protected Q_SLOTS:
void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) override; void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) override;
void rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) override; void rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) override;
void layoutChangedSlot(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>(), void layoutChangedSlot(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>(),
QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint); QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint);
private: private:
QString m_name; QString m_name;
QPointer<AssetBrowserTableModel> m_tableModel; QPointer<AssetBrowserTableModel> m_tableModel;

@ -71,6 +71,7 @@ namespace AzToolsFramework
} }
m_uniformScaleManipulator->SetVisualOrientationOverride(QuaternionFromTransformNoScaling(localTransform)); m_uniformScaleManipulator->SetVisualOrientationOverride(QuaternionFromTransformNoScaling(localTransform));
m_uniformScaleManipulator->SetLocalPosition(localTransform.GetTranslation());
m_uniformScaleManipulator->SetLocalOrientation(AZ::Quaternion::CreateIdentity()); m_uniformScaleManipulator->SetLocalOrientation(AZ::Quaternion::CreateIdentity());
} }

@ -13,14 +13,14 @@
namespace AzToolsFramework namespace AzToolsFramework
{ {
static const float s_surfaceManipulatorTransparency = 0.75f; static const float SurfaceManipulatorTransparency = 0.75f;
static const float s_axisLength = 2.0f; static const float LinearManipulatorAxisLength = 2.0f;
static const float s_surfaceManipulatorRadius = 0.1f; static const float SurfaceManipulatorRadius = 0.1f;
static const AZ::Color s_xAxisColor = AZ::Color(1.0f, 0.0f, 0.0f, 1.0f); static const AZ::Color LinearManipulatorXAxisColor = AZ::Color(1.0f, 0.0f, 0.0f, 1.0f);
static const AZ::Color s_yAxisColor = AZ::Color(0.0f, 1.0f, 0.0f, 1.0f); static const AZ::Color LinearManipulatorYAxisColor = AZ::Color(0.0f, 1.0f, 0.0f, 1.0f);
static const AZ::Color s_zAxisColor = AZ::Color(0.0f, 0.0f, 1.0f, 1.0f); static const AZ::Color LinearManipulatorZAxisColor = AZ::Color(0.0f, 0.0f, 1.0f, 1.0f);
static const AZ::Color s_surfaceManipulatorColor = AZ::Color(1.0f, 1.0f, 0.0f, 0.5f); static const AZ::Color SurfaceManipulatorColor = AZ::Color(1.0f, 1.0f, 0.0f, 0.5f);
TranslationManipulators::TranslationManipulators( TranslationManipulators::TranslationManipulators(
const Dimensions dimensions, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale) const Dimensions dimensions, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale)
@ -291,7 +291,7 @@ namespace AzToolsFramework
{ {
const AZ::Color color[2] = { const AZ::Color color[2] = {
defaultColor, defaultColor,
Vector3ToVector4(BaseManipulator::s_defaultMouseOverColor.GetAsVector3(), s_surfaceManipulatorTransparency) Vector3ToVector4(BaseManipulator::s_defaultMouseOverColor.GetAsVector3(), SurfaceManipulatorTransparency)
}; };
return color[mouseOver]; return color[mouseOver];
@ -325,15 +325,16 @@ namespace AzToolsFramework
void ConfigureTranslationManipulatorAppearance3d(TranslationManipulators* translationManipulators) void ConfigureTranslationManipulatorAppearance3d(TranslationManipulators* translationManipulators)
{ {
translationManipulators->SetAxes(AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ()); translationManipulators->SetAxes(AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ());
translationManipulators->ConfigurePlanarView(s_xAxisColor, s_yAxisColor, s_zAxisColor); translationManipulators->ConfigurePlanarView(LinearManipulatorXAxisColor, LinearManipulatorYAxisColor, LinearManipulatorZAxisColor);
translationManipulators->ConfigureLinearView(s_axisLength, s_xAxisColor, s_yAxisColor, s_zAxisColor); translationManipulators->ConfigureLinearView(
translationManipulators->ConfigureSurfaceView(s_surfaceManipulatorRadius, s_surfaceManipulatorColor); LinearManipulatorAxisLength, LinearManipulatorXAxisColor, LinearManipulatorYAxisColor, LinearManipulatorZAxisColor);
translationManipulators->ConfigureSurfaceView(SurfaceManipulatorRadius, SurfaceManipulatorColor);
} }
void ConfigureTranslationManipulatorAppearance2d(TranslationManipulators* translationManipulators) void ConfigureTranslationManipulatorAppearance2d(TranslationManipulators* translationManipulators)
{ {
translationManipulators->SetAxes(AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY()); translationManipulators->SetAxes(AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY());
translationManipulators->ConfigurePlanarView(s_xAxisColor); translationManipulators->ConfigurePlanarView(LinearManipulatorXAxisColor);
translationManipulators->ConfigureLinearView(s_axisLength, s_xAxisColor, s_yAxisColor); translationManipulators->ConfigureLinearView(LinearManipulatorAxisLength, LinearManipulatorXAxisColor, LinearManipulatorYAxisColor);
} }
} // namespace AzToolsFramework } // namespace AzToolsFramework

@ -700,13 +700,6 @@ namespace AzToolsFramework
switch (referenceFrame) switch (referenceFrame)
{ {
case ReferenceFrame::Local: case ReferenceFrame::Local:
// if we have a group selection, always use the pivot override if one
// is set when moving to local space (can't pick individual local space)
if (entityIdMap.size() > 1)
{
pivot.m_worldOrientation = pivotOverrideFrame.m_orientationOverride.value();
}
break;
case ReferenceFrame::Parent: case ReferenceFrame::Parent:
pivot.m_worldOrientation = pivotOverrideFrame.m_orientationOverride.value(); pivot.m_worldOrientation = pivotOverrideFrame.m_orientationOverride.value();
break; break;
@ -784,7 +777,7 @@ namespace AzToolsFramework
SortEntitiesByLocationInHierarchy(sortedEntityIdsOut); SortEntitiesByLocationInHierarchy(sortedEntityIdsOut);
} }
static void UpdateInitialRotation(EntityIdManipulators& entityManipulators) static void UpdateInitialTransform(EntityIdManipulators& entityManipulators)
{ {
// save new start orientation (if moving rotation axes separate from object // save new start orientation (if moving rotation axes separate from object
// or switching type of rotation (modifier keys change)) // or switching type of rotation (modifier keys change))
@ -797,22 +790,17 @@ namespace AzToolsFramework
} }
} }
// utility function to immediately return the current reference frame // utility function to immediately return the current reference frame based on the state of the modifiers
// based on the state of the modifiers
static ReferenceFrame ReferenceFrameFromModifiers(const ViewportInteraction::KeyboardModifiers modifiers) static ReferenceFrame ReferenceFrameFromModifiers(const ViewportInteraction::KeyboardModifiers modifiers)
{ {
if (modifiers.Shift() && !modifiers.Alt()) return modifiers.Shift() ? ReferenceFrame::World : ReferenceFrame::Local;
{
return ReferenceFrame::World;
}
else if (modifiers.Alt() && !modifiers.Shift())
{
return ReferenceFrame::Local;
} }
else
// utility function to immediately return the current sphere of influence of the manipulators based on the
// state of the modifiers
static Influence InfluenceFromModifiers(const ViewportInteraction::KeyboardModifiers modifiers)
{ {
return ReferenceFrame::Parent; return modifiers.Alt() ? Influence::Individual : Influence::Group;
}
} }
template<typename Action, typename EntityIdContainer> template<typename Action, typename EntityIdContainer>
@ -838,6 +826,7 @@ namespace AzToolsFramework
else else
{ {
const ReferenceFrame referenceFrame = spaceLock.value_or(ReferenceFrameFromModifiers(action.m_modifiers)); const ReferenceFrame referenceFrame = spaceLock.value_or(ReferenceFrameFromModifiers(action.m_modifiers));
const Influence influence = InfluenceFromModifiers(action.m_modifiers);
// note: used for parent and world depending on the current reference frame // note: used for parent and world depending on the current reference frame
const auto pivotOrientation = const auto pivotOrientation =
@ -854,9 +843,9 @@ namespace AzToolsFramework
const AZ::Vector3 worldTranslation = GetWorldTranslation(entityId); const AZ::Vector3 worldTranslation = GetWorldTranslation(entityId);
switch (referenceFrame) switch (influence)
{ {
case ReferenceFrame::Local: case Influence::Individual:
{ {
// move in each entities local space at once // move in each entities local space at once
AZ::Quaternion worldOrientation = AZ::Quaternion::CreateIdentity(); AZ::Quaternion worldOrientation = AZ::Quaternion::CreateIdentity();
@ -876,10 +865,9 @@ namespace AzToolsFramework
entityId, entityItLookupIt->second.m_initial.GetTranslation() + localOffset, transformChangedInternally); entityId, entityItLookupIt->second.m_initial.GetTranslation() + localOffset, transformChangedInternally);
} }
break; break;
case ReferenceFrame::Parent: case Influence::Group:
case ReferenceFrame::World:
{ {
AZ::Quaternion offsetRotation = pivotOrientation.m_worldOrientation * const AZ::Quaternion offsetRotation = pivotOrientation.m_worldOrientation *
QuaternionFromTransformNoScaling(entityIdManipulators.m_manipulators->GetLocalTransform().GetInverse()); QuaternionFromTransformNoScaling(entityIdManipulators.m_manipulators->GetLocalTransform().GetInverse());
const AZ::Vector3 localOffset = offsetRotation.TransformVector(action.LocalPositionOffset()); const AZ::Vector3 localOffset = offsetRotation.TransformVector(action.LocalPositionOffset());
@ -1062,7 +1050,8 @@ namespace AzToolsFramework
{ {
AZStd::chrono::milliseconds timeNow; AZStd::chrono::milliseconds timeNow;
AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::BroadcastResult( AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::BroadcastResult(
timeNow, &AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Events::EditorViewportInputTimeNow); timeNow,
&AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Events::EditorViewportInputTimeNow);
return timeNow; return timeNow;
}); });
} }
@ -1263,7 +1252,7 @@ namespace AzToolsFramework
AZStd::unique_ptr<TranslationManipulators> translationManipulators = AZStd::make_unique<TranslationManipulators>( AZStd::unique_ptr<TranslationManipulators> translationManipulators = AZStd::make_unique<TranslationManipulators>(
TranslationManipulators::Dimensions::Three, AZ::Transform::CreateIdentity(), AZ::Vector3::CreateOne()); TranslationManipulators::Dimensions::Three, AZ::Transform::CreateIdentity(), AZ::Vector3::CreateOne());
translationManipulators->SetLineBoundWidth(ManipulatorLineBoundWidth(ViewportUi::DefaultViewportId)); translationManipulators->SetLineBoundWidth(ManipulatorLineBoundWidth());
InitializeManipulators(*translationManipulators); InitializeManipulators(*translationManipulators);
@ -1297,7 +1286,7 @@ namespace AzToolsFramework
ViewportInteraction::KeyboardModifiers prevModifiers{}; ViewportInteraction::KeyboardModifiers prevModifiers{};
translationManipulators->InstallLinearManipulatorMouseMoveCallback( translationManipulators->InstallLinearManipulatorMouseMoveCallback(
[this, prevModifiers, manipulatorEntityIds](const LinearManipulator::Action& action) mutable -> void [this, prevModifiers, manipulatorEntityIds](const LinearManipulator::Action& action) mutable
{ {
UpdateTranslationManipulator( UpdateTranslationManipulator(
action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers, action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers,
@ -1331,7 +1320,7 @@ namespace AzToolsFramework
}); });
translationManipulators->InstallPlanarManipulatorMouseMoveCallback( translationManipulators->InstallPlanarManipulatorMouseMoveCallback(
[this, prevModifiers, manipulatorEntityIds](const PlanarManipulator::Action& action) mutable -> void [this, prevModifiers, manipulatorEntityIds](const PlanarManipulator::Action& action) mutable
{ {
UpdateTranslationManipulator( UpdateTranslationManipulator(
action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers, action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers,
@ -1364,7 +1353,7 @@ namespace AzToolsFramework
}); });
translationManipulators->InstallSurfaceManipulatorMouseMoveCallback( translationManipulators->InstallSurfaceManipulatorMouseMoveCallback(
[this, prevModifiers, manipulatorEntityIds](const SurfaceManipulator::Action& action) mutable -> void [this, prevModifiers, manipulatorEntityIds](const SurfaceManipulator::Action& action) mutable
{ {
UpdateTranslationManipulator( UpdateTranslationManipulator(
action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers, action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers,
@ -1391,7 +1380,7 @@ namespace AzToolsFramework
AZStd::unique_ptr<RotationManipulators> rotationManipulators = AZStd::unique_ptr<RotationManipulators> rotationManipulators =
AZStd::make_unique<RotationManipulators>(AZ::Transform::CreateIdentity()); AZStd::make_unique<RotationManipulators>(AZ::Transform::CreateIdentity());
rotationManipulators->SetCircleBoundWidth(ManipulatorCicleBoundWidth(ViewportUi::DefaultViewportId)); rotationManipulators->SetCircleBoundWidth(ManipulatorCicleBoundWidth());
InitializeManipulators(*rotationManipulators); InitializeManipulators(*rotationManipulators);
@ -1415,7 +1404,7 @@ namespace AzToolsFramework
AZStd::shared_ptr<SharedRotationState> sharedRotationState = AZStd::make_shared<SharedRotationState>(); AZStd::shared_ptr<SharedRotationState> sharedRotationState = AZStd::make_shared<SharedRotationState>();
rotationManipulators->InstallLeftMouseDownCallback( rotationManipulators->InstallLeftMouseDownCallback(
[this, sharedRotationState]([[maybe_unused]] const AngularManipulator::Action& action) mutable -> void [this, sharedRotationState]([[maybe_unused]] const AngularManipulator::Action& action) mutable
{ {
sharedRotationState->m_savedOrientation = AZ::Quaternion::CreateIdentity(); sharedRotationState->m_savedOrientation = AZ::Quaternion::CreateIdentity();
sharedRotationState->m_referenceFrameAtMouseDown = m_referenceFrame; sharedRotationState->m_referenceFrameAtMouseDown = m_referenceFrame;
@ -1437,11 +1426,13 @@ namespace AzToolsFramework
BeginRecordManipulatorCommand(); BeginRecordManipulatorCommand();
}); });
ViewportInteraction::KeyboardModifiers prevModifiers{};
rotationManipulators->InstallMouseMoveCallback( rotationManipulators->InstallMouseMoveCallback(
[this, prevModifiers, sharedRotationState](const AngularManipulator::Action& action) mutable -> void [this, prevModifiers = ViewportInteraction::KeyboardModifiers(),
sharedRotationState](const AngularManipulator::Action& action) mutable
{ {
const ReferenceFrame referenceFrame = m_spaceCluster.m_spaceLock.value_or(ReferenceFrameFromModifiers(action.m_modifiers)); const ReferenceFrame referenceFrame = m_spaceCluster.m_spaceLock.value_or(ReferenceFrameFromModifiers(action.m_modifiers));
const Influence influence = InfluenceFromModifiers(action.m_modifiers);
const AZ::Quaternion manipulatorOrientation = action.m_start.m_rotation * action.m_current.m_delta; const AZ::Quaternion manipulatorOrientation = action.m_start.m_rotation * action.m_current.m_delta;
// store the pivot override frame when positioning the manipulator manually (ctrl) // store the pivot override frame when positioning the manipulator manually (ctrl)
// so we don't lose the orientation when adding/removing entities from the selection // so we don't lose the orientation when adding/removing entities from the selection
@ -1452,9 +1443,7 @@ namespace AzToolsFramework
// only update the manipulator orientation if we're rotating in a local reference frame or we're // only update the manipulator orientation if we're rotating in a local reference frame or we're
// manually modifying the manipulator orientation independent of the entity by holding ctrl // manually modifying the manipulator orientation independent of the entity by holding ctrl
if ((sharedRotationState->m_referenceFrameAtMouseDown == ReferenceFrame::Local && if (sharedRotationState->m_referenceFrameAtMouseDown == ReferenceFrame::Local || action.m_modifiers.Ctrl())
m_entityIdManipulators.m_lookups.size() == 1) ||
action.m_modifiers.Ctrl())
{ {
m_entityIdManipulators.m_manipulators->SetLocalTransform(AZ::Transform::CreateFromQuaternionAndTranslation( m_entityIdManipulators.m_manipulators->SetLocalTransform(AZ::Transform::CreateFromQuaternionAndTranslation(
manipulatorOrientation, m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation())); manipulatorOrientation, m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation()));
@ -1463,20 +1452,24 @@ namespace AzToolsFramework
// save state if we change the type of rotation we're doing to to prevent snapping // save state if we change the type of rotation we're doing to to prevent snapping
if (prevModifiers != action.m_modifiers) if (prevModifiers != action.m_modifiers)
{ {
UpdateInitialRotation(m_entityIdManipulators); UpdateInitialTransform(m_entityIdManipulators);
sharedRotationState->m_savedOrientation = action.m_current.m_delta.GetInverseFull(); sharedRotationState->m_savedOrientation = action.m_current.m_delta.GetInverseFull();
} }
// allow the user to modify the orientation without moving the object if ctrl is held // allow the user to modify the orientation without moving the object if ctrl is held
if (action.m_modifiers.Ctrl()) if (action.m_modifiers.Ctrl())
{ {
UpdateInitialRotation(m_entityIdManipulators); UpdateInitialTransform(m_entityIdManipulators);
sharedRotationState->m_savedOrientation = action.m_current.m_delta.GetInverseFull(); sharedRotationState->m_savedOrientation = action.m_current.m_delta.GetInverseFull();
} }
else else
{ {
const auto pivotOrientation = ETCS::CalculateSelectionPivotOrientation( // only update the pivot override if the orientation is being modified in local space and we have
m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, ReferenceFrame::Parent); // more than one entity selected (so rotating a single entity does not set the orientation override)
if (referenceFrame == ReferenceFrame::Local && sharedRotationState->m_entityIds.size() > 1)
{
m_pivotOverrideFrame.m_orientationOverride = manipulatorOrientation;
}
// note: must use sorted entityIds based on hierarchy order when updating transforms // note: must use sorted entityIds based on hierarchy order when updating transforms
for (AZ::EntityId entityId : sharedRotationState->m_entityIds) for (AZ::EntityId entityId : sharedRotationState->m_entityIds)
@ -1492,9 +1485,9 @@ namespace AzToolsFramework
const AZ::Transform offsetRotation = const AZ::Transform offsetRotation =
AZ::Transform::CreateFromQuaternion(sharedRotationState->m_savedOrientation * action.m_current.m_delta); AZ::Transform::CreateFromQuaternion(sharedRotationState->m_savedOrientation * action.m_current.m_delta);
switch (referenceFrame) switch (influence)
{ {
case ReferenceFrame::Local: case Influence::Individual:
{ {
const AZ::Quaternion rotation = entityIdLookupIt->second.m_initial.GetRotation().GetNormalized(); const AZ::Quaternion rotation = entityIdLookupIt->second.m_initial.GetRotation().GetNormalized();
const AZ::Vector3 position = entityIdLookupIt->second.m_initial.GetTranslation(); const AZ::Vector3 position = entityIdLookupIt->second.m_initial.GetTranslation();
@ -1510,23 +1503,10 @@ namespace AzToolsFramework
AZ::Transform::CreateTranslation(-centerOffset) * AZ::Transform::CreateUniformScale(scale)); AZ::Transform::CreateTranslation(-centerOffset) * AZ::Transform::CreateUniformScale(scale));
} }
break; break;
case ReferenceFrame::Parent: case Influence::Group:
{
const AZ::Transform pivotTransform = AZ::Transform::CreateFromQuaternionAndTranslation(
pivotOrientation.m_worldOrientation,
m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation());
const AZ::Transform transformInPivotSpace =
pivotTransform.GetInverse() * entityIdLookupIt->second.m_initial;
SetEntityWorldTransform(entityId, pivotTransform * offsetRotation * transformInPivotSpace);
}
break;
case ReferenceFrame::World:
{ {
const AZ::Transform pivotTransform = AZ::Transform::CreateFromQuaternionAndTranslation( const AZ::Transform pivotTransform = AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateIdentity(), manipulatorOrientation, m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation());
m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation());
const AZ::Transform transformInPivotSpace = const AZ::Transform transformInPivotSpace =
pivotTransform.GetInverse() * entityIdLookupIt->second.m_initial; pivotTransform.GetInverse() * entityIdLookupIt->second.m_initial;
@ -1561,7 +1541,7 @@ namespace AzToolsFramework
AZ_PROFILE_FUNCTION(AzToolsFramework); AZ_PROFILE_FUNCTION(AzToolsFramework);
AZStd::unique_ptr<ScaleManipulators> scaleManipulators = AZStd::make_unique<ScaleManipulators>(AZ::Transform::CreateIdentity()); AZStd::unique_ptr<ScaleManipulators> scaleManipulators = AZStd::make_unique<ScaleManipulators>(AZ::Transform::CreateIdentity());
scaleManipulators->SetLineBoundWidth(ManipulatorLineBoundWidth(ViewportUi::DefaultViewportId)); scaleManipulators->SetLineBoundWidth(ManipulatorLineBoundWidth());
InitializeManipulators(*scaleManipulators); InitializeManipulators(*scaleManipulators);
@ -1571,13 +1551,20 @@ namespace AzToolsFramework
scaleManipulators->SetAxes(AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ()); scaleManipulators->SetAxes(AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ());
scaleManipulators->ConfigureView(2.0f, AZ::Color::CreateOne(), AZ::Color::CreateOne(), AZ::Color::CreateOne()); scaleManipulators->ConfigureView(2.0f, AZ::Color::CreateOne(), AZ::Color::CreateOne(), AZ::Color::CreateOne());
struct SharedScaleState
{
AZ::Vector3 m_savedScaleOffset = AZ::Vector3::CreateZero();
EntityIdList m_entityIds;
};
// lambdas capture shared_ptr by value to increment ref count // lambdas capture shared_ptr by value to increment ref count
auto manipulatorEntityIds = AZStd::make_shared<ManipulatorEntityIds>(); auto sharedScaleState = AZStd::make_shared<SharedScaleState>();
auto uniformLeftMouseDownCallback = [this, manipulatorEntityIds]([[maybe_unused]] const LinearManipulator::Action& action) auto uniformLeftMouseDownCallback = [this, sharedScaleState]([[maybe_unused]] const LinearManipulator::Action& action)
{ {
sharedScaleState->m_savedScaleOffset = AZ::Vector3::CreateZero();
// important to sort entityIds based on hierarchy order when updating transforms // important to sort entityIds based on hierarchy order when updating transforms
BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds->m_entityIds); BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, sharedScaleState->m_entityIds);
for (auto& entityIdLookup : m_entityIdManipulators.m_lookups) for (auto& entityIdLookup : m_entityIdManipulators.m_lookups)
{ {
@ -1591,20 +1578,32 @@ namespace AzToolsFramework
m_axisPreview.m_orientation = QuaternionFromTransformNoScaling(m_entityIdManipulators.m_manipulators->GetLocalTransform()); m_axisPreview.m_orientation = QuaternionFromTransformNoScaling(m_entityIdManipulators.m_manipulators->GetLocalTransform());
}; };
auto uniformLeftMouseUpCallback = [this, manipulatorEntityIds]([[maybe_unused]] const LinearManipulator::Action& action) auto uniformLeftMouseUpCallback = [this, sharedScaleState]([[maybe_unused]] const LinearManipulator::Action& action)
{ {
AzToolsFramework::EditorTransformChangeNotificationBus::Broadcast( AzToolsFramework::EditorTransformChangeNotificationBus::Broadcast(
&AzToolsFramework::EditorTransformChangeNotificationBus::Events::OnEntityTransformChanged, &AzToolsFramework::EditorTransformChangeNotificationBus::Events::OnEntityTransformChanged, sharedScaleState->m_entityIds);
manipulatorEntityIds->m_entityIds);
m_entityIdManipulators.m_manipulators->SetLocalTransform(RecalculateAverageManipulatorTransform( m_entityIdManipulators.m_manipulators->SetLocalTransform(RecalculateAverageManipulatorTransform(
m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, m_pivotMode, m_referenceFrame)); m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, m_pivotMode, m_referenceFrame));
}; };
auto uniformLeftMouseMoveCallback = [this, manipulatorEntityIds](const LinearManipulator::Action& action) auto uniformLeftMouseMoveCallback = [this, sharedScaleState, prevModifiers = ViewportInteraction::KeyboardModifiers()](
const LinearManipulator::Action& action) mutable
{ {
// do nothing to modify the manipulator
if (action.m_modifiers.Ctrl())
{
return;
}
if (prevModifiers != action.m_modifiers)
{
UpdateInitialTransform(m_entityIdManipulators);
sharedScaleState->m_savedScaleOffset = action.LocalScaleOffset();
}
// note: must use sorted entityIds based on hierarchy order when updating transforms // note: must use sorted entityIds based on hierarchy order when updating transforms
for (AZ::EntityId entityId : manipulatorEntityIds->m_entityIds) for (AZ::EntityId entityId : sharedScaleState->m_entityIds)
{ {
auto entityIdLookupIt = m_entityIdManipulators.m_lookups.find(entityId); auto entityIdLookupIt = m_entityIdManipulators.m_lookups.find(entityId);
if (entityIdLookupIt == m_entityIdManipulators.m_lookups.end()) if (entityIdLookupIt == m_entityIdManipulators.m_lookups.end())
@ -1620,18 +1619,22 @@ namespace AzToolsFramework
return vec.GetX() + vec.GetY() + vec.GetZ(); return vec.GetX() + vec.GetY() + vec.GetZ();
}; };
const float uniformScale = action.m_start.m_sign * sumVectorElements(action.LocalScaleOffset()); const float uniformScale =
action.m_start.m_sign * sumVectorElements(action.LocalScaleOffset() - sharedScaleState->m_savedScaleOffset);
const float scale = AZ::GetClamp(1.0f + uniformScale / initialScale, AZ::MinTransformScale, AZ::MaxTransformScale); const float scale = AZ::GetClamp(1.0f + uniformScale / initialScale, AZ::MinTransformScale, AZ::MaxTransformScale);
const AZ::Transform scaleTransform = AZ::Transform::CreateUniformScale(scale); const AZ::Transform scaleTransform = AZ::Transform::CreateUniformScale(scale);
if (action.m_modifiers.Alt()) switch (InfluenceFromModifiers(action.m_modifiers))
{
case Influence::Individual:
{ {
const AZ::Transform pivotTransform = TransformNormalizedScale(entityIdLookupIt->second.m_initial); const AZ::Transform pivotTransform = TransformNormalizedScale(entityIdLookupIt->second.m_initial);
const AZ::Transform transformInPivotSpace = pivotTransform.GetInverse() * initial; const AZ::Transform transformInPivotSpace = pivotTransform.GetInverse() * initial;
SetEntityWorldTransform(entityId, pivotTransform * scaleTransform * transformInPivotSpace); SetEntityWorldTransform(entityId, pivotTransform * scaleTransform * transformInPivotSpace);
} }
else break;
case Influence::Group:
{ {
const AZ::Transform pivotTransform = const AZ::Transform pivotTransform =
TransformNormalizedScale(m_entityIdManipulators.m_manipulators->GetLocalTransform()); TransformNormalizedScale(m_entityIdManipulators.m_manipulators->GetLocalTransform());
@ -1640,6 +1643,9 @@ namespace AzToolsFramework
SetEntityWorldTransform(entityId, pivotTransform * scaleTransform * transformInPivotSpace); SetEntityWorldTransform(entityId, pivotTransform * scaleTransform * transformInPivotSpace);
} }
} }
}
prevModifiers = action.m_modifiers;
}; };
scaleManipulators->InstallAxisLeftMouseDownCallback(uniformLeftMouseDownCallback); scaleManipulators->InstallAxisLeftMouseDownCallback(uniformLeftMouseDownCallback);
@ -2729,8 +2735,7 @@ namespace AzToolsFramework
if (m_pivotOverrideFrame.m_orientationOverride && m_entityIdManipulators.m_manipulators) if (m_pivotOverrideFrame.m_orientationOverride && m_entityIdManipulators.m_manipulators)
{ {
m_pivotOverrideFrame.m_orientationOverride = m_pivotOverrideFrame.m_orientationOverride = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetRotation();
QuaternionFromTransformNoScaling(m_entityIdManipulators.m_manipulators->GetLocalTransform());
} }
if (m_pivotOverrideFrame.m_translationOverride && m_entityIdManipulators.m_manipulators) if (m_pivotOverrideFrame.m_translationOverride && m_entityIdManipulators.m_manipulators)
@ -3337,7 +3342,7 @@ namespace AzToolsFramework
display.SetLineWidth(4.0f); display.SetLineWidth(4.0f);
const auto axisFlip = [&transform, &cameraState](const AZ::Vector3& axis) -> float const auto axisFlip = [&transform, &cameraState](const AZ::Vector3& axis)
{ {
return ShouldFlipCameraAxis( return ShouldFlipCameraAxis(
AZ::Transform::CreateIdentity(), transform.GetTranslation(), TransformDirectionNoScaling(transform, axis), AZ::Transform::CreateIdentity(), transform.GetTranslation(), TransformDirectionNoScaling(transform, axis),
@ -3554,7 +3559,7 @@ namespace AzToolsFramework
// screen space // screen space
const auto calculateGizmoAxis = [&cameraView, &cameraProjection, &screenOffset](const AZ::Vector3& axis) const auto calculateGizmoAxis = [&cameraView, &cameraProjection, &screenOffset](const AZ::Vector3& axis)
{ {
auto result = AZ::Vector2(AzFramework::WorldToScreenNDC(axis, cameraView, cameraProjection)); auto result = AZ::Vector2(AzFramework::WorldToScreenNdc(axis, cameraView, cameraProjection));
result.SetY(1.0f - result.GetY()); result.SetY(1.0f - result.GetY());
return result + screenOffset; return result + screenOffset;
}; };

@ -96,6 +96,14 @@ namespace AzToolsFramework
AZ::u8 m_pickTypes = PickType::None; //!< What mode(s) were we in when picking an EntityId override. AZ::u8 m_pickTypes = PickType::None; //!< What mode(s) were we in when picking an EntityId override.
}; };
//! How a manipulator should treat an adjustment.
//! @note Determines if a transform is applied to an individual entity or the whole group.
enum class Influence
{
Group,
Individual
};
//! What frame/space is the manipulator currently operating in. //! What frame/space is the manipulator currently operating in.
enum class ReferenceFrame enum class ReferenceFrame
{ {
@ -328,7 +336,8 @@ namespace AzToolsFramework
OptionalFrame m_pivotOverrideFrame; //!< Has a pivot override been set. OptionalFrame m_pivotOverrideFrame; //!< Has a pivot override been set.
Mode m_mode = Mode::Translation; //!< Manipulator mode - default to translation. Mode m_mode = Mode::Translation; //!< Manipulator mode - default to translation.
Pivot m_pivotMode = Pivot::Object; //!< Entity pivot mode - default to object (authored root). 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. ReferenceFrame m_referenceFrame = ReferenceFrame::Local; //!< What reference frame is the Manipulator currently operating in.
Influence m_influence = Influence::Group; //!< What sphere of influence does the Manipulator have.
Frame m_axisPreview; //!< Axes of entity at the time of mouse down to indicate delta of translation. 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_triedToRefresh = false; //!< Did a refresh event occur to recalculate the current Manipulator transform.
//! Was EditorTransformComponentSelection responsible for the most recent entity selection change. //! Was EditorTransformComponentSelection responsible for the most recent entity selection change.

@ -242,11 +242,11 @@ namespace UnitTest
{ {
// the initial starting position of the entities // the initial starting position of the entities
AZ::TransformBus::Event( AZ::TransformBus::Event(
m_entityId1, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(m_entity1WorldTranslation)); m_entityId1, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(Entity1WorldTranslation));
AZ::TransformBus::Event( AZ::TransformBus::Event(
m_entityId2, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(m_entity2WorldTranslation)); m_entityId2, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(Entity2WorldTranslation));
AZ::TransformBus::Event( AZ::TransformBus::Event(
m_entityId3, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(m_entity3WorldTranslation)); m_entityId3, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(Entity3WorldTranslation));
} }
static void PositionCamera(AzFramework::CameraState& cameraState) static void PositionCamera(AzFramework::CameraState& cameraState)
@ -261,9 +261,10 @@ namespace UnitTest
AZ::EntityId m_entityId1; AZ::EntityId m_entityId1;
AZ::EntityId m_entityId2; AZ::EntityId m_entityId2;
AZ::EntityId m_entityId3; AZ::EntityId m_entityId3;
AZ::Vector3 m_entity1WorldTranslation = AZ::Vector3(5.0f, 15.0f, 10.0f);
AZ::Vector3 m_entity2WorldTranslation = AZ::Vector3(5.0f, 14.0f, 10.0f); static inline const AZ::Vector3 Entity1WorldTranslation = AZ::Vector3(5.0f, 15.0f, 10.0f);
AZ::Vector3 m_entity3WorldTranslation = AZ::Vector3(5.0f, 16.0f, 10.0f); static inline const AZ::Vector3 Entity2WorldTranslation = AZ::Vector3(5.0f, 14.0f, 10.0f);
static inline const AZ::Vector3 Entity3WorldTranslation = AZ::Vector3(5.0f, 16.0f, 10.0f);
}; };
void ArrangeIndividualRotatedEntitySelection(const AzToolsFramework::EntityIdList& entityIds, const AZ::Quaternion& orientation) void ArrangeIndividualRotatedEntitySelection(const AzToolsFramework::EntityIdList& entityIds, const AZ::Quaternion& orientation)
@ -371,16 +372,16 @@ namespace UnitTest
// Given // Given
AzToolsFramework::SelectEntity(m_entityId1); AzToolsFramework::SelectEntity(m_entityId1);
ArrangeIndividualRotatedEntitySelection(m_entityIds, AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f))); const auto entityTransform = AZ::Transform::CreateFromQuaternion(AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)));
ArrangeIndividualRotatedEntitySelection(m_entityIds, entityTransform.GetRotation());
RefreshManipulators(EditorTransformComponentSelectionRequestBus::Events::RefreshType::All); RefreshManipulators(EditorTransformComponentSelectionRequestBus::Events::RefreshType::All);
SetTransformMode(EditorTransformComponentSelectionRequestBus::Events::Mode::Rotation); SetTransformMode(EditorTransformComponentSelectionRequestBus::Events::Mode::Rotation);
const AZ::Transform manipulatorTransformBefore = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()); const AZ::Transform manipulatorTransformBefore = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity());
// check preconditions - manipulator transform matches parent/world transform (identity) // check preconditions - manipulator transform matches the entity transform
EXPECT_THAT(manipulatorTransformBefore.GetBasisY(), IsClose(AZ::Vector3::CreateAxisY())); EXPECT_THAT(manipulatorTransformBefore, IsClose(entityTransform));
EXPECT_THAT(manipulatorTransformBefore.GetBasisZ(), IsClose(AZ::Vector3::CreateAxisZ()));
/////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -624,7 +625,7 @@ namespace UnitTest
EXPECT_TRUE(selectedEntitiesBefore.empty()); EXPECT_TRUE(selectedEntitiesBefore.empty());
// calculate the position in screen space of the initial entity position // calculate the position in screen space of the initial entity position
const auto entity1ScreenPosition = AzFramework::WorldToScreen(m_entity1WorldTranslation, m_cameraState); const auto entity1ScreenPosition = AzFramework::WorldToScreen(Entity1WorldTranslation, m_cameraState);
// click the entity in the viewport // click the entity in the viewport
m_actionDispatcher->SetStickySelect(true) m_actionDispatcher->SetStickySelect(true)
@ -649,7 +650,7 @@ namespace UnitTest
EXPECT_TRUE(selectedEntitiesBefore.empty()); EXPECT_TRUE(selectedEntitiesBefore.empty());
// calculate the position in screen space of the initial entity position // calculate the position in screen space of the initial entity position
const auto entity1ScreenPosition = AzFramework::WorldToScreen(m_entity1WorldTranslation, m_cameraState); const auto entity1ScreenPosition = AzFramework::WorldToScreen(Entity1WorldTranslation, m_cameraState);
// click the entity in the viewport // click the entity in the viewport
m_actionDispatcher->SetStickySelect(false) m_actionDispatcher->SetStickySelect(false)
@ -728,7 +729,7 @@ namespace UnitTest
AzToolsFramework::SelectEntity(m_entityId1); AzToolsFramework::SelectEntity(m_entityId1);
// calculate the position in screen space of the second entity // calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState); const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// click the entity in the viewport // click the entity in the viewport
m_actionDispatcher->SetStickySelect(true) m_actionDispatcher->SetStickySelect(true)
@ -754,7 +755,7 @@ namespace UnitTest
AzToolsFramework::SelectEntity(m_entityId1); AzToolsFramework::SelectEntity(m_entityId1);
// calculate the position in screen space of the second entity // calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState); const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// click the entity in the viewport // click the entity in the viewport
m_actionDispatcher->SetStickySelect(false) m_actionDispatcher->SetStickySelect(false)
@ -780,7 +781,7 @@ namespace UnitTest
AzToolsFramework::SelectEntity(m_entityId1); AzToolsFramework::SelectEntity(m_entityId1);
// calculate the position in screen space of the second entity // calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState); const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// click the entity in the viewport // click the entity in the viewport
m_actionDispatcher->SetStickySelect(true) m_actionDispatcher->SetStickySelect(true)
@ -806,7 +807,7 @@ namespace UnitTest
AzToolsFramework::SelectEntity(m_entityId1); AzToolsFramework::SelectEntity(m_entityId1);
// calculate the position in screen space of the second entity // calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState); const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// click the entity in the viewport // click the entity in the viewport
m_actionDispatcher->SetStickySelect(false) m_actionDispatcher->SetStickySelect(false)
@ -832,7 +833,7 @@ namespace UnitTest
AzToolsFramework::SelectEntities({ m_entityId1, m_entityId2 }); AzToolsFramework::SelectEntities({ m_entityId1, m_entityId2 });
// calculate the position in screen space of the second entity // calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState); const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// click the entity in the viewport // click the entity in the viewport
m_actionDispatcher->SetStickySelect(true) m_actionDispatcher->SetStickySelect(true)
@ -858,7 +859,7 @@ namespace UnitTest
AzToolsFramework::SelectEntities({ m_entityId1, m_entityId2 }); AzToolsFramework::SelectEntities({ m_entityId1, m_entityId2 });
// calculate the position in screen space of the second entity // calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState); const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// click the entity in the viewport // click the entity in the viewport
m_actionDispatcher->SetStickySelect(false) m_actionDispatcher->SetStickySelect(false)
@ -1001,7 +1002,7 @@ namespace UnitTest
AzToolsFramework::SelectEntity(m_entityId1); AzToolsFramework::SelectEntity(m_entityId1);
// calculate the position in screen space of the second entity // calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState); const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// single click select entity2 // single click select entity2
m_actionDispatcher->SetStickySelect(false) m_actionDispatcher->SetStickySelect(false)
@ -1035,7 +1036,7 @@ namespace UnitTest
AzToolsFramework::SelectEntity(m_entityId1); AzToolsFramework::SelectEntity(m_entityId1);
// calculate the position in screen space of the second entity // calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState); const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// single click select entity2 // single click select entity2
m_actionDispatcher->SetStickySelect(GetParam()) m_actionDispatcher->SetStickySelect(GetParam())
@ -1056,7 +1057,7 @@ namespace UnitTest
manipulatorTransform, AzToolsFramework::GetEntityContextId(), manipulatorTransform, AzToolsFramework::GetEntityContextId(),
&AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform); &AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(m_entity2WorldTranslation)); EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(Entity2WorldTranslation));
} }
TEST_P( TEST_P(
@ -1069,7 +1070,7 @@ namespace UnitTest
AzToolsFramework::SelectEntity(m_entityId1); AzToolsFramework::SelectEntity(m_entityId1);
// calculate the position in screen space of the second entity // calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState); const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// position in space above the entities // position in space above the entities
const auto clickOffPositionWorld = AZ::Vector3(5.0f, 15.0f, 12.0f); const auto clickOffPositionWorld = AZ::Vector3(5.0f, 15.0f, 12.0f);
@ -1096,7 +1097,7 @@ namespace UnitTest
manipulatorTransform, AzToolsFramework::GetEntityContextId(), manipulatorTransform, AzToolsFramework::GetEntityContextId(),
&AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform); &AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(m_entity2WorldTranslation)); EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(Entity2WorldTranslation));
}) })
->MousePosition(clickOffPositionScreen) ->MousePosition(clickOffPositionScreen)
->KeyboardModifierDown(AzToolsFramework::ViewportInteraction::KeyboardModifier::Control) ->KeyboardModifierDown(AzToolsFramework::ViewportInteraction::KeyboardModifier::Control)
@ -1113,11 +1114,560 @@ namespace UnitTest
&AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform); &AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
// manipulator transform is reset // manipulator transform is reset
EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(m_entity1WorldTranslation)); EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(Entity1WorldTranslation));
} }
INSTANTIATE_TEST_CASE_P(All, EditorTransformComponentSelectionViewportPickingManipulatorTestFixtureParam, testing::Values(true, false)); INSTANTIATE_TEST_CASE_P(All, EditorTransformComponentSelectionViewportPickingManipulatorTestFixtureParam, testing::Values(true, false));
// create alias for EditorTransformComponentSelectionViewportPickingManipulatorTestFixture to help group tests
using EditorTransformComponentSelectionManipulatorInteractionTestFixture =
EditorTransformComponentSelectionViewportPickingManipulatorTestFixture;
// type to group related inputs and outcomes for parameterized tests (single entity)
struct ManipulatorOptionsSingle
{
AzToolsFramework::ViewportInteraction::KeyboardModifier m_keyboardModifier;
AZ::Transform m_expectedManipulatorTransformAfter;
AZ::Transform m_expectedEntityTransformAfter;
};
class EditorTransformComponentSelectionRotationManipulatorSingleEntityTestFixtureParam
: public EditorTransformComponentSelectionManipulatorInteractionTestFixture
, public ::testing::WithParamInterface<ManipulatorOptionsSingle>
{
};
TEST_P(
EditorTransformComponentSelectionRotationManipulatorSingleEntityTestFixtureParam,
RotatingASingleEntityWithDifferentModifierCombinations)
{
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
PositionEntities();
PositionCamera(m_cameraState);
SetTransformMode(EditorTransformComponentSelectionRequestBus::Events::Mode::Rotation);
AzToolsFramework::SelectEntity(m_entityId1);
const float screenToWorldMultiplier = AzToolsFramework::CalculateScreenToWorldMultiplier(Entity1WorldTranslation, m_cameraState);
const float manipulatorRadius = 2.0f * screenToWorldMultiplier;
const auto rotationManipulatorStartHoldWorldPosition = Entity1WorldTranslation +
AZ::Quaternion::CreateRotationX(AZ::DegToRad(-45.0f)).TransformVector(AZ::Vector3::CreateAxisY(-manipulatorRadius));
const auto rotationManipulatorEndHoldWorldPosition = Entity1WorldTranslation +
AZ::Quaternion::CreateRotationX(AZ::DegToRad(-135.0f)).TransformVector(AZ::Vector3::CreateAxisY(-manipulatorRadius));
// calculate screen space positions
const auto rotationManipulatorHoldScreenPosition =
AzFramework::WorldToScreen(rotationManipulatorStartHoldWorldPosition, m_cameraState);
const auto rotationManipulatorEndHoldScreenPosition =
AzFramework::WorldToScreen(rotationManipulatorEndHoldWorldPosition, m_cameraState);
m_actionDispatcher->CameraState(m_cameraState)
->MousePosition(rotationManipulatorHoldScreenPosition)
->KeyboardModifierDown(GetParam().m_keyboardModifier)
->MouseLButtonDown()
->MousePosition(rotationManipulatorEndHoldScreenPosition)
->MouseLButtonUp();
const auto expectedEntityTransform = GetParam().m_expectedEntityTransformAfter;
const auto expectedManipulatorTransform = GetParam().m_expectedManipulatorTransformAfter;
const auto manipulatorTransform = GetManipulatorTransform();
const auto entityTransform = AzToolsFramework::GetWorldTransform(m_entityId1);
EXPECT_THAT(*manipulatorTransform, IsClose(expectedManipulatorTransform));
EXPECT_THAT(entityTransform, IsClose(expectedEntityTransform));
}
static const AZ::Transform ExpectedTransformAfterLocalRotationManipulatorMotion = AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateRotationX(AZ::DegToRad(-90.0f)),
EditorTransformComponentSelectionViewportPickingFixture::Entity1WorldTranslation);
INSTANTIATE_TEST_CASE_P(
All,
EditorTransformComponentSelectionRotationManipulatorSingleEntityTestFixtureParam,
testing::Values(
// this replicates rotating an entity in local space with no modifiers held
// manipulator and entity rotate
ManipulatorOptionsSingle{ AzToolsFramework::ViewportInteraction::KeyboardModifier::None,
ExpectedTransformAfterLocalRotationManipulatorMotion,
ExpectedTransformAfterLocalRotationManipulatorMotion },
// this replicates rotating an entity in local space with the alt modifier held
// manipulator and entity rotate
ManipulatorOptionsSingle{ AzToolsFramework::ViewportInteraction::KeyboardModifier::Alt,
ExpectedTransformAfterLocalRotationManipulatorMotion,
ExpectedTransformAfterLocalRotationManipulatorMotion },
// this replicates rotating an entity in world space with the shift modifier held
// entity rotates, manipulator remains aligned to world
ManipulatorOptionsSingle{
AzToolsFramework::ViewportInteraction::KeyboardModifier::Shift,
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity1WorldTranslation),
ExpectedTransformAfterLocalRotationManipulatorMotion },
// this replicates rotating the manipulator in local space with the ctrl modifier held (entity is unchanged)
ManipulatorOptionsSingle{
AzToolsFramework::ViewportInteraction::KeyboardModifier::Ctrl, ExpectedTransformAfterLocalRotationManipulatorMotion,
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity1WorldTranslation) }));
// type to group related inputs and outcomes for parameterized tests (two entities)
struct ManipulatorOptionsMultiple
{
AzToolsFramework::ViewportInteraction::KeyboardModifier m_keyboardModifier;
AZ::Transform m_expectedManipulatorTransformAfter;
AZ::Transform m_firstExpectedEntityTransformAfter;
AZ::Transform m_secondExpectedEntityTransformAfter;
};
class EditorTransformComponentSelectionRotationManipulatorMultipleEntityTestFixtureParam
: public EditorTransformComponentSelectionManipulatorInteractionTestFixture
, public ::testing::WithParamInterface<ManipulatorOptionsMultiple>
{
};
TEST_P(
EditorTransformComponentSelectionRotationManipulatorMultipleEntityTestFixtureParam,
RotatingMultipleEntitiesWithDifferentModifierCombinations)
{
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
PositionEntities();
PositionCamera(m_cameraState);
SetTransformMode(EditorTransformComponentSelectionRequestBus::Events::Mode::Rotation);
AzToolsFramework::SelectEntities({ m_entityId2, m_entityId3 });
// manipulator should be centered between the two entities
const auto initialManipulatorTransform = GetManipulatorTransform();
const float screenToWorldMultiplier =
AzToolsFramework::CalculateScreenToWorldMultiplier(initialManipulatorTransform->GetTranslation(), m_cameraState);
const float manipulatorRadius = 2.0f * screenToWorldMultiplier;
const auto rotationManipulatorStartHoldWorldPosition = initialManipulatorTransform->GetTranslation() +
AZ::Quaternion::CreateRotationX(AZ::DegToRad(-45.0f)).TransformVector(AZ::Vector3::CreateAxisY(-manipulatorRadius));
const auto rotationManipulatorEndHoldWorldPosition = initialManipulatorTransform->GetTranslation() +
AZ::Quaternion::CreateRotationX(AZ::DegToRad(-135.0f)).TransformVector(AZ::Vector3::CreateAxisY(-manipulatorRadius));
// calculate screen space positions
const auto rotationManipulatorHoldScreenPosition =
AzFramework::WorldToScreen(rotationManipulatorStartHoldWorldPosition, m_cameraState);
const auto rotationManipulatorEndHoldScreenPosition =
AzFramework::WorldToScreen(rotationManipulatorEndHoldWorldPosition, m_cameraState);
m_actionDispatcher->CameraState(m_cameraState)
->MousePosition(rotationManipulatorHoldScreenPosition)
->KeyboardModifierDown(GetParam().m_keyboardModifier)
->MouseLButtonDown()
->MousePosition(rotationManipulatorEndHoldScreenPosition)
->MouseLButtonUp();
const auto expectedEntity2Transform = GetParam().m_firstExpectedEntityTransformAfter;
const auto expectedEntity3Transform = GetParam().m_secondExpectedEntityTransformAfter;
const auto expectedManipulatorTransform = GetParam().m_expectedManipulatorTransformAfter;
const auto manipulatorTransformAfter = GetManipulatorTransform();
const auto entity2Transform = AzToolsFramework::GetWorldTransform(m_entityId2);
const auto entity3Transform = AzToolsFramework::GetWorldTransform(m_entityId3);
EXPECT_THAT(*manipulatorTransformAfter, IsClose(expectedManipulatorTransform));
EXPECT_THAT(entity2Transform, IsClose(expectedEntity2Transform));
EXPECT_THAT(entity3Transform, IsClose(expectedEntity3Transform));
}
// note: The aggregate manipulator position will be the average of entity 2 and 3 combined which
// winds up being the same as entity 1
static const AZ::Vector3 AggregateManipulatorPositionWithEntity2and3Selected =
EditorTransformComponentSelectionViewportPickingFixture::Entity1WorldTranslation;
static const AZ::Transform ExpectedEntity2TransformAfterLocalGroupRotationManipulatorMotion =
AZ::Transform::CreateTranslation(AggregateManipulatorPositionWithEntity2and3Selected) *
AZ::Transform::CreateFromQuaternion(AZ::Quaternion::CreateRotationX(AZ::DegToRad(-90.0f))) *
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(-1.0f));
static const AZ::Transform ExpectedEntity3TransformAfterLocalGroupRotationManipulatorMotion =
AZ::Transform::CreateTranslation(AggregateManipulatorPositionWithEntity2and3Selected) *
AZ::Transform::CreateFromQuaternion(AZ::Quaternion::CreateRotationX(AZ::DegToRad(-90.0f))) *
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(1.0f));
static const AZ::Transform ExpectedEntity2TransformAfterLocalIndividualRotationManipulatorMotion =
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation) *
AZ::Transform::CreateFromQuaternion(AZ::Quaternion::CreateRotationX(AZ::DegToRad(-90.0f)));
static const AZ::Transform ExpectedEntity3TransformAfterLocalIndividualRotationManipulatorMotion =
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation) *
AZ::Transform::CreateFromQuaternion(AZ::Quaternion::CreateRotationX(AZ::DegToRad(-90.0f)));
INSTANTIATE_TEST_CASE_P(
All,
EditorTransformComponentSelectionRotationManipulatorMultipleEntityTestFixtureParam,
testing::Values(
// this replicates rotating a group of entities in local space with no modifiers held
// manipulator and entity rotate
ManipulatorOptionsMultiple{ AzToolsFramework::ViewportInteraction::KeyboardModifier::None,
ExpectedTransformAfterLocalRotationManipulatorMotion,
ExpectedEntity2TransformAfterLocalGroupRotationManipulatorMotion,
ExpectedEntity3TransformAfterLocalGroupRotationManipulatorMotion },
// this replicates rotating a group of entities in local space with the alt modifier held
// manipulator and entity rotate
ManipulatorOptionsMultiple{ AzToolsFramework::ViewportInteraction::KeyboardModifier::Alt,
ExpectedTransformAfterLocalRotationManipulatorMotion,
ExpectedEntity2TransformAfterLocalIndividualRotationManipulatorMotion,
ExpectedEntity3TransformAfterLocalIndividualRotationManipulatorMotion },
// this replicates rotating a group of entities in world space with the shift modifier held
// entity rotates, manipulator remains aligned to world
ManipulatorOptionsMultiple{
AzToolsFramework::ViewportInteraction::KeyboardModifier::Shift,
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity1WorldTranslation),
ExpectedEntity2TransformAfterLocalGroupRotationManipulatorMotion,
ExpectedEntity3TransformAfterLocalGroupRotationManipulatorMotion },
// this replicates rotating the manipulator in local space with the ctrl modifier held (entity is unchanged)
ManipulatorOptionsMultiple{
AzToolsFramework::ViewportInteraction::KeyboardModifier::Ctrl, ExpectedTransformAfterLocalRotationManipulatorMotion,
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation),
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation) }));
class EditorTransformComponentSelectionTranslationManipulatorSingleEntityTestFixtureParam
: public EditorTransformComponentSelectionManipulatorInteractionTestFixture
, public ::testing::WithParamInterface<ManipulatorOptionsSingle>
{
};
static const float LinearManipulatorYAxisMovement = -3.0f;
static const float LinearManipulatorZAxisMovement = 2.0f;
TEST_P(
EditorTransformComponentSelectionTranslationManipulatorSingleEntityTestFixtureParam,
TranslatingASingleEntityWithDifferentModifierCombinations)
{
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
PositionEntities();
// move camera up and to the left so it's just above the normal row of entities
AzFramework::SetCameraTransform(
m_cameraState,
AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), AZ::Vector3(10.0f, 14.5, 11.0f)));
SetTransformMode(EditorTransformComponentSelectionRequestBus::Events::Mode::Translation);
AzToolsFramework::SelectEntity(m_entityId1);
const auto entity1Transform = AzToolsFramework::GetWorldTransform(m_entityId1);
const float screenToWorldMultiplier = AzToolsFramework::CalculateScreenToWorldMultiplier(
AzToolsFramework::GetWorldTransform(m_entityId1).GetTranslation(), m_cameraState);
// calculate positions for two click and drag motions (moving a linear manipulator)
// begin each click in the center of the line of the linear manipulators
const auto translationManipulatorStartHoldWorldPosition1 =
AzToolsFramework::GetWorldTransform(m_entityId1).GetTranslation() + entity1Transform.GetBasisZ() * screenToWorldMultiplier;
const auto translationManipulatorEndHoldWorldPosition1 =
translationManipulatorStartHoldWorldPosition1 + AZ::Vector3::CreateAxisZ(LinearManipulatorZAxisMovement);
const auto translationManipulatorStartHoldWorldPosition2 = AzToolsFramework::GetWorldTransform(m_entityId1).GetTranslation() +
AZ::Vector3::CreateAxisZ(LinearManipulatorZAxisMovement) - entity1Transform.GetBasisY() * screenToWorldMultiplier;
const auto translationManipulatorEndHoldWorldPosition2 =
translationManipulatorStartHoldWorldPosition2 + AZ::Vector3::CreateAxisY(LinearManipulatorYAxisMovement);
// transform to screen space
const auto translationManipulatorStartHoldScreenPosition1 =
AzFramework::WorldToScreen(translationManipulatorStartHoldWorldPosition1, m_cameraState);
const auto translationManipulatorEndHoldScreenPosition1 =
AzFramework::WorldToScreen(translationManipulatorEndHoldWorldPosition1, m_cameraState);
const auto translationManipulatorStartHoldScreenPosition2 =
AzFramework::WorldToScreen(translationManipulatorStartHoldWorldPosition2, m_cameraState);
const auto translationManipulatorEndHoldScreenPosition2 =
AzFramework::WorldToScreen(translationManipulatorEndHoldWorldPosition2, m_cameraState);
m_actionDispatcher->CameraState(m_cameraState)
->MousePosition(translationManipulatorStartHoldScreenPosition1)
->KeyboardModifierDown(GetParam().m_keyboardModifier)
->MouseLButtonDown()
->MousePosition(translationManipulatorEndHoldScreenPosition1)
->MouseLButtonUp()
->MousePosition(translationManipulatorStartHoldScreenPosition2)
->MouseLButtonDown()
->MousePosition(translationManipulatorEndHoldScreenPosition2)
->MouseLButtonUp();
const auto expectedEntityTransform = GetParam().m_expectedEntityTransformAfter;
const auto expectedManipulatorTransform = GetParam().m_expectedManipulatorTransformAfter;
const auto manipulatorTransform = GetManipulatorTransform();
const auto entityTransform = AzToolsFramework::GetWorldTransform(m_entityId1);
EXPECT_THAT(*manipulatorTransform, IsCloseTolerance(expectedManipulatorTransform, 0.01f));
EXPECT_THAT(entityTransform, IsCloseTolerance(expectedEntityTransform, 0.01f));
}
static const AZ::Transform ExpectedTransformAfterLocalTranslationManipulatorMotion = AZ::Transform::CreateTranslation(
EditorTransformComponentSelectionViewportPickingFixture::Entity1WorldTranslation +
AZ::Vector3(0.0f, LinearManipulatorYAxisMovement, LinearManipulatorZAxisMovement));
// where the manipulator should end up after the input from TranslatingMultipleEntitiesWithDifferentModifierCombinations
static const AZ::Transform ExpectedManipulatorTransformAfterGroupTranslationManipulatorMotion = AZ::Transform::CreateTranslation(
AggregateManipulatorPositionWithEntity2and3Selected +
AZ::Vector3(0.0f, LinearManipulatorYAxisMovement, LinearManipulatorZAxisMovement));
INSTANTIATE_TEST_CASE_P(
All,
EditorTransformComponentSelectionTranslationManipulatorSingleEntityTestFixtureParam,
testing::Values(
// this replicates translating an entity in local space with no modifiers held
// manipulator and entity translate
ManipulatorOptionsSingle{ AzToolsFramework::ViewportInteraction::KeyboardModifier::None,
ExpectedTransformAfterLocalTranslationManipulatorMotion,
ExpectedTransformAfterLocalTranslationManipulatorMotion },
// this replicates translating an entity in local space with the alt modifier held
// manipulator and entity translate (to the user, equivalent to no modifiers with one entity selected)
ManipulatorOptionsSingle{ AzToolsFramework::ViewportInteraction::KeyboardModifier::Alt,
ExpectedTransformAfterLocalTranslationManipulatorMotion,
ExpectedTransformAfterLocalTranslationManipulatorMotion },
// this replicates translating an entity in world space with the shift modifier held
// manipulator and entity translate
ManipulatorOptionsSingle{ AzToolsFramework::ViewportInteraction::KeyboardModifier::Shift,
ExpectedTransformAfterLocalTranslationManipulatorMotion,
ExpectedTransformAfterLocalTranslationManipulatorMotion },
// this replicates translating the manipulator in local space with the ctrl modifier held
// entity is unchanged, manipulator moves
ManipulatorOptionsSingle{
AzToolsFramework::ViewportInteraction::KeyboardModifier::Ctrl, ExpectedTransformAfterLocalTranslationManipulatorMotion,
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity1WorldTranslation) }));
class EditorTransformComponentSelectionTranslationManipulatorMultipleEntityTestFixtureParam
: public EditorTransformComponentSelectionManipulatorInteractionTestFixture
, public ::testing::WithParamInterface<ManipulatorOptionsMultiple>
{
};
static const AZ::Transform Entity2RotationForLocalTranslation =
AZ::Transform::CreateFromQuaternion(AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)));
TEST_P(
EditorTransformComponentSelectionTranslationManipulatorMultipleEntityTestFixtureParam,
TranslatingMultipleEntitiesWithDifferentModifierCombinations)
{
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
PositionEntities();
// move camera up and to the left so it's just above the normal row of entities
AzFramework::SetCameraTransform(
m_cameraState,
AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), AZ::Vector3(10.0f, 14.5, 11.0f)));
SetTransformMode(EditorTransformComponentSelectionRequestBus::Events::Mode::Translation);
// give entity 2 a different orientation to entity 3 so when moving in local space their translation vectors will be different
AZ::TransformBus::Event(
m_entityId2, &AZ::TransformBus::Events::SetWorldRotationQuaternion, Entity2RotationForLocalTranslation.GetRotation());
AzToolsFramework::SelectEntities({ m_entityId2, m_entityId3 });
const auto initialManipulatorTransform = GetManipulatorTransform();
const float screenToWorldMultiplier = AzToolsFramework::CalculateScreenToWorldMultiplier(
AzToolsFramework::GetWorldTransform(m_entityId1).GetTranslation(), m_cameraState);
// calculate positions for two click and drag motions (moving a linear manipulator)
// begin each click in the center of the line of the linear manipulators
const auto translationManipulatorStartHoldWorldPosition1 = AzToolsFramework::GetWorldTransform(m_entityId1).GetTranslation() +
initialManipulatorTransform->GetBasisZ() * screenToWorldMultiplier;
const auto translationManipulatorEndHoldWorldPosition1 =
translationManipulatorStartHoldWorldPosition1 + AZ::Vector3::CreateAxisZ(LinearManipulatorZAxisMovement);
const auto translationManipulatorStartHoldWorldPosition2 = AzToolsFramework::GetWorldTransform(m_entityId1).GetTranslation() +
AZ::Vector3::CreateAxisZ(LinearManipulatorZAxisMovement) - initialManipulatorTransform->GetBasisY() * screenToWorldMultiplier;
const auto translationManipulatorEndHoldWorldPosition2 =
translationManipulatorStartHoldWorldPosition2 + AZ::Vector3::CreateAxisY(LinearManipulatorYAxisMovement);
// transform to screen space
const auto translationManipulatorStartHoldScreenPosition1 =
AzFramework::WorldToScreen(translationManipulatorStartHoldWorldPosition1, m_cameraState);
const auto translationManipulatorEndHoldScreenPosition1 =
AzFramework::WorldToScreen(translationManipulatorEndHoldWorldPosition1, m_cameraState);
const auto translationManipulatorStartHoldScreenPosition2 =
AzFramework::WorldToScreen(translationManipulatorStartHoldWorldPosition2, m_cameraState);
const auto translationManipulatorEndHoldScreenPosition2 =
AzFramework::WorldToScreen(translationManipulatorEndHoldWorldPosition2, m_cameraState);
m_actionDispatcher->CameraState(m_cameraState)
->MousePosition(translationManipulatorStartHoldScreenPosition1)
->KeyboardModifierDown(GetParam().m_keyboardModifier)
->MouseLButtonDown()
->MousePosition(translationManipulatorEndHoldScreenPosition1)
->MouseLButtonUp()
->MousePosition(translationManipulatorStartHoldScreenPosition2)
->MouseLButtonDown()
->MousePosition(translationManipulatorEndHoldScreenPosition2)
->MouseLButtonUp();
const auto expectedEntity2Transform = GetParam().m_firstExpectedEntityTransformAfter;
const auto expectedEntity3Transform = GetParam().m_secondExpectedEntityTransformAfter;
const auto expectedManipulatorTransform = GetParam().m_expectedManipulatorTransformAfter;
const auto manipulatorTransformAfter = GetManipulatorTransform();
const auto entity2Transform = AzToolsFramework::GetWorldTransform(m_entityId2);
const auto entity3Transform = AzToolsFramework::GetWorldTransform(m_entityId3);
EXPECT_THAT(*manipulatorTransformAfter, IsCloseTolerance(expectedManipulatorTransform, 0.01f));
EXPECT_THAT(entity2Transform, IsCloseTolerance(expectedEntity2Transform, 0.01f));
EXPECT_THAT(entity3Transform, IsCloseTolerance(expectedEntity3Transform, 0.01f));
}
static const AZ::Transform ExpectedEntity2TransformAfterLocalGroupTranslationManipulatorMotion =
AZ::Transform::CreateTranslation(
EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation +
AZ::Vector3(0.0f, LinearManipulatorYAxisMovement, LinearManipulatorZAxisMovement)) *
Entity2RotationForLocalTranslation;
static const AZ::Transform ExpectedEntity3TransformAfterLocalGroupTranslationManipulatorMotion = AZ::Transform::CreateTranslation(
EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation +
AZ::Vector3(0.0f, LinearManipulatorYAxisMovement, LinearManipulatorZAxisMovement));
// note: as entity has been rotated by 90 degrees about Z in TranslatingMultipleEntitiesWithDifferentModifierCombinations then
// LinearManipulatorYAxisMovement is now aligned to the world x-axis
static const AZ::Transform ExpectedEntity2TransformAfterLocalIndividualTranslationManipulatorMotion =
AZ::Transform::CreateTranslation(
EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation +
AZ::Vector3(-LinearManipulatorYAxisMovement, 0.0f, LinearManipulatorZAxisMovement)) *
Entity2RotationForLocalTranslation;
static const AZ::Transform ExpectedEntity3TransformAfterLocalIndividualTranslationManipulatorMotion = AZ::Transform::CreateTranslation(
EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation +
AZ::Vector3(0.0f, LinearManipulatorYAxisMovement, LinearManipulatorZAxisMovement));
INSTANTIATE_TEST_CASE_P(
All,
EditorTransformComponentSelectionTranslationManipulatorMultipleEntityTestFixtureParam,
testing::Values(
// this replicates translating a group of entities in local space with no modifiers held (group influence)
// manipulator and entity translate
ManipulatorOptionsMultiple{ AzToolsFramework::ViewportInteraction::KeyboardModifier::None,
ExpectedManipulatorTransformAfterGroupTranslationManipulatorMotion,
ExpectedEntity2TransformAfterLocalGroupTranslationManipulatorMotion,
ExpectedEntity3TransformAfterLocalGroupTranslationManipulatorMotion },
// this replicates translating a group of entities in local space with the alt modifier held
// entities move in their own local space (individual influence)
ManipulatorOptionsMultiple{ AzToolsFramework::ViewportInteraction::KeyboardModifier::Alt,
ExpectedManipulatorTransformAfterGroupTranslationManipulatorMotion,
ExpectedEntity2TransformAfterLocalIndividualTranslationManipulatorMotion,
ExpectedEntity3TransformAfterLocalIndividualTranslationManipulatorMotion },
// this replicates translating a group of entities in world space with the shift modifier held
// entities and manipulator move in world space
ManipulatorOptionsMultiple{ AzToolsFramework::ViewportInteraction::KeyboardModifier::Shift,
ExpectedManipulatorTransformAfterGroupTranslationManipulatorMotion,
ExpectedEntity2TransformAfterLocalGroupTranslationManipulatorMotion,
ExpectedEntity3TransformAfterLocalGroupTranslationManipulatorMotion },
// this replicates translating the manipulator in local space with the ctrl modifier held (entities are unchanged)
ManipulatorOptionsMultiple{
AzToolsFramework::ViewportInteraction::KeyboardModifier::Ctrl,
ExpectedManipulatorTransformAfterGroupTranslationManipulatorMotion,
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation) *
Entity2RotationForLocalTranslation,
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation) }));
class EditorTransformComponentSelectionScaleManipulatorMultipleEntityTestFixtureParam
: public EditorTransformComponentSelectionManipulatorInteractionTestFixture
, public ::testing::WithParamInterface<ManipulatorOptionsMultiple>
{
};
static const float LinearManipulatorZAxisMovementScale = 0.5f;
TEST_P(
EditorTransformComponentSelectionScaleManipulatorMultipleEntityTestFixtureParam,
ScalingMultipleEntitiesWithDifferentModifierCombinations)
{
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
PositionEntities();
// move camera up and to the left so it's just above the normal row of entities
AzFramework::SetCameraTransform(
m_cameraState,
AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), AZ::Vector3(10.0f, 15.0f, 10.1f)));
SetTransformMode(EditorTransformComponentSelectionRequestBus::Events::Mode::Scale);
AzToolsFramework::SelectEntities({ m_entityId2, m_entityId3 });
// manipulator should be centered between the two entities
const auto initialManipulatorTransform = GetManipulatorTransform();
const float screenToWorldMultiplier =
AzToolsFramework::CalculateScreenToWorldMultiplier(initialManipulatorTransform->GetTranslation(), m_cameraState);
const auto translationManipulatorStartHoldWorldPosition1 = AzToolsFramework::GetWorldTransform(m_entityId1).GetTranslation() +
initialManipulatorTransform->GetBasisZ() * screenToWorldMultiplier;
const auto translationManipulatorEndHoldWorldPosition1 =
translationManipulatorStartHoldWorldPosition1 + AZ::Vector3::CreateAxisZ(LinearManipulatorZAxisMovementScale);
// calculate screen space positions
const auto scaleManipulatorHoldScreenPosition =
AzFramework::WorldToScreen(translationManipulatorStartHoldWorldPosition1, m_cameraState);
const auto scaleManipulatorEndHoldScreenPosition =
AzFramework::WorldToScreen(translationManipulatorEndHoldWorldPosition1, m_cameraState);
m_actionDispatcher->CameraState(m_cameraState)
->MousePosition(scaleManipulatorHoldScreenPosition)
->KeyboardModifierDown(GetParam().m_keyboardModifier)
->MouseLButtonDown()
->MousePosition(scaleManipulatorEndHoldScreenPosition)
->MouseLButtonUp();
const auto expectedEntity2Transform = GetParam().m_firstExpectedEntityTransformAfter;
const auto expectedEntity3Transform = GetParam().m_secondExpectedEntityTransformAfter;
const auto expectedManipulatorTransform = GetParam().m_expectedManipulatorTransformAfter;
const auto manipulatorTransformAfter = GetManipulatorTransform();
const auto entity2Transform = AzToolsFramework::GetWorldTransform(m_entityId2);
const auto entity3Transform = AzToolsFramework::GetWorldTransform(m_entityId3);
EXPECT_THAT(*manipulatorTransformAfter, IsCloseTolerance(expectedManipulatorTransform, 0.01f));
EXPECT_THAT(entity2Transform, IsCloseTolerance(expectedEntity2Transform, 0.01f));
EXPECT_THAT(entity3Transform, IsCloseTolerance(expectedEntity3Transform, 0.01f));
}
static const AZ::Transform ExpectedEntity2TransformAfterLocalGroupScaleManipulatorMotion =
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation) *
AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, -1.0f, 0.0f)) *
AZ::Transform::CreateUniformScale(LinearManipulatorZAxisMovement);
static const AZ::Transform ExpectedEntity3TransformAfterLocalGroupScaleManipulatorMotion =
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation) *
AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, 1.0f, 0.0f)) * AZ::Transform::CreateUniformScale(LinearManipulatorZAxisMovement);
static const AZ::Transform ExpectedEntity2TransformAfterLocalIndividualScaleManipulatorMotion =
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation) *
AZ::Transform::CreateUniformScale(LinearManipulatorZAxisMovement);
static const AZ::Transform ExpectedEntity3TransformAfterLocalIndividualScaleManipulatorMotion =
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation) *
AZ::Transform::CreateUniformScale(LinearManipulatorZAxisMovement);
INSTANTIATE_TEST_CASE_P(
All,
EditorTransformComponentSelectionScaleManipulatorMultipleEntityTestFixtureParam,
testing::Values(
// this replicates scaling a group of entities in local space with no modifiers held
// entities scale relative to manipulator pivot
ManipulatorOptionsMultiple{ AzToolsFramework::ViewportInteraction::KeyboardModifier::None,
AZ::Transform::CreateTranslation(AggregateManipulatorPositionWithEntity2and3Selected),
ExpectedEntity2TransformAfterLocalGroupScaleManipulatorMotion,
ExpectedEntity3TransformAfterLocalGroupScaleManipulatorMotion },
// this replicates scaling a group of entities in local space with the alt modifier held
// entities scale about their own pivot
ManipulatorOptionsMultiple{ AzToolsFramework::ViewportInteraction::KeyboardModifier::Alt,
AZ::Transform::CreateTranslation(AggregateManipulatorPositionWithEntity2and3Selected),
ExpectedEntity2TransformAfterLocalIndividualScaleManipulatorMotion,
ExpectedEntity3TransformAfterLocalIndividualScaleManipulatorMotion },
// this replicates scaling a group of entities in world space with the shift modifier held
// entities scale relative to manipulator pivot in world space
ManipulatorOptionsMultiple{ AzToolsFramework::ViewportInteraction::KeyboardModifier::Shift,
AZ::Transform::CreateTranslation(AggregateManipulatorPositionWithEntity2and3Selected),
ExpectedEntity2TransformAfterLocalGroupScaleManipulatorMotion,
ExpectedEntity3TransformAfterLocalGroupScaleManipulatorMotion },
// this has no effect (entities and manipulator are unchanged)
ManipulatorOptionsMultiple{
AzToolsFramework::ViewportInteraction::KeyboardModifier::Ctrl,
AZ::Transform::CreateTranslation(AggregateManipulatorPositionWithEntity2and3Selected),
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation),
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation) }));
using EditorTransformComponentSelectionManipulatorTestFixture = using EditorTransformComponentSelectionManipulatorTestFixture =
IndirectCallManipulatorViewportInteractionFixtureMixin<EditorTransformComponentSelectionFixture>; IndirectCallManipulatorViewportInteractionFixtureMixin<EditorTransformComponentSelectionFixture>;
@ -1661,7 +2211,7 @@ namespace UnitTest
All, All,
EditorTransformComponentSelectionSingleEntityPivotAndOverrideFixture, EditorTransformComponentSelectionSingleEntityPivotAndOverrideFixture,
testing::Values( testing::Values(
ReferenceFrameWithOrientation{ AzToolsFramework::ReferenceFrame::Local, ChildExpectedPivotLocalOrientationInWorldSpace }, ReferenceFrameWithOrientation{ AzToolsFramework::ReferenceFrame::Local, PivotOverrideLocalOrientationInWorldSpace },
ReferenceFrameWithOrientation{ AzToolsFramework::ReferenceFrame::Parent, PivotOverrideLocalOrientationInWorldSpace }, ReferenceFrameWithOrientation{ AzToolsFramework::ReferenceFrame::Parent, PivotOverrideLocalOrientationInWorldSpace },
ReferenceFrameWithOrientation{ AzToolsFramework::ReferenceFrame::World, AZ::Quaternion::CreateIdentity() })); ReferenceFrameWithOrientation{ AzToolsFramework::ReferenceFrame::World, AZ::Quaternion::CreateIdentity() }));

@ -6,8 +6,8 @@
* *
*/ */
#include <AzCore/Math/Matrix3x4.h>
#include <AzCore/Math/Matrix3x3.h> #include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Matrix3x4.h>
#include <AzCore/Math/Matrix4x4.h> #include <AzCore/Math/Matrix4x4.h>
#include <AzCore/Math/Transform.h> #include <AzCore/Math/Transform.h>
#include <AzCore/Math/VectorConversions.h> #include <AzCore/Math/VectorConversions.h>
@ -21,17 +21,16 @@
namespace UnitTest namespace UnitTest
{ {
// transform a point from normalized device coordinates to world space, and then from world space back to normalized device coordinates // transform a point from normalized device coordinates to world space, and then from world space back to normalized device coordinates
AZ::Vector2 ScreenNDCToWorldToScreenNDC( AZ::Vector2 ScreenNdcToWorldToScreenNdc(const AZ::Vector2& ndcPoint, const AzFramework::CameraState& cameraState)
const AZ::Vector2& ndcPoint, const AzFramework::CameraState& cameraState)
{ {
const auto worldResult = AzFramework::ScreenNDCToWorld(ndcPoint, InverseCameraView(cameraState), InverseCameraProjection(cameraState)); const auto worldResult =
const auto ndcResult = AzFramework::WorldToScreenNDC(worldResult, CameraView(cameraState), CameraProjection(cameraState)); AzFramework::ScreenNdcToWorld(ndcPoint, InverseCameraView(cameraState), InverseCameraProjection(cameraState));
const auto ndcResult = AzFramework::WorldToScreenNdc(worldResult, CameraView(cameraState), CameraProjection(cameraState));
return AZ::Vector3ToVector2(ndcResult); return AZ::Vector3ToVector2(ndcResult);
} }
// transform a point from screen space to world space, and then from world space back to screen space // transform a point from screen space to world space, and then from world space back to screen space
AzFramework::ScreenPoint ScreenToWorldToScreen( AzFramework::ScreenPoint ScreenToWorldToScreen(const AzFramework::ScreenPoint& screenPoint, const AzFramework::CameraState& cameraState)
const AzFramework::ScreenPoint& screenPoint, const AzFramework::CameraState& cameraState)
{ {
const auto worldResult = AzFramework::ScreenToWorld(screenPoint, cameraState); const auto worldResult = AzFramework::ScreenToWorld(screenPoint, cameraState);
return AzFramework::WorldToScreen(worldResult, cameraState); return AzFramework::WorldToScreen(worldResult, cameraState);
@ -47,25 +46,25 @@ namespace UnitTest
const auto cameraState = AzFramework::CreateIdentityDefaultCamera(cameraPosition, screenDimensions); const auto cameraState = AzFramework::CreateIdentityDefaultCamera(cameraPosition, screenDimensions);
{ {
const auto expectedScreenPoint = ScreenPoint{600, 450}; const auto expectedScreenPoint = ScreenPoint{ 600, 450 };
const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, cameraState); const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, cameraState);
EXPECT_EQ(resultScreenPoint, expectedScreenPoint); EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
} }
{ {
const auto expectedScreenPoint = ScreenPoint{400, 300}; const auto expectedScreenPoint = ScreenPoint{ 400, 300 };
const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, cameraState); const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, cameraState);
EXPECT_EQ(resultScreenPoint, expectedScreenPoint); EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
} }
{ {
const auto expectedScreenPoint = ScreenPoint{0, 0}; const auto expectedScreenPoint = ScreenPoint{ 0, 0 };
const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, cameraState); const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, cameraState);
EXPECT_EQ(resultScreenPoint, expectedScreenPoint); EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
} }
{ {
const auto expectedScreenPoint = ScreenPoint{800, 600}; const auto expectedScreenPoint = ScreenPoint{ 800, 600 };
const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, cameraState); const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, cameraState);
EXPECT_EQ(resultScreenPoint, expectedScreenPoint); EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
} }
@ -81,7 +80,7 @@ namespace UnitTest
const auto cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions); const auto cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions);
const auto expectedScreenPoint = ScreenPoint{200, 300}; const auto expectedScreenPoint = ScreenPoint{ 200, 300 };
const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, cameraState); const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, cameraState);
EXPECT_EQ(resultScreenPoint, expectedScreenPoint); EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
} }
@ -93,12 +92,12 @@ namespace UnitTest
using AzFramework::ScreenPoint; using AzFramework::ScreenPoint;
const auto screenDimensions = AZ::Vector2(800.0f, 600.0f); const auto screenDimensions = AZ::Vector2(800.0f, 600.0f);
const auto cameraTransform = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 0.0f, 0.0f)) * const auto cameraTransform =
AZ::Transform::CreateRotationZ(AZ::DegToRad(-90.0f)); AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 0.0f, 0.0f)) * AZ::Transform::CreateRotationZ(AZ::DegToRad(-90.0f));
const auto cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions); const auto cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions);
const auto worldResult = AzFramework::ScreenToWorld(ScreenPoint{400, 300}, cameraState); const auto worldResult = AzFramework::ScreenToWorld(ScreenPoint{ 400, 300 }, cameraState);
EXPECT_THAT(worldResult, IsClose(AZ::Vector3(10.1f, 0.0f, 0.0f))); EXPECT_THAT(worldResult, IsClose(AZ::Vector3(10.1f, 0.0f, 0.0f)));
} }
@ -113,26 +112,26 @@ namespace UnitTest
const auto cameraState = AzFramework::CreateIdentityDefaultCamera(cameraPosition, screenDimensions); const auto cameraState = AzFramework::CreateIdentityDefaultCamera(cameraPosition, screenDimensions);
{ {
const auto expectedNdcPoint = NdcPoint{0.75f, 0.75f}; const auto expectedNdcPoint = NdcPoint{ 0.75f, 0.75f };
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState); const auto resultNdcPoint = ScreenNdcToWorldToScreenNdc(expectedNdcPoint, cameraState);
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint)); EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
} }
{ {
const auto expectedNdcPoint = NdcPoint{0.5f, 0.5f}; const auto expectedNdcPoint = NdcPoint{ 0.5f, 0.5f };
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState); const auto resultNdcPoint = ScreenNdcToWorldToScreenNdc(expectedNdcPoint, cameraState);
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint)); EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
} }
{ {
const auto expectedNdcPoint = NdcPoint{0.0f, 0.0f}; const auto expectedNdcPoint = NdcPoint{ 0.0f, 0.0f };
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState); const auto resultNdcPoint = ScreenNdcToWorldToScreenNdc(expectedNdcPoint, cameraState);
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint)); EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
} }
{ {
const auto expectedNdcPoint = NdcPoint{1.0f, 1.0f}; const auto expectedNdcPoint = NdcPoint{ 1.0f, 1.0f };
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState); const auto resultNdcPoint = ScreenNdcToWorldToScreenNdc(expectedNdcPoint, cameraState);
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint)); EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
} }
} }
@ -147,8 +146,8 @@ namespace UnitTest
const auto cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions); const auto cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions);
const auto expectedNdcPoint = NdcPoint{0.25f, 0.5f}; const auto expectedNdcPoint = NdcPoint{ 0.25f, 0.5f };
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState); const auto resultNdcPoint = ScreenNdcToWorldToScreenNdc(expectedNdcPoint, cameraState);
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint)); EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
} }
@ -159,12 +158,13 @@ namespace UnitTest
using NdcPoint = AZ::Vector2; using NdcPoint = AZ::Vector2;
const auto screenDimensions = AZ::Vector2(800.0f, 600.0f); const auto screenDimensions = AZ::Vector2(800.0f, 600.0f);
const auto cameraTransform = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 0.0f, 0.0f)) * const auto cameraTransform =
AZ::Transform::CreateRotationZ(AZ::DegToRad(-90.0f)); AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 0.0f, 0.0f)) * AZ::Transform::CreateRotationZ(AZ::DegToRad(-90.0f));
const auto cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions); const auto cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions);
const auto worldResult = AzFramework::ScreenNDCToWorld(NdcPoint{0.5f, 0.5f}, InverseCameraView(cameraState), InverseCameraProjection(cameraState)); const auto worldResult =
AzFramework::ScreenNdcToWorld(NdcPoint{ 0.5f, 0.5f }, InverseCameraView(cameraState), InverseCameraProjection(cameraState));
EXPECT_THAT(worldResult, IsClose(AZ::Vector3(10.1f, 0.0f, 0.0f))); EXPECT_THAT(worldResult, IsClose(AZ::Vector3(10.1f, 0.0f, 0.0f)));
} }
@ -175,7 +175,7 @@ namespace UnitTest
using AzFramework::ScreenPoint; using AzFramework::ScreenPoint;
using AzFramework::ScreenVector; using AzFramework::ScreenVector;
const ScreenVector screenVector = ScreenPoint{100, 200} - ScreenPoint{10, 20}; const ScreenVector screenVector = ScreenPoint{ 100, 200 } - ScreenPoint{ 10, 20 };
EXPECT_EQ(screenVector, ScreenVector(90, 180)); EXPECT_EQ(screenVector, ScreenVector(90, 180));
} }
@ -184,7 +184,7 @@ namespace UnitTest
using AzFramework::ScreenPoint; using AzFramework::ScreenPoint;
using AzFramework::ScreenVector; using AzFramework::ScreenVector;
const ScreenPoint screenPoint = ScreenPoint{100, 200} + ScreenVector{50, 25}; const ScreenPoint screenPoint = ScreenPoint{ 100, 200 } + ScreenVector{ 50, 25 };
EXPECT_EQ(screenPoint, ScreenPoint(150, 225)); EXPECT_EQ(screenPoint, ScreenPoint(150, 225));
} }
@ -193,7 +193,7 @@ namespace UnitTest
using AzFramework::ScreenPoint; using AzFramework::ScreenPoint;
using AzFramework::ScreenVector; using AzFramework::ScreenVector;
const ScreenPoint screenPoint = ScreenPoint{120, 200} - ScreenVector{50, 20}; const ScreenPoint screenPoint = ScreenPoint{ 120, 200 } - ScreenVector{ 50, 20 };
EXPECT_EQ(screenPoint, ScreenPoint(70, 180)); EXPECT_EQ(screenPoint, ScreenPoint(70, 180));
} }
@ -202,7 +202,7 @@ namespace UnitTest
using AzFramework::ScreenPoint; using AzFramework::ScreenPoint;
using AzFramework::ScreenVector; using AzFramework::ScreenVector;
const ScreenVector screenVector = ScreenVector{100, 200} + ScreenVector{50, 25}; const ScreenVector screenVector = ScreenVector{ 100, 200 } + ScreenVector{ 50, 25 };
EXPECT_EQ(screenVector, ScreenVector(150, 225)); EXPECT_EQ(screenVector, ScreenVector(150, 225));
} }
@ -211,7 +211,7 @@ namespace UnitTest
using AzFramework::ScreenPoint; using AzFramework::ScreenPoint;
using AzFramework::ScreenVector; using AzFramework::ScreenVector;
const ScreenVector screenVector = ScreenVector{100, 200} - ScreenVector{50, 25}; const ScreenVector screenVector = ScreenVector{ 100, 200 } - ScreenVector{ 50, 25 };
EXPECT_EQ(screenVector, ScreenVector(50, 175)); EXPECT_EQ(screenVector, ScreenVector(50, 175));
} }
@ -220,8 +220,8 @@ namespace UnitTest
using AzFramework::ScreenPoint; using AzFramework::ScreenPoint;
using AzFramework::ScreenVector; using AzFramework::ScreenVector;
const ScreenPoint screenPoint = ScreenPoint{100, 200}; const ScreenPoint screenPoint = ScreenPoint{ 100, 200 };
const ScreenVector screenVector = ScreenVector{50, 25}; const ScreenVector screenVector = ScreenVector{ 50, 25 };
const AZ::Vector2 fromScreenPoint = AzFramework::Vector2FromScreenPoint(screenPoint); const AZ::Vector2 fromScreenPoint = AzFramework::Vector2FromScreenPoint(screenPoint);
const AZ::Vector2 fromScreenVector = AzFramework::Vector2FromScreenVector(screenVector); const AZ::Vector2 fromScreenVector = AzFramework::Vector2FromScreenVector(screenVector);
@ -295,6 +295,58 @@ namespace UnitTest
EXPECT_NEAR(AzFramework::ScreenVectorLength(ScreenVector(12, 15)), 19.20937f, 0.001f); EXPECT_NEAR(AzFramework::ScreenVectorLength(ScreenVector(12, 15)), 19.20937f, 0.001f);
} }
TEST(ViewportScreen, ScreenVectorTransformedByScalarUpwards)
{
using AzFramework::ScreenVector;
auto screenVector = ScreenVector(5, 10);
auto scaledScreenVector = screenVector * 2.0f;
EXPECT_EQ(scaledScreenVector, ScreenVector(10, 20));
}
TEST(ViewportScreen, ScreenVectorTransformedByScalarWithRounding)
{
using AzFramework::ScreenVector;
auto screenVector = ScreenVector(1, 6);
auto scaledScreenVector = screenVector * 0.1f;
// value less than 0.5 rounds down, greater than or equal to 0.5 rounds up
EXPECT_EQ(scaledScreenVector, ScreenVector(0, 1));
}
TEST(ViewportScreen, ScreenVectorTransformedByScalarWithRoundingAtHalfwayBoundary)
{
using AzFramework::ScreenVector;
auto screenVector = ScreenVector(5, 10);
auto scaledScreenVector = screenVector * 0.1f;
// value less than 0.5 rounds down, greater than or equal to 0.5 rounds up
EXPECT_EQ(scaledScreenVector, ScreenVector(1, 1));
}
TEST(ViewportScreen, ScreenVectorTransformedByScalarDownwards)
{
using AzFramework::ScreenVector;
auto screenVector = ScreenVector(6, 12);
auto scaledScreenVector = screenVector * 0.5f;
EXPECT_EQ(scaledScreenVector, ScreenVector(3, 6));
}
TEST(ViewportScreen, ScreenVectorTransformedByScalarInplace)
{
using AzFramework::ScreenVector;
auto screenVector = ScreenVector(13, 37);
screenVector *= 10.0f;
EXPECT_EQ(screenVector, ScreenVector(130, 370));
}
//////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////
// Other tests // Other tests
TEST(ViewportScreen, CanGetCameraTransformFromCameraViewAndBack) TEST(ViewportScreen, CanGetCameraTransformFromCameraViewAndBack)

@ -1726,7 +1726,7 @@ void AZ::FFont::DrawScreenAlignedText3d(
{ {
return; return;
} }
AZ::Vector3 positionNDC = AzFramework::WorldToScreenNDC( AZ::Vector3 positionNDC = AzFramework::WorldToScreenNdc(
params.m_position, params.m_position,
currentView->GetWorldToViewMatrix(), currentView->GetWorldToViewMatrix(),
currentView->GetViewToClipMatrix() currentView->GetViewToClipMatrix()

@ -676,7 +676,6 @@ namespace PhysX
LmbrCentral::ShapeComponentNotificationsBus::Handler::BusDisconnect(); LmbrCentral::ShapeComponentNotificationsBus::Handler::BusDisconnect();
AZ::TransformNotificationBus::Handler::BusDisconnect(); AZ::TransformNotificationBus::Handler::BusDisconnect();
AzToolsFramework::EntitySelectionEvents::Bus::Handler::BusDisconnect(); AzToolsFramework::EntitySelectionEvents::Bus::Handler::BusDisconnect();
AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect();
AzToolsFramework::Components::EditorComponentBase::Deactivate(); AzToolsFramework::Components::EditorComponentBase::Deactivate();
if (m_sceneInterface && m_editorBodyHandle != AzPhysics::InvalidSimulatedBodyHandle) if (m_sceneInterface && m_editorBodyHandle != AzPhysics::InvalidSimulatedBodyHandle)

@ -10,7 +10,6 @@
#include <AzCore/Component/TransformBus.h> #include <AzCore/Component/TransformBus.h>
#include <AzCore/Component/NonUniformScaleBus.h> #include <AzCore/Component/NonUniformScaleBus.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <AzFramework/Physics/Shape.h> #include <AzFramework/Physics/Shape.h>
#include <AzFramework/Physics/Components/SimulatedBodyComponentBus.h> #include <AzFramework/Physics/Components/SimulatedBodyComponentBus.h>
#include <AzFramework/Physics/Common/PhysicsEvents.h> #include <AzFramework/Physics/Common/PhysicsEvents.h>
@ -58,7 +57,6 @@ namespace PhysX
//! component to create geometry in the PhysX simulation. //! component to create geometry in the PhysX simulation.
class EditorShapeColliderComponent class EditorShapeColliderComponent
: public AzToolsFramework::Components::EditorComponentBase : public AzToolsFramework::Components::EditorComponentBase
, protected AzFramework::EntityDebugDisplayEventBus::Handler
, protected AzToolsFramework::EntitySelectionEvents::Bus::Handler , protected AzToolsFramework::EntitySelectionEvents::Bus::Handler
, private AZ::TransformNotificationBus::Handler , private AZ::TransformNotificationBus::Handler
, protected DebugDraw::DisplayCallback , protected DebugDraw::DisplayCallback

@ -83,7 +83,8 @@
"CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4",
"CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_LY_PROJECTS": "AutomatedTesting",
"CMAKE_TARGET": "all", "CMAKE_TARGET": "all",
"CTEST_OPTIONS": "-E Gem::EMotionFX.Editor.Tests -LE (SUITE_sandbox|SUITE_awsi) -L FRAMEWORK_googletest" "CTEST_OPTIONS": "-E Gem::EMotionFX.Editor.Tests -LE (SUITE_sandbox|SUITE_awsi) -L FRAMEWORK_googletest",
"TEST_RESULTS": "True"
} }
}, },
"test_profile_nounity": { "test_profile_nounity": {
@ -95,7 +96,8 @@
"CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4",
"CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_LY_PROJECTS": "AutomatedTesting",
"CMAKE_TARGET": "all", "CMAKE_TARGET": "all",
"CTEST_OPTIONS": "-E Gem::EMotionFX.Editor.Tests -LE (SUITE_sandbox|SUITE_awsi) -L FRAMEWORK_googletest" "CTEST_OPTIONS": "-E Gem::EMotionFX.Editor.Tests -LE (SUITE_sandbox|SUITE_awsi) -L FRAMEWORK_googletest",
"TEST_RESULTS": "True"
} }
}, },
"asset_profile": { "asset_profile": {
@ -143,7 +145,8 @@
"CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4",
"CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_LY_PROJECTS": "AutomatedTesting",
"CMAKE_TARGET": "TEST_SUITE_periodic", "CMAKE_TARGET": "TEST_SUITE_periodic",
"CTEST_OPTIONS": "-L (SUITE_periodic)" "CTEST_OPTIONS": "-L (SUITE_periodic)",
"TEST_RESULTS": "True"
} }
}, },
"sandbox_test_profile": { "sandbox_test_profile": {
@ -178,7 +181,8 @@
"CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4",
"CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_LY_PROJECTS": "AutomatedTesting",
"CMAKE_TARGET": "TEST_SUITE_benchmark", "CMAKE_TARGET": "TEST_SUITE_benchmark",
"CTEST_OPTIONS": "-L (SUITE_benchmark)" "CTEST_OPTIONS": "-L (SUITE_benchmark)",
"TEST_RESULTS": "True"
} }
}, },
"release": { "release": {

@ -102,7 +102,8 @@
"CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE", "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_LY_PROJECTS": "AutomatedTesting",
"CMAKE_TARGET": "TEST_SUITE_periodic", "CMAKE_TARGET": "TEST_SUITE_periodic",
"CTEST_OPTIONS": "-L \"(SUITE_periodic)\"" "CTEST_OPTIONS": "-L \"(SUITE_periodic)\"",
"TEST_RESULTS": "True"
} }
}, },
"benchmark_test_profile": { "benchmark_test_profile": {
@ -118,7 +119,8 @@
"CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE", "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_LY_PROJECTS": "AutomatedTesting",
"CMAKE_TARGET": "TEST_SUITE_benchmark", "CMAKE_TARGET": "TEST_SUITE_benchmark",
"CTEST_OPTIONS": "-L \"(SUITE_benchmark)\"" "CTEST_OPTIONS": "-L \"(SUITE_benchmark)\"",
"TEST_RESULTS": "True"
} }
}, },
"release": { "release": {

Loading…
Cancel
Save