Merge branch 'development' into Atom/guthadam/material_editor_add_toggle_alternate_skybox_to_toolbar3
This commit is contained in:
@@ -141,6 +141,10 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent)
|
||||
m_ui->m_assetBrowserTreeViewWidget, &AzAssetBrowser::AssetBrowserTreeView::ClearTypeFilter, m_ui->m_searchWidget,
|
||||
&AzAssetBrowser::SearchWidget::ClearTypeFilter);
|
||||
|
||||
connect(
|
||||
this, &AzAssetBrowserWindow::SizeChangedSignal, m_ui->m_assetBrowserTableViewWidget,
|
||||
&AzAssetBrowser::AssetBrowserTableView::UpdateSizeSlot);
|
||||
|
||||
m_ui->m_assetBrowserTreeViewWidget->SetName("AssetBrowserTreeView_main");
|
||||
}
|
||||
|
||||
@@ -164,6 +168,25 @@ QObject* AzAssetBrowserWindow::createListenerForShowAssetEditorEvent(QObject* pa
|
||||
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()
|
||||
{
|
||||
CreateSwitchViewMenu();
|
||||
|
||||
@@ -53,9 +53,17 @@ public:
|
||||
|
||||
static QObject* createListenerForShowAssetEditorEvent(QObject* parent);
|
||||
|
||||
|
||||
Q_SIGNALS:
|
||||
void SizeChangedSignal(int newWidth);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* resizeEvent) override;
|
||||
|
||||
private:
|
||||
void OnInitViewToggleButton();
|
||||
void UpdateDisplayInfo();
|
||||
|
||||
protected slots:
|
||||
void CreateSwitchViewMenu();
|
||||
void SetExpandedAssetBrowserMode();
|
||||
|
||||
@@ -2428,7 +2428,7 @@ void EditorViewportWidget::RestoreViewportAfterGameMode()
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -649,6 +649,16 @@ namespace AZ
|
||||
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
|
||||
{
|
||||
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.
|
||||
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.
|
||||
//! All state changes will be signaled through this 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.
|
||||
AZStd::string m_name;
|
||||
|
||||
u32 m_spawnTicketId = 0;
|
||||
|
||||
//! The state of the entity.
|
||||
State m_state;
|
||||
|
||||
|
||||
@@ -91,15 +91,6 @@ namespace AzFramework
|
||||
*/
|
||||
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.
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param id The ID of the entity to activate.
|
||||
|
||||
@@ -11,9 +11,10 @@
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzFramework/Entity/EntityContext.h>
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzFramework/Spawnable/SpawnableEntitiesInterface.h>
|
||||
|
||||
#include "GameEntityContextComponent.h"
|
||||
|
||||
@@ -47,9 +48,9 @@ namespace AzFramework
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
->Event("CreateGameEntity", &GameEntityContextRequestBus::Events::CreateGameEntityForBehaviorContext)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
|
||||
->Event("DestroyGameEntity", &GameEntityContextRequestBus::Events::DestroyGameEntityOnlyInSliceMode)
|
||||
->Event("DestroyGameEntity", &GameEntityContextRequestBus::Events::DestroyGameEntity)
|
||||
->Event(
|
||||
"DestroyGameEntityAndDescendants", &GameEntityContextRequestBus::Events::DestroyGameEntityAndDescendantsOnlyInSliceMode)
|
||||
"DestroyGameEntityAndDescendants", &GameEntityContextRequestBus::Events::DestroyGameEntityAndDescendants)
|
||||
->Event("ActivateGameEntity", &GameEntityContextRequestBus::Events::ActivateGameEntity)
|
||||
->Event("DeactivateGameEntity", &GameEntityContextRequestBus::Events::DeactivateGameEntity)
|
||||
->Attribute(AZ::ScriptCanvasAttributes::DeactivatesInputEntity, true)
|
||||
@@ -249,23 +250,6 @@ namespace AzFramework
|
||||
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
|
||||
//=========================================================================
|
||||
@@ -274,24 +258,6 @@ namespace AzFramework
|
||||
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
|
||||
//=========================================================================
|
||||
@@ -319,6 +285,28 @@ namespace AzFramework
|
||||
EBUS_EVENT_RESULT(currentEntity, AZ::ComponentApplicationBus, FindEntity, *entityIdIter);
|
||||
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)
|
||||
{
|
||||
// Deactivate the entity, we'll destroy it as soon as it is safe.
|
||||
|
||||
@@ -90,11 +90,6 @@ namespace AzFramework
|
||||
}
|
||||
|
||||
private:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// GameEntityContextRequestBus
|
||||
void DestroyGameEntityOnlyInSliceMode(const AZ::EntityId&) override;
|
||||
void DestroyGameEntityAndDescendantsOnlyInSliceMode(const AZ::EntityId&) override;
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AzFramework::EntityVisibilityBoundsUnionSystem m_entityVisibilityBoundsUnionSystem;
|
||||
};
|
||||
|
||||
@@ -283,8 +283,12 @@ namespace AzFramework
|
||||
: m_payload(rhs.m_payload)
|
||||
, m_id(rhs.m_id)
|
||||
{
|
||||
auto manager = SpawnableEntitiesInterface::Get();
|
||||
AZ_Assert(manager, "SpawnableEntitiesInterface has no implementation.");
|
||||
rhs.m_payload = nullptr;
|
||||
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)
|
||||
@@ -294,6 +298,8 @@ namespace AzFramework
|
||||
AZStd::pair<EntitySpawnTicket::Id, void*> result = manager->CreateTicket(AZStd::move(spawnable));
|
||||
m_id = result.first;
|
||||
m_payload = result.second;
|
||||
AZStd::scoped_lock lock(manager->m_entitySpawnTicketMapMutex);
|
||||
manager->m_entitySpawnTicketMap.insert_or_assign(m_id, this);
|
||||
}
|
||||
|
||||
EntitySpawnTicket::~EntitySpawnTicket()
|
||||
@@ -304,6 +310,8 @@ namespace AzFramework
|
||||
AZ_Assert(manager, "Attempting to destroy an entity spawn ticket while the SpawnableEntitiesInterface has no implementation.");
|
||||
manager->DestroyTicket(m_payload);
|
||||
m_payload = nullptr;
|
||||
AZStd::scoped_lock lock(manager->m_entitySpawnTicketMapMutex);
|
||||
manager->m_entitySpawnTicketMap.erase(m_id);
|
||||
m_id = 0;
|
||||
}
|
||||
}
|
||||
@@ -312,17 +320,23 @@ namespace AzFramework
|
||||
{
|
||||
if (this != &rhs)
|
||||
{
|
||||
auto manager = SpawnableEntitiesInterface::Get();
|
||||
AZ_Assert(manager, "Attempting to destroy an entity spawn ticket while the SpawnableEntitiesInterface has no implementation.");
|
||||
if (m_payload)
|
||||
{
|
||||
auto manager = SpawnableEntitiesInterface::Get();
|
||||
AZ_Assert(manager, "Attempting to destroy an entity spawn ticket while the SpawnableEntitiesInterface has no implementation.");
|
||||
manager->DestroyTicket(m_payload);
|
||||
}
|
||||
|
||||
Id previousId = m_id;
|
||||
m_id = rhs.m_id;
|
||||
rhs.m_id = 0;
|
||||
|
||||
m_payload = rhs.m_payload;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ namespace AzFramework
|
||||
public:
|
||||
friend class SpawnableEntitiesDefinition;
|
||||
|
||||
using Id = uint64_t;
|
||||
using Id = uint32_t;
|
||||
|
||||
EntitySpawnTicket() = default;
|
||||
EntitySpawnTicket(const EntitySpawnTicket&) = delete;
|
||||
@@ -176,6 +176,7 @@ namespace AzFramework
|
||||
using EntitySpawnCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableConstEntityContainerView)>;
|
||||
using EntityPreInsertionCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableEntityContainerView)>;
|
||||
using EntityDespawnCallback = AZStd::function<void(EntitySpawnTicket::Id)>;
|
||||
using RetrieveEntitySpawnTicketCallback = AZStd::function<void(EntitySpawnTicket*)>;
|
||||
using ReloadSpawnableCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableConstEntityContainerView)>;
|
||||
using ListEntitiesCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableConstEntityContainerView)>;
|
||||
using ListIndicesEntitiesCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableConstIndexEntityContainerView)>;
|
||||
@@ -220,12 +221,21 @@ namespace AzFramework
|
||||
struct DespawnAllEntitiesOptionalArgs final
|
||||
{
|
||||
//! 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;
|
||||
//! The priority at which this call will be executed.
|
||||
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
|
||||
{
|
||||
//! 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;
|
||||
//! Removes all entities in the provided list from the environment.
|
||||
//! @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.
|
||||
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.
|
||||
//! @param ticket Holds the information on the entities to reload.
|
||||
//! @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);
|
||||
}
|
||||
|
||||
AZStd::unordered_map<EntitySpawnTicket::Id, EntitySpawnTicket*> m_entitySpawnTicketMap;
|
||||
AZStd::recursive_mutex m_entitySpawnTicketMapMutex;
|
||||
};
|
||||
|
||||
using SpawnableEntitiesInterface = AZ::Interface<SpawnableEntitiesDefinition>;
|
||||
|
||||
@@ -85,6 +85,35 @@ namespace AzFramework
|
||||
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(
|
||||
EntitySpawnTicket& ticket, AZ::Data::Asset<Spawnable> spawnable, ReloadSpawnableOptionalArgs optionalArgs)
|
||||
{
|
||||
@@ -223,12 +252,13 @@ namespace AzFramework
|
||||
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();
|
||||
result->m_spawnable = AZStd::move(spawnable);
|
||||
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -420,6 +451,7 @@ namespace AzFramework
|
||||
// Add to the game context, now the entities are active
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -447,8 +479,10 @@ namespace AzFramework
|
||||
{
|
||||
if (entity != nullptr)
|
||||
{
|
||||
// Setting it to 0 is needed to avoid the infite loop between GameEntityContext and SpawnableEntitiesManager.
|
||||
entity->SetSpawnTicketId(0);
|
||||
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)
|
||||
{
|
||||
Ticket& ticket = *request.m_ticket;
|
||||
@@ -482,8 +550,10 @@ namespace AzFramework
|
||||
{
|
||||
if (entity != nullptr)
|
||||
{
|
||||
// Setting it to 0 is needed to avoid the infite loop between GameEntityContext and SpawnableEntitiesManager.
|
||||
entity->SetSpawnTicketId(0);
|
||||
GameEntityContextRequestBus::Broadcast(
|
||||
&GameEntityContextRequestBus::Events::DestroyGameEntityAndDescendants, entity->GetId());
|
||||
&GameEntityContextRequestBus::Events::DestroyGameEntity, entity->GetId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -636,8 +706,10 @@ namespace AzFramework
|
||||
{
|
||||
if (entity != nullptr)
|
||||
{
|
||||
// Setting it to 0 is needed to avoid the infite loop between GameEntityContext and SpawnableEntitiesManager.
|
||||
entity->SetSpawnTicketId(0);
|
||||
GameEntityContextRequestBus::Broadcast(
|
||||
&GameEntityContextRequestBus::Events::DestroyGameEntityAndDescendants, entity->GetId());
|
||||
&GameEntityContextRequestBus::Events::DestroyGameEntity, entity->GetId());
|
||||
}
|
||||
}
|
||||
delete request.m_ticket;
|
||||
|
||||
@@ -57,6 +57,8 @@ namespace AzFramework
|
||||
void SpawnEntities(
|
||||
EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices, SpawnEntitiesOptionalArgs 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(
|
||||
EntitySpawnTicket& ticket, AZ::Data::Asset<Spawnable> spawnable, ReloadSpawnableOptionalArgs optionalArgs = {}) override;
|
||||
|
||||
@@ -132,6 +134,14 @@ namespace AzFramework
|
||||
EntitySpawnTicket::Id m_ticketId;
|
||||
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
|
||||
{
|
||||
AZ::Data::Asset<Spawnable> m_spawnable;
|
||||
@@ -176,8 +186,16 @@ namespace AzFramework
|
||||
};
|
||||
|
||||
using Requests = AZStd::variant<
|
||||
SpawnAllEntitiesCommand, SpawnEntitiesCommand, DespawnAllEntitiesCommand, ReloadSpawnableCommand, ListEntitiesCommand,
|
||||
ListIndicesEntitiesCommand, ClaimEntitiesCommand, BarrierCommand, DestroyTicketCommand>;
|
||||
SpawnAllEntitiesCommand,
|
||||
SpawnEntitiesCommand,
|
||||
DespawnAllEntitiesCommand,
|
||||
DespawnEntityCommand,
|
||||
ReloadSpawnableCommand,
|
||||
ListEntitiesCommand,
|
||||
ListIndicesEntitiesCommand,
|
||||
ClaimEntitiesCommand,
|
||||
BarrierCommand,
|
||||
DestroyTicketCommand>;
|
||||
|
||||
struct Queue
|
||||
{
|
||||
@@ -199,6 +217,7 @@ namespace AzFramework
|
||||
bool ProcessRequest(SpawnAllEntitiesCommand& request);
|
||||
bool ProcessRequest(SpawnEntitiesCommand& request);
|
||||
bool ProcessRequest(DespawnAllEntitiesCommand& request);
|
||||
bool ProcessRequest(DespawnEntityCommand& request);
|
||||
bool ProcessRequest(ReloadSpawnableCommand& request);
|
||||
bool ProcessRequest(ListEntitiesCommand& request);
|
||||
bool ProcessRequest(ListIndicesEntitiesCommand& request);
|
||||
|
||||
@@ -8,11 +8,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/Math/Vector2.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/RTTI/TypeInfoSimple.h>
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
#include <AzCore/Math/Vector2.h>
|
||||
#include <AzCore/RTTI/TypeInfoSimple.h>
|
||||
#include <AzCore/base.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -27,7 +26,11 @@ namespace AzFramework
|
||||
AZ_TYPE_INFO(ScreenPoint, "{8472B6C2-527F-44FC-87F8-C226B1A57A97}");
|
||||
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_y; //!< Y screen position.
|
||||
@@ -42,7 +45,11 @@ namespace AzFramework
|
||||
AZ_TYPE_INFO(ScreenVector, "{1EAA2C62-8FDB-4A28-9FE3-1FA4F1418894}");
|
||||
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_y; //!< Y screen delta.
|
||||
@@ -71,14 +78,14 @@ namespace AzFramework
|
||||
|
||||
inline const ScreenPoint operator+(const ScreenPoint& lhs, const ScreenVector& rhs)
|
||||
{
|
||||
ScreenPoint result{lhs};
|
||||
ScreenPoint result{ lhs };
|
||||
result += rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline const ScreenPoint operator-(const ScreenPoint& lhs, const ScreenVector& rhs)
|
||||
{
|
||||
ScreenPoint result{lhs};
|
||||
ScreenPoint result{ lhs };
|
||||
result -= rhs;
|
||||
return result;
|
||||
}
|
||||
@@ -99,14 +106,14 @@ namespace AzFramework
|
||||
|
||||
inline const ScreenVector operator+(const ScreenVector& lhs, const ScreenVector& rhs)
|
||||
{
|
||||
ScreenVector result{lhs};
|
||||
ScreenVector result{ lhs };
|
||||
result += rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline const ScreenVector operator-(const ScreenVector& lhs, const ScreenVector& rhs)
|
||||
{
|
||||
ScreenVector result{lhs};
|
||||
ScreenVector result{ lhs };
|
||||
result -= rhs;
|
||||
return result;
|
||||
}
|
||||
@@ -131,23 +138,25 @@ namespace AzFramework
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
inline ScreenVector& operator*=(ScreenVector& lhs, const float rhs)
|
||||
{
|
||||
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 const ScreenVector operator*(const ScreenVector& lhs, const float rhs)
|
||||
{
|
||||
ScreenVector result{ lhs };
|
||||
result *= rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline float ScreenVectorLength(const ScreenVector& screenVector)
|
||||
{
|
||||
return aznumeric_cast<float>(AZStd::sqrt(screenVector.m_x * screenVector.m_x + screenVector.m_y * screenVector.m_y));
|
||||
}
|
||||
|
||||
inline ScreenPoint ScreenPointFromNDC(const AZ::Vector3& screenNDC, const AZ::Vector2& viewportSize)
|
||||
{
|
||||
return ScreenPoint(
|
||||
aznumeric_caster(AZStd::round(screenNDC.GetX() * viewportSize.GetX())),
|
||||
aznumeric_caster(AZStd::round((1.0f - screenNDC.GetY()) * viewportSize.GetY())));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
//! Return an AZ::Vector2 from a ScreenPoint.
|
||||
inline AZ::Vector2 Vector2FromScreenPoint(const ScreenPoint& screenPoint)
|
||||
{
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace AzFramework
|
||||
// y-up, z into the screen, x-left
|
||||
//
|
||||
// x -> -x
|
||||
// y -> z
|
||||
// y -> z
|
||||
// z -> y
|
||||
//
|
||||
// the same transform can be used to go to/from z-up - the only difference is the order of
|
||||
@@ -37,15 +37,15 @@ namespace AzFramework
|
||||
// yaw = AZ::Matrix4x4::CreateRotationZ(AZ::DegToRad(180.0f));
|
||||
// conversion = pitch * yaw
|
||||
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(0.0f, 1.0f, 0.0f, 0.0f), AZ::Vector4(0.0f, 0.0f, 0.0f, 1.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, 0.0f, 0.0f, 1.0f));
|
||||
}
|
||||
|
||||
AZ::Matrix4x4 CameraTransform(const CameraState& cameraState)
|
||||
{
|
||||
return AZ::Matrix4x4::CreateFromColumns(
|
||||
AZ::Vector3ToVector4(cameraState.m_side), AZ::Vector3ToVector4(cameraState.m_forward),
|
||||
AZ::Vector3ToVector4(cameraState.m_up), AZ::Vector3ToVector4(cameraState.m_position, 1.0f));
|
||||
AZ::Vector3ToVector4(cameraState.m_side), AZ::Vector3ToVector4(cameraState.m_forward), AZ::Vector3ToVector4(cameraState.m_up),
|
||||
AZ::Vector3ToVector4(cameraState.m_position, 1.0f));
|
||||
}
|
||||
|
||||
AZ::Matrix4x4 CameraView(const CameraState& cameraState)
|
||||
@@ -63,8 +63,7 @@ namespace AzFramework
|
||||
AZ::Matrix4x4 CameraProjection(const CameraState& cameraState)
|
||||
{
|
||||
return AZ::Matrix4x4::CreateProjection(
|
||||
cameraState.VerticalFovRadian(), AspectRatio(cameraState.m_viewportSize), cameraState.m_nearClip,
|
||||
cameraState.m_farClip);
|
||||
cameraState.VerticalFovRadian(), AspectRatio(cameraState.m_viewportSize), cameraState.m_nearClip, cameraState.m_farClip);
|
||||
}
|
||||
|
||||
AZ::Matrix4x4 InverseCameraProjection(const CameraState& cameraState)
|
||||
@@ -93,12 +92,11 @@ namespace AzFramework
|
||||
const auto cameraWorldTransform = AZ::Transform::CreateFromMatrix3x3AndTranslation(
|
||||
AZ::Matrix3x3::CreateFromMatrix4x4(worldFromView), worldFromView.GetTranslation());
|
||||
return AZ::ViewFrustumAttributes(
|
||||
cameraWorldTransform, AspectRatio(cameraState.m_viewportSize), cameraState.m_fovOrZoom,
|
||||
cameraState.m_nearClip, cameraState.m_farClip);
|
||||
cameraWorldTransform, AspectRatio(cameraState.m_viewportSize), cameraState.m_fovOrZoom, cameraState.m_nearClip,
|
||||
cameraState.m_farClip);
|
||||
}
|
||||
|
||||
AZ::Vector3 WorldToScreenNDC(
|
||||
const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection)
|
||||
AZ::Vector3 WorldToScreenNdc(const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection)
|
||||
{
|
||||
// transform the world space position to clip space
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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 auto ndcNormalizedPosition = WorldToScreenNDC(worldPosition, cameraView, cameraProjection);
|
||||
const auto ndcNormalizedPosition = WorldToScreenNdc(worldPosition, cameraView, cameraProjection);
|
||||
// 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)
|
||||
{
|
||||
return WorldToScreen(
|
||||
worldPosition, CameraView(cameraState), CameraProjection(cameraState), cameraState.m_viewportSize);
|
||||
return WorldToScreen(worldPosition, CameraView(cameraState), CameraProjection(cameraState), cameraState.m_viewportSize);
|
||||
}
|
||||
|
||||
AZ::Vector3 ScreenNDCToWorld(
|
||||
const AZ::Vector2& normalizedScreenPosition, const AZ::Matrix4x4& inverseCameraView,
|
||||
const AZ::Matrix4x4& inverseCameraProjection)
|
||||
AZ::Vector3 ScreenNdcToWorld(
|
||||
const AZ::Vector2& normalizedScreenPosition, const AZ::Matrix4x4& inverseCameraView, const AZ::Matrix4x4& inverseCameraProjection)
|
||||
{
|
||||
// convert screen space coordinates from <0, 1> to <-1,1> range
|
||||
const auto ndcPosition = normalizedScreenPosition * 2.0f - AZ::Vector2::CreateOne();
|
||||
@@ -142,18 +139,19 @@ namespace AzFramework
|
||||
}
|
||||
|
||||
AZ::Vector3 ScreenToWorld(
|
||||
const ScreenPoint& screenPosition, const AZ::Matrix4x4& inverseCameraView,
|
||||
const AZ::Matrix4x4& inverseCameraProjection, const AZ::Vector2& viewportSize)
|
||||
const ScreenPoint& screenPosition,
|
||||
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)
|
||||
{
|
||||
return ScreenToWorld(
|
||||
screenPosition, InverseCameraView(cameraState), InverseCameraProjection(cameraState),
|
||||
cameraState.m_viewportSize);
|
||||
screenPosition, InverseCameraView(cameraState), InverseCameraProjection(cameraState), cameraState.m_viewportSize);
|
||||
}
|
||||
} // namespace AzFramework
|
||||
|
||||
@@ -8,26 +8,42 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
#include <AzCore/Math/Vector2.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzFramework/Viewport/ScreenGeometry.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class Frustum;
|
||||
class Matrix4x4;
|
||||
class Vector3;
|
||||
struct ViewFrustumAttributes;
|
||||
} // namespace AZ
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
struct CameraState;
|
||||
struct ScreenPoint;
|
||||
struct ViewportInfo;
|
||||
|
||||
//! 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);
|
||||
//! Returns a position in screen space (in the range [0-viewportSize.x, 0-viewportSize.y]) from normalized device
|
||||
//! coordinates (in the range 0.0-1.0).
|
||||
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.
|
||||
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
|
||||
//! is called many times in a loop.
|
||||
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);
|
||||
|
||||
//! 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
|
||||
//! is called many times in a loop.
|
||||
AZ::Vector3 ScreenToWorld(
|
||||
const ScreenPoint& screenPosition, const AZ::Matrix4x4& inverseCameraView,
|
||||
const AZ::Matrix4x4& inverseCameraProjection, const AZ::Vector2& viewportSize);
|
||||
const ScreenPoint& screenPosition,
|
||||
const AZ::Matrix4x4& inverseCameraView,
|
||||
const AZ::Matrix4x4& inverseCameraProjection,
|
||||
const AZ::Vector2& viewportSize);
|
||||
|
||||
//! 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.
|
||||
AZ::Vector3 ScreenNDCToWorld(
|
||||
const AZ::Vector2& ndcPosition, const AZ::Matrix4x4& inverseCameraView,
|
||||
const AZ::Matrix4x4& inverseCameraProjection);
|
||||
AZ::Vector3 ScreenNdcToWorld(
|
||||
const AZ::Vector2& ndcPosition, const AZ::Matrix4x4& inverseCameraView, const AZ::Matrix4x4& inverseCameraProjection);
|
||||
|
||||
//! Returns the camera projection for the current camera state.
|
||||
AZ::Matrix4x4 CameraProjection(const CameraState& cameraState);
|
||||
|
||||
@@ -40,6 +40,11 @@ namespace AzFramework
|
||||
|
||||
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(
|
||||
ReloadSpawnable,
|
||||
void(EntitySpawnTicket& ticket, AZ::Data::Asset<Spawnable> spawnable, ReloadSpawnableOptionalArgs optionalArgs));
|
||||
|
||||
+10
@@ -151,6 +151,10 @@ namespace AzToolsFramework
|
||||
m_ui->m_assetBrowserTableViewWidget, &AssetBrowserTableView::ClearTypeFilter, m_ui->m_searchWidget,
|
||||
&SearchWidget::ClearTypeFilter);
|
||||
|
||||
connect(
|
||||
this, &AssetPickerDialog::SizeChangedSignal, m_ui->m_assetBrowserTableViewWidget,
|
||||
&AssetBrowserTableView::UpdateSizeSlot);
|
||||
|
||||
m_ui->m_assetBrowserTableViewWidget->SetName("AssetBrowserTableView_main");
|
||||
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)
|
||||
{
|
||||
// Until search widget is revised, Return key should not close the dialog,
|
||||
|
||||
+4
@@ -46,6 +46,9 @@ namespace AzToolsFramework
|
||||
explicit AssetPickerDialog(AssetSelectionModel& selection, QWidget* parent = nullptr);
|
||||
virtual ~AssetPickerDialog();
|
||||
|
||||
Q_SIGNALS:
|
||||
void SizeChangedSignal(int newWidth);
|
||||
|
||||
protected:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// QDialog
|
||||
@@ -53,6 +56,7 @@ namespace AzToolsFramework
|
||||
void accept() override;
|
||||
void reject() override;
|
||||
void keyPressEvent(QKeyEvent* e) override;
|
||||
void resizeEvent(QResizeEvent* resizeEvent) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void DoubleClickedSlot(const QModelIndex& index);
|
||||
|
||||
+3
-3
@@ -117,6 +117,9 @@
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="AzToolsFramework::AssetBrowser::AssetBrowserTableView" name="m_assetBrowserTableViewWidget"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="AzToolsFramework::AssetBrowser::AssetBrowserTreeView" name="m_assetBrowserTreeViewWidget">
|
||||
<property name="sizePolicy">
|
||||
@@ -142,9 +145,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="AzToolsFramework::AssetBrowser::AssetBrowserTableView" name="m_assetBrowserTableViewWidget"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="verticalLayoutWidget">
|
||||
|
||||
+18
-3
@@ -20,13 +20,17 @@ AZ_PUSH_DISABLE_WARNING(
|
||||
#include <QCoreApplication>
|
||||
#include <QHeaderView>
|
||||
#include <QMenu>
|
||||
|
||||
#include <QResizeEvent>
|
||||
#include <QTimer>
|
||||
AZ_POP_DISABLE_WARNING
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace AssetBrowser
|
||||
{
|
||||
const float MinHeaderResizeProportion = .25f;
|
||||
const float MaxHeaderResizeProportion = .75f;
|
||||
const float DefaultHeaderResizeProportion = .5f;
|
||||
|
||||
AssetBrowserTableView::AssetBrowserTableView(QWidget* parent)
|
||||
: AzQtComponents::TableView(parent)
|
||||
, m_delegate(new SearchEntryDelegate(this))
|
||||
@@ -65,8 +69,10 @@ namespace AzToolsFramework
|
||||
AzQtComponents::TableView::setModel(model);
|
||||
connect(m_tableModel, &AssetBrowserTableModel::layoutChanged, this, &AssetBrowserTableView::layoutChangedSlot);
|
||||
|
||||
header()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch);
|
||||
header()->setSectionResizeMode(1, QHeaderView::ResizeMode::Stretch);
|
||||
header()->setStretchLastSection(true);
|
||||
header()->setSectionResizeMode(0, QHeaderView::ResizeMode::Interactive);
|
||||
header()->setSectionResizeMode(1, QHeaderView::ResizeMode::Interactive);
|
||||
UpdateSizeSlot(parentWidget()->width());
|
||||
header()->setSortIndicatorShown(false);
|
||||
header()->setSectionsClickable(false);
|
||||
}
|
||||
@@ -148,8 +154,17 @@ namespace AzToolsFramework
|
||||
|
||||
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)
|
||||
{
|
||||
const auto& selectedAssets = GetSelectedAssets();
|
||||
|
||||
+3
-1
@@ -59,12 +59,14 @@ namespace AzToolsFramework
|
||||
void ClearStringFilter();
|
||||
void ClearTypeFilter();
|
||||
|
||||
public Q_SLOTS:
|
||||
void UpdateSizeSlot(int newWidth);
|
||||
|
||||
protected Q_SLOTS:
|
||||
void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) override;
|
||||
void rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) override;
|
||||
void layoutChangedSlot(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>(),
|
||||
QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint);
|
||||
|
||||
private:
|
||||
QString m_name;
|
||||
QPointer<AssetBrowserTableModel> m_tableModel;
|
||||
|
||||
@@ -381,12 +381,22 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
//orphan any children that remain attached to the entity
|
||||
auto children = entityInfo.GetChildren();
|
||||
for (auto childId : children)
|
||||
bool isPrefabSystemEnabled = false;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(
|
||||
isPrefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
|
||||
|
||||
// For slices, orphan any children that remain attached to the entity
|
||||
// For prefabs, this is an unneeded operation because the prefab system handles the orphans
|
||||
// and the extra reparenting operation can be problematic for consumers subscribed to entity
|
||||
// events, such as the entity outliner.
|
||||
if (!isPrefabSystemEnabled)
|
||||
{
|
||||
ReparentChild(childId, AZ::EntityId(), entityId);
|
||||
m_entityOrphanTable[entityId].insert(childId);
|
||||
auto children = entityInfo.GetChildren();
|
||||
for (auto childId : children)
|
||||
{
|
||||
ReparentChild(childId, AZ::EntityId(), entityId);
|
||||
m_entityOrphanTable[entityId].insert(childId);
|
||||
}
|
||||
}
|
||||
|
||||
m_savedOrderInfo[entityId] = AZStd::make_pair(entityInfo.GetParent(), entityInfo.GetIndexForSorting());
|
||||
|
||||
+2
@@ -56,5 +56,7 @@ namespace AzToolsFramework
|
||||
virtual void StopPlayInEditor() = 0;
|
||||
|
||||
virtual void CreateNewLevelPrefab(AZStd::string_view filename, const AZStd::string& templateFilename) = 0;
|
||||
|
||||
virtual bool IsRootPrefabAssigned() const = 0;
|
||||
};
|
||||
}
|
||||
|
||||
+9
@@ -79,6 +79,8 @@ namespace AzToolsFramework
|
||||
|
||||
void PrefabEditorEntityOwnershipService::Reset()
|
||||
{
|
||||
m_isRootPrefabAssigned = false;
|
||||
|
||||
if (m_rootInstance)
|
||||
{
|
||||
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
|
||||
@@ -203,6 +205,7 @@ namespace AzToolsFramework
|
||||
m_rootInstance->SetTemplateSourcePath(m_loaderInterface->GenerateRelativePath(filename));
|
||||
m_rootInstance->SetContainerEntityName("Level");
|
||||
m_prefabSystemComponent->PropagateTemplateChanges(templateId);
|
||||
m_isRootPrefabAssigned = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -302,6 +305,12 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
m_prefabSystemComponent->PropagateTemplateChanges(templateId);
|
||||
m_isRootPrefabAssigned = true;
|
||||
}
|
||||
|
||||
bool PrefabEditorEntityOwnershipService::IsRootPrefabAssigned() const
|
||||
{
|
||||
return m_isRootPrefabAssigned;
|
||||
}
|
||||
|
||||
Prefab::InstanceOptionalReference PrefabEditorEntityOwnershipService::CreatePrefab(
|
||||
|
||||
+2
@@ -167,6 +167,7 @@ namespace AzToolsFramework
|
||||
void StopPlayInEditor() override;
|
||||
|
||||
void CreateNewLevelPrefab(AZStd::string_view filename, const AZStd::string& templateFilename) override;
|
||||
bool IsRootPrefabAssigned() const override;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -215,5 +216,6 @@ namespace AzToolsFramework
|
||||
Prefab::PrefabLoaderInterface* m_loaderInterface;
|
||||
AzFramework::EntityContextId m_entityContextId;
|
||||
AZ::SerializeContext m_serializeContext;
|
||||
bool m_isRootPrefabAssigned = false;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
m_uniformScaleManipulator->SetVisualOrientationOverride(QuaternionFromTransformNoScaling(localTransform));
|
||||
m_uniformScaleManipulator->SetLocalPosition(localTransform.GetTranslation());
|
||||
m_uniformScaleManipulator->SetLocalOrientation(AZ::Quaternion::CreateIdentity());
|
||||
}
|
||||
|
||||
|
||||
+14
-13
@@ -13,14 +13,14 @@
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
static const float s_surfaceManipulatorTransparency = 0.75f;
|
||||
static const float s_axisLength = 2.0f;
|
||||
static const float s_surfaceManipulatorRadius = 0.1f;
|
||||
static const float SurfaceManipulatorTransparency = 0.75f;
|
||||
static const float LinearManipulatorAxisLength = 2.0f;
|
||||
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 s_yAxisColor = 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 s_surfaceManipulatorColor = AZ::Color(1.0f, 1.0f, 0.0f, 0.5f);
|
||||
static const AZ::Color LinearManipulatorXAxisColor = AZ::Color(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
static const AZ::Color LinearManipulatorYAxisColor = AZ::Color(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
static const AZ::Color LinearManipulatorZAxisColor = AZ::Color(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
static const AZ::Color SurfaceManipulatorColor = AZ::Color(1.0f, 1.0f, 0.0f, 0.5f);
|
||||
|
||||
TranslationManipulators::TranslationManipulators(
|
||||
const Dimensions dimensions, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale)
|
||||
@@ -291,7 +291,7 @@ namespace AzToolsFramework
|
||||
{
|
||||
const AZ::Color color[2] = {
|
||||
defaultColor,
|
||||
Vector3ToVector4(BaseManipulator::s_defaultMouseOverColor.GetAsVector3(), s_surfaceManipulatorTransparency)
|
||||
Vector3ToVector4(BaseManipulator::s_defaultMouseOverColor.GetAsVector3(), SurfaceManipulatorTransparency)
|
||||
};
|
||||
|
||||
return color[mouseOver];
|
||||
@@ -325,15 +325,16 @@ namespace AzToolsFramework
|
||||
void ConfigureTranslationManipulatorAppearance3d(TranslationManipulators* translationManipulators)
|
||||
{
|
||||
translationManipulators->SetAxes(AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ());
|
||||
translationManipulators->ConfigurePlanarView(s_xAxisColor, s_yAxisColor, s_zAxisColor);
|
||||
translationManipulators->ConfigureLinearView(s_axisLength, s_xAxisColor, s_yAxisColor, s_zAxisColor);
|
||||
translationManipulators->ConfigureSurfaceView(s_surfaceManipulatorRadius, s_surfaceManipulatorColor);
|
||||
translationManipulators->ConfigurePlanarView(LinearManipulatorXAxisColor, LinearManipulatorYAxisColor, LinearManipulatorZAxisColor);
|
||||
translationManipulators->ConfigureLinearView(
|
||||
LinearManipulatorAxisLength, LinearManipulatorXAxisColor, LinearManipulatorYAxisColor, LinearManipulatorZAxisColor);
|
||||
translationManipulators->ConfigureSurfaceView(SurfaceManipulatorRadius, SurfaceManipulatorColor);
|
||||
}
|
||||
|
||||
void ConfigureTranslationManipulatorAppearance2d(TranslationManipulators* translationManipulators)
|
||||
{
|
||||
translationManipulators->SetAxes(AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY());
|
||||
translationManipulators->ConfigurePlanarView(s_xAxisColor);
|
||||
translationManipulators->ConfigureLinearView(s_axisLength, s_xAxisColor, s_yAxisColor);
|
||||
translationManipulators->ConfigurePlanarView(LinearManipulatorXAxisColor);
|
||||
translationManipulators->ConfigureLinearView(LinearManipulatorAxisLength, LinearManipulatorXAxisColor, LinearManipulatorYAxisColor);
|
||||
}
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -329,6 +329,11 @@ namespace AzToolsFramework
|
||||
return AZ::Failure(AZStd::string("Could not instantiate prefab - internal error "
|
||||
"(PrefabEditorEntityOwnershipInterface unavailable)."));
|
||||
}
|
||||
if (!prefabEditorEntityOwnershipInterface->IsRootPrefabAssigned())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Could not instantiate prefab - no root prefab assigned. "
|
||||
"Currently, prefabs can only be instantiated inside a level"));
|
||||
}
|
||||
|
||||
InstanceOptionalReference instanceToParentUnder;
|
||||
|
||||
|
||||
+7
-7
@@ -82,6 +82,8 @@ namespace AzToolsFramework
|
||||
, m_entityExpansionState()
|
||||
, m_entityFilteredState()
|
||||
{
|
||||
m_focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
|
||||
AZ_Assert(m_focusModeInterface != nullptr, "EntityOutlinerListModel requires a FocusModeInterface instance on construction.");
|
||||
}
|
||||
|
||||
EntityOutlinerListModel::~EntityOutlinerListModel()
|
||||
@@ -106,11 +108,6 @@ namespace AzToolsFramework
|
||||
m_editorEntityUiInterface = AZ::Interface<AzToolsFramework::EditorEntityUiInterface>::Get();
|
||||
AZ_Assert(m_editorEntityUiInterface != nullptr,
|
||||
"EntityOutlinerListModel requires a EditorEntityUiInterface instance on Initialize.");
|
||||
|
||||
m_focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
|
||||
AZ_Assert(
|
||||
m_focusModeInterface != nullptr,
|
||||
"EntityOutlinerListModel requires a FocusModeInterface instance on Initialize.");
|
||||
}
|
||||
|
||||
int EntityOutlinerListModel::rowCount(const QModelIndex& parent) const
|
||||
@@ -1347,7 +1344,10 @@ namespace AzToolsFramework
|
||||
//add/remove operations trigger selection change signals which assert and break undo/redo operations in progress in inspector etc.
|
||||
//so disallow selection updates until change is complete
|
||||
emit EnableSelectionUpdates(false);
|
||||
beginResetModel();
|
||||
|
||||
auto parentIndex = GetIndexFromEntity(parentId);
|
||||
auto childIndex = GetIndexFromEntity(childId);
|
||||
beginRemoveRows(parentIndex, childIndex.row(), childIndex.row());
|
||||
}
|
||||
|
||||
void EntityOutlinerListModel::OnEntityInfoUpdatedRemoveChildEnd(AZ::EntityId parentId, AZ::EntityId childId)
|
||||
@@ -1355,7 +1355,7 @@ namespace AzToolsFramework
|
||||
(void)childId;
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
endResetModel();
|
||||
endRemoveRows();
|
||||
|
||||
//must refresh partial lock/visibility of parents
|
||||
m_isFilterDirty = true;
|
||||
|
||||
@@ -232,6 +232,9 @@ namespace AzToolsFramework
|
||||
m_gui->m_objectTree->header()->setSortIndicatorShown(false);
|
||||
m_gui->m_objectTree->header()->setStretchLastSection(false);
|
||||
|
||||
// Always expand root entity (level entity) - needed if the widget is re-created while a level is already open.
|
||||
m_gui->m_objectTree->expand(m_proxyModel->index(0, 0));
|
||||
|
||||
// resize the icon columns so that the Visibility and Lock toggle icon columns stay right-justified
|
||||
m_gui->m_objectTree->header()->setStretchLastSection(false);
|
||||
m_gui->m_objectTree->header()->setMinimumSectionSize(0);
|
||||
|
||||
+92
-87
@@ -700,13 +700,6 @@ namespace AzToolsFramework
|
||||
switch (referenceFrame)
|
||||
{
|
||||
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:
|
||||
pivot.m_worldOrientation = pivotOverrideFrame.m_orientationOverride.value();
|
||||
break;
|
||||
@@ -784,7 +777,7 @@ namespace AzToolsFramework
|
||||
SortEntitiesByLocationInHierarchy(sortedEntityIdsOut);
|
||||
}
|
||||
|
||||
static void UpdateInitialRotation(EntityIdManipulators& entityManipulators)
|
||||
static void UpdateInitialTransform(EntityIdManipulators& entityManipulators)
|
||||
{
|
||||
// save new start orientation (if moving rotation axes separate from object
|
||||
// or switching type of rotation (modifier keys change))
|
||||
@@ -797,22 +790,17 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
// utility function to immediately return the current reference frame
|
||||
// based on the state of the modifiers
|
||||
// utility function to immediately return the current reference frame based on the state of the modifiers
|
||||
static ReferenceFrame ReferenceFrameFromModifiers(const ViewportInteraction::KeyboardModifiers modifiers)
|
||||
{
|
||||
if (modifiers.Shift() && !modifiers.Alt())
|
||||
{
|
||||
return ReferenceFrame::World;
|
||||
}
|
||||
else if (modifiers.Alt() && !modifiers.Shift())
|
||||
{
|
||||
return ReferenceFrame::Local;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ReferenceFrame::Parent;
|
||||
}
|
||||
return modifiers.Shift() ? ReferenceFrame::World : ReferenceFrame::Local;
|
||||
}
|
||||
|
||||
// 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 modifiers.Alt() ? Influence::Individual : Influence::Group;
|
||||
}
|
||||
|
||||
template<typename Action, typename EntityIdContainer>
|
||||
@@ -838,6 +826,7 @@ namespace AzToolsFramework
|
||||
else
|
||||
{
|
||||
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
|
||||
const auto pivotOrientation =
|
||||
@@ -854,9 +843,9 @@ namespace AzToolsFramework
|
||||
|
||||
const AZ::Vector3 worldTranslation = GetWorldTranslation(entityId);
|
||||
|
||||
switch (referenceFrame)
|
||||
switch (influence)
|
||||
{
|
||||
case ReferenceFrame::Local:
|
||||
case Influence::Individual:
|
||||
{
|
||||
// move in each entities local space at once
|
||||
AZ::Quaternion worldOrientation = AZ::Quaternion::CreateIdentity();
|
||||
@@ -876,10 +865,9 @@ namespace AzToolsFramework
|
||||
entityId, entityItLookupIt->second.m_initial.GetTranslation() + localOffset, transformChangedInternally);
|
||||
}
|
||||
break;
|
||||
case ReferenceFrame::Parent:
|
||||
case ReferenceFrame::World:
|
||||
case Influence::Group:
|
||||
{
|
||||
AZ::Quaternion offsetRotation = pivotOrientation.m_worldOrientation *
|
||||
const AZ::Quaternion offsetRotation = pivotOrientation.m_worldOrientation *
|
||||
QuaternionFromTransformNoScaling(entityIdManipulators.m_manipulators->GetLocalTransform().GetInverse());
|
||||
|
||||
const AZ::Vector3 localOffset = offsetRotation.TransformVector(action.LocalPositionOffset());
|
||||
@@ -1062,7 +1050,8 @@ namespace AzToolsFramework
|
||||
{
|
||||
AZStd::chrono::milliseconds timeNow;
|
||||
AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::BroadcastResult(
|
||||
timeNow, &AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Events::EditorViewportInputTimeNow);
|
||||
timeNow,
|
||||
&AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Events::EditorViewportInputTimeNow);
|
||||
return timeNow;
|
||||
});
|
||||
}
|
||||
@@ -1263,7 +1252,7 @@ namespace AzToolsFramework
|
||||
|
||||
AZStd::unique_ptr<TranslationManipulators> translationManipulators = AZStd::make_unique<TranslationManipulators>(
|
||||
TranslationManipulators::Dimensions::Three, AZ::Transform::CreateIdentity(), AZ::Vector3::CreateOne());
|
||||
translationManipulators->SetLineBoundWidth(ManipulatorLineBoundWidth(ViewportUi::DefaultViewportId));
|
||||
translationManipulators->SetLineBoundWidth(ManipulatorLineBoundWidth());
|
||||
|
||||
InitializeManipulators(*translationManipulators);
|
||||
|
||||
@@ -1297,7 +1286,7 @@ namespace AzToolsFramework
|
||||
|
||||
ViewportInteraction::KeyboardModifiers prevModifiers{};
|
||||
translationManipulators->InstallLinearManipulatorMouseMoveCallback(
|
||||
[this, prevModifiers, manipulatorEntityIds](const LinearManipulator::Action& action) mutable -> void
|
||||
[this, prevModifiers, manipulatorEntityIds](const LinearManipulator::Action& action) mutable
|
||||
{
|
||||
UpdateTranslationManipulator(
|
||||
action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers,
|
||||
@@ -1331,7 +1320,7 @@ namespace AzToolsFramework
|
||||
});
|
||||
|
||||
translationManipulators->InstallPlanarManipulatorMouseMoveCallback(
|
||||
[this, prevModifiers, manipulatorEntityIds](const PlanarManipulator::Action& action) mutable -> void
|
||||
[this, prevModifiers, manipulatorEntityIds](const PlanarManipulator::Action& action) mutable
|
||||
{
|
||||
UpdateTranslationManipulator(
|
||||
action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers,
|
||||
@@ -1364,7 +1353,7 @@ namespace AzToolsFramework
|
||||
});
|
||||
|
||||
translationManipulators->InstallSurfaceManipulatorMouseMoveCallback(
|
||||
[this, prevModifiers, manipulatorEntityIds](const SurfaceManipulator::Action& action) mutable -> void
|
||||
[this, prevModifiers, manipulatorEntityIds](const SurfaceManipulator::Action& action) mutable
|
||||
{
|
||||
UpdateTranslationManipulator(
|
||||
action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers,
|
||||
@@ -1391,7 +1380,7 @@ namespace AzToolsFramework
|
||||
|
||||
AZStd::unique_ptr<RotationManipulators> rotationManipulators =
|
||||
AZStd::make_unique<RotationManipulators>(AZ::Transform::CreateIdentity());
|
||||
rotationManipulators->SetCircleBoundWidth(ManipulatorCicleBoundWidth(ViewportUi::DefaultViewportId));
|
||||
rotationManipulators->SetCircleBoundWidth(ManipulatorCicleBoundWidth());
|
||||
|
||||
InitializeManipulators(*rotationManipulators);
|
||||
|
||||
@@ -1415,7 +1404,7 @@ namespace AzToolsFramework
|
||||
AZStd::shared_ptr<SharedRotationState> sharedRotationState = AZStd::make_shared<SharedRotationState>();
|
||||
|
||||
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_referenceFrameAtMouseDown = m_referenceFrame;
|
||||
@@ -1437,11 +1426,13 @@ namespace AzToolsFramework
|
||||
BeginRecordManipulatorCommand();
|
||||
});
|
||||
|
||||
ViewportInteraction::KeyboardModifiers prevModifiers{};
|
||||
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 Influence influence = InfluenceFromModifiers(action.m_modifiers);
|
||||
|
||||
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)
|
||||
// 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
|
||||
// manually modifying the manipulator orientation independent of the entity by holding ctrl
|
||||
if ((sharedRotationState->m_referenceFrameAtMouseDown == ReferenceFrame::Local &&
|
||||
m_entityIdManipulators.m_lookups.size() == 1) ||
|
||||
action.m_modifiers.Ctrl())
|
||||
if (sharedRotationState->m_referenceFrameAtMouseDown == ReferenceFrame::Local || action.m_modifiers.Ctrl())
|
||||
{
|
||||
m_entityIdManipulators.m_manipulators->SetLocalTransform(AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
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
|
||||
if (prevModifiers != action.m_modifiers)
|
||||
{
|
||||
UpdateInitialRotation(m_entityIdManipulators);
|
||||
UpdateInitialTransform(m_entityIdManipulators);
|
||||
sharedRotationState->m_savedOrientation = action.m_current.m_delta.GetInverseFull();
|
||||
}
|
||||
|
||||
// allow the user to modify the orientation without moving the object if ctrl is held
|
||||
if (action.m_modifiers.Ctrl())
|
||||
{
|
||||
UpdateInitialRotation(m_entityIdManipulators);
|
||||
UpdateInitialTransform(m_entityIdManipulators);
|
||||
sharedRotationState->m_savedOrientation = action.m_current.m_delta.GetInverseFull();
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto pivotOrientation = ETCS::CalculateSelectionPivotOrientation(
|
||||
m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, ReferenceFrame::Parent);
|
||||
// only update the pivot override if the orientation is being modified in local space and we have
|
||||
// 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
|
||||
for (AZ::EntityId entityId : sharedRotationState->m_entityIds)
|
||||
@@ -1492,9 +1485,9 @@ namespace AzToolsFramework
|
||||
const AZ::Transform offsetRotation =
|
||||
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::Vector3 position = entityIdLookupIt->second.m_initial.GetTranslation();
|
||||
@@ -1510,23 +1503,10 @@ namespace AzToolsFramework
|
||||
AZ::Transform::CreateTranslation(-centerOffset) * AZ::Transform::CreateUniformScale(scale));
|
||||
}
|
||||
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(
|
||||
AZ::Quaternion::CreateIdentity(),
|
||||
m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation());
|
||||
manipulatorOrientation, m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation());
|
||||
const AZ::Transform transformInPivotSpace =
|
||||
pivotTransform.GetInverse() * entityIdLookupIt->second.m_initial;
|
||||
|
||||
@@ -1561,7 +1541,7 @@ namespace AzToolsFramework
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
AZStd::unique_ptr<ScaleManipulators> scaleManipulators = AZStd::make_unique<ScaleManipulators>(AZ::Transform::CreateIdentity());
|
||||
scaleManipulators->SetLineBoundWidth(ManipulatorLineBoundWidth(ViewportUi::DefaultViewportId));
|
||||
scaleManipulators->SetLineBoundWidth(ManipulatorLineBoundWidth());
|
||||
|
||||
InitializeManipulators(*scaleManipulators);
|
||||
|
||||
@@ -1571,13 +1551,20 @@ namespace AzToolsFramework
|
||||
scaleManipulators->SetAxes(AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ());
|
||||
scaleManipulators->ConfigureView(2.0f, AZ::Color::CreateOne(), AZ::Color::CreateOne(), AZ::Color::CreateOne());
|
||||
|
||||
// lambdas capture shared_ptr by value to increment ref count
|
||||
auto manipulatorEntityIds = AZStd::make_shared<ManipulatorEntityIds>();
|
||||
|
||||
auto uniformLeftMouseDownCallback = [this, manipulatorEntityIds]([[maybe_unused]] const LinearManipulator::Action& action)
|
||||
struct SharedScaleState
|
||||
{
|
||||
AZ::Vector3 m_savedScaleOffset = AZ::Vector3::CreateZero();
|
||||
EntityIdList m_entityIds;
|
||||
};
|
||||
|
||||
// lambdas capture shared_ptr by value to increment ref count
|
||||
auto sharedScaleState = AZStd::make_shared<SharedScaleState>();
|
||||
|
||||
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
|
||||
BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds->m_entityIds);
|
||||
BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, sharedScaleState->m_entityIds);
|
||||
|
||||
for (auto& entityIdLookup : m_entityIdManipulators.m_lookups)
|
||||
{
|
||||
@@ -1591,20 +1578,32 @@ namespace AzToolsFramework
|
||||
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::Events::OnEntityTransformChanged,
|
||||
manipulatorEntityIds->m_entityIds);
|
||||
&AzToolsFramework::EditorTransformChangeNotificationBus::Events::OnEntityTransformChanged, sharedScaleState->m_entityIds);
|
||||
|
||||
m_entityIdManipulators.m_manipulators->SetLocalTransform(RecalculateAverageManipulatorTransform(
|
||||
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
|
||||
for (AZ::EntityId entityId : manipulatorEntityIds->m_entityIds)
|
||||
for (AZ::EntityId entityId : sharedScaleState->m_entityIds)
|
||||
{
|
||||
auto entityIdLookupIt = m_entityIdManipulators.m_lookups.find(entityId);
|
||||
if (entityIdLookupIt == m_entityIdManipulators.m_lookups.end())
|
||||
@@ -1620,26 +1619,33 @@ namespace AzToolsFramework
|
||||
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 AZ::Transform scaleTransform = AZ::Transform::CreateUniformScale(scale);
|
||||
|
||||
if (action.m_modifiers.Alt())
|
||||
switch (InfluenceFromModifiers(action.m_modifiers))
|
||||
{
|
||||
const AZ::Transform pivotTransform = TransformNormalizedScale(entityIdLookupIt->second.m_initial);
|
||||
const AZ::Transform transformInPivotSpace = pivotTransform.GetInverse() * initial;
|
||||
case Influence::Individual:
|
||||
{
|
||||
const AZ::Transform pivotTransform = TransformNormalizedScale(entityIdLookupIt->second.m_initial);
|
||||
const AZ::Transform transformInPivotSpace = pivotTransform.GetInverse() * initial;
|
||||
|
||||
SetEntityWorldTransform(entityId, pivotTransform * scaleTransform * transformInPivotSpace);
|
||||
}
|
||||
else
|
||||
{
|
||||
const AZ::Transform pivotTransform =
|
||||
TransformNormalizedScale(m_entityIdManipulators.m_manipulators->GetLocalTransform());
|
||||
const AZ::Transform transformInPivotSpace = pivotTransform.GetInverse() * initial;
|
||||
SetEntityWorldTransform(entityId, pivotTransform * scaleTransform * transformInPivotSpace);
|
||||
}
|
||||
break;
|
||||
case Influence::Group:
|
||||
{
|
||||
const AZ::Transform pivotTransform =
|
||||
TransformNormalizedScale(m_entityIdManipulators.m_manipulators->GetLocalTransform());
|
||||
const AZ::Transform transformInPivotSpace = pivotTransform.GetInverse() * initial;
|
||||
|
||||
SetEntityWorldTransform(entityId, pivotTransform * scaleTransform * transformInPivotSpace);
|
||||
SetEntityWorldTransform(entityId, pivotTransform * scaleTransform * transformInPivotSpace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prevModifiers = action.m_modifiers;
|
||||
};
|
||||
|
||||
scaleManipulators->InstallAxisLeftMouseDownCallback(uniformLeftMouseDownCallback);
|
||||
@@ -2729,8 +2735,7 @@ namespace AzToolsFramework
|
||||
|
||||
if (m_pivotOverrideFrame.m_orientationOverride && m_entityIdManipulators.m_manipulators)
|
||||
{
|
||||
m_pivotOverrideFrame.m_orientationOverride =
|
||||
QuaternionFromTransformNoScaling(m_entityIdManipulators.m_manipulators->GetLocalTransform());
|
||||
m_pivotOverrideFrame.m_orientationOverride = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetRotation();
|
||||
}
|
||||
|
||||
if (m_pivotOverrideFrame.m_translationOverride && m_entityIdManipulators.m_manipulators)
|
||||
@@ -3337,7 +3342,7 @@ namespace AzToolsFramework
|
||||
|
||||
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(
|
||||
AZ::Transform::CreateIdentity(), transform.GetTranslation(), TransformDirectionNoScaling(transform, axis),
|
||||
@@ -3554,7 +3559,7 @@ namespace AzToolsFramework
|
||||
// screen space
|
||||
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());
|
||||
return result + screenOffset;
|
||||
};
|
||||
|
||||
+10
-1
@@ -96,6 +96,14 @@ namespace AzToolsFramework
|
||||
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.
|
||||
enum class ReferenceFrame
|
||||
{
|
||||
@@ -328,7 +336,8 @@ namespace AzToolsFramework
|
||||
OptionalFrame m_pivotOverrideFrame; //!< Has a pivot override been set.
|
||||
Mode m_mode = Mode::Translation; //!< Manipulator mode - default to translation.
|
||||
Pivot m_pivotMode = Pivot::Object; //!< Entity pivot mode - default to object (authored root).
|
||||
ReferenceFrame m_referenceFrame = ReferenceFrame::Parent; //!< What reference frame is the Manipulator currently operating in.
|
||||
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.
|
||||
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.
|
||||
|
||||
@@ -242,11 +242,11 @@ namespace UnitTest
|
||||
{
|
||||
// the initial starting position of the entities
|
||||
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(
|
||||
m_entityId2, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(m_entity2WorldTranslation));
|
||||
m_entityId2, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(Entity2WorldTranslation));
|
||||
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)
|
||||
@@ -261,9 +261,10 @@ namespace UnitTest
|
||||
AZ::EntityId m_entityId1;
|
||||
AZ::EntityId m_entityId2;
|
||||
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);
|
||||
AZ::Vector3 m_entity3WorldTranslation = AZ::Vector3(5.0f, 16.0f, 10.0f);
|
||||
|
||||
static inline const AZ::Vector3 Entity1WorldTranslation = AZ::Vector3(5.0f, 15.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)
|
||||
@@ -371,16 +372,16 @@ namespace UnitTest
|
||||
// Given
|
||||
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);
|
||||
|
||||
SetTransformMode(EditorTransformComponentSelectionRequestBus::Events::Mode::Rotation);
|
||||
|
||||
const AZ::Transform manipulatorTransformBefore = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity());
|
||||
|
||||
// check preconditions - manipulator transform matches parent/world transform (identity)
|
||||
EXPECT_THAT(manipulatorTransformBefore.GetBasisY(), IsClose(AZ::Vector3::CreateAxisY()));
|
||||
EXPECT_THAT(manipulatorTransformBefore.GetBasisZ(), IsClose(AZ::Vector3::CreateAxisZ()));
|
||||
// check preconditions - manipulator transform matches the entity transform
|
||||
EXPECT_THAT(manipulatorTransformBefore, IsClose(entityTransform));
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -624,7 +625,7 @@ namespace UnitTest
|
||||
EXPECT_TRUE(selectedEntitiesBefore.empty());
|
||||
|
||||
// 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
|
||||
m_actionDispatcher->SetStickySelect(true)
|
||||
@@ -649,7 +650,7 @@ namespace UnitTest
|
||||
EXPECT_TRUE(selectedEntitiesBefore.empty());
|
||||
|
||||
// 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
|
||||
m_actionDispatcher->SetStickySelect(false)
|
||||
@@ -728,7 +729,7 @@ namespace UnitTest
|
||||
AzToolsFramework::SelectEntity(m_entityId1);
|
||||
|
||||
// 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
|
||||
m_actionDispatcher->SetStickySelect(true)
|
||||
@@ -754,7 +755,7 @@ namespace UnitTest
|
||||
AzToolsFramework::SelectEntity(m_entityId1);
|
||||
|
||||
// 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
|
||||
m_actionDispatcher->SetStickySelect(false)
|
||||
@@ -780,7 +781,7 @@ namespace UnitTest
|
||||
AzToolsFramework::SelectEntity(m_entityId1);
|
||||
|
||||
// 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
|
||||
m_actionDispatcher->SetStickySelect(true)
|
||||
@@ -806,7 +807,7 @@ namespace UnitTest
|
||||
AzToolsFramework::SelectEntity(m_entityId1);
|
||||
|
||||
// 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
|
||||
m_actionDispatcher->SetStickySelect(false)
|
||||
@@ -832,7 +833,7 @@ namespace UnitTest
|
||||
AzToolsFramework::SelectEntities({ m_entityId1, m_entityId2 });
|
||||
|
||||
// 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
|
||||
m_actionDispatcher->SetStickySelect(true)
|
||||
@@ -858,7 +859,7 @@ namespace UnitTest
|
||||
AzToolsFramework::SelectEntities({ m_entityId1, m_entityId2 });
|
||||
|
||||
// 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
|
||||
m_actionDispatcher->SetStickySelect(false)
|
||||
@@ -1001,7 +1002,7 @@ namespace UnitTest
|
||||
AzToolsFramework::SelectEntity(m_entityId1);
|
||||
|
||||
// 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
|
||||
m_actionDispatcher->SetStickySelect(false)
|
||||
@@ -1035,7 +1036,7 @@ namespace UnitTest
|
||||
AzToolsFramework::SelectEntity(m_entityId1);
|
||||
|
||||
// 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
|
||||
m_actionDispatcher->SetStickySelect(GetParam())
|
||||
@@ -1056,7 +1057,7 @@ namespace UnitTest
|
||||
manipulatorTransform, AzToolsFramework::GetEntityContextId(),
|
||||
&AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
|
||||
|
||||
EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(m_entity2WorldTranslation));
|
||||
EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(Entity2WorldTranslation));
|
||||
}
|
||||
|
||||
TEST_P(
|
||||
@@ -1069,7 +1070,7 @@ namespace UnitTest
|
||||
AzToolsFramework::SelectEntity(m_entityId1);
|
||||
|
||||
// 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
|
||||
const auto clickOffPositionWorld = AZ::Vector3(5.0f, 15.0f, 12.0f);
|
||||
@@ -1096,7 +1097,7 @@ namespace UnitTest
|
||||
manipulatorTransform, AzToolsFramework::GetEntityContextId(),
|
||||
&AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
|
||||
|
||||
EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(m_entity2WorldTranslation));
|
||||
EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(Entity2WorldTranslation));
|
||||
})
|
||||
->MousePosition(clickOffPositionScreen)
|
||||
->KeyboardModifierDown(AzToolsFramework::ViewportInteraction::KeyboardModifier::Control)
|
||||
@@ -1113,11 +1114,560 @@ namespace UnitTest
|
||||
&AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
|
||||
|
||||
// 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));
|
||||
|
||||
// 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 =
|
||||
IndirectCallManipulatorViewportInteractionFixtureMixin<EditorTransformComponentSelectionFixture>;
|
||||
|
||||
@@ -1661,7 +2211,7 @@ namespace UnitTest
|
||||
All,
|
||||
EditorTransformComponentSelectionSingleEntityPivotAndOverrideFixture,
|
||||
testing::Values(
|
||||
ReferenceFrameWithOrientation{ AzToolsFramework::ReferenceFrame::Local, ChildExpectedPivotLocalOrientationInWorldSpace },
|
||||
ReferenceFrameWithOrientation{ AzToolsFramework::ReferenceFrame::Local, PivotOverrideLocalOrientationInWorldSpace },
|
||||
ReferenceFrameWithOrientation{ AzToolsFramework::ReferenceFrame::Parent, PivotOverrideLocalOrientationInWorldSpace },
|
||||
ReferenceFrameWithOrientation{ AzToolsFramework::ReferenceFrame::World, AZ::Quaternion::CreateIdentity() }));
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Math/Matrix3x4.h>
|
||||
#include <AzCore/Math/Matrix3x3.h>
|
||||
#include <AzCore/Math/Matrix3x4.h>
|
||||
#include <AzCore/Math/Matrix4x4.h>
|
||||
#include <AzCore/Math/Transform.h>
|
||||
#include <AzCore/Math/VectorConversions.h>
|
||||
@@ -20,18 +20,17 @@
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
// transform a point from normalized device coordinates to world space, and then from world space back to normalized device coordinates
|
||||
AZ::Vector2 ScreenNDCToWorldToScreenNDC(
|
||||
const AZ::Vector2& ndcPoint, const AzFramework::CameraState& cameraState)
|
||||
// transform a point from normalized device coordinates to world space, and then from world space back to normalized device coordinates
|
||||
AZ::Vector2 ScreenNdcToWorldToScreenNdc(const AZ::Vector2& ndcPoint, const AzFramework::CameraState& cameraState)
|
||||
{
|
||||
const auto worldResult = AzFramework::ScreenNDCToWorld(ndcPoint, InverseCameraView(cameraState), InverseCameraProjection(cameraState));
|
||||
const auto ndcResult = AzFramework::WorldToScreenNDC(worldResult, CameraView(cameraState), CameraProjection(cameraState));
|
||||
const auto worldResult =
|
||||
AzFramework::ScreenNdcToWorld(ndcPoint, InverseCameraView(cameraState), InverseCameraProjection(cameraState));
|
||||
const auto ndcResult = AzFramework::WorldToScreenNdc(worldResult, CameraView(cameraState), CameraProjection(cameraState));
|
||||
return AZ::Vector3ToVector2(ndcResult);
|
||||
}
|
||||
|
||||
// transform a point from screen space to world space, and then from world space back to screen space
|
||||
AzFramework::ScreenPoint ScreenToWorldToScreen(
|
||||
const AzFramework::ScreenPoint& screenPoint, const AzFramework::CameraState& cameraState)
|
||||
AzFramework::ScreenPoint ScreenToWorldToScreen(const AzFramework::ScreenPoint& screenPoint, const AzFramework::CameraState& cameraState)
|
||||
{
|
||||
const auto worldResult = AzFramework::ScreenToWorld(screenPoint, cameraState);
|
||||
return AzFramework::WorldToScreen(worldResult, cameraState);
|
||||
@@ -47,25 +46,25 @@ namespace UnitTest
|
||||
|
||||
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);
|
||||
EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
|
||||
}
|
||||
|
||||
{
|
||||
const auto expectedScreenPoint = ScreenPoint{400, 300};
|
||||
const auto expectedScreenPoint = ScreenPoint{ 400, 300 };
|
||||
const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, cameraState);
|
||||
EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
|
||||
}
|
||||
|
||||
{
|
||||
const auto expectedScreenPoint = ScreenPoint{0, 0};
|
||||
const auto expectedScreenPoint = ScreenPoint{ 0, 0 };
|
||||
const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, cameraState);
|
||||
EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
|
||||
}
|
||||
|
||||
{
|
||||
const auto expectedScreenPoint = ScreenPoint{800, 600};
|
||||
const auto expectedScreenPoint = ScreenPoint{ 800, 600 };
|
||||
const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, cameraState);
|
||||
EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
|
||||
}
|
||||
@@ -81,7 +80,7 @@ namespace UnitTest
|
||||
|
||||
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);
|
||||
EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
|
||||
}
|
||||
@@ -93,46 +92,46 @@ namespace UnitTest
|
||||
using AzFramework::ScreenPoint;
|
||||
|
||||
const auto screenDimensions = AZ::Vector2(800.0f, 600.0f);
|
||||
const auto cameraTransform = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 0.0f, 0.0f)) *
|
||||
AZ::Transform::CreateRotationZ(AZ::DegToRad(-90.0f));
|
||||
const auto cameraTransform =
|
||||
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 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)));
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// NDC tests
|
||||
TEST(ViewportScreen, WorldToScreenNDCAndScreenNDCToWorldReturnsTheSameValueIdentityCameraOffsetFromOrigin)
|
||||
{
|
||||
using NdcPoint = AZ::Vector2;
|
||||
|
||||
|
||||
const auto screenDimensions = AZ::Vector2(800.0f, 600.0f);
|
||||
const auto cameraPosition = AZ::Vector3::CreateAxisY(-10.0f);
|
||||
|
||||
const auto cameraState = AzFramework::CreateIdentityDefaultCamera(cameraPosition, screenDimensions);
|
||||
{
|
||||
const auto expectedNdcPoint = NdcPoint{0.75f, 0.75f};
|
||||
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState);
|
||||
const auto expectedNdcPoint = NdcPoint{ 0.75f, 0.75f };
|
||||
const auto resultNdcPoint = ScreenNdcToWorldToScreenNdc(expectedNdcPoint, cameraState);
|
||||
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
|
||||
}
|
||||
|
||||
{
|
||||
const auto expectedNdcPoint = NdcPoint{0.5f, 0.5f};
|
||||
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState);
|
||||
const auto expectedNdcPoint = NdcPoint{ 0.5f, 0.5f };
|
||||
const auto resultNdcPoint = ScreenNdcToWorldToScreenNdc(expectedNdcPoint, cameraState);
|
||||
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
|
||||
}
|
||||
|
||||
{
|
||||
const auto expectedNdcPoint = NdcPoint{0.0f, 0.0f};
|
||||
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState);
|
||||
const auto expectedNdcPoint = NdcPoint{ 0.0f, 0.0f };
|
||||
const auto resultNdcPoint = ScreenNdcToWorldToScreenNdc(expectedNdcPoint, cameraState);
|
||||
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
|
||||
}
|
||||
|
||||
{
|
||||
const auto expectedNdcPoint = NdcPoint{1.0f, 1.0f};
|
||||
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState);
|
||||
const auto expectedNdcPoint = NdcPoint{ 1.0f, 1.0f };
|
||||
const auto resultNdcPoint = ScreenNdcToWorldToScreenNdc(expectedNdcPoint, cameraState);
|
||||
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
|
||||
}
|
||||
}
|
||||
@@ -147,8 +146,8 @@ namespace UnitTest
|
||||
|
||||
const auto cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions);
|
||||
|
||||
const auto expectedNdcPoint = NdcPoint{0.25f, 0.5f};
|
||||
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState);
|
||||
const auto expectedNdcPoint = NdcPoint{ 0.25f, 0.5f };
|
||||
const auto resultNdcPoint = ScreenNdcToWorldToScreenNdc(expectedNdcPoint, cameraState);
|
||||
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
|
||||
}
|
||||
|
||||
@@ -159,12 +158,13 @@ namespace UnitTest
|
||||
using NdcPoint = AZ::Vector2;
|
||||
|
||||
const auto screenDimensions = AZ::Vector2(800.0f, 600.0f);
|
||||
const auto cameraTransform = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 0.0f, 0.0f)) *
|
||||
AZ::Transform::CreateRotationZ(AZ::DegToRad(-90.0f));
|
||||
const auto cameraTransform =
|
||||
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 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)));
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ namespace UnitTest
|
||||
using AzFramework::ScreenPoint;
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ namespace UnitTest
|
||||
using AzFramework::ScreenPoint;
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ namespace UnitTest
|
||||
using AzFramework::ScreenPoint;
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ namespace UnitTest
|
||||
using AzFramework::ScreenPoint;
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ namespace UnitTest
|
||||
using AzFramework::ScreenPoint;
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -220,8 +220,8 @@ namespace UnitTest
|
||||
using AzFramework::ScreenPoint;
|
||||
using AzFramework::ScreenVector;
|
||||
|
||||
const ScreenPoint screenPoint = ScreenPoint{100, 200};
|
||||
const ScreenVector screenVector = ScreenVector{50, 25};
|
||||
const ScreenPoint screenPoint = ScreenPoint{ 100, 200 };
|
||||
const ScreenVector screenVector = ScreenVector{ 50, 25 };
|
||||
|
||||
const AZ::Vector2 fromScreenPoint = AzFramework::Vector2FromScreenPoint(screenPoint);
|
||||
const AZ::Vector2 fromScreenVector = AzFramework::Vector2FromScreenVector(screenVector);
|
||||
@@ -295,6 +295,58 @@ namespace UnitTest
|
||||
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
|
||||
TEST(ViewportScreen, CanGetCameraTransformFromCameraViewAndBack)
|
||||
|
||||
@@ -358,8 +358,9 @@ namespace O3DE::ProjectManager
|
||||
painter.drawPixmap(backgroundRect, m_background);
|
||||
|
||||
// Draw a semi-transparent overlay to darken down the colors.
|
||||
painter.setCompositionMode (QPainter::CompositionMode_DestinationIn);
|
||||
const float overlayTransparency = 0.7f;
|
||||
// Use SourceOver, DestinationIn will make background transparent on Mac
|
||||
painter.setCompositionMode (QPainter::CompositionMode_SourceOver);
|
||||
const float overlayTransparency = 0.3f;
|
||||
painter.fillRect(backgroundRect, QColor(0, 0, 0, static_cast<int>(255.0f * overlayTransparency)));
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/EditContextConstants.inl>
|
||||
|
||||
#include <Atom/Feature/RenderCommon.h>
|
||||
#include <Atom/Feature/CoreLights/EsmShadowmapsPassData.h>
|
||||
#include <Atom/Feature/CoreLights/PhotometricValue.h>
|
||||
#include <Atom/Feature/CoreLights/ShadowConstants.h>
|
||||
@@ -33,8 +34,6 @@
|
||||
#include <CoreLights/ShadowmapPass.h>
|
||||
#include <CoreLights/ProjectedShadowmapsPass.h>
|
||||
|
||||
#include <RenderCommon.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
|
||||
@@ -6,10 +6,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <RenderCommon.h>
|
||||
|
||||
#include <Atom/RHI/RHIUtils.h>
|
||||
#include <Atom/RHI.Reflect/InputStreamLayoutBuilder.h>
|
||||
#include <Atom/Feature/RenderCommon.h>
|
||||
#include <Atom/Feature/Mesh/MeshFeatureProcessor.h>
|
||||
#include <Atom/Feature/ReflectionProbe/ReflectionProbeFeatureProcessor.h>
|
||||
#include <Atom/RPI.Public/Model/ModelLodUtils.h>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Math/Transform.h>
|
||||
#include <RenderCommon.h>
|
||||
#include <Atom/Feature/RenderCommon.h>
|
||||
#include <Atom/Feature/Mesh/MeshFeatureProcessorInterface.h>
|
||||
#include <Atom/RPI.Public/Base.h>
|
||||
#include <Atom/RPI.Public/Model/Model.h>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Include/Atom/Feature/RenderCommon.h
|
||||
Include/Atom/Feature/Utils/EditorRenderComponentAdapter.h
|
||||
Include/Atom/Feature/Utils/EditorRenderComponentAdapter.inl
|
||||
Include/Atom/Feature/Utils/EditorLightingPreset.h
|
||||
@@ -16,7 +17,6 @@ set(FILES
|
||||
Source/EditorCommonSystemComponent.cpp
|
||||
Source/EditorCommonSystemComponent.h
|
||||
Source/CommonModule.cpp
|
||||
Source/RenderCommon.h
|
||||
Source/Material/ConvertEmissiveUnitFunctorSourceData.cpp
|
||||
Source/Material/ConvertEmissiveUnitFunctorSourceData.h
|
||||
Source/Material/MaterialConverterSystemComponent.cpp
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
set(FILES
|
||||
3rdParty/ACES/ACES/Aces.h
|
||||
Include/Atom/Feature/RenderCommon.h
|
||||
Include/Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h
|
||||
Include/Atom/Feature/Automation/AtomAutomationBus.h
|
||||
Include/Atom/Feature/AuxGeom/AuxGeomFeatureProcessor.h
|
||||
@@ -52,7 +53,6 @@ set(FILES
|
||||
Source/FrameCaptureSystemComponent.h
|
||||
Source/ProfilingCaptureSystemComponent.cpp
|
||||
Source/ProfilingCaptureSystemComponent.h
|
||||
Source/RenderCommon.h
|
||||
3rdParty/ACES/ACES/Aces.cpp
|
||||
Source/ACES/AcesDisplayMapperFeatureProcessor.cpp
|
||||
Source/AuxGeom/AuxGeomBase.h
|
||||
|
||||
@@ -31,20 +31,26 @@ namespace AtomToolsFramework
|
||||
|
||||
void InspectorWidget::AddHeading(QWidget* headingWidget)
|
||||
{
|
||||
headingWidget->setParent(m_ui->m_headingSection);
|
||||
m_ui->m_headingSectionLayout->addWidget(headingWidget);
|
||||
headingWidget->setParent(this);
|
||||
m_ui->m_headingContentsLayout->addWidget(headingWidget);
|
||||
}
|
||||
|
||||
void InspectorWidget::ClearHeading()
|
||||
{
|
||||
qDeleteAll(m_ui->m_headingSection->findChildren<QWidget*>(QString(), Qt::FindDirectChildrenOnly));
|
||||
qDeleteAll(m_ui->m_headingSectionLayout->children());
|
||||
while (QLayoutItem* child = m_ui->m_headingContentsLayout->takeAt(0))
|
||||
{
|
||||
delete child->widget();
|
||||
delete child;
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorWidget::Reset()
|
||||
{
|
||||
qDeleteAll(m_ui->m_groupContents->findChildren<QWidget*>(QString(), Qt::FindDirectChildrenOnly));
|
||||
qDeleteAll(m_ui->m_groupContentsLayout->children());
|
||||
while (QLayoutItem* child = m_ui->m_groupContentsLayout->takeAt(0))
|
||||
{
|
||||
delete child->widget();
|
||||
delete child;
|
||||
}
|
||||
m_groups.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -30,104 +30,85 @@
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" name="m_sections">
|
||||
<layout class="QVBoxLayout" name="m_sectionsLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" name="m_headingSection" native="true">
|
||||
<layout class="QVBoxLayout" name="m_headingSectionLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
<layout class="QVBoxLayout" name="m_headingContentsLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="m_groupSectionLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="m_groupScrollArea">
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAsNeeded</enum>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="m_groupSection" native="true">
|
||||
<layout class="QVBoxLayout" name="m_groupSectionLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="m_groupScrollArea">
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAsNeeded</enum>
|
||||
<widget class="QWidget" name="m_groupContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>679</width>
|
||||
<height>767</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="m_groupContentsLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="m_groupContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>683</width>
|
||||
<height>763</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="m_groupContentsLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
||||
@@ -1726,7 +1726,7 @@ void AZ::FFont::DrawScreenAlignedText3d(
|
||||
{
|
||||
return;
|
||||
}
|
||||
AZ::Vector3 positionNDC = AzFramework::WorldToScreenNDC(
|
||||
AZ::Vector3 positionNDC = AzFramework::WorldToScreenNdc(
|
||||
params.m_position,
|
||||
currentView->GetWorldToViewMatrix(),
|
||||
currentView->GetViewToClipMatrix()
|
||||
|
||||
@@ -676,7 +676,6 @@ namespace PhysX
|
||||
LmbrCentral::ShapeComponentNotificationsBus::Handler::BusDisconnect();
|
||||
AZ::TransformNotificationBus::Handler::BusDisconnect();
|
||||
AzToolsFramework::EntitySelectionEvents::Bus::Handler::BusDisconnect();
|
||||
AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect();
|
||||
AzToolsFramework::Components::EditorComponentBase::Deactivate();
|
||||
|
||||
if (m_sceneInterface && m_editorBodyHandle != AzPhysics::InvalidSimulatedBodyHandle)
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/Component/NonUniformScaleBus.h>
|
||||
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
|
||||
#include <AzFramework/Physics/Shape.h>
|
||||
#include <AzFramework/Physics/Components/SimulatedBodyComponentBus.h>
|
||||
#include <AzFramework/Physics/Common/PhysicsEvents.h>
|
||||
@@ -58,7 +57,6 @@ namespace PhysX
|
||||
//! component to create geometry in the PhysX simulation.
|
||||
class EditorShapeColliderComponent
|
||||
: public AzToolsFramework::Components::EditorComponentBase
|
||||
, protected AzFramework::EntityDebugDisplayEventBus::Handler
|
||||
, protected AzToolsFramework::EntitySelectionEvents::Bus::Handler
|
||||
, private AZ::TransformNotificationBus::Handler
|
||||
, protected DebugDraw::DisplayCallback
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"description": "",
|
||||
"materialType": "PbrTerrain.materialtype",
|
||||
"parentMaterial": "",
|
||||
"propertyLayoutVersion": 1,
|
||||
"properties": {
|
||||
"settings": {
|
||||
"baseColor": [ 0.78, 0.59, 0.48 ]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
{
|
||||
"description": "A material for rendering terrain with a physically-based rendering (PBR) material shading model.",
|
||||
"propertyLayout": {
|
||||
"version": 1,
|
||||
"groups": [
|
||||
{
|
||||
"id": "settings",
|
||||
"displayName": "Settings"
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"general": [
|
||||
{
|
||||
"id": "applySpecularAA",
|
||||
"displayName": "Apply Specular AA",
|
||||
"description": "Whether to apply specular anti-aliasing in the shader.",
|
||||
"type": "Bool",
|
||||
"defaultValue": false,
|
||||
"connection": {
|
||||
"type": "ShaderOption",
|
||||
"id": "o_applySpecularAA"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "enableShadows",
|
||||
"displayName": "Enable Shadows",
|
||||
"description": "Whether to use the shadow maps.",
|
||||
"type": "Bool",
|
||||
"defaultValue": true,
|
||||
"connection": {
|
||||
"type": "ShaderOption",
|
||||
"id": "o_enableShadows"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "enableDirectionalLights",
|
||||
"displayName": "Enable Directional Lights",
|
||||
"description": "Whether to use directional lights.",
|
||||
"type": "Bool",
|
||||
"defaultValue": true,
|
||||
"connection": {
|
||||
"type": "ShaderOption",
|
||||
"id": "o_enableDirectionalLights"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "enablePunctualLights",
|
||||
"displayName": "Enable Punctual Lights",
|
||||
"description": "Whether to use punctual lights.",
|
||||
"type": "Bool",
|
||||
"defaultValue": true,
|
||||
"connection": {
|
||||
"type": "ShaderOption",
|
||||
"id": "o_enablePunctualLights"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "enableAreaLights",
|
||||
"displayName": "Enable Area Lights",
|
||||
"description": "Whether to use area lights.",
|
||||
"type": "Bool",
|
||||
"defaultValue": true,
|
||||
"connection": {
|
||||
"type": "ShaderOption",
|
||||
"id": "o_enableAreaLights"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "enableIBL",
|
||||
"displayName": "Enable IBL",
|
||||
"description": "Whether to use Image Based Lighting (IBL).",
|
||||
"type": "Bool",
|
||||
"defaultValue": true,
|
||||
"connection": {
|
||||
"type": "ShaderOption",
|
||||
"id": "o_enableIBL"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "forwardPassIBLSpecular",
|
||||
"displayName": "Forward Pass IBL Specular",
|
||||
"description": "Whether to apply IBL specular in the forward pass.",
|
||||
"type": "Bool",
|
||||
"defaultValue": false,
|
||||
"connection": {
|
||||
"type": "ShaderOption",
|
||||
"id": "o_materialUseForwardPassIBLSpecular"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settings": [
|
||||
{
|
||||
"id": "heightmapImage",
|
||||
"displayName": "Heightmap Image",
|
||||
"description": "Heightmap of the terrain, controlled by the runtime.",
|
||||
"type": "Image",
|
||||
"connection": {
|
||||
"type": "ShaderInput",
|
||||
"id": "m_heightmapImage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "baseColor",
|
||||
"displayName": "Base Color",
|
||||
"type": "Color",
|
||||
"defaultValue": [ 0.18, 0.18, 0.18 ],
|
||||
"connection": {
|
||||
"type": "ShaderInput",
|
||||
"id": "m_baseColor"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "roughness",
|
||||
"displayName": "Roughness",
|
||||
"type": "Float",
|
||||
"defaultValue": 1.0,
|
||||
"min": 0.0,
|
||||
"max": 1.0,
|
||||
"connection": {
|
||||
"type": "ShaderInput",
|
||||
"id": "m_roughness"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"shaders": [
|
||||
{
|
||||
"file": "../../Shaders/Terrain/TerrainPBR_ForwardPass.shader"
|
||||
},
|
||||
{
|
||||
"file": "../../Shaders/Terrain/Terrain_Shadowmap.shader"
|
||||
},
|
||||
{
|
||||
"file": "../../Shaders/Terrain/Terrain_DepthPass.shader"
|
||||
}
|
||||
],
|
||||
"functors": [
|
||||
]
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Atom/Features/SrgSemantics.azsli>
|
||||
#include <scenesrg.srgi>
|
||||
#include <viewsrg.srgi>
|
||||
#include "TerrainCommon.azsli"
|
||||
|
||||
struct VertexOutput
|
||||
{
|
||||
linear centroid float4 m_position : SV_Position;
|
||||
float3 m_normal : NORMAL;
|
||||
float3 m_tangent : TANGENT;
|
||||
float3 m_bitangent : BITANGENT;
|
||||
float m_height : UV;
|
||||
};
|
||||
|
||||
struct ForwardPassOutput
|
||||
{
|
||||
float4 m_diffuseColor : SV_Target0; //!< RGB = Diffuse Lighting, A = Blend Alpha (for blended surfaces) OR A = special encoding of surfaceScatteringFactor, m_subsurfaceScatteringQuality, o_enableSubsurfaceScattering
|
||||
float4 m_specularColor : SV_Target1; //!< RGB = Specular Lighting, A = Unused
|
||||
float4 m_albedo : SV_Target2; //!< RGB = Surface albedo pre-multiplied by other factors that will be multiplied later by diffuse GI, A = specularOcclusion
|
||||
float4 m_specularF0 : SV_Target3; //!< RGB = Specular F0, A = roughness
|
||||
float4 m_normal : SV_Target4; //!< RGB10 = EncodeNormalSignedOctahedron(worldNormal), A2 = multiScatterCompensationEnabled
|
||||
};
|
||||
|
||||
struct PixelOutput
|
||||
{
|
||||
float4 m_color : SV_Target0;
|
||||
};
|
||||
|
||||
VertexOutput MainVS(in VertexInput input)
|
||||
{
|
||||
VertexOutput output;
|
||||
ObjectSrg::TerrainData terrainData = ObjectSrg::m_terrainData;
|
||||
|
||||
float2 uv = input.m_uv;
|
||||
float2 origUv = lerp(terrainData.m_uvMin, terrainData.m_uvMax, uv);
|
||||
output.m_position = GetTerrainProjectedPosition(terrainData, input.m_position, origUv);
|
||||
|
||||
// Calculate normal
|
||||
float up = GetHeight(origUv + terrainData.m_uvStep * float2( 0.0f, -1.0f));
|
||||
float right = GetHeight(origUv + terrainData.m_uvStep * float2( 1.0f, 0.0f));
|
||||
float down = GetHeight(origUv + terrainData.m_uvStep * float2( 0.0f, 1.0f));
|
||||
float left = GetHeight(origUv + terrainData.m_uvStep * float2(-1.0f, 0.0f));
|
||||
|
||||
output.m_bitangent = normalize(float3(0.0, terrainData.m_sampleSpacing * 2.0f, down - up));
|
||||
output.m_tangent = normalize(float3(terrainData.m_sampleSpacing * 2.0f, 0.0, right - left));
|
||||
output.m_normal = cross(output.m_tangent, output.m_bitangent);
|
||||
|
||||
output.m_height = GetHeight(origUv);
|
||||
return output;
|
||||
}
|
||||
|
||||
ForwardPassOutput MainPS(in VertexOutput input)
|
||||
{
|
||||
ForwardPassOutput output;
|
||||
|
||||
float3 lightDirection = normalize(float3(-1.0, 1.0, -1.0));
|
||||
float3 lightIntensity = float3(1.0, 1.0, 1.0);
|
||||
if (SceneSrg::m_directionalLightCount > 0)
|
||||
{
|
||||
lightDirection = SceneSrg::m_directionalLights[0].m_direction;
|
||||
lightIntensity = SceneSrg::m_directionalLights[0].m_rgbIntensityLux;
|
||||
}
|
||||
|
||||
// Fake light intensity ranges from 1.0 for normals directly facing the light to zero for those
|
||||
// directly facing away.
|
||||
const float minLight = 0.01;
|
||||
const float midLight = 0.1;
|
||||
float lightDot = dot(normalize(input.m_normal), -lightDirection);
|
||||
lightIntensity *= lightDot > 0.0 ?
|
||||
lightDot * (1.0 - midLight) + midLight : // surface facing light
|
||||
(lightDot + 1.0) * (midLight - minLight) + minLight; // surface facing away
|
||||
|
||||
output.m_diffuseColor.rgb = 0.5 * lightIntensity;
|
||||
output.m_diffuseColor.a = 0.0f;
|
||||
|
||||
output.m_specularColor.rgb = 0.0;
|
||||
|
||||
output.m_albedo.rgb = 0.25 + input.m_height * 0.5;
|
||||
output.m_albedo.a = 0.0;
|
||||
|
||||
output.m_specularF0.rgb = 0.04;
|
||||
output.m_specularF0.a = 1.0;
|
||||
|
||||
output.m_normal.rgb = input.m_normal;
|
||||
output.m_normal.a = 0.0;
|
||||
|
||||
return output;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"Source" : "./Terrain.azsl",
|
||||
|
||||
"DepthStencilState" :
|
||||
{
|
||||
"Depth" :
|
||||
{
|
||||
"Enable" : true,
|
||||
"CompareFunc" : "GreaterEqual"
|
||||
}
|
||||
},
|
||||
|
||||
"DrawList" : "forward",
|
||||
|
||||
"ProgramSettings":
|
||||
{
|
||||
"EntryPoints":
|
||||
[
|
||||
{
|
||||
"name": "MainVS",
|
||||
"type": "Vertex"
|
||||
},
|
||||
{
|
||||
"name": "MainPS",
|
||||
"type": "Fragment"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"BlendState" : {
|
||||
"Enable" : false
|
||||
}
|
||||
}
|
||||
@@ -9,18 +9,6 @@
|
||||
|
||||
ShaderResourceGroup ObjectSrg : SRG_PerObject
|
||||
{
|
||||
Texture2D<float> m_heightmapImage;
|
||||
|
||||
Sampler PointSampler
|
||||
{
|
||||
MinFilter = Point;
|
||||
MagFilter = Point;
|
||||
MipFilter = Point;
|
||||
AddressU = Clamp;
|
||||
AddressV = Clamp;
|
||||
AddressW = Clamp;
|
||||
};
|
||||
|
||||
row_major float3x4 m_modelToWorld;
|
||||
|
||||
struct TerrainData
|
||||
@@ -33,14 +21,131 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject
|
||||
};
|
||||
|
||||
TerrainData m_terrainData;
|
||||
|
||||
// The below shouldn't be in this SRG but needs to be for now because the lighting functions depend on them.
|
||||
|
||||
//! Reflection Probe (smallest probe volume that overlaps the object position)
|
||||
struct ReflectionProbeData
|
||||
{
|
||||
row_major float3x4 m_modelToWorld;
|
||||
row_major float3x4 m_modelToWorldInverse; // does not include extents
|
||||
float3 m_outerObbHalfLengths;
|
||||
float3 m_innerObbHalfLengths;
|
||||
float m_padding;
|
||||
bool m_useReflectionProbe;
|
||||
bool m_useParallaxCorrection;
|
||||
};
|
||||
|
||||
ReflectionProbeData m_reflectionProbeData;
|
||||
TextureCube m_reflectionProbeCubeMap;
|
||||
|
||||
float4x4 GetReflectionProbeWorldMatrix()
|
||||
{
|
||||
float4x4 modelToWorld = float4x4(
|
||||
float4(1, 0, 0, 0),
|
||||
float4(0, 1, 0, 0),
|
||||
float4(0, 0, 1, 0),
|
||||
float4(0, 0, 0, 1));
|
||||
|
||||
modelToWorld[0] = m_reflectionProbeData.m_modelToWorld[0];
|
||||
modelToWorld[1] = m_reflectionProbeData.m_modelToWorld[1];
|
||||
modelToWorld[2] = m_reflectionProbeData.m_modelToWorld[2];
|
||||
return modelToWorld;
|
||||
}
|
||||
|
||||
float4x4 GetReflectionProbeWorldMatrixInverse()
|
||||
{
|
||||
float4x4 modelToWorldInverse = float4x4(
|
||||
float4(1, 0, 0, 0),
|
||||
float4(0, 1, 0, 0),
|
||||
float4(0, 0, 1, 0),
|
||||
float4(0, 0, 0, 1));
|
||||
|
||||
modelToWorldInverse[0] = m_reflectionProbeData.m_modelToWorldInverse[0];
|
||||
modelToWorldInverse[1] = m_reflectionProbeData.m_modelToWorldInverse[1];
|
||||
modelToWorldInverse[2] = m_reflectionProbeData.m_modelToWorldInverse[2];
|
||||
return modelToWorldInverse;
|
||||
}
|
||||
}
|
||||
|
||||
ShaderResourceGroup TerrainMaterialSrg : SRG_PerMaterial
|
||||
{
|
||||
Texture2D<float> m_heightmapImage;
|
||||
|
||||
Sampler HeightmapSampler
|
||||
{
|
||||
MinFilter = Linear;
|
||||
MagFilter = Linear;
|
||||
MipFilter = Point;
|
||||
AddressU = Clamp;
|
||||
AddressV = Clamp;
|
||||
AddressW = Clamp;
|
||||
};
|
||||
|
||||
float3 m_baseColor;
|
||||
float m_roughness;
|
||||
}
|
||||
|
||||
option bool o_useTerrainSmoothing = false;
|
||||
|
||||
struct VertexInput
|
||||
{
|
||||
float2 m_position : POSITION;
|
||||
float2 m_uv : UV;
|
||||
};
|
||||
|
||||
// Sample a texture with a 5 tap B-Spline. Consider ripping this out and putting in a more general location.
|
||||
// This function samples a 4x4 neighborhood around the uv. Normally this would take 16 samples, but by taking
|
||||
// advantage of bilinear filtering this can be done with 9 taps on the edges between pixels. The cost is further
|
||||
// reduced by dropping the diagonals.
|
||||
float SampleBSpline5Tap(Texture2D<float> texture, SamplerState textureSampler, float2 uv, float2 textureSize, float2 rcpTextureSize)
|
||||
{
|
||||
// Think of sample locations in the 4x4 neighborhood as having a top left coordinate of 0,0 and
|
||||
// a bottom right coordinate of 3,3.
|
||||
|
||||
// Find the position in texture space then round it to get the center of the 1,1 pixel (tc1)
|
||||
float2 texelPos = uv * textureSize;
|
||||
float2 tc1= floor(texelPos - 0.5) + 0.5;
|
||||
|
||||
// Offset from center position to texel
|
||||
float2 f = texelPos - tc1;
|
||||
|
||||
// Compute B-Spline weights based on the offset
|
||||
float2 OneMinusF = (1.0 - f);
|
||||
float2 OneMinusF2 = OneMinusF * OneMinusF;
|
||||
float2 OneMinusF3 = OneMinusF2 * OneMinusF;
|
||||
float2 w0 = OneMinusF3;
|
||||
float2 w1 = 4.0 + 3.0 * f * f * f - 6.0 * f * f;
|
||||
float2 w2 = 4.0 + 3.0 * OneMinusF3 - 6.0 * OneMinusF2;
|
||||
float2 w3 = f * f * f;
|
||||
|
||||
float2 w12 = w1 + w2;
|
||||
|
||||
// Compute uv coordinates for sampling the texture
|
||||
float2 tc0 = (tc1 - 1.0f) * rcpTextureSize;
|
||||
float2 tc3 = (tc1 + 2.0f) * rcpTextureSize;
|
||||
float2 tc12 = (tc1 + w2 / w12) * rcpTextureSize;
|
||||
|
||||
// Compute sample weights
|
||||
float sw0 = w12.x * w12.y; // middle
|
||||
float sw1 = w12.x * w0.y; // top
|
||||
float sw2 = w0.x * w12.y; // left
|
||||
float sw3 = w12.x * w3.y; // bottom
|
||||
float sw4 = w3.x * w12.y; // right
|
||||
|
||||
// total weight of samples to normalize result.
|
||||
float totalWeight = sw0 + sw1 + sw2 + sw3 + sw4;
|
||||
|
||||
float result = 0.0f;
|
||||
result += texture.SampleLevel(textureSampler, float2(tc12.x, tc12.y), 0.0).r * sw0;
|
||||
result += texture.SampleLevel(textureSampler, float2(tc12.x, tc0.y), 0.0).r * sw1;
|
||||
result += texture.SampleLevel(textureSampler, float2( tc0.x, tc12.y), 0.0).r * sw2;
|
||||
result += texture.SampleLevel(textureSampler, float2(tc12.x, tc3.y), 0.0).r * sw3;
|
||||
result += texture.SampleLevel(textureSampler, float2( tc3.x, tc12.y), 0.0).r * sw4;
|
||||
|
||||
return result / totalWeight;
|
||||
}
|
||||
|
||||
float4x4 GetObject_WorldMatrix()
|
||||
{
|
||||
float4x4 modelToWorld = float4x4(
|
||||
@@ -58,10 +163,23 @@ float4x4 GetObject_WorldMatrix()
|
||||
float GetHeight(float2 origUv)
|
||||
{
|
||||
float2 uv = clamp(origUv + (ObjectSrg::m_terrainData.m_uvStep * 0.5f), 0.0f, 1.0f);
|
||||
return ObjectSrg::m_terrainData.m_heightScale * (ObjectSrg::m_heightmapImage.SampleLevel(ObjectSrg::PointSampler, uv, 0).r - 0.5f);
|
||||
float height = 0.0f;
|
||||
|
||||
if (o_useTerrainSmoothing)
|
||||
{
|
||||
float2 textureSize;
|
||||
TerrainMaterialSrg::m_heightmapImage.GetDimensions(textureSize.x, textureSize.y);
|
||||
height = SampleBSpline5Tap(TerrainMaterialSrg::m_heightmapImage, TerrainMaterialSrg::HeightmapSampler, uv, textureSize, rcp(textureSize));
|
||||
}
|
||||
else
|
||||
{
|
||||
height = TerrainMaterialSrg::m_heightmapImage.SampleLevel(TerrainMaterialSrg::HeightmapSampler, uv, 0).r;
|
||||
}
|
||||
|
||||
return ObjectSrg::m_terrainData.m_heightScale * (height - 0.5f);
|
||||
}
|
||||
|
||||
float4 GetTerrainProjectedPosition(ObjectSrg::TerrainData terrainData, float2 vertexPosition, float2 uv)
|
||||
float3 GetTerrainWorldPosition(ObjectSrg::TerrainData terrainData, float2 vertexPosition, float2 uv)
|
||||
{
|
||||
// Remove all vertices outside our bounds by turning them into NaN positions.
|
||||
if (any(uv > 1.0) || any (uv < 0.0))
|
||||
@@ -71,6 +189,10 @@ float4 GetTerrainProjectedPosition(ObjectSrg::TerrainData terrainData, float2 ve
|
||||
|
||||
// Loop up the height and calculate our final position.
|
||||
float height = GetHeight(uv);
|
||||
float4 worldPosition = mul(GetObject_WorldMatrix(), float4(vertexPosition, height, 1.0f));
|
||||
return mul(ViewSrg::m_viewProjectionMatrix, worldPosition);
|
||||
return mul(GetObject_WorldMatrix(), float4(vertexPosition, height, 1.0f)).xyz;
|
||||
}
|
||||
|
||||
float4 GetTerrainProjectedPosition(ObjectSrg::TerrainData terrainData, float2 vertexPosition, float2 uv)
|
||||
{
|
||||
return mul(ViewSrg::m_viewProjectionMatrix, float4(GetTerrainWorldPosition(terrainData, vertexPosition, uv), 1.0));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Atom/Features/SrgSemantics.azsli>
|
||||
#include <viewsrg.srgi>
|
||||
#include <TerrainCommon.azsli>
|
||||
#include <Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli>
|
||||
#include <Atom/Features/PBR/ForwardPassSrg.azsli>
|
||||
#include <Atom/Features/PBR/ForwardPassOutput.azsli>
|
||||
#include <Atom/Features/PBR/AlphaUtils.azsli>
|
||||
#include <Atom/Features/ColorManagement/TransformColor.azsli>
|
||||
#include <Atom/Features/PBR/Lighting/StandardLighting.azsli>
|
||||
#include <Atom/Features/Shadow/DirectionalLightShadow.azsli>
|
||||
#include <Atom/Features/PBR/Decals.azsli>
|
||||
|
||||
struct VSOutput
|
||||
{
|
||||
float4 m_position : SV_Position;
|
||||
float3 m_normal: NORMAL;
|
||||
float3 m_tangent : TANGENT;
|
||||
float3 m_bitangent : BITANGENT;
|
||||
float3 m_worldPosition : UV0;
|
||||
float3 m_shadowCoords[ViewSrg::MaxCascadeCount] : UV2;
|
||||
float2 m_uv : UV1;
|
||||
};
|
||||
|
||||
VSOutput TerrainPBR_MainPassVS(VertexInput IN)
|
||||
{
|
||||
VSOutput OUT;
|
||||
|
||||
ObjectSrg::TerrainData terrainData = ObjectSrg::m_terrainData;
|
||||
|
||||
float2 uv = IN.m_uv;
|
||||
float2 origUv = lerp(terrainData.m_uvMin, terrainData.m_uvMax, uv);
|
||||
float3 worldPosition = GetTerrainWorldPosition(terrainData, IN.m_position, origUv);
|
||||
OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(worldPosition, 1.0));
|
||||
OUT.m_worldPosition = worldPosition;
|
||||
|
||||
// Calculate normal
|
||||
float up = GetHeight(origUv + terrainData.m_uvStep * float2( 0.0f, -1.0f));
|
||||
float right = GetHeight(origUv + terrainData.m_uvStep * float2( 1.0f, 0.0f));
|
||||
float down = GetHeight(origUv + terrainData.m_uvStep * float2( 0.0f, 1.0f));
|
||||
float left = GetHeight(origUv + terrainData.m_uvStep * float2(-1.0f, 0.0f));
|
||||
|
||||
OUT.m_bitangent = normalize(float3(0.0, terrainData.m_sampleSpacing * 2.0f, down - up));
|
||||
OUT.m_tangent = normalize(float3(terrainData.m_sampleSpacing * 2.0f, 0.0, right - left));
|
||||
OUT.m_normal = cross(OUT.m_tangent, OUT.m_bitangent);
|
||||
OUT.m_uv = uv;
|
||||
|
||||
// directional light shadow
|
||||
const uint shadowIndex = ViewSrg::m_shadowIndexDirectionalLight;
|
||||
if (o_enableShadows && shadowIndex < SceneSrg::m_directionalLightCount)
|
||||
{
|
||||
DirectionalLightShadow::GetShadowCoords(
|
||||
shadowIndex,
|
||||
worldPosition,
|
||||
OUT.m_shadowCoords);
|
||||
}
|
||||
|
||||
return OUT;
|
||||
}
|
||||
|
||||
ForwardPassOutput TerrainPBR_MainPassPS(VSOutput IN)
|
||||
{
|
||||
// ------- Surface -------
|
||||
|
||||
Surface surface;
|
||||
|
||||
// Position, Normal, Roughness
|
||||
surface.position = IN.m_worldPosition.xyz;
|
||||
surface.normal = normalize(IN.m_normal);
|
||||
surface.roughnessLinear = TerrainMaterialSrg::m_roughness;
|
||||
surface.CalculateRoughnessA();
|
||||
|
||||
// Albedo, SpecularF0
|
||||
const float specularF0Factor = 0.5f;
|
||||
float3 color = TerrainMaterialSrg::m_baseColor;
|
||||
surface.SetAlbedoAndSpecularF0(color, specularF0Factor, 0.0);
|
||||
|
||||
// Clear Coat, Transmission
|
||||
surface.clearCoat.InitializeToZero();
|
||||
surface.transmission.InitializeToZero();
|
||||
|
||||
// ------- LightingData -------
|
||||
|
||||
LightingData lightingData;
|
||||
|
||||
// Light iterator
|
||||
lightingData.tileIterator.Init(IN.m_position, PassSrg::m_lightListRemapped, PassSrg::m_tileLightData);
|
||||
lightingData.Init(surface.position, surface.normal, surface.roughnessLinear);
|
||||
|
||||
// Shadow, Occlusion
|
||||
lightingData.shadowCoords = IN.m_shadowCoords;
|
||||
|
||||
// Diffuse and Specular response
|
||||
lightingData.specularResponse = FresnelSchlickWithRoughness(lightingData.NdotV, surface.specularF0, surface.roughnessLinear);
|
||||
lightingData.diffuseResponse = 1.0f - lightingData.specularResponse;
|
||||
|
||||
const float alpha = 1.0f;
|
||||
|
||||
// ------- Lighting Calculation -------
|
||||
|
||||
// Apply Decals
|
||||
ApplyDecals(lightingData.tileIterator, surface);
|
||||
|
||||
// Apply Direct Lighting
|
||||
ApplyDirectLighting(surface, lightingData);
|
||||
|
||||
// Apply Image Based Lighting (IBL)
|
||||
ApplyIBL(surface, lightingData);
|
||||
|
||||
// Finalize Lighting
|
||||
lightingData.FinalizeLighting(surface.transmission.tint);
|
||||
|
||||
PbrLightingOutput lightingOutput = GetPbrLightingOutput(surface, lightingData, alpha);
|
||||
|
||||
// ------- Output -------
|
||||
|
||||
ForwardPassOutput OUT;
|
||||
|
||||
OUT.m_diffuseColor = lightingOutput.m_diffuseColor;
|
||||
OUT.m_diffuseColor.w = -1; // Subsurface scattering is disabled
|
||||
OUT.m_specularColor = lightingOutput.m_specularColor;
|
||||
OUT.m_specularF0 = lightingOutput.m_specularF0;
|
||||
OUT.m_albedo = lightingOutput.m_albedo;
|
||||
OUT.m_normal = lightingOutput.m_normal;
|
||||
|
||||
return OUT;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"Source" : "./TerrainPBR_ForwardPass.azsl",
|
||||
|
||||
"DepthStencilState" :
|
||||
{
|
||||
"Depth" :
|
||||
{
|
||||
"Enable" : true,
|
||||
"CompareFunc" : "GreaterEqual"
|
||||
},
|
||||
"Stencil" :
|
||||
{
|
||||
"Enable" : true,
|
||||
"ReadMask" : "0x00",
|
||||
"WriteMask" : "0xFF",
|
||||
"FrontFace" :
|
||||
{
|
||||
"Func" : "Always",
|
||||
"DepthFailOp" : "Keep",
|
||||
"FailOp" : "Keep",
|
||||
"PassOp" : "Replace"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"ProgramSettings":
|
||||
{
|
||||
"EntryPoints":
|
||||
[
|
||||
{
|
||||
"name": "TerrainPBR_MainPassVS",
|
||||
"type": "Vertex"
|
||||
},
|
||||
{
|
||||
"name": "TerrainPBR_MainPassPS",
|
||||
"type": "Fragment"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"DrawList" : "forward"
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <Atom/Features/SrgSemantics.azsli>
|
||||
#include <viewsrg.srgi>
|
||||
#include "TerrainCommon.azsli"
|
||||
#include <Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli>
|
||||
|
||||
struct VSDepthOutput
|
||||
{
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"Source" : "Terrain_DepthPass",
|
||||
|
||||
"DepthStencilState" : {
|
||||
"Depth" : { "Enable" : true, "CompareFunc" : "LessEqual" }
|
||||
},
|
||||
|
||||
"DrawList" : "shadow",
|
||||
|
||||
"RasterState" :
|
||||
{
|
||||
"depthBias" : "10",
|
||||
"depthBiasSlopeScale" : "4"
|
||||
},
|
||||
|
||||
"ProgramSettings":
|
||||
{
|
||||
"EntryPoints":
|
||||
[
|
||||
{
|
||||
"name": "MainVS",
|
||||
"type": "Vertex"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ ly_add_target(
|
||||
AZ::AzFramework
|
||||
Gem::Atom_RPI.Public
|
||||
Gem::Atom_Utils.Static
|
||||
Gem::Atom_Feature_Common
|
||||
Gem::GradientSignal
|
||||
Gem::SurfaceData
|
||||
Gem::LmbrCentral
|
||||
|
||||
@@ -20,28 +20,39 @@
|
||||
#include <Atom/RPI.Public/RPIUtils.h>
|
||||
#include <Atom/RPI.Public/Scene.h>
|
||||
#include <Atom/RPI.Public/View.h>
|
||||
#include <Atom/RPI.Public/MeshDrawPacket.h>
|
||||
#include <Atom/RPI.Public/AuxGeom/AuxGeomFeatureProcessorInterface.h>
|
||||
#include <Atom/RPI.Public/AuxGeom/AuxGeomDraw.h>
|
||||
#include <Atom/RPI.Public/Buffer/BufferSystem.h>
|
||||
#include <Atom/RPI.Public/Image/ImageSystemInterface.h>
|
||||
#include <Atom/RPI.Public/Image/StreamingImagePool.h>
|
||||
#include <Atom/RPI.Public/Model/Model.h>
|
||||
#include <Atom/RPI.Public/Material/Material.h>
|
||||
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
|
||||
#include <Atom/RPI.Reflect/Buffer/BufferAssetCreator.h>
|
||||
#include <Atom/RPI.Reflect/Model/ModelAssetCreator.h>
|
||||
#include <Atom/RPI.Reflect/Model/ModelLodAssetCreator.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
|
||||
#include <Atom/RPI.Reflect/Image/ImageMipChainAssetCreator.h>
|
||||
#include <Atom/RPI.Reflect/Image/StreamingImageAssetCreator.h>
|
||||
#include <Atom/RHI.Reflect/InputStreamLayout.h>
|
||||
#include <Atom/RHI.Reflect/InputStreamLayoutBuilder.h>
|
||||
|
||||
#include <Atom/Feature/RenderCommon.h>
|
||||
|
||||
namespace Terrain
|
||||
{
|
||||
namespace
|
||||
{
|
||||
const uint32_t DEFAULT_UploadBufferSize = 512 * 1024; // 512k
|
||||
[[maybe_unused]] const char* TerrainFPName = "TerrainFeatureProcessor";
|
||||
}
|
||||
|
||||
namespace MaterialInputs
|
||||
{
|
||||
static const char* const HeightmapImage("settings.heightmapImage");
|
||||
}
|
||||
|
||||
namespace ShaderInputs
|
||||
{
|
||||
static const char* const HeightmapImage("m_heightmapImage");
|
||||
static const char* const ModelToWorld("m_modelToWorld");
|
||||
static const char* const TerrainData("m_terrainData");
|
||||
}
|
||||
@@ -60,142 +71,44 @@ namespace Terrain
|
||||
void TerrainFeatureProcessor::Activate()
|
||||
{
|
||||
m_areaData = {};
|
||||
|
||||
InitializeAtomStuff();
|
||||
EnableSceneNotification();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
void TerrainFeatureProcessor::ConfigurePipelineState(ShaderState& shaderState, bool assertOnFail)
|
||||
void TerrainFeatureProcessor::Initialize()
|
||||
{
|
||||
if (shaderState.m_shader == nullptr)
|
||||
{
|
||||
AZ_Assert(shaderState.m_shader || !assertOnFail, "Terrain shader failed to load correctly.");
|
||||
return;
|
||||
}
|
||||
|
||||
bool success = GetParentScene()->ConfigurePipelineState(shaderState.m_shader->GetDrawListTag(), shaderState.m_pipelineStateDescriptor);
|
||||
AZ_Assert(success || !assertOnFail, "Couldn't configure the pipeline state.");
|
||||
if (success)
|
||||
{
|
||||
shaderState.m_pipelineState = shaderState.m_shader->AcquirePipelineState(shaderState.m_pipelineStateDescriptor);
|
||||
AZ_Assert(shaderState.m_pipelineState, "Failed to acquire default pipeline state.");
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainFeatureProcessor::InitializeAtomStuff()
|
||||
{
|
||||
m_rhiSystem = AZ::RHI::RHISystemInterface::Get();
|
||||
|
||||
{
|
||||
auto LoadShader = [this](const char* filePath, ShaderState& shaderState)
|
||||
{
|
||||
shaderState.m_shader = AZ::RPI::LoadShader(filePath);
|
||||
if (!shaderState.m_shader)
|
||||
// Load the terrain material asynchronously
|
||||
const AZStd::string materialFilePath = "Materials/Terrain/DefaultPbrTerrain.azmaterial";
|
||||
m_materialAssetLoader = AZStd::make_unique<AZ::RPI::AssetUtils::AsyncAssetLoader>();
|
||||
*m_materialAssetLoader = AZ::RPI::AssetUtils::AsyncAssetLoader::Create<AZ::RPI::MaterialAsset>(materialFilePath, 0u,
|
||||
[&](AZ::Data::Asset<AZ::Data::AssetData> assetData, bool success) -> void
|
||||
{
|
||||
AZ_Error(TerrainFPName, false, "Failed to find or create a shader instance from shader asset '%s'", filePath);
|
||||
return;
|
||||
const AZ::Data::Asset<AZ::RPI::MaterialAsset>& materialAsset = static_cast<AZ::Data::Asset<AZ::RPI::MaterialAsset>>(assetData);
|
||||
if (success)
|
||||
{
|
||||
m_materialInstance = AZ::RPI::Material::FindOrCreate(assetData);
|
||||
if (!materialAsset->GetObjectSrgLayout())
|
||||
{
|
||||
AZ_Error("TerrainFeatureProcessor", false, "No per-object ShaderResourceGroup found on terrain material.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the data layout
|
||||
shaderState.m_pipelineStateDescriptor = AZ::RHI::PipelineStateDescriptorForDraw{};
|
||||
{
|
||||
AZ::RHI::InputStreamLayoutBuilder layoutBuilder;
|
||||
|
||||
layoutBuilder.AddBuffer()
|
||||
->Channel("POSITION", AZ::RHI::Format::R32G32_FLOAT)
|
||||
->Channel("UV", AZ::RHI::Format::R32G32_FLOAT)
|
||||
;
|
||||
shaderState.m_pipelineStateDescriptor.m_inputStreamLayout = layoutBuilder.End();
|
||||
}
|
||||
|
||||
auto shaderVariant = shaderState.m_shader->GetVariant(AZ::RPI::ShaderAsset::RootShaderVariantStableId);
|
||||
shaderVariant.ConfigurePipelineState(shaderState.m_pipelineStateDescriptor);
|
||||
|
||||
// If this fails to run now, it's ok, we'll initialize it in OnRenderPipelineAdded later.
|
||||
ConfigurePipelineState(shaderState, false);
|
||||
};
|
||||
|
||||
LoadShader("Shaders/Terrain/Terrain.azshader", m_shaderStates[ShaderType::Forward]);
|
||||
LoadShader("Shaders/Terrain/Terrain_DepthPass.azshader", m_shaderStates[ShaderType::Depth]);
|
||||
|
||||
// Forward and depth shader use same srg layout.
|
||||
AZ::RHI::Ptr<AZ::RHI::ShaderResourceGroupLayout> perObjectSrgLayout =
|
||||
m_shaderStates[ShaderType::Forward].m_shader->FindShaderResourceGroupLayout(AZ::Name{"ObjectSrg"});
|
||||
|
||||
if (!perObjectSrgLayout)
|
||||
{
|
||||
AZ_Error(TerrainFPName, false, "Failed to get shader resource group layout");
|
||||
return;
|
||||
}
|
||||
else if (!perObjectSrgLayout->IsFinalized())
|
||||
{
|
||||
AZ_Error(TerrainFPName, false, "Shader resource group layout is not loaded");
|
||||
return;
|
||||
}
|
||||
|
||||
m_heightmapImageIndex = perObjectSrgLayout->FindShaderInputImageIndex(AZ::Name(ShaderInputs::HeightmapImage));
|
||||
AZ_Error(TerrainFPName, m_heightmapImageIndex.IsValid(), "Failed to find shader input image %s.", ShaderInputs::HeightmapImage);
|
||||
|
||||
m_modelToWorldIndex = perObjectSrgLayout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::ModelToWorld));
|
||||
AZ_Error(TerrainFPName, m_modelToWorldIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::ModelToWorld);
|
||||
|
||||
m_terrainDataIndex = perObjectSrgLayout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::TerrainData));
|
||||
AZ_Error(TerrainFPName, m_terrainDataIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::TerrainData);
|
||||
);
|
||||
}
|
||||
|
||||
AZ::RHI::BufferPoolDescriptor dmaPoolDescriptor;
|
||||
dmaPoolDescriptor.m_heapMemoryLevel = AZ::RHI::HeapMemoryLevel::Host;
|
||||
dmaPoolDescriptor.m_bindFlags = AZ::RHI::BufferBindFlags::InputAssembly;
|
||||
|
||||
m_hostPool = AZ::RHI::Factory::Get().CreateBufferPool();
|
||||
m_hostPool->SetName(AZ::Name("TerrainVertexPool"));
|
||||
AZ::RHI::ResultCode resultCode = m_hostPool->Init(*m_rhiSystem->GetDevice(), dmaPoolDescriptor);
|
||||
|
||||
if (resultCode != AZ::RHI::ResultCode::Success)
|
||||
{
|
||||
AZ_Error(TerrainFPName, false, "Failed to create host buffer pool from RPI");
|
||||
return;
|
||||
}
|
||||
|
||||
InitializeTerrainPatch();
|
||||
|
||||
if (!InitializeRenderBuffers())
|
||||
if (!InitializePatchModel())
|
||||
{
|
||||
AZ_Error(TerrainFPName, false, "Failed to create Terrain render buffers!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainFeatureProcessor::OnRenderPipelineAdded([[maybe_unused]] AZ::RPI::RenderPipelinePtr pipeline)
|
||||
{
|
||||
for (ShaderState& shaderState: m_shaderStates)
|
||||
{
|
||||
ConfigurePipelineState(shaderState, true);
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainFeatureProcessor::OnRenderPipelineRemoved([[maybe_unused]] AZ::RPI::RenderPipeline* pipeline)
|
||||
{
|
||||
}
|
||||
|
||||
void TerrainFeatureProcessor::OnRenderPipelinePassesChanged([[maybe_unused]] AZ::RPI::RenderPipeline* renderPipeline)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void TerrainFeatureProcessor::Deactivate()
|
||||
{
|
||||
DisableSceneNotification();
|
||||
|
||||
DestroyRenderBuffers();
|
||||
m_patchModel = {};
|
||||
m_areaData = {};
|
||||
|
||||
if (m_hostPool)
|
||||
{
|
||||
m_hostPool.reset();
|
||||
}
|
||||
|
||||
m_rhiSystem = nullptr;
|
||||
}
|
||||
|
||||
void TerrainFeatureProcessor::Render(const AZ::RPI::FeatureProcessor::RenderPacket& packet)
|
||||
@@ -254,240 +167,269 @@ namespace Terrain
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzRender);
|
||||
|
||||
if ((m_shaderStates[ShaderType::Forward].m_shader == nullptr) ||
|
||||
(m_shaderStates[ShaderType::Depth].m_shader == nullptr) ||
|
||||
m_shaderStates[ShaderType::Forward].m_shader->GetDrawListTag().IsNull() ||
|
||||
m_shaderStates[ShaderType::Depth].m_shader->GetDrawListTag().IsNull())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_areaData.m_terrainBounds.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_areaData.m_propertiesDirty)
|
||||
if (m_areaData.m_propertiesDirty && m_materialInstance)
|
||||
{
|
||||
m_areaData.m_propertiesDirty = false;
|
||||
m_sectorData.clear();
|
||||
|
||||
AZ::RHI::DrawPacketBuilder drawPacketBuilder;
|
||||
AZ::RPI::MaterialPropertyIndex heightmapPropertyIndex =
|
||||
m_materialInstance->GetMaterialPropertiesLayout()->FindPropertyIndex(AZ::Name(MaterialInputs::HeightmapImage));
|
||||
AZ_Error(TerrainFPName, heightmapPropertyIndex.IsValid(), "Failed to find material input constant %s.", MaterialInputs::HeightmapImage);
|
||||
AZ::Data::Instance<AZ::RPI::Image> heightmapImage = m_areaData.m_heightmapImage;
|
||||
m_materialInstance->SetPropertyValue(heightmapPropertyIndex, heightmapImage);
|
||||
m_materialInstance->Compile();
|
||||
|
||||
uint32_t numIndices = static_cast<uint32_t>(m_gridIndices.size());
|
||||
const auto layout = m_materialInstance->GetAsset()->GetObjectSrgLayout();
|
||||
|
||||
AZ::RHI::DrawIndexed drawIndexed;
|
||||
drawIndexed.m_indexCount = numIndices;
|
||||
drawIndexed.m_indexOffset = 0;
|
||||
drawIndexed.m_vertexOffset = 0;
|
||||
m_modelToWorldIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::ModelToWorld));
|
||||
AZ_Error(TerrainFPName, m_modelToWorldIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::ModelToWorld);
|
||||
|
||||
m_terrainDataIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::TerrainData));
|
||||
AZ_Error(TerrainFPName, m_terrainDataIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::TerrainData);
|
||||
|
||||
float xFirstPatchStart =
|
||||
m_areaData.m_terrainBounds.GetMin().GetX() - fmod(m_areaData.m_terrainBounds.GetMin().GetX(), m_gridMeters);
|
||||
float xLastPatchStart = m_areaData.m_terrainBounds.GetMax().GetX() - fmod(m_areaData.m_terrainBounds.GetMax().GetX(), m_gridMeters);
|
||||
m_areaData.m_terrainBounds.GetMin().GetX() - fmod(m_areaData.m_terrainBounds.GetMin().GetX(), GridMeters);
|
||||
float xLastPatchStart = m_areaData.m_terrainBounds.GetMax().GetX() - fmod(m_areaData.m_terrainBounds.GetMax().GetX(), GridMeters);
|
||||
float yFirstPatchStart =
|
||||
m_areaData.m_terrainBounds.GetMin().GetY() - fmod(m_areaData.m_terrainBounds.GetMin().GetY(), m_gridMeters);
|
||||
float yLastPatchStart = m_areaData.m_terrainBounds.GetMax().GetY() - fmod(m_areaData.m_terrainBounds.GetMax().GetY(), m_gridMeters);
|
||||
m_areaData.m_terrainBounds.GetMin().GetY() - fmod(m_areaData.m_terrainBounds.GetMin().GetY(), GridMeters);
|
||||
float yLastPatchStart = m_areaData.m_terrainBounds.GetMax().GetY() - fmod(m_areaData.m_terrainBounds.GetMax().GetY(), GridMeters);
|
||||
|
||||
for (float yPatch = yFirstPatchStart; yPatch <= yLastPatchStart; yPatch += m_gridMeters)
|
||||
for (float yPatch = yFirstPatchStart; yPatch <= yLastPatchStart; yPatch += GridMeters)
|
||||
{
|
||||
for (float xPatch = xFirstPatchStart; xPatch <= xLastPatchStart; xPatch += m_gridMeters)
|
||||
for (float xPatch = xFirstPatchStart; xPatch <= xLastPatchStart; xPatch += GridMeters)
|
||||
{
|
||||
drawPacketBuilder.Begin(nullptr);
|
||||
drawPacketBuilder.SetDrawArguments(drawIndexed);
|
||||
drawPacketBuilder.SetIndexBufferView(m_indexBufferView);
|
||||
auto& forwardShader = m_shaderStates[ShaderType::Forward].m_shader;
|
||||
|
||||
auto resourceGroup = AZ::RPI::ShaderResourceGroup::Create(forwardShader->GetAsset(), forwardShader->GetSupervariantIndex(), AZ::Name("ObjectSrg"));
|
||||
if (!resourceGroup)
|
||||
const auto& materialAsset = m_materialInstance->GetAsset();
|
||||
auto& shaderAsset = materialAsset->GetMaterialTypeAsset()->GetShaderAssetForObjectSrg();
|
||||
auto objectSrg = AZ::RPI::ShaderResourceGroup::Create(shaderAsset, materialAsset->GetObjectSrgLayout()->GetName());
|
||||
if (!objectSrg)
|
||||
{
|
||||
AZ_Error(TerrainFPName, false, "Failed to create shader resource group");
|
||||
return;
|
||||
AZ_Warning("TerrainFeatureProcessor", false, "Failed to create a new shader resource group, skipping.");
|
||||
continue;
|
||||
}
|
||||
|
||||
AZStd::array<float, 2> uvMin = { 0.0f, 0.0f };
|
||||
AZStd::array<float, 2> uvMax = { 1.0f, 1.0f };
|
||||
{ // Update SRG
|
||||
|
||||
AZStd::array<float, 2> uvMin = { 0.0f, 0.0f };
|
||||
AZStd::array<float, 2> uvMax = { 1.0f, 1.0f };
|
||||
|
||||
uvMin[0] = (float)((xPatch - m_areaData.m_terrainBounds.GetMin().GetX()) / m_areaData.m_terrainBounds.GetXExtent());
|
||||
uvMin[1] = (float)((yPatch - m_areaData.m_terrainBounds.GetMin().GetY()) / m_areaData.m_terrainBounds.GetYExtent());
|
||||
uvMin[0] = (float)((xPatch - m_areaData.m_terrainBounds.GetMin().GetX()) / m_areaData.m_terrainBounds.GetXExtent());
|
||||
uvMin[1] = (float)((yPatch - m_areaData.m_terrainBounds.GetMin().GetY()) / m_areaData.m_terrainBounds.GetYExtent());
|
||||
|
||||
uvMax[0] =
|
||||
(float)(((xPatch + m_gridMeters) - m_areaData.m_terrainBounds.GetMin().GetX()) / m_areaData.m_terrainBounds.GetXExtent());
|
||||
uvMax[1] =
|
||||
(float)(((yPatch + m_gridMeters) - m_areaData.m_terrainBounds.GetMin().GetY()) / m_areaData.m_terrainBounds.GetYExtent());
|
||||
uvMax[0] =
|
||||
(float)(((xPatch + GridMeters) - m_areaData.m_terrainBounds.GetMin().GetX()) / m_areaData.m_terrainBounds.GetXExtent());
|
||||
uvMax[1] =
|
||||
(float)(((yPatch + GridMeters) - m_areaData.m_terrainBounds.GetMin().GetY()) / m_areaData.m_terrainBounds.GetYExtent());
|
||||
|
||||
AZStd::array<float, 2> uvStep =
|
||||
{
|
||||
1.0f / m_areaData.m_heightmapImageWidth, 1.0f / m_areaData.m_heightmapImageHeight,
|
||||
};
|
||||
AZStd::array<float, 2> uvStep =
|
||||
{
|
||||
1.0f / m_areaData.m_heightmapImageWidth, 1.0f / m_areaData.m_heightmapImageHeight,
|
||||
};
|
||||
|
||||
AZ::Transform transform = m_areaData.m_transform;
|
||||
transform.SetTranslation(xPatch, yPatch, m_areaData.m_transform.GetTranslation().GetZ());
|
||||
AZ::Transform transform = m_areaData.m_transform;
|
||||
transform.SetTranslation(xPatch, yPatch, m_areaData.m_transform.GetTranslation().GetZ());
|
||||
|
||||
AZ::Matrix3x4 matrix3x4 = AZ::Matrix3x4::CreateFromTransform(transform);
|
||||
AZ::Matrix3x4 matrix3x4 = AZ::Matrix3x4::CreateFromTransform(transform);
|
||||
|
||||
resourceGroup->SetImage(m_heightmapImageIndex, m_areaData.m_heightmapImage);
|
||||
resourceGroup->SetConstant(m_modelToWorldIndex, matrix3x4);
|
||||
objectSrg->SetConstant(m_modelToWorldIndex, matrix3x4);
|
||||
|
||||
ShaderTerrainData terrainDataForSrg;
|
||||
terrainDataForSrg.m_sampleSpacing = m_areaData.m_sampleSpacing;
|
||||
terrainDataForSrg.m_heightScale = m_areaData.m_heightScale;
|
||||
terrainDataForSrg.m_uvMin = uvMin;
|
||||
terrainDataForSrg.m_uvMax = uvMax;
|
||||
terrainDataForSrg.m_uvStep = uvStep;
|
||||
resourceGroup->SetConstant(m_terrainDataIndex, terrainDataForSrg);
|
||||
ShaderTerrainData terrainDataForSrg;
|
||||
terrainDataForSrg.m_sampleSpacing = m_areaData.m_sampleSpacing;
|
||||
terrainDataForSrg.m_heightScale = m_areaData.m_heightScale;
|
||||
terrainDataForSrg.m_uvMin = uvMin;
|
||||
terrainDataForSrg.m_uvMax = uvMax;
|
||||
terrainDataForSrg.m_uvStep = uvStep;
|
||||
objectSrg->SetConstant(m_terrainDataIndex, terrainDataForSrg);
|
||||
|
||||
resourceGroup->Compile();
|
||||
drawPacketBuilder.AddShaderResourceGroup(resourceGroup->GetRHIShaderResourceGroup());
|
||||
|
||||
auto addDrawItem = [&](ShaderState& shaderState)
|
||||
{
|
||||
AZ::RHI::DrawPacketBuilder::DrawRequest drawRequest;
|
||||
drawRequest.m_listTag = shaderState.m_shader->GetDrawListTag();
|
||||
drawRequest.m_pipelineState = shaderState.m_pipelineState.get();
|
||||
drawRequest.m_streamBufferViews = AZStd::array_view<AZ::RHI::StreamBufferView>(&m_vertexBufferView, 1);
|
||||
drawPacketBuilder.AddDrawItem(drawRequest);
|
||||
};
|
||||
|
||||
for (ShaderState& shaderState : m_shaderStates)
|
||||
{
|
||||
addDrawItem(shaderState);
|
||||
objectSrg->Compile();
|
||||
}
|
||||
//addDrawItem(m_shaderStates[ShaderType::Forward]);
|
||||
|
||||
m_sectorData.emplace_back(
|
||||
drawPacketBuilder.End(),
|
||||
|
||||
m_sectorData.push_back();
|
||||
SectorData& sectorData = m_sectorData.back();
|
||||
|
||||
for (auto& lod : m_patchModel->GetLods())
|
||||
{
|
||||
AZ::RPI::ModelLod& modelLod = *lod.get();
|
||||
sectorData.m_drawPackets.emplace_back(modelLod, 0, m_materialInstance, objectSrg);
|
||||
AZ::RPI::MeshDrawPacket& drawPacket = sectorData.m_drawPackets.back();
|
||||
|
||||
// set the shader option to select forward pass IBL specular if necessary
|
||||
if (!drawPacket.SetShaderOption(AZ::Name("o_meshUseForwardPassIBLSpecular"), AZ::RPI::ShaderOptionValue{ false }))
|
||||
{
|
||||
AZ_Warning("MeshDrawPacket", false, "Failed to set o_meshUseForwardPassIBLSpecular on mesh draw packet");
|
||||
}
|
||||
uint8_t stencilRef = AZ::Render::StencilRefs::UseDiffuseGIPass | AZ::Render::StencilRefs::UseIBLSpecularPass;
|
||||
drawPacket.SetStencilRef(stencilRef);
|
||||
drawPacket.Update(*GetParentScene(), true);
|
||||
}
|
||||
|
||||
sectorData.m_aabb =
|
||||
AZ::Aabb::CreateFromMinMax(
|
||||
AZ::Vector3(xPatch, yPatch, m_areaData.m_terrainBounds.GetMin().GetZ()),
|
||||
AZ::Vector3(xPatch + m_gridMeters, yPatch + m_gridMeters, m_areaData.m_terrainBounds.GetMax().GetZ())
|
||||
),
|
||||
resourceGroup
|
||||
);
|
||||
AZ::Vector3(xPatch + GridMeters, yPatch + GridMeters, m_areaData.m_terrainBounds.GetMax().GetZ())
|
||||
);
|
||||
sectorData.m_srg = objectSrg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& view : process.m_views)
|
||||
|
||||
for (auto& sectorData : m_sectorData)
|
||||
{
|
||||
AZ::Frustum viewFrustum = AZ::Frustum::CreateFromMatrixColumnMajor(view->GetWorldToClipMatrix());
|
||||
for (auto& sectorData : m_sectorData)
|
||||
uint8_t lodChoice = AZ::RPI::ModelLodAsset::LodCountMax;
|
||||
|
||||
// Go through all cameras and choose an LOD based on the closest camera.
|
||||
for (auto& view : process.m_views)
|
||||
{
|
||||
if ((view->GetUsageFlags() & AZ::RPI::View::UsageFlags::UsageCamera) > 0)
|
||||
{
|
||||
AZ::Vector3 cameraPosition = view->GetCameraTransform().GetTranslation();
|
||||
AZ::Vector2 cameraPositionXY = AZ::Vector2(cameraPosition.GetX(), cameraPosition.GetY());
|
||||
AZ::Vector2 sectorCenterXY = AZ::Vector2(sectorData.m_aabb.GetCenter().GetX(), sectorData.m_aabb.GetCenter().GetY());
|
||||
|
||||
float sectorDistance = sectorCenterXY.GetDistance(cameraPositionXY);
|
||||
float lodForCamera = ceilf(AZ::GetMax(0.0f, log2f(sectorDistance / (GridMeters * 4.0f))));
|
||||
lodChoice = AZ::GetMin(lodChoice, aznumeric_cast<uint8_t>(lodForCamera));
|
||||
}
|
||||
}
|
||||
|
||||
// Add the correct LOD draw packet for visible sectors.
|
||||
for (auto& view : process.m_views)
|
||||
{
|
||||
AZ::Frustum viewFrustum = AZ::Frustum::CreateFromMatrixColumnMajor(view->GetWorldToClipMatrix());
|
||||
if (viewFrustum.IntersectAabb(sectorData.m_aabb) != AZ::IntersectResult::Exterior)
|
||||
{
|
||||
view->AddDrawPacket(sectorData.m_drawPacket.get());
|
||||
uint8_t lodToRender = AZ::GetMin(lodChoice, aznumeric_cast<uint8_t>(sectorData.m_drawPackets.size() - 1));
|
||||
view->AddDrawPacket(sectorData.m_drawPackets.at(lodToRender).GetRHIDrawPacket());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainFeatureProcessor::InitializeTerrainPatch()
|
||||
void TerrainFeatureProcessor::InitializeTerrainPatch(uint16_t gridSize, float gridSpacing, PatchData& patchdata)
|
||||
{
|
||||
m_gridVertices.clear();
|
||||
m_gridIndices.clear();
|
||||
patchdata.m_positions.clear();
|
||||
patchdata.m_uvs.clear();
|
||||
patchdata.m_indices.clear();
|
||||
|
||||
for (float y = 0.0f; y < m_gridMeters; y += m_gridSpacing)
|
||||
uint16_t gridVertices = gridSize + 1; // For m_gridSize quads, (m_gridSize + 1) vertices are needed.
|
||||
size_t size = gridVertices * gridVertices;
|
||||
|
||||
patchdata.m_positions.reserve(size);
|
||||
patchdata.m_uvs.reserve(size);
|
||||
|
||||
for (uint16_t y = 0; y < gridVertices; ++y)
|
||||
{
|
||||
for (float x = 0.0f; x < m_gridMeters; x += m_gridSpacing)
|
||||
for (uint16_t x = 0; x < gridVertices; ++x)
|
||||
{
|
||||
float x0 = x;
|
||||
float x1 = x + m_gridSpacing;
|
||||
float y0 = y;
|
||||
float y1 = y + m_gridSpacing;
|
||||
patchdata.m_positions.push_back({ aznumeric_cast<float>(x) * gridSpacing, aznumeric_cast<float>(y) * gridSpacing });
|
||||
patchdata.m_uvs.push_back({ aznumeric_cast<float>(x) / gridSize, aznumeric_cast<float>(y) / gridSize });
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t startIndex = (uint16_t)(m_gridVertices.size());
|
||||
patchdata.m_indices.reserve(gridSize * gridSize * 6); // total number of quads, 2 triangles with 6 indices per quad.
|
||||
|
||||
for (uint16_t y = 0; y < gridSize; ++y)
|
||||
{
|
||||
for (uint16_t x = 0; x < gridSize; ++x)
|
||||
{
|
||||
uint16_t topLeft = y * gridVertices + x;
|
||||
uint16_t topRight = topLeft + 1;
|
||||
uint16_t bottomLeft = (y + 1) * gridVertices + x;
|
||||
uint16_t bottomRight = bottomLeft + 1;
|
||||
|
||||
m_gridVertices.emplace_back(x0, y0, x0 / m_gridMeters, y0 / m_gridMeters);
|
||||
m_gridVertices.emplace_back(x1, y0, x1 / m_gridMeters, y0 / m_gridMeters);
|
||||
m_gridVertices.emplace_back(x0, y1, x0 / m_gridMeters, y1 / m_gridMeters);
|
||||
m_gridVertices.emplace_back(x1, y1, x1 / m_gridMeters, y1 / m_gridMeters);
|
||||
|
||||
m_gridIndices.emplace_back(startIndex);
|
||||
m_gridIndices.emplace_back(aznumeric_cast<uint16_t>(startIndex + 1));
|
||||
m_gridIndices.emplace_back(aznumeric_cast<uint16_t>(startIndex + 2));
|
||||
m_gridIndices.emplace_back(aznumeric_cast<uint16_t>(startIndex + 1));
|
||||
m_gridIndices.emplace_back(aznumeric_cast<uint16_t>(startIndex + 3));
|
||||
m_gridIndices.emplace_back(aznumeric_cast<uint16_t>(startIndex + 2));
|
||||
patchdata.m_indices.emplace_back(topLeft);
|
||||
patchdata.m_indices.emplace_back(topRight);
|
||||
patchdata.m_indices.emplace_back(bottomLeft);
|
||||
patchdata.m_indices.emplace_back(bottomLeft);
|
||||
patchdata.m_indices.emplace_back(topRight);
|
||||
patchdata.m_indices.emplace_back(bottomRight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TerrainFeatureProcessor::InitializeRenderBuffers()
|
||||
|
||||
AZ::Outcome<AZ::Data::Asset<AZ::RPI::BufferAsset>> TerrainFeatureProcessor::CreateBufferAsset(
|
||||
const void* data, const AZ::RHI::BufferViewDescriptor& bufferViewDescriptor, const AZStd::string& bufferName)
|
||||
{
|
||||
AZ::RHI::ResultCode result = AZ::RHI::ResultCode::Fail;
|
||||
AZ::RPI::BufferAssetCreator creator;
|
||||
creator.Begin(AZ::Uuid::CreateRandom());
|
||||
|
||||
// Create geometry buffers
|
||||
m_indexBuffer = AZ::RHI::Factory::Get().CreateBuffer();
|
||||
m_vertexBuffer = AZ::RHI::Factory::Get().CreateBuffer();
|
||||
AZ::RHI::BufferDescriptor bufferDescriptor;
|
||||
bufferDescriptor.m_bindFlags = AZ::RHI::BufferBindFlags::InputAssembly | AZ::RHI::BufferBindFlags::ShaderRead;
|
||||
bufferDescriptor.m_byteCount = static_cast<uint64_t>(bufferViewDescriptor.m_elementSize) * static_cast<uint64_t>(bufferViewDescriptor.m_elementCount);
|
||||
|
||||
m_indexBuffer->SetName(AZ::Name("TerrainIndexBuffer"));
|
||||
m_vertexBuffer->SetName(AZ::Name("TerrainVertexBuffer"));
|
||||
creator.SetBuffer(data, bufferDescriptor.m_byteCount, bufferDescriptor);
|
||||
creator.SetBufferViewDescriptor(bufferViewDescriptor);
|
||||
creator.SetUseCommonPool(AZ::RPI::CommonBufferPoolType::StaticInputAssembly);
|
||||
|
||||
AZStd::vector<AZ::RHI::Ptr<AZ::RHI::Buffer>> buffers = { m_indexBuffer , m_vertexBuffer };
|
||||
|
||||
// Fill our buffers with the vertex/index data
|
||||
for (size_t bufferIndex = 0; bufferIndex < buffers.size(); ++bufferIndex)
|
||||
AZ::Data::Asset<AZ::RPI::BufferAsset> bufferAsset;
|
||||
if (creator.End(bufferAsset))
|
||||
{
|
||||
AZ::RHI::Ptr<AZ::RHI::Buffer> buffer = buffers[bufferIndex];
|
||||
bufferAsset.SetHint(bufferName);
|
||||
return AZ::Success(bufferAsset);
|
||||
}
|
||||
|
||||
// Initialize the buffer
|
||||
return AZ::Failure();
|
||||
}
|
||||
|
||||
AZ::RHI::BufferInitRequest bufferRequest;
|
||||
bufferRequest.m_descriptor = AZ::RHI::BufferDescriptor{ AZ::RHI::BufferBindFlags::InputAssembly, DEFAULT_UploadBufferSize };
|
||||
bufferRequest.m_buffer = buffer.get();
|
||||
bool TerrainFeatureProcessor::InitializePatchModel()
|
||||
{
|
||||
AZ::RPI::ModelAssetCreator modelAssetCreator;
|
||||
modelAssetCreator.Begin(AZ::Uuid::CreateRandom());
|
||||
|
||||
result = m_hostPool->InitBuffer(bufferRequest);
|
||||
uint16_t gridSize = GridSize;
|
||||
float gridSpacing = GridSpacing;
|
||||
|
||||
if (result != AZ::RHI::ResultCode::Success)
|
||||
for (uint32_t i = 0; i < AZ::RPI::ModelLodAsset::LodCountMax && gridSize > 0; ++i)
|
||||
{
|
||||
PatchData patchData;
|
||||
InitializeTerrainPatch(gridSize, gridSpacing, patchData);
|
||||
|
||||
auto positionBufferViewDesc = AZ::RHI::BufferViewDescriptor::CreateTyped(0, aznumeric_cast<uint32_t>(patchData.m_positions.size()), AZ::RHI::Format::R32G32_FLOAT);
|
||||
auto positionsOutcome = CreateBufferAsset(patchData.m_positions.data(), positionBufferViewDesc, "TerrainPatchPositions");
|
||||
|
||||
auto uvBufferViewDesc = AZ::RHI::BufferViewDescriptor::CreateTyped(0, aznumeric_cast<uint32_t>(patchData.m_uvs.size()), AZ::RHI::Format::R32G32_FLOAT);
|
||||
auto uvsOutcome = CreateBufferAsset(patchData.m_uvs.data(), uvBufferViewDesc, "TerrainPatchUvs");
|
||||
|
||||
auto indexBufferViewDesc = AZ::RHI::BufferViewDescriptor::CreateTyped(0, aznumeric_cast<uint32_t>(patchData.m_indices.size()), AZ::RHI::Format::R16_UINT);
|
||||
auto indicesOutcome = CreateBufferAsset(patchData.m_indices.data(), indexBufferViewDesc, "TerrainPatchIndices");
|
||||
|
||||
if (!positionsOutcome.IsSuccess() || !uvsOutcome.IsSuccess() || !indicesOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(TerrainFPName, false, "Failed to create GPU buffers for Terrain");
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ::RPI::ModelLodAssetCreator modelLodAssetCreator;
|
||||
modelLodAssetCreator.Begin(AZ::Uuid::CreateRandom());
|
||||
|
||||
// Grab a pointer to the buffer's data
|
||||
modelLodAssetCreator.BeginMesh();
|
||||
modelLodAssetCreator.AddMeshStreamBuffer(AZ::RHI::ShaderSemantic{ "POSITION" }, AZ::Name(), {positionsOutcome.GetValue(), positionBufferViewDesc});
|
||||
modelLodAssetCreator.AddMeshStreamBuffer(AZ::RHI::ShaderSemantic{ "UV" }, AZ::Name(), {uvsOutcome.GetValue(), uvBufferViewDesc});
|
||||
modelLodAssetCreator.SetMeshIndexBuffer({indicesOutcome.GetValue(), indexBufferViewDesc});
|
||||
|
||||
m_hostPool->OrphanBuffer(*buffer);
|
||||
AZ::Aabb aabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0, 0.0, 0.0), AZ::Vector3(GridMeters, GridMeters, 0.0));
|
||||
modelLodAssetCreator.SetMeshAabb(AZStd::move(aabb));
|
||||
modelLodAssetCreator.SetMeshName(AZ::Name("Terrain Patch"));
|
||||
modelLodAssetCreator.EndMesh();
|
||||
|
||||
AZ::RHI::BufferMapResponse mapResponse;
|
||||
m_hostPool->MapBuffer(AZ::RHI::BufferMapRequest(*buffer, 0, DEFAULT_UploadBufferSize), mapResponse);
|
||||
AZ::Data::Asset<AZ::RPI::ModelLodAsset> modelLodAsset;
|
||||
modelLodAssetCreator.End(modelLodAsset);
|
||||
|
||||
modelAssetCreator.AddLodAsset(AZStd::move(modelLodAsset));
|
||||
|
||||
auto* mappedData = reinterpret_cast<uint8_t*>(mapResponse.m_data);
|
||||
|
||||
//0th index should always be the index buffer
|
||||
if (bufferIndex == 0)
|
||||
{
|
||||
// Fill the index buffer with our terrain patch indices
|
||||
const uint64_t idxSize = m_gridIndices.size() * sizeof(uint16_t);
|
||||
memcpy(mappedData, m_gridIndices.data(), idxSize);
|
||||
|
||||
m_indexBufferView = AZ::RHI::IndexBufferView(
|
||||
*buffer, 0, static_cast<uint32_t>(idxSize), AZ::RHI::IndexFormat::Uint16);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fill the vertex buffer with our terrain patch vertices
|
||||
const uint64_t elementSize = m_gridVertices.size() * sizeof(Vertex);
|
||||
memcpy(mappedData, m_gridVertices.data(), elementSize);
|
||||
|
||||
m_vertexBufferView = AZ::RHI::StreamBufferView(
|
||||
*buffer, 0, static_cast<uint32_t>(elementSize), static_cast<uint32_t>(sizeof(Vertex)));
|
||||
}
|
||||
|
||||
m_hostPool->UnmapBuffer(*buffer);
|
||||
gridSize = gridSize / 2;
|
||||
gridSpacing *= 2.0f;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
AZ::Data::Asset<AZ::RPI::ModelAsset> modelAsset;
|
||||
bool success = modelAssetCreator.End(modelAsset);
|
||||
|
||||
void TerrainFeatureProcessor::DestroyRenderBuffers()
|
||||
{
|
||||
m_indexBuffer.reset();
|
||||
m_vertexBuffer.reset();
|
||||
m_patchModel = AZ::RPI::Model::FindOrCreate(modelAsset);
|
||||
|
||||
m_indexBufferView = {};
|
||||
m_vertexBufferView = {};
|
||||
|
||||
for (ShaderState& shaderState : m_shaderStates)
|
||||
{
|
||||
shaderState.Reset();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,11 @@
|
||||
#include <LmbrCentral/Shape/ShapeComponentBus.h>
|
||||
|
||||
#include <Atom/RPI.Public/FeatureProcessor.h>
|
||||
#include <Atom/RPI.Public/Material/Material.h>
|
||||
#include <Atom/RPI.Public/Shader/Shader.h>
|
||||
|
||||
#include <Atom/RPI.Public/Image/StreamingImage.h>
|
||||
#include <Atom/RPI.Public/MeshDrawPacket.h>
|
||||
#include <Atom/RHI/ShaderResourceGroup.h>
|
||||
#include <Atom/RHI/BufferPool.h>
|
||||
#include <Atom/RHI/DrawPacket.h>
|
||||
@@ -26,6 +28,15 @@
|
||||
#include <Atom/RHI/RHISystemInterface.h>
|
||||
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
|
||||
|
||||
namespace AZ::RPI
|
||||
{
|
||||
namespace AssetUtils
|
||||
{
|
||||
class AsyncAssetLoader;
|
||||
}
|
||||
class Model;
|
||||
}
|
||||
|
||||
namespace Terrain
|
||||
{
|
||||
class TerrainFeatureProcessor final
|
||||
@@ -57,28 +68,6 @@ namespace Terrain
|
||||
|
||||
private:
|
||||
|
||||
// System-level references to the shader, pipeline, and shader-related information
|
||||
enum ShaderType
|
||||
{
|
||||
Depth,
|
||||
Forward,
|
||||
Count,
|
||||
};
|
||||
|
||||
struct ShaderState
|
||||
{
|
||||
AZ::Data::Instance<AZ::RPI::Shader> m_shader;
|
||||
AZ::RHI::ConstPtr<AZ::RHI::PipelineState> m_pipelineState;
|
||||
AZ::RHI::PipelineStateDescriptorForDraw m_pipelineStateDescriptor;
|
||||
|
||||
void Reset()
|
||||
{
|
||||
m_shader.reset();
|
||||
m_pipelineState.reset();
|
||||
m_pipelineStateDescriptor = {};
|
||||
}
|
||||
};
|
||||
|
||||
struct ShaderTerrainData // Must align with struct in Object Srg
|
||||
{
|
||||
AZStd::array<float, 2> m_uvMin;
|
||||
@@ -87,62 +76,47 @@ namespace Terrain
|
||||
float m_sampleSpacing;
|
||||
float m_heightScale;
|
||||
};
|
||||
|
||||
// RPI::SceneNotificationBus overrides ...
|
||||
void OnRenderPipelineAdded(AZ::RPI::RenderPipelinePtr pipeline) override;
|
||||
void OnRenderPipelineRemoved(AZ::RPI::RenderPipeline* pipeline) override;
|
||||
void OnRenderPipelinePassesChanged(AZ::RPI::RenderPipeline* renderPipeline) override;
|
||||
|
||||
void InitializeAtomStuff();
|
||||
void ConfigurePipelineState(ShaderState& shaderState, bool assertOnFail);
|
||||
|
||||
void InitializeTerrainPatch();
|
||||
|
||||
bool InitializeRenderBuffers();
|
||||
void DestroyRenderBuffers();
|
||||
|
||||
void ProcessSurfaces(const FeatureProcessor::RenderPacket& process);
|
||||
|
||||
// System-level parameters
|
||||
const float m_gridSpacing{ 1.0f };
|
||||
const float m_gridMeters{ 32.0f };
|
||||
|
||||
// System-level cached reference to the Atom RHI
|
||||
AZ::RHI::RHISystemInterface* m_rhiSystem = nullptr;
|
||||
|
||||
AZStd::array<ShaderState, ShaderType::Count> m_shaderStates;
|
||||
|
||||
AZ::RHI::ShaderInputImageIndex m_heightmapImageIndex;
|
||||
AZ::RHI::ShaderInputConstantIndex m_modelToWorldIndex;
|
||||
AZ::RHI::ShaderInputConstantIndex m_terrainDataIndex;
|
||||
|
||||
// Pos_float_2 + UV_float_2
|
||||
struct Vertex
|
||||
|
||||
struct VertexPosition
|
||||
{
|
||||
float m_posx;
|
||||
float m_posy;
|
||||
float m_u;
|
||||
float m_v;
|
||||
|
||||
Vertex(float posx, float posy, float u, float v)
|
||||
: m_posx(posx)
|
||||
, m_posy(posy)
|
||||
, m_u(u)
|
||||
, m_v(v)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// System-level definition of a grid patch. (ex: 32m x 32m)
|
||||
AZStd::vector<Vertex> m_gridVertices;
|
||||
AZStd::vector<uint16_t> m_gridIndices;
|
||||
struct VertexUv
|
||||
{
|
||||
float m_u;
|
||||
float m_v;
|
||||
};
|
||||
|
||||
// System-level data related to the grid patch
|
||||
AZ::RHI::Ptr<AZ::RHI::BufferPool> m_hostPool = nullptr;
|
||||
AZ::RHI::Ptr<AZ::RHI::Buffer> m_indexBuffer;
|
||||
AZ::RHI::Ptr<AZ::RHI::Buffer> m_vertexBuffer;
|
||||
AZ::RHI::IndexBufferView m_indexBufferView;
|
||||
AZ::RHI::StreamBufferView m_vertexBufferView;
|
||||
struct PatchData
|
||||
{
|
||||
AZStd::vector<VertexPosition> m_positions;
|
||||
AZStd::vector<VertexUv> m_uvs;
|
||||
AZStd::vector<uint16_t> m_indices;
|
||||
};
|
||||
|
||||
void Initialize();
|
||||
void InitializeTerrainPatch(uint16_t gridSize, float gridSpacing, PatchData& patchdata);
|
||||
bool InitializePatchModel();
|
||||
|
||||
void ProcessSurfaces(const FeatureProcessor::RenderPacket& process);
|
||||
|
||||
AZ::Outcome<AZ::Data::Asset<AZ::RPI::BufferAsset>> CreateBufferAsset(
|
||||
const void* data, const AZ::RHI::BufferViewDescriptor& bufferViewDescriptor, const AZStd::string& bufferName);
|
||||
|
||||
// System-level parameters
|
||||
static constexpr float GridSpacing{ 1.0f };
|
||||
static constexpr uint32_t GridSize{ 64 }; // number of terrain quads (vertices are m_gridSize + 1)
|
||||
static constexpr float GridMeters{ GridSpacing * GridSize };
|
||||
|
||||
AZStd::unique_ptr<AZ::RPI::AssetUtils::AsyncAssetLoader> m_materialAssetLoader;
|
||||
AZ::Data::Instance<AZ::RPI::Material> m_materialInstance;
|
||||
|
||||
AZ::RHI::ShaderInputConstantIndex m_modelToWorldIndex;
|
||||
AZ::RHI::ShaderInputConstantIndex m_terrainDataIndex;
|
||||
|
||||
AZ::Data::Instance<AZ::RPI::Model> m_patchModel;
|
||||
|
||||
// Per-area data
|
||||
struct TerrainAreaData
|
||||
@@ -161,15 +135,9 @@ namespace Terrain
|
||||
|
||||
struct SectorData
|
||||
{
|
||||
AZ::Data::Instance<AZ::RPI::ShaderResourceGroup> m_srg;
|
||||
AZ::Data::Instance<AZ::RPI::ShaderResourceGroup> m_srg; // Hold on to ref so it's not dropped
|
||||
AZ::Aabb m_aabb;
|
||||
AZStd::unique_ptr<const AZ::RHI::DrawPacket> m_drawPacket;
|
||||
|
||||
SectorData(const AZ::RHI::DrawPacket* drawPacket, AZ::Aabb aabb, AZ::Data::Instance<AZ::RPI::ShaderResourceGroup> srg)
|
||||
: m_srg(srg)
|
||||
, m_aabb(aabb)
|
||||
, m_drawPacket(drawPacket)
|
||||
{}
|
||||
AZStd::fixed_vector<AZ::RPI::MeshDrawPacket, AZ::RPI::ModelLodAsset::LodCountMax> m_drawPackets;
|
||||
};
|
||||
|
||||
AZStd::vector<SectorData> m_sectorData;
|
||||
|
||||
@@ -39,6 +39,14 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND AutomatedT
|
||||
TEST_SUITE smoke
|
||||
COMPONENT TestTools
|
||||
)
|
||||
|
||||
ly_add_pytest(
|
||||
NAME LyTestTools_IntegTests_ProcessUtilsLinux_smoke_no_gpu
|
||||
PATH ${CMAKE_CURRENT_LIST_DIR}/integ/test_process_utils_linux.py
|
||||
TEST_SERIAL
|
||||
TEST_SUITE smoke
|
||||
COMPONENT TestTools
|
||||
)
|
||||
|
||||
# Regression tests.
|
||||
ly_add_pytest(
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
import subprocess
|
||||
import pytest
|
||||
|
||||
import ly_test_tools.environment.process_utils as process_utils
|
||||
import ly_test_tools.environment.waiter as waiter
|
||||
from ly_test_tools import LINUX
|
||||
|
||||
if LINUX:
|
||||
pytestmark = pytest.mark.SUITE_smoke
|
||||
else:
|
||||
pytestmark = pytest.mark.skipif(not LINUX, reason="Only runs on Linux")
|
||||
|
||||
|
||||
class TestProcessUtils(object):
|
||||
|
||||
def test_KillLinuxProcess_ProcessStarted_KilledSuccessfully(self):
|
||||
# Construct a simple timeout command
|
||||
linux_executable = 'timeout'
|
||||
command = [linux_executable, '5s', 'echo']
|
||||
|
||||
# Verification function for the waiter to call
|
||||
def process_killed():
|
||||
return not process_utils.process_exists(linux_executable, ignore_extensions=True)
|
||||
|
||||
# Create a new process with no output in a new session
|
||||
with subprocess.Popen(command, stdout=subprocess.DEVNULL, start_new_session=True):
|
||||
# Ensure that the process was started
|
||||
assert process_utils.process_exists(linux_executable, ignore_extensions=True), \
|
||||
f"Process '{linux_executable}' was expected to exist, but could not be found."
|
||||
# Kill the process using the process_utils module
|
||||
process_utils.kill_processes_named(linux_executable, ignore_extensions=True)
|
||||
# Verify that the process was killed
|
||||
waiter.wait_for(process_killed, timeout=2) # Raises exception if the process is alive.
|
||||
@@ -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_LY_PROJECTS": "AutomatedTesting",
|
||||
"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": {
|
||||
@@ -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_LY_PROJECTS": "AutomatedTesting",
|
||||
"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": {
|
||||
@@ -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_LY_PROJECTS": "AutomatedTesting",
|
||||
"CMAKE_TARGET": "TEST_SUITE_periodic",
|
||||
"CTEST_OPTIONS": "-L (SUITE_periodic)"
|
||||
"CTEST_OPTIONS": "-L (SUITE_periodic)",
|
||||
"TEST_RESULTS": "True"
|
||||
}
|
||||
},
|
||||
"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_LY_PROJECTS": "AutomatedTesting",
|
||||
"CMAKE_TARGET": "TEST_SUITE_benchmark",
|
||||
"CTEST_OPTIONS": "-L (SUITE_benchmark)"
|
||||
"CTEST_OPTIONS": "-L (SUITE_benchmark)",
|
||||
"TEST_RESULTS": "True"
|
||||
}
|
||||
},
|
||||
"release": {
|
||||
|
||||
@@ -102,7 +102,8 @@
|
||||
"CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE",
|
||||
"CMAKE_LY_PROJECTS": "AutomatedTesting",
|
||||
"CMAKE_TARGET": "TEST_SUITE_periodic",
|
||||
"CTEST_OPTIONS": "-L \"(SUITE_periodic)\""
|
||||
"CTEST_OPTIONS": "-L \"(SUITE_periodic)\"",
|
||||
"TEST_RESULTS": "True"
|
||||
}
|
||||
},
|
||||
"benchmark_test_profile": {
|
||||
@@ -118,7 +119,8 @@
|
||||
"CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE",
|
||||
"CMAKE_LY_PROJECTS": "AutomatedTesting",
|
||||
"CMAKE_TARGET": "TEST_SUITE_benchmark",
|
||||
"CTEST_OPTIONS": "-L \"(SUITE_benchmark)\""
|
||||
"CTEST_OPTIONS": "-L \"(SUITE_benchmark)\"",
|
||||
"TEST_RESULTS": "True"
|
||||
}
|
||||
},
|
||||
"release": {
|
||||
|
||||
Reference in New Issue
Block a user