diff --git a/Code/Framework/AzCore/AzCore/Serialization/IdUtils.h b/Code/Framework/AzCore/AzCore/Serialization/IdUtils.h index a571379883..15ce299000 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/IdUtils.h +++ b/Code/Framework/AzCore/AzCore/Serialization/IdUtils.h @@ -28,7 +28,13 @@ namespace AZ { namespace IdUtils { - template + /** + * \param AllowDuplicates - If true allows the same id to be registered multiple times, + with the newer value overwriting the stored value. If false, duplicates are not allowed and + the first stored value is kept.The default is false. + */ + + template struct Remapper { /** @@ -138,14 +144,18 @@ namespace AZ * \param context - The serialize context for enumerating the @classPtr elements */ template - static void GenerateNewIdsAndFixRefs(T* object, MapType& newIdMap, AZ::SerializeContext* context = nullptr) + static void GenerateNewIdsAndFixRefs( + T* object, MapType& newIdMap, AZ::SerializeContext* context = nullptr) { if (!context) { AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationRequests::GetSerializeContext); if (!context) { - AZ_Error("Serialization", false, "No serialize context provided! Failed to get component application default serialize context! ComponentApp is not started or input serialize context should not be null!"); + AZ_Error( + "Serialization", false, + "No serialize context provided! Failed to get component application default serialize context! ComponentApp is " + "not started or input serialize context should not be null!"); return; } } @@ -156,8 +166,16 @@ namespace AZ { if (idGenerator) { - auto it = newIdMap.emplace(originalId, idGenerator()); - return it.first->second; + if constexpr(AllowDuplicates) + { + auto it = newIdMap.insert_or_assign(originalId, idGenerator()); + return it.first->second; + } + else + { + auto it = newIdMap.emplace(originalId, idGenerator()); + return it.first->second; + } } return originalId; } diff --git a/Code/Framework/AzCore/AzCore/Serialization/IdUtils.inl b/Code/Framework/AzCore/AzCore/Serialization/IdUtils.inl index 01617cf5aa..0dc87dfe18 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/IdUtils.inl +++ b/Code/Framework/AzCore/AzCore/Serialization/IdUtils.inl @@ -30,8 +30,10 @@ namespace AZ bool m_isModifiedContainer; }; - template - unsigned int Remapper::RemapIds(void* classPtr, const AZ::Uuid& classUuid, const typename Remapper::IdMapper& mapper, AZ::SerializeContext* context, bool replaceId) + template + unsigned int Remapper::RemapIds( + void* classPtr, const AZ::Uuid& classUuid, const typename Remapper::IdMapper& mapper, + AZ::SerializeContext* context, bool replaceId) { if (!context) { @@ -152,16 +154,18 @@ namespace AZ return replaced; } - template - unsigned int Remapper::ReplaceIdsAndIdRefs(void* classPtr, const AZ::Uuid& classUuid, const IdMapper& mapper, AZ::SerializeContext* context /*= nullptr*/) + template + unsigned int Remapper::ReplaceIdsAndIdRefs(void* classPtr, const AZ::Uuid& classUuid, const IdMapper& mapper, AZ::SerializeContext* context /*= nullptr*/) { unsigned int replaced = RemapIds(classPtr, classUuid, mapper, context, true); replaced += RemapIds(classPtr, classUuid, mapper, context, false); return replaced; } - template - unsigned int Remapper::RemapIdsAndIdRefs(void* classPtr, const AZ::Uuid& classUuid, const typename Remapper::IdReplacer& mapper, AZ::SerializeContext* context) + template + unsigned int Remapper::RemapIdsAndIdRefs( + void* classPtr, const AZ::Uuid& classUuid, const typename Remapper::IdReplacer& mapper, + AZ::SerializeContext* context) { if (!context) { diff --git a/Code/Framework/AzCore/AzCore/std/math.h b/Code/Framework/AzCore/AzCore/std/math.h index 9e9be7944a..f5e2ac7ea7 100644 --- a/Code/Framework/AzCore/AzCore/std/math.h +++ b/Code/Framework/AzCore/AzCore/std/math.h @@ -21,11 +21,14 @@ namespace AZStd using std::asin; using std::atan; using std::atan2; + using std::ceil; using std::cos; using std::exp2; + using std::floor; using std::fmod; using std::round; using std::sin; using std::sqrt; using std::tan; + using std::trunc; } // namespace AZStd diff --git a/Code/Framework/AzCore/Platform/Common/Apple/AzCore/IO/SystemFile_Apple.h b/Code/Framework/AzCore/Platform/Common/Apple/AzCore/IO/SystemFile_Apple.h index 3967cafc90..2ebb79634a 100644 --- a/Code/Framework/AzCore/Platform/Common/Apple/AzCore/IO/SystemFile_Apple.h +++ b/Code/Framework/AzCore/Platform/Common/Apple/AzCore/IO/SystemFile_Apple.h @@ -14,6 +14,9 @@ #include #include #include +#include + +#include namespace AZ { diff --git a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp index 4a80db2b24..04573eb2e5 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp @@ -1681,13 +1681,11 @@ namespace AZ::IO AZStd::vector files; do { - if (AZStd::wildcard_match(pWildcardIn, fileIterator.m_filename)) - { - AZStd::string foundFilename{ fileIterator.m_filename }; - AZStd::to_lower(foundFilename.begin(), foundFilename.end()); - files.emplace_back(AZStd::move(foundFilename)); - } - } while (fileIterator = FindNext(fileIterator)); + AZStd::string foundFilename{ fileIterator.m_filename }; + AZStd::to_lower(foundFilename.begin(), foundFilename.end()); + files.emplace_back(AZStd::move(foundFilename)); + } + while (fileIterator = FindNext(fileIterator)); // Open files in alphabet order. AZStd::sort(files.begin(), files.end()); diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.cpp index 808de74e71..a945ea4edc 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.cpp @@ -38,20 +38,20 @@ namespace AzFramework void SpawnableEntitiesContainer::SpawnAllEntities() { AZ_Assert(m_threadData, "Calling SpawnAllEntities on a Spawnable container that's not set."); - SpawnableEntitiesInterface::Get()->SpawnAllEntities(m_threadData->m_spawnedEntitiesTicket, SpawnablePriority_Default); + SpawnableEntitiesInterface::Get()->SpawnAllEntities(m_threadData->m_spawnedEntitiesTicket); } void SpawnableEntitiesContainer::SpawnEntities(AZStd::vector entityIndices) { AZ_Assert(m_threadData, "Calling SpawnEntities on a Spawnable container that's not set."); SpawnableEntitiesInterface::Get()->SpawnEntities( - m_threadData->m_spawnedEntitiesTicket, SpawnablePriority_Default, AZStd::move(entityIndices)); + m_threadData->m_spawnedEntitiesTicket, AZStd::move(entityIndices)); } void SpawnableEntitiesContainer::DespawnAllEntities() { AZ_Assert(m_threadData, "Calling DespawnEntities on a Spawnable container that's not set."); - SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_threadData->m_spawnedEntitiesTicket, SpawnablePriority_Default); + SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_threadData->m_spawnedEntitiesTicket); } void SpawnableEntitiesContainer::Reset(AZ::Data::Asset spawnable) @@ -69,7 +69,6 @@ namespace AzFramework SpawnableEntitiesInterface::Get()->Barrier( m_threadData->m_spawnedEntitiesTicket, - SpawnablePriority_Default, [threadData = m_threadData](EntitySpawnTicket::Id) mutable { threadData.reset(); @@ -88,7 +87,6 @@ namespace AzFramework AZ_Assert(m_threadData, "Calling DespawnEntities on a Spawnable container that's not set."); SpawnableEntitiesInterface::Get()->Barrier( m_threadData->m_spawnedEntitiesTicket, - SpawnablePriority_Default, [generation = m_threadData->m_generation, callback = AZStd::move(callback)](EntitySpawnTicket::Id) { callback(generation); @@ -115,7 +113,6 @@ namespace AzFramework AZ_Assert(m_threadData, "SpawnableEntitiesContainer is monitoring a spawnable, but doesn't have the associated data."); AZ_TracePrintf("Spawnables", "Reloading spawnable '%s'.\n", replacementAsset.GetHint().c_str()); - SpawnableEntitiesInterface::Get()->ReloadSpawnable( - m_threadData->m_spawnedEntitiesTicket, SpawnablePriority_Default, AZStd::move(replacementAsset)); + SpawnableEntitiesInterface::Get()->ReloadSpawnable(m_threadData->m_spawnedEntitiesTicket, AZStd::move(replacementAsset)); } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h index 27f45064b6..d2a5872fc8 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h @@ -21,6 +21,7 @@ namespace AZ { class Entity; + class SerializeContext; } namespace AzFramework @@ -171,6 +172,77 @@ namespace AzFramework using ClaimEntitiesCallback = AZStd::function; using BarrierCallback = AZStd::function; + struct SpawnAllEntitiesOptionalArgs final + { + //! Callback that's called after instances of entities have been created, but before they're spawned into the world. This + //! gives the opportunity to modify the entities if needed such as injecting additional components or modifying components. + EntityPreInsertionCallback m_preInsertionCallback; + //! Callback that's called when spawning entities has completed. This can be triggered from a different thread than the one that + //! made the function call to spawn. The returned list of entities contains all the newly created entities. + EntitySpawnCallback m_completionCallback; + //! The Serialize Context used to clone entities with. If this is not provided the global Serialize Contetx will be used. + AZ::SerializeContext* m_serializeContext { nullptr }; + //! The priority at which this call will be executed. + SpawnablePriority m_priority { SpawnablePriority_Default }; + }; + + struct SpawnEntitiesOptionalArgs final + { + //! Callback that's called after instances of entities have been created, but before they're spawned into the world. This + //! gives the opportunity to modify the entities if needed such as injecting additional components or modifying components. + EntityPreInsertionCallback m_preInsertionCallback; + //! Callback that's called when spawning entities has completed. This can be triggered from a different thread than the one that + //! made the function call to spawn. The returned list of entities contains all the newly created entities. + EntitySpawnCallback m_completionCallback; + //! The Serialize Context used to clone entities with. If this is not provided the global Serialize Contetx will be used. + AZ::SerializeContext* m_serializeContext{ nullptr }; + //! The priority at which this call will be executed. + SpawnablePriority m_priority{ SpawnablePriority_Default }; + //! Entity references are resolved by referring to the last entity spawned from a template entity in the spawnable. If this + //! is set to false entities from previous spawn calls are not taken into account. If set to true entity references may be + //! resolved to a previously spawned entity. A lookup table has to be constructed when true, which may negatively impact + //! performance, especially if a large number of entities are present on a ticket. + bool m_referencePreviouslySpawnedEntities{ false }; + }; + + 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. + 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 + //! made the function call to respawn. The returned list of entities contains all the newly created entities. + ReloadSpawnableCallback m_completionCallback; + //! The Serialize Context used to clone entities with. If this is not provided the global Serialize Context will be used. + AZ::SerializeContext* m_serializeContext { nullptr }; + //! The priority at which this call will be executed. + SpawnablePriority m_priority { SpawnablePriority_Default }; + }; + + struct ListEntitiesOptionalArgs final + { + //! The priority at which this call will be executed. + SpawnablePriority m_priority{ SpawnablePriority_Default }; + }; + + struct ClaimEntitiesOptionalArgs final + { + //! The priority at which this call will be executed. + SpawnablePriority m_priority{ SpawnablePriority_Default }; + }; + + struct BarrierOptionalArgs final + { + //! The priority at which this call will be executed. + SpawnablePriority m_priority{ SpawnablePriority_Default }; + }; + //! Interface definition to (de)spawn entities from a spawnable into the game world. //! //! While the callbacks of the individual calls are being processed they will block processing any other request. Callbacks can be @@ -196,47 +268,34 @@ namespace AzFramework //! Spawn instances of all entities in the spawnable. //! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them. - //! @param priority The priority at which this call will be executed. - //! @param completionCallback Optional callback that's called when spawning entities has completed. This can be called from - //! a different thread than the one that made the function call. The returned list of entities contains all the newly - //! created entities. - virtual void SpawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, EntityPreInsertionCallback preInsertionCallback = {}, - EntitySpawnCallback completionCallback = {}) = 0; + //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs. + virtual void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnAllEntitiesOptionalArgs optionalArgs = {}) = 0; //! Spawn instances of some entities in the spawnable. //! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them. //! @param priority The priority at which this call will be executed. //! @param entityIndices The indices into the template entities stored in the spawnable that will be used to spawn entities from. - //! @param completionCallback Optional callback that's called when spawning entities has completed. This can be called from - //! a different thread than the one that made this function call. The returned list of entities contains all the newly - //! created entities. + //! @param optionalArgs Optional additional arguments, see SpawnEntitiesOptionalArgs. virtual void SpawnEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, AZStd::vector entityIndices, - EntityPreInsertionCallback preInsertionCallback = {}, EntitySpawnCallback completionCallback = {}) = 0; + EntitySpawnTicket& ticket, AZStd::vector 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 completionCallback Optional callback that's called when despawning entities has completed. This can be called from - //! a different thread than the one that made this function call. - virtual void DespawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, EntityDespawnCallback completionCallback = {}) = 0; - + //! @param optionalArgs Optional additional arguments, see DespawnAllEntitiesOptionalArgs. + virtual void DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs = {}) = 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. //! @param spawnable The spawnable that will replace the existing spawnable. Both need to have the same asset id. - //! @param completionCallback Optional callback that's called when the entities have been reloaded. This can be called from - //! a different thread than the one that made this function call. The returned list of entities contains all the replacement - //! entities. + //! @param optionalArgs Optional additional arguments, see ReloadSpawnableOptionalArgs. virtual void ReloadSpawnable( - EntitySpawnTicket& ticket, SpawnablePriority priority, AZ::Data::Asset spawnable, - ReloadSpawnableCallback completionCallback = {}) = 0; + EntitySpawnTicket& ticket, AZ::Data::Asset spawnable, ReloadSpawnableOptionalArgs optionalArgs = {}) = 0; //! List all entities that are spawned using this ticket. //! @param ticket Only the entities associated with this ticket will be listed. - //! @param priority The priority at which this call will be executed. //! @param listCallback Required callback that will be called to list the entities on. - virtual void ListEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ListEntitiesCallback listCallback) = 0; + //! @param optionalArgs Optional additional arguments, see ListEntitiesOptionalArgs. + virtual void ListEntities( + EntitySpawnTicket& ticket, ListEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) = 0; //! List all entities that are spawned using this ticket with their spawnable index. //! Spawnables contain a flat list of entities, which are used as templates to spawn entities from. For every spawned entity //! the index of the entity in the spawnable that was used as a template is stored. This version of ListEntities will return @@ -244,23 +303,24 @@ namespace AzFramework //! the same index may appear multiple times as there are no restriction on how many instance of a specific entity can be //! created. //! @param ticket Only the entities associated with this ticket will be listed. - //! @param priority The priority at which this call will be executed. //! @param listCallback Required callback that will be called to list the entities and indices on. + //! @param optionalArgs Optional additional arguments, see ListEntitiesOptionalArgs. virtual void ListIndicesAndEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, ListIndicesEntitiesCallback listCallback) = 0; + EntitySpawnTicket& ticket, ListIndicesEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) = 0; //! Claim all entities that are spawned using this ticket. Ownership of the entities is transferred from the ticket to the //! caller through the callback. After this call the ticket will have no entities associated with it. The caller of //! this function will need to manage the entities after this call. //! @param ticket Only the entities associated with this ticket will be released. - //! @param priority The priority at which this call will be executed. //! @param listCallback Required callback that will be called to transfer the entities through. - virtual void ClaimEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ClaimEntitiesCallback listCallback) = 0; + //! @param optionalArgs Optional additional arguments, see ClaimEntitiesOptionalArgs. + virtual void ClaimEntities( + EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback, ClaimEntitiesOptionalArgs optionalArgs = {}) = 0; //! Blocks until all operations made on the provided ticket before the barrier call have completed. //! @param ticket The ticket to monitor. - //! @param priority The priority at which this call will be executed. //! @param completionCallback Required callback that will be called as soon as the barrier has been reached. - virtual void Barrier(EntitySpawnTicket& ticket, SpawnablePriority priority, BarrierCallback completionCallback) = 0; + //! @param optionalArgs Optional additional arguments, see BarrierOptionalArgs. + virtual void Barrier(EntitySpawnTicket& ticket, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs = {}) = 0; //! Register a handler for OnSpawned events. virtual void AddOnSpawnedHandler(AZ::Event>::Handler& handler) = 0; diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index 7b767d2a72..a482c1d4f9 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -38,6 +38,10 @@ namespace AzFramework SpawnableEntitiesManager::SpawnableEntitiesManager() { + AZ::ComponentApplicationBus::BroadcastResult(m_defaultSerializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext); + AZ_Assert( + m_defaultSerializeContext, "Failed to retrieve serialization context during construction of the Spawnable Entities Manager."); + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) { AZ::u64 value = aznumeric_caster(m_highPriorityThreshold); @@ -46,58 +50,61 @@ namespace AzFramework } } - void SpawnableEntitiesManager::SpawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, EntityPreInsertionCallback preInsertionCallback, - EntitySpawnCallback completionCallback) + void SpawnableEntitiesManager::SpawnAllEntities(EntitySpawnTicket& ticket, SpawnAllEntitiesOptionalArgs optionalArgs) { AZ_Assert(ticket.IsValid(), "Ticket provided to SpawnAllEntities hasn't been initialized."); SpawnAllEntitiesCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); - queueEntry.m_completionCallback = AZStd::move(completionCallback); - queueEntry.m_preInsertionCallback = AZStd::move(preInsertionCallback); - QueueRequest(ticket, priority, AZStd::move(queueEntry)); + queueEntry.m_serializeContext = + optionalArgs.m_serializeContext == nullptr ? m_defaultSerializeContext : optionalArgs.m_serializeContext; + queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback); + queueEntry.m_preInsertionCallback = AZStd::move(optionalArgs.m_preInsertionCallback); + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } void SpawnableEntitiesManager::SpawnEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, AZStd::vector entityIndices, - EntityPreInsertionCallback preInsertionCallback, EntitySpawnCallback completionCallback) + EntitySpawnTicket& ticket, AZStd::vector entityIndices, SpawnEntitiesOptionalArgs optionalArgs) { AZ_Assert(ticket.IsValid(), "Ticket provided to SpawnEntities hasn't been initialized."); SpawnEntitiesCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_entityIndices = AZStd::move(entityIndices); - queueEntry.m_completionCallback = AZStd::move(completionCallback); - queueEntry.m_preInsertionCallback = AZStd::move(preInsertionCallback); - QueueRequest(ticket, priority, AZStd::move(queueEntry)); + queueEntry.m_serializeContext = + optionalArgs.m_serializeContext == nullptr ? m_defaultSerializeContext : optionalArgs.m_serializeContext; + queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback); + queueEntry.m_preInsertionCallback = AZStd::move(optionalArgs.m_preInsertionCallback); + queueEntry.m_referencePreviouslySpawnedEntities = optionalArgs.m_referencePreviouslySpawnedEntities; + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } - void SpawnableEntitiesManager::DespawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, EntityDespawnCallback completionCallback) + void SpawnableEntitiesManager::DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs) { AZ_Assert(ticket.IsValid(), "Ticket provided to DespawnAllEntities hasn't been initialized."); DespawnAllEntitiesCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); - queueEntry.m_completionCallback = AZStd::move(completionCallback); - QueueRequest(ticket, priority, AZStd::move(queueEntry)); + queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback); + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } void SpawnableEntitiesManager::ReloadSpawnable( - EntitySpawnTicket& ticket, SpawnablePriority priority, AZ::Data::Asset spawnable, - ReloadSpawnableCallback completionCallback) + EntitySpawnTicket& ticket, AZ::Data::Asset spawnable, ReloadSpawnableOptionalArgs optionalArgs) { AZ_Assert(ticket.IsValid(), "Ticket provided to ReloadSpawnable hasn't been initialized."); ReloadSpawnableCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_spawnable = AZStd::move(spawnable); - queueEntry.m_completionCallback = AZStd::move(completionCallback); - QueueRequest(ticket, priority, AZStd::move(queueEntry)); + queueEntry.m_serializeContext = + optionalArgs.m_serializeContext == nullptr ? m_defaultSerializeContext : optionalArgs.m_serializeContext; + queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback); + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } - void SpawnableEntitiesManager::ListEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ListEntitiesCallback listCallback) + void SpawnableEntitiesManager::ListEntities( + EntitySpawnTicket& ticket, ListEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs) { AZ_Assert(listCallback, "ListEntities called on spawnable entities without a valid callback to use."); AZ_Assert(ticket.IsValid(), "Ticket provided to ListEntities hasn't been initialized."); @@ -105,11 +112,11 @@ namespace AzFramework ListEntitiesCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_listCallback = AZStd::move(listCallback); - QueueRequest(ticket, priority, AZStd::move(queueEntry)); + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } void SpawnableEntitiesManager::ListIndicesAndEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, ListIndicesEntitiesCallback listCallback) + EntitySpawnTicket& ticket, ListIndicesEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs) { AZ_Assert(listCallback, "ListEntities called on spawnable entities without a valid callback to use."); AZ_Assert(ticket.IsValid(), "Ticket provided to ListEntities hasn't been initialized."); @@ -117,10 +124,11 @@ namespace AzFramework ListIndicesEntitiesCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_listCallback = AZStd::move(listCallback); - QueueRequest(ticket, priority, AZStd::move(queueEntry)); + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } - void SpawnableEntitiesManager::ClaimEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ClaimEntitiesCallback listCallback) + void SpawnableEntitiesManager::ClaimEntities( + EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback, ClaimEntitiesOptionalArgs optionalArgs) { AZ_Assert(listCallback, "ClaimEntities called on spawnable entities without a valid callback to use."); AZ_Assert(ticket.IsValid(), "Ticket provided to ClaimEntities hasn't been initialized."); @@ -128,10 +136,10 @@ namespace AzFramework ClaimEntitiesCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_listCallback = AZStd::move(listCallback); - QueueRequest(ticket, priority, AZStd::move(queueEntry)); + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } - void SpawnableEntitiesManager::Barrier(EntitySpawnTicket& ticket, SpawnablePriority priority, BarrierCallback completionCallback) + void SpawnableEntitiesManager::Barrier(EntitySpawnTicket& ticket, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs) { AZ_Assert(completionCallback, "Barrier on spawnable entities called without a valid callback to use."); AZ_Assert(ticket.IsValid(), "Ticket provided to Barrier hasn't been initialized."); @@ -139,7 +147,7 @@ namespace AzFramework BarrierCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_completionCallback = AZStd::move(completionCallback); - QueueRequest(ticket, priority, AZStd::move(queueEntry)); + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } void SpawnableEntitiesManager::AddOnSpawnedHandler(AZ::Event>::Handler& handler) @@ -174,58 +182,57 @@ namespace AzFramework auto SpawnableEntitiesManager::ProcessQueue(Queue& queue) -> CommandQueueStatus { - AZStd::queue pendingRequestQueue; + // Process delayed requests first. + // Only process the requests that are currently in this queue, not the ones that could be re-added if they still can't complete. + size_t delayedSize = queue.m_delayed.size(); + for (size_t i = 0; i < delayedSize; ++i) { - AZStd::scoped_lock queueLock(queue.m_pendingRequestMutex); - queue.m_pendingRequest.swap(pendingRequestQueue); + Requests& request = queue.m_delayed.front(); + bool result = AZStd::visit( + [this](auto&& args) -> bool + { + return ProcessRequest(args); + }, + request); + if (!result) + { + queue.m_delayed.emplace_back(AZStd::move(request)); + } + queue.m_delayed.pop_front(); } - if (!pendingRequestQueue.empty() || !queue.m_delayed.empty()) + // Process newly added requests. + while (true) { - AZ::SerializeContext* serializeContext = nullptr; - AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext); - AZ_Assert(serializeContext, "Failed to retrieve serialization context."); - - // Only process the requests that are currently in this queue, not the ones that could be re-added if they still can't complete. - size_t delayedSize = queue.m_delayed.size(); - for (size_t i = 0; i < delayedSize; ++i) + AZStd::queue pendingRequestQueue; { - Requests& request = queue.m_delayed.front(); - bool result = AZStd::visit([this, serializeContext](auto&& args) -> bool - { - return ProcessRequest(args, *serializeContext); - }, request); - if (!result) - { - queue.m_delayed.emplace_back(AZStd::move(request)); - } - queue.m_delayed.pop_front(); + AZStd::scoped_lock queueLock(queue.m_pendingRequestMutex); + queue.m_pendingRequest.swap(pendingRequestQueue); } - do + if (!pendingRequestQueue.empty()) { while (!pendingRequestQueue.empty()) { Requests& request = pendingRequestQueue.front(); - bool result = AZStd::visit([this, serializeContext](auto&& args) -> bool + bool result = AZStd::visit( + [this](auto&& args) -> bool { - return ProcessRequest(args, *serializeContext); - }, request); + return ProcessRequest(args); + }, + request); if (!result) { queue.m_delayed.emplace_back(AZStd::move(request)); } pendingRequestQueue.pop(); } - - // Spawning entities can result in more entities being queued to spawn. Repeat spawning until the queue is - // empty to avoid a chain of entity spawning getting dragged out over multiple frames. - { - AZStd::scoped_lock queueLock(queue.m_pendingRequestMutex); - queue.m_pendingRequest.swap(pendingRequestQueue); - } - } while (!pendingRequestQueue.empty()); - } + } + else + { + break; + } + }; return queue.m_delayed.empty() ? CommandQueueStatus::NoCommandsLeft : CommandQueueStatus::HasCommandsLeft; } @@ -250,24 +257,14 @@ namespace AzFramework } } - AZ::Entity* SpawnableEntitiesManager::SpawnSingleEntity(const AZ::Entity& entityTemplate, AZ::SerializeContext& serializeContext) - { - AZ::Entity* clone = serializeContext.CloneObject(&entityTemplate); - AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); - clone->SetId(AZ::Entity::MakeId()); - - GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, clone); - return clone; - } - AZ::Entity* SpawnableEntitiesManager::CloneSingleEntity(const AZ::Entity& entityTemplate, - EntityIdMap& templateToCloneEntityIdMap, AZ::SerializeContext& serializeContext) + EntityIdMap& templateToCloneMap, AZ::SerializeContext& serializeContext) { - return AZ::IdUtils::Remapper::CloneObjectAndGenerateNewIdsAndFixRefs( - &entityTemplate, templateToCloneEntityIdMap, &serializeContext); + return AZ::IdUtils::Remapper::CloneObjectAndGenerateNewIdsAndFixRefs( + &entityTemplate, templateToCloneMap, &serializeContext); } - bool SpawnableEntitiesManager::ProcessRequest(SpawnAllEntitiesCommand& request, AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(SpawnAllEntitiesCommand& request) { Ticket& ticket = *request.m_ticket; if (ticket.m_spawnable.IsReady() && request.m_requestId == ticket.m_currentRequestId) @@ -291,13 +288,9 @@ namespace AzFramework spawnedEntityIndices.reserve(spawnedEntityIndices.size() + entitiesToSpawnSize); templateToCloneEntityIdMap.reserve(entitiesToSpawnSize); - // Mark all indices as spawned for (size_t i = 0; i < entitiesToSpawnSize; ++i) { - const AZ::Entity& entityTemplate = *entitiesToSpawn[i]; - - AZ::Entity* clone = CloneSingleEntity(entityTemplate, templateToCloneEntityIdMap, serializeContext); - + AZ::Entity* clone = CloneSingleEntity(*entitiesToSpawn[i], templateToCloneEntityIdMap, *request.m_serializeContext); AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); spawnedEntities.emplace_back(clone); @@ -305,16 +298,8 @@ namespace AzFramework } // loadAll is true if every entity has been spawned only once - if (spawnedEntities.size() == entitiesToSpawnSize) - { - ticket.m_loadAll = true; - } - else - { - // Case where there were already spawns from a previous request - ticket.m_loadAll = false; - } - + ticket.m_loadAll = (spawnedEntities.size() == entitiesToSpawnSize); + // Let other systems know about newly spawned entities for any pre-processing before adding to the scene/game context. if (request.m_preInsertionCallback) { @@ -323,11 +308,10 @@ namespace AzFramework } // Add to the game context, now the entities are active - AZStd::for_each(ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end(), - [](AZ::Entity* entity) + for (auto it = ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount; it != ticket.m_spawnedEntities.end(); ++it) { - GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, entity); - }); + GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it); + } // Let other systems know about newly spawned entities for any post-processing after adding to the scene/game context. if (request.m_completionCallback) @@ -347,21 +331,41 @@ namespace AzFramework } } - bool SpawnableEntitiesManager::ProcessRequest(SpawnEntitiesCommand& request, AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(SpawnEntitiesCommand& request) { Ticket& ticket = *request.m_ticket; if (ticket.m_spawnable.IsReady() && request.m_requestId == ticket.m_currentRequestId) { AZStd::vector& spawnedEntities = ticket.m_spawnedEntities; AZStd::vector& spawnedEntityIndices = ticket.m_spawnedEntityIndices; + AZ_Assert( + spawnedEntities.size() == spawnedEntityIndices.size(), + "The indices for the spawned entities has gone out of sync with the entities."); - // Keep track how many entities there were in the array initially + // Keep track of how many entities there were in the array initially size_t spawnedEntitiesInitialCount = spawnedEntities.size(); // These are 'template' entities we'll be cloning from const Spawnable::EntityList& entitiesToSpawn = ticket.m_spawnable->GetEntities(); size_t entitiesToSpawnSize = request.m_entityIndices.size(); + // Reconstruct the template to entity mapping. + EntityIdMap templateToCloneEntityIdMap; + if (!request.m_referencePreviouslySpawnedEntities) + { + templateToCloneEntityIdMap.reserve(entitiesToSpawnSize); + } + else + { + templateToCloneEntityIdMap.reserve(spawnedEntitiesInitialCount + entitiesToSpawnSize); + SpawnableConstIndexEntityContainerView indexEntityView( + spawnedEntities.begin(), spawnedEntityIndices.begin(), spawnedEntities.size()); + for (auto& entry : indexEntityView) + { + templateToCloneEntityIdMap.insert_or_assign(entitiesToSpawn[entry.GetIndex()]->GetId(), entry.GetEntity()->GetId()); + } + } + spawnedEntities.reserve(spawnedEntities.size() + entitiesToSpawnSize); spawnedEntityIndices.reserve(spawnedEntityIndices.size() + entitiesToSpawnSize); @@ -369,15 +373,11 @@ namespace AzFramework { if (index < entitiesToSpawn.size()) { - const AZ::Entity& entityTemplate = *entitiesToSpawn[index]; - - AZ::Entity* clone = serializeContext.CloneObject(&entityTemplate); + AZ::Entity* clone = CloneSingleEntity(*entitiesToSpawn[index], templateToCloneEntityIdMap, *request.m_serializeContext); AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); - clone->SetId(AZ::Entity::MakeId()); spawnedEntities.push_back(clone); spawnedEntityIndices.push_back(index); - } } ticket.m_loadAll = false; @@ -390,11 +390,10 @@ namespace AzFramework } // Add to the game context, now the entities are active - AZStd::for_each(ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end(), - [](AZ::Entity* entity) + for (auto it = ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount; it != ticket.m_spawnedEntities.end(); ++it) { - GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, entity); - }); + GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it); + } if (request.m_completionCallback) { @@ -413,8 +412,7 @@ namespace AzFramework } } - bool SpawnableEntitiesManager::ProcessRequest(DespawnAllEntitiesCommand& request, - [[maybe_unused]] AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(DespawnAllEntitiesCommand& request) { Ticket& ticket = *request.m_ticket; if (request.m_requestId == ticket.m_currentRequestId) @@ -447,7 +445,7 @@ namespace AzFramework } } - bool SpawnableEntitiesManager::ProcessRequest(ReloadSpawnableCommand& request, AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(ReloadSpawnableCommand& request) { Ticket& ticket = *request.m_ticket; AZ_Assert(ticket.m_spawnable.GetId() == request.m_spawnable.GetId(), @@ -470,39 +468,43 @@ namespace AzFramework // Rebuild the list of entities. ticket.m_spawnedEntities.clear(); const Spawnable::EntityList& entities = request.m_spawnable->GetEntities(); + + // Map keeps track of ids from template (spawnable) to clone (instance) + // Allowing patch ups of fields referring to entityIds outside of a given entity + EntityIdMap templateToCloneEntityIdMap; + if (ticket.m_loadAll) { // The new spawnable may have a different number of entities and since the intent of the user was - // to load every, simply start over. + // to spawn every entity, simply start over. ticket.m_spawnedEntityIndices.clear(); - size_t entitiesToSpawnSize = entities.size(); - - // Map keeps track of ids from template (spawnable) to clone (instance) - // Allowing patch ups of fields referring to entityIds outside of a given entity - EntityIdMap templateToCloneEntityIdMap; templateToCloneEntityIdMap.reserve(entitiesToSpawnSize); - // Mark all indices as spawned for (size_t i = 0; i < entitiesToSpawnSize; ++i) { - const AZ::Entity& entityTemplate = *entities[i]; - - AZ::Entity* clone = CloneSingleEntity(entityTemplate, templateToCloneEntityIdMap, serializeContext); - + AZ::Entity* clone = CloneSingleEntity(*entities[i], templateToCloneEntityIdMap, *request.m_serializeContext); AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); - ticket.m_spawnedEntities.emplace_back(clone); + ticket.m_spawnedEntities.push_back(clone); ticket.m_spawnedEntityIndices.push_back(i); } } else { size_t entitiesSize = entities.size(); + templateToCloneEntityIdMap.reserve(entitiesSize); for (size_t index : ticket.m_spawnedEntityIndices) { - ticket.m_spawnedEntities.push_back( - index < entitiesSize ? SpawnSingleEntity(*entities[index], serializeContext) : nullptr); + // It's possible for the new spawnable to have a different number of entities, so guard against this. + // It's also possible that the entities have moved within the spawnable to a new index. This can't be + // detected and will result in the incorrect entities being spawned. + if (index < entitiesSize) + { + AZ::Entity* clone = CloneSingleEntity(*entities[index], templateToCloneEntityIdMap, *request.m_serializeContext); + AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); + ticket.m_spawnedEntities.push_back(clone); + } } } ticket.m_spawnable = AZStd::move(request.m_spawnable); @@ -525,7 +527,7 @@ namespace AzFramework } } - bool SpawnableEntitiesManager::ProcessRequest(ListEntitiesCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(ListEntitiesCommand& request) { Ticket& ticket = *request.m_ticket; if (request.m_requestId == ticket.m_currentRequestId) @@ -541,7 +543,7 @@ namespace AzFramework } } - bool SpawnableEntitiesManager::ProcessRequest(ListIndicesEntitiesCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(ListIndicesEntitiesCommand& request) { Ticket& ticket = *request.m_ticket; if (request.m_requestId == ticket.m_currentRequestId) @@ -560,7 +562,7 @@ namespace AzFramework } } - bool SpawnableEntitiesManager::ProcessRequest(ClaimEntitiesCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(ClaimEntitiesCommand& request) { Ticket& ticket = *request.m_ticket; if (request.m_requestId == ticket.m_currentRequestId) @@ -580,7 +582,7 @@ namespace AzFramework } } - bool SpawnableEntitiesManager::ProcessRequest(BarrierCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(BarrierCommand& request) { Ticket& ticket = *request.m_ticket; if (request.m_requestId == ticket.m_currentRequestId) @@ -599,7 +601,7 @@ namespace AzFramework } } - bool SpawnableEntitiesManager::ProcessRequest(DestroyTicketCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(DestroyTicketCommand& request) { if (request.m_requestId == request.m_ticket->m_currentRequestId) { diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h index afffdab8b5..638559f3f1 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h @@ -37,7 +37,7 @@ namespace AzFramework AZ_CLASS_ALLOCATOR(SpawnableEntitiesManager, AZ::SystemAllocator, 0); using EntityIdMap = AZStd::unordered_map; - + enum class CommandQueueStatus : bool { HasCommandsLeft, @@ -57,26 +57,21 @@ namespace AzFramework // The following functions are thread safe // - void SpawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, EntityPreInsertionCallback preInsertionCallback = {}, - EntitySpawnCallback completionCallback = {}) override; + void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnAllEntitiesOptionalArgs optionalArgs = {}) override; void SpawnEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, AZStd::vector entityIndices, - EntityPreInsertionCallback preInsertionCallback = {}, - EntitySpawnCallback completionCallback = {}) override; - void DespawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, EntityDespawnCallback completionCallback = {}) override; - + EntitySpawnTicket& ticket, AZStd::vector entityIndices, SpawnEntitiesOptionalArgs optionalArgs = {}) override; + void DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs = {}) override; void ReloadSpawnable( - EntitySpawnTicket& ticket, SpawnablePriority priority, AZ::Data::Asset spawnable, - ReloadSpawnableCallback completionCallback = {}) override; + EntitySpawnTicket& ticket, AZ::Data::Asset spawnable, ReloadSpawnableOptionalArgs optionalArgs = {}) override; - void ListEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ListEntitiesCallback listCallback) override; + void ListEntities( + EntitySpawnTicket& ticket, ListEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) override; void ListIndicesAndEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, ListIndicesEntitiesCallback listCallback) override; - void ClaimEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ClaimEntitiesCallback listCallback) override; + EntitySpawnTicket& ticket, ListIndicesEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) override; + void ClaimEntities( + EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback, ClaimEntitiesOptionalArgs optionalArgs = {}) override; - void Barrier(EntitySpawnTicket& spawnInfo, SpawnablePriority priority, BarrierCallback completionCallback) override; + void Barrier(EntitySpawnTicket& spawnInfo, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs = {}) override; void AddOnSpawnedHandler(AZ::Event>::Handler& handler) override; void AddOnDespawnedHandler(AZ::Event>::Handler& handler) override; @@ -105,6 +100,7 @@ namespace AzFramework { EntitySpawnCallback m_completionCallback; EntityPreInsertionCallback m_preInsertionCallback; + AZ::SerializeContext* m_serializeContext; Ticket* m_ticket; EntitySpawnTicket::Id m_ticketId; uint32_t m_requestId; @@ -114,9 +110,11 @@ namespace AzFramework AZStd::vector m_entityIndices; EntitySpawnCallback m_completionCallback; EntityPreInsertionCallback m_preInsertionCallback; + AZ::SerializeContext* m_serializeContext; Ticket* m_ticket; EntitySpawnTicket::Id m_ticketId; uint32_t m_requestId; + bool m_referencePreviouslySpawnedEntities; }; struct DespawnAllEntitiesCommand { @@ -129,6 +127,7 @@ namespace AzFramework { AZ::Data::Asset m_spawnable; ReloadSpawnableCallback m_completionCallback; + AZ::SerializeContext* m_serializeContext; Ticket* m_ticket; EntitySpawnTicket::Id m_ticketId; uint32_t m_requestId; @@ -185,21 +184,18 @@ namespace AzFramework CommandQueueStatus ProcessQueue(Queue& queue); - AZ::Entity* SpawnSingleEntity(const AZ::Entity& entityTemplate, - AZ::SerializeContext& serializeContext); - - AZ::Entity* CloneSingleEntity(const AZ::Entity& entityTemplate, - EntityIdMap& templateToCloneEntityIdMap, AZ::SerializeContext& serializeContext); - - bool ProcessRequest(SpawnAllEntitiesCommand& request, AZ::SerializeContext& serializeContext); - bool ProcessRequest(SpawnEntitiesCommand& request, AZ::SerializeContext& serializeContext); - bool ProcessRequest(DespawnAllEntitiesCommand& request, AZ::SerializeContext& serializeContext); - bool ProcessRequest(ReloadSpawnableCommand& request, AZ::SerializeContext& serializeContext); - bool ProcessRequest(ListEntitiesCommand& request, AZ::SerializeContext& serializeContext); - bool ProcessRequest(ListIndicesEntitiesCommand& request, AZ::SerializeContext& serializeContext); - bool ProcessRequest(ClaimEntitiesCommand& request, AZ::SerializeContext& serializeContext); - bool ProcessRequest(BarrierCommand& request, AZ::SerializeContext& serializeContext); - bool ProcessRequest(DestroyTicketCommand& request, AZ::SerializeContext& serializeContext); + AZ::Entity* CloneSingleEntity( + const AZ::Entity& entityTemplate, EntityIdMap& templateToCloneMap, AZ::SerializeContext& serializeContext); + + bool ProcessRequest(SpawnAllEntitiesCommand& request); + bool ProcessRequest(SpawnEntitiesCommand& request); + bool ProcessRequest(DespawnAllEntitiesCommand& request); + bool ProcessRequest(ReloadSpawnableCommand& request); + bool ProcessRequest(ListEntitiesCommand& request); + bool ProcessRequest(ListIndicesEntitiesCommand& request); + bool ProcessRequest(ClaimEntitiesCommand& request); + bool ProcessRequest(BarrierCommand& request); + bool ProcessRequest(DestroyTicketCommand& request); Queue m_highPriorityQueue; Queue m_regularPriorityQueue; @@ -207,6 +203,7 @@ namespace AzFramework AZ::Event> m_onSpawnedEvent; AZ::Event> m_onDespawnedEvent; + AZ::SerializeContext* m_defaultSerializeContext { nullptr }; //! The threshold used to determine if a request goes in the regular (if bigger than the value) or high priority queue (if smaller //! or equal to this value). The starting value of 64 is chosen as it's between default values SpawnablePriority_High and //! SpawnablePriority_Default which gives users a bit of room to fine tune the priorities as this value can be configured diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp index 559f7ce460..674e10812b 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp @@ -22,7 +22,11 @@ namespace AzFramework { AZ_CVAR( - float, ed_cameraSystemDefaultPlaneHeight, 34.0f, nullptr, AZ::ConsoleFunctorFlags::Null, + float, + ed_cameraSystemDefaultPlaneHeight, + 34.0f, + nullptr, + AZ::ConsoleFunctorFlags::Null, "The default height of the ground plane to do intersection tests against when orbiting"); AZ_CVAR(float, ed_cameraSystemBoostMultiplier, 3.0f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); AZ_CVAR(float, ed_cameraSystemTranslateSpeed, 10.0f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); @@ -41,7 +45,11 @@ namespace AzFramework AZ_CVAR( AZ::CVarFixedString, ed_cameraSystemTranslateForwardKey, "keyboard_key_alphanumeric_W", nullptr, AZ::ConsoleFunctorFlags::Null, ""); AZ_CVAR( - AZ::CVarFixedString, ed_cameraSystemTranslateBackwardKey, "keyboard_key_alphanumeric_S", nullptr, AZ::ConsoleFunctorFlags::Null, + AZ::CVarFixedString, + ed_cameraSystemTranslateBackwardKey, + "keyboard_key_alphanumeric_S", + nullptr, + AZ::ConsoleFunctorFlags::Null, ""); AZ_CVAR( AZ::CVarFixedString, ed_cameraSystemTranslateLeftKey, "keyboard_key_alphanumeric_A", nullptr, AZ::ConsoleFunctorFlags::Null, ""); @@ -326,7 +334,9 @@ namespace AzFramework } Camera RotateCameraInput::StepCamera( - const Camera& targetCamera, const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta, + const Camera& targetCamera, + const ScreenVector& cursorDelta, + [[maybe_unused]] const float scrollDelta, [[maybe_unused]] const float deltaTime) { Camera nextCamera = targetCamera; @@ -374,7 +384,9 @@ namespace AzFramework } Camera PanCameraInput::StepCamera( - const Camera& targetCamera, const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta, + const Camera& targetCamera, + const ScreenVector& cursorDelta, + [[maybe_unused]] const float scrollDelta, [[maybe_unused]] const float deltaTime) { Camera nextCamera = targetCamera; @@ -473,7 +485,9 @@ namespace AzFramework } Camera TranslateCameraInput::StepCamera( - const Camera& targetCamera, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta, + const Camera& targetCamera, + [[maybe_unused]] const ScreenVector& cursorDelta, + [[maybe_unused]] const float scrollDelta, const float deltaTime) { Camera nextCamera = targetCamera; @@ -630,7 +644,9 @@ namespace AzFramework } Camera OrbitDollyScrollCameraInput::StepCamera( - const Camera& targetCamera, [[maybe_unused]] const ScreenVector& cursorDelta, const float scrollDelta, + const Camera& targetCamera, + [[maybe_unused]] const ScreenVector& cursorDelta, + const float scrollDelta, [[maybe_unused]] const float deltaTime) { Camera nextCamera = targetCamera; @@ -666,7 +682,9 @@ namespace AzFramework } Camera OrbitDollyCursorMoveCameraInput::StepCamera( - const Camera& targetCamera, const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta, + const Camera& targetCamera, + const ScreenVector& cursorDelta, + [[maybe_unused]] const float scrollDelta, [[maybe_unused]] const float deltaTime) { Camera nextCamera = targetCamera; @@ -686,7 +704,9 @@ namespace AzFramework } Camera ScrollTranslationCameraInput::StepCamera( - const Camera& targetCamera, [[maybe_unused]] const ScreenVector& cursorDelta, const float scrollDelta, + const Camera& targetCamera, + [[maybe_unused]] const ScreenVector& cursorDelta, + const float scrollDelta, [[maybe_unused]] const float deltaTime) { Camera nextCamera = targetCamera; diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.cpp index 5d2d02a398..8283e0b1f2 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.cpp +++ b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.cpp @@ -128,12 +128,12 @@ namespace AzFramework worldPosition, CameraView(cameraState), CameraProjection(cameraState), cameraState.m_viewportSize); } - AZ::Vector3 ScreenToWorld( - const ScreenPoint& screenPosition, const AZ::Matrix4x4& inverseCameraView, - const AZ::Matrix4x4& inverseCameraProjection, const AZ::Vector2& viewportSize) + 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 = NDCFromScreenPoint(screenPosition, viewportSize) * 2.0f - AZ::Vector2::CreateOne(); + const auto ndcPosition = normalizedScreenPosition * 2.0f - AZ::Vector2::CreateOne(); // transform ndc space position to clip space const auto clipSpacePosition = inverseCameraProjection * Vector2ToVector4(ndcPosition, -1.0f, 1.0f); @@ -145,6 +145,15 @@ namespace AzFramework return worldPosition; } + AZ::Vector3 ScreenToWorld( + const ScreenPoint& screenPosition, const AZ::Matrix4x4& inverseCameraView, + const AZ::Matrix4x4& inverseCameraProjection, const AZ::Vector2& viewportSize) + { + const auto normalizedScreenPosition = NDCFromScreenPoint(screenPosition, viewportSize); + + return ScreenNDCToWorld(normalizedScreenPosition, inverseCameraView, inverseCameraProjection); + } + AZ::Vector3 ScreenToWorld(const ScreenPoint& screenPosition, const CameraState& cameraState) { return ScreenToWorld( diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.h b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.h index a2c650465f..844f52b565 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.h +++ b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.h @@ -42,7 +42,7 @@ namespace AzFramework const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection, const AZ::Vector2& viewportSize); - //! Unprojects a position in screen space to world space. + //! Unprojects a position in screen space pixel coordinates to world space. //! Note: The position returned will be on the near clip plane of the camera in world space. AZ::Vector3 ScreenToWorld(const ScreenPoint& screenPosition, const CameraState& cameraState); @@ -52,6 +52,12 @@ namespace AzFramework 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); + //! Returns the camera projection for the current camera state. AZ::Matrix4x4 CameraProjection(const CameraState& cameraState); diff --git a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ActionDispatcher.h b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ActionDispatcher.h index c9211e9f26..1eba4f3799 100644 --- a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ActionDispatcher.h +++ b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ActionDispatcher.h @@ -97,7 +97,7 @@ namespace AzManipulatorTestFramework if (m_logging) { AZStd::string message = AZStd::string::format(format, args...); - std::cout << "[ActionDispatcher] " << message.c_str() << "\n"; + AZ_Printf("[ActionDispatcher] %s", message.c_str()); } } diff --git a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h index f1c32e4d8d..9d380003e2 100644 --- a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h +++ b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h @@ -21,12 +21,14 @@ namespace AzManipulatorTestFramework { //! Create a linear manipulator with a unit sphere bound. AZStd::shared_ptr CreateLinearManipulator( - const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, const AZ::Vector3& position = AZ::Vector3::CreateZero(), + const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, + const AZ::Vector3& position = AZ::Vector3::CreateZero(), float radius = 1.0f); //! Create a planar manipulator with a unit sphere bound. AZStd::shared_ptr CreatePlanarManipulator( - const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, const AZ::Vector3& position = AZ::Vector3::CreateZero(), + const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, + const AZ::Vector3& position = AZ::Vector3::CreateZero(), float radius = 1.0f); //! Create a mouse pick from the specified ray and screen point. @@ -39,7 +41,8 @@ namespace AzManipulatorTestFramework //! Create a mouse interaction from the specified pick, buttons, interaction id and keyboard modifiers. AzToolsFramework::ViewportInteraction::MouseInteraction CreateMouseInteraction( - const AzToolsFramework::ViewportInteraction::MousePick& mousePick, AzToolsFramework::ViewportInteraction::MouseButtons buttons, + const AzToolsFramework::ViewportInteraction::MousePick& mousePick, + AzToolsFramework::ViewportInteraction::MouseButtons buttons, AzToolsFramework::ViewportInteraction::InteractionId interactionId, AzToolsFramework::ViewportInteraction::KeyboardModifiers modifiers); diff --git a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ImmediateModeActionDispatcher.h b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ImmediateModeActionDispatcher.h index 6759c2255f..1faf4eff65 100644 --- a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ImmediateModeActionDispatcher.h +++ b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ImmediateModeActionDispatcher.h @@ -12,14 +12,13 @@ #pragma once -#include #include +#include namespace AzManipulatorTestFramework { //! Dispatches actions immediately to the manipulators. - class ImmediateModeActionDispatcher - : public ActionDispatcher + class ImmediateModeActionDispatcher : public ActionDispatcher { using KeyboardModifier = AzToolsFramework::ViewportInteraction::KeyboardModifier; using KeyboardModifiers = AzToolsFramework::ViewportInteraction::KeyboardModifiers; @@ -62,7 +61,7 @@ namespace AzManipulatorTestFramework void MouseLButtonUpImpl() override; void MousePositionImpl(const AzFramework::ScreenPoint& position) override; void KeyboardModifierDownImpl(const KeyboardModifier& keyModifier) override; - void KeyboardModifierUpImpl(const KeyboardModifier& keyModifier) override; + void KeyboardModifierUpImpl(const KeyboardModifier& keyModifier) override; void ExpectManipulatorBeingInteractedImpl() override; void ExpectManipulatorNotBeingInteractedImpl() override; void SetEntityWorldTransformImpl(AZ::EntityId entityId, const AZ::Transform& transform) override; @@ -97,8 +96,7 @@ namespace AzManipulatorTestFramework return this; } - inline ImmediateModeActionDispatcher* ImmediateModeActionDispatcher::GetKeyboardModifiers( - KeyboardModifiers& keyboardModifiers) + inline ImmediateModeActionDispatcher* ImmediateModeActionDispatcher::GetKeyboardModifiers(KeyboardModifiers& keyboardModifiers) { keyboardModifiers = GetKeyboardModifiers(); return this; diff --git a/Code/Framework/AzManipulatorTestFramework/Source/AzManipulatorTestFrameworkUtils.cpp b/Code/Framework/AzManipulatorTestFramework/Source/AzManipulatorTestFrameworkUtils.cpp index 985c21cf8e..83746c9490 100644 --- a/Code/Framework/AzManipulatorTestFramework/Source/AzManipulatorTestFrameworkUtils.cpp +++ b/Code/Framework/AzManipulatorTestFramework/Source/AzManipulatorTestFrameworkUtils.cpp @@ -30,8 +30,10 @@ namespace AzManipulatorTestFramework // create a default sphere view for a manipulator for simple intersection template void SetupManipulatorView( - AZStd::shared_ptr manipulator, const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, - const AZ::Vector3& position, const float radius) + AZStd::shared_ptr manipulator, + const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, + const AZ::Vector3& position, + const float radius) { // unit sphere view auto sphereView = AzToolsFramework::CreateManipulatorViewSphere( diff --git a/Code/Framework/AzManipulatorTestFramework/Source/DirectManipulatorViewportInteraction.cpp b/Code/Framework/AzManipulatorTestFramework/Source/DirectManipulatorViewportInteraction.cpp index 5c5f77629c..192202cee2 100644 --- a/Code/Framework/AzManipulatorTestFramework/Source/DirectManipulatorViewportInteraction.cpp +++ b/Code/Framework/AzManipulatorTestFramework/Source/DirectManipulatorViewportInteraction.cpp @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include #include @@ -19,10 +19,10 @@ namespace AzManipulatorTestFramework using MouseInteraction = AzToolsFramework::ViewportInteraction::MouseInteraction; using MouseInteractionEvent = AzToolsFramework::ViewportInteraction::MouseInteractionEvent; - class CustomManipulatorManager - : public AzToolsFramework::ManipulatorManager + class CustomManipulatorManager : public AzToolsFramework::ManipulatorManager { using ManagerBase = AzToolsFramework::ManipulatorManager; + public: using ManagerBase::ManagerBase; @@ -31,18 +31,17 @@ namespace AzManipulatorTestFramework }; //! Implementation of the manipulator interface using direct access to the manipulator manager. - class DirectCallManipulatorManager - : public ManipulatorManagerInterface + class DirectCallManipulatorManager : public ManipulatorManagerInterface { public: DirectCallManipulatorManager( - ViewportInteractionInterface* viewportInteraction, - AZStd::shared_ptr manipulatorManager); - + ViewportInteractionInterface* viewportInteraction, AZStd::shared_ptr manipulatorManager); + // ManipulatorManagerInterface ... void ConsumeMouseInteractionEvent(const MouseInteractionEvent& event); AzToolsFramework::ManipulatorManagerId GetId() const override; bool ManipulatorBeingInteracted() const override; + private: // Trigger the updating of manipulator bounds. void DrawManipulators(const MouseInteraction& mouseInteraction); @@ -61,8 +60,7 @@ namespace AzManipulatorTestFramework } DirectCallManipulatorManager::DirectCallManipulatorManager( - ViewportInteractionInterface* viewportInteraction, - AZStd::shared_ptr manipulatorManager) + ViewportInteractionInterface* viewportInteraction, AZStd::shared_ptr manipulatorManager) : m_viewportInteraction(viewportInteraction) , m_manipulatorManager(AZStd::move(manipulatorManager)) { @@ -126,11 +124,9 @@ namespace AzManipulatorTestFramework DirectCallManipulatorViewportInteraction::DirectCallManipulatorViewportInteraction() : m_customManager( - AZStd::make_unique( - AzToolsFramework::ManipulatorManagerId(AZ::Crc32("TestManipulatorManagerId")))) + AZStd::make_unique(AzToolsFramework::ManipulatorManagerId(AZ::Crc32("TestManipulatorManagerId")))) , m_viewportInteraction(AZStd::make_unique()) - , m_manipulatorManager( - AZStd::make_unique(m_viewportInteraction.get(), m_customManager)) + , m_manipulatorManager(AZStd::make_unique(m_viewportInteraction.get(), m_customManager)) { } diff --git a/Code/Framework/AzManipulatorTestFramework/Source/ImmediateModeActionDispatcher.cpp b/Code/Framework/AzManipulatorTestFramework/Source/ImmediateModeActionDispatcher.cpp index ad59d9578a..356a146125 100644 --- a/Code/Framework/AzManipulatorTestFramework/Source/ImmediateModeActionDispatcher.cpp +++ b/Code/Framework/AzManipulatorTestFramework/Source/ImmediateModeActionDispatcher.cpp @@ -10,8 +10,8 @@ * */ -#include #include +#include #include #include @@ -33,8 +33,7 @@ namespace AzManipulatorTestFramework using KeyboardModifier = AzToolsFramework::ViewportInteraction::KeyboardModifier; using MouseInteractionEvent = AzToolsFramework::ViewportInteraction::MouseInteractionEvent; - ImmediateModeActionDispatcher::ImmediateModeActionDispatcher( - ManipulatorViewportInteraction& viewportManipulatorInteraction) + ImmediateModeActionDispatcher::ImmediateModeActionDispatcher(ManipulatorViewportInteraction& viewportManipulatorInteraction) : m_viewportManipulatorInteraction(viewportManipulatorInteraction) { } @@ -126,8 +125,7 @@ namespace AzManipulatorTestFramework void ImmediateModeActionDispatcher::EnterComponentModeImpl(const AZ::Uuid& uuid) { using AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus; - ComponentModeSystemRequestBus::Broadcast( - &ComponentModeSystemRequestBus::Events::AddSelectedComponentModesOfType, uuid); + ComponentModeSystemRequestBus::Broadcast(&ComponentModeSystemRequestBus::Events::AddSelectedComponentModesOfType, uuid); } const AzToolsFramework::ViewportInteraction::MouseInteractionEvent* ImmediateModeActionDispatcher::GetMouseInteractionEvent() const @@ -144,8 +142,7 @@ namespace AzManipulatorTestFramework AzToolsFramework::ViewportInteraction::MouseInteractionEvent* ImmediateModeActionDispatcher::GetMouseInteractionEvent() { - return const_cast( - static_cast(this)->GetMouseInteractionEvent()); + return const_cast(static_cast(this)->GetMouseInteractionEvent()); } ImmediateModeActionDispatcher* ImmediateModeActionDispatcher::ExpectTrue(bool result) @@ -162,8 +159,7 @@ namespace AzManipulatorTestFramework return this; } - ImmediateModeActionDispatcher* ImmediateModeActionDispatcher::GetEntityWorldTransform( - AZ::EntityId entityId, AZ::Transform& transform) + ImmediateModeActionDispatcher* ImmediateModeActionDispatcher::GetEntityWorldTransform(AZ::EntityId entityId, AZ::Transform& transform) { Log("Getting entity world transform"); transform = AzToolsFramework::GetWorldTransform(entityId); diff --git a/Code/Framework/AzManipulatorTestFramework/Tests/BusCallTest.cpp b/Code/Framework/AzManipulatorTestFramework/Tests/BusCallTest.cpp index 8cc906f339..b2da9965e6 100644 --- a/Code/Framework/AzManipulatorTestFramework/Tests/BusCallTest.cpp +++ b/Code/Framework/AzManipulatorTestFramework/Tests/BusCallTest.cpp @@ -11,17 +11,18 @@ */ #include "AzManipulatorTestFrameworkTestFixtures.h" -#include #include +#include namespace UnitTest { - class AzManipulatorTestFrameworkBusCallTestFixture - : public LinearManipulatorTestFixture + class AzManipulatorTestFrameworkBusCallTestFixture : public LinearManipulatorTestFixture { protected: AzManipulatorTestFrameworkBusCallTestFixture() - : LinearManipulatorTestFixture(AzToolsFramework::g_mainManipulatorManagerId) {} + : LinearManipulatorTestFixture(AzToolsFramework::g_mainManipulatorManagerId) + { + } bool IsManipulatorInteractingBusCall() const { @@ -37,8 +38,8 @@ namespace UnitTest TEST_F(AzManipulatorTestFrameworkBusCallTestFixture, ConsumeViewportLeftMouseClick) { // given a left mouse down ray in world space - auto event = AzManipulatorTestFramework::CreateMouseInteractionEvent( - m_interaction, AzToolsFramework::ViewportInteraction::MouseEvent::Down); + auto event = + AzManipulatorTestFramework::CreateMouseInteractionEvent(m_interaction, AzToolsFramework::ViewportInteraction::MouseEvent::Down); // consume the mouse down and up events AzManipulatorTestFramework::DispatchMouseInteractionEvent(event); @@ -56,8 +57,8 @@ namespace UnitTest TEST_F(AzManipulatorTestFrameworkBusCallTestFixture, ConsumeViewportMouseMoveHover) { // given a left mouse down ray in world space - const auto event = AzManipulatorTestFramework::CreateMouseInteractionEvent( - m_interaction, AzToolsFramework::ViewportInteraction::MouseEvent::Move); + const auto event = + AzManipulatorTestFramework::CreateMouseInteractionEvent(m_interaction, AzToolsFramework::ViewportInteraction::MouseEvent::Move); // consume the mouse move event AzManipulatorTestFramework::DispatchMouseInteractionEvent(event); @@ -75,8 +76,8 @@ namespace UnitTest TEST_F(AzManipulatorTestFrameworkBusCallTestFixture, ConsumeViewportMouseMoveActive) { // given a left mouse down ray in world space - auto event = AzManipulatorTestFramework::CreateMouseInteractionEvent( - m_interaction, AzToolsFramework::ViewportInteraction::MouseEvent::Down); + auto event = + AzManipulatorTestFramework::CreateMouseInteractionEvent(m_interaction, AzToolsFramework::ViewportInteraction::MouseEvent::Down); // consume the mouse down event AzManipulatorTestFramework::DispatchMouseInteractionEvent(event); @@ -110,14 +111,14 @@ namespace UnitTest const AZ::Vector3 initialManipulatorPosition = m_linearManipulator->GetLocalPosition(); m_linearManipulator->InstallMouseMoveCallback( [&movementAlongAxis, this](const AzToolsFramework::LinearManipulator::Action& action) - { - movementAlongAxis = action.LocalPositionOffset(); - m_linearManipulator->SetLocalPosition(action.LocalPosition()); - }); + { + movementAlongAxis = action.LocalPositionOffset(); + m_linearManipulator->SetLocalPosition(action.LocalPosition()); + }); // given a left mouse down ray in world space - auto event = AzManipulatorTestFramework::CreateMouseInteractionEvent( - m_interaction, AzToolsFramework::ViewportInteraction::MouseEvent::Down); + auto event = + AzManipulatorTestFramework::CreateMouseInteractionEvent(m_interaction, AzToolsFramework::ViewportInteraction::MouseEvent::Down); // consume the mouse down event AzManipulatorTestFramework::DispatchMouseInteractionEvent(event); @@ -134,7 +135,7 @@ namespace UnitTest // consume the mouse up event event.m_mouseEvent = AzToolsFramework::ViewportInteraction::MouseEvent::Up; AzManipulatorTestFramework::DispatchMouseInteractionEvent(event); - + // expect the left mouse down/up sanity flags to be set EXPECT_TRUE(m_receivedLeftMouseDown); EXPECT_TRUE(m_receivedLeftMouseUp); diff --git a/Code/Framework/AzManipulatorTestFramework/Tests/DirectCallTest.cpp b/Code/Framework/AzManipulatorTestFramework/Tests/DirectCallTest.cpp index bec61ce9a8..af810c1dcf 100644 --- a/Code/Framework/AzManipulatorTestFramework/Tests/DirectCallTest.cpp +++ b/Code/Framework/AzManipulatorTestFramework/Tests/DirectCallTest.cpp @@ -14,10 +14,10 @@ namespace UnitTest { - class CustomManipulatorManager - : public AzToolsFramework::ManipulatorManager + class CustomManipulatorManager : public AzToolsFramework::ManipulatorManager { using ManagerBase = AzToolsFramework::ManipulatorManager; + public: using ManagerBase::ManagerBase; @@ -27,17 +27,17 @@ namespace UnitTest } }; - class AzManipulatorTestFrameworkCustomManagerTestFixture - : public LinearManipulatorTestFixture + class AzManipulatorTestFrameworkCustomManagerTestFixture : public LinearManipulatorTestFixture { protected: AzManipulatorTestFrameworkCustomManagerTestFixture() - : LinearManipulatorTestFixture(AzToolsFramework::ManipulatorManagerId(AZ::Crc32("TestManipulatorManagerId"))) {} + : LinearManipulatorTestFixture(AzToolsFramework::ManipulatorManagerId(AZ::Crc32("TestManipulatorManagerId"))) + { + } void SetUpEditorFixtureImpl() override { - m_manipulatorManager = - AZStd::make_shared(m_manipulatorManagerId); + m_manipulatorManager = AZStd::make_shared(m_manipulatorManagerId); LinearManipulatorTestFixture::SetUpEditorFixtureImpl(); } @@ -115,9 +115,9 @@ namespace UnitTest m_linearManipulator->InstallMouseMoveCallback( [&movementAlongAxis](const AzToolsFramework::LinearManipulator::Action& action) - { - movementAlongAxis = action.m_current.m_localPositionOffset; - }); + { + movementAlongAxis = action.m_current.m_localPositionOffset; + }); // consume the mouse down event m_manipulatorManager->ConsumeViewportMousePress(m_interaction); @@ -141,4 +141,3 @@ namespace UnitTest EXPECT_EQ(movementAlongAxis, expectedPositionAfterMovementAlongAxis); } } // namespace UnitTest - diff --git a/Code/Framework/AzManipulatorTestFramework/Tests/GridSnappingTest.cpp b/Code/Framework/AzManipulatorTestFramework/Tests/GridSnappingTest.cpp index d6006ba74f..160a9933d9 100644 --- a/Code/Framework/AzManipulatorTestFramework/Tests/GridSnappingTest.cpp +++ b/Code/Framework/AzManipulatorTestFramework/Tests/GridSnappingTest.cpp @@ -12,6 +12,7 @@ #include "AzManipulatorTestFrameworkTestFixtures.h" +#include #include #include #include @@ -22,7 +23,6 @@ #include #include #include -#include namespace UnitTest { @@ -94,7 +94,8 @@ namespace UnitTest template void ValidateManipulatorSnappingBehavior( - AZStd::shared_ptr manipulator, AzManipulatorTestFramework::ImmediateModeActionDispatcher* actionDispatcher, + AZStd::shared_ptr manipulator, + AzManipulatorTestFramework::ImmediateModeActionDispatcher* actionDispatcher, const AzFramework::CameraState& cameraState) { manipulator->SetLocalOrientation(AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(180.0f, 0.0f, 135.0f))); diff --git a/Code/Framework/AzManipulatorTestFramework/Tests/ViewportInteractionTest.cpp b/Code/Framework/AzManipulatorTestFramework/Tests/ViewportInteractionTest.cpp index 6ba44cc71b..4aad2ea138 100644 --- a/Code/Framework/AzManipulatorTestFramework/Tests/ViewportInteractionTest.cpp +++ b/Code/Framework/AzManipulatorTestFramework/Tests/ViewportInteractionTest.cpp @@ -15,8 +15,7 @@ namespace UnitTest { - class AValidViewportInteraction - : public ToolsApplicationFixture + class AValidViewportInteraction : public ToolsApplicationFixture { public: AValidViewportInteraction() @@ -27,8 +26,7 @@ namespace UnitTest protected: void SetUpEditorFixtureImpl() override { - m_cameraState = - AzFramework::CreateIdentityDefaultCamera(AZ::Vector3::CreateZero(), AZ::Vector2(800.0f, 600.0f)); + m_cameraState = AzFramework::CreateIdentityDefaultCamera(AZ::Vector3::CreateZero(), AZ::Vector2(800.0f, 600.0f)); } public: diff --git a/Code/Framework/AzManipulatorTestFramework/Tests/WorldSpaceBuilderTest.cpp b/Code/Framework/AzManipulatorTestFramework/Tests/WorldSpaceBuilderTest.cpp index a6c58971f6..6168e9cece 100644 --- a/Code/Framework/AzManipulatorTestFramework/Tests/WorldSpaceBuilderTest.cpp +++ b/Code/Framework/AzManipulatorTestFramework/Tests/WorldSpaceBuilderTest.cpp @@ -11,49 +11,48 @@ */ #include -#include #include +#include #include -#include #include -#include +#include #include +#include namespace UnitTest { - class AzManipulatorTestFrameworkWorldSpaceBuilderTestFixture - : public ToolsApplicationFixture + class AzManipulatorTestFrameworkWorldSpaceBuilderTestFixture : public ToolsApplicationFixture { protected: struct State { State(AZStd::unique_ptr viewportManipulatorInteraction) : m_viewportManipulatorInteraction(viewportManipulatorInteraction.release()) - , m_actionDispatcher(AZStd::make_unique(*m_viewportManipulatorInteraction)) - , m_linearManipulator( - AzManipulatorTestFramework::CreateLinearManipulator( - m_viewportManipulatorInteraction->GetManipulatorManager().GetId(), - /*position=*/AZ::Vector3(0.0f, 50.0f, 0.0f), - /*radius=*/m_boundsRadius)) + , m_actionDispatcher( + AZStd::make_unique(*m_viewportManipulatorInteraction)) + , m_linearManipulator(AzManipulatorTestFramework::CreateLinearManipulator( + m_viewportManipulatorInteraction->GetManipulatorManager().GetId(), + /*position=*/AZ::Vector3(0.0f, 50.0f, 0.0f), + /*radius=*/m_boundsRadius)) { // default sanity check call backs m_linearManipulator->InstallLeftMouseDownCallback( [this]([[maybe_unused]] const AzToolsFramework::LinearManipulator::Action& action) - { - m_receivedLeftMouseDown = true; - }); + { + m_receivedLeftMouseDown = true; + }); m_linearManipulator->InstallMouseMoveCallback( [this]([[maybe_unused]] const AzToolsFramework::LinearManipulator::Action& action) - { - m_receivedMouseMove = true; - }); + { + m_receivedMouseMove = true; + }); m_linearManipulator->InstallLeftMouseUpCallback( [this]([[maybe_unused]] const AzToolsFramework::LinearManipulator::Action& action) - { - m_receivedLeftMouseUp = true; - }); + { + m_receivedLeftMouseUp = true; + }); } ~State() = default; @@ -79,13 +78,12 @@ namespace UnitTest protected: void SetUpEditorFixtureImpl() override { - m_directState = AZStd::make_unique( - AZStd::make_unique()); - m_busState = AZStd::make_unique( - AZStd::make_unique()); + m_directState = + AZStd::make_unique(AZStd::make_unique()); + m_busState = + AZStd::make_unique(AZStd::make_unique()); m_cameraState = - AzFramework::CreateIdentityDefaultCamera( - AZ::Vector3::CreateZero(), AzManipulatorTestFramework::DefaultViewportSize); + AzFramework::CreateIdentityDefaultCamera(AZ::Vector3::CreateZero(), AzManipulatorTestFramework::DefaultViewportSize); } void TearDownEditorFixtureImpl() override @@ -105,8 +103,7 @@ namespace UnitTest { // given a left mouse down ray in world space // consume the mouse down and up events - state.m_actionDispatcher - ->CameraState(m_cameraState) + state.m_actionDispatcher->CameraState(m_cameraState) ->MousePosition(AzManipulatorTestFramework::GetCameraStateViewportCenter(m_cameraState)) ->MouseLButtonDown() ->Trace("Expecting left mouse button down") @@ -126,31 +123,27 @@ namespace UnitTest ->ExpectTrue(state.m_receivedLeftMouseUp) ->ExpectTrue(state.m_receivedMouseMove) ->ExpectFalse(state.m_linearManipulator->PerformingAction()) - ->ExpectManipulatorNotBeingInteracted() - ; + ->ExpectManipulatorNotBeingInteracted(); } void AzManipulatorTestFrameworkWorldSpaceBuilderTestFixture::ConsumeViewportMouseMoveHover(State& state) { // given a left mouse down ray in world space // consume the mouse move event - state.m_actionDispatcher - ->CameraState(m_cameraState) + state.m_actionDispatcher->CameraState(m_cameraState) ->MousePosition(AzManipulatorTestFramework::GetCameraStateViewportCenter(m_cameraState)) ->ExpectFalse(state.m_linearManipulator->PerformingAction()) ->ExpectManipulatorNotBeingInteracted() ->ExpectFalse(state.m_receivedLeftMouseDown) ->ExpectFalse(state.m_receivedMouseMove) - ->ExpectFalse(state.m_receivedLeftMouseUp) - ; + ->ExpectFalse(state.m_receivedLeftMouseUp); } void AzManipulatorTestFrameworkWorldSpaceBuilderTestFixture::ConsumeViewportMouseMoveActive(State& state) { // given a left mouse down ray in world space // consume the mouse move event - state.m_actionDispatcher - ->CameraState(m_cameraState) + state.m_actionDispatcher->CameraState(m_cameraState) ->MouseLButtonDown() ->MousePosition(AzManipulatorTestFramework::GetCameraStateViewportCenter(m_cameraState)) ->ExpectTrue(state.m_linearManipulator->PerformingAction()) @@ -158,8 +151,7 @@ namespace UnitTest ->MouseLButtonUp() ->ExpectTrue(state.m_receivedLeftMouseDown) ->ExpectTrue(state.m_receivedMouseMove) - ->ExpectTrue(state.m_receivedLeftMouseUp) - ; + ->ExpectTrue(state.m_receivedLeftMouseUp); } void AzManipulatorTestFrameworkWorldSpaceBuilderTestFixture::MoveManipulatorAlongAxis(State& state) @@ -176,8 +168,7 @@ namespace UnitTest // adjusted final world position taking into account the manipulator position relative to the camera const auto finalPositionWorldAdjusted = finalPositionWorld - (vectorToInitialPositionWorld * scaledRadiusBound); // calculate the position in screen space of the initial position of the manipulator - const auto initialPositionScreen = - AzFramework::WorldToScreen(initialPositionWorld, m_cameraState); + const auto initialPositionScreen = AzFramework::WorldToScreen(initialPositionWorld, m_cameraState); // calculate the position in screen space of the final position of the manipulator const auto finalPositionScreen = AzFramework::WorldToScreen(finalPositionWorldAdjusted, m_cameraState); @@ -185,12 +176,11 @@ namespace UnitTest state.m_linearManipulator->InstallMouseMoveCallback( [&movementAlongAxis](const AzToolsFramework::LinearManipulator::Action& action) - { - movementAlongAxis = action.LocalPosition(); - }); + { + movementAlongAxis = action.LocalPosition(); + }); - state.m_actionDispatcher - ->CameraState(m_cameraState) + state.m_actionDispatcher->CameraState(m_cameraState) ->MousePosition(initialPositionScreen) ->MouseLButtonDown() ->ExpectTrue(state.m_linearManipulator->PerformingAction()) @@ -199,8 +189,7 @@ namespace UnitTest ->MouseLButtonUp() ->ExpectTrue(state.m_receivedLeftMouseDown) ->ExpectTrue(state.m_receivedLeftMouseUp) - ->ExpectTrue(movementAlongAxis.IsClose(finalPositionWorld, 0.01f)) - ; + ->ExpectTrue(movementAlongAxis.IsClose(finalPositionWorld, 0.01f)); } TEST_F(AzManipulatorTestFrameworkWorldSpaceBuilderTestFixture, ConsumeViewportLeftMouseClick) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/Locked_Status.svg b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/Locked_Status.svg new file mode 100644 index 0000000000..2612059dce --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/Locked_Status.svg @@ -0,0 +1,12 @@ + + + Icon / Locked Status + + + + + + + + + \ No newline at end of file diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/resources.qrc b/Code/Framework/AzQtComponents/AzQtComponents/Components/resources.qrc index 00fa95d094..7070bd372b 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/resources.qrc +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/resources.qrc @@ -356,7 +356,8 @@ img/UI20/toolbar/Load.svg img/UI20/toolbar/Local.svg img/UI20/toolbar/Locked.svg - img/UI20/toolbar/LUA.svg + img/UI20/toolbar/Locked_Status.svg + img/UI20/toolbar/LUA.svg img/UI20/toolbar/Material.svg img/UI20/toolbar/Measure.svg img/UI20/toolbar/Move.svg diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/AngularManipulator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/AngularManipulator.cpp index 9a4a952d8f..6141fd9642 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/AngularManipulator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/AngularManipulator.cpp @@ -1,28 +1,32 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "AngularManipulator.h" #include -#include #include +#include namespace AzToolsFramework { - static const float s_circularRotateThresholdDegrees = 80.0f; + static const float CircularRotateThresholdDegrees = 80.0f; AngularManipulator::ActionInternal AngularManipulator::CalculateManipulationDataStart( - const Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Transform& localTransform, - const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDirection, const float rayDistance) + const Fixed& fixed, + const AZ::Transform& worldFromLocal, + const AZ::Transform& localTransform, + const AZ::Vector3& rayOrigin, + const AZ::Vector3& rayDirection, + const float rayDistance) { const AZ::Transform worldFromLocalWithTransform = worldFromLocal * localTransform; const AZ::Vector3 worldAxis = TransformDirectionNoScaling(worldFromLocalWithTransform, fixed.m_axis); @@ -35,7 +39,7 @@ namespace AzToolsFramework // if angular manipulator axis is at right angles to us, use initial ray direction // as plane normal and use hit position on manipulator as plane point const float pickAngle = AZ::RadToDeg(AZ::Acos(AZ::Abs(rayDirection.Dot(worldAxis)))); - if (pickAngle > s_circularRotateThresholdDegrees) + if (pickAngle > CircularRotateThresholdDegrees) { actionInternal.m_start.m_planeNormal = -rayDirection; actionInternal.m_start.m_planePoint = rayOrigin + rayDirection * rayDistance; @@ -43,8 +47,8 @@ namespace AzToolsFramework // store initial world hit position Internal::CalculateRayPlaneIntersectingPoint( - rayOrigin, rayDirection, actionInternal.m_start.m_planePoint, - actionInternal.m_start.m_planeNormal, actionInternal.m_current.m_worldHitPosition); + rayOrigin, rayDirection, actionInternal.m_start.m_planePoint, actionInternal.m_start.m_planeNormal, + actionInternal.m_current.m_worldHitPosition); // store entity transform (to go from local to world space) // and store our own starting local transform @@ -56,31 +60,33 @@ namespace AzToolsFramework } AngularManipulator::Action AngularManipulator::CalculateManipulationDataAction( - const Fixed& fixed, ActionInternal& actionInternal, const AZ::Transform& worldFromLocal, - const AZ::Transform& localTransform, const bool snapping, const float angleStepDegrees, - const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDirection, + const Fixed& fixed, + ActionInternal& actionInternal, + const AZ::Transform& worldFromLocal, + const AZ::Transform& localTransform, + const bool snapping, + const float angleStepDegrees, + const AZ::Vector3& rayOrigin, + const AZ::Vector3& rayDirection, const ViewportInteraction::KeyboardModifiers keyboardModifiers) { const AZ::Transform worldFromLocalWithTransform = worldFromLocal * localTransform; const AZ::Vector3 worldAxis = TransformDirectionNoScaling(worldFromLocalWithTransform, fixed.m_axis); AZ::Vector3 worldHitPosition = AZ::Vector3::CreateZero(); - Internal::CalculateRayPlaneIntersectingPoint(rayOrigin, rayDirection, - actionInternal.m_start.m_planePoint, actionInternal.m_start.m_planeNormal, - worldHitPosition); + Internal::CalculateRayPlaneIntersectingPoint( + rayOrigin, rayDirection, actionInternal.m_start.m_planePoint, actionInternal.m_start.m_planeNormal, worldHitPosition); // get vector from center of rotation for current and previous frame const AZ::Vector3 center = worldFromLocalWithTransform.GetTranslation(); const AZ::Vector3 currentWorldHitVector = (worldHitPosition - center).GetNormalizedSafe(); - const AZ::Vector3 previousWorldHitVector = - (actionInternal.m_current.m_worldHitPosition - center).GetNormalizedSafe(); + const AZ::Vector3 previousWorldHitVector = (actionInternal.m_current.m_worldHitPosition - center).GetNormalizedSafe(); // calculate which direction we rotated const AZ::Vector3 worldAxisRight = worldAxis.Cross(previousWorldHitVector); const float rotateSign = Sign(currentWorldHitVector.Dot(worldAxisRight)); // how far did we rotate this frame - const float rotationAngleRad = AZ::Acos(AZ::GetMin( - 1.0f, currentWorldHitVector.Dot(previousWorldHitVector))); + const float rotationAngleRad = AZ::Acos(AZ::GetMin(1.0f, currentWorldHitVector.Dot(previousWorldHitVector))); actionInternal.m_current.m_worldHitPosition = worldHitPosition; // if we're snapping, only increment current radians when we know @@ -148,16 +154,13 @@ namespace AzToolsFramework // calculate initial state when mouse press first happens m_actionInternal = CalculateManipulationDataStart( m_fixed, TransformNormalizedScale(GetSpace()), TransformNormalizedScale(GetLocalTransform()), - interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection, - rayIntersectionDistance); + interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection, rayIntersectionDistance); if (m_onLeftMouseDownCallback) { m_onLeftMouseDownCallback(CalculateManipulationDataAction( - m_fixed, m_actionInternal, m_actionInternal.m_start.m_worldFromLocal, - m_actionInternal.m_start.m_localTransform, snapping, angleStep, - interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection, - interaction.m_keyboardModifiers)); + m_fixed, m_actionInternal, m_actionInternal.m_start.m_worldFromLocal, m_actionInternal.m_start.m_localTransform, snapping, + angleStep, interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection, interaction.m_keyboardModifiers)); } } @@ -167,12 +170,9 @@ namespace AzToolsFramework { // calculate delta rotation m_onMouseMoveCallback(CalculateManipulationDataAction( - m_fixed, m_actionInternal, m_actionInternal.m_start.m_worldFromLocal, - m_actionInternal.m_start.m_localTransform, - AngleSnapping(interaction.m_interactionId.m_viewportId), - AngleStep(interaction.m_interactionId.m_viewportId), - interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection, - interaction.m_keyboardModifiers)); + m_fixed, m_actionInternal, m_actionInternal.m_start.m_worldFromLocal, m_actionInternal.m_start.m_localTransform, + AngleSnapping(interaction.m_interactionId.m_viewportId), AngleStep(interaction.m_interactionId.m_viewportId), + interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection, interaction.m_keyboardModifiers)); } } @@ -181,12 +181,9 @@ namespace AzToolsFramework if (m_onLeftMouseUpCallback) { m_onLeftMouseUpCallback(CalculateManipulationDataAction( - m_fixed, m_actionInternal, m_actionInternal.m_start.m_worldFromLocal, - m_actionInternal.m_start.m_localTransform, - AngleSnapping(interaction.m_interactionId.m_viewportId), - AngleStep(interaction.m_interactionId.m_viewportId), - interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection, - interaction.m_keyboardModifiers)); + m_fixed, m_actionInternal, m_actionInternal.m_start.m_worldFromLocal, m_actionInternal.m_start.m_localTransform, + AngleSnapping(interaction.m_interactionId.m_viewportId), AngleStep(interaction.m_interactionId.m_viewportId), + interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection, interaction.m_keyboardModifiers)); } } @@ -197,12 +194,9 @@ namespace AzToolsFramework const ViewportInteraction::MouseInteraction& mouseInteraction) { m_manipulatorView->Draw( - GetManipulatorManagerId(), managerState, - GetManipulatorId(), { - ApplySpace(GetLocalTransform()), GetNonUniformScale(), - AZ::Vector3::CreateZero(), MouseOver() - }, - debugDisplay, cameraState, mouseInteraction); + GetManipulatorManagerId(), managerState, GetManipulatorId(), + { ApplySpace(GetLocalTransform()), GetNonUniformScale(), AZ::Vector3::CreateZero(), MouseOver() }, debugDisplay, cameraState, + mouseInteraction); } void AngularManipulator::SetAxis(const AZ::Vector3& axis) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/AngularManipulator.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/AngularManipulator.h index e2f95fce5e..8e0468aa90 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/AngularManipulator.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/AngularManipulator.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -22,14 +22,14 @@ namespace AzToolsFramework { class ManipulatorView; - /// AngularManipulator serves as a visual tool for users to change a component's property based on rotation - /// around an axis. The rotation angle increases if the rotation goes counter clock-wise when looking - /// in the opposite direction the rotation axis points to. + //! AngularManipulator serves as a visual tool for users to change a component's property based on rotation + //! around an axis. The rotation angle increases if the rotation goes counter clock-wise when looking + //! in the opposite direction the rotation axis points to. class AngularManipulator : public BaseManipulator , public ManipulatorSpaceWithLocalTransform { - /// Private constructor. + //! Private constructor. explicit AngularManipulator(const AZ::Transform& worldFromLocal); public: @@ -42,33 +42,36 @@ namespace AzToolsFramework ~AngularManipulator() = default; - /// A Manipulator must only be created and managed through a shared_ptr. + //! A Manipulator must only be created and managed through a shared_ptr. static AZStd::shared_ptr MakeShared(const AZ::Transform& worldFromLocal); - /// The state of the manipulator at the start of an interaction. + //! The state of the manipulator at the start of an interaction. struct Start { - AZ::Quaternion m_space; ///< Starting orientation space of manipulator. - AZ::Quaternion m_rotation; ///< Starting local rotation of the manipulator. + AZ::Quaternion m_space; //!< Starting orientation space of manipulator. + AZ::Quaternion m_rotation; //!< Starting local rotation of the manipulator. }; - /// The state of the manipulator during an interaction. + //! The state of the manipulator during an interaction. struct Current { - AZ::Quaternion m_delta; ///< Amount of rotation to apply to manipulator during action. + AZ::Quaternion m_delta; //!< Amount of rotation to apply to manipulator during action. }; - /// Mouse action data used by MouseActionCallback (wraps Start and Current manipulator state). + //! Mouse action data used by MouseActionCallback (wraps Start and Current manipulator state). struct Action { Start m_start; Current m_current; ViewportInteraction::KeyboardModifiers m_modifiers; - AZ::Quaternion LocalOrientation() const { return m_start.m_rotation * m_current.m_delta; } + AZ::Quaternion LocalOrientation() const + { + return m_start.m_rotation * m_current.m_delta; + } }; - /// This is the function signature of callbacks that will be invoked whenever a manipulator - /// is clicked on or dragged. + //! This is the function signature of callbacks that will be invoked whenever a manipulator + //! is clicked on or dragged. using MouseActionCallback = AZStd::function; void InstallLeftMouseDownCallback(const MouseActionCallback& onMouseDownCallback); @@ -82,46 +85,49 @@ namespace AzToolsFramework const ViewportInteraction::MouseInteraction& mouseInteraction) override; void SetAxis(const AZ::Vector3& axis); - const AZ::Vector3& GetAxis() const { return m_fixed.m_axis; } + const AZ::Vector3& GetAxis() const + { + return m_fixed.m_axis; + } void SetView(AZStd::unique_ptr&& view); - ManipulatorView* GetView() const { return m_manipulatorView.get(); } + ManipulatorView* GetView() const + { + return m_manipulatorView.get(); + } private: - void OnLeftMouseDownImpl( - const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance) override; - void OnLeftMouseUpImpl( - const ViewportInteraction::MouseInteraction& interaction) override; - void OnMouseMoveImpl( - const ViewportInteraction::MouseInteraction& interaction) override; + void OnLeftMouseDownImpl(const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance) override; + void OnLeftMouseUpImpl(const ViewportInteraction::MouseInteraction& interaction) override; + void OnMouseMoveImpl(const ViewportInteraction::MouseInteraction& interaction) override; void SetBoundsDirtyImpl() override; void InvalidateImpl() override; - /// Unchanging data set once for the angular manipulator. + //! Unchanging data set once for the angular manipulator. struct Fixed { - AZ::Vector3 m_axis = AZ::Vector3::CreateAxisX(); ///< Axis for this angular manipulator to rotate around. + AZ::Vector3 m_axis = AZ::Vector3::CreateAxisX(); //!< Axis for this angular manipulator to rotate around. }; - /// Initial data recorded when a press first happens with an angular manipulator. + //! Initial data recorded when a press first happens with an angular manipulator. struct StartInternal { - AZ::Transform m_worldFromLocal; ///< Initial transform when pressed. - AZ::Transform m_localTransform; ///< Additional transform (offset) to apply to manipulator. - AZ::Vector3 m_planePoint; ///< Position on plane to use for ray intersection. - AZ::Vector3 m_planeNormal; ///< Normal of plane to use for ray intersection. + AZ::Transform m_worldFromLocal; //!< Initial transform when pressed. + AZ::Transform m_localTransform; //!< Additional transform (offset) to apply to manipulator. + AZ::Vector3 m_planePoint; //!< Position on plane to use for ray intersection. + AZ::Vector3 m_planeNormal; //!< Normal of plane to use for ray intersection. }; - /// Current data recorded each frame during an interaction with an angular manipulator. + //! Current data recorded each frame during an interaction with an angular manipulator. struct CurrentInternal { - float m_preSnapRadians = 0.0f; ///< Amount of rotation before a snap (snap increment accumulator). - float m_radians = 0.0f; ///< Amount of rotation about the axis for this action. - AZ::Vector3 m_worldHitPosition; ///< Initial world space hit position. + float m_preSnapRadians = 0.0f; //!< Amount of rotation before a snap (snap increment accumulator). + float m_radians = 0.0f; //!< Amount of rotation about the axis for this action. + AZ::Vector3 m_worldHitPosition; //!< Initial world space hit position. }; - /// Wrap start and current internal data during an interaction with an angular manipulator. + //! Wrap start and current internal data during an interaction with an angular manipulator. struct ActionInternal { StartInternal m_start; @@ -135,16 +141,25 @@ namespace AzToolsFramework MouseActionCallback m_onLeftMouseUpCallback = nullptr; MouseActionCallback m_onMouseMoveCallback = nullptr; - AZStd::unique_ptr m_manipulatorView; ///< Look of manipulator. + AZStd::unique_ptr m_manipulatorView; //!< Look of manipulator. static ActionInternal CalculateManipulationDataStart( - const Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Transform& localTransform, - const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDirection, float rayDistance); + const Fixed& fixed, + const AZ::Transform& worldFromLocal, + const AZ::Transform& localTransform, + const AZ::Vector3& rayOrigin, + const AZ::Vector3& rayDirection, + float rayDistance); static Action CalculateManipulationDataAction( - const Fixed& fixed, ActionInternal& actionInternal, const AZ::Transform& worldFromLocal, - const AZ::Transform& localTransform, bool snapping, float angleStepDegrees, - const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDirection, + const Fixed& fixed, + ActionInternal& actionInternal, + const AZ::Transform& worldFromLocal, + const AZ::Transform& localTransform, + bool snapping, + float angleStepDegrees, + const AZ::Vector3& rayOrigin, + const AZ::Vector3& rayDirection, ViewportInteraction::KeyboardModifiers keyboardModifiers); }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.cpp index 955d10d3bd..73d0dc72be 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.cpp @@ -1,33 +1,30 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "BaseManipulator.h" #include -#include #include +#include namespace AzToolsFramework { - AZ_CVAR( - bool, cl_manipulatorDrawDebug, false, nullptr, AZ::ConsoleFunctorFlags::Null, - "Enable debug drawing for Manipulators"); + AZ_CVAR(bool, cl_manipulatorDrawDebug, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Enable debug drawing for Manipulators"); const AZ::Color BaseManipulator::s_defaultMouseOverColor = AZ::Color(1.0f, 1.0f, 0.0f, 1.0f); // yellow AZ_CLASS_ALLOCATOR_IMPL(BaseManipulator, AZ::SystemAllocator, 0) - static bool EntityIdAndEntityComponentIdComparison( - const AZ::EntityId entityId, const AZ::EntityComponentIdPair& entityComponentId) + static bool EntityIdAndEntityComponentIdComparison(const AZ::EntityId entityId, const AZ::EntityComponentIdPair& entityComponentId) { return entityId == entityComponentId.GetEntityId(); } @@ -38,8 +35,7 @@ namespace AzToolsFramework EndUndoBatch(); } - bool BaseManipulator::OnLeftMouseDown( - const ViewportInteraction::MouseInteraction& interaction, const float rayIntersectionDistance) + bool BaseManipulator::OnLeftMouseDown(const ViewportInteraction::MouseInteraction& interaction, const float rayIntersectionDistance) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -57,8 +53,7 @@ namespace AzToolsFramework (*this.*m_onLeftMouseDownImpl)(interaction, rayIntersectionDistance); - ToolsApplicationNotificationBus::Broadcast( - &ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, Refresh_Values); + ToolsApplicationNotificationBus::Broadcast(&ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, Refresh_Values); return true; } @@ -66,8 +61,7 @@ namespace AzToolsFramework return false; } - bool BaseManipulator::OnRightMouseDown( - const ViewportInteraction::MouseInteraction& interaction, const float rayIntersectionDistance) + bool BaseManipulator::OnRightMouseDown(const ViewportInteraction::MouseInteraction& interaction, const float rayIntersectionDistance) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -85,8 +79,7 @@ namespace AzToolsFramework (*this.*m_onRightMouseDownImpl)(interaction, rayIntersectionDistance); - ToolsApplicationNotificationBus::Broadcast( - &ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, Refresh_Values); + ToolsApplicationNotificationBus::Broadcast(&ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, Refresh_Values); return true; } @@ -118,8 +111,7 @@ namespace AzToolsFramework EndUndoBatch(); } - bool BaseManipulator::OnMouseOver( - const ManipulatorId manipulatorId, const ViewportInteraction::MouseInteraction& interaction) + bool BaseManipulator::OnMouseOver(const ManipulatorId manipulatorId, const ViewportInteraction::MouseInteraction& interaction) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -132,8 +124,7 @@ namespace AzToolsFramework { OnMouseWheelImpl(interaction); - ToolsApplicationNotificationBus::Broadcast( - &ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, Refresh_Values); + ToolsApplicationNotificationBus::Broadcast(&ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, Refresh_Values); } void BaseManipulator::OnMouseMove(const ViewportInteraction::MouseInteraction& interaction) @@ -142,16 +133,13 @@ namespace AzToolsFramework if (!m_performingAction) { - AZ_Warning( - "Manipulators", false, - "MouseMove action received, but this manipulator is not performing an action"); + AZ_Warning("Manipulators", false, "MouseMove action received, but this manipulator is not performing an action"); return; } // ensure property grid (entity inspector) values are refreshed - ToolsApplicationNotificationBus::Broadcast( - &ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, Refresh_Values); + ToolsApplicationNotificationBus::Broadcast(&ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, Refresh_Values); OnMouseMoveImpl(interaction); } @@ -170,16 +158,14 @@ namespace AzToolsFramework Unregister(); } - ManipulatorManagerRequestBus::Event(managerId, - &ManipulatorManagerRequestBus::Events::RegisterManipulator, shared_from_this()); + ManipulatorManagerRequestBus::Event(managerId, &ManipulatorManagerRequestBus::Events::RegisterManipulator, shared_from_this()); } void BaseManipulator::Unregister() { // if the manipulator has already been unregistered, the m_manipulatorManagerId // should be invalid which makes the call below a no-op. - ManipulatorManagerRequestBus::Event(m_manipulatorManagerId, - &ManipulatorManagerRequestBus::Events::UnregisterManipulator, this); + ManipulatorManagerRequestBus::Event(m_manipulatorManagerId, &ManipulatorManagerRequestBus::Events::UnregisterManipulator, this); } void BaseManipulator::Invalidate() @@ -197,8 +183,7 @@ namespace AzToolsFramework if (m_performingAction) { AZ_Warning( - "Manipulators", false, - "MouseDown action received, but the manipulator (id: %d) is still performing an action", + "Manipulators", false, "MouseDown action received, but the manipulator (id: %d) is still performing an action", GetManipulatorId()); return; @@ -214,8 +199,7 @@ namespace AzToolsFramework if (!m_performingAction) { AZ_Warning( - "Manipulators", false, - "MouseUp action received, but this manipulator (id: %d) didn't receive MouseDown action before", + "Manipulators", false, "MouseUp action received, but this manipulator (id: %d) didn't receive MouseDown action before", GetManipulatorId()); return; } @@ -263,13 +247,13 @@ namespace AzToolsFramework if (entityComponentIdPair.GetComponentId() != AZ::InvalidComponentId) { PropertyEditorEntityChangeNotificationBus::Event( - entityComponentIdPair.GetEntityId(), - &PropertyEditorEntityChangeNotifications::OnEntityComponentPropertyChanged, + entityComponentIdPair.GetEntityId(), &PropertyEditorEntityChangeNotifications::OnEntityComponentPropertyChanged, entityComponentIdPair.GetComponentId()); } else { - AZ_Warning("Manipulators", false, + AZ_Warning( + "Manipulators", false, "This Manipulator was only registered with an EntityId and not an EntityComponentIdPair. " "Please use AddEntityComponentIdPair() instead of AddEntityId() when registering what this " "Manipulator is changing."); @@ -280,8 +264,7 @@ namespace AzToolsFramework for (const AZ::Component* component : entity->GetComponents()) { PropertyEditorEntityChangeNotificationBus::Event( - entity->GetId(), &PropertyEditorEntityChangeNotifications::OnEntityComponentPropertyChanged, - component->GetId()); + entity->GetId(), &PropertyEditorEntityChangeNotifications::OnEntityComponentPropertyChanged, component->GetId()); } } } @@ -298,9 +281,7 @@ namespace AzToolsFramework { // look for a match (keep looking in case we have several entity ids with different component ids) const auto entityComponentPairId = - m_entityComponentIdPairs.find_as( - entityId, AZStd::hash(), - &EntityIdAndEntityComponentIdComparison); + m_entityComponentIdPairs.find_as(entityId, AZStd::hash(), &EntityIdAndEntityComponentIdComparison); // update the afterErased variable so we can return an iterator // to the correct position in the container. @@ -334,9 +315,8 @@ namespace AzToolsFramework bool BaseManipulator::HasEntityId(const AZ::EntityId entityId) const { - return m_entityComponentIdPairs.find_as( - entityId, AZStd::hash(), - &EntityIdAndEntityComponentIdComparison) != m_entityComponentIdPairs.end(); + return m_entityComponentIdPairs.find_as(entityId, AZStd::hash(), &EntityIdAndEntityComponentIdComparison) != + m_entityComponentIdPairs.end(); } bool BaseManipulator::HasEntityComponentIdPair(const AZ::EntityComponentIdPair& entityComponentIdPair) const @@ -346,7 +326,8 @@ namespace AzToolsFramework void Manipulators::Register(const ManipulatorManagerId manipulatorManagerId) { - ProcessManipulators([manipulatorManagerId](BaseManipulator* manipulator) + ProcessManipulators( + [manipulatorManagerId](BaseManipulator* manipulator) { manipulator->Register(manipulatorManagerId); }); @@ -354,7 +335,8 @@ namespace AzToolsFramework void Manipulators::Unregister() { - ProcessManipulators([](BaseManipulator* manipulator) + ProcessManipulators( + [](BaseManipulator* manipulator) { if (manipulator->Registered()) { @@ -365,7 +347,8 @@ namespace AzToolsFramework void Manipulators::SetBoundsDirty() { - ProcessManipulators([](BaseManipulator* manipulator) + ProcessManipulators( + [](BaseManipulator* manipulator) { manipulator->SetBoundsDirty(); }); @@ -373,7 +356,8 @@ namespace AzToolsFramework void Manipulators::AddEntityComponentIdPair(const AZ::EntityComponentIdPair& entityComponentIdPair) { - ProcessManipulators([&entityComponentIdPair](BaseManipulator* manipulator) + ProcessManipulators( + [&entityComponentIdPair](BaseManipulator* manipulator) { manipulator->AddEntityComponentIdPair(entityComponentIdPair); }); @@ -381,7 +365,8 @@ namespace AzToolsFramework void Manipulators::RemoveEntityComponentIdPair(const AZ::EntityComponentIdPair& entityComponentIdPair) { - ProcessManipulators([&entityComponentIdPair](BaseManipulator* manipulator) + ProcessManipulators( + [&entityComponentIdPair](BaseManipulator* manipulator) { manipulator->RemoveEntityComponentIdPair(entityComponentIdPair); }); @@ -389,7 +374,8 @@ namespace AzToolsFramework void Manipulators::RemoveEntityId(const AZ::EntityId entityId) { - ProcessManipulators([entityId](BaseManipulator* manipulator) + ProcessManipulators( + [entityId](BaseManipulator* manipulator) { manipulator->RemoveEntityId(entityId); }); @@ -398,7 +384,8 @@ namespace AzToolsFramework bool Manipulators::PerformingAction() { bool performingAction = false; - ProcessManipulators([&performingAction](BaseManipulator* manipulator) + ProcessManipulators( + [&performingAction](BaseManipulator* manipulator) { if (manipulator->PerformingAction()) { @@ -412,7 +399,8 @@ namespace AzToolsFramework bool Manipulators::Registered() { bool registered = false; - ProcessManipulators([®istered](BaseManipulator* manipulator) + ProcessManipulators( + [®istered](BaseManipulator* manipulator) { if (manipulator->Registered()) { @@ -470,8 +458,12 @@ namespace AzToolsFramework namespace Internal { - bool CalculateRayPlaneIntersectingPoint(const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDirection, - const AZ::Vector3& pointOnPlane, const AZ::Vector3& planeNormal, AZ::Vector3& resultIntersectingPoint) + bool CalculateRayPlaneIntersectingPoint( + const AZ::Vector3& rayOrigin, + const AZ::Vector3& rayDirection, + const AZ::Vector3& pointOnPlane, + const AZ::Vector3& planeNormal, + AZ::Vector3& resultIntersectingPoint) { float t = 0.0f; if (AZ::Intersect::IntersectRayPlane(rayOrigin, rayDirection, pointOnPlane, planeNormal, t) > 0) @@ -484,11 +476,12 @@ namespace AzToolsFramework } AZ::Vector3 TryConstrainHitPositionToView( - const AZ::Vector3& currentLocalHitPosition, const AZ::Vector3& startLocalHitPosition, - const AZ::Transform& localFromWorld, const AzFramework::CameraState& cameraState) + const AZ::Vector3& currentLocalHitPosition, + const AZ::Vector3& startLocalHitPosition, + const AZ::Transform& localFromWorld, + const AzFramework::CameraState& cameraState) { - if (currentLocalHitPosition.GetDistance(localFromWorld.TransformPoint(cameraState.m_position)) - > cameraState.m_farClip) + if (currentLocalHitPosition.GetDistance(localFromWorld.TransformPoint(cameraState.m_position)) > cameraState.m_farClip) { return startLocalHitPosition; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.h index 2b078f62b7..04f2bc456b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -22,13 +22,13 @@ #include #include #include -#include "ManipulatorSpace.h" +#include namespace AzFramework { struct CameraState; class DebugDisplayRequests; -} +} // namespace AzFramework namespace AzToolsFramework { @@ -46,9 +46,8 @@ namespace AzToolsFramework struct ManipulatorManagerState; - /// The base class for manipulators, providing interfaces for users of manipulators to talk to. - class BaseManipulator - : public AZStd::enable_shared_from_this + //! The base class for manipulators, providing interfaces for users of manipulators to talk to. + class BaseManipulator : public AZStd::enable_shared_from_this { public: AZ_CLASS_ALLOCATOR_DECL @@ -61,139 +60,181 @@ namespace AzToolsFramework using EntityComponentIds = AZStd::unordered_set; - /// Callback for the event when the mouse pointer is over this manipulator and the left mouse button is pressed. - /// @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera through the mouse pointer. - /// @param rayIntersectionDistance The parameter value in the ray's explicit equation that represents the intersecting point on the target manipulator in world space. - /// @return Return true if OnLeftMouseDownImpl was attached and will be used. + //! Callback for the event when the mouse pointer is over this manipulator and the left mouse button is pressed. + //! @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera + //! through the mouse pointer. + //! @param rayIntersectionDistance The parameter value in the ray's explicit equation that represents the intersecting point on the + //! target manipulator in world space. + //! @return Return true if OnLeftMouseDownImpl was attached and will be used. bool OnLeftMouseDown(const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance); - /// Callback for the event when this manipulator is active and the left mouse button is released. - /// @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera through the mouse pointer. + //! Callback for the event when this manipulator is active and the left mouse button is released. + //! @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera + //! through the mouse pointer. void OnLeftMouseUp(const ViewportInteraction::MouseInteraction& interaction); - /// Callback for the event when the mouse pointer is over this manipulator and the right mouse button is pressed . - /// @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera through the mouse pointer. - /// @param rayIntersectionDistance The parameter value in the ray's explicit equation that represents the intersecting point on the target manipulator in world space. - /// @return Return true if OnRightMouseDownImpl was attached and will be used. + //! Callback for the event when the mouse pointer is over this manipulator and the right mouse button is pressed . + //! @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera + //! through the mouse pointer. + //! @param rayIntersectionDistance The parameter value in the ray's explicit equation that represents the intersecting point on the + //! target manipulator in world space. + //! @return Return true if OnRightMouseDownImpl was attached and will be used. bool OnRightMouseDown(const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance); - /// Callback for the event when this manipulator is active and the right mouse button is released. - /// @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera through the mouse pointer. + //! Callback for the event when this manipulator is active and the right mouse button is released. + //! @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera + //! through the mouse pointer. void OnRightMouseUp(const ViewportInteraction::MouseInteraction& interaction); - /// Callback for the event when this manipulator is active and the mouse is moved. - /// @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera through the mouse pointer. + //! Callback for the event when this manipulator is active and the mouse is moved. + //! @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera + //! through the mouse pointer. void OnMouseMove(const ViewportInteraction::MouseInteraction& interaction); - /// Callback for the event when this manipulator is active and the mouse wheel is scrolled. - /// @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera through the mouse pointer. + //! Callback for the event when this manipulator is active and the mouse wheel is scrolled. + //! @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera + //! through the mouse pointer. void OnMouseWheel(const ViewportInteraction::MouseInteraction& interaction); - /// This function changes the state indicating whether the manipulator is under the mouse pointer. - /// It is called in the event of OnMouseMove and OnMouseWheel only when there is no manipulator currently performing actions. + //! This function changes the state indicating whether the manipulator is under the mouse pointer. + //! It is called in the event of OnMouseMove and OnMouseWheel only when there is no manipulator currently performing actions. bool OnMouseOver(ManipulatorId manipulatorId, const ViewportInteraction::MouseInteraction& interaction); - /// Register itself to a manipulator manager so that it can receive various mouse events and perform manipulations. - /// @param managerId The id identifying a unique manipulator manager. + //! Register itself to a manipulator manager so that it can receive various mouse events and perform manipulations. + //! @param managerId The id identifying a unique manipulator manager. void Register(ManipulatorManagerId managerId); - /// Unregister itself from the manipulator manager it was registered with. + //! Unregister itself from the manipulator manager it was registered with. void Unregister(); - /// Bounds will need to be recalculated next time we render. + //! Bounds will need to be recalculated next time we render. void SetBoundsDirty(); - /// Is this manipulator currently registered with a manipulator manager. + //! Is this manipulator currently registered with a manipulator manager. bool Registered() const { - return m_manipulatorId != InvalidManipulatorId && - m_manipulatorManagerId != InvalidManipulatorManagerId; + return m_manipulatorId != InvalidManipulatorId && m_manipulatorManagerId != InvalidManipulatorManagerId; } - /// Is the manipulator in the middle of an action (between mouse down and mouse up). - bool PerformingAction() const { return m_performingAction; } + //! Is the manipulator in the middle of an action (between mouse down and mouse up). + bool PerformingAction() const + { + return m_performingAction; + } - /// Is the mouse currently over the manipulator (intersecting manipulator bound). - bool MouseOver() const { return m_mouseOver; } + //! Is the mouse currently over the manipulator (intersecting manipulator bound). + bool MouseOver() const + { + return m_mouseOver; + } - /// The unique id of this manipulator. - ManipulatorId GetManipulatorId() const { return m_manipulatorId; } + //! The unique id of this manipulator. + ManipulatorId GetManipulatorId() const + { + return m_manipulatorId; + } - /// The unique id of the manager this manipulator was registered with. - ManipulatorManagerId GetManipulatorManagerId() const { return m_manipulatorManagerId; } + //! The unique id of the manager this manipulator was registered with. + ManipulatorManagerId GetManipulatorManagerId() const + { + return m_manipulatorManagerId; + } - /// Returns all EntityComponentIdPairs associated with this manipulator. + //! Returns all EntityComponentIdPairs associated with this manipulator. const EntityComponentIds& EntityComponentIdPairs() const { return m_entityComponentIdPairs; } - /// Add an entity and component the manipulator is responsible for. + //! Add an entity and component the manipulator is responsible for. void AddEntityComponentIdPair(const AZ::EntityComponentIdPair& entityComponentIdPair); - /// Remove an entity from being affected by this manipulator. - /// @note All components on this entity registered with the manipulator will be removed. + //! Remove an entity from being affected by this manipulator. + //! @note All components on this entity registered with the manipulator will be removed. EntityComponentIds::iterator RemoveEntityId(AZ::EntityId entityId); - /// Remove a specific component (via a EntityComponentIdPair) being affected by this manipulator. + //! Remove a specific component (via a EntityComponentIdPair) being affected by this manipulator. EntityComponentIds::iterator RemoveEntityComponentIdPair(const AZ::EntityComponentIdPair& entityComponentIdPair); - /// Is this entity currently being tracked by this manipulator. + //! Is this entity currently being tracked by this manipulator. bool HasEntityId(AZ::EntityId entityId) const; - /// Is this entity component pair currently being tracked by this manipulator. + //! Is this entity component pair currently being tracked by this manipulator. bool HasEntityComponentIdPair(const AZ::EntityComponentIdPair& entityComponentIdPair) const; - /// Forward a mouse over event in a case where we need the manipulator to immediately refresh. - /// @note Only call this when a mouse over event has just happened. + //! Forward a mouse over event in a case where we need the manipulator to immediately refresh. + //! @note Only call this when a mouse over event has just happened. void ForwardMouseOverEvent(const ViewportInteraction::MouseInteraction& interaction); static const AZ::Color s_defaultMouseOverColor; protected: - /// Protected constructor. + //! Protected constructor. BaseManipulator() = default; - /// Called when unregistering - users of manipulators should not call it directly. + //! Called when unregistering - users of manipulators should not call it directly. void Invalidate(); - /// The implementation to override in a derived class for Invalidate. - virtual void InvalidateImpl() {} + //! The implementation to override in a derived class for Invalidate. + virtual void InvalidateImpl() + { + } - /// The implementation to override in a derived class for OnLeftMouseDown. - /// Note: When implementing this function you must also call AttachLeftMouseDownImpl to ensure - /// m_onLeftMouseDownImpl is set to OnLeftMouseDownImpl, otherwise it will not be called - virtual void OnLeftMouseDownImpl( - const ViewportInteraction::MouseInteraction& /*interaction*/, float /*rayIntersectionDistance*/) {} - void AttachLeftMouseDownImpl() { m_onLeftMouseDownImpl = &BaseManipulator::OnLeftMouseDownImpl; } + //! The implementation to override in a derived class for OnLeftMouseDown. + //! Note: When implementing this function you must also call AttachLeftMouseDownImpl to ensure + //! m_onLeftMouseDownImpl is set to OnLeftMouseDownImpl, otherwise it will not be called + virtual void OnLeftMouseDownImpl(const ViewportInteraction::MouseInteraction& /*interaction*/, float /*rayIntersectionDistance*/) + { + } - /// The implementation to override in a derived class for OnRightMouseDown. - /// Note: When implementing this function you must also call AttachRightMouseDownImpl to ensure - /// m_onRightMouseDownImpl is set to OnRightMouseDownImpl, otherwise it will not be called - virtual void OnRightMouseDownImpl( - const ViewportInteraction::MouseInteraction& /*interaction*/, float /*rayIntersectionDistance*/) {} - void AttachRightMouseDownImpl() { m_onRightMouseDownImpl = &BaseManipulator::OnRightMouseDownImpl; } + void AttachLeftMouseDownImpl() + { + m_onLeftMouseDownImpl = &BaseManipulator::OnLeftMouseDownImpl; + } - /// The implementation to override in a derived class for OnLeftMouseUp. - virtual void OnLeftMouseUpImpl(const ViewportInteraction::MouseInteraction& /*interaction*/) {} + //! The implementation to override in a derived class for OnRightMouseDown. + //! Note: When implementing this function you must also call AttachRightMouseDownImpl to ensure + //! m_onRightMouseDownImpl is set to OnRightMouseDownImpl, otherwise it will not be called + virtual void OnRightMouseDownImpl(const ViewportInteraction::MouseInteraction& /*interaction*/, float /*rayIntersectionDistance*/) + { + } - /// The implementation to override in a derived class for OnRightMouseUp. - virtual void OnRightMouseUpImpl(const ViewportInteraction::MouseInteraction& /*interaction*/) {} + void AttachRightMouseDownImpl() + { + m_onRightMouseDownImpl = &BaseManipulator::OnRightMouseDownImpl; + } - /// The implementation to override in a derived class for OnMouseMove. - virtual void OnMouseMoveImpl(const ViewportInteraction::MouseInteraction& /*interaction*/) {} + //! The implementation to override in a derived class for OnLeftMouseUp. + virtual void OnLeftMouseUpImpl(const ViewportInteraction::MouseInteraction& /*interaction*/) + { + } - /// The implementation to override in a derived class for OnMouseOver. - virtual void OnMouseOverImpl( - ManipulatorId /*manipulatorId*/, const ViewportInteraction::MouseInteraction& /*interaction*/) {} + //! The implementation to override in a derived class for OnRightMouseUp. + virtual void OnRightMouseUpImpl(const ViewportInteraction::MouseInteraction& /*interaction*/) + { + } - /// The implementation to override in a derived class for OnMouseWheel. - virtual void OnMouseWheelImpl(const ViewportInteraction::MouseInteraction& /*interaction*/) {} + //! The implementation to override in a derived class for OnMouseMove. + virtual void OnMouseMoveImpl(const ViewportInteraction::MouseInteraction& /*interaction*/) + { + } - /// The implementation to override in a derived class for SetBoundsDirty. - virtual void SetBoundsDirtyImpl() {} + //! The implementation to override in a derived class for OnMouseOver. + virtual void OnMouseOverImpl(ManipulatorId /*manipulatorId*/, const ViewportInteraction::MouseInteraction& /*interaction*/) + { + } - /// Rendering for the manipulator - it is recommended drawing be delegated to a ManipulatorView. + //! The implementation to override in a derived class for OnMouseWheel. + virtual void OnMouseWheelImpl(const ViewportInteraction::MouseInteraction& /*interaction*/) + { + } + + //! The implementation to override in a derived class for SetBoundsDirty. + virtual void SetBoundsDirtyImpl() + { + } + + //! Rendering for the manipulator - it is recommended drawing be delegated to a ManipulatorView. virtual void Draw( const ManipulatorManagerState& managerState, AzFramework::DebugDisplayRequests& debugDisplay, @@ -202,39 +243,39 @@ namespace AzToolsFramework private: friend class ManipulatorManager; - AZStd::unordered_set m_entityComponentIdPairs; ///< The entities this manipulator is associated with. + AZStd::unordered_set m_entityComponentIdPairs; //!< The entities this manipulator is associated with. - ManipulatorId m_manipulatorId = InvalidManipulatorId; ///< The unique id of this manipulator. - ManipulatorManagerId m_manipulatorManagerId = InvalidManipulatorManagerId; ///< The manager this manipulator was registered with. - UndoSystem::URSequencePoint* m_undoBatch = nullptr; ///< Undo active while mouse is pressed. - bool m_performingAction = false; ///< After mouse down and before mouse up. - bool m_mouseOver = false; ///< Is the mouse pointer over the manipulator bound. + ManipulatorId m_manipulatorId = InvalidManipulatorId; //!< The unique id of this manipulator. + ManipulatorManagerId m_manipulatorManagerId = InvalidManipulatorManagerId; //!< The manager this manipulator was registered with. + UndoSystem::URSequencePoint* m_undoBatch = nullptr; //!< Undo active while mouse is pressed. + bool m_performingAction = false; //!< After mouse down and before mouse up. + bool m_mouseOver = false; //!< Is the mouse pointer over the manipulator bound. - /// Member function pointers to OnLeftMouseDownImpl and OnRightMouseDownImpl. - /// Set in AttachLeft/RightMouseDownImpl. + //! Member function pointers to OnLeftMouseDownImpl and OnRightMouseDownImpl. + //! Set in AttachLeft/RightMouseDownImpl. void (BaseManipulator::*m_onLeftMouseDownImpl)( const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance) = nullptr; void (BaseManipulator::*m_onRightMouseDownImpl)( const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance) = nullptr; - /// Update the mouseOver state for this manipulator. + //! Update the mouseOver state for this manipulator. void UpdateMouseOver(ManipulatorId manipulatorId); - /// Manage correctly ending the undo batch. + //! Manage correctly ending the undo batch. void EndUndoBatch(); - /// Record an action as having started. + //! Record an action as having started. void BeginAction(); - /// Record an action as having stopped. + //! Record an action as having stopped. void EndAction(); - /// Let other systems (UI) know that a component property has been modified by a manipulator. + //! Let other systems (UI) know that a component property has been modified by a manipulator. void NotifyEntityComponentPropertyChanged(); }; - /// Base class to be used when composing aggregate manipulator types - wraps some - /// common functionality all manipulators need. + //! Base class to be used when composing aggregate manipulator types - wraps some + //! common functionality all manipulators need. class Manipulators { public: @@ -249,8 +290,10 @@ namespace AzToolsFramework bool PerformingAction(); bool Registered(); - /// Refresh the Manipulator and/or View based on the current view position. - virtual void RefreshView(const AZ::Vector3& /*worldViewPosition*/) {} + //! Refresh the Manipulator and/or View based on the current view position. + virtual void RefreshView(const AZ::Vector3& /*worldViewPosition*/) + { + } const AZ::Transform& GetLocalTransform() const; const AZ::Transform& GetSpace() const; @@ -262,39 +305,59 @@ namespace AzToolsFramework void SetNonUniformScale(const AZ::Vector3& nonUniformScale); protected: - /// Common processing for base manipulator type - Implement for all - /// individual manipulators used in an aggregate manipulator. + //! Common processing for base manipulator type - Implement for all + //! individual manipulators used in an aggregate manipulator. virtual void ProcessManipulators(const AZStd::function&) = 0; - ///@{ - /// Allows implementers to perform additional logic when updating the location of the manipulator group. - virtual void SetSpaceImpl([[maybe_unused]] const AZ::Transform& worldFromLocal) {} - virtual void SetLocalTransformImpl([[maybe_unused]] const AZ::Transform& localTransform) {} - virtual void SetLocalPositionImpl([[maybe_unused]] const AZ::Vector3& localPosition) {} - virtual void SetLocalOrientationImpl([[maybe_unused]] const AZ::Quaternion& localOrientation) {} - virtual void SetNonUniformScaleImpl([[maybe_unused]] const AZ::Vector3& nonUniformScale) {} - ///@} + //!@{ + //! Allows implementers to perform additional logic when updating the location of the manipulator group. + virtual void SetSpaceImpl([[maybe_unused]] const AZ::Transform& worldFromLocal) + { + } - ManipulatorSpaceWithLocalTransform m_manipulatorSpaceWithLocalTransform; ///< The space and local transform for the manipulators. + virtual void SetLocalTransformImpl([[maybe_unused]] const AZ::Transform& localTransform) + { + } + + virtual void SetLocalPositionImpl([[maybe_unused]] const AZ::Vector3& localPosition) + { + } + + virtual void SetLocalOrientationImpl([[maybe_unused]] const AZ::Quaternion& localOrientation) + { + } + + virtual void SetNonUniformScaleImpl([[maybe_unused]] const AZ::Vector3& nonUniformScale) + { + } + //!@} + + ManipulatorSpaceWithLocalTransform m_manipulatorSpaceWithLocalTransform; //!< The space and local transform for the manipulators. }; namespace Internal { - /// This helper function calculates the intersecting point between a ray and a plane. - /// @param rayOrigin The origin of the ray to test. - /// @param rayDirection The direction of the ray to test. - /// @param maxRayLength - /// @param pointOnPlane A point on the plane. - /// @param planeNormal The normal vector of the plane. - /// @param[out] resultIntersectingPoint This stores the result intersecting point. It will be left unchanged - /// if there is no intersection between the ray and the plane. - /// @return Was there an intersection - bool CalculateRayPlaneIntersectingPoint(const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDirection, - const AZ::Vector3& pointOnPlane, const AZ::Vector3& planeNormal, AZ::Vector3& resultIntersectingPoint); + //! This helper function calculates the intersecting point between a ray and a plane. + //! @param rayOrigin The origin of the ray to test. + //! @param rayDirection The direction of the ray to test. + //! @param maxRayLength The maximum length of the ray to test. + //! @param pointOnPlane A point on the plane. + //! @param planeNormal The normal vector of the plane. + //! @param[out] resultIntersectingPoint This stores the result intersecting point. It will be left unchanged + //! if there is no intersection between the ray and the plane. + //! @return Was there an intersection + bool CalculateRayPlaneIntersectingPoint( + const AZ::Vector3& rayOrigin, + const AZ::Vector3& rayDirection, + const AZ::Vector3& pointOnPlane, + const AZ::Vector3& planeNormal, + AZ::Vector3& resultIntersectingPoint); - /// Returns startLocalHitPosition if currentLocalHitPosition is further away than the camera's far clip plane. + //! Returns startLocalHitPosition if currentLocalHitPosition is further away than the camera's far clip plane. AZ::Vector3 TryConstrainHitPositionToView( - const AZ::Vector3& currentLocalHitPosition, const AZ::Vector3& startLocalHitPosition, - const AZ::Transform& localFromWorld, const AzFramework::CameraState& cameraState); - } + const AZ::Vector3& currentLocalHitPosition, + const AZ::Vector3& startLocalHitPosition, + const AZ::Transform& localFromWorld, + const AzFramework::CameraState& cameraState); + } // namespace Internal } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BoxManipulatorRequestBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BoxManipulatorRequestBus.h index 05da73fa20..69920f5f52 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BoxManipulatorRequestBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BoxManipulatorRequestBus.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -21,31 +21,30 @@ namespace AZ namespace AzToolsFramework { - /// Interface for handling box manipulator requests. - /// Used by \ref BoxComponentMode. - class BoxManipulatorRequests - : public AZ::EntityComponentBus + //! Interface for handling box manipulator requests. + //! Used by \ref BoxComponentMode. + class BoxManipulatorRequests : public AZ::EntityComponentBus { public: - /// Get the X/Y/Z dimensions of the box shape/collider. + //! Get the X/Y/Z dimensions of the box shape/collider. virtual AZ::Vector3 GetDimensions() = 0; - /// Set the X/Y/Z dimensions of the box shape/collider. + //! Set the X/Y/Z dimensions of the box shape/collider. virtual void SetDimensions(const AZ::Vector3& dimensions) = 0; - /// Get the transform of the box shape/collider. - /// This is used by \ref BoxComponentMode instead of the \ref \AZ::TransformBus - /// because a collider may have an additional translation/orientation offset from - /// the Entity transform. + //! Get the transform of the box shape/collider. + //! This is used by \ref BoxComponentMode instead of the \ref \AZ::TransformBus + //! because a collider may have an additional translation/orientation offset from + //! the Entity transform. virtual AZ::Transform GetCurrentTransform() = 0; - /// Get the scale currently applied to the box. - /// With the Box Shape, the largest x/y/z component is taken - /// so scale is always uniform, with colliders the scale may - /// be different per component. + //! Get the scale currently applied to the box. + //! With the Box Shape, the largest x/y/z component is taken + //! so scale is always uniform, with colliders the scale may + //! be different per component. virtual AZ::Vector3 GetBoxScale() = 0; protected: ~BoxManipulatorRequests() = default; }; - /// Type to inherit to implement BoxManipulatorRequests + //! Type to inherit to implement BoxManipulatorRequests using BoxManipulatorRequestBus = AZ::EBus; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/EditorVertexSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/EditorVertexSelection.cpp index 55a8464ba6..c8be488c91 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/EditorVertexSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/EditorVertexSelection.cpp @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "EditorVertexSelection.h" @@ -16,10 +16,10 @@ #include #include #include -#include #include -#include #include +#include +#include #include #include #include @@ -37,7 +37,7 @@ using Vertex3LookupReverseIter = namespace std { - template <> + template<> struct iterator_traits { using difference_type = typename Vertex2LookupReverseIter::difference_type; @@ -47,7 +47,7 @@ namespace std using reference = typename Vertex2LookupReverseIter::reference; }; - template <> + template<> struct iterator_traits { using difference_type = typename Vertex3LookupReverseIter::difference_type; @@ -56,7 +56,7 @@ namespace std using pointer = typename Vertex3LookupReverseIter::pointer; using reference = typename Vertex3LookupReverseIter::reference; }; -} +} // namespace std namespace AzToolsFramework { @@ -73,14 +73,11 @@ namespace AzToolsFramework OnEntityComponentPropertyChanged(entityComponentIdPair); // ensure property grid values are refreshed - ToolsApplicationNotificationBus::Broadcast( - &ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, - Refresh_EntireTree); + ToolsApplicationNotificationBus::Broadcast(&ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, Refresh_EntireTree); } template - bool EditorVertexSelectionBase::HandleMouse( - const ViewportInteraction::MouseInteractionEvent& mouseInteraction) + bool EditorVertexSelectionBase::HandleMouse(const ViewportInteraction::MouseInteractionEvent& mouseInteraction) { m_editorBoxSelect.HandleMouseInteraction(mouseInteraction); @@ -115,18 +112,17 @@ namespace AzToolsFramework } template - void EditorVertexSelectionBase::SnapVerticesToTerrain( - const ViewportInteraction::MouseInteractionEvent& mouseInteraction) + void EditorVertexSelectionBase::SnapVerticesToTerrain(const ViewportInteraction::MouseInteractionEvent& mouseInteraction) { ScopedUndoBatch surfaceSnapUndo("Snap to Surface"); ScopedUndoBatch::MarkEntityDirty(GetEntityId()); const int viewportId = mouseInteraction.m_mouseInteraction.m_interactionId.m_viewportId; // get unsnapped terrain position (world space) - AZ::Vector3 worldSurfacePosition = AZ::Vector3::CreateZero();; + AZ::Vector3 worldSurfacePosition = AZ::Vector3::CreateZero(); + ; ViewportInteraction::MainEditorViewportInteractionRequestBus::EventResult( - worldSurfacePosition, viewportId, - &ViewportInteraction::MainEditorViewportInteractionRequestBus::Events::PickTerrain, + worldSurfacePosition, viewportId, &ViewportInteraction::MainEditorViewportInteractionRequestBus::Events::PickTerrain, mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates); AZ::Transform worldFromLocal; @@ -136,8 +132,7 @@ namespace AzToolsFramework // convert to local space - snap if enabled const GridSnapParameters gridSnapParams = GridSnapSettings(viewportId); const AZ::Vector3 localFinalSurfacePosition = gridSnapParams.m_gridSnap - ? CalculateSnappedTerrainPosition( - worldSurfacePosition, worldFromLocal, viewportId, gridSnapParams.m_gridSize) + ? CalculateSnappedTerrainPosition(worldSurfacePosition, worldFromLocal, viewportId, gridSnapParams.m_gridSize) : localFromWorld.TransformPoint(worldSurfacePosition); SetSelectedPosition(localFinalSurfacePosition); @@ -145,17 +140,16 @@ namespace AzToolsFramework OnEntityComponentPropertyChanged(GetEntityComponentIdPair()); // ensure property grid values are refreshed - ToolsApplicationNotificationBus::Broadcast( - &ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, - Refresh_Values); + ToolsApplicationNotificationBus::Broadcast(&ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, Refresh_Values); } - /// Iterate over all vertices currently associated with the translation manipulator and update their - /// positions by taking their starting positions and modifying them by an offset. + // iterate over all vertices currently associated with the translation manipulator and update their + // positions by taking their starting positions and modifying them by an offset. template void EditorVertexSelectionBase::UpdateManipulatorsAndVerticesFromOffset( IndexedTranslationManipulator& translationManipulator, - const AZ::Vector3& localManipulatorStartPosition, const AZ::Vector3& localManipulatorOffset) + const AZ::Vector3& localManipulatorStartPosition, + const AZ::Vector3& localManipulatorOffset) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -164,22 +158,19 @@ namespace AzToolsFramework AZ::FixedVerticesRequestBus::Bind(fixedVertices, GetEntityId()); translationManipulator.Process( - [this, localManipulatorOffset, fixedVertices] - (typename IndexedTranslationManipulator::VertexLookup& vertex) - { - vertex.m_offset = AZ::AdaptVertexIn(localManipulatorOffset); + [this, localManipulatorOffset, fixedVertices](typename IndexedTranslationManipulator::VertexLookup& vertex) + { + vertex.m_offset = AZ::AdaptVertexIn(localManipulatorOffset); - bool updated = false; - const Vertex vertexPosition = vertex.m_start + vertex.m_offset; - AZ::FixedVerticesRequestBus::EventResult( - updated, fixedVertices, &AZ::FixedVerticesRequestBus::Handler::UpdateVertex, - vertex.m_index, vertexPosition); + bool updated = false; + const Vertex vertexPosition = vertex.m_start + vertex.m_offset; + AZ::FixedVerticesRequestBus::EventResult( + updated, fixedVertices, &AZ::FixedVerticesRequestBus::Handler::UpdateVertex, vertex.m_index, vertexPosition); - m_selectionManipulators[vertex.m_index]->SetLocalPosition(AZ::AdaptVertexOut(vertexPosition)); - }); + m_selectionManipulators[vertex.m_index]->SetLocalPosition(AZ::AdaptVertexOut(vertexPosition)); + }); - m_translationManipulator->m_manipulator.SetLocalPosition( - localManipulatorStartPosition + localManipulatorOffset); + m_translationManipulator->m_manipulator.SetLocalPosition(localManipulatorStartPosition + localManipulatorOffset); // after vertex positions have changed, anything else which relies on their positions may update if (m_onVertexPositionsUpdated) @@ -188,11 +179,10 @@ namespace AzToolsFramework } } - /// In OnMouseDown for various manipulators (linear/planar/surface), ensure we record the vertex starting position - /// for each vertex associated with the translation manipulator to use with offset calculations when updating. + // in OnMouseDown for various manipulators (linear/planar/surface), ensure we record the vertex starting position + // for each vertex associated with the translation manipulator to use with offset calculations when updating. template - void InitializeVertexLookup( - IndexedTranslationManipulator& translationManipulator, const AZ::EntityId entityId) + void InitializeVertexLookup(IndexedTranslationManipulator& translationManipulator, const AZ::EntityId entityId) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -201,29 +191,28 @@ namespace AzToolsFramework AZ::FixedVerticesRequestBus::Bind(fixedVertices, entityId); translationManipulator.Process( - [fixedVertices] - (typename IndexedTranslationManipulator::VertexLookup& vertexLookup) - { - Vertex vertex; - bool found = false; - AZ::FixedVerticesRequestBus::EventResult( - found, fixedVertices, &AZ::FixedVerticesRequestBus::Handler::GetVertex, - vertexLookup.m_index, vertex); - - if (found) + [fixedVertices](typename IndexedTranslationManipulator::VertexLookup& vertexLookup) { - vertexLookup.m_start = vertex; - vertexLookup.m_offset = Vertex::CreateZero(); - } - }); + Vertex vertex; + bool found = false; + AZ::FixedVerticesRequestBus::EventResult( + found, fixedVertices, &AZ::FixedVerticesRequestBus::Handler::GetVertex, vertexLookup.m_index, vertex); + + if (found) + { + vertexLookup.m_start = vertex; + vertexLookup.m_offset = Vertex::CreateZero(); + } + }); } - /// Create a translation manipulator for a specific vertex and setup its corresponding callbacks etc. + // create a translation manipulator for a specific vertex and setup its corresponding callbacks etc. template void EditorVertexSelectionBase::CreateTranslationManipulator( const AZ::EntityComponentIdPair& entityComponentIdPair, const ManipulatorManagerId managerId, - const Vertex& vertex, size_t vertexIndex) + const Vertex& vertex, + size_t vertexIndex) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -250,65 +239,65 @@ namespace AzToolsFramework // linear manipulator callbacks m_translationManipulator->m_manipulator.InstallLinearManipulatorMouseDownCallback( [this]([[maybe_unused]] const LinearManipulator::Action& action) - { - BeginBatchMovement(); - InitializeVertexLookup(*m_translationManipulator, GetEntityId()); - }); + { + BeginBatchMovement(); + InitializeVertexLookup(*m_translationManipulator, GetEntityId()); + }); m_translationManipulator->m_manipulator.InstallLinearManipulatorMouseMoveCallback( [this](const LinearManipulator::Action& action) - { - UpdateManipulatorsAndVerticesFromOffset( - *m_translationManipulator, action.m_start.m_localPosition, action.LocalPositionOffset()); - }); + { + UpdateManipulatorsAndVerticesFromOffset( + *m_translationManipulator, action.m_start.m_localPosition, action.LocalPositionOffset()); + }); m_translationManipulator->m_manipulator.InstallLinearManipulatorMouseUpCallback( [this]([[maybe_unused]] const LinearManipulator::Action& action) - { - EndBatchMovement(); - }); + { + EndBatchMovement(); + }); // planar manipulator callbacks m_translationManipulator->m_manipulator.InstallPlanarManipulatorMouseDownCallback( [this]([[maybe_unused]] const PlanarManipulator::Action& action) - { - BeginBatchMovement(); - InitializeVertexLookup(*m_translationManipulator, GetEntityId()); - }); + { + BeginBatchMovement(); + InitializeVertexLookup(*m_translationManipulator, GetEntityId()); + }); m_translationManipulator->m_manipulator.InstallPlanarManipulatorMouseMoveCallback( [this](const PlanarManipulator::Action& action) - { - UpdateManipulatorsAndVerticesFromOffset( - *m_translationManipulator, action.m_start.m_localPosition, action.LocalPositionOffset()); - }); + { + UpdateManipulatorsAndVerticesFromOffset( + *m_translationManipulator, action.m_start.m_localPosition, action.LocalPositionOffset()); + }); m_translationManipulator->m_manipulator.InstallPlanarManipulatorMouseUpCallback( [this]([[maybe_unused]] const PlanarManipulator::Action& action) - { - EndBatchMovement(); - }); + { + EndBatchMovement(); + }); // surface manipulator callbacks m_translationManipulator->m_manipulator.InstallSurfaceManipulatorMouseDownCallback( [this]([[maybe_unused]] const SurfaceManipulator::Action& action) - { - BeginBatchMovement(); - InitializeVertexLookup(*m_translationManipulator, GetEntityId()); - }); + { + BeginBatchMovement(); + InitializeVertexLookup(*m_translationManipulator, GetEntityId()); + }); m_translationManipulator->m_manipulator.InstallSurfaceManipulatorMouseMoveCallback( [this](const SurfaceManipulator::Action& action) - { - UpdateManipulatorsAndVerticesFromOffset( - *m_translationManipulator, action.m_start.m_localPosition, action.LocalPositionOffset()); - }); + { + UpdateManipulatorsAndVerticesFromOffset( + *m_translationManipulator, action.m_start.m_localPosition, action.LocalPositionOffset()); + }); m_translationManipulator->m_manipulator.InstallSurfaceManipulatorMouseUpCallback( [this]([[maybe_unused]] const SurfaceManipulator::Action& action) - { - EndBatchMovement(); - }); + { + EndBatchMovement(); + }); // register the m_translation manipulator so it appears where the selection manipulator previously was m_translationManipulator->m_manipulator.Register(managerId); @@ -330,9 +319,9 @@ namespace AzToolsFramework AZStd::transform( vertexLookups.begin(), vertexLookups.end(), AZStd::back_inserter(vertexIndices), [](const typename IndexedTranslationManipulator::VertexLookup& vertexLookup) - { - return vertexLookup.m_index; - }); + { + return vertexLookup.m_index; + }); return vertexIndices; } @@ -348,12 +337,13 @@ namespace AzToolsFramework bool m_additive = true; // is the box select adding or removing things from the selection }; - template void DoBoxSelect( - const AZ::EntityId entityId, BoxSelectData& boxSelectData, + const AZ::EntityId entityId, + BoxSelectData& boxSelectData, const ViewportInteraction::KeyboardModifiers keyboardModifiers, - const int viewportId, const EditorBoxSelect& editorBoxSelect, + const int viewportId, + const EditorBoxSelect& editorBoxSelect, const AZStd::vector>& selectionManipulators) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -384,8 +374,7 @@ namespace AzToolsFramework if (editorBoxSelect.BoxRegion()) { AZ::Transform worldFromLocal; - AZ::TransformBus::EventResult( - worldFromLocal, entityId, &AZ::TransformBus::Events::GetWorldTM); + AZ::TransformBus::EventResult(worldFromLocal, entityId, &AZ::TransformBus::Events::GetWorldTM); // bind FixedVerticesRequestBus for improved performance typename AZ::FixedVerticesRequestBus::BusPtr fixedVertices; @@ -396,8 +385,7 @@ namespace AzToolsFramework Vertex localVertex; bool found = false; AZ::FixedVerticesRequestBus::EventResult( - found, fixedVertices, &AZ::FixedVerticesRequestBus::Handler::GetVertex, - vertexIndex, localVertex); + found, fixedVertices, &AZ::FixedVerticesRequestBus::Handler::GetVertex, vertexIndex, localVertex); const AZ::Vector3 worldVertex = worldFromLocal.TransformPoint(AZ::AdaptVertexOut(localVertex)); const AzFramework::ScreenPoint screenPosition = GetScreenPosition(viewportId, worldVertex); @@ -406,8 +394,8 @@ namespace AzToolsFramework if (editorBoxSelect.BoxRegion()->contains(ViewportInteraction::QPointFromScreenPoint(screenPosition))) { // see if vertexIndex is in active selection - auto vertexIt = AZStd::find( - boxSelectData.m_activeSelection.begin(), boxSelectData.m_activeSelection.end(), vertexIndex); + auto vertexIt = + AZStd::find(boxSelectData.m_activeSelection.begin(), boxSelectData.m_activeSelection.end(), vertexIndex); if (!keyboardModifiers.Ctrl()) { @@ -437,8 +425,8 @@ namespace AzToolsFramework else { // not in box region - see if vertexIndex is in delta selection - auto vertexItDelta = AZStd::find( - boxSelectData.m_deltaSelection.begin(), boxSelectData.m_deltaSelection.end(), vertexIndex); + auto vertexItDelta = + AZStd::find(boxSelectData.m_deltaSelection.begin(), boxSelectData.m_deltaSelection.end(), vertexIndex); // if we find the vertex in the delta selection if (vertexItDelta != boxSelectData.m_deltaSelection.end()) @@ -451,8 +439,8 @@ namespace AzToolsFramework boxSelectData.m_deltaSelection.erase(vertexItDelta); // remove the vertex from the active selection as well - auto vertexItStart = AZStd::find( - boxSelectData.m_activeSelection.begin(), boxSelectData.m_activeSelection.end(), vertexIndex); + auto vertexItStart = + AZStd::find(boxSelectData.m_activeSelection.begin(), boxSelectData.m_activeSelection.end(), vertexIndex); if (vertexItStart != boxSelectData.m_activeSelection.end()) { @@ -467,8 +455,8 @@ namespace AzToolsFramework boxSelectData.m_deltaSelection.erase(vertexItDelta); // also add it back to the active selection - auto vertexItStart = AZStd::find( - boxSelectData.m_activeSelection.begin(), boxSelectData.m_activeSelection.end(), vertexIndex); + auto vertexItStart = + AZStd::find(boxSelectData.m_activeSelection.begin(), boxSelectData.m_activeSelection.end(), vertexIndex); if (vertexItStart == boxSelectData.m_activeSelection.end()) { @@ -491,7 +479,8 @@ namespace AzToolsFramework template void EditorVertexSelectionBase::Create( - const AZ::EntityComponentIdPair& entityComponentIdPair, const ManipulatorManagerId managerId, + const AZ::EntityComponentIdPair& entityComponentIdPair, + const ManipulatorManagerId managerId, AZStd::unique_ptr hoverSelection, const TranslationManipulators::Dimensions dimensions, const TranslationManipulatorConfiguratorFn translationManipulatorConfigurator) @@ -509,8 +498,7 @@ namespace AzToolsFramework AZ::FixedVerticesRequestBus::Bind(fixedVertices, GetEntityId()); size_t vertexCount = 0; - AZ::FixedVerticesRequestBus::EventResult( - vertexCount, fixedVertices, &AZ::FixedVerticesRequestBus::Handler::Size); + AZ::FixedVerticesRequestBus::EventResult(vertexCount, fixedVertices, &AZ::FixedVerticesRequestBus::Handler::Size); m_selectionManipulators.reserve(vertexCount); // initialize manipulators for all spline vertices @@ -519,12 +507,10 @@ namespace AzToolsFramework Vertex vertex; bool found = false; AZ::FixedVerticesRequestBus::EventResult( - found, fixedVertices, &AZ::FixedVerticesRequestBus::Handler::GetVertex, - vertexIndex, vertex); + found, fixedVertices, &AZ::FixedVerticesRequestBus::Handler::GetVertex, vertexIndex, vertex); - m_selectionManipulators.push_back(SelectionManipulator::MakeShared( - WorldFromLocalWithUniformScale(GetEntityId()), - GetNonUniformScale(GetEntityId()))); + m_selectionManipulators.push_back( + SelectionManipulator::MakeShared(WorldFromLocalWithUniformScale(GetEntityId()), GetNonUniformScale(GetEntityId()))); const auto& selectionManipulator = m_selectionManipulators.back(); selectionManipulator->Register(managerId); @@ -539,156 +525,153 @@ namespace AzToolsFramework m_editorBoxSelect.InstallLeftMouseDown( [this, vertexBoxSelectData](const ViewportInteraction::MouseInteractionEvent& /*mouseInteraction*/) - { - // grab currently selected entities (the starting selection) - vertexBoxSelectData->m_startSelection = m_translationManipulator - ? MapFromLookupsToIndices(m_translationManipulator->m_vertices) - : AZStd::vector(); + { + // grab currently selected entities (the starting selection) + vertexBoxSelectData->m_startSelection = m_translationManipulator + ? MapFromLookupsToIndices(m_translationManipulator->m_vertices) + : AZStd::vector(); - // active selection is the same as start selection on mouse down - vertexBoxSelectData->m_activeSelection = vertexBoxSelectData->m_startSelection; + // active selection is the same as start selection on mouse down + vertexBoxSelectData->m_activeSelection = vertexBoxSelectData->m_startSelection; - size_t size = 0; - AZ::FixedVerticesRequestBus::EventResult( - size, GetEntityId(), &AZ::FixedVerticesRequestBus::Handler::Size); + size_t size = 0; + AZ::FixedVerticesRequestBus::EventResult(size, GetEntityId(), &AZ::FixedVerticesRequestBus::Handler::Size); - // populate vector of all indices in container to compare against - vertexBoxSelectData->m_all.resize(size); - std::iota(vertexBoxSelectData->m_all.begin(), vertexBoxSelectData->m_all.end(), static_cast(0)); - }); + // populate vector of all indices in container to compare against + vertexBoxSelectData->m_all.resize(size); + std::iota(vertexBoxSelectData->m_all.begin(), vertexBoxSelectData->m_all.end(), static_cast(0)); + }); m_editorBoxSelect.InstallMouseMove( [this, vertexBoxSelectData](const ViewportInteraction::MouseInteractionEvent& mouseInteraction) - { - DoBoxSelect( - GetEntityId(), *vertexBoxSelectData, mouseInteraction.m_mouseInteraction.m_keyboardModifiers, - mouseInteraction.m_mouseInteraction.m_interactionId.m_viewportId, - m_editorBoxSelect, m_selectionManipulators); - }); - - m_editorBoxSelect.InstallLeftMouseUp([this, vertexBoxSelectData]() - { - if (vertexBoxSelectData->m_additive) { - // bind FixedVerticesRequestBus for improved performance - typename AZ::FixedVerticesRequestBus::BusPtr fixedVertices; - AZ::FixedVerticesRequestBus::Bind(fixedVertices, GetEntityId()); + DoBoxSelect( + GetEntityId(), *vertexBoxSelectData, mouseInteraction.m_mouseInteraction.m_keyboardModifiers, + mouseInteraction.m_mouseInteraction.m_interactionId.m_viewportId, m_editorBoxSelect, m_selectionManipulators); + }); - const AZ::EntityComponentIdPair entityComponentIdPair = m_entityComponentIdPair; - for (size_t vertexIndex : vertexBoxSelectData->m_deltaSelection) - { - Vertex vertex; - bool found = false; - AZ::FixedVerticesRequestBus::EventResult( - found, fixedVertices, &AZ::FixedVerticesRequestBus::Handler::GetVertex, - vertexIndex, vertex); - - // if we already have a translation manipulator, add additional vertices to it - if (m_translationManipulator) - { - // otherwise add the new selected vertex - m_translationManipulator->m_vertices.push_back( - typename IndexedTranslationManipulator::VertexLookup{ vertex, Vertex::CreateZero(), vertexIndex }); - } - else - { - // create a new translation manipulator if one did not already exist with the first vertex - CreateTranslationManipulator(entityComponentIdPair, m_manipulatorManagerId, vertex, vertexIndex); - // default to ensuring selection manipulators are 'selected' - m_selectionManipulators[vertexIndex]->Select(); - } - } - } - else - { - // removing vertices with an active translation manipulator - if (m_translationManipulator) - { - // iterate through all delta vertices (ones that were either - // added or removed during selection) and remove them - for (size_t vertexIndex : vertexBoxSelectData->m_deltaSelection) - { - auto vertexIt = AZStd::find_if( - m_translationManipulator->m_vertices.begin(), m_translationManipulator->m_vertices.end(), - [vertexIndex](const auto& vertexLookup) - { - return vertexLookup.m_index == vertexIndex; - }); - - // remove vertex from translation manipulator - if (vertexIt != m_translationManipulator->m_vertices.end()) - { - m_translationManipulator->m_vertices.erase(vertexIt); - - // ensure it is registered to receive input and draw - if (!m_selectionManipulators[vertexIndex]->Registered()) - { - m_selectionManipulators[vertexIndex]->Register(m_manipulatorManagerId); - } - } - } - - // if we have no vertices left, clear selection (restore all selection - // manipulators and destroy translation manipulator) - if (m_translationManipulator->m_vertices.empty()) - { - ClearSelected(); - } - } - } - - // with a selection of more than one or zero, we want to ensure all selection - // manipulators are registered (can be clicked on) - if (vertexBoxSelectData->m_activeSelection.size() > 1) - { - for (size_t vertexIndex : vertexBoxSelectData->m_activeSelection) - { - if (!m_selectionManipulators[vertexIndex]->Registered()) - { - m_selectionManipulators[vertexIndex]->Register(m_manipulatorManagerId); - } - } - } - // special case handling for only one vertex - don't want to display it when - // translation manipulator will be in exactly the same location - else if (vertexBoxSelectData->m_activeSelection.size() == 1) + m_editorBoxSelect.InstallLeftMouseUp( + [this, vertexBoxSelectData]() { if (vertexBoxSelectData->m_additive) { - m_selectionManipulators[vertexBoxSelectData->m_activeSelection[0]]->Unregister(); + // bind FixedVerticesRequestBus for improved performance + typename AZ::FixedVerticesRequestBus::BusPtr fixedVertices; + AZ::FixedVerticesRequestBus::Bind(fixedVertices, GetEntityId()); + + const AZ::EntityComponentIdPair entityComponentIdPair = m_entityComponentIdPair; + for (size_t vertexIndex : vertexBoxSelectData->m_deltaSelection) + { + Vertex vertex; + bool found = false; + AZ::FixedVerticesRequestBus::EventResult( + found, fixedVertices, &AZ::FixedVerticesRequestBus::Handler::GetVertex, vertexIndex, vertex); + + // if we already have a translation manipulator, add additional vertices to it + if (m_translationManipulator) + { + // otherwise add the new selected vertex + m_translationManipulator->m_vertices.push_back( + typename IndexedTranslationManipulator::VertexLookup{ vertex, Vertex::CreateZero(), vertexIndex }); + } + else + { + // create a new translation manipulator if one did not already exist with the first vertex + CreateTranslationManipulator(entityComponentIdPair, m_manipulatorManagerId, vertex, vertexIndex); + // default to ensuring selection manipulators are 'selected' + m_selectionManipulators[vertexIndex]->Select(); + } + } } else { - m_selectionManipulators[vertexBoxSelectData->m_activeSelection[0]]->Register(m_manipulatorManagerId); + // removing vertices with an active translation manipulator + if (m_translationManipulator) + { + // iterate through all delta vertices (ones that were either + // added or removed during selection) and remove them + for (size_t vertexIndex : vertexBoxSelectData->m_deltaSelection) + { + auto vertexIt = AZStd::find_if( + m_translationManipulator->m_vertices.begin(), m_translationManipulator->m_vertices.end(), + [vertexIndex](const auto& vertexLookup) + { + return vertexLookup.m_index == vertexIndex; + }); + + // remove vertex from translation manipulator + if (vertexIt != m_translationManipulator->m_vertices.end()) + { + m_translationManipulator->m_vertices.erase(vertexIt); + + // ensure it is registered to receive input and draw + if (!m_selectionManipulators[vertexIndex]->Registered()) + { + m_selectionManipulators[vertexIndex]->Register(m_manipulatorManagerId); + } + } + } + + // if we have no vertices left, clear selection (restore all selection + // manipulators and destroy translation manipulator) + if (m_translationManipulator->m_vertices.empty()) + { + ClearSelected(); + } + } } - } - // update manipulator positions (ensure translation manipulator is - // centered on current selection) - RefreshTranslationManipulator(); + // with a selection of more than one or zero, we want to ensure all selection + // manipulators are registered (can be clicked on) + if (vertexBoxSelectData->m_activeSelection.size() > 1) + { + for (size_t vertexIndex : vertexBoxSelectData->m_activeSelection) + { + if (!m_selectionManipulators[vertexIndex]->Registered()) + { + m_selectionManipulators[vertexIndex]->Register(m_manipulatorManagerId); + } + } + } + // special case handling for only one vertex - don't want to display it when + // translation manipulator will be in exactly the same location + else if (vertexBoxSelectData->m_activeSelection.size() == 1) + { + if (vertexBoxSelectData->m_additive) + { + m_selectionManipulators[vertexBoxSelectData->m_activeSelection[0]]->Unregister(); + } + else + { + m_selectionManipulators[vertexBoxSelectData->m_activeSelection[0]]->Register(m_manipulatorManagerId); + } + } - // restore state once box select has completed - vertexBoxSelectData->m_startSelection.clear(); - vertexBoxSelectData->m_deltaSelection.clear(); - vertexBoxSelectData->m_activeSelection.clear(); - vertexBoxSelectData->m_all.clear(); - }); + // update manipulator positions (ensure translation manipulator is + // centered on current selection) + RefreshTranslationManipulator(); - m_editorBoxSelect.InstallDisplayScene( - [this, vertexBoxSelectData] - (const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& /*debugDisplay*/) - { - const auto keyboardModifiers = ViewportInteraction::KeyboardModifiers( - ViewportInteraction::TranslateKeyboardModifiers(QApplication::queryKeyboardModifiers())); + // restore state once box select has completed + vertexBoxSelectData->m_startSelection.clear(); + vertexBoxSelectData->m_deltaSelection.clear(); + vertexBoxSelectData->m_activeSelection.clear(); + vertexBoxSelectData->m_all.clear(); + }); - // when modifiers change ensure we refresh box selection for immediate update - if (keyboardModifiers != m_editorBoxSelect.PreviousModifiers()) - { - DoBoxSelect( - GetEntityId(), *vertexBoxSelectData, keyboardModifiers, - viewportInfo.m_viewportId, m_editorBoxSelect, m_selectionManipulators); - } - }); + m_editorBoxSelect.InstallDisplayScene( + [this, vertexBoxSelectData](const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& /*debugDisplay*/) + { + const auto keyboardModifiers = ViewportInteraction::KeyboardModifiers( + ViewportInteraction::TranslateKeyboardModifiers(QApplication::queryKeyboardModifiers())); + + // when modifiers change ensure we refresh box selection for immediate update + if (keyboardModifiers != m_editorBoxSelect.PreviousModifiers()) + { + DoBoxSelect( + GetEntityId(), *vertexBoxSelectData, keyboardModifiers, viewportInfo.m_viewportId, m_editorBoxSelect, + m_selectionManipulators); + } + }); AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(GetEntityId()); AzFramework::ViewportDebugDisplayEventBus::Handler::BusConnect(GetEntityContextId()); @@ -734,12 +717,12 @@ namespace AzToolsFramework { // re-enable all selection manipulators associated with the translation // manipulator which is now being removed. - m_translationManipulator->Process([this]( - typename IndexedTranslationManipulator::VertexLookup& vertex) - { - m_selectionManipulators[vertex.m_index]->Register(m_manipulatorManagerId); - m_selectionManipulators[vertex.m_index]->Deselect(); - }); + m_translationManipulator->Process( + [this](typename IndexedTranslationManipulator::VertexLookup& vertex) + { + m_selectionManipulators[vertex.m_index]->Register(m_manipulatorManagerId); + m_selectionManipulators[vertex.m_index]->Deselect(); + }); m_translationManipulator->m_manipulator.Unregister(); m_translationManipulator.reset(); @@ -755,8 +738,7 @@ namespace AzToolsFramework template void EditorVertexSelectionBase::DisplayEntityViewport( - const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) + const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -767,8 +749,7 @@ namespace AzToolsFramework template void EditorVertexSelectionBase::DisplayViewport2d( - const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) + const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -777,8 +758,7 @@ namespace AzToolsFramework template template::value>::type*> - void EditorVertexSelectionBase::UpdateManipulatorSpace( - const AzFramework::ViewportInfo& viewportInfo) + void EditorVertexSelectionBase::UpdateManipulatorSpace(const AzFramework::ViewportInfo& viewportInfo) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -789,23 +769,19 @@ namespace AzToolsFramework &ViewportInteraction::MainEditorViewportInteractionRequestBus::Events::ShowingWorldSpace); // update the manipulator to be in the correct space if it changed - if ( m_translationManipulator - && !m_translationManipulator->m_manipulator.PerformingAction() - && worldSpace != m_worldSpace) + if (m_translationManipulator && !m_translationManipulator->m_manipulator.PerformingAction() && worldSpace != m_worldSpace) { const AZ::Transform worldFromLocal = WorldFromLocalWithUniformScale(GetEntityId()); - m_translationManipulator->m_manipulator.SetLocalOrientation(worldSpace - ? QuaternionFromTransformNoScaling(worldFromLocal).GetInverseFull() - : AZ::Quaternion::CreateIdentity()); + m_translationManipulator->m_manipulator.SetLocalOrientation( + worldSpace ? QuaternionFromTransformNoScaling(worldFromLocal).GetInverseFull() : AZ::Quaternion::CreateIdentity()); m_worldSpace = worldSpace; } } template template::value>::type*> - void EditorVertexSelectionBase::UpdateManipulatorSpace( - const AzFramework::ViewportInfo& /*viewportInfo*/) const + void EditorVertexSelectionBase::UpdateManipulatorSpace(const AzFramework::ViewportInfo& /*viewportInfo*/) const { } @@ -813,8 +789,7 @@ namespace AzToolsFramework static bool CanDeleteSelection(const AZ::EntityId entityId, const int64_t selectedCount) { size_t vertexCount = 0; - AZ::VariableVerticesRequestBus::EventResult( - vertexCount, entityId, &AZ::VariableVerticesRequestBus::Handler::Size); + AZ::VariableVerticesRequestBus::EventResult(vertexCount, entityId, &AZ::VariableVerticesRequestBus::Handler::Size); // prevent deleting all vertices const int64_t remaining = aznumeric_cast(vertexCount) - selectedCount; @@ -825,9 +800,8 @@ namespace AzToolsFramework void EditorVertexSelectionVariable::ShowVertexDeletionWarning() { QMessageBox::information( - AzToolsFramework::GetActiveWindow(), "Information", - "It is not possible to delete all vertices.", - QMessageBox::Ok, QMessageBox::NoButton); + AzToolsFramework::GetActiveWindow(), "Information", "It is not possible to delete all vertices.", QMessageBox::Ok, + QMessageBox::NoButton); } template @@ -856,19 +830,19 @@ namespace AzToolsFramework EditorVertexSelectionBase::m_translationManipulator; // ensure we remove vertices in reverse order - std::sort(translationManipulator->m_vertices.rbegin(), translationManipulator->m_vertices.rend(), + std::sort( + translationManipulator->m_vertices.rbegin(), translationManipulator->m_vertices.rend(), [](const typename IndexedTranslationManipulator::VertexLookup& lhs, - const typename IndexedTranslationManipulator::VertexLookup& rhs) - { - return lhs.m_index < rhs.m_index; - }); + const typename IndexedTranslationManipulator::VertexLookup& rhs) + { + return lhs.m_index < rhs.m_index; + }); - translationManipulator->Process([this]( - typename IndexedTranslationManipulator::VertexLookup& vertex) - { - SafeRemoveVertex( - EditorVertexSelectionBase::GetEntityComponentIdPair(), vertex.m_index); - }); + translationManipulator->Process( + [this](typename IndexedTranslationManipulator::VertexLookup& vertex) + { + SafeRemoveVertex(EditorVertexSelectionBase::GetEntityComponentIdPair(), vertex.m_index); + }); translationManipulator->m_manipulator.Unregister(); translationManipulator.reset(); @@ -876,8 +850,7 @@ namespace AzToolsFramework if (EditorVertexSelectionBase::m_hoverSelection) { - EditorVertexSelectionBase::m_hoverSelection->Register( - EditorVertexSelectionBase::GetManipulatorManagerId()); + EditorVertexSelectionBase::m_hoverSelection->Register(EditorVertexSelectionBase::GetManipulatorManagerId()); } EditorVertexSelectionBase::SetState(EditorVertexSelectionBase::State::Selecting); @@ -895,11 +868,9 @@ namespace AzToolsFramework InitializeVertexLookup(*m_translationManipulator, GetEntityId()); // note: AdaptVertexIn/Out is to ensure we clamp the vertex local Z position to 0 if // dealing with Vector2s when setting the position of the manipulator. - const AZ::Vector3 localOffset = - localPosition - m_translationManipulator->m_manipulator.GetLocalTransform().GetTranslation(); + const AZ::Vector3 localOffset = localPosition - m_translationManipulator->m_manipulator.GetLocalTransform().GetTranslation(); UpdateManipulatorsAndVerticesFromOffset( - *m_translationManipulator, - AZ::AdaptVertexOut(AZ::AdaptVertexIn(localPosition)), + *m_translationManipulator, AZ::AdaptVertexOut(AZ::AdaptVertexIn(localPosition)), AZ::AdaptVertexOut(AZ::AdaptVertexIn(localOffset))); RefreshTranslationManipulator(); @@ -928,23 +899,20 @@ namespace AzToolsFramework // calculate average position of selected vertices for translation manipulator MidpointCalculator midpointCalculator; m_translationManipulator->Process( - [this, &midpointCalculator, fixedVertices] - (typename IndexedTranslationManipulator::VertexLookup& vertex) - { - Vertex v; - bool found = false; - AZ::FixedVerticesRequestBus::EventResult( - found, fixedVertices, &AZ::FixedVerticesRequestBus::Handler::GetVertex, - vertex.m_index, v); - - if (found) + [this, &midpointCalculator, fixedVertices](typename IndexedTranslationManipulator::VertexLookup& vertex) { - midpointCalculator.AddPosition(AZ::AdaptVertexOut(v)); - } - }); + Vertex v; + bool found = false; + AZ::FixedVerticesRequestBus::EventResult( + found, fixedVertices, &AZ::FixedVerticesRequestBus::Handler::GetVertex, vertex.m_index, v); - m_translationManipulator->m_manipulator.SetLocalPosition( - AZ::AdaptVertexOut(midpointCalculator.CalculateMidpoint())); + if (found) + { + midpointCalculator.AddPosition(AZ::AdaptVertexOut(v)); + } + }); + + m_translationManipulator->m_manipulator.SetLocalPosition(AZ::AdaptVertexOut(midpointCalculator.CalculateMidpoint())); } } @@ -970,8 +938,7 @@ namespace AzToolsFramework Vertex vertex; bool found = false; AZ::FixedVerticesRequestBus::EventResult( - found, fixedVertices, &AZ::FixedVerticesRequestBus::Handler::GetVertex, - manipulatorIndex, vertex); + found, fixedVertices, &AZ::FixedVerticesRequestBus::Handler::GetVertex, manipulatorIndex, vertex); if (found) { @@ -1037,39 +1004,41 @@ namespace AzToolsFramework } } - /// Handle correctly selecting/deselecting vertices in a vertex selection. + // handle correctly selecting/deselecting vertices in a vertex selection. template void EditorVertexSelectionBase::SelectionManipulatorSelectCallback( - const size_t vertexIndex, const ViewportInteraction::MouseInteraction& interaction, - const AZ::EntityComponentIdPair& entityComponentIdPair, const ManipulatorManagerId managerId) + const size_t vertexIndex, + const ViewportInteraction::MouseInteraction& interaction, + const AZ::EntityComponentIdPair& entityComponentIdPair, + const ManipulatorManagerId managerId) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); Vertex vertex; bool found = false; AZ::FixedVerticesRequestBus::EventResult( - found, GetEntityId(), &AZ::FixedVerticesRequestBus::Handler::GetVertex, - vertexIndex, vertex); + found, GetEntityId(), &AZ::FixedVerticesRequestBus::Handler::GetVertex, vertexIndex, vertex); if (m_translationManipulator != nullptr && interaction.m_keyboardModifiers.Ctrl()) { // ensure all selection manipulators are enabled when selecting more than one (the first // will have been disabled when only selecting an individual vertex - m_translationManipulator->Process([this, managerId]( - typename IndexedTranslationManipulator::VertexLookup& vertexLookup) - { - m_selectionManipulators[vertexLookup.m_index]->Register(managerId); - }); + m_translationManipulator->Process( + [this, managerId](typename IndexedTranslationManipulator::VertexLookup& vertexLookup) + { + m_selectionManipulators[vertexLookup.m_index]->Register(managerId); + }); // if selection manipulator was selected, find it in the vector of vertices stored in // the translation manipulator and remove it if (m_selectionManipulators[vertexIndex]->Selected()) { - auto it = AZStd::find_if(m_translationManipulator->m_vertices.begin(), m_translationManipulator->m_vertices.end(), + auto it = AZStd::find_if( + m_translationManipulator->m_vertices.begin(), m_translationManipulator->m_vertices.end(), [vertexIndex](const typename IndexedTranslationManipulator::VertexLookup vertexLookup) - { - return vertexIndex == vertexLookup.m_index; - }); + { + return vertexIndex == vertexLookup.m_index; + }); if (it != m_translationManipulator->m_vertices.end()) { @@ -1099,28 +1068,27 @@ namespace AzToolsFramework { // if one does not already exist, or we're not holding shift, create a new translation // manipulator at this vertex - CreateTranslationManipulator( - entityComponentIdPair, managerId, vertex, vertexIndex); + CreateTranslationManipulator(entityComponentIdPair, managerId, vertex, vertexIndex); } } - /// Configure the selection manipulator for fixed editor selection - this configures the view and action - /// of interacting with the selection manipulator. Vertices can just be selected (create a translation - /// manipulator) but not added or removed. + // configure the selection manipulator for fixed editor selection - this configures the view and action + // of interacting with the selection manipulator. Vertices can just be selected (create a translation + // manipulator) but not added or removed. template void EditorVertexSelectionFixed::SetupSelectionManipulator( const AZStd::shared_ptr& selectionManipulator, const AZ::EntityComponentIdPair& entityComponentIdPair, - const ManipulatorManagerId managerId, const size_t vertexIndex) + const ManipulatorManagerId managerId, + const size_t vertexIndex) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); // setup selection manipulator - const AZStd::shared_ptr selectionView = - AzToolsFramework::CreateManipulatorViewSphere(AZ::Color(1.0f, 0.0f, 0.0f, 1.0f), - g_defaultManipulatorSphereRadius, [&selectionManipulator] - (const ViewportInteraction::MouseInteraction& /*mouseInteraction*/, - const bool mouseOver, const AZ::Color& defaultColor) + const AZStd::shared_ptr selectionView = AzToolsFramework::CreateManipulatorViewSphere( + AZ::Color(1.0f, 0.0f, 0.0f, 1.0f), g_defaultManipulatorSphereRadius, + [&selectionManipulator]( + const ViewportInteraction::MouseInteraction& /*mouseInteraction*/, const bool mouseOver, const AZ::Color& defaultColor) { if (selectionManipulator->Selected()) { @@ -1128,72 +1096,68 @@ namespace AzToolsFramework } const float opacity[2] = { 0.5f, 1.0f }; - return AZ::Color( - defaultColor.GetR(), defaultColor.GetG(), defaultColor.GetB(), opacity[mouseOver]); + return AZ::Color(defaultColor.GetR(), defaultColor.GetG(), defaultColor.GetB(), opacity[mouseOver]); }); - selectionManipulator->SetViews(ManipulatorViews{selectionView}); + selectionManipulator->SetViews(ManipulatorViews{ selectionView }); - selectionManipulator->InstallLeftMouseUpCallback([ - this, entityComponentIdPair, vertexIndex, managerId]( - const ViewportInteraction::MouseInteraction& interaction) - { - EditorVertexSelectionBase::SelectionManipulatorSelectCallback( - vertexIndex, interaction, entityComponentIdPair, managerId); - }); + selectionManipulator->InstallLeftMouseUpCallback( + [this, entityComponentIdPair, vertexIndex, managerId](const ViewportInteraction::MouseInteraction& interaction) + { + EditorVertexSelectionBase::SelectionManipulatorSelectCallback( + vertexIndex, interaction, entityComponentIdPair, managerId); + }); } - /// Configure the selection manipulator for variable editor selection - this configures the view and action - /// of interacting with the selection manipulator. In this case, hovering the mouse with a modifier key held - /// will indicate removal, and clicking with a modifier key will remove the vertex. + // configure the selection manipulator for variable editor selection - this configures the view and action + // of interacting with the selection manipulator. In this case, hovering the mouse with a modifier key held + // will indicate removal, and clicking with a modifier key will remove the vertex. template void EditorVertexSelectionVariable::SetupSelectionManipulator( const AZStd::shared_ptr& selectionManipulator, const AZ::EntityComponentIdPair& entityComponentIdPair, - const ManipulatorManagerId managerId, const size_t vertexIndex) + const ManipulatorManagerId managerId, + const size_t vertexIndex) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); // setup selection manipulator - const AZStd::shared_ptr manipulatorView = - AzToolsFramework::CreateManipulatorViewSphere(AZ::Color(1.0f, 0.0f, 0.0f, 1.0f), - g_defaultManipulatorSphereRadius, [&selectionManipulator] - (const ViewportInteraction::MouseInteraction& mouseInteraction, - const bool mouseOver, const AZ::Color& defaultColor) + const AZStd::shared_ptr manipulatorView = AzToolsFramework::CreateManipulatorViewSphere( + AZ::Color(1.0f, 0.0f, 0.0f, 1.0f), g_defaultManipulatorSphereRadius, + [&selectionManipulator]( + const ViewportInteraction::MouseInteraction& mouseInteraction, const bool mouseOver, const AZ::Color& defaultColor) + { + if (mouseInteraction.m_keyboardModifiers.Alt() && mouseOver) { - if (mouseInteraction.m_keyboardModifiers.Alt() && mouseOver) - { - // indicate removal of manipulator - return AZ::Color(0.5f, 0.5f, 0.5f, 0.5f); - } + // indicate removal of manipulator + return AZ::Color(0.5f, 0.5f, 0.5f, 0.5f); + } - // highlight or not if mouse is over - const float opacity[2] = { 0.5f, 1.0f }; - if (selectionManipulator->Selected()) - { - return AZ::Color(1.0f, 1.0f, 0.0f, opacity[mouseOver]); - } + // highlight or not if mouse is over + const float opacity[2] = { 0.5f, 1.0f }; + if (selectionManipulator->Selected()) + { + return AZ::Color(1.0f, 1.0f, 0.0f, opacity[mouseOver]); + } - return AZ::Color( - defaultColor.GetR(), defaultColor.GetG(), defaultColor.GetB(), opacity[mouseOver]); - }); + return AZ::Color(defaultColor.GetR(), defaultColor.GetG(), defaultColor.GetB(), opacity[mouseOver]); + }); - - selectionManipulator->SetViews(ManipulatorViews{manipulatorView}); + selectionManipulator->SetViews(ManipulatorViews{ manipulatorView }); selectionManipulator->InstallLeftMouseUpCallback( [this, entityComponentIdPair, vertexIndex, managerId](const ViewportInteraction::MouseInteraction& interaction) - { - if (interaction.m_keyboardModifiers.Alt()) { - SafeRemoveVertex(entityComponentIdPair, vertexIndex); - } - else - { - EditorVertexSelectionBase::SelectionManipulatorSelectCallback( - vertexIndex, interaction, entityComponentIdPair, managerId); - } - }); + if (interaction.m_keyboardModifiers.Alt()) + { + SafeRemoveVertex(entityComponentIdPair, vertexIndex); + } + else + { + EditorVertexSelectionBase::SelectionManipulatorSelectCallback( + vertexIndex, interaction, entityComponentIdPair, managerId); + } + }); } template @@ -1240,15 +1204,17 @@ namespace AzToolsFramework template void EditorVertexSelectionFixed::PrepareActions() { - ActionOverride backAction = CreateBackAction("Deselect Vertex", "Deselect current vertex selection", [this]() - { - EditorVertexSelectionBase::ClearSelected(); - }); + ActionOverride backAction = CreateBackAction( + "Deselect Vertex", "Deselect current vertex selection", + [this]() + { + EditorVertexSelectionBase::ClearSelected(); + }); backAction.SetEntityComponentIdPair(EditorVertexSelectionBase::GetEntityComponentIdPair()); - EditorVertexSelectionBase::m_actionOverrides[static_cast( - EditorVertexSelectionBase::State::Translating)] = AZStd::vector { backAction }; + EditorVertexSelectionBase::m_actionOverrides[static_cast(EditorVertexSelectionBase::State::Translating)] = + AZStd::vector{ backAction }; } template @@ -1261,12 +1227,13 @@ namespace AzToolsFramework MidpointCalculator midpointCalculator; // sort in descending order - std::sort(manipulators.rbegin(), manipulators.rend(), + std::sort( + manipulators.rbegin(), manipulators.rend(), [](const typename IndexedTranslationManipulator::VertexLookup& lhs, const typename IndexedTranslationManipulator::VertexLookup& rhs) - { - return lhs.m_index < rhs.m_index; - }); + { + return lhs.m_index < rhs.m_index; + }); // iterate over current selection for (size_t manipulatorIndex = 0; manipulatorIndex < manipulators.size(); ++manipulatorIndex) @@ -1313,8 +1280,7 @@ namespace AzToolsFramework // create translation manipulator for duplicated vertices at new position EditorVertexSelectionBase::CreateTranslationManipulator( - EditorVertexSelectionBase::GetEntityComponentIdPair(), - EditorVertexSelectionBase::GetManipulatorManagerId(), + EditorVertexSelectionBase::GetEntityComponentIdPair(), EditorVertexSelectionBase::GetManipulatorManagerId(), localCenterPosition, vertices[0].m_index); // clear all selection manipulators to default unselected state @@ -1337,47 +1303,46 @@ namespace AzToolsFramework template void EditorVertexSelectionVariable::PrepareActions() { - ActionOverride deleteAction = CreateDeleteAction(s_deleteVerticesTitle, s_duplicateVerticesDesc, [this]() - { - DestroySelected(); - }); + ActionOverride deleteAction = CreateDeleteAction( + s_deleteVerticesTitle, s_duplicateVerticesDesc, + [this]() + { + DestroySelected(); + }); const AZ::EntityComponentIdPair entityComponentIdPair( - EditorVertexSelectionBase::GetEntityId(), - EditorVertexSelectionBase::GetComponentId()); + EditorVertexSelectionBase::GetEntityId(), EditorVertexSelectionBase::GetComponentId()); // note: important to register which entity/component id pair this action is associated with deleteAction.SetEntityComponentIdPair(entityComponentIdPair); - ActionOverride deselectAction = CreateBackAction(s_deselectVerticesTitle, s_deselectVerticesDesc, [this]() - { - EditorVertexSelectionBase::ClearSelected(); - }); + ActionOverride deselectAction = CreateBackAction( + s_deselectVerticesTitle, s_deselectVerticesDesc, + [this]() + { + EditorVertexSelectionBase::ClearSelected(); + }); // note: important to register which entity/component id pair this action is associated with deselectAction.SetEntityComponentIdPair(entityComponentIdPair); EditorVertexSelectionBase::m_actionOverrides[static_cast(EditorVertexSelectionBase::State::Translating)] = - AZStd::vector - { - ActionOverride() - .SetUri(AzToolsFramework::s_duplicateAction) - .SetKeySequence(QKeySequence(Qt::CTRL + Qt::Key_D)) - .SetTitle(s_duplicateVerticesTitle) - .SetTip(s_duplicateVerticesDesc) - .SetCallback([this]() - { - DuplicateSelected(); - }) - .SetEntityComponentIdPair(entityComponentIdPair), - deleteAction, - deselectAction - }; + AZStd::vector{ ActionOverride() + .SetUri(AzToolsFramework::s_duplicateAction) + .SetKeySequence(QKeySequence(Qt::CTRL + Qt::Key_D)) + .SetTitle(s_duplicateVerticesTitle) + .SetTip(s_duplicateVerticesDesc) + .SetCallback( + [this]() + { + DuplicateSelected(); + }) + .SetEntityComponentIdPair(entityComponentIdPair), + deleteAction, deselectAction }; } template - void InsertVertexAfter( - const AZ::EntityComponentIdPair& entityComponentIdPair, const size_t vertexIndex, const Vertex& localPosition) + void InsertVertexAfter(const AZ::EntityComponentIdPair& entityComponentIdPair, const size_t vertexIndex, const Vertex& localPosition) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -1389,15 +1354,13 @@ namespace AzToolsFramework if (insertPosition >= size) { AZ::VariableVerticesRequestBus::Event( - entityComponentIdPair.GetEntityId(), &AZ::VariableVerticesRequestBus::Handler::AddVertex, - localPosition); + entityComponentIdPair.GetEntityId(), &AZ::VariableVerticesRequestBus::Handler::AddVertex, localPosition); } else { bool updated = false; AZ::VariableVerticesRequestBus::EventResult( - updated, entityComponentIdPair.GetEntityId(), - &AZ::VariableVerticesRequestBus::Handler::InsertVertex, + updated, entityComponentIdPair.GetEntityId(), &AZ::VariableVerticesRequestBus::Handler::InsertVertex, insertPosition, localPosition); } @@ -1409,21 +1372,16 @@ namespace AzToolsFramework { bool removed = false; AZ::VariableVerticesRequestBus::EventResult( - removed, entityComponentIdPair.GetEntityId(), - &AZ::VariableVerticesRequestBus::Handler::RemoveVertex, vertexIndex); + removed, entityComponentIdPair.GetEntityId(), &AZ::VariableVerticesRequestBus::Handler::RemoveVertex, vertexIndex); RefreshUiAfterAddRemove(entityComponentIdPair); } // explicit instantiations - template void InsertVertexAfter( - const AZ::EntityComponentIdPair& entityComponentIdPair, size_t, const AZ::Vector2&); - template void InsertVertexAfter( - const AZ::EntityComponentIdPair& entityComponentIdPair, size_t, const AZ::Vector3&); - template void SafeRemoveVertex( - const AZ::EntityComponentIdPair& entityComponentIdPair, size_t vertexIndex); - template void SafeRemoveVertex( - const AZ::EntityComponentIdPair& entityComponentIdPair, size_t vertexIndex); + template void InsertVertexAfter(const AZ::EntityComponentIdPair& entityComponentIdPair, size_t, const AZ::Vector2&); + template void InsertVertexAfter(const AZ::EntityComponentIdPair& entityComponentIdPair, size_t, const AZ::Vector3&); + template void SafeRemoveVertex(const AZ::EntityComponentIdPair& entityComponentIdPair, size_t vertexIndex); + template void SafeRemoveVertex(const AZ::EntityComponentIdPair& entityComponentIdPair, size_t vertexIndex); AZ_CLASS_ALLOCATOR_IMPL_TEMPLATE(EditorVertexSelectionFixed, AZ::SystemAllocator, 0) AZ_CLASS_ALLOCATOR_IMPL_TEMPLATE(EditorVertexSelectionFixed, AZ::SystemAllocator, 0) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/EditorVertexSelection.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/EditorVertexSelection.h index 4d1c3ce43f..0036ea42d4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/EditorVertexSelection.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/EditorVertexSelection.h @@ -1,22 +1,22 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once #include #include #include -#include #include +#include #include #include #include @@ -24,37 +24,74 @@ namespace AzToolsFramework { - /// Concrete implementation of AZ::VariableVertices backed by an AZ::VertexContainer. + //! Concrete implementation of AZ::VariableVertices backed by an AZ::VertexContainer. template - class VariableVerticesVertexContainer - : public AZ::VariableVertices + class VariableVerticesVertexContainer : public AZ::VariableVertices { public: explicit VariableVerticesVertexContainer(AZ::VertexContainer& vertexContainer) - : m_vertexContainer(vertexContainer) {} + : m_vertexContainer(vertexContainer) + { + } - bool GetVertex(size_t index, Vertex& vertex) const override { return m_vertexContainer.GetVertex(index, vertex); } - bool UpdateVertex(size_t index, const Vertex& vertex) override { return m_vertexContainer.UpdateVertex(index, vertex); }; - void AddVertex(const Vertex& vertex) override { m_vertexContainer.AddVertex(vertex); } - bool InsertVertex(size_t index, const Vertex& vertex) override { return m_vertexContainer.InsertVertex(index, vertex); } - bool RemoveVertex(size_t index) override { return m_vertexContainer.RemoveVertex(index); } - void SetVertices(const AZStd::vector& vertices) override { m_vertexContainer.SetVertices(vertices); }; - void ClearVertices() override { m_vertexContainer.Clear(); } - size_t Size() const override { return m_vertexContainer.Size(); } - bool Empty() const override { return m_vertexContainer.Empty(); } + bool GetVertex(size_t index, Vertex& vertex) const override + { + return m_vertexContainer.GetVertex(index, vertex); + } + + bool UpdateVertex(size_t index, const Vertex& vertex) override + { + return m_vertexContainer.UpdateVertex(index, vertex); + }; + + void AddVertex(const Vertex& vertex) override + { + m_vertexContainer.AddVertex(vertex); + } + + bool InsertVertex(size_t index, const Vertex& vertex) override + { + return m_vertexContainer.InsertVertex(index, vertex); + } + + bool RemoveVertex(size_t index) override + { + return m_vertexContainer.RemoveVertex(index); + } + + void SetVertices(const AZStd::vector& vertices) override + { + m_vertexContainer.SetVertices(vertices); + }; + + void ClearVertices() override + { + m_vertexContainer.Clear(); + } + + size_t Size() const override + { + return m_vertexContainer.Size(); + } + + bool Empty() const override + { + return m_vertexContainer.Empty(); + } private: AZ::VertexContainer& m_vertexContainer; }; - /// Concrete implementation of AZ::FixedVertices backed by an AZStd::array. + //! Concrete implementation of AZ::FixedVertices backed by an AZStd::array. template - class FixedVerticesArray - : public AZ::FixedVertices + class FixedVerticesArray : public AZ::FixedVertices { public: explicit FixedVerticesArray(AZStd::array& array) - : m_array(array) {} + : m_array(array) + { + } bool GetVertex(size_t index, Vertex& vertex) const override { @@ -72,22 +109,26 @@ namespace AzToolsFramework if (index < m_array.size()) { m_array[index] = vertex; - return true;; + return true; + ; } return false; } - size_t Size() const override { return m_array.size(); } + size_t Size() const override + { + return m_array.size(); + } private: AZStd::array& m_array; }; - /// EditorVertexSelection provides an interface for a collection of manipulators to expose - /// editing of vertices in a container/collection. EditorVertexSelection is templated on the - /// type of Vertex (Vector2/Vector3) stored in the container. - /// EditorVertexSelectionBase provides common behavior shared across Fixed and Variable selections. + //! EditorVertexSelection provides an interface for a collection of manipulators to expose + //! editing of vertices in a container/collection. EditorVertexSelection is templated on the + //! type of Vertex (Vector2/Vector3) stored in the container. + //! EditorVertexSelectionBase provides common behavior shared across Fixed and Variable selections. template class EditorVertexSelectionBase : private AzFramework::EntityDebugDisplayEventBus::Handler @@ -99,89 +140,110 @@ namespace AzToolsFramework EditorVertexSelectionBase& operator=(EditorVertexSelectionBase&&) = default; virtual ~EditorVertexSelectionBase() = default; - /// Setup and configure the EditorVertexSelection for operation. + //! Setup and configure the EditorVertexSelection for operation. void Create( - const AZ::EntityComponentIdPair& entityComponentIdPair, ManipulatorManagerId managerId, + const AZ::EntityComponentIdPair& entityComponentIdPair, + ManipulatorManagerId managerId, AZStd::unique_ptr hoverSelection, TranslationManipulators::Dimensions dimensions, TranslationManipulatorConfiguratorFn translationManipulatorConfigurator); - /// Create a translation manipulator for a given vertex. + //! Create a translation manipulator for a given vertex. void CreateTranslationManipulator( - const AZ::EntityComponentIdPair& entityComponentIdPair, - ManipulatorManagerId managerId, const Vertex& vertex, size_t index); + const AZ::EntityComponentIdPair& entityComponentIdPair, ManipulatorManagerId managerId, const Vertex& vertex, size_t index); - /// Destroy all manipulators associated with the vertex selection. + //! Destroy all manipulators associated with the vertex selection. void Destroy(); - /// Set custom callback for when vertex positions are updated. + //! Set custom callback for when vertex positions are updated. void SetVertexPositionsUpdatedCallback(const AZStd::function& callback); - /// Update manipulators based on local changes to vertex positions. + //! Update manipulators based on local changes to vertex positions. void RefreshLocal(); - /// Update the translation manipulator to be correctly positioned based - /// on the current selection (recenter it). + //! Update the translation manipulator to be correctly positioned based + //! on the current selection (recenter it). void RefreshTranslationManipulator(); - /// Update manipulators based on changes to the entity's transform and non-uniform scale. + //! Update manipulators based on changes to the entity's transform and non-uniform scale. void RefreshSpace(const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale = AZ::Vector3::CreateOne()); - /// Set bounds dirty (need recalculating) for all owned manipulators (selection, translation, hover). + //! Set bounds dirty (need recalculating) for all owned manipulators (selection, translation, hover). void SetBoundsDirty(); - /// How should the EditorVertexSelection respond to mouse input. - virtual bool HandleMouse( - const ViewportInteraction::MouseInteractionEvent& mouseInteraction); + //! How should the EditorVertexSelection respond to mouse input. + virtual bool HandleMouse(const ViewportInteraction::MouseInteractionEvent& mouseInteraction); - /// Snap the selected vertices to the terrain. - /// Note: With a multi-selection the manipulator will be translated to the picked - /// terrain position with all verts moved relative to it. - void SnapVerticesToTerrain( - const ViewportInteraction::MouseInteractionEvent& mouseInteraction); + //! Snap the selected vertices to the terrain. + //! Note: With a multi-selection the manipulator will be translated to the picked + //! terrain position with all vertices moved relative to it. + void SnapVerticesToTerrain(const ViewportInteraction::MouseInteractionEvent& mouseInteraction); - /// The Actions provided by the EditorVertexSelection while it is active. - /// e.g. Vertex deletion, duplication etc. + //! The Actions provided by the EditorVertexSelection while it is active. + //! e.g. Vertex deletion, duplication etc. AZStd::vector ActionOverrides() const; - /// Let the EditorVertexSelection know a batch movement is about to begin so it - /// can avoid certain unnecessary updates. + //! Let the EditorVertexSelection know a batch movement is about to begin so it + //! can avoid certain unnecessary updates. void BeginBatchMovement(); - /// Let the EditorVertexSelection know a batch movement has ended so it can return - /// to its normal state. + //! Let the EditorVertexSelection know a batch movement has ended so it can return + //! to its normal state. void EndBatchMovement(); - /// Set the position of the TranslationManipulators (if active). + //! Set the position of the TranslationManipulators (if active). void SetSelectedPosition(const AZ::Vector3& localPosition); - AZ::EntityId GetEntityId() const { return m_entityComponentIdPair.GetEntityId(); } + AZ::EntityId GetEntityId() const + { + return m_entityComponentIdPair.GetEntityId(); + } protected: - /// Internal interface for EditorVertexSelection. + //! Internal interface for EditorVertexSelection. virtual void SetupSelectionManipulator( const AZStd::shared_ptr& selectionManipulator, const AZ::EntityComponentIdPair& entityComponentIdPair, - ManipulatorManagerId managerId, size_t index) = 0; + ManipulatorManagerId managerId, + size_t index) = 0; virtual void PrepareActions() = 0; - /// Default behavior when clicking on a selection manipulator (representing a vertex). + //! Default behavior when clicking on a selection manipulator (representing a vertex). void SelectionManipulatorSelectCallback( - size_t index, const ViewportInteraction::MouseInteraction& interaction, - const AZ::EntityComponentIdPair& entityComponentIdPair, ManipulatorManagerId managerId); + size_t index, + const ViewportInteraction::MouseInteraction& interaction, + const AZ::EntityComponentIdPair& entityComponentIdPair, + ManipulatorManagerId managerId); - /// Destroy the translation manipulator and deselect all vertices. + //! Destroy the translation manipulator and deselect all vertices. void ClearSelected(); - AZ::ComponentId GetComponentId() const { return m_entityComponentIdPair.GetComponentId(); } - const AZ::EntityComponentIdPair& GetEntityComponentIdPair() const { return m_entityComponentIdPair; } - ManipulatorManagerId GetManipulatorManagerId() const { return m_manipulatorManagerId; } + AZ::ComponentId GetComponentId() const + { + return m_entityComponentIdPair.GetComponentId(); + } - /// Is the translation vertex manipulator in 2D or 3D. - TranslationManipulators::Dimensions Dimensions() const { return m_dimensions; } + const AZ::EntityComponentIdPair& GetEntityComponentIdPair() const + { + return m_entityComponentIdPair; + } - /// How to configure the translation manipulator (view and axes). - TranslationManipulatorConfiguratorFn ConfiguratorFn() const { return m_manipulatorConfiguratorFn; } + ManipulatorManagerId GetManipulatorManagerId() const + { + return m_manipulatorManagerId; + } - /// The state we are in when editing vertices. + //! Is the translation vertex manipulator in 2D or 3D. + TranslationManipulators::Dimensions Dimensions() const + { + return m_dimensions; + } + + //! How to configure the translation manipulator (view and axes). + TranslationManipulatorConfiguratorFn ConfiguratorFn() const + { + return m_manipulatorConfiguratorFn; + } + + //! The state we are in when editing vertices. enum class State { Selecting, @@ -190,23 +252,22 @@ namespace AzToolsFramework void SetState(State state); - AZStd::unique_ptr m_hoverSelection = nullptr; ///< Interface to hover selection, representing bounds that can be selected. - AZStd::shared_ptr> m_translationManipulator = nullptr; ///< Manipulator when vertex is selected to translate it. - AZStd::vector> m_selectionManipulators; ///< Manipulators for each vertex when entity is selected. - AZStd::array, 2> m_actionOverrides; ///< Available actions corresponding to each mode. + AZStd::unique_ptr m_hoverSelection = + nullptr; //!< Interface to hover selection, representing bounds that can be selected. + AZStd::shared_ptr> m_translationManipulator = + nullptr; //!< Manipulator when vertex is selected to translate it. + AZStd::vector> + m_selectionManipulators; //!< Manipulators for each vertex when entity is selected. + AZStd::array, 2> m_actionOverrides; //!< Available actions corresponding to each mode. private: // AzFramework::EntityDebugDisplayEventBus - void DisplayEntityViewport( - const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) override; + void DisplayEntityViewport(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; // AzFramework::ViewportDebugDisplayEventBus - void DisplayViewport2d( - const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) override; + void DisplayViewport2d(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; - /// Set selected manipulator and vertices position from offset from starting position when pressed. + //! Set selected manipulator and vertices position from offset from starting position when pressed. void UpdateManipulatorsAndVerticesFromOffset( IndexedTranslationManipulator& translationManipulator, const AZ::Vector3& localManipulatorStartPosition, @@ -217,23 +278,24 @@ namespace AzToolsFramework template::value>::type* = nullptr> void UpdateManipulatorSpace(const AzFramework::ViewportInfo& viewportInfo) const; - EditorBoxSelect m_editorBoxSelect; ///< Provide box select support for vertex selection. - AZ::EntityComponentIdPair m_entityComponentIdPair; ///< Id of the Entity and Component this editor vertex selection was created on. - ManipulatorManagerId m_manipulatorManagerId; ///< Id of the manager manipulators created from this type will be associated with. - TranslationManipulators::Dimensions m_dimensions = TranslationManipulators::Dimensions::Three; ///< The dimensions this vertex selection was created with. - TranslationManipulatorConfiguratorFn m_manipulatorConfiguratorFn = nullptr; ///< Function pointer set on Create to decide look and functionality of translation manipulator. - AZStd::function m_onVertexPositionsUpdated = nullptr; ///< Callback for when vertex positions are changed. - State m_state = State::Selecting; ///< Different states VertexSelection can be in. - bool m_worldSpace = false; ///< Are the manipulators being used in local or world space. - bool m_batchMovementInProgress = false; ///< If a batch movement operation is in progress we do not want to - ///< refresh the VertexSelection during it for performance reasons. + EditorBoxSelect m_editorBoxSelect; //!< Provide box select support for vertex selection. + AZ::EntityComponentIdPair m_entityComponentIdPair; //!< Id of the Entity and Component this editor vertex selection was created on. + ManipulatorManagerId m_manipulatorManagerId; //!< Id of the manager manipulators created from this type will be associated with. + TranslationManipulators::Dimensions m_dimensions = + TranslationManipulators::Dimensions::Three; //!< The dimensions this vertex selection was created with. + TranslationManipulatorConfiguratorFn m_manipulatorConfiguratorFn = + nullptr; //!< Function pointer set on Create to decide look and functionality of translation manipulator. + AZStd::function m_onVertexPositionsUpdated = nullptr; //!< Callback for when vertex positions are changed. + State m_state = State::Selecting; //!< Different states VertexSelection can be in. + bool m_worldSpace = false; //!< Are the manipulators being used in local or world space. + bool m_batchMovementInProgress = false; //!< If a batch movement operation is in progress we do not want to + //!< refresh the VertexSelection during it for performance reasons. }; - /// EditorVertexSelectionFixed provides selection and editing for a fixed length number of - /// vertices. New vertices cannot be inserted/added or removed. + //! EditorVertexSelectionFixed provides selection and editing for a fixed length number of + //! vertices. New vertices cannot be inserted/added or removed. template - class EditorVertexSelectionFixed - : public EditorVertexSelectionBase + class EditorVertexSelectionFixed : public EditorVertexSelectionBase { public: AZ_CLASS_ALLOCATOR_DECL @@ -247,15 +309,15 @@ namespace AzToolsFramework void SetupSelectionManipulator( const AZStd::shared_ptr& selectionManipulator, const AZ::EntityComponentIdPair& entityComponentIdPair, - ManipulatorManagerId managerId, size_t index) override; + ManipulatorManagerId managerId, + size_t index) override; void PrepareActions() override; }; - /// EditorVertexSelectionVariable provides selection and editing for a variable length number of - /// vertices. New vertices can be inserted/added or removed from the collection. + //! EditorVertexSelectionVariable provides selection and editing for a variable length number of + //! vertices. New vertices can be inserted/added or removed from the collection. template - class EditorVertexSelectionVariable - : public EditorVertexSelectionBase + class EditorVertexSelectionVariable : public EditorVertexSelectionBase { public: AZ_CLASS_ALLOCATOR_DECL @@ -272,7 +334,8 @@ namespace AzToolsFramework void SetupSelectionManipulator( const AZStd::shared_ptr& selectionManipulator, const AZ::EntityComponentIdPair& entityComponentIdPair, - ManipulatorManagerId managerId, size_t vertIndex) override; + ManipulatorManagerId managerId, + size_t vertIndex) override; //! Presents a warning to the user that vertices will not be deleted. //! @note Allow overriding by derived classes to make this a noop if required. @@ -281,21 +344,18 @@ namespace AzToolsFramework private: void PrepareActions() override; - /// @return The center point of the selected vertices. - Vertex InsertSelectedInPlace( - AZStd::vector::VertexLookup>& manipulators); + //! @return The center point of the selected vertices. + Vertex InsertSelectedInPlace(AZStd::vector::VertexLookup>& manipulators); }; - /// Helper for inserting a vertex in a variable vertices container. + //! Helper for inserting a vertex in a variable vertices container. template - void InsertVertexAfter( - const AZ::EntityComponentIdPair& entityComponentIdPair, size_t vertIndex, const Vertex& localPosition); + void InsertVertexAfter(const AZ::EntityComponentIdPair& entityComponentIdPair, size_t vertIndex, const Vertex& localPosition); - /// Helper for removing a vertex in a variable vertices container. - /// Remove a vertex from the container and ensure the associated manipulator is unset and - /// property display values are refreshed. + //! Helper for removing a vertex in a variable vertices container. + //! Remove a vertex from the container and ensure the associated manipulator is unset and + //! property display values are refreshed. template - void SafeRemoveVertex( - const AZ::EntityComponentIdPair& entityComponentIdPair, size_t vertexIndex); + void SafeRemoveVertex(const AZ::EntityComponentIdPair& entityComponentIdPair, size_t vertexIndex); } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/HoverSelection.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/HoverSelection.h index c5f58f0d56..a389237b2b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/HoverSelection.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/HoverSelection.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -16,10 +16,10 @@ namespace AzToolsFramework { - /// HoverSelection provides an interface for manipulator/s offering selection when - /// the mouse is hovered over a particular bound. This interface is used to represent - /// a Spline manipulator bound, and a series of LineSegment manipulator bounds. - /// This generic interface allows EditorVertexSelection to use either Spline or LineSegment selection. + //! HoverSelection provides an interface for manipulator/s offering selection when + //! the mouse is hovered over a particular bound. This interface is used to represent + //! a Spline manipulator bound, and a series of LineSegment manipulator bounds. + //! This generic interface allows EditorVertexSelection to use either Spline or LineSegment selection. class HoverSelection { public: @@ -33,21 +33,37 @@ namespace AzToolsFramework virtual void SetNonUniformScale(const AZ::Vector3& nonUniformScale) = 0; }; - /// NullHoverSelection is used when vertices cannot be inserted. This serves as a no-op - /// and is used to prevent the need for additional null checks in EditorVertexSelection. - class NullHoverSelection - : public HoverSelection + //! NullHoverSelection is used when vertices cannot be inserted. This serves as a no-op + //! and is used to prevent the need for additional null checks in EditorVertexSelection. + class NullHoverSelection : public HoverSelection { public: NullHoverSelection() = default; NullHoverSelection(const NullHoverSelection&) = delete; NullHoverSelection& operator=(const NullHoverSelection&) = delete; - void Register(ManipulatorManagerId /*managerId*/) override {} - void Unregister() override {} - void SetBoundsDirty() override {} - void Refresh() override {} - void SetSpace(const AZ::Transform& /*worldFromLocal*/) override {} - void SetNonUniformScale([[maybe_unused]] const AZ::Vector3& nonUniformScale) override {} + void Register([[maybe_unused]] ManipulatorManagerId managerId) override + { + } + + void Unregister() override + { + } + + void SetBoundsDirty() override + { + } + + void Refresh() override + { + } + + void SetSpace([[maybe_unused]] const AZ::Transform& worldFromLocal) override + { + } + + void SetNonUniformScale([[maybe_unused]] const AZ::Vector3& nonUniformScale) override + { + } }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LineHoverSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LineHoverSelection.cpp index e26c154be9..12d5f399c3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LineHoverSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LineHoverSelection.cpp @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "LineHoverSelection.h" @@ -22,17 +22,15 @@ namespace AzToolsFramework { - static const AZ::Color s_lineSelectManipulatorColor = AZ::Color(0.0f, 1.0f, 0.0f, 1.0f); + static const AZ::Color LineSelectManipulatorColor = AZ::Color(0.0f, 1.0f, 0.0f, 1.0f); template - static void UpdateLineSegmentPosition( - const size_t vertIndex, const AZ::EntityId entityId, LineSegmentSelectionManipulator& lineSegment) + static void UpdateLineSegmentPosition(const size_t vertIndex, const AZ::EntityId entityId, LineSegmentSelectionManipulator& lineSegment) { Vertex start; bool foundStart = false; AZ::FixedVerticesRequestBus::EventResult( - foundStart, entityId, &AZ::FixedVerticesRequestBus::Handler::GetVertex, - vertIndex, start); + foundStart, entityId, &AZ::FixedVerticesRequestBus::Handler::GetVertex, vertIndex, start); if (foundStart) { @@ -40,14 +38,12 @@ namespace AzToolsFramework } size_t size = 0; - AZ::FixedVerticesRequestBus::EventResult( - size, entityId, &AZ::FixedVerticesRequestBus::Handler::Size); + AZ::FixedVerticesRequestBus::EventResult(size, entityId, &AZ::FixedVerticesRequestBus::Handler::Size); Vertex end; bool foundEnd = false; AZ::FixedVerticesRequestBus::EventResult( - foundEnd, entityId, &AZ::FixedVerticesRequestBus::Handler::GetVertex, - (vertIndex + 1) % size, end); + foundEnd, entityId, &AZ::FixedVerticesRequestBus::Handler::GetVertex, (vertIndex + 1) % size, end); if (foundEnd) { @@ -56,8 +52,7 @@ namespace AzToolsFramework // update the view const float lineWidth = 0.05f; - lineSegment.SetView( - CreateManipulatorViewLineSelect(lineSegment, s_lineSelectManipulatorColor, lineWidth)); + lineSegment.SetView(CreateManipulatorViewLineSelect(lineSegment, LineSelectManipulatorColor, lineWidth)); } template @@ -66,9 +61,8 @@ namespace AzToolsFramework : m_entityId(entityComponentIdPair.GetEntityId()) { // create a line segment manipulator from vertex positions and setup its callback - auto setupLineSegment = [this] ( - const AZ::EntityComponentIdPair& entityComponentIdPair, - const ManipulatorManagerId managerId, const size_t vertIndex) + auto setupLineSegment = + [this](const AZ::EntityComponentIdPair& entityComponentIdPair, const ManipulatorManagerId managerId, const size_t vertIndex) { m_lineSegmentManipulators.push_back(LineSegmentSelectionManipulator::MakeShared()); AZStd::shared_ptr& lineSegmentManipulator = m_lineSegmentManipulators.back(); @@ -81,11 +75,9 @@ namespace AzToolsFramework lineSegmentManipulator->InstallLeftMouseUpCallback( [vertIndex, entityComponentIdPair](const LineSegmentSelectionManipulator::Action& action) - { - InsertVertexAfter( - entityComponentIdPair, vertIndex, - AZ::AdaptVertexIn(action.m_localLineHitPosition)); - }); + { + InsertVertexAfter(entityComponentIdPair, vertIndex, AZ::AdaptVertexIn(action.m_localLineHitPosition)); + }); }; // create all line segment manipulators for the polygon prism (used for selection bounds) @@ -150,8 +142,7 @@ namespace AzToolsFramework void LineSegmentHoverSelection::Refresh() { size_t vertexCount = 0; - AZ::FixedVerticesRequestBus::EventResult( - vertexCount, m_entityId, &AZ::FixedVerticesRequestBus::Handler::Size); + AZ::FixedVerticesRequestBus::EventResult(vertexCount, m_entityId, &AZ::FixedVerticesRequestBus::Handler::Size); // update the start/end positions of all the line segment manipulators to ensure // they stay consistent with the polygon prism shape diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LineHoverSelection.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LineHoverSelection.h index eb5e3991c5..e14930f9ca 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LineHoverSelection.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LineHoverSelection.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -25,17 +25,14 @@ namespace AzToolsFramework { class LineSegmentSelectionManipulator; - /// LineSegmentHoverSelection is a concrete implementation of HoverSelection wrapping a collection/container - /// of vertices and a list of LineSegmentManipulators. The underlying manipulators are used to control selection - /// by highlighting where on the line a new vertex will be inserted. + //! LineSegmentHoverSelection is a concrete implementation of HoverSelection wrapping a collection/container + //! of vertices and a list of LineSegmentManipulators. The underlying manipulators are used to control selection + //! by highlighting where on the line a new vertex will be inserted. template - class LineSegmentHoverSelection - : public HoverSelection + class LineSegmentHoverSelection : public HoverSelection { public: - explicit LineSegmentHoverSelection( - const AZ::EntityComponentIdPair& entityComponentIdPair, - ManipulatorManagerId managerId); + explicit LineSegmentHoverSelection(const AZ::EntityComponentIdPair& entityComponentIdPair, ManipulatorManagerId managerId); LineSegmentHoverSelection(const LineSegmentHoverSelection&) = delete; LineSegmentHoverSelection& operator=(const LineSegmentHoverSelection&) = delete; ~LineSegmentHoverSelection(); @@ -49,6 +46,6 @@ namespace AzToolsFramework private: AZ::EntityId m_entityId; - AZStd::vector> m_lineSegmentManipulators; ///< Manipulators for each line. + AZStd::vector> m_lineSegmentManipulators; //!< Manipulators for each line. }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LineSegmentSelectionManipulator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LineSegmentSelectionManipulator.cpp index 8874e0dcd9..45f2803818 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LineSegmentSelectionManipulator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LineSegmentSelectionManipulator.cpp @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "LineSegmentSelectionManipulator.h" @@ -20,15 +20,20 @@ namespace AzToolsFramework { LineSegmentSelectionManipulator::Action CalculateManipulationDataAction( - const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Vector3& rayOrigin, - const AZ::Vector3& rayDirection, const float rayLength, const AZ::Vector3& localStart, const AZ::Vector3& localEnd) + const AZ::Transform& worldFromLocal, + const AZ::Vector3& nonUniformScale, + const AZ::Vector3& rayOrigin, + const AZ::Vector3& rayDirection, + const float rayLength, + const AZ::Vector3& localStart, + const AZ::Vector3& localEnd) { AZ::Vector3 worldClosestPositionRay, worldClosestPositionLineSegment; float rayProportion, lineSegmentProportion; AZ::Intersect::ClosestSegmentSegment( - rayOrigin, rayOrigin + rayDirection * rayLength, - worldFromLocal.TransformPoint(nonUniformScale * localStart), worldFromLocal.TransformPoint(nonUniformScale * localEnd), - rayProportion, lineSegmentProportion, worldClosestPositionRay, worldClosestPositionLineSegment); + rayOrigin, rayOrigin + rayDirection * rayLength, worldFromLocal.TransformPoint(nonUniformScale * localStart), + worldFromLocal.TransformPoint(nonUniformScale * localEnd), rayProportion, lineSegmentProportion, worldClosestPositionRay, + worldClosestPositionLineSegment); AZ::Transform worldFromLocalNormalized = worldFromLocal; const AZ::Vector3 scale = worldFromLocalNormalized.ExtractUniformScale() * nonUniformScale; @@ -47,7 +52,9 @@ namespace AzToolsFramework AttachLeftMouseDownImpl(); } - LineSegmentSelectionManipulator::~LineSegmentSelectionManipulator() {} + LineSegmentSelectionManipulator::~LineSegmentSelectionManipulator() + { + } void LineSegmentSelectionManipulator::InstallLeftMouseDownCallback(const MouseActionCallback& onMouseDownCallback) { @@ -112,12 +119,9 @@ namespace AzToolsFramework if (mouseInteraction.m_keyboardModifiers.Ctrl() && !mouseInteraction.m_keyboardModifiers.Shift()) { m_manipulatorView->Draw( - GetManipulatorManagerId(), managerState, - GetManipulatorId(), { - TransformUniformScale(GetSpace()), GetNonUniformScale(), - m_localStart, MouseOver() - }, - debugDisplay, cameraState, mouseInteraction); + GetManipulatorManagerId(), managerState, GetManipulatorId(), + { TransformUniformScale(GetSpace()), GetNonUniformScale(), m_localStart, MouseOver() }, debugDisplay, cameraState, + mouseInteraction); } } @@ -135,4 +139,4 @@ namespace AzToolsFramework { m_manipulatorView->Invalidate(GetManipulatorManagerId()); } -} +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LineSegmentSelectionManipulator.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LineSegmentSelectionManipulator.h index d9e317c378..107a0ccf9f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LineSegmentSelectionManipulator.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LineSegmentSelectionManipulator.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -20,12 +20,12 @@ namespace AzToolsFramework { class ManipulatorView; - /// A manipulator to expose where on a line a user is moving their mouse. + //! A manipulator to expose where on a line a user is moving their mouse. class LineSegmentSelectionManipulator : public BaseManipulator , public ManipulatorSpace { - /// Private constructor. + //! Private constructor. LineSegmentSelectionManipulator(); public: @@ -37,10 +37,10 @@ namespace AzToolsFramework ~LineSegmentSelectionManipulator(); - /// A Manipulator must only be created and managed through a shared_ptr. + //! A Manipulator must only be created and managed through a shared_ptr. static AZStd::shared_ptr MakeShared(); - /// Mouse action data used by MouseActionCallback. + //! Mouse action data used by MouseActionCallback. struct Action { AZ::Vector3 m_localLineHitPosition; @@ -57,18 +57,31 @@ namespace AzToolsFramework const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& mouseInteraction) override; - void SetStart(const AZ::Vector3& startLocal) { m_localStart = startLocal; } - void SetEnd(const AZ::Vector3& endLocal) { m_localEnd = endLocal; } - const AZ::Vector3& GetStart() const { return m_localStart; } - const AZ::Vector3& GetEnd() const { return m_localEnd; } + void SetStart(const AZ::Vector3& startLocal) + { + m_localStart = startLocal; + } + + void SetEnd(const AZ::Vector3& endLocal) + { + m_localEnd = endLocal; + } + + const AZ::Vector3& GetStart() const + { + return m_localStart; + } + + const AZ::Vector3& GetEnd() const + { + return m_localEnd; + } void SetView(AZStd::unique_ptr&& view); private: - void OnLeftMouseDownImpl( - const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance) override; - void OnLeftMouseUpImpl( - const ViewportInteraction::MouseInteraction& interaction) override; + void OnLeftMouseDownImpl(const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance) override; + void OnLeftMouseUpImpl(const ViewportInteraction::MouseInteraction& interaction) override; void InvalidateImpl() override; void SetBoundsDirtyImpl() override; @@ -79,12 +92,18 @@ namespace AzToolsFramework MouseActionCallback m_onLeftMouseDownCallback = nullptr; MouseActionCallback m_onLeftMouseUpCallback = nullptr; - ViewportInteraction::KeyboardModifiers m_keyboardModifiers; ///< What modifier keys are pressed when interacting with this manipulator. + ViewportInteraction::KeyboardModifiers + m_keyboardModifiers; //!< What modifier keys are pressed when interacting with this manipulator. - AZStd::unique_ptr m_manipulatorView = nullptr; ///< Look of manipulator. + AZStd::unique_ptr m_manipulatorView = nullptr; //!< Look of manipulator. }; LineSegmentSelectionManipulator::Action CalculateManipulationDataAction( - const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Vector3& rayOrigin, - const AZ::Vector3& rayDirection, float rayLength, const AZ::Vector3& localStart, const AZ::Vector3& localEnd); + const AZ::Transform& worldFromLocal, + const AZ::Vector3& nonUniformScale, + const AZ::Vector3& rayOrigin, + const AZ::Vector3& rayDirection, + float rayLength, + const AZ::Vector3& localStart, + const AZ::Vector3& localEnd); } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.cpp index 34ae28bd13..604e74b6f5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.cpp @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "LinearManipulator.h" @@ -22,13 +22,16 @@ namespace AzToolsFramework { LinearManipulator::Starter CalculateLinearManipulationDataStart( - const LinearManipulator::Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, - const AZ::Transform& localTransform, const ViewportInteraction::MouseInteraction& interaction, const float intersectionDistance, + const LinearManipulator::Fixed& fixed, + const AZ::Transform& worldFromLocal, + const AZ::Vector3& nonUniformScale, + const AZ::Transform& localTransform, + const ViewportInteraction::MouseInteraction& interaction, + const float intersectionDistance, const AzFramework::CameraState& cameraState) { - const ManipulatorInteraction manipulatorInteraction = - BuildManipulatorInteraction( - worldFromLocal, nonUniformScale, interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection); + const ManipulatorInteraction manipulatorInteraction = BuildManipulatorInteraction( + worldFromLocal, nonUniformScale, interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection); const AZ::Vector3 axis = TransformDirectionNoScaling(localTransform, fixed.m_axis); const AZ::Vector3 rayCrossAxis = manipulatorInteraction.m_localRayDirection.Cross(axis); @@ -47,32 +50,35 @@ namespace AzToolsFramework manipulatorInteraction.m_localRayOrigin + manipulatorInteraction.m_localRayDirection * intersectionDistance; Internal::CalculateRayPlaneIntersectingPoint( - manipulatorInteraction.m_localRayOrigin, manipulatorInteraction.m_localRayDirection, - localIntersectionPoint, startTransition.m_localNormal, start.m_localHitPosition); + manipulatorInteraction.m_localRayOrigin, manipulatorInteraction.m_localRayDirection, localIntersectionPoint, + startTransition.m_localNormal, start.m_localHitPosition); start.m_screenPosition = interaction.m_mousePick.m_screenCoordinates; start.m_localPosition = localTransform.GetTranslation(); - start.m_localScale = AZ::Vector3(localTransform.GetUniformScale());; + start.m_localScale = AZ::Vector3(localTransform.GetUniformScale()); + ; start.m_localAxis = axis; // sign to determine which side of the linear axis we pressed // (useful to know when the visual axis flips to face the camera) - start.m_sign = - AZ::GetSign((start.m_localHitPosition - localTransform.GetTranslation()).Dot(axis)); + start.m_sign = AZ::GetSign((start.m_localHitPosition - localTransform.GetTranslation()).Dot(axis)); startTransition.m_screenToWorldScale = 1.0f / CalculateScreenToWorldMultiplier((worldFromLocal * localTransform).GetTranslation(), cameraState); - return {startTransition, start}; + return { startTransition, start }; } LinearManipulator::Action CalculateLinearManipulationDataAction( - const LinearManipulator::Fixed& fixed, const LinearManipulator::Starter& starter, - const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform, - const GridSnapParameters& gridSnapParams, const ViewportInteraction::MouseInteraction& interaction) + const LinearManipulator::Fixed& fixed, + const LinearManipulator::Starter& starter, + const AZ::Transform& worldFromLocal, + const AZ::Vector3& nonUniformScale, + const AZ::Transform& localTransform, + const GridSnapParameters& gridSnapParams, + const ViewportInteraction::MouseInteraction& interaction) { - const ManipulatorInteraction manipulatorInteraction = - BuildManipulatorInteraction( - worldFromLocal, nonUniformScale, interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection); + const ManipulatorInteraction manipulatorInteraction = BuildManipulatorInteraction( + worldFromLocal, nonUniformScale, interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection); const auto& [startTransition, start] = starter; @@ -81,8 +87,8 @@ namespace AzToolsFramework // if an invalid ray intersection is attempted AZ::Vector3 localHitPosition = start.m_localHitPosition; Internal::CalculateRayPlaneIntersectingPoint( - manipulatorInteraction.m_localRayOrigin, manipulatorInteraction.m_localRayDirection, - start.m_localHitPosition, startTransition.m_localNormal, localHitPosition); + manipulatorInteraction.m_localRayOrigin, manipulatorInteraction.m_localRayDirection, start.m_localHitPosition, + startTransition.m_localNormal, localHitPosition); localHitPosition = Internal::TryConstrainHitPositionToView( localHitPosition, start.m_localHitPosition, worldFromLocal.GetInverse(), @@ -103,9 +109,8 @@ namespace AzToolsFramework LinearManipulator::Action action; action.m_fixed = fixed; action.m_start = start; - action.m_current.m_localPositionOffset = snapping - ? CalculateSnappedAmount(unsnappedOffset, axis, gridSize * scaleRecip) - : unsnappedOffset; + action.m_current.m_localPositionOffset = + snapping ? CalculateSnappedAmount(unsnappedOffset, axis, gridSize * scaleRecip) : unsnappedOffset; action.m_current.m_screenPosition = interaction.m_mousePick.m_screenCoordinates; action.m_viewportId = interaction.m_interactionId.m_viewportId; @@ -191,7 +196,8 @@ namespace AzToolsFramework // note: m_localTransform must not be made uniform as it may contain a local scale we want to snap m_onLeftMouseUpCallback(CalculateLinearManipulationDataAction( - m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(), gridSnapParams, interaction)); + m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(), gridSnapParams, + interaction)); } } @@ -202,16 +208,14 @@ namespace AzToolsFramework const ViewportInteraction::MouseInteraction& mouseInteraction) { const AZ::Transform localTransform = m_useVisualsOverride - ? AZ::Transform::CreateFromQuaternionAndTranslation( - m_visualOrientationOverride, GetLocalPosition()) + ? AZ::Transform::CreateFromQuaternionAndTranslation(m_visualOrientationOverride, GetLocalPosition()) : GetLocalTransform(); if (cl_manipulatorDrawDebug) { if (PerformingAction()) { - const GridSnapParameters gridSnapParams = - GridSnapSettings(mouseInteraction.m_interactionId.m_viewportId); + const GridSnapParameters gridSnapParams = GridSnapSettings(mouseInteraction.m_interactionId.m_viewportId); const auto action = CalculateLinearManipulationDataAction( m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(), gridSnapParams, @@ -219,9 +223,10 @@ namespace AzToolsFramework // display the exact hit (ray intersection) of the mouse pick on the manipulator DrawTransformAxes( - debugDisplay, TransformUniformScale(GetSpace()) * - AZ::Transform::CreateTranslation( - action.m_start.m_localHitPosition + GetNonUniformScale() * action.m_current.m_localPositionOffset)); + debugDisplay, + TransformUniformScale(GetSpace()) * + AZ::Transform::CreateTranslation( + action.m_start.m_localHitPosition + GetNonUniformScale() * action.m_current.m_localPositionOffset)); } AZ::Transform combined = GetLocalTransform(); @@ -229,8 +234,7 @@ namespace AzToolsFramework combined = GetSpace() * combined; DrawTransformAxes(debugDisplay, combined); - DrawAxis( - debugDisplay, combined.GetTranslation(), TransformDirectionNoScaling(combined, m_fixed.m_axis)); + DrawAxis(debugDisplay, combined.GetTranslation(), TransformDirectionNoScaling(combined, m_fixed.m_axis)); } for (auto& view : m_manipulatorViews) @@ -238,12 +242,9 @@ namespace AzToolsFramework auto nonUniformScale = GetNonUniformScale(); view->Draw( - GetManipulatorManagerId(), managerState, - GetManipulatorId(), { - ApplySpace(localTransform), GetNonUniformScale(), - AZ::Vector3::CreateZero(), MouseOver() - }, - debugDisplay, cameraState, mouseInteraction); + GetManipulatorManagerId(), managerState, GetManipulatorId(), + { ApplySpace(localTransform), GetNonUniformScale(), AZ::Vector3::CreateZero(), MouseOver() }, debugDisplay, cameraState, + mouseInteraction); } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.h index c3d43a2535..240d4c7b9b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -22,13 +22,13 @@ namespace AzToolsFramework { struct GridSnapParameters; - /// LinearManipulator serves as a visual tool for users to modify values - /// in one dimension on an axis defined in 3D space. + //! LinearManipulator serves as a visual tool for users to modify values + //! in one dimension on an axis defined in 3D space. class LinearManipulator : public BaseManipulator , public ManipulatorSpaceWithLocalTransform { - /// Private constructor. + //! Private constructor. explicit LinearManipulator(const AZ::Transform& worldFromLocal); public: @@ -41,68 +41,80 @@ namespace AzToolsFramework ~LinearManipulator() = default; - /// A Manipulator must only be created and managed through a shared_ptr. - /// @note worldFromLocal should not contain scale. + //! A Manipulator must only be created and managed through a shared_ptr. + //! @note worldFromLocal should not contain scale. static AZStd::shared_ptr MakeShared(const AZ::Transform& worldFromLocal); - /// Unchanging data set once for the linear manipulator. + //! Unchanging data set once for the linear manipulator. struct Fixed { - AZ::Vector3 m_axis = AZ::Vector3::CreateAxisX(); ///< The axis the manipulator will move along. + AZ::Vector3 m_axis = AZ::Vector3::CreateAxisX(); //!< The axis the manipulator will move along. }; - /// Data passed between the initial press and first movement of the linear manipulator. + //! Data passed between the initial press and first movement of the linear manipulator. struct StartTransition { - /// The normal in local space of the manipulator when the mouse down event happens. + //! The normal in local space of the manipulator when the mouse down event happens. AZ::Vector3 m_localNormal; - /// Used to scale movement based on camera distance if we want screen space instead - /// of world space displacement. + //! Used to scale movement based on camera distance if we want screen space instead + //! of world space displacement. float m_screenToWorldScale; }; - /// The state of the manipulator at the start of an interaction. + //! The state of the manipulator at the start of an interaction. struct Start { - AZ::Vector3 m_localPosition; ///< The current position of the manipulator in local space. - AZ::Vector3 m_localScale; ///< The current scale of the manipulator in local space. - AZ::Vector3 m_localHitPosition; ///< The intersection point in local space between the ray and the manipulator when the mouse down event happens. - AZ::Vector3 m_localAxis; ///< The axis in the local space of the manipulator itself. - float m_sign; ///< Used to determine which side of the axis we clicked on in case it's flipped to face the camera. - AzFramework::ScreenPoint m_screenPosition; ///< The initial position in screen space of the manipulator. + AZ::Vector3 m_localPosition; //!< The current position of the manipulator in local space. + AZ::Vector3 m_localScale; //!< The current scale of the manipulator in local space. + AZ::Vector3 m_localHitPosition; //!< The intersection point in local space between the ray and the manipulator when the mouse + //!< down event happens. + AZ::Vector3 m_localAxis; //!< The axis in the local space of the manipulator itself. + float m_sign; //!< Used to determine which side of the axis we clicked on in case it's flipped to face the camera. + AzFramework::ScreenPoint m_screenPosition; //!< The initial position in screen space of the manipulator. }; - /// The state of the manipulator during an interaction. + //! The state of the manipulator during an interaction. struct Current { - AZ::Vector3 m_localPositionOffset; ///< The current offset of the manipulator from its starting position in local space. - AZ::Vector3 m_localScaleOffset; ///< The current offset of the manipulator from its starting scale in local space. - AzFramework::ScreenPoint m_screenPosition; ///< The current position in screen space of the manipulator. + AZ::Vector3 m_localPositionOffset; //!< The current offset of the manipulator from its starting position in local space. + AZ::Vector3 m_localScaleOffset; //!< The current offset of the manipulator from its starting scale in local space. + AzFramework::ScreenPoint m_screenPosition; //!< The current position in screen space of the manipulator. }; - /// Mouse action data used by MouseActionCallback (wraps Fixed, Start and Current manipulator state). + //! Mouse action data used by MouseActionCallback (wraps Fixed, Start and Current manipulator state). struct Action { Fixed m_fixed; Start m_start; Current m_current; ViewportInteraction::KeyboardModifiers m_modifiers; - int m_viewportId; ///< The id of the viewport this manipulator is being used in. - AZ::Vector3 LocalScale() const { return m_start.m_localScale + m_current.m_localScaleOffset; } - AZ::Vector3 LocalScaleOffset() const { return m_current.m_localScaleOffset; } - AZ::Vector3 LocalPosition() const { return m_start.m_localPosition + m_current.m_localPositionOffset; } - AZ::Vector3 LocalPositionOffset() const { return m_current.m_localPositionOffset; } + int m_viewportId; //!< The id of the viewport this manipulator is being used in. + AZ::Vector3 LocalScale() const + { + return m_start.m_localScale + m_current.m_localScaleOffset; + } + AZ::Vector3 LocalScaleOffset() const + { + return m_current.m_localScaleOffset; + } + AZ::Vector3 LocalPosition() const + { + return m_start.m_localPosition + m_current.m_localPositionOffset; + } + AZ::Vector3 LocalPositionOffset() const + { + return m_current.m_localPositionOffset; + } AZ::Vector2 ScreenOffset() const { - return AzFramework::Vector2FromScreenVector( - m_current.m_screenPosition - m_start.m_screenPosition); + return AzFramework::Vector2FromScreenVector(m_current.m_screenPosition - m_start.m_screenPosition); } }; - /// This is the function signature of callbacks that will be invoked whenever a manipulator - /// is clicked on or dragged. + //! This is the function signature of callbacks that will be invoked whenever a manipulator + //! is clicked on or dragged. using MouseActionCallback = AZStd::function; - /// Tuple of StartTransition (initial mouse down to mouse move) and Start state. + //! Tuple of StartTransition (initial mouse down to mouse move) and Start state. using Starter = AZStd::tuple; void InstallLeftMouseDownCallback(const MouseActionCallback& onMouseDownCallback); @@ -116,7 +128,10 @@ namespace AzToolsFramework const ViewportInteraction::MouseInteraction& mouseInteraction) override; void SetAxis(const AZ::Vector3& axis); - const AZ::Vector3& GetAxis() const { return m_fixed.m_axis; } + const AZ::Vector3& GetAxis() const + { + return m_fixed.m_axis; + } template void SetViews(Views&& views) @@ -135,12 +150,9 @@ namespace AzToolsFramework } private: - void OnLeftMouseDownImpl( - const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance) override; - void OnLeftMouseUpImpl( - const ViewportInteraction::MouseInteraction& interaction) override; - void OnMouseMoveImpl( - const ViewportInteraction::MouseInteraction& interaction) override; + void OnLeftMouseDownImpl(const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance) override; + void OnLeftMouseUpImpl(const ViewportInteraction::MouseInteraction& interaction) override; + void OnMouseMoveImpl(const ViewportInteraction::MouseInteraction& interaction) override; void InvalidateImpl() override; void SetBoundsDirtyImpl() override; @@ -155,16 +167,24 @@ namespace AzToolsFramework MouseActionCallback m_onLeftMouseUpCallback = nullptr; MouseActionCallback m_onMouseMoveCallback = nullptr; - ManipulatorViews m_manipulatorViews; ///< Look of manipulator. + ManipulatorViews m_manipulatorViews; //!< Look of manipulator. }; LinearManipulator::Starter CalculateLinearManipulationDataStart( - const LinearManipulator::Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, - const AZ::Transform& localTransform, const ViewportInteraction::MouseInteraction& interaction, float intersectionDistance, + const LinearManipulator::Fixed& fixed, + const AZ::Transform& worldFromLocal, + const AZ::Vector3& nonUniformScale, + const AZ::Transform& localTransform, + const ViewportInteraction::MouseInteraction& interaction, + float intersectionDistance, const AzFramework::CameraState& cameraState); LinearManipulator::Action CalculateLinearManipulationDataAction( - const LinearManipulator::Fixed& fixed, const LinearManipulator::Starter& starter, - const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform, - const GridSnapParameters& gridSnapParams, const ViewportInteraction::MouseInteraction& interaction); + const LinearManipulator::Fixed& fixed, + const LinearManipulator::Starter& starter, + const AZ::Transform& worldFromLocal, + const AZ::Vector3& nonUniformScale, + const AZ::Transform& localTransform, + const GridSnapParameters& gridSnapParams, + const ViewportInteraction::MouseInteraction& interaction); } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorBus.h index 7768368d6b..a79a14abc2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorBus.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -26,56 +26,55 @@ namespace AzToolsFramework using ManipulatorManagerId = IdType; static const ManipulatorManagerId InvalidManipulatorManagerId = ManipulatorManagerId(0); - /// EBus interface used to send requests to ManipulatorManager. - class ManipulatorManagerRequests - : public AZ::EBusTraits + //! EBus interface used to send requests to ManipulatorManager. + class ManipulatorManagerRequests : public AZ::EBusTraits { public: - static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; /**< We can have multiple manipulator managers. - In the case where there are multiple viewports, each displaying - a different set of entities, a different manipulator manager is required - to provide a different collision space for each viewport so that mouse - hit detection can be handled properly. */ + //! We can have multiple manipulator managers. + //! In the case where there are multiple viewports, each displaying + //! a different set of entities, a different manipulator manager is required + //! to provide a different collision space for each viewport so that mouse + //! hit detection can be handled properly. + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; using BusIdType = ManipulatorManagerId; static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; virtual ~ManipulatorManagerRequests() = default; - /// Register a manipulator with the Manipulator Manager. - /// @param manipulator The manipulator parameter is passed as a shared_ptr so - /// that the system responsible for managing manipulators can maintain ownership - /// of the manipulator even if is destroyed while in use. + //! Register a manipulator with the Manipulator Manager. + //! @param manipulator The manipulator parameter is passed as a shared_ptr so + //! that the system responsible for managing manipulators can maintain ownership + //! of the manipulator even if is destroyed while in use. virtual void RegisterManipulator(AZStd::shared_ptr manipulator) = 0; - /// Unregister a manipulator from the Manipulator Manager. - /// After unregistering the manipulator, it will be excluded from mouse hit detection - /// and will not receive any mouse action events. The Manipulator Manager will also - /// relinquish ownership of the manipulator. + //! Unregister a manipulator from the Manipulator Manager. + //! After unregistering the manipulator, it will be excluded from mouse hit detection + //! and will not receive any mouse action events. The Manipulator Manager will also + //! relinquish ownership of the manipulator. virtual void UnregisterManipulator(BaseManipulator* manipulator) = 0; - /// Delete a manipulator bound. + //! Delete a manipulator bound. virtual void DeleteManipulatorBound(Picking::RegisteredBoundId boundId) = 0; - /// Mark the bound of a manipulator dirty so it's excluded from mouse hit detection. - /// This should be called whenever a manipulator is moved. + //! Mark the bound of a manipulator dirty so it's excluded from mouse hit detection. + //! This should be called whenever a manipulator is moved. virtual void SetBoundDirty(Picking::RegisteredBoundId boundId) = 0; - /// Returns true if the manipulator manager is currently interacting, otherwise false. + //! Returns true if the manipulator manager is currently interacting, otherwise false. virtual bool Interacting() const = 0; - /// Update the bound for a manipulator. - /// If \ref boundId hasn't been registered before or it's invalid, a new bound is created and set using \ref boundShapeData - /// @param manipulatorId The id of the manipulator whose bound needs to update. - /// @param boundId The id of the bound that needs to update. - /// @param boundShapeData The pointer to the new bound shape data. - /// @return If \ref boundId has been registered return the same id, otherwise create a new bound and return its id. + //! Update the bound for a manipulator. + //! If \ref boundId hasn't been registered before or it's invalid, a new bound is created and set using \ref boundShapeData. + //! @param manipulatorId The id of the manipulator whose bound needs to update. + //! @param boundId The id of the bound that needs to update. + //! @param boundShapeData The pointer to the new bound shape data. + //! @return If \ref boundId has been registered return the same id, otherwise create a new bound and return its id. virtual Picking::RegisteredBoundId UpdateBound( - ManipulatorId manipulatorId, Picking::RegisteredBoundId boundId, - const Picking::BoundRequestShapeBase& boundShapeData) = 0; + ManipulatorId manipulatorId, Picking::RegisteredBoundId boundId, const Picking::BoundRequestShapeBase& boundShapeData) = 0; }; - /// Type to inherit to implement ManipulatorManagerRequests. + //! Type to inherit to implement ManipulatorManagerRequests. using ManipulatorManagerRequestBus = AZ::EBus; -}//namespace AzToolsFramework +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorManager.cpp index c856c5f9a8..3a6a2487c3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorManager.cpp @@ -1,17 +1,17 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ -#include "BaseManipulator.h" #include "ManipulatorManager.h" +#include "BaseManipulator.h" #include #include @@ -51,7 +51,8 @@ namespace AzToolsFramework if (manipulator->Registered()) { - AZ_Assert(manipulator->GetManipulatorManagerId() == m_manipulatorManagerId, + AZ_Assert( + manipulator->GetManipulatorManagerId() == m_manipulatorManagerId, "This manipulator was registered with a different manipulator manager!"); return; } @@ -75,8 +76,7 @@ namespace AzToolsFramework } Picking::RegisteredBoundId ManipulatorManager::UpdateBound( - const ManipulatorId manipulatorId, const Picking::RegisteredBoundId boundId, - const Picking::BoundRequestShapeBase& boundShapeData) + const ManipulatorId manipulatorId, const Picking::RegisteredBoundId boundId, const Picking::BoundRequestShapeBase& boundShapeData) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -99,8 +99,7 @@ namespace AzToolsFramework AZ_Assert(boundItr->second == manipulatorId, "Manipulator and its bounds are out of synchronization!"); } - const Picking::RegisteredBoundId newBoundId = - m_boundManager.UpdateOrRegisterBound(boundShapeData, boundId); + const Picking::RegisteredBoundId newBoundId = m_boundManager.UpdateOrRegisterBound(boundShapeData, boundId); if (newBoundId != boundId) { @@ -142,13 +141,6 @@ namespace AzToolsFramework } } - void ManipulatorManager::CheckModifierKeysChanged( - [[maybe_unused]] const ViewportInteraction::KeyboardModifiers keyboardModifiers, - const ViewportInteraction::MousePick& mousePick) - { - RefreshMouseOverState(mousePick); - } - void ManipulatorManager::DrawManipulators( AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, @@ -181,7 +173,8 @@ namespace AzToolsFramework if (found != m_boundIdToManipulatorIdMap.end()) { const auto manipulatorFound = m_manipulatorIdToPtrMap.find(found->second); - AZ_Assert(manipulatorFound != m_manipulatorIdToPtrMap.end(), + AZ_Assert( + manipulatorFound != m_manipulatorIdToPtrMap.end(), "Found a bound without a corresponding Manipulator, " "it's likely a bound was not cleaned up correctly"); rayIntersectionDistance = hitItr.second; @@ -194,10 +187,9 @@ namespace AzToolsFramework bool ManipulatorManager::ConsumeViewportMousePress(const ViewportInteraction::MouseInteraction& interaction) { - if (auto pickedManipulator = PickManipulator(interaction.m_mousePick); - pickedManipulator.has_value()) + if (auto pickedManipulator = PickManipulator(interaction.m_mousePick); pickedManipulator.has_value()) { - auto[manipulator, intersectionDistance] = pickedManipulator.value(); + auto [manipulator, intersectionDistance] = pickedManipulator.value(); if (interaction.m_mouseButtons.Left()) { @@ -249,24 +241,19 @@ namespace AzToolsFramework const ViewportInteraction::MousePick& mousePick) { float intersectionDistance = 0.0f; - const AZStd::shared_ptr pickedManipulator = PerformRaycast( - mousePick.m_rayOrigin, mousePick.m_rayDirection, intersectionDistance); + const AZStd::shared_ptr pickedManipulator = + PerformRaycast(mousePick.m_rayOrigin, mousePick.m_rayDirection, intersectionDistance); - return pickedManipulator.get() != nullptr - ? AZStd::make_optional(AZStd::make_tuple(pickedManipulator, intersectionDistance)) - : AZStd::nullopt; + return pickedManipulator.get() != nullptr ? AZStd::make_optional(AZStd::make_tuple(pickedManipulator, intersectionDistance)) + : AZStd::nullopt; } - ManipulatorManager::PickedManipulatorId ManipulatorManager::PickManipulatorId( - const ViewportInteraction::MousePick& mousePick) + ManipulatorManager::PickedManipulatorId ManipulatorManager::PickManipulatorId(const ViewportInteraction::MousePick& mousePick) { - auto [manipulator, intersectionDistance] = - PickManipulator(mousePick).value_or(PickedManipulator(nullptr, 0.0f)); - const ManipulatorId pickedManipulatorId = manipulator - ? manipulator->GetManipulatorId() - : InvalidManipulatorId; + auto [manipulator, intersectionDistance] = PickManipulator(mousePick).value_or(PickedManipulator(nullptr, 0.0f)); + const ManipulatorId pickedManipulatorId = manipulator ? manipulator->GetManipulatorId() : InvalidManipulatorId; - return PickedManipulatorId{pickedManipulatorId, intersectionDistance}; + return PickedManipulatorId{ pickedManipulatorId, intersectionDistance }; } ManipulatorManager::ConsumeMouseMoveResult ManipulatorManager::ConsumeViewportMouseMove( diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorManager.h index 5f57738b8c..66b38579ef 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorManager.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -23,7 +23,7 @@ namespace AzFramework { struct CameraState; class DebugDisplayRequests; -} +} // namespace AzFramework namespace AzToolsFramework { @@ -40,15 +40,15 @@ namespace AzToolsFramework class BaseManipulator; class LinearManipulator; - /// State of overall manipulator manager. + //! State of overall manipulator manager. struct ManipulatorManagerState { bool m_interacting; }; - /// This class serves to manage all relevant mouse events and coordinate all registered manipulators to function properly. - /// ManipulatorManager does not manage the life cycle of specific manipulators. The users of manipulators are responsible - /// for creating and deleting them at right time, as well as registering and unregistering accordingly. + //! This class serves to manage all relevant mouse events and coordinate all registered manipulators to function properly. + //! ManipulatorManager does not manage the life cycle of specific manipulators. The users of manipulators are responsible + //! for creating and deleting them at right time, as well as registering and unregistering accordingly. class ManipulatorManager : private ManipulatorManagerRequestBus::Handler , private EditorEntityInfoNotificationBus::Handler @@ -59,7 +59,7 @@ namespace AzToolsFramework explicit ManipulatorManager(ManipulatorManagerId managerId); ~ManipulatorManager(); - /// The result of consuming a mouse move. + //! The result of consuming a mouse move. enum class ConsumeMouseMoveResult { None, @@ -80,57 +80,52 @@ namespace AzToolsFramework void DeleteManipulatorBound(Picking::RegisteredBoundId boundId) override; void SetBoundDirty(Picking::RegisteredBoundId boundId) override; Picking::RegisteredBoundId UpdateBound( - ManipulatorId manipulatorId, Picking::RegisteredBoundId boundId, - const Picking::BoundRequestShapeBase& boundShapeData) override; - bool Interacting() const override { return m_activeManipulator != nullptr; } + ManipulatorId manipulatorId, Picking::RegisteredBoundId boundId, const Picking::BoundRequestShapeBase& boundShapeData) override; + bool Interacting() const override + { + return m_activeManipulator != nullptr; + } void DrawManipulators( AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& mouseInteraction); - // O3DE_DEPRECATED(LY-117150) - /// Check if the modifier key state has changed - if so we may need to refresh - /// certain manipulator bounds. - AZ_DEPRECATED( - void CheckModifierKeysChanged( - ViewportInteraction::KeyboardModifiers keyboardModifiers, - const ViewportInteraction::MousePick& mousePick), - "CheckModifierKeysChanged is deprecated and will be removed in a future release"); - protected: - /// @param rayOrigin The origin of the ray to test intersection with. - /// @param rayDirection The direction of the ray to test intersection with. - /// @param[out] rayIntersectionDistance The result intersecting point equals "rayOrigin + rayIntersectionDistance * rayDirection". - /// @return A pointer to a manipulator that the ray intersects. Null pointer if no intersection is detected. + //! @param rayOrigin The origin of the ray to test intersection with. + //! @param rayDirection The direction of the ray to test intersection with. + //! @param[out] rayIntersectionDistance The result intersecting point equals "rayOrigin + rayIntersectionDistance * rayDirection". + //! @return A pointer to a manipulator that the ray intersects. Null pointer if no intersection is detected. AZStd::shared_ptr PerformRaycast( const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDirection, float& rayIntersectionDistance); // EditorEntityInfoNotifications ... void OnEntityInfoUpdatedVisibility(AZ::EntityId entityId, bool visible) override; - /// Alias for a Manipulator and intersection distance. + //! Alias for a Manipulator and intersection distance. using PickedManipulator = AZStd::tuple, float>; - /// Alias for a ManipulatorId and intersection distance. + //! Alias for a ManipulatorId and intersection distance. using PickedManipulatorId = AZStd::tuple; - /// Return the picked manipulator and intersection distance if a manipulator was intersected. + //! Return the picked manipulator and intersection distance if a manipulator was intersected. AZStd::optional PickManipulator(const ViewportInteraction::MousePick& mousePick); - /// Wrapper for PickManipulator to return the ManipulatorId directly. + //! Wrapper for PickManipulator to return the ManipulatorId directly. PickedManipulatorId PickManipulatorId(const ViewportInteraction::MousePick& mousePick); - /// Called once per frame after all manipulators have been drawn (and their - /// bounds updated if required). + //! Called once per frame after all manipulators have been drawn (and their + //! bounds updated if required). void RefreshMouseOverState(const ViewportInteraction::MousePick& mousePick); - ManipulatorManagerId m_manipulatorManagerId; ///< This manipulator manager's id. - ManipulatorId m_nextManipulatorIdToGenerate; ///< Id to use for the next manipulator that is registered with this manager. + ManipulatorManagerId m_manipulatorManagerId; //!< This manipulator manager's id. + ManipulatorId m_nextManipulatorIdToGenerate; //!< Id to use for the next manipulator that is registered with this manager. - AZStd::unordered_map> m_manipulatorIdToPtrMap; ///< Mapping from a manipulatorId to the corresponding manipulator. - AZStd::unordered_map m_boundIdToManipulatorIdMap; ///< Mapping from a boundId to the corresponding manipulatorId. + AZStd::unordered_map> + m_manipulatorIdToPtrMap; //!< Mapping from a manipulatorId to the corresponding manipulator. + AZStd::unordered_map + m_boundIdToManipulatorIdMap; //!< Mapping from a boundId to the corresponding manipulatorId. - AZStd::shared_ptr m_activeManipulator; ///< The manipulator we are currently interacting with. - Picking::ManipulatorBoundManager m_boundManager; ///< All active manipulator bounds that could be interacted with. + AZStd::shared_ptr m_activeManipulator; //!< The manipulator we are currently interacting with. + Picking::ManipulatorBoundManager m_boundManager; //!< All active manipulator bounds that could be interacted with. }; // The main/default ManipulatorManagerId to be used for diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.cpp index 8a6398d025..427bb9e9d1 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.cpp @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "ManipulatorSnapping.h" @@ -19,19 +19,27 @@ #include AZ_CVAR( - AZ::Color, cl_viewportGridMainColor, AZ::Color::CreateFromRgba(26, 26, 26, 127), nullptr, - AZ::ConsoleFunctorFlags::Null, "Main color for snapping grid"); + AZ::Color, + cl_viewportGridMainColor, + AZ::Color::CreateFromRgba(26, 26, 26, 127), + nullptr, + AZ::ConsoleFunctorFlags::Null, + "Main color for snapping grid"); AZ_CVAR( - AZ::Color, cl_viewportGridFadeColor, AZ::Color::CreateFromRgba(127, 127, 127, 0), nullptr, - AZ::ConsoleFunctorFlags::Null, "Fade color for snapping grid"); + AZ::Color, + cl_viewportGridFadeColor, + AZ::Color::CreateFromRgba(127, 127, 127, 0), + nullptr, + AZ::ConsoleFunctorFlags::Null, + "Fade color for snapping grid"); +AZ_CVAR(int, cl_viewportGridSquareCount, 20, nullptr, AZ::ConsoleFunctorFlags::Null, "Number of grid squares for snapping grid"); +AZ_CVAR(float, cl_viewportGridLineWidth, 1.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "Width of grid lines for snapping grid"); AZ_CVAR( - int, cl_viewportGridSquareCount, 20, nullptr, AZ::ConsoleFunctorFlags::Null, - "Number of grid squares for snapping grid"); -AZ_CVAR( - float, cl_viewportGridLineWidth, 1.0f, nullptr, AZ::ConsoleFunctorFlags::Null, - "Width of grid lines for snapping grid"); -AZ_CVAR( - float, cl_viewportFadeLineDistanceScale, 1.0f, nullptr, AZ::ConsoleFunctorFlags::Null, + float, + cl_viewportFadeLineDistanceScale, + 1.0f, + nullptr, + AZ::ConsoleFunctorFlags::Null, "The scale to be applied to the line that fades out (scales the current gridSize)"); namespace AzToolsFramework @@ -43,16 +51,17 @@ namespace AzToolsFramework } ManipulatorInteraction BuildManipulatorInteraction( - const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, - const AZ::Vector3& worldRayOrigin, const AZ::Vector3& worldRayDirection) + const AZ::Transform& worldFromLocal, + const AZ::Vector3& nonUniformScale, + const AZ::Vector3& worldRayOrigin, + const AZ::Vector3& worldRayDirection) { const AZ::Transform worldFromLocalUniform = AzToolsFramework::TransformUniformScale(worldFromLocal); const AZ::Transform localFromWorldUniform = worldFromLocalUniform.GetInverse(); - return {localFromWorldUniform.TransformPoint(worldRayOrigin), - TransformDirectionNoScaling(localFromWorldUniform, worldRayDirection), - NonUniformScaleReciprocal(nonUniformScale), - ScaleReciprocal(worldFromLocalUniform)}; + return { localFromWorldUniform.TransformPoint(worldRayOrigin), + TransformDirectionNoScaling(localFromWorldUniform, worldRayDirection), NonUniformScaleReciprocal(nonUniformScale), + ScaleReciprocal(worldFromLocalUniform) }; } struct SnapAdjustment @@ -87,8 +96,7 @@ namespace AzToolsFramework } AZ::Vector3 CalculateSnappedTerrainPosition( - const AZ::Vector3& worldSurfacePosition, const AZ::Transform& worldFromLocal, - const int viewportId, const float gridSize) + const AZ::Vector3& worldSurfacePosition, const AZ::Transform& worldFromLocal, const int viewportId, const float gridSize) { const AZ::Transform localFromWorld = worldFromLocal.GetInverse(); const AZ::Vector3 localSurfacePosition = localFromWorld.TransformPoint(worldSurfacePosition); @@ -101,8 +109,7 @@ namespace AzToolsFramework // find terrain height at xy snapped location float terrainHeight = 0.0f; ViewportInteraction::MainEditorViewportInteractionRequestBus::EventResult( - terrainHeight, viewportId, - &ViewportInteraction::MainEditorViewportInteractionRequestBus::Events::TerrainHeight, + terrainHeight, viewportId, &ViewportInteraction::MainEditorViewportInteractionRequestBus::Events::TerrainHeight, Vector3ToVector2(worldFromLocal.TransformPoint(localSnappedSurfacePosition))); // set snapped z value to terrain height @@ -116,8 +123,7 @@ namespace AzToolsFramework { bool snapping = false; ViewportInteraction::ViewportInteractionRequestBus::EventResult( - snapping, viewportId, - &ViewportInteraction::ViewportInteractionRequestBus::Events::GridSnappingEnabled); + snapping, viewportId, &ViewportInteraction::ViewportInteractionRequestBus::Events::GridSnappingEnabled); return snapping; } @@ -126,8 +132,7 @@ namespace AzToolsFramework { float gridSize = 0.0f; ViewportInteraction::ViewportInteractionRequestBus::EventResult( - gridSize, viewportId, - &ViewportInteraction::ViewportInteractionRequestBus::Events::GridSize); + gridSize, viewportId, &ViewportInteraction::ViewportInteractionRequestBus::Events::GridSize); return gridSize; } @@ -136,7 +141,8 @@ namespace AzToolsFramework { bool snapping = GridSnapping(viewportId); const float gridSize = GridSize(viewportId); - if (AZ::IsClose(gridSize, 0.0f, 1e-2f)) // Same threshold value as min value for m_spinBox in SnapToWidget constructor in MainWindow.cpp + if (AZ::IsClose( + gridSize, 0.0f, 1e-2f)) // Same threshold value as min value for m_spinBox in SnapToWidget constructor in MainWindow.cpp { snapping = false; } @@ -148,8 +154,7 @@ namespace AzToolsFramework { bool snapping = false; ViewportInteraction::ViewportInteractionRequestBus::EventResult( - snapping, viewportId, - &ViewportInteraction::ViewportInteractionRequestBus::Events::AngleSnappingEnabled); + snapping, viewportId, &ViewportInteraction::ViewportInteractionRequestBus::Events::AngleSnappingEnabled); return snapping; } @@ -158,8 +163,7 @@ namespace AzToolsFramework { float angle = 0.0f; ViewportInteraction::ViewportInteractionRequestBus::EventResult( - angle, viewportId, - &ViewportInteraction::ViewportInteractionRequestBus::Events::AngleStep); + angle, viewportId, &ViewportInteraction::ViewportInteractionRequestBus::Events::AngleStep); return angle; } @@ -168,14 +172,12 @@ namespace AzToolsFramework { bool show = false; ViewportInteraction::ViewportInteractionRequestBus::EventResult( - show, viewportId, - &ViewportInteraction::ViewportInteractionRequestBus::Events::ShowGrid); + show, viewportId, &ViewportInteraction::ViewportInteractionRequestBus::Events::ShowGrid); return show; } - void DrawSnappingGrid( - AzFramework::DebugDisplayRequests& debugDisplay, const AZ::Transform& worldFromLocal, const float squareSize) + void DrawSnappingGrid(AzFramework::DebugDisplayRequests& debugDisplay, const AZ::Transform& worldFromLocal, const float squareSize) { debugDisplay.PushMatrix(worldFromLocal); @@ -197,21 +199,17 @@ namespace AzToolsFramework // draw the faded end parts of the grid lines debugDisplay.DrawLine( - AZ::Vector3(lineOffset, -halfGridSize, 0.0f), - AZ::Vector3(lineOffset, -(halfGridSize + fadeLineLength), 0.0f), + AZ::Vector3(lineOffset, -halfGridSize, 0.0f), AZ::Vector3(lineOffset, -(halfGridSize + fadeLineLength), 0.0f), gridMainColor, gridFadeColor); debugDisplay.DrawLine( - AZ::Vector3(lineOffset, halfGridSize, 0.0f), - AZ::Vector3(lineOffset, (halfGridSize + fadeLineLength), 0.0f), + AZ::Vector3(lineOffset, halfGridSize, 0.0f), AZ::Vector3(lineOffset, (halfGridSize + fadeLineLength), 0.0f), gridMainColor, + gridFadeColor); + debugDisplay.DrawLine( + AZ::Vector3(-halfGridSize, lineOffset, 0.0f), AZ::Vector3(-(halfGridSize + fadeLineLength), lineOffset, 0.0f), gridMainColor, gridFadeColor); debugDisplay.DrawLine( - AZ::Vector3(-halfGridSize, lineOffset, 0.0f), - AZ::Vector3(-(halfGridSize + fadeLineLength), lineOffset, 0.0f), - gridMainColor, gridFadeColor); - debugDisplay.DrawLine( - AZ::Vector3(halfGridSize, lineOffset, 0.0f), - AZ::Vector3((halfGridSize + fadeLineLength), lineOffset, 0.0f), - gridMainColor, gridFadeColor); + AZ::Vector3(halfGridSize, lineOffset, 0.0f), AZ::Vector3((halfGridSize + fadeLineLength), lineOffset, 0.0f), gridMainColor, + gridFadeColor); // build a vector of the main grid lines to draw (start and end positions) lines.push_back(AZ::Vector3(lineOffset, -halfGridSize, 0.0f)); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h index 11860780c7..f2fa104d4c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h @@ -1,19 +1,19 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once -#include #include +#include namespace AzFramework { @@ -22,7 +22,7 @@ namespace AzFramework namespace AzToolsFramework { - /// Structure to encapsulate grid snapping properties. + //! Structure to encapsulate grid snapping properties. struct GridSnapParameters { GridSnapParameters(bool gridSnap, float gridSize); @@ -31,96 +31,92 @@ namespace AzToolsFramework float m_gridSize; }; - /// Structure to hold transformed incoming viewport interaction from world space to manipulator space. + //! Structure to hold transformed incoming viewport interaction from world space to manipulator space. struct ManipulatorInteraction { - AZ::Vector3 m_localRayOrigin; ///< The ray origin (start) in the reference from of the manipulator. - AZ::Vector3 m_localRayDirection; ///< The ray direction in the reference from of the manipulator. - AZ::Vector3 m_nonUniformScaleReciprocal; ///< Handles inverting any non-uniform scale which was applied - ///< separately from the transform. - float m_scaleReciprocal; ///< The scale reciprocal (1.0 / scale) of the transform used to move the - ///< ray from world space to local space. + AZ::Vector3 m_localRayOrigin; //!< The ray origin (start) in the reference from of the manipulator. + AZ::Vector3 m_localRayDirection; //!< The ray direction in the reference from of the manipulator. + AZ::Vector3 m_nonUniformScaleReciprocal; //!< Handles inverting any non-uniform scale which was applied + //!< separately from the transform. + float m_scaleReciprocal; //!< The scale reciprocal (1.0 / scale) of the transform used to move the + //!< ray from world space to local space. }; - /// Build a ManipulatorInteraction structure from the incoming viewport interaction. + //! Build a ManipulatorInteraction structure from the incoming viewport interaction. ManipulatorInteraction BuildManipulatorInteraction( - const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, - const AZ::Vector3& worldRayOrigin, const AZ::Vector3& worldRayDirection); + const AZ::Transform& worldFromLocal, + const AZ::Vector3& nonUniformScale, + const AZ::Vector3& worldRayOrigin, + const AZ::Vector3& worldRayDirection); - /// Calculate the offset along an axis to adjust a position to stay snapped to a given grid size. - /// @note This is snap up or down to the nearest grid segment (e.g. 0.2 snaps to 0.0 -> delta 0.2, - /// 0.7 snaps to 1.0 -> delta 0.3). - AZ::Vector3 CalculateSnappedOffset( - const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, float size); + //! Calculate the offset along an axis to adjust a position to stay snapped to a given grid size. + //! @note This is snap up or down to the nearest grid segment (e.g. 0.2 snaps to 0.0 -> delta 0.2, + //! 0.7 snaps to 1.0 -> delta 0.3). + AZ::Vector3 CalculateSnappedOffset(const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, float size); - /// Return the amount to snap from the starting position given the current grid size. - /// @note A movement of more than half size (in either direction) will cause a snap by size. + //! Return the amount to snap from the starting position given the current grid size. + //! @note A movement of more than half size (in either direction) will cause a snap by size. AZ::Vector3 CalculateSnappedAmount(const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, float size); - /// For a given point on the terrain, calculate the closest xy position snapped to the grid - /// (z position is aligned to terrain height, not snapped to z grid) + //! For a given point on the terrain, calculate the closest xy position snapped to the grid + //! (z position is aligned to terrain height, not snapped to z grid) AZ::Vector3 CalculateSnappedTerrainPosition( - const AZ::Vector3& worldSurfacePosition, const AZ::Transform& worldFromLocal, - int viewportId, float gridSize); + const AZ::Vector3& worldSurfacePosition, const AZ::Transform& worldFromLocal, int viewportId, float gridSize); - /// Wrapper for grid snapping and grid size bus calls. + //! Wrapper for grid snapping and grid size bus calls. GridSnapParameters GridSnapSettings(int viewportId); - /// Wrapper for angle snapping enabled bus call. + //! Wrapper for angle snapping enabled bus call. bool AngleSnapping(int viewportId); - /// Wrapper for angle snapping increment bus call. - /// @return Angle in degrees + //! Wrapper for angle snapping increment bus call. + //! @return Angle in degrees. float AngleStep(int viewportId); - /// Wrapper for grid rendering check call. + //! Wrapper for grid rendering check call. bool ShowingGrid(int viewportId); - /// Render the grid used for snapping. - void DrawSnappingGrid( - AzFramework::DebugDisplayRequests& debugDisplay, const AZ::Transform& worldFromLocal, float squareSize); + //! Render the grid used for snapping. + void DrawSnappingGrid(AzFramework::DebugDisplayRequests& debugDisplay, const AZ::Transform& worldFromLocal, float squareSize); - /// Round to x number of significant digits. - /// @param value Number to round. - /// @param exponent Precision to use when rounding. + //! Round to x number of significant digits. + //! @param value Number to round. + //! @param exponent Precision to use when rounding. inline float Round(const float value, const float exponent) { const float precision = std::pow(10.0f, exponent); return roundf(value * precision) / precision; } - /// Round to 3 significant digits (3 digits common usage). + //! Round to 3 significant digits (3 digits common usage). inline float Round3(const float value) { return Round(value, 3.0f); } - /// Util to return sign of floating point number. - /// value > 0 return 1.0 - /// value < 0 return -1.0 - /// value == 0 return 0.0 + //! Util to return sign of floating point number. + //! value > 0 return 1.0 + //! value < 0 return -1.0 + //! value == 0 return 0.0 inline float Sign(const float value) { return static_cast((0.0f < value) - (value < 0.0f)); } - /// Find the max scale element and return the reciprocal of it. - /// Note: The reciprocal will be rounded to three significant digits to eliminate - /// noise in the value returned when dealing with values far from the origin. + //! Find the max scale element and return the reciprocal of it. + //! Note: The reciprocal will be rounded to three significant digits to eliminate + //! noise in the value returned when dealing with values far from the origin. inline float ScaleReciprocal(const AZ::Transform& transform) { return Round3(1.0f / transform.GetUniformScale()); } - /// Find the reciprocal of the non-uniform scale. - /// Each element will be rounded to three significant digits to eliminate noise - /// when dealing with values far from the origin. + //! Find the reciprocal of the non-uniform scale. + //! Each element will be rounded to three significant digits to eliminate noise + //! when dealing with values far from the origin. inline AZ::Vector3 NonUniformScaleReciprocal(const AZ::Vector3& nonUniformScale) { AZ::Vector3 scaleReciprocal = nonUniformScale.GetReciprocal(); - return AZ::Vector3( - Round3(scaleReciprocal.GetX()), - Round3(scaleReciprocal.GetY()), - Round3(scaleReciprocal.GetZ())); + return AZ::Vector3(Round3(scaleReciprocal.GetX()), Round3(scaleReciprocal.GetY()), Round3(scaleReciprocal.GetZ())); } } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSpace.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSpace.h index e26c6b8947..7c1176dd2c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSpace.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSpace.h @@ -20,7 +20,7 @@ namespace AZ namespace AzToolsFramework { - /// Handles location for manipulators which have a global space but no local transformation. + //! Handles location for manipulators which have a global space but no local transformation. class ManipulatorSpace { public: @@ -32,17 +32,16 @@ namespace AzToolsFramework const AZ::Vector3& GetNonUniformScale() const; void SetNonUniformScale(const AZ::Vector3& nonUniformScale); - /// Calculates a transform combining the space and local transform, taking non-uniform scale into account. + //! Calculates a transform combining the space and local transform, taking non-uniform scale into account. AZ::Transform ApplySpace(const AZ::Transform& localTransform) const; private: - AZ::Transform m_space = AZ::Transform::CreateIdentity(); ///< Space the manipulator is in. - AZ::Vector3 m_nonUniformScale = AZ::Vector3::CreateOne(); ///< Handles non-uniform scale for the space the manipulator is in. + AZ::Transform m_space = AZ::Transform::CreateIdentity(); //!< Space the manipulator is in. + AZ::Vector3 m_nonUniformScale = AZ::Vector3::CreateOne(); //!< Handles non-uniform scale for the space the manipulator is in. }; - /// Handles location for manipulators which have a global space and a local position, but no local rotation. - class ManipulatorSpaceWithLocalPosition - : public ManipulatorSpace + //! Handles location for manipulators which have a global space and a local position, but no local rotation. + class ManipulatorSpaceWithLocalPosition : public ManipulatorSpace { public: AZ_TYPE_INFO(ManipulatorSpaceWithLocalPosition, "{47BE15AF-60A8-436B-8F3F-7DDFB97220E6}") @@ -52,12 +51,11 @@ namespace AzToolsFramework void SetLocalPosition(const AZ::Vector3& localPosition); private: - AZ::Vector3 m_localPosition = AZ::Vector3::CreateZero(); ///< Position in local space. + AZ::Vector3 m_localPosition = AZ::Vector3::CreateZero(); //!< Position in local space. }; - /// Handles location for manipulators which have a global space and a local transform (position and rotation). - class ManipulatorSpaceWithLocalTransform - : public ManipulatorSpace + //! Handles location for manipulators which have a global space and a local transform (position and rotation). + class ManipulatorSpaceWithLocalTransform : public ManipulatorSpace { public: AZ_TYPE_INFO(ManipulatorSpaceWithLocalTransform, "{6D100797-1DD8-45B0-A21C-8893B770C0BC}") @@ -72,6 +70,6 @@ namespace AzToolsFramework void SetLocalOrientation(const AZ::Quaternion& localOrientation); private: - AZ::Transform m_localTransform = AZ::Transform::CreateIdentity(); ///< Local transform. + AZ::Transform m_localTransform = AZ::Transform::CreateIdentity(); //!< Local transform. }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorView.cpp index 150e23041d..a4c4a59ee6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorView.cpp @@ -1,26 +1,26 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "ManipulatorView.h" -#include #include +#include #include #include #include -#include #include -#include #include +#include +#include #include #include #include @@ -33,8 +33,7 @@ namespace AzToolsFramework AZ::Transform WorldFromLocalWithUniformScale(const AZ::EntityId entityId) { AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity(); - AZ::TransformBus::EventResult( - worldFromLocal, entityId, &AZ::TransformBus::Events::GetWorldTM); + AZ::TransformBus::EventResult(worldFromLocal, entityId, &AZ::TransformBus::Events::GetWorldTM); return TransformUniformScale(worldFromLocal); } @@ -56,13 +55,16 @@ namespace AzToolsFramework return AzToolsFramework::TransformDirectionNoScaling(m_worldFromLocal, direction); } - /// Take into account the location of the camera and orientate the axis so it faces the camera. - /// if we did correct the camera (shouldCorrect is true) then we know the axis facing us it negative. - /// we can use this to change the rendering for a flipped axis if we wish. + // Take into account the location of the camera and orientate the axis so it faces the camera. + // if we did correct the camera (shouldCorrect is true) then we know the axis facing us it negative. + // we can use this to change the rendering for a flipped axis if we wish. static void CameraCorrectAxis( - const AZ::Vector3& axis, AZ::Vector3& correctedAxis, const ManipulatorManagerState& managerState, + const AZ::Vector3& axis, + AZ::Vector3& correctedAxis, + const ManipulatorManagerState& managerState, const ViewportInteraction::MouseInteraction& mouseInteraction, - const AZ::Transform& worldFromLocal, const AZ::Vector3& localPosition, + const AZ::Transform& worldFromLocal, + const AZ::Vector3& localPosition, const AzFramework::CameraState& cameraState, bool* shouldCorrect = nullptr) { @@ -74,9 +76,7 @@ namespace AzToolsFramework const bool correcting = ShouldFlipCameraAxis(worldFromLocal, localPosition, axis, cameraState); // the corrected axis, if no flip was required, output == input - correctedAxis = correcting - ? -axis - : axis; + correctedAxis = correcting ? -axis : axis; // optional out ref to use if we care about the result if (shouldCorrect) @@ -86,10 +86,13 @@ namespace AzToolsFramework } } - /// Calculate quad bound in world space. + // calculate quad bound in world space. static Picking::BoundShapeQuad CalculateQuadBound( - const AZ::Vector3& localPosition, const ManipulatorState& manipulatorState, - const AZ::Vector3& axis1, const AZ::Vector3& axis2, const float size) + const AZ::Vector3& localPosition, + const ManipulatorState& manipulatorState, + const AZ::Vector3& axis1, + const AZ::Vector3& axis2, + const float size) { const AZ::Vector3 worldPosition = manipulatorState.TransformPoint(localPosition); const AZ::Vector3 endAxis1World = manipulatorState.TransformDirectionNoScaling(axis1) * size; @@ -104,8 +107,10 @@ namespace AzToolsFramework } static Picking::BoundShapeQuad CalculateQuadBoundBillboard( - const AZ::Vector3& localPosition, const AZ::Transform& worldFromLocal, - const float size, const AzFramework::CameraState& cameraState) + const AZ::Vector3& localPosition, + const AZ::Transform& worldFromLocal, + const float size, + const AzFramework::CameraState& cameraState) { const AZ::Vector3 worldPosition = worldFromLocal.TransformPoint(localPosition); @@ -117,10 +122,13 @@ namespace AzToolsFramework return quadBound; } - /// Calculate line bound in world space (axis and length). + // calculate line bound in world space (axis and length). static Picking::BoundShapeLineSegment CalculateLineBound( - const AZ::Vector3& localPosition, const AZ::Transform& worldFromLocal, - const AZ::Vector3& axis, const float length, const float width) + const AZ::Vector3& localPosition, + const AZ::Transform& worldFromLocal, + const AZ::Vector3& axis, + const float length, + const float width) { Picking::BoundShapeLineSegment lineBound; lineBound.m_start = worldFromLocal.TransformPoint(localPosition); @@ -129,7 +137,7 @@ namespace AzToolsFramework return lineBound; } - /// Calculate line bound in world space (start and end point). + // calculate line bound in world space (start and end point). static Picking::BoundShapeLineSegment CalculateLineBound( const AZ::Vector3& localStartPosition, const AZ::Vector3& localEndPosition, @@ -143,10 +151,14 @@ namespace AzToolsFramework return lineBound; } - /// Calculate cone bound in world space. + // calculate cone bound in world space. static Picking::BoundShapeCone CalculateConeBound( - const AZ::Vector3& localPosition, const AZ::Transform& worldFromLocal, - const AZ::Vector3& axis, const AZ::Vector3& offset, const float length, const float radius) + const AZ::Vector3& localPosition, + const AZ::Transform& worldFromLocal, + const AZ::Vector3& axis, + const AZ::Vector3& offset, + const float length, + const float radius) { Picking::BoundShapeCone coneBound; coneBound.m_radius = radius; @@ -156,10 +168,13 @@ namespace AzToolsFramework return coneBound; } - /// Calculate box bound in world space. + // calculate box bound in world space. static Picking::BoundShapeBox CalculateBoxBound( - const AZ::Vector3& localPosition, const AZ::Transform& worldFromLocal, - const AZ::Quaternion& orientation, const AZ::Vector3& offset, const AZ::Vector3& halfExtents) + const AZ::Vector3& localPosition, + const AZ::Transform& worldFromLocal, + const AZ::Quaternion& orientation, + const AZ::Vector3& offset, + const AZ::Vector3& halfExtents) { Picking::BoundShapeBox boxBound; boxBound.m_halfExtents = halfExtents; @@ -168,10 +183,13 @@ namespace AzToolsFramework return boxBound; } - /// Calculate cylinder bound in world space. + // calculate cylinder bound in world space. static Picking::BoundShapeCylinder CalculateCylinderBound( - const AZ::Vector3& localPosition, const AZ::Transform& worldFromLocal, - const AZ::Vector3& axis, const float length, const float radius) + const AZ::Vector3& localPosition, + const AZ::Transform& worldFromLocal, + const AZ::Vector3& axis, + const float length, + const float radius) { Picking::BoundShapeCylinder boxBound; boxBound.m_base = worldFromLocal.TransformPoint(localPosition); @@ -181,10 +199,9 @@ namespace AzToolsFramework return boxBound; } - /// Calculate sphere bound in world space. + // calculate sphere bound in world space. static Picking::BoundShapeSphere CalculateSphereBound( - const AZ::Vector3& localPosition, const ManipulatorState& manipulatorState, - const float radius) + const AZ::Vector3& localPosition, const ManipulatorState& manipulatorState, const float radius) { Picking::BoundShapeSphere sphereBound; sphereBound.m_center = manipulatorState.TransformPoint(localPosition); @@ -192,10 +209,13 @@ namespace AzToolsFramework return sphereBound; } - /// Calculate torus bound in world space. + // calculate torus bound in world space. static Picking::BoundShapeTorus CalculateTorusBound( - const AZ::Vector3& localPosition, const AZ::Transform& worldFromLocal, - const AZ::Vector3& axis, const float radius, const float width) + const AZ::Vector3& localPosition, + const AZ::Transform& worldFromLocal, + const AZ::Vector3& axis, + const float radius, + const float width) { Picking::BoundShapeTorus torusBound; torusBound.m_center = worldFromLocal.TransformPoint(localPosition); @@ -205,7 +225,7 @@ namespace AzToolsFramework return torusBound; } - /// Calculate spline bound in world space. + // calculate spline bound in world space. static Picking::BoundShapeSpline CalculateSplineBound( const AZStd::weak_ptr& spline, const AZ::Transform& worldFromLocal, const float width) { @@ -224,8 +244,7 @@ namespace AzToolsFramework return lineWidth[mouseOver]; } - static AZ::Color ViewColor( - const bool mouseOver, const AZ::Color& defaultColor, const AZ::Color& mouseOverColor) + static AZ::Color ViewColor(const bool mouseOver, const AZ::Color& defaultColor, const AZ::Color& mouseOverColor) { const AZStd::array viewColor = { { defaultColor, mouseOverColor } }; return viewColor[mouseOver].GetAsVector4(); @@ -250,19 +269,16 @@ namespace AzToolsFramework void ManipulatorView::SetBoundDirty(const ManipulatorManagerId managerId) { - ManipulatorManagerRequestBus::Event( - managerId, &ManipulatorManagerRequestBus::Events::SetBoundDirty, m_boundId); + ManipulatorManagerRequestBus::Event(managerId, &ManipulatorManagerRequestBus::Events::SetBoundDirty, m_boundId); m_boundDirty = true; } void ManipulatorView::RefreshBound( - const ManipulatorManagerId managerId, const ManipulatorId manipulatorId, - const Picking::BoundRequestShapeBase& bound) + const ManipulatorManagerId managerId, const ManipulatorId manipulatorId, const Picking::BoundRequestShapeBase& bound) { ManipulatorManagerRequestBus::EventResult( - m_boundId, managerId, &ManipulatorManagerRequestBus::Events::UpdateBound, - manipulatorId, m_boundId, bound); + m_boundId, managerId, &ManipulatorManagerRequestBus::Events::UpdateBound, manipulatorId, m_boundId, bound); // store the manager id if we know the bound has been registered m_managerId = managerId; @@ -271,8 +287,7 @@ namespace AzToolsFramework } void ManipulatorView::RefreshBoundInternal( - const ManipulatorManagerId managerId, const ManipulatorId manipulatorId, - const Picking::BoundRequestShapeBase& bound) + const ManipulatorManagerId managerId, const ManipulatorId manipulatorId, const Picking::BoundRequestShapeBase& bound) { // update the manipulator's bounds if necessary // if m_screenSizeFixed is true, any camera movement can potentially change the size @@ -287,8 +302,7 @@ namespace AzToolsFramework { if (m_boundId != Picking::InvalidBoundId) { - ManipulatorManagerRequestBus::Event( - managerId, &ManipulatorManagerRequestBus::Events::DeleteManipulatorBound, m_boundId); + ManipulatorManagerRequestBus::Event(managerId, &ManipulatorManagerRequestBus::Events::DeleteManipulatorBound, m_boundId); m_boundId = Picking::InvalidBoundId; } @@ -297,33 +311,34 @@ namespace AzToolsFramework float ManipulatorView::ManipulatorViewScaleMultiplier( const AZ::Vector3& worldPosition, const AzFramework::CameraState& cameraState) const { - return ScreenSizeFixed() - ? CalculateScreenToWorldMultiplier(worldPosition, cameraState) - : 1.0f; + return ScreenSizeFixed() ? CalculateScreenToWorldMultiplier(worldPosition, cameraState) : 1.0f; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ManipulatorViewQuad::Draw( - const ManipulatorManagerId managerId, const ManipulatorManagerState& managerState, - const ManipulatorId manipulatorId, const ManipulatorState& manipulatorState, - AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, + const ManipulatorManagerId managerId, + const ManipulatorManagerState& managerState, + const ManipulatorId manipulatorId, + const ManipulatorState& manipulatorState, + AzFramework::DebugDisplayRequests& debugDisplay, + const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& mouseInteraction) { const AZ::Vector3 axis1 = m_axis1; const AZ::Vector3 axis2 = m_axis2; CameraCorrectAxis( - axis1, m_cameraCorrectedAxis1, managerState, mouseInteraction, - manipulatorState.m_worldFromLocal, manipulatorState.m_localPosition, cameraState); + axis1, m_cameraCorrectedAxis1, managerState, mouseInteraction, manipulatorState.m_worldFromLocal, + manipulatorState.m_localPosition, cameraState); CameraCorrectAxis( - axis2, m_cameraCorrectedAxis2, managerState, mouseInteraction, - manipulatorState.m_worldFromLocal, manipulatorState.m_localPosition, cameraState); + axis2, m_cameraCorrectedAxis2, managerState, mouseInteraction, manipulatorState.m_worldFromLocal, + manipulatorState.m_localPosition, cameraState); - const Picking::BoundShapeQuad quadBound = - CalculateQuadBound( - manipulatorState.m_localPosition, manipulatorState, m_cameraCorrectedAxis1, m_cameraCorrectedAxis2, - m_size * ManipulatorViewScaleMultiplier( + const Picking::BoundShapeQuad quadBound = CalculateQuadBound( + manipulatorState.m_localPosition, manipulatorState, m_cameraCorrectedAxis1, m_cameraCorrectedAxis2, + m_size * + ManipulatorViewScaleMultiplier( manipulatorState.m_worldFromLocal.TransformPoint(manipulatorState.m_localPosition), cameraState)); debugDisplay.SetLineWidth(defaultLineWidth(manipulatorState.m_mouseOver)); @@ -339,9 +354,7 @@ namespace AzToolsFramework debugDisplay.SetColor(Vector3ToVector4(m_mouseOverColor.GetAsVector3(), 0.5f)); debugDisplay.CullOff(); - debugDisplay.DrawQuad( - quadBound.m_corner1, quadBound.m_corner2, - quadBound.m_corner3, quadBound.m_corner4); + debugDisplay.DrawQuad(quadBound.m_corner1, quadBound.m_corner2, quadBound.m_corner3, quadBound.m_corner4); debugDisplay.CullOn(); } @@ -349,41 +362,46 @@ namespace AzToolsFramework } void ManipulatorViewQuadBillboard::Draw( - const ManipulatorManagerId managerId, const ManipulatorManagerState& /*managerState*/, - const ManipulatorId manipulatorId, const ManipulatorState& manipulatorState, - AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, + const ManipulatorManagerId managerId, + const ManipulatorManagerState& /*managerState*/, + const ManipulatorId manipulatorId, + const ManipulatorState& manipulatorState, + AzFramework::DebugDisplayRequests& debugDisplay, + const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& /*mouseInteraction*/) { - const Picking::BoundShapeQuad quadBound = - CalculateQuadBoundBillboard(manipulatorState.m_localPosition, manipulatorState.m_worldFromLocal, - m_size * ManipulatorViewScaleMultiplier( - manipulatorState.m_worldFromLocal.TransformPoint(manipulatorState.m_localPosition), cameraState), cameraState); + const Picking::BoundShapeQuad quadBound = CalculateQuadBoundBillboard( + manipulatorState.m_localPosition, manipulatorState.m_worldFromLocal, + m_size * + ManipulatorViewScaleMultiplier( + manipulatorState.m_worldFromLocal.TransformPoint(manipulatorState.m_localPosition), cameraState), + cameraState); debugDisplay.SetColor(ViewColor(manipulatorState.m_mouseOver, m_color, m_mouseOverColor).GetAsVector4()); - debugDisplay.DrawQuad( - quadBound.m_corner1, quadBound.m_corner2, - quadBound.m_corner3, quadBound.m_corner4); + debugDisplay.DrawQuad(quadBound.m_corner1, quadBound.m_corner2, quadBound.m_corner3, quadBound.m_corner4); RefreshBoundInternal(managerId, manipulatorId, quadBound); } void ManipulatorViewLine::Draw( - const ManipulatorManagerId managerId, const ManipulatorManagerState& managerState, - const ManipulatorId manipulatorId, const ManipulatorState& manipulatorState, - AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, + const ManipulatorManagerId managerId, + const ManipulatorManagerState& managerState, + const ManipulatorId manipulatorId, + const ManipulatorState& manipulatorState, + AzFramework::DebugDisplayRequests& debugDisplay, + const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& mouseInteraction) { - const float viewScale = ManipulatorViewScaleMultiplier( - manipulatorState.m_worldFromLocal.TransformPoint(manipulatorState.m_localPosition), cameraState); + const float viewScale = + ManipulatorViewScaleMultiplier(manipulatorState.m_worldFromLocal.TransformPoint(manipulatorState.m_localPosition), cameraState); CameraCorrectAxis( - m_axis, m_cameraCorrectedAxis, managerState, mouseInteraction, - manipulatorState.m_worldFromLocal, manipulatorState.m_localPosition, cameraState); + m_axis, m_cameraCorrectedAxis, managerState, mouseInteraction, manipulatorState.m_worldFromLocal, + manipulatorState.m_localPosition, cameraState); - const Picking::BoundShapeLineSegment lineBound = - CalculateLineBound( - manipulatorState.m_localPosition, manipulatorState.m_worldFromLocal, - m_cameraCorrectedAxis, m_length * viewScale, m_width * viewScale); + const Picking::BoundShapeLineSegment lineBound = CalculateLineBound( + manipulatorState.m_localPosition, manipulatorState.m_worldFromLocal, m_cameraCorrectedAxis, m_length * viewScale, + m_width * viewScale); debugDisplay.SetColor(ViewColor(manipulatorState.m_mouseOver, m_color, m_mouseOverColor).GetAsVector4()); debugDisplay.SetLineWidth(defaultLineWidth(manipulatorState.m_mouseOver)); @@ -393,13 +411,16 @@ namespace AzToolsFramework } void ManipulatorViewLineSelect::Draw( - const ManipulatorManagerId managerId, const ManipulatorManagerState& /*managerState*/, - const ManipulatorId manipulatorId, const ManipulatorState& manipulatorState, - AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, + const ManipulatorManagerId managerId, + const ManipulatorManagerState& /*managerState*/, + const ManipulatorId manipulatorId, + const ManipulatorState& manipulatorState, + AzFramework::DebugDisplayRequests& debugDisplay, + const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& mouseInteraction) { - const float viewScale = ManipulatorViewScaleMultiplier( - manipulatorState.m_worldFromLocal.TransformPoint(manipulatorState.m_localPosition), cameraState); + const float viewScale = + ManipulatorViewScaleMultiplier(manipulatorState.m_worldFromLocal.TransformPoint(manipulatorState.m_localPosition), cameraState); const Picking::BoundShapeLineSegment lineBound = CalculateLineBound(m_localStart, m_localEnd, manipulatorState, m_width * viewScale); @@ -407,44 +428,42 @@ namespace AzToolsFramework if (manipulatorState.m_mouseOver) { const LineSegmentSelectionManipulator::Action action = CalculateManipulationDataAction( - manipulatorState.m_worldFromLocal, manipulatorState.m_nonUniformScale, - mouseInteraction.m_mousePick.m_rayOrigin, mouseInteraction.m_mousePick.m_rayDirection, - cameraState.m_farClip, m_localStart, m_localEnd); + manipulatorState.m_worldFromLocal, manipulatorState.m_nonUniformScale, mouseInteraction.m_mousePick.m_rayOrigin, + mouseInteraction.m_mousePick.m_rayDirection, cameraState.m_farClip, m_localStart, m_localEnd); const AZ::Vector3 worldLineHitPosition = manipulatorState.TransformPoint(action.m_localLineHitPosition); debugDisplay.SetColor(AZ::Vector4(0.0f, 1.0f, 0.0f, 1.0f)); debugDisplay.DrawBall( - worldLineHitPosition, ManipulatorViewScaleMultiplier(worldLineHitPosition, cameraState) - * g_defaultManipulatorSphereRadius, false); + worldLineHitPosition, ManipulatorViewScaleMultiplier(worldLineHitPosition, cameraState) * g_defaultManipulatorSphereRadius, + false); } RefreshBoundInternal(managerId, manipulatorId, lineBound); } void ManipulatorViewCone::Draw( - const ManipulatorManagerId managerId, const ManipulatorManagerState& managerState, - const ManipulatorId manipulatorId, const ManipulatorState& manipulatorState, - AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, + const ManipulatorManagerId managerId, + const ManipulatorManagerState& managerState, + const ManipulatorId manipulatorId, + const ManipulatorState& manipulatorState, + AzFramework::DebugDisplayRequests& debugDisplay, + const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& mouseInteraction) { - const float viewScale = ManipulatorViewScaleMultiplier( - manipulatorState.m_worldFromLocal.TransformPoint(manipulatorState.m_localPosition), cameraState); + const float viewScale = + ManipulatorViewScaleMultiplier(manipulatorState.m_worldFromLocal.TransformPoint(manipulatorState.m_localPosition), cameraState); CameraCorrectAxis( - m_axis, m_cameraCorrectedAxis, managerState, mouseInteraction, - manipulatorState.m_worldFromLocal, manipulatorState.m_localPosition, - cameraState, &m_shouldCorrect); + m_axis, m_cameraCorrectedAxis, managerState, mouseInteraction, manipulatorState.m_worldFromLocal, + manipulatorState.m_localPosition, cameraState, &m_shouldCorrect); CameraCorrectAxis( - m_offset, m_cameraCorrectedOffset, managerState, mouseInteraction, - manipulatorState.m_worldFromLocal, manipulatorState.m_localPosition, cameraState); + m_offset, m_cameraCorrectedOffset, managerState, mouseInteraction, manipulatorState.m_worldFromLocal, + manipulatorState.m_localPosition, cameraState); - const Picking::BoundShapeCone coneBound = - CalculateConeBound( - manipulatorState.m_localPosition, manipulatorState.m_worldFromLocal, m_cameraCorrectedAxis, - m_cameraCorrectedOffset * viewScale, - m_length * viewScale, - m_radius * viewScale); + const Picking::BoundShapeCone coneBound = CalculateConeBound( + manipulatorState.m_localPosition, manipulatorState.m_worldFromLocal, m_cameraCorrectedAxis, m_cameraCorrectedOffset * viewScale, + m_length * viewScale, m_radius * viewScale); debugDisplay.SetColor(ViewColor(manipulatorState.m_mouseOver, m_color, m_mouseOverColor).GetAsVector4()); if (m_shouldCorrect) @@ -460,73 +479,77 @@ namespace AzToolsFramework } void ManipulatorViewBox::Draw( - const ManipulatorManagerId managerId, const ManipulatorManagerState& managerState, - const ManipulatorId manipulatorId, const ManipulatorState& manipulatorState, - AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, + const ManipulatorManagerId managerId, + const ManipulatorManagerState& managerState, + const ManipulatorId manipulatorId, + const ManipulatorState& manipulatorState, + AzFramework::DebugDisplayRequests& debugDisplay, + const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& mouseInteraction) { - const float viewScale = ManipulatorViewScaleMultiplier( - manipulatorState.m_worldFromLocal.TransformPoint(manipulatorState.m_localPosition), cameraState); + const float viewScale = + ManipulatorViewScaleMultiplier(manipulatorState.m_worldFromLocal.TransformPoint(manipulatorState.m_localPosition), cameraState); const AZ::Quaternion orientation = m_orientation; CameraCorrectAxis( - m_offset, m_cameraCorrectedOffset, managerState, mouseInteraction, - manipulatorState.m_worldFromLocal, manipulatorState.m_localPosition, - cameraState); + m_offset, m_cameraCorrectedOffset, managerState, mouseInteraction, manipulatorState.m_worldFromLocal, + manipulatorState.m_localPosition, cameraState); - const Picking::BoundShapeBox boxBound = - CalculateBoxBound(manipulatorState.m_localPosition, manipulatorState.m_worldFromLocal, orientation, - m_cameraCorrectedOffset * viewScale, - m_halfExtents * viewScale); + const Picking::BoundShapeBox boxBound = CalculateBoxBound( + manipulatorState.m_localPosition, manipulatorState.m_worldFromLocal, orientation, m_cameraCorrectedOffset * viewScale, + m_halfExtents * viewScale); const AZ::Vector3 xAxis = boxBound.m_orientation.TransformVector(AZ::Vector3::CreateAxisX()); const AZ::Vector3 yAxis = boxBound.m_orientation.TransformVector(AZ::Vector3::CreateAxisY()); const AZ::Vector3 zAxis = boxBound.m_orientation.TransformVector(AZ::Vector3::CreateAxisZ()); debugDisplay.SetColor(ViewColor(manipulatorState.m_mouseOver, m_color, m_mouseOverColor).GetAsVector4()); - debugDisplay.DrawSolidOBB(boxBound.m_center, - xAxis, yAxis, zAxis, boxBound.m_halfExtents); + debugDisplay.DrawSolidOBB(boxBound.m_center, xAxis, yAxis, zAxis, boxBound.m_halfExtents); RefreshBoundInternal(managerId, manipulatorId, boxBound); } void ManipulatorViewCylinder::Draw( - const ManipulatorManagerId managerId, const ManipulatorManagerState& managerState, - const ManipulatorId manipulatorId, const ManipulatorState& manipulatorState, - AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, + const ManipulatorManagerId managerId, + const ManipulatorManagerState& managerState, + const ManipulatorId manipulatorId, + const ManipulatorState& manipulatorState, + AzFramework::DebugDisplayRequests& debugDisplay, + const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& mouseInteraction) { - const float viewScale = ManipulatorViewScaleMultiplier( - manipulatorState.m_worldFromLocal.TransformPoint(manipulatorState.m_localPosition), cameraState); + const float viewScale = + ManipulatorViewScaleMultiplier(manipulatorState.m_worldFromLocal.TransformPoint(manipulatorState.m_localPosition), cameraState); CameraCorrectAxis( - m_axis, m_cameraCorrectedAxis, managerState, mouseInteraction, - manipulatorState.m_worldFromLocal, manipulatorState.m_localPosition, cameraState); + m_axis, m_cameraCorrectedAxis, managerState, mouseInteraction, manipulatorState.m_worldFromLocal, + manipulatorState.m_localPosition, cameraState); - const Picking::BoundShapeCylinder cylinderBound = - CalculateCylinderBound( - manipulatorState.m_localPosition, manipulatorState.m_worldFromLocal, m_cameraCorrectedAxis, - m_length * viewScale, - m_radius * viewScale); + const Picking::BoundShapeCylinder cylinderBound = CalculateCylinderBound( + manipulatorState.m_localPosition, manipulatorState.m_worldFromLocal, m_cameraCorrectedAxis, m_length * viewScale, + m_radius * viewScale); debugDisplay.SetColor(ViewColor(manipulatorState.m_mouseOver, m_color, m_mouseOverColor).GetAsVector4()); - debugDisplay.DrawSolidCylinder(cylinderBound.m_base + cylinderBound.m_axis * cylinderBound.m_height * 0.5f, - cylinderBound.m_axis, cylinderBound.m_radius, cylinderBound.m_height, false); + debugDisplay.DrawSolidCylinder( + cylinderBound.m_base + cylinderBound.m_axis * cylinderBound.m_height * 0.5f, cylinderBound.m_axis, cylinderBound.m_radius, + cylinderBound.m_height, false); RefreshBoundInternal(managerId, manipulatorId, cylinderBound); } void ManipulatorViewSphere::Draw( - const ManipulatorManagerId managerId, const ManipulatorManagerState& /*managerState*/, - const ManipulatorId manipulatorId, const ManipulatorState& manipulatorState, - AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, + const ManipulatorManagerId managerId, + const ManipulatorManagerState& /*managerState*/, + const ManipulatorId manipulatorId, + const ManipulatorState& manipulatorState, + AzFramework::DebugDisplayRequests& debugDisplay, + const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& mouseInteraction) { - const Picking::BoundShapeSphere sphereBound = - CalculateSphereBound(manipulatorState.m_localPosition, manipulatorState, - m_radius * ManipulatorViewScaleMultiplier( - manipulatorState.TransformPoint(manipulatorState.m_localPosition), cameraState)); + const Picking::BoundShapeSphere sphereBound = CalculateSphereBound( + manipulatorState.m_localPosition, manipulatorState, + m_radius * ManipulatorViewScaleMultiplier(manipulatorState.TransformPoint(manipulatorState.m_localPosition), cameraState)); if (m_depthTest) { @@ -545,31 +568,32 @@ namespace AzToolsFramework } void ManipulatorViewCircle::Draw( - const ManipulatorManagerId managerId, const ManipulatorManagerState& /*managerState*/, - const ManipulatorId manipulatorId, const ManipulatorState& manipulatorState, - AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, + const ManipulatorManagerId managerId, + const ManipulatorManagerState& /*managerState*/, + const ManipulatorId manipulatorId, + const ManipulatorState& manipulatorState, + AzFramework::DebugDisplayRequests& debugDisplay, + const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& /*mouseInteraction*/) { - const float viewScale = ManipulatorViewScaleMultiplier( - manipulatorState.m_worldFromLocal.TransformPoint(manipulatorState.m_localPosition), cameraState); + const float viewScale = + ManipulatorViewScaleMultiplier(manipulatorState.m_worldFromLocal.TransformPoint(manipulatorState.m_localPosition), cameraState); - const Picking::BoundShapeTorus torusBound = - CalculateTorusBound( - manipulatorState.m_localPosition, manipulatorState.m_worldFromLocal, m_axis, - m_radius * viewScale, - m_width * viewScale); + const Picking::BoundShapeTorus torusBound = CalculateTorusBound( + manipulatorState.m_localPosition, manipulatorState.m_worldFromLocal, m_axis, m_radius * viewScale, m_width * viewScale); // transform circle based on delta between default z up axis and other axes const AZ::Transform worldFromLocalWithOrientation = AZ::Transform::CreateTranslation(manipulatorState.m_worldFromLocal.GetTranslation()) * - AZ::Transform::CreateFromQuaternion( - (QuaternionFromTransformNoScaling(manipulatorState.m_worldFromLocal) * - AZ::Quaternion::CreateShortestArc(AZ::Vector3::CreateAxisZ(), m_axis)).GetNormalized()); + AZ::Transform::CreateFromQuaternion((QuaternionFromTransformNoScaling(manipulatorState.m_worldFromLocal) * + AZ::Quaternion::CreateShortestArc(AZ::Vector3::CreateAxisZ(), m_axis)) + .GetNormalized()); debugDisplay.CullOn(); debugDisplay.PushMatrix(worldFromLocalWithOrientation); debugDisplay.SetColor(ViewColor(manipulatorState.m_mouseOver, m_color, m_mouseOverColor).GetAsVector4()); - m_drawCircleFunc(debugDisplay, manipulatorState.m_localPosition, torusBound.m_majorRadius, + m_drawCircleFunc( + debugDisplay, manipulatorState.m_localPosition, torusBound.m_majorRadius, worldFromLocalWithOrientation.GetInverse().TransformPoint(cameraState.m_position)); debugDisplay.PopMatrix(); debugDisplay.CullOff(); @@ -578,27 +602,28 @@ namespace AzToolsFramework } void DrawHalfDottedCircle( - AzFramework::DebugDisplayRequests& debugDisplay, const AZ::Vector3& position, - const float radius, const AZ::Vector3& viewPos) + AzFramework::DebugDisplayRequests& debugDisplay, const AZ::Vector3& position, const float radius, const AZ::Vector3& viewPos) { debugDisplay.DrawHalfDottedCircle(position, radius, viewPos); } void DrawFullCircle( - AzFramework::DebugDisplayRequests& debugDisplay, const AZ::Vector3& position, - const float radius, const AZ::Vector3& /*viewPos*/) + AzFramework::DebugDisplayRequests& debugDisplay, const AZ::Vector3& position, const float radius, const AZ::Vector3& /*viewPos*/) { - debugDisplay.DrawCircle(position, radius); + debugDisplay.DrawCircle(position, radius); } void ManipulatorViewSplineSelect::Draw( - const ManipulatorManagerId managerId, const ManipulatorManagerState& /*managerState*/, - const ManipulatorId manipulatorId, const ManipulatorState& manipulatorState, - AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, + const ManipulatorManagerId managerId, + const ManipulatorManagerState& /*managerState*/, + const ManipulatorId manipulatorId, + const ManipulatorState& manipulatorState, + AzFramework::DebugDisplayRequests& debugDisplay, + const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& mouseInteraction) { - const float viewScale = ManipulatorViewScaleMultiplier( - manipulatorState.m_worldFromLocal.TransformPoint(manipulatorState.m_localPosition), cameraState); + const float viewScale = + ManipulatorViewScaleMultiplier(manipulatorState.m_worldFromLocal.TransformPoint(manipulatorState.m_localPosition), cameraState); const Picking::BoundShapeSpline splineBound = CalculateSplineBound(m_spline, manipulatorState.m_worldFromLocal, m_width * viewScale); @@ -606,16 +631,15 @@ namespace AzToolsFramework if (manipulatorState.m_mouseOver) { const SplineSelectionManipulator::Action action = CalculateManipulationDataAction( - manipulatorState.m_worldFromLocal, mouseInteraction.m_mousePick.m_rayOrigin, - mouseInteraction.m_mousePick.m_rayDirection, m_spline); + manipulatorState.m_worldFromLocal, mouseInteraction.m_mousePick.m_rayOrigin, mouseInteraction.m_mousePick.m_rayDirection, + m_spline); - const AZ::Vector3 worldSplineHitPosition = - manipulatorState.m_worldFromLocal.TransformPoint(action.m_localSplineHitPosition); + const AZ::Vector3 worldSplineHitPosition = manipulatorState.m_worldFromLocal.TransformPoint(action.m_localSplineHitPosition); debugDisplay.SetColor(m_color.GetAsVector4()); debugDisplay.DrawBall( - worldSplineHitPosition, ManipulatorViewScaleMultiplier(worldSplineHitPosition, cameraState) - * g_defaultManipulatorSphereRadius, false); + worldSplineHitPosition, + ManipulatorViewScaleMultiplier(worldSplineHitPosition, cameraState) * g_defaultManipulatorSphereRadius, false); } RefreshBoundInternal(managerId, manipulatorId, splineBound); @@ -624,8 +648,7 @@ namespace AzToolsFramework /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// AZStd::unique_ptr CreateManipulatorViewQuad( - const PlanarManipulator& planarManipulator, const AZ::Color& axis1Color, - const AZ::Color& axis2Color, const float size) + const PlanarManipulator& planarManipulator, const AZ::Color& axis1Color, const AZ::Color& axis2Color, const float size) { AZStd::unique_ptr viewQuad = AZStd::make_unique(); viewQuad->m_axis1 = planarManipulator.GetAxis1(); @@ -636,8 +659,7 @@ namespace AzToolsFramework return viewQuad; } - AZStd::unique_ptr CreateManipulatorViewQuadBillboard( - const AZ::Color& color, const float size) + AZStd::unique_ptr CreateManipulatorViewQuadBillboard(const AZ::Color& color, const float size) { AZStd::unique_ptr viewQuad = AZStd::make_unique(); viewQuad->m_size = size; @@ -646,8 +668,7 @@ namespace AzToolsFramework } AZStd::unique_ptr CreateManipulatorViewLine( - const LinearManipulator& linearManipulator, const AZ::Color& color, - const float length, const float width) + const LinearManipulator& linearManipulator, const AZ::Color& color, const float length, const float width) { AZStd::unique_ptr viewLine = AZStd::make_unique(); viewLine->m_axis = linearManipulator.GetAxis(); @@ -658,8 +679,7 @@ namespace AzToolsFramework } AZStd::unique_ptr CreateManipulatorViewLineSelect( - const LineSegmentSelectionManipulator& lineSegmentManipulator, - const AZ::Color& color, const float width) + const LineSegmentSelectionManipulator& lineSegmentManipulator, const AZ::Color& color, const float width) { AZStd::unique_ptr viewLineSelect = AZStd::make_unique(); viewLineSelect->m_localStart = lineSegmentManipulator.GetStart(); @@ -670,8 +690,11 @@ namespace AzToolsFramework } AZStd::unique_ptr CreateManipulatorViewCone( - const LinearManipulator& linearManipulator, const AZ::Color& color, - const AZ::Vector3& offset, const float length, const float radius) + const LinearManipulator& linearManipulator, + const AZ::Color& color, + const AZ::Vector3& offset, + const float length, + const float radius) { AZStd::unique_ptr viewCone = AZStd::make_unique(); viewCone->m_axis = linearManipulator.GetAxis(); @@ -683,8 +706,7 @@ namespace AzToolsFramework } AZStd::unique_ptr CreateManipulatorViewBox( - const AZ::Transform& transform, const AZ::Color& color, - const AZ::Vector3& offset, const AZ::Vector3& halfExtents) + const AZ::Transform& transform, const AZ::Color& color, const AZ::Vector3& offset, const AZ::Vector3& halfExtents) { AZStd::unique_ptr viewBox = AZStd::make_unique(); viewBox->m_orientation = transform.GetRotation(); @@ -695,8 +717,7 @@ namespace AzToolsFramework } AZStd::unique_ptr CreateManipulatorViewCylinder( - const LinearManipulator& linearManipulator, const AZ::Color& color, - const float length, const float radius) + const LinearManipulator& linearManipulator, const AZ::Color& color, const float length, const float radius) { AZStd::unique_ptr viewCylinder = AZStd::make_unique(); viewCylinder->m_axis = linearManipulator.GetAxis(); @@ -718,8 +739,11 @@ namespace AzToolsFramework } AZStd::unique_ptr CreateManipulatorViewCircle( - const AngularManipulator& angularManipulator, const AZ::Color& color, - const float radius, const float width, const ManipulatorViewCircle::DrawCircleFunc drawFunc) + const AngularManipulator& angularManipulator, + const AZ::Color& color, + const float radius, + const float width, + const ManipulatorViewCircle::DrawCircleFunc drawFunc) { AZStd::unique_ptr viewCircle = AZStd::make_unique(); viewCircle->m_axis = angularManipulator.GetAxis(); @@ -731,8 +755,7 @@ namespace AzToolsFramework } AZStd::unique_ptr CreateManipulatorViewSplineSelect( - const SplineSelectionManipulator& splineManipulator, - const AZ::Color& color, const float width) + const SplineSelectionManipulator& splineManipulator, const AZ::Color& color, const float width) { AZStd::unique_ptr viewSplineSelect = AZStd::make_unique(); viewSplineSelect->m_spline = splineManipulator.GetSpline(); @@ -741,16 +764,12 @@ namespace AzToolsFramework return viewSplineSelect; } - AZ::Vector3 CalculateViewDirection( - const Manipulators& manipulators, const AZ::Vector3& worldViewPosition) + AZ::Vector3 CalculateViewDirection(const Manipulators& manipulators, const AZ::Vector3& worldViewPosition) { - const AZ::Transform worldFromLocalWithTransform = - manipulators.GetSpace() * manipulators.GetLocalTransform(); + const AZ::Transform worldFromLocalWithTransform = manipulators.GetSpace() * manipulators.GetLocalTransform(); - AZ::Vector3 lookDirection = - (worldFromLocalWithTransform.GetTranslation() - worldViewPosition).GetNormalized(); + AZ::Vector3 lookDirection = (worldFromLocalWithTransform.GetTranslation() - worldViewPosition).GetNormalized(); - return TransformDirectionNoScaling( - worldFromLocalWithTransform.GetInverse(), lookDirection); + return TransformDirectionNoScaling(worldFromLocalWithTransform.GetInverse(), lookDirection); } } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorView.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorView.h index da426d2340..8573f8ce32 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorView.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorView.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -26,13 +26,12 @@ namespace AzToolsFramework class LineSegmentSelectionManipulator; class SplineSelectionManipulator; - using DecideColorFn = AZStd::function; + using DecideColorFn = + AZStd::function; extern const float g_defaultManipulatorSphereRadius; - /// State of an individual manipulator. + //! State of an individual manipulator. struct ManipulatorState { AZ::Transform m_worldFromLocal; @@ -40,18 +39,18 @@ namespace AzToolsFramework AZ::Vector3 m_localPosition; bool m_mouseOver; - /// Transforms a point, taking non-uniform scale into account. + //! Transforms a point, taking non-uniform scale into account. AZ::Vector3 TransformPoint(const AZ::Vector3& point) const; - /// Rotates a direction into the space of the manipulator and normalizes it. - /// Non-uniform scaling and translation are not applied. + //! Rotates a direction into the space of the manipulator and normalizes it. + //! Non-uniform scaling and translation are not applied. AZ::Vector3 TransformDirectionNoScaling(const AZ::Vector3& direction) const; }; - /// The base interface for the visual representation of manipulators. - /// The View represents the appearance and bounds of the manipulator for - /// the user to interact with. Any manipulator can have any view (some may - /// be more appropriate than others in certain cases). + //! The base interface for the visual representation of manipulators. + //! The View represents the appearance and bounds of the manipulator for + //! the user to interact with. Any manipulator can have any view (some may + //! be more appropriate than others in certain cases). class ManipulatorView { public: @@ -65,98 +64,107 @@ namespace AzToolsFramework ManipulatorView& operator=(ManipulatorView&&) = default; void SetBoundDirty(ManipulatorManagerId managerId); - void RefreshBound( - ManipulatorManagerId managerId, ManipulatorId manipulatorId, const Picking::BoundRequestShapeBase& bound); + void RefreshBound(ManipulatorManagerId managerId, ManipulatorId manipulatorId, const Picking::BoundRequestShapeBase& bound); void Invalidate(ManipulatorManagerId managerId); virtual void Draw( - ManipulatorManagerId managerId, const ManipulatorManagerState& managerState, - ManipulatorId manipulatorId, const ManipulatorState& manipulatorState, - AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, + ManipulatorManagerId managerId, + const ManipulatorManagerState& managerState, + ManipulatorId manipulatorId, + const ManipulatorState& manipulatorState, + AzFramework::DebugDisplayRequests& debugDisplay, + const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& mouseInteraction) = 0; - bool ScreenSizeFixed() const { return m_screenSizeFixed; } + bool ScreenSizeFixed() const + { + return m_screenSizeFixed; + } protected: - AZ::Color m_mouseOverColor = BaseManipulator::s_defaultMouseOverColor; ///< What color should the manipulator - ///< be when the mouse is hovering over it. - /// Scale the manipulator based on the distance - /// from the camera if m_screenSizeFixed is true. - float ManipulatorViewScaleMultiplier( - const AZ::Vector3& worldPosition, const AzFramework::CameraState& cameraState) const; + AZ::Color m_mouseOverColor = BaseManipulator::s_defaultMouseOverColor; //!< What color should the manipulator + //!< be when the mouse is hovering over it. + //! Scale the manipulator based on the distance + //! from the camera if m_screenSizeFixed is true. + float ManipulatorViewScaleMultiplier(const AZ::Vector3& worldPosition, const AzFramework::CameraState& cameraState) const; - /// Wrap the logic for updating a bound. - /// Should be called at the end of the Draw function once a concrete BoundRequestShape has - /// been created to use for dimensions for rendering. - void RefreshBoundInternal( - ManipulatorManagerId managerId, ManipulatorId manipulatorId, const Picking::BoundRequestShapeBase& bound); + //! Wrap the logic for updating a bound. + //! Should be called at the end of the Draw function once a concrete BoundRequestShape has + //! been created to use for dimensions for rendering. + void RefreshBoundInternal(ManipulatorManagerId managerId, ManipulatorId manipulatorId, const Picking::BoundRequestShapeBase& bound); private: - Picking::RegisteredBoundId m_boundId = Picking::InvalidBoundId; ///< Used for hit detection. - ManipulatorManagerId m_managerId = InvalidManipulatorManagerId; /// The manipulator manager this view has been registered with. - bool m_screenSizeFixed = true; ///< Should manipulator size be adjusted based on camera distance. - bool m_boundDirty = true; ///< Do the bounds need to be recalculated. + Picking::RegisteredBoundId m_boundId = Picking::InvalidBoundId; //!< Used for hit detection. + ManipulatorManagerId m_managerId = InvalidManipulatorManagerId; //! The manipulator manager this view has been registered with. + bool m_screenSizeFixed = true; //!< Should manipulator size be adjusted based on camera distance. + bool m_boundDirty = true; //!< Do the bounds need to be recalculated. }; // A collection of views (a manipulator may have 1 - * views) using ManipulatorViews = AZStd::vector>; - /// Display a quad representing part of a plane, rendered as 4 lines. - class ManipulatorViewQuad - : public ManipulatorView + //! Display a quad representing part of a plane, rendered as 4 lines. + class ManipulatorViewQuad : public ManipulatorView { public: AZ_CLASS_ALLOCATOR(ManipulatorViewQuad, AZ::SystemAllocator, 0) AZ_RTTI(ManipulatorViewQuad, "{D85E1B45-495E-4755-BCF2-6AE45F8BB2B0}", ManipulatorView) void Draw( - ManipulatorManagerId managerId, const ManipulatorManagerState& managerState, - ManipulatorId manipulatorId, const ManipulatorState& manipulatorState, - AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, + ManipulatorManagerId managerId, + const ManipulatorManagerState& managerState, + ManipulatorId manipulatorId, + const ManipulatorState& manipulatorState, + AzFramework::DebugDisplayRequests& debugDisplay, + const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& mouseInteraction) override; AZ::Vector3 m_axis1 = AZ::Vector3(1.0f, 0.0f, 0.0f); AZ::Vector3 m_axis2 = AZ::Vector3(0.0f, 1.0f, 0.0f); AZ::Color m_axis1Color = AZ::Color(1.0f, 0.0f, 0.0f, 1.0f); AZ::Color m_axis2Color = AZ::Color(1.0f, 0.0f, 0.0f, 1.0f); - float m_size = 0.06f; ///< size to render and do mouse ray intersection tests against. + float m_size = 0.06f; //!< size to render and do mouse ray intersection tests against. private: AZ::Vector3 m_cameraCorrectedAxis1; AZ::Vector3 m_cameraCorrectedAxis2; }; - /// A screen aligned quad, centered at the position of the manipulator, display filled. - class ManipulatorViewQuadBillboard - : public ManipulatorView + //! A screen aligned quad, centered at the position of the manipulator, display filled. + class ManipulatorViewQuadBillboard : public ManipulatorView { public: AZ_CLASS_ALLOCATOR(ManipulatorViewQuadBillboard, AZ::SystemAllocator, 0) AZ_RTTI(ManipulatorViewQuadBillboard, "{C205E967-E8C6-4A73-A31B-41EE5529B15B}", ManipulatorView) void Draw( - ManipulatorManagerId managerId, const ManipulatorManagerState& managerState, - ManipulatorId manipulatorId, const ManipulatorState& manipulatorState, - AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, + ManipulatorManagerId managerId, + const ManipulatorManagerState& managerState, + ManipulatorId manipulatorId, + const ManipulatorState& manipulatorState, + AzFramework::DebugDisplayRequests& debugDisplay, + const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& mouseInteraction) override; AZ::Color m_color = AZ::Color(1.0f, 0.0f, 0.0f, 1.0f); - float m_size = 0.005f; ///< size to render and do mouse ray intersection tests against. + float m_size = 0.005f; //!< size to render and do mouse ray intersection tests against. }; - /// Displays a debug style line starting from the manipulator's transform, - /// width determines the click area. - class ManipulatorViewLine - : public ManipulatorView + //! Displays a debug style line starting from the manipulator's transform, + //! width determines the click area. + class ManipulatorViewLine : public ManipulatorView { public: AZ_CLASS_ALLOCATOR(ManipulatorViewLine, AZ::SystemAllocator, 0) AZ_RTTI(ManipulatorViewLine, "{831EEF66-4A5C-450C-B152-EA4A0BC8A272}", ManipulatorView) void Draw( - ManipulatorManagerId managerId, const ManipulatorManagerState& managerState, - ManipulatorId manipulatorId, const ManipulatorState& manipulatorState, - AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, + ManipulatorManagerId managerId, + const ManipulatorManagerState& managerState, + ManipulatorId manipulatorId, + const ManipulatorState& manipulatorState, + AzFramework::DebugDisplayRequests& debugDisplay, + const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& mouseInteraction) override; AZ::Vector3 m_axis; @@ -168,19 +176,21 @@ namespace AzToolsFramework AZ::Vector3 m_cameraCorrectedAxis; }; - /// Variant of ManipulatorViewLine which instead of using an axis, provides begin and end - /// points for the line. Used for selection when inserting points along a line. - class ManipulatorViewLineSelect - : public ManipulatorView + //! Variant of ManipulatorViewLine which instead of using an axis, provides begin and end + //! points for the line. Used for selection when inserting points along a line. + class ManipulatorViewLineSelect : public ManipulatorView { public: AZ_CLASS_ALLOCATOR(ManipulatorViewLineSelect, AZ::SystemAllocator, 0) AZ_RTTI(ManipulatorViewLineSelect, "{BF26A947-91F8-4595-9A5B-481876EB2C48}", ManipulatorView) void Draw( - ManipulatorManagerId managerId, const ManipulatorManagerState& managerState, - ManipulatorId manipulatorId, const ManipulatorState& manipulatorState, - AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, + ManipulatorManagerId managerId, + const ManipulatorManagerState& managerState, + ManipulatorId manipulatorId, + const ManipulatorState& manipulatorState, + AzFramework::DebugDisplayRequests& debugDisplay, + const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& mouseInteraction) override; AZ::Vector3 m_localStart; @@ -189,20 +199,22 @@ namespace AzToolsFramework float m_width = 0.0f; }; - /// Displays a filled cone along the specified axis, offset is local translation from - /// the manipulator transform (often used in conjunction with other views to build - /// aggregate views such as arrows - e.g. a line and cone). - class ManipulatorViewCone - : public ManipulatorView + //! Displays a filled cone along the specified axis, offset is local translation from + //! the manipulator transform (often used in conjunction with other views to build + //! aggregate views such as arrows - e.g. a line and cone). + class ManipulatorViewCone : public ManipulatorView { public: AZ_CLASS_ALLOCATOR(ManipulatorViewCone, AZ::SystemAllocator, 0) AZ_RTTI(ManipulatorViewCone, "{BF042887-1F51-4FD8-8CA5-4A649B4AF356}", ManipulatorView) void Draw( - ManipulatorManagerId managerId, const ManipulatorManagerState& managerState, - ManipulatorId manipulatorId, const ManipulatorState& manipulatorState, - AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, + ManipulatorManagerId managerId, + const ManipulatorManagerState& managerState, + ManipulatorId manipulatorId, + const ManipulatorState& manipulatorState, + AzFramework::DebugDisplayRequests& debugDisplay, + const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& mouseInteraction) override; AZ::Vector3 m_offset; @@ -217,20 +229,22 @@ namespace AzToolsFramework bool m_shouldCorrect = false; }; - /// Displays a filled box, offset is local translation from the manipulator - /// transform, box is often used in conjunction with other views, orientation allows - /// the box to be orientated separately from the manipulator transform. - class ManipulatorViewBox - : public ManipulatorView + //! Displays a filled box, offset is local translation from the manipulator + //! transform, box is often used in conjunction with other views, orientation allows + //! the box to be orientated separately from the manipulator transform. + class ManipulatorViewBox : public ManipulatorView { public: AZ_CLASS_ALLOCATOR(ManipulatorViewBox, AZ::SystemAllocator, 0) AZ_RTTI(ManipulatorViewBox, "{2D082201-7878-4C1B-A3DD-7A629E5AD598}", ManipulatorView) void Draw( - ManipulatorManagerId managerId, const ManipulatorManagerState& managerState, - ManipulatorId manipulatorId, const ManipulatorState& manipulatorState, - AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, + ManipulatorManagerId managerId, + const ManipulatorManagerState& managerState, + ManipulatorId manipulatorId, + const ManipulatorState& manipulatorState, + AzFramework::DebugDisplayRequests& debugDisplay, + const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& mouseInteraction) override; AZ::Vector3 m_offset; @@ -242,18 +256,20 @@ namespace AzToolsFramework AZ::Vector3 m_cameraCorrectedOffset; }; - /// Displays a filled cylinder along the axis provided. - class ManipulatorViewCylinder - : public ManipulatorView + //! Displays a filled cylinder along the axis provided. + class ManipulatorViewCylinder : public ManipulatorView { public: AZ_CLASS_ALLOCATOR(ManipulatorViewCylinder, AZ::SystemAllocator, 0) AZ_RTTI(ManipulatorViewCylinder, "{9B8E5EF4-0F85-4CD0-A5FF-3C7097DF58AC}", ManipulatorView) void Draw( - ManipulatorManagerId managerId, const ManipulatorManagerState& managerState, - ManipulatorId manipulatorId, const ManipulatorState& manipulatorState, - AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, + ManipulatorManagerId managerId, + const ManipulatorManagerState& managerState, + ManipulatorId manipulatorId, + const ManipulatorState& manipulatorState, + AzFramework::DebugDisplayRequests& debugDisplay, + const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& mouseInteraction) override; AZ::Vector3 m_axis; @@ -265,20 +281,22 @@ namespace AzToolsFramework AZ::Vector3 m_cameraCorrectedAxis; }; - /// Displays a filled sphere at the transform of the manipulator, often used as - /// a selection manipulator. DecideColorFn allows more complex logic to be used - /// to decide the color of the manipulator (based on hover state etc.) - class ManipulatorViewSphere - : public ManipulatorView + //! Displays a filled sphere at the transform of the manipulator, often used as + //! a selection manipulator. DecideColorFn allows more complex logic to be used + //! to decide the color of the manipulator (based on hover state etc.) + class ManipulatorViewSphere : public ManipulatorView { public: AZ_CLASS_ALLOCATOR(ManipulatorViewSphere, AZ::SystemAllocator, 0) AZ_RTTI(ManipulatorViewSphere, "{324D8329-6E7B-4A5D-AC8A-8C0E1C984E38}", ManipulatorView) void Draw( - ManipulatorManagerId managerId, const ManipulatorManagerState& managerState, - ManipulatorId manipulatorId, const ManipulatorState& manipulatorState, - AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, + ManipulatorManagerId managerId, + const ManipulatorManagerState& managerState, + ManipulatorId manipulatorId, + const ManipulatorState& manipulatorState, + AzFramework::DebugDisplayRequests& debugDisplay, + const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& mouseInteraction) override; float m_radius = 0.0f; @@ -287,23 +305,24 @@ namespace AzToolsFramework bool m_depthTest = false; }; - /// Displays a wire circle. DrawCircleFunc can be used to either draw a full - /// circle or a half dotted circle where the part of the circle facing away - /// from the camera is dotted (useful for angular/rotation manipulators). - class ManipulatorViewCircle - : public ManipulatorView + //! Displays a wire circle. DrawCircleFunc can be used to either draw a full + //! circle or a half dotted circle where the part of the circle facing away + //! from the camera is dotted (useful for angular/rotation manipulators). + class ManipulatorViewCircle : public ManipulatorView { public: AZ_CLASS_ALLOCATOR(ManipulatorViewCircle, AZ::SystemAllocator, 0) AZ_RTTI(ManipulatorViewCircle, "{26563A03-3E48-49EB-9DCF-30EE4F567FCD}", ManipulatorView) - using DrawCircleFunc = - void(*)(AzFramework::DebugDisplayRequests&, const AZ::Vector3&, float, const AZ::Vector3&); + using DrawCircleFunc = void (*)(AzFramework::DebugDisplayRequests&, const AZ::Vector3&, float, const AZ::Vector3&); void Draw( - ManipulatorManagerId managerId, const ManipulatorManagerState& managerState, - ManipulatorId manipulatorId, const ManipulatorState& manipulatorState, - AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, + ManipulatorManagerId managerId, + const ManipulatorManagerState& managerState, + ManipulatorId manipulatorId, + const ManipulatorState& manipulatorState, + AzFramework::DebugDisplayRequests& debugDisplay, + const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& mouseInteraction) override; AZ::Vector3 m_axis; @@ -316,26 +335,26 @@ namespace AzToolsFramework // helpers to provide consistent function pointer interface for deciding // on type of circle to draw (see DrawCircleFunc in ManipulatorViewCircle above) void DrawHalfDottedCircle( - AzFramework::DebugDisplayRequests& debugDisplay, const AZ::Vector3& position, - float radius, const AZ::Vector3& viewPos); + AzFramework::DebugDisplayRequests& debugDisplay, const AZ::Vector3& position, float radius, const AZ::Vector3& viewPos); void DrawFullCircle( - AzFramework::DebugDisplayRequests& debugDisplay, const AZ::Vector3& position, - float radius, const AZ::Vector3& viewPos); + AzFramework::DebugDisplayRequests& debugDisplay, const AZ::Vector3& position, float radius, const AZ::Vector3& viewPos); - /// Used for interaction with spline primitive - it will generate a spline bound - /// to be interacted with and will display the intersection point on the spline - /// where a user may wish to insert a point. - class ManipulatorViewSplineSelect - : public ManipulatorView + //! Used for interaction with spline primitive - it will generate a spline bound + //! to be interacted with and will display the intersection point on the spline + //! where a user may wish to insert a point. + class ManipulatorViewSplineSelect : public ManipulatorView { public: AZ_CLASS_ALLOCATOR(ManipulatorViewSplineSelect, AZ::SystemAllocator, 0) AZ_RTTI(ManipulatorViewSplineSelect, "{60996E49-D6BF-4817-BAA3-D27A407DD21A}", ManipulatorView) void Draw( - ManipulatorManagerId managerId, const ManipulatorManagerState& managerState, - ManipulatorId manipulatorId, const ManipulatorState& manipulatorState, - AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, + ManipulatorManagerId managerId, + const ManipulatorManagerState& managerState, + ManipulatorId manipulatorId, + const ManipulatorState& manipulatorState, + AzFramework::DebugDisplayRequests& debugDisplay, + const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& mouseInteraction) override; AZStd::weak_ptr m_spline; @@ -343,65 +362,61 @@ namespace AzToolsFramework AZ::Color m_color = AZ::Color(0.0f, 1.0f, 0.0f, 1.0f); }; - /// Returns true if axis is pointing away from us (we should flip it). + //! Returns true if axis is pointing away from us (we should flip it). inline bool ShouldFlipCameraAxis( - const AZ::Transform& worldFromLocal, const AZ::Vector3& localPosition, - const AZ::Vector3& axis, const AzFramework::CameraState& cameraState) + const AZ::Transform& worldFromLocal, + const AZ::Vector3& localPosition, + const AZ::Vector3& axis, + const AzFramework::CameraState& cameraState) { - return (worldFromLocal.TransformPoint(localPosition) - cameraState.m_position).Dot( - TransformDirectionNoScaling(worldFromLocal, axis)) > 0.0f; + return (worldFromLocal.TransformPoint(localPosition) - cameraState.m_position) + .Dot(TransformDirectionNoScaling(worldFromLocal, axis)) > 0.0f; } - /// @brief Return the world transform of the entity with uniform scale - choose - /// the largest element. + //! @brief Return the world transform of the entity with uniform scale - choose + //! the largest element. AZ::Transform WorldFromLocalWithUniformScale(AZ::EntityId entityId); - /// Get the non-uniform scale for this entity id. + //! Get the non-uniform scale for this entity id. AZ::Vector3 GetNonUniformScale(AZ::EntityId entityId); // Helpers to create various manipulator views. AZStd::unique_ptr CreateManipulatorViewQuad( - const PlanarManipulator& planarManipulator, const AZ::Color& axis1Color, - const AZ::Color& axis2Color, float size); + const PlanarManipulator& planarManipulator, const AZ::Color& axis1Color, const AZ::Color& axis2Color, float size); - AZStd::unique_ptr CreateManipulatorViewQuadBillboard( - const AZ::Color& color, float size); + AZStd::unique_ptr CreateManipulatorViewQuadBillboard(const AZ::Color& color, float size); AZStd::unique_ptr CreateManipulatorViewLine( - const LinearManipulator& linearManipulator, const AZ::Color& color, - float length, float width); + const LinearManipulator& linearManipulator, const AZ::Color& color, float length, float width); AZStd::unique_ptr CreateManipulatorViewLineSelect( - const LineSegmentSelectionManipulator& lineSegmentManipulator, const AZ::Color& color, - float width); + const LineSegmentSelectionManipulator& lineSegmentManipulator, const AZ::Color& color, float width); AZStd::unique_ptr CreateManipulatorViewCone( - const LinearManipulator& linearManipulator, const AZ::Color& color, - const AZ::Vector3& offset, float length, float radius); + const LinearManipulator& linearManipulator, const AZ::Color& color, const AZ::Vector3& offset, float length, float radius); AZStd::unique_ptr CreateManipulatorViewBox( - const AZ::Transform& transform, const AZ::Color& color, - const AZ::Vector3& offset, const AZ::Vector3& halfExtents); + const AZ::Transform& transform, const AZ::Color& color, const AZ::Vector3& offset, const AZ::Vector3& halfExtents); AZStd::unique_ptr CreateManipulatorViewCylinder( - const LinearManipulator& linearManipulator, const AZ::Color& color, - float length, float radius); + const LinearManipulator& linearManipulator, const AZ::Color& color, float length, float radius); AZStd::unique_ptr CreateManipulatorViewSphere( const AZ::Color& color, float radius, const DecideColorFn& decideColor, bool enableDepthTest = false); AZStd::unique_ptr CreateManipulatorViewCircle( - const AngularManipulator& angularManipulator, const AZ::Color& color, - float radius, float width, ManipulatorViewCircle::DrawCircleFunc drawFunc); + const AngularManipulator& angularManipulator, + const AZ::Color& color, + float radius, + float width, + ManipulatorViewCircle::DrawCircleFunc drawFunc); AZStd::unique_ptr CreateManipulatorViewSplineSelect( - const SplineSelectionManipulator& splineManipulator, const AZ::Color& color, - float width); + const SplineSelectionManipulator& splineManipulator, const AZ::Color& color, float width); - /// Returns the vector between the view (camera) and the manipulator in the space - /// of the Manipulator (manipulator space + local transform). - AZ::Vector3 CalculateViewDirection( - const Manipulators& manipulators, const AZ::Vector3& worldViewPosition); + //! Returns the vector between the view (camera) and the manipulator in the space + //! of the Manipulator (manipulator space + local transform). + AZ::Vector3 CalculateViewDirection(const Manipulators& manipulators, const AZ::Vector3& worldViewPosition); } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/MultiLinearManipulator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/MultiLinearManipulator.cpp index 83c4c28e9a..922ac95bf7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/MultiLinearManipulator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/MultiLinearManipulator.cpp @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "MultiLinearManipulator.h" @@ -56,10 +56,13 @@ namespace AzToolsFramework } static MultiLinearManipulator::Action BuildMultiLinearManipulatorAction( - const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform, + const AZ::Transform& worldFromLocal, + const AZ::Vector3& nonUniformScale, + const AZ::Transform& localTransform, const ViewportInteraction::MouseInteraction& interaction, const AZStd::vector& fixedAxes, - const AZStd::vector& starterStates, const GridSnapParameters& gridSnapParams) + const AZStd::vector& starterStates, + const GridSnapParameters& gridSnapParams) { MultiLinearManipulator::Action action; action.m_viewportId = interaction.m_interactionId.m_viewportId; @@ -96,8 +99,8 @@ namespace AzToolsFramework const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId); // pass action containing all linear actions for each axis to handler m_onLeftMouseDownCallback(BuildMultiLinearManipulatorAction( - worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), - interaction, m_fixedAxes, m_starters, gridSnapParams)); + worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), interaction, m_fixedAxes, m_starters, + gridSnapParams)); } } @@ -108,8 +111,8 @@ namespace AzToolsFramework const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace()); const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId); m_onMouseMoveCallback(BuildMultiLinearManipulatorAction( - worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), - interaction, m_fixedAxes, m_starters, gridSnapParams)); + worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), interaction, m_fixedAxes, m_starters, + gridSnapParams)); } } @@ -120,8 +123,8 @@ namespace AzToolsFramework const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace()); const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId); m_onLeftMouseUpCallback(BuildMultiLinearManipulatorAction( - worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), - interaction, m_fixedAxes, m_starters, gridSnapParams)); + worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), interaction, m_fixedAxes, m_starters, + gridSnapParams)); m_starters.clear(); } @@ -138,36 +141,31 @@ namespace AzToolsFramework const AZ::Transform combined = TransformUniformScale(GetSpace()) * GetLocalTransform(); for (const auto& fixed : m_fixedAxes) { - DrawAxis( - debugDisplay, combined.GetTranslation(), TransformDirectionNoScaling(combined, fixed.m_axis)); + DrawAxis(debugDisplay, combined.GetTranslation(), TransformDirectionNoScaling(combined, fixed.m_axis)); } } for (auto& view : m_manipulatorViews) { view->Draw( - GetManipulatorManagerId(), managerState, - GetManipulatorId(), { - ApplySpace(GetLocalTransform()), GetNonUniformScale(), - AZ::Vector3::CreateZero(), MouseOver() - }, - debugDisplay, cameraState, mouseInteraction); + GetManipulatorManagerId(), managerState, GetManipulatorId(), + { ApplySpace(GetLocalTransform()), GetNonUniformScale(), AZ::Vector3::CreateZero(), MouseOver() }, debugDisplay, + cameraState, mouseInteraction); } } void MultiLinearManipulator::AddAxis(const AZ::Vector3& axis) { - m_fixedAxes.push_back(LinearManipulator::Fixed{axis}); + m_fixedAxes.push_back(LinearManipulator::Fixed{ axis }); } void MultiLinearManipulator::AddAxes(const AZStd::vector& axes) { AZStd::transform( - axes.begin(), axes.end(), - AZStd::back_inserter(m_fixedAxes), + axes.begin(), axes.end(), AZStd::back_inserter(m_fixedAxes), [](const AZ::Vector3& axis) { - return LinearManipulator::Fixed{axis}; + return LinearManipulator::Fixed{ axis }; }); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/MultiLinearManipulator.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/MultiLinearManipulator.h index 8e31e02605..7cae803dfa 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/MultiLinearManipulator.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/MultiLinearManipulator.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -80,12 +80,9 @@ namespace AzToolsFramework } private: - void OnLeftMouseDownImpl( - const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance) override; - void OnLeftMouseUpImpl( - const ViewportInteraction::MouseInteraction& interaction) override; - void OnMouseMoveImpl( - const ViewportInteraction::MouseInteraction& interaction) override; + void OnLeftMouseDownImpl(const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance) override; + void OnLeftMouseUpImpl(const ViewportInteraction::MouseInteraction& interaction) override; + void OnMouseMoveImpl(const ViewportInteraction::MouseInteraction& interaction) override; void InvalidateImpl() override; void SetBoundsDirtyImpl() override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/PlanarManipulator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/PlanarManipulator.cpp index 6eb96f081c..f146ca4430 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/PlanarManipulator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/PlanarManipulator.cpp @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "PlanarManipulator.h" @@ -22,12 +22,15 @@ namespace AzToolsFramework { PlanarManipulator::StartInternal PlanarManipulator::CalculateManipulationDataStart( - const Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform, - const ViewportInteraction::MouseInteraction& interaction, const float intersectionDistance) + const Fixed& fixed, + const AZ::Transform& worldFromLocal, + const AZ::Vector3& nonUniformScale, + const AZ::Transform& localTransform, + const ViewportInteraction::MouseInteraction& interaction, + const float intersectionDistance) { - const ManipulatorInteraction manipulatorInteraction = - BuildManipulatorInteraction( - worldFromLocal, nonUniformScale, interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection); + const ManipulatorInteraction manipulatorInteraction = BuildManipulatorInteraction( + worldFromLocal, nonUniformScale, interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection); const AZ::Vector3 normal = TransformDirectionNoScaling(localTransform, fixed.m_normal); @@ -37,8 +40,8 @@ namespace AzToolsFramework StartInternal startInternal; Internal::CalculateRayPlaneIntersectingPoint( - manipulatorInteraction.m_localRayOrigin, manipulatorInteraction.m_localRayDirection, - localIntersectionPoint, normal, startInternal.m_localHitPosition); + manipulatorInteraction.m_localRayOrigin, manipulatorInteraction.m_localRayDirection, localIntersectionPoint, normal, + startInternal.m_localHitPosition); startInternal.m_localPosition = localTransform.GetTranslation(); @@ -46,13 +49,16 @@ namespace AzToolsFramework } PlanarManipulator::Action PlanarManipulator::CalculateManipulationDataAction( - const Fixed& fixed, const StartInternal& startInternal, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, - const AZ::Transform& localTransform, const GridSnapParameters& gridSnapParams, + const Fixed& fixed, + const StartInternal& startInternal, + const AZ::Transform& worldFromLocal, + const AZ::Vector3& nonUniformScale, + const AZ::Transform& localTransform, + const GridSnapParameters& gridSnapParams, const ViewportInteraction::MouseInteraction& interaction) { - const ManipulatorInteraction manipulatorInteraction = - BuildManipulatorInteraction( - worldFromLocal, nonUniformScale, interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection); + const ManipulatorInteraction manipulatorInteraction = BuildManipulatorInteraction( + worldFromLocal, nonUniformScale, interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection); const AZ::Vector3 normal = TransformDirectionNoScaling(localTransform, fixed.m_normal); @@ -61,8 +67,8 @@ namespace AzToolsFramework // if an invalid ray intersection is attempted AZ::Vector3 localHitPosition = startInternal.m_localHitPosition; Internal::CalculateRayPlaneIntersectingPoint( - manipulatorInteraction.m_localRayOrigin, manipulatorInteraction.m_localRayDirection, - startInternal.m_localHitPosition, normal, localHitPosition); + manipulatorInteraction.m_localRayOrigin, manipulatorInteraction.m_localRayDirection, startInternal.m_localHitPosition, normal, + localHitPosition); localHitPosition = Internal::TryConstrainHitPositionToView( localHitPosition, startInternal.m_localHitPosition, worldFromLocal.GetInverse(), @@ -126,8 +132,8 @@ namespace AzToolsFramework const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace()); m_startInternal = CalculateManipulationDataStart( - m_fixed, worldFromLocalUniformScale, GetNonUniformScale(), TransformNormalizedScale(GetLocalTransform()), - interaction, rayIntersectionDistance); + m_fixed, worldFromLocalUniformScale, GetNonUniformScale(), TransformNormalizedScale(GetLocalTransform()), interaction, + rayIntersectionDistance); if (m_onLeftMouseDownCallback) { @@ -180,9 +186,10 @@ namespace AzToolsFramework // display the exact hit (ray intersection) of the mouse pick on the manipulator DrawTransformAxes( - debugDisplay, TransformUniformScale(GetSpace()) * - AZ::Transform::CreateTranslation( - action.m_start.m_localHitPosition + GetNonUniformScale() * action.m_current.m_localOffset)); + debugDisplay, + TransformUniformScale(GetSpace()) * + AZ::Transform::CreateTranslation( + action.m_start.m_localHitPosition + GetNonUniformScale() * action.m_current.m_localOffset)); } AZ::Transform combined = GetLocalTransform(); @@ -191,23 +198,16 @@ namespace AzToolsFramework DrawTransformAxes(debugDisplay, combined); - DrawAxis( - debugDisplay, combined.GetTranslation(), - TransformDirectionNoScaling(GetLocalTransform(), m_fixed.m_axis1)); - DrawAxis( - debugDisplay, combined.GetTranslation(), - TransformDirectionNoScaling(GetLocalTransform(), m_fixed.m_axis2)); + DrawAxis(debugDisplay, combined.GetTranslation(), TransformDirectionNoScaling(GetLocalTransform(), m_fixed.m_axis1)); + DrawAxis(debugDisplay, combined.GetTranslation(), TransformDirectionNoScaling(GetLocalTransform(), m_fixed.m_axis2)); } for (auto& view : m_manipulatorViews) { view->Draw( - GetManipulatorManagerId(), managerState, - GetManipulatorId(), { - ApplySpace(GetLocalTransform()), GetNonUniformScale(), - AZ::Vector3::CreateZero(), MouseOver() - }, - debugDisplay, cameraState, mouseInteraction); + GetManipulatorManagerId(), managerState, GetManipulatorId(), + { ApplySpace(GetLocalTransform()), GetNonUniformScale(), AZ::Vector3::CreateZero(), MouseOver() }, debugDisplay, + cameraState, mouseInteraction); } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/PlanarManipulator.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/PlanarManipulator.h index 154ed4c7d6..3bd028ec0a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/PlanarManipulator.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/PlanarManipulator.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -23,13 +23,13 @@ namespace AzToolsFramework class ManipulatorView; struct GridSnapParameters; - /// PlanarManipulator serves as a visual tool for users to modify values - /// in two dimension in a plane defined two non-collinear axes in 3D space. + //! PlanarManipulator serves as a visual tool for users to modify values + //! in two dimension in a plane defined two non-collinear axes in 3D space. class PlanarManipulator : public BaseManipulator , public ManipulatorSpaceWithLocalTransform { - /// Private constructor. + //! Private constructor. explicit PlanarManipulator(const AZ::Transform& worldFromLocal); public: @@ -42,43 +42,51 @@ namespace AzToolsFramework ~PlanarManipulator() = default; - /// A Manipulator must only be created and managed through a shared_ptr. + //! A Manipulator must only be created and managed through a shared_ptr. static AZStd::shared_ptr MakeShared(const AZ::Transform& worldFromLocal); - /// Unchanging data set once for the planar manipulator. + //! Unchanging data set once for the planar manipulator. struct Fixed { - AZ::Vector3 m_axis1 = AZ::Vector3::CreateAxisX(); ///< m_axis1 and m_axis2 have to be orthogonal, they together define a plane in 3d space. + AZ::Vector3 m_axis1 = + AZ::Vector3::CreateAxisX(); //!< m_axis1 and m_axis2 have to be orthogonal, they together define a plane in 3d space. AZ::Vector3 m_axis2 = AZ::Vector3::CreateAxisY(); - AZ::Vector3 m_normal = AZ::Vector3::CreateAxisZ(); ///< m_normal is calculated automatically when setting the axes. + AZ::Vector3 m_normal = AZ::Vector3::CreateAxisZ(); //!< m_normal is calculated automatically when setting the axes. }; - /// The state of the manipulator at the start of an interaction. + //! The state of the manipulator at the start of an interaction. struct Start { - AZ::Vector3 m_localPosition; ///< The current position of the manipulator in local space. - AZ::Vector3 m_localHitPosition; ///< The intersection point in local space between the ray and the manipulator when the mouse down event happens. + AZ::Vector3 m_localPosition; //!< The current position of the manipulator in local space. + AZ::Vector3 m_localHitPosition; //!< The intersection point in local space between the ray and the manipulator when the mouse + //!< down event happens. }; - /// The state of the manipulator during an interaction. + //! The state of the manipulator during an interaction. struct Current { - AZ::Vector3 m_localOffset; ///< The current position of the manipulator in local space. + AZ::Vector3 m_localOffset; //!< The current position of the manipulator in local space. }; - /// Mouse action data used by MouseActionCallback (wraps Start and Current manipulator state). + //! Mouse action data used by MouseActionCallback (wraps Start and Current manipulator state). struct Action { Fixed m_fixed; Start m_start; Current m_current; ViewportInteraction::KeyboardModifiers m_modifiers; - AZ::Vector3 LocalPosition() const { return m_start.m_localPosition + m_current.m_localOffset; } - AZ::Vector3 LocalPositionOffset() const { return m_current.m_localOffset; } + AZ::Vector3 LocalPosition() const + { + return m_start.m_localPosition + m_current.m_localOffset; + } + AZ::Vector3 LocalPositionOffset() const + { + return m_current.m_localOffset; + } }; - /// This is the function signature of callbacks that will be invoked whenever a manipulator - /// is being clicked on or dragged. + //! This is the function signature of callbacks that will be invoked whenever a manipulator + //! is being clicked on or dragged. using MouseActionCallback = AZStd::function; void InstallLeftMouseDownCallback(const MouseActionCallback& onMouseDownCallback); @@ -91,11 +99,17 @@ namespace AzToolsFramework const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& mouseInteraction) override; - /// Ensure @param axis1 and @param axis2 are not collinear. + //! Ensure @param axis1 and @param axis2 are not collinear. void SetAxes(const AZ::Vector3& axis1, const AZ::Vector3& axis2); - const AZ::Vector3& GetAxis1() const { return m_fixed.m_axis1; } - const AZ::Vector3& GetAxis2() const { return m_fixed.m_axis2; } + const AZ::Vector3& GetAxis1() const + { + return m_fixed.m_axis1; + } + const AZ::Vector3& GetAxis2() const + { + return m_fixed.m_axis2; + } template void SetViews(Views&& views) @@ -104,21 +118,19 @@ namespace AzToolsFramework } private: - void OnLeftMouseDownImpl( - const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance) override; - void OnLeftMouseUpImpl( - const ViewportInteraction::MouseInteraction& interaction) override; - void OnMouseMoveImpl( - const ViewportInteraction::MouseInteraction& interaction) override; + void OnLeftMouseDownImpl(const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance) override; + void OnLeftMouseUpImpl(const ViewportInteraction::MouseInteraction& interaction) override; + void OnMouseMoveImpl(const ViewportInteraction::MouseInteraction& interaction) override; void InvalidateImpl() override; void SetBoundsDirtyImpl() override; - /// Initial data recorded when a press first happens with a planar manipulator. + //! Initial data recorded when a press first happens with a planar manipulator. struct StartInternal { - AZ::Vector3 m_localPosition; ///< The starting position of the manipulator in local space. - AZ::Vector3 m_localHitPosition; ///< The intersection point in world space between the ray and the manipulator when the mouse down event happens. + AZ::Vector3 m_localPosition; //!< The starting position of the manipulator in local space. + AZ::Vector3 m_localHitPosition; //!< The intersection point in world space between the ray and the manipulator when the mouse + //!< down event happens. }; Fixed m_fixed; @@ -128,15 +140,23 @@ namespace AzToolsFramework MouseActionCallback m_onLeftMouseUpCallback = nullptr; MouseActionCallback m_onMouseMoveCallback = nullptr; - ManipulatorViews m_manipulatorViews; ///< Look of manipulator. + ManipulatorViews m_manipulatorViews; //!< Look of manipulator. static StartInternal CalculateManipulationDataStart( - const Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, - const AZ::Transform& localTransform, const ViewportInteraction::MouseInteraction& interaction, float intersectionDistance); + const Fixed& fixed, + const AZ::Transform& worldFromLocal, + const AZ::Vector3& nonUniformScale, + const AZ::Transform& localTransform, + const ViewportInteraction::MouseInteraction& interaction, + float intersectionDistance); static Action CalculateManipulationDataAction( - const Fixed& fixed, const StartInternal& startInternal, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, - const AZ::Transform& localTransform, const GridSnapParameters& gridSnapParams, + const Fixed& fixed, + const StartInternal& startInternal, + const AZ::Transform& worldFromLocal, + const AZ::Vector3& nonUniformScale, + const AZ::Transform& localTransform, + const GridSnapParameters& gridSnapParams, const ViewportInteraction::MouseInteraction& interaction); }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/RotationManipulators.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/RotationManipulators.cpp index 399bea4024..4bb39509e3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/RotationManipulators.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/RotationManipulators.cpp @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "RotationManipulators.h" @@ -28,8 +28,7 @@ namespace AzToolsFramework m_manipulatorSpaceWithLocalTransform.SetSpace(worldFromLocal); } - void RotationManipulators::InstallLeftMouseDownCallback( - const AngularManipulator::MouseActionCallback& onMouseDownCallback) + void RotationManipulators::InstallLeftMouseDownCallback(const AngularManipulator::MouseActionCallback& onMouseDownCallback) { for (AZStd::shared_ptr& manipulator : m_localAngularManipulators) { @@ -39,8 +38,7 @@ namespace AzToolsFramework m_viewAngularManipulator->InstallLeftMouseDownCallback(onMouseDownCallback); } - void RotationManipulators::InstallMouseMoveCallback( - const AngularManipulator::MouseActionCallback& onMouseMoveCallback) + void RotationManipulators::InstallMouseMoveCallback(const AngularManipulator::MouseActionCallback& onMouseMoveCallback) { for (AZStd::shared_ptr& manipulator : m_localAngularManipulators) { @@ -50,8 +48,7 @@ namespace AzToolsFramework m_viewAngularManipulator->InstallMouseMoveCallback(onMouseMoveCallback); } - void RotationManipulators::InstallLeftMouseUpCallback( - const AngularManipulator::MouseActionCallback& onMouseUpCallback) + void RotationManipulators::InstallLeftMouseUpCallback(const AngularManipulator::MouseActionCallback& onMouseUpCallback) { for (AZStd::shared_ptr& manipulator : m_localAngularManipulators) { @@ -109,14 +106,13 @@ namespace AzToolsFramework m_viewAngularManipulator->SetSpace(worldFromLocal); } - void RotationManipulators::SetLocalAxes( - const AZ::Vector3& axis1, const AZ::Vector3& axis2, const AZ::Vector3& axis3) + void RotationManipulators::SetLocalAxes(const AZ::Vector3& axis1, const AZ::Vector3& axis2, const AZ::Vector3& axis3) { const AZ::Vector3 axes[] = { axis1, axis2, axis3 }; for (size_t manipulatorIndex = 0; manipulatorIndex < m_localAngularManipulators.size(); ++manipulatorIndex) { - m_localAngularManipulators[manipulatorIndex]->SetAxis(axes[manipulatorIndex]); + m_localAngularManipulators[manipulatorIndex]->SetAxis(axes[manipulatorIndex]); } } @@ -124,34 +120,25 @@ namespace AzToolsFramework { m_viewAngularManipulator->SetAxis(axis); - if (auto circleView = azrtti_cast( - m_viewAngularManipulator->GetView())) + if (auto circleView = azrtti_cast(m_viewAngularManipulator->GetView())) { circleView->m_axis = axis; } } void RotationManipulators::ConfigureView( - const float radius, const AZ::Color& axis1Color, - const AZ::Color& axis2Color, const AZ::Color& axis3Color) + const float radius, const AZ::Color& axis1Color, const AZ::Color& axis2Color, const AZ::Color& axis3Color) { - const AZ::Color colors[] = { - axis1Color, axis2Color, axis3Color - }; + const AZ::Color colors[] = { axis1Color, axis2Color, axis3Color }; for (size_t manipulatorIndex = 0; manipulatorIndex < m_localAngularManipulators.size(); ++manipulatorIndex) { - m_localAngularManipulators[manipulatorIndex]->SetView( - CreateManipulatorViewCircle( - *m_localAngularManipulators[manipulatorIndex], colors[manipulatorIndex], - radius, 0.05f, DrawHalfDottedCircle)); + m_localAngularManipulators[manipulatorIndex]->SetView(CreateManipulatorViewCircle( + *m_localAngularManipulators[manipulatorIndex], colors[manipulatorIndex], radius, 0.05f, DrawHalfDottedCircle)); } - m_viewAngularManipulator->SetView( - CreateManipulatorViewCircle( - *m_viewAngularManipulator, - AZ::Color(1.0f, 1.0f, 1.0f, 1.0f), - radius + (radius * 0.12f), 0.05f, DrawFullCircle)); + m_viewAngularManipulator->SetView(CreateManipulatorViewCircle( + *m_viewAngularManipulator, AZ::Color(1.0f, 1.0f, 1.0f, 1.0f), radius + (radius * 0.12f), 0.05f, DrawFullCircle)); } bool RotationManipulators::PerformingActionViewAxis() const diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/RotationManipulators.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/RotationManipulators.h index 11b6e0838c..5ecfa21f26 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/RotationManipulators.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/RotationManipulators.h @@ -1,27 +1,26 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once #include -#include #include +#include namespace AzToolsFramework { - /// RotationManipulators is an aggregation of 3 angular manipulators who share the same origin - /// in addition to a view aligned angular manipulator (facing the camera). - class RotationManipulators - : public Manipulators + //! RotationManipulators is an aggregation of 3 angular manipulators who share the same origin + //! in addition to a view aligned angular manipulator (facing the camera). + class RotationManipulators : public Manipulators { public: AZ_RTTI(RotationManipulators, "{5D1F1D47-1D5B-4E42-B47E-23F108F8BF7D}") @@ -40,12 +39,10 @@ namespace AzToolsFramework void SetLocalOrientationImpl(const AZ::Quaternion& localOrientation) override; void RefreshView(const AZ::Vector3& worldViewPosition) override; - void SetLocalAxes( - const AZ::Vector3& axis1, const AZ::Vector3& axis2, const AZ::Vector3& axis3); + void SetLocalAxes(const AZ::Vector3& axis1, const AZ::Vector3& axis2, const AZ::Vector3& axis3); void SetViewAxis(const AZ::Vector3& axis); - void ConfigureView( - float radius, const AZ::Color& axis1Color, const AZ::Color& axis2Color, const AZ::Color& axis3Color); + void ConfigureView(float radius, const AZ::Color& axis1Color, const AZ::Color& axis2Color, const AZ::Color& axis3Color); bool PerformingActionViewAxis() const; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ScaleManipulators.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ScaleManipulators.cpp index caeedd834f..079fde669a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ScaleManipulators.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ScaleManipulators.cpp @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "ScaleManipulators.h" @@ -28,8 +28,7 @@ namespace AzToolsFramework m_manipulatorSpaceWithLocalTransform.SetSpace(worldFromLocal); } - void ScaleManipulators::InstallAxisLeftMouseDownCallback( - const LinearManipulator::MouseActionCallback& onMouseDownCallback) + void ScaleManipulators::InstallAxisLeftMouseDownCallback(const LinearManipulator::MouseActionCallback& onMouseDownCallback) { for (AZStd::shared_ptr& manipulator : m_axisScaleManipulators) { @@ -37,8 +36,7 @@ namespace AzToolsFramework } } - void ScaleManipulators::InstallAxisMouseMoveCallback( - const LinearManipulator::MouseActionCallback& onMouseMoveCallback) + void ScaleManipulators::InstallAxisMouseMoveCallback(const LinearManipulator::MouseActionCallback& onMouseMoveCallback) { for (AZStd::shared_ptr& manipulator : m_axisScaleManipulators) { @@ -46,8 +44,7 @@ namespace AzToolsFramework } } - void ScaleManipulators::InstallAxisLeftMouseUpCallback( - const LinearManipulator::MouseActionCallback& onMouseUpCallback) + void ScaleManipulators::InstallAxisLeftMouseUpCallback(const LinearManipulator::MouseActionCallback& onMouseUpCallback) { for (AZStd::shared_ptr& manipulator : m_axisScaleManipulators) { @@ -55,22 +52,19 @@ namespace AzToolsFramework } } - void ScaleManipulators::InstallUniformLeftMouseDownCallback( - const LinearManipulator::MouseActionCallback& onMouseDownCallback) + void ScaleManipulators::InstallUniformLeftMouseDownCallback(const LinearManipulator::MouseActionCallback& onMouseDownCallback) { m_uniformScaleManipulator->InstallLeftMouseDownCallback(onMouseDownCallback); } - void ScaleManipulators::InstallUniformMouseMoveCallback( - const LinearManipulator::MouseActionCallback& onMouseMoveCallback) + void ScaleManipulators::InstallUniformMouseMoveCallback(const LinearManipulator::MouseActionCallback& onMouseMoveCallback) { - m_uniformScaleManipulator->InstallMouseMoveCallback(onMouseMoveCallback); + m_uniformScaleManipulator->InstallMouseMoveCallback(onMouseMoveCallback); } - void ScaleManipulators::InstallUniformLeftMouseUpCallback( - const LinearManipulator::MouseActionCallback& onMouseUpCallback) + void ScaleManipulators::InstallUniformLeftMouseUpCallback(const LinearManipulator::MouseActionCallback& onMouseUpCallback) { - m_uniformScaleManipulator->InstallLeftMouseUpCallback(onMouseUpCallback); + m_uniformScaleManipulator->InstallLeftMouseUpCallback(onMouseUpCallback); } void ScaleManipulators::SetLocalTransformImpl(const AZ::Transform& localTransform) @@ -80,8 +74,7 @@ namespace AzToolsFramework manipulator->SetLocalTransform(localTransform); } - m_uniformScaleManipulator->SetVisualOrientationOverride( - QuaternionFromTransformNoScaling(localTransform)); + m_uniformScaleManipulator->SetVisualOrientationOverride(QuaternionFromTransformNoScaling(localTransform)); m_uniformScaleManipulator->SetLocalOrientation(AZ::Quaternion::CreateIdentity()); } @@ -113,14 +106,13 @@ namespace AzToolsFramework m_uniformScaleManipulator->SetSpace(worldFromLocal); } - void ScaleManipulators::SetAxes( - const AZ::Vector3& axis1, const AZ::Vector3& axis2, const AZ::Vector3& axis3) + void ScaleManipulators::SetAxes(const AZ::Vector3& axis1, const AZ::Vector3& axis2, const AZ::Vector3& axis3) { - AZ::Vector3 axes[] = { axis1, axis2, axis3 }; + AZ::Vector3 axes[] = { axis1, axis2, axis3 }; for (size_t manipulatorIndex = 0; manipulatorIndex < m_axisScaleManipulators.size(); ++manipulatorIndex) { - m_axisScaleManipulators[manipulatorIndex]->SetAxis(axes[manipulatorIndex]); + m_axisScaleManipulators[manipulatorIndex]->SetAxis(axes[manipulatorIndex]); } // uniform scale manipulator uses Z axis for scaling (always in world space) @@ -129,32 +121,27 @@ namespace AzToolsFramework } void ScaleManipulators::ConfigureView( - const float axisLength, const AZ::Color& axis1Color, - const AZ::Color& axis2Color, const AZ::Color& axis3Color) + const float axisLength, const AZ::Color& axis1Color, const AZ::Color& axis2Color, const AZ::Color& axis3Color) { const float boxSize = 0.1f; const float lineWidth = 0.05f; - const AZ::Color colors[] = { - axis1Color, axis2Color, axis3Color - }; + const AZ::Color colors[] = { axis1Color, axis2Color, axis3Color }; for (size_t manipulatorIndex = 0; manipulatorIndex < m_axisScaleManipulators.size(); ++manipulatorIndex) { ManipulatorViews views; - views.emplace_back(CreateManipulatorViewLine( - *m_axisScaleManipulators[manipulatorIndex], colors[manipulatorIndex], axisLength, lineWidth)); + views.emplace_back( + CreateManipulatorViewLine(*m_axisScaleManipulators[manipulatorIndex], colors[manipulatorIndex], axisLength, lineWidth)); views.emplace_back(CreateManipulatorViewBox( AZ::Transform::CreateIdentity(), colors[manipulatorIndex], - m_axisScaleManipulators[manipulatorIndex]->GetAxis() * (axisLength - boxSize), - AZ::Vector3(boxSize))); + m_axisScaleManipulators[manipulatorIndex]->GetAxis() * (axisLength - boxSize), AZ::Vector3(boxSize))); m_axisScaleManipulators[manipulatorIndex]->SetViews(AZStd::move(views)); } ManipulatorViews views; views.emplace_back(CreateManipulatorViewBox( - AZ::Transform::CreateIdentity(), AZ::Color::CreateOne(), - AZ::Vector3::CreateZero(), AZ::Vector3(boxSize))); + AZ::Transform::CreateIdentity(), AZ::Color::CreateOne(), AZ::Vector3::CreateZero(), AZ::Vector3(boxSize))); m_uniformScaleManipulator->SetViews(AZStd::move(views)); } @@ -167,4 +154,4 @@ namespace AzToolsFramework manipulatorFn(m_uniformScaleManipulator.get()); } -} +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ScaleManipulators.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ScaleManipulators.h index 24df3cda7b..b06d8f92c5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ScaleManipulators.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ScaleManipulators.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -17,11 +17,10 @@ namespace AzToolsFramework { - /// ScaleManipulators is an aggregation of 3 linear manipulators for each basis axis who share - /// the same transform, and a single linear manipulator at the center of the transform whose - /// axis is world up (z). - class ScaleManipulators - : public Manipulators + //! ScaleManipulators is an aggregation of 3 linear manipulators for each basis axis who share + //! the same transform, and a single linear manipulator at the center of the transform whose + //! axis is world up (z). + class ScaleManipulators : public Manipulators { public: AZ_RTTI(ScaleManipulators, "{C6350CE0-7B7A-46F8-B65F-D4A54DD9A7D9}") @@ -42,16 +41,9 @@ namespace AzToolsFramework void SetLocalPositionImpl(const AZ::Vector3& localPosition) override; void SetLocalOrientationImpl(const AZ::Quaternion& localOrientation) override; - void SetAxes( - const AZ::Vector3& axis1, - const AZ::Vector3& axis2, - const AZ::Vector3& axis3); + void SetAxes(const AZ::Vector3& axis1, const AZ::Vector3& axis2, const AZ::Vector3& axis3); - void ConfigureView( - float axisLength, - const AZ::Color& axis1Color, - const AZ::Color& axis2Color, - const AZ::Color& axis3Color); + void ConfigureView(float axisLength, const AZ::Color& axis1Color, const AZ::Color& axis2Color, const AZ::Color& axis3Color); private: AZ_DISABLE_COPY_MOVE(ScaleManipulators) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SelectionManipulator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SelectionManipulator.cpp index 4925651580..3071ca74ae 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SelectionManipulator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SelectionManipulator.cpp @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "SelectionManipulator.h" @@ -16,8 +16,8 @@ namespace AzToolsFramework { - AZStd::shared_ptr SelectionManipulator::MakeShared(const AZ::Transform& worldFromLocal, - const AZ::Vector3& nonUniformScale) + AZStd::shared_ptr SelectionManipulator::MakeShared( + const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale) { return AZStd::shared_ptr(aznew SelectionManipulator(worldFromLocal, nonUniformScale)); } @@ -93,12 +93,9 @@ namespace AzToolsFramework for (auto& view : m_manipulatorViews) { view->Draw( - GetManipulatorManagerId(), managerState, - GetManipulatorId(), { - TransformUniformScale(GetSpace()), GetNonUniformScale(), - GetLocalPosition(), MouseOver() - }, - debugDisplay, cameraState, mouseInteraction); + GetManipulatorManagerId(), managerState, GetManipulatorId(), + { TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalPosition(), MouseOver() }, debugDisplay, cameraState, + mouseInteraction); } } @@ -117,4 +114,4 @@ namespace AzToolsFramework view->Invalidate(GetManipulatorManagerId()); } } -} +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SelectionManipulator.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SelectionManipulator.h index 1ae6ceb729..b862dfd684 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SelectionManipulator.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SelectionManipulator.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -20,13 +20,13 @@ namespace AzToolsFramework { class ManipulatorView; - /// Represents a sphere that can be clicked on to trigger a particular behavior - /// For example clicking a preview point to create a translation manipulator. + //! Represents a sphere that can be clicked on to trigger a particular behavior. + //! For example clicking a preview point to create a translation manipulator. class SelectionManipulator : public BaseManipulator , public ManipulatorSpaceWithLocalPosition { - /// Private constructor. + //! Private constructor. SelectionManipulator(const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale = AZ::Vector3::CreateOne()); public: @@ -39,12 +39,12 @@ namespace AzToolsFramework ~SelectionManipulator() = default; - /// A Manipulator must only be created and managed through a shared_ptr. - static AZStd::shared_ptr MakeShared(const AZ::Transform& worldFromLocal, - const AZ::Vector3& nonUniformScale = AZ::Vector3::CreateOne()); + //! A Manipulator must only be created and managed through a shared_ptr. + static AZStd::shared_ptr MakeShared( + const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale = AZ::Vector3::CreateOne()); - /// This is the function signature of callbacks that will be invoked - /// whenever a selection manipulator is clicked on. + //! This is the function signature of callbacks that will be invoked + //! whenever a selection manipulator is clicked on. using MouseActionCallback = AZStd::function; void InstallLeftMouseDownCallback(const MouseActionCallback& onMouseDownCallback); @@ -58,10 +58,25 @@ namespace AzToolsFramework const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& mouseInteraction) override; - bool Selected() const { return m_selected; } - void Select() { m_selected = true; } - void Deselect() { m_selected = false; } - void ToggleSelected() { m_selected = !m_selected; } + bool Selected() const + { + return m_selected; + } + + void Select() + { + m_selected = true; + } + + void Deselect() + { + m_selected = false; + } + + void ToggleSelected() + { + m_selected = !m_selected; + } template void SetViews(Views&& views) @@ -70,13 +85,9 @@ namespace AzToolsFramework } private: - void OnLeftMouseDownImpl( - const ViewportInteraction::MouseInteraction& interaction, - float rayIntersectionDistance) override; + void OnLeftMouseDownImpl(const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance) override; void OnLeftMouseUpImpl(const ViewportInteraction::MouseInteraction& interaction) override; - void OnRightMouseDownImpl( - const ViewportInteraction::MouseInteraction& interaction, - float rayIntersectionDistance) override; + void OnRightMouseDownImpl(const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance) override; void OnRightMouseUpImpl(const ViewportInteraction::MouseInteraction& interaction) override; void InvalidateImpl() override; @@ -89,6 +100,6 @@ namespace AzToolsFramework MouseActionCallback m_onRightMouseDownCallback = nullptr; MouseActionCallback m_onRightMouseUpCallback = nullptr; - ManipulatorViews m_manipulatorViews; ///< Look of manipulator. + ManipulatorViews m_manipulatorViews; //!< Look of manipulator. }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SplineHoverSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SplineHoverSelection.cpp index 43fbbce80b..98972bcf33 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SplineHoverSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SplineHoverSelection.cpp @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "SplineHoverSelection.h" @@ -20,11 +20,12 @@ namespace AzToolsFramework { - static const AZ::Color s_splineSelectManipulatorColor = AZ::Color(0.0f, 1.0f, 0.0f, 1.0f); + static const AZ::Color SplineSelectManipulatorColor = AZ::Color(0.0f, 1.0f, 0.0f, 1.0f); SplineHoverSelection::SplineHoverSelection( const AZ::EntityComponentIdPair& entityComponentIdPair, - const ManipulatorManagerId managerId, const AZStd::shared_ptr& spline) + const ManipulatorManagerId managerId, + const AZStd::shared_ptr& spline) { m_splineSelectionManipulator = SplineSelectionManipulator::MakeShared(); m_splineSelectionManipulator->Register(managerId); @@ -33,16 +34,14 @@ namespace AzToolsFramework const float splineWidth = 0.05f; m_splineSelectionManipulator->SetSpline(spline); - m_splineSelectionManipulator->SetView(CreateManipulatorViewSplineSelect( - *m_splineSelectionManipulator, s_splineSelectManipulatorColor, splineWidth)); + m_splineSelectionManipulator->SetView( + CreateManipulatorViewSplineSelect(*m_splineSelectionManipulator, SplineSelectManipulatorColor, splineWidth)); m_splineSelectionManipulator->InstallLeftMouseUpCallback( [entityComponentIdPair](const SplineSelectionManipulator::Action& action) - { - InsertVertexAfter( - entityComponentIdPair, action.m_splineAddress.m_segmentIndex, - action.m_localSplineHitPosition); - }); + { + InsertVertexAfter(entityComponentIdPair, action.m_splineAddress.m_segmentIndex, action.m_localSplineHitPosition); + }); } SplineHoverSelection::~SplineHoverSelection() diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SplineHoverSelection.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SplineHoverSelection.h index 11b10f8516..d8dcd573af 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SplineHoverSelection.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SplineHoverSelection.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -19,20 +19,20 @@ namespace AZ { class Spline; class EntityComponentIdPair; -} +} // namespace AZ namespace AzToolsFramework { class SplineSelectionManipulator; - /// SplineHoverSelection is a concrete implementation of HoverSelection wrapping a Spline and - /// SplineManipulator. The underlying manipulators are used to control selection. - class SplineHoverSelection - : public HoverSelection + //! SplineHoverSelection is a concrete implementation of HoverSelection wrapping a Spline and + //! SplineManipulator. The underlying manipulators are used to control selection. + class SplineHoverSelection : public HoverSelection { public: explicit SplineHoverSelection( - const AZ::EntityComponentIdPair& entityComponentIdPair, ManipulatorManagerId managerId, + const AZ::EntityComponentIdPair& entityComponentIdPair, + ManipulatorManagerId managerId, const AZStd::shared_ptr& spline); SplineHoverSelection(const SplineHoverSelection&) = delete; SplineHoverSelection& operator=(const SplineHoverSelection&) = delete; @@ -46,6 +46,6 @@ namespace AzToolsFramework void SetNonUniformScale(const AZ::Vector3& nonUniformScale) override; private: - AZStd::shared_ptr m_splineSelectionManipulator; ///< Manipulator for adding points to spline. + AZStd::shared_ptr m_splineSelectionManipulator; //!< Manipulator for adding points to spline. }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SplineSelectionManipulator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SplineSelectionManipulator.cpp index 5bfccefe48..39dbcb67fa 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SplineSelectionManipulator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SplineSelectionManipulator.cpp @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "SplineSelectionManipulator.h" @@ -18,8 +18,10 @@ namespace AzToolsFramework { SplineSelectionManipulator::Action CalculateManipulationDataAction( - const AZ::Transform& worldFromLocal, const AZ::Vector3& rayOrigin, - const AZ::Vector3& rayDirection, const AZStd::weak_ptr& spline) + const AZ::Transform& worldFromLocal, + const AZ::Vector3& rayOrigin, + const AZ::Vector3& rayDirection, + const AZStd::weak_ptr& spline) { SplineSelectionManipulator::Action action; if (const AZStd::shared_ptr splinePtr = spline.lock()) @@ -65,9 +67,7 @@ namespace AzToolsFramework if (m_onLeftMouseDownCallback) { m_onLeftMouseDownCallback(CalculateManipulationDataAction( - TransformUniformScale(GetSpace()), - interaction.m_mousePick.m_rayOrigin, - interaction.m_mousePick.m_rayDirection, m_spline)); + TransformUniformScale(GetSpace()), interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection, m_spline)); } } @@ -76,9 +76,7 @@ namespace AzToolsFramework if (MouseOver() && m_onLeftMouseUpCallback) { m_onLeftMouseUpCallback(CalculateManipulationDataAction( - TransformUniformScale(GetSpace()), - interaction.m_mousePick.m_rayOrigin, - interaction.m_mousePick.m_rayDirection, m_spline)); + TransformUniformScale(GetSpace()), interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection, m_spline)); } } @@ -99,12 +97,9 @@ namespace AzToolsFramework if (mouseInteraction.m_keyboardModifiers.Ctrl() && !mouseInteraction.m_keyboardModifiers.Shift()) { m_manipulatorView->Draw( - GetManipulatorManagerId(), managerState, - GetManipulatorId(), { - TransformUniformScale(GetSpace()), GetNonUniformScale(), - AZ::Vector3::CreateZero(), MouseOver() - }, - debugDisplay, cameraState, mouseInteraction); + GetManipulatorManagerId(), managerState, GetManipulatorId(), + { TransformUniformScale(GetSpace()), GetNonUniformScale(), AZ::Vector3::CreateZero(), MouseOver() }, debugDisplay, + cameraState, mouseInteraction); } } @@ -122,4 +117,4 @@ namespace AzToolsFramework { m_manipulatorView->Invalidate(GetManipulatorManagerId()); } -} +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SplineSelectionManipulator.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SplineSelectionManipulator.h index 721c3413ef..e1a2fe26ab 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SplineSelectionManipulator.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SplineSelectionManipulator.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -23,13 +23,13 @@ namespace AzToolsFramework { class ManipulatorView; - /// A manipulator to represent selection of a spline. Underlying spline data is - /// used to test mouse picking ray against to preview closest point on spline. + //! A manipulator to represent selection of a spline. Underlying spline data is + //! used to test mouse picking ray against to preview closest point on spline. class SplineSelectionManipulator : public BaseManipulator , public ManipulatorSpace { - /// Private constructor. + //! Private constructor. SplineSelectionManipulator(); public: @@ -41,10 +41,10 @@ namespace AzToolsFramework ~SplineSelectionManipulator(); - /// A Manipulator must only be created and managed through a shared_ptr. + //! A Manipulator must only be created and managed through a shared_ptr. static AZStd::shared_ptr MakeShared(); - /// Mouse action data used by MouseActionCallback. + //! Mouse action data used by MouseActionCallback. struct Action { AZ::Vector3 m_localSplineHitPosition; @@ -62,29 +62,36 @@ namespace AzToolsFramework const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteraction& mouseInteraction) override; - void SetSpline(AZStd::shared_ptr spline) { m_spline = AZStd::move(spline); } - AZStd::weak_ptr GetSpline() const { return m_spline; } + void SetSpline(AZStd::shared_ptr spline) + { + m_spline = AZStd::move(spline); + } + AZStd::weak_ptr GetSpline() const + { + return m_spline; + } void SetView(AZStd::unique_ptr&& view); private: - void OnLeftMouseDownImpl( - const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance) override; - void OnLeftMouseUpImpl( - const ViewportInteraction::MouseInteraction& interaction) override; + void OnLeftMouseDownImpl(const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance) override; + void OnLeftMouseUpImpl(const ViewportInteraction::MouseInteraction& interaction) override; void InvalidateImpl() override; void SetBoundsDirtyImpl() override; AZStd::weak_ptr m_spline; - AZStd::unique_ptr m_manipulatorView = nullptr; ///< Look of manipulator and bounds for interaction. + AZStd::unique_ptr m_manipulatorView = nullptr; //!< Look of manipulator and bounds for interaction. MouseActionCallback m_onLeftMouseDownCallback = nullptr; MouseActionCallback m_onLeftMouseUpCallback = nullptr; - ViewportInteraction::KeyboardModifiers m_keyboardModifiers; ///< What modifier keys are pressed when interacting with this manipulator. + ViewportInteraction::KeyboardModifiers + m_keyboardModifiers; //!< What modifier keys are pressed when interacting with this manipulator. }; SplineSelectionManipulator::Action CalculateManipulationDataAction( - const AZ::Transform& worldFromLocal, const AZ::Vector3& rayOrigin, - const AZ::Vector3& rayDirection, const AZStd::weak_ptr& spline); + const AZ::Transform& worldFromLocal, + const AZ::Vector3& rayOrigin, + const AZ::Vector3& rayDirection, + const AZStd::weak_ptr& spline); } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SurfaceManipulator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SurfaceManipulator.cpp index f570c20a7e..aa3f3f5882 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SurfaceManipulator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SurfaceManipulator.cpp @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "SurfaceManipulator.h" @@ -18,19 +18,22 @@ namespace AzToolsFramework { SurfaceManipulator::StartInternal SurfaceManipulator::CalculateManipulationDataStart( - const AZ::Transform& worldFromLocal, const AZ::Vector3& worldSurfacePosition, - const AZ::Vector3& localStartPosition, const bool snapping, const float gridSize, const int viewportId) + const AZ::Transform& worldFromLocal, + const AZ::Vector3& worldSurfacePosition, + const AZ::Vector3& localStartPosition, + const bool snapping, + const float gridSize, + const int viewportId) { const AZ::Transform worldFromLocalUniform = AzToolsFramework::TransformUniformScale(worldFromLocal); const AZ::Transform localFromWorldUniform = worldFromLocalUniform.GetInverse(); const AZ::Vector3 localFinalSurfacePosition = snapping - ? CalculateSnappedTerrainPosition( - // note: gridSize is not scaled by scaleRecip here as localStartPosition is - // unscaled itself so the position returned by CalculateSnappedTerrainPosition - // must be in the same space (if localStartPosition were also scaled, gridSize - // would need to be multiplied by scaleRecip) - worldSurfacePosition, worldFromLocalUniform, viewportId, gridSize) + // note: gridSize is not scaled by scaleRecip here as localStartPosition is + // unscaled itself so the position returned by CalculateSnappedTerrainPosition + // must be in the same space (if localStartPosition were also scaled, gridSize + // would need to be multiplied by scaleRecip) + ? CalculateSnappedTerrainPosition(worldSurfacePosition, worldFromLocalUniform, viewportId, gridSize) : localFromWorldUniform.TransformPoint(worldSurfacePosition); // delta/offset between initial vertex position and terrain pick position @@ -44,9 +47,13 @@ namespace AzToolsFramework } SurfaceManipulator::Action SurfaceManipulator::CalculateManipulationDataAction( - const StartInternal& startInternal, const AZ::Transform& worldFromLocal, - const AZ::Vector3& worldSurfacePosition, const bool snapping, const float gridSize, - const ViewportInteraction::KeyboardModifiers keyboardModifiers, const int viewportId) + const StartInternal& startInternal, + const AZ::Transform& worldFromLocal, + const AZ::Vector3& worldSurfacePosition, + const bool snapping, + const float gridSize, + const ViewportInteraction::KeyboardModifiers keyboardModifiers, + const int viewportId) { const AZ::Transform worldFromLocalUniform = AzToolsFramework::TransformUniformScale(worldFromLocal); const AZ::Transform localFromWorldUniform = worldFromLocalUniform.GetInverse(); @@ -54,8 +61,7 @@ namespace AzToolsFramework const float scaleRecip = ScaleReciprocal(worldFromLocalUniform); const AZ::Vector3 localFinalSurfacePosition = snapping - ? CalculateSnappedTerrainPosition( - worldSurfacePosition, worldFromLocalUniform, viewportId, gridSize * scaleRecip) + ? CalculateSnappedTerrainPosition(worldSurfacePosition, worldFromLocalUniform, viewportId, gridSize * scaleRecip) : localFromWorldUniform.TransformPoint(worldSurfacePosition); Action action; @@ -106,17 +112,14 @@ namespace AzToolsFramework interaction.m_mousePick.m_screenCoordinates); m_startInternal = CalculateManipulationDataStart( - worldFromLocalUniformScale, worldSurfacePosition, GetLocalPosition(), - gridSnapParams.m_gridSnap, gridSnapParams.m_gridSize, + worldFromLocalUniformScale, worldSurfacePosition, GetLocalPosition(), gridSnapParams.m_gridSnap, gridSnapParams.m_gridSize, interaction.m_interactionId.m_viewportId); if (m_onLeftMouseDownCallback) { m_onLeftMouseDownCallback(CalculateManipulationDataAction( - m_startInternal, worldFromLocalUniformScale, worldSurfacePosition, - gridSnapParams.m_gridSnap, gridSnapParams.m_gridSize, - interaction.m_keyboardModifiers, - interaction.m_interactionId.m_viewportId)); + m_startInternal, worldFromLocalUniformScale, worldSurfacePosition, gridSnapParams.m_gridSnap, gridSnapParams.m_gridSize, + interaction.m_keyboardModifiers, interaction.m_interactionId.m_viewportId)); } } @@ -133,10 +136,8 @@ namespace AzToolsFramework const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId); m_onLeftMouseUpCallback(CalculateManipulationDataAction( - m_startInternal, TransformUniformScale(GetSpace()), worldSurfacePosition, - gridSnapParams.m_gridSnap, - gridSnapParams.m_gridSize, - interaction.m_keyboardModifiers, interaction.m_interactionId.m_viewportId)); + m_startInternal, TransformUniformScale(GetSpace()), worldSurfacePosition, gridSnapParams.m_gridSnap, + gridSnapParams.m_gridSize, interaction.m_keyboardModifiers, interaction.m_interactionId.m_viewportId)); } } @@ -153,10 +154,8 @@ namespace AzToolsFramework const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId); m_onMouseMoveCallback(CalculateManipulationDataAction( - m_startInternal, TransformUniformScale(GetSpace()), worldSurfacePosition, - gridSnapParams.m_gridSnap, - gridSnapParams.m_gridSize, - interaction.m_keyboardModifiers, interaction.m_interactionId.m_viewportId)); + m_startInternal, TransformUniformScale(GetSpace()), worldSurfacePosition, gridSnapParams.m_gridSnap, + gridSnapParams.m_gridSize, interaction.m_keyboardModifiers, interaction.m_interactionId.m_viewportId)); } } @@ -172,12 +171,9 @@ namespace AzToolsFramework const ViewportInteraction::MouseInteraction& mouseInteraction) { m_manipulatorView->Draw( - GetManipulatorManagerId(), managerState, - GetManipulatorId(), { - TransformUniformScale(GetSpace()), GetNonUniformScale(), - GetLocalPosition(), MouseOver() - }, - debugDisplay, cameraState, mouseInteraction); + GetManipulatorManagerId(), managerState, GetManipulatorId(), + { TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalPosition(), MouseOver() }, debugDisplay, cameraState, + mouseInteraction); } void SurfaceManipulator::InvalidateImpl() @@ -189,4 +185,4 @@ namespace AzToolsFramework { m_manipulatorView = AZStd::move(view); } -} +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SurfaceManipulator.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SurfaceManipulator.h index 72eb3a5cda..6461954353 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SurfaceManipulator.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SurfaceManipulator.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -21,13 +21,13 @@ namespace AzToolsFramework { class ManipulatorView; - /// Surface manipulator will ensure the point(s) it controls snap precisely to the xy grid - /// while also staying aligned exactly to the height of the terrain. + //! Surface manipulator will ensure the point(s) it controls snap precisely to the xy grid + //! while also staying aligned exactly to the height of the terrain. class SurfaceManipulator : public BaseManipulator , public ManipulatorSpaceWithLocalPosition { - /// Private constructor. + //! Private constructor. explicit SurfaceManipulator(const AZ::Transform& worldFromLocal); public: @@ -40,30 +40,36 @@ namespace AzToolsFramework ~SurfaceManipulator() = default; - /// A Manipulator must only be created and managed through a shared_ptr. + //! A Manipulator must only be created and managed through a shared_ptr. static AZStd::shared_ptr MakeShared(const AZ::Transform& worldFromLocal); - /// The state of the manipulator at the start of an interaction. + //! The state of the manipulator at the start of an interaction. struct Start { - AZ::Vector3 m_localPosition; ///< The current position of the manipulator in local space. - AZ::Vector3 m_snapOffset; ///< The snap offset amount to ensure manipulator is aligned to the grid. + AZ::Vector3 m_localPosition; //!< The current position of the manipulator in local space. + AZ::Vector3 m_snapOffset; //!< The snap offset amount to ensure manipulator is aligned to the grid. }; - /// The state of the manipulator during an interaction. + //! The state of the manipulator during an interaction. struct Current { - AZ::Vector3 m_localOffset; ///< The current offset of the manipulator from its starting position in local space. + AZ::Vector3 m_localOffset; //!< The current offset of the manipulator from its starting position in local space. }; - /// Mouse action data used by MouseActionCallback (wraps Start and Current manipulator state). + //! Mouse action data used by MouseActionCallback (wraps Start and Current manipulator state). struct Action { Start m_start; Current m_current; ViewportInteraction::KeyboardModifiers m_modifiers; - AZ::Vector3 LocalPosition() const { return m_start.m_localPosition + m_current.m_localOffset; } - AZ::Vector3 LocalPositionOffset() const { return m_current.m_localOffset; } + AZ::Vector3 LocalPosition() const + { + return m_start.m_localPosition + m_current.m_localOffset; + } + AZ::Vector3 LocalPositionOffset() const + { + return m_current.m_localOffset; + } }; using MouseActionCallback = AZStd::function; @@ -81,39 +87,44 @@ namespace AzToolsFramework void SetView(AZStd::unique_ptr&& view); private: - void OnLeftMouseDownImpl( - const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance) override; - void OnLeftMouseUpImpl( - const ViewportInteraction::MouseInteraction& interaction) override; - void OnMouseMoveImpl( - const ViewportInteraction::MouseInteraction& interaction) override; + void OnLeftMouseDownImpl(const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance) override; + void OnLeftMouseUpImpl(const ViewportInteraction::MouseInteraction& interaction) override; + void OnMouseMoveImpl(const ViewportInteraction::MouseInteraction& interaction) override; void InvalidateImpl() override; void SetBoundsDirtyImpl() override; - /// Initial data recorded when a press first happens with a surface manipulator. + //! Initial data recorded when a press first happens with a surface manipulator. struct StartInternal { - AZ::Vector3 m_localPosition; ///< The current position of the manipulator in local space. - AZ::Vector3 m_localHitPosition; ///< The hit position with the terrain in local space. - AZ::Vector3 m_snapOffset; ///< The snap offset amount to ensure manipulator is aligned to the grid. + AZ::Vector3 m_localPosition; //!< The current position of the manipulator in local space. + AZ::Vector3 m_localHitPosition; //!< The hit position with the terrain in local space. + AZ::Vector3 m_snapOffset; //!< The snap offset amount to ensure manipulator is aligned to the grid. }; - StartInternal m_startInternal; ///< Internal initial state recorded/created in OnMouseDown. + StartInternal m_startInternal; //!< Internal initial state recorded/created in OnMouseDown. - AZStd::unique_ptr m_manipulatorView = nullptr; ///< Look of manipulator. + AZStd::unique_ptr m_manipulatorView = nullptr; //!< Look of manipulator. MouseActionCallback m_onLeftMouseDownCallback = nullptr; MouseActionCallback m_onLeftMouseUpCallback = nullptr; MouseActionCallback m_onMouseMoveCallback = nullptr; static StartInternal CalculateManipulationDataStart( - const AZ::Transform& worldFromLocal, const AZ::Vector3& worldSurfacePosition, - const AZ::Vector3& localPosition, bool snapping, float gridSize, int viewportId); + const AZ::Transform& worldFromLocal, + const AZ::Vector3& worldSurfacePosition, + const AZ::Vector3& localPosition, + bool snapping, + float gridSize, + int viewportId); static Action CalculateManipulationDataAction( - const StartInternal& startInternal, const AZ::Transform& worldFromLocal, - const AZ::Vector3& worldSurfacePosition, bool snapping, float gridSize, - ViewportInteraction::KeyboardModifiers keyboardModifiers, int viewportId); + const StartInternal& startInternal, + const AZ::Transform& worldFromLocal, + const AZ::Vector3& worldSurfacePosition, + bool snapping, + float gridSize, + ViewportInteraction::KeyboardModifiers keyboardModifiers, + int viewportId); }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.cpp index bfdfd8ba08..57d175c34e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.cpp @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "TranslationManipulators.h" @@ -77,8 +77,7 @@ namespace AzToolsFramework } } - void TranslationManipulators::InstallLinearManipulatorMouseUpCallback( - const LinearManipulator::MouseActionCallback& onMouseUpCallback) + void TranslationManipulators::InstallLinearManipulatorMouseUpCallback(const LinearManipulator::MouseActionCallback& onMouseUpCallback) { for (AZStd::shared_ptr& manipulator : m_linearManipulators) { @@ -104,8 +103,7 @@ namespace AzToolsFramework } } - void TranslationManipulators::InstallPlanarManipulatorMouseUpCallback( - const PlanarManipulator::MouseActionCallback& onMouseUpCallback) + void TranslationManipulators::InstallPlanarManipulatorMouseUpCallback(const PlanarManipulator::MouseActionCallback& onMouseUpCallback) { for (AZStd::shared_ptr& manipulator : m_planarManipulators) { @@ -122,8 +120,7 @@ namespace AzToolsFramework } } - void TranslationManipulators::InstallSurfaceManipulatorMouseUpCallback( - const SurfaceManipulator::MouseActionCallback& onMouseUpCallback) + void TranslationManipulators::InstallSurfaceManipulatorMouseUpCallback(const SurfaceManipulator::MouseActionCallback& onMouseUpCallback) { if (m_surfaceManipulator) { @@ -242,7 +239,9 @@ namespace AzToolsFramework } void TranslationManipulators::ConfigureLinearView( - float axisLength, const AZ::Color& axis1Color, const AZ::Color& axis2Color, + float axisLength, + const AZ::Color& axis1Color, + const AZ::Color& axis2Color, const AZ::Color& axis3Color /*= AZ::Color(0.0f, 0.0f, 1.0f, 0.5f)*/) { const float coneLength = 0.28f; @@ -251,15 +250,13 @@ namespace AzToolsFramework const AZ::Color axesColor[] = { axis1Color, axis2Color, axis3Color }; - const auto configureLinearView = [lineWidth, coneLength, axisLength, coneRadius]( - LinearManipulator* linearManipulator, const AZ::Color& color) + const auto configureLinearView = + [lineWidth, coneLength, axisLength, coneRadius](LinearManipulator* linearManipulator, const AZ::Color& color) { ManipulatorViews views; - views.emplace_back(CreateManipulatorViewLine( - *linearManipulator, color, axisLength, lineWidth)); + views.emplace_back(CreateManipulatorViewLine(*linearManipulator, color, axisLength, lineWidth)); views.emplace_back(CreateManipulatorViewCone( - *linearManipulator, color, linearManipulator->GetAxis() * (axisLength - coneLength), - coneLength, coneRadius)); + *linearManipulator, color, linearManipulator->GetAxis() * (axisLength - coneLength), coneLength, coneRadius)); linearManipulator->SetViews(AZStd::move(views)); }; @@ -270,7 +267,8 @@ namespace AzToolsFramework } void TranslationManipulators::ConfigurePlanarView( - const AZ::Color& plane1Color, const AZ::Color& plane2Color /*= AZ::Color(0.0f, 1.0f, 0.0f, 0.5f)*/, + const AZ::Color& plane1Color, + const AZ::Color& plane2Color /*= AZ::Color(0.0f, 1.0f, 0.0f, 0.5f)*/, const AZ::Color& plane3Color /*= AZ::Color(0.0f, 0.0f, 1.0f, 0.5f)*/) { const float planeSize = 0.6f; @@ -278,34 +276,29 @@ namespace AzToolsFramework for (size_t manipulatorIndex = 0; manipulatorIndex < m_planarManipulators.size(); ++manipulatorIndex) { - const AZStd::shared_ptr manipulatorView = - CreateManipulatorViewQuad( - *m_planarManipulators[manipulatorIndex], planesColor[manipulatorIndex], - planesColor[(manipulatorIndex + 1) % 3], - planeSize); + const AZStd::shared_ptr manipulatorView = CreateManipulatorViewQuad( + *m_planarManipulators[manipulatorIndex], planesColor[manipulatorIndex], planesColor[(manipulatorIndex + 1) % 3], planeSize); - m_planarManipulators[manipulatorIndex]->SetViews(ManipulatorViews{manipulatorView}); + m_planarManipulators[manipulatorIndex]->SetViews(ManipulatorViews{ manipulatorView }); } } - void TranslationManipulators::ConfigureSurfaceView( - const float radius, const AZ::Color& color) + void TranslationManipulators::ConfigureSurfaceView(const float radius, const AZ::Color& color) { if (m_surfaceManipulator) { - m_surfaceManipulator->SetView(CreateManipulatorViewSphere(color, radius, - [](const ViewportInteraction::MouseInteraction& /*mouseInteraction*/, - bool mouseOver, const AZ::Color& defaultColor) -> AZ::Color - { - const AZ::Color color[2] = + m_surfaceManipulator->SetView(CreateManipulatorViewSphere( + color, radius, + [](const ViewportInteraction::MouseInteraction& /*mouseInteraction*/, bool mouseOver, + const AZ::Color& defaultColor) -> AZ::Color { - defaultColor, - Vector3ToVector4( - BaseManipulator::s_defaultMouseOverColor.GetAsVector3(), s_surfaceManipulatorTransparency) - }; + const AZ::Color color[2] = { + defaultColor, + Vector3ToVector4(BaseManipulator::s_defaultMouseOverColor.GetAsVector3(), s_surfaceManipulatorTransparency) + }; - return color[mouseOver]; - })); + return color[mouseOver]; + })); } } @@ -327,27 +320,17 @@ namespace AzToolsFramework } } - void ConfigureTranslationManipulatorAppearance3d( - TranslationManipulators* translationManipulators) + 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->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); } - void ConfigureTranslationManipulatorAppearance2d( - TranslationManipulators* translationManipulators) + void ConfigureTranslationManipulatorAppearance2d(TranslationManipulators* translationManipulators) { - translationManipulators->SetAxes( - AZ::Vector3::CreateAxisX(), - AZ::Vector3::CreateAxisY()); + translationManipulators->SetAxes(AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY()); translationManipulators->ConfigurePlanarView(s_xAxisColor); translationManipulators->ConfigureLinearView(s_axisLength, s_xAxisColor, s_yAxisColor); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.h index 0e7d3108aa..5f5f1a71e3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -19,16 +19,15 @@ namespace AzToolsFramework { - /// TranslationManipulators is an aggregation of 3 linear manipulators, 3 planar manipulators - /// and one surface manipulator who share the same transform. - class TranslationManipulators - : public Manipulators + //! TranslationManipulators is an aggregation of 3 linear manipulators, 3 planar manipulators + //! and one surface manipulator who share the same transform. + class TranslationManipulators : public Manipulators { public: AZ_RTTI(TranslationManipulators, "{D5E49EA2-30E0-42BC-A51D-6A7F87818260}") AZ_CLASS_ALLOCATOR(TranslationManipulators, AZ::SystemAllocator, 0) - /// How many dimensions does this translation manipulator have + //! How many dimensions does this translation manipulator have. enum class Dimensions { Two, @@ -55,9 +54,7 @@ namespace AzToolsFramework void SetLocalOrientationImpl(const AZ::Quaternion& localOrientation) override; void SetNonUniformScaleImpl(const AZ::Vector3& nonUniformScale) override; - void SetAxes( - const AZ::Vector3& axis1, const AZ::Vector3& axis2, - const AZ::Vector3& axis3 = AZ::Vector3::CreateAxisZ()); + void SetAxes(const AZ::Vector3& axis1, const AZ::Vector3& axis2, const AZ::Vector3& axis3 = AZ::Vector3::CreateAxisZ()); void ConfigurePlanarView( const AZ::Color& plane1Color, @@ -66,11 +63,11 @@ namespace AzToolsFramework void ConfigureLinearView( float axisLength, - const AZ::Color& axis1Color, const AZ::Color& axis2Color, + const AZ::Color& axis1Color, + const AZ::Color& axis2Color, const AZ::Color& axis3Color = AZ::Color(0.0f, 0.0f, 1.0f, 0.5f)); - void ConfigureSurfaceView( - float radius, const AZ::Color& color); + void ConfigureSurfaceView(float radius, const AZ::Color& color); private: AZ_DISABLE_COPY_MOVE(TranslationManipulators) @@ -78,37 +75,43 @@ namespace AzToolsFramework // Manipulators void ProcessManipulators(const AZStd::function&) override; - const Dimensions m_dimensions; ///< How many dimensions of freedom does this manipulator have. + const Dimensions m_dimensions; //!< How many dimensions of freedom does this manipulator have. AZStd::vector> m_linearManipulators; AZStd::vector> m_planarManipulators; AZStd::shared_ptr m_surfaceManipulator = nullptr; }; - /// IndexedTranslationManipulator wraps a standard TranslationManipulators and allows it to be linked - /// to a particular index in a list of vertices/points. + //! IndexedTranslationManipulator wraps a standard TranslationManipulators and allows it to be linked + //! to a particular index in a list of vertices/points. template struct IndexedTranslationManipulator { explicit IndexedTranslationManipulator( - TranslationManipulators::Dimensions dimensions, AZ::u64 vertIndex, - const Vertex& position, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale) - : m_manipulator(dimensions, worldFromLocal, nonUniformScale) + TranslationManipulators::Dimensions dimensions, + AZ::u64 vertIndex, + const Vertex& position, + const AZ::Transform& worldFromLocal, + const AZ::Vector3& nonUniformScale) + : m_manipulator(dimensions, worldFromLocal, nonUniformScale) { m_vertices.push_back({ position, Vertex::CreateZero(), vertIndex }); } - /// Store vertex start position as manipulator event occurs, index refers to location in container. + //! Store vertex start position as manipulator event occurs, index refers to location in container. struct VertexLookup { Vertex m_start; Vertex m_offset; AZ::u64 m_index; - Vertex CurrentPosition() const { return m_start + m_offset; } + Vertex CurrentPosition() const + { + return m_start + m_offset; + } }; - /// Helper to iterate over all vertices stored by the manipulator. + //! Helper to iterate over all vertices stored by the manipulator. void Process(AZStd::function fn) { for (VertexLookup& vertex : m_vertices) @@ -117,16 +120,14 @@ namespace AzToolsFramework } } - AZStd::vector m_vertices; ///< List of vertices currently associated with this translation manipulator. + AZStd::vector m_vertices; //!< List of vertices currently associated with this translation manipulator. TranslationManipulators m_manipulator; }; - /// Function pointer to configure how a translation manipulator should look and behave (dimensions/axes/views). - using TranslationManipulatorConfiguratorFn = void(*)(TranslationManipulators*); + //! Function pointer to configure how a translation manipulator should look and behave (dimensions/axes/views). + using TranslationManipulatorConfiguratorFn = void (*)(TranslationManipulators*); - void ConfigureTranslationManipulatorAppearance3d( - TranslationManipulators* translationManipulators); - void ConfigureTranslationManipulatorAppearance2d( - TranslationManipulators* translationManipulators); + void ConfigureTranslationManipulatorAppearance3d(TranslationManipulators* translationManipulators); + void ConfigureTranslationManipulatorAppearance2d(TranslationManipulators* translationManipulators); } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/BoundInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/BoundInterface.h index 3b579e889e..e8e822ca32 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/BoundInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/BoundInterface.h @@ -1,14 +1,15 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + #pragma once #include @@ -16,30 +17,44 @@ namespace AzToolsFramework { - /** - * Provide unique type alias for AZ::u64 for manipulator, bounds and manager. - */ + //! Provide unique type alias for AZ::u64 for manipulator, bounds and manager. template class IdType { public: explicit IdType(AZ::u64 id = 0) - : m_id(id) {} - operator AZ::u64() const { return m_id; } + : m_id(id) + { + } + + operator AZ::u64() const + { + return m_id; + } + + bool operator==(IdType other) const + { + return m_id == other.m_id; + } + + bool operator!=(IdType other) const + { + return m_id != other.m_id; + } - bool operator==(IdType other) const { return m_id == other.m_id; } - bool operator!=(IdType other) const { return m_id != other.m_id; } IdType& operator++() // pre-increment { ++m_id; return *this; } + IdType operator++(int) // post-increment { IdType temp = *this; ++*this; return temp; } + private: AZ::u64 m_id; }; @@ -51,10 +66,8 @@ namespace AzToolsFramework using RegisteredBoundId = IdType; static const RegisteredBoundId InvalidBoundId = RegisteredBoundId(0); - /** - * This class serves as the base class for the actual bound shapes that various DefaultContextBoundManager-derived - * classes return from the function CreateShape. - */ + //! This class serves as the base class for the actual bound shapes that various DefaultContextBoundManager-derived + //! classes return from the function CreateShape. class BoundShapeInterface { public: @@ -63,25 +76,33 @@ namespace AzToolsFramework explicit BoundShapeInterface(const RegisteredBoundId boundId) : m_boundId(boundId) , m_valid(false) - {} + { + } virtual ~BoundShapeInterface() = default; - RegisteredBoundId GetBoundId() const { return m_boundId; } + RegisteredBoundId GetBoundId() const + { + return m_boundId; + } - /** - * @param rayOrigin The origin of the ray to test with. - * @param rayDir The direction of the ray to test with. - * @param[out] rayIntersectionDistance The distance of the intersecting point closest to the ray origin. - * @return Boolean indicating whether there is a least one intersecting point between this bound shape and the ray. - */ - virtual bool IntersectRay( - const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDir, float& rayIntersectionDistance) = 0; + //! @param rayOrigin The origin of the ray to test with. + //! @param rayDir The direction of the ray to test with. + //! @param[out] rayIntersectionDistance The distance of the intersecting point closest to the ray origin. + //! @return Boolean indicating whether there is a least one intersecting point between this bound shape and the ray. + virtual bool IntersectRay(const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDir, float& rayIntersectionDistance) = 0; virtual void SetShapeData(const BoundRequestShapeBase& shapeData) = 0; - void SetValidity(bool valid) { m_valid = valid; } - bool IsValid() const { return m_valid; } + void SetValidity(bool valid) + { + m_valid = valid; + } + + bool IsValid() const + { + return m_valid; + } private: RegisteredBoundId m_boundId; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/ContextBoundAPI.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/ContextBoundAPI.h index 4a053ea758..7127ac82ee 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/ContextBoundAPI.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/ContextBoundAPI.h @@ -1,22 +1,22 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once #include -#include #include #include #include +#include #include #include #include @@ -27,9 +27,7 @@ namespace AzToolsFramework { namespace Picking { - /** - * An interface concrete shape types can implement to create specific BoundShapeInterfaces. - */ + //! An interface concrete shape types can implement to create specific BoundShapeInterfaces. class BoundRequestShapeBase { public: @@ -114,11 +112,9 @@ namespace AzToolsFramework float m_radius; }; - /** - * The quad shape consists of 4 points in 3D space. Please set them from \ref m_corner1 to \ref m_corner4 - * in either clock-wise winding or counter clock-wise winding. In another word, \ref m_corner1 and - * \ref corner_2 cannot be diagonal corners. - */ + //! The quad shape consists of 4 points in 3D space. Please set them from \ref m_corner1 to \ref m_corner4 + //! in either clock-wise winding or counter clock-wise winding. In another word, \ref m_corner1 and + //! \ref corner_2 cannot be diagonal corners. class BoundShapeQuad : public BoundRequestShapeBase { public: @@ -138,9 +134,7 @@ namespace AzToolsFramework AZ::Vector3 m_corner4; }; - /** - * The line segment consists of two points in 3D space defining a line the user can interact with. - */ + //! The line segment consists of two points in 3D space defining a line the user can interact with. class BoundShapeLineSegment : public BoundRequestShapeBase { public: @@ -159,10 +153,8 @@ namespace AzToolsFramework float m_width; }; - /** - * The torus shape is approximated by a cylinder whose radius is the sum of the torus's major radius - * and minor radius and height is twice the torus's minor radius. - */ + //! The torus shape is approximated by a cylinder whose radius is the sum of the torus's major radius + //! and minor radius and height is twice the torus's minor radius. class BoundShapeTorus : public BoundRequestShapeBase { public: @@ -182,10 +174,8 @@ namespace AzToolsFramework float m_minorRadius; }; - /** - * The spline is specified by a number of vertices. A piecewise approximation of the curve - * is computed by using a number of linear steps (defined by the granularity of the curve). - */ + //! The spline is specified by a number of vertices. A piecewise approximation of the curve + //! is computed by using a number of linear steps (defined by the granularity of the curve). class BoundShapeSpline : public BoundRequestShapeBase { public: @@ -204,16 +194,14 @@ namespace AzToolsFramework float m_width; }; - /** - * Ray query for intersection against bounds. - */ + //! Ray query for intersection against bounds. struct RaySelectInfo { - AZ::Vector3 m_origin; ///< Start of ray. - AZ::Vector3 m_direction; ///< Direction of ray - make sure m_direction is unit length. - AZStd::vector> m_boundIdsHit; ///< Store the id of the intersected bound - ///< and the parameter of the corresponding - ///< intersecting point. + AZ::Vector3 m_origin; //!< Start of ray. + AZ::Vector3 m_direction; //!< Direction of ray - make sure m_direction is unit length. + AZStd::vector> m_boundIdsHit; //!< Store the id of the intersected bound + //!< and the parameter of the corresponding + //!< intersecting point. }; } // namespace Picking } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBoundManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBoundManager.cpp index d838413a67..342dc4b99e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBoundManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBoundManager.cpp @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "ManipulatorBoundManager.h" @@ -16,8 +16,7 @@ namespace AzToolsFramework { namespace Picking { - RegisteredBoundId ManipulatorBoundManager::UpdateOrRegisterBound( - const BoundRequestShapeBase& shapeData, RegisteredBoundId boundId) + RegisteredBoundId ManipulatorBoundManager::UpdateOrRegisterBound(const BoundRequestShapeBase& shapeData, RegisteredBoundId boundId) { if (boundId == InvalidBoundId) { @@ -25,8 +24,7 @@ namespace AzToolsFramework boundId = m_nextBoundId++; } - if (auto result = m_boundIdToShapeMap.find(boundId); - result == m_boundIdToShapeMap.end()) + if (auto result = m_boundIdToShapeMap.find(boundId); result == m_boundIdToShapeMap.end()) { if (AZStd::shared_ptr createdShape = CreateShape(shapeData, boundId)) { @@ -49,19 +47,16 @@ namespace AzToolsFramework void ManipulatorBoundManager::UnregisterBound(const RegisteredBoundId boundId) { - if (const auto findIter = m_boundIdToShapeMap.find(boundId); - findIter != m_boundIdToShapeMap.end()) + if (const auto findIter = m_boundIdToShapeMap.find(boundId); findIter != m_boundIdToShapeMap.end()) { DeleteShape(findIter->second.get()); m_boundIdToShapeMap.erase(findIter); } } - void ManipulatorBoundManager::SetBoundValidity( - const RegisteredBoundId boundId, const bool valid) + void ManipulatorBoundManager::SetBoundValidity(const RegisteredBoundId boundId, const bool valid) { - if (auto found = m_boundIdToShapeMap.find(boundId); - found != m_boundIdToShapeMap.end()) + if (auto found = m_boundIdToShapeMap.find(boundId); found != m_boundIdToShapeMap.end()) { found->second->SetValidity(valid); } @@ -104,9 +99,9 @@ namespace AzToolsFramework const auto hitItr = AZStd::lower_bound( rayHits.begin(), rayHits.end(), BoundIdHitDistance(0, t), [](const BoundIdHitDistance& lhs, const BoundIdHitDistance& rhs) - { - return lhs.second < rhs.second; - }); + { + return lhs.second < rhs.second; + }); rayHits.insert(hitItr, AZStd::make_pair(bound->GetBoundId(), t)); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBoundManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBoundManager.h index 78e714c256..504f06bb49 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBoundManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBoundManager.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -21,10 +21,8 @@ namespace AzToolsFramework { class BoundShapeInterface; - /** - * Handle creating, destroying and storing all active manipulator - * bounds for performing raycasts/picking against. - */ + //! Handle creating, destroying and storing all active manipulator + //! bounds for performing raycasts/picking against. class ManipulatorBoundManager { public: @@ -35,21 +33,19 @@ namespace AzToolsFramework ManipulatorBoundManager& operator=(const ManipulatorBoundManager&) = delete; ~ManipulatorBoundManager() = default; - RegisteredBoundId UpdateOrRegisterBound( - const BoundRequestShapeBase& shapeData, RegisteredBoundId id); + RegisteredBoundId UpdateOrRegisterBound(const BoundRequestShapeBase& shapeData, RegisteredBoundId id); void UnregisterBound(RegisteredBoundId boundId); void SetBoundValidity(RegisteredBoundId boundId, bool valid); - void RaySelect(RaySelectInfo &rayInfo); + void RaySelect(RaySelectInfo& rayInfo); private: - AZStd::shared_ptr CreateShape( - const BoundRequestShapeBase& ptrShape, RegisteredBoundId id); + AZStd::shared_ptr CreateShape(const BoundRequestShapeBase& ptrShape, RegisteredBoundId id); void DeleteShape(const BoundShapeInterface* boundShape); AZStd::unordered_map> m_boundIdToShapeMap; - AZStd::vector> m_bounds; ///< All current manipulator bounds. + AZStd::vector> m_bounds; //!< All current manipulator bounds. - RegisteredBoundId m_nextBoundId = RegisteredBoundId(1); ///< Next bound id to use when a bound is registered. + RegisteredBoundId m_nextBoundId = RegisteredBoundId(1); //!< Next bound id to use when a bound is registered. }; } // namespace Picking } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBounds.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBounds.cpp index 8a9ebbacbc..8dca45cc9a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBounds.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBounds.cpp @@ -1,17 +1,18 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include #include +#include #include #include @@ -23,8 +24,7 @@ namespace AzToolsFramework const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDirection, float& rayIntersectionDistance) { float vecRayIntersectionDistance; - if (AZ::Intersect::IntersectRaySphere( - rayOrigin, rayDirection, m_center, m_radius, vecRayIntersectionDistance) > 0) + if (AZ::Intersect::IntersectRaySphere(rayOrigin, rayDirection, m_center, m_radius, vecRayIntersectionDistance) > 0) { rayIntersectionDistance = vecRayIntersectionDistance; return true; @@ -45,8 +45,9 @@ namespace AzToolsFramework bool ManipulatorBoundBox::IntersectRay( const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDirection, float& rayIntersectionDistance) { - return AZ::Intersect::IntersectRayBox(rayOrigin, rayDirection, m_center, m_axis1, m_axis2, m_axis3, - m_halfExtents.GetX(), m_halfExtents.GetY(), m_halfExtents.GetZ(), rayIntersectionDistance) > 0; + return AZ::Intersect::IntersectRayBox( + rayOrigin, rayDirection, m_center, m_axis1, m_axis2, m_axis3, m_halfExtents.GetX(), m_halfExtents.GetY(), + m_halfExtents.GetZ(), rayIntersectionDistance) > 0; } void ManipulatorBoundBox::SetShapeData(const BoundRequestShapeBase& shapeData) @@ -66,8 +67,7 @@ namespace AzToolsFramework { float t1 = std::numeric_limits::max(); float t2 = std::numeric_limits::max(); - if (AZ::Intersect::IntersectRayCappedCylinder( - rayOrigin, rayDirection, m_base, m_axis, m_height, m_radius, t1, t2) > 0) + if (AZ::Intersect::IntersectRayCappedCylinder(rayOrigin, rayDirection, m_base, m_axis, m_height, m_radius, t1, t2) > 0) { rayIntersectionDistance = AZStd::GetMin(t1, t2); return true; @@ -92,8 +92,7 @@ namespace AzToolsFramework { float t1 = std::numeric_limits::max(); float t2 = std::numeric_limits::max(); - if (AZ::Intersect::IntersectRayCone( - rayOrigin, rayDirection, m_apexPosition, m_dir, m_height, m_radius, t1, t2) > 0) + if (AZ::Intersect::IntersectRayCone(rayOrigin, rayDirection, m_apexPosition, m_dir, m_height, m_radius, t1, t2) > 0) { rayIntersectionDistance = AZStd::GetMin(t1, t2); return true; @@ -117,7 +116,7 @@ namespace AzToolsFramework const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDirection, float& rayIntersectionDistance) { return AZ::Intersect::IntersectRayQuad( - rayOrigin, rayDirection, m_corner1, m_corner2, m_corner3, m_corner4, rayIntersectionDistance) > 0; + rayOrigin, rayDirection, m_corner1, m_corner2, m_corner3, m_corner4, rayIntersectionDistance) > 0; } void ManipulatorBoundQuad::SetShapeData(const BoundRequestShapeBase& shapeData) @@ -157,8 +156,7 @@ namespace AzToolsFramework float rayProportion, lineSegmentProportion; // note: here out param is proportion/percentage of line AZ::Intersect::ClosestSegmentSegment( - rayOrigin, rayOrigin + rayDirection * rayLength, - m_worldStart, m_worldEnd, rayProportion, lineSegmentProportion, + rayOrigin, rayOrigin + rayDirection * rayLength, m_worldStart, m_worldEnd, rayProportion, lineSegmentProportion, closestPosRay, closestPosLineSegment); float distanceFromLine = (closestPosRay - closestPosLineSegment).GetLength(); @@ -188,8 +186,7 @@ namespace AzToolsFramework { if (const AZStd::shared_ptr spline = m_spline.lock()) { - AZ::RaySplineQueryResult splineQueryResult = - AZ::IntersectSpline(m_transform, rayOrigin, rayDirection, *spline); + AZ::RaySplineQueryResult splineQueryResult = AZ::IntersectSpline(m_transform, rayOrigin, rayDirection, *spline); if (splineQueryResult.m_distanceSq <= m_width * m_width) { @@ -214,22 +211,25 @@ namespace AzToolsFramework } bool IntersectHollowCylinder( - const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDirection, - const AZ::Vector3& center, const AZ::Vector3& axis, - const float minorRadius, const float majorRadius, + const AZ::Vector3& rayOrigin, + const AZ::Vector3& rayDirection, + const AZ::Vector3& center, + const AZ::Vector3& axis, + const float minorRadius, + const float majorRadius, float& rayIntersectionDistance) { - float t1 = std::numeric_limits::max(); - float t2 = std::numeric_limits::max(); + float t1 = AZStd::numeric_limits::max(); + float t2 = AZStd::numeric_limits::max(); const AZ::Vector3 base = center - axis * minorRadius; if (AZ::Intersect::IntersectRayCappedCylinder( - rayOrigin, rayDirection, base, axis, minorRadius * 2.0f, majorRadius + minorRadius, t1, t2) > 0) + rayOrigin, rayDirection, base, axis, minorRadius * 2.0f, majorRadius + minorRadius, t1, t2) > 0) { - const float thresholdSq = powf(majorRadius - minorRadius, 2.0f); + const float threshold = majorRadius - minorRadius; + const float thresholdSq = threshold * threshold; // util lambda used for distance checks at both 't' values - const auto validHolowCylinderHit = - [&rayOrigin, &rayDirection, ¢er, thresholdSq](const float t) + const auto validHolowCylinderHit = [&rayOrigin, &rayDirection, ¢er, thresholdSq](const float t) { // only return a valid intersection if the hit was // not in the 'hollow' part of the cylinder diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBounds.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBounds.h index ca9eeded11..d72e11fd0a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBounds.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBounds.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -27,36 +27,36 @@ namespace AzToolsFramework { namespace Picking { - class ManipulatorBoundSphere - : public BoundShapeInterface + class ManipulatorBoundSphere : public BoundShapeInterface { public: AZ_RTTI(ManipulatorBoundSphere, "{64D1B863-F574-4B31-A4F2-C9744D8567B3}", BoundShapeInterface); AZ_CLASS_ALLOCATOR(ManipulatorBoundSphere, AZ::SystemAllocator, 0); explicit ManipulatorBoundSphere(RegisteredBoundId boundId) - : BoundShapeInterface(boundId) {} + : BoundShapeInterface(boundId) + { + } - bool IntersectRay( - const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDir, float& rayIntersectionDistance) override; + bool IntersectRay(const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDir, float& rayIntersectionDistance) override; void SetShapeData(const BoundRequestShapeBase& shapeData) override; AZ::Vector3 m_center = AZ::Vector3::CreateZero(); float m_radius = 0.0f; }; - class ManipulatorBoundBox - : public BoundShapeInterface + class ManipulatorBoundBox : public BoundShapeInterface { public: AZ_RTTI(ManipulatorBoundBox, "{3AD46067-933F-49B4-82E1-DBF12C7BC02E}", BoundShapeInterface); AZ_CLASS_ALLOCATOR(ManipulatorBoundBox, AZ::SystemAllocator, 0); explicit ManipulatorBoundBox(RegisteredBoundId boundId) - : BoundShapeInterface(boundId) {} + : BoundShapeInterface(boundId) + { + } - bool IntersectRay( - const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDir, float& rayIntersectionDistance) override; + bool IntersectRay(const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDir, float& rayIntersectionDistance) override; void SetShapeData(const BoundRequestShapeBase& shapeData) override; AZ::Vector3 m_center = AZ::Vector3::CreateZero(); @@ -66,38 +66,38 @@ namespace AzToolsFramework AZ::Vector3 m_halfExtents = AZ::Vector3::CreateZero(); }; - class ManipulatorBoundCylinder - : public BoundShapeInterface + class ManipulatorBoundCylinder : public BoundShapeInterface { public: AZ_RTTI(ManipulatorBoundCylinder, "{D248F9E4-22E6-41A8-898D-704DF307B533}", BoundShapeInterface); AZ_CLASS_ALLOCATOR(ManipulatorBoundCylinder, AZ::SystemAllocator, 0); explicit ManipulatorBoundCylinder(RegisteredBoundId boundId) - : BoundShapeInterface(boundId) {} + : BoundShapeInterface(boundId) + { + } - bool IntersectRay( - const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDir, float& rayIntersectionDistance) override; + bool IntersectRay(const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDir, float& rayIntersectionDistance) override; void SetShapeData(const BoundRequestShapeBase& shapeData) override; - AZ::Vector3 m_base = AZ::Vector3::CreateZero(); ///< The center of the circle at the base of the cylinder. + AZ::Vector3 m_base = AZ::Vector3::CreateZero(); //!< The center of the circle at the base of the cylinder. AZ::Vector3 m_axis = AZ::Vector3::CreateZero(); float m_height = 0.0f; float m_radius = 0.0f; }; - class ManipulatorBoundCone - : public BoundShapeInterface + class ManipulatorBoundCone : public BoundShapeInterface { public: AZ_RTTI(ManipulatorBoundCone, "{9430440D-DFF2-4A60-9073-507C4E9DD65D}", BoundShapeInterface); AZ_CLASS_ALLOCATOR(ManipulatorBoundCone, AZ::SystemAllocator, 0); explicit ManipulatorBoundCone(RegisteredBoundId boundId) - : BoundShapeInterface(boundId) {} + : BoundShapeInterface(boundId) + { + } - bool IntersectRay( - const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDir, float& rayIntersectionDistance) override; + bool IntersectRay(const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDir, float& rayIntersectionDistance) override; void SetShapeData(const BoundRequestShapeBase& shapeData) override; AZ::Vector3 m_apexPosition = AZ::Vector3::CreateZero(); @@ -106,23 +106,21 @@ namespace AzToolsFramework float m_height = 0.0f; }; - /** - * The quad shape consists of 4 points in 3D space. Please set them from \ref m_corner1 to \ref m_corner4 - * in either clock-wise winding or counter clock-wise winding. In another word, \ref m_corner1 and - * \ref corner_2 cannot be diagonal corners. - */ - class ManipulatorBoundQuad - : public BoundShapeInterface + //! The quad shape consists of 4 points in 3D space. Please set them from \ref m_corner1 to \ref m_corner4 + //! in either clock-wise winding or counter clock-wise winding. In another word, \ref m_corner1 and + //! \ref corner_2 cannot be diagonal corners. + class ManipulatorBoundQuad : public BoundShapeInterface { public: AZ_RTTI(ManipulatorBoundQuad, "{3CDED61C-5786-4299-B5F2-5970DE4457AD}", BoundShapeInterface); AZ_CLASS_ALLOCATOR(ManipulatorBoundQuad, AZ::SystemAllocator, 0); explicit ManipulatorBoundQuad(RegisteredBoundId boundId) - : BoundShapeInterface(boundId) {} + : BoundShapeInterface(boundId) + { + } - bool IntersectRay( - const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDir, float& rayIntersectionDistance) override; + bool IntersectRay(const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDir, float& rayIntersectionDistance) override; void SetShapeData(const BoundRequestShapeBase& shapeData) override; AZ::Vector3 m_corner1 = AZ::Vector3::CreateZero(); @@ -131,18 +129,18 @@ namespace AzToolsFramework AZ::Vector3 m_corner4 = AZ::Vector3::CreateZero(); }; - class ManipulatorBoundTorus - : public BoundShapeInterface + class ManipulatorBoundTorus : public BoundShapeInterface { public: AZ_RTTI(ManipulatorBoundTorus, "{46E4711C-178A-4F97-BC14-A048D096E7A1}", BoundShapeInterface); AZ_CLASS_ALLOCATOR(ManipulatorBoundTorus, AZ::SystemAllocator, 0); explicit ManipulatorBoundTorus(RegisteredBoundId boundId) - : BoundShapeInterface(boundId) {} + : BoundShapeInterface(boundId) + { + } - bool IntersectRay( - const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDir, float& rayIntersectionDistance) override; + bool IntersectRay(const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDir, float& rayIntersectionDistance) override; void SetShapeData(const BoundRequestShapeBase& shapeData) override; // Approximate a torus as a thin cylinder. A ray intersects a torus when the ray and the torus' @@ -150,22 +148,22 @@ namespace AzToolsFramework // center of the torus. AZ::Vector3 m_center = AZ::Vector3::CreateZero(); AZ::Vector3 m_axis = AZ::Vector3::CreateZero(); - float m_majorRadius = 0.0f; ///< Usually denoted as "R", the distance from the center of the tube to the center of the torus. - float m_minorRadius = 0.0f; ///< Usually denoted as "r", the radius of the tube. + float m_majorRadius = 0.0f; //!< Usually denoted as "R", the distance from the center of the tube to the center of the torus. + float m_minorRadius = 0.0f; //!< Usually denoted as "r", the radius of the tube. }; - class ManipulatorBoundLineSegment - : public BoundShapeInterface + class ManipulatorBoundLineSegment : public BoundShapeInterface { public: AZ_RTTI(ManipulatorBoundLineSegment, "{66801554-1C1A-4E79-B1E7-342DFA779D53}", BoundShapeInterface); AZ_CLASS_ALLOCATOR(ManipulatorBoundLineSegment, AZ::SystemAllocator, 0); explicit ManipulatorBoundLineSegment(RegisteredBoundId boundId) - : BoundShapeInterface(boundId) {} + : BoundShapeInterface(boundId) + { + } - bool IntersectRay( - const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDir, float& rayIntersectionDistance) override; + bool IntersectRay(const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDir, float& rayIntersectionDistance) override; void SetShapeData(const BoundRequestShapeBase& shapeData) override; AZ::Vector3 m_worldStart = AZ::Vector3::CreateZero(); @@ -173,18 +171,18 @@ namespace AzToolsFramework float m_width = 0.0f; }; - class ManipulatorBoundSpline - : public BoundShapeInterface + class ManipulatorBoundSpline : public BoundShapeInterface { public: AZ_RTTI(ManipulatorBoundSpline, "{777760FF-8547-45AD-876F-16BA4D9D0584}", BoundShapeInterface); AZ_CLASS_ALLOCATOR(ManipulatorBoundSpline, AZ::SystemAllocator, 0); explicit ManipulatorBoundSpline(RegisteredBoundId boundId) - : BoundShapeInterface(boundId) {} + : BoundShapeInterface(boundId) + { + } - bool IntersectRay( - const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDir, float& rayIntersectionDistance) override; + bool IntersectRay(const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDir, float& rayIntersectionDistance) override; void SetShapeData(const BoundRequestShapeBase& shapeData) override; AZStd::weak_ptr m_spline; @@ -192,11 +190,14 @@ namespace AzToolsFramework float m_width = 0.0f; }; - /// Approximate intersection with a torus-like shape. + //! Approximate intersection with a torus-like shape. bool IntersectHollowCylinder( - const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDirection, - const AZ::Vector3& center, const AZ::Vector3& axis, - float minorRadius, float majorRadius, + const AZ::Vector3& rayOrigin, + const AZ::Vector3& rayDirection, + const AZ::Vector3& center, + const AZ::Vector3& axis, + float minorRadius, + float majorRadius, float& rayIntersectionDistance); } // namespace Picking diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/EditorContextMenu.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/EditorContextMenu.cpp index 7fa9724d65..308f0f074f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/EditorContextMenu.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/EditorContextMenu.cpp @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "EditorContextMenu.h" @@ -16,8 +16,7 @@ namespace AzToolsFramework { - void EditorContextMenuUpdate( - EditorContextMenu& contextMenu, const ViewportInteraction::MouseInteractionEvent& mouseInteraction) + void EditorContextMenuUpdate(EditorContextMenu& contextMenu, const ViewportInteraction::MouseInteractionEvent& mouseInteraction) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -26,18 +25,17 @@ namespace AzToolsFramework mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down) { contextMenu.m_shouldOpen = true; - contextMenu.m_clickPoint = ViewportInteraction::QPointFromScreenPoint( - mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates); + contextMenu.m_clickPoint = + ViewportInteraction::QPointFromScreenPoint(mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates); } // disable shouldOpen if right clicking an moving the mouse if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Move) { - const QPoint currentScreenCoords = ViewportInteraction::QPointFromScreenPoint( - mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates); + const QPoint currentScreenCoords = + ViewportInteraction::QPointFromScreenPoint(mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates); - contextMenu.m_shouldOpen = contextMenu.m_shouldOpen && - (currentScreenCoords - contextMenu.m_clickPoint).manhattanLength() < 2; + contextMenu.m_shouldOpen = contextMenu.m_shouldOpen && (currentScreenCoords - contextMenu.m_clickPoint).manhattanLength() < 2; } // do show the context menu @@ -58,16 +56,16 @@ namespace AzToolsFramework // Populate global context menu. const int contextMenuFlag = 0; EditorEvents::Bus::BroadcastReverse( - &EditorEvents::PopulateEditorGlobalContextMenu, - contextMenu.m_menu.data(), AzFramework::Vector2FromScreenPoint( - mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates), + &EditorEvents::PopulateEditorGlobalContextMenu, contextMenu.m_menu.data(), + AzFramework::Vector2FromScreenPoint(mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates), contextMenuFlag); if (!contextMenu.m_menu->isEmpty()) { - contextMenu.m_menu->exec(QCursor::pos()); + // Use popup instead of exec; this avoids blocking input event processing while the menu dialog is active + contextMenu.m_menu->popup(QCursor::pos()); } } } } -} +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/EditorContextMenu.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/EditorContextMenu.h index ebce02fa4c..ff5bffc544 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/EditorContextMenu.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/EditorContextMenu.h @@ -1,22 +1,22 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once #include +#include #include #include -#include namespace AzToolsFramework { @@ -25,7 +25,7 @@ namespace AzToolsFramework struct MouseInteractionEvent; } - /// State of when and where the right-click context menu should appear. + //! State of when and where the right-click context menu should appear. struct EditorContextMenu final { bool m_shouldOpen = false; @@ -33,8 +33,6 @@ namespace AzToolsFramework QPointer m_menu; }; - /// Update to run for context menu (when should it appear/disappear etc). - void EditorContextMenuUpdate( - EditorContextMenu& contextMenu, - const ViewportInteraction::MouseInteractionEvent& mouseInteraction); + //! Update to run for context menu (when should it appear/disappear etc). + void EditorContextMenuUpdate(EditorContextMenu& contextMenu, const ViewportInteraction::MouseInteractionEvent& mouseInteraction); } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/VertexContainerDisplay.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/VertexContainerDisplay.cpp index cd9382c497..10016fbb13 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/VertexContainerDisplay.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/VertexContainerDisplay.cpp @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "VertexContainerDisplay.h" @@ -23,8 +23,7 @@ namespace AzToolsFramework const AZ::Vector3 DefaultVertexTextOffset = AZ::Vector3(0.0f, 0.0f, -0.1f); void DisplayVertexContainerIndex( - AzFramework::DebugDisplayRequests& debugDisplay, - const AZ::Vector3& position, const size_t index, const float textSize) + AzFramework::DebugDisplayRequests& debugDisplay, const AZ::Vector3& position, const size_t index, const float textSize) { AZStd::string indexFormat = AZStd::string::format("[%zu]", index); debugDisplay.DrawTextLabel(position, textSize, indexFormat.c_str(), true); @@ -36,7 +35,8 @@ namespace AzToolsFramework const AZ::FixedVertices& vertices, const AZ::Transform& transform, const AZ::Vector3& nonUniformScale, - const bool selected, const float textSize, + const bool selected, + const float textSize, const AZ::Color& textColor, const AZ::Vector3& textOffset) { @@ -52,11 +52,12 @@ namespace AzToolsFramework if (vertices.GetVertex(vertIndex, vertex)) { DisplayVertexContainerIndex( - debugDisplay, transform.TransformPoint(nonUniformScale * (AdaptVertexOut(vertex) + textOffset)), vertIndex, textSize); + debugDisplay, transform.TransformPoint(nonUniformScale * (AdaptVertexOut(vertex) + textOffset)), vertIndex, + textSize); } } } - } + } // namespace VertexContainerDisplay // explicit template instantiations template void VertexContainerDisplay::DisplayVertexContainerIndices( @@ -64,7 +65,8 @@ namespace AzToolsFramework const AZ::FixedVertices& vertices, const AZ::Transform& transform, const AZ::Vector3& nonUniformScale, - bool selected, float textSize, + bool selected, + float textSize, const AZ::Color& textColor, const AZ::Vector3& textOffset); template void VertexContainerDisplay::DisplayVertexContainerIndices( @@ -72,7 +74,8 @@ namespace AzToolsFramework const AZ::FixedVertices& vertices, const AZ::Transform& transform, const AZ::Vector3& nonUniformScale, - bool selected, float textSize, + bool selected, + float textSize, const AZ::Color& textColor, const AZ::Vector3& textOffset); -} +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/VertexContainerDisplay.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/VertexContainerDisplay.h index 1717d770df..c734740d3c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/VertexContainerDisplay.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/VertexContainerDisplay.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -22,22 +22,23 @@ namespace AzFramework namespace AzToolsFramework { - /// Utility functions for rendering vertex container indices. + //! Utility functions for rendering vertex container indices. namespace VertexContainerDisplay { extern const float DefaultVertexTextSize; extern const AZ::Color DefaultVertexTextColor; extern const AZ::Vector3 DefaultVertexTextOffset; - /// Displays all vertex container indices as text at the position of each vertex when selected + //! Displays all vertex container indices as text at the position of each vertex when selected template void DisplayVertexContainerIndices( AzFramework::DebugDisplayRequests& debugDisplay, const AZ::FixedVertices& vertices, const AZ::Transform& transform, const AZ::Vector3& nonUniformScale, - bool selected, float textSize = DefaultVertexTextSize, + bool selected, + float textSize = DefaultVertexTextSize, const AZ::Color& textColor = DefaultVertexTextColor, const AZ::Vector3& textOffset = DefaultVertexTextOffset); - } + } // namespace VertexContainerDisplay } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h index 91eee18cb7..85250f2a32 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -17,8 +17,8 @@ #include #include #include -#include #include +#include #include #include @@ -31,45 +31,51 @@ namespace AzToolsFramework { namespace ViewportInteraction { - /// Result of handling mouse interaction. + //! Result of handling mouse interaction. enum class MouseInteractionResult { - Manipulator, ///< The manipulator manager handled the interaction. - Viewport, ///< The viewport handled the interaction. - None ///< The interaction was not handled. + Manipulator, //!< The manipulator manager handled the interaction. + Viewport, //!< The viewport handled the interaction. + None //!< The interaction was not handled. }; - /// Interface for handling mouse viewport events. + //! Interface for handling mouse viewport events. class MouseViewportRequests { public: - /// @cond + //! @cond virtual ~MouseViewportRequests() = default; - /// @endcond + //! @endcond - /// Implement this function to handle a particular mouse event. - virtual bool HandleMouseInteraction( - const MouseInteractionEvent& /*mouseInteraction*/) { return false; } + //! Implement this function to handle a particular mouse event. + virtual bool HandleMouseInteraction(const MouseInteractionEvent& /*mouseInteraction*/) + { + return false; + } }; - - /// Interface for internal handling mouse viewport events. + + //! Interface for internal handling mouse viewport events. class InternalMouseViewportRequests { public: - /// @cond + //! @cond virtual ~InternalMouseViewportRequests() = default; - /// @endcond + //! @endcond - /// Implement this function to have the viewport handle this mouse event. - virtual bool InternalHandleMouseViewportInteraction( - const MouseInteractionEvent& /*mouseInteraction*/) { return false; } + //! Implement this function to have the viewport handle this mouse event. + virtual bool InternalHandleMouseViewportInteraction(const MouseInteractionEvent& /*mouseInteraction*/) + { + return false; + } - /// Implement this function to have manipulators handle this mouse event. - virtual bool InternalHandleMouseManipulatorInteraction( - const MouseInteractionEvent& /*mouseInteraction*/) { return false; } + //! Implement this function to have manipulators handle this mouse event. + virtual bool InternalHandleMouseManipulatorInteraction(const MouseInteractionEvent& /*mouseInteraction*/) + { + return false; + } - /// Helper to call both viewport and manipulator handle mouse events - /// @note Manipulators always attempt to intercept the event first. + //! Helper to call both viewport and manipulator handle mouse events + //! @note Manipulators always attempt to intercept the event first. MouseInteractionResult InternalHandleAllMouseInteractions(const MouseInteractionEvent& mouseInteraction); }; @@ -90,117 +96,118 @@ namespace AzToolsFramework } } - /// Interface for viewport selection behaviors. + //! Interface for viewport selection behaviors. class ViewportDisplayNotifications { public: - /// @cond + //! @cond virtual ~ViewportDisplayNotifications() = default; - /// @endcond + //! @endcond - /// Display drawing in world space. - /// \ref DisplayViewportSelection is called from \ref EditorInteractionSystemComponent::DisplayViewport. - /// DisplayViewport exists on the \ref AzFramework::ViewportDebugDisplayEventBus and is called from \ref CRenderViewport. - /// \ref DisplayViewportSelection is called after \ref CalculateVisibleEntityDatas on the \ref EditorVisibleEntityDataCache, - /// this ensures usage of the entity cache will be up to date (do not implement \ref AzFramework::ViewportDebugDisplayEventBus - /// directly if wishing to use the \ref EditorVisibleEntityDataCache). + //! Display drawing in world space. + //! \ref DisplayViewportSelection is called from \ref EditorInteractionSystemComponent::DisplayViewport. + //! DisplayViewport exists on the \ref AzFramework::ViewportDebugDisplayEventBus and is called from \ref CRenderViewport. + //! \ref DisplayViewportSelection is called after \ref CalculateVisibleEntityDatas on the \ref EditorVisibleEntityDataCache, + //! this ensures usage of the entity cache will be up to date (do not implement \ref AzFramework::ViewportDebugDisplayEventBus + //! directly if wishing to use the \ref EditorVisibleEntityDataCache). virtual void DisplayViewportSelection( - const AzFramework::ViewportInfo& /*viewportInfo*/, - AzFramework::DebugDisplayRequests& /*debugDisplay*/) {} - /// Display drawing in screen space. - /// \ref DisplayViewportSelection2d is called after \ref DisplayViewportSelection when the viewport has been - /// configured to be orthographic in \ref CRenderViewport. All screen space drawing can be performed here. + const AzFramework::ViewportInfo& /*viewportInfo*/, AzFramework::DebugDisplayRequests& /*debugDisplay*/) + { + } + //! Display drawing in screen space. + //! \ref DisplayViewportSelection2d is called after \ref DisplayViewportSelection when the viewport has been + //! configured to be orthographic in \ref CRenderViewport. All screen space drawing can be performed here. virtual void DisplayViewportSelection2d( - const AzFramework::ViewportInfo& /*viewportInfo*/, - AzFramework::DebugDisplayRequests& /*debugDisplay*/) {} + const AzFramework::ViewportInfo& /*viewportInfo*/, AzFramework::DebugDisplayRequests& /*debugDisplay*/) + { + } }; - /// Interface for internal handling mouse viewport events and display notifications. - /// Implement this for types wishing to provide viewport functionality and - /// set it by using \ref EditorInteractionSystemViewportSelectionRequestBus. + //! Interface for internal handling mouse viewport events and display notifications. + //! Implement this for types wishing to provide viewport functionality and + //! set it by using \ref EditorInteractionSystemViewportSelectionRequestBus. class InternalViewportSelectionRequests : public ViewportDisplayNotifications , public InternalMouseViewportRequests { }; - /// Interface for handling mouse viewport events and display notifications. - /// Use this interface for composition types used by InternalViewportSelectionRequests. + //! Interface for handling mouse viewport events and display notifications. + //! Use this interface for composition types used by InternalViewportSelectionRequests. class ViewportSelectionRequests : public ViewportDisplayNotifications , public MouseViewportRequests { }; - /// The EBusTraits for ViewportInteractionRequests. - class ViewportEBusTraits - : public AZ::EBusTraits + //! The EBusTraits for ViewportInteractionRequests. + class ViewportEBusTraits : public AZ::EBusTraits { public: - using BusIdType = AzFramework::ViewportId; ///< ViewportId - used to address requests to this EBus. + using BusIdType = AzFramework::ViewportId; //!< ViewportId - used to address requests to this EBus. static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; }; - /// A ray projection, originating from a point and extending in a direction specified as a normal. + //! A ray projection, originating from a point and extending in a direction specified as a normal. struct ProjectedViewportRay { AZ::Vector3 origin; AZ::Vector3 direction; }; - /// Requests that can be made to the viewport to query and modify its state. + //! Requests that can be made to the viewport to query and modify its state. class ViewportInteractionRequests { public: - /// Return the current camera state for this viewport. + //! Return the current camera state for this viewport. virtual AzFramework::CameraState GetCameraState() = 0; - /// Return if grid snapping is enabled. + //! Return if grid snapping is enabled. virtual bool GridSnappingEnabled() = 0; - /// Return the grid snapping size. + //! Return the grid snapping size. virtual float GridSize() = 0; - /// Does the grid currently want to be displayed. + //! Does the grid currently want to be displayed. virtual bool ShowGrid() = 0; - /// Return if angle snapping is enabled. + //! Return if angle snapping is enabled. virtual bool AngleSnappingEnabled() = 0; - /// Return the angle snapping/step size. + //! Return the angle snapping/step size. virtual float AngleStep() = 0; - /// Transform a point in world space to screen space coordinates in Qt Widget space. - /// Multiply by DeviceScalingFactor to get the position in viewport pixel space. + //! Transform a point in world space to screen space coordinates in Qt Widget space. + //! Multiply by DeviceScalingFactor to get the position in viewport pixel space. virtual AzFramework::ScreenPoint ViewportWorldToScreen(const AZ::Vector3& worldPosition) = 0; - /// Transform a point from Qt widget screen space to world space based on the given clip space depth. - /// Depth specifies a relative camera depth to project in the range of [0.f, 1.f]. - /// Returns the world space position if successful. + //! Transform a point from Qt widget screen space to world space based on the given clip space depth. + //! Depth specifies a relative camera depth to project in the range of [0.f, 1.f]. + //! Returns the world space position if successful. virtual AZStd::optional ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition, float depth) = 0; - /// Casts a point in screen space to a ray in world space originating from the viewport camera frustum's near plane. - /// Returns a ray containing the ray's origin and a direction normal, if successful. + //! Casts a point in screen space to a ray in world space originating from the viewport camera frustum's near plane. + //! Returns a ray containing the ray's origin and a direction normal, if successful. virtual AZStd::optional ViewportScreenToWorldRay(const AzFramework::ScreenPoint& screenPosition) = 0; - /// Gets the DPI scaling factor that translates Qt widget space into viewport pixel space. + //! Gets the DPI scaling factor that translates Qt widget space into viewport pixel space. virtual float DeviceScalingFactor() = 0; protected: ~ViewportInteractionRequests() = default; }; - /// Interface to return only viewport specific settings (e.g. snapping). + //! Interface to return only viewport specific settings (e.g. snapping). class ViewportSettings { public: virtual ~ViewportSettings() = default; - /// Return if grid snapping is enabled. + //! Return if grid snapping is enabled. virtual bool GridSnappingEnabled() const = 0; - /// Return the grid snapping size. + //! Return the grid snapping size. virtual float GridSize() const = 0; - /// Does the grid currently want to be displayed. + //! Does the grid currently want to be displayed. virtual bool ShowGrid() const = 0; - /// Return if angle snapping is enabled. + //! Return if angle snapping is enabled. virtual bool AngleSnappingEnabled() const = 0; - /// Return the angle snapping/step size. + //! Return the angle snapping/step size. virtual float AngleStep() const = 0; }; - /// Type to inherit to implement ViewportInteractionRequests. + //! Type to inherit to implement ViewportInteractionRequests. using ViewportInteractionRequestBus = AZ::EBus; //! Requests to freeze the Viewport Input @@ -221,62 +228,62 @@ namespace AzToolsFramework //! Type to inherit to implement ViewportFreezeRequests. using ViewportFreezeRequestBus = AZ::EBus; - /// Viewport requests that are only guaranteed to be serviced by the Main Editor viewport. + //! Viewport requests that are only guaranteed to be serviced by the Main Editor viewport. class MainEditorViewportInteractionRequests { public: - /// Given a point in screen space, return the picked entity (if any). - /// Picked EntityId will be returned, InvalidEntityId will be returned on failure. + //! Given a point in screen space, return the picked entity (if any). + //! Picked EntityId will be returned, InvalidEntityId will be returned on failure. virtual AZ::EntityId PickEntity(const AzFramework::ScreenPoint& point) = 0; - /// Given a point in screen space, return the terrain position in world space. + //! Given a point in screen space, return the terrain position in world space. virtual AZ::Vector3 PickTerrain(const AzFramework::ScreenPoint& point) = 0; - /// Return the terrain height given a world position in 2d (xy plane). + //! Return the terrain height given a world position in 2d (xy plane). virtual float TerrainHeight(const AZ::Vector2& position) = 0; - /// Given the current view frustum (viewport) return all visible entities. + //! Given the current view frustum (viewport) return all visible entities. virtual void FindVisibleEntities(AZStd::vector& visibleEntities) = 0; - /// Is the user holding a modifier key to move the manipulator space from local to world. + //! Is the user holding a modifier key to move the manipulator space from local to world. virtual bool ShowingWorldSpace() = 0; - /// Return the widget to use as the parent for the viewport context menu. + //! Return the widget to use as the parent for the viewport context menu. virtual QWidget* GetWidgetForViewportContextMenu() = 0; - /// Set the render context for the viewport. + //! Set the render context for the viewport. virtual void BeginWidgetContext() = 0; - /// End the render context for the viewport. - /// Return to previous context before Begin was called. + //! End the render context for the viewport. + //! Return to previous context before Begin was called. virtual void EndWidgetContext() = 0; protected: ~MainEditorViewportInteractionRequests() = default; }; - /// Type to inherit to implement MainEditorViewportInteractionRequests. + //! Type to inherit to implement MainEditorViewportInteractionRequests. using MainEditorViewportInteractionRequestBus = AZ::EBus; - /// Viewport requests for managing the viewport's cursor state. + //! Viewport requests for managing the viewport's cursor state. class ViewportMouseCursorRequests { public: - /// Begins hiding the cursor and locking it in place, to prevent the cursor from escaping the viewport window. + //! Begins hiding the cursor and locking it in place, to prevent the cursor from escaping the viewport window. virtual void BeginCursorCapture() = 0; - /// Restores the cursor and ends locking it in place, allowing it to be moved freely. + //! Restores the cursor and ends locking it in place, allowing it to be moved freely. virtual void EndCursorCapture() = 0; - /// Gets the most recent recorded cursor position in the viewport in screen space coordinates. + //! Gets the most recent recorded cursor position in the viewport in screen space coordinates. virtual AzFramework::ScreenPoint ViewportCursorScreenPosition() = 0; - /// Gets the cursor position recorded prior to the most recent cursor position. - /// Note: The cursor may be captured by the viewport, in which case this may not correspond to the last result - /// from ViewportCursorScreenPosition. This method will always return the correct position to generate a mouse - /// position delta. + //! Gets the cursor position recorded prior to the most recent cursor position. + //! Note: The cursor may be captured by the viewport, in which case this may not correspond to the last result + //! from ViewportCursorScreenPosition. This method will always return the correct position to generate a mouse + //! position delta. virtual AZStd::optional PreviousViewportCursorScreenPosition() = 0; - /// Is mouse over viewport. + //! Is mouse over viewport. virtual bool IsMouseOver() const = 0; protected: ~ViewportMouseCursorRequests() = default; }; - /// Type to inherit to implement MainEditorViewportInteractionRequests. + //! Type to inherit to implement MainEditorViewportInteractionRequests. using ViewportMouseCursorRequestBus = AZ::EBus; - /// A helper to wrap Begin/EndWidgetContext. + //! A helper to wrap Begin/EndWidgetContext. class WidgetContextGuard { public: @@ -294,17 +301,16 @@ namespace AzToolsFramework } private: - int m_viewportId; ///< The viewport id the widget context is being set on. + int m_viewportId; //!< The viewport id the widget context is being set on. }; } // namespace ViewportInteraction - /// Utility function to return EntityContextId. + //! Utility function to return EntityContextId. inline AzFramework::EntityContextId GetEntityContextId() { AzFramework::EntityContextId entityContextId; - EditorEntityContextRequestBus::BroadcastResult( - entityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); + EditorEntityContextRequestBus::BroadcastResult(entityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); return entityContextId; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportTypes.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportTypes.cpp index 8d26054562..99808fcd56 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportTypes.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportTypes.cpp @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "ViewportTypes.h" @@ -23,26 +23,24 @@ namespace AzToolsFramework { if (auto serializeContext = azrtti_cast(context)) { - serializeContext->Class()-> - Field("KeyboardModifiers", &KeyboardModifiers::m_keyModifiers); + serializeContext->Class()->Field("KeyboardModifiers", &KeyboardModifiers::m_keyModifiers); - serializeContext->Class()-> - Field("MouseButtons", &MouseButtons::m_mouseButtons); + serializeContext->Class()->Field("MouseButtons", &MouseButtons::m_mouseButtons); - serializeContext->Class()-> - Field("CameraId", &InteractionId::m_cameraId)-> - Field("ViewportId", &InteractionId::m_viewportId); + serializeContext->Class() + ->Field("CameraId", &InteractionId::m_cameraId) + ->Field("ViewportId", &InteractionId::m_viewportId); - serializeContext->Class()-> - Field("RayOrigin", &MousePick::m_rayOrigin)-> - Field("RayDirection", &MousePick::m_rayDirection)-> - Field("ScreenCoordinates", &MousePick::m_screenCoordinates); + serializeContext->Class() + ->Field("RayOrigin", &MousePick::m_rayOrigin) + ->Field("RayDirection", &MousePick::m_rayDirection) + ->Field("ScreenCoordinates", &MousePick::m_screenCoordinates); - serializeContext->Class()-> - Field("MousePick", &MouseInteraction::m_mousePick)-> - Field("MouseButtons", &MouseInteraction::m_mouseButtons)-> - Field("InteractionId", &MouseInteraction::m_interactionId)-> - Field("KeyboardModifiers", &MouseInteraction::m_keyboardModifiers); + serializeContext->Class() + ->Field("MousePick", &MouseInteraction::m_mousePick) + ->Field("MouseButtons", &MouseInteraction::m_mouseButtons) + ->Field("InteractionId", &MouseInteraction::m_interactionId) + ->Field("KeyboardModifiers", &MouseInteraction::m_keyboardModifiers); MouseInteractionEvent::Reflect(*serializeContext); } @@ -50,10 +48,10 @@ namespace AzToolsFramework void MouseInteractionEvent::Reflect(AZ::SerializeContext& serializeContext) { - serializeContext.Class()-> - Field("MouseInteraction", &MouseInteractionEvent::m_mouseInteraction)-> - Field("MouseEvent", &MouseInteractionEvent::m_mouseEvent)-> - Field("WheelDelta", &MouseInteractionEvent::m_wheelDelta); + serializeContext.Class() + ->Field("MouseInteraction", &MouseInteractionEvent::m_mouseInteraction) + ->Field("MouseEvent", &MouseInteractionEvent::m_mouseEvent) + ->Field("WheelDelta", &MouseInteractionEvent::m_wheelDelta); } - } -} + } // namespace ViewportInteraction +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportTypes.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportTypes.h index d59044e68f..ad045888bc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportTypes.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportTypes.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -27,57 +27,75 @@ namespace AZ namespace AzToolsFramework { - /// Viewport related types that are used when interacting with the viewport. + //! Viewport related types that are used when interacting with the viewport. namespace ViewportInteraction { - /// Flags to represent each modifier key. + //! Flags to represent each modifier key. enum class KeyboardModifier : AZ::u32 { - None = 0, ///< No keyboard modifier. - Alt = 0x01, ///< Alt keyboard modifier. - Shift = 0x02, ///< Shift keyboard modifier. - Ctrl = 0x04, ///< Ctrl keyboard modifier. - Control = Ctrl ///< Alias for Ctrl modifier. + None = 0, //!< No keyboard modifier. + Alt = 0x01, //!< Alt keyboard modifier. + Shift = 0x02, //!< Shift keyboard modifier. + Ctrl = 0x04, //!< Ctrl keyboard modifier. + Control = Ctrl //!< Alias for Ctrl modifier. }; - /// Flags to represent each mouse button. + //! Flags to represent each mouse button. enum class MouseButton : AZ::u32 { - None = 0, ///< No mouse buttons. - Left = 0x01, ///< Left mouse button. - Middle = 0x02, ///< Middle mouse button. - Right = 0x04 ///< Right mouse button. + None = 0, //!< No mouse buttons. + Left = 0x01, //!< Left mouse button. + Middle = 0x02, //!< Middle mouse button. + Right = 0x04 //!< Right mouse button. }; - /// The type of mouse event that occurred. + //! The type of mouse event that occurred. enum class MouseEvent { - Up, ///< Mouse up event, - Down, ///< Mouse down event. - DoubleClick, ///< Mouse double click event. - Wheel, ///< Mouse wheel event. - Move, ///< Mouse move event. + Up, //!< Mouse up event, + Down, //!< Mouse down event. + DoubleClick, //!< Mouse double click event. + Wheel, //!< Mouse wheel event. + Move, //!< Mouse move event. }; - /// Interface over keyboard modifier to query which key is pressed. + //! Interface over keyboard modifier to query which key is pressed. struct KeyboardModifiers { - /// @cond + //! @cond AZ_TYPE_INFO(KeyboardModifiers, "{2635F4DF-E7DC-4919-A97B-9AE35FE086D8}"); KeyboardModifiers() = default; - /// @endcond + //! @endcond - /// Explicit constructor to create a KeyboardModifier struct. - explicit KeyboardModifiers(const AZ::u32 keyModifiers) : m_keyModifiers(keyModifiers) {} + //! Explicit constructor to create a KeyboardModifier struct. + explicit KeyboardModifiers(const AZ::u32 keyModifiers) + : m_keyModifiers(keyModifiers) + { + } - /// Given the current keyboard modifiers, is the Alt key held. - bool Alt() const { return (m_keyModifiers & static_cast(KeyboardModifier::Alt)) != 0; } - /// Given the current keyboard modifiers, is the Shift key held. - bool Shift() const { return (m_keyModifiers & static_cast(KeyboardModifier::Shift)) != 0; } - /// Given the current keyboard modifiers, is the Ctrl key held. - bool Ctrl() const { return (m_keyModifiers & static_cast(KeyboardModifier::Ctrl)) != 0; } - /// Given the current keyboard modifiers, are none being held. - bool None() const { return m_keyModifiers == static_cast(KeyboardModifier::None); } + //! Given the current keyboard modifiers, is the Alt key held. + bool Alt() const + { + return (m_keyModifiers & static_cast(KeyboardModifier::Alt)) != 0; + } + + //! Given the current keyboard modifiers, is the Shift key held. + bool Shift() const + { + return (m_keyModifiers & static_cast(KeyboardModifier::Shift)) != 0; + } + + //! Given the current keyboard modifiers, is the Ctrl key held. + bool Ctrl() const + { + return (m_keyModifiers & static_cast(KeyboardModifier::Ctrl)) != 0; + } + + //! Given the current keyboard modifiers, are none being held. + bool None() const + { + return m_keyModifiers == static_cast(KeyboardModifier::None); + } bool operator==(const KeyboardModifiers& keyboardModifiers) const { @@ -89,132 +107,162 @@ namespace AzToolsFramework return m_keyModifiers != keyboardModifiers.m_keyModifiers; } - AZ::u32 m_keyModifiers = 0; ///< Raw keyboard modifier state. + AZ::u32 m_keyModifiers = 0; //!< Raw keyboard modifier state. }; - /// Interface over mouse buttons to query which button is pressed. + //! Interface over mouse buttons to query which button is pressed. struct MouseButtons { - /// @cond + //! @cond AZ_TYPE_INFO(MouseButtons, "{1D137B5D-73BF-4FD9-BECA-85E6DC3786CB}"); MouseButtons() = default; - /// @endcond + //! @endcond - /// Explicit constructor to create a MouseButton struct. - explicit MouseButtons(const AZ::u32 mouseButtons) : m_mouseButtons(mouseButtons) {} + //! Explicit constructor to create a MouseButton struct. + explicit MouseButtons(const AZ::u32 mouseButtons) + : m_mouseButtons(mouseButtons) + { + } - /// Given the current mouse state, is the left mouse button held. - bool Left() const { return (m_mouseButtons & static_cast(MouseButton::Left)) != 0; } - /// Given the current mouse state, is the middle mouse button held. - bool Middle() const { return (m_mouseButtons & static_cast(MouseButton::Middle)) != 0; } - /// Given the current mouse state, is the right mouse button held. - bool Right() const { return (m_mouseButtons & static_cast(MouseButton::Right)) != 0; } - /// Given the current mouse state, are no mouse buttons held. - bool None() const { return m_mouseButtons == static_cast(MouseButton::None); } - /// Given the current mouse state, are any mouse buttons held. - bool Any() const { return m_mouseButtons != static_cast(MouseButton::None); } + //! Given the current mouse state, is the left mouse button held. + bool Left() const + { + return (m_mouseButtons & static_cast(MouseButton::Left)) != 0; + } - AZ::u32 m_mouseButtons = 0; ///< Current mouse button state (flags). + //! Given the current mouse state, is the middle mouse button held. + bool Middle() const + { + return (m_mouseButtons & static_cast(MouseButton::Middle)) != 0; + } + + //! Given the current mouse state, is the right mouse button held. + bool Right() const + { + return (m_mouseButtons & static_cast(MouseButton::Right)) != 0; + } + + //! Given the current mouse state, are no mouse buttons held. + bool None() const + { + return m_mouseButtons == static_cast(MouseButton::None); + } + + //! Given the current mouse state, are any mouse buttons held. + bool Any() const + { + return m_mouseButtons != static_cast(MouseButton::None); + } + + AZ::u32 m_mouseButtons = 0; //!< Current mouse button state (flags). }; - /// Information relevant when interacting with a particular viewport. + //! Information relevant when interacting with a particular viewport. struct InteractionId { - /// @cond + //! @cond AZ_TYPE_INFO(InteractionId, "{35593FC2-846F-4AAD-8044-4CD84EC84F9A}"); InteractionId() = default; - /// @endcond + //! @endcond InteractionId(AZ::EntityId cameraId, int viewportId) - : m_cameraId(cameraId), m_viewportId(viewportId) {} + : m_cameraId(cameraId) + , m_viewportId(viewportId) + { + } - AZ::EntityId m_cameraId; ///< The entity id of the viewport camera. - int m_viewportId = 0; ///< The id of the viewport being interacted with. + AZ::EntityId m_cameraId; //!< The entity id of the viewport camera. + int m_viewportId = 0; //!< The id of the viewport being interacted with. }; - /// Data representing a mouse pick ray. + //! Data representing a mouse pick ray. struct MousePick { - /// @cond + //! @cond AZ_TYPE_INFO(MousePick, "{A69B9562-FC8C-4DE7-9137-0FF867B1513D}"); MousePick() = default; - /// @endcond + //! @endcond - AZ::Vector3 m_rayOrigin = AZ::Vector3::CreateZero(); ///< World space. - AZ::Vector3 m_rayDirection = AZ::Vector3::CreateZero(); ///< World space - normalized. - AzFramework::ScreenPoint m_screenCoordinates = {}; ///< Screen space. + AZ::Vector3 m_rayOrigin = AZ::Vector3::CreateZero(); //!< World space. + AZ::Vector3 m_rayDirection = AZ::Vector3::CreateZero(); //!< World space - normalized. + AzFramework::ScreenPoint m_screenCoordinates = {}; //!< Screen space. }; - /// State relating to an individual mouse interaction. + //! State relating to an individual mouse interaction. struct MouseInteraction { - /// @cond + //! @cond AZ_TYPE_INFO(MouseInteraction, "{E67357C3-DFE1-40DF-921F-9CBCFE63A68C}"); MouseInteraction() = default; - /// @endcond + //! @endcond - MousePick m_mousePick; ///< The mouse pick ray in world space and screen coordinates in screen space. - MouseButtons m_mouseButtons; ///< The current state of the mouse buttons. + MousePick m_mousePick; //!< The mouse pick ray in world space and screen coordinates in screen space. + MouseButtons m_mouseButtons; //!< The current state of the mouse buttons. InteractionId m_interactionId; /**< The EntityId of the camera this click came from - * and the id of the viewport it originated from. */ - KeyboardModifiers m_keyboardModifiers; ///< The state of the keyboard modifiers (Alt/Ctrl/Shift). + * and the id of the viewport it originated from. */ + KeyboardModifiers m_keyboardModifiers; //!< The state of the keyboard modifiers (Alt/Ctrl/Shift). }; - /// Structure to compose MouseInteraction (mouse state) and - /// MouseEvent (MouseEvent::MouseUp/MouseEvent::DownMove etc.) + //! Structure to compose MouseInteraction (mouse state) and + //! MouseEvent (MouseEvent::MouseUp/MouseEvent::DownMove etc.) struct MouseInteractionEvent { - /// @cond + //! @cond AZ_TYPE_INFO(MouseInteractionEvent, "{67FE0826-DD59-4B5B-BEFE-421E83EA7F31}"); MouseInteractionEvent() = default; - /// @endcond + //! @endcond static void Reflect(AZ::SerializeContext& context); - /// Constructor to create a default MouseInteractionEvent + //! Constructor to create a default MouseInteractionEvent MouseInteractionEvent(MouseInteraction mouseInteraction, const MouseEvent mouseEvent) : m_mouseInteraction(std::move(mouseInteraction)) - , m_mouseEvent(mouseEvent) {} + , m_mouseEvent(mouseEvent) + { + } - /// Special constructor for mouse wheel event. + //! Special constructor for mouse wheel event. MouseInteractionEvent(MouseInteraction mouseInteraction, const float wheelDelta) : m_mouseInteraction(std::move(mouseInteraction)) , m_mouseEvent(MouseEvent::Wheel) - , m_wheelDelta(wheelDelta) {} + , m_wheelDelta(wheelDelta) + { + } - MouseInteraction m_mouseInteraction; ///< Mouse state. - MouseEvent m_mouseEvent; ///< Mouse event. + MouseInteraction m_mouseInteraction; //!< Mouse state. + MouseEvent m_mouseEvent; //!< Mouse event. - /// Special friend function to return the mouse wheel delta (scroll amount) - /// if the event was of type MouseEvent::Wheel. + //! Special friend function to return the mouse wheel delta (scroll amount) + //! if the event was of type MouseEvent::Wheel. friend float MouseWheelDelta(const MouseInteractionEvent& mouseInteractionEvent); private: - float m_wheelDelta = 0.0f; ///< The amount the mouse wheel moved during a mouse wheel event. + float m_wheelDelta = 0.0f; //!< The amount the mouse wheel moved during a mouse wheel event. }; - /// Checked access to mouse wheel delta - ensure event originated from the mouse wheel. + //! Checked access to mouse wheel delta - ensure event originated from the mouse wheel. inline float MouseWheelDelta(const MouseInteractionEvent& mouseInteractionEvent) { - AZ_Assert(mouseInteractionEvent.m_mouseEvent == MouseEvent::Wheel, + AZ_Assert( + mouseInteractionEvent.m_mouseEvent == MouseEvent::Wheel, "Attempting to access mouse wheel delta when mouse interaction event was not mouse wheel"); return mouseInteractionEvent.m_wheelDelta; } - /// Return QPoint from AzFramework::ScreenPoint. + //! Return QPoint from AzFramework::ScreenPoint. inline QPoint QPointFromScreenPoint(const AzFramework::ScreenPoint& screenPoint) { - return {screenPoint.m_x, screenPoint.m_y}; + return { screenPoint.m_x, screenPoint.m_y }; } - /// Return AzFramework::ScreenPoint from QPoint. + //! Return AzFramework::ScreenPoint from QPoint. inline AzFramework::ScreenPoint ScreenPointFromQPoint(const QPoint& qpoint) { - return AzFramework::ScreenPoint{qpoint.x(), qpoint.y()}; + return AzFramework::ScreenPoint{ qpoint.x(), qpoint.y() }; } - /// Map from Qt -> Open 3D Engine buttons.>>>>>>> main + //! Map from Qt -> Open 3D Engine buttons.>>>>>>> main inline AZ::u32 TranslateMouseButtons(const Qt::MouseButtons buttons) { AZ::u32 result = 0; @@ -224,7 +272,7 @@ namespace AzToolsFramework return result; } - /// Map from Qt -> Open 3D Engine modifiers. + //! Map from Qt -> Open 3D Engine modifiers. inline AZ::u32 TranslateKeyboardModifiers(const Qt::KeyboardModifiers modifiers) { AZ::u32 result = 0; @@ -234,19 +282,19 @@ namespace AzToolsFramework return result; } - /// Interface to translate Qt modifiers to Open 3D Engine modifiers. + //! Interface to translate Qt modifiers to Open 3D Engine modifiers. inline KeyboardModifiers BuildKeyboardModifiers(const Qt::KeyboardModifiers modifiers) { return KeyboardModifiers(TranslateKeyboardModifiers(modifiers)); } - /// Interface to translate Qt buttons to Open 3D Engine buttons. + //! Interface to translate Qt buttons to Open 3D Engine buttons. inline MouseButtons BuildMouseButtons(const Qt::MouseButtons buttons) { return MouseButtons(TranslateMouseButtons(buttons)); } - /// Generate mouse buttons from single button enum. + //! Generate mouse buttons from single button enum. inline MouseButtons MouseButtonsFromButton(MouseButton button) { MouseButtons mouseButtons; @@ -254,7 +302,7 @@ namespace AzToolsFramework return mouseButtons; } - /// Reflect all viewport related types. + //! Reflect all viewport related types. void ViewportInteractionReflect(AZ::ReflectContext* context); } // namespace ViewportInteraction } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorDefaultSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorDefaultSelection.cpp index a7f500cc5f..ed776f90ea 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorDefaultSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorDefaultSelection.cpp @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "EditorDefaultSelection.h" @@ -30,8 +30,7 @@ namespace AzToolsFramework ActionOverrideRequestBus::Handler::BusConnect(GetEntityContextId()); ComponentModeFramework::ComponentModeSystemRequestBus::Handler::BusConnect(); - m_manipulatorManager = - AZStd::make_shared(AzToolsFramework::g_mainManipulatorManagerId); + m_manipulatorManager = AZStd::make_shared(AzToolsFramework::g_mainManipulatorManagerId); m_transformComponentSelection = AZStd::make_unique(entityDataCache); } @@ -75,23 +74,18 @@ namespace AzToolsFramework for (const auto& componentModeBuilder : entityAndComponentModeBuilders.m_componentModeBuilders) { m_componentModeCollection.AddComponentMode( - AZ::EntityComponentIdPair( - entityAndComponentModeBuilders.m_entityId, componentModeBuilder.m_componentId), - componentModeBuilder.m_componentType, - componentModeBuilder.m_componentModeBuilder); + AZ::EntityComponentIdPair(entityAndComponentModeBuilders.m_entityId, componentModeBuilder.m_componentId), + componentModeBuilder.m_componentType, componentModeBuilder.m_componentModeBuilder); } } void EditorDefaultSelection::TransitionToComponentMode() { // entering ComponentMode - disable all default actions in the ActionManager - EditorActionRequestBus::Broadcast( - &EditorActionRequests::DisableDefaultActions); + EditorActionRequestBus::Broadcast(&EditorActionRequests::DisableDefaultActions); // attach widget to store ComponentMode specific actions - EditorActionRequestBus::Broadcast( - &EditorActionRequests::AttachOverride, - &PhantomWidget()); + EditorActionRequestBus::Broadcast(&EditorActionRequests::AttachOverride, &PhantomWidget()); if (m_transformComponentSelection) { @@ -103,8 +97,7 @@ namespace AzToolsFramework // refresh button ui ToolsApplicationEvents::Bus::Broadcast( - &ToolsApplicationEvents::Bus::Events::InvalidatePropertyDisplay, - PropertyModificationRefreshLevel::Refresh_EntireTree); + &ToolsApplicationEvents::Bus::Events::InvalidatePropertyDisplay, PropertyModificationRefreshLevel::Refresh_EntireTree); } void EditorDefaultSelection::TransitionFromComponentMode() @@ -117,19 +110,16 @@ namespace AzToolsFramework m_transformComponentSelection->RegisterManipulator(); } - EditorActionRequestBus::Broadcast( - &EditorActionRequests::DetachOverride); + EditorActionRequestBus::Broadcast(&EditorActionRequests::DetachOverride); ClearActionOverrides(); // leaving ComponentMode - enable all default actions in ActionManager - EditorActionRequestBus::Broadcast( - &EditorActionRequests::EnableDefaultActions); + EditorActionRequestBus::Broadcast(&EditorActionRequests::EnableDefaultActions); // refresh button ui ToolsApplicationEvents::Bus::Broadcast( - &ToolsApplicationEvents::Bus::Events::InvalidatePropertyDisplay, - PropertyModificationRefreshLevel::Refresh_EntireTree); + &ToolsApplicationEvents::Bus::Events::InvalidatePropertyDisplay, PropertyModificationRefreshLevel::Refresh_EntireTree); } void EditorDefaultSelection::EndComponentMode() @@ -142,8 +132,7 @@ namespace AzToolsFramework m_componentModeCollection.Refresh(entityComponentIdPair); } - bool EditorDefaultSelection::AddedToComponentMode( - const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Uuid& componentType) + bool EditorDefaultSelection::AddedToComponentMode(const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Uuid& componentType) { return m_componentModeCollection.AddedToComponentMode(entityComponentIdPair, componentType); } @@ -152,10 +141,10 @@ namespace AzToolsFramework { ComponentModeFramework::ComponentModeDelegateRequestBus::EnumerateHandlers( [componentType](ComponentModeFramework::ComponentModeDelegateRequestBus::InterfaceType* componentModeMouseRequests) - { - componentModeMouseRequests->AddComponentModeOfType(componentType); - return true; - }); + { + componentModeMouseRequests->AddComponentModeOfType(componentType); + return true; + }); TransitionToComponentMode(); } @@ -238,8 +227,7 @@ namespace AzToolsFramework } } - bool EditorDefaultSelection::InternalHandleMouseViewportInteraction( - const ViewportInteraction::MouseInteractionEvent& mouseInteraction) + bool EditorDefaultSelection::InternalHandleMouseViewportInteraction(const ViewportInteraction::MouseInteractionEvent& mouseInteraction) { bool enterComponentModeAttempted = false; const bool componentModeBefore = InComponentMode(); @@ -249,15 +237,15 @@ namespace AzToolsFramework { // enumerate all ComponentModeDelegateRequestBus and check if any triggered AddComponentModes ComponentModeFramework::ComponentModeDelegateRequestBus::EnumerateHandlers( - [&mouseInteraction, &enterComponentModeAttempted] - (ComponentModeFramework::ComponentModeDelegateRequestBus::InterfaceType* componentModeMouseRequests) - { - // detect if a double click happened on any Component in the viewport, attempting - // to move it into ComponentMode (note: this is not guaranteed to succeed as an - // incompatible multi-selection may prevent it) - enterComponentModeAttempted = componentModeMouseRequests->DetectEnterComponentModeInteraction(mouseInteraction); - return !enterComponentModeAttempted; - }); + [&mouseInteraction, &enterComponentModeAttempted]( + ComponentModeFramework::ComponentModeDelegateRequestBus::InterfaceType* componentModeMouseRequests) + { + // detect if a double click happened on any Component in the viewport, attempting + // to move it into ComponentMode (note: this is not guaranteed to succeed as an + // incompatible multi-selection may prevent it) + enterComponentModeAttempted = componentModeMouseRequests->DetectEnterComponentModeInteraction(mouseInteraction); + return !enterComponentModeAttempted; + }); // here we know ComponentMode was entered successfully and was not prohibited if (m_componentModeCollection.ModesAdded()) @@ -272,25 +260,24 @@ namespace AzToolsFramework else { ComponentModeFramework::ComponentModeRequestBus::EnumerateHandlers( - [&mouseInteraction, &handled] - (ComponentModeFramework::ComponentModeRequestBus::InterfaceType* componentModeRequest) - { - if (componentModeRequest->HandleMouseInteraction(mouseInteraction)) + [&mouseInteraction, &handled](ComponentModeFramework::ComponentModeRequestBus::InterfaceType* componentModeRequest) { - handled = true; - } + if (componentModeRequest->HandleMouseInteraction(mouseInteraction)) + { + handled = true; + } - return true; - }); + return true; + }); if (!handled) { ComponentModeFramework::ComponentModeDelegateRequestBus::EnumerateHandlers( - [&mouseInteraction] - (ComponentModeFramework::ComponentModeDelegateRequestBus::InterfaceType* componentModeDelegateRequests) - { - return !componentModeDelegateRequests->DetectLeaveComponentModeInteraction(mouseInteraction); - }); + [&mouseInteraction]( + ComponentModeFramework::ComponentModeDelegateRequestBus::InterfaceType* componentModeDelegateRequests) + { + return !componentModeDelegateRequests->DetectLeaveComponentModeInteraction(mouseInteraction); + }); } } @@ -311,8 +298,7 @@ namespace AzToolsFramework } void EditorDefaultSelection::DisplayViewportSelection( - const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) + const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) { if (m_transformComponentSelection) { @@ -330,8 +316,7 @@ namespace AzToolsFramework } void EditorDefaultSelection::DisplayViewportSelection2d( - const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) + const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) { if (m_transformComponentSelection) { @@ -355,11 +340,12 @@ namespace AzToolsFramework void EditorDefaultSelection::AddActionOverride(const ActionOverride& actionOverride) { // check if an action with this uri is already added - const auto actionIt = AZStd::find_if(m_actions.begin(), m_actions.end(), + const auto actionIt = AZStd::find_if( + m_actions.begin(), m_actions.end(), [actionOverride](const AZStd::shared_ptr& actionOverrideMapping) - { - return actionOverride.m_uri == actionOverrideMapping->m_uri; - }); + { + return actionOverride.m_uri == actionOverrideMapping->m_uri; + }); // if an action with the same uri is already added, store the callback for this action if (actionIt != m_actions.end()) @@ -381,44 +367,45 @@ namespace AzToolsFramework // set callbacks that should happen when this action is triggered auto index = static_cast(m_actions.size()); - QObject::connect(action.get(), &QAction::triggered, [this, index]() - { - const auto vec = m_actions; // increment ref count of shared_ptr, callback may clear actions - for (auto& callback : vec[index]->m_callbacks) + QObject::connect( + action.get(), &QAction::triggered, + [this, index]() { - callback(); - } - }); + const auto vec = m_actions; // increment ref count of shared_ptr, callback may clear actions + for (auto& callback : vec[index]->m_callbacks) + { + callback(); + } + }); - m_actions.emplace_back( - AZStd::make_shared( - actionOverride.m_uri, AZStd::vector>{ actionOverride.m_callback }, - AZStd::move(action))); + m_actions.emplace_back(AZStd::make_shared( + actionOverride.m_uri, AZStd::vector>{ actionOverride.m_callback }, AZStd::move(action))); // register action with edit menu - EditorMenuRequestBus::Broadcast( - &EditorMenuRequests::AddEditMenuAction, m_actions.back()->m_action.get()); + EditorMenuRequestBus::Broadcast(&EditorMenuRequests::AddEditMenuAction, m_actions.back()->m_action.get()); } } void EditorDefaultSelection::ClearActionOverrides() { - AZStd::for_each(m_actions.begin(), m_actions.end(), + AZStd::for_each( + m_actions.begin(), m_actions.end(), [this](const AZStd::shared_ptr& actionMapping) - { - PhantomWidget().removeAction(actionMapping->m_action.get()); - }); + { + PhantomWidget().removeAction(actionMapping->m_action.get()); + }); m_actions.clear(); } void EditorDefaultSelection::RemoveActionOverride(const AZ::Crc32 actionOverrideUri) { - const auto it = AZStd::find_if(m_actions.begin(), m_actions.end(), + const auto it = AZStd::find_if( + m_actions.begin(), m_actions.end(), [actionOverrideUri](const AZStd::shared_ptr& actionMapping) - { - return actionMapping->m_uri == actionOverrideUri; - }); + { + return actionMapping->m_uri == actionOverrideUri; + }); if (it != m_actions.end()) { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorDefaultSelection.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorDefaultSelection.h index 414e163e2f..d2f2c2fea5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorDefaultSelection.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorDefaultSelection.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -19,7 +19,7 @@ namespace AzToolsFramework { - /// The default selection/input handler for the editor (includes handling ComponentMode). + //! The default selection/input handler for the editor (includes handling ComponentMode). class EditorDefaultSelection : public ViewportInteraction::InternalViewportSelectionRequests , private ActionOverrideRequestBus::Handler @@ -28,30 +28,26 @@ namespace AzToolsFramework public: AZ_CLASS_ALLOCATOR_DECL - /// @cond + //! @cond explicit EditorDefaultSelection(const EditorVisibleEntityDataCache* entityDataCache); EditorDefaultSelection(const EditorDefaultSelection&) = delete; EditorDefaultSelection& operator=(const EditorDefaultSelection&) = delete; virtual ~EditorDefaultSelection(); - /// @endcond + //! @endcond - /// Override the default widget used to store QActions while in ComponentMode. - /// @note This should not be necessary during normal operation and is provided - /// as a customization point to aid with testing. + //! Override the default widget used to store QActions while in ComponentMode. + //! @note This should not be necessary during normal operation and is provided + //! as a customization point to aid with testing. void SetOverridePhantomWidget(QWidget* phantomOverrideWidget); private: // ViewportInteraction::InternalMouseViewportRequests ... - bool InternalHandleMouseViewportInteraction( - const ViewportInteraction::MouseInteractionEvent& mouseInteraction) override; - bool InternalHandleMouseManipulatorInteraction( - const ViewportInteraction::MouseInteractionEvent& mouseInteraction) override; + bool InternalHandleMouseViewportInteraction(const ViewportInteraction::MouseInteractionEvent& mouseInteraction) override; + bool InternalHandleMouseManipulatorInteraction(const ViewportInteraction::MouseInteractionEvent& mouseInteraction) override; void DisplayViewportSelection( - const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) override; + const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; void DisplayViewportSelection2d( - const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) override; + const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; // ActionOverrideRequestBus ... void SetupActionOverrideHandler(QWidget* parent) override; @@ -65,7 +61,10 @@ namespace AzToolsFramework const AZStd::vector& entityAndComponentModeBuilders) override; void AddComponentModes(const ComponentModeFramework::EntityAndComponentModeBuilders& entityAndComponentModeBuilders) override; void EndComponentMode() override; - bool InComponentMode() override { return m_componentModeCollection.InComponentMode(); } + bool InComponentMode() override + { + return m_componentModeCollection.InComponentMode(); + } void Refresh(const AZ::EntityComponentIdPair& entityComponentIdPair) override; bool AddedToComponentMode(const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Uuid& componentType) override; void AddSelectedComponentModesOfType(const AZ::Uuid& componentType) override; @@ -77,41 +76,43 @@ namespace AzToolsFramework bool HasMultipleComponentTypes() override; void RefreshActions() override; - /// Helpers to deal with moving in and out of ComponentMode. + //! Helpers to deal with moving in and out of ComponentMode. void TransitionToComponentMode(); void TransitionFromComponentMode(); - /// Accessor used internally to refer to the phantom widget. - /// This will either be the default widget or the override if non-null. + //! Accessor used internally to refer to the phantom widget. + //! This will either be the default widget or the override if non-null. QWidget& PhantomWidget(); - QWidget m_phantomWidget; ///< The phantom widget responsible for holding QActions while in ComponentMode. - QWidget* m_phantomOverrideWidget = nullptr; ///< It's possible to override the phantom widget in special circumstances (eg testing). - ComponentModeFramework::ComponentModeCollection m_componentModeCollection; ///< Handles all active ComponentMode types. - AZStd::unique_ptr m_transformComponentSelection = nullptr; ///< Viewport selection (responsible for - ///< manipulators and transform modifications). - const EditorVisibleEntityDataCache* m_entityDataCache = nullptr; ///< Reference to cached visible EntityData. + QWidget m_phantomWidget; //!< The phantom widget responsible for holding QActions while in ComponentMode. + QWidget* m_phantomOverrideWidget = nullptr; //!< It's possible to override the phantom widget in special circumstances (eg testing). + ComponentModeFramework::ComponentModeCollection m_componentModeCollection; //!< Handles all active ComponentMode types. + AZStd::unique_ptr m_transformComponentSelection = + nullptr; //!< Viewport selection (responsible for + //!< manipulators and transform modifications). + const EditorVisibleEntityDataCache* m_entityDataCache = nullptr; //!< Reference to cached visible EntityData. - /// Mapping between passed ActionOverride (AddActionOverride) and allocated QAction. + //! Mapping between passed ActionOverride (AddActionOverride) and allocated QAction. struct ActionOverrideMapping { ActionOverrideMapping( - const AZ::Crc32 uri, const AZStd::vector>& callbacks, - AZStd::unique_ptr action) + const AZ::Crc32 uri, const AZStd::vector>& callbacks, AZStd::unique_ptr action) : m_uri(uri) , m_callbacks(callbacks) - , m_action(AZStd::move(action)) {} + , m_action(AZStd::move(action)) + { + } - AZ::Crc32 m_uri; ///< Unique identifier for the Action. (In the form 'com.amazon.action.---"). - AZStd::vector> m_callbacks; ///< Callbacks associated with this Action (note: with multi-selections there - ///< will be a callback per Entity/Component). - AZStd::unique_ptr m_action; ///< The QAction associated with the overrideWidget for all ComponentMode actions. + AZ::Crc32 m_uri; //!< Unique identifier for the Action. (In the form 'com.amazon.action.---"). + AZStd::vector> m_callbacks; //!< Callbacks associated with this Action (note: with multi-selections + //!< there will be a callback per Entity/Component). + AZStd::unique_ptr m_action; //!< The QAction associated with the overrideWidget for all ComponentMode actions. }; - AZStd::vector> m_actions; ///< Currently bound actions (corresponding to those set - ///< on the override widget). + AZStd::vector> m_actions; //!< Currently bound actions (corresponding to those set + //!< on the override widget). - AZStd::shared_ptr m_manipulatorManager; ///< The default manipulator manager. - ViewportInteraction::MouseInteraction m_currentInteraction; ///< Current mouse interaction to be used for drawing manipulators. + AZStd::shared_ptr m_manipulatorManager; //!< The default manipulator manager. + ViewportInteraction::MouseInteraction m_currentInteraction; //!< Current mouse interaction to be used for drawing manipulators. }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp index 31da01fadc..6080f6f7ae 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "EditorHelpers.h" @@ -16,21 +16,33 @@ #include #include #include +#include #include #include #include -#include #include -#include +#include AZ_CVAR( - bool, ed_visibility_showAggregateEntitySelectionBounds, false, nullptr, AZ::ConsoleFunctorFlags::Null, + bool, + ed_visibility_showAggregateEntitySelectionBounds, + false, + nullptr, + AZ::ConsoleFunctorFlags::Null, "Display the aggregate selection bounds for a given entity (the union of all component Aabbs)"); AZ_CVAR( - bool, ed_visibility_showAggregateEntityTransformedLocalBounds, false, nullptr, AZ::ConsoleFunctorFlags::Null, + bool, + ed_visibility_showAggregateEntityTransformedLocalBounds, + false, + nullptr, + AZ::ConsoleFunctorFlags::Null, "Display the aggregate transformed local bounds for a given entity (the union of all local component Aabbs)"); AZ_CVAR( - bool, ed_visibility_showAggregateEntityWorldBounds, false, nullptr, AZ::ConsoleFunctorFlags::Null, + bool, + ed_visibility_showAggregateEntityWorldBounds, + false, + nullptr, + AZ::ConsoleFunctorFlags::Null, "Display the aggregate world bounds for a given entity (the union of all world component Aabbs)"); namespace AzToolsFramework @@ -48,8 +60,7 @@ namespace AzToolsFramework static bool HelpersVisible() { bool helpersVisible = false; - EditorRequestBus::BroadcastResult( - helpersVisible, &EditorRequests::DisplayHelpersVisible); + EditorRequestBus::BroadcastResult(helpersVisible, &EditorRequests::DisplayHelpersVisible); return helpersVisible; } @@ -59,25 +70,23 @@ namespace AzToolsFramework { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); - return s_iconMinScale + (s_iconMaxScale - s_iconMinScale) * + return s_iconMinScale + + (s_iconMaxScale - s_iconMinScale) * (1.0f - AZ::GetClamp(AZ::GetMax(0.0f, sqrtf(distSq) - s_iconCloseDist) / s_iconFarDist, 0.0f, 1.0f)); } static void DisplayComponents( - const AZ::EntityId entityId, const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) + const AZ::EntityId entityId, const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); const AZ::Entity* entity = AZ::Interface::Get()->FindEntity(entityId); AzFramework::EntityDebugDisplayEventBus::Event( - entityId, &AzFramework::EntityDebugDisplayEvents::DisplayEntityViewport, - viewportInfo, debugDisplay); + entityId, &AzFramework::EntityDebugDisplayEvents::DisplayEntityViewport, viewportInfo, debugDisplay); if (ed_visibility_showAggregateEntitySelectionBounds) { - if (const AZ::Aabb aabb = AzToolsFramework::CalculateEditorEntitySelectionBounds(entityId, viewportInfo); - aabb.IsValid()) + if (const AZ::Aabb aabb = AzToolsFramework::CalculateEditorEntitySelectionBounds(entityId, viewportInfo); aabb.IsValid()) { debugDisplay.SetColor(AZ::Colors::Orange); debugDisplay.DrawWireBox(aabb.GetMin(), aabb.GetMax()); @@ -107,8 +116,7 @@ namespace AzToolsFramework } AZ::EntityId EditorHelpers::HandleMouseInteraction( - const AzFramework::CameraState& cameraState, - const ViewportInteraction::MouseInteractionEvent& mouseInteraction) + const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteractionEvent& mouseInteraction) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -127,8 +135,7 @@ namespace AzToolsFramework { const AZ::EntityId entityId = m_entityDataCache->GetVisibleEntityId(entityCacheIndex); - if ( m_entityDataCache->IsVisibleEntityLocked(entityCacheIndex) - || !m_entityDataCache->IsVisibleEntityVisible(entityCacheIndex)) + if (m_entityDataCache->IsVisibleEntityLocked(entityCacheIndex) || !m_entityDataCache->IsVisibleEntityVisible(entityCacheIndex)) { continue; } @@ -148,10 +155,8 @@ namespace AzToolsFramework const auto iconRange = static_cast(GetIconScale(distSqFromCamera) * s_iconSize * 0.5f); const auto screenCoords = mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates; - if ( screenCoords.m_x >= screenPosition.m_x - iconRange - && screenCoords.m_x <= screenPosition.m_x + iconRange - && screenCoords.m_y >= screenPosition.m_y - iconRange - && screenCoords.m_y <= screenPosition.m_y + iconRange) + if (screenCoords.m_x >= screenPosition.m_x - iconRange && screenCoords.m_x <= screenPosition.m_x + iconRange && + screenCoords.m_y >= screenPosition.m_y - iconRange && screenCoords.m_y <= screenPosition.m_y + iconRange) { entityIdUnderCursor = entityId; break; @@ -161,16 +166,13 @@ namespace AzToolsFramework using AzFramework::ViewportInfo; // check if components provide an aabb - if (const AZ::Aabb aabb = CalculateEditorEntitySelectionBounds(entityId, ViewportInfo{viewportId}); - aabb.IsValid()) + if (const AZ::Aabb aabb = CalculateEditorEntitySelectionBounds(entityId, ViewportInfo{ viewportId }); aabb.IsValid()) { // coarse grain check if (AabbIntersectMouseRay(mouseInteraction.m_mouseInteraction, aabb)) { // if success, pick against specific component - if (PickEntity( - entityId, mouseInteraction.m_mouseInteraction, - closestDistance, viewportId)) + if (PickEntity(entityId, mouseInteraction.m_mouseInteraction, closestDistance, viewportId)) { entityIdUnderCursor = entityId; } @@ -182,7 +184,8 @@ namespace AzToolsFramework } void EditorHelpers::DisplayHelpers( - const AzFramework::ViewportInfo& viewportInfo, const AzFramework::CameraState& cameraState, + const AzFramework::ViewportInfo& viewportInfo, + const AzFramework::CameraState& cameraState, AzFramework::DebugDisplayRequests& debugDisplay, const AZStd::function& showIconCheck) { @@ -202,8 +205,8 @@ namespace AzToolsFramework // notify components to display DisplayComponents(entityId, viewportInfo, debugDisplay); - if ( m_entityDataCache->IsVisibleEntityIconHidden(entityCacheIndex) - || (m_entityDataCache->IsVisibleEntitySelected(entityCacheIndex) && !showIconCheck(entityId))) + if (m_entityDataCache->IsVisibleEntityIconHidden(entityCacheIndex) || + (m_entityDataCache->IsVisibleEntitySelected(entityCacheIndex) && !showIconCheck(entityId))) { continue; } @@ -219,7 +222,8 @@ namespace AzToolsFramework const float iconSize = s_iconSize * iconScale; using ComponentEntityAccentType = Components::EditorSelectionAccentSystemComponent::ComponentEntityAccentType; - const AZ::Color iconHighlight = [this, entityCacheIndex]() { + const AZ::Color iconHighlight = [this, entityCacheIndex]() + { if (m_entityDataCache->IsVisibleEntityLocked(entityCacheIndex)) { return AZ::Color(AZ::u8(100), AZ::u8(100), AZ::u8(100), AZ::u8(255)); @@ -233,14 +237,9 @@ namespace AzToolsFramework return AZ::Color(1.0f, 1.0f, 1.0f, 1.0f); }(); - EditorViewportIconDisplay::Get()->DrawIcon({ - viewportInfo.m_viewportId, - iconTextureId, - iconHighlight, - entityPosition, - EditorViewportIconDisplayInterface::CoordinateSpace::WorldSpace, - AZ::Vector2{iconSize, iconSize} - }); + EditorViewportIconDisplay::Get()->DrawIcon({ viewportInfo.m_viewportId, iconTextureId, iconHighlight, entityPosition, + EditorViewportIconDisplayInterface::CoordinateSpace::WorldSpace, + AZ::Vector2{ iconSize, iconSize } }); } } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h index e36203e31d..926cadee34 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -21,7 +21,7 @@ namespace AzFramework class DebugDisplayRequests; struct ViewportInfo; struct CameraState; -} +} // namespace AzFramework namespace AzToolsFramework { @@ -32,37 +32,39 @@ namespace AzToolsFramework struct MouseInteractionEvent; } - /// EditorHelpers are the visualizations that appear for entities - /// when 'Display Helpers' is toggled on inside the editor. - /// These include but are not limited to entity icons and shape visualizations. + //! EditorHelpers are the visualizations that appear for entities + //! when 'Display Helpers' is toggled on inside the editor. + //! These include but are not limited to entity icons and shape visualizations. class EditorHelpers { public: AZ_CLASS_ALLOCATOR_DECL - /// An EditorVisibleEntityDataCache must be passed to EditorHelpers to allow it to - /// efficiently read entity data without resorting to EBus calls. + //! An EditorVisibleEntityDataCache must be passed to EditorHelpers to allow it to + //! efficiently read entity data without resorting to EBus calls. explicit EditorHelpers(const EditorVisibleEntityDataCache* entityDataCache) - : m_entityDataCache(entityDataCache) {} + : m_entityDataCache(entityDataCache) + { + } EditorHelpers(const EditorHelpers&) = delete; EditorHelpers& operator=(const EditorHelpers&) = delete; ~EditorHelpers() = default; - /// Handle any mouse interaction with the EditorHelpers. - /// Used to check if a particular entity was selected. + //! Handle any mouse interaction with the EditorHelpers. + //! Used to check if a particular entity was selected. AZ::EntityId HandleMouseInteraction( - const AzFramework::CameraState& cameraState, - const ViewportInteraction::MouseInteractionEvent& mouseInteraction); + const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteractionEvent& mouseInteraction); - /// Do the drawing responsible for the EditorHelpers. - /// @param showIconCheck Provide a custom callback to filter certain entities from - /// displaying an icon under certain conditions. + //! Do the drawing responsible for the EditorHelpers. + //! @param showIconCheck Provide a custom callback to filter certain entities from + //! displaying an icon under certain conditions. void DisplayHelpers( - const AzFramework::ViewportInfo& viewportInfo, const AzFramework::CameraState& cameraState, + const AzFramework::ViewportInfo& viewportInfo, + const AzFramework::CameraState& cameraState, AzFramework::DebugDisplayRequests& debugDisplay, const AZStd::function& showIconCheck); private: - const EditorVisibleEntityDataCache* m_entityDataCache = nullptr; ///< Entity Data queried by the EditorHelpers. + const EditorVisibleEntityDataCache* m_entityDataCache = nullptr; //!< Entity Data queried by the EditorHelpers. }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.cpp index 12f10e9784..578e113aaf 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.cpp @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "EditorInteractionSystemComponent.h" @@ -45,8 +45,7 @@ namespace AzToolsFramework return m_interactionRequests->InternalHandleMouseManipulatorInteraction(mouseInteraction); } - void EditorInteractionSystemComponent::SetHandler( - const ViewportSelectionRequestsBuilderFn& interactionRequestsBuilder) + void EditorInteractionSystemComponent::SetHandler(const ViewportSelectionRequestsBuilderFn& interactionRequestsBuilder) { // when setting a handler, make sure we're connected to the ViewportDebugDisplayEventBus so we // can forward calls to the specific type implementing ViewportSelectionRequests @@ -57,32 +56,30 @@ namespace AzToolsFramework m_entityDataCache = AZStd::make_unique(); - m_interactionRequests.reset(); // BusConnect/Disconnect in constructor/destructor, + m_interactionRequests.reset(); // BusConnect/Disconnect in constructor/destructor, // so have to reset before assigning the new one m_interactionRequests = interactionRequestsBuilder(m_entityDataCache.get()); } void EditorInteractionSystemComponent::SetDefaultHandler() { - SetHandler([](const EditorVisibleEntityDataCache* entityDataCache) - { - return AZStd::make_unique(entityDataCache); - }); + SetHandler( + [](const EditorVisibleEntityDataCache* entityDataCache) + { + return AZStd::make_unique(entityDataCache); + }); } void EditorInteractionSystemComponent::Reflect(AZ::ReflectContext* context) { if (auto serializeContext = azrtti_cast(context)) { - serializeContext->Class() - ->Version(0) - ; + serializeContext->Class()->Version(0); } } void EditorInteractionSystemComponent::DisplayViewport( - const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) + const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -93,8 +90,7 @@ namespace AzToolsFramework } void EditorInteractionSystemComponent::DisplayViewport2d( - const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) + const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) { m_interactionRequests->DisplayViewportSelection2d(viewportInfo, debugDisplay); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.h index 2205add970..8fba5c923d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -18,9 +18,9 @@ namespace AzToolsFramework { - /// System Component to wrap active input handler. - /// EditorInteractionSystemComponent is notified of viewport mouse events from RenderViewport - /// and forwards them to a concrete implementation of ViewportSelectionRequests. + //! System Component to wrap active input handler. + //! EditorInteractionSystemComponent is notified of viewport mouse events from RenderViewport + //! and forwards them to a concrete implementation of ViewportSelectionRequests. class EditorInteractionSystemComponent : public AZ::Component , private EditorInteractionSystemViewportSelectionRequestBus::Handler @@ -37,18 +37,12 @@ namespace AzToolsFramework void SetDefaultHandler() override; // EditorInteractionSystemViewportSelectionRequestBus ... - bool InternalHandleMouseViewportInteraction( - const ViewportInteraction::MouseInteractionEvent& mouseInteraction) override; - bool InternalHandleMouseManipulatorInteraction( - const ViewportInteraction::MouseInteractionEvent& mouseInteraction) override; + bool InternalHandleMouseViewportInteraction(const ViewportInteraction::MouseInteractionEvent& mouseInteraction) override; + bool InternalHandleMouseManipulatorInteraction(const ViewportInteraction::MouseInteractionEvent& mouseInteraction) override; // AzFramework::ViewportDebugDisplayEventBus - void DisplayViewport( - const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) override; - void DisplayViewport2d( - const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) override; + void DisplayViewport(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; + void DisplayViewport2d(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; private: // AZ::Component @@ -58,11 +52,11 @@ namespace AzToolsFramework // EditorEventsBus void NotifyCentralWidgetInitialized() override; - AZStd::unique_ptr m_entityDataCache = nullptr; ///< Visible EntityData cache to be used by concrete - ///< instantiations of ViewportSelectionRequests. + AZStd::unique_ptr m_entityDataCache = nullptr; //!< Visible EntityData cache to be used by concrete + //!< instantiations of ViewportSelectionRequests. - AZStd::unique_ptr m_interactionRequests; ///< Hold a concrete implementation of - ///< ViewportSelectionRequests to handle viewport - ///< input and drawing for the Editor. + AZStd::unique_ptr m_interactionRequests; //!< Hold a concrete implementation of + //!< ViewportSelectionRequests to handle viewport + //!< input and drawing for the Editor. }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h index 09135069d8..184fa29aa0 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -22,10 +22,9 @@ namespace AzToolsFramework { class EditorVisibleEntityDataCache; - /// Bus to handle all mouse events originating from the viewport. - /// Coordinated by the EditorInteractionSystemComponent - class EditorInteractionSystemViewportSelectionRequests - : public AZ::EBusTraits + //! Bus to handle all mouse events originating from the viewport. + //! Coordinated by the EditorInteractionSystemComponent + class EditorInteractionSystemViewportSelectionRequests : public AZ::EBusTraits { public: using BusIdType = AzFramework::EntityContextId; @@ -36,32 +35,31 @@ namespace AzToolsFramework ~EditorInteractionSystemViewportSelectionRequests() = default; }; - /// Alias for factory function to create a new type implementing the ViewportSelectionRequests interface. + //! Alias for factory function to create a new type implementing the ViewportSelectionRequests interface. using ViewportSelectionRequestsBuilderFn = AZStd::function(const EditorVisibleEntityDataCache*)>; - /// Interface for system component implementing the ViewportSelectionRequests interface. - /// This interface also includes a setter to set a custom handler also implementing - /// the ViewportSelectionRequests interface to customize editor behavior. - class EditorInteractionSystemViewportSelection - : public ViewportInteraction::InternalViewportSelectionRequests + //! Interface for system component implementing the ViewportSelectionRequests interface. + //! This interface also includes a setter to set a custom handler also implementing + //! the ViewportSelectionRequests interface to customize editor behavior. + class EditorInteractionSystemViewportSelection : public ViewportInteraction::InternalViewportSelectionRequests { public: - /// \ref SetHandler takes a factory function to create a new type implementing - /// the ViewportSelectionRequests interface. - /// It provides a handler implementing ViewportSelectionRequests to handle all - /// viewport mouse input and drawing. + //! \ref SetHandler takes a factory function to create a new type implementing + //! the ViewportSelectionRequests interface. + //! It provides a handler implementing ViewportSelectionRequests to handle all + //! viewport mouse input and drawing. virtual void SetHandler(const ViewportSelectionRequestsBuilderFn& interactionRequestsBuilder) = 0; - /// \ref SetDefaultHandler is a utility function to set the - /// default editor handler (currently \ref EditorDefaultSelection). - /// This is useful to call after setting another mode and then wishing - /// to return to normal operation of the editor. + //! \ref SetDefaultHandler is a utility function to set the + //! default editor handler (currently \ref EditorDefaultSelection). + //! This is useful to call after setting another mode and then wishing + //! to return to normal operation of the editor. virtual void SetDefaultHandler() = 0; }; - /// Type to inherit to implement EditorInteractionSystemViewportSelection. - /// @note Called by viewport events (RenderViewport) and then handled by concrete - /// implementation of InternalViewportSelectionRequests. + //! Type to inherit to implement EditorInteractionSystemViewportSelection. + //! @note Called by viewport events (RenderViewport) and then handled by concrete + //! implementation of InternalViewportSelectionRequests. using EditorInteractionSystemViewportSelectionRequestBus = AZ::EBus; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorPickEntitySelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorPickEntitySelection.cpp index 1be8ee927b..e026c1f9e0 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorPickEntitySelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorPickEntitySelection.cpp @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "EditorPickEntitySelection.h" @@ -19,9 +19,8 @@ namespace AzToolsFramework { AZ_CLASS_ALLOCATOR_IMPL(EditorPickEntitySelection, AZ::SystemAllocator, 0) - EditorPickEntitySelection::EditorPickEntitySelection( - const EditorVisibleEntityDataCache* entityDataCache) - : m_editorHelpers(AZStd::make_unique(entityDataCache)) + EditorPickEntitySelection::EditorPickEntitySelection(const EditorVisibleEntityDataCache* entityDataCache) + : m_editorHelpers(AZStd::make_unique(entityDataCache)) { } @@ -29,8 +28,7 @@ namespace AzToolsFramework { if (m_hoveredEntityId.IsValid()) { - ToolsApplicationRequestBus::Broadcast( - &ToolsApplicationRequests::SetEntityHighlighted, m_hoveredEntityId, false); + ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetEntityHighlighted, m_hoveredEntityId, false); } } @@ -41,8 +39,7 @@ namespace AzToolsFramework // highlighted - hoveredEntityId is an in/out param that is updated based on the change in // entityIdUnderCursor. static void HandleAccents( - const AZ::EntityId entityIdUnderCursor, AZ::EntityId& hoveredEntityId, - const ViewportInteraction::MouseButtons mouseButtons) + const AZ::EntityId entityIdUnderCursor, AZ::EntityId& hoveredEntityId, const ViewportInteraction::MouseButtons mouseButtons) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -55,8 +52,7 @@ namespace AzToolsFramework { if (hoveredEntityId.IsValid()) { - ToolsApplicationRequestBus::Broadcast( - &ToolsApplicationRequests::SetEntityHighlighted, hoveredEntityId, false); + ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetEntityHighlighted, hoveredEntityId, false); hoveredEntityId.SetInvalid(); } @@ -68,8 +64,7 @@ namespace AzToolsFramework { if (entityIdUnderCursor.IsValid() && entityIdUnderCursor != hoveredEntityId) { - ToolsApplicationRequestBus::Broadcast( - &ToolsApplicationRequests::SetEntityHighlighted, entityIdUnderCursor, true); + ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetEntityHighlighted, entityIdUnderCursor, true); hoveredEntityId = entityIdUnderCursor; } @@ -93,8 +88,7 @@ namespace AzToolsFramework if (m_cachedEntityIdUnderCursor.IsValid()) { // if we clicked on a valid entity id, actually try to set it - EditorPickModeRequestBus::Broadcast( - &EditorPickModeRequests::PickModeSelectEntity, m_cachedEntityIdUnderCursor); + EditorPickModeRequestBus::Broadcast(&EditorPickModeRequests::PickModeSelectEntity, m_cachedEntityIdUnderCursor); } // after a click, always stop pick mode, whether we set an entity or not @@ -105,16 +99,18 @@ namespace AzToolsFramework } void EditorPickEntitySelection::DisplayViewportSelection( - const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) + const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) { const AzFramework::CameraState cameraState = GetCameraState(viewportInfo.m_viewportId); m_editorHelpers->DisplayHelpers( - viewportInfo, cameraState, debugDisplay, [](AZ::EntityId){ return true; }); + viewportInfo, cameraState, debugDisplay, + [](AZ::EntityId) + { + return true; + }); HandleAccents( - m_cachedEntityIdUnderCursor, m_hoveredEntityId, - ViewportInteraction::BuildMouseButtons(QGuiApplication::mouseButtons())); + m_cachedEntityIdUnderCursor, m_hoveredEntityId, ViewportInteraction::BuildMouseButtons(QGuiApplication::mouseButtons())); } } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorPickEntitySelection.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorPickEntitySelection.h index 07aafe2607..2c956e1534 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorPickEntitySelection.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorPickEntitySelection.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -17,10 +17,9 @@ namespace AzToolsFramework { - /// Viewport interaction that will handle assigning an entity in the viewport to - /// an entity field in the entity inspector. - class EditorPickEntitySelection - : public ViewportInteraction::InternalViewportSelectionRequests + //! Viewport interaction that will handle assigning an entity in the viewport to + //! an entity field in the entity inspector. + class EditorPickEntitySelection : public ViewportInteraction::InternalViewportSelectionRequests { public: AZ_CLASS_ALLOCATOR_DECL @@ -30,15 +29,12 @@ namespace AzToolsFramework private: // ViewportInteraction::InternalViewportSelectionRequests ... - bool InternalHandleMouseViewportInteraction( - const ViewportInteraction::MouseInteractionEvent& mouseInteraction) override; + bool InternalHandleMouseViewportInteraction(const ViewportInteraction::MouseInteractionEvent& mouseInteraction) override; void DisplayViewportSelection( - const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) override; + const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; - AZStd::unique_ptr m_editorHelpers; ///< Editor visualization of entities (icons, shapes, debug visuals etc). - - AZ::EntityId m_hoveredEntityId; ///< What EntityId is the mouse currently hovering over (if any). - AZ::EntityId m_cachedEntityIdUnderCursor; ///< Store the EntityId on each mouse move for use in Display. + AZStd::unique_ptr m_editorHelpers; //!< Editor visualization of entities (icons, shapes, debug visuals etc). + AZ::EntityId m_hoveredEntityId; //!< What EntityId is the mouse currently hovering over (if any). + AZ::EntityId m_cachedEntityIdUnderCursor; //!< Store the EntityId on each mouse move for use in Display. }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorSelectionUtil.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorSelectionUtil.cpp index d0143c5517..7856c159ab 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorSelectionUtil.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorSelectionUtil.cpp @@ -1,38 +1,36 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "EditorSelectionUtil.h" -#include -#include -#include #include +#include +#include +#include #include #include #include namespace AzToolsFramework { - /// Default ray length for picking in the viewport. + // default ray length for picking in the viewport static const float s_pickRayLength = 1000.0f; - AZ::Vector3 CalculateCenterOffset( - const AZ::EntityId entityId, const EditorTransformComponentSelectionRequests::Pivot pivot) + AZ::Vector3 CalculateCenterOffset(const AZ::EntityId entityId, const EditorTransformComponentSelectionRequests::Pivot pivot) { if (Centered(pivot)) { const AZ::Entity* entity = AZ::Interface::Get()->FindEntity(entityId); - if (const AZ::Aabb localBound = AzFramework::CalculateEntityLocalBoundsUnion(entity); - localBound.IsValid()) + if (const AZ::Aabb localBound = AzFramework::CalculateEntityLocalBoundsUnion(entity); localBound.IsValid()) { return localBound.GetCenter(); } @@ -41,76 +39,71 @@ namespace AzToolsFramework return AZ::Vector3::CreateZero(); } - float CalculateScreenToWorldMultiplier( - const AZ::Vector3& worldPosition, const AzFramework::CameraState& cameraState) + float CalculateScreenToWorldMultiplier(const AZ::Vector3& worldPosition, const AzFramework::CameraState& cameraState) { const float apparentDistance = 10.0f; // compute the distance from the camera, projected onto the camera's forward direction // note: this keeps the scale value the same when positions are at the edge of the screen - const float projectedCameraDistance = - std::abs((cameraState.m_position - worldPosition).Dot(cameraState.m_forward)); + const float projectedCameraDistance = std::abs((cameraState.m_position - worldPosition).Dot(cameraState.m_forward)); // author sizes of bounds/manipulators as they would appear // in perspective 10 meters from the camera. return AZ::GetMax(projectedCameraDistance, cameraState.m_nearClip) / apparentDistance; } - AzFramework::ScreenPoint GetScreenPosition(const int viewportId, const AZ::Vector3& worldTranslation) + AzFramework::ScreenPoint GetScreenPosition(const int viewportId, const AZ::Vector3& worldTranslation) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); auto screenPosition = AzFramework::ScreenPoint(0, 0); ViewportInteraction::ViewportInteractionRequestBus::EventResult( - screenPosition, viewportId, - &ViewportInteraction::ViewportInteractionRequestBus::Events::ViewportWorldToScreen, + screenPosition, viewportId, &ViewportInteraction::ViewportInteractionRequestBus::Events::ViewportWorldToScreen, worldTranslation); return screenPosition; } - bool AabbIntersectMouseRay( - const ViewportInteraction::MouseInteraction& mouseInteraction, const AZ::Aabb& aabb) + bool AabbIntersectMouseRay(const ViewportInteraction::MouseInteraction& mouseInteraction, const AZ::Aabb& aabb) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); - const AZ::Vector3 rayScaledDir = - mouseInteraction.m_mousePick.m_rayDirection * s_pickRayLength; + const AZ::Vector3 rayScaledDir = mouseInteraction.m_mousePick.m_rayDirection * s_pickRayLength; AZ::Vector3 startNormal; float t, end; return AZ::Intersect::IntersectRayAABB( - mouseInteraction.m_mousePick.m_rayOrigin, rayScaledDir, - rayScaledDir.GetReciprocal(), aabb, t, end, startNormal) > 0; + mouseInteraction.m_mousePick.m_rayOrigin, rayScaledDir, rayScaledDir.GetReciprocal(), aabb, t, end, startNormal) > 0; } bool PickEntity( - const AZ::EntityId entityId, const ViewportInteraction::MouseInteraction& mouseInteraction, - float& closestDistance, const int viewportId) + const AZ::EntityId entityId, + const ViewportInteraction::MouseInteraction& mouseInteraction, + float& closestDistance, + const int viewportId) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Entity); bool entityPicked = false; EditorComponentSelectionRequestsBus::EnumerateHandlersId( - entityId, [mouseInteraction, &entityPicked, &closestDistance, viewportId] - (EditorComponentSelectionRequests* handler) -> bool - { - if (handler->SupportsEditorRayIntersect()) + entityId, + [mouseInteraction, &entityPicked, &closestDistance, viewportId](EditorComponentSelectionRequests* handler) -> bool { - float distance = std::numeric_limits::max(); - const bool intersection = handler->EditorSelectionIntersectRayViewport( - { viewportId }, mouseInteraction.m_mousePick.m_rayOrigin, - mouseInteraction.m_mousePick.m_rayDirection, distance); - - if (intersection && distance < closestDistance) + if (handler->SupportsEditorRayIntersect()) { - entityPicked = true; - closestDistance = distance; - } - } + float distance = std::numeric_limits::max(); + const bool intersection = handler->EditorSelectionIntersectRayViewport( + { viewportId }, mouseInteraction.m_mousePick.m_rayOrigin, mouseInteraction.m_mousePick.m_rayDirection, distance); - return true; // iterate over all handlers - }); + if (intersection && distance < closestDistance) + { + entityPicked = true; + closestDistance = distance; + } + } + + return true; // iterate over all handlers + }); return entityPicked; } @@ -119,9 +112,8 @@ namespace AzToolsFramework { AzFramework::CameraState cameraState; ViewportInteraction::ViewportInteractionRequestBus::EventResult( - cameraState, viewportId, - &ViewportInteraction::ViewportInteractionRequestBus::Events::GetCameraState); - + cameraState, viewportId, &ViewportInteraction::ViewportInteractionRequestBus::Events::GetCameraState); + return cameraState; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorSelectionUtil.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorSelectionUtil.h index e904277078..a7c6368d65 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorSelectionUtil.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorSelectionUtil.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -30,56 +30,53 @@ namespace AzFramework namespace AzToolsFramework { - /// Is the pivot at the center of the object (middle of extents) or at the - /// exported authored object root position. + //! Is the pivot at the center of the object (middle of extents) or at the + //! exported authored object root position. inline bool Centered(const EditorTransformComponentSelectionRequests::Pivot pivot) { return pivot == EditorTransformComponentSelectionRequests::Pivot::Center; } - /// Return offset from object pivot to center if center is true, otherwise Vector3::Zero. + //! Return offset from object pivot to center if center is true, otherwise Vector3::Zero. AZ::Vector3 CalculateCenterOffset(AZ::EntityId entityId, EditorTransformComponentSelectionRequests::Pivot pivot); - /// Calculate scale factor based on distance from camera - float CalculateScreenToWorldMultiplier( - const AZ::Vector3& worldPosition, const AzFramework::CameraState& cameraState); + //! Calculate scale factor based on distance from camera + float CalculateScreenToWorldMultiplier(const AZ::Vector3& worldPosition, const AzFramework::CameraState& cameraState); - /// Map from world space to screen space. - AzFramework::ScreenPoint GetScreenPosition(int viewportId, const AZ::Vector3& worldTranslation); + //! Map from world space to screen space. + AzFramework::ScreenPoint GetScreenPosition(int viewportId, const AZ::Vector3& worldTranslation); - /// Given a mouse interaction, determine if the pick ray from its position - /// in screen space intersected an aabb in world space. - bool AabbIntersectMouseRay( - const ViewportInteraction::MouseInteraction& mouseInteraction, const AZ::Aabb& aabb); + //! Given a mouse interaction, determine if the pick ray from its position + //! in screen space intersected an aabb in world space. + bool AabbIntersectMouseRay(const ViewportInteraction::MouseInteraction& mouseInteraction, const AZ::Aabb& aabb); - /// Return if a mouse interaction (pick ray) did intersect the tested EntityId. + //! Return if a mouse interaction (pick ray) did intersect the tested EntityId. bool PickEntity( - AZ::EntityId entityId, const ViewportInteraction::MouseInteraction& mouseInteraction, - float& closestDistance, int viewportId); + AZ::EntityId entityId, const ViewportInteraction::MouseInteraction& mouseInteraction, float& closestDistance, int viewportId); - /// Wrapper for EBus call to return the CameraState for a given viewport. + //! Wrapper for EBus call to return the CameraState for a given viewport. AzFramework::CameraState GetCameraState(int viewportId); - /// Wrapper for EBus call to return the DPI scaling for a given viewport. - float GetScreenDisplayScaling(const int viewportId); + //! Wrapper for EBus call to return the DPI scaling for a given viewport. + float GetScreenDisplayScaling(int viewportId); - /// A utility to return the center of several points. - /// Take several positions and store the min and max of each in - /// turn - when all points have been added return the center/midpoint. + //! A utility to return the center of several points. + //! Take several positions and store the min and max of each in + //! turn - when all points have been added return the center/midpoint. class MidpointCalculator { public: - /// Default constructed with min and max initialized to opposites. + //! Default constructed with min and max initialized to opposites. MidpointCalculator() = default; - /// Call this for all positions you want to be considered. + //! Call this for all positions you want to be considered. void AddPosition(const AZ::Vector3& position) { m_minPosition = position.GetMin(m_minPosition); m_maxPosition = position.GetMax(m_maxPosition); } - /// Once all positions have been added, call this to return the midpoint. + //! Once all positions have been added, call this to return the midpoint. AZ::Vector3 CalculateMidpoint() const { return m_minPosition + (m_maxPosition - m_minPosition) * 0.5f; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp index db321d6818..fee0267766 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp @@ -1,29 +1,30 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "EditorTransformComponentSelection.h" -#include #include #include #include #include +#include #include #include #include #include -#include +#include #include #include +#include #include #include #include @@ -36,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -47,20 +47,40 @@ namespace AzToolsFramework AZ_CLASS_ALLOCATOR_IMPL(EditorTransformComponentSelection, AZ::SystemAllocator, 0) AZ_CVAR( - float, cl_viewportGizmoAxisLineWidth, 4.0f, nullptr, AZ::ConsoleFunctorFlags::Null, + float, + cl_viewportGizmoAxisLineWidth, + 4.0f, + nullptr, + AZ::ConsoleFunctorFlags::Null, "The width of the line for the viewport axis gizmo"); AZ_CVAR( - float, cl_viewportGizmoAxisLineLength, 0.7f, nullptr, AZ::ConsoleFunctorFlags::Null, + float, + cl_viewportGizmoAxisLineLength, + 0.7f, + nullptr, + AZ::ConsoleFunctorFlags::Null, "The length of the line for the viewport axis gizmo"); AZ_CVAR( - float, cl_viewportGizmoAxisLabelOffset, 1.15f, nullptr, AZ::ConsoleFunctorFlags::Null, + float, + cl_viewportGizmoAxisLabelOffset, + 1.15f, + nullptr, + AZ::ConsoleFunctorFlags::Null, "The offset of the label for the viewport axis gizmo"); AZ_CVAR( - float, cl_viewportGizmoAxisLabelSize, 1.0f, nullptr, AZ::ConsoleFunctorFlags::Null, + float, + cl_viewportGizmoAxisLabelSize, + 1.0f, + nullptr, + AZ::ConsoleFunctorFlags::Null, "The size of each label for the viewport axis gizmo"); AZ_CVAR( - AZ::Vector2, cl_viewportGizmoAxisScreenPosition, AZ::Vector2(0.045f, 0.9f), nullptr, - AZ::ConsoleFunctorFlags::Null, "The screen position of the gizmo in normalized (0-1) ndc space"); + AZ::Vector2, + cl_viewportGizmoAxisScreenPosition, + AZ::Vector2(0.045f, 0.9f), + nullptr, + AZ::ConsoleFunctorFlags::Null, + "The screen position of the gizmo in normalized (0-1) ndc space"); // strings related to new viewport interaction model (EditorTransformComponentSelection) static const char* const s_togglePivotTitleRightClick = "Toggle pivot"; @@ -125,7 +145,8 @@ namespace AzToolsFramework static const int s_defaultViewportId = 0; - static const float s_pivotSize = 0.075f; ///< The size of the pivot (box) to render when selected. + static const float s_pivotSize = 0.075f; // the size of the pivot (box) to render when selected + // data passed to manipulators when processing mouse interactions // m_entityIds should be sorted based on the entity hierarchy // (see SortEntitiesByLocationInHierarchy and BuildSortedEntityIdVectorFromEntityIdContainer) @@ -146,8 +167,7 @@ namespace AzToolsFramework bool OptionalFrame::HasTransformOverride() const { - return m_translationOverride.has_value() - || m_orientationOverride.has_value(); + return m_translationOverride.has_value() || m_orientationOverride.has_value(); } bool OptionalFrame::HasEntityOverride() const @@ -226,7 +246,7 @@ namespace AzToolsFramework return mouseInteraction.m_mouseInteraction.m_mouseButtons.Middle() && mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down && (mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Alt() || - mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Ctrl()); + mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Ctrl()); } static bool ManipulatorDitto(const ViewportInteraction::MouseInteractionEvent& mouseInteraction) @@ -263,8 +283,7 @@ namespace AzToolsFramework } } - static EditorTransformComponentSelectionRequests::Pivot TogglePivotMode( - const EditorTransformComponentSelectionRequests::Pivot pivot) + static EditorTransformComponentSelectionRequests::Pivot TogglePivotMode(const EditorTransformComponentSelectionRequests::Pivot pivot) { switch (pivot) { @@ -282,8 +301,7 @@ namespace AzToolsFramework template static AZStd::vector EntityIdVectorFromContainer(const EntityIdContainer& entityIdContainer) { - static_assert(AZStd::is_same::value, - "Container type is not an EntityId"); + static_assert(AZStd::is_same::value, "Container type is not an EntityId"); AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); return AZStd::vector(entityIdContainer.begin(), entityIdContainer.end()); @@ -293,8 +311,7 @@ namespace AzToolsFramework template static AZStd::vector EntityIdVectorFromMap(const EntityIdMap& entityIdMap) { - static_assert(AZStd::is_same::value, - "Container key type is not an EntityId"); + static_assert(AZStd::is_same::value, "Container key type is not an EntityId"); AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -318,10 +335,15 @@ namespace AzToolsFramework template static void BoxSelectAddRemoveToEntitySelection( - const AZStd::optional& boxSelect, const AzFramework::ScreenPoint& screenPosition, const AZ::EntityId visibleEntityId, - const EntityIdContainer& incomingEntityIds, EntityIdContainer& outgoingEntityIds, + const AZStd::optional& boxSelect, + const AzFramework::ScreenPoint& screenPosition, + const AZ::EntityId visibleEntityId, + const EntityIdContainer& incomingEntityIds, + EntityIdContainer& outgoingEntityIds, EditorTransformComponentSelection& entityTransformComponentSelection, - EntitySelectFuncType selectFunc1, EntitySelectFuncType selectFunc2, Compare outgoingCheck) + EntitySelectFuncType selectFunc1, + EntitySelectFuncType selectFunc2, + Compare outgoingCheck) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -349,10 +371,14 @@ namespace AzToolsFramework template static void EntityBoxSelectUpdateGeneral( - const AZStd::optional& boxSelect, EditorTransformComponentSelection& editorTransformComponentSelection, - const EntityIdContainer& activeSelectedEntityIds, EntityIdContainer& selectedEntityIdsBeforeBoxSelect, - EntityIdContainer& potentialSelectedEntityIds, EntityIdContainer& potentialDeselectedEntityIds, - const EditorVisibleEntityDataCache& entityDataCache, const int viewportId, + const AZStd::optional& boxSelect, + EditorTransformComponentSelection& editorTransformComponentSelection, + const EntityIdContainer& activeSelectedEntityIds, + EntityIdContainer& selectedEntityIdsBeforeBoxSelect, + EntityIdContainer& potentialSelectedEntityIds, + EntityIdContainer& potentialDeselectedEntityIds, + const EditorVisibleEntityDataCache& entityDataCache, + const int viewportId, const ViewportInteraction::KeyboardModifiers currentKeyboardModifiers, const ViewportInteraction::KeyboardModifiers& previousKeyboardModifiers) { @@ -382,8 +408,7 @@ namespace AzToolsFramework for (size_t entityCacheIndex = 0; entityCacheIndex < entityDataCache.VisibleEntityDataCount(); ++entityCacheIndex) { - if ( entityDataCache.IsVisibleEntityLocked(entityCacheIndex) - || !entityDataCache.IsVisibleEntityVisible(entityCacheIndex)) + if (entityDataCache.IsVisibleEntityLocked(entityCacheIndex) || !entityDataCache.IsVisibleEntityVisible(entityCacheIndex)) { continue; } @@ -396,10 +421,8 @@ namespace AzToolsFramework if (currentKeyboardModifiers.Ctrl()) { BoxSelectAddRemoveToEntitySelection( - boxSelect, screenPosition, entityId, - selectedEntityIdsBeforeBoxSelect, potentialDeselectedEntityIds, - editorTransformComponentSelection, - &EditorTransformComponentSelection::RemoveEntityFromSelection, + boxSelect, screenPosition, entityId, selectedEntityIdsBeforeBoxSelect, potentialDeselectedEntityIds, + editorTransformComponentSelection, &EditorTransformComponentSelection::RemoveEntityFromSelection, &EditorTransformComponentSelection::AddEntityToSelection, [](const typename EntityIdContainer::const_iterator entityId, const EntityIdContainer& entityIds) { @@ -409,10 +432,8 @@ namespace AzToolsFramework else { BoxSelectAddRemoveToEntitySelection( - boxSelect, screenPosition, entityId, - activeSelectedEntityIds, potentialSelectedEntityIds, - editorTransformComponentSelection, - &EditorTransformComponentSelection::AddEntityToSelection, + boxSelect, screenPosition, entityId, activeSelectedEntityIds, potentialSelectedEntityIds, + editorTransformComponentSelection, &EditorTransformComponentSelection::AddEntityToSelection, &EditorTransformComponentSelection::RemoveEntityFromSelection, [](const typename EntityIdContainer::const_iterator entityId, const EntityIdContainer& entityIds) { @@ -429,62 +450,53 @@ namespace AzToolsFramework for (auto& entityIdLookup : entityIdManipulators.m_lookups) { - entityIdLookup.second.m_initial = - AZ::Transform::CreateTranslation(GetWorldTranslation(entityIdLookup.first)); + entityIdLookup.second.m_initial = AZ::Transform::CreateTranslation(GetWorldTranslation(entityIdLookup.first)); } } static void DestroyCluster(const ViewportUi::ClusterId clusterId) { ViewportUi::ViewportUiRequestBus::Event( - ViewportUi::DefaultViewportId, - &ViewportUi::ViewportUiRequestBus::Events::RemoveCluster, - clusterId); + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::RemoveCluster, clusterId); } static void SetViewportUiClusterVisible(const ViewportUi::ClusterId clusterId, const bool visible) { ViewportUi::ViewportUiRequestBus::Event( - ViewportUi::DefaultViewportId, - &ViewportUi::ViewportUiRequestBus::Events::SetClusterVisible, - clusterId, visible); + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterVisible, clusterId, visible); } static void SetViewportUiClusterActiveButton(const ViewportUi::ClusterId clusterId, const ViewportUi::ButtonId buttonId) { ViewportUi::ViewportUiRequestBus::Event( - ViewportUi::DefaultViewportId, - &ViewportUi::ViewportUiRequestBus::Events::SetClusterActiveButton, - clusterId, buttonId); + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterActiveButton, clusterId, buttonId); } static ViewportUi::ButtonId RegisterClusterButton(const ViewportUi::ClusterId clusterId, const char* iconName) { ViewportUi::ButtonId buttonId; ViewportUi::ViewportUiRequestBus::EventResult( - buttonId, ViewportUi::DefaultViewportId, - &ViewportUi::ViewportUiRequestBus::Events::CreateClusterButton, - clusterId, AZStd::string::format(":/stylesheet/img/UI20/toolbar/%s.svg", iconName)); + buttonId, ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateClusterButton, clusterId, + AZStd::string::format(":/stylesheet/img/UI20/toolbar/%s.svg", iconName)); return buttonId; } // return either center or entity pivot - static AZ::Vector3 CalculatePivotTranslation( - const AZ::EntityId entityId, const EditorTransformComponentSelectionRequests::Pivot pivot) + static AZ::Vector3 CalculatePivotTranslation(const AZ::EntityId entityId, const EditorTransformComponentSelectionRequests::Pivot pivot) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity(); - AZ::TransformBus::EventResult( - worldFromLocal, entityId, &AZ::TransformBus::Events::GetWorldTM); + AZ::TransformBus::EventResult(worldFromLocal, entityId, &AZ::TransformBus::Events::GetWorldTM); return worldFromLocal.TransformPoint(CalculateCenterOffset(entityId, pivot)); } void EditorTransformComponentSelection::UpdateSpaceCluster(const ReferenceFrame referenceFrame) { - auto buttonIdFromFrameFn = [this](const ReferenceFrame referenceFrame) { + auto buttonIdFromFrameFn = [this](const ReferenceFrame referenceFrame) + { switch (referenceFrame) { case ReferenceFrame::Local: @@ -498,14 +510,13 @@ namespace AzToolsFramework }; ViewportUi::ViewportUiRequestBus::Event( - ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterActiveButton, m_spaceCluster.m_spaceClusterId, - buttonIdFromFrameFn(referenceFrame)); + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterActiveButton, + m_spaceCluster.m_spaceClusterId, buttonIdFromFrameFn(referenceFrame)); } namespace ETCS { - PivotOrientationResult CalculatePivotOrientation( - const AZ::EntityId entityId, const ReferenceFrame referenceFrame) + PivotOrientationResult CalculatePivotOrientation(const AZ::EntityId entityId, const ReferenceFrame referenceFrame) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -515,21 +526,17 @@ namespace AzToolsFramework switch (referenceFrame) { case ReferenceFrame::Local: - AZ::TransformBus::EventResult( - result.m_worldOrientation, entityId, - &AZ::TransformBus::Events::GetWorldRotationQuaternion); + AZ::TransformBus::EventResult(result.m_worldOrientation, entityId, &AZ::TransformBus::Events::GetWorldRotationQuaternion); break; case ReferenceFrame::Parent: { AZ::EntityId parentId; - AZ::TransformBus::EventResult( - parentId, entityId, &AZ::TransformBus::Events::GetParentId); + AZ::TransformBus::EventResult(parentId, entityId, &AZ::TransformBus::Events::GetParentId); if (parentId.IsValid()) { AZ::TransformBus::EventResult( - result.m_worldOrientation, parentId, - &AZ::TransformBus::Events::GetWorldRotationQuaternion); + result.m_worldOrientation, parentId, &AZ::TransformBus::Events::GetWorldRotationQuaternion); result.m_parentId = parentId; } @@ -559,8 +566,7 @@ namespace AzToolsFramework { // check if this entity has a parent AZ::EntityId parentId; - AZ::TransformBus::EventResult( - parentId, entityIdLookupIt->first, &AZ::TransformBus::Events::GetParentId); + AZ::TransformBus::EventResult(parentId, entityIdLookupIt->first, &AZ::TransformBus::Events::GetParentId); // if no parent, space will be world, terminate if (!parentId.IsValid()) @@ -575,9 +581,7 @@ namespace AzToolsFramework if (!commonParentId.IsValid()) { commonParentId = parentId; - AZ::TransformBus::EventResult( - result.m_worldOrientation, parentId, - &AZ::TransformBus::Events::GetWorldRotationQuaternion); + AZ::TransformBus::EventResult(result.m_worldOrientation, parentId, &AZ::TransformBus::Events::GetWorldRotationQuaternion); } // if we know we still have a parent in common @@ -602,8 +606,7 @@ namespace AzToolsFramework static AZ::Vector3 CalculatePivotTranslationForEntityIds( const EntityIdMap& entityIdMap, const EditorTransformComponentSelectionRequests::Pivot pivot) { - static_assert(AZStd::is_same::value, - "Container key type is not an EntityId"); + static_assert(AZStd::is_same::value, "Container key type is not an EntityId"); AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -628,11 +631,9 @@ namespace AzToolsFramework namespace ETCS { template - PivotOrientationResult CalculatePivotOrientationForEntityIds( - const EntityIdMap& entityIdMap, const ReferenceFrame referenceFrame) + PivotOrientationResult CalculatePivotOrientationForEntityIds(const EntityIdMap& entityIdMap, const ReferenceFrame referenceFrame) { - static_assert(AZStd::is_same::value, - "Container key type is not an EntityId"); + static_assert(AZStd::is_same::value, "Container key type is not an EntityId"); AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -660,12 +661,11 @@ namespace AzToolsFramework { template PivotOrientationResult CalculateSelectionPivotOrientation( - const EntityIdMap& entityIdMap, const OptionalFrame& pivotOverrideFrame, - const ReferenceFrame referenceFrame) + const EntityIdMap& entityIdMap, const OptionalFrame& pivotOverrideFrame, const ReferenceFrame referenceFrame) { - static_assert(AZStd::is_same::value, - "Container key type is not an EntityId"); - static_assert(AZStd::is_same::value, + static_assert(AZStd::is_same::value, "Container key type is not an EntityId"); + static_assert( + AZStd::is_same::value, "Container value type is not an EntityIdManipulators::Lookup"); AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -728,20 +728,16 @@ namespace AzToolsFramework { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); - return pivotOverrideFrame.m_translationOverride.value_or( - CalculatePivotTranslationForEntityIds(entityIdMap, pivot)); + return pivotOverrideFrame.m_translationOverride.value_or(CalculatePivotTranslationForEntityIds(entityIdMap, pivot)); } template static AZ::Quaternion RecalculateAverageManipulatorOrientation( - const EntityIdMap& entityIdMap, - const OptionalFrame& pivotOverrideFrame, - const ReferenceFrame referenceFrame) + const EntityIdMap& entityIdMap, const OptionalFrame& pivotOverrideFrame, const ReferenceFrame referenceFrame) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); - return ETCS::CalculateSelectionPivotOrientation( - entityIdMap, pivotOverrideFrame, referenceFrame).m_worldOrientation; + return ETCS::CalculateSelectionPivotOrientation(entityIdMap, pivotOverrideFrame, referenceFrame).m_worldOrientation; } template @@ -756,15 +752,12 @@ namespace AzToolsFramework // return final transform, if we have an override for translation use that, otherwise // use centered translation of selection return AZ::Transform::CreateFromQuaternionAndTranslation( - RecalculateAverageManipulatorOrientation( - entityIdMap, pivotOverrideFrame, referenceFrame), - RecalculateAverageManipulatorTranslation( - entityIdMap, pivotOverrideFrame, pivot)); + RecalculateAverageManipulatorOrientation(entityIdMap, pivotOverrideFrame, referenceFrame), + RecalculateAverageManipulatorTranslation(entityIdMap, pivotOverrideFrame, pivot)); } template - static void BuildSortedEntityIdVectorFromEntityIdMap( - const EntityIdMap& entityIds, EntityIdList& sortedEntityIdsOut) + static void BuildSortedEntityIdVectorFromEntityIdMap(const EntityIdMap& entityIds, EntityIdList& sortedEntityIdsOut) { sortedEntityIdsOut = EntityIdVectorFromMap(entityIds); SortEntitiesByLocationInHierarchy(sortedEntityIdsOut); @@ -777,8 +770,7 @@ namespace AzToolsFramework for (auto& entityIdLookup : entityManipulators.m_lookups) { AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity(); - AZ::TransformBus::EventResult( - worldFromLocal, entityIdLookup.first, &AZ::TransformBus::Events::GetWorldTM); + AZ::TransformBus::EventResult(worldFromLocal, entityIdLookup.first, &AZ::TransformBus::Events::GetWorldTM); entityIdLookup.second.m_initial = worldFromLocal; } @@ -804,11 +796,13 @@ namespace AzToolsFramework template static void UpdateTranslationManipulator( - const Action& action, const EntityIdContainer& entityIdContainer, + const Action& action, + const EntityIdContainer& entityIdContainer, EntityIdManipulators& entityIdManipulators, OptionalFrame& pivotOverrideFrame, ViewportInteraction::KeyboardModifiers& prevModifiers, - bool& transformChangedInternally, const AZStd::optional spaceLock) + bool& transformChangedInternally, + const AZStd::optional spaceLock) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -817,8 +811,7 @@ namespace AzToolsFramework if (action.m_modifiers.Ctrl()) { // moving with ctrl - setting override - pivotOverrideFrame.m_translationOverride = - entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); + pivotOverrideFrame.m_translationOverride = entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); InitializeTranslationLookup(entityIdManipulators); } else @@ -827,8 +820,7 @@ namespace AzToolsFramework // note: used for parent and world depending on the current reference frame const auto pivotOrientation = - ETCS::CalculateSelectionPivotOrientation( - entityIdManipulators.m_lookups, pivotOverrideFrame, referenceFrame); + ETCS::CalculateSelectionPivotOrientation(entityIdManipulators.m_lookups, pivotOverrideFrame, referenceFrame); // note: must use sorted entityIds based on hierarchy order when updating transforms for (AZ::EntityId entityId : entityIdContainer) @@ -847,46 +839,37 @@ namespace AzToolsFramework { // move in each entities local space at once AZ::Quaternion worldOrientation = AZ::Quaternion::CreateIdentity(); - AZ::TransformBus::EventResult( - worldOrientation, entityId, &AZ::TransformBus::Events::GetWorldRotationQuaternion); + AZ::TransformBus::EventResult(worldOrientation, entityId, &AZ::TransformBus::Events::GetWorldRotationQuaternion); - const AZ::Transform space = - entityIdManipulators.m_manipulators->GetLocalTransform().GetInverse() * - AZ::Transform::CreateFromQuaternionAndTranslation( - worldOrientation, worldTranslation); + const AZ::Transform space = entityIdManipulators.m_manipulators->GetLocalTransform().GetInverse() * + AZ::Transform::CreateFromQuaternionAndTranslation(worldOrientation, worldTranslation); - const AZ::Vector3 localOffset = space.TransformVector(action.LocalPositionOffset()); + const AZ::Vector3 localOffset = space.TransformVector(action.LocalPositionOffset()); if (action.m_modifiers != prevModifiers) { - entityItLookupIt->second.m_initial = - AZ::Transform::CreateTranslation(worldTranslation - localOffset); + entityItLookupIt->second.m_initial = AZ::Transform::CreateTranslation(worldTranslation - localOffset); } ETCS::SetEntityWorldTranslation( - entityId, entityItLookupIt->second.m_initial.GetTranslation() + localOffset, - transformChangedInternally); + entityId, entityItLookupIt->second.m_initial.GetTranslation() + localOffset, transformChangedInternally); } break; case ReferenceFrame::Parent: case ReferenceFrame::World: { - AZ::Quaternion offsetRotation = - pivotOrientation.m_worldOrientation * - QuaternionFromTransformNoScaling( - entityIdManipulators.m_manipulators->GetLocalTransform().GetInverse()); + AZ::Quaternion offsetRotation = pivotOrientation.m_worldOrientation * + QuaternionFromTransformNoScaling(entityIdManipulators.m_manipulators->GetLocalTransform().GetInverse()); const AZ::Vector3 localOffset = offsetRotation.TransformVector(action.LocalPositionOffset()); if (action.m_modifiers != prevModifiers) { - entityItLookupIt->second.m_initial = - AZ::Transform::CreateTranslation(worldTranslation - localOffset); + entityItLookupIt->second.m_initial = AZ::Transform::CreateTranslation(worldTranslation - localOffset); } ETCS::SetEntityWorldTranslation( - entityId, entityItLookupIt->second.m_initial.GetTranslation() + localOffset, - transformChangedInternally); + entityId, entityItLookupIt->second.m_initial.GetTranslation() + localOffset, transformChangedInternally); } break; } @@ -895,8 +878,7 @@ namespace AzToolsFramework // if transform pivot override has been set, make sure to update it when we move it if (pivotOverrideFrame.m_translationOverride) { - pivotOverrideFrame.m_translationOverride = - entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); + pivotOverrideFrame.m_translationOverride = entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); } } @@ -904,8 +886,10 @@ namespace AzToolsFramework } static void HandleAccents( - const bool hasSelectedEntities, const AZ::EntityId entityIdUnderCursor, - const bool ctrlHeld, AZ::EntityId& hoveredEntityId, + const bool hasSelectedEntities, + const AZ::EntityId entityIdUnderCursor, + const bool ctrlHeld, + AZ::EntityId& hoveredEntityId, const ViewportInteraction::MouseButtons mouseButtons, const bool usingBoxSelect) { @@ -914,13 +898,11 @@ namespace AzToolsFramework const bool invalidMouseButtonHeld = mouseButtons.Middle() || mouseButtons.Right(); if ((hoveredEntityId.IsValid() && hoveredEntityId != entityIdUnderCursor) || - (hasSelectedEntities && !ctrlHeld && hoveredEntityId.IsValid()) || - invalidMouseButtonHeld) + (hasSelectedEntities && !ctrlHeld && hoveredEntityId.IsValid()) || invalidMouseButtonHeld) { if (hoveredEntityId.IsValid()) { - ToolsApplicationRequestBus::Broadcast( - &ToolsApplicationRequests::SetEntityHighlighted, hoveredEntityId, false); + ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetEntityHighlighted, hoveredEntityId, false); hoveredEntityId.SetInvalid(); } @@ -930,8 +912,7 @@ namespace AzToolsFramework { if (entityIdUnderCursor.IsValid()) { - ToolsApplicationRequestBus::Broadcast( - &ToolsApplicationRequests::SetEntityHighlighted, entityIdUnderCursor, true); + ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetEntityHighlighted, entityIdUnderCursor, true); hoveredEntityId = entityIdUnderCursor; } @@ -946,15 +927,13 @@ namespace AzToolsFramework // get unsnapped terrain position (world space) AZ::Vector3 worldSurfacePosition; ViewportInteraction::MainEditorViewportInteractionRequestBus::EventResult( - worldSurfacePosition, viewportId, - &ViewportInteraction::MainEditorViewportInteractionRequestBus::Events::PickTerrain, + worldSurfacePosition, viewportId, &ViewportInteraction::MainEditorViewportInteractionRequestBus::Events::PickTerrain, mouseInteraction.m_mousePick.m_screenCoordinates); // convert to local space - snap if enabled const GridSnapParameters gridSnapParams = GridSnapSettings(viewportId); const AZ::Vector3 finalSurfacePosition = gridSnapParams.m_gridSnap - ? CalculateSnappedTerrainPosition( - worldSurfacePosition, AZ::Transform::CreateIdentity(), viewportId, gridSnapParams.m_gridSize) + ? CalculateSnappedTerrainPosition(worldSurfacePosition, AZ::Transform::CreateIdentity(), viewportId, gridSnapParams.m_gridSize) : worldSurfacePosition; return finalSurfacePosition; @@ -981,10 +960,9 @@ namespace AzToolsFramework for (AZ::EntityId entityId : entityIds) { AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity(); - AZ::TransformBus::EventResult( - worldFromLocal, entityId, &AZ::TransformBus::Events::GetWorldTM); + AZ::TransformBus::EventResult(worldFromLocal, entityId, &AZ::TransformBus::Events::GetWorldTM); - transformsBefore.insert({ entityId, worldFromLocal }); + transformsBefore.insert({ entityId, worldFromLocal }); } return transformsBefore; @@ -992,8 +970,7 @@ namespace AzToolsFramework // ask the visible entity data cache if the entity is selectable in the viewport // (useful in the context of drawing when we only care about entities we can see) - static bool SelectableInVisibleViewportCache( - const EditorVisibleEntityDataCache& entityDataCache, const AZ::EntityId entityId) + static bool SelectableInVisibleViewportCache(const EditorVisibleEntityDataCache& entityDataCache, const AZ::EntityId entityId) { if (auto entityIndex = entityDataCache.GetVisibleEntityIndexFromId(entityId)) { @@ -1021,15 +998,12 @@ namespace AzToolsFramework // is handled internally - this call is often required after an action/shortcut of some kind static void RefreshUiAfterChange(const EntityIdList& entitiyIds) { - EditorTransformChangeNotificationBus::Broadcast( - &EditorTransformChangeNotifications::OnEntityTransformChanged, entitiyIds); + EditorTransformChangeNotificationBus::Broadcast(&EditorTransformChangeNotifications::OnEntityTransformChanged, entitiyIds); - ToolsApplicationNotificationBus::Broadcast( - &ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, Refresh_Values); + ToolsApplicationNotificationBus::Broadcast(&ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, Refresh_Values); } - EditorTransformComponentSelection::EditorTransformComponentSelection( - const EditorVisibleEntityDataCache* entityDataCache) + EditorTransformComponentSelection::EditorTransformComponentSelection(const EditorVisibleEntityDataCache* entityDataCache) : m_entityDataCache(entityDataCache) { const AzFramework::EntityContextId entityContextId = GetEntityContextId(); @@ -1090,101 +1064,96 @@ namespace AzToolsFramework m_boxSelect.InstallLeftMouseDown( [this, entityBoxSelectData](const ViewportInteraction::MouseInteractionEvent& /*mouseInteraction*/) - { - // begin selection undo/redo command - entityBoxSelectData->m_boxSelectSelectionCommand = - AZStd::make_unique(EntityIdList(), s_entityBoxSelectUndoRedoDesc); - // grab currently selected entities - entityBoxSelectData->m_selectedEntityIdsBeforeBoxSelect = m_selectedEntityIds; - }); + { + // begin selection undo/redo command + entityBoxSelectData->m_boxSelectSelectionCommand = + AZStd::make_unique(EntityIdList(), s_entityBoxSelectUndoRedoDesc); + // grab currently selected entities + entityBoxSelectData->m_selectedEntityIdsBeforeBoxSelect = m_selectedEntityIds; + }); m_boxSelect.InstallMouseMove( [this, entityBoxSelectData](const ViewportInteraction::MouseInteractionEvent& mouseInteraction) - { - EntityBoxSelectUpdateGeneral( - m_boxSelect.BoxRegion(), *this, m_selectedEntityIds, entityBoxSelectData->m_selectedEntityIdsBeforeBoxSelect, - entityBoxSelectData->m_potentialSelectedEntityIds, entityBoxSelectData->m_potentialDeselectedEntityIds, - *m_entityDataCache, mouseInteraction.m_mouseInteraction.m_interactionId.m_viewportId, - mouseInteraction.m_mouseInteraction.m_keyboardModifiers, - m_boxSelect.PreviousModifiers()); - }); + { + EntityBoxSelectUpdateGeneral( + m_boxSelect.BoxRegion(), *this, m_selectedEntityIds, entityBoxSelectData->m_selectedEntityIdsBeforeBoxSelect, + entityBoxSelectData->m_potentialSelectedEntityIds, entityBoxSelectData->m_potentialDeselectedEntityIds, + *m_entityDataCache, mouseInteraction.m_mouseInteraction.m_interactionId.m_viewportId, + mouseInteraction.m_mouseInteraction.m_keyboardModifiers, m_boxSelect.PreviousModifiers()); + }); m_boxSelect.InstallLeftMouseUp( [this, entityBoxSelectData]() - { - entityBoxSelectData->m_boxSelectSelectionCommand->UpdateSelection(EntityIdVectorFromContainer(m_selectedEntityIds)); - - // if we know a change in selection has occurred, record the undo step - if ( !entityBoxSelectData->m_potentialDeselectedEntityIds.empty() - || !entityBoxSelectData->m_potentialSelectedEntityIds.empty()) { - ScopedUndoBatch undoBatch(s_entityBoxSelectUndoRedoDesc); + entityBoxSelectData->m_boxSelectSelectionCommand->UpdateSelection(EntityIdVectorFromContainer(m_selectedEntityIds)); - // restore manipulator overrides when undoing - if (m_entityIdManipulators.m_manipulators && m_selectedEntityIds.empty()) + // if we know a change in selection has occurred, record the undo step + if (!entityBoxSelectData->m_potentialDeselectedEntityIds.empty() || + !entityBoxSelectData->m_potentialSelectedEntityIds.empty()) { - CreateEntityManipulatorDeselectCommand(undoBatch); + ScopedUndoBatch undoBatch(s_entityBoxSelectUndoRedoDesc); + + // restore manipulator overrides when undoing + if (m_entityIdManipulators.m_manipulators && m_selectedEntityIds.empty()) + { + CreateEntityManipulatorDeselectCommand(undoBatch); + } + + entityBoxSelectData->m_boxSelectSelectionCommand->SetParent(undoBatch.GetUndoBatch()); + entityBoxSelectData->m_boxSelectSelectionCommand.release(); + + SetSelectedEntities(EntityIdVectorFromContainer(m_selectedEntityIds)); + // note: manipulators will be updated in AfterEntitySelectionChanged + + // clear pivot override when selection is empty + if (m_selectedEntityIds.empty()) + { + m_pivotOverrideFrame.Reset(); + } + } + else + { + entityBoxSelectData->m_boxSelectSelectionCommand.reset(); } - entityBoxSelectData->m_boxSelectSelectionCommand->SetParent(undoBatch.GetUndoBatch()); - entityBoxSelectData->m_boxSelectSelectionCommand.release(); - - SetSelectedEntities(EntityIdVectorFromContainer(m_selectedEntityIds)); - // note: manipulators will be updated in AfterEntitySelectionChanged - - // clear pivot override when selection is empty - if (m_selectedEntityIds.empty()) - { - m_pivotOverrideFrame.Reset(); - } - } - else - { - entityBoxSelectData->m_boxSelectSelectionCommand.reset(); - } - - entityBoxSelectData->m_potentialSelectedEntityIds.clear(); - entityBoxSelectData->m_potentialDeselectedEntityIds.clear(); - entityBoxSelectData->m_selectedEntityIdsBeforeBoxSelect.clear(); - }); + entityBoxSelectData->m_potentialSelectedEntityIds.clear(); + entityBoxSelectData->m_potentialDeselectedEntityIds.clear(); + entityBoxSelectData->m_selectedEntityIdsBeforeBoxSelect.clear(); + }); m_boxSelect.InstallDisplayScene( - [this, entityBoxSelectData] - (const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) - { - const auto modifiers = ViewportInteraction::KeyboardModifiers( - ViewportInteraction::TranslateKeyboardModifiers(QApplication::queryKeyboardModifiers())); - - if (m_boxSelect.PreviousModifiers() != modifiers) + [this, entityBoxSelectData](const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) { - EntityBoxSelectUpdateGeneral( - m_boxSelect.BoxRegion(), *this, m_selectedEntityIds, - entityBoxSelectData->m_selectedEntityIdsBeforeBoxSelect, - entityBoxSelectData->m_potentialSelectedEntityIds, - entityBoxSelectData->m_potentialDeselectedEntityIds, - *m_entityDataCache, viewportInfo.m_viewportId, modifiers, - m_boxSelect.PreviousModifiers()); - } + const auto modifiers = ViewportInteraction::KeyboardModifiers( + ViewportInteraction::TranslateKeyboardModifiers(QApplication::queryKeyboardModifiers())); - debugDisplay.DepthTestOff(); - debugDisplay.SetColor(s_selectedEntityAabbColor); - - for (AZ::EntityId entityId : entityBoxSelectData->m_potentialSelectedEntityIds) - { - const auto entityIdIt = entityBoxSelectData->m_selectedEntityIdsBeforeBoxSelect.find(entityId); - - // don't show box when re-adding from previous selection - if (entityIdIt != entityBoxSelectData->m_selectedEntityIdsBeforeBoxSelect.end()) + if (m_boxSelect.PreviousModifiers() != modifiers) { - continue; + EntityBoxSelectUpdateGeneral( + m_boxSelect.BoxRegion(), *this, m_selectedEntityIds, entityBoxSelectData->m_selectedEntityIdsBeforeBoxSelect, + entityBoxSelectData->m_potentialSelectedEntityIds, entityBoxSelectData->m_potentialDeselectedEntityIds, + *m_entityDataCache, viewportInfo.m_viewportId, modifiers, m_boxSelect.PreviousModifiers()); } - const AZ::Aabb bound = CalculateEditorEntitySelectionBounds(entityId, viewportInfo); - debugDisplay.DrawSolidBox(bound.GetMin(), bound.GetMax()); - } + debugDisplay.DepthTestOff(); + debugDisplay.SetColor(s_selectedEntityAabbColor); - debugDisplay.DepthTestOn(); - }); + for (AZ::EntityId entityId : entityBoxSelectData->m_potentialSelectedEntityIds) + { + const auto entityIdIt = entityBoxSelectData->m_selectedEntityIdsBeforeBoxSelect.find(entityId); + + // don't show box when re-adding from previous selection + if (entityIdIt != entityBoxSelectData->m_selectedEntityIdsBeforeBoxSelect.end()) + { + continue; + } + + const AZ::Aabb bound = CalculateEditorEntitySelectionBounds(entityId, viewportInfo); + debugDisplay.DrawSolidBox(bound.GetMin(), bound.GetMax()); + } + + debugDisplay.DepthTestOn(); + }); } EntityManipulatorCommand::State EditorTransformComponentSelection::CreateManipulatorCommandStateFromSelf() const @@ -1197,14 +1166,9 @@ namespace AzToolsFramework return {}; } - return { - BuildPivotOverride( - m_pivotOverrideFrame.HasTranslationOverride(), - m_pivotOverrideFrame.HasOrientationOverride()), - TransformNormalizedScale( - m_entityIdManipulators.m_manipulators->GetLocalTransform()), - m_pivotOverrideFrame.m_pickedEntityIdOverride - }; + return { BuildPivotOverride(m_pivotOverrideFrame.HasTranslationOverride(), m_pivotOverrideFrame.HasOrientationOverride()), + TransformNormalizedScale(m_entityIdManipulators.m_manipulators->GetLocalTransform()), + m_pivotOverrideFrame.m_pickedEntityIdOverride }; } void EditorTransformComponentSelection::BeginRecordManipulatorCommand() @@ -1214,14 +1178,13 @@ namespace AzToolsFramework // we must have an existing parent undo batch active when beginning to record // a manipulator command UndoSystem::URSequencePoint* currentUndoOperation = nullptr; - ToolsApplicationRequests::Bus::BroadcastResult( - currentUndoOperation, &ToolsApplicationRequests::GetCurrentUndoBatch); + ToolsApplicationRequests::Bus::BroadcastResult(currentUndoOperation, &ToolsApplicationRequests::GetCurrentUndoBatch); if (currentUndoOperation) { // check here if translation or orientation override are set - m_manipulatorMoveCommand = AZStd::make_unique( - CreateManipulatorCommandStateFromSelf(), s_manipulatorUndoRedoName); + m_manipulatorMoveCommand = + AZStd::make_unique(CreateManipulatorCommandStateFromSelf(), s_manipulatorUndoRedoName); } } @@ -1234,10 +1197,11 @@ namespace AzToolsFramework m_manipulatorMoveCommand->SetManipulatorAfter(CreateManipulatorCommandStateFromSelf()); UndoSystem::URSequencePoint* currentUndoOperation = nullptr; - ToolsApplicationRequests::Bus::BroadcastResult( - currentUndoOperation, &ToolsApplicationRequests::GetCurrentUndoBatch); + ToolsApplicationRequests::Bus::BroadcastResult(currentUndoOperation, &ToolsApplicationRequests::GetCurrentUndoBatch); - AZ_Assert(currentUndoOperation, "The only way we should have reached this block is if " + AZ_Assert( + currentUndoOperation, + "The only way we should have reached this block is if " "m_manipulatorMoveCommand was created by calling BeginRecordManipulatorMouseMoveCommand. " "If we've reached this point and currentUndoOperation is null, something bad has happened " "in the undo system"); @@ -1254,18 +1218,15 @@ namespace AzToolsFramework { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); - AZStd::unique_ptr translationManipulators = - AZStd::make_unique( - TranslationManipulators::Dimensions::Three, - AZ::Transform::CreateIdentity(), AZ::Vector3::CreateOne()); + AZStd::unique_ptr translationManipulators = AZStd::make_unique( + TranslationManipulators::Dimensions::Three, AZ::Transform::CreateIdentity(), AZ::Vector3::CreateOne()); InitializeManipulators(*translationManipulators); ConfigureTranslationManipulatorAppearance3d(&*translationManipulators); translationManipulators->SetLocalTransform( - RecalculateAverageManipulatorTransform( - m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, m_pivotMode, m_referenceFrame)); + RecalculateAverageManipulatorTransform(m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, m_pivotMode, m_referenceFrame)); // lambdas capture shared_ptr by value to increment ref count auto manipulatorEntityIds = AZStd::make_shared(); @@ -1277,95 +1238,92 @@ namespace AzToolsFramework // linear translationManipulators->InstallLinearManipulatorMouseDownCallback( [this, manipulatorEntityIds]([[maybe_unused]] const LinearManipulator::Action& action) mutable - { - // important to sort entityIds based on hierarchy order when updating transforms - BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds->m_entityIds); + { + // important to sort entityIds based on hierarchy order when updating transforms + BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds->m_entityIds); - InitializeTranslationLookup(m_entityIdManipulators); + InitializeTranslationLookup(m_entityIdManipulators); - m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); - m_axisPreview.m_orientation = QuaternionFromTransformNoScaling( - m_entityIdManipulators.m_manipulators->GetLocalTransform()); + m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); + m_axisPreview.m_orientation = QuaternionFromTransformNoScaling(m_entityIdManipulators.m_manipulators->GetLocalTransform()); - // [ref 1.] - BeginRecordManipulatorCommand(); - }); + // [ref 1.] + BeginRecordManipulatorCommand(); + }); ViewportInteraction::KeyboardModifiers prevModifiers{}; translationManipulators->InstallLinearManipulatorMouseMoveCallback( [this, prevModifiers, manipulatorEntityIds](const LinearManipulator::Action& action) mutable -> void - { - UpdateTranslationManipulator( - action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers, + { + UpdateTranslationManipulator( + action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers, m_transformChangedInternally, m_spaceCluster.m_spaceLock); - }); + }); translationManipulators->InstallLinearManipulatorMouseUpCallback( [this]([[maybe_unused]] const LinearManipulator::Action& action) mutable - { - EndRecordManipulatorCommand(); - }); + { + EndRecordManipulatorCommand(); + }); // planar translationManipulators->InstallPlanarManipulatorMouseDownCallback( [this, manipulatorEntityIds]([[maybe_unused]] const PlanarManipulator::Action& action) - { - // important to sort entityIds based on hierarchy order when updating transforms - BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds->m_entityIds); + { + // important to sort entityIds based on hierarchy order when updating transforms + BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds->m_entityIds); - InitializeTranslationLookup(m_entityIdManipulators); + InitializeTranslationLookup(m_entityIdManipulators); - m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); - m_axisPreview.m_orientation = QuaternionFromTransformNoScaling( - m_entityIdManipulators.m_manipulators->GetLocalTransform()); + m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); + m_axisPreview.m_orientation = QuaternionFromTransformNoScaling(m_entityIdManipulators.m_manipulators->GetLocalTransform()); - // [ref 1.] - BeginRecordManipulatorCommand(); - }); + // [ref 1.] + BeginRecordManipulatorCommand(); + }); translationManipulators->InstallPlanarManipulatorMouseMoveCallback( [this, prevModifiers, manipulatorEntityIds](const PlanarManipulator::Action& action) mutable -> void - { - UpdateTranslationManipulator( - action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers, + { + UpdateTranslationManipulator( + action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers, m_transformChangedInternally, m_spaceCluster.m_spaceLock); - }); + }); translationManipulators->InstallPlanarManipulatorMouseUpCallback( [this, manipulatorEntityIds](const PlanarManipulator::Action& /*action*/) - { - EndRecordManipulatorCommand(); - }); + { + EndRecordManipulatorCommand(); + }); // surface translationManipulators->InstallSurfaceManipulatorMouseDownCallback( [this, manipulatorEntityIds]([[maybe_unused]] const SurfaceManipulator::Action& action) - { - BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds->m_entityIds); + { + BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds->m_entityIds); - InitializeTranslationLookup(m_entityIdManipulators); + InitializeTranslationLookup(m_entityIdManipulators); - m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); - m_axisPreview.m_orientation = QuaternionFromTransformNoScaling( - m_entityIdManipulators.m_manipulators->GetLocalTransform()); + m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); + m_axisPreview.m_orientation = QuaternionFromTransformNoScaling(m_entityIdManipulators.m_manipulators->GetLocalTransform()); - // [ref 1.] - BeginRecordManipulatorCommand(); - }); + // [ref 1.] + BeginRecordManipulatorCommand(); + }); translationManipulators->InstallSurfaceManipulatorMouseMoveCallback( [this, prevModifiers, manipulatorEntityIds](const SurfaceManipulator::Action& action) mutable -> void - { - UpdateTranslationManipulator( - action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers, + { + UpdateTranslationManipulator( + action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers, m_transformChangedInternally, m_spaceCluster.m_spaceLock); - }); + }); translationManipulators->InstallSurfaceManipulatorMouseUpCallback( [this, manipulatorEntityIds](const SurfaceManipulator::Action& /*action*/) - { - EndRecordManipulatorCommand(); - }); + { + EndRecordManipulatorCommand(); + }); // transfer ownership m_entityIdManipulators.m_manipulators = AZStd::move(translationManipulators); @@ -1381,18 +1339,12 @@ namespace AzToolsFramework InitializeManipulators(*rotationManipulators); rotationManipulators->SetLocalTransform( - RecalculateAverageManipulatorTransform( - m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, m_pivotMode, m_referenceFrame)); + RecalculateAverageManipulatorTransform(m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, m_pivotMode, m_referenceFrame)); // view - rotationManipulators->SetLocalAxes( - AZ::Vector3::CreateAxisX(), - AZ::Vector3::CreateAxisY(), - AZ::Vector3::CreateAxisZ()); + rotationManipulators->SetLocalAxes(AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ()); rotationManipulators->ConfigureView( - 2.0f, - AzFramework::ViewportColors::XAxisColor, - AzFramework::ViewportColors::YAxisColor, + 2.0f, AzFramework::ViewportColors::XAxisColor, AzFramework::ViewportColors::YAxisColor, AzFramework::ViewportColors::ZAxisColor); struct SharedRotationState @@ -1403,149 +1355,139 @@ namespace AzToolsFramework }; // lambdas capture shared_ptr by value to increment ref count - AZStd::shared_ptr sharedRotationState = - AZStd::make_shared(); + AZStd::shared_ptr sharedRotationState = AZStd::make_shared(); rotationManipulators->InstallLeftMouseDownCallback( [this, sharedRotationState](const AngularManipulator::Action& /*action*/) mutable -> void - { - sharedRotationState->m_savedOrientation = AZ::Quaternion::CreateIdentity(); - sharedRotationState->m_referenceFrameAtMouseDown = m_referenceFrame; - // important to sort entityIds based on hierarchy order when updating transforms - BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, sharedRotationState->m_entityIds); - - for (auto& entityIdLookup : m_entityIdManipulators.m_lookups) { - AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity(); - AZ::TransformBus::EventResult( - worldFromLocal, entityIdLookup.first, &AZ::TransformBus::Events::GetWorldTM); + sharedRotationState->m_savedOrientation = AZ::Quaternion::CreateIdentity(); + sharedRotationState->m_referenceFrameAtMouseDown = m_referenceFrame; + // important to sort entityIds based on hierarchy order when updating transforms + BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, sharedRotationState->m_entityIds); - entityIdLookup.second.m_initial = worldFromLocal; - } + for (auto& entityIdLookup : m_entityIdManipulators.m_lookups) + { + AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity(); + AZ::TransformBus::EventResult(worldFromLocal, entityIdLookup.first, &AZ::TransformBus::Events::GetWorldTM); - m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); - m_axisPreview.m_orientation = QuaternionFromTransformNoScaling( - m_entityIdManipulators.m_manipulators->GetLocalTransform()); + entityIdLookup.second.m_initial = worldFromLocal; + } - // [ref 1.] - BeginRecordManipulatorCommand(); - }); + m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); + m_axisPreview.m_orientation = QuaternionFromTransformNoScaling(m_entityIdManipulators.m_manipulators->GetLocalTransform()); + + // [ref 1.] + BeginRecordManipulatorCommand(); + }); ViewportInteraction::KeyboardModifiers prevModifiers{}; rotationManipulators->InstallMouseMoveCallback( - [this, prevModifiers, sharedRotationState] - (const AngularManipulator::Action& action) mutable -> void - { - const ReferenceFrame referenceFrame = m_spaceCluster.m_spaceLock.value_or(ReferenceFrameFromModifiers(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 - if (action.m_modifiers.Ctrl()) + [this, prevModifiers, sharedRotationState](const AngularManipulator::Action& action) mutable -> void { - m_pivotOverrideFrame.m_orientationOverride = manipulatorOrientation; - } + const ReferenceFrame referenceFrame = m_spaceCluster.m_spaceLock.value_or(ReferenceFrameFromModifiers(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 + if (action.m_modifiers.Ctrl()) + { + m_pivotOverrideFrame.m_orientationOverride = manipulatorOrientation; + } - // 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()) - { - m_entityIdManipulators.m_manipulators->SetLocalTransform( - AZ::Transform::CreateFromQuaternionAndTranslation( - manipulatorOrientation, - m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation())); - } + // 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()) + { + m_entityIdManipulators.m_manipulators->SetLocalTransform(AZ::Transform::CreateFromQuaternionAndTranslation( + manipulatorOrientation, m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation())); + } - // save state if we change the type of rotation we're doing to to prevent snapping - if (prevModifiers != action.m_modifiers) - { - UpdateInitialRotation(m_entityIdManipulators); - sharedRotationState->m_savedOrientation = action.m_current.m_delta.GetInverseFull(); - } + // save state if we change the type of rotation we're doing to to prevent snapping + if (prevModifiers != action.m_modifiers) + { + UpdateInitialRotation(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); - sharedRotationState->m_savedOrientation = action.m_current.m_delta.GetInverseFull(); - } - else - { - const auto pivotOrientation = - ETCS::CalculateSelectionPivotOrientation( + // allow the user to modify the orientation without moving the object if ctrl is held + if (action.m_modifiers.Ctrl()) + { + UpdateInitialRotation(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); - // note: must use sorted entityIds based on hierarchy order when updating transforms - for (AZ::EntityId entityId : sharedRotationState->m_entityIds) - { - auto entityIdLookupIt = m_entityIdManipulators.m_lookups.find(entityId); - if (entityIdLookupIt == m_entityIdManipulators.m_lookups.end()) + // note: must use sorted entityIds based on hierarchy order when updating transforms + for (AZ::EntityId entityId : sharedRotationState->m_entityIds) { - continue; - } - - // make sure we take into account how we move the axis independent of object - // if Ctrl was held to adjust the orientation of the axes separately - const AZ::Transform offsetRotation = AZ::Transform::CreateFromQuaternion( - sharedRotationState->m_savedOrientation * action.m_current.m_delta); - - switch (referenceFrame) - { - case ReferenceFrame::Local: + auto entityIdLookupIt = m_entityIdManipulators.m_lookups.find(entityId); + if (entityIdLookupIt == m_entityIdManipulators.m_lookups.end()) { - const AZ::Quaternion rotation = entityIdLookupIt->second.m_initial.GetRotation().GetNormalized(); - const AZ::Vector3 position = entityIdLookupIt->second.m_initial.GetTranslation(); - const float scale = entityIdLookupIt->second.m_initial.GetUniformScale(); - - const AZ::Vector3 centerOffset = CalculateCenterOffset(entityId, m_pivotMode); - - // scale -> rotate -> translate - SetEntityWorldTransform( - entityId, - AZ::Transform::CreateTranslation(position) * - AZ::Transform::CreateFromQuaternion(rotation) * - AZ::Transform::CreateTranslation(centerOffset) * offsetRotation * - AZ::Transform::CreateTranslation(-centerOffset) * - AZ::Transform::CreateUniformScale(scale)); + continue; } - break; - case ReferenceFrame::Parent: + + // make sure we take into account how we move the axis independent of object + // if Ctrl was held to adjust the orientation of the axes separately + const AZ::Transform offsetRotation = + AZ::Transform::CreateFromQuaternion(sharedRotationState->m_savedOrientation * action.m_current.m_delta); + + switch (referenceFrame) { - const AZ::Transform pivotTransform = - AZ::Transform::CreateFromQuaternionAndTranslation( + case ReferenceFrame::Local: + { + const AZ::Quaternion rotation = entityIdLookupIt->second.m_initial.GetRotation().GetNormalized(); + const AZ::Vector3 position = entityIdLookupIt->second.m_initial.GetTranslation(); + const float scale = entityIdLookupIt->second.m_initial.GetUniformScale(); + + const AZ::Vector3 centerOffset = CalculateCenterOffset(entityId, m_pivotMode); + + // scale -> rotate -> translate + SetEntityWorldTransform( + entityId, + AZ::Transform::CreateTranslation(position) * AZ::Transform::CreateFromQuaternion(rotation) * + AZ::Transform::CreateTranslation(centerOffset) * offsetRotation * + AZ::Transform::CreateTranslation(-centerOffset) * AZ::Transform::CreateUniformScale(scale)); + } + break; + case ReferenceFrame::Parent: + { + 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; + 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( + 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()); - const AZ::Transform transformInPivotSpace = - pivotTransform.GetInverse() * entityIdLookupIt->second.m_initial; + const AZ::Transform transformInPivotSpace = + pivotTransform.GetInverse() * entityIdLookupIt->second.m_initial; - SetEntityWorldTransform(entityId, pivotTransform * offsetRotation * transformInPivotSpace); + SetEntityWorldTransform(entityId, pivotTransform * offsetRotation * transformInPivotSpace); + } + break; } - break; } } - } - prevModifiers = action.m_modifiers; - }); + prevModifiers = action.m_modifiers; + }); rotationManipulators->InstallLeftMouseUpCallback( [this](const AngularManipulator::Action& /*action*/) - { - EndRecordManipulatorCommand(); - }); + { + EndRecordManipulatorCommand(); + }); rotationManipulators->Register(g_mainManipulatorManagerId); @@ -1557,30 +1499,20 @@ namespace AzToolsFramework { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); - AZStd::unique_ptr scaleManipulators = - AZStd::make_unique(AZ::Transform::CreateIdentity()); + AZStd::unique_ptr scaleManipulators = AZStd::make_unique(AZ::Transform::CreateIdentity()); InitializeManipulators(*scaleManipulators); scaleManipulators->SetLocalTransform( - RecalculateAverageManipulatorTransform( - m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, m_pivotMode, m_referenceFrame)); + RecalculateAverageManipulatorTransform(m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, m_pivotMode, m_referenceFrame)); - scaleManipulators->SetAxes( - AZ::Vector3::CreateAxisX(), - AZ::Vector3::CreateAxisY(), - AZ::Vector3::CreateAxisZ()); - scaleManipulators->ConfigureView( - 2.0f, - AZ::Color::CreateOne(), - AZ::Color::CreateOne(), - AZ::Color::CreateOne()); + scaleManipulators->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(); - auto uniformLeftMouseDownCallback = - [this, manipulatorEntityIds]([[maybe_unused]] const LinearManipulator::Action& action) + auto uniformLeftMouseDownCallback = [this, manipulatorEntityIds]([[maybe_unused]] const LinearManipulator::Action& action) { // important to sort entityIds based on hierarchy order when updating transforms BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds->m_entityIds); @@ -1588,22 +1520,19 @@ namespace AzToolsFramework for (auto& entityIdLookup : m_entityIdManipulators.m_lookups) { AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity(); - AZ::TransformBus::EventResult( - worldFromLocal, entityIdLookup.first, &AZ::TransformBus::Events::GetWorldTM); + AZ::TransformBus::EventResult(worldFromLocal, entityIdLookup.first, &AZ::TransformBus::Events::GetWorldTM); entityIdLookup.second.m_initial = worldFromLocal; } m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); - m_axisPreview.m_orientation = QuaternionFromTransformNoScaling( - m_entityIdManipulators.m_manipulators->GetLocalTransform()); + m_axisPreview.m_orientation = QuaternionFromTransformNoScaling(m_entityIdManipulators.m_manipulators->GetLocalTransform()); }; auto uniformLeftMouseUpCallback = [this, manipulatorEntityIds]([[maybe_unused]] const LinearManipulator::Action& action) { - m_entityIdManipulators.m_manipulators->SetLocalTransform( - RecalculateAverageManipulatorTransform( - m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, m_pivotMode, m_referenceFrame)); + m_entityIdManipulators.m_manipulators->SetLocalTransform(RecalculateAverageManipulatorTransform( + m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, m_pivotMode, m_referenceFrame)); }; auto uniformLeftMouseMoveCallback = [this, manipulatorEntityIds](const LinearManipulator::Action& action) @@ -1620,7 +1549,8 @@ namespace AzToolsFramework const AZ::Transform initial = entityIdLookupIt->second.m_initial; const float initialScale = initial.GetUniformScale(); - const auto sumVectorElements = [](const AZ::Vector3& vec) { + const auto sumVectorElements = [](const AZ::Vector3& vec) + { return vec.GetX() + vec.GetY() + vec.GetZ(); }; @@ -1630,19 +1560,16 @@ namespace AzToolsFramework if (action.m_modifiers.Alt()) { - const AZ::Transform pivotTransform = TransformNormalizedScale( - entityIdLookupIt->second.m_initial); - const AZ::Transform transformInPivotSpace = - pivotTransform.GetInverse() * initial; + 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; + const AZ::Transform pivotTransform = + TransformNormalizedScale(m_entityIdManipulators.m_manipulators->GetLocalTransform()); + const AZ::Transform transformInPivotSpace = pivotTransform.GetInverse() * initial; SetEntityWorldTransform(entityId, pivotTransform * scaleTransform * transformInPivotSpace); } @@ -1674,11 +1601,10 @@ namespace AzToolsFramework { if (IsSelectableInViewport(entityId)) { - const AZ::ComponentId transformComponentId = GetTransformComponentId(entityId); + const AZ::ComponentId transformComponentId = GetTransformComponentId(entityId); if (transformComponentId != AZ::InvalidComponentId) { - manipulators.AddEntityComponentIdPair( - AZ::EntityComponentIdPair(entityId, transformComponentId)); + manipulators.AddEntityComponentIdPair(AZ::EntityComponentIdPair(entityId, transformComponentId)); m_entityIdManipulators.m_lookups.insert_key(entityId); } } @@ -1692,11 +1618,10 @@ namespace AzToolsFramework { if (IsSelectableInViewport(entityId)) { - const AZ::ComponentId transformComponentId = GetTransformComponentId(entityId); + const AZ::ComponentId transformComponentId = GetTransformComponentId(entityId); if (transformComponentId != AZ::InvalidComponentId) { - manipulators.AddEntityComponentIdPair( - AZ::EntityComponentIdPair(entityId, transformComponentId)); + manipulators.AddEntityComponentIdPair(AZ::EntityComponentIdPair(entityId, transformComponentId)); m_entityIdManipulators.m_lookups.insert_key(entityId); } } @@ -1754,8 +1679,7 @@ namespace AzToolsFramework CreateEntityManipulatorDeselectCommand(undoBatch); } - auto selectionCommand = - AZStd::make_unique(nextEntityIds, s_entityDeselectUndoRedoDesc); + auto selectionCommand = AZStd::make_unique(nextEntityIds, s_entityDeselectUndoRedoDesc); selectionCommand->SetParent(undoBatch.GetUndoBatch()); selectionCommand.release(); @@ -1785,8 +1709,7 @@ namespace AzToolsFramework return false; } - bool EditorTransformComponentSelection::HandleMouseInteraction( - const ViewportInteraction::MouseInteractionEvent& mouseInteraction) + bool EditorTransformComponentSelection::HandleMouseInteraction(const ViewportInteraction::MouseInteractionEvent& mouseInteraction) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -1816,17 +1739,15 @@ namespace AzToolsFramework } AZ::Transform worldFromLocal; - AZ::TransformBus::EventResult( - worldFromLocal, entityId, &AZ::TransformBus::Events::GetWorldTM); + AZ::TransformBus::EventResult(worldFromLocal, entityId, &AZ::TransformBus::Events::GetWorldTM); const AZ::Vector3 boxPosition = worldFromLocal.TransformPoint(CalculateCenterOffset(entityId, m_pivotMode)); - const AZ::Vector3 scaledSize = AZ::Vector3(s_pivotSize) * - CalculateScreenToWorldMultiplier(worldFromLocal.GetTranslation(), cameraState); + const AZ::Vector3 scaledSize = + AZ::Vector3(s_pivotSize) * CalculateScreenToWorldMultiplier(worldFromLocal.GetTranslation(), cameraState); if (AabbIntersectMouseRay( - mouseInteraction.m_mouseInteraction, - AZ::Aabb::CreateFromMinMax(boxPosition - scaledSize, boxPosition + scaledSize))) + mouseInteraction.m_mouseInteraction, AZ::Aabb::CreateFromMinMax(boxPosition - scaledSize, boxPosition + scaledSize))) { m_cachedEntityIdUnderCursor = entityId; } @@ -1834,16 +1755,15 @@ namespace AzToolsFramework const AZ::EntityId entityIdUnderCursor = m_cachedEntityIdUnderCursor; - EditorContextMenuUpdate( - m_contextMenu, mouseInteraction); + EditorContextMenuUpdate(m_contextMenu, mouseInteraction); m_boxSelect.HandleMouseInteraction(mouseInteraction); if (Input::CycleManipulator(mouseInteraction)) { const size_t scrollBound = 2; - const auto nextMode = (static_cast(m_mode) + scrollBound + - (MouseWheelDelta(mouseInteraction) < 0.0f ? 1 : -1)) % scrollBound; + const auto nextMode = + (static_cast(m_mode) + scrollBound + (MouseWheelDelta(mouseInteraction) < 0.0f ? 1 : -1)) % scrollBound; SetTransformMode(static_cast(nextMode)); @@ -1883,8 +1803,7 @@ namespace AzToolsFramework if (entityIdUnderCursor.IsValid()) { AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity(); - AZ::TransformBus::EventResult( - worldFromLocal, entityIdUnderCursor, &AZ::TransformBus::Events::GetWorldTM); + AZ::TransformBus::EventResult(worldFromLocal, entityIdUnderCursor, &AZ::TransformBus::Events::GetWorldTM); switch (m_mode) { @@ -1912,8 +1831,7 @@ namespace AzToolsFramework if (entityIdUnderCursor.IsValid()) { AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity(); - AZ::TransformBus::EventResult( - worldFromLocal, entityIdUnderCursor, &AZ::TransformBus::Events::GetWorldTM); + AZ::TransformBus::EventResult(worldFromLocal, entityIdUnderCursor, &AZ::TransformBus::Events::GetWorldTM); switch (m_mode) { @@ -1938,15 +1856,14 @@ namespace AzToolsFramework // try snapping to the terrain (if in Translation mode) and entity wasn't picked if (Input::SnapTerrain(mouseInteraction)) { - for(AZ::EntityId entityId : m_selectedEntityIds) + for (AZ::EntityId entityId : m_selectedEntityIds) { ScopedUndoBatch::MarkEntityDirty(entityId); } if (m_mode == Mode::Translation) { - const AZ::Vector3 finalSurfacePosition = - PickTerrainPosition(mouseInteraction.m_mouseInteraction); + const AZ::Vector3 finalSurfacePosition = PickTerrainPosition(mouseInteraction.m_mouseInteraction); // handle modifier alternatives if (Input::IndividualDitto(mouseInteraction)) @@ -1958,7 +1875,7 @@ namespace AzToolsFramework CopyTranslationToSelectedEntitiesGroup(finalSurfacePosition); } } - else if(m_mode == Mode::Rotation) + else if (m_mode == Mode::Rotation) { // handle modifier alternatives if (Input::IndividualDitto(mouseInteraction)) @@ -1981,14 +1898,13 @@ namespace AzToolsFramework { ScopedUndoBatch undoBatch(s_dittoManipulatorUndoRedoDesc); - auto manipulatorCommand = AZStd::make_unique( - CreateManipulatorCommandStateFromSelf(), s_manipulatorUndoRedoName); + auto manipulatorCommand = + AZStd::make_unique(CreateManipulatorCommandStateFromSelf(), s_manipulatorUndoRedoName); if (entityIdUnderCursor.IsValid()) { AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity(); - AZ::TransformBus::EventResult( - worldFromLocal, entityIdUnderCursor, &AZ::TransformBus::Events::GetWorldTM); + AZ::TransformBus::EventResult(worldFromLocal, entityIdUnderCursor, &AZ::TransformBus::Events::GetWorldTM); // set orientation/translation to match picked entity switch (m_mode) @@ -2029,13 +1945,9 @@ namespace AzToolsFramework DelegateClearManipulatorOverride(); } - manipulatorCommand->SetManipulatorAfter( - EntityManipulatorCommand::State( - BuildPivotOverride( - m_pivotOverrideFrame.HasTranslationOverride(), - m_pivotOverrideFrame.HasOrientationOverride()), - m_entityIdManipulators.m_manipulators->GetLocalTransform(), - entityIdUnderCursor)); + manipulatorCommand->SetManipulatorAfter(EntityManipulatorCommand::State( + BuildPivotOverride(m_pivotOverrideFrame.HasTranslationOverride(), m_pivotOverrideFrame.HasOrientationOverride()), + m_entityIdManipulators.m_manipulators->GetLocalTransform(), entityIdUnderCursor)); manipulatorCommand->SetParent(undoBatch.GetUndoBatch()); manipulatorCommand.release(); @@ -2073,9 +1985,7 @@ namespace AzToolsFramework QObject::connect(actions.back().get(), &QAction::triggered, actions.back().get(), callback); - EditorActionRequestBus::Broadcast( - &EditorActionRequests::AddActionViaBus, - actionId, actions.back().get()); + EditorActionRequestBus::Broadcast(&EditorActionRequests::AddActionViaBus, actionId, actions.back().get()); } void EditorTransformComponentSelection::OnEscape() @@ -2090,18 +2000,17 @@ namespace AzToolsFramework AZ::ComponentApplicationBus::Broadcast( &AZ::ComponentApplicationRequests::EnumerateEntities, [&func](const AZ::Entity* entity) - { - const AZ::EntityId entityId = entity->GetId(); - - bool editorEntity = false; - EditorEntityContextRequestBus::BroadcastResult( - editorEntity, &EditorEntityContextRequests::IsEditorEntity, entityId); - - if (editorEntity) { - func(entityId); - } - }); + const AZ::EntityId entityId = entity->GetId(); + + bool editorEntity = false; + EditorEntityContextRequestBus::BroadcastResult(editorEntity, &EditorEntityContextRequests::IsEditorEntity, entityId); + + if (editorEntity) + { + func(entityId); + } + }); } void EditorTransformComponentSelection::DelegateClearManipulatorOverride() @@ -2149,22 +2058,22 @@ namespace AzToolsFramework }; // lock selection - AddAction(m_actions, { QKeySequence(Qt::Key_L) }, - /*ID_EDIT_FREEZE =*/ 32900, - s_lockSelectionTitle, s_lockSelectionDesc, + AddAction( + m_actions, { QKeySequence(Qt::Key_L) }, + /*ID_EDIT_FREEZE =*/32900, s_lockSelectionTitle, s_lockSelectionDesc, [lockUnlock]() - { - lockUnlock(true); - }); + { + lockUnlock(true); + }); // unlock selection - AddAction(m_actions, { QKeySequence(Qt::CTRL + Qt::Key_L) }, - /*ID_EDIT_UNFREEZE =*/ 32973, - s_lockSelectionTitle, s_lockSelectionDesc, + AddAction( + m_actions, { QKeySequence(Qt::CTRL + Qt::Key_L) }, + /*ID_EDIT_UNFREEZE =*/32973, s_lockSelectionTitle, s_lockSelectionDesc, [lockUnlock]() - { - lockUnlock(false); - }); + { + lockUnlock(false); + }); const auto showHide = [this](const bool show) { @@ -2189,145 +2098,148 @@ namespace AzToolsFramework }; // hide selection - AddAction(m_actions, { QKeySequence(Qt::Key_H) }, - /*ID_EDIT_HIDE =*/ 32898, - s_hideSelectionTitle, s_hideSelectionDesc, + AddAction( + m_actions, { QKeySequence(Qt::Key_H) }, + /*ID_EDIT_HIDE =*/32898, s_hideSelectionTitle, s_hideSelectionDesc, [showHide]() - { - showHide(false); - }); + { + showHide(false); + }); // show selection - AddAction(m_actions, { QKeySequence(Qt::CTRL + Qt::Key_H) }, - /*ID_EDIT_UNHIDE =*/ 32974, - s_hideSelectionTitle, s_hideSelectionDesc, + AddAction( + m_actions, { QKeySequence(Qt::CTRL + Qt::Key_H) }, + /*ID_EDIT_UNHIDE =*/32974, s_hideSelectionTitle, s_hideSelectionDesc, [showHide]() - { - showHide(true); - }); + { + showHide(true); + }); // unlock all entities in the level/scene - AddAction(m_actions, { QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_L) }, - /*ID_EDIT_UNFREEZEALL =*/ 32901, - s_unlockAllTitle, s_unlockAllDesc, + AddAction( + m_actions, { QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_L) }, + /*ID_EDIT_UNFREEZEALL =*/32901, s_unlockAllTitle, s_unlockAllDesc, []() - { - AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); - - ScopedUndoBatch undoBatch(s_unlockAllUndoRedoDesc); - - EnumerateEditorEntities([](AZ::EntityId entityId) { - ScopedUndoBatch::MarkEntityDirty(entityId); - SetEntityLockState(entityId, false); + AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); + + ScopedUndoBatch undoBatch(s_unlockAllUndoRedoDesc); + + EnumerateEditorEntities( + [](AZ::EntityId entityId) + { + ScopedUndoBatch::MarkEntityDirty(entityId); + SetEntityLockState(entityId, false); + }); }); - }); // show all entities in the level/scene - AddAction(m_actions, { QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_H) }, - /*ID_EDIT_UNHIDEALL =*/ 32899, - s_showAllTitle, s_showAllDesc, + AddAction( + m_actions, { QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_H) }, + /*ID_EDIT_UNHIDEALL =*/32899, s_showAllTitle, s_showAllDesc, []() - { - AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); - - ScopedUndoBatch undoBatch(s_showAllEntitiesUndoRedoDesc); - - EnumerateEditorEntities([](AZ::EntityId entityId) { - ScopedUndoBatch::MarkEntityDirty(entityId); - SetEntityVisibility(entityId, true); + AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); + + ScopedUndoBatch undoBatch(s_showAllEntitiesUndoRedoDesc); + + EnumerateEditorEntities( + [](AZ::EntityId entityId) + { + ScopedUndoBatch::MarkEntityDirty(entityId); + SetEntityVisibility(entityId, true); + }); }); - }); // select all entities in the level/scene - AddAction(m_actions, { QKeySequence(Qt::CTRL + Qt::Key_A) }, - /*ID_EDIT_SELECTALL =*/ 33376, - s_selectAllTitle, s_selectAllDesc, + AddAction( + m_actions, { QKeySequence(Qt::CTRL + Qt::Key_A) }, + /*ID_EDIT_SELECTALL =*/33376, s_selectAllTitle, s_selectAllDesc, [this]() - { - AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); - - ScopedUndoBatch undoBatch(s_selectAllEntitiesUndoRedoDesc); - - if (m_entityIdManipulators.m_manipulators) { - auto manipulatorCommand = AZStd::make_unique( - CreateManipulatorCommandStateFromSelf(), s_manipulatorUndoRedoName); + AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); - // note, nothing will change that the manipulatorCommand needs to keep track - // for after so no need to call SetManipulatorAfter + ScopedUndoBatch undoBatch(s_selectAllEntitiesUndoRedoDesc); - manipulatorCommand->SetParent(undoBatch.GetUndoBatch()); - manipulatorCommand.release(); - } - - EnumerateEditorEntities([this](AZ::EntityId entityId) - { - if (IsSelectableInViewport(entityId)) + if (m_entityIdManipulators.m_manipulators) { - AddEntityToSelection(entityId); + auto manipulatorCommand = + AZStd::make_unique(CreateManipulatorCommandStateFromSelf(), s_manipulatorUndoRedoName); + + // note, nothing will change that the manipulatorCommand needs to keep track + // for after so no need to call SetManipulatorAfter + + manipulatorCommand->SetParent(undoBatch.GetUndoBatch()); + manipulatorCommand.release(); } + + EnumerateEditorEntities( + [this](AZ::EntityId entityId) + { + if (IsSelectableInViewport(entityId)) + { + AddEntityToSelection(entityId); + } + }); + + auto nextEntityIds = EntityIdVectorFromContainer(m_selectedEntityIds); + + auto selectionCommand = AZStd::make_unique(nextEntityIds, s_selectAllEntitiesUndoRedoDesc); + selectionCommand->SetParent(undoBatch.GetUndoBatch()); + selectionCommand.release(); + + SetSelectedEntities(nextEntityIds); + RegenerateManipulators(); }); - auto nextEntityIds = EntityIdVectorFromContainer(m_selectedEntityIds); - - auto selectionCommand = AZStd::make_unique( - nextEntityIds, s_selectAllEntitiesUndoRedoDesc); - selectionCommand->SetParent(undoBatch.GetUndoBatch()); - selectionCommand.release(); - - SetSelectedEntities(nextEntityIds); - RegenerateManipulators(); - }); - // invert current selection - AddAction(m_actions, { QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_I) }, - /*ID_EDIT_INVERTSELECTION =*/ 33692, - s_invertSelectionTitle, s_invertSelectionDesc, + AddAction( + m_actions, { QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_I) }, + /*ID_EDIT_INVERTSELECTION =*/33692, s_invertSelectionTitle, s_invertSelectionDesc, [this]() - { - AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); - - ScopedUndoBatch undoBatch(s_invertSelectionUndoRedoDesc); - - if (m_entityIdManipulators.m_manipulators) { - auto manipulatorCommand = AZStd::make_unique( - CreateManipulatorCommandStateFromSelf(), s_manipulatorUndoRedoName); + AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); - // note, nothing will change that the manipulatorCommand needs to keep track - // for after so no need to call SetManipulatorAfter + ScopedUndoBatch undoBatch(s_invertSelectionUndoRedoDesc); - manipulatorCommand->SetParent(undoBatch.GetUndoBatch()); - manipulatorCommand.release(); - } - - EntityIdSet entityIds; - EnumerateEditorEntities([this, &entityIds](AZ::EntityId entityId) - { - const auto entityIdIt = AZStd::find(m_selectedEntityIds.begin(), m_selectedEntityIds.end(), entityId); - if (entityIdIt == m_selectedEntityIds.end()) + if (m_entityIdManipulators.m_manipulators) { - if (IsSelectableInViewport(entityId)) - { - entityIds.insert(entityId); - } + auto manipulatorCommand = + AZStd::make_unique(CreateManipulatorCommandStateFromSelf(), s_manipulatorUndoRedoName); + + // note, nothing will change that the manipulatorCommand needs to keep track + // for after so no need to call SetManipulatorAfter + + manipulatorCommand->SetParent(undoBatch.GetUndoBatch()); + manipulatorCommand.release(); } + + EntityIdSet entityIds; + EnumerateEditorEntities( + [this, &entityIds](AZ::EntityId entityId) + { + const auto entityIdIt = AZStd::find(m_selectedEntityIds.begin(), m_selectedEntityIds.end(), entityId); + if (entityIdIt == m_selectedEntityIds.end()) + { + if (IsSelectableInViewport(entityId)) + { + entityIds.insert(entityId); + } + } + }); + + m_selectedEntityIds = entityIds; + + auto nextEntityIds = EntityIdVectorFromContainer(entityIds); + + auto selectionCommand = AZStd::make_unique(nextEntityIds, s_invertSelectionUndoRedoDesc); + selectionCommand->SetParent(undoBatch.GetUndoBatch()); + selectionCommand.release(); + + SetSelectedEntities(nextEntityIds); + RegenerateManipulators(); }); - m_selectedEntityIds = entityIds; - - auto nextEntityIds = EntityIdVectorFromContainer(entityIds); - - auto selectionCommand = AZStd::make_unique(nextEntityIds, s_invertSelectionUndoRedoDesc); - selectionCommand->SetParent(undoBatch.GetUndoBatch()); - selectionCommand.release(); - - SetSelectedEntities(nextEntityIds); - RegenerateManipulators(); - }); - bool isPrefabSystemEnabled = false; AzFramework::ApplicationRequests::Bus::BroadcastResult( isPrefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled); @@ -2340,8 +2252,10 @@ namespace AzToolsFramework { // duplicate selection AddAction( - m_actions, {QKeySequence(Qt::CTRL + Qt::Key_D)}, - /*ID_EDIT_CLONE =*/33525, s_duplicateTitle, s_duplicateDesc, []() { + m_actions, { QKeySequence(Qt::CTRL + Qt::Key_D) }, + /*ID_EDIT_CLONE =*/33525, s_duplicateTitle, s_duplicateDesc, + []() + { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); // Clear Widget selection - Prevents issues caused by cloning entities while a property in the Reflected Property Editor @@ -2366,121 +2280,113 @@ namespace AzToolsFramework // delete selection AddAction( m_actions, { QKeySequence(Qt::Key_Delete) }, - /*ID_EDIT_DELETE=*/ 33480, - s_deleteTitle, s_deleteDesc, + /*ID_EDIT_DELETE=*/33480, s_deleteTitle, s_deleteDesc, [this]() - { - AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); + { + AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); - ScopedUndoBatch undoBatch(s_deleteUndoRedoDesc); + ScopedUndoBatch undoBatch(s_deleteUndoRedoDesc); - CreateEntityManipulatorDeselectCommand(undoBatch); + CreateEntityManipulatorDeselectCommand(undoBatch); - ToolsApplicationRequestBus::Broadcast( - &ToolsApplicationRequests::DeleteEntitiesAndAllDescendants, - EntityIdVectorFromContainer(m_selectedEntityIds)); + ToolsApplicationRequestBus::Broadcast( + &ToolsApplicationRequests::DeleteEntitiesAndAllDescendants, EntityIdVectorFromContainer(m_selectedEntityIds)); - m_selectedEntityIds.clear(); - m_pivotOverrideFrame.Reset(); - }); + m_selectedEntityIds.clear(); + m_pivotOverrideFrame.Reset(); + }); AddAction( m_actions, { QKeySequence(Qt::Key_Space) }, - /*ID_EDIT_ESCAPE=*/ 33513, - "", "", + /*ID_EDIT_ESCAPE=*/33513, "", "", [this]() - { - DeselectEntities(); - }); + { + DeselectEntities(); + }); AddAction( m_actions, { QKeySequence(Qt::Key_P) }, - /*ID_EDIT_PIVOT=*/ 36203, - s_togglePivotTitleEditMenu, s_togglePivotDesc, + /*ID_EDIT_PIVOT=*/36203, s_togglePivotTitleEditMenu, s_togglePivotDesc, [this]() - { - ToggleCenterPivotSelection(); - }); + { + ToggleCenterPivotSelection(); + }); AddAction( m_actions, { QKeySequence(Qt::Key_R) }, - /*ID_EDIT_RESET=*/ 36204, - s_resetEntityTransformTitle, s_resetEntityTransformDesc, + /*ID_EDIT_RESET=*/36204, s_resetEntityTransformTitle, s_resetEntityTransformDesc, [this]() - { - switch (m_mode) { - case Mode::Rotation: - ResetOrientationForSelectedEntitiesLocal(); - break; - case Mode::Scale: - CopyScaleToSelectedEntitiesIndividualLocal(1.0f); - break; - case Mode::Translation: - ResetTranslationForSelectedEntitiesLocal(); - break; - } - }); + switch (m_mode) + { + case Mode::Rotation: + ResetOrientationForSelectedEntitiesLocal(); + break; + case Mode::Scale: + CopyScaleToSelectedEntitiesIndividualLocal(1.0f); + break; + case Mode::Translation: + ResetTranslationForSelectedEntitiesLocal(); + break; + } + }); AddAction( m_actions, { QKeySequence(Qt::CTRL + Qt::Key_R) }, - /*ID_EDIT_RESET_MANIPULATOR=*/ 36207, - s_resetManipulatorTitle, s_resetManipulatorDesc, + /*ID_EDIT_RESET_MANIPULATOR=*/36207, s_resetManipulatorTitle, s_resetManipulatorDesc, AZStd::bind(AZStd::mem_fn(&EditorTransformComponentSelection::DelegateClearManipulatorOverride), this)); AddAction( m_actions, { QKeySequence(Qt::ALT + Qt::Key_R) }, - /*ID_EDIT_RESET_LOCAL=*/ 36205, - s_resetTransformLocalTitle, s_resetTransformLocalDesc, + /*ID_EDIT_RESET_LOCAL=*/36205, s_resetTransformLocalTitle, s_resetTransformLocalDesc, [this]() - { - switch (m_mode) { - case Mode::Rotation: - ResetOrientationForSelectedEntitiesLocal(); - break; - case Mode::Scale: - CopyScaleToSelectedEntitiesIndividualWorld(1.0f); - break; - case Mode::Translation: - // do nothing - break; - } - }); + switch (m_mode) + { + case Mode::Rotation: + ResetOrientationForSelectedEntitiesLocal(); + break; + case Mode::Scale: + CopyScaleToSelectedEntitiesIndividualWorld(1.0f); + break; + case Mode::Translation: + // do nothing + break; + } + }); AddAction( m_actions, { QKeySequence(Qt::SHIFT + Qt::Key_R) }, - /*ID_EDIT_RESET_WORLD=*/ 36206, - s_resetTransformWorldTitle, s_resetTransformWorldDesc, + /*ID_EDIT_RESET_WORLD=*/36206, s_resetTransformWorldTitle, s_resetTransformWorldDesc, [this]() - { - switch (m_mode) { - case Mode::Rotation: + switch (m_mode) { - // begin an undo batch so operations inside CopyOrientation... and - // DelegateClear... are grouped into a single undo/redo - ScopedUndoBatch undoBatch { s_resetTransformWorldTitle }; - CopyOrientationToSelectedEntitiesIndividual(AZ::Quaternion::CreateIdentity()); - ClearManipulatorOrientationOverride(); + case Mode::Rotation: + { + // begin an undo batch so operations inside CopyOrientation... and + // DelegateClear... are grouped into a single undo/redo + ScopedUndoBatch undoBatch{ s_resetTransformWorldTitle }; + CopyOrientationToSelectedEntitiesIndividual(AZ::Quaternion::CreateIdentity()); + ClearManipulatorOrientationOverride(); + } + break; + case Mode::Scale: + case Mode::Translation: + break; } - break; - case Mode::Scale: - case Mode::Translation: - break; - } - }); - + }); + AddAction( m_actions, { QKeySequence(Qt::Key_U) }, /*ID_VIEWPORTUI_VISIBLE=*/50040, "Toggle ViewportUI", "Hide/Unhide Viewport UI", - [this]() - { + [this]() + { SetViewportUiClusterVisible(m_transformModeClusterId, !m_viewportUiVisible); SetViewportUiClusterVisible(m_spaceCluster.m_spaceClusterId, !m_viewportUiVisible); m_viewportUiVisible = !m_viewportUiVisible; - }); - + }); + EditorMenuRequestBus::Broadcast(&EditorMenuRequests::RestoreEditMenuToDefault); } @@ -2488,8 +2394,7 @@ namespace AzToolsFramework { for (auto& action : m_actions) { - EditorActionRequestBus::Broadcast( - &EditorActionRequests::RemoveActionViaBus, action.get()); + EditorActionRequestBus::Broadcast(&EditorActionRequests::RemoveActionViaBus, action.get()); } m_actions.clear(); @@ -2558,42 +2463,37 @@ namespace AzToolsFramework { // create the cluster for changing transform mode ViewportUi::ViewportUiRequestBus::EventResult( - m_transformModeClusterId, ViewportUi::DefaultViewportId, - &ViewportUi::ViewportUiRequestBus::Events::CreateCluster, ViewportUi::Alignment::TopLeft); + m_transformModeClusterId, ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateCluster, + ViewportUi::Alignment::TopLeft); // create and register the buttons (strings correspond to icons even if the values appear different) m_translateButtonId = RegisterClusterButton(m_transformModeClusterId, "Move"); m_rotateButtonId = RegisterClusterButton(m_transformModeClusterId, "Translate"); m_scaleButtonId = RegisterClusterButton(m_transformModeClusterId, "Scale"); - auto onButtonClicked = - [this](ViewportUi::ButtonId buttonId) + auto onButtonClicked = [this](ViewportUi::ButtonId buttonId) + { + if (buttonId == m_translateButtonId) { - if (buttonId == m_translateButtonId) - { - SetTransformMode(Mode::Translation); - } - else if (buttonId == m_rotateButtonId) - { - SetTransformMode(Mode::Rotation); - } - else if (buttonId == m_scaleButtonId) - { - SetTransformMode(Mode::Scale); - } - }; + SetTransformMode(Mode::Translation); + } + else if (buttonId == m_rotateButtonId) + { + SetTransformMode(Mode::Rotation); + } + else if (buttonId == m_scaleButtonId) + { + SetTransformMode(Mode::Scale); + } + }; m_transformModeSelectionHandler = AZ::Event::Handler(onButtonClicked); ViewportUi::ViewportUiRequestBus::Event( - ViewportUi::DefaultViewportId, - &ViewportUi::ViewportUiRequestBus::Events::SetClusterActiveButton, - m_transformModeClusterId, + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterActiveButton, m_transformModeClusterId, m_translateButtonId); ViewportUi::ViewportUiRequestBus::Event( - ViewportUi::DefaultViewportId, - &ViewportUi::ViewportUiRequestBus::Events::RegisterClusterEventHandler, - m_transformModeClusterId, + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::RegisterClusterEventHandler, m_transformModeClusterId, m_transformModeSelectionHandler); } @@ -2609,7 +2509,8 @@ namespace AzToolsFramework m_spaceCluster.m_parentButtonId = RegisterClusterButton(m_spaceCluster.m_spaceClusterId, "Parent"); m_spaceCluster.m_localButtonId = RegisterClusterButton(m_spaceCluster.m_spaceClusterId, "Local"); - auto onButtonClicked = [this](ViewportUi::ButtonId buttonId) { + auto onButtonClicked = [this](ViewportUi::ButtonId buttonId) + { if (buttonId == m_spaceCluster.m_localButtonId) { // Unlock @@ -2646,6 +2547,9 @@ namespace AzToolsFramework m_spaceCluster.m_spaceLock = ReferenceFrame::World; } } + ViewportUi::ViewportUiRequestBus::Event( + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonLocked, + m_spaceCluster.m_spaceClusterId, buttonId, m_spaceCluster.m_spaceLock.has_value()); }; m_spaceCluster.m_spaceSelectionHandler = AZ::Event::Handler(onButtonClicked); @@ -2671,14 +2575,13 @@ 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 = + QuaternionFromTransformNoScaling(m_entityIdManipulators.m_manipulators->GetLocalTransform()); } if (m_pivotOverrideFrame.m_translationOverride && m_entityIdManipulators.m_manipulators) { - m_pivotOverrideFrame.m_translationOverride = - m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); + m_pivotOverrideFrame.m_translationOverride = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); } m_mode = mode; @@ -2755,8 +2658,7 @@ namespace AzToolsFramework // we are responsible for updating the current selection m_didSetSelectedEntities = true; - ToolsApplicationRequestBus::Broadcast( - &ToolsApplicationRequests::SetSelectedEntities, entityIds); + ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetSelectedEntities, entityIds); } void EditorTransformComponentSelection::RefreshManipulators(const RefreshType refreshType) @@ -2776,15 +2678,13 @@ namespace AzToolsFramework break; case RefreshType::Orientation: transform = AZ::Transform::CreateFromQuaternionAndTranslation( - RecalculateAverageManipulatorOrientation( - m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, m_referenceFrame), + RecalculateAverageManipulatorOrientation(m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, m_referenceFrame), m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation()); break; case RefreshType::Translation: transform = AZ::Transform::CreateFromQuaternionAndTranslation( m_entityIdManipulators.m_manipulators->GetLocalTransform().GetRotation(), - RecalculateAverageManipulatorTranslation( - m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, m_pivotMode)); + RecalculateAverageManipulatorTranslation(m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, m_pivotMode)); break; } @@ -2807,9 +2707,8 @@ namespace AzToolsFramework if (m_entityIdManipulators.m_manipulators) { - m_entityIdManipulators.m_manipulators->SetLocalTransform( - AZ::Transform::CreateFromQuaternionAndTranslation( - orientation, m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation())); + m_entityIdManipulators.m_manipulators->SetLocalTransform(AZ::Transform::CreateFromQuaternionAndTranslation( + orientation, m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation())); m_entityIdManipulators.m_manipulators->SetBoundsDirty(); } @@ -2836,15 +2735,14 @@ namespace AzToolsFramework { ScopedUndoBatch undoBatch(s_resetManipulatorTranslationUndoRedoDesc); - auto manipulatorCommand = AZStd::make_unique( - CreateManipulatorCommandStateFromSelf(), s_manipulatorUndoRedoName); + auto manipulatorCommand = + AZStd::make_unique(CreateManipulatorCommandStateFromSelf(), s_manipulatorUndoRedoName); m_pivotOverrideFrame.ResetPickedTranslation(); m_pivotOverrideFrame.m_pickedEntityIdOverride.SetInvalid(); - m_entityIdManipulators.m_manipulators->SetLocalTransform( - RecalculateAverageManipulatorTransform( - m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, m_pivotMode, m_referenceFrame)); + m_entityIdManipulators.m_manipulators->SetLocalTransform(RecalculateAverageManipulatorTransform( + m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, m_pivotMode, m_referenceFrame)); m_entityIdManipulators.m_manipulators->SetBoundsDirty(); @@ -2861,20 +2759,18 @@ namespace AzToolsFramework if (m_entityIdManipulators.m_manipulators) { - ScopedUndoBatch undoBatch { s_resetManipulatorOrientationUndoRedoDesc }; + ScopedUndoBatch undoBatch{ s_resetManipulatorOrientationUndoRedoDesc }; - auto manipulatorCommand = AZStd::make_unique( - CreateManipulatorCommandStateFromSelf(), s_manipulatorUndoRedoName); + auto manipulatorCommand = + AZStd::make_unique(CreateManipulatorCommandStateFromSelf(), s_manipulatorUndoRedoName); m_pivotOverrideFrame.ResetPickedOrientation(); m_pivotOverrideFrame.m_pickedEntityIdOverride.SetInvalid(); // parent reference frame is the default (when no modifiers are held) - m_entityIdManipulators.m_manipulators->SetLocalTransform( - AZ::Transform::CreateFromQuaternionAndTranslation( - ETCS::CalculatePivotOrientationForEntityIds( - m_entityIdManipulators.m_lookups, ReferenceFrame::Parent).m_worldOrientation, - m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation())); + m_entityIdManipulators.m_manipulators->SetLocalTransform(AZ::Transform::CreateFromQuaternionAndTranslation( + ETCS::CalculatePivotOrientationForEntityIds(m_entityIdManipulators.m_lookups, ReferenceFrame::Parent).m_worldOrientation, + m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation())); m_entityIdManipulators.m_manipulators->SetBoundsDirty(); @@ -2897,8 +2793,7 @@ namespace AzToolsFramework { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); - static_assert(AZStd::is_same::value, - "Container key type is not an EntityId"); + static_assert(AZStd::is_same::value, "Container key type is not an EntityId"); AZ::EntityId parentId; AZ::TransformBus::EventResult(parentId, entityId, &AZ::TransformBus::Events::GetParentId); @@ -2932,11 +2827,10 @@ namespace AzToolsFramework ScopedUndoBatch undoBatch(s_dittoTranslationGroupUndoRedoDesc); // store previous translation manipulator position - const AZ::Vector3 previousPivotTranslation = - m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); + const AZ::Vector3 previousPivotTranslation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); - auto manipulatorCommand = AZStd::make_unique( - CreateManipulatorCommandStateFromSelf(), s_manipulatorUndoRedoName); + auto manipulatorCommand = + AZStd::make_unique(CreateManipulatorCommandStateFromSelf(), s_manipulatorUndoRedoName); // refresh the transform pivot override if it's set if (m_pivotOverrideFrame.m_translationOverride) @@ -2944,15 +2838,11 @@ namespace AzToolsFramework OverrideManipulatorTranslation(translation); } - manipulatorCommand->SetManipulatorAfter( - EntityManipulatorCommand::State( - BuildPivotOverride( - m_pivotOverrideFrame.HasTranslationOverride(), - m_pivotOverrideFrame.HasOrientationOverride()), - AZ::Transform::CreateFromQuaternionAndTranslation( - QuaternionFromTransformNoScaling( - m_entityIdManipulators.m_manipulators->GetLocalTransform()), translation), - m_pivotOverrideFrame.m_pickedEntityIdOverride)); + manipulatorCommand->SetManipulatorAfter(EntityManipulatorCommand::State( + BuildPivotOverride(m_pivotOverrideFrame.HasTranslationOverride(), m_pivotOverrideFrame.HasOrientationOverride()), + AZ::Transform::CreateFromQuaternionAndTranslation( + QuaternionFromTransformNoScaling(m_entityIdManipulators.m_manipulators->GetLocalTransform()), translation), + m_pivotOverrideFrame.m_pickedEntityIdOverride)); manipulatorCommand->SetParent(undoBatch.GetUndoBatch()); manipulatorCommand.release(); @@ -2992,8 +2882,8 @@ namespace AzToolsFramework { ScopedUndoBatch undoBatch(s_dittoTranslationIndividualUndoRedoDesc); - auto manipulatorCommand = AZStd::make_unique( - CreateManipulatorCommandStateFromSelf(), s_manipulatorUndoRedoName); + auto manipulatorCommand = + AZStd::make_unique(CreateManipulatorCommandStateFromSelf(), s_manipulatorUndoRedoName); // refresh the transform pivot override if it's set if (m_pivotOverrideFrame.m_translationOverride) @@ -3001,15 +2891,11 @@ namespace AzToolsFramework OverrideManipulatorTranslation(translation); } - manipulatorCommand->SetManipulatorAfter( - EntityManipulatorCommand::State( - BuildPivotOverride( - m_pivotOverrideFrame.HasTranslationOverride(), - m_pivotOverrideFrame.HasOrientationOverride()), - AZ::Transform::CreateFromQuaternionAndTranslation( - QuaternionFromTransformNoScaling( - m_entityIdManipulators.m_manipulators->GetLocalTransform()), translation), - m_pivotOverrideFrame.m_pickedEntityIdOverride)); + manipulatorCommand->SetManipulatorAfter(EntityManipulatorCommand::State( + BuildPivotOverride(m_pivotOverrideFrame.HasTranslationOverride(), m_pivotOverrideFrame.HasOrientationOverride()), + AZ::Transform::CreateFromQuaternionAndTranslation( + QuaternionFromTransformNoScaling(m_entityIdManipulators.m_manipulators->GetLocalTransform()), translation), + m_pivotOverrideFrame.m_pickedEntityIdOverride)); manipulatorCommand->SetParent(undoBatch.GetUndoBatch()); manipulatorCommand.release(); @@ -3081,17 +2967,16 @@ namespace AzToolsFramework RefreshUiAfterChange(manipulatorEntityIds.m_entityIds); } - void EditorTransformComponentSelection::CopyOrientationToSelectedEntitiesIndividual( - const AZ::Quaternion& orientation) + void EditorTransformComponentSelection::CopyOrientationToSelectedEntitiesIndividual(const AZ::Quaternion& orientation) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); if (m_entityIdManipulators.m_manipulators) { - ScopedUndoBatch undoBatch { s_dittoEntityOrientationIndividualUndoRedoDesc }; + ScopedUndoBatch undoBatch{ s_dittoEntityOrientationIndividualUndoRedoDesc }; - auto manipulatorCommand = AZStd::make_unique( - CreateManipulatorCommandStateFromSelf(), s_manipulatorUndoRedoName); + auto manipulatorCommand = + AZStd::make_unique(CreateManipulatorCommandStateFromSelf(), s_manipulatorUndoRedoName); ManipulatorEntityIds manipulatorEntityIds; BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds.m_entityIds); @@ -3127,8 +3012,7 @@ namespace AzToolsFramework } } - void EditorTransformComponentSelection::CopyOrientationToSelectedEntitiesGroup( - const AZ::Quaternion& orientation) + void EditorTransformComponentSelection::CopyOrientationToSelectedEntitiesGroup(const AZ::Quaternion& orientation) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -3136,8 +3020,8 @@ namespace AzToolsFramework { ScopedUndoBatch undoBatch(s_dittoEntityOrientationGroupUndoRedoDesc); - auto manipulatorCommand = AZStd::make_unique( - CreateManipulatorCommandStateFromSelf(), s_manipulatorUndoRedoName); + auto manipulatorCommand = + AZStd::make_unique(CreateManipulatorCommandStateFromSelf(), s_manipulatorUndoRedoName); ManipulatorEntityIds manipulatorEntityIds; BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds.m_entityIds); @@ -3145,8 +3029,7 @@ namespace AzToolsFramework // save initial transforms const auto transformsBefore = RecordTransformsBefore(manipulatorEntityIds.m_entityIds); - const AZ::Transform currentTransform = - m_entityIdManipulators.m_manipulators->GetLocalTransform(); + const AZ::Transform currentTransform = m_entityIdManipulators.m_manipulators->GetLocalTransform(); const AZ::Transform nextTransform = AZ::Transform::CreateFromQuaternionAndTranslation( orientation, m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation()); @@ -3160,8 +3043,7 @@ namespace AzToolsFramework const auto transformIt = transformsBefore.find(entityId); if (transformIt != transformsBefore.end()) { - const AZ::Transform transformInPivotSpace = - currentTransform.GetInverse() * transformIt->second; + const AZ::Transform transformInPivotSpace = currentTransform.GetInverse() * transformIt->second; SetEntityWorldTransform(entityId, nextTransform * transformInPivotSpace); } @@ -3183,7 +3065,7 @@ namespace AzToolsFramework AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); ScopedUndoBatch undoBatch(s_resetOrientationToParentUndoRedoDesc); - for (const auto& entityIdLookup: m_entityIdManipulators.m_lookups) + for (const auto& entityIdLookup : m_entityIdManipulators.m_lookups) { ScopedUndoBatch::MarkEntityDirty(entityIdLookup.first); SetEntityLocalRotation(entityIdLookup.first, AZ::Vector3::CreateZero()); @@ -3206,14 +3088,12 @@ namespace AzToolsFramework ScopedUndoBatch undoBatch(s_resetTranslationToParentUndoRedoDesc); ManipulatorEntityIds manipulatorEntityIds; - BuildSortedEntityIdVectorFromEntityIdMap( - m_entityIdManipulators.m_lookups, manipulatorEntityIds.m_entityIds); + BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds.m_entityIds); for (AZ::EntityId entityId : manipulatorEntityIds.m_entityIds) { AZ::EntityId parentId; - AZ::TransformBus::EventResult( - parentId, entityId, &AZ::TransformBus::Events::GetParentId); + AZ::TransformBus::EventResult(parentId, entityId, &AZ::TransformBus::Events::GetParentId); if (parentId.IsValid()) { @@ -3228,11 +3108,15 @@ namespace AzToolsFramework } } - void EditorTransformComponentSelection::PopulateEditorGlobalContextMenu( - QMenu* menu, const AZ::Vector2& /*point*/, const int /*flags*/) + void EditorTransformComponentSelection::PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& /*point*/, const int /*flags*/) { QAction* action = menu->addAction(QObject::tr(s_togglePivotTitleRightClick)); - QObject::connect(action, &QAction::triggered, action, [this]() { ToggleCenterPivotSelection(); }); + QObject::connect( + action, &QAction::triggered, action, + [this]() + { + ToggleCenterPivotSelection(); + }); } void EditorTransformComponentSelection::BeforeEntitySelectionChanged() @@ -3278,8 +3162,10 @@ namespace AzToolsFramework } static void DrawPreviewAxis( - AzFramework::DebugDisplayRequests& display, const AZ::Transform& transform, - const float axisLength, const AzFramework::CameraState& cameraState) + AzFramework::DebugDisplayRequests& display, + const AZ::Transform& transform, + const float axisLength, + const AzFramework::CameraState& cameraState) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -3294,8 +3180,8 @@ namespace AzToolsFramework const auto axisFlip = [&transform, &cameraState](const AZ::Vector3& axis) -> float { return ShouldFlipCameraAxis( - AZ::Transform::CreateIdentity(), transform.GetTranslation(), - TransformDirectionNoScaling(transform, axis), cameraState) + AZ::Transform::CreateIdentity(), transform.GetTranslation(), TransformDirectionNoScaling(transform, axis), + cameraState) ? -1.0f : 1.0f; }; @@ -3303,18 +3189,15 @@ namespace AzToolsFramework display.SetColor(s_fadedXAxisColor); display.DrawLine( transform.GetTranslation(), - transform.GetTranslation() + transform.GetBasisX().GetNormalizedSafe() * - axisLength * axisFlip(AZ::Vector3::CreateAxisX())); + transform.GetTranslation() + transform.GetBasisX().GetNormalizedSafe() * axisLength * axisFlip(AZ::Vector3::CreateAxisX())); display.SetColor(s_fadedYAxisColor); display.DrawLine( transform.GetTranslation(), - transform.GetTranslation() + transform.GetBasisY().GetNormalizedSafe() * - axisLength * axisFlip(AZ::Vector3::CreateAxisY())); + transform.GetTranslation() + transform.GetBasisY().GetNormalizedSafe() * axisLength * axisFlip(AZ::Vector3::CreateAxisY())); display.SetColor(s_fadedZAxisColor); display.DrawLine( transform.GetTranslation(), - transform.GetTranslation() + transform.GetBasisZ().GetNormalizedSafe() * - axisLength * axisFlip(AZ::Vector3::CreateAxisZ())); + transform.GetTranslation() + transform.GetBasisZ().GetNormalizedSafe() * axisLength * axisFlip(AZ::Vector3::CreateAxisZ())); display.DepthWriteOn(); display.DepthTestOn(); @@ -3327,15 +3210,11 @@ namespace AzToolsFramework static void DrawManipulatorGrid( AzFramework::DebugDisplayRequests& debugDisplay, const EntityIdManipulators& entityIdManipulators, const float gridSize) { - const AZ::Matrix3x3 orientation = - AZ::Matrix3x3::CreateFromTransform(entityIdManipulators.m_manipulators->GetLocalTransform()); + const AZ::Matrix3x3 orientation = AZ::Matrix3x3::CreateFromTransform(entityIdManipulators.m_manipulators->GetLocalTransform()); - const AZ::Vector3 translation = - entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); + const AZ::Vector3 translation = entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); - DrawSnappingGrid( - debugDisplay, AZ::Transform::CreateFromMatrix3x3AndTranslation(orientation, translation), - gridSize); + DrawSnappingGrid(debugDisplay, AZ::Transform::CreateFromMatrix3x3AndTranslation(orientation, translation), gridSize); } void EditorTransformComponentSelection::DisplayViewportSelection( @@ -3345,16 +3224,14 @@ namespace AzToolsFramework CheckDirtyEntityIds(); - const auto modifiers = ViewportInteraction::KeyboardModifiers( - ViewportInteraction::TranslateKeyboardModifiers(QApplication::queryKeyboardModifiers())); + const auto modifiers = + ViewportInteraction::KeyboardModifiers(ViewportInteraction::TranslateKeyboardModifiers(QApplication::queryKeyboardModifiers())); m_cursorState.Update(); HandleAccents( - !m_selectedEntityIds.empty(), m_cachedEntityIdUnderCursor, - modifiers.Ctrl(), m_hoveredEntityId, - ViewportInteraction::BuildMouseButtons( - QGuiApplication::mouseButtons()), m_boxSelect.Active()); + !m_selectedEntityIds.empty(), m_cachedEntityIdUnderCursor, modifiers.Ctrl(), m_hoveredEntityId, + ViewportInteraction::BuildMouseButtons(QGuiApplication::mouseButtons()), m_boxSelect.Active()); const ReferenceFrame referenceFrame = m_spaceCluster.m_spaceLock.value_or(ReferenceFrameFromModifiers(modifiers)); @@ -3367,10 +3244,8 @@ namespace AzToolsFramework refresh = true; } - refresh = refresh - || (m_triedToRefresh - && m_entityIdManipulators.m_manipulators - && !m_entityIdManipulators.m_manipulators->PerformingAction()); + refresh = refresh || + (m_triedToRefresh && m_entityIdManipulators.m_manipulators && !m_entityIdManipulators.m_manipulators->PerformingAction()); // we've moved from parent to world space, parent to local space or vice versa by holding or // releasing shift and/or alt - make sure we update the manipulator orientation appropriately @@ -3383,8 +3258,7 @@ namespace AzToolsFramework const auto entityFilter = [this](AZ::EntityId entityId) { - const bool entityHasManipulator = - m_entityIdManipulators.m_lookups.find(entityId) != m_entityIdManipulators.m_lookups.end(); + const bool entityHasManipulator = m_entityIdManipulators.m_lookups.find(entityId) != m_entityIdManipulators.m_lookups.end(); return !entityHasManipulator; }; @@ -3395,15 +3269,12 @@ namespace AzToolsFramework { if (m_pivotOverrideFrame.m_pickedEntityIdOverride.IsValid()) { - const AZ::Transform pickedEntityWorldTransform = - AZ::Transform::CreateFromQuaternionAndTranslation( - ETCS::CalculatePivotOrientation( - m_pivotOverrideFrame.m_pickedEntityIdOverride, referenceFrame).m_worldOrientation, - CalculatePivotTranslation( - m_pivotOverrideFrame.m_pickedEntityIdOverride, m_pivotMode)); + const AZ::Transform pickedEntityWorldTransform = AZ::Transform::CreateFromQuaternionAndTranslation( + ETCS::CalculatePivotOrientation(m_pivotOverrideFrame.m_pickedEntityIdOverride, referenceFrame).m_worldOrientation, + CalculatePivotTranslation(m_pivotOverrideFrame.m_pickedEntityIdOverride, m_pivotMode)); - const float scaledSize = s_pivotSize * - CalculateScreenToWorldMultiplier(pickedEntityWorldTransform.GetTranslation(), cameraState); + const float scaledSize = + s_pivotSize * CalculateScreenToWorldMultiplier(pickedEntityWorldTransform.GetTranslation(), cameraState); debugDisplay.DepthWriteOff(); debugDisplay.DepthTestOff(); @@ -3418,8 +3289,8 @@ namespace AzToolsFramework // check what pivot orientation we are in (based on if a modifier is // held to move us from parent to world space or parent to local space) // or if we set a pivot override - const auto pivotResult = ETCS::CalculateSelectionPivotOrientation( - m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, m_referenceFrame); + const auto pivotResult = + ETCS::CalculateSelectionPivotOrientation(m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, m_referenceFrame); // if the reference frame was parent space and the selection does have a // valid parent, draw a preview axis at its position/orientation @@ -3429,8 +3300,7 @@ namespace AzToolsFramework { const AZ::Transform& worldFromLocal = m_entityDataCache->GetVisibleEntityTransform(*parentEntityIndex); - const float adjustedLineLength = - CalculateScreenToWorldMultiplier(worldFromLocal.GetTranslation(), cameraState); + const float adjustedLineLength = CalculateScreenToWorldMultiplier(worldFromLocal.GetTranslation(), cameraState); DrawPreviewAxis(debugDisplay, worldFromLocal, adjustedLineLength, cameraState); } @@ -3449,10 +3319,11 @@ namespace AzToolsFramework const AZ::Vector3 boxPosition = worldFromLocal.TransformPoint(CalculateCenterOffset(entityId, m_pivotMode)); - const AZ::Vector3 scaledSize = AZ::Vector3(s_pivotSize) * - CalculateScreenToWorldMultiplier(worldFromLocal.GetTranslation(), cameraState); + const AZ::Vector3 scaledSize = + AZ::Vector3(s_pivotSize) * CalculateScreenToWorldMultiplier(worldFromLocal.GetTranslation(), cameraState); - const AZ::Color hiddenNormal[] = { AzFramework::ViewportColors::SelectedColor, AzFramework::ViewportColors::HiddenColor }; + const AZ::Color hiddenNormal[] = { AzFramework::ViewportColors::SelectedColor, + AzFramework::ViewportColors::HiddenColor }; AZ::Color boxColor = hiddenNormal[hidden]; const AZ::Color lockedOther[] = { boxColor, AzFramework::ViewportColors::LockColor }; boxColor = lockedOther[locked]; @@ -3467,8 +3338,7 @@ namespace AzToolsFramework debugDisplay.DepthWriteOn(); debugDisplay.DepthTestOn(); - if (ShowingGrid(viewportInfo.m_viewportId) && m_mode == Mode::Translation && - !ComponentModeFramework::InComponentMode()) + if (ShowingGrid(viewportInfo.m_viewportId) && m_mode == Mode::Translation && !ComponentModeFramework::InComponentMode()) { const GridSnapParameters gridSnapParams = GridSnapSettings(viewportInfo.m_viewportId); if (gridSnapParams.m_gridSnap && m_entityIdManipulators.m_manipulators) @@ -3488,11 +3358,11 @@ namespace AzToolsFramework if (m_entityIdManipulators.m_manipulators->PerformingAction()) { - const float adjustedLineLength = 2.0f * - CalculateScreenToWorldMultiplier(m_axisPreview.m_translation, cameraState); + const float adjustedLineLength = 2.0f * CalculateScreenToWorldMultiplier(m_axisPreview.m_translation, cameraState); - DrawPreviewAxis(debugDisplay, AZ::Transform::CreateFromQuaternionAndTranslation( - m_axisPreview.m_orientation, m_axisPreview.m_translation), + DrawPreviewAxis( + debugDisplay, + AZ::Transform::CreateFromQuaternionAndTranslation(m_axisPreview.m_orientation, m_axisPreview.m_translation), adjustedLineLength, cameraState); } } @@ -3500,20 +3370,17 @@ namespace AzToolsFramework m_boxSelect.DisplayScene(viewportInfo, debugDisplay); } - static void DrawAxisGizmo( - const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) + static void DrawAxisGizmo(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) { // get the editor cameras current orientation const int viewportId = viewportInfo.m_viewportId; const AzFramework::CameraState editorCameraState = GetCameraState(viewportId); - const AZ::Matrix3x3& editorCameraOrientation = - AZ::Matrix3x3::CreateFromMatrix4x4(AzFramework::CameraTransform(editorCameraState)); + const AZ::Matrix3x3& editorCameraOrientation = AZ::Matrix3x3::CreateFromMatrix4x4(AzFramework::CameraTransform(editorCameraState)); // create a gizmo camera transform about the origin matching the orientation of the editor camera // (10 units back in the y axis to produce an orbit effect) const AZ::Transform gizmoCameraOffset = AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)); - const AZ::Transform gizmoCameraTransform = - AZ::Transform::CreateFromMatrix3x3(editorCameraOrientation) * gizmoCameraOffset; + const AZ::Transform gizmoCameraTransform = AZ::Transform::CreateFromMatrix3x3(editorCameraOrientation) * gizmoCameraOffset; const AzFramework::CameraState gizmoCameraState = AzFramework::CreateDefaultCamera(gizmoCameraTransform, editorCameraState.m_viewportSize); @@ -3526,16 +3393,9 @@ namespace AzToolsFramework // map from a position in world space (relative to the the gizmo camera near the origin) to a position in // screen space - const auto calculateGizmoAxis = - [&cameraView, &cameraProjection, &screenOffset] - (const AZ::Vector3& axis) + const auto calculateGizmoAxis = [&cameraView, &cameraProjection, &screenOffset](const AZ::Vector3& axis) { - auto result = AZ::Vector2( - AzFramework::WorldToScreenNDC( - axis, - cameraView, - cameraProjection) - ); + auto result = AZ::Vector2(AzFramework::WorldToScreenNDC(axis, cameraView, cameraProjection)); result.SetY(1.0f - result.GetY()); return result + screenOffset; }; @@ -3549,7 +3409,7 @@ namespace AzToolsFramework const AZ::Vector2 gizmoAxisX = gizmoEndAxisX - gizmoStart; const AZ::Vector2 gizmoAxisY = gizmoEndAxisY - gizmoStart; - const AZ::Vector2 gizmoAxisZ = gizmoEndAxisZ - gizmoStart; + const AZ::Vector2 gizmoAxisZ = gizmoEndAxisZ - gizmoStart; // draw the axes of the gizmo debugDisplay.SetLineWidth(cl_viewportGizmoAxisLineWidth); @@ -3576,8 +3436,7 @@ namespace AzToolsFramework } void EditorTransformComponentSelection::DisplayViewportSelection2d( - const AzFramework::ViewportInfo& viewportInfo, - AzFramework::DebugDisplayRequests& debugDisplay) + const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -3592,8 +3451,7 @@ namespace AzToolsFramework // check what the 'authoritative' selected entity ids are after an undo/redo EntityIdList selectedEntityIds; - ToolsApplicationRequests::Bus::BroadcastResult( - selectedEntityIds, &ToolsApplicationRequests::GetSelectedEntities); + ToolsApplicationRequests::Bus::BroadcastResult(selectedEntityIds, &ToolsApplicationRequests::GetSelectedEntities); RefreshSelectedEntityIds(selectedEntityIds); } @@ -3611,9 +3469,7 @@ namespace AzToolsFramework // update selected entityId set m_selectedEntityIds.clear(); m_selectedEntityIds.reserve(selectedEntityIds.size()); - AZStd::copy( - selectedEntityIds.begin(), selectedEntityIds.end(), - AZStd::inserter(m_selectedEntityIds, m_selectedEntityIds.end())); + AZStd::copy(selectedEntityIds.begin(), selectedEntityIds.end(), AZStd::inserter(m_selectedEntityIds, m_selectedEntityIds.end())); } void EditorTransformComponentSelection::OnTransformChanged( @@ -3687,8 +3543,7 @@ namespace AzToolsFramework m_selectedEntityIdsAndManipulatorsDirty = true; } - void EditorTransformComponentSelection::EnteredComponentMode( - const AZStd::vector& /*componentModeTypes*/) + void EditorTransformComponentSelection::EnteredComponentMode(const AZStd::vector& /*componentModeTypes*/) { SetViewportUiClusterVisible(m_transformModeClusterId, false); @@ -3697,8 +3552,7 @@ namespace AzToolsFramework ToolsApplicationNotificationBus::Handler::BusDisconnect(); } - void EditorTransformComponentSelection::LeftComponentMode( - const AZStd::vector& /*componentModeTypes*/) + void EditorTransformComponentSelection::LeftComponentMode(const AZStd::vector& /*componentModeTypes*/) { SetViewportUiClusterVisible(m_transformModeClusterId, true); @@ -3711,8 +3565,8 @@ namespace AzToolsFramework { if (m_entityIdManipulators.m_manipulators) { - auto manipulatorCommand = AZStd::make_unique( - CreateManipulatorCommandStateFromSelf(), s_manipulatorUndoRedoName); + auto manipulatorCommand = + AZStd::make_unique(CreateManipulatorCommandStateFromSelf(), s_manipulatorUndoRedoName); manipulatorCommand->SetManipulatorAfter(EntityManipulatorCommand::State()); @@ -3731,32 +3585,27 @@ namespace AzToolsFramework return {}; } - void EditorTransformComponentSelection::SetEntityWorldTranslation( - const AZ::EntityId entityId, const AZ::Vector3& worldTranslation) + void EditorTransformComponentSelection::SetEntityWorldTranslation(const AZ::EntityId entityId, const AZ::Vector3& worldTranslation) { ETCS::SetEntityWorldTranslation(entityId, worldTranslation, m_transformChangedInternally); } - void EditorTransformComponentSelection::SetEntityLocalTranslation( - const AZ::EntityId entityId, const AZ::Vector3& localTranslation) + void EditorTransformComponentSelection::SetEntityLocalTranslation(const AZ::EntityId entityId, const AZ::Vector3& localTranslation) { ETCS::SetEntityLocalTranslation(entityId, localTranslation, m_transformChangedInternally); } - void EditorTransformComponentSelection::SetEntityWorldTransform( - const AZ::EntityId entityId, const AZ::Transform& worldTransform) + void EditorTransformComponentSelection::SetEntityWorldTransform(const AZ::EntityId entityId, const AZ::Transform& worldTransform) { ETCS::SetEntityWorldTransform(entityId, worldTransform, m_transformChangedInternally); } - void EditorTransformComponentSelection::SetEntityLocalScale( - const AZ::EntityId entityId, const float localScale) + void EditorTransformComponentSelection::SetEntityLocalScale(const AZ::EntityId entityId, const float localScale) { ETCS::SetEntityLocalScale(entityId, localScale, m_transformChangedInternally); } - void EditorTransformComponentSelection::SetEntityLocalRotation( - const AZ::EntityId entityId, const AZ::Vector3& localRotation) + void EditorTransformComponentSelection::SetEntityLocalRotation(const AZ::EntityId entityId, const AZ::Vector3& localRotation) { ETCS::SetEntityLocalRotation(entityId, localRotation, m_transformChangedInternally); } @@ -3785,44 +3634,37 @@ namespace AzToolsFramework void SetEntityWorldTranslation(AZ::EntityId entityId, const AZ::Vector3& worldTranslation, bool& internal) { ScopeSwitch sw(internal); - AZ::TransformBus::Event( - entityId, &AZ::TransformBus::Events::SetWorldTranslation, worldTranslation); + AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetWorldTranslation, worldTranslation); } void SetEntityLocalTranslation(AZ::EntityId entityId, const AZ::Vector3& localTranslation, bool& internal) { ScopeSwitch sw(internal); - AZ::TransformBus::Event( - entityId, &AZ::TransformBus::Events::SetLocalTranslation, localTranslation); + AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetLocalTranslation, localTranslation); } void SetEntityWorldTransform(AZ::EntityId entityId, const AZ::Transform& worldTransform, bool& internal) { ScopeSwitch sw(internal); - AZ::TransformBus::Event( - entityId, &AZ::TransformBus::Events::SetWorldTM, worldTransform); + AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetWorldTM, worldTransform); } void SetEntityLocalScale(AZ::EntityId entityId, float localScale, bool& internal) { ScopeSwitch sw(internal); - AZ::TransformBus::Event( - entityId, &AZ::TransformBus::Events::SetLocalUniformScale, localScale); + AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetLocalUniformScale, localScale); } void SetEntityLocalRotation(AZ::EntityId entityId, const AZ::Vector3& localRotation, bool& internal) { ScopeSwitch sw(internal); - AZ::TransformBus::Event( - entityId, &AZ::TransformBus::Events::SetLocalRotation, localRotation); + AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetLocalRotation, localRotation); } } // namespace ETCS // explicit instantiations - template ETCS::PivotOrientationResult - ETCS::CalculatePivotOrientationForEntityIds( - const EntityIdManipulatorLookups&, ReferenceFrame); - template ETCS::PivotOrientationResult - ETCS::CalculateSelectionPivotOrientation( - const EntityIdManipulatorLookups&, const OptionalFrame&, const ReferenceFrame referenceFrame); + template ETCS::PivotOrientationResult ETCS::CalculatePivotOrientationForEntityIds( + const EntityIdManipulatorLookups&, ReferenceFrame); + template ETCS::PivotOrientationResult ETCS::CalculateSelectionPivotOrientation( + const EntityIdManipulatorLookups&, const OptionalFrame&, const ReferenceFrame referenceFrame); } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.cpp index 9ca20068ed..8cd19b8d2d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.cpp @@ -1,17 +1,17 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ -#include #include "EditorTransformComponentSelectionRequestBus.h" +#include namespace AzToolsFramework { @@ -19,50 +19,68 @@ namespace AzToolsFramework { if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) { - #define TEMP_SET_ATTRIBUTES_FOR_EditorTransformComponentSelectionRequests() \ - ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) \ - ->Attribute(AZ::Script::Attributes::Category, "Editor") \ - ->Attribute(AZ::Script::Attributes::Module, "editor") +#define TEMP_SET_ATTRIBUTES_FOR_EditorTransformComponentSelectionRequests() \ + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) \ + ->Attribute(AZ::Script::Attributes::Category, "Editor") \ + ->Attribute(AZ::Script::Attributes::Module, "editor") - behaviorContext->EnumProperty(EditorTransformComponentSelectionRequests::Mode::Rotation)>("TransformMode_Rotation") - TEMP_SET_ATTRIBUTES_FOR_EditorTransformComponentSelectionRequests(); - behaviorContext->EnumProperty(EditorTransformComponentSelectionRequests::Mode::Translation)>("TransformMode_Translation") - TEMP_SET_ATTRIBUTES_FOR_EditorTransformComponentSelectionRequests(); + behaviorContext->EnumProperty(EditorTransformComponentSelectionRequests::Mode::Rotation)>( + "TransformMode_Rotation") TEMP_SET_ATTRIBUTES_FOR_EditorTransformComponentSelectionRequests(); + behaviorContext->EnumProperty(EditorTransformComponentSelectionRequests::Mode::Translation)>( + "TransformMode_Translation") TEMP_SET_ATTRIBUTES_FOR_EditorTransformComponentSelectionRequests(); behaviorContext->EnumProperty(EditorTransformComponentSelectionRequests::Mode::Scale)>("TransformMode_Scale") TEMP_SET_ATTRIBUTES_FOR_EditorTransformComponentSelectionRequests(); - behaviorContext->EnumProperty(EditorTransformComponentSelectionRequests::RefreshType::Translation)>("TransformRefreshType_Translation") - TEMP_SET_ATTRIBUTES_FOR_EditorTransformComponentSelectionRequests(); - behaviorContext->EnumProperty(EditorTransformComponentSelectionRequests::RefreshType::Orientation)>("TransformRefreshType_Orientation") - TEMP_SET_ATTRIBUTES_FOR_EditorTransformComponentSelectionRequests(); - behaviorContext->EnumProperty(EditorTransformComponentSelectionRequests::RefreshType::All)>("TransformRefreshType_All") - TEMP_SET_ATTRIBUTES_FOR_EditorTransformComponentSelectionRequests(); + behaviorContext->EnumProperty(EditorTransformComponentSelectionRequests::RefreshType::Translation)>( + "TransformRefreshType_Translation") TEMP_SET_ATTRIBUTES_FOR_EditorTransformComponentSelectionRequests(); + behaviorContext->EnumProperty(EditorTransformComponentSelectionRequests::RefreshType::Orientation)>( + "TransformRefreshType_Orientation") TEMP_SET_ATTRIBUTES_FOR_EditorTransformComponentSelectionRequests(); + behaviorContext->EnumProperty(EditorTransformComponentSelectionRequests::RefreshType::All)>( + "TransformRefreshType_All") TEMP_SET_ATTRIBUTES_FOR_EditorTransformComponentSelectionRequests(); - behaviorContext->EnumProperty(EditorTransformComponentSelectionRequests::Pivot::Object)>("TransformPivot_Object") - TEMP_SET_ATTRIBUTES_FOR_EditorTransformComponentSelectionRequests(); - behaviorContext->EnumProperty(EditorTransformComponentSelectionRequests::Pivot::Center)>("TransformPivot_Center") - TEMP_SET_ATTRIBUTES_FOR_EditorTransformComponentSelectionRequests(); + behaviorContext->EnumProperty(EditorTransformComponentSelectionRequests::Pivot::Object)>( + "TransformPivot_Object") TEMP_SET_ATTRIBUTES_FOR_EditorTransformComponentSelectionRequests(); + behaviorContext->EnumProperty(EditorTransformComponentSelectionRequests::Pivot::Center)>( + "TransformPivot_Center") TEMP_SET_ATTRIBUTES_FOR_EditorTransformComponentSelectionRequests(); - behaviorContext->EBus("EditorTransformComponentSelectionRequestBus") - TEMP_SET_ATTRIBUTES_FOR_EditorTransformComponentSelectionRequests() + behaviorContext + ->EBus("EditorTransformComponentSelectionRequestBus") + TEMP_SET_ATTRIBUTES_FOR_EditorTransformComponentSelectionRequests() ->Event("SetTransformMode", &EditorTransformComponentSelectionRequestBus::Events::SetTransformMode) ->Event("GetTransformMode", &EditorTransformComponentSelectionRequestBus::Events::GetTransformMode) // Reflecting GetManipulatorTransform will require hash to be implemented, a pending task. //->Event("GetManipulatorTransform", &EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform) ->Event("RefreshManipulators", &EditorTransformComponentSelectionRequestBus::Events::RefreshManipulators) - ->Event("OverrideManipulatorOrientation", &EditorTransformComponentSelectionRequestBus::Events::OverrideManipulatorOrientation) - ->Event("OverrideManipulatorTranslation", &EditorTransformComponentSelectionRequestBus::Events::OverrideManipulatorTranslation) - ->Event("CopyTranslationToSelectedEntitiesIndividual", &EditorTransformComponentSelectionRequestBus::Events::CopyTranslationToSelectedEntitiesIndividual) - ->Event("CopyTranslationToSelectedEntitiesGroup", &EditorTransformComponentSelectionRequestBus::Events::CopyTranslationToSelectedEntitiesGroup) - ->Event("ResetTranslationForSelectedEntitiesLocal", &EditorTransformComponentSelectionRequestBus::Events::ResetTranslationForSelectedEntitiesLocal) - ->Event("CopyOrientationToSelectedEntitiesIndividual", &EditorTransformComponentSelectionRequestBus::Events::CopyOrientationToSelectedEntitiesIndividual) - ->Event("CopyOrientationToSelectedEntitiesGroup", &EditorTransformComponentSelectionRequestBus::Events::CopyOrientationToSelectedEntitiesGroup) - ->Event("ResetOrientationForSelectedEntitiesLocal", &EditorTransformComponentSelectionRequestBus::Events::ResetOrientationForSelectedEntitiesLocal) - ->Event("CopyScaleToSelectedEntitiesIndividualLocal", &EditorTransformComponentSelectionRequestBus::Events::CopyScaleToSelectedEntitiesIndividualLocal) - ->Event("CopyScaleToSelectedEntitiesIndividualWorld", &EditorTransformComponentSelectionRequestBus::Events::CopyScaleToSelectedEntitiesIndividualWorld) - ; + ->Event( + "OverrideManipulatorOrientation", &EditorTransformComponentSelectionRequestBus::Events::OverrideManipulatorOrientation) + ->Event( + "OverrideManipulatorTranslation", &EditorTransformComponentSelectionRequestBus::Events::OverrideManipulatorTranslation) + ->Event( + "CopyTranslationToSelectedEntitiesIndividual", + &EditorTransformComponentSelectionRequestBus::Events::CopyTranslationToSelectedEntitiesIndividual) + ->Event( + "CopyTranslationToSelectedEntitiesGroup", + &EditorTransformComponentSelectionRequestBus::Events::CopyTranslationToSelectedEntitiesGroup) + ->Event( + "ResetTranslationForSelectedEntitiesLocal", + &EditorTransformComponentSelectionRequestBus::Events::ResetTranslationForSelectedEntitiesLocal) + ->Event( + "CopyOrientationToSelectedEntitiesIndividual", + &EditorTransformComponentSelectionRequestBus::Events::CopyOrientationToSelectedEntitiesIndividual) + ->Event( + "CopyOrientationToSelectedEntitiesGroup", + &EditorTransformComponentSelectionRequestBus::Events::CopyOrientationToSelectedEntitiesGroup) + ->Event( + "ResetOrientationForSelectedEntitiesLocal", + &EditorTransformComponentSelectionRequestBus::Events::ResetOrientationForSelectedEntitiesLocal) + ->Event( + "CopyScaleToSelectedEntitiesIndividualLocal", + &EditorTransformComponentSelectionRequestBus::Events::CopyScaleToSelectedEntitiesIndividualLocal) + ->Event( + "CopyScaleToSelectedEntitiesIndividualWorld", + &EditorTransformComponentSelectionRequestBus::Events::CopyScaleToSelectedEntitiesIndividualWorld); - #undef TEMP_SET_ATTRIBUTES_FOR_EditorTransformComponentSelectionRequests +#undef TEMP_SET_ATTRIBUTES_FOR_EditorTransformComponentSelectionRequests } } } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h index 9cd78f8c50..966f9333fc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -16,12 +16,10 @@ #include #include - namespace AzToolsFramework { - /// Provide interface for EditorTransformComponentSelection requests. - class EditorTransformComponentSelectionRequests - : public AZ::EBusTraits + //! Provide interface for EditorTransformComponentSelection requests. + class EditorTransformComponentSelectionRequests : public AZ::EBusTraits { public: using BusIdType = AzFramework::EntityContextId; @@ -30,7 +28,7 @@ namespace AzToolsFramework static void Reflect(AZ::ReflectContext* context); - /// What type of transform editing are we in. + //! What type of transform editing are we in. enum class Mode { // note: ordering of these is important - do not change. @@ -40,7 +38,7 @@ namespace AzToolsFramework Scale }; - /// Specify the type of refresh (what type of transform modification caused the refresh). + //! Specify the type of refresh (what type of transform modification caused the refresh). enum class RefreshType { Translation, @@ -48,69 +46,69 @@ namespace AzToolsFramework All }; - /// How is the pivot aligned (object/authored position or center). + //! How is the pivot aligned (object/authored position or center). enum class Pivot { Object, Center }; - /// Set what kind of transform the type that implements this bus should use. + //! Set what kind of transform the type that implements this bus should use. virtual void SetTransformMode(Mode mode) = 0; - /// Return what transform mode the type that implements this bus is using. + //! Return what transform mode the type that implements this bus is using. virtual Mode GetTransformMode() = 0; - /// Return the current Entity Manipulator transform. - /// An AZStd::optional is returned as if we do not have a selection - /// there will be no Manipulator present. In this case we return an empty optional. + //! Return the current Entity Manipulator transform. + //! An AZStd::optional is returned as if we do not have a selection + //! there will be no Manipulator present. In this case we return an empty optional. virtual AZStd::optional GetManipulatorTransform() = 0; - /// Refresh the Manipulator based on the current entity selection. - /// This may be useful if the Entity transform has been set outside - /// of the EditorTransformComponentSelection and we want to make sure the - /// Manipulator stays up to date (in sync) with the current Entity transform. + //! Refresh the Manipulator based on the current entity selection. + //! This may be useful if the Entity transform has been set outside + //! of the EditorTransformComponentSelection and we want to make sure the + //! Manipulator stays up to date (in sync) with the current Entity transform. virtual void RefreshManipulators(RefreshType refreshType) = 0; - /// Set an orientation override for the Manipulator. - /// Useful if we've picked another Entity transform to use as our reference point. + //! Set an orientation override for the Manipulator. + //! Useful if we've picked another Entity transform to use as our reference point. virtual void OverrideManipulatorOrientation(const AZ::Quaternion& orientation) = 0; - /// Set a translation override for the Manipulator. - /// Useful if we've picked another Entity transform to use as our reference point. + //! Set a translation override for the Manipulator. + //! Useful if we've picked another Entity transform to use as our reference point. virtual void OverrideManipulatorTranslation(const AZ::Vector3& translation) = 0; - /// Copy translation to each individual entity so they all appear in the exact same position. + //! Copy translation to each individual entity so they all appear in the exact same position. virtual void CopyTranslationToSelectedEntitiesIndividual(const AZ::Vector3& translation) = 0; - /// Copy translation to manipulator position with each entity keeping the same relative position as before. + //! Copy translation to manipulator position with each entity keeping the same relative position as before. virtual void CopyTranslationToSelectedEntitiesGroup(const AZ::Vector3& translation) = 0; - /// Reset the translation of an entity to the same position as its parent. - /// Note: This is a noop if the entity does not have a parent. + //! Reset the translation of an entity to the same position as its parent. + //! Note: This is a noop if the entity does not have a parent. virtual void ResetTranslationForSelectedEntitiesLocal() = 0; - /// Copy orientation to each individual entity so they all appear in the exact same orientation. + //! Copy orientation to each individual entity so they all appear in the exact same orientation. virtual void CopyOrientationToSelectedEntitiesIndividual(const AZ::Quaternion& orientation) = 0; - /// Copy orientation to manipulator with each entity keeping the same relative orientation as before. + //! Copy orientation to manipulator with each entity keeping the same relative orientation as before. virtual void CopyOrientationToSelectedEntitiesGroup(const AZ::Quaternion& orientation) = 0; - /// Reset the orientation of an entity to the same orientation as its parent. - /// Note: This will be the aligned to the world axes (identity) if the entity does not have a parent. + //! Reset the orientation of an entity to the same orientation as its parent. + //! Note: This will be the aligned to the world axes (identity) if the entity does not have a parent. virtual void ResetOrientationForSelectedEntitiesLocal() = 0; - /// Copy scale to each individual entity in local space without moving position. + //! Copy scale to each individual entity in local space without moving position. virtual void CopyScaleToSelectedEntitiesIndividualLocal(float scale) = 0; - /// Copy scale to to each individual entity in world (absolute) space. + //! Copy scale to to each individual entity in world (absolute) space. virtual void CopyScaleToSelectedEntitiesIndividualWorld(float scale) = 0; protected: ~EditorTransformComponentSelectionRequests() = default; }; - /// Type to inherit to implement EditorTransformComponentSelectionRequests. + //! Type to inherit to implement EditorTransformComponentSelectionRequests. using EditorTransformComponentSelectionRequestBus = AZ::EBus; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.cpp index 253c27fe02..0e066684d2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.cpp @@ -1,33 +1,31 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "EditorVisibleEntityDataCache.h" #include +#include #include #include -#include namespace AzToolsFramework { - /// Cached Entity data required by the selection. + //! Cached Entity data required by the selection. struct EntityData final { using ComponentEntityAccentType = Components::EditorSelectionAccentSystemComponent::ComponentEntityAccentType; EntityData() = default; - EntityData( - AZ::EntityId entityId, const AZ::Transform& worldFromLocal, - bool locked, bool visible, bool selected, bool iconHidden); + EntityData(AZ::EntityId entityId, const AZ::Transform& worldFromLocal, bool locked, bool visible, bool selected, bool iconHidden); AZ::Transform m_worldFromLocal; AZ::EntityId m_entityId; @@ -38,7 +36,7 @@ namespace AzToolsFramework bool m_iconHidden = false; }; - using EntityDatas = AZStd::vector; ///< Alias for vector of EntityDatas. + using EntityDatas = AZStd::vector; //!< Alias for vector of EntityDatas. // Predicate to sort EntityIds with EntityDatas interchangeably. struct EntityDataComparer @@ -52,18 +50,27 @@ namespace AzToolsFramework class EditorVisibleEntityDataCache::EditorVisibleEntityDataCacheImpl { public: - EntityIdList m_visibleEntityIds; ///< The EntityIds that are visible this frame. - EntityIdList m_prevVisibleEntityIds; ///< The EntityIds that were visible the previous frame (unsorted). - EntityDatas m_visibleEntityDatas; ///< Cached EntityData required by EditorTransformComponentSelection. + EntityIdList m_visibleEntityIds; //!< The EntityIds that are visible this frame. + EntityIdList m_prevVisibleEntityIds; //!< The EntityIds that were visible the previous frame (unsorted). + EntityDatas m_visibleEntityDatas; //!< Cached EntityData required by EditorTransformComponentSelection. }; // constructor for EntityData to support emplace_back in vector EntityData::EntityData( - const AZ::EntityId entityId, const AZ::Transform& worldFromLocal, - const bool locked, const bool visible, const bool selected, const bool iconHidden) - : m_worldFromLocal(worldFromLocal), m_entityId(entityId) - , m_locked(locked), m_visible(visible), m_selected(selected) - , m_iconHidden(iconHidden) {} + const AZ::EntityId entityId, + const AZ::Transform& worldFromLocal, + const bool locked, + const bool visible, + const bool selected, + const bool iconHidden) + : m_worldFromLocal(worldFromLocal) + , m_entityId(entityId) + , m_locked(locked) + , m_visible(visible) + , m_selected(selected) + , m_iconHidden(iconHidden) + { + } bool EntityDataComparer::operator()(const AZ::EntityId lhs, const EntityData& rhs) const { @@ -98,20 +105,17 @@ namespace AzToolsFramework static EntityData EntityDataFromEntityId(const AZ::EntityId entityId) { bool visible = false; - EditorEntityInfoRequestBus::EventResult( - visible, entityId, &EditorEntityInfoRequestBus::Events::IsVisible); + EditorEntityInfoRequestBus::EventResult(visible, entityId, &EditorEntityInfoRequestBus::Events::IsVisible); bool locked = false; - EditorEntityInfoRequestBus::EventResult( - locked, entityId, &EditorEntityInfoRequestBus::Events::IsLocked); + EditorEntityInfoRequestBus::EventResult(locked, entityId, &EditorEntityInfoRequestBus::Events::IsLocked); bool iconHidden = false; EditorEntityIconComponentRequestBus::EventResult( iconHidden, entityId, &EditorEntityIconComponentRequests::IsEntityIconHiddenInViewport); AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity(); - AZ::TransformBus::EventResult( - worldFromLocal, entityId, &AZ::TransformBus::Events::GetWorldTM); + AZ::TransformBus::EventResult(worldFromLocal, entityId, &AZ::TransformBus::Events::GetWorldTM); return { entityId, worldFromLocal, locked, visible, IsSelected(entityId), iconHidden }; } @@ -155,16 +159,14 @@ namespace AzToolsFramework AZStd::sort(m_impl->m_visibleEntityDatas.begin(), m_impl->m_visibleEntityDatas.end()); } - void EditorVisibleEntityDataCache::CalculateVisibleEntityDatas( - const AzFramework::ViewportInfo& viewportInfo) + void EditorVisibleEntityDataCache::CalculateVisibleEntityDatas(const AzFramework::ViewportInfo& viewportInfo) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); // request list of visible entities from authoritative system EntityIdList nextVisibleEntityIds; ViewportInteraction::MainEditorViewportInteractionRequestBus::Event( - viewportInfo.m_viewportId, - &ViewportInteraction::MainEditorViewportInteractionRequestBus::Events::FindVisibleEntities, + viewportInfo.m_viewportId, &ViewportInteraction::MainEditorViewportInteractionRequestBus::Events::FindVisibleEntities, nextVisibleEntityIds); // only bother resorting if we know the lists have changed @@ -181,31 +183,26 @@ namespace AzToolsFramework // find entities that are visible this frame but weren't last frame AZStd::vector added; std::set_difference( - m_impl->m_visibleEntityIds.begin(), m_impl->m_visibleEntityIds.end(), - m_impl->m_visibleEntityDatas.begin(), m_impl->m_visibleEntityDatas.end(), - std::back_inserter(added), EntityDataComparer()); + m_impl->m_visibleEntityIds.begin(), m_impl->m_visibleEntityIds.end(), m_impl->m_visibleEntityDatas.begin(), + m_impl->m_visibleEntityDatas.end(), std::back_inserter(added), EntityDataComparer()); // find entities that are not visible this frame but were last frame AZStd::vector removed; std::set_difference( - m_impl->m_visibleEntityDatas.begin(), m_impl->m_visibleEntityDatas.end(), - m_impl->m_visibleEntityIds.begin(), m_impl->m_visibleEntityIds.end(), - std::back_inserter(removed), EntityDataComparer()); + m_impl->m_visibleEntityDatas.begin(), m_impl->m_visibleEntityDatas.end(), m_impl->m_visibleEntityIds.begin(), + m_impl->m_visibleEntityIds.end(), std::back_inserter(removed), EntityDataComparer()); // search for entityData in removed list, return true if it is found const auto removePredicate = [&removed](const EntityData& entityData) { - const auto removeIt = std::equal_range( - removed.begin(), removed.end(), entityData); + const auto removeIt = std::equal_range(removed.begin(), removed.end(), entityData); return removeIt.first != removeIt.second; }; // erase-remove idiom - bubble entities to be removed to the end, then erase them in one go m_impl->m_visibleEntityDatas.erase( - AZStd::remove_if( - m_impl->m_visibleEntityDatas.begin(), - m_impl->m_visibleEntityDatas.end(), removePredicate), + AZStd::remove_if(m_impl->m_visibleEntityDatas.begin(), m_impl->m_visibleEntityDatas.end(), removePredicate), m_impl->m_visibleEntityDatas.end()); // for newly added entities, request their initial state when first cached @@ -240,8 +237,7 @@ namespace AzToolsFramework return m_impl->m_visibleEntityDatas[index].m_entityId; } - EditorVisibleEntityDataCache::ComponentEntityAccentType EditorVisibleEntityDataCache::GetVisibleEntityAccent( - const size_t index) const + EditorVisibleEntityDataCache::ComponentEntityAccentType EditorVisibleEntityDataCache::GetVisibleEntityAccent(const size_t index) const { return m_impl->m_visibleEntityDatas[index].m_accent; } @@ -273,8 +269,8 @@ namespace AzToolsFramework AZStd::optional EditorVisibleEntityDataCache::GetVisibleEntityIndexFromId(const AZ::EntityId entityId) const { - const auto entityIdIt = std::equal_range( - m_impl->m_visibleEntityDatas.begin(), m_impl->m_visibleEntityDatas.end(), entityId, EntityDataComparer()); + const auto entityIdIt = + std::equal_range(m_impl->m_visibleEntityDatas.begin(), m_impl->m_visibleEntityDatas.end(), entityId, EntityDataComparer()); if (entityIdIt.first != entityIdIt.second) { @@ -318,8 +314,7 @@ namespace AzToolsFramework } } - void EditorVisibleEntityDataCache::OnTransformChanged( - const AZ::Transform& /*local*/, const AZ::Transform& world) + void EditorVisibleEntityDataCache::OnTransformChanged(const AZ::Transform& /*local*/, const AZ::Transform& world) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -367,8 +362,7 @@ namespace AzToolsFramework } } - void EditorVisibleEntityDataCache::OnEntityIconChanged( - const AZ::Data::AssetId& /*entityIconAssetId*/) + void EditorVisibleEntityDataCache::OnEntityIconChanged(const AZ::Data::AssetId& /*entityIconAssetId*/) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h index 72c1595e7b..0d74825bf8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -22,8 +22,8 @@ namespace AzToolsFramework { - /// A cache of packed EntityData that can be iterated over efficiently without - /// the need to make individual EBus calls + //! A cache of packed EntityData that can be iterated over efficiently without + //! the need to make individual EBus calls class EditorVisibleEntityDataCache : private EditorEntityVisibilityNotificationBus::Router , private EditorEntityLockComponentNotificationBus::Router @@ -45,7 +45,7 @@ namespace AzToolsFramework void CalculateVisibleEntityDatas(const AzFramework::ViewportInfo& viewportInfo); - /// EditorVisibleEntityDataCache interface + //! EditorVisibleEntityDataCache interface size_t VisibleEntityDataCount() const; AZ::Vector3 GetVisibleEntityPosition(size_t index) const; const AZ::Transform& GetVisibleEntityTransform(size_t index) const; @@ -72,8 +72,7 @@ namespace AzToolsFramework void OnEntityLockChanged(bool locked) override; // TransformNotificationBus - void OnTransformChanged( - const AZ::Transform& local, const AZ::Transform& world) override; + void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override; // EditorComponentSelectionNotificationsBus void OnAccentTypeChanged(EntityAccentType accent) override; @@ -86,6 +85,6 @@ namespace AzToolsFramework void OnEntityIconChanged(const AZ::Data::AssetId& entityIconAssetId) override; class EditorVisibleEntityDataCacheImpl; - AZStd::unique_ptr m_impl; ///< Internal representation of entity data cache. + AZStd::unique_ptr m_impl; //!< Internal representation of entity data cache. }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiCluster.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiCluster.cpp index 79744f1dbf..d452d47508 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiCluster.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiCluster.cpp @@ -108,6 +108,61 @@ namespace AzToolsFramework::ViewportUi::Internal m_widgetCallbacks.Update(); } + void ViewportUiCluster::SetButtonLocked(const ButtonId buttonId, const bool isLocked) + { + const auto& buttons = m_buttonGroup->GetButtons(); + + // unlocked previously locked button + if (m_lockedButtonId.has_value() && isLocked) + { + // find the button to extract the old icon (without overlay) + auto findLocked = [this](const Button* button) { return (button->m_buttonId == m_lockedButtonId); }; + if (auto lockedButtonIt = AZStd::find_if(buttons.begin(), buttons.end(), findLocked); lockedButtonIt != buttons.end()) + { + // get the action corresponding to the lockedButtonId + if (auto actionEntry = m_buttonActionMap.find(m_lockedButtonId.value()); actionEntry != m_buttonActionMap.end()) + { + // remove the overlay + auto action = actionEntry->second; + action->setIcon(QIcon(QString((*lockedButtonIt)->m_icon.c_str()))); + } + } + } + + auto found = [buttonId](Button* button) { return (button->m_buttonId == buttonId); }; + if (auto buttonIt = AZStd::find_if(buttons.begin(), buttons.end(), found); buttonIt != buttons.end()) + { + QIcon newIcon; + + if (isLocked) + { + // overlay the locked icon ontop of the button's icon + QPixmap comboPixmap(24, 24); + comboPixmap.fill(Qt::transparent); + QPixmap firstImage(QString((*buttonIt)->m_icon.c_str())); + QPixmap secondImage(QString(":/stylesheet/img/UI20/toolbar/Locked_Status.svg")); + + QPainter painter(&comboPixmap); + painter.drawPixmap(0, 0, firstImage); + painter.drawPixmap(0, 0, secondImage); + newIcon.addPixmap(comboPixmap); + m_lockedButtonId = buttonId; + } + else + { + // remove the overlay + newIcon = QIcon(QString((*buttonIt)->m_icon.c_str())); + m_lockedButtonId = AZStd::nullopt; + } + + if (auto actionEntry = m_buttonActionMap.find(buttonId); actionEntry != m_buttonActionMap.end()) + { + auto action = actionEntry->second; + action->setIcon(newIcon); + } + } + } + ViewportUiWidgetCallbacks ViewportUiCluster::GetWidgetCallbacks() { return m_widgetCallbacks; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiCluster.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiCluster.h index 4f738177ea..027a201a9c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiCluster.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiCluster.h @@ -17,6 +17,7 @@ #include #include #include +#include namespace AzToolsFramework::ViewportUi::Internal { @@ -38,6 +39,8 @@ namespace AzToolsFramework::ViewportUi::Internal void RemoveButton(ButtonId buttonId); //! Updates all registered actions. void Update(); + //! Adds a locked overlay to the button's icon. + void SetButtonLocked(ButtonId buttonId, bool isLocked); //! Returns the widget manager. ViewportUiWidgetCallbacks GetWidgetCallbacks(); @@ -52,5 +55,6 @@ namespace AzToolsFramework::ViewportUi::Internal AZStd::shared_ptr m_buttonGroup; //!< Data structure which the cluster will be displaying to the Viewport UI. AZStd::unordered_map> m_buttonActionMap; //!< Map for buttons to their corresponding actions. ViewportUiWidgetCallbacks m_widgetCallbacks; //!< Registers actions and manages updates. + AZStd::optional m_lockedButtonId = AZStd::nullopt; //!< Used to track the last button locked. }; } // namespace AzToolsFramework::ViewportUi::Internal diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp index e9e7dcc1cc..3565d33174 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp @@ -100,6 +100,14 @@ namespace AzToolsFramework::ViewportUi::Internal } } + void ViewportUiDisplay::SetClusterButtonLocked(const ViewportUiElementId clusterId, const ButtonId buttonId, const bool isLocked) + { + if (auto viewportUiCluster = qobject_cast(GetViewportUiElement(clusterId).get())) + { + viewportUiCluster->SetButtonLocked(buttonId, isLocked); + } + } + void ViewportUiDisplay::RemoveClusterButton(ViewportUiElementId clusterId, ButtonId buttonId) { if (auto cluster = qobject_cast(GetViewportUiElement(clusterId).get())) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.h index d46e01c978..19d04e63ff 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.h @@ -58,6 +58,7 @@ namespace AzToolsFramework::ViewportUi::Internal void AddCluster(AZStd::shared_ptr buttonGroup, Alignment align); void AddClusterButton(ViewportUiElementId clusterId, Button* button); + void SetClusterButtonLocked(ViewportUiElementId clusterId, ButtonId buttonId, bool isLocked); void RemoveClusterButton(ViewportUiElementId clusterId, ButtonId buttonId); void UpdateCluster(const ViewportUiElementId clusterId); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.cpp index 12c3b5c9bb..0668af383b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.cpp @@ -67,6 +67,16 @@ namespace AzToolsFramework::ViewportUi } } + void ViewportUiManager::SetClusterButtonLocked(const ClusterId clusterId, const ButtonId buttonId, const bool isLocked) + { + if (auto clusterIt = m_clusterButtonGroups.find(clusterId); clusterIt != m_clusterButtonGroups.end()) + { + auto cluster = clusterIt->second; + m_viewportUi->SetClusterButtonLocked(cluster->GetViewportUiElementId(), buttonId, isLocked); + UpdateButtonGroupUi(cluster.get()); + } + } + void ViewportUiManager::RegisterClusterEventHandler(const ClusterId clusterId, AZ::Event::Handler& handler) { if (auto clusterIt = m_clusterButtonGroups.find(clusterId); clusterIt != m_clusterButtonGroups.end()) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.h index 04a58cef65..14609ccc14 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.h @@ -35,6 +35,7 @@ namespace AzToolsFramework::ViewportUi const SwitcherId CreateSwitcher(Alignment align) override; void SetClusterActiveButton(ClusterId clusterId, ButtonId buttonId) override; void SetSwitcherActiveButton(SwitcherId switcherId, ButtonId buttonId) override; + void SetClusterButtonLocked(ClusterId clusterId, ButtonId buttonId, bool isLocked) override; const ButtonId CreateClusterButton(ClusterId clusterId, const AZStd::string& icon) override; const ButtonId CreateSwitcherButton( SwitcherId switcherId, const AZStd::string& icon, const AZStd::string& name = AZStd::string()) override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiRequestBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiRequestBus.h index 3879817ccb..a068ffe9ee 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiRequestBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiRequestBus.h @@ -65,6 +65,8 @@ namespace AzToolsFramework::ViewportUi virtual void SetClusterActiveButton(ClusterId clusterId, ButtonId buttonId) = 0; //! Sets the active button of the switcher. This is the button which has a text label. virtual void SetSwitcherActiveButton(SwitcherId clusterId, ButtonId buttonId) = 0; + //! Adds a locked overlay to the cluster button's icon. + virtual void SetClusterButtonLocked(ClusterId clusterId, ButtonId buttonId, bool isLocked) = 0; //! Registers a new button onto a cluster. virtual const ButtonId CreateClusterButton(const ClusterId clusterId, const AZStd::string& icon) = 0; //! Registers a new button onto a switcher. diff --git a/Code/Framework/AzToolsFramework/Tests/ComponentModeTests.cpp b/Code/Framework/AzToolsFramework/Tests/ComponentModeTests.cpp index a4ee80fa5f..45699ddb78 100644 --- a/Code/Framework/AzToolsFramework/Tests/ComponentModeTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/ComponentModeTests.cpp @@ -1,30 +1,31 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include "ComponentModeTestDoubles.h" #include "ComponentModeTestFixture.h" #include +#include #include #include #include -#include #include +#include #include #include #include #include -#include #include +#include #include #include #include @@ -32,7 +33,6 @@ #include #include #include -#include #include namespace UnitTest @@ -47,19 +47,16 @@ namespace UnitTest /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Given QWidget rootWidget; - ActionOverrideRequestBus::Event( - GetEntityContextId(), &ActionOverrideRequests::SetupActionOverrideHandler, &rootWidget); + ActionOverrideRequestBus::Event(GetEntityContextId(), &ActionOverrideRequests::SetupActionOverrideHandler, &rootWidget); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // When ComponentModeSystemRequestBus::Broadcast( - &ComponentModeSystemRequests::BeginComponentMode, - AZStd::vector{}); + &ComponentModeSystemRequests::BeginComponentMode, AZStd::vector{}); bool inComponentMode = false; - ComponentModeSystemRequestBus::BroadcastResult( - inComponentMode, &ComponentModeSystemRequests::InComponentMode); + ComponentModeSystemRequestBus::BroadcastResult(inComponentMode, &ComponentModeSystemRequests::InComponentMode); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -69,11 +66,9 @@ namespace UnitTest /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // When - ComponentModeSystemRequestBus::Broadcast( - &ComponentModeSystemRequests::EndComponentMode); + ComponentModeSystemRequestBus::Broadcast(&ComponentModeSystemRequests::EndComponentMode); - ComponentModeSystemRequestBus::BroadcastResult( - inComponentMode, &ComponentModeSystemRequests::InComponentMode); + ComponentModeSystemRequestBus::BroadcastResult(inComponentMode, &ComponentModeSystemRequests::InComponentMode); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -81,8 +76,7 @@ namespace UnitTest EXPECT_FALSE(inComponentMode); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ActionOverrideRequestBus::Event( - GetEntityContextId(), &ActionOverrideRequests::TeardownActionOverrideHandler); + ActionOverrideRequestBus::Event(GetEntityContextId(), &ActionOverrideRequests::TeardownActionOverrideHandler); } TEST_F(ComponentModeTestFixture, TwoComponentsOnSingleEntityWithSameComponentModeBothBegin) @@ -104,8 +98,7 @@ namespace UnitTest // mimic selecting the entity in the viewport (after selection the ComponentModeDelegate // connects to the ComponentModeDelegateRequestBus on the entity/component pair address) const AzToolsFramework::EntityIdList entityIds = { entityId }; - ToolsApplicationRequestBus::Broadcast( - &ToolsApplicationRequests::SetSelectedEntities, entityIds); + ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetSelectedEntities, entityIds); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -113,8 +106,7 @@ namespace UnitTest // move all selected components into ComponentMode // (mimic pressing the 'Edit' button to begin Component Mode) ComponentModeSystemRequestBus::Broadcast( - &ComponentModeSystemRequests::AddSelectedComponentModesOfType, - AZ::AzTypeInfo::Uuid()); + &ComponentModeSystemRequests::AddSelectedComponentModesOfType, AZ::AzTypeInfo::Uuid()); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -152,8 +144,7 @@ namespace UnitTest // mimic selecting the entity in the viewport (after selection the ComponentModeDelegate // connects to the ComponentModeDelegateRequestBus on the entity/component pair address) const AzToolsFramework::EntityIdList entityIds = { entityId }; - ToolsApplicationRequestBus::Broadcast( - &ToolsApplicationRequests::SetSelectedEntities, entityIds); + ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetSelectedEntities, entityIds); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -161,8 +152,7 @@ namespace UnitTest // move all selected components into ComponentMode // (mimic pressing the 'Edit' button to begin Component Mode) ComponentModeSystemRequestBus::Broadcast( - &ComponentModeSystemRequests::AddSelectedComponentModesOfType, - AZ::AzTypeInfo::Uuid()); + &ComponentModeSystemRequests::AddSelectedComponentModesOfType, AZ::AzTypeInfo::Uuid()); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -202,8 +192,7 @@ namespace UnitTest // mimic selecting the entity in the viewport (after selection the ComponentModeDelegate // connects to the ComponentModeDelegateRequestBus on the entity/component pair address) const AzToolsFramework::EntityIdList entityIds = { entityId }; - ToolsApplicationRequestBus::Broadcast( - &ToolsApplicationRequests::SetSelectedEntities, entityIds); + ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetSelectedEntities, entityIds); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -211,16 +200,13 @@ namespace UnitTest // move all selected components into ComponentMode // (mimic pressing the 'Edit' button to begin Component Mode) ComponentModeSystemRequestBus::Broadcast( - &ComponentModeSystemRequests::AddSelectedComponentModesOfType, - AZ::AzTypeInfo::Uuid()); + &ComponentModeSystemRequests::AddSelectedComponentModesOfType, AZ::AzTypeInfo::Uuid()); bool nextModeCycled = true; - ComponentModeSystemRequestBus::BroadcastResult( - nextModeCycled, &ComponentModeSystemRequests::SelectNextActiveComponentMode); + ComponentModeSystemRequestBus::BroadcastResult(nextModeCycled, &ComponentModeSystemRequests::SelectNextActiveComponentMode); bool previousModeCycled = true; - ComponentModeSystemRequestBus::BroadcastResult( - previousModeCycled, &ComponentModeSystemRequests::SelectPreviousActiveComponentMode); + ComponentModeSystemRequestBus::BroadcastResult(previousModeCycled, &ComponentModeSystemRequests::SelectPreviousActiveComponentMode); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -250,8 +236,7 @@ namespace UnitTest // mimic selecting the entity in the viewport (after selection the ComponentModeDelegate // connects to the ComponentModeDelegateRequestBus on the entity/component pair address) const AzToolsFramework::EntityIdList entityIds = { entityId }; - ToolsApplicationRequestBus::Broadcast( - &ToolsApplicationRequests::SetSelectedEntities, entityIds); + ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetSelectedEntities, entityIds); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -259,15 +244,13 @@ namespace UnitTest // move all selected components into ComponentMode // (mimic pressing the 'Edit' button to begin Component Mode) ComponentModeSystemRequestBus::Broadcast( - &ComponentModeSystemRequests::AddSelectedComponentModesOfType, - AZ::AzTypeInfo::Uuid()); + &ComponentModeSystemRequests::AddSelectedComponentModesOfType, AZ::AzTypeInfo::Uuid()); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Then bool multipleComponentModeTypes = true; - ComponentModeSystemRequestBus::BroadcastResult( - multipleComponentModeTypes, &ComponentModeSystemRequests::HasMultipleComponentTypes); + ComponentModeSystemRequestBus::BroadcastResult(multipleComponentModeTypes, &ComponentModeSystemRequests::HasMultipleComponentTypes); EXPECT_FALSE(multipleComponentModeTypes); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -294,8 +277,7 @@ namespace UnitTest // mimic selecting the entity in the viewport (after selection the ComponentModeDelegate // connects to the ComponentModeDelegateRequestBus on the entity/component pair address) const AzToolsFramework::EntityIdList entityIds = { entityId }; - ToolsApplicationRequestBus::Broadcast( - &ToolsApplicationRequests::SetSelectedEntities, entityIds); + ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetSelectedEntities, entityIds); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -303,15 +285,13 @@ namespace UnitTest // move all selected components into ComponentMode // (mimic pressing the 'Edit' button to begin Component Mode) ComponentModeSystemRequestBus::Broadcast( - &ComponentModeSystemRequests::AddSelectedComponentModesOfType, - AZ::AzTypeInfo::Uuid()); + &ComponentModeSystemRequests::AddSelectedComponentModesOfType, AZ::AzTypeInfo::Uuid()); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Then bool multipleComponentModeTypes = true; - ComponentModeSystemRequestBus::BroadcastResult( - multipleComponentModeTypes, &ComponentModeSystemRequests::HasMultipleComponentTypes); + ComponentModeSystemRequestBus::BroadcastResult(multipleComponentModeTypes, &ComponentModeSystemRequests::HasMultipleComponentTypes); EXPECT_FALSE(multipleComponentModeTypes); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -341,8 +321,7 @@ namespace UnitTest // mimic selecting the entity in the viewport (after selection the ComponentModeDelegate // connects to the ComponentModeDelegateRequestBus on the entity/component pair address) const AzToolsFramework::EntityIdList entityIds = { entityId }; - ToolsApplicationRequestBus::Broadcast( - &ToolsApplicationRequests::SetSelectedEntities, entityIds); + ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetSelectedEntities, entityIds); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -350,15 +329,13 @@ namespace UnitTest // move all selected components into ComponentMode // (mimic pressing the 'Edit' button to begin Component Mode) ComponentModeSystemRequestBus::Broadcast( - &ComponentModeSystemRequests::AddSelectedComponentModesOfType, - AZ::AzTypeInfo::Uuid()); + &ComponentModeSystemRequests::AddSelectedComponentModesOfType, AZ::AzTypeInfo::Uuid()); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Then bool multipleComponentModeTypes = false; - ComponentModeSystemRequestBus::BroadcastResult( - multipleComponentModeTypes, &ComponentModeSystemRequests::HasMultipleComponentTypes); + ComponentModeSystemRequestBus::BroadcastResult(multipleComponentModeTypes, &ComponentModeSystemRequests::HasMultipleComponentTypes); bool secondComponentModeInstantiated = false; ComponentModeSystemRequestBus::BroadcastResult( @@ -366,8 +343,7 @@ namespace UnitTest AZ::EntityComponentIdPair(entityId, placeholder2->GetId())); AZ::Uuid activeComponentType = AZ::Uuid::CreateNull(); - ComponentModeSystemRequestBus::BroadcastResult( - activeComponentType, &ComponentModeSystemRequests::ActiveComponentMode); + ComponentModeSystemRequestBus::BroadcastResult(activeComponentType, &ComponentModeSystemRequests::ActiveComponentMode); EXPECT_TRUE(multipleComponentModeTypes); EXPECT_TRUE(secondComponentModeInstantiated); @@ -412,13 +388,11 @@ namespace UnitTest // Component Mode is will sent the notification to the correct address. ComponentModeActionSignalRequestBus::Event( AZ::EntityComponentIdPair(entityId, placeholder1->GetId()), - &ComponentModeActionSignalRequests::SetComponentModeActionNotificationBusToNotify, - checkerBusId); + &ComponentModeActionSignalRequests::SetComponentModeActionNotificationBusToNotify, checkerBusId); ComponentModeActionSignalRequestBus::Event( AZ::EntityComponentIdPair(entityId, placeholder2->GetId()), - &ComponentModeActionSignalRequests::SetComponentModeActionNotificationBusToNotify, - checkerBusId); + &ComponentModeActionSignalRequests::SetComponentModeActionNotificationBusToNotify, checkerBusId); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -470,8 +444,7 @@ namespace UnitTest using MouseInteractionResult = AzToolsFramework::ViewportInteraction::MouseInteractionResult; MouseInteractionResult handled = MouseInteractionResult::None; EditorInteractionSystemViewportSelectionRequestBus::BroadcastResult( - handled, &EditorInteractionSystemViewportSelectionRequestBus::Events::InternalHandleAllMouseInteractions, - interactionEvent); + handled, &EditorInteractionSystemViewportSelectionRequestBus::Events::InternalHandleAllMouseInteractions, interactionEvent); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -482,8 +455,7 @@ namespace UnitTest } // Test version of EntityPropertyEditor to detect/ensure certain functions were called - class TestEntityPropertyEditor - : public AzToolsFramework::EntityPropertyEditor + class TestEntityPropertyEditor : public AzToolsFramework::EntityPropertyEditor { public: void InvalidatePropertyDisplay(PropertyModificationRefreshLevel level) override; @@ -496,8 +468,7 @@ namespace UnitTest } // Simple fixture to encapsulate a TestEntityPropertyEditor - class ComponentModePinnedSelectionFixture - : public ToolsApplicationFixture + class ComponentModePinnedSelectionFixture : public ToolsApplicationFixture { public: void SetUpEditorFixtureImpl() override @@ -533,7 +504,7 @@ namespace UnitTest /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // When // select entity - const auto selectedEntities = AzToolsFramework::EntityIdList { entityId }; + const auto selectedEntities = AzToolsFramework::EntityIdList{ entityId }; SelectEntities(selectedEntities); // pin entity @@ -549,8 +520,7 @@ namespace UnitTest EXPECT_TRUE(m_testEntityPropertyEditor->IsLockedToSpecificEntities()); EXPECT_TRUE(m_testEntityPropertyEditor->m_invalidatePropertyDisplayCalled); - bool couldBeginComponentMode = - AzToolsFramework::ComponentModeFramework::CouldBeginComponentModeWithEntity(entityId); + bool couldBeginComponentMode = AzToolsFramework::ComponentModeFramework::CouldBeginComponentModeWithEntity(entityId); EXPECT_FALSE(couldBeginComponentMode); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -566,29 +536,26 @@ namespace UnitTest entity->Deactivate(); AzToolsFramework::EntityCompositionRequestBus::Broadcast( - &AzToolsFramework::EntityCompositionRequestBus::Events::AddComponentsToEntities, - AzToolsFramework::EntityIdList{entityId}, + &AzToolsFramework::EntityCompositionRequestBus::Events::AddComponentsToEntities, AzToolsFramework::EntityIdList{ entityId }, AZ::ComponentTypeList{ AZ::AzTypeInfo::Uuid() }); AzToolsFramework::EntityCompositionRequestBus::Broadcast( - &AzToolsFramework::EntityCompositionRequestBus::Events::AddComponentsToEntities, - AzToolsFramework::EntityIdList{entityId}, - AZ::ComponentTypeList{AZ::AzTypeInfo::Uuid()}); + &AzToolsFramework::EntityCompositionRequestBus::Events::AddComponentsToEntities, AzToolsFramework::EntityIdList{ entityId }, + AZ::ComponentTypeList{ AZ::AzTypeInfo::Uuid() }); entity->Activate(); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // When - SelectEntities(AzToolsFramework::EntityIdList{entityId}); + SelectEntities(AzToolsFramework::EntityIdList{ entityId }); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Then AZ::Entity::ComponentArrayType pendingComponents; AzToolsFramework::EditorPendingCompositionRequestBus::Event( - entityId, &AzToolsFramework::EditorPendingCompositionRequestBus::Events::GetPendingComponents, - pendingComponents); + entityId, &AzToolsFramework::EditorPendingCompositionRequestBus::Events::GetPendingComponents, pendingComponents); // ensure we do have pending components EXPECT_EQ(pendingComponents.size(), 1); diff --git a/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportScreenTests.cpp b/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportScreenTests.cpp index 18462bc97b..d82c7ec425 100644 --- a/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportScreenTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportScreenTests.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,15 @@ 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) + { + 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) @@ -30,7 +40,8 @@ namespace UnitTest const auto worldResult = AzFramework::ScreenToWorld(screenPoint, cameraState); return AzFramework::WorldToScreen(worldResult, cameraState); } - + //////////////////////////////////////////////////////////////////////////////////////////////////////// + // ScreenPoint tests TEST(ViewportScreen, WorldToScreenAndScreenToWorldReturnsTheSameValueIdentityCameraOffsetFromOrigin) { using AzFramework::ScreenPoint; @@ -38,8 +49,6 @@ namespace UnitTest const auto screenDimensions = AZ::Vector2(800.0f, 600.0f); const auto cameraPosition = AZ::Vector3::CreateAxisY(-10.0f); - // note: nearClip is 0.1 - the world space value returned will be aligned to the near clip - // plane of the camera so use that to confirm the mapping to/from is correct const auto cameraState = AzFramework::CreateIdentityDefaultCamera(cameraPosition, screenDimensions); { const auto expectedScreenPoint = ScreenPoint{600, 450}; @@ -81,6 +90,8 @@ namespace UnitTest EXPECT_EQ(resultScreenPoint, expectedScreenPoint); } + // note: nearClip is 0.1 - the world space value returned will be aligned to the near clip + // plane of the camera so use that to confirm the mapping to/from is correct TEST(ViewportScreen, ScreenToWorldReturnsPositionOnNearClipPlaneInWorldSpace) { using AzFramework::ScreenPoint; @@ -94,7 +105,75 @@ namespace UnitTest 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); + EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint)); + } + + { + 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); + EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint)); + } + + { + const auto expectedNdcPoint = NdcPoint{1.0f, 1.0f}; + const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState); + EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint)); + } + } + + TEST(ViewportScreen, WorldToScreenNDCAndScreenNDCToWorldReturnsTheSameValueOrientatedCamera) + { + using NdcPoint = AZ::Vector2; + + const auto screenDimensions = AZ::Vector2(800.0f, 600.0f); + const auto cameraTransform = + AZ::Transform::CreateRotationX(AZ::DegToRad(45.0f)) * AZ::Transform::CreateRotationZ(AZ::DegToRad(90.0f)); + + const auto cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions); + + const auto expectedNdcPoint = NdcPoint{0.25f, 0.5f}; + const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState); + EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint)); + } + + // note: nearClip is 0.1 - the world space value returned will be aligned to the near clip + // plane of the camera so use that to confirm the mapping to/from is correct + TEST(ViewportScreen, ScreenNDCToWorldReturnsPositionOnNearClipPlaneInWorldSpace) + { + 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 cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions); + + 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))); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////// + // ScreenVector tests TEST(ViewportScreen, SubstractingScreenPointGivesScreenVector) { using AzFramework::ScreenPoint; @@ -220,6 +299,8 @@ namespace UnitTest EXPECT_NEAR(AzFramework::ScreenVectorLength(ScreenVector(12, 15)), 19.20937f, 0.001f); } + //////////////////////////////////////////////////////////////////////////////////////////////////////// + // Other tests TEST(ViewportScreen, CanGetCameraTransformFromCameraViewAndBack) { const auto screenDimensions = AZ::Vector2(1024.0f, 768.0f); diff --git a/Code/Framework/Tests/ArchiveTests.cpp b/Code/Framework/Tests/ArchiveTests.cpp index 6dc081ee72..ddc7060083 100644 --- a/Code/Framework/Tests/ArchiveTests.cpp +++ b/Code/Framework/Tests/ArchiveTests.cpp @@ -281,6 +281,39 @@ namespace UnitTest TestFGetCachedFileData(fileInArchiveFile, dataString.size(), dataString.data()); } + TEST_F(ArchiveTestFixture, TestArchiveOpenPacks_FindsMultiplePaks_Works) + { + AZ::IO::IArchive* archive = AZ::Interface::Get(); + ASSERT_NE(nullptr, archive); + + AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance(); + ASSERT_NE(nullptr, fileIo); + + auto resetArchiveFile = [archive, fileIo](const AZStd::string& filePath) + { + archive->ClosePack(filePath.c_str()); + fileIo->Remove(filePath.c_str()); + + auto pArchive = archive->OpenArchive(filePath.c_str(), nullptr, AZ::IO::INestedArchive::FLAGS_CREATE_NEW); + EXPECT_NE(nullptr, pArchive); + pArchive.reset(); + archive->ClosePack(filePath.c_str()); + }; + + AZStd::string testArchivePath_pakOne = "@usercache@/one.pak"; + AZStd::string testArchivePath_pakTwo = "@usercache@/two.pak"; + + // reset test files in case they already exist + resetArchiveFile(testArchivePath_pakOne); + resetArchiveFile(testArchivePath_pakTwo); + + // open and fetch the opened pak file using a *.pak + AZStd::vector fullPaths; + archive->OpenPacks("@usercache@/*.pak", AZ::IO::IArchive::EPathResolutionRules::FLAGS_PATH_REAL, &fullPaths); + EXPECT_TRUE(AZStd::any_of(fullPaths.cbegin(), fullPaths.cend(), [](auto& path) { return path.ends_with("one.pak"); })); + EXPECT_TRUE(AZStd::any_of(fullPaths.cbegin(), fullPaths.cend(), [](auto& path) { return path.ends_with("two.pak"); })); + } + TEST_F(ArchiveTestFixture, TestArchiveFGetCachedFileData_LooseFile) { // ------setup loose file FGetCachedFileData tests ------------------------- diff --git a/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp b/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp index 484b7f46d7..f0614aa13b 100644 --- a/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp +++ b/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp @@ -11,9 +11,11 @@ */ #include +#include #include #include #include +#include #include namespace UnitTest @@ -40,6 +42,10 @@ namespace UnitTest m_application = new TestApplication(); AZ::ComponentApplication::Descriptor descriptor; m_application->Start(descriptor); + // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is + // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash + // in the unit tests. + AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); m_spawnable = aznew AzFramework::Spawnable( AZ::Data::AssetId::CreateString("{EB2E8A2B-F253-4A90-BBF4-55F2EED786B8}:0"), AZ::Data::AssetData::AssetStatus::Ready); @@ -81,6 +87,42 @@ namespace UnitTest } } + void CreateRecursiveHierarchy() + { + AzFramework::Spawnable::EntityList& entities = m_spawnable->GetEntities(); + size_t numElements = entities.size(); + AZ::EntityId parent; + for (size_t i=0; i& entity = entities[i]; + auto component = entity->CreateComponent(); + if (i > 0) + { + component->SetParent(parent); + } + parent = entity->GetId(); + } + } + + void CreateSingleParent() + { + AzFramework::Spawnable::EntityList& entities = m_spawnable->GetEntities(); + size_t numElements = entities.size(); + if (numElements > 0) + { + AZ::EntityId parent = entities[0]->GetId(); + for (size_t i = 0; i < numElements; ++i) + { + AZStd::unique_ptr& entity = entities[i]; + auto component = entity->CreateComponent(); + if (i > 0) + { + component->SetParent(parent); + } + } + } + } + protected: AZ::Data::Asset* m_spawnableAsset { nullptr }; AzFramework::SpawnableEntitiesManager* m_manager { nullptr }; @@ -104,17 +146,50 @@ namespace UnitTest { spawnedEntitiesCount += entities.size(); }; - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, {}, AZStd::move(callback)); + AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs)); m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); EXPECT_EQ(NumEntities, spawnedEntitiesCount); } + TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_SetParentOnSpawnedEntities_LineageIsPreserved) + { + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + CreateRecursiveHierarchy(); + + auto callback = [](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + AZ::EntityId parentId; + bool isFirst = true; + for (const AZ::Entity* entity : entities) + { + if (!isFirst) + { + auto transform = entity->GetTransform(); + ASSERT_NE(nullptr, transform); + EXPECT_EQ(parentId, transform->GetParentId()); + } + else + { + isFirst = false; + } + parentId = entity->GetId(); + } + }; + AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + } + TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_DeleteTicketBeforeCall_NoCrash) { { AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); - m_manager->SpawnAllEntities(ticket, AzFramework::SpawnablePriority_Default); + m_manager->SpawnAllEntities(ticket); } m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } @@ -124,11 +199,175 @@ namespace UnitTest // SpawnEntities // + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_Call_AllEntitiesSpawned) + { + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + + AZStd::vector indices = { 0, 2, 3, 1 }; + + size_t spawnedEntitiesCount = 0; + auto callback = [&spawnedEntitiesCount](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + spawnedEntitiesCount += entities.size(); + }; + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_EQ(NumEntities, spawnedEntitiesCount); + } + + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_SpawnTheSameEntity_AllEntitiesSpawned) + { + static constexpr size_t NumEntities = 1; + FillSpawnable(NumEntities); + + AZStd::vector indices = { 0, 0 }; + + size_t spawnedEntitiesCount = 0; + auto callback = + [&spawnedEntitiesCount](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + spawnedEntitiesCount += entities.size(); + }; + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_EQ(NumEntities * 2, spawnedEntitiesCount); + } + + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_MultipleSpawns_AllEntitiesSpawned) + { + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + + AZStd::vector indices = { 0, 2, 3, 1 }; + + size_t spawnedEntitiesCount = 0; + auto callback = + [&spawnedEntitiesCount](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + spawnedEntitiesCount += entities.size(); + }; + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnEntities(*m_ticket, indices, optionalArgs); + m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_EQ(NumEntities * 2, spawnedEntitiesCount); + } + + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_ReferencesAreRemappedForNewBatch_AllPointToLatestParent) + { + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + CreateSingleParent(); + + AZStd::vector indices = { 0, 1, 2, 3 }; + AZStd::vector parents; + + auto callback = [&parents](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + AZ::EntityId parent = (*entities.begin())->GetId(); + parents.push_back(parent); + auto it = entities.begin(); + ++it; // Skip the first as that is the parent. + for (; it != entities.end(); ++it) + { + AZ::TransformInterface* transform = (*it)->GetTransform(); + ASSERT_NE(nullptr, transform); + ASSERT_EQ(parent, transform->GetParentId()); + } + }; + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + optionalArgs.m_referencePreviouslySpawnedEntities = false; + m_manager->SpawnEntities(*m_ticket, indices, optionalArgs); + m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_NE(parents[0], parents[1]); + } + + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_ReferencesAreRemappedForContinuedBatch_AllPointToLatestParent) + { + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + CreateSingleParent(); + + AZStd::vector indices = { 0, 1, 2, 3 }; + AZStd::vector parents; + + auto callback = + [&parents](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + AZ::EntityId parent = (*entities.begin())->GetId(); + parents.push_back(parent); + auto it = entities.begin(); + ++it; // Skip the first as that is the parent. + for (; it!=entities.end(); ++it) + { + AZ::TransformInterface* transform = (*it)->GetTransform(); + ASSERT_NE(nullptr, transform); + ASSERT_EQ(parent, transform->GetParentId()); + } + }; + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + optionalArgs.m_referencePreviouslySpawnedEntities = true; + m_manager->SpawnEntities(*m_ticket, indices, optionalArgs); + m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_NE(parents[0], parents[1]); + } + + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_ReferencesAreRemappedAcrossBatches_AllPointToLatestParent) + { + FillSpawnable(4); + CreateSingleParent(); + + // Spawn a regular batch but with two parents and store the id of the last entity. This will the parent for the next batch. + AZ::EntityId parent; + auto getParent = [&parent](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + ASSERT_NE(entities.begin(), entities.end()); + parent = (*AZStd::prev(entities.end()))->GetId(); + }; + + AzFramework::SpawnEntitiesOptionalArgs optionalArgsFirstBatch; + optionalArgsFirstBatch.m_completionCallback = AZStd::move(getParent); + optionalArgsFirstBatch.m_referencePreviouslySpawnedEntities = true; + m_manager->SpawnEntities(*m_ticket, {0, 1, 2, 3, 0}, AZStd::move(optionalArgsFirstBatch)); + + // Next, spawn all the entities that have a reference to the parent that was just stored. + auto parentCheck = [&parent](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + for (auto& it : entities) + { + AZ::TransformInterface* transform = it->GetTransform(); + ASSERT_NE(nullptr, transform); + ASSERT_EQ(parent, transform->GetParentId()); + } + }; + AzFramework::SpawnEntitiesOptionalArgs optionalArgsSecondBatch; + optionalArgsSecondBatch.m_completionCallback = AZStd::move(parentCheck); + optionalArgsSecondBatch.m_referencePreviouslySpawnedEntities = true; + m_manager->SpawnEntities(*m_ticket, {1, 2, 3}, AZStd::move(optionalArgsSecondBatch)); + + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + } + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_DeleteTicketBeforeCall_NoCrash) { { AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); - m_manager->SpawnEntities(ticket, AzFramework::SpawnablePriority_Default, {}); + m_manager->SpawnEntities(ticket, {/* Deliberate empty list of indices. */}); } m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } @@ -142,7 +381,7 @@ namespace UnitTest { { AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); - m_manager->DespawnAllEntities(ticket, AzFramework::SpawnablePriority_Default); + m_manager->DespawnAllEntities(ticket); } m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } @@ -156,7 +395,7 @@ namespace UnitTest { { AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); - m_manager->ReloadSpawnable(ticket, AzFramework::SpawnablePriority_Default, *m_spawnableAsset); + m_manager->ReloadSpawnable(ticket, *m_spawnableAsset); } m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } @@ -183,8 +422,8 @@ namespace UnitTest spawnedEntitiesCount += entities.size(); }; - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default); - m_manager->ListEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); + m_manager->SpawnAllEntities(*m_ticket); + m_manager->ListEntities(*m_ticket, AZStd::move(callback)); m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); EXPECT_TRUE(allValidEntityIds); @@ -197,7 +436,7 @@ namespace UnitTest { AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); - m_manager->ListEntities(ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); + m_manager->ListEntities(ticket, AZStd::move(callback)); } m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } @@ -228,8 +467,8 @@ namespace UnitTest } }; - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default); - m_manager->ListIndicesAndEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); + m_manager->SpawnAllEntities(*m_ticket); + m_manager->ListIndicesAndEntities(*m_ticket, AZStd::move(callback)); m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); EXPECT_TRUE(allValidEntityIds); @@ -242,7 +481,7 @@ namespace UnitTest { AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); - m_manager->ListIndicesAndEntities(ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); + m_manager->ListIndicesAndEntities(ticket, AZStd::move(callback)); } m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } @@ -258,7 +497,7 @@ namespace UnitTest { AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); - m_manager->ClaimEntities(ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); + m_manager->ClaimEntities(ticket, AZStd::move(callback)); } m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } @@ -274,7 +513,7 @@ namespace UnitTest { AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); - m_manager->Barrier(ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); + m_manager->Barrier(ticket, AZStd::move(callback)); } m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } @@ -305,8 +544,16 @@ namespace UnitTest defaultPriorityCallId = callCounter++; }; - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, {}, AZStd::move(defaultCallback)); - m_manager->SpawnAllEntities(highPriorityTicket, AzFramework::SpawnablePriority_High, {}, AZStd::move(highCallback)); + AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(defaultCallback); + optionalArgs.m_priority = AzFramework::SpawnablePriority_Default; + m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs)); + + AzFramework::SpawnAllEntitiesOptionalArgs highPriortyOptionalArgs; + highPriortyOptionalArgs.m_completionCallback = AZStd::move(highCallback); + highPriortyOptionalArgs.m_priority = AzFramework::SpawnablePriority_High; + m_manager->SpawnAllEntities(highPriorityTicket, AZStd::move(highPriortyOptionalArgs)); + m_manager->ProcessQueue( AzFramework::SpawnableEntitiesManager::CommandQueuePriority::High | AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); @@ -333,8 +580,16 @@ namespace UnitTest defaultPriorityCallId = callCounter++; }; - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, {}, AZStd::move(defaultCallback)); - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_High, {}, AZStd::move(highCallback)); + AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(defaultCallback); + optionalArgs.m_priority = AzFramework::SpawnablePriority_Default; + m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs)); + + AzFramework::SpawnAllEntitiesOptionalArgs highPriortyOptionalArgs; + highPriortyOptionalArgs.m_completionCallback = AZStd::move(highCallback); + highPriortyOptionalArgs.m_priority = AzFramework::SpawnablePriority_High; + m_manager->SpawnAllEntities(*m_ticket, AZStd::move(highPriortyOptionalArgs)); + m_manager->ProcessQueue( AzFramework::SpawnableEntitiesManager::CommandQueuePriority::High | AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); diff --git a/Code/Sandbox/Editor/CMakeLists.txt b/Code/Sandbox/Editor/CMakeLists.txt index 01b58e3f77..58d5e86a2f 100644 --- a/Code/Sandbox/Editor/CMakeLists.txt +++ b/Code/Sandbox/Editor/CMakeLists.txt @@ -191,8 +191,8 @@ ly_add_translations( ) ly_add_dependencies(Editor AssetProcessor) -if(LY_FIRST_PROJECT_PATH) - set_property(TARGET Editor APPEND PROPERTY VS_DEBUGGER_COMMAND_ARGUMENTS "--project-path=\"${LY_FIRST_PROJECT_PATH}\"") +if(LY_DEFAULT_PROJECT_PATH) + set_property(TARGET Editor APPEND PROPERTY VS_DEBUGGER_COMMAND_ARGUMENTS "--project-path=\"${LY_DEFAULT_PROJECT_PATH}\"") endif() ################################################################################ diff --git a/Code/Sandbox/Editor/EditorPreferencesDialog.cpp b/Code/Sandbox/Editor/EditorPreferencesDialog.cpp index e5bcfd6bad..679c73e7df 100644 --- a/Code/Sandbox/Editor/EditorPreferencesDialog.cpp +++ b/Code/Sandbox/Editor/EditorPreferencesDialog.cpp @@ -35,6 +35,7 @@ #include "EditorPreferencesPageViewportMovement.h" #include "EditorPreferencesPageViewportDebug.h" #include "EditorPreferencesPageExperimentalLighting.h" +#include "EditorPreferencesPageAWS.h" #include "LyViewPaneNames.h" AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING @@ -72,6 +73,7 @@ EditorPreferencesDialog::EditorPreferencesDialog(QWidget* pParent) CEditorPreferencesPage_ViewportMovement::Reflect(*serializeContext); CEditorPreferencesPage_ViewportDebug::Reflect(*serializeContext); CEditorPreferencesPage_ExperimentalLighting::Reflect(*serializeContext); + CEditorPreferencesPage_AWS::Reflect(*serializeContext); } } diff --git a/Code/Sandbox/Editor/EditorPreferencesPageAWS.cpp b/Code/Sandbox/Editor/EditorPreferencesPageAWS.cpp new file mode 100644 index 0000000000..edaf813cd8 --- /dev/null +++ b/Code/Sandbox/Editor/EditorPreferencesPageAWS.cpp @@ -0,0 +1,151 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#include "EditorDefs.h" + +#include "EditorPreferencesPageAWS.h" + +// AzCore +#include +#include +#include + +void CEditorPreferencesPage_AWS::Reflect(AZ::SerializeContext& serialize) +{ + serialize.Class() + ->Version(1) + ->Field("AWSAttributionEnabled", &UsageOptions::m_awsAttributionEnabled); + + serialize.Class() + ->Version(1) + ->Field("UsageOptions", &CEditorPreferencesPage_AWS::m_usageOptions); + + AZ::EditContext* editContext = serialize.GetEditContext(); + if (editContext) + { + editContext->Class("Options", "") + ->DataElement(AZ::Edit::UIHandlers::CheckBox, &UsageOptions::m_awsAttributionEnabled, "Send Metrics usage to AWS", + "Reports Gem usage to AWS on Editor launch"); + + editContext->Class("AWS Preferences", "AWS Preferences") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ_CRC("PropertyVisibility_ShowChildrenOnly", 0xef428f20)) + ->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_AWS::m_usageOptions, "AWS Usage Data", "AWS Usage Options"); + } +} + + +CEditorPreferencesPage_AWS::CEditorPreferencesPage_AWS() +{ + m_settingsRegistry = AZStd::make_unique(); + InitializeSettings(); + + // TODO Update with AWS svg. + m_icon = QIcon(":/res/AWS_preferences_icon.svg"); +} + +CEditorPreferencesPage_AWS::~CEditorPreferencesPage_AWS() +{ + m_settingsRegistry.reset(); +} + +const char* CEditorPreferencesPage_AWS::GetTitle() +{ + return "AWS"; +} + +QIcon& CEditorPreferencesPage_AWS::GetIcon() +{ + return m_icon; +} + +void CEditorPreferencesPage_AWS::OnApply() +{ + m_settingsRegistry->Set(AWSAttributionEnabledKey, m_usageOptions.m_awsAttributionEnabled); + SaveSettingsRegistryFile(); +} + +const CEditorPreferencesPage_AWS::UsageOptions& CEditorPreferencesPage_AWS::GetUsageOptions() +{ + return m_usageOptions; +} + +void CEditorPreferencesPage_AWS::SaveSettingsRegistryFile() +{ + AZ::Job* job = AZ::CreateJobFunction( + [this]() + { + AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); + AZ_Assert(fileIO, "File IO is not initialized."); + + // Resolve path to editor_aws_preferences.setreg + AZStd::string editorPreferencesFilePath = + AZStd::string::format("@user@/%s/%s", AZ::SettingsRegistryInterface::RegistryFolder, EditorAWSPreferencesFileName); + AZStd::array resolvedPath{}; + fileIO->ResolvePath(editorPreferencesFilePath.c_str(), resolvedPath.data(), resolvedPath.size()); + + AZ::SettingsRegistryMergeUtils::DumperSettings dumperSettings; + dumperSettings.m_prettifyOutput = true; + dumperSettings.m_jsonPointerPrefix = AWSAttributionSettingsPrefixKey; + + AZStd::string stringBuffer; + AZ::IO::ByteContainerStream stringStream(&stringBuffer); + if (!AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream( + *m_settingsRegistry, AWSAttributionSettingsPrefixKey, stringStream, dumperSettings)) + { + AZ_Warning( + "AWSAttributionManager", false, R"(Unable to save changes to the Editor AWS Preferences registry file at "%s"\n)", + resolvedPath.data()); + return; + } + + bool saved{}; + constexpr auto configurationMode = + AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY; + if (AZ::IO::SystemFile outputFile; outputFile.Open(resolvedPath.data(), configurationMode)) + { + saved = outputFile.Write(stringBuffer.data(), stringBuffer.size()) == stringBuffer.size(); + } + + AZ_Warning( + "AWSAttributionManager", saved, R"(Unable to save Editor AWS Preferences registry file to path "%s"\n)", + editorPreferencesFilePath.c_str()); + }, + true); + job->Start(); +} + +void CEditorPreferencesPage_AWS::InitializeSettings() +{ + AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); + AZ_Assert(fileIO, "File IO is not initialized."); + + // Resolve path to editor_aws_preferences.setreg + AZStd::string editorAWSPreferencesFilePath = + AZStd::string::format("@user@/%s/%s", AZ::SettingsRegistryInterface::RegistryFolder, EditorAWSPreferencesFileName); + AZStd::array resolvedPathAWSPreference{}; + if (!fileIO->ResolvePath(editorAWSPreferencesFilePath.c_str(), resolvedPathAWSPreference.data(), resolvedPathAWSPreference.size())) + { + AZ_Warning("AWSAttributionManager", false, "Error resolving path %s", resolvedPathAWSPreference.data()); + return; + } + + if (fileIO->Exists(resolvedPathAWSPreference.data())) + { + m_settingsRegistry->MergeSettingsFile(resolvedPathAWSPreference.data(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, ""); + } + + if (!m_settingsRegistry->Get(m_usageOptions.m_awsAttributionEnabled, AWSAttributionEnabledKey)) + { + // If key is missing default to on. + m_usageOptions.m_awsAttributionEnabled = true; + } +} diff --git a/Code/Sandbox/Editor/EditorPreferencesPageAWS.h b/Code/Sandbox/Editor/EditorPreferencesPageAWS.h new file mode 100644 index 0000000000..b0dc7ee740 --- /dev/null +++ b/Code/Sandbox/Editor/EditorPreferencesPageAWS.h @@ -0,0 +1,60 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include "Include/IPreferencesPage.h" +#include +#include +#include +#include + +class CEditorPreferencesPage_AWS + : public IPreferencesPage +{ +public: + AZ_RTTI(CEditorPreferencesPage_AWS, "{51FB9557-ABA3-4FD7-803A-1784F5B06F5F}", IPreferencesPage) + + static void Reflect(AZ::SerializeContext& serialize); + + CEditorPreferencesPage_AWS(); + virtual ~CEditorPreferencesPage_AWS(); + + // IPreferencesPage interface methods. + virtual const char* GetCategory() override { return "AWS"; } + virtual const char* GetTitle() override; + virtual QIcon& GetIcon() override; + virtual void OnApply() override; + virtual void OnCancel() override {} + virtual bool OnQueryCancel() override { return true; } + +protected: + struct UsageOptions + { + AZ_TYPE_INFO(UsageOptions, "{2B7D9B19-D13B-4E54-B724-B2FD8D0828B3}") + + bool m_awsAttributionEnabled; + }; + + const UsageOptions& GetUsageOptions(); + +private: + void InitializeSettings(); + void SaveSettingsRegistryFile(); + UsageOptions m_usageOptions; + QIcon m_icon; + AZStd::unique_ptr m_settingsRegistry; + + static constexpr char AWSAttributionEnabledKey[] = "/Amazon/AWS/Preferences/AWSAttributionEnabled"; + static constexpr char EditorPreferencesFileName[] = "editorpreferences.setreg"; + static constexpr char EditorAWSPreferencesFileName[] = "editor_aws_preferences.setreg"; + static constexpr char AWSAttributionSettingsPrefixKey[] = "/Amazon/AWS/Preferences"; +}; diff --git a/Code/Sandbox/Editor/MainWindow.qrc b/Code/Sandbox/Editor/MainWindow.qrc index 476159fbfd..fd207ebe13 100644 --- a/Code/Sandbox/Editor/MainWindow.qrc +++ b/Code/Sandbox/Editor/MainWindow.qrc @@ -143,6 +143,7 @@ res/Camera.svg res/Debug.svg res/Experimental.svg + res/AWS_preferences_icon.svg res/Files.svg res/Gizmos.svg res/Global.svg diff --git a/Code/Sandbox/Editor/PreferencesStdPages.cpp b/Code/Sandbox/Editor/PreferencesStdPages.cpp index 032b01e353..3a66d6e3f4 100644 --- a/Code/Sandbox/Editor/PreferencesStdPages.cpp +++ b/Code/Sandbox/Editor/PreferencesStdPages.cpp @@ -15,6 +15,8 @@ #include "PreferencesStdPages.h" +#include + // Editor #include "EditorPreferencesPageGeneral.h" #include "EditorPreferencesPageFiles.h" @@ -23,6 +25,7 @@ #include "EditorPreferencesPageViewportMovement.h" #include "EditorPreferencesPageViewportDebug.h" #include "EditorPreferencesPageExperimentalLighting.h" +#include "EditorPreferencesPageAWS.h" ////////////////////////////////////////////////////////////////////////// @@ -42,6 +45,11 @@ CStdPreferencesClassDesc::CStdPreferencesClassDesc() }; m_pageCreators.push_back([]() { return new CEditorPreferencesPage_ExperimentalLighting(); }); + + if (AzToolsFramework::IsComponentWithServiceRegistered(AZ_CRC_CE("AWSCoreEditorService"))) + { + m_pageCreators.push_back([]() { return new CEditorPreferencesPage_AWS(); }); + } } HRESULT CStdPreferencesClassDesc::QueryInterface(const IID& riid, void** ppvObj) diff --git a/Code/Sandbox/Editor/editor_lib_files.cmake b/Code/Sandbox/Editor/editor_lib_files.cmake index e1cf18df55..ebd7f89cfb 100644 --- a/Code/Sandbox/Editor/editor_lib_files.cmake +++ b/Code/Sandbox/Editor/editor_lib_files.cmake @@ -586,6 +586,8 @@ set(FILES EditorPreferencesPageViewportDebug.cpp EditorPreferencesPageExperimentalLighting.h EditorPreferencesPageExperimentalLighting.cpp + EditorPreferencesPageAWS.h + EditorPreferencesPageAWS.cpp EditorPreferencesDialog.h EditorPreferencesDialog.cpp EditorPreferencesDialog.ui diff --git a/Code/Sandbox/Editor/res/AWS_preferences_icon.svg b/Code/Sandbox/Editor/res/AWS_preferences_icon.svg new file mode 100644 index 0000000000..e2a86cf162 --- /dev/null +++ b/Code/Sandbox/Editor/res/AWS_preferences_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/Code/Tools/AssetProcessor/CMakeLists.txt b/Code/Tools/AssetProcessor/CMakeLists.txt index 6c12ab6024..2db888218b 100644 --- a/Code/Tools/AssetProcessor/CMakeLists.txt +++ b/Code/Tools/AssetProcessor/CMakeLists.txt @@ -125,8 +125,8 @@ ly_add_target( AZ::AssetProcessorBatch.Static ) -if(LY_FIRST_PROJECT_PATH) - set_property(TARGET AssetProcessor AssetProcessorBatch APPEND PROPERTY VS_DEBUGGER_COMMAND_ARGUMENTS "--project-path=\"${LY_FIRST_PROJECT_PATH}\"") +if(LY_DEFAULT_PROJECT_PATH) + set_property(TARGET AssetProcessor AssetProcessorBatch APPEND PROPERTY VS_DEBUGGER_COMMAND_ARGUMENTS "--project-path=\"${LY_DEFAULT_PROJECT_PATH}\"") endif() # Adds the AssetProcessorBatch target as a C preprocessor define so that it can be used as a Settings Registry diff --git a/Code/Tools/ProjectManager/Resources/ArrowBack_Hover.svg b/Code/Tools/ProjectManager/Resources/ArrowBack_Hover.svg new file mode 100644 index 0000000000..5b3b14f09a --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/ArrowBack_Hover.svg @@ -0,0 +1,3 @@ + + + diff --git a/Code/Tools/ProjectManager/Resources/CarrotArrowDown.svg b/Code/Tools/ProjectManager/Resources/CarrotArrowDown.svg new file mode 100644 index 0000000000..73545968d5 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/CarrotArrowDown.svg @@ -0,0 +1,3 @@ + + + diff --git a/Code/Tools/ProjectManager/Resources/DefaultProjectImage.png b/Code/Tools/ProjectManager/Resources/DefaultProjectImage.png index cc1eda5bb8..a3e13481c9 100644 --- a/Code/Tools/ProjectManager/Resources/DefaultProjectImage.png +++ b/Code/Tools/ProjectManager/Resources/DefaultProjectImage.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f82f22df64b93d4bec91e56b60efa3d5ce2915ce388a2dc627f1ab720678e3d5 -size 334987 +oid sha256:4a5881b8d6cfbc4ceefb14ab96844484fe19407ee030824768f9fcce2f729d35 +size 2949 diff --git a/Code/Tools/ProjectManager/Resources/DefaultTemplate.png b/Code/Tools/ProjectManager/Resources/DefaultTemplate.png new file mode 100644 index 0000000000..2634c383fc --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/DefaultTemplate.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8358f4dad9878c662b9819b2b346622af691eb45f8eddc28fff79a50650ae6cf +size 2503 diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc index 04d5e98a10..33acfa9e1b 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc @@ -7,6 +7,7 @@ AddOffset.svg AddOffset_Hover.svg ArrowBack.svg + ArrowBack_Hover.svg build.svg FolderOffset.svg FolderOffset_Hover.svg @@ -18,6 +19,7 @@ Linux.svg macOS.svg DefaultProjectImage.png + DefaultTemplate.png ArrowDownLine.svg ArrowUpLine.svg o3de.svg @@ -26,5 +28,8 @@ Backgrounds/FirstTimeBackgroundImage.jpg ArrowDownLine.svg ArrowUpLine.svg + CarrotArrowDown.svg + Summary.svg + WindowClose.svg diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index 224574f522..c18d61fc24 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -115,7 +115,7 @@ QTabBar::tab:pressed /************** General (Modal windows) **************/ #header { - background-color:#111111; + background-color:#111111; min-height:80px; max-height:80px; } @@ -136,11 +136,9 @@ QTabBar::tab:pressed #header QPushButton:focus { border:none; } -#header QPushButton:hover { - background:#333333 url(:/ArrowBack.svg) no-repeat center; -} +#header QPushButton:hover, #header QPushButton:pressed { - background:#222222 url(:/ArrowBack.svg) no-repeat center; + background:transparent url(:/ArrowBack_Hover.svg) no-repeat center; } #headerTitle { @@ -172,8 +170,8 @@ QTabBar::tab:pressed #footer > QPushButton { qproperty-flat: true; - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #0095f2, stop: 1.0 #1e70eb); + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #0095f2, stop: 1.0 #1e70eb); border-radius: 3px; min-height: 28px; max-height: 28px; @@ -181,26 +179,26 @@ QTabBar::tab:pressed margin-right:30px; } #footer > QPushButton:hover { - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #10A5f2, stop: 1.0 #2e80eb); + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #10A5f2, stop: 1.0 #2e80eb); } #footer > QPushButton:pressed { - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #0085e2, stop: 1.0 #0e60db); + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #0085e2, stop: 1.0 #0e60db); } #footer > QPushButton[secondary="true"] { margin-right: 10px; - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #888888, stop: 1.0 #555555); + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #888888, stop: 1.0 #555555); } #footer > QPushButton[secondary="true"]:hover { - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #999999, stop: 1.0 #666666); + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #999999, stop: 1.0 #666666); } #footer > QPushButton[secondary="true"]:pressed { - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #555555, stop: 1.0 #777777); + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #555555, stop: 1.0 #777777); } /************** Project Settings **************/ @@ -210,9 +208,6 @@ QTabBar::tab:pressed #projectTemplate { margin: 55px 0 0 50px; - max-width: 780px; - min-height:200px; - max-height:200px; } #projectTemplateLabel { font-size:16px; @@ -227,11 +222,67 @@ QTabBar::tab:pressed #projectTemplateDetails { background-color:#444444; - max-width:240px; + max-width:20%; min-width:240px; margin-left:30px; } +#projectTemplateDetails #displayName, +#projectTemplateDetails #includedGemsTitle { + font-size:18px; +} + +#projectTemplateDetails #moreGems { + font-size:14px; + margin-top:20px; +} + +#projectTemplateDetails #includedGemsTitle { + margin-top:5px; + margin-bottom:5px; +} + +#projectTemplateDetails #summary { + padding-bottom:0px; + border-bottom:2px solid #555555; + min-height:80px; + qproperty-alignment: AlignTop; +} + +#projectTemplateDetails #browseCatalog { + margin:5px 0px 15px 0px; +} + +#projectTemplate QPushButton { + qproperty-flat: true; + min-width: 96px; + max-width: 96px; + min-height: 160px; + max-height: 160px; +} +#projectTemplate #templateLabel { + qproperty-alignment: AlignCenter; +} +#projectTemplate QPushButton #templateImage { + border:3px solid transparent; + border-radius: 4px; +} +#projectTemplate QPushButton[Checked="true"] #templateImage { + border:3px solid #1e70eb; +} +#projectTemplate QPushButton[Checked="true"] #templateLabel { + font-weight:bold; +} +#projectTemplate QPushButton:hover { + background-color: #444444; +} +#projectTemplate QPushButton:focus { + outline: none; + border:none; +} + + + #projectSettingsTab::tab-bar { left: 60px; } @@ -311,6 +362,7 @@ QTabBar::tab:pressed #projectButton > #labelButton { border:1px solid white; } + #projectButton > #labelButton:hover, #projectButton > #labelButton:pressed { border:1px solid #1e70eb; @@ -350,12 +402,38 @@ QTabBar::tab:pressed max-height:278px; } +QProgressBar { + border: none; + background-color: transparent; + padding: 0px; + min-height: 14px; + font-size: 2px; +} + +QProgressBar::chunk { + background-color: #1E70EB; +} + /************** Gem Catalog **************/ #GemCatalogTitle { font-size: 18px; } +#GemCatalogCart { + background-color: #555555; +} + +#GemCatalogCartCountLabel { + font-size: 12px; + background-color: #4285F4; + border-radius: 3px; +} + +#GemCatalogCartOverlaySectionLabel { + font-weight: 600; +} + /************** Gem Catalog (Inspector) **************/ #GemCatalogInspector { diff --git a/Code/Tools/ProjectManager/Resources/Summary.svg b/Code/Tools/ProjectManager/Resources/Summary.svg new file mode 100644 index 0000000000..fe26718aff --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/Summary.svg @@ -0,0 +1,3 @@ + + + diff --git a/Code/Tools/ProjectManager/Resources/WindowClose.svg b/Code/Tools/ProjectManager/Resources/WindowClose.svg new file mode 100644 index 0000000000..0485ff95cd --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/WindowClose.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp index 85e34aeced..c8ed3954ac 100644 --- a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -41,24 +42,33 @@ namespace O3DE::ProjectManager m_stack = new QStackedWidget(this); m_stack->setObjectName("body"); - m_stack->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding)); - m_stack->addWidget(new NewProjectSettingsScreen()); - m_gemCatalog = new GemCatalogScreen(); - m_stack->addWidget(m_gemCatalog); + m_stack->setSizePolicy(QSizePolicy(QSizePolicy::Preferred,QSizePolicy::Expanding)); + + m_newProjectSettingsScreen = new NewProjectSettingsScreen(this); + m_stack->addWidget(m_newProjectSettingsScreen); + + m_gemCatalogScreen = new GemCatalogScreen(this); + m_stack->addWidget(m_gemCatalogScreen); vLayout->addWidget(m_stack); - QDialogButtonBox* backNextButtons = new QDialogButtonBox(); - backNextButtons->setObjectName("footer"); - vLayout->addWidget(backNextButtons); + QDialogButtonBox* buttons = new QDialogButtonBox(); + buttons->setObjectName("footer"); + vLayout->addWidget(buttons); - m_backButton = backNextButtons->addButton(tr("Back"), QDialogButtonBox::RejectRole); - m_backButton->setProperty("secondary", true); - m_nextButton = backNextButtons->addButton(tr("Next"), QDialogButtonBox::ApplyRole); +#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED + connect(m_newProjectSettingsScreen, &ScreenWidget::ChangeScreenRequest, this, &CreateProjectCtrl::OnChangeScreenRequest); - connect(m_backButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandleBackButton); - connect(m_nextButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandleNextButton); + m_secondaryButton = buttons->addButton(tr("Back"), QDialogButtonBox::RejectRole); + m_secondaryButton->setProperty("secondary", true); + m_secondaryButton->setVisible(false); + connect(m_secondaryButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandleSecondaryButton); Update(); +#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED + + m_primaryButton = buttons->addButton(tr("Create Project"), QDialogButtonBox::ApplyRole); + connect(m_primaryButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandlePrimaryButton); + setLayout(vLayout); } @@ -80,8 +90,10 @@ namespace O3DE::ProjectManager { if (m_stack->currentIndex() > 0) { - m_stack->setCurrentIndex(m_stack->currentIndex() - 1); - Update(); +#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED + PreviousScreen(); +#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED + } else { @@ -89,70 +101,123 @@ namespace O3DE::ProjectManager } } - void CreateProjectCtrl::HandleNextButton() +#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED + void CreateProjectCtrl::HandleSecondaryButton() { - ScreenWidget* currentScreen = reinterpret_cast(m_stack->currentWidget()); - ProjectManagerScreen screenEnum = currentScreen->GetScreenEnum(); - - if (screenEnum == ProjectManagerScreen::NewProjectSettings) + if (m_stack->currentIndex() > 0) { - auto newProjectScreen = reinterpret_cast(currentScreen); - if (newProjectScreen) - { - if (!newProjectScreen->Validate()) - { - QMessageBox::critical(this, tr("Invalid project settings"), tr("Invalid project settings")); - return; - } - - m_projectInfo = newProjectScreen->GetProjectInfo(); - m_projectTemplatePath = newProjectScreen->GetProjectTemplatePath(); - - // The next page is the gem catalog. Gather the available gems that will be shown in the gem catalog. - m_gemCatalog->ReinitForProject(m_projectInfo.m_path, /*isNewProject=*/true); - } - } - - if (m_stack->currentIndex() != m_stack->count() - 1) - { - m_stack->setCurrentIndex(m_stack->currentIndex() + 1); - Update(); + // return to Project Settings page + PreviousScreen(); } else { - auto result = PythonBindingsInterface::Get()->CreateProject(m_projectTemplatePath, m_projectInfo); + // Configure Gems + NextScreen(); + } + } + + void CreateProjectCtrl::Update() + { + if (m_stack->currentWidget() == m_gemCatalogScreen) + { + m_header->setSubTitle(tr("Configure project with Gems")); + m_secondaryButton->setVisible(false); + } + else + { + m_header->setSubTitle(tr("Enter Project Details")); + m_secondaryButton->setVisible(true); + m_secondaryButton->setText(tr("Configure Gems")); + } + } + + void CreateProjectCtrl::OnChangeScreenRequest(ProjectManagerScreen screen) + { + if (screen == ProjectManagerScreen::GemCatalog) + { + HandleSecondaryButton(); + } + else + { + emit ChangeScreenRequest(screen); + } + } + + void CreateProjectCtrl::NextScreen() + { + if (m_stack->currentIndex() < m_stack->count()) + { + if(CurrentScreenIsValid()) + { + m_stack->setCurrentIndex(m_stack->currentIndex() + 1); + + QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath(); + m_gemCatalogScreen->ReinitForProject(projectTemplatePath + "/Template", /*isNewProject=*/true); + + Update(); + } + else + { + QMessageBox::warning(this, tr("Invalid project settings"), tr("Please correct the indicated project settings and try again.")); + } + } + } + + void CreateProjectCtrl::PreviousScreen() + { + // we don't require the current screen to be valid when moving back + if (m_stack->currentIndex() > 0) + { + m_stack->setCurrentIndex(m_stack->currentIndex() - 1); + Update(); + } + } +#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED + + void CreateProjectCtrl::HandlePrimaryButton() + { + CreateProject(); + } + + bool CreateProjectCtrl::CurrentScreenIsValid() + { + if (m_stack->currentWidget() == m_newProjectSettingsScreen) + { + return m_newProjectSettingsScreen->Validate(); + } + + return true; + } + + void CreateProjectCtrl::CreateProject() + { + if (m_newProjectSettingsScreen->Validate()) + { + ProjectInfo projectInfo = m_newProjectSettingsScreen->GetProjectInfo(); + QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath(); + + auto result = PythonBindingsInterface::Get()->CreateProject(projectTemplatePath, projectInfo); if (result.IsSuccess()) { // automatically register the project - PythonBindingsInterface::Get()->AddProject(m_projectInfo.m_path); + PythonBindingsInterface::Get()->AddProject(projectInfo.m_path); - // adding gems is not implemented yet because we don't know what targets to add or how to add them +#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED + m_gemCatalogScreen->EnableDisableGemsForProject(projectInfo.m_path); +#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED + + projectInfo.m_needsBuild = true; + emit NotifyBuildProject(projectInfo); emit ChangeScreenRequest(ProjectManagerScreen::Projects); } else { QMessageBox::critical(this, tr("Project creation failed"), tr("Failed to create project.")); } - - // Enable/disable gems for the newly created project. - m_gemCatalog->EnableDisableGemsForProject(m_projectInfo.m_path); - } - } - - void CreateProjectCtrl::Update() - { - ScreenWidget* currentScreen = reinterpret_cast(m_stack->currentWidget()); - if (currentScreen && currentScreen->GetScreenEnum() == ProjectManagerScreen::GemCatalog) - { - m_header->setTitle(tr("Create Project")); - m_header->setSubTitle(tr("Configure project with Gems")); - m_nextButton->setText(tr("Create Project")); } else { - m_header->setTitle(tr("Create Project")); - m_header->setSubTitle(tr("Enter Project Details")); - m_nextButton->setText(tr("Next")); + QMessageBox::warning(this, tr("Invalid project settings"), tr("Please correct the indicated project settings and try again.")); } } diff --git a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h index 89d18a9ebc..e802b6845a 100644 --- a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h +++ b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h @@ -14,9 +14,11 @@ #if !defined(Q_MOC_RUN) #include #include -#include #endif +// due to current limitations, customizing template Gems is disabled +#define TEMPLATE_GEM_CONFIGURATION_ENABLED + QT_FORWARD_DECLARE_CLASS(QStackedWidget) QT_FORWARD_DECLARE_CLASS(QPushButton) QT_FORWARD_DECLARE_CLASS(QLabel) @@ -24,6 +26,8 @@ QT_FORWARD_DECLARE_CLASS(QLabel) namespace O3DE::ProjectManager { QT_FORWARD_DECLARE_CLASS(ScreenHeader) + QT_FORWARD_DECLARE_CLASS(NewProjectSettingsScreen) + QT_FORWARD_DECLARE_CLASS(GemCatalogScreen) class CreateProjectCtrl : public ScreenWidget @@ -36,21 +40,37 @@ namespace O3DE::ProjectManager protected slots: void HandleBackButton(); - void HandleNextButton(); + void HandlePrimaryButton(); + +#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED + void OnChangeScreenRequest(ProjectManagerScreen screen); + void HandleSecondaryButton(); +#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED private: +#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED void Update(); + void NextScreen(); + void PreviousScreen(); +#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED - QStackedWidget* m_stack; - ScreenHeader* m_header; + bool CurrentScreenIsValid(); + void CreateProject(); - QPushButton* m_backButton; - QPushButton* m_nextButton; + QStackedWidget* m_stack = nullptr; + ScreenHeader* m_header = nullptr; + + QPushButton* m_primaryButton = nullptr; + +#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED + QPushButton* m_secondaryButton = nullptr; +#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED QString m_projectTemplatePath; ProjectInfo m_projectInfo; - - GemCatalogScreen* m_gemCatalog = nullptr; + + NewProjectSettingsScreen* m_newProjectSettingsScreen = nullptr; + GemCatalogScreen* m_gemCatalogScreen = nullptr; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp index 6402121e4a..a98135d3e6 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp @@ -10,24 +10,229 @@ * */ -#include #include #include +#include #include +#include +#include namespace O3DE::ProjectManager { - GemCatalogHeaderWidget::GemCatalogHeaderWidget(GemSortFilterProxyModel* filterProxyModel, QWidget* parent) + CartOverlayWidget::CartOverlayWidget(GemModel* gemModel, QWidget* parent) + : QWidget(parent) + , m_gemModel(gemModel) + { + setObjectName("GemCatalogCart"); + + m_layout = new QVBoxLayout(); + m_layout->setSpacing(0); + m_layout->setMargin(0); + m_layout->setAlignment(Qt::AlignTop); + setLayout(m_layout); + + QHBoxLayout* hLayout = new QHBoxLayout(); + + QPushButton* closeButton = new QPushButton(); + closeButton->setFlat(true); + closeButton->setFocusPolicy(Qt::NoFocus); + closeButton->setIcon(QIcon(":/WindowClose.svg")); + connect(closeButton, &QPushButton::clicked, this, [=] + { + deleteLater(); + }); + hLayout->addSpacerItem(new QSpacerItem(10, 0, QSizePolicy::Expanding)); + hLayout->addWidget(closeButton); + m_layout->addLayout(hLayout); + + // enabled + { + m_enabledWidget = new QWidget(); + m_enabledWidget->setFixedWidth(s_width); + m_layout->addWidget(m_enabledWidget); + + QVBoxLayout* layout = new QVBoxLayout(); + layout->setAlignment(Qt::AlignTop); + m_enabledWidget->setLayout(layout); + + m_enabledLabel = new QLabel(); + m_enabledLabel->setObjectName("GemCatalogCartOverlaySectionLabel"); + layout->addWidget(m_enabledLabel); + m_enabledTagContainer = new TagContainerWidget(); + layout->addWidget(m_enabledTagContainer); + } + + // disabled + { + m_disabledWidget = new QWidget(); + m_disabledWidget->setFixedWidth(s_width); + m_layout->addWidget(m_disabledWidget); + + QVBoxLayout* layout = new QVBoxLayout(); + layout->setAlignment(Qt::AlignTop); + m_disabledWidget->setLayout(layout); + + m_disabledLabel = new QLabel(); + m_disabledLabel->setObjectName("GemCatalogCartOverlaySectionLabel"); + layout->addWidget(m_disabledLabel); + m_disabledTagContainer = new TagContainerWidget(); + layout->addWidget(m_disabledTagContainer); + } + + setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog); + + Update(); + connect(gemModel, &GemModel::dataChanged, this, [=] + { + Update(); + }); + } + + void CartOverlayWidget::Update() + { + const QVector toBeAdded = m_gemModel->GatherGemsToBeAdded(); + if (toBeAdded.isEmpty()) + { + m_enabledWidget->hide(); + } + else + { + m_enabledTagContainer->Update(ConvertFromModelIndices(toBeAdded)); + m_enabledLabel->setText(QString("%1 %2").arg(QString::number(toBeAdded.size()), tr("Gems to be enabled"))); + m_enabledWidget->show(); + } + + const QVector toBeRemoved = m_gemModel->GatherGemsToBeRemoved(); + if (toBeRemoved.isEmpty()) + { + m_disabledWidget->hide(); + } + else + { + m_disabledTagContainer->Update(ConvertFromModelIndices(toBeRemoved)); + m_disabledLabel->setText(QString("%1 %2").arg(QString::number(toBeRemoved.size()), tr("Gems to be disabled"))); + m_disabledWidget->show(); + } + } + + QStringList CartOverlayWidget::ConvertFromModelIndices(const QVector& gems) const + { + QStringList gemNames; + gemNames.reserve(gems.size()); + for (const QModelIndex& modelIndex : gems) + { + gemNames.push_back(GemModel::GetName(modelIndex)); + } + return gemNames; + } + + CartButton::CartButton(GemModel* gemModel, QWidget* parent) + : QWidget(parent) + , m_gemModel(gemModel) + { + m_layout = new QHBoxLayout(); + m_layout->setMargin(0); + setLayout(m_layout); + + QPushButton* iconButton = new QPushButton(); + iconButton->setFlat(true); + iconButton->setFocusPolicy(Qt::NoFocus); + iconButton->setIcon(QIcon(":/Summary.svg")); + iconButton->setFixedSize(s_iconSize, s_iconSize); + connect(iconButton, &QPushButton::clicked, this, &CartButton::ShowOverlay); + m_layout->addWidget(iconButton); + + m_countLabel = new QLabel(); + m_countLabel->setObjectName("GemCatalogCartCountLabel"); + m_countLabel->setFixedHeight(s_iconSize - 1); // Compensate for the empty icon space by using a slightly smaller label height. + m_layout->addWidget(m_countLabel); + + m_dropDownButton = new QPushButton(); + m_dropDownButton->setFlat(true); + m_dropDownButton->setFocusPolicy(Qt::NoFocus); + m_dropDownButton->setIcon(QIcon(":/CarrotArrowDown.svg")); + m_dropDownButton->setFixedSize(s_arrowDownIconSize, s_arrowDownIconSize); + connect(m_dropDownButton, &QPushButton::clicked, this, &CartButton::ShowOverlay); + m_layout->addWidget(m_dropDownButton); + + // Adjust the label text whenever the model gets updated. + connect(gemModel, &GemModel::dataChanged, [=] + { + const QVector toBeAdded = m_gemModel->GatherGemsToBeAdded(); + const QVector toBeRemoved = m_gemModel->GatherGemsToBeRemoved(); + + const int count = toBeAdded.size() + toBeRemoved.size(); + m_countLabel->setText(QString::number(count)); + + m_dropDownButton->setVisible(!toBeAdded.isEmpty() || !toBeRemoved.isEmpty()); + + // Automatically close the overlay window in case there are no gems to be enabled or disabled anymore. + if (m_cartOverlay && toBeAdded.isEmpty() && toBeRemoved.isEmpty()) + { + m_cartOverlay->deleteLater(); + m_cartOverlay = nullptr; + } + }); + } + + void CartButton::mousePressEvent([[maybe_unused]] QMouseEvent* event) + { + ShowOverlay(); + } + + void CartButton::ShowOverlay() + { + const QVector toBeAdded = m_gemModel->GatherGemsToBeAdded(); + const QVector toBeRemoved = m_gemModel->GatherGemsToBeRemoved(); + if (toBeAdded.isEmpty() && toBeRemoved.isEmpty()) + { + return; + } + + if (m_cartOverlay) + { + // Directly delete the former overlay before creating the new one. + // Don't use deleteLater() here. This might overwrite the new overlay pointer + // depending on the event queue. + delete m_cartOverlay; + } + + m_cartOverlay = new CartOverlayWidget(m_gemModel, this); + connect(m_cartOverlay, &QWidget::destroyed, this, [=] + { + // Reset the overlay pointer on destruction to prevent dangling pointers. + m_cartOverlay = nullptr; + }); + m_cartOverlay->show(); + + const QPoint parentPos = m_dropDownButton->mapToParent(m_dropDownButton->pos()); + const QPoint globalPos = m_dropDownButton->mapToGlobal(m_dropDownButton->pos()); + const QPoint offset(-4, 10); + m_cartOverlay->setGeometry(globalPos.x() - parentPos.x() - m_cartOverlay->width() + width() + offset.x(), + globalPos.y() + offset.y(), + m_cartOverlay->width(), + m_cartOverlay->height()); + } + + CartButton::~CartButton() + { + // Make sure the overlay window is automatically closed in case the gem catalog is destroyed. + if (m_cartOverlay) + { + m_cartOverlay->deleteLater(); + } + } + + GemCatalogHeaderWidget::GemCatalogHeaderWidget(GemModel* gemModel, GemSortFilterProxyModel* filterProxyModel, QWidget* parent) : QFrame(parent) { QHBoxLayout* hLayout = new QHBoxLayout(); hLayout->setAlignment(Qt::AlignLeft); - hLayout->setMargin(0); + hLayout->setContentsMargins(10, 7, 10, 7); setLayout(hLayout); setObjectName("GemCatalogHeaderWidget"); - - hLayout->addSpacing(7); + setFixedHeight(s_height); QLabel* titleLabel = new QLabel(tr("Gem Catalog")); titleLabel->setObjectName("GemCatalogTitle"); @@ -35,17 +240,23 @@ namespace O3DE::ProjectManager hLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding)); - AzQtComponents::SearchLineEdit* filterLineEdit = new AzQtComponents::SearchLineEdit(); - filterLineEdit->setStyleSheet("background-color: #DDDDDD;"); - connect(filterLineEdit, &QLineEdit::textChanged, this, [=](const QString& text) + m_filterLineEdit = new AzQtComponents::SearchLineEdit(); + m_filterLineEdit->setStyleSheet("background-color: #DDDDDD;"); + connect(m_filterLineEdit, &QLineEdit::textChanged, this, [=](const QString& text) { filterProxyModel->SetSearchString(text); }); - hLayout->addWidget(filterLineEdit); + hLayout->addWidget(m_filterLineEdit); hLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding)); - hLayout->addSpacerItem(new QSpacerItem(140, 0, QSizePolicy::Fixed)); - - setFixedHeight(60); + hLayout->addSpacerItem(new QSpacerItem(75, 0, QSizePolicy::Fixed)); + + CartButton* cartButton = new CartButton(gemModel); + hLayout->addWidget(cartButton); + } + + void GemCatalogHeaderWidget::ReinitForProject() + { + m_filterLineEdit->setText({}); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h index 3e065edd8f..bef7555618 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h @@ -13,19 +13,81 @@ #pragma once #if !defined(Q_MOC_RUN) +#include +#include #include +#include #include +#include +#include +#include +#include #endif namespace O3DE::ProjectManager { + class CartOverlayWidget + : public QWidget + { + Q_OBJECT // AUTOMOC + + public: + CartOverlayWidget(GemModel* gemModel, QWidget* parent = nullptr); + void Update(); + + private: + QStringList ConvertFromModelIndices(const QVector& gems) const; + + QVBoxLayout* m_layout = nullptr; + GemModel* m_gemModel = nullptr; + + QWidget* m_enabledWidget = nullptr; + QLabel* m_enabledLabel = nullptr; + TagContainerWidget* m_enabledTagContainer = nullptr; + + QWidget* m_disabledWidget = nullptr; + QLabel* m_disabledLabel = nullptr; + TagContainerWidget* m_disabledTagContainer = nullptr; + + inline constexpr static int s_width = 240; + }; + + class CartButton + : public QWidget + { + Q_OBJECT // AUTOMOC + + public: + CartButton(GemModel* gemModel, QWidget* parent = nullptr); + ~CartButton(); + void ShowOverlay(); + + private: + void mousePressEvent(QMouseEvent* event) override; + + GemModel* m_gemModel = nullptr; + QHBoxLayout* m_layout = nullptr; + QLabel* m_countLabel = nullptr; + QPushButton* m_dropDownButton = nullptr; + CartOverlayWidget* m_cartOverlay = nullptr; + + inline constexpr static int s_iconSize = 24; + inline constexpr static int s_arrowDownIconSize = 8; + }; + class GemCatalogHeaderWidget : public QFrame { Q_OBJECT // AUTOMOC public: - explicit GemCatalogHeaderWidget(GemSortFilterProxyModel* filterProxyModel, QWidget* parent = nullptr); + explicit GemCatalogHeaderWidget(GemModel* gemModel, GemSortFilterProxyModel* filterProxyModel, QWidget* parent = nullptr); ~GemCatalogHeaderWidget() = default; + + void ReinitForProject(); + + private: + AzQtComponents::SearchLineEdit* m_filterLineEdit = nullptr; + inline constexpr static int s_height = 60; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index aa36c1b0ab..4424767c0b 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include #include @@ -35,8 +34,8 @@ namespace O3DE::ProjectManager vLayout->setSpacing(0); setLayout(vLayout); - GemCatalogHeaderWidget* headerWidget = new GemCatalogHeaderWidget(m_proxModel); - vLayout->addWidget(headerWidget); + m_headerWidget = new GemCatalogHeaderWidget(m_gemModel, m_proxModel); + vLayout->addWidget(m_headerWidget); QHBoxLayout* hLayout = new QHBoxLayout(); hLayout->setMargin(0); @@ -77,10 +76,11 @@ namespace O3DE::ProjectManager m_filterWidget->deleteLater(); } + m_proxModel->ResetFilters(); m_filterWidget = new GemFilterWidget(m_proxModel); m_filterWidgetLayout->addWidget(m_filterWidget); - m_proxModel->InvalidateFilter(); + m_headerWidget->ReinitForProject(); // Select the first entry after everything got correctly sized QTimer::singleShot(200, [=]{ diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h index 0847d9b74e..f5092e837a 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h @@ -14,6 +14,7 @@ #if !defined(Q_MOC_RUN) #include +#include #include #include #include @@ -40,6 +41,7 @@ namespace O3DE::ProjectManager GemListView* m_gemListView = nullptr; GemInspector* m_gemInspector = nullptr; GemModel* m_gemModel = nullptr; + GemCatalogHeaderWidget* m_headerWidget = nullptr; GemSortFilterProxyModel* m_proxModel = nullptr; QVBoxLayout* m_filterWidgetLayout = nullptr; GemFilterWidget* m_filterWidget = nullptr; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp index 0fc0d89fcb..8aa68fb7a2 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp @@ -53,6 +53,7 @@ namespace O3DE::ProjectManager QFont standardFont(options.font); standardFont.setPixelSize(s_fontSize); + QFontMetrics standardFontMetrics(standardFont); painter->save(); painter->setClipping(true); @@ -78,8 +79,10 @@ namespace O3DE::ProjectManager } // Gem name - const QString gemName = GemModel::GetName(modelIndex); + QString gemName = GemModel::GetName(modelIndex); QFont gemNameFont(options.font); + const int firstColumnMaxTextWidth = s_summaryStartX - 30; + gemName = QFontMetrics(gemNameFont).elidedText(gemName, Qt::TextElideMode::ElideRight, firstColumnMaxTextWidth); gemNameFont.setPixelSize(s_gemNameFontSize); gemNameFont.setBold(true); QRect gemNameRect = GetTextRect(gemNameFont, gemName, s_gemNameFontSize); @@ -90,7 +93,8 @@ namespace O3DE::ProjectManager painter->drawText(gemNameRect, Qt::TextSingleLine, gemName); // Gem creator - const QString gemCreator = GemModel::GetCreator(modelIndex); + QString gemCreator = GemModel::GetCreator(modelIndex); + gemCreator = standardFontMetrics.elidedText(gemCreator, Qt::TextElideMode::ElideRight, firstColumnMaxTextWidth); QRect gemCreatorRect = GetTextRect(standardFont, gemCreator, s_fontSize); gemCreatorRect.moveTo(contentRect.left(), contentRect.top() + gemNameRect.height()); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp index 33936f417e..d8f41c077e 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp @@ -130,4 +130,15 @@ namespace O3DE::ProjectManager invalidate(); emit OnInvalidated(); } + + void GemSortFilterProxyModel::ResetFilters() + { + m_searchString.clear(); + m_gemOriginFilter = {}; + m_platformFilter = {}; + m_typeFilter = {}; + m_featureFilter = {}; + + InvalidateFilter(); + } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h index e5554c020c..f24a724ecf 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h @@ -51,6 +51,7 @@ namespace O3DE::ProjectManager void SetFeatures(const QSet& features) { m_featureFilter = features; InvalidateFilter(); } void InvalidateFilter(); + void ResetFilters(); signals: void OnInvalidated(); diff --git a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp index c8dc8451ae..5faa6cb8bd 100644 --- a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp @@ -14,8 +14,12 @@ #include #include #include +#include #include #include +#include +#include +#include #include #include @@ -28,10 +32,12 @@ #include #include #include +#include +#include namespace O3DE::ProjectManager { - constexpr const char* k_pathProperty = "Path"; + constexpr const char* k_templateIndexProperty = "TemplateIndex"; NewProjectSettingsScreen::NewProjectSettingsScreen(QWidget* parent) : ProjectSettingsScreen(parent) @@ -59,30 +65,69 @@ namespace O3DE::ProjectManager projectTemplateDetailsLabel->setObjectName("projectTemplateDetailsLabel"); containerLayout->addWidget(projectTemplateDetailsLabel); - QHBoxLayout* templateLayout = new QHBoxLayout(this); - containerLayout->addItem(templateLayout); + + // we might have enough templates that we need to scroll + QScrollArea* templatesScrollArea = new QScrollArea(this); + QWidget* scrollWidget = new QWidget(); + + FlowLayout* flowLayout = new FlowLayout(0, s_spacerSize, s_spacerSize); + scrollWidget->setLayout(flowLayout); + + templatesScrollArea->setWidget(scrollWidget); + templatesScrollArea->setWidgetResizable(true); m_projectTemplateButtonGroup = new QButtonGroup(this); m_projectTemplateButtonGroup->setObjectName("templateButtonGroup"); + + // QButtonGroup has overloaded buttonClicked methods so we need the QOverload + connect( + m_projectTemplateButtonGroup, QOverload::of(&QButtonGroup::buttonClicked), this, + [=](QAbstractButton* button) + { + if (button && button->property(k_templateIndexProperty).isValid()) + { + int projectIndex = button->property(k_templateIndexProperty).toInt(); + UpdateTemplateDetails(m_templates.at(projectIndex)); + } + }); + auto templatesResult = PythonBindingsInterface::Get()->GetProjectTemplates(); if (templatesResult.IsSuccess() && !templatesResult.GetValue().isEmpty()) { - for (const ProjectTemplateInfo& projectTemplate : templatesResult.GetValue()) - { - QRadioButton* radioButton = new QRadioButton(projectTemplate.m_name, this); - radioButton->setProperty(k_pathProperty, projectTemplate.m_path); - m_projectTemplateButtonGroup->addButton(radioButton); + m_templates = templatesResult.GetValue(); - containerLayout->addWidget(radioButton); + // sort alphabetically by display name because they could be in any order + std::sort(m_templates.begin(), m_templates.end(), [](const ProjectTemplateInfo& arg1, const ProjectTemplateInfo& arg2) + { + return arg1.m_displayName.toLower() < arg2.m_displayName.toLower(); + }); + + for (int index = 0; index < m_templates.size(); ++index) + { + ProjectTemplateInfo projectTemplate = m_templates.at(index); + QString projectPreviewPath = projectTemplate.m_path + "/Template/preview.png"; + QFileInfo doesPreviewExist(projectPreviewPath); + if (!doesPreviewExist.exists() || !doesPreviewExist.isFile()) + { + projectPreviewPath = ":/DefaultTemplate.png"; + } + TemplateButton* templateButton = new TemplateButton(projectPreviewPath, projectTemplate.m_displayName, this); + templateButton->setCheckable(true); + templateButton->setProperty(k_templateIndexProperty, index); + + m_projectTemplateButtonGroup->addButton(templateButton); + + flowLayout->addWidget(templateButton); } m_projectTemplateButtonGroup->buttons().first()->setChecked(true); } + containerLayout->addWidget(templatesScrollArea); } projectTemplateWidget->setLayout(containerLayout); m_verticalLayout->addWidget(projectTemplateWidget); - QWidget* projectTemplateDetails = new QWidget(this); + QFrame* projectTemplateDetails = CreateTemplateDetails(s_templateDetailsContentMargin); projectTemplateDetails->setObjectName("projectTemplateDetails"); m_horizontalLayout->addWidget(projectTemplateDetails); } @@ -109,11 +154,71 @@ namespace O3DE::ProjectManager void NewProjectSettingsScreen::NotifyCurrentScreen() { + if (!m_templates.isEmpty()) + { + UpdateTemplateDetails(m_templates.first()); + } + Validate(); } QString NewProjectSettingsScreen::GetProjectTemplatePath() { - return m_projectTemplateButtonGroup->checkedButton()->property(k_pathProperty).toString(); + const int templateIndex = m_projectTemplateButtonGroup->checkedButton()->property(k_templateIndexProperty).toInt(); + return m_templates.at(templateIndex).m_path; + } + + QFrame* NewProjectSettingsScreen::CreateTemplateDetails(int margin) + { + QFrame* projectTemplateDetails = new QFrame(this); + projectTemplateDetails->setObjectName("projectTemplateDetails"); + QVBoxLayout* templateDetailsLayout = new QVBoxLayout(); + templateDetailsLayout->setContentsMargins(margin, margin, margin, margin); + templateDetailsLayout->setAlignment(Qt::AlignTop); + { + m_templateDisplayName = new QLabel(this); + m_templateDisplayName->setObjectName("displayName"); + templateDetailsLayout->addWidget(m_templateDisplayName); + + m_templateSummary = new QLabel(this); + m_templateSummary->setObjectName("summary"); + m_templateSummary->setWordWrap(true); + templateDetailsLayout->addWidget(m_templateSummary); + + QLabel* includedGemsTitle = new QLabel(tr("Included Gems"), this); + includedGemsTitle->setObjectName("includedGemsTitle"); + templateDetailsLayout->addWidget(includedGemsTitle); + + m_templateIncludedGems = new TagContainerWidget(this); + m_templateIncludedGems->setObjectName("includedGems"); + templateDetailsLayout->addWidget(m_templateIncludedGems); + +#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED + QLabel* moreGemsLabel = new QLabel(tr("Looking for more Gems?"), this); + moreGemsLabel->setObjectName("moreGems"); + templateDetailsLayout->addWidget(moreGemsLabel); + + QLabel* browseCatalogLabel = new QLabel(tr("Browse the Gems Catalog to further customize your project."), this); + browseCatalogLabel->setObjectName("browseCatalog"); + browseCatalogLabel->setWordWrap(true); + templateDetailsLayout->addWidget(browseCatalogLabel); + + QPushButton* configureGemsButton = new QPushButton(tr("Configure with more Gems"), this); + connect(configureGemsButton, &QPushButton::clicked, this, [=]() + { + emit ChangeScreenRequest(ProjectManagerScreen::GemCatalog); + }); + templateDetailsLayout->addWidget(configureGemsButton); +#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED + } + projectTemplateDetails->setLayout(templateDetailsLayout); + return projectTemplateDetails; + } + + void NewProjectSettingsScreen::UpdateTemplateDetails(const ProjectTemplateInfo& templateInfo) + { + m_templateDisplayName->setText(templateInfo.m_displayName); + m_templateSummary->setText(templateInfo.m_summary); + m_templateIncludedGems->Update(templateInfo.m_includedGems); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h index 6a4b6ec57d..ce77915404 100644 --- a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h +++ b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h @@ -13,12 +13,17 @@ #if !defined(Q_MOC_RUN) #include +#include +#include #endif QT_FORWARD_DECLARE_CLASS(QButtonGroup) +QT_FORWARD_DECLARE_CLASS(QLabel) +QT_FORWARD_DECLARE_CLASS(QFrame) namespace O3DE::ProjectManager { + QT_FORWARD_DECLARE_CLASS(TagContainerWidget) class NewProjectSettingsScreen : public ProjectSettingsScreen { @@ -33,8 +38,17 @@ namespace O3DE::ProjectManager private: QString GetDefaultProjectPath(); + QFrame* CreateTemplateDetails(int margin); + void UpdateTemplateDetails(const ProjectTemplateInfo& templateInfo); QButtonGroup* m_projectTemplateButtonGroup; + QLabel* m_templateDisplayName; + QLabel* m_templateSummary; + TagContainerWidget* m_templateIncludedGems; + QVector m_templates; + + inline constexpr static int s_spacerSize = 20; + inline constexpr static int s_templateDetailsContentMargin = 20; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectBuilder.cpp b/Code/Tools/ProjectManager/Source/ProjectBuilder.cpp new file mode 100644 index 0000000000..8cdab93c6a --- /dev/null +++ b/Code/Tools/ProjectManager/Source/ProjectBuilder.cpp @@ -0,0 +1,250 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +//#define MOCK_BUILD_PROJECT true + +namespace O3DE::ProjectManager +{ + // 10 Minutes + constexpr int MaxBuildTimeMSecs = 600000; + static const QString BuildPathPostfix = "windows_vs2019"; + static const QString ErrorLogPathPostfix = "CMakeFiles/CMakeProjectBuildError.log"; + + ProjectBuilderWorker::ProjectBuilderWorker(const ProjectInfo& projectInfo) + : QObject() + , m_projectInfo(projectInfo) + { + } + + void ProjectBuilderWorker::BuildProject() + { +#ifdef MOCK_BUILD_PROJECT + for (int i = 0; i < 10; ++i) + { + QThread::sleep(1); + UpdateProgress(i * 10); + } + Done(m_projectPath); +#else + EngineInfo engineInfo; + + AZ::Outcome engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo(); + if (engineInfoResult.IsSuccess()) + { + engineInfo = engineInfoResult.GetValue(); + } + else + { + emit Done(tr("Failed to get engine info.")); + return; + } + + // Show some kind of progress with very approximate estimates + UpdateProgress(1); + + QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment()); + // Append cmake path to PATH incase it is missing + QDir cmakePath(engineInfo.m_path); + cmakePath.cd("cmake/runtime/bin"); + QString pathValue = currentEnvironment.value("PATH"); + pathValue += ";" + cmakePath.path(); + currentEnvironment.insert("PATH", pathValue); + + QProcess configProjectProcess; + configProjectProcess.setProcessChannelMode(QProcess::MergedChannels); + configProjectProcess.setWorkingDirectory(m_projectInfo.m_path); + configProjectProcess.setProcessEnvironment(currentEnvironment); + + configProjectProcess.start( + "cmake", + QStringList + { + "-B", + QDir(m_projectInfo.m_path).filePath(BuildPathPostfix), + "-S", + m_projectInfo.m_path, + "-G", + "Visual Studio 16", + "-DLY_3RDPARTY_PATH=" + engineInfo.m_thirdPartyPath + }); + + if (!configProjectProcess.waitForStarted()) + { + emit Done(tr("Configuring project failed to start.")); + return; + } + if (!configProjectProcess.waitForFinished(MaxBuildTimeMSecs)) + { + WriteErrorLog(configProjectProcess.readAllStandardOutput()); + emit Done(tr("Configuring project timed out. See log for details")); + return; + } + + QString configProjectOutput(configProjectProcess.readAllStandardOutput()); + if (configProjectProcess.exitCode() != 0 || !configProjectOutput.contains("Generating done")) + { + WriteErrorLog(configProjectOutput); + emit Done(tr("Configuring project failed. See log for details.")); + return; + } + + UpdateProgress(20); + + QProcess buildProjectProcess; + buildProjectProcess.setProcessChannelMode(QProcess::MergedChannels); + buildProjectProcess.setWorkingDirectory(m_projectInfo.m_path); + buildProjectProcess.setProcessEnvironment(currentEnvironment); + + buildProjectProcess.start( + "cmake", + QStringList + { + "--build", + QDir(m_projectInfo.m_path).filePath(BuildPathPostfix), + "--target", + m_projectInfo.m_projectName + ".GameLauncher", + "Editor", + "--config", + "profile" + }); + + if (!buildProjectProcess.waitForStarted()) + { + emit Done(tr("Building project failed to start.")); + return; + } + if (!buildProjectProcess.waitForFinished(MaxBuildTimeMSecs)) + { + WriteErrorLog(configProjectProcess.readAllStandardOutput()); + emit Done(tr("Building project timed out. See log for details")); + return; + } + + QString buildProjectOutput(buildProjectProcess.readAllStandardOutput()); + if (configProjectProcess.exitCode() != 0) + { + WriteErrorLog(buildProjectOutput); + emit Done(tr("Building project failed. See log for details.")); + } + else + { + emit Done(""); + } +#endif + } + + QString ProjectBuilderWorker::LogFilePath() const + { + QDir logFilePath(m_projectInfo.m_path); + logFilePath.cd(BuildPathPostfix); + return logFilePath.filePath(ErrorLogPathPostfix); + } + + void ProjectBuilderWorker::WriteErrorLog(const QString& log) + { + QFile logFile(LogFilePath()); + // Overwrite file with truncate + if (logFile.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) + { + QTextStream output(&logFile); + output << log; + logFile.close(); + } + } + + ProjectBuilderController::ProjectBuilderController(const ProjectInfo& projectInfo, ProjectButton* projectButton, QWidget* parent) + : QObject() + , m_projectInfo(projectInfo) + , m_projectButton(projectButton) + , m_parent(parent) + { + m_worker = new ProjectBuilderWorker(m_projectInfo); + m_worker->moveToThread(&m_workerThread); + + connect(&m_workerThread, &QThread::finished, m_worker, &ProjectBuilderWorker::deleteLater); + connect(&m_workerThread, &QThread::started, m_worker, &ProjectBuilderWorker::BuildProject); + connect(m_worker, &ProjectBuilderWorker::Done, this, &ProjectBuilderController::HandleResults); + connect(m_worker, &ProjectBuilderWorker::UpdateProgress, this, &ProjectBuilderController::UpdateUIProgress); + } + + ProjectBuilderController::~ProjectBuilderController() + { + m_workerThread.quit(); + m_workerThread.wait(); + } + + void ProjectBuilderController::Start() + { + m_workerThread.start(); + UpdateUIProgress(0); + } + + void ProjectBuilderController::SetProjectButton(ProjectButton* projectButton) + { + m_projectButton = projectButton; + } + + QString ProjectBuilderController::GetProjectPath() const + { + return m_projectInfo.m_path; + } + + void ProjectBuilderController::UpdateUIProgress(int progress) + { + if (m_projectButton) + { + m_projectButton->SetButtonOverlayText(QString("%1 (%2%)\n\n").arg(tr("Building Project..."), QString::number(progress))); + m_projectButton->SetProgressBarValue(progress); + } + } + + void ProjectBuilderController::HandleResults(const QString& result) + { + if (!result.isEmpty()) + { + if (result.contains(tr("log"))) + { + QMessageBox::StandardButton openLog = QMessageBox::critical( + m_parent, + tr("Project Failed to Build!"), + result + tr("\n\nWould you like to view log?"), + QMessageBox::No | QMessageBox::Yes); + + if (openLog == QMessageBox::Yes) + { + // Open application assigned to this file type + QDesktopServices::openUrl(QUrl("file:///" + m_worker->LogFilePath())); + } + } + else + { + QMessageBox::critical(m_parent, tr("Project Failed to Build!"), result); + } + } + + emit Done(); + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectBuilder.h b/Code/Tools/ProjectManager/Source/ProjectBuilder.h new file mode 100644 index 0000000000..de84a351ee --- /dev/null +++ b/Code/Tools/ProjectManager/Source/ProjectBuilder.h @@ -0,0 +1,73 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ +#pragma once + +#if !defined(Q_MOC_RUN) +#include + +#include +#endif + +namespace O3DE::ProjectManager +{ + QT_FORWARD_DECLARE_CLASS(ProjectButton) + + class ProjectBuilderWorker : public QObject + { + Q_OBJECT + + public: + explicit ProjectBuilderWorker(const ProjectInfo& projectInfo); + ~ProjectBuilderWorker() = default; + + QString LogFilePath() const; + + public slots: + void BuildProject(); + + signals: + void UpdateProgress(int progress); + void Done(QString result); + + private: + void WriteErrorLog(const QString& log); + + ProjectInfo m_projectInfo; + }; + + class ProjectBuilderController : public QObject + { + Q_OBJECT + + public: + explicit ProjectBuilderController(const ProjectInfo& projectInfo, ProjectButton* projectButton, QWidget* parent = nullptr); + ~ProjectBuilderController(); + + void SetProjectButton(ProjectButton* projectButton); + QString GetProjectPath() const; + + public slots: + void Start(); + void UpdateUIProgress(int progress); + void HandleResults(const QString& result); + + signals: + void Done(); + + private: + ProjectInfo m_projectInfo; + ProjectBuilderWorker* m_worker; + QThread m_workerThread; + ProjectButton* m_projectButton; + QWidget* m_parent; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp index ee4d48fe7f..db1b1a4850 100644 --- a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp @@ -21,6 +21,7 @@ #include #include #include +#include namespace O3DE::ProjectManager { @@ -31,11 +32,26 @@ namespace O3DE::ProjectManager : QLabel(parent) { setObjectName("labelButton"); + + QVBoxLayout* vLayout = new QVBoxLayout(this); + vLayout->setContentsMargins(0, 0, 0, 0); + vLayout->setSpacing(5); + + setLayout(vLayout); m_overlayLabel = new QLabel("", this); m_overlayLabel->setObjectName("labelButtonOverlay"); m_overlayLabel->setWordWrap(true); m_overlayLabel->setAlignment(Qt::AlignCenter); m_overlayLabel->setVisible(false); + vLayout->addWidget(m_overlayLabel); + + m_buildButton = new QPushButton(tr("Build Project"), this); + m_buildButton->setVisible(false); + + m_progressBar = new QProgressBar(this); + m_progressBar->setObjectName("labelButtonProgressBar"); + m_progressBar->setVisible(false); + vLayout->addWidget(m_progressBar); } void LabelButton::mousePressEvent([[maybe_unused]] QMouseEvent* event) @@ -57,7 +73,22 @@ namespace O3DE::ProjectManager m_overlayLabel->setText(text); } - ProjectButton::ProjectButton(const ProjectInfo& projectInfo, QWidget* parent) + QLabel* LabelButton::GetOverlayLabel() + { + return m_overlayLabel; + } + + QProgressBar* LabelButton::GetProgressBar() + { + return m_progressBar; + } + + QPushButton* LabelButton::GetBuildButton() + { + return m_buildButton; + } + + ProjectButton::ProjectButton(const ProjectInfo& projectInfo, QWidget* parent, bool processing) : QFrame(parent) , m_projectInfo(projectInfo) { @@ -66,10 +97,18 @@ namespace O3DE::ProjectManager m_projectInfo.m_imagePath = ":/DefaultProjectImage.png"; } - Setup(); + BaseSetup(); + if (processing) + { + ProcessingSetup(); + } + else + { + ReadySetup(); + } } - void ProjectButton::Setup() + void ProjectButton::BaseSetup() { setObjectName("projectButton"); @@ -87,8 +126,37 @@ namespace O3DE::ProjectManager m_projectImageLabel->setPixmap( QPixmap(m_projectInfo.m_imagePath).scaled(m_projectImageLabel->size(), Qt::KeepAspectRatioByExpanding)); + m_projectFooter = new QFrame(this); + QHBoxLayout* hLayout = new QHBoxLayout(); + hLayout->setContentsMargins(0, 0, 0, 0); + m_projectFooter->setLayout(hLayout); + { + QLabel* projectNameLabel = new QLabel(m_projectInfo.m_displayName, this); + hLayout->addWidget(projectNameLabel); + } + + vLayout->addWidget(m_projectFooter); + } + + void ProjectButton::ProcessingSetup() + { + m_projectImageLabel->GetOverlayLabel()->setAlignment(Qt::AlignHCenter | Qt::AlignBottom); + m_projectImageLabel->SetEnabled(false); + m_projectImageLabel->SetOverlayText(tr("Processing...\n\n")); + + QProgressBar* progressBar = m_projectImageLabel->GetProgressBar(); + progressBar->setVisible(true); + progressBar->setValue(0); + } + + void ProjectButton::ReadySetup() + { + connect(m_projectImageLabel, &LabelButton::triggered, [this]() { emit OpenProject(m_projectInfo.m_path); }); + connect(m_projectImageLabel->GetBuildButton(), &QPushButton::clicked, [this](){ emit BuildProject(m_projectInfo); }); + QMenu* menu = new QMenu(this); menu->addAction(tr("Edit Project Settings..."), this, [this]() { emit EditProject(m_projectInfo.m_path); }); + menu->addAction(tr("Build"), this, [this]() { emit BuildProject(m_projectInfo); }); menu->addSeparator(); menu->addAction(tr("Open Project folder..."), this, [this]() { @@ -100,30 +168,33 @@ namespace O3DE::ProjectManager menu->addAction(tr("Remove from O3DE"), this, [this]() { emit RemoveProject(m_projectInfo.m_path); }); menu->addAction(tr("Delete this Project"), this, [this]() { emit DeleteProject(m_projectInfo.m_path); }); - QFrame* footer = new QFrame(this); - QHBoxLayout* hLayout = new QHBoxLayout(); - hLayout->setContentsMargins(0, 0, 0, 0); - footer->setLayout(hLayout); - { - QLabel* projectNameLabel = new QLabel(m_projectInfo.m_displayName, this); - hLayout->addWidget(projectNameLabel); - - QPushButton* projectMenuButton = new QPushButton(this); - projectMenuButton->setObjectName("projectMenuButton"); - projectMenuButton->setMenu(menu); - hLayout->addWidget(projectMenuButton); - } - - vLayout->addWidget(footer); + QPushButton* projectMenuButton = new QPushButton(this); + projectMenuButton->setObjectName("projectMenuButton"); + projectMenuButton->setMenu(menu); + m_projectFooter->layout()->addWidget(projectMenuButton); } - void ProjectButton::SetButtonEnabled(bool enabled) + void ProjectButton::SetLaunchButtonEnabled(bool enabled) { m_projectImageLabel->SetEnabled(enabled); } + void ProjectButton::ShowBuildButton(bool show) + { + QSpacerItem* buttonSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding); + + m_projectImageLabel->layout()->addItem(buttonSpacer); + m_projectImageLabel->layout()->addWidget(m_projectImageLabel->GetBuildButton()); + m_projectImageLabel->GetBuildButton()->setVisible(show); + } + void ProjectButton::SetButtonOverlayText(const QString& text) { m_projectImageLabel->SetOverlayText(text); } + + void ProjectButton::SetProgressBarValue(int progress) + { + m_projectImageLabel->GetProgressBar()->setValue(progress); + } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h index bb61f7354b..1178c8ea76 100644 --- a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h +++ b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h @@ -21,6 +21,7 @@ QT_FORWARD_DECLARE_CLASS(QPixmap) QT_FORWARD_DECLARE_CLASS(QPushButton) QT_FORWARD_DECLARE_CLASS(QAction) +QT_FORWARD_DECLARE_CLASS(QProgressBar) namespace O3DE::ProjectManager { @@ -36,6 +37,10 @@ namespace O3DE::ProjectManager void SetEnabled(bool enabled); void SetOverlayText(const QString& text); + QLabel* GetOverlayLabel(); + QProgressBar* GetProgressBar(); + QPushButton* GetBuildButton(); + signals: void triggered(); @@ -44,6 +49,8 @@ namespace O3DE::ProjectManager private: QLabel* m_overlayLabel; + QProgressBar* m_progressBar; + QPushButton* m_buildButton; bool m_enabled = true; }; @@ -53,11 +60,13 @@ namespace O3DE::ProjectManager Q_OBJECT // AUTOMOC public: - explicit ProjectButton(const ProjectInfo& m_projectInfo, QWidget* parent = nullptr); + explicit ProjectButton(const ProjectInfo& m_projectInfo, QWidget* parent = nullptr, bool processing = false); ~ProjectButton() = default; - void SetButtonEnabled(bool enabled); + void SetLaunchButtonEnabled(bool enabled); + void ShowBuildButton(bool show); void SetButtonOverlayText(const QString& text); + void SetProgressBarValue(int progress); signals: void OpenProject(const QString& projectName); @@ -65,11 +74,15 @@ namespace O3DE::ProjectManager void CopyProject(const QString& projectName); void RemoveProject(const QString& projectName); void DeleteProject(const QString& projectName); + void BuildProject(const ProjectInfo& projectInfo); private: - void Setup(); + void BaseSetup(); + void ProcessingSetup(); + void ReadySetup(); ProjectInfo m_projectInfo; LabelButton* m_projectImageLabel; + QFrame* m_projectFooter; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectInfo.cpp b/Code/Tools/ProjectManager/Source/ProjectInfo.cpp index f0dc05cc62..da0b4ebd61 100644 --- a/Code/Tools/ProjectManager/Source/ProjectInfo.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectInfo.cpp @@ -15,13 +15,13 @@ namespace O3DE::ProjectManager { ProjectInfo::ProjectInfo(const QString& path, const QString& projectName, const QString& displayName, - const QString& imagePath, const QString& backgroundImagePath, bool isNew) + const QString& imagePath, const QString& backgroundImagePath, bool needsBuild) : m_path(path) , m_projectName(projectName) , m_displayName(displayName) , m_imagePath(imagePath) , m_backgroundImagePath(backgroundImagePath) - , m_isNew(isNew) + , m_needsBuild(needsBuild) { } diff --git a/Code/Tools/ProjectManager/Source/ProjectInfo.h b/Code/Tools/ProjectManager/Source/ProjectInfo.h index 71fa12b344..857e6ea4d5 100644 --- a/Code/Tools/ProjectManager/Source/ProjectInfo.h +++ b/Code/Tools/ProjectManager/Source/ProjectInfo.h @@ -24,7 +24,7 @@ namespace O3DE::ProjectManager public: ProjectInfo() = default; ProjectInfo(const QString& path, const QString& projectName, const QString& displayName, - const QString& imagePath, const QString& backgroundImagePath, bool isNew); + const QString& imagePath, const QString& backgroundImagePath, bool needsBuild); bool operator==(const ProjectInfo& rhs); bool operator!=(const ProjectInfo& rhs); @@ -42,6 +42,6 @@ namespace O3DE::ProjectManager QString m_backgroundImagePath; // Used in project creation - bool m_isNew = false; //! Is this a new project or existing + bool m_needsBuild = false; //! Does this project need to be built }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectTemplateInfo.h b/Code/Tools/ProjectManager/Source/ProjectTemplateInfo.h index 0477968050..e75c64ec90 100644 --- a/Code/Tools/ProjectManager/Source/ProjectTemplateInfo.h +++ b/Code/Tools/ProjectManager/Source/ProjectTemplateInfo.h @@ -31,6 +31,7 @@ namespace O3DE::ProjectManager QString m_name; QString m_path; QString m_summary; + QStringList m_includedGems; QStringList m_canonicalTags; QStringList m_userTags; }; diff --git a/Code/Tools/ProjectManager/Source/ProjectUtils.cpp b/Code/Tools/ProjectManager/Source/ProjectUtils.cpp index 58e4c5c60f..3e2b3c13e1 100644 --- a/Code/Tools/ProjectManager/Source/ProjectUtils.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectUtils.cpp @@ -16,7 +16,9 @@ #include #include #include -#include +#include +#include +#include namespace O3DE::ProjectManager { @@ -192,6 +194,49 @@ namespace O3DE::ProjectManager return true; } + static bool IsVS2019Installed_internal() + { + QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); + QString programFilesPath = environment.value("ProgramFiles(x86)"); + QString vsWherePath = programFilesPath + "\\Microsoft Visual Studio\\Installer\\vswhere.exe"; + + QFileInfo vsWhereFile(vsWherePath); + if (vsWhereFile.exists() && vsWhereFile.isFile()) + { + QProcess vsWhereProcess; + vsWhereProcess.setProcessChannelMode(QProcess::MergedChannels); + + vsWhereProcess.start( + vsWherePath, + QStringList{ "-version", "16.0", "-latest", "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "-property", "isComplete" }); + + if (!vsWhereProcess.waitForStarted()) + { + return false; + } + + while (vsWhereProcess.waitForReadyRead()) + { + } + + QString vsWhereOutput(vsWhereProcess.readAllStandardOutput()); + if (vsWhereOutput.startsWith("1")) + { + return true; + } + } + + return false; + } + + bool IsVS2019Installed() + { + static bool vs2019Installed = IsVS2019Installed_internal(); + + return vs2019Installed; + } + ProjectManagerScreen GetProjectManagerScreen(const QString& screen) { auto iter = s_ProjectManagerStringNames.find(screen); @@ -202,6 +247,5 @@ namespace O3DE::ProjectManager return ProjectManagerScreen::Invalid; } - } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectUtils.h b/Code/Tools/ProjectManager/Source/ProjectUtils.h index d556d682f2..9c711ad187 100644 --- a/Code/Tools/ProjectManager/Source/ProjectUtils.h +++ b/Code/Tools/ProjectManager/Source/ProjectUtils.h @@ -25,6 +25,9 @@ namespace O3DE::ProjectManager bool CopyProject(const QString& origPath, const QString& newPath); bool DeleteProjectFiles(const QString& path, bool force = false); bool MoveProject(const QString& origPath, const QString& newPath, QWidget* parent = nullptr); + + bool IsVS2019Installed(); + ProjectManagerScreen GetProjectManagerScreen(const QString& screen); } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp index 425aa8514d..8e41e52643 100644 --- a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include @@ -42,6 +44,8 @@ #include #include #include +#include +#include //#define DISPLAY_PROJECT_DEV_DATA true @@ -66,6 +70,14 @@ namespace O3DE::ProjectManager m_stack->addWidget(m_projectsContent); vLayout->addWidget(m_stack); + + connect(reinterpret_cast(parent), &ScreensCtrl::NotifyBuildProject, this, &ProjectsScreen::SuggestBuildProject); + } + + ProjectsScreen::~ProjectsScreen() + + { + delete m_currentBuilder; } QFrame* ProjectsScreen::CreateFirstTimeContent() @@ -110,7 +122,7 @@ namespace O3DE::ProjectManager return frame; } - QFrame* ProjectsScreen::CreateProjectsContent() + QFrame* ProjectsScreen::CreateProjectsContent(QString buildProjectPath, ProjectButton** projectButton) { QFrame* frame = new QFrame(this); frame->setObjectName("projectsContent"); @@ -158,30 +170,43 @@ namespace O3DE::ProjectManager projectsScrollArea->setWidgetResizable(true); #ifndef DISPLAY_PROJECT_DEV_DATA + // Iterate once to insert building project first + if (!buildProjectPath.isEmpty()) + { + buildProjectPath = QDir::fromNativeSeparators(buildProjectPath); + for (auto project : projectsResult.GetValue()) + { + if (QDir::fromNativeSeparators(project.m_path) == buildProjectPath) + { + ProjectButton* buildingProjectButton = CreateProjectButton(project, flowLayout, true); + + if (projectButton) + { + *projectButton = buildingProjectButton; + } + + break; + } + } + } + for (auto project : projectsResult.GetValue()) #else ProjectInfo project = projectsResult.GetValue().at(0); for (int i = 0; i < 15; i++) #endif { - ProjectButton* projectButton; - - QString projectPreviewPath = project.m_path + m_projectPreviewImagePath; - QFileInfo doesPreviewExist(projectPreviewPath); - if (doesPreviewExist.exists() && doesPreviewExist.isFile()) + // Add all other projects skipping building project + // Safe if no building project because it is just an empty string + if (project.m_path != buildProjectPath) { - project.m_imagePath = projectPreviewPath; + ProjectButton* projectButtonWidget = CreateProjectButton(project, flowLayout); + + if (RequiresBuildProjectIterator(project.m_path) != m_requiresBuild.end()) + { + projectButtonWidget->ShowBuildButton(true); + } } - - projectButton = new ProjectButton(project, this); - - flowLayout->addWidget(projectButton); - - connect(projectButton, &ProjectButton::OpenProject, this, &ProjectsScreen::HandleOpenProject); - connect(projectButton, &ProjectButton::EditProject, this, &ProjectsScreen::HandleEditProject); - connect(projectButton, &ProjectButton::CopyProject, this, &ProjectsScreen::HandleCopyProject); - connect(projectButton, &ProjectButton::RemoveProject, this, &ProjectsScreen::HandleRemoveProject); - connect(projectButton, &ProjectButton::DeleteProject, this, &ProjectsScreen::HandleDeleteProject); } layout->addWidget(projectsScrollArea); @@ -191,6 +216,60 @@ namespace O3DE::ProjectManager return frame; } + ProjectButton* ProjectsScreen::CreateProjectButton(ProjectInfo& project, QLayout* flowLayout, bool processing) + { + ProjectButton* projectButton; + + QString projectPreviewPath = project.m_path + m_projectPreviewImagePath; + QFileInfo doesPreviewExist(projectPreviewPath); + if (doesPreviewExist.exists() && doesPreviewExist.isFile()) + { + project.m_imagePath = projectPreviewPath; + } + + projectButton = new ProjectButton(project, this, processing); + + flowLayout->addWidget(projectButton); + + if (!processing) + { + connect(projectButton, &ProjectButton::OpenProject, this, &ProjectsScreen::HandleOpenProject); + connect(projectButton, &ProjectButton::EditProject, this, &ProjectsScreen::HandleEditProject); + connect(projectButton, &ProjectButton::CopyProject, this, &ProjectsScreen::HandleCopyProject); + connect(projectButton, &ProjectButton::RemoveProject, this, &ProjectsScreen::HandleRemoveProject); + connect(projectButton, &ProjectButton::DeleteProject, this, &ProjectsScreen::HandleDeleteProject); + } + connect(projectButton, &ProjectButton::BuildProject, this, &ProjectsScreen::QueueBuildProject); + + return projectButton; + } + + void ProjectsScreen::ResetProjectsContent() + { + // refresh the projects content by re-creating it for now + if (m_projectsContent) + { + m_stack->removeWidget(m_projectsContent); + m_projectsContent->deleteLater(); + } + + // Make sure to update builder with latest Project Button + if (m_currentBuilder) + { + ProjectButton* projectButtonPtr; + + m_projectsContent = CreateProjectsContent(m_currentBuilder->GetProjectPath(), &projectButtonPtr); + m_currentBuilder->SetProjectButton(projectButtonPtr); + } + else + { + m_projectsContent = CreateProjectsContent(); + } + + m_stack->addWidget(m_projectsContent); + m_stack->setCurrentWidget(m_projectsContent); + } + ProjectManagerScreen ProjectsScreen::GetScreenEnum() { return ProjectManagerScreen::Projects; @@ -237,7 +316,7 @@ namespace O3DE::ProjectManager { if (ProjectUtils::AddProjectDialog(this)) { - emit ResetScreenRequest(ProjectManagerScreen::Projects); + ResetProjectsContent(); emit ChangeScreenRequest(ProjectManagerScreen::Projects); } } @@ -245,38 +324,47 @@ namespace O3DE::ProjectManager { if (!projectPath.isEmpty()) { - AZ::IO::FixedMaxPath executableDirectory = AZ::Utils::GetExecutableDirectory(); - AZStd::string executableFilename = "Editor"; - AZ::IO::FixedMaxPath editorExecutablePath = executableDirectory / (executableFilename + AZ_TRAIT_OS_EXECUTABLE_EXTENSION); - auto cmdPath = AZ::IO::FixedMaxPathString::format("%s -regset=\"/Amazon/AzCore/Bootstrap/project_path=%s\"", editorExecutablePath.c_str(), projectPath.toStdString().c_str()); + if (!WarnIfInBuildQueue(projectPath)) + { + AZ::IO::FixedMaxPath executableDirectory = AZ::Utils::GetExecutableDirectory(); + AZStd::string executableFilename = "Editor"; + AZ::IO::FixedMaxPath editorExecutablePath = executableDirectory / (executableFilename + AZ_TRAIT_OS_EXECUTABLE_EXTENSION); + auto cmdPath = AZ::IO::FixedMaxPathString::format( + "%s -regset=\"/Amazon/AzCore/Bootstrap/project_path=%s\"", editorExecutablePath.c_str(), + projectPath.toStdString().c_str()); - AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; - processLaunchInfo.m_commandlineParameters = cmdPath; - bool launchSucceeded = AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo); - if (!launchSucceeded) - { - AZ_Error("ProjectManager", false, "Failed to launch editor"); - QMessageBox::critical( this, tr("Error"), tr("Failed to launch the Editor, please verify the project settings are valid.")); - } - else - { - // prevent the user from accidentally pressing the button while the editor is launching - // and let them know what's happening - ProjectButton* button = qobject_cast(sender()); - if (button) + AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; + processLaunchInfo.m_commandlineParameters = cmdPath; + bool launchSucceeded = AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo); + if (!launchSucceeded) { - button->SetButtonEnabled(false); - button->SetButtonOverlayText(tr("Opening Editor...")); + AZ_Error("ProjectManager", false, "Failed to launch editor"); + QMessageBox::critical( + this, tr("Error"), tr("Failed to launch the Editor, please verify the project settings are valid.")); } + else + { + // prevent the user from accidentally pressing the button while the editor is launching + // and let them know what's happening + ProjectButton* button = qobject_cast(sender()); + if (button) + { + button->SetLaunchButtonEnabled(false); + button->SetButtonOverlayText(tr("Opening Editor...")); + } - // enable the button after 3 seconds - constexpr int waitTimeInMs = 3000; - QTimer::singleShot(waitTimeInMs, this, [this, button] { - if (button) + // enable the button after 3 seconds + constexpr int waitTimeInMs = 3000; + QTimer::singleShot( + waitTimeInMs, this, + [this, button] { - button->SetButtonEnabled(true); - } - }); + if (button) + { + button->SetLaunchButtonEnabled(true); + } + }); + } } } else @@ -288,38 +376,90 @@ namespace O3DE::ProjectManager } void ProjectsScreen::HandleEditProject(const QString& projectPath) { - emit NotifyCurrentProject(projectPath); - emit ChangeScreenRequest(ProjectManagerScreen::UpdateProject); + if (!WarnIfInBuildQueue(projectPath)) + { + emit NotifyCurrentProject(projectPath); + emit ChangeScreenRequest(ProjectManagerScreen::UpdateProject); + } } void ProjectsScreen::HandleCopyProject(const QString& projectPath) { - // Open file dialog and choose location for copied project then register copy with O3DE - if (ProjectUtils::CopyProjectDialog(projectPath, this)) + if (!WarnIfInBuildQueue(projectPath)) { - emit ResetScreenRequest(ProjectManagerScreen::Projects); - emit ChangeScreenRequest(ProjectManagerScreen::Projects); + // Open file dialog and choose location for copied project then register copy with O3DE + if (ProjectUtils::CopyProjectDialog(projectPath, this)) + { + ResetProjectsContent(); + emit ChangeScreenRequest(ProjectManagerScreen::Projects); + } } } void ProjectsScreen::HandleRemoveProject(const QString& projectPath) { - // Unregister Project from O3DE and reload projects - if (ProjectUtils::UnregisterProject(projectPath)) + if (!WarnIfInBuildQueue(projectPath)) { - emit ResetScreenRequest(ProjectManagerScreen::Projects); - emit ChangeScreenRequest(ProjectManagerScreen::Projects); + // Unregister Project from O3DE and reload projects + if (ProjectUtils::UnregisterProject(projectPath)) + { + ResetProjectsContent(); + emit ChangeScreenRequest(ProjectManagerScreen::Projects); + } } } void ProjectsScreen::HandleDeleteProject(const QString& projectPath) { - QMessageBox::StandardButton warningResult = QMessageBox::warning( - this, tr("Delete Project"), tr("Are you sure?\nProject will be removed from O3DE and directory will be deleted!"), - QMessageBox::No | QMessageBox::Yes); - - if (warningResult == QMessageBox::Yes) + if (!WarnIfInBuildQueue(projectPath)) { - // Remove project from O3DE and delete from disk - HandleRemoveProject(projectPath); - ProjectUtils::DeleteProjectFiles(projectPath); + QMessageBox::StandardButton warningResult = QMessageBox::warning(this, + tr("Delete Project"), + tr("Are you sure?\nProject will be unregistered from O3DE and project directory will be deleted from your disk."), + QMessageBox::No | QMessageBox::Yes); + + if (warningResult == QMessageBox::Yes) + { + // Remove project from O3DE and delete from disk + HandleRemoveProject(projectPath); + ProjectUtils::DeleteProjectFiles(projectPath); + } + } + } + + void ProjectsScreen::SuggestBuildProject(const ProjectInfo& projectInfo) + { + if (projectInfo.m_needsBuild) + { + if (RequiresBuildProjectIterator(projectInfo.m_path) == m_requiresBuild.end()) + { + m_requiresBuild.append(projectInfo); + } + ResetProjectsContent(); + } + else + { + QMessageBox::information(this, + tr("Project Should be rebuilt."), + projectInfo.m_projectName + tr(" project likely needs to be rebuilt.")); + } + } + + void ProjectsScreen::QueueBuildProject(const ProjectInfo& projectInfo) + { + auto requiredIter = RequiresBuildProjectIterator(projectInfo.m_path); + if (requiredIter != m_requiresBuild.end()) + { + m_requiresBuild.erase(requiredIter); + } + + if (!BuildQueueContainsProject(projectInfo.m_path)) + { + if (m_buildQueue.empty() && !m_currentBuilder) + { + StartProjectBuild(projectInfo); + } + else + { + m_buildQueue.append(projectInfo); + } } } @@ -331,17 +471,7 @@ namespace O3DE::ProjectManager } else { - // refresh the projects content by re-creating it for now - if (m_projectsContent) - { - m_stack->removeWidget(m_projectsContent); - m_projectsContent->deleteLater(); - } - - m_projectsContent = CreateProjectsContent(); - - m_stack->addWidget(m_projectsContent); - m_stack->setCurrentWidget(m_projectsContent); + ResetProjectsContent(); } } @@ -363,4 +493,89 @@ namespace O3DE::ProjectManager return displayFirstTimeContent; } + void ProjectsScreen::StartProjectBuild(const ProjectInfo& projectInfo) + { + if (ProjectUtils::IsVS2019Installed()) + { + QMessageBox::StandardButton buildProject = QMessageBox::information( + this, + tr("Building \"%1\"").arg(projectInfo.m_projectName), + tr("Ready to build \"%1\"?").arg(projectInfo.m_projectName), + QMessageBox::No | QMessageBox::Yes); + + if (buildProject == QMessageBox::Yes) + { + m_currentBuilder = new ProjectBuilderController(projectInfo, nullptr, this); + ResetProjectsContent(); + connect(m_currentBuilder, &ProjectBuilderController::Done, this, &ProjectsScreen::ProjectBuildDone); + + m_currentBuilder->Start(); + } + else + { + ProjectBuildDone(); + } + } + } + + void ProjectsScreen::ProjectBuildDone() + { + delete m_currentBuilder; + m_currentBuilder = nullptr; + + if (!m_buildQueue.empty()) + { + StartProjectBuild(m_buildQueue.front()); + m_buildQueue.pop_front(); + } + else + { + ResetProjectsContent(); + } + } + + QList::iterator ProjectsScreen::RequiresBuildProjectIterator(const QString& projectPath) + { + QString nativeProjPath(QDir::toNativeSeparators(projectPath)); + auto projectIter = m_requiresBuild.begin(); + for (; projectIter != m_requiresBuild.end(); ++projectIter) + { + if (QDir::toNativeSeparators(projectIter->m_path) == nativeProjPath) + { + break; + } + } + + return projectIter; + } + + bool ProjectsScreen::BuildQueueContainsProject(const QString& projectPath) + { + QString nativeProjPath(QDir::toNativeSeparators(projectPath)); + for (const ProjectInfo& project : m_buildQueue) + { + if (QDir::toNativeSeparators(project.m_path) == nativeProjPath) + { + return true; + } + } + + return false; + } + + bool ProjectsScreen::WarnIfInBuildQueue(const QString& projectPath) + { + if (BuildQueueContainsProject(projectPath)) + { + QMessageBox::warning( + this, + tr("Action Temporarily Disabled!"), + tr("Action not allowed on projects in build queue.")); + + return true; + } + + return false; + } + } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectsScreen.h b/Code/Tools/ProjectManager/Source/ProjectsScreen.h index e02b34525b..bc28d4ef30 100644 --- a/Code/Tools/ProjectManager/Source/ProjectsScreen.h +++ b/Code/Tools/ProjectManager/Source/ProjectsScreen.h @@ -13,21 +13,28 @@ #if !defined(Q_MOC_RUN) #include +#include + +#include #endif QT_FORWARD_DECLARE_CLASS(QPaintEvent) QT_FORWARD_DECLARE_CLASS(QFrame) QT_FORWARD_DECLARE_CLASS(QStackedWidget) +QT_FORWARD_DECLARE_CLASS(QLayout) namespace O3DE::ProjectManager { + QT_FORWARD_DECLARE_CLASS(ProjectBuilderController); + QT_FORWARD_DECLARE_CLASS(ProjectButton); + class ProjectsScreen : public ScreenWidget { public: explicit ProjectsScreen(QWidget* parent = nullptr); - ~ProjectsScreen() = default; + ~ProjectsScreen(); ProjectManagerScreen GetScreenEnum() override; QString GetTabText() override; @@ -35,6 +42,7 @@ namespace O3DE::ProjectManager protected: void NotifyCurrentScreen() override; + void ProjectBuildDone(); protected slots: void HandleNewProjectButton(); @@ -45,19 +53,32 @@ namespace O3DE::ProjectManager void HandleRemoveProject(const QString& projectPath); void HandleDeleteProject(const QString& projectPath); + void SuggestBuildProject(const ProjectInfo& projectInfo); + void QueueBuildProject(const ProjectInfo& projectInfo); + void paintEvent(QPaintEvent* event) override; private: QFrame* CreateFirstTimeContent(); - QFrame* CreateProjectsContent(); + QFrame* CreateProjectsContent(QString buildProjectPath = "", ProjectButton** projectButton = nullptr); + ProjectButton* CreateProjectButton(ProjectInfo& project, QLayout* flowLayout, bool processing = false); + void ResetProjectsContent(); bool ShouldDisplayFirstTimeContent(); - QAction* m_createNewProjectAction; - QAction* m_addExistingProjectAction; + void StartProjectBuild(const ProjectInfo& projectInfo); + QList::iterator RequiresBuildProjectIterator(const QString& projectPath); + bool BuildQueueContainsProject(const QString& projectPath); + bool WarnIfInBuildQueue(const QString& projectPath); + + QAction* m_createNewProjectAction = nullptr; + QAction* m_addExistingProjectAction = nullptr; QPixmap m_background; - QFrame* m_firstTimeContent; - QFrame* m_projectsContent; - QStackedWidget* m_stack; + QFrame* m_firstTimeContent = nullptr; + QFrame* m_projectsContent = nullptr; + QStackedWidget* m_stack = nullptr; + QList m_requiresBuild; + QQueue m_buildQueue; + ProjectBuilderController* m_currentBuilder = nullptr; const QString m_projectPreviewImagePath = "/preview.png"; diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 37e636caef..d7d0414c1f 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -384,7 +384,9 @@ namespace O3DE::ProjectManager engineInfo.m_defaultProjectsFolder = Py_To_String(o3deData["default_projects_folder"]); engineInfo.m_defaultRestrictedFolder = Py_To_String(o3deData["default_restricted_folder"]); engineInfo.m_defaultTemplatesFolder = Py_To_String(o3deData["default_templates_folder"]); - engineInfo.m_thirdPartyPath = Py_To_String(o3deData["default_third_party_folder"]); + + pybind11::str defaultThirdPartyFolder = m_manifest.attr("get_o3de_third_party_folder")(); + engineInfo.m_thirdPartyPath = Py_To_String_Optional(o3deData,"default_third_party_folder", Py_To_String(defaultThirdPartyFolder)); } auto engineData = m_manifest.attr("get_engine_json_data")(pybind11::none(), enginePath); @@ -667,7 +669,7 @@ namespace O3DE::ProjectManager { ProjectInfo projectInfo; projectInfo.m_path = Py_To_String(path); - projectInfo.m_isNew = false; + projectInfo.m_needsBuild = false; auto projectData = m_manifest.attr("get_project_json_data")(pybind11::none(), path); if (pybind11::isinstance(projectData)) @@ -754,7 +756,7 @@ namespace O3DE::ProjectManager ProjectTemplateInfo PythonBindings::ProjectTemplateInfoFromPath(pybind11::handle path) { ProjectTemplateInfo templateInfo; - templateInfo.m_path = Py_To_String(path); + templateInfo.m_path = Py_To_String(pybind11::str(path)); auto data = m_manifest.attr("get_template_json_data")(pybind11::none(), path); if (pybind11::isinstance(data)) @@ -781,6 +783,13 @@ namespace O3DE::ProjectManager templateInfo.m_canonicalTags.push_back(Py_To_String(tag)); } } + if (data.contains("included_gems")) + { + for (auto gem : data["included_gems"]) + { + templateInfo.m_includedGems.push_back(Py_To_String(gem)); + } + } } catch ([[maybe_unused]] const std::exception& e) { diff --git a/Code/Tools/ProjectManager/Source/ScreenWidget.h b/Code/Tools/ProjectManager/Source/ScreenWidget.h index 2ad6d30201..47baed261c 100644 --- a/Code/Tools/ProjectManager/Source/ScreenWidget.h +++ b/Code/Tools/ProjectManager/Source/ScreenWidget.h @@ -13,6 +13,7 @@ #if !defined(Q_MOC_RUN) #include +#include #include #include @@ -61,6 +62,7 @@ namespace O3DE::ProjectManager void GotoPreviousScreenRequest(); void ResetScreenRequest(ProjectManagerScreen screen); void NotifyCurrentProject(const QString& projectPath); + void NotifyBuildProject(const ProjectInfo& projectInfo); }; diff --git a/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp b/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp index 52fcbf354a..646f66a557 100644 --- a/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp @@ -177,6 +177,7 @@ namespace O3DE::ProjectManager connect(newScreen, &ScreenWidget::GotoPreviousScreenRequest, this, &ScreensCtrl::GotoPreviousScreen); connect(newScreen, &ScreenWidget::ResetScreenRequest, this, &ScreensCtrl::ResetScreen); connect(newScreen, &ScreenWidget::NotifyCurrentProject, this, &ScreensCtrl::NotifyCurrentProject); + connect(newScreen, &ScreenWidget::NotifyBuildProject, this, &ScreensCtrl::NotifyBuildProject); } void ScreensCtrl::ResetAllScreens() diff --git a/Code/Tools/ProjectManager/Source/ScreensCtrl.h b/Code/Tools/ProjectManager/Source/ScreensCtrl.h index 3b51ed529a..841108dff7 100644 --- a/Code/Tools/ProjectManager/Source/ScreensCtrl.h +++ b/Code/Tools/ProjectManager/Source/ScreensCtrl.h @@ -13,6 +13,7 @@ #if !defined(Q_MOC_RUN) #include +#include #include #include @@ -39,6 +40,7 @@ namespace O3DE::ProjectManager signals: void NotifyCurrentProject(const QString& projectPath); + void NotifyBuildProject(const ProjectInfo& projectInfo); public slots: bool ChangeToScreen(ProjectManagerScreen screen); diff --git a/Code/Tools/ProjectManager/Source/TemplateButtonWidget.cpp b/Code/Tools/ProjectManager/Source/TemplateButtonWidget.cpp new file mode 100644 index 0000000000..41b5e51c99 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/TemplateButtonWidget.cpp @@ -0,0 +1,65 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include +#include +#include +#include +#include +#include + +namespace O3DE::ProjectManager +{ + + TemplateButton::TemplateButton(const QString& imagePath, const QString& labelText, QWidget* parent) + : QPushButton(parent) + { + setAutoExclusive(true); + + setObjectName("templateButton"); + + QVBoxLayout* vLayout = new QVBoxLayout(); + vLayout->setSpacing(0); + vLayout->setContentsMargins(0, 0, 0, 0); + setLayout(vLayout); + + QLabel* image = new QLabel(this); + image->setObjectName("templateImage"); + image->setPixmap( + QPixmap(imagePath).scaled(QSize(s_templateImageWidth,s_templateImageHeight) , Qt::KeepAspectRatio, Qt::SmoothTransformation)); + vLayout->addWidget(image); + + QLabel* label = new QLabel(labelText, this); + label->setObjectName("templateLabel"); + vLayout->addWidget(label); + + connect(this, &QAbstractButton::toggled, this, &TemplateButton::onToggled); + } + + void TemplateButton::onToggled() + { + setProperty("Checked", isChecked()); + + // we must unpolish/polish every child after changing a property + // or else they won't use the correct stylesheet selector + for (auto child : findChildren()) + { + child->style()->unpolish(child); + child->style()->polish(child); + } + + style()->unpolish(this); + style()->polish(this); + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/TemplateButtonWidget.h b/Code/Tools/ProjectManager/Source/TemplateButtonWidget.h new file mode 100644 index 0000000000..db0f5f39c8 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/TemplateButtonWidget.h @@ -0,0 +1,37 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#endif + +namespace O3DE::ProjectManager +{ + class TemplateButton + : public QPushButton + { + Q_OBJECT // AUTOMOC + + public: + explicit TemplateButton(const QString& imagePath, const QString& labelText, QWidget* parent = nullptr); + ~TemplateButton() = default; + + protected slots: + void onToggled(); + + private: + inline constexpr static int s_templateImageWidth = 92; + inline constexpr static int s_templateImageHeight = 122; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp index a383a0f93b..6fcb1b1c71 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp @@ -119,7 +119,9 @@ namespace O3DE::ProjectManager void UpdateProjectCtrl::HandleNextButton() { - if (m_stack->currentIndex() == ScreenOrder::Settings) + bool shouldRebuild = false; + + if (m_stack->currentIndex() == ScreenOrder::Settings && m_updateSettingsScreen) { if (m_updateSettingsScreen) { @@ -155,11 +157,17 @@ namespace O3DE::ProjectManager m_projectInfo = newProjectSettings; } } - - if (m_stack->currentIndex() == ScreenOrder::Gems && m_gemCatalogScreen) + else if (m_stack->currentIndex() == ScreenOrder::Gems && m_gemCatalogScreen) { // Enable or disable the gems that got adjusted in the gem catalog and apply them to the given project. m_gemCatalogScreen->EnableDisableGemsForProject(m_projectInfo.m_path); + + shouldRebuild = true; + } + + if (shouldRebuild) + { + emit NotifyBuildProject(m_projectInfo); } emit ChangeScreenRequest(ProjectManagerScreen::Projects); diff --git a/Code/Tools/ProjectManager/project_manager_files.cmake b/Code/Tools/ProjectManager/project_manager_files.cmake index a7a36f26ab..eb9cd1145e 100644 --- a/Code/Tools/ProjectManager/project_manager_files.cmake +++ b/Code/Tools/ProjectManager/project_manager_files.cmake @@ -38,6 +38,8 @@ set(FILES Source/ProjectInfo.cpp Source/ProjectUtils.h Source/ProjectUtils.cpp + Source/ProjectBuilder.h + Source/ProjectBuilder.cpp Source/UpdateProjectSettingsScreen.h Source/UpdateProjectSettingsScreen.cpp Source/NewProjectSettingsScreen.h @@ -60,6 +62,8 @@ set(FILES Source/LinkWidget.cpp Source/TagWidget.h Source/TagWidget.cpp + Source/TemplateButtonWidget.h + Source/TemplateButtonWidget.cpp Source/GemCatalog/GemCatalogHeaderWidget.h Source/GemCatalog/GemCatalogHeaderWidget.cpp Source/GemCatalog/GemCatalogScreen.h diff --git a/Gems/AWSCore/Code/CMakeLists.txt b/Gems/AWSCore/Code/CMakeLists.txt index 7edb22124f..d6cd57355f 100644 --- a/Gems/AWSCore/Code/CMakeLists.txt +++ b/Gems/AWSCore/Code/CMakeLists.txt @@ -64,11 +64,13 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) Include/Public BUILD_DEPENDENCIES PRIVATE + AZ::AzQtComponents 3rdParty::Qt::Core 3rdParty::Qt::Widgets - AZ::AzQtComponents + Gem::AWSCore.Static PUBLIC AZ::AzToolsFramework + 3rdParty::AWSNativeSDK::AWSCore ) ly_add_target( diff --git a/Gems/AWSCore/Code/Include/Private/AWSCoreEditorModule.h b/Gems/AWSCore/Code/Include/Private/AWSCoreEditorModule.h index 45a2c1f9f7..144976f355 100644 --- a/Gems/AWSCore/Code/Include/Private/AWSCoreEditorModule.h +++ b/Gems/AWSCore/Code/Include/Private/AWSCoreEditorModule.h @@ -16,7 +16,7 @@ namespace AWSCore { class AWSCoreEditorModule - :public AZ::Module + : public AZ::Module { public: AZ_RTTI(AWSCoreEditorModule, "{C1C9B898-848B-4C2F-A7AA-69642D12BCB5}", AZ::Module); diff --git a/Gems/AWSCore/Code/Include/Private/Editor/Attribution/AWSAttributionServiceApi.h b/Gems/AWSCore/Code/Include/Private/Editor/Attribution/AWSAttributionServiceApi.h new file mode 100644 index 0000000000..d14e51589c --- /dev/null +++ b/Gems/AWSCore/Code/Include/Private/Editor/Attribution/AWSAttributionServiceApi.h @@ -0,0 +1,71 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include + +namespace AWSCore +{ + namespace ServiceAPI + { + //! Struct for storing the success response. + struct AWSAtrributionSuccessResponse + { + //! Identify the expected property type and provide a location where the property value can be stored. + //! @param key Name of the property. + //! @param reader JSON reader to read the property. + bool OnJsonKey(const char* key, AWSCore::JsonReader& reader); + + AZStd::string result; //!< Processing result for the input record. + }; + + // Service RequestJobs + AWS_FEATURE_GEM_SERVICE(AWSAttribution); + + //! POST request to send attribution metric to the backend. + //! The path for this service API is "/prod/metrics". + class AWSAttributionRequest + : public AWSCore::ServiceRequest + { + public: + SERVICE_REQUEST(AWSAttribution, HttpMethod::HTTP_POST, "/metrics"); + + bool UseAWSCredentials() + { + return false; + } + + //! Request body for the service API request. + struct Parameters + { + //! Build the service API request. + //! @request Builder for generating the request. + //! @return Whether the request is built successfully. + bool BuildRequest(AWSCore::RequestBuilder& request); + + //! Write to the service API request body. + //! @param writer JSON writer for the serialization. + //! @return Whether the serialization is successful. + bool WriteJson(AWSCore::JsonWriter& writer) const; + + AttributionMetric metric; + }; + + AWSAtrributionSuccessResponse result; + Parameters parameters; //! Request parameter. + }; + + using AWSAttributionRequestJob = AWSCore::ServiceRequestJob; + } // ServiceAPI +} // AWSMetrics diff --git a/Gems/AWSCore/Code/Include/Private/Editor/Attribution/AWSCoreAttributionConstant.h b/Gems/AWSCore/Code/Include/Private/Editor/Attribution/AWSCoreAttributionConstant.h new file mode 100644 index 0000000000..503abfb9cf --- /dev/null +++ b/Gems/AWSCore/Code/Include/Private/Editor/Attribution/AWSCoreAttributionConstant.h @@ -0,0 +1,25 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +namespace AWSCore +{ + //! Default metrics attribute keys + static constexpr char AwsAttributionAttributeKeyVersion[] = "version"; + static constexpr char AwsAttributionAttributeKeyO3DEVersion[] = "o3de_version"; + static constexpr char AwsAttributionAttributeKeyPlatform[] = "platform"; + static constexpr char AwsAttributionAttributeKeyPlatformVersion[] = "platform_version"; + static constexpr char AwsAttributionAttributeKeyActiveAWSGems[] = "aws_gems"; + static constexpr char AwsAttributionAttributeKeyTimestamp[] = "timestamp"; + +} // namespace AWSCOre diff --git a/Gems/AWSCore/Code/Include/Private/Editor/Attribution/AWSCoreAttributionManager.h b/Gems/AWSCore/Code/Include/Private/Editor/Attribution/AWSCoreAttributionManager.h new file mode 100644 index 0000000000..ad3b8c72c9 --- /dev/null +++ b/Gems/AWSCore/Code/Include/Private/Editor/Attribution/AWSCoreAttributionManager.h @@ -0,0 +1,53 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once +#include +#include + +#include +#include + +namespace AWSCore +{ + //! Manages operational metrics for AWS gems + class AWSAttributionManager + { + public: + AWSAttributionManager(); + virtual ~AWSAttributionManager(); + + //! Perform initialization + void Init(); + + //! Run metric check + void MetricCheck(); + + protected: + virtual void SubmitMetric(AttributionMetric& metric); + virtual void UpdateMetric(AttributionMetric& metric); + void UpdateLastSend(); + void SetApiEndpointAndRegion(ServiceAPI::AWSAttributionRequestJob::Config* config); + + private: + bool ShouldGenerateMetric() const; + + AZStd::string GetEngineVersion() const; + AZStd::string GetPlatform() const; + void GetActiveAWSGems(AZStd::vector& gemNames); + + void SaveSettingsRegistryFile(); + + AZStd::unique_ptr m_settingsRegistry; + }; + +} // namespace AWSCore diff --git a/Gems/AWSCore/Code/Include/Private/Editor/Attribution/AWSCoreAttributionMetric.h b/Gems/AWSCore/Code/Include/Private/Editor/Attribution/AWSCoreAttributionMetric.h new file mode 100644 index 0000000000..42518b8df5 --- /dev/null +++ b/Gems/AWSCore/Code/Include/Private/Editor/Attribution/AWSCoreAttributionMetric.h @@ -0,0 +1,62 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or + * a third party where indicated. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include +#include +#include +#include + +namespace AWSCore +{ + //! Defines the operational metric sent periodically + class AttributionMetric + { + public: + AZ_TYPE_INFO(MetricsAttribute, "{6483F481-0C18-4171-8B59-A44F2F28EAE5}") + + AttributionMetric(); + AttributionMetric(const AZStd::string& timestamp); + ~AttributionMetric() = default; + + void SetO3DEVersion(const AZStd::string& version); + void SetPlatform(const AZStd::string& platform, const AZStd::string& platformVersion); + void AddActiveGem(const AZStd::string& gemName); + + //! Serialize the metrics object queue to a string. + //! @return Serialized string. + AZStd::string SerializeToJson(); + + //! Serialize the metrics object to JSON for the sending requests. + //! @param writer JSON writer for the serialization. + //! @return Whether the metrics event is serialized successfully. + bool SerializeToJson(AWSCore::JsonWriter& writer) const; + + //! Read from a JSON value to the metrics event. + //! @param metricsObjVal JSON value to read from. + //! @return Whether the metrics event is created successfully. + bool ReadFromJson(rapidjson::Value& metricsObjVal); + + //! Generates a UTC 8601 formatted timestamp + static AZStd::string GenerateTimeStamp(); + private: + AZStd::string m_version; //!< Schema version in use + AZStd::string m_o3deVersion; //!< O3DE editor version in use + AZStd::string m_platform; //!< OS type + AZStd::string m_platformVersion; //!< OS subtype + AZStd::string m_timestamp; //!< Metric generation time + AZStd::vector m_activeAWSGems; //!< Active AWS Gems in project + }; +} // namespace AWSCore diff --git a/Gems/AWSCore/Code/Include/Private/Editor/Attribution/AWSCoreAttributionSystemComponent.h b/Gems/AWSCore/Code/Include/Private/Editor/Attribution/AWSCoreAttributionSystemComponent.h new file mode 100644 index 0000000000..476b7ba842 --- /dev/null +++ b/Gems/AWSCore/Code/Include/Private/Editor/Attribution/AWSCoreAttributionSystemComponent.h @@ -0,0 +1,50 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include + +namespace AWSCore +{ + class AWSAttributionManager; + + //! Attribution System Component. Responsible for instantiating and managing AWS Attribution Manager + class AWSAttributionSystemComponent: + public AZ::Component + { + public: + AZ_COMPONENT(AWSAttributionSystemComponent, "{366861EC-8337-4180-A202-4E4DF082A3A8}"); + + AWSAttributionSystemComponent(); + ~AWSAttributionSystemComponent() = default; + + static void Reflect(AZ::ReflectContext* context);\ + + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + protected: + //////////////////////////////////////////////////////////////////////// + // AZ::Component interface implementation + void Init() override; + void Activate() override; + void Deactivate() override; + //////////////////////////////////////////////////////////////////////// + + private: + AZStd::unique_ptr m_manager; //!< pointer to the attribution manager which handles operational metrics + }; +} // namespace AWSCore diff --git a/Gems/AWSCore/Code/Include/Public/Framework/ServiceClientJobConfig.h b/Gems/AWSCore/Code/Include/Public/Framework/ServiceClientJobConfig.h index 722fc098a0..709e786adc 100644 --- a/Gems/AWSCore/Code/Include/Public/Framework/ServiceClientJobConfig.h +++ b/Gems/AWSCore/Code/Include/Public/Framework/ServiceClientJobConfig.h @@ -113,7 +113,13 @@ namespace AWSCore /// needed. See it's use in ServiceRequestJobConfig. const AZStd::string GetServiceUrl() override { + if (endpointOverride.has_value()) + { + return endpointOverride.value().c_str(); + } + AZStd::string serviceUrl; + if (!ServiceTraitsType::RESTApiIdKeyName && !ServiceTraitsType::RESTApiStageKeyName) { AWSResourceMappingRequestBus::BroadcastResult( diff --git a/Gems/AWSCore/Code/Source/AWSCoreEditorModule.cpp b/Gems/AWSCore/Code/Source/AWSCoreEditorModule.cpp index 69e45bfd68..2f4519c960 100644 --- a/Gems/AWSCore/Code/Source/AWSCoreEditorModule.cpp +++ b/Gems/AWSCore/Code/Source/AWSCoreEditorModule.cpp @@ -11,6 +11,7 @@ #include #include +#include namespace AWSCore { @@ -19,6 +20,7 @@ namespace AWSCore // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. m_descriptors.insert(m_descriptors.end(), { AWSCoreEditorSystemComponent::CreateDescriptor(), + AWSAttributionSystemComponent::CreateDescriptor() }); } @@ -28,7 +30,8 @@ namespace AWSCore AZ::ComponentTypeList AWSCoreEditorModule::GetRequiredSystemComponents() const { return AZ::ComponentTypeList{ - azrtti_typeid() + azrtti_typeid(), + azrtti_typeid() }; } diff --git a/Gems/AWSCore/Code/Source/Editor/Attribution/AWSAttributionServiceApi.cpp b/Gems/AWSCore/Code/Source/Editor/Attribution/AWSAttributionServiceApi.cpp new file mode 100644 index 0000000000..3c82ec98f9 --- /dev/null +++ b/Gems/AWSCore/Code/Source/Editor/Attribution/AWSAttributionServiceApi.cpp @@ -0,0 +1,45 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include + +namespace AWSCore +{ + namespace ServiceAPI + { + constexpr char AwsAttributionServiceResultResponseKey[] = "statusCode"; + + bool AWSAtrributionSuccessResponse::OnJsonKey(const char* key, AWSCore::JsonReader& reader) + { + if (strcmp(key, AwsAttributionServiceResultResponseKey) == 0) + { + return reader.Accept(result); + } + return reader.Ignore(); + } + + bool AWSAttributionRequest::Parameters::BuildRequest(AWSCore::RequestBuilder& request) + { + bool ok = true; + ok = ok && request.WriteJsonBodyParameter(*this); + return ok; + } + + bool AWSAttributionRequest::Parameters::WriteJson(AWSCore::JsonWriter& writer) const + { + bool ok = true; + ok = ok && metric.SerializeToJson(writer); + return ok; + } + } +} diff --git a/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionManager.cpp b/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionManager.cpp new file mode 100644 index 0000000000..e7a6828903 --- /dev/null +++ b/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionManager.cpp @@ -0,0 +1,275 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +namespace AWSCore +{ + static constexpr const char* EngineVersionJsonKey = "O3DEVersion"; + + constexpr char EditorAWSPreferencesFileName[] = "editor_aws_preferences.setreg"; + constexpr char AWSAttributionSettingsPrefixKey[] = "/Amazon/AWS/Preferences"; + constexpr char AWSAttributionEnabledKey[] = "/Amazon/AWS/Preferences/AWSAttributionEnabled"; + constexpr char AWSAttributionDelaySecondsKey[] = "/Amazon/AWS/Preferences/AWSAttributionDelaySeconds"; + constexpr char AWSAttributionLastTimeStampKey[] = "/Amazon/AWS/Preferences/AWSAttributionLastTimeStamp"; + constexpr char AWSAttributionApiId[] = "xbzx78kvbk"; + constexpr char AWSAttributionChinaApiId[] = ""; + constexpr char AWSAttributionApiStage[] = "prod"; + + AWSAttributionManager::AWSAttributionManager() + { + m_settingsRegistry = AZStd::make_unique(); + } + + AWSAttributionManager::~AWSAttributionManager() + { + m_settingsRegistry.reset(); + } + + void AWSAttributionManager::Init() + { + } + + void AWSAttributionManager::MetricCheck() + { + if (ShouldGenerateMetric()) + { + // 1. Gather metadata and assemble metric + AttributionMetric metric; + UpdateMetric(metric); + // 2. Identify region and chose attribution endpoint + + // 3. Post metric + SubmitMetric(metric); + } + } + + bool AWSAttributionManager::ShouldGenerateMetric() const + { + AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); + AZ_Assert(fileIO, "File IO is not initialized."); + + // Resolve path to editor_aws_preferences.setreg + AZStd::string editorAWSPreferencesFilePath = + AZStd::string::format("@user@/%s/%s", AZ::SettingsRegistryInterface::RegistryFolder, EditorAWSPreferencesFileName); + AZStd::array resolvedPathAWSPreference{}; + if (!fileIO->ResolvePath(editorAWSPreferencesFilePath.c_str(), resolvedPathAWSPreference.data(), resolvedPathAWSPreference.size())) + { + AZ_Warning("AWSAttributionManager", false, "Error resolving path %s", resolvedPathAWSPreference.data()); + return false; + } + + if (fileIO->Exists(resolvedPathAWSPreference.data())) + { + m_settingsRegistry->MergeSettingsFile(resolvedPathAWSPreference.data(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, ""); + } + + bool awsAttributionEnabled = false; + if (!m_settingsRegistry->Get(awsAttributionEnabled, AWSAttributionEnabledKey)) + { + // If not found default to sending the metric. + awsAttributionEnabled = true; + } + + if (!awsAttributionEnabled) + { + return false; + } + + // If delayInSeconds is not found, set default to a day + AZ::u64 delayInSeconds = 0; + if (!m_settingsRegistry->Get(delayInSeconds, AWSAttributionDelaySecondsKey)) + { + AZ_Warning("AWSAttributionManager", false, "AWSAttribution delay key not found. Defaulting to delay to day"); + delayInSeconds = 86400; + m_settingsRegistry->Set(AWSAttributionDelaySecondsKey, delayInSeconds); + } + + AZ::u64 lastSendTimeStampSeconds = 0; + if (!m_settingsRegistry->Get(lastSendTimeStampSeconds, AWSAttributionLastTimeStampKey)) + { + // If last time stamp not found, assume this is the first attempt at sending. + return true; + } + + AZStd::chrono::seconds lastSendTimeStamp = AZStd::chrono::seconds(lastSendTimeStampSeconds); + AZStd::chrono::seconds secondsSinceLastSend = + AZStd::chrono::duration_cast(AZStd::chrono::system_clock::now().time_since_epoch()) - lastSendTimeStamp; + if (secondsSinceLastSend.count() >= delayInSeconds) + { + return true; + } + + return false; + } + + void AWSAttributionManager::SaveSettingsRegistryFile() + { + AZ::Job* job = AZ::CreateJobFunction( + [this]() + { + AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); + AZ_Assert(fileIO, "File IO is not initialized."); + + // Resolve path to editor_aws_preferences.setreg + AZStd::string editorPreferencesFilePath = AZStd::string::format("@user@/%s/%s", AZ::SettingsRegistryInterface::RegistryFolder, EditorAWSPreferencesFileName); + AZStd::array resolvedPath {}; + fileIO->ResolvePath(editorPreferencesFilePath.c_str(), resolvedPath.data(), resolvedPath.size()); + + AZ::SettingsRegistryMergeUtils::DumperSettings dumperSettings; + dumperSettings.m_prettifyOutput = true; + dumperSettings.m_jsonPointerPrefix = AWSAttributionSettingsPrefixKey; + + AZStd::string stringBuffer; + AZ::IO::ByteContainerStream stringStream(&stringBuffer); + if (!AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream( + *m_settingsRegistry, AWSAttributionSettingsPrefixKey, stringStream, dumperSettings)) + { + AZ_Warning( + "AWSAttributionManager", false, R"(Unable to save changes to the Editor AWS Preferences registry file at "%s"\n)", + resolvedPath.data()); + return; + } + + bool saved {}; + constexpr auto configurationMode = + AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY; + if (AZ::IO::SystemFile outputFile; outputFile.Open(resolvedPath.data(), configurationMode)) + { + saved = outputFile.Write(stringBuffer.data(), stringBuffer.size()) == stringBuffer.size(); + } + + AZ_Warning( + "AWSAttributionManager", saved, R"(Unable to save Editor AWS Preferences registry file to path "%s"\n)", + editorPreferencesFilePath.c_str()); + }, + true); + job->Start(); + + } + + void AWSAttributionManager::UpdateLastSend() + { + if (!m_settingsRegistry->Set(AWSAttributionLastTimeStampKey, + AZStd::chrono::duration_cast(AZStd::chrono::system_clock::now().time_since_epoch()).count())) + { + AZ_Warning("AWSAttributionManager", true, "Failed to set AWSAttributionLastTimeStamp"); + return; + } + SaveSettingsRegistryFile(); + } + + void AWSAttributionManager::SetApiEndpointAndRegion(AWSCore::ServiceAPI::AWSAttributionRequestJob::Config* config) + { + // Get default config for the process to check the region. + // Assumption to determine China region is the default profile is set to China region. + auto profile_name = Aws::Auth::GetConfigProfileName(); + Aws::Client::ClientConfiguration clientConfig(profile_name.c_str()); + AZStd::string apiId = AWSAttributionApiId; + + if (clientConfig.region == Aws::Region::CN_NORTH_1 || clientConfig.region == Aws::Region::CN_NORTHWEST_1) + { + config->region = Aws::Region::CN_NORTH_1; + apiId = AWSAttributionChinaApiId; + } + + config->region = Aws::Region::US_WEST_2; + config->endpointOverride = + AWSResourceMappingUtils::FormatRESTApiUrl(apiId, config->region.value().c_str(), AWSAttributionApiStage).c_str(); + } + + AZStd::string AWSAttributionManager::GetEngineVersion() const + { + AZStd::string engineVersion; + auto engineSettingsPath = AZ::IO::FixedMaxPath{ AZ::Utils::GetEnginePath() } / "engine.json"; + if (AZ::IO::SystemFile::Exists(engineSettingsPath.c_str())) + { + AZ::SettingsRegistryImpl settingsRegistry; + if (settingsRegistry.MergeSettingsFile( + engineSettingsPath.Native(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, AZ::SettingsRegistryMergeUtils::EngineSettingsRootKey)) + { + settingsRegistry.Get(engineVersion, AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::EngineSettingsRootKey) + "/" + EngineVersionJsonKey); + } + } + return engineVersion; + } + + AZStd::string AWSAttributionManager::GetPlatform() const + { + return AZ::GetPlatformName(AZ::g_currentPlatform); + } + + void AWSAttributionManager::GetActiveAWSGems(AZStd::vector& gems) + { + AZ::ModuleManagerRequestBus::Broadcast( + &AZ::ModuleManagerRequestBus::Events::EnumerateModules, + [this, &gems](const AZ::ModuleData& moduleData) + { + AZ::Entity* moduleEntity = moduleData.GetEntity(); + auto moduleEntityName = moduleEntity->GetName(); + if (moduleEntityName.contains("AWS")) + gems.push_back(moduleEntityName.substr(0, moduleEntityName.find_last_of("."))); + return true; + }); + } + + void AWSAttributionManager::UpdateMetric(AttributionMetric& metric) + { + AZStd::string engineVersion = this->GetEngineVersion(); + metric.SetO3DEVersion(engineVersion); + + AZStd::string platform = this->GetPlatform(); + metric.SetPlatform(platform, ""); + + AZStd::vector gemNames; + GetActiveAWSGems(gemNames); + for (AZStd::string& gemName : gemNames) + { + metric.AddActiveGem(gemName); + } + } + + void AWSAttributionManager::SubmitMetric(AttributionMetric& metric) + { + AWSCore::ServiceAPI::AWSAttributionRequestJob::Config* config = ServiceAPI::AWSAttributionRequestJob::GetDefaultConfig(); + SetApiEndpointAndRegion(config); + + ServiceAPI::AWSAttributionRequestJob* requestJob = ServiceAPI::AWSAttributionRequestJob::Create( + [this](ServiceAPI::AWSAttributionRequestJob* successJob) + { + AZ_UNUSED(successJob); + + UpdateLastSend(); + AZ_Printf("AWSAttributionManager", "AWSAttribution metric submit success"); + + }, {}, config); + + requestJob->parameters.metric = metric; + requestJob->Start(); + } + +} // namespace AWSCore diff --git a/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionMetric.cpp b/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionMetric.cpp new file mode 100644 index 0000000000..6ad322995e --- /dev/null +++ b/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionMetric.cpp @@ -0,0 +1,106 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include +#include + +#pragma warning(disable : 4996) + +namespace AWSCore +{ + AttributionMetric::AttributionMetric(const AZStd::string& timestamp) + : m_version("1.1") + , m_timestamp(timestamp) + { + } + + AttributionMetric::AttributionMetric() + : m_version("1.1") + { + m_timestamp = AttributionMetric::GenerateTimeStamp(); + } + + void AttributionMetric::SetO3DEVersion(const AZStd::string& version) + { + m_o3deVersion = version; + } + + void AttributionMetric::SetPlatform(const AZStd::string& platform, const AZStd::string& platformVersion) + { + m_platform = platform; + m_platformVersion = platformVersion; + } + + void AttributionMetric::AddActiveGem(const AZStd::string& gemName) + { + m_activeAWSGems.push_back(gemName); + } + + AZStd::string AttributionMetric::SerializeToJson() + { + std::stringstream stringStream; + AWSCore::JsonOutputStream jsonStream{stringStream}; + AWSCore::JsonWriter writer{jsonStream}; + + SerializeToJson(writer); + + return stringStream.str().c_str(); + } + + bool AttributionMetric::SerializeToJson(AWSCore::JsonWriter& writer) const + { + bool ok = true; + ok = ok && writer.StartObject(); + + writer.Write(AwsAttributionAttributeKeyVersion, m_version.c_str()); + writer.Write(AwsAttributionAttributeKeyO3DEVersion, m_o3deVersion.c_str()); + writer.Write(AwsAttributionAttributeKeyPlatform, m_platform.c_str()); + writer.Write(AwsAttributionAttributeKeyPlatformVersion, m_platformVersion.c_str()); + + if (m_activeAWSGems.size() > 0) + { + writer.Key(AwsAttributionAttributeKeyActiveAWSGems); + writer.StartArray(); // to store Array of objects + for (auto& iter : m_activeAWSGems) + { + writer.String(iter.c_str()); + } + writer.EndArray(); + } + + writer.Write(AwsAttributionAttributeKeyTimestamp, m_timestamp.c_str()); + + ok = ok && writer.EndObject(); + return ok; + } + + bool AttributionMetric::ReadFromJson(rapidjson::Value& metricsObjVal) + { + AZ_UNUSED(metricsObjVal); + return false; + } + + AZStd::string AttributionMetric::GenerateTimeStamp() + { + // Timestamp format is using the UTC ISO8601 format + // TODO: Move to a general util as Metrics has similar requirement + time_t now; + time(&now); + char buffer[50]; + strftime(buffer, sizeof(buffer), "%FT%TZ", gmtime(&now)); + + return buffer; + } + +} // namespace AWSCore diff --git a/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionSystemComponent.cpp b/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionSystemComponent.cpp new file mode 100644 index 0000000000..ea388972d3 --- /dev/null +++ b/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionSystemComponent.cpp @@ -0,0 +1,81 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace AWSCore +{ + + AWSAttributionSystemComponent::AWSAttributionSystemComponent() + : m_manager(AZStd::make_unique()) + { + } + + void AWSAttributionSystemComponent::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serialize = azrtti_cast(context)) + { + serialize->Class()->Version(0); + if (AZ::EditContext* ec = serialize->GetEditContext()) + { + ec->Class("AWSCoreAttributions", "Generates operation metrics for AWSCore gem") + ->ClassElement(AZ::Edit::ClassElements::EditorData, ""); + } + } + } + + void AWSAttributionSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("AWSCoreAttributionService")); + } + + void AWSAttributionSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC_CE("AWSCoreAttributionService")); + } + + void AWSAttributionSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + required.push_back(AZ_CRC_CE("AWSCoreService")); + } + + void AWSAttributionSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + AZ_UNUSED(dependent); + } + + void AWSAttributionSystemComponent::Init() + { + // load config if required - ie check if attributions should be generated and pass to manager + m_manager->Init(); + } + + void AWSAttributionSystemComponent::Activate() + { + m_manager->MetricCheck(); + } + + void AWSAttributionSystemComponent::Deactivate() + { + m_manager.reset(); + } + +} // namespace AWSCore + diff --git a/Gems/AWSCore/Code/Tests/Editor/Attribution/AWSAttributionServiceApiTest.cpp b/Gems/AWSCore/Code/Tests/Editor/Attribution/AWSAttributionServiceApiTest.cpp new file mode 100644 index 0000000000..affa1ad69c --- /dev/null +++ b/Gems/AWSCore/Code/Tests/Editor/Attribution/AWSAttributionServiceApiTest.cpp @@ -0,0 +1,85 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include + +#include + +using namespace AWSCore; + +namespace AWSCoreUnitTest +{ + class JsonReaderMock + : public AWSCore::JsonReader + { + public: + MOCK_METHOD0(Ignore, bool()); + MOCK_METHOD1(Accept, bool(bool& target)); + MOCK_METHOD1(Accept, bool(AZStd::string& target)); + MOCK_METHOD1(Accept, bool(int& target)); + MOCK_METHOD1(Accept, bool(unsigned& target)); + MOCK_METHOD1(Accept, bool(int64_t& target)); + MOCK_METHOD1(Accept, bool(uint64_t& target)); + MOCK_METHOD1(Accept, bool(double& target)); + MOCK_METHOD1(Accept, bool(AWSCore::JsonKeyHandler keyHandler)); + MOCK_METHOD1(Accept, bool(AWSCore::JsonArrayHandler arrayHandler)); + }; + + class AWSAttributionServiceApiTest + : public UnitTest::ScopedAllocatorSetupFixture + { + public: + testing::NiceMock JsonReader; + }; + + TEST_F(AWSAttributionServiceApiTest, AWSAtrributionSuccessResponse_Serialization) + { + ServiceAPI::AWSAtrributionSuccessResponse response; + response.result = "ok"; + + EXPECT_CALL(JsonReader, Accept(response.result)).Times(1); + EXPECT_CALL(JsonReader, Ignore()).Times(0); + + response.OnJsonKey("statusCode", JsonReader); + } + + TEST_F(AWSAttributionServiceApiTest, AWSAtrributionSuccessResponse_Serialization_Ignore) + { + ServiceAPI::AWSAtrributionSuccessResponse response; + response.result = "ok"; + + EXPECT_CALL(JsonReader, Accept(response.result)).Times(0); + EXPECT_CALL(JsonReader, Ignore()).Times(1); + + response.OnJsonKey("", JsonReader); + } + + TEST_F(AWSAttributionServiceApiTest, BuildRequestBody_PostProducerEventsRequest_SerializedMetricsQueue) + { + ServiceAPI::AWSAttributionRequest request; + request.parameters.metric = AttributionMetric(); + + AWSCore::RequestBuilder requestBuilder{}; + EXPECT_TRUE(request.parameters.BuildRequest(requestBuilder)); + std::shared_ptr bodyContent = requestBuilder.GetBodyContent(); + EXPECT_TRUE(bodyContent != nullptr); + + AZStd::string bodyString; + std::istreambuf_iterator eos; + bodyString = AZStd::string{ std::istreambuf_iterator(*bodyContent), eos }; + AZ_Printf("AWSAttributionServiceApiTest", bodyString.c_str()); + EXPECT_TRUE(bodyString.find(AZStd::string::format("{\"%s\":\"1.1\"", AwsAttributionAttributeKeyVersion)) != AZStd::string::npos); + } +} diff --git a/Gems/AWSCore/Code/Tests/Editor/Attribution/AWSCoreAttributionManagerTest.cpp b/Gems/AWSCore/Code/Tests/Editor/Attribution/AWSCoreAttributionManagerTest.cpp new file mode 100644 index 0000000000..298c250086 --- /dev/null +++ b/Gems/AWSCore/Code/Tests/Editor/Attribution/AWSCoreAttributionManagerTest.cpp @@ -0,0 +1,414 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +using namespace AWSCore; + +namespace AWSAttributionUnitTest +{ + class ModuleDataMock: + public AZ::ModuleData + { + public: + AZStd::shared_ptr m_entity; + ModuleDataMock(AZStd::string name) + { + m_entity = AZStd::make_shared(); + m_entity->SetName(name); + } + virtual ~ModuleDataMock() + { + m_entity.reset(); + } + + AZ::DynamicModuleHandle* GetDynamicModuleHandle() const override + { + return nullptr; + } + /// Get the handle to the module class + AZ::Module* GetModule() const override + { + return nullptr; + } + /// Get the entity this module uses as a System Entity + AZ::Entity* GetEntity() const override + { + return m_entity.get(); + } + /// Get the debug name of the module + const char* GetDebugName() const override + { + return m_entity->GetName().c_str(); + } + }; + + class ModuleManagerRequestBusMock + : public AZ::ModuleManagerRequestBus::Handler + { + public: + + void EnumerateModulesMock(AZ::ModuleManagerRequests::EnumerateModulesCallback perModuleCallback) + { + auto data = ModuleDataMock("AWSCore.Editor.dll"); + perModuleCallback(data); + data = ModuleDataMock("AWSClientAuth.so"); + perModuleCallback(data); + } + + ModuleManagerRequestBusMock() + { + AZ::ModuleManagerRequestBus::Handler::BusConnect(); + ON_CALL(*this, EnumerateModules(testing::_)).WillByDefault(testing::Invoke(this, &ModuleManagerRequestBusMock::EnumerateModulesMock)); + } + + ~ModuleManagerRequestBusMock() + { + AZ::ModuleManagerRequestBus::Handler::BusDisconnect(); + } + + MOCK_METHOD1(EnumerateModules, void(AZ::ModuleManagerRequests::EnumerateModulesCallback perModuleCallback)); + MOCK_METHOD3(LoadDynamicModule, AZ::ModuleManagerRequests::LoadModuleOutcome(const char* modulePath, AZ::ModuleInitializationSteps lastStepToPerform, bool maintainReference)); + MOCK_METHOD3(LoadDynamicModules, AZ::ModuleManagerRequests::LoadModulesResult(const AZ::ModuleDescriptorList& modules, AZ::ModuleInitializationSteps lastStepToPerform, bool maintainReferences)); + MOCK_METHOD2(LoadStaticModules, AZ::ModuleManagerRequests::LoadModulesResult(AZ::CreateStaticModulesCallback staticModulesCb, AZ::ModuleInitializationSteps lastStepToPerform)); + MOCK_METHOD1(IsModuleLoaded, bool(const char* modulePath)); + }; + + class AWSAttributionManagerMock + : public AWSAttributionManager + { + public: + using AWSAttributionManager::SubmitMetric; + using AWSAttributionManager::UpdateMetric; + using AWSAttributionManager::SetApiEndpointAndRegion; + + + AWSAttributionManagerMock() + { + ON_CALL(*this, SubmitMetric(testing::_)).WillByDefault(testing::Invoke(this, &AWSAttributionManagerMock::SubmitMetricMock)); + } + + MOCK_METHOD1(SubmitMetric, void(AttributionMetric& metric)); + + void SubmitMetricMock(AttributionMetric& metric) + { + AZ_UNUSED(metric); + UpdateLastSend(); + } + }; + + class AttributionManagerTest + : public AWSCoreFixture + { + public: + + virtual ~AttributionManagerTest() = default; + + protected: + AZStd::shared_ptr m_serializeContext; + AZStd::unique_ptr m_registrationContext; + AZStd::shared_ptr m_settingsRegistry; + AZStd::unique_ptr m_jobContext; + AZStd::unique_ptr m_jobCancelGroup; + AZStd::unique_ptr m_jobManager; + AZStd::array m_resolvedSettingsPath; + ModuleManagerRequestBusMock m_moduleManagerRequestBusMock; + + void SetUp() override + { + AWSCoreFixture::SetUp(); + + char rootPath[AZ_MAX_PATH_LEN]; + AZ::Utils::GetExecutableDirectory(rootPath, AZ_MAX_PATH_LEN); + m_localFileIO->SetAlias("@user@", AZ_TRAIT_TEST_ROOT_FOLDER); + + m_localFileIO->ResolvePath("@user@/Registry/", m_resolvedSettingsPath.data(), m_resolvedSettingsPath.size()); + AZ::IO::SystemFile::CreateDir(m_resolvedSettingsPath.data()); + + m_localFileIO->ResolvePath("@user@/Registry/editor_aws_preferences.setreg", m_resolvedSettingsPath.data(), m_resolvedSettingsPath.size()); + + m_serializeContext = AZStd::make_unique(); + + AZ::JsonSystemComponent::Reflect(m_registrationContext.get()); + + m_settingsRegistry = AZStd::make_unique(); + + m_settingsRegistry->SetContext(m_serializeContext.get()); + m_settingsRegistry->SetContext(m_registrationContext.get()); + + AZ::SettingsRegistry::Register(m_settingsRegistry.get()); + + AZ::JobManagerDesc jobManagerDesc; + AZ::JobManagerThreadDesc threadDesc; + + m_jobManager.reset(aznew AZ::JobManager(jobManagerDesc)); + m_jobCancelGroup.reset(aznew AZ::JobCancelGroup()); + jobManagerDesc.m_workerThreads.push_back(threadDesc); + m_jobContext.reset(aznew AZ::JobContext(*m_jobManager, *m_jobCancelGroup)); + AZ::JobContext::SetGlobalContext(m_jobContext.get()); + } + + void TearDown() override + { + AZ::JobContext::SetGlobalContext(nullptr); + m_jobContext.reset(); + m_jobCancelGroup.reset(); + m_jobManager.reset(); + + AZ::SettingsRegistry::Unregister(m_settingsRegistry.get()); + + m_settingsRegistry.reset(); + m_serializeContext.reset(); + m_registrationContext.reset(); + + m_localFileIO->ResolvePath("@user@/Registry/", m_resolvedSettingsPath.data(), m_resolvedSettingsPath.size()); + AZ::IO::SystemFile::DeleteDir(m_resolvedSettingsPath.data()); + + delete AZ::IO::FileIOBase::GetInstance(); + AZ::IO::FileIOBase::SetInstance(nullptr); + + AWSCoreFixture::TearDown(); + } + }; + + TEST_F(AttributionManagerTest, MetricsSettings_AttributionDisabled_SkipsSend) + { + // GIVEN + AWSAttributionManagerMock manager; + manager.Init(); + + CreateFile(m_resolvedSettingsPath.data(), R"({ + "Amazon": { + "AWS": { + "Preferences": { + "AWSAttributionEnabled": false, + "AWSAttributionDelaySeconds": 30 + } + } + } + })"); + + EXPECT_CALL(manager, SubmitMetric(testing::_)).Times(0); + EXPECT_CALL(m_moduleManagerRequestBusMock, EnumerateModules(testing::_)).Times(0); + + // WHEN + manager.MetricCheck(); + + // THEN + m_settingsRegistry->MergeSettingsFile(m_resolvedSettingsPath.data(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, ""); + AZ::u64 timeStamp = 0; + m_settingsRegistry->Get(timeStamp, "/Amazon/AWS/Preferences/AWSAttributionLastTimeStamp"); + ASSERT_TRUE(timeStamp == 0); + + RemoveFile(m_resolvedSettingsPath.data()); + } + + TEST_F(AttributionManagerTest, AttributionEnabled_NoPreviousTimeStamp_SendSuccess) + { + // GIVEN + AWSAttributionManagerMock manager; + manager.Init(); + + CreateFile(m_resolvedSettingsPath.data(), R"({ + "Amazon": { + "AWS": { + "Preferences": { + "AWSAttributionEnabled": true, + "AWSAttributionDelaySeconds": 30, + } + } + } + })"); + + EXPECT_CALL(manager, SubmitMetric(testing::_)).Times(1); + EXPECT_CALL(m_moduleManagerRequestBusMock, EnumerateModules(testing::_)).Times(1); + + // WHEN + manager.MetricCheck(); + + // THEN + m_settingsRegistry->MergeSettingsFile(m_resolvedSettingsPath.data(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, ""); + AZ::u64 timeStamp = 0; + m_settingsRegistry->Get(timeStamp, "/Amazon/AWS/Preferences/AWSAttributionLastTimeStamp"); + ASSERT_TRUE(timeStamp > 0); + + + RemoveFile(m_resolvedSettingsPath.data()); + } + + TEST_F(AttributionManagerTest, AttributionEnabled_ValidPreviousTimeStamp_SendSuccess) + { + // GIVEN + AWSAttributionManagerMock manager; + manager.Init(); + + CreateFile(m_resolvedSettingsPath.data(), R"({ + "Amazon": { + "AWS": { + "Preferences": { + "AWSAttributionEnabled": true, + "AWSAttributionDelaySeconds": 30, + "AWSAttributionLastTimeStamp": 629400 + } + } + } + })"); + + EXPECT_CALL(manager, SubmitMetric(testing::_)).Times(1); + EXPECT_CALL(m_moduleManagerRequestBusMock, EnumerateModules(testing::_)).Times(1); + + // WHEN + manager.MetricCheck(); + + // THEN + m_settingsRegistry->MergeSettingsFile(m_resolvedSettingsPath.data(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, ""); + AZ::u64 timeStamp = 0; + m_settingsRegistry->Get(timeStamp, "/Amazon/AWS/Preferences/AWSAttributionLastTimeStamp"); + ASSERT_TRUE(timeStamp > 0); + + RemoveFile(m_resolvedSettingsPath.data()); + } + + TEST_F(AttributionManagerTest, AttributionEnabled_DelayNotSatisfied_SendFail) + { + // GIVEN + AWSAttributionManagerMock manager; + manager.Init(); + + + CreateFile(m_resolvedSettingsPath.data(), R"({ + "Amazon": { + "AWS": { + "Preferences": { + "AWSAttributionEnabled": true, + "AWSAttributionDelaySeconds": 300, + "AWSAttributionLastTimeStamp": 0 + } + } + } + })"); + + AZ::u64 delayInSeconds = AZStd::chrono::duration_cast(AZStd::chrono::system_clock::now().time_since_epoch()).count(); + ASSERT_TRUE(m_settingsRegistry->Set("/Amazon/AWS/Preferences/AWSAttributionLastTimeStamp", delayInSeconds)); + + EXPECT_CALL(manager, SubmitMetric(testing::_)).Times(1); + EXPECT_CALL(m_moduleManagerRequestBusMock, EnumerateModules(testing::_)).Times(1); + + // WHEN + manager.MetricCheck(); + + // THEN + m_settingsRegistry->MergeSettingsFile(m_resolvedSettingsPath.data(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, ""); + AZ::u64 timeStamp = 0; + m_settingsRegistry->Get(timeStamp, "/Amazon/AWS/Preferences/AWSAttributionLastTimeStamp"); + ASSERT_TRUE(timeStamp == delayInSeconds); + + RemoveFile(m_resolvedSettingsPath.data()); + } + + TEST_F(AttributionManagerTest, AttributionEnabledNotFound_SendSuccess) + { + // GIVEN + AWSAttributionManagerMock manager; + manager.Init(); + + CreateFile(m_resolvedSettingsPath.data(), R"({ + "Amazon": { + "AWS": { + "Preferences": { + } + } + } + })"); + + EXPECT_CALL(manager, SubmitMetric(testing::_)).Times(1); + EXPECT_CALL(m_moduleManagerRequestBusMock, EnumerateModules(testing::_)).Times(1); + + // WHEN + manager.MetricCheck(); + + // THEN + m_settingsRegistry->MergeSettingsFile(m_resolvedSettingsPath.data(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, ""); + AZ::u64 timeStamp = 0; + m_settingsRegistry->Get(timeStamp, "/Amazon/AWS/Preferences/AWSAttributionLastTimeStamp"); + ASSERT_TRUE(timeStamp != 0); + + RemoveFile(m_resolvedSettingsPath.data()); + } + + TEST_F(AttributionManagerTest, SetApiEndpointAndRegion_Success) + { + // GIVEN + AWSAttributionManagerMock manager; + AWSCore::ServiceAPI::AWSAttributionRequestJob::Config* config = aznew AWSCore::ServiceAPI::AWSAttributionRequestJob::Config(); + + // WHEN + manager.SetApiEndpointAndRegion(config); + + // THEN + ASSERT_TRUE(config->region == Aws::Region::US_WEST_2); + ASSERT_TRUE(config->endpointOverride->find("execute-api.us-west-2.amazonaws.com") != Aws::String::npos); + + delete config; + } + + TEST_F(AttributionManagerTest, UpdateMetric_Success) + { + // GIVEN + AWSAttributionManagerMock manager; + AttributionMetric metric; + + AZStd::array engineJsonPath; + m_localFileIO->ResolvePath("@user@/Registry/engine.json", engineJsonPath.data(), engineJsonPath.size()); + CreateFile(engineJsonPath.data(), R"({"O3DEVersion": "1.0.0.0"})"); + + m_localFileIO->ResolvePath("@user@/Registry/", engineJsonPath.data(), engineJsonPath.size()); + m_settingsRegistry->Set(AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder, engineJsonPath.data()); + + EXPECT_CALL(m_moduleManagerRequestBusMock, EnumerateModules(testing::_)).Times(1); + + // WHEN + manager.UpdateMetric(metric); + + // THEN + AZStd::string serializedMetricValue = metric.SerializeToJson(); + ASSERT_TRUE(serializedMetricValue.find("\"o3de_version\":\"1.0.0.0\"") != AZStd::string::npos); + ASSERT_TRUE(serializedMetricValue.find(AZ::GetPlatformName(AZ::g_currentPlatform)) != AZStd::string::npos); + ASSERT_TRUE(serializedMetricValue.find("AWSCore.Editor") != AZStd::string::npos); + ASSERT_TRUE(serializedMetricValue.find("AWSClientAuth") != AZStd::string::npos); + + RemoveFile(engineJsonPath.data()); + } + +} // namespace AWSCoreUnitTest diff --git a/Gems/AWSCore/Code/Tests/Editor/Attribution/AWSCoreAttributionMetricTest.cpp b/Gems/AWSCore/Code/Tests/Editor/Attribution/AWSCoreAttributionMetricTest.cpp new file mode 100644 index 0000000000..c93b8bb08e --- /dev/null +++ b/Gems/AWSCore/Code/Tests/Editor/Attribution/AWSCoreAttributionMetricTest.cpp @@ -0,0 +1,50 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include + +namespace AWSCore +{ + using AttributionMetricTest = UnitTest::ScopedAllocatorSetupFixture; + + TEST_F(AttributionMetricTest, Contruction_Test) + { + AZStd::string timestamp = AttributionMetric::GenerateTimeStamp(); + AttributionMetric metric(timestamp); + + AZStd::string serializedMetric = AZStd::string::format( + "{\"version\":\"1.1\",\"o3de_version\":\"\",\"platform\":\"\",\"platform_version\":\"\",\"timestamp\":\"%s\"}", timestamp.c_str()); + ASSERT_EQ(metric.SerializeToJson(), serializedMetric); + } + + TEST_F(AttributionMetricTest, AddActiveGems) + { + AZStd::string timestamp = AttributionMetric::GenerateTimeStamp(); + AttributionMetric metric(timestamp); + + AZStd::string gem1 = "AWSGem1"; + AZStd::string gem2 = "AWSGem2"; + + metric.AddActiveGem(gem1); + metric.AddActiveGem(gem2); + + AZStd::string serializedMetric = AZStd::string::format( + "{\"version\":\"1.1\",\"o3de_version\":\"\",\"platform\":\"\",\"platform_version\":\"\",\"aws_gems\":[\"%s\",\"%s\"],\"timestamp\":\"%s\"}", + gem1.c_str(), gem2.c_str(), timestamp.c_str()); + + AZStd::string actualValue = metric.SerializeToJson(); + ASSERT_EQ(actualValue, serializedMetric); + } + +} // namespace AWSCore diff --git a/Gems/AWSCore/Code/Tests/Editor/Attribution/AWSCoreAttributionSystemComponentTest.cpp b/Gems/AWSCore/Code/Tests/Editor/Attribution/AWSCoreAttributionSystemComponentTest.cpp new file mode 100644 index 0000000000..4419c78467 --- /dev/null +++ b/Gems/AWSCore/Code/Tests/Editor/Attribution/AWSCoreAttributionSystemComponentTest.cpp @@ -0,0 +1,132 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace AWSCore; + +namespace AWSCoreUnitTest +{ + class AWSCoreSystemComponentMock : public AZ::Component + { + public: + AZ_COMPONENT(AWSCoreSystemComponentMock, "{5F48030D-EB59-4820-BC65-69EC7CC6C119}"); + + static void Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serialize = azrtti_cast(context)) + { + serialize->Class()->Version(0); + + if (AZ::EditContext* ec = serialize->GetEditContext()) + { + ec->Class("AWSCoreMock", "Adds core support for working with AWS") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System")) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true); + } + } + } + + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("AWSCoreService")); + } + + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + AZ_UNUSED(incompatible); + } + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + AZ_UNUSED(required); + } + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + AZ_UNUSED(dependent); + } + + ~AWSCoreSystemComponentMock() = default; + + MOCK_METHOD0(Init, void()); + MOCK_METHOD0(Activate, void()); + MOCK_METHOD0(Deactivate, void()); + }; + + class AWSAttributionSystemComponentTest : public AWSCoreFixture + { + void SetUp() override + { + AWSCoreFixture::SetUp(); + m_serializeContext = AZStd::make_unique(); + m_serializeContext->CreateEditContext(); + m_behaviorContext = AZStd::make_unique(); + + m_awsCoreComponentDescriptor.reset(AWSCoreSystemComponentMock::CreateDescriptor()); + m_awsCoreComponentDescriptor->Reflect(m_serializeContext.get()); + m_awsCoreComponentDescriptor->Reflect(m_behaviorContext.get()); + + m_componentDescriptor.reset(AWSAttributionSystemComponent::CreateDescriptor()); + m_componentDescriptor->Reflect(m_serializeContext.get()); + m_componentDescriptor->Reflect(m_behaviorContext.get()); + + m_entity = aznew AZ::Entity(); + m_awsCoreSystemComponentMock = aznew testing::NiceMock(); + m_entity->AddComponent(m_awsCoreSystemComponentMock); + m_attributionSystemsComponent.reset(m_entity->CreateComponent()); + } + + void TearDown() override + { + m_entity->Deactivate(); + m_entity->RemoveComponent(m_attributionSystemsComponent.get()); + m_entity->RemoveComponent(m_awsCoreSystemComponentMock); + delete m_entity; + m_entity = nullptr; + + m_attributionSystemsComponent.reset(); + delete m_awsCoreSystemComponentMock; + m_awsCoreComponentDescriptor.reset(); + m_componentDescriptor.reset(); + m_behaviorContext.reset(); + m_serializeContext.reset(); + AWSCoreFixture::TearDown(); + } + + public: + AZStd::unique_ptr m_attributionSystemsComponent; + testing::NiceMock* m_awsCoreSystemComponentMock; + AZ::Entity* m_entity; + + private: + AZStd::unique_ptr m_serializeContext; + AZStd::unique_ptr m_behaviorContext; + AZStd::unique_ptr m_componentDescriptor; + AZStd::unique_ptr m_awsCoreComponentDescriptor; + }; + + TEST_F(AWSAttributionSystemComponentTest, SystemComponentInitActivate_Success) + { + m_entity->Init(); + m_entity->Activate(); + } +} + + diff --git a/Gems/AWSCore/Code/Tests/TestFramework/AWSCoreFixture.h b/Gems/AWSCore/Code/Tests/TestFramework/AWSCoreFixture.h index 1810af9c82..d921ffaf07 100644 --- a/Gems/AWSCore/Code/Tests/TestFramework/AWSCoreFixture.h +++ b/Gems/AWSCore/Code/Tests/TestFramework/AWSCoreFixture.h @@ -127,13 +127,40 @@ public: void TearDown() override { AZ::IO::FileIOBase::SetInstance(nullptr); - delete m_localFileIO; - AZ::IO::FileIOBase::SetInstance(m_otherFileIO); + + if (m_otherFileIO) + { + delete m_localFileIO; + AZ::IO::FileIOBase::SetInstance(m_otherFileIO); + } AZ::AllocatorInstance::Destroy(); AZ::AllocatorInstance::Destroy(); } + bool CreateFile(const AZStd::string& filePath, const AZStd::string& content) + { + AZ::IO::HandleType fileHandle; + if (!m_localFileIO->Open(filePath.c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeText, fileHandle)) + { + return false; + } + + m_localFileIO->Write(fileHandle, content.c_str(), content.size()); + m_localFileIO->Close(fileHandle); + return true; + } + + bool RemoveFile(const AZStd::string& filePath) + { + if (m_localFileIO->Exists(filePath.c_str())) + { + return m_localFileIO->Remove(filePath.c_str()); + } + + return true; + } + AZ::IO::FileIOBase* m_localFileIO = nullptr; private: diff --git a/Gems/AWSCore/Code/awscore_editor_files.cmake b/Gems/AWSCore/Code/awscore_editor_files.cmake index 13bfbb6102..6e16fc7d27 100644 --- a/Gems/AWSCore/Code/awscore_editor_files.cmake +++ b/Gems/AWSCore/Code/awscore_editor_files.cmake @@ -11,6 +11,11 @@ set(FILES Include/Private/AWSCoreEditorSystemComponent.h + Include/Private/Editor/Attribution/AWSCoreAttributionConstant.h + Include/Private/Editor/Attribution/AWSCoreAttributionMetric.h + Include/Private/Editor/Attribution/AWSCoreAttributionManager.h + Include/Private/Editor/Attribution/AWSCoreAttributionSystemComponent.h + Include/Private/Editor/Attribution/AWSAttributionServiceApi.h Include/Private/Editor/AWSCoreEditorManager.h Include/Private/Editor/Constants/AWSCoreEditorMenuLinks.h Include/Private/Editor/Constants/AWSCoreEditorMenuNames.h @@ -18,6 +23,10 @@ set(FILES Include/Private/Editor/UI/AWSCoreResourceMappingToolAction.h Source/AWSCoreEditorSystemComponent.cpp Source/Editor/AWSCoreEditorManager.cpp + Source/Editor/Attribution/AWSCoreAttributionMetric.cpp + Source/Editor/Attribution/AWSCoreAttributionManager.cpp + Source/Editor/Attribution/AWSCoreAttributionSystemComponent.cpp + Source/Editor/Attribution/AWSAttributionServiceApi.cpp Source/Editor/UI/AWSCoreEditorMenu.cpp Source/Editor/UI/AWSCoreResourceMappingToolAction.cpp ) diff --git a/Gems/AWSCore/Code/awscore_editor_tests_files.cmake b/Gems/AWSCore/Code/awscore_editor_tests_files.cmake index ff89b6b5bd..bba830d5d1 100644 --- a/Gems/AWSCore/Code/awscore_editor_tests_files.cmake +++ b/Gems/AWSCore/Code/awscore_editor_tests_files.cmake @@ -11,6 +11,10 @@ set(FILES Tests/AWSCoreEditorSystemComponentTest.cpp + Tests/Editor/Attribution/AWSCoreAttributionManagerTest.cpp + Tests/Editor/Attribution/AWSCoreAttributionMetricTest.cpp + Tests/Editor/Attribution/AWSCoreAttributionSystemComponentTest.cpp + Tests/Editor/Attribution/AWSAttributionServiceApiTest.cpp Tests/Editor/UI/AWSCoreEditorMenuTest.cpp Tests/Editor/UI/AWSCoreEditorUIFixture.h Tests/Editor/UI/AWSCoreResourceMappingToolActionTest.cpp diff --git a/Gems/Atom/Feature/Common/Assets/Materials/OcclusionCullingPlane/OcclusionCullingPlaneTransparentVisualization.material b/Gems/Atom/Feature/Common/Assets/Materials/OcclusionCullingPlane/OcclusionCullingPlaneTransparentVisualization.material new file mode 100644 index 0000000000..981e392eef --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Materials/OcclusionCullingPlane/OcclusionCullingPlaneTransparentVisualization.material @@ -0,0 +1,22 @@ +{ + "materialType": "Materials\\Types\\StandardPBR.materialtype", + "propertyLayoutVersion": 3, + "properties": { + "general": { + "enableShadows": false, + "enableDirectionalLights": false, + "enablePunctualLights": false, + "enableAreaLights": false, + "enableIBL": true + }, + "baseColor": { + "color": [ 0.0, 1.0, 0.0 ] + }, + "opacity": { + "alphaSource": "None", + "doubleSided": true, + "factor": 0.25, + "mode": "TintedTransparent" + } + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Materials/OcclusionCullingPlane/OcclusionCullingPlaneVisualization.material b/Gems/Atom/Feature/Common/Assets/Materials/OcclusionCullingPlane/OcclusionCullingPlaneVisualization.material new file mode 100644 index 0000000000..4446cc2d9d --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Materials/OcclusionCullingPlane/OcclusionCullingPlaneVisualization.material @@ -0,0 +1,22 @@ +{ + "materialType": "Materials\\Types\\StandardPBR.materialtype", + "propertyLayoutVersion": 3, + "properties": { + "general": { + "enableShadows": false, + "enableDirectionalLights": false, + "enablePunctualLights": false, + "enableAreaLights": false, + "enableIBL": true + }, + "baseColor": { + "color": [ 0.0, 1.0, 0.0 ] + }, + "opacity": { + "alphaSource": "None", + "doubleSided": true, + "factor": 1.0, + "mode": "TintedTransparent" + } + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Models/OcclusionCullingPlane.fbx b/Gems/Atom/Feature/Common/Assets/Models/OcclusionCullingPlane.fbx new file mode 100644 index 0000000000..f91d1015f9 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Models/OcclusionCullingPlane.fbx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75cdf73fcb9698a76a38294a1cf927a4fb41a34869e0429e1f02bf8d361a7258 +size 20400 diff --git a/Gems/Atom/Feature/Common/Assets/Passes/ContrastAdaptiveSharpening.pass b/Gems/Atom/Feature/Common/Assets/Passes/ContrastAdaptiveSharpening.pass new file mode 100644 index 0000000000..44ab6f4a52 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Passes/ContrastAdaptiveSharpening.pass @@ -0,0 +1,75 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "PassAsset", + "ClassData": { + "PassTemplate": { + "Name": "ContrastAdaptiveSharpeningTemplate", + "PassClass": "ComputePass", + "Slots": [ + { + "Name": "InputColor", + "SlotType": "Input", + "ShaderInputName": "m_inputColor", + "ScopeAttachmentUsage": "Shader" + }, + { + "Name": "OutputColor", + "SlotType": "Output", + "ShaderInputName": "m_outputColor", + "ScopeAttachmentUsage": "Shader" + } + ], + "ImageAttachments": [ + { + "Name": "Output", + "FormatSource": { + "Pass": "This", + "Attachment": "InputColor" + }, + "SizeSource": { + "Source": { + "Pass": "This", + "Attachment": "InputColor" + } + }, + "ImageDescriptor": { + "Format": "R16G16B16A16_FLOAT", + "BindFlags": "3", + "SharedQueueMask": "1" + } + } + ], + "Connections": [ + { + "LocalSlot": "OutputColor", + "AttachmentRef": { + "Pass": "This", + "Attachment": "Output" + } + } + ], + "FallbackConnections": [ + { + "Input": "InputColor", + "Output": "OutputColor" + } + ], + "PassData": { + "$type": "ComputePassData", + "ShaderAsset": { + "FilePath": "Shaders/Postprocessing/ContrastAdaptiveSharpening.shader" + }, + "Make Fullscreen Pass": true, + "ShaderDataMappings": { + "FloatMappings": [ + { + "Name": "m_strength", + "Value": 0.25 + } + ] + } + } + } + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Passes/MainPipeline.pass b/Gems/Atom/Feature/Common/Assets/Passes/MainPipeline.pass index af7408b48c..b2e0cf088e 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/MainPipeline.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/MainPipeline.pass @@ -341,6 +341,13 @@ "Attachment": "Depth" } }, + { + "LocalSlot": "MotionVectors", + "AttachmentRef": { + "Pass": "MotionVectorPass", + "Attachment": "MotionVectorOutput" + } + }, { "LocalSlot": "SwapChainOutput", "AttachmentRef": { diff --git a/Gems/Atom/Feature/Common/Assets/Passes/MeshMotionVector.pass b/Gems/Atom/Feature/Common/Assets/Passes/MeshMotionVector.pass index 57600440b4..4c14fd9b3f 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/MeshMotionVector.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/MeshMotionVector.pass @@ -13,22 +13,11 @@ "SlotType": "Input", "ScopeAttachmentUsage": "InputAssembly" }, - // Outputs... + // Input/Output... { - "Name": "Output", - "SlotType": "Output", - "ScopeAttachmentUsage": "RenderTarget", - "LoadStoreAction": { - "ClearValue": { - "Value": [ - 0.0, - 0.0, - 0.0, - {} - ] - }, - "LoadAction": "Clear" - } + "Name": "MotionInputOutput", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "RenderTarget" }, { "Name": "OutputDepthStencil", @@ -46,19 +35,6 @@ } ], "ImageAttachments": [ - { - "Name": "MotionBuffer", - "SizeSource": { - "Source": { - "Pass": "Parent", - "Attachment": "SwapChainOutput" - } - }, - "ImageDescriptor": { - "Format": "R16G16_FLOAT", - "SharedQueueMask": "Graphics" - } - }, { "Name": "DepthStencil", "SizeSource": { @@ -74,13 +50,6 @@ } ], "Connections": [ - { - "LocalSlot": "Output", - "AttachmentRef": { - "Pass": "This", - "Attachment": "MotionBuffer" - } - }, { "LocalSlot": "OutputDepthStencil", "AttachmentRef": { diff --git a/Gems/Atom/Feature/Common/Assets/Passes/MotionVectorParent.pass b/Gems/Atom/Feature/Common/Assets/Passes/MotionVectorParent.pass index a8369e4618..d7f4894706 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/MotionVectorParent.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/MotionVectorParent.pass @@ -20,6 +20,19 @@ { "Name": "SwapChainOutput", "SlotType": "InputOutput" + }, + { + "Name": "MotionVectorOutput", + "SlotType": "Output" + } + ], + "Connections": [ + { + "LocalSlot": "MotionVectorOutput", + "AttachmentRef": { + "Pass": "MeshMotionVectorPass", + "Attachment": "MotionInputOutput" + } } ], "PassRequests": [ @@ -50,6 +63,13 @@ "Pass": "Parent", "Attachment": "SkinnedMeshes" } + }, + { + "LocalSlot": "MotionInputOutput", + "AttachmentRef": { + "Pass": "CameraMotionVectorPass", + "Attachment": "Output" + } } ], "PassData": { diff --git a/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset b/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset index c56e8932b1..702ac8fe72 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset +++ b/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset @@ -284,6 +284,14 @@ "Name": "SMAA1xApplyPerceptualColorTemplate", "Path": "Passes/SMAA1xApplyPerceptualColor.pass" }, + { + "Name": "TaaTemplate", + "Path": "Passes/Taa.pass" + }, + { + "Name": "ContrastAdaptiveSharpeningTemplate", + "Path": "Passes/ContrastAdaptiveSharpening.pass" + }, { "Name": "SsaoParentTemplate", "Path": "Passes/SsaoParent.pass" diff --git a/Gems/Atom/Feature/Common/Assets/Passes/PostProcessParent.pass b/Gems/Atom/Feature/Common/Assets/Passes/PostProcessParent.pass index 36f7f1e985..fb27770da3 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/PostProcessParent.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/PostProcessParent.pass @@ -16,6 +16,10 @@ "Name": "Depth", "SlotType": "Input" }, + { + "Name": "MotionVectors", + "SlotType": "Input" + }, // SwapChain here is only used to reference the frame height and format { "Name": "SwapChainOutput", @@ -40,8 +44,8 @@ { "LocalSlot": "Output", "AttachmentRef": { - "Pass": "LightAdaptation", - "Attachment": "Output" + "Pass": "ContrastAdaptiveSharpeningPass", + "Attachment": "OutputColor" } }, { @@ -80,6 +84,34 @@ } ] }, + { + "Name": "TaaPass", + "TemplateName": "TaaTemplate", + "Enabled": false, + "Connections": [ + { + "LocalSlot": "InputColor", + "AttachmentRef": { + "Pass": "SMAA1xApplyLinearHDRColorPass", + "Attachment": "OutputColor" + } + }, + { + "LocalSlot": "InputDepth", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "Depth" + } + }, + { + "LocalSlot": "MotionVectors", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "MotionVectors" + } + } + ] + }, { "Name": "DepthOfFieldPass", "TemplateName": "DepthOfFieldTemplate", @@ -88,7 +120,7 @@ { "LocalSlot": "DoFColorInput", "AttachmentRef": { - "Pass": "SMAA1xApplyLinearHDRColorPass", + "Pass": "TaaPass", "Attachment": "OutputColor" } }, @@ -134,6 +166,20 @@ } } ] + }, + { + "Name": "ContrastAdaptiveSharpeningPass", + "TemplateName": "ContrastAdaptiveSharpeningTemplate", + "Enabled": false, + "Connections": [ + { + "LocalSlot": "InputColor", + "AttachmentRef": { + "Pass": "LightAdaptation", + "Attachment": "Output" + } + } + ] } ] } diff --git a/Gems/Atom/Feature/Common/Assets/Passes/SMAA1xApplyLinearHDRColor.pass b/Gems/Atom/Feature/Common/Assets/Passes/SMAA1xApplyLinearHDRColor.pass index 98700d5f0c..70604fba25 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/SMAA1xApplyLinearHDRColor.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/SMAA1xApplyLinearHDRColor.pass @@ -40,6 +40,12 @@ } } ], + "FallbackConnections": [ + { + "Input": "InputColor", + "Output": "OutputColor" + } + ], "PassRequests": [ { "Name": "SMAAConvertToPerceptualColor", diff --git a/Gems/Atom/Feature/Common/Assets/Passes/Taa.pass b/Gems/Atom/Feature/Common/Assets/Passes/Taa.pass new file mode 100644 index 0000000000..f1ba156007 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Passes/Taa.pass @@ -0,0 +1,113 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "PassAsset", + "ClassData": { + "PassTemplate": { + "Name": "TaaTemplate", + "PassClass": "TaaPass", + "Slots": [ + { + "Name": "InputColor", + "SlotType": "Input", + "ShaderInputName": "m_inputColor", + "ScopeAttachmentUsage": "Shader" + }, + { + "Name": "InputDepth", + "SlotType": "Input", + "ShaderInputName": "m_inputDepth", + "ScopeAttachmentUsage": "Shader" + }, + { + "Name": "MotionVectors", + "SlotType": "Input", + "ShaderInputName": "m_motionVectors", + "ScopeAttachmentUsage": "Shader" + }, + { + "Name": "LastFrameAccumulation", + "SlotType": "Input", + "ShaderInputName": "m_lastFrameAccumulation", + "ScopeAttachmentUsage": "Shader" + }, + { + "Name": "OutputColor", + "SlotType": "Output", + "ShaderInputName": "m_outputColor", + "ScopeAttachmentUsage": "Shader" + } + ], + "ImageAttachments": [ + { + "Name": "Accumulation1", + "Lifetime": "Imported", + "FormatSource": { + "Pass": "This", + "Attachment": "InputColor" + }, + "SizeSource": { + "Source": { + "Pass": "This", + "Attachment": "InputColor" + } + }, + "ImageDescriptor": { + "Format": "R16G16B16A16_FLOAT", + "BindFlags": "3", + "SharedQueueMask": "1" + } + }, + { + "Name": "Accumulation2", + "Lifetime": "Imported", + "FormatSource": { + "Pass": "This", + "Attachment": "InputColor" + }, + "SizeSource": { + "Source": { + "Pass": "This", + "Attachment": "InputColor" + } + }, + "ImageDescriptor": { + "Format": "R16G16B16A16_FLOAT", + "BindFlags": "3", + "SharedQueueMask": "1" + } + } + ], + "FallbackConnections": [ + { + "Input": "InputColor", + "Output": "OutputColor" + } + ], + "PassData": { + "$type": "TaaPassData", + "ShaderAsset": { + "FilePath": "Shaders/Postprocessing/Taa.shader" + }, + "Make Fullscreen Pass": true, + "ShaderDataMappings": { + "FloatMappings": [ + { + "Name": "m_currentFrameContribution", + "Value": 0.1 + }, + { + "Name": "m_clampGamma", + "Value": 1.0 + }, + { + "Name": "m_maxDeviationBeforeDampening", + "Value": 0.5 + } + ] + }, + "NumJitterPositions": 16 + } + } + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseComposite.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseComposite.azsl index b3b26c4d42..3e2fda8d5d 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseComposite.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseComposite.azsl @@ -37,6 +37,9 @@ ShaderResourceGroup PassSrg : SRG_PerPass AddressV = Clamp; AddressW = Clamp; }; + + // scale multiplier of the downsampled size to the fullscreen size (e.g., 4) + uint m_imageScale; } #include @@ -148,13 +151,10 @@ float3 SampleGlobalIBL(uint sampleIndex, uint2 screenCoords, float depth, float3 PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) { uint2 screenCoords = IN.m_position.xy; - - // [GFX TODO][ATOM-6172] Add image scale PassSrg constant to the DiffuseProbeGrid downsample/upsample - const uint ImageScale = 4; - const float ImageScaleInverse = 1.0f / ImageScale; + float imageScaleInverse = 1.0f / PassSrg::m_imageScale; // compute image coords for the downsampled probe irradiance image - uint2 probeIrradianceCoords = screenCoords * ImageScaleInverse; + uint2 probeIrradianceCoords = screenCoords * imageScaleInverse; float depth = PassSrg::m_depth.Load(screenCoords, sampleIndex).r; float4 encodedNormal = PassSrg::m_normal.Load(screenCoords, sampleIndex); @@ -165,7 +165,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) float3 diffuse = float3(0.0f, 0.0f, 0.0f); if (useProbeIrradiance > 0.0f) { - float3 irradiance = SampleProbeIrradiance(sampleIndex, probeIrradianceCoords, depth, normal, albedo, ImageScale); + float3 irradiance = SampleProbeIrradiance(sampleIndex, probeIrradianceCoords, depth, normal, albedo, PassSrg::m_imageScale); diffuse = (albedo.rgb / PI) * irradiance; } else diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample.azsl index e051317567..85772c6577 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample.azsl @@ -31,6 +31,9 @@ ShaderResourceGroup PassSrg : SRG_PerPass AddressV = Clamp; AddressW = Clamp; }; + + // scale multiplier of the downsampled size to the fullscreen size (e.g., 4) + uint m_outputImageScale; } #include @@ -56,16 +59,13 @@ struct PSOutput // Pixel Shader PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) { - // the downsample is 1/4 resolution - // [GFX TODO][ATOM-6172] Add image scale PassSrg constant to the DiffuseProbeGrid downsample/upsample - const uint ImageScale = 4; - uint2 screenCoords = IN.m_position.xy * ImageScale; + uint2 screenCoords = IN.m_position.xy * PassSrg::m_outputImageScale; float downsampledDepth = 0; float4 downsampledEncodedNormal; - for (uint y = 0; y < ImageScale; ++y) + for (uint y = 0; y < PassSrg::m_outputImageScale; ++y) { - for (uint x = 0; x < ImageScale; ++x) + for (uint x = 0; x < PassSrg::m_outputImageScale; ++x) { float depth = PassSrg::m_depth.Load(screenCoords + int2(x, y), sampleIndex).r; float4 encodedNormal = PassSrg::m_normal.Load(screenCoords + int2(x, y), sampleIndex); diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/CameraMotionVector.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/CameraMotionVector.azsl index a073f42f03..c83e5138e6 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/CameraMotionVector.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/CameraMotionVector.azsl @@ -39,10 +39,27 @@ PSOutput MainPS(VSOutput IN) PSOutput OUT; float depth = PassSrg::m_depthStencil.Sample(PassSrg::LinearSampler, IN.m_texCoord).r; + + // If depth is 0, that means depth is on the far plane. This should be treated as being infinitely far + // away, not actually on the far plane, because the infinitely far background shouldn't move as a result + // of camera translation. Tweaking the depth to -near/far distance makes that happen. Keep in mind near + // and far are inverted, so this normally a very small value. + if (depth == 0.0) + { + depth = -ViewSrg::GetFarZ() / ViewSrg::GetNearZ(); + } + float2 clipPos = float2(mad(IN.m_texCoord.x, 2.0, -1.0), mad(IN.m_texCoord.y, -2.0, 1.0)); float4 worldPos = mul(ViewSrg::m_viewProjectionInverseMatrix, float4(clipPos, depth, 1.0)); + float4 clipPosPrev = mul(ViewSrg::m_viewProjectionPrevMatrix, float4((worldPos / worldPos.w).xyz, 1.0)); - - OUT.m_motion = (clipPos - (clipPosPrev / clipPosPrev.w).xy) * 0.5; + clipPosPrev = (clipPosPrev / clipPosPrev.w); + + // Clip space is from -1.0 to 1.0, so the motion vectors are 2x as big as they should be + OUT.m_motion = (clipPos - clipPosPrev.xy) * 0.5; + + // Flip y to line up with uv coordinates + OUT.m_motion.y = -OUT.m_motion.y; + return OUT; } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorCommon.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorCommon.azsli index c11e9d9e0e..ff2758af87 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorCommon.azsli +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorCommon.azsli @@ -41,5 +41,9 @@ PSOutput MainPS(VSOutput IN) float2 motion = (clipPos.xy / clipPos.w - clipPosPrev.xy / clipPosPrev.w) * 0.5; OUT.m_motion = motion; + + // Flip y to line up with uv coordinates + OUT.m_motion.y = -OUT.m_motion.y; + return OUT; } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ContrastAdaptiveSharpening.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ContrastAdaptiveSharpening.azsl new file mode 100644 index 0000000000..14fa942a7d --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ContrastAdaptiveSharpening.azsl @@ -0,0 +1,85 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include + +#define TILE_DIM_X 16 +#define TILE_DIM_Y 16 + +ShaderResourceGroup PassSrg : SRG_PerPass +{ + Texture2D m_inputColor; + RWTexture2D m_outputColor; + + float m_strength; // Strength of the sharpening effect. Range from 0 to 1. +} + + +// Constrast Adaptive Sharpening, based on AMD FidelityFX CAS - https://gpuopen.com/fidelityfx-cas/ + +// This shader sharpens the input based on the contrast of the local neighborhood +// so that only areas that need sharpening are sharpened, while high constast areas +// are mostly left alone. + +[numthreads(TILE_DIM_X, TILE_DIM_Y, 1)] +void MainCS( + uint3 dispatchThreadID : SV_DispatchThreadID, + uint3 groupID : SV_GroupID, + uint groupIndex : SV_GroupIndex) +{ + uint2 pixelCoord = dispatchThreadID.xy; + + // Fetch local neighborhood to determin sharpening weight. + // a + // b c d + // e + + float3 sampleA = PassSrg::m_inputColor[pixelCoord + int2( 0, -1)].rgb; + float3 sampleB = PassSrg::m_inputColor[pixelCoord + int2(-1, 0)].rgb; + float3 sampleC = PassSrg::m_inputColor[pixelCoord + int2( 0, 0)].rgb; + float3 sampleD = PassSrg::m_inputColor[pixelCoord + int2( 1, 0)].rgb; + float3 sampleE = PassSrg::m_inputColor[pixelCoord + int2( 0, 1)].rgb; + + float lumA = GetLuminance(sampleA); + float lumB = GetLuminance(sampleB); + float lumC = GetLuminance(sampleC); + float lumD = GetLuminance(sampleD); + float lumE = GetLuminance(sampleE); + + // Get the min and max. Just use the green channel for luminance. + float minLum = min(min(lumA, lumB), min(lumC, min(lumD, lumE))); + float maxLum = max(max(lumA, lumB), max(lumC, max(lumD, lumE))); + + float dMinLum = minLum; // Distance from 0 to minimum + float dMaxLum = 1.0 - maxLum; // Distance from 1 to the maximum + + // baseSharpening is higher when local contrast is lower to avoid over-sharpening. + float baseSharpening = min(dMinLum, dMaxLum) / max(maxLum, 0.0001); + baseSharpening = sqrt(baseSharpening); // bias towards more sharpening + + // Negative weights for sharpening effect, center pixel is always weighted 1. + float developerMaximum = lerp(-0.125, -0.2, PassSrg::m_strength); + float weight = baseSharpening * developerMaximum; + float totalWeight = weight * 4 + 1.0; + + float3 output = + ( + sampleA * weight + + sampleB * weight + + sampleC + + sampleD * weight + + sampleE * weight + ) / totalWeight; + + PassSrg::m_outputColor[pixelCoord] = float4(output, 1.0); +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ContrastAdaptiveSharpening.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ContrastAdaptiveSharpening.shader new file mode 100644 index 0000000000..756ce0ec7a --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ContrastAdaptiveSharpening.shader @@ -0,0 +1,11 @@ +{ + "Source": "ContrastAdaptiveSharpening", + "ProgramSettings": { + "EntryPoints": [ + { + "name": "MainCS", + "type": "Compute" + } + ] + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/Taa.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/Taa.azsl new file mode 100644 index 0000000000..94944df9de --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/Taa.azsl @@ -0,0 +1,271 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include + +#define TILE_DIM_X 16 +#define TILE_DIM_Y 16 + +ShaderResourceGroup PassSrg : SRG_PerPass +{ + Texture2D m_inputColor; + Texture2D m_inputDepth; + Texture2D m_motionVectors; + Texture2D m_lastFrameAccumulation; + + RWTexture2D m_outputColor; + + Sampler LinearSampler + { + MinFilter = Linear; + MagFilter = Linear; + MipFilter = Linear; + AddressU = Clamp; + AddressV = Clamp; + AddressW = Clamp; + }; + + // Current frame's default contribution to the history. + float m_currentFrameContribution; + + // Increase this value for weaker clamping, decrease for stronger clamping, default 1.0. + float m_clampGamma; + + // Default 0.5, used for flicker reduction. Any sample further than this many standard deviations outside the neighborhood + // will have its weight decreased. The further outside the max deviation, the more its weight is reduced. + float m_maxDeviationBeforeDampening; + + struct Constants + { + uint2 m_inputColorSize; + float2 m_inputColorRcpSize; + + // 3x3 filter weights + // 8 2 6 + // 3 0 1 + // 7 4 5 + float4 m_weights1; // 0 1 2 3 + float4 m_weights2; // 4 5 6 7 + float4 m_weights3; // 8 x x x + }; + Constants m_constantData; +} + +static const int2 offsets[9] = +{ + // Center + int2(0, 0), + // Cross + int2( 1, 0), + int2( 0,-1), + int2(-1, 0), + int2( 0, 1), + // Diagonals + int2( 1,-1), + int2( 1, 1), + int2(-1,-1), + int2(-1, 1), +}; + +float3 RgbToYCoCg(float3 rgb) +{ + const float3x3 conversionMatrix = + { + 0.25, 0.50, 0.25, + 0.50, 0.00, -0.50, + -0.25, 0.50, -0.25 + }; + return mul(conversionMatrix, rgb); +} + +float3 YCoCgToRgb(float3 yCoCg) +{ + const float3x3 conversionMatrix = + { + 1.0, 1.0, -1.0, + 1.0, 0.0, 1.0, + 1.0, -1.0, -1.0 + }; + return mul(conversionMatrix, yCoCg); +} + +// Sample a texture with a 5 tap Catmull-Rom. Consider ripping this out and putting in a more general location. +// This function samples a 4x4 neighborhood around the uv. By taking advantage of bilinear filtering this can be +// done with only 9 taps on the edges between pixels. The cost is further reduced by dropping the 4 diagonal +// samples as their influence is negligible. +float4 SampleCatmullRom5Tap(Texture2D texture, SamplerState linearSampler, float2 uv, float2 textureSize, float2 rcpTextureSize, float sharpness) +{ + // 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 Catmull-Rom weights based on the offset and sharpness + float c = sharpness; + float2 w0 = f * (-c + f * (2.0 * c - c * f)); + float2 w1 = 1.0 + f * f * (c -3.0 + (2.0 - c) * f); + float2 w2 = f * (c + f * ((3.0 - 2.0 * c) - (2.0 - c) * f)); + float2 w3 = f * f * (c * f - c); + + 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 * w0.y; + float sw1 = w0.x * w12.y; + float sw2 = w12.x * w12.y; + float sw3 = w3.x * w12.y; + float sw4 = w12.x * w3.y; + + // total weight of samples to normalize result. + float totalWeight = sw0 + sw1 + sw2 + sw3 + sw4; + + float4 result = 0.0f; + result += texture.SampleLevel(linearSampler, float2(tc12.x, tc0.y), 0.0) * sw0; + result += texture.SampleLevel(linearSampler, float2( tc0.x, tc12.y), 0.0) * sw1; + result += texture.SampleLevel(linearSampler, float2(tc12.x, tc12.y), 0.0) * sw2; + result += texture.SampleLevel(linearSampler, float2( tc3.x, tc12.y), 0.0) * sw3; + result += texture.SampleLevel(linearSampler, float2(tc12.x, tc3.y), 0.0) * sw4; + + return result / totalWeight; +} + +[numthreads(TILE_DIM_X, TILE_DIM_Y, 1)] +void MainCS( + uint3 dispatchThreadID : SV_DispatchThreadID, + uint3 groupID : SV_GroupID, + uint groupIndex : SV_GroupIndex) +{ + uint2 pixelCoord = dispatchThreadID.xy; + + const float filterWeights[9] = + { + PassSrg::m_constantData.m_weights1.x, + PassSrg::m_constantData.m_weights1.y, + PassSrg::m_constantData.m_weights1.z, + PassSrg::m_constantData.m_weights1.w, + PassSrg::m_constantData.m_weights2.x, + PassSrg::m_constantData.m_weights2.y, + PassSrg::m_constantData.m_weights2.z, + PassSrg::m_constantData.m_weights2.w, + PassSrg::m_constantData.m_weights3.x, + }; + + float3 sum = 0.0; + float3 sumOfSquares = 0.0; + float nearestDepth = 1.0; + uint2 nearestDepthPixelCoord; + + float3 thisFrameColor = float3(0.0, 0.0, 0.0); + + // Sample the neighborhood to filter the current pixel, gather statistics about + // its neighbors, and find the closest neighbor to choose a motion vector. + [unroll] for (int i = 0; i < 9; ++i) + { + uint2 neighborhoodPixelCoord = pixelCoord + offsets[i]; + float3 neighborhoodColor = PassSrg::m_inputColor[neighborhoodPixelCoord].rgb; + + // Convert to YCoCg space for better clipping. + neighborhoodColor = RgbToYCoCg(neighborhoodColor); + + sum += neighborhoodColor; + sumOfSquares += neighborhoodColor * neighborhoodColor; + thisFrameColor += neighborhoodColor * filterWeights[i]; + + // Find the coordinate of the nearest depth + float neighborhoodDepth = PassSrg::m_inputDepth[neighborhoodPixelCoord].r; + if (neighborhoodDepth < nearestDepth) + { + nearestDepth = neighborhoodDepth; + nearestDepthPixelCoord = neighborhoodPixelCoord; + } + } + + // Variance clipping, see http://developer.download.nvidia.com/gameworks/events/GDC2016/msalvi_temporal_supersampling.pdf + float3 mean = sum / 9.0; + float3 standardDeviation = max(0.0, sqrt(sumOfSquares / 9.0 - mean * mean)); + standardDeviation *= PassSrg::m_clampGamma; + + // Grab the motion vector from the closest pixel in the 3x3 neighborhood. This is done so that motion vectors correctly + // track edges. For instance, if a pixel lies on the edge of a moving object, where the color is a blend of the + // forground and background, it's possible for the pixel center to hit the (not moving) background. However, the correct + // history for this pixel will be the location this edge was the previous frame. By choosing the motion of the nearest + // pixel in the neighborhood that edge will be correctly tracked. + + // Motion vectors store the direction of movement, so to look up where things were in the previous frame, it's negated. + float2 previousPositionOffset = -PassSrg::m_motionVectors[nearestDepthPixelCoord]; + + // Get the uv coordinate for the previous frame. + float2 rcpSize = PassSrg::m_constantData.m_inputColorRcpSize; + float2 uvCoord = (pixelCoord + 0.5f) * rcpSize; + float2 uvOld = uvCoord + previousPositionOffset; + float2 previousPositionOffsetInPixels = float2(PassSrg::m_constantData.m_inputColorSize) * previousPositionOffset; + + // Sample the last frame using a 5-tap Catmull-Rom + float3 lastFrameColor = SampleCatmullRom5Tap(PassSrg::m_lastFrameAccumulation, PassSrg::LinearSampler, uvOld, PassSrg::m_constantData.m_inputColorSize, PassSrg::m_constantData.m_inputColorRcpSize, 0.5).rgb; + lastFrameColor = RgbToYCoCg(lastFrameColor); + + // Last frame color relative to mean + float3 centerColorOffset = lastFrameColor - mean; + float3 colorOffsetStandardDeviationRatio = abs(standardDeviation / centerColorOffset); + + // Clamp the color by the aabb of the standardDeviation. Can never be greater than 1, so will always be inside or on the bounds of the aabb. + float clampedColorLength = min(min(min(1, colorOffsetStandardDeviationRatio.x), colorOffsetStandardDeviationRatio.y), colorOffsetStandardDeviationRatio.z); + + // Calculate the true clamped color by offsetting it back from the mean. + float3 lastFrameClampedColor = mean + centerColorOffset * clampedColorLength; + + // Anti-flickering - Reduce current frame weight the more it deviates from the history based on the standard deviation of the neighborhood. + // Start reducing weight at differences greater than m_maxDeviationBeforeDampening standard deviations in luminance. + float standardDeviationWeight = standardDeviation.r * PassSrg::m_maxDeviationBeforeDampening; + float3 sdFromLastFrame = standardDeviationWeight / abs(lastFrameClampedColor.r - thisFrameColor.r); + + float currentFrameWeight = PassSrg::m_currentFrameContribution; + currentFrameWeight *= saturate(sdFromLastFrame * sdFromLastFrame); + + // Back to Rgb space + thisFrameColor = YCoCgToRgb(thisFrameColor); + lastFrameClampedColor = YCoCgToRgb(lastFrameClampedColor); + + // Out of bounds protection. + if (any(uvOld > 1.0) || any(uvOld < 0.0)) + { + currentFrameWeight = 1.0f; + } + + // Blend should be in perceptual space, so tonemap first + float luminance = GetLuminance(thisFrameColor); + thisFrameColor = thisFrameColor / (1 + luminance); + lastFrameClampedColor = lastFrameClampedColor / (1 + luminance); + + // Blend color with history + float3 color = lerp(lastFrameClampedColor, thisFrameColor, currentFrameWeight); + + // Un-tonemap color + color = color * (1.0 + luminance); + + // NaN protection (without this NaNs could get in the history buffer and quickly consume the frame) + color = max(0.0, color); + + PassSrg::m_outputColor[pixelCoord].rgb = color; + +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/Taa.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/Taa.shader new file mode 100644 index 0000000000..f30ff92f20 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/Taa.shader @@ -0,0 +1,11 @@ +{ + "Source": "Taa", + "ProgramSettings": { + "EntryPoints": [ + { + "name": "MainCS", + "type": "Compute" + } + ] + } +} diff --git a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake index a9ba765329..3dfabc586a 100644 --- a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake +++ b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake @@ -89,6 +89,7 @@ set(FILES Passes/CascadedShadowmaps.pass Passes/CheckerboardResolveColor.pass Passes/CheckerboardResolveDepth.pass + Passes/ContrastAdaptiveSharpening.pass Passes/ConvertToAcescg.pass Passes/DebugOverlayParent.pass Passes/DeferredFog.pass @@ -207,6 +208,7 @@ set(FILES Passes/SsaoHalfRes.pass Passes/SsaoParent.pass Passes/SubsurfaceScattering.pass + Passes/Taa.pass Passes/Transparent.pass Passes/TransparentParent.pass Passes/UI.pass diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessorInterface.h new file mode 100644 index 0000000000..88faac1728 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessorInterface.h @@ -0,0 +1,40 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include + +namespace AZ +{ + namespace Render + { + enum class DiffuseGlobalIlluminationQualityLevel : uint8_t + { + Low, + Medium, + High + }; + + //! This class provides general features and configuration for the diffuse global illumination environment, + //! which consists of DiffuseProbeGrids and the diffuse Global IBL cubemap. + class DiffuseGlobalIlluminationFeatureProcessorInterface + : public RPI::FeatureProcessor + { + public: + AZ_RTTI(AZ::Render::DiffuseProbeGridFeatureProcessorInterface, "{BD8CA35A-47C3-4FD8-932B-18495EF07527}"); + + virtual void SetQualityLevel(DiffuseGlobalIlluminationQualityLevel qualityLevel) = 0; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessorInterface.h similarity index 100% rename from Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessorInterface.h rename to Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessorInterface.h diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h new file mode 100644 index 0000000000..8ffbb7f235 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h @@ -0,0 +1,43 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class OcclusionCullingPlane; + + using OcclusionCullingPlaneHandle = AZStd::shared_ptr; + + // OcclusionCullingPlaneFeatureProcessorInterface provides an interface to the feature processor for code outside of Atom + class OcclusionCullingPlaneFeatureProcessorInterface + : public RPI::FeatureProcessor + { + public: + AZ_RTTI(AZ::Render::OcclusionCullingPlaneFeatureProcessorInterface, "{50F6B45E-A622-44EC-B962-DE25FBD44095}"); + + virtual OcclusionCullingPlaneHandle AddOcclusionCullingPlane(const AZ::Transform& transform) = 0; + virtual void RemoveOcclusionCullingPlane(OcclusionCullingPlaneHandle& handle) = 0; + virtual bool IsValidOcclusionCullingPlaneHandle(const OcclusionCullingPlaneHandle& occlusionCullingPlane) const = 0; + virtual void SetTransform(const OcclusionCullingPlaneHandle& occlusionCullingPlane, const AZ::Transform& transform) = 0; + virtual void SetEnabled(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool enabled) = 0; + virtual void ShowVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool showVisualization) = 0; + virtual void SetTransparentVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool transparentVisualization) = 0; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp index 00c55cbade..af28624357 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp @@ -64,6 +64,7 @@ #include #include #include +#include #include #include #include @@ -91,17 +92,19 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include +#include namespace AZ { @@ -131,11 +134,14 @@ namespace AZ PostProcessFeatureProcessor::Reflect(context); ImGuiPassData::Reflect(context); RayTracingPassData::Reflect(context); + TaaPassData::Reflect(context); LightingPreset::Reflect(context); ModelPreset::Reflect(context); DiffuseProbeGridFeatureProcessor::Reflect(context); + DiffuseGlobalIlluminationFeatureProcessor::Reflect(context); RayTracingFeatureProcessor::Reflect(context); + OcclusionCullingPlaneFeatureProcessor::Reflect(context); if (SerializeContext* serialize = azrtti_cast(context)) { @@ -191,7 +197,9 @@ namespace AZ AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); + AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); + AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); // Add SkyBox pass auto* passSystem = RPI::PassSystemInterface::Get(); @@ -224,6 +232,9 @@ namespace AZ // Add Depth Downsample/Upsample passes passSystem->AddPassCreator(Name("DepthUpsamplePass"), &DepthUpsamplePass::Create); + + // Add Taa Pass + passSystem->AddPassCreator(Name("TaaPass"), &TaaPass::Create); // Add DepthOfField pass passSystem->AddPassCreator(Name("DepthOfFieldCompositePass"), &DepthOfFieldCompositePass::Create); @@ -285,6 +296,7 @@ namespace AZ void CommonSystemComponent::Deactivate() { AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); + AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); @@ -297,6 +309,7 @@ namespace AZ AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); + AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); } void CommonSystemComponent::LoadPassTemplateMappings() diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp new file mode 100644 index 0000000000..5040665456 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp @@ -0,0 +1,113 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + void DiffuseGlobalIlluminationFeatureProcessor::Reflect(ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext + ->Class() + ->Version(0); + } + } + + void DiffuseGlobalIlluminationFeatureProcessor::Activate() + { + EnableSceneNotification(); + } + + void DiffuseGlobalIlluminationFeatureProcessor::Deactivate() + { + DisableSceneNotification(); + } + + void DiffuseGlobalIlluminationFeatureProcessor::SetQualityLevel(DiffuseGlobalIlluminationQualityLevel qualityLevel) + { + m_qualityLevel = qualityLevel; + + UpdatePasses(); + } + + void DiffuseGlobalIlluminationFeatureProcessor::OnRenderPipelinePassesChanged([[maybe_unused]] RPI::RenderPipeline* renderPipeline) + { + UpdatePasses(); + } + + void DiffuseGlobalIlluminationFeatureProcessor::OnRenderPipelineAdded([[maybe_unused]] RPI::RenderPipelinePtr pipeline) + { + UpdatePasses(); + } + + void DiffuseGlobalIlluminationFeatureProcessor::UpdatePasses() + { + float sizeMultiplier = 0.0f; + switch (m_qualityLevel) + { + case DiffuseGlobalIlluminationQualityLevel::Low: + sizeMultiplier = 0.25f; + break; + case DiffuseGlobalIlluminationQualityLevel::Medium: + sizeMultiplier = 0.5f; + break; + case DiffuseGlobalIlluminationQualityLevel::High: + sizeMultiplier = 1.0f; + break; + default: + AZ_Assert(false, "Unknown DiffuseGlobalIlluminationQualityLevel [%d]", m_qualityLevel); + break; + } + + // update the size multiplier on the DiffuseProbeGridDownsamplePass output + AZStd::vector downsamplePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseProbeGridDownsamplePass") }; + RPI::PassHierarchyFilter downsamplePassFilter(downsamplePassHierarchy); + const AZStd::vector& downsamplePasses = RPI::PassSystemInterface::Get()->FindPasses(downsamplePassFilter); + for (RPI::Pass* pass : downsamplePasses) + { + for (uint32_t outputIndex = 0; outputIndex < pass->GetOutputCount(); ++outputIndex) + { + RPI::Ptr outputAttachment = pass->GetOutputBinding(outputIndex).m_attachment; + RPI::PassAttachmentSizeMultipliers& sizeMultipliers = outputAttachment->m_sizeMultipliers; + + sizeMultipliers.m_widthMultiplier = sizeMultiplier; + sizeMultipliers.m_heightMultiplier = sizeMultiplier; + } + + // set the output scale on the PassSrg + RPI::FullscreenTrianglePass* downsamplePass = static_cast(pass); + auto constantIndex = downsamplePass->GetShaderResourceGroup()->FindShaderInputConstantIndex(Name("m_outputImageScale")); + downsamplePass->GetShaderResourceGroup()->SetConstant(constantIndex, aznumeric_cast(1.0f / sizeMultiplier)); + } + + // update the image scale on the DiffuseComposite pass + AZStd::vector compositePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseCompositePass") }; + RPI::PassHierarchyFilter compositePassFilter(compositePassHierarchy); + const AZStd::vector& compositePasses = RPI::PassSystemInterface::Get()->FindPasses(compositePassFilter); + for (RPI::Pass* pass : compositePasses) + { + RPI::FullscreenTrianglePass* compositePass = static_cast(pass); + auto constantIndex = compositePass->GetShaderResourceGroup()->FindShaderInputConstantIndex(Name("m_imageScale")); + compositePass->GetShaderResourceGroup()->SetConstant(constantIndex, aznumeric_cast(1.0f / sizeMultiplier)); + } + } + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.h new file mode 100644 index 0000000000..81dc1487fa --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.h @@ -0,0 +1,52 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include + +namespace AZ +{ + namespace Render + { + //! This class provides general features and configuration for the diffuse global illumination environment, + //! which consists of DiffuseProbeGrids and the diffuse Global IBL cubemap. + class DiffuseGlobalIlluminationFeatureProcessor final + : public DiffuseGlobalIlluminationFeatureProcessorInterface + { + public: + AZ_RTTI(AZ::Render::DiffuseGlobalIlluminationFeatureProcessor, "{14F7DF46-AA2C-49EF-8A2C-0A7CB7390BB7}", DiffuseGlobalIlluminationFeatureProcessorInterface); + + static void Reflect(AZ::ReflectContext* context); + + DiffuseGlobalIlluminationFeatureProcessor() = default; + virtual ~DiffuseGlobalIlluminationFeatureProcessor() = default; + + void Activate() override; + void Deactivate() override; + + // DiffuseGlobalIlluminationFeatureProcessorInterface overrides + void SetQualityLevel(DiffuseGlobalIlluminationQualityLevel qualityLevel) override; + + private: + AZ_DISABLE_COPY_MOVE(DiffuseGlobalIlluminationFeatureProcessor); + + // RPI::SceneNotificationBus::Handler overrides + void OnRenderPipelinePassesChanged(RPI::RenderPipeline* renderPipeline) override; + void OnRenderPipelineAdded(RPI::RenderPipelinePtr pipeline) override; + + void UpdatePasses(); + + DiffuseGlobalIlluminationQualityLevel m_qualityLevel = DiffuseGlobalIlluminationQualityLevel::Low; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.cpp similarity index 99% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.cpp index a55d8fc78c..cfdfd9efa4 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.h similarity index 99% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.h index ff6ad719cf..4a732ae7af 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.h @@ -17,7 +17,7 @@ #include #include #include -#include +#include namespace AZ { diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.cpp similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.cpp index 2a06dacf3f..04e68136f1 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.cpp @@ -10,7 +10,6 @@ * */ -#include #include #include #include @@ -18,7 +17,8 @@ #include #include #include -#include +#include +#include #include namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.h similarity index 100% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.h diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.cpp similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.cpp index 4818018ea3..f1fe792542 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.cpp @@ -10,7 +10,6 @@ * */ -#include #include #include #include @@ -18,7 +17,8 @@ #include #include #include -#include +#include +#include #include namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.h similarity index 100% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.h diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.cpp similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.cpp index 8821de8a9d..a24d54d9b7 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.cpp @@ -10,7 +10,6 @@ * */ -#include #include #include #include @@ -18,7 +17,8 @@ #include #include #include -#include +#include +#include #include namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.h similarity index 100% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.h diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.cpp similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.cpp index 65f1c2dd5a..8d39e6fcc8 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.cpp @@ -10,7 +10,6 @@ * */ -#include #include #include #include @@ -22,7 +21,8 @@ #include #include #include -#include +#include +#include #include namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.h similarity index 97% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.h index 677e16ac42..eef59c3753 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.h @@ -12,7 +12,6 @@ #pragma once #include - #include #include #include @@ -22,7 +21,7 @@ #include #include #include -#include +#include namespace AZ { diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp similarity index 99% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp index 060d51d1d0..ba24bcc001 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.h similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.h index 19e9bf1b1d..3fb70830d0 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.h @@ -12,8 +12,8 @@ #pragma once -#include -#include +#include +#include namespace AZ { diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRayTracingPass.cpp similarity index 99% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRayTracingPass.cpp index 65d71b8272..c77f66645e 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRayTracingPass.cpp @@ -10,7 +10,6 @@ * */ -#include #include #include #include @@ -25,7 +24,8 @@ #include #include #include -#include +#include +#include #include namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRayTracingPass.h similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRayTracingPass.h index bb35803e51..910537a2f1 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRayTracingPass.h @@ -19,7 +19,7 @@ #include #include #include -#include +#include namespace AZ { diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.cpp similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.cpp index 86a26f002d..2f0ed373b9 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.cpp @@ -10,7 +10,6 @@ * */ -#include #include #include #include @@ -22,7 +21,8 @@ #include #include #include -#include +#include +#include #include namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.h similarity index 97% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.h index 437ac2f3f5..1900f8b786 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.h @@ -12,7 +12,6 @@ #pragma once #include - #include #include #include @@ -22,7 +21,7 @@ #include #include #include -#include +#include namespace AZ { diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRenderPass.cpp similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRenderPass.cpp index 4f9221a65f..f025079994 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRenderPass.cpp @@ -10,12 +10,12 @@ * */ -#include -#include #include #include #include #include +#include +#include #include namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRenderPass.h similarity index 100% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRenderPass.h diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.cpp similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.cpp index bc619cc277..5465d0da33 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.cpp @@ -10,8 +10,8 @@ * */ -#include -#include +#include +#include namespace AZ { diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.h similarity index 96% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.h index 1becd6fb3e..6f7ebb9240 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.h @@ -14,7 +14,7 @@ #include #include #include -#include +#include namespace AZ { diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlane.cpp b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlane.cpp new file mode 100644 index 0000000000..10004a72e4 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlane.cpp @@ -0,0 +1,113 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + static const char* OcclusionCullingPlaneDrawListTag("occlusioncullingplanevisualization"); + + OcclusionCullingPlane::~OcclusionCullingPlane() + { + Data::AssetBus::MultiHandler::BusDisconnect(); + m_meshFeatureProcessor->ReleaseMesh(m_visualizationMeshHandle); + } + + void OcclusionCullingPlane::Init(RPI::Scene* scene) + { + AZ_Assert(scene, "OcclusionCullingPlane::Init called with a null Scene pointer"); + + m_meshFeatureProcessor = scene->GetFeatureProcessor(); + + // load visualization plane model and material + m_visualizationModelAsset = AZ::RPI::AssetUtils::GetAssetByProductPath( + "Models/OcclusionCullingPlane.azmodel", + AZ::RPI::AssetUtils::TraceLevel::Assert); + + m_visualizationMeshHandle = m_meshFeatureProcessor->AcquireMesh(m_visualizationModelAsset); + m_meshFeatureProcessor->SetExcludeFromReflectionCubeMaps(m_visualizationMeshHandle, true); + m_meshFeatureProcessor->SetRayTracingEnabled(m_visualizationMeshHandle, false); + m_meshFeatureProcessor->SetTransform(m_visualizationMeshHandle, AZ::Transform::CreateIdentity()); + + SetVisualizationMaterial(); + } + + void OcclusionCullingPlane::SetVisualizationMaterial() + { + AZStd::string materialAssetPath; + if (m_transparentVisualization) + { + materialAssetPath = "Materials/OcclusionCullingPlane/OcclusionCullingPlaneTransparentVisualization.azmaterial"; + } + else + { + materialAssetPath = "Materials/OcclusionCullingPlane/OcclusionCullingPlaneVisualization.azmaterial"; + } + + RPI::AssetUtils::TraceLevel traceLevel = AZ::RPI::AssetUtils::TraceLevel::Assert; + m_visualizationMaterialAsset = AZ::RPI::AssetUtils::GetAssetByProductPath(materialAssetPath.c_str(), traceLevel); + m_visualizationMaterialAsset.QueueLoad(); + Data::AssetBus::MultiHandler::BusConnect(m_visualizationMaterialAsset.GetId()); + } + + void OcclusionCullingPlane::OnAssetReady(Data::Asset asset) + { + if (m_visualizationMaterialAsset.GetId() == asset.GetId()) + { + m_visualizationMaterialAsset = asset; + Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId()); + + m_visualizationMaterial = AZ::RPI::Material::FindOrCreate(m_visualizationMaterialAsset); + m_meshFeatureProcessor->SetMaterialAssignmentMap(m_visualizationMeshHandle, m_visualizationMaterial); + } + } + + void OcclusionCullingPlane::OnAssetError(Data::Asset asset) + { + AZ_Error("OcclusionCullingPlane", false, "Failed to load OcclusionCullingPlane visualization asset %s", asset.ToString().c_str()); + Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId()); + } + + void OcclusionCullingPlane::SetTransform(const AZ::Transform& transform) + { + m_transform = transform; + + // update visualization plane transform + m_meshFeatureProcessor->SetTransform(m_visualizationMeshHandle, transform); + } + + void OcclusionCullingPlane::ShowVisualization(bool showVisualization) + { + if (m_showVisualization != showVisualization) + { + m_meshFeatureProcessor->SetVisible(m_visualizationMeshHandle, showVisualization); + SetVisualizationMaterial(); + } + } + + void OcclusionCullingPlane::SetTransparentVisualization(bool transparentVisualization) + { + if (m_transparentVisualization != transparentVisualization) + { + m_transparentVisualization = transparentVisualization; + SetVisualizationMaterial(); + } + } + + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlane.h b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlane.h new file mode 100644 index 0000000000..4701c4977f --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlane.h @@ -0,0 +1,65 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include + +namespace AZ +{ + namespace Render + { + //! This class represents an OcclusionCullingPlane which is used to cull meshes that are inside the view frustum + class OcclusionCullingPlane final + : public AZ::Data::AssetBus::MultiHandler + { + public: + OcclusionCullingPlane() = default; + ~OcclusionCullingPlane(); + + void Init(RPI::Scene* scene); + + void SetTransform(const AZ::Transform& transform); + const AZ::Transform& GetTransform() const { return m_transform; } + + void SetEnabled(bool enabled) { m_enabled = enabled; } + bool GetEnabled() const { return m_enabled; } + + // enables or disables rendering of the visualization plane + void ShowVisualization(bool showVisualization); + + // sets the visualization to transparent mode + void SetTransparentVisualization(bool transparentVisualization); + + private: + + void SetVisualizationMaterial(); + + // AZ::Data::AssetBus::Handler overrides... + void OnAssetReady(Data::Asset asset) override; + void OnAssetError(Data::Asset asset) override; + + AZ::Transform m_transform; + bool m_enabled = true; + bool m_showVisualization = true; + bool m_transparentVisualization = false; + + // visualization + AZ::Render::MeshFeatureProcessorInterface* m_meshFeatureProcessor = nullptr; + Data::Asset m_visualizationModelAsset; + Data::Asset m_visualizationMaterialAsset; + Data::Instance m_visualizationMaterial; + AZ::Render::MeshFeatureProcessorInterface::MeshHandle m_visualizationMeshHandle; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp new file mode 100644 index 0000000000..3c61f616d9 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp @@ -0,0 +1,146 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + void OcclusionCullingPlaneFeatureProcessor::Reflect(ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext + ->Class() + ->Version(0); + } + } + + void OcclusionCullingPlaneFeatureProcessor::Activate() + { + m_occlusionCullingPlanes.reserve(InitialOcclusionCullingPlanesAllocationSize); + m_rpiOcclusionPlanes.reserve(InitialOcclusionCullingPlanesAllocationSize); + + EnableSceneNotification(); + } + + void OcclusionCullingPlaneFeatureProcessor::Deactivate() + { + AZ_Warning("OcclusionCullingPlaneFeatureProcessor", m_occlusionCullingPlanes.size() == 0, + "Deactivating the OcclusionCullingPlaneFeatureProcessor, but there are still outstanding occlusion planes. Components\n" + "using OcclusionCullingPlaneHandles should free them before the OcclusionCullingPlaneFeatureProcessor is deactivated.\n" + ); + + DisableSceneNotification(); + } + + void OcclusionCullingPlaneFeatureProcessor::OnBeginPrepareRender() + { + if (m_rpiListNeedsUpdate) + { + // rebuild the RPI occlusion list + m_rpiOcclusionPlanes.clear(); + + for (auto& occlusionCullingPlane : m_occlusionCullingPlanes) + { + if (!occlusionCullingPlane->GetEnabled()) + { + continue; + } + + RPI::CullingScene::OcclusionPlane rpiOcclusionPlane; + + static const Vector3 BL = Vector3(-0.5f, 0.0f, -0.5f); + static const Vector3 TL = Vector3(-0.5f, 0.0f, 0.5f); + static const Vector3 TR = Vector3( 0.5f, 0.0f, 0.5f); + static const Vector3 BR = Vector3( 0.5f, 0.0f, -0.5f); + + const AZ::Transform& transform = occlusionCullingPlane->GetTransform(); + + // convert corners to world space + rpiOcclusionPlane.m_cornerBL = transform.TransformPoint(BL); + rpiOcclusionPlane.m_cornerTL = transform.TransformPoint(TL); + rpiOcclusionPlane.m_cornerTR = transform.TransformPoint(TR); + rpiOcclusionPlane.m_cornerBR = transform.TransformPoint(BR); + + // build world space AABB + AZ::Vector3 aabbMin = rpiOcclusionPlane.m_cornerBL.GetMin(rpiOcclusionPlane.m_cornerTR); + AZ::Vector3 aabbMax = rpiOcclusionPlane.m_cornerBL.GetMax(rpiOcclusionPlane.m_cornerTR); + rpiOcclusionPlane.m_aabb = Aabb::CreateFromMinMax(aabbMin, aabbMax); + + m_rpiOcclusionPlanes.push_back(rpiOcclusionPlane); + } + + GetParentScene()->GetCullingScene()->SetOcclusionPlanes(m_rpiOcclusionPlanes); + + m_rpiListNeedsUpdate = false; + } + } + + OcclusionCullingPlaneHandle OcclusionCullingPlaneFeatureProcessor::AddOcclusionCullingPlane(const AZ::Transform& transform) + { + AZStd::shared_ptr occlusionCullingPlane = AZStd::make_shared(); + occlusionCullingPlane->Init(GetParentScene()); + occlusionCullingPlane->SetTransform(transform); + m_occlusionCullingPlanes.push_back(occlusionCullingPlane); + m_rpiListNeedsUpdate = true; + + return occlusionCullingPlane; + } + + void OcclusionCullingPlaneFeatureProcessor::RemoveOcclusionCullingPlane(OcclusionCullingPlaneHandle& occlusionCullingPlane) + { + AZ_Assert(occlusionCullingPlane.get(), "RemoveOcclusionCullingPlane called with an invalid handle"); + + auto itEntry = AZStd::find_if(m_occlusionCullingPlanes.begin(), m_occlusionCullingPlanes.end(), [&](AZStd::shared_ptr const& entry) + { + return (entry == occlusionCullingPlane); + }); + + AZ_Assert(itEntry != m_occlusionCullingPlanes.end(), "RemoveOcclusionCullingPlane called with an occlusion plane that is not in the occlusion plane list"); + m_occlusionCullingPlanes.erase(itEntry); + occlusionCullingPlane = nullptr; + m_rpiListNeedsUpdate = true; + } + + void OcclusionCullingPlaneFeatureProcessor::SetTransform(const OcclusionCullingPlaneHandle& occlusionCullingPlane, const AZ::Transform& transform) + { + AZ_Assert(occlusionCullingPlane.get(), "SetTransform called with an invalid handle"); + occlusionCullingPlane->SetTransform(transform); + m_rpiListNeedsUpdate = true; + } + + void OcclusionCullingPlaneFeatureProcessor::SetEnabled(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool enabled) + { + AZ_Assert(occlusionCullingPlane.get(), "Enable called with an invalid handle"); + occlusionCullingPlane->SetEnabled(enabled); + m_rpiListNeedsUpdate = true; + } + + void OcclusionCullingPlaneFeatureProcessor::ShowVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool showVisualization) + { + AZ_Assert(occlusionCullingPlane.get(), "ShowVisualization called with an invalid handle"); + occlusionCullingPlane->ShowVisualization(showVisualization); + } + + void OcclusionCullingPlaneFeatureProcessor::SetTransparentVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool transparentVisualization) + { + AZ_Assert(occlusionCullingPlane.get(), "SetTransparentVisualization called with an invalid handle"); + occlusionCullingPlane->SetTransparentVisualization(transparentVisualization); + } + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h new file mode 100644 index 0000000000..8b3ac3f58d --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h @@ -0,0 +1,66 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include + +namespace AZ +{ + namespace Render + { + //! This class manages OcclusionCullingPlanes which are used to cull meshes that are inside the view frustum + class OcclusionCullingPlaneFeatureProcessor final + : public OcclusionCullingPlaneFeatureProcessorInterface + { + public: + AZ_RTTI(AZ::Render::OcclusionCullingPlaneFeatureProcessor, "{C3DE91D7-EF7A-4A82-A55F-E22BC52074EA}", OcclusionCullingPlaneFeatureProcessorInterface); + + static void Reflect(AZ::ReflectContext* context); + + OcclusionCullingPlaneFeatureProcessor() = default; + virtual ~OcclusionCullingPlaneFeatureProcessor() = default; + + // OcclusionCullingPlaneFeatureProcessorInterface overrides + OcclusionCullingPlaneHandle AddOcclusionCullingPlane(const AZ::Transform& transform) override; + void RemoveOcclusionCullingPlane(OcclusionCullingPlaneHandle& handle) override; + bool IsValidOcclusionCullingPlaneHandle(const OcclusionCullingPlaneHandle& occlusionCullingPlane) const override { return (occlusionCullingPlane.get() != nullptr); } + void SetTransform(const OcclusionCullingPlaneHandle& occlusionCullingPlane, const AZ::Transform& transform) override; + void SetEnabled(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool enable) override; + void ShowVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool showVisualization) override; + void SetTransparentVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool transparentVisualization) override; + + // FeatureProcessor overrides + void Activate() override; + void Deactivate() override; + + // RPI::SceneNotificationBus overrides ... + void OnBeginPrepareRender() override; + + // retrieve the full list of occlusion planes + using OcclusionCullingPlaneVector = AZStd::vector>; + OcclusionCullingPlaneVector& GetOcclusionCullingPlanes() { return m_occlusionCullingPlanes; } + + private: + AZ_DISABLE_COPY_MOVE(OcclusionCullingPlaneFeatureProcessor); + + // list of occlusion planes + const size_t InitialOcclusionCullingPlanesAllocationSize = 64; + OcclusionCullingPlaneVector m_occlusionCullingPlanes; + + // prebuilt list of RPI scene occlusion planes + RPI::CullingScene::OcclusionPlaneVector m_rpiOcclusionPlanes; + bool m_rpiListNeedsUpdate = false; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.cpp new file mode 100644 index 0000000000..9f885ede70 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.cpp @@ -0,0 +1,247 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace AZ::Render +{ + + RPI::Ptr TaaPass::Create(const RPI::PassDescriptor& descriptor) + { + RPI::Ptr pass = aznew TaaPass(descriptor); + return pass; + } + + TaaPass::TaaPass(const RPI::PassDescriptor& descriptor) + : Base(descriptor) + { + uint32_t numJitterPositions = 8; + + const TaaPassData* taaPassData = RPI::PassUtils::GetPassData(descriptor); + if (taaPassData) + { + numJitterPositions = taaPassData->m_numJitterPositions; + } + + // The coprimes 2, 3 are commonly used for halton sequences because they have an even distribution even for + // few samples. With larger primes you need to offset by some amount between each prime to have the same + // effect. We could allow this to be configurable in the future. + SetupSubPixelOffsets(2, 3, numJitterPositions); + } + + void TaaPass::CompileResources(const RHI::FrameGraphCompileContext& context) + { + struct TaaConstants + { + AZStd::array m_size = { 1, 1 }; + AZStd::array m_rcpSize = { 0.0, 0.0 }; + + AZStd::array m_weights1 = { 0.0 }; + AZStd::array m_weights2 = { 0.0 }; + AZStd::array m_weights3 = { 0.0 }; + }; + + TaaConstants cb; + RHI::Size inputSize = m_lastFrameAccumulationBinding->m_attachment->m_descriptor.m_image.m_size; + cb.m_size[0] = inputSize.m_width; + cb.m_size[1] = inputSize.m_height; + cb.m_rcpSize[0] = 1.0f / inputSize.m_width; + cb.m_rcpSize[1] = 1.0f / inputSize.m_height; + + Offset jitterOffset = m_subPixelOffsets.at(m_offsetIndex); + GenerateFilterWeights(Vector2(jitterOffset.m_xOffset, jitterOffset.m_yOffset)); + cb.m_weights1 = { m_filterWeights[0], m_filterWeights[1], m_filterWeights[2], m_filterWeights[3] }; + cb.m_weights2 = { m_filterWeights[4], m_filterWeights[5], m_filterWeights[6], m_filterWeights[7] }; + cb.m_weights3 = { m_filterWeights[8], 0.0f, 0.0f, 0.0f }; + + m_shaderResourceGroup->SetConstant(m_constantDataIndex, cb); + + + Base::CompileResources(context); + } + + void TaaPass::FrameBeginInternal(FramePrepareParams params) + { + RHI::Size inputSize = m_inputColorBinding->m_attachment->m_descriptor.m_image.m_size; + Vector2 rcpInputSize = Vector2(1.0 / inputSize.m_width, 1.0 / inputSize.m_height); + + RPI::ViewPtr view = GetRenderPipeline()->GetDefaultView(); + m_offsetIndex = (m_offsetIndex + 1) % m_subPixelOffsets.size(); + Offset offset = m_subPixelOffsets.at(m_offsetIndex); + view->SetClipSpaceOffset(offset.m_xOffset * rcpInputSize.GetX(), offset.m_yOffset * rcpInputSize.GetY()); + + m_lastFrameAccumulationBinding->SetAttachment(m_accumulationAttachments[m_accumulationOuptutIndex]); + m_accumulationOuptutIndex ^= 1; // swap which attachment is the output and last frame + + UpdateAttachmentImage(m_accumulationAttachments[m_accumulationOuptutIndex]); + m_outputColorBinding->SetAttachment(m_accumulationAttachments[m_accumulationOuptutIndex]); + + Base::FrameBeginInternal(params); + } + + void TaaPass::ResetInternal() + { + m_accumulationAttachments[0].reset(); + m_accumulationAttachments[1].reset(); + + m_inputColorBinding = nullptr; + m_lastFrameAccumulationBinding = nullptr; + m_outputColorBinding = nullptr; + + Base::ResetInternal(); + } + + void TaaPass::BuildAttachmentsInternal() + { + m_accumulationAttachments[0] = FindAttachment(Name("Accumulation1")); + m_accumulationAttachments[1] = FindAttachment(Name("Accumulation2")); + + bool hasAttachments = m_accumulationAttachments[0] || m_accumulationAttachments[1]; + AZ_Error("TaaPass", hasAttachments, "TaaPass must have Accumulation1 and Accumulation2 ImageAttachments defined."); + + if (hasAttachments) + { + // Make sure the attachments have images when the pass first loads. + for (auto i : { 0, 1 }) + { + if (!m_accumulationAttachments[i]->m_importedResource) + { + UpdateAttachmentImage(m_accumulationAttachments[i]); + } + } + } + + m_inputColorBinding = FindAttachmentBinding(Name("InputColor")); + AZ_Error("TaaPass", m_inputColorBinding, "TaaPass requires a slot for InputColor."); + m_lastFrameAccumulationBinding = FindAttachmentBinding(Name("LastFrameAccumulation")); + AZ_Error("TaaPass", m_lastFrameAccumulationBinding, "TaaPass requires a slot for LastFrameAccumulation."); + m_outputColorBinding = FindAttachmentBinding(Name("OutputColor")); + AZ_Error("TaaPass", m_outputColorBinding, "TaaPass requires a slot for OutputColor."); + + // Set up the attachment for last frame accumulation and output color if it's never been done to + // ensure SRG indices are set up correctly by the pass system. + if (m_lastFrameAccumulationBinding->m_attachment == nullptr) + { + m_lastFrameAccumulationBinding->SetAttachment(m_accumulationAttachments[0]); + m_outputColorBinding->SetAttachment(m_accumulationAttachments[1]); + } + + Base::BuildAttachmentsInternal(); + } + + void TaaPass::UpdateAttachmentImage(RPI::Ptr& attachment) + { + if (!attachment) + { + return; + } + + // update the image attachment descriptor to sync up size and format + attachment->Update(true); + RHI::ImageDescriptor& imageDesc = attachment->m_descriptor.m_image; + RPI::AttachmentImage* currentImage = azrtti_cast(attachment->m_importedResource.get()); + + if (attachment->m_importedResource && imageDesc.m_size == currentImage->GetDescriptor().m_size) + { + // If there's a resource already and the size didn't change, just keep using the old AttachmentImage. + return; + } + + Data::Instance pool = RPI::ImageSystemInterface::Get()->GetSystemAttachmentPool(); + + // set the bind flags + imageDesc.m_bindFlags |= RHI::ImageBindFlags::Color | RHI::ImageBindFlags::ShaderReadWrite; + + // The ImageViewDescriptor must be specified to make sure the frame graph compiler doesn't treat this as a transient image. + RHI::ImageViewDescriptor viewDesc = RHI::ImageViewDescriptor::Create(imageDesc.m_format, 0, 0); + viewDesc.m_aspectFlags = RHI::ImageAspectFlags::Color; + viewDesc.m_overrideBindFlags = RHI::ImageBindFlags::ShaderReadWrite; + + // The full path name is needed for the attachment image so it's not deduplicated from accumulation images in different pipelines. + AZStd::string imageName = RPI::ConcatPassString(GetPathName(), attachment->m_path); + auto attachmentImage = RPI::AttachmentImage::Create(*pool.get(), imageDesc, Name(imageName), nullptr, &viewDesc); + + attachment->m_path = attachmentImage->GetAttachmentId(); + attachment->m_importedResource = attachmentImage; + } + + void TaaPass::SetupSubPixelOffsets(uint32_t haltonX, uint32_t haltonY, uint32_t length) + { + m_subPixelOffsets.resize(length); + HaltonSequence<2> sequence = HaltonSequence<2>({haltonX, haltonY}); + sequence.FillHaltonSequence(m_subPixelOffsets.begin(), m_subPixelOffsets.end()); + + // Adjust to the -1.0 to 1.0 range. This is done because the view needs offsets in clip + // space and is one less calculation that would need to be done in FrameBeginInternal() + AZStd::for_each(m_subPixelOffsets.begin(), m_subPixelOffsets.end(), + [](Offset& offset) + { + offset.m_xOffset = 2.0f * offset.m_xOffset - 1.0f; + offset.m_yOffset = 2.0f * offset.m_yOffset - 1.0f; + } + ); + } + + // Approximation of a Blackman Harris window function of width 3.3. + // https://en.wikipedia.org/wiki/Window_function#Blackman%E2%80%93Harris_window + static float BlackmanHarris(AZ::Vector2 uv) + { + return expf(-2.29f * (uv.GetX() * uv.GetX() + uv.GetY() * uv.GetY())); + } + + // Generates filter weights for the 3x3 neighborhood of a pixel. Since jitter positions are the + // same for every pixel we can calculate this once here and upload to the SRG. + // Jitter weights are based on a window function centered at the pixel center (we use Blackman-Harris). + // As the jitter position moves around, some neighborhood locations decrease in weight, and others + // increase in weight based on their distance from the center of the pixel. + void TaaPass::GenerateFilterWeights(AZ::Vector2 jitterOffset) + { + static const AZStd::array pixelOffsets = + { + // Center + Vector2(0.0f, 0.0f), + // Cross + Vector2( 1.0f, 0.0f), + Vector2( 0.0f, 1.0f), + Vector2(-1.0f, 0.0f), + Vector2( 0.0f, -1.0f), + // Diagonals + Vector2( 1.0f, 1.0f), + Vector2( 1.0f, -1.0f), + Vector2(-1.0f, 1.0f), + Vector2(-1.0f, -1.0f), + }; + + float sum = 0.0f; + for (uint32_t i = 0; i < 9; ++i) + { + m_filterWeights[i] = BlackmanHarris(pixelOffsets[i] + jitterOffset); + sum += m_filterWeights[i]; + } + + // Normalize the weight so the sum of all weights is 1.0. + float normalization = 1.0f / sum; + for (uint32_t i = 0; i < 9; ++i) + { + m_filterWeights[i] *= normalization; + } + } + +} // namespace AZ::Render diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.h new file mode 100644 index 0000000000..6133720691 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.h @@ -0,0 +1,105 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include +#include + +namespace AZ::Render +{ + //! Custom data for the Taa Pass. + struct TaaPassData + : public RPI::ComputePassData + { + AZ_RTTI(TaaPassData, "{BCDF5C7D-7A78-4C69-A460-FA6899C3B960}", ComputePassData); + AZ_CLASS_ALLOCATOR(TaaPassData, SystemAllocator, 0); + + TaaPassData() = default; + virtual ~TaaPassData() = default; + + static void Reflect(ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1) + ->Field("NumJitterPositions", &TaaPassData::m_numJitterPositions) + ; + } + } + + uint32_t m_numJitterPositions = 8; + }; + + class TaaPass : public RPI::ComputePass + { + using Base = RPI::ComputePass; + AZ_RPI_PASS(TaaPass); + + public: + AZ_RTTI(AZ::Render::TaaPass, "{AB3BD4EA-33D7-477F-82B4-21DDFB517499}", Base); + AZ_CLASS_ALLOCATOR(TaaPass, SystemAllocator, 0); + virtual ~TaaPass() = default; + + /// Creates a TaaPass + static RPI::Ptr Create(const RPI::PassDescriptor& descriptor); + + private: + + TaaPass(const RPI::PassDescriptor& descriptor); + + // Scope producer functions... + void CompileResources(const RHI::FrameGraphCompileContext& context) override; + + // Pass behavior overrides... + void FrameBeginInternal(FramePrepareParams params) override; + void ResetInternal() override; + void BuildAttachmentsInternal() override; + + void UpdateAttachmentImage(RPI::Ptr& attachment); + + void SetupSubPixelOffsets(uint32_t haltonX, uint32_t haltonY, uint32_t length); + void GenerateFilterWeights(AZ::Vector2 jitterOffset); + + RHI::ShaderInputNameIndex m_outputIndex = "m_output"; + RHI::ShaderInputNameIndex m_lastFrameAccumulationIndex = "m_lastFrameAccumulation"; + RHI::ShaderInputNameIndex m_constantDataIndex = "m_constantData"; + + Data::Instance m_accumulationAttachments[2]; + + RPI::PassAttachmentBinding* m_inputColorBinding = nullptr; + RPI::PassAttachmentBinding* m_lastFrameAccumulationBinding = nullptr; + RPI::PassAttachmentBinding* m_outputColorBinding = nullptr; + + struct Offset + { + Offset() = default; + + // Constructor for implicit conversion from array output by HaltonSequence. + Offset(AZStd::array offsets) + : m_xOffset(offsets[0]) + , m_yOffset(offsets[1]) + {}; + + float m_xOffset = 0.0f; + float m_yOffset = 0.0f; + }; + + AZStd::array m_filterWeights = { 0.0f }; + + AZStd::vector m_subPixelOffsets; + uint32_t m_offsetIndex = 0; + + uint8_t m_accumulationOuptutIndex = 0; + + }; +} // namespace AZ::Render diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake index 76b71e5fae..a759de77fa 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake @@ -119,26 +119,28 @@ set(FILES Source/Decals/AsyncLoadTracker.h Source/Decals/DecalTextureArrayFeatureProcessor.h Source/Decals/DecalTextureArrayFeatureProcessor.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.h - Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.h - Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.h - Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.h - Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.h - Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.h - Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.h - Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp - Source/DiffuseProbeGrid/DiffuseProbeGrid.h - Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h - Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h - Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridRayTracingPass.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridRayTracingPass.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridRenderPass.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridRenderPass.h + Source/DiffuseGlobalIllumination/DiffuseProbeGrid.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGrid.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp + Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.h + Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp Source/DisplayMapper/AcesOutputTransformPass.cpp Source/DisplayMapper/AcesOutputTransformLutPass.cpp Source/DisplayMapper/ApplyShaperLookupTablePass.cpp @@ -173,6 +175,10 @@ set(FILES Source/MorphTargets/MorphTargetComputePass.h Source/MorphTargets/MorphTargetDispatchItem.cpp Source/MorphTargets/MorphTargetDispatchItem.h + Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h + Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp + Source/OcclusionCullingPlane/OcclusionCullingPlane.h + Source/OcclusionCullingPlane/OcclusionCullingPlane.cpp Source/PostProcess/PostProcessBase.cpp Source/PostProcess/PostProcessBase.h Source/PostProcess/PostProcessFeatureProcessor.cpp @@ -246,6 +252,8 @@ set(FILES Source/PostProcessing/SsaoPasses.h Source/PostProcessing/SubsurfaceScatteringPass.cpp Source/PostProcessing/SubsurfaceScatteringPass.h + Source/PostProcessing/TaaPass.h + Source/PostProcessing/TaaPass.cpp Source/RayTracing/RayTracingFeatureProcessor.h Source/RayTracing/RayTracingFeatureProcessor.cpp Source/RayTracing/RayTracingAccelerationStructurePass.cpp diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake index 9034859707..fd667634aa 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake @@ -22,7 +22,8 @@ set(FILES Include/Atom/Feature/CoreLights/SimpleSpotLightFeatureProcessorInterface.h Include/Atom/Feature/CoreLights/ShadowConstants.h Include/Atom/Feature/Decals/DecalFeatureProcessorInterface.h - Include/Atom/Feature/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessorInterface.h + Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessorInterface.h + Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessorInterface.h Include/Atom/Feature/DisplayMapper/DisplayMapperFeatureProcessorInterface.h Include/Atom/Feature/ImageBasedLights/ImageBasedLightFeatureProcessorInterface.h Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h @@ -44,6 +45,7 @@ set(FILES Include/Atom/Feature/ParamMacros/StartParamFunctionsVirtual.inl Include/Atom/Feature/ParamMacros/StartParamMembers.inl Include/Atom/Feature/ParamMacros/StartParamSerializeContext.inl + Include/Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h Include/Atom/Feature/PostProcess/PostProcessFeatureProcessorInterface.h Include/Atom/Feature/PostProcess/PostProcessParams.inl Include/Atom/Feature/PostProcess/PostProcessSettings.inl diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/FrameGraphAttachmentInterface.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/FrameGraphAttachmentInterface.h index c3194efbb6..af29079a97 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/FrameGraphAttachmentInterface.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/FrameGraphAttachmentInterface.h @@ -89,6 +89,12 @@ namespace AZ return m_attachmentDatabase.IsAttachmentValid(attachmentId); } + //! Returns the FrameAttachment for a given AttachmentId, or nullptr if not found. + const FrameAttachment* FindAttachment(const AttachmentId& attachmentId) const + { + return m_attachmentDatabase.FindAttachment(attachmentId); + } + //! Resolves an attachment id to a buffer descriptor. This is useful when accessing buffer information for //! an attachment that was declared in a different scope. //! \param attachmentId The attachment id used to lookup the descriptors. diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.cpp index 76662ebc6e..04f2465e78 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.cpp @@ -720,7 +720,7 @@ namespace AZ StringList deviceExtensions = physicalDevice.GetDeviceExtensionNames(); StringList::iterator itRayTracingExtension = AZStd::find(deviceExtensions.begin(), deviceExtensions.end(), VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME); m_features.m_rayTracing = (itRayTracingExtension != deviceExtensions.end()); - m_features.m_unboundedArrays = true; + m_features.m_unboundedArrays = physicalDevice.GetPhysicalDeviceDescriptorIndexingFeatures().shaderStorageTexelBufferArrayNonUniformIndexing; const auto& deviceLimits = physicalDevice.GetDeviceLimits(); m_limits.m_maxImageDimension1D = deviceLimits.maxImageDimension1D; diff --git a/Gems/Atom/RPI/Code/CMakeLists.txt b/Gems/Atom/RPI/Code/CMakeLists.txt index d2b7fba071..2898967add 100644 --- a/Gems/Atom/RPI/Code/CMakeLists.txt +++ b/Gems/Atom/RPI/Code/CMakeLists.txt @@ -9,6 +9,17 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +ly_get_list_relative_pal_filename(pal_source_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/${PAL_PLATFORM_NAME}) + +#for PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED and PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED +include(${pal_source_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) + +if(PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED) + set(MASKED_OCCLUSION_CULLING_FILES "atom_rpi_masked_occlusion_files.cmake") +else() + set(MASKED_OCCLUSION_CULLING_FILES "") +endif() + ly_add_target( NAME Atom_RPI.Public STATIC NAMESPACE Gem @@ -16,11 +27,15 @@ ly_add_target( atom_rpi_reflect_files.cmake atom_rpi_public_files.cmake ../Assets/atom_rpi_asset_files.cmake + ${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + ${MASKED_OCCLUSION_CULLING_FILES} INCLUDE_DIRECTORIES PRIVATE Source + ${pal_source_dir} PUBLIC Include + External BUILD_DEPENDENCIES PRIVATE AZ::AtomCore @@ -159,8 +174,6 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) ly_get_list_relative_pal_filename(pal_source_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/${PAL_PLATFORM_NAME}) ly_get_list_relative_pal_filename(common_source_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/Common) - include(${pal_source_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) #for PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED - if(NOT PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED) # Create a stub diff --git a/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/CompilerSpecific.inl b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/CompilerSpecific.inl new file mode 100644 index 0000000000..a6203ff939 --- /dev/null +++ b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/CompilerSpecific.inl @@ -0,0 +1,98 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +//////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Common shared include file to hide compiler/os specific functions from the rest of the code. +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__) + #define __MICROSOFT_COMPILER +#endif + +#if defined(_WIN32) && (defined(_MSC_VER) || defined(__INTEL_COMPILER) || defined(__clang__)) // Windows: MSVC / Intel compiler / clang + #include + #include + + #define FORCE_INLINE __forceinline + + FORCE_INLINE unsigned long find_clear_lsb(unsigned int *mask) + { + unsigned long idx; + _BitScanForward(&idx, *mask); + *mask &= *mask - 1; + return idx; + } + + FORCE_INLINE void *aligned_alloc(size_t alignment, size_t size) + { + return _aligned_malloc(size, alignment); + } + + FORCE_INLINE void aligned_free(void *ptr) + { + _aligned_free(ptr); + } + +#elif defined(__GNUG__) || defined(__clang__) // G++ or clang + #include +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) + #include // memalign +#else + #include // memalign +#endif + #include + #include + #include + + #define FORCE_INLINE inline + + FORCE_INLINE unsigned long find_clear_lsb(unsigned int *mask) + { + unsigned long idx; + idx = __builtin_ctzl(*mask); + *mask &= *mask - 1; + return idx; + } + + FORCE_INLINE void *aligned_alloc(size_t alignment, size_t size) + { + return memalign(alignment, size); + } + + FORCE_INLINE void aligned_free(void *ptr) + { + free(ptr); + } + + FORCE_INLINE void __cpuidex(int* cpuinfo, int function, int subfunction) + { + __cpuid_count(function, subfunction, cpuinfo[0], cpuinfo[1], cpuinfo[2], cpuinfo[3]); + } + + FORCE_INLINE unsigned long long _xgetbv(unsigned int index) + { + unsigned int eax, edx; + __asm__ __volatile__( + "xgetbv;" + : "=a" (eax), "=d"(edx) + : "c" (index) + ); + return ((unsigned long long)edx << 32) | eax; + } + +#else + #error Unsupported compiler +#endif diff --git a/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/LICENSE.txt b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/LICENSE.txt new file mode 100644 index 0000000000..f1b08a582c --- /dev/null +++ b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/LICENSE.txt @@ -0,0 +1,181 @@ + +Apache License + Version 2.0, January 2004 + + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, +non-exclusive, no-charge, royalty-free, irrevocable copyright license to +reproduce, prepare Derivative Works of, publicly display, publicly perform, +sublicense, and distribute the Work and such Derivative Works in Source or +Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) with the Work +to which such Contribution(s) was submitted. If You institute patent litigation +against any entity (including a cross-claim or counterclaim in a lawsuit) +alleging that the Work or a Contribution incorporated within the Work +constitutes direct or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate as of the date +such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and in +Source or Object form, provided that You meet the following conditions: + You must give any other recipients of the Work or Derivative Works a copy of + this License; and + + + You must cause any modified files to carry prominent notices stating that You + changed the files; and + + + You must retain, in the Source form of any Derivative Works that You + distribute, all copyright, patent, trademark, and attribution notices from the + Source form of the Work, excluding those notices that do not pertain to any + part of the Derivative Works; and + + + If the Work includes a "NOTICE" text file as part of its distribution, then + any Derivative Works that You distribute must include a readable copy of the + attribution notices contained within such NOTICE file, excluding those notices + that do not pertain to any part of the Derivative Works, in at least one of + the following places: within a NOTICE text file distributed as part of the + Derivative Works; within the Source form or documentation, if provided along + with the Derivative Works; or, within a display generated by the Derivative + Works, if and wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and do not modify the + License. You may add Your own attribution notices within Derivative Works that + You distribute, alongside or as an addendum to the NOTICE text from the Work, + provided that such additional attribution notices cannot be construed as + modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without any +additional terms or conditions. Notwithstanding the above, nothing herein shall +supersede or modify the terms of any separate license agreement you may have +executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in +writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any risks +associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in +tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to in +writing, shall any Contributor be liable to You for damages, including any +direct, indirect, special, incidental, or consequential damages of any character +arising as a result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, work stoppage, +computer failure or malfunction, or any and all other commercial damages or +losses), even if such Contributor has been advised of the possibility of such +damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or +Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such +obligations, You may act only on Your own behalf and on Your sole +responsibility, not on behalf of any other Contributor, and only if You agree to +indemnify, defend, and hold each Contributor harmless for any liability incurred +by, or claims asserted against, such Contributor by reason of your accepting any +such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + +Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, +Version 2.0 (the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or +agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +or implied. See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp new file mode 100644 index 0000000000..2844fbde00 --- /dev/null +++ b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp @@ -0,0 +1,456 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +//////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include "MaskedOcclusionCulling.h" +#include "CompilerSpecific.inl" + +#if MOC_RECORDER_ENABLE +#include "FrameRecorder.h" +#endif + +#if defined(__AVX__) || defined(__AVX2__) + // For performance reasons, the MaskedOcclusionCullingAVX2/512.cpp files should be compiled with VEX encoding for SSE instructions (to avoid + // AVX-SSE transition penalties, see https://software.intel.com/en-us/articles/avoiding-avx-sse-transition-penalties). However, this file + // _must_ be compiled without VEX encoding to allow backwards compatibility. Best practice is to use lowest supported target platform + // (/arch:SSE2) as project default, and elevate only the MaskedOcclusionCullingAVX2/512.cpp files. + #error The MaskedOcclusionCulling.cpp should be compiled with lowest supported target platform, e.g. /arch:SSE2 +#endif + +static MaskedOcclusionCulling::Implementation DetectCPUFeatures(MaskedOcclusionCulling::pfnAlignedAlloc alignedAlloc, MaskedOcclusionCulling::pfnAlignedFree alignedFree) +{ + struct CpuInfo { int regs[4]; }; + + // Get regular CPUID values + int regs[4]; + __cpuidex(regs, 0, 0); + + // MOCVectorAllocator mocalloc( alignedAlloc, alignedFree ); + // std::vector> cpuId( mocalloc ), cpuIdEx( mocalloc ); + // cpuId.resize( regs[0] ); + size_t cpuIdCount = regs[0]; + CpuInfo * cpuId = (CpuInfo*)alignedAlloc( 64, sizeof(CpuInfo) * cpuIdCount ); + + for (size_t i = 0; i < cpuIdCount; ++i) + __cpuidex(cpuId[i].regs, (int)i, 0); + + // Get extended CPUID values + __cpuidex(regs, 0x80000000, 0); + + //cpuIdEx.resize(regs[0] - 0x80000000); + size_t cpuIdExCount = regs[0] - 0x80000000; + CpuInfo * cpuIdEx = (CpuInfo*)alignedAlloc( 64, sizeof( CpuInfo ) * cpuIdExCount ); + + for (size_t i = 0; i < cpuIdExCount; ++i) + __cpuidex(cpuIdEx[i].regs, 0x80000000 + (int)i, 0); + + #define TEST_BITS(A, B) (((A) & (B)) == (B)) + #define TEST_FMA_MOVE_OXSAVE (cpuIdCount >= 1 && TEST_BITS(cpuId[1].regs[2], (1 << 12) | (1 << 22) | (1 << 27))) + #define TEST_LZCNT (cpuIdExCount >= 1 && TEST_BITS(cpuIdEx[1].regs[2], 0x20)) + #define TEST_SSE41 (cpuIdCount >= 1 && TEST_BITS(cpuId[1].regs[2], (1 << 19))) + #define TEST_XMM_YMM (cpuIdCount >= 1 && TEST_BITS(_xgetbv(0), (1 << 2) | (1 << 1))) + #define TEST_OPMASK_ZMM (cpuIdCount >= 1 && TEST_BITS(_xgetbv(0), (1 << 7) | (1 << 6) | (1 << 5))) + #define TEST_BMI1_BMI2_AVX2 (cpuIdCount >= 7 && TEST_BITS(cpuId[7].regs[1], (1 << 3) | (1 << 5) | (1 << 8))) + #define TEST_AVX512_F_BW_DQ (cpuIdCount >= 7 && TEST_BITS(cpuId[7].regs[1], (1 << 16) | (1 << 17) | (1 << 30))) + + MaskedOcclusionCulling::Implementation retVal = MaskedOcclusionCulling::SSE2; + if (TEST_FMA_MOVE_OXSAVE && TEST_LZCNT && TEST_SSE41) + { + if (TEST_XMM_YMM && TEST_OPMASK_ZMM && TEST_BMI1_BMI2_AVX2 && TEST_AVX512_F_BW_DQ) + retVal = MaskedOcclusionCulling::AVX512; + else if (TEST_XMM_YMM && TEST_BMI1_BMI2_AVX2) + retVal = MaskedOcclusionCulling::AVX2; + } + else if (TEST_SSE41) + retVal = MaskedOcclusionCulling::SSE41; + alignedFree( cpuId ); + alignedFree( cpuIdEx ); + return retVal; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Utility functions (not directly related to the algorithm/rasterizer) +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void MaskedOcclusionCulling::TransformVertices(const float *mtx, const float *inVtx, float *xfVtx, unsigned int nVtx, const VertexLayout &vtxLayout) +{ + // This function pretty slow, about 10-20% slower than if the vertices are stored in aligned SOA form. + if (nVtx == 0) + return; + + // Load matrix and swizzle out the z component. For post-multiplication (OGL), the matrix is assumed to be column + // major, with one column per SSE register. For pre-multiplication (DX), the matrix is assumed to be row major. + __m128 mtxCol0 = _mm_loadu_ps(mtx); + __m128 mtxCol1 = _mm_loadu_ps(mtx + 4); + __m128 mtxCol2 = _mm_loadu_ps(mtx + 8); + __m128 mtxCol3 = _mm_loadu_ps(mtx + 12); + + int stride = vtxLayout.mStride; + const char *vPtr = (const char *)inVtx; + float *outPtr = xfVtx; + + // Iterate through all vertices and transform + for (unsigned int vtx = 0; vtx < nVtx; ++vtx) + { + __m128 xVal = _mm_load1_ps((float*)(vPtr)); + __m128 yVal = _mm_load1_ps((float*)(vPtr + vtxLayout.mOffsetY)); + __m128 zVal = _mm_load1_ps((float*)(vPtr + vtxLayout.mOffsetZ)); + + __m128 xform = _mm_add_ps(_mm_mul_ps(mtxCol0, xVal), _mm_add_ps(_mm_mul_ps(mtxCol1, yVal), _mm_add_ps(_mm_mul_ps(mtxCol2, zVal), mtxCol3))); + _mm_storeu_ps(outPtr, xform); + vPtr += stride; + outPtr += 4; + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Typedefs +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef MaskedOcclusionCulling::pfnAlignedAlloc pfnAlignedAlloc; +typedef MaskedOcclusionCulling::pfnAlignedFree pfnAlignedFree; +typedef MaskedOcclusionCulling::VertexLayout VertexLayout; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Common SSE2/SSE4.1 defines +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define SIMD_LANES 4 +#define TILE_HEIGHT_SHIFT 2 + +#define SIMD_LANE_IDX _mm_setr_epi32(0, 1, 2, 3) + +#define SIMD_SUB_TILE_COL_OFFSET _mm_setr_epi32(0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3) +#define SIMD_SUB_TILE_ROW_OFFSET _mm_setzero_si128() +#define SIMD_SUB_TILE_COL_OFFSET_F _mm_setr_ps(0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3) +#define SIMD_SUB_TILE_ROW_OFFSET_F _mm_setzero_ps() + +#define SIMD_LANE_YCOORD_I _mm_setr_epi32(128, 384, 640, 896) +#define SIMD_LANE_YCOORD_F _mm_setr_ps(128.0f, 384.0f, 640.0f, 896.0f) + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Common SSE2/SSE4.1 functions +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef __m128 __mw; +typedef __m128i __mwi; + +#define _mmw_set1_ps _mm_set1_ps +#define _mmw_setzero_ps _mm_setzero_ps +#define _mmw_and_ps _mm_and_ps +#define _mmw_or_ps _mm_or_ps +#define _mmw_xor_ps _mm_xor_ps +#define _mmw_not_ps(a) _mm_xor_ps((a), _mm_castsi128_ps(_mm_set1_epi32(~0))) +#define _mmw_andnot_ps _mm_andnot_ps +#define _mmw_neg_ps(a) _mm_xor_ps((a), _mm_set1_ps(-0.0f)) +#define _mmw_abs_ps(a) _mm_and_ps((a), _mm_castsi128_ps(_mm_set1_epi32(0x7FFFFFFF))) +#define _mmw_add_ps _mm_add_ps +#define _mmw_sub_ps _mm_sub_ps +#define _mmw_mul_ps _mm_mul_ps +#define _mmw_div_ps _mm_div_ps +#define _mmw_min_ps _mm_min_ps +#define _mmw_max_ps _mm_max_ps +#define _mmw_movemask_ps _mm_movemask_ps +#define _mmw_cmpge_ps(a,b) _mm_cmpge_ps(a, b) +#define _mmw_cmpgt_ps(a,b) _mm_cmpgt_ps(a, b) +#define _mmw_cmpeq_ps(a,b) _mm_cmpeq_ps(a, b) +#define _mmw_fmadd_ps(a,b,c) _mm_add_ps(_mm_mul_ps(a,b), c) +#define _mmw_fmsub_ps(a,b,c) _mm_sub_ps(_mm_mul_ps(a,b), c) +#define _mmw_shuffle_ps _mm_shuffle_ps +#define _mmw_insertf32x4_ps(a,b,c) (b) +#define _mmw_cvtepi32_ps _mm_cvtepi32_ps +#define _mmw_blendv_epi32(a,b,c) simd_cast<__mwi>(_mmw_blendv_ps(simd_cast<__mw>(a), simd_cast<__mw>(b), simd_cast<__mw>(c))) + +#define _mmw_set1_epi32 _mm_set1_epi32 +#define _mmw_setzero_epi32 _mm_setzero_si128 +#define _mmw_and_epi32 _mm_and_si128 +#define _mmw_or_epi32 _mm_or_si128 +#define _mmw_xor_epi32 _mm_xor_si128 +#define _mmw_not_epi32(a) _mm_xor_si128((a), _mm_set1_epi32(~0)) +#define _mmw_andnot_epi32 _mm_andnot_si128 +#define _mmw_neg_epi32(a) _mm_sub_epi32(_mm_set1_epi32(0), (a)) +#define _mmw_add_epi32 _mm_add_epi32 +#define _mmw_sub_epi32 _mm_sub_epi32 +#define _mmw_subs_epu16 _mm_subs_epu16 +#define _mmw_cmpeq_epi32 _mm_cmpeq_epi32 +#define _mmw_cmpgt_epi32 _mm_cmpgt_epi32 +#define _mmw_srai_epi32 _mm_srai_epi32 +#define _mmw_srli_epi32 _mm_srli_epi32 +#define _mmw_slli_epi32 _mm_slli_epi32 +#define _mmw_cvtps_epi32 _mm_cvtps_epi32 +#define _mmw_cvttps_epi32 _mm_cvttps_epi32 + +#define _mmx_fmadd_ps _mmw_fmadd_ps +#define _mmx_max_epi32 _mmw_max_epi32 +#define _mmx_min_epi32 _mmw_min_epi32 + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SIMD casting functions +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template FORCE_INLINE T simd_cast(Y A); +template<> FORCE_INLINE __m128 simd_cast<__m128>(float A) { return _mm_set1_ps(A); } +template<> FORCE_INLINE __m128 simd_cast<__m128>(__m128i A) { return _mm_castsi128_ps(A); } +template<> FORCE_INLINE __m128 simd_cast<__m128>(__m128 A) { return A; } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(int A) { return _mm_set1_epi32(A); } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(__m128 A) { return _mm_castps_si128(A); } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(__m128i A) { return A; } + +#define MAKE_ACCESSOR(name, simd_type, base_type, is_const, elements) \ + FORCE_INLINE is_const base_type * name(is_const simd_type &a) { \ + union accessor { simd_type m_native; base_type m_array[elements]; }; \ + is_const accessor *acs = reinterpret_cast(&a); \ + return acs->m_array; \ + } + +MAKE_ACCESSOR(simd_f32, __m128, float, , 4) +MAKE_ACCESSOR(simd_f32, __m128, float, const, 4) +MAKE_ACCESSOR(simd_i32, __m128i, int, , 4) +MAKE_ACCESSOR(simd_i32, __m128i, int, const, 4) + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Specialized SSE input assembly function for general vertex gather +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +FORCE_INLINE void GatherVertices(__m128 *vtxX, __m128 *vtxY, __m128 *vtxW, const float *inVtx, const unsigned int *inTrisPtr, int numLanes, const VertexLayout &vtxLayout) +{ + for (int lane = 0; lane < numLanes; lane++) + { + for (int i = 0; i < 3; i++) + { + char *vPtrX = (char *)inVtx + inTrisPtr[lane * 3 + i] * vtxLayout.mStride; + char *vPtrY = vPtrX + vtxLayout.mOffsetY; + char *vPtrW = vPtrX + vtxLayout.mOffsetW; + + simd_f32(vtxX[i])[lane] = *((float*)vPtrX); + simd_f32(vtxY[i])[lane] = *((float*)vPtrY); + simd_f32(vtxW[i])[lane] = *((float*)vPtrW); + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SSE4.1 version +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +namespace MaskedOcclusionCullingSSE41 +{ + FORCE_INLINE __m128i _mmw_mullo_epi32(const __m128i &a, const __m128i &b) { return _mm_mullo_epi32(a, b); } + FORCE_INLINE __m128i _mmw_min_epi32(const __m128i &a, const __m128i &b) { return _mm_min_epi32(a, b); } + FORCE_INLINE __m128i _mmw_max_epi32(const __m128i &a, const __m128i &b) { return _mm_max_epi32(a, b); } + FORCE_INLINE __m128i _mmw_abs_epi32(const __m128i &a) { return _mm_abs_epi32(a); } + FORCE_INLINE __m128 _mmw_blendv_ps(const __m128 &a, const __m128 &b, const __m128 &c) { return _mm_blendv_ps(a, b, c); } + FORCE_INLINE int _mmw_testz_epi32(const __m128i &a, const __m128i &b) { return _mm_testz_si128(a, b); } + FORCE_INLINE __m128 _mmx_dp4_ps(const __m128 &a, const __m128 &b) { return _mm_dp_ps(a, b, 0xFF); } + FORCE_INLINE __m128 _mmw_floor_ps(const __m128 &a) { return _mm_round_ps(a, _MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC); } + FORCE_INLINE __m128 _mmw_ceil_ps(const __m128 &a) { return _mm_round_ps(a, _MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC); } + FORCE_INLINE __m128i _mmw_transpose_epi8(const __m128i &a) + { + const __m128i shuff = _mm_setr_epi8(0x0, 0x4, 0x8, 0xC, 0x1, 0x5, 0x9, 0xD, 0x2, 0x6, 0xA, 0xE, 0x3, 0x7, 0xB, 0xF); + return _mm_shuffle_epi8(a, shuff); + } + FORCE_INLINE __m128i _mmw_sllv_ones(const __m128i &ishift) + { + __m128i shift = _mm_min_epi32(ishift, _mm_set1_epi32(32)); + + // Uses lookup tables and _mm_shuffle_epi8 to perform _mm_sllv_epi32(~0, shift) + const __m128i byteShiftLUT = _mm_setr_epi8((char)0xFF, (char)0xFE, (char)0xFC, (char)0xF8, (char)0xF0, (char)0xE0, (char)0xC0, (char)0x80, 0, 0, 0, 0, 0, 0, 0, 0); + const __m128i byteShiftOffset = _mm_setr_epi8(0, 8, 16, 24, 0, 8, 16, 24, 0, 8, 16, 24, 0, 8, 16, 24); + const __m128i byteShiftShuffle = _mm_setr_epi8(0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x8, 0x8, 0x8, 0x8, 0xC, 0xC, 0xC, 0xC); + + __m128i byteShift = _mm_shuffle_epi8(shift, byteShiftShuffle); + byteShift = _mm_min_epi8(_mm_subs_epu8(byteShift, byteShiftOffset), _mm_set1_epi8(8)); + __m128i retMask = _mm_shuffle_epi8(byteShiftLUT, byteShift); + + return retMask; + } + + static MaskedOcclusionCulling::Implementation gInstructionSet = MaskedOcclusionCulling::SSE41; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Include common algorithm implementation (general, SIMD independent code) + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + #include "MaskedOcclusionCullingCommon.inl" + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Utility function to create a new object using the allocator callbacks + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree) + { + MaskedOcclusionCullingPrivate *object = (MaskedOcclusionCullingPrivate *)alignedAlloc(64, sizeof(MaskedOcclusionCullingPrivate)); + new (object) MaskedOcclusionCullingPrivate(alignedAlloc, alignedFree); + return object; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SSE2 version +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +namespace MaskedOcclusionCullingSSE2 +{ + FORCE_INLINE __m128i _mmw_mullo_epi32(const __m128i &a, const __m128i &b) + { + // Do products for even / odd lanes & merge the result + __m128i even = _mm_and_si128(_mm_mul_epu32(a, b), _mm_setr_epi32(~0, 0, ~0, 0)); + __m128i odd = _mm_slli_epi64(_mm_mul_epu32(_mm_srli_epi64(a, 32), _mm_srli_epi64(b, 32)), 32); + return _mm_or_si128(even, odd); + } + FORCE_INLINE __m128i _mmw_min_epi32(const __m128i &a, const __m128i &b) + { + __m128i cond = _mm_cmpgt_epi32(a, b); + return _mm_or_si128(_mm_andnot_si128(cond, a), _mm_and_si128(cond, b)); + } + FORCE_INLINE __m128i _mmw_max_epi32(const __m128i &a, const __m128i &b) + { + __m128i cond = _mm_cmpgt_epi32(b, a); + return _mm_or_si128(_mm_andnot_si128(cond, a), _mm_and_si128(cond, b)); + } + FORCE_INLINE __m128i _mmw_abs_epi32(const __m128i &a) + { + __m128i mask = _mm_cmplt_epi32(a, _mm_setzero_si128()); + return _mm_add_epi32(_mm_xor_si128(a, mask), _mm_srli_epi32(mask, 31)); + } + FORCE_INLINE int _mmw_testz_epi32(const __m128i &a, const __m128i &b) + { + return _mm_movemask_epi8(_mm_cmpeq_epi8(_mm_and_si128(a, b), _mm_setzero_si128())) == 0xFFFF; + } + FORCE_INLINE __m128 _mmw_blendv_ps(const __m128 &a, const __m128 &b, const __m128 &c) + { + __m128 cond = _mm_castsi128_ps(_mm_srai_epi32(_mm_castps_si128(c), 31)); + return _mm_or_ps(_mm_andnot_ps(cond, a), _mm_and_ps(cond, b)); + } + FORCE_INLINE __m128 _mmx_dp4_ps(const __m128 &a, const __m128 &b) + { + // Product and two shuffle/adds pairs (similar to hadd_ps) + __m128 prod = _mm_mul_ps(a, b); + __m128 dp = _mm_add_ps(prod, _mm_shuffle_ps(prod, prod, _MM_SHUFFLE(2, 3, 0, 1))); + dp = _mm_add_ps(dp, _mm_shuffle_ps(dp, dp, _MM_SHUFFLE(0, 1, 2, 3))); + return dp; + } + FORCE_INLINE __m128 _mmw_floor_ps(const __m128 &a) + { + int originalMode = _MM_GET_ROUNDING_MODE(); + _MM_SET_ROUNDING_MODE(_MM_ROUND_DOWN); + __m128 rounded = _mm_cvtepi32_ps(_mm_cvtps_epi32(a)); + _MM_SET_ROUNDING_MODE(originalMode); + return rounded; + } + FORCE_INLINE __m128 _mmw_ceil_ps(const __m128 &a) + { + int originalMode = _MM_GET_ROUNDING_MODE(); + _MM_SET_ROUNDING_MODE(_MM_ROUND_UP); + __m128 rounded = _mm_cvtepi32_ps(_mm_cvtps_epi32(a)); + _MM_SET_ROUNDING_MODE(originalMode); + return rounded; + } + FORCE_INLINE __m128i _mmw_transpose_epi8(const __m128i &a) + { + // Perform transpose through two 16->8 bit pack and byte shifts + __m128i res = a; + const __m128i mask = _mm_setr_epi8(~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0); + res = _mm_packus_epi16(_mm_and_si128(res, mask), _mm_srli_epi16(res, 8)); + res = _mm_packus_epi16(_mm_and_si128(res, mask), _mm_srli_epi16(res, 8)); + return res; + } + FORCE_INLINE __m128i _mmw_sllv_ones(const __m128i &ishift) + { + __m128i shift = _mmw_min_epi32(ishift, _mm_set1_epi32(32)); + + // Uses scalar approach to perform _mm_sllv_epi32(~0, shift) + static const unsigned int maskLUT[33] = { + ~0U << 0, ~0U << 1, ~0U << 2 , ~0U << 3, ~0U << 4, ~0U << 5, ~0U << 6 , ~0U << 7, ~0U << 8, ~0U << 9, ~0U << 10 , ~0U << 11, ~0U << 12, ~0U << 13, ~0U << 14 , ~0U << 15, + ~0U << 16, ~0U << 17, ~0U << 18 , ~0U << 19, ~0U << 20, ~0U << 21, ~0U << 22 , ~0U << 23, ~0U << 24, ~0U << 25, ~0U << 26 , ~0U << 27, ~0U << 28, ~0U << 29, ~0U << 30 , ~0U << 31, + 0U }; + + __m128i retMask; + simd_i32(retMask)[0] = (int)maskLUT[simd_i32(shift)[0]]; + simd_i32(retMask)[1] = (int)maskLUT[simd_i32(shift)[1]]; + simd_i32(retMask)[2] = (int)maskLUT[simd_i32(shift)[2]]; + simd_i32(retMask)[3] = (int)maskLUT[simd_i32(shift)[3]]; + return retMask; + } + + static MaskedOcclusionCulling::Implementation gInstructionSet = MaskedOcclusionCulling::SSE2; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Include common algorithm implementation (general, SIMD independent code) + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + #include "MaskedOcclusionCullingCommon.inl" + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Utility function to create a new object using the allocator callbacks + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree) + { + MaskedOcclusionCullingPrivate *object = (MaskedOcclusionCullingPrivate *)alignedAlloc(64, sizeof(MaskedOcclusionCullingPrivate)); + new (object) MaskedOcclusionCullingPrivate(alignedAlloc, alignedFree); + return object; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Object construction and allocation +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +namespace MaskedOcclusionCullingAVX512 +{ + extern MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree); +} + +namespace MaskedOcclusionCullingAVX2 +{ + extern MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree); +} + +MaskedOcclusionCulling *MaskedOcclusionCulling::Create(Implementation RequestedSIMD) +{ + return Create(RequestedSIMD, aligned_alloc, aligned_free); +} + +MaskedOcclusionCulling *MaskedOcclusionCulling::Create(Implementation RequestedSIMD, pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree) +{ + MaskedOcclusionCulling *object = nullptr; + + MaskedOcclusionCulling::Implementation impl = DetectCPUFeatures(alignedAlloc, alignedFree); + + if (RequestedSIMD < impl) + impl = RequestedSIMD; + + // Return best supported version + if (object == nullptr && impl >= AVX512) + object = MaskedOcclusionCullingAVX512::CreateMaskedOcclusionCulling(alignedAlloc, alignedFree); // Use AVX512 version + if (object == nullptr && impl >= AVX2) + object = MaskedOcclusionCullingAVX2::CreateMaskedOcclusionCulling(alignedAlloc, alignedFree); // Use AVX2 version + if (object == nullptr && impl >= SSE41) + object = MaskedOcclusionCullingSSE41::CreateMaskedOcclusionCulling(alignedAlloc, alignedFree); // Use SSE4.1 version + if (object == nullptr) + object = MaskedOcclusionCullingSSE2::CreateMaskedOcclusionCulling(alignedAlloc, alignedFree); // Use SSE2 (slow) version + + return object; +} + +void MaskedOcclusionCulling::Destroy(MaskedOcclusionCulling *moc) +{ + pfnAlignedFree alignedFreeCallback = moc->mAlignedFreeCallback; + moc->~MaskedOcclusionCulling(); + alignedFreeCallback(moc); +} diff --git a/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCulling.h b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCulling.h new file mode 100644 index 0000000000..4ace525887 --- /dev/null +++ b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCulling.h @@ -0,0 +1,592 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +//////////////////////////////////////////////////////////////////////////////// +#pragma once + +/*! + * \file MaskedOcclusionCulling.h + * \brief Masked Occlusion Culling + * + * General information + * - Input to all API functions are (x,y,w) clip-space coordinates (x positive left, y positive up, w positive away from camera). + * We entirely skip the z component and instead compute it as 1 / w, see next bullet. For TestRect the input is NDC (x/w, y/w). + * - We use a simple z = 1 / w transform, which is a bit faster than OGL/DX depth transforms. Thus, depth is REVERSED and z = 0 at + * the far plane and z = inf at w = 0. We also have to use a GREATER depth function, which explains why all the conservative + * tests will be reversed compared to what you might be used to (for example zMaxTri >= zMinBuffer is a visibility test) + * - We support different layouts for vertex data (basic AoS and SoA), but note that it's beneficial to store the position data + * as tightly in memory as possible to reduce cache misses. Big strides are bad, so it's beneficial to keep position as a separate + * stream (rather than bundled with attributes) or to keep a copy of the position data for the occlusion culling system. + * - The resolution width must be a multiple of 8 and height a multiple of 4. + * - The hierarchical Z buffer is stored OpenGL-style with the y axis pointing up. This includes the scissor box. + * - This code is only tested with Visual Studio 2015, but should hopefully be easy to port to other compilers. + */ + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Defines used to configure the implementation +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef QUICK_MASK +/*! + * Configure the algorithm used for updating and merging hierarchical z buffer entries. If QUICK_MASK + * is defined to 1, use the algorithm from the paper "Masked Software Occlusion Culling", which has good + * balance between performance and low leakage. If QUICK_MASK is defined to 0, use the algorithm from + * "Masked Depth Culling for Graphics Hardware" which has less leakage, but also lower performance. + */ +#define QUICK_MASK 1 + +#endif + +#ifndef USE_D3D +/*! + * Configures the library for use with Direct3D (default) or OpenGL rendering. This changes whether the + * screen space Y axis points downwards (D3D) or upwards (OGL), and is primarily important in combination + * with the PRECISE_COVERAGE define, where this is important to ensure correct rounding and tie-breaker + * behaviour. It also affects the ScissorRect screen space coordinates. + */ +#define USE_D3D 1 + +#endif + +#ifndef PRECISE_COVERAGE +/*! + * Define PRECISE_COVERAGE to 1 to more closely match GPU rasterization rules. The increased precision comes + * at a cost of slightly lower performance. + */ +#define PRECISE_COVERAGE 1 + +#endif + +#ifndef USE_AVX512 +/*! + * Define USE_AVX512 to 1 to enable experimental AVX-512 support. It's currently mostly untested and only + * validated on simple examples using Intel SDE. Older compilers may not support AVX-512 intrinsics. + */ +#define USE_AVX512 0 + +#endif + +#ifndef CLIPPING_PRESERVES_ORDER +/*! + * Define CLIPPING_PRESERVES_ORDER to 1 to prevent clipping from reordering triangle rasterization + * order; This comes at a cost (approx 3-4%) but removes one source of temporal frame-to-frame instability. + */ +#define CLIPPING_PRESERVES_ORDER 1 + +#endif + +#ifndef ENABLE_STATS +/*! + * Define ENABLE_STATS to 1 to gather various statistics during occlusion culling. Can be used for profiling + * and debugging. Note that enabling this function will reduce performance significantly. + */ +#define ENABLE_STATS 0 + +#endif + +#ifndef MOC_RECORDER_ENABLE +/*! + * Define MOC_RECORDER_ENABLE to 1 to enable frame recorder (see FrameRecorder.h/cpp for details) + */ +#define MOC_RECORDER_ENABLE 0 + +#endif + +#if MOC_RECORDER_ENABLE +#ifndef MOC_RECORDER_ENABLE_PLAYBACK +/*! + * Define MOC_RECORDER_ENABLE_PLAYBACK to 1 to enable compilation of the playback code (not needed + for recording) + */ +#define MOC_RECORDER_ENABLE_PLAYBACK 0 +#endif +#endif + + +#if MOC_RECORDER_ENABLE + +#include + +class FrameRecorder; + +#endif // #if MOC_RECORDER_ENABLE + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Masked occlusion culling class +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class MaskedOcclusionCulling +{ +public: + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Memory management callback functions + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + typedef void *(*pfnAlignedAlloc)(size_t alignment, size_t size); + typedef void (*pfnAlignedFree) (void *ptr); + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Enums + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + enum Implementation + { + SSE2 = 0, + SSE41 = 1, + AVX2 = 2, + AVX512 = 3 + }; + + enum BackfaceWinding + { + BACKFACE_NONE = 0, + BACKFACE_CW = 1, + BACKFACE_CCW = 2, + }; + + enum CullingResult + { + VISIBLE = 0x0, + OCCLUDED = 0x1, + VIEW_CULLED = 0x3 + }; + + enum ClipPlanes + { + CLIP_PLANE_NONE = 0x00, + CLIP_PLANE_NEAR = 0x01, + CLIP_PLANE_LEFT = 0x02, + CLIP_PLANE_RIGHT = 0x04, + CLIP_PLANE_BOTTOM = 0x08, + CLIP_PLANE_TOP = 0x10, + CLIP_PLANE_SIDES = (CLIP_PLANE_LEFT | CLIP_PLANE_RIGHT | CLIP_PLANE_BOTTOM | CLIP_PLANE_TOP), + CLIP_PLANE_ALL = (CLIP_PLANE_LEFT | CLIP_PLANE_RIGHT | CLIP_PLANE_BOTTOM | CLIP_PLANE_TOP | CLIP_PLANE_NEAR) + }; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Structs + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /*! + * Used to specify custom vertex layout. Memory offsets to y and z coordinates are set through + * mOffsetY and mOffsetW, and vertex stride is given by mStride. It's possible to configure both + * AoS and SoA layouts. Note that large strides may cause more cache misses and decrease + * performance. It is advisable to store position data as compactly in memory as possible. + */ + struct VertexLayout + { + VertexLayout() {} + VertexLayout(int stride, int offsetY, int offsetZW) : + mStride(stride), mOffsetY(offsetY), mOffsetW(offsetZW) {} + + int mStride; //!< byte stride between vertices + int mOffsetY; //!< byte offset from X to Y coordinate + union { + int mOffsetZ; //!< byte offset from X to Z coordinate + int mOffsetW; //!< byte offset from X to W coordinate + }; + }; + + /*! + * Used to control scissoring during rasterization. Note that we only provide coarse scissor support. + * The scissor box x coordinates must be a multiple of 32, and the y coordinates a multiple of 8. + * Scissoring is mainly meant as a means of enabling binning (sort middle) rasterizers in case + * application developers want to use that approach for multithreading. + */ + struct ScissorRect + { + ScissorRect() {} + ScissorRect(int minX, int minY, int maxX, int maxY) : + mMinX(minX), mMinY(minY), mMaxX(maxX), mMaxY(maxY) {} + + int mMinX; //!< Screen space X coordinate for left side of scissor rect, inclusive and must be a multiple of 32 + int mMinY; //!< Screen space Y coordinate for bottom side of scissor rect, inclusive and must be a multiple of 8 + int mMaxX; //!< Screen space X coordinate for right side of scissor rect, non inclusive and must be a multiple of 32 + int mMaxY; //!< Screen space Y coordinate for top side of scissor rect, non inclusive and must be a multiple of 8 + }; + + /*! + * Used to specify storage area for a binlist, containing triangles. This struct is used for binning + * and multithreading. The host application is responsible for allocating memory for the binlists. + */ + struct TriList + { + unsigned int mNumTriangles; //!< Maximum number of triangles that may be stored in mPtr + unsigned int mTriIdx; //!< Index of next triangle to be written, clear before calling BinTriangles to start from the beginning of the list + float *mPtr; //!< Scratchpad buffer allocated by the host application + }; + + /*! + * Statistics that can be gathered during occluder rendering and visibility to aid debugging + * and profiling. Must be enabled by changing the ENABLE_STATS define. + */ + struct OcclusionCullingStatistics + { + struct + { + long long mNumProcessedTriangles; //!< Number of occluder triangles processed in total + long long mNumRasterizedTriangles; //!< Number of occluder triangles passing view frustum and backface culling + long long mNumTilesTraversed; //!< Number of tiles traversed by the rasterizer + long long mNumTilesUpdated; //!< Number of tiles where the hierarchical z buffer was updated + long long mNumTilesMerged; //!< Number of tiles where the hierarchical z buffer was updated + } mOccluders; + + struct + { + long long mNumProcessedRectangles; //!< Number of rects processed (TestRect()) + long long mNumProcessedTriangles; //!< Number of ocludee triangles processed (TestTriangles()) + long long mNumRasterizedTriangles; //!< Number of ocludee triangle passing view frustum and backface culling + long long mNumTilesTraversed; //!< Number of tiles traversed by triangle & rect rasterizers + } mOccludees; + }; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Functions + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /*! + * \brief Creates a new object with default state, no z buffer attached/allocated. + */ + static MaskedOcclusionCulling *Create(Implementation RequestedSIMD = AVX512); + + /*! + * \brief Creates a new object with default state, no z buffer attached/allocated. + * \param alignedAlloc Pointer to a callback function used when allocating memory + * \param alignedFree Pointer to a callback function used when freeing memory + */ + static MaskedOcclusionCulling *Create(Implementation RequestedSIMD, pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree); + + /*! + * \brief Destroys an object and frees the z buffer memory. Note that you cannot + * use the delete operator, and should rather use this function to free up memory. + */ + static void Destroy(MaskedOcclusionCulling *moc); + + /*! + * \brief Sets the resolution of the hierarchical depth buffer. This function will + * re-allocate the current depth buffer (if present). The contents of the + * buffer is undefined until ClearBuffer() is called. + * + * \param witdh The width of the buffer in pixels, must be a multiple of 8 + * \param height The height of the buffer in pixels, must be a multiple of 4 + */ + virtual void SetResolution(unsigned int width, unsigned int height) = 0; + + /*! + * \brief Gets the resolution of the hierarchical depth buffer. + * + * \param witdh Output: The width of the buffer in pixels + * \param height Output: The height of the buffer in pixels + */ + virtual void GetResolution(unsigned int &width, unsigned int &height) const = 0; + + /*! + * \brief Returns the tile size for the current implementation. + * + * \param nBinsW Number of vertical bins, the screen is divided into nBinsW x nBinsH + * rectangular bins. + * \param nBinsH Number of horizontal bins, the screen is divided into nBinsW x nBinsH + * rectangular bins. + * \param outBinWidth Output: The width of the single bin in pixels (except for the + * rightmost bin width, which is extended to resolution width) + * \param outBinHeight Output: The height of the single bin in pixels (except for the + * bottommost bin height, which is extended to resolution height) + */ + virtual void ComputeBinWidthHeight(unsigned int nBinsW, unsigned int nBinsH, unsigned int & outBinWidth, unsigned int & outBinHeight) = 0; + + /*! + * \brief Sets the distance for the near clipping plane. Default is nearDist = 0. + * + * \param nearDist The distance to the near clipping plane, given as clip space w + */ + virtual void SetNearClipPlane(float nearDist) = 0; + + /*! + * \brief Gets the distance for the near clipping plane. + */ + virtual float GetNearClipPlane() const = 0; + + /*! + * \brief Clears the hierarchical depth buffer. + */ + virtual void ClearBuffer() = 0; + + /*! + * \brief Merge a second hierarchical depth buffer into the main buffer. + */ + virtual void MergeBuffer(MaskedOcclusionCulling* BufferB) = 0; + + /*! + * \brief Renders a mesh of occluder triangles and updates the hierarchical z buffer + * with conservative depth values. + * + * This function is optimized for vertex layouts with stride 16 and y and w + * offsets of 4 and 12 bytes, respectively. + * + * \param inVtx Pointer to an array of input vertices, should point to the x component + * of the first vertex. The input vertices are given as (x,y,w) coordinates + * in clip space. The memory layout can be changed using vtxLayout. + * \param inTris Pointer to an array of vertex indices. Each triangle is created + * from three indices consecutively fetched from the array. + * \param nTris The number of triangles to render (inTris must contain atleast 3*nTris + * entries) + * \param modelToClipMatrix all vertices will be transformed by this matrix before + * performing projection. If nullptr is passed the transform step will be skipped + * \param bfWinding Sets triangle winding order to consider backfacing, must be one one + * of (BACKFACE_NONE, BACKFACE_CW and BACKFACE_CCW). Back-facing triangles are culled + * and will not be rasterized. You may use BACKFACE_NONE to disable culling for + * double sided geometry + * \param clipPlaneMask A mask indicating which clip planes should be considered by the + * triangle clipper. Can be used as an optimization if your application can + * determine (for example during culling) that a group of triangles does not + * intersect a certain frustum plane. However, setting an incorrect mask may + * cause out of bounds memory accesses. + * \param vtxLayout A struct specifying the vertex layout (see struct for detailed + * description). For best performance, it is advisable to store position data + * as compactly in memory as possible. + * \return Will return VIEW_CULLED if all triangles are either outside the frustum or + * backface culled, returns VISIBLE otherwise. + */ + virtual CullingResult RenderTriangles(const float *inVtx, const unsigned int *inTris, int nTris, const float *modelToClipMatrix = nullptr, BackfaceWinding bfWinding = BACKFACE_CW, ClipPlanes clipPlaneMask = CLIP_PLANE_ALL, const VertexLayout &vtxLayout = VertexLayout(16, 4, 12)) = 0; + + /*! + * \brief Occlusion query for a rectangle with a given depth. The rectangle is given + * in normalized device coordinates where (x,y) coordinates between [-1,1] map + * to the visible screen area. The query uses a GREATER_EQUAL (reversed) depth + * test meaning that depth values equal to the contents of the depth buffer are + * counted as visible. + * + * \param xmin NDC coordinate of the left side of the rectangle. + * \param ymin NDC coordinate of the bottom side of the rectangle. + * \param xmax NDC coordinate of the right side of the rectangle. + * \param ymax NDC coordinate of the top side of the rectangle. + * \param ymax NDC coordinate of the top side of the rectangle. + * \param wmin Clip space W coordinate for the rectangle. + * \return The query will return VISIBLE if the rectangle may be visible, OCCLUDED + * if the rectangle is occluded by a previously rendered object, or VIEW_CULLED + * if the rectangle is outside the view frustum. + */ + virtual CullingResult TestRect(float xmin, float ymin, float xmax, float ymax, float wmin) const = 0; + + /*! + * \brief This function is similar to RenderTriangles(), but performs an occlusion + * query instead and does not update the hierarchical z buffer. The query uses + * a GREATER_EQUAL (reversed) depth test meaning that depth values equal to the + * contents of the depth buffer are counted as visible. + * + * This function is optimized for vertex layouts with stride 16 and y and w + * offsets of 4 and 12 bytes, respectively. + * + * \param inVtx Pointer to an array of input vertices, should point to the x component + * of the first vertex. The input vertices are given as (x,y,w) coordinates + * in clip space. The memory layout can be changed using vtxLayout. + * \param inTris Pointer to an array of triangle indices. Each triangle is created + * from three indices consecutively fetched from the array. + * \param nTris The number of triangles to render (inTris must contain atleast 3*nTris + * entries) + * \param modelToClipMatrix all vertices will be transformed by this matrix before + * performing projection. If nullptr is passed the transform step will be skipped + * \param bfWinding Sets triangle winding order to consider backfacing, must be one one + * of (BACKFACE_NONE, BACKFACE_CW and BACKFACE_CCW). Back-facing triangles are culled + * and will not be occlusion tested. You may use BACKFACE_NONE to disable culling + * for double sided geometry + * \param clipPlaneMask A mask indicating which clip planes should be considered by the + * triangle clipper. Can be used as an optimization if your application can + * determine (for example during culling) that a group of triangles does not + * intersect a certain frustum plane. However, setting an incorrect mask may + * cause out of bounds memory accesses. + * \param vtxLayout A struct specifying the vertex layout (see struct for detailed + * description). For best performance, it is advisable to store position data + * as compactly in memory as possible. + * \return The query will return VISIBLE if the triangle mesh may be visible, OCCLUDED + * if the mesh is occluded by a previously rendered object, or VIEW_CULLED if all + * triangles are entirely outside the view frustum or backface culled. + */ + virtual CullingResult TestTriangles(const float *inVtx, const unsigned int *inTris, int nTris, const float *modelToClipMatrix = nullptr, BackfaceWinding bfWinding = BACKFACE_CW, ClipPlanes clipPlaneMask = CLIP_PLANE_ALL, const VertexLayout &vtxLayout = VertexLayout(16, 4, 12)) = 0; + + /*! + * \brief Perform input assembly, clipping , projection, triangle setup, and write + * triangles to the screen space bins they overlap. This function can be used to + * distribute work for threading (See the CullingThreadpool class for an example) + * + * \param inVtx Pointer to an array of input vertices, should point to the x component + * of the first vertex. The input vertices are given as (x,y,w) coordinates + * in clip space. The memory layout can be changed using vtxLayout. + * \param inTris Pointer to an array of vertex indices. Each triangle is created + * from three indices consecutively fetched from the array. + * \param nTris The number of triangles to render (inTris must contain atleast 3*nTris + * entries) + * \param triLists Pointer to an array of TriList objects with one TriList object per + * bin. If a triangle overlaps a bin, it will be written to the corresponding + * trilist. Note that this method appends the triangles to the current list, to + * start writing from the beginning of the list, set triList.mTriIdx = 0 + * \param nBinsW Number of vertical bins, the screen is divided into nBinsW x nBinsH + * rectangular bins. + * \param nBinsH Number of horizontal bins, the screen is divided into nBinsW x nBinsH + * rectangular bins. + * \param modelToClipMatrix all vertices will be transformed by this matrix before + * performing projection. If nullptr is passed the transform step will be skipped + * \param clipPlaneMask A mask indicating which clip planes should be considered by the + * triangle clipper. Can be used as an optimization if your application can + * determine (for example during culling) that a group of triangles does not + * intersect a certain frustum plane. However, setting an incorrect mask may + * cause out of bounds memory accesses. + * \param vtxLayout A struct specifying the vertex layout (see struct for detailed + * description). For best performance, it is advisable to store position data + * as compactly in memory as possible. + * \param bfWinding Sets triangle winding order to consider backfacing, must be one one + * of (BACKFACE_NONE, BACKFACE_CW and BACKFACE_CCW). Back-facing triangles are culled + * and will not be binned / rasterized. You may use BACKFACE_NONE to disable culling + * for double sided geometry + */ + virtual void BinTriangles(const float *inVtx, const unsigned int *inTris, int nTris, TriList *triLists, unsigned int nBinsW, unsigned int nBinsH, const float *modelToClipMatrix = nullptr, BackfaceWinding bfWinding = BACKFACE_CW, ClipPlanes clipPlaneMask = CLIP_PLANE_ALL, const VertexLayout &vtxLayout = VertexLayout(16, 4, 12)) = 0; + + /*! + * \brief Renders all occluder triangles in a trilist. This function can be used in + * combination with BinTriangles() to create a threded (binning) rasterizer. The + * bins can be processed independently by different threads without risking writing + * to overlapping memory regions. + * + * \param triLists A triangle list, filled using the BinTriangles() function that is to + * be rendered. + * \param scissor A scissor box limiting the rendering region to the bin. The size of each + * bin must be a multiple of 32x8 pixels due to implementation constraints. For a + * render target with (width, height) resolution and (nBinsW, nBinsH) bins, the + * size of a bin is: + * binWidth = (width / nBinsW) - (width / nBinsW) % 32; + * binHeight = (height / nBinsH) - (height / nBinsH) % 8; + * The last row and column of tiles have a different size: + * lastColBinWidth = width - (nBinsW-1)*binWidth; + * lastRowBinHeight = height - (nBinsH-1)*binHeight; + */ + virtual void RenderTrilist(const TriList &triList, const ScissorRect *scissor) = 0; + + /*! + * \brief Creates a per-pixel depth buffer from the hierarchical z buffer representation. + * Intended for visualizing the hierarchical depth buffer for debugging. The + * buffer is written in scanline order, from the top to bottom (D3D) or bottom to + * top (OGL) of the surface. See the USE_D3D define. + * + * \param depthData Pointer to memory where the per-pixel depth data is written. Must + * hold storage for atleast width*height elements as set by setResolution. + */ + virtual void ComputePixelDepthBuffer(float *depthData, bool flipY) = 0; + + /*! + * \brief Fetch occlusion culling statistics, returns zeroes if ENABLE_STATS define is + * not defined. The statistics can be used for profiling or debugging. + */ + virtual OcclusionCullingStatistics GetStatistics() = 0; + + /*! + * \brief Returns the implementation (CPU instruction set) version of this object. + */ + virtual Implementation GetImplementation() = 0; + + /*! + * \brief Utility function for transforming vertices and outputting them to an (x,y,z,w) + * format suitable for the occluder rasterization and occludee testing functions. + * + * \param mtx Pointer to matrix data. The matrix should column major for post + * multiplication (OGL) and row major for pre-multiplication (DX). This is + * consistent with OpenGL / DirectX behavior. + * \param inVtx Pointer to an array of input vertices. The input vertices are given as + * (x,y,z) coordinates. The memory layout can be changed using vtxLayout. + * \param xfVtx Pointer to an array to store transformed vertices. The transformed + * vertices are always stored as array of structs (AoS) (x,y,z,w) packed in memory. + * \param nVtx Number of vertices to transform. + * \param vtxLayout A struct specifying the vertex layout (see struct for detailed + * description). For best performance, it is advisable to store position data + * as compactly in memory as possible. Note that for this function, the + * w-component is assumed to be 1.0. + */ + static void TransformVertices(const float *mtx, const float *inVtx, float *xfVtx, unsigned int nVtx, const VertexLayout &vtxLayout = VertexLayout(12, 4, 8)); + + /*! + * \brief Get used memory alloc/free callbacks. + */ + void GetAllocFreeCallback( pfnAlignedAlloc & allocCallback, pfnAlignedFree & freeCallback ) { allocCallback = mAlignedAllocCallback, freeCallback = mAlignedFreeCallback; } + +#if MOC_RECORDER_ENABLE + /*! + * \brief Start recording subsequent rasterization and testing calls using the FrameRecorder. + * The function calls that are recorded are: + * - ClearBuffer + * - RenderTriangles + * - TestTriangles + * - TestRect + * All inputs and outputs are recorded, which can be used for correctness validation + * and performance testing. + * + * \param outputFilePath Pointer to name of the output file. + * \return 'true' if recording was started successfully, 'false' otherwise (file access error). + */ + bool RecorderStart( const char * outputFilePath ) const; + + /*! + * \brief Stop recording, flush output and release used memory. + */ + void RecorderStop( ) const; + + /*! + * \brief Manually record triangles. This is called automatically from MaskedOcclusionCulling::RenderTriangles + * if the recording is started, but not from BinTriangles/RenderTrilist (used in multithreaded codepath), in + * which case it has to be called manually. + * + * \param inVtx Pointer to an array of input vertices, should point to the x component + * of the first vertex. The input vertices are given as (x,y,w) coordinates + * in clip space. The memory layout can be changed using vtxLayout. + * \param inTris Pointer to an array of triangle indices. Each triangle is created + * from three indices consecutively fetched from the array. + * \param nTris The number of triangles to render (inTris must contain atleast 3*nTris + * entries) + * \param modelToClipMatrix all vertices will be transformed by this matrix before + * performing projection. If nullptr is passed the transform step will be skipped + * \param bfWinding Sets triangle winding order to consider backfacing, must be one one + * of (BACKFACE_NONE, BACKFACE_CW and BACKFACE_CCW). Back-facing triangles are culled + * and will not be occlusion tested. You may use BACKFACE_NONE to disable culling + * for double sided geometry + * \param clipPlaneMask A mask indicating which clip planes should be considered by the + * triangle clipper. Can be used as an optimization if your application can + * determine (for example during culling) that a group of triangles does not + * intersect a certain frustum plane. However, setting an incorrect mask may + * cause out of bounds memory accesses. + * \param vtxLayout A struct specifying the vertex layout (see struct for detailed + * description). For best performance, it is advisable to store position data + * as compactly in memory as possible. + * \param cullingResult cull result value expected to be returned by executing the + * RenderTriangles call with recorded parameters. + */ + // + // merge the binned data back into original layout; in this case, call it manually from your Threadpool implementation (already added to CullingThreadpool). + // If recording is not enabled, calling this function will do nothing. + void RecordRenderTriangles( const float *inVtx, const unsigned int *inTris, int nTris, const float *modelToClipMatrix = nullptr, ClipPlanes clipPlaneMask = CLIP_PLANE_ALL, BackfaceWinding bfWinding = BACKFACE_CW, const VertexLayout &vtxLayout = VertexLayout( 16, 4, 12 ), CullingResult cullingResult = (CullingResult)-1 ); +#endif // #if MOC_RECORDER_ENABLE + +protected: + pfnAlignedAlloc mAlignedAllocCallback; + pfnAlignedFree mAlignedFreeCallback; + + mutable OcclusionCullingStatistics mStats; + +#if MOC_RECORDER_ENABLE + mutable FrameRecorder * mRecorder; + mutable std::mutex mRecorderMutex; +#endif // #if MOC_RECORDER_ENABLE + + virtual ~MaskedOcclusionCulling() {} +}; diff --git a/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp new file mode 100644 index 0000000000..b129f12943 --- /dev/null +++ b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp @@ -0,0 +1,243 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +//////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include "MaskedOcclusionCulling.h" +#include "CompilerSpecific.inl" + +#if MOC_RECORDER_ENABLE +#include "FrameRecorder.h" +#endif + +#if defined(__MICROSOFT_COMPILER) && _MSC_VER < 1900 + // If you remove/comment this error, the code will compile & use the SSE41 version instead. + #error Older versions than visual studio 2015 not supported due to compiler bug(s) +#endif + +#if !defined(__MICROSOFT_COMPILER) || _MSC_VER >= 1900 + +// For performance reasons, the MaskedOcclusionCullingAVX2.cpp file should be compiled with VEX encoding for SSE instructions (to avoid +// AVX-SSE transition penalties, see https://software.intel.com/en-us/articles/avoiding-avx-sse-transition-penalties). However, the SSE +// version in MaskedOcclusionCulling.cpp _must_ be compiled without VEX encoding to allow backwards compatibility. Best practice is to +// use lowest supported target platform (e.g. /arch:SSE2) as project default, and elevate only the MaskedOcclusionCullingAVX2/512.cpp files. +#ifndef __AVX2__ + #error For best performance, MaskedOcclusionCullingAVX2.cpp should be compiled with /arch:AVX2 +#endif + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// AVX specific defines and constants +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define SIMD_LANES 8 +#define TILE_HEIGHT_SHIFT 3 + +#define SIMD_LANE_IDX _mm256_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7) + +#define SIMD_SUB_TILE_COL_OFFSET _mm256_setr_epi32(0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3) +#define SIMD_SUB_TILE_ROW_OFFSET _mm256_setr_epi32(0, 0, 0, 0, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT) +#define SIMD_SUB_TILE_COL_OFFSET_F _mm256_setr_ps(0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3) +#define SIMD_SUB_TILE_ROW_OFFSET_F _mm256_setr_ps(0, 0, 0, 0, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT) + +#define SIMD_SHUFFLE_SCANLINE_TO_SUBTILES _mm256_setr_epi8(0x0, 0x4, 0x8, 0xC, 0x1, 0x5, 0x9, 0xD, 0x2, 0x6, 0xA, 0xE, 0x3, 0x7, 0xB, 0xF, 0x0, 0x4, 0x8, 0xC, 0x1, 0x5, 0x9, 0xD, 0x2, 0x6, 0xA, 0xE, 0x3, 0x7, 0xB, 0xF) + +#define SIMD_LANE_YCOORD_I _mm256_setr_epi32(128, 384, 640, 896, 1152, 1408, 1664, 1920) +#define SIMD_LANE_YCOORD_F _mm256_setr_ps(128.0f, 384.0f, 640.0f, 896.0f, 1152.0f, 1408.0f, 1664.0f, 1920.0f) + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// AVX specific typedefs and functions +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef __m256 __mw; +typedef __m256i __mwi; + +#define _mmw_set1_ps _mm256_set1_ps +#define _mmw_setzero_ps _mm256_setzero_ps +#define _mmw_and_ps _mm256_and_ps +#define _mmw_or_ps _mm256_or_ps +#define _mmw_xor_ps _mm256_xor_ps +#define _mmw_not_ps(a) _mm256_xor_ps((a), _mm256_castsi256_ps(_mm256_set1_epi32(~0))) +#define _mmw_andnot_ps _mm256_andnot_ps +#define _mmw_neg_ps(a) _mm256_xor_ps((a), _mm256_set1_ps(-0.0f)) +#define _mmw_abs_ps(a) _mm256_and_ps((a), _mm256_castsi256_ps(_mm256_set1_epi32(0x7FFFFFFF))) +#define _mmw_add_ps _mm256_add_ps +#define _mmw_sub_ps _mm256_sub_ps +#define _mmw_mul_ps _mm256_mul_ps +#define _mmw_div_ps _mm256_div_ps +#define _mmw_min_ps _mm256_min_ps +#define _mmw_max_ps _mm256_max_ps +#define _mmw_fmadd_ps _mm256_fmadd_ps +#define _mmw_fmsub_ps _mm256_fmsub_ps +#define _mmw_movemask_ps _mm256_movemask_ps +#define _mmw_blendv_ps _mm256_blendv_ps +#define _mmw_cmpge_ps(a,b) _mm256_cmp_ps(a, b, _CMP_GE_OQ) +#define _mmw_cmpgt_ps(a,b) _mm256_cmp_ps(a, b, _CMP_GT_OQ) +#define _mmw_cmpeq_ps(a,b) _mm256_cmp_ps(a, b, _CMP_EQ_OQ) +#define _mmw_floor_ps(x) _mm256_round_ps(x, _MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC) +#define _mmw_ceil_ps(x) _mm256_round_ps(x, _MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC) +#define _mmw_shuffle_ps _mm256_shuffle_ps +#define _mmw_insertf32x4_ps _mm256_insertf128_ps +#define _mmw_cvtepi32_ps _mm256_cvtepi32_ps +#define _mmw_blendv_epi32(a,b,c) simd_cast<__mwi>(_mmw_blendv_ps(simd_cast<__mw>(a), simd_cast<__mw>(b), simd_cast<__mw>(c))) + +#define _mmw_set1_epi32 _mm256_set1_epi32 +#define _mmw_setzero_epi32 _mm256_setzero_si256 +#define _mmw_and_epi32 _mm256_and_si256 +#define _mmw_or_epi32 _mm256_or_si256 +#define _mmw_xor_epi32 _mm256_xor_si256 +#define _mmw_not_epi32(a) _mm256_xor_si256((a), _mm256_set1_epi32(~0)) +#define _mmw_andnot_epi32 _mm256_andnot_si256 +#define _mmw_neg_epi32(a) _mm256_sub_epi32(_mm256_set1_epi32(0), (a)) +#define _mmw_add_epi32 _mm256_add_epi32 +#define _mmw_sub_epi32 _mm256_sub_epi32 +#define _mmw_min_epi32 _mm256_min_epi32 +#define _mmw_max_epi32 _mm256_max_epi32 +#define _mmw_subs_epu16 _mm256_subs_epu16 +#define _mmw_mullo_epi32 _mm256_mullo_epi32 +#define _mmw_cmpeq_epi32 _mm256_cmpeq_epi32 +#define _mmw_testz_epi32 _mm256_testz_si256 +#define _mmw_cmpgt_epi32 _mm256_cmpgt_epi32 +#define _mmw_srai_epi32 _mm256_srai_epi32 +#define _mmw_srli_epi32 _mm256_srli_epi32 +#define _mmw_slli_epi32 _mm256_slli_epi32 +#define _mmw_sllv_ones(x) _mm256_sllv_epi32(SIMD_BITS_ONE, x) +#define _mmw_transpose_epi8(x) _mm256_shuffle_epi8(x, SIMD_SHUFFLE_SCANLINE_TO_SUBTILES) +#define _mmw_abs_epi32 _mm256_abs_epi32 +#define _mmw_cvtps_epi32 _mm256_cvtps_epi32 +#define _mmw_cvttps_epi32 _mm256_cvttps_epi32 + +#define _mmx_dp4_ps(a, b) _mm_dp_ps(a, b, 0xFF) +#define _mmx_fmadd_ps _mm_fmadd_ps +#define _mmx_max_epi32 _mm_max_epi32 +#define _mmx_min_epi32 _mm_min_epi32 + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SIMD casting functions +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template FORCE_INLINE T simd_cast(Y A); +template<> FORCE_INLINE __m128 simd_cast<__m128>(float A) { return _mm_set1_ps(A); } +template<> FORCE_INLINE __m128 simd_cast<__m128>(__m128i A) { return _mm_castsi128_ps(A); } +template<> FORCE_INLINE __m128 simd_cast<__m128>(__m128 A) { return A; } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(int A) { return _mm_set1_epi32(A); } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(__m128 A) { return _mm_castps_si128(A); } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(__m128i A) { return A; } +template<> FORCE_INLINE __m256 simd_cast<__m256>(float A) { return _mm256_set1_ps(A); } +template<> FORCE_INLINE __m256 simd_cast<__m256>(__m256i A) { return _mm256_castsi256_ps(A); } +template<> FORCE_INLINE __m256 simd_cast<__m256>(__m256 A) { return A; } +template<> FORCE_INLINE __m256i simd_cast<__m256i>(int A) { return _mm256_set1_epi32(A); } +template<> FORCE_INLINE __m256i simd_cast<__m256i>(__m256 A) { return _mm256_castps_si256(A); } +template<> FORCE_INLINE __m256i simd_cast<__m256i>(__m256i A) { return A; } + +#define MAKE_ACCESSOR(name, simd_type, base_type, is_const, elements) \ + FORCE_INLINE is_const base_type * name(is_const simd_type &a) { \ + union accessor { simd_type m_native; base_type m_array[elements]; }; \ + is_const accessor *acs = reinterpret_cast(&a); \ + return acs->m_array; \ + } + +MAKE_ACCESSOR(simd_f32, __m128, float, , 4) +MAKE_ACCESSOR(simd_f32, __m128, float, const, 4) +MAKE_ACCESSOR(simd_i32, __m128i, int, , 4) +MAKE_ACCESSOR(simd_i32, __m128i, int, const, 4) + +MAKE_ACCESSOR(simd_f32, __m256, float, , 8) +MAKE_ACCESSOR(simd_f32, __m256, float, const, 8) +MAKE_ACCESSOR(simd_i32, __m256i, int, , 8) +MAKE_ACCESSOR(simd_i32, __m256i, int, const, 8) + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Specialized AVX input assembly function for general vertex gather +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef MaskedOcclusionCulling::VertexLayout VertexLayout; + +FORCE_INLINE void GatherVertices(__m256 *vtxX, __m256 *vtxY, __m256 *vtxW, const float *inVtx, const unsigned int *inTrisPtr, int numLanes, const VertexLayout &vtxLayout) +{ + assert(numLanes >= 1); + + const __m256i SIMD_TRI_IDX_OFFSET = _mm256_setr_epi32(0, 3, 6, 9, 12, 15, 18, 21); + static const __m256i SIMD_LANE_MASK[9] = { + _mm256_setr_epi32( 0, 0, 0, 0, 0, 0, 0, 0), + _mm256_setr_epi32(~0, 0, 0, 0, 0, 0, 0, 0), + _mm256_setr_epi32(~0, ~0, 0, 0, 0, 0, 0, 0), + _mm256_setr_epi32(~0, ~0, ~0, 0, 0, 0, 0, 0), + _mm256_setr_epi32(~0, ~0, ~0, ~0, 0, 0, 0, 0), + _mm256_setr_epi32(~0, ~0, ~0, ~0, ~0, 0, 0, 0), + _mm256_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, 0, 0), + _mm256_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, 0), + _mm256_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0) + }; + + // Compute per-lane index list offset that guards against out of bounds memory accesses + __m256i safeTriIdxOffset = _mm256_and_si256(SIMD_TRI_IDX_OFFSET, SIMD_LANE_MASK[numLanes]); + + // Fetch triangle indices. + __m256i vtxIdx[3]; + vtxIdx[0] = _mmw_mullo_epi32(_mm256_i32gather_epi32((const int*)inTrisPtr + 0, safeTriIdxOffset, 4), _mmw_set1_epi32(vtxLayout.mStride)); + vtxIdx[1] = _mmw_mullo_epi32(_mm256_i32gather_epi32((const int*)inTrisPtr + 1, safeTriIdxOffset, 4), _mmw_set1_epi32(vtxLayout.mStride)); + vtxIdx[2] = _mmw_mullo_epi32(_mm256_i32gather_epi32((const int*)inTrisPtr + 2, safeTriIdxOffset, 4), _mmw_set1_epi32(vtxLayout.mStride)); + + char *vPtr = (char *)inVtx; + + // Fetch triangle vertices + for (int i = 0; i < 3; i++) + { + vtxX[i] = _mm256_i32gather_ps((float *)vPtr, vtxIdx[i], 1); + vtxY[i] = _mm256_i32gather_ps((float *)(vPtr + vtxLayout.mOffsetY), vtxIdx[i], 1); + vtxW[i] = _mm256_i32gather_ps((float *)(vPtr + vtxLayout.mOffsetW), vtxIdx[i], 1); + } +} + +namespace MaskedOcclusionCullingAVX2 +{ + static MaskedOcclusionCulling::Implementation gInstructionSet = MaskedOcclusionCulling::AVX2; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Include common algorithm implementation (general, SIMD independent code) + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + #include "MaskedOcclusionCullingCommon.inl" + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Utility function to create a new object using the allocator callbacks + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + typedef MaskedOcclusionCulling::pfnAlignedAlloc pfnAlignedAlloc; + typedef MaskedOcclusionCulling::pfnAlignedFree pfnAlignedFree; + + MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree) + { + MaskedOcclusionCullingPrivate *object = (MaskedOcclusionCullingPrivate *)alignedAlloc(64, sizeof(MaskedOcclusionCullingPrivate)); + new (object) MaskedOcclusionCullingPrivate(alignedAlloc, alignedFree); + return object; + } +}; + +#else + +namespace MaskedOcclusionCullingAVX2 +{ + typedef MaskedOcclusionCulling::pfnAlignedAlloc pfnAlignedAlloc; + typedef MaskedOcclusionCulling::pfnAlignedFree pfnAlignedFree; + + MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree) + { + return nullptr; + } +}; + +#endif diff --git a/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp new file mode 100644 index 0000000000..1dccccd83e --- /dev/null +++ b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp @@ -0,0 +1,309 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +//////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include "MaskedOcclusionCulling.h" +#include "CompilerSpecific.inl" + +#if MOC_RECORDER_ENABLE +#include "FrameRecorder.h" +#endif + +// Make sure compiler supports AVX-512 intrinsics: Visual Studio 2017 (Update 3) || Intel C++ Compiler 16.0 || Clang 4.0 || GCC 5.0 +#if USE_AVX512 != 0 && ((defined(_MSC_VER) && _MSC_VER >= 1911) || (defined(__INTEL_COMPILER) && __INTEL_COMPILER >= 1600) || (defined(__clang__) && __clang_major__ >= 4) || (defined(__GNUC__) && __GNUC__ >= 5)) + +// The MaskedOcclusionCullingAVX512.cpp file should be compiled avx2/avx512 architecture options turned on in the compiler. However, the SSE +// version in MaskedOcclusionCulling.cpp _must_ be compiled with SSE2 architecture allow backwards compatibility. Best practice is to +// use lowest supported target platform (e.g. /arch:SSE2) as project default, and elevate only the MaskedOcclusionCullingAVX2/512.cpp files. +#ifndef __AVX2__ + #error For best performance, MaskedOcclusionCullingAVX512.cpp should be compiled with /arch:AVX2 +#endif + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// AVX specific defines and constants +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define SIMD_LANES 16 +#define TILE_HEIGHT_SHIFT 4 + +#define SIMD_LANE_IDX _mm512_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) + +#define SIMD_SUB_TILE_COL_OFFSET _mm512_setr_epi32(0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3) +#define SIMD_SUB_TILE_ROW_OFFSET _mm512_setr_epi32(0, 0, 0, 0, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 3, SUB_TILE_HEIGHT * 3, SUB_TILE_HEIGHT * 3, SUB_TILE_HEIGHT * 3) +#define SIMD_SUB_TILE_COL_OFFSET_F _mm512_setr_ps(0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3) +#define SIMD_SUB_TILE_ROW_OFFSET_F _mm512_setr_ps(0, 0, 0, 0, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 3, SUB_TILE_HEIGHT * 3, SUB_TILE_HEIGHT * 3, SUB_TILE_HEIGHT * 3) + +#define SIMD_SHUFFLE_SCANLINE_TO_SUBTILES _mm512_set_epi32(0x0F0B0703, 0x0E0A0602, 0x0D090501, 0x0C080400, 0x0F0B0703, 0x0E0A0602, 0x0D090501, 0x0C080400, 0x0F0B0703, 0x0E0A0602, 0x0D090501, 0x0C080400, 0x0F0B0703, 0x0E0A0602, 0x0D090501, 0x0C080400) + +#define SIMD_LANE_YCOORD_I _mm512_setr_epi32(128, 384, 640, 896, 1152, 1408, 1664, 1920, 2176, 2432, 2688, 2944, 3200, 3456, 3712, 3968) +#define SIMD_LANE_YCOORD_F _mm512_setr_ps(128.0f, 384.0f, 640.0f, 896.0f, 1152.0f, 1408.0f, 1664.0f, 1920.0f, 2176.0f, 2432.0f, 2688.0f, 2944.0f, 3200.0f, 3456.0f, 3712.0f, 3968.0f) + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// AVX specific typedefs and functions +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef __m512 __mw; +typedef __m512i __mwi; + +#define _mmw_set1_ps _mm512_set1_ps +#define _mmw_setzero_ps _mm512_setzero_ps +#define _mmw_and_ps _mm512_and_ps +#define _mmw_or_ps _mm512_or_ps +#define _mmw_xor_ps _mm512_xor_ps +#define _mmw_not_ps(a) _mm512_xor_ps((a), _mm512_castsi512_ps(_mm512_set1_epi32(~0))) +#define _mmw_andnot_ps _mm512_andnot_ps +#define _mmw_neg_ps(a) _mm512_xor_ps((a), _mm512_set1_ps(-0.0f)) +#define _mmw_abs_ps(a) _mm512_and_ps((a), _mm512_castsi512_ps(_mm512_set1_epi32(0x7FFFFFFF))) +#define _mmw_add_ps _mm512_add_ps +#define _mmw_sub_ps _mm512_sub_ps +#define _mmw_mul_ps _mm512_mul_ps +#define _mmw_div_ps _mm512_div_ps +#define _mmw_min_ps _mm512_min_ps +#define _mmw_max_ps _mm512_max_ps +#define _mmw_fmadd_ps _mm512_fmadd_ps +#define _mmw_fmsub_ps _mm512_fmsub_ps +#define _mmw_shuffle_ps _mm512_shuffle_ps +#define _mmw_insertf32x4_ps _mm512_insertf32x4 +#define _mmw_cvtepi32_ps _mm512_cvtepi32_ps +#define _mmw_blendv_epi32(a,b,c) simd_cast<__mwi>(_mmw_blendv_ps(simd_cast<__mw>(a), simd_cast<__mw>(b), simd_cast<__mw>(c))) + +#define _mmw_set1_epi32 _mm512_set1_epi32 +#define _mmw_setzero_epi32 _mm512_setzero_si512 +#define _mmw_and_epi32 _mm512_and_si512 +#define _mmw_or_epi32 _mm512_or_si512 +#define _mmw_xor_epi32 _mm512_xor_si512 +#define _mmw_not_epi32(a) _mm512_xor_si512((a), _mm512_set1_epi32(~0)) +#define _mmw_andnot_epi32 _mm512_andnot_si512 +#define _mmw_neg_epi32(a) _mm512_sub_epi32(_mm512_set1_epi32(0), (a)) +#define _mmw_add_epi32 _mm512_add_epi32 +#define _mmw_sub_epi32 _mm512_sub_epi32 +#define _mmw_min_epi32 _mm512_min_epi32 +#define _mmw_max_epi32 _mm512_max_epi32 +#define _mmw_subs_epu16 _mm512_subs_epu16 +#define _mmw_mullo_epi32 _mm512_mullo_epi32 +#define _mmw_srai_epi32 _mm512_srai_epi32 +#define _mmw_srli_epi32 _mm512_srli_epi32 +#define _mmw_slli_epi32 _mm512_slli_epi32 +#define _mmw_sllv_ones(x) _mm512_sllv_epi32(SIMD_BITS_ONE, x) +#define _mmw_transpose_epi8(x) _mm512_shuffle_epi8(x, SIMD_SHUFFLE_SCANLINE_TO_SUBTILES) +#define _mmw_abs_epi32 _mm512_abs_epi32 +#define _mmw_cvtps_epi32 _mm512_cvtps_epi32 +#define _mmw_cvttps_epi32 _mm512_cvttps_epi32 + +#define _mmx_dp4_ps(a, b) _mm_dp_ps(a, b, 0xFF) +#define _mmx_fmadd_ps _mm_fmadd_ps +#define _mmx_max_epi32 _mm_max_epi32 +#define _mmx_min_epi32 _mm_min_epi32 + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SIMD casting functions +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template FORCE_INLINE T simd_cast(Y A); +template<> FORCE_INLINE __m128 simd_cast<__m128>(float A) { return _mm_set1_ps(A); } +template<> FORCE_INLINE __m128 simd_cast<__m128>(__m128i A) { return _mm_castsi128_ps(A); } +template<> FORCE_INLINE __m128 simd_cast<__m128>(__m128 A) { return A; } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(int A) { return _mm_set1_epi32(A); } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(__m128 A) { return _mm_castps_si128(A); } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(__m128i A) { return A; } +template<> FORCE_INLINE __m256 simd_cast<__m256>(float A) { return _mm256_set1_ps(A); } +template<> FORCE_INLINE __m256 simd_cast<__m256>(__m256i A) { return _mm256_castsi256_ps(A); } +template<> FORCE_INLINE __m256 simd_cast<__m256>(__m256 A) { return A; } +template<> FORCE_INLINE __m256i simd_cast<__m256i>(int A) { return _mm256_set1_epi32(A); } +template<> FORCE_INLINE __m256i simd_cast<__m256i>(__m256 A) { return _mm256_castps_si256(A); } +template<> FORCE_INLINE __m256i simd_cast<__m256i>(__m256i A) { return A; } +template<> FORCE_INLINE __m512 simd_cast<__m512>(float A) { return _mm512_set1_ps(A); } +template<> FORCE_INLINE __m512 simd_cast<__m512>(__m512i A) { return _mm512_castsi512_ps(A); } +template<> FORCE_INLINE __m512 simd_cast<__m512>(__m512 A) { return A; } +template<> FORCE_INLINE __m512i simd_cast<__m512i>(int A) { return _mm512_set1_epi32(A); } +template<> FORCE_INLINE __m512i simd_cast<__m512i>(__m512 A) { return _mm512_castps_si512(A); } +template<> FORCE_INLINE __m512i simd_cast<__m512i>(__m512i A) { return A; } + +#define MAKE_ACCESSOR(name, simd_type, base_type, is_const, elements) \ + FORCE_INLINE is_const base_type * name(is_const simd_type &a) { \ + union accessor { simd_type m_native; base_type m_array[elements]; }; \ + is_const accessor *acs = reinterpret_cast(&a); \ + return acs->m_array; \ + } + +MAKE_ACCESSOR(simd_f32, __m128, float, , 4) +MAKE_ACCESSOR(simd_f32, __m128, float, const, 4) +MAKE_ACCESSOR(simd_i32, __m128i, int, , 4) +MAKE_ACCESSOR(simd_i32, __m128i, int, const, 4) + +MAKE_ACCESSOR(simd_f32, __m256, float, , 8) +MAKE_ACCESSOR(simd_f32, __m256, float, const, 8) +MAKE_ACCESSOR(simd_i32, __m256i, int, , 8) +MAKE_ACCESSOR(simd_i32, __m256i, int, const, 8) + +MAKE_ACCESSOR(simd_f32, __m512, float, , 16) +MAKE_ACCESSOR(simd_f32, __m512, float, const, 16) +MAKE_ACCESSOR(simd_i32, __m512i, int, , 16) +MAKE_ACCESSOR(simd_i32, __m512i, int, const, 16) + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Specialized AVX input assembly function for general vertex gather +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef MaskedOcclusionCulling::VertexLayout VertexLayout; + +FORCE_INLINE void GatherVertices(__m512 *vtxX, __m512 *vtxY, __m512 *vtxW, const float *inVtx, const unsigned int *inTrisPtr, int numLanes, const VertexLayout &vtxLayout) +{ + assert(numLanes >= 1); + + const __m512i SIMD_TRI_IDX_OFFSET = _mm512_setr_epi32(0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45); + static const __m512i SIMD_LANE_MASK[17] = { + _mm512_setr_epi32( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0) + }; + + // Compute per-lane index list offset that guards against out of bounds memory accesses + __m512i safeTriIdxOffset = _mm512_and_si512(SIMD_TRI_IDX_OFFSET, SIMD_LANE_MASK[numLanes]); + + // Fetch triangle indices. + __m512i vtxIdx[3]; + vtxIdx[0] = _mmw_mullo_epi32(_mm512_i32gather_epi32(safeTriIdxOffset, (const int*)inTrisPtr + 0, 4), _mmw_set1_epi32(vtxLayout.mStride)); + vtxIdx[1] = _mmw_mullo_epi32(_mm512_i32gather_epi32(safeTriIdxOffset, (const int*)inTrisPtr + 1, 4), _mmw_set1_epi32(vtxLayout.mStride)); + vtxIdx[2] = _mmw_mullo_epi32(_mm512_i32gather_epi32(safeTriIdxOffset, (const int*)inTrisPtr + 2, 4), _mmw_set1_epi32(vtxLayout.mStride)); + + char *vPtr = (char *)inVtx; + + // Fetch triangle vertices + for (int i = 0; i < 3; i++) + { + vtxX[i] = _mm512_i32gather_ps(vtxIdx[i], (float *)vPtr, 1); + vtxY[i] = _mm512_i32gather_ps(vtxIdx[i], (float *)(vPtr + vtxLayout.mOffsetY), 1); + vtxW[i] = _mm512_i32gather_ps(vtxIdx[i], (float *)(vPtr + vtxLayout.mOffsetW), 1); + } +} + +namespace MaskedOcclusionCullingAVX512 +{ + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Poorly implemented functions. TODO: fix common (maskedOcclusionCullingCommon.inl) code to improve perf + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + FORCE_INLINE __m512 _mmw_floor_ps(__m512 x) + { + return _mm512_roundscale_ps(x, 1); // 1 = floor + } + + FORCE_INLINE __m512 _mmw_ceil_ps(__m512 x) + { + return _mm512_roundscale_ps(x, 2); // 2 = ceil + } + + FORCE_INLINE __m512i _mmw_cmpeq_epi32(__m512i a, __m512i b) + { + __mmask16 mask = _mm512_cmpeq_epi32_mask(a, b); + return _mm512_mask_mov_epi32(_mm512_set1_epi32(0), mask, _mm512_set1_epi32(~0)); + } + + FORCE_INLINE __m512i _mmw_cmpgt_epi32(__m512i a, __m512i b) + { + __mmask16 mask = _mm512_cmpgt_epi32_mask(a, b); + return _mm512_mask_mov_epi32(_mm512_set1_epi32(0), mask, _mm512_set1_epi32(~0)); + } + + FORCE_INLINE bool _mmw_testz_epi32(__m512i a, __m512i b) + { + __mmask16 mask = _mm512_cmpeq_epi32_mask(_mm512_and_si512(a, b), _mm512_set1_epi32(0)); + return mask == 0xFFFF; + } + + FORCE_INLINE __m512 _mmw_cmpge_ps(__m512 a, __m512 b) + { + __mmask16 mask = _mm512_cmp_ps_mask(a, b, _CMP_GE_OQ); + return _mm512_castsi512_ps(_mm512_mask_mov_epi32(_mm512_set1_epi32(0), mask, _mm512_set1_epi32(~0))); + } + + FORCE_INLINE __m512 _mmw_cmpgt_ps(__m512 a, __m512 b) + { + __mmask16 mask = _mm512_cmp_ps_mask(a, b, _CMP_GT_OQ); + return _mm512_castsi512_ps(_mm512_mask_mov_epi32(_mm512_set1_epi32(0), mask, _mm512_set1_epi32(~0))); + } + + FORCE_INLINE __m512 _mmw_cmpeq_ps(__m512 a, __m512 b) + { + __mmask16 mask = _mm512_cmp_ps_mask(a, b, _CMP_EQ_OQ); + return _mm512_castsi512_ps(_mm512_mask_mov_epi32(_mm512_set1_epi32(0), mask, _mm512_set1_epi32(~0))); + } + + FORCE_INLINE __mmask16 _mmw_movemask_ps(const __m512 &a) + { + __mmask16 mask = _mm512_cmp_epi32_mask(_mm512_and_si512(_mm512_castps_si512(a), _mm512_set1_epi32(0x80000000)), _mm512_set1_epi32(0), 4); // a & 0x8000000 != 0 + return mask; + } + + FORCE_INLINE __m512 _mmw_blendv_ps(const __m512 &a, const __m512 &b, const __m512 &c) + { + __mmask16 mask = _mmw_movemask_ps(c); + return _mm512_mask_mov_ps(a, mask, b); + } + + static MaskedOcclusionCulling::Implementation gInstructionSet = MaskedOcclusionCulling::AVX512; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Include common algorithm implementation (general, SIMD independent code) + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + #include "MaskedOcclusionCullingCommon.inl" + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Utility function to create a new object using the allocator callbacks + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + typedef MaskedOcclusionCulling::pfnAlignedAlloc pfnAlignedAlloc; + typedef MaskedOcclusionCulling::pfnAlignedFree pfnAlignedFree; + + MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree) + { + MaskedOcclusionCullingPrivate *object = (MaskedOcclusionCullingPrivate *)alignedAlloc(64, sizeof(MaskedOcclusionCullingPrivate)); + new (object) MaskedOcclusionCullingPrivate(alignedAlloc, alignedFree); + return object; + } +}; + +#else + +namespace MaskedOcclusionCullingAVX512 +{ + typedef MaskedOcclusionCulling::pfnAlignedAlloc pfnAlignedAlloc; + typedef MaskedOcclusionCulling::pfnAlignedFree pfnAlignedFree; + + MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree) + { + return nullptr; + } +}; + +#endif diff --git a/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingCommon.inl b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingCommon.inl new file mode 100644 index 0000000000..331ca69964 --- /dev/null +++ b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingCommon.inl @@ -0,0 +1,2053 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +//////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Common SIMD math utility functions +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template FORCE_INLINE T max(const T &a, const T &b) { return a > b ? a : b; } +template FORCE_INLINE T min(const T &a, const T &b) { return a < b ? a : b; } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Common defines and constants +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define SIMD_ALL_LANES_MASK ((1 << SIMD_LANES) - 1) + +// Tile dimensions are 32xN pixels. These values are not tweakable and the code must also be modified +// to support different tile sizes as it is tightly coupled with the SSE/AVX register size +#define TILE_WIDTH_SHIFT 5 +#define TILE_WIDTH (1 << TILE_WIDTH_SHIFT) +#define TILE_HEIGHT (1 << TILE_HEIGHT_SHIFT) + +// Sub-tiles (used for updating the masked HiZ buffer) are 8x4 tiles, so there are 4x2 sub-tiles in a tile +#define SUB_TILE_WIDTH 8 +#define SUB_TILE_HEIGHT 4 + +// The number of fixed point bits used to represent vertex coordinates / edge slopes. +#if PRECISE_COVERAGE != 0 + #define FP_BITS 8 + #define FP_HALF_PIXEL (1 << (FP_BITS - 1)) + #define FP_INV (1.0f / (float)(1 << FP_BITS)) +#else + // Note that too low precision, without precise coverage, may cause overshoots / false coverage during rasterization. + // This is configured for 14 bits for AVX512 and 16 bits for SSE. Max tile slope delta is roughly + // (screenWidth + 2*(GUARD_BAND_PIXEL_SIZE + 1)) * (2^FP_BITS * (TILE_HEIGHT + GUARD_BAND_PIXEL_SIZE + 1)) + // and must fit in 31 bits. With this config, max image resolution (width) is ~3272, so stay well clear of this limit. + #define FP_BITS (19 - TILE_HEIGHT_SHIFT) +#endif + +// Tile dimensions in fixed point coordinates +#define FP_TILE_HEIGHT_SHIFT (FP_BITS + TILE_HEIGHT_SHIFT) +#define FP_TILE_HEIGHT (1 << FP_TILE_HEIGHT_SHIFT) + +// Maximum number of triangles that may be generated during clipping. We process SIMD_LANES triangles at a time and +// clip against 5 planes, so the max should be 5*8 = 40 (we immediately draw the first clipped triangle). +// This number must be a power of two. +#define MAX_CLIPPED (8*SIMD_LANES) +#define MAX_CLIPPED_WRAP (MAX_CLIPPED - 1) + +// Size of guard band in pixels. Clipping doesn't seem to be very expensive so we use a small guard band +// to improve rasterization performance. It's not recommended to set the guard band to zero, as this may +// cause leakage along the screen border due to precision/rounding. +#define GUARD_BAND_PIXEL_SIZE 1.0f + +// We classify triangles as big if the bounding box is wider than this given threshold and use a tighter +// but slightly more expensive traversal algorithm. This improves performance greatly for sliver triangles +#define BIG_TRIANGLE 3 + +// Only gather statistics if enabled. +#if ENABLE_STATS != 0 + #define STATS_ADD(var, val) _InterlockedExchangeAdd64( &var, val ) +#else + #define STATS_ADD(var, val) +#endif + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SIMD common defines (constant values) +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define SIMD_BITS_ONE _mmw_set1_epi32(~0) +#define SIMD_BITS_ZERO _mmw_setzero_epi32() +#define SIMD_TILE_WIDTH _mmw_set1_epi32(TILE_WIDTH) + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Vertex fetch utility function, need to be in global namespace due to template specialization +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template FORCE_INLINE void VtxFetch4(__mw *v, const unsigned int *inTrisPtr, int triVtx, const float *inVtx, int numLanes) +{ + // Fetch 4 vectors (matching 1 sse part of the SIMD register), and continue to the next + const int ssePart = (SIMD_LANES / 4) - N; + for (int k = 0; k < 4; k++) + { + int lane = 4 * ssePart + k; + if (numLanes > lane) + v[k] = _mmw_insertf32x4_ps(v[k], _mm_loadu_ps(&inVtx[inTrisPtr[lane * 3 + triVtx] << 2]), ssePart); + } + VtxFetch4(v, inTrisPtr, triVtx, inVtx, numLanes); +} + +template<> FORCE_INLINE void VtxFetch4<0>(__mw *v, const unsigned int *inTrisPtr, int triVtx, const float *inVtx, int numLanes) +{ + // Workaround for unused parameter warning + (void)v; (void)inTrisPtr; (void)triVtx; (void)inVtx; (void)numLanes; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Private class containing the implementation +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class MaskedOcclusionCullingPrivate : public MaskedOcclusionCulling +{ +public: + struct ZTile + { + __mw mZMin[2]; + __mwi mMask; + }; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Member variables + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + __mw mHalfWidth; + __mw mHalfHeight; + __mw mCenterX; + __mw mCenterY; + __m128 mCSFrustumPlanes[5]; + __m128 mIHalfSize; + __m128 mICenter; + __m128i mIScreenSize; + + float mNearDist; + int mWidth; + int mHeight; + int mTilesWidth; + int mTilesHeight; + + ZTile *mMaskedHiZBuffer; + ScissorRect mFullscreenScissor; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Constructors and state handling + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + MaskedOcclusionCullingPrivate(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree) : mFullscreenScissor(0, 0, 0, 0) + { + mMaskedHiZBuffer = nullptr; + mAlignedAllocCallback = alignedAlloc; + mAlignedFreeCallback = alignedFree; +#if MOC_RECORDER_ENABLE + mRecorder = nullptr; +#endif + + SetNearClipPlane(0.0f); + mCSFrustumPlanes[0] = _mm_setr_ps(0.0f, 0.0f, 1.0f, 0.0f); + mCSFrustumPlanes[1] = _mm_setr_ps(1.0f, 0.0f, 1.0f, 0.0f); + mCSFrustumPlanes[2] = _mm_setr_ps(-1.0f, 0.0f, 1.0f, 0.0f); + mCSFrustumPlanes[3] = _mm_setr_ps(0.0f, 1.0f, 1.0f, 0.0f); + mCSFrustumPlanes[4] = _mm_setr_ps(0.0f, -1.0f, 1.0f, 0.0f); + + memset(&mStats, 0, sizeof(OcclusionCullingStatistics)); + + SetResolution(0, 0); + } + + ~MaskedOcclusionCullingPrivate() override + { + if (mMaskedHiZBuffer != nullptr) + mAlignedFreeCallback(mMaskedHiZBuffer); + mMaskedHiZBuffer = nullptr; + +#if MOC_RECORDER_ENABLE + assert( mRecorder == nullptr ); // forgot to call StopRecording()? +#endif + } + + void SetResolution(unsigned int width, unsigned int height) override + { + // Resolution must be a multiple of the subtile size + assert(width % SUB_TILE_WIDTH == 0 && height % SUB_TILE_HEIGHT == 0); +#if PRECISE_COVERAGE == 0 + // Test if combination of resolution & SLOPE_FP_BITS bits may cause 32-bit overflow. Note that the maximum resolution estimate + // is only an estimate (not conservative). It's advicable to stay well below the limit. + assert(width < ((1U << 31) - 1U) / ((1U << FP_BITS) * (TILE_HEIGHT + (unsigned int)(GUARD_BAND_PIXEL_SIZE + 1.0f))) - (2U * (unsigned int)(GUARD_BAND_PIXEL_SIZE + 1.0f))); +#endif + + // Delete current masked hierarchical Z buffer + if (mMaskedHiZBuffer != nullptr) + mAlignedFreeCallback(mMaskedHiZBuffer); + mMaskedHiZBuffer = nullptr; + + // Setup various resolution dependent constant values + mWidth = (int)width; + mHeight = (int)height; + mTilesWidth = (int)(width + TILE_WIDTH - 1) >> TILE_WIDTH_SHIFT; + mTilesHeight = (int)(height + TILE_HEIGHT - 1) >> TILE_HEIGHT_SHIFT; + mCenterX = _mmw_set1_ps((float)mWidth * 0.5f); + mCenterY = _mmw_set1_ps((float)mHeight * 0.5f); + mICenter = _mm_setr_ps((float)mWidth * 0.5f, (float)mWidth * 0.5f, (float)mHeight * 0.5f, (float)mHeight * 0.5f); + mHalfWidth = _mmw_set1_ps((float)mWidth * 0.5f); +#if USE_D3D != 0 + mHalfHeight = _mmw_set1_ps((float)-mHeight * 0.5f); + mIHalfSize = _mm_setr_ps((float)mWidth * 0.5f, (float)mWidth * 0.5f, (float)-mHeight * 0.5f, (float)-mHeight * 0.5f); +#else + mHalfHeight = _mmw_set1_ps((float)mHeight * 0.5f); + mIHalfSize = _mm_setr_ps((float)mWidth * 0.5f, (float)mWidth * 0.5f, (float)mHeight * 0.5f, (float)mHeight * 0.5f); +#endif + mIScreenSize = _mm_setr_epi32(mWidth - 1, mWidth - 1, mHeight - 1, mHeight - 1); + + // Setup a full screen scissor rectangle + mFullscreenScissor.mMinX = 0; + mFullscreenScissor.mMinY = 0; + mFullscreenScissor.mMaxX = mTilesWidth << TILE_WIDTH_SHIFT; + mFullscreenScissor.mMaxY = mTilesHeight << TILE_HEIGHT_SHIFT; + + // Adjust clip planes to include a small guard band to avoid clipping leaks + if (mWidth > 0.0f && mHeight > 0.0f) + { + float guardBandWidth = (2.0f / (float)mWidth) * GUARD_BAND_PIXEL_SIZE; + float guardBandHeight = (2.0f / (float)mHeight) * GUARD_BAND_PIXEL_SIZE; + mCSFrustumPlanes[1] = _mm_setr_ps(1.0f - guardBandWidth, 0.0f, 1.0f, 0.0f); + mCSFrustumPlanes[2] = _mm_setr_ps(-1.0f + guardBandWidth, 0.0f, 1.0f, 0.0f); + mCSFrustumPlanes[3] = _mm_setr_ps(0.0f, 1.0f - guardBandHeight, 1.0f, 0.0f); + mCSFrustumPlanes[4] = _mm_setr_ps(0.0f, -1.0f + guardBandHeight, 1.0f, 0.0f); + } + + // Allocate masked hierarchical Z buffer (if zero size leave at nullptr) + if(mTilesWidth * mTilesHeight > 0) + mMaskedHiZBuffer = (ZTile *)mAlignedAllocCallback(64, sizeof(ZTile) * mTilesWidth * mTilesHeight); + } + + void GetResolution(unsigned int &width, unsigned int &height) const override + { + width = mWidth; + height = mHeight; + } + + void ComputeBinWidthHeight(unsigned int nBinsW, unsigned int nBinsH, unsigned int & outBinWidth, unsigned int & outBinHeight) override + { + outBinWidth = (mWidth / nBinsW) - ((mWidth / nBinsW) % TILE_WIDTH); + outBinHeight = (mHeight / nBinsH) - ((mHeight / nBinsH) % TILE_HEIGHT); + } + + void SetNearClipPlane(float nearDist) override + { + // Setup the near frustum plane + mNearDist = nearDist; + mCSFrustumPlanes[0] = _mm_setr_ps(0.0f, 0.0f, 1.0f, -nearDist); + } + + float GetNearClipPlane() const override + { + return mNearDist; + } + + void ClearBuffer() override + { + assert(mMaskedHiZBuffer != nullptr); + + // Iterate through all depth tiles and clear to default values + for (int i = 0; i < mTilesWidth * mTilesHeight; i++) + { + mMaskedHiZBuffer[i].mMask = _mmw_setzero_epi32(); + + // Clear z0 to beyond infinity to ensure we never merge with clear data + mMaskedHiZBuffer[i].mZMin[0] = _mmw_set1_ps(-1.0f); +#if QUICK_MASK != 0 + // Clear z1 to nearest depth value as it is pushed back on each update + mMaskedHiZBuffer[i].mZMin[1] = _mmw_set1_ps(FLT_MAX); +#else + mMaskedHiZBuffer[i].mZMin[1] = _mmw_setzero_ps(); +#endif + } + +#if ENABLE_STATS != 0 + memset(&mStats, 0, sizeof(OcclusionCullingStatistics)); +#endif + +#if MOC_RECORDER_ENABLE != 0 + { + std::lock_guard lock( mRecorderMutex ); + if( mRecorder != nullptr ) mRecorder->RecordClearBuffer(); + } +#endif + } + + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // MergeBuffer + // Utility Function merges another MOC buffer into the existing one + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void MergeBuffer(MaskedOcclusionCulling* BufferB) override + { + assert(mMaskedHiZBuffer != nullptr); + + //// Iterate through all depth tiles and merge the 2 tiles + for (int i = 0; i < mTilesWidth * mTilesHeight; i++) + { + __mw *zMinB = ((MaskedOcclusionCullingPrivate*)BufferB)->mMaskedHiZBuffer[i].mZMin; + __mw *zMinA = mMaskedHiZBuffer[i].mZMin; + __mwi RastMaskB = ((MaskedOcclusionCullingPrivate*)BufferB)->mMaskedHiZBuffer[i].mMask; + +#if QUICK_MASK != 0 + // Clear z0 to beyond infinity to ensure we never merge with clear data + __mwi sign0 = _mmw_srai_epi32(simd_cast<__mwi>(zMinB[0]), 31); + // Only merge tiles that have data in zMinB[0], use the sign bit to determine if they are still in a clear state + sign0 = _mmw_cmpeq_epi32(sign0, SIMD_BITS_ZERO); + if (!_mmw_testz_epi32(sign0, sign0)) + { + STATS_ADD(mStats.mOccluders.mNumTilesMerged, 1); + zMinA[0] = _mmw_max_ps(zMinA[0], zMinB[0]); + + __mwi rastMask = mMaskedHiZBuffer[i].mMask; + __mwi deadLane = _mmw_cmpeq_epi32(rastMask, SIMD_BITS_ZERO); + // Mask out all subtiles failing the depth test (don't update these subtiles) + deadLane = _mmw_or_epi32(deadLane, _mmw_srai_epi32(simd_cast<__mwi>(_mmw_sub_ps(zMinA[1], zMinA[0])), 31)); + mMaskedHiZBuffer[i].mMask = _mmw_andnot_epi32(deadLane, rastMask); + } + + // Set 32bit value to -1 if any pixels are set incide the coverage mask for a subtile + __mwi LiveTile = _mmw_cmpeq_epi32(RastMaskB, SIMD_BITS_ZERO); + // invert to have bits set for clear subtiles + __mwi t0inv = _mmw_not_epi32(LiveTile); + // VPTEST sets the ZF flag if all the resulting bits are 0 (ie if all tiles are clear) + if (!_mmw_testz_epi32(t0inv, t0inv)) + { + STATS_ADD(mStats.mOccluders.mNumTilesMerged, 1); + UpdateTileQuick(i, RastMaskB, zMinB[1]); + } +#else + // Clear z0 to beyond infinity to ensure we never merge with clear data + __mwi sign1 = _mmw_srai_epi32(simd_cast<__mwi>(mMaskedHiZBuffer[i].mZMin[0]), 31); + // Only merge tiles that have data in zMinB[0], use the sign bit to determine if they are still in a clear state + sign1 = _mmw_cmpeq_epi32(sign1, SIMD_BITS_ZERO); + + // Set 32bit value to -1 if any pixels are set incide the coverage mask for a subtile + __mwi LiveTile1 = _mmw_cmpeq_epi32(mMaskedHiZBuffer[i].mMask, SIMD_BITS_ZERO); + // invert to have bits set for clear subtiles + __mwi t1inv = _mmw_not_epi32(LiveTile1); + // VPTEST sets the ZF flag if all the resulting bits are 0 (ie if all tiles are clear) + if (_mmw_testz_epi32(sign1, sign1) && _mmw_testz_epi32(t1inv, t1inv)) + { + mMaskedHiZBuffer[i].mMask = ((MaskedOcclusionCullingPrivate*)BufferB)->mMaskedHiZBuffer[i].mMask; + mMaskedHiZBuffer[i].mZMin[0] = zMinB[0]; + mMaskedHiZBuffer[i].mZMin[1] = zMinB[1]; + } + else + { + // Clear z0 to beyond infinity to ensure we never merge with clear data + __mwi sign0 = _mmw_srai_epi32(simd_cast<__mwi>(zMinB[0]), 31); + sign0 = _mmw_cmpeq_epi32(sign0, SIMD_BITS_ZERO); + // Only merge tiles that have data in zMinB[0], use the sign bit to determine if they are still in a clear state + if (!_mmw_testz_epi32(sign0, sign0)) + { + // build a mask for Zmin[0], full if the layer has been completed, or partial if tile is still partly filled. + // cant just use the completement of the mask, as tiles might not get updated by merge + __mwi sign1 = _mmw_srai_epi32(simd_cast<__mwi>(zMinB[1]), 31); + __mwi LayerMask0 = _mmw_not_epi32(sign1); + __mwi LayerMask1 = _mmw_not_epi32(((MaskedOcclusionCullingPrivate*)BufferB)->mMaskedHiZBuffer[i].mMask); + __mwi rastMask = _mmw_or_epi32(LayerMask0, LayerMask1); + + UpdateTileAccurate(i, rastMask, zMinB[0]); + } + + // Set 32bit value to -1 if any pixels are set incide the coverage mask for a subtile + __mwi LiveTile = _mmw_cmpeq_epi32(((MaskedOcclusionCullingPrivate*)BufferB)->mMaskedHiZBuffer[i].mMask, SIMD_BITS_ZERO); + // invert to have bits set for clear subtiles + __mwi t0inv = _mmw_not_epi32(LiveTile); + // VPTEST sets the ZF flag if all the resulting bits are 0 (ie if all tiles are clear) + if (!_mmw_testz_epi32(t0inv, t0inv)) + { + UpdateTileAccurate(i, ((MaskedOcclusionCullingPrivate*)BufferB)->mMaskedHiZBuffer[i].mMask, zMinB[1]); + } + + //if (_mmw_testz_epi32(sign0, sign0) && _mmw_testz_epi32(t0inv, t0inv)) + // STATS_ADD(mStats.mOccluders.mNumTilesMerged, 1); + + } + +#endif + } + } + + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Polygon clipping functions + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + FORCE_INLINE int ClipPolygon(__m128 *outVtx, __m128 *inVtx, const __m128 &plane, int n) const + { + __m128 p0 = inVtx[n - 1]; + __m128 dist0 = _mmx_dp4_ps(p0, plane); + + // Loop over all polygon edges and compute intersection with clip plane (if any) + int nout = 0; + for (int k = 0; k < n; k++) + { + __m128 p1 = inVtx[k]; + __m128 dist1 = _mmx_dp4_ps(p1, plane); + int dist0Neg = _mm_movemask_ps(dist0); + if (!dist0Neg) // dist0 > 0.0f + outVtx[nout++] = p0; + + // Edge intersects the clip plane if dist0 and dist1 have opposing signs + if (_mm_movemask_ps(_mm_xor_ps(dist0, dist1))) + { + // Always clip from the positive side to avoid T-junctions + if (!dist0Neg) + { + __m128 t = _mm_div_ps(dist0, _mm_sub_ps(dist0, dist1)); + outVtx[nout++] = _mmx_fmadd_ps(_mm_sub_ps(p1, p0), t, p0); + } + else + { + __m128 t = _mm_div_ps(dist1, _mm_sub_ps(dist1, dist0)); + outVtx[nout++] = _mmx_fmadd_ps(_mm_sub_ps(p0, p1), t, p1); + } + } + + dist0 = dist1; + p0 = p1; + } + return nout; + } + + template void TestClipPlane(__mw *vtxX, __mw *vtxY, __mw *vtxW, unsigned int &straddleMask, unsigned int &triMask, ClipPlanes clipPlaneMask) + { + straddleMask = 0; + // Skip masked clip planes + if (!(clipPlaneMask & CLIP_PLANE)) + return; + + // Evaluate all 3 vertices against the frustum plane + __mw planeDp[3]; + for (int i = 0; i < 3; ++i) + { + switch (CLIP_PLANE) + { + case ClipPlanes::CLIP_PLANE_LEFT: planeDp[i] = _mmw_add_ps(vtxW[i], vtxX[i]); break; + case ClipPlanes::CLIP_PLANE_RIGHT: planeDp[i] = _mmw_sub_ps(vtxW[i], vtxX[i]); break; + case ClipPlanes::CLIP_PLANE_BOTTOM: planeDp[i] = _mmw_add_ps(vtxW[i], vtxY[i]); break; + case ClipPlanes::CLIP_PLANE_TOP: planeDp[i] = _mmw_sub_ps(vtxW[i], vtxY[i]); break; + case ClipPlanes::CLIP_PLANE_NEAR: planeDp[i] = _mmw_sub_ps(vtxW[i], _mmw_set1_ps(mNearDist)); break; + } + } + + // Look at FP sign and determine if tri is inside, outside or straddles the frustum plane + __mw inside = _mmw_andnot_ps(planeDp[0], _mmw_andnot_ps(planeDp[1], _mmw_not_ps(planeDp[2]))); + __mw outside = _mmw_and_ps(planeDp[0], _mmw_and_ps(planeDp[1], planeDp[2])); + unsigned int inMask = (unsigned int)_mmw_movemask_ps(inside); + unsigned int outMask = (unsigned int)_mmw_movemask_ps(outside); + straddleMask = (~outMask) & (~inMask); + triMask &= ~outMask; + } + + FORCE_INLINE void ClipTriangleAndAddToBuffer(__mw *vtxX, __mw *vtxY, __mw *vtxW, __m128 *clippedTrisBuffer, int &clipWriteIdx, unsigned int &triMask, unsigned int triClipMask, ClipPlanes clipPlaneMask) + { + if (!triClipMask) + return; + + // Inside test all 3 triangle vertices against all active frustum planes + unsigned int straddleMask[5]; + TestClipPlane(vtxX, vtxY, vtxW, straddleMask[0], triMask, clipPlaneMask); + TestClipPlane(vtxX, vtxY, vtxW, straddleMask[1], triMask, clipPlaneMask); + TestClipPlane(vtxX, vtxY, vtxW, straddleMask[2], triMask, clipPlaneMask); + TestClipPlane(vtxX, vtxY, vtxW, straddleMask[3], triMask, clipPlaneMask); + TestClipPlane(vtxX, vtxY, vtxW, straddleMask[4], triMask, clipPlaneMask); + + // Clip triangle against straddling planes and add to the clipped triangle buffer + __m128 vtxBuf[2][8]; + +#if CLIPPING_PRESERVES_ORDER != 0 + unsigned int clipMask = triClipMask & triMask; + unsigned int clipAndStraddleMask = (straddleMask[0] | straddleMask[1] | straddleMask[2] | straddleMask[3] | straddleMask[4]) & clipMask; + // no clipping needed after all - early out + if (clipAndStraddleMask == 0) + return; + while( clipMask ) + { + // Find and setup next triangle to clip + unsigned int triIdx = find_clear_lsb(&clipMask); + unsigned int triBit = (1U << triIdx); + assert(triIdx < SIMD_LANES); + + int bufIdx = 0; + int nClippedVerts = 3; + for (int i = 0; i < 3; i++) + vtxBuf[0][i] = _mm_setr_ps(simd_f32(vtxX[i])[triIdx], simd_f32(vtxY[i])[triIdx], simd_f32(vtxW[i])[triIdx], 1.0f); + + // Clip triangle with straddling planes. + for (int i = 0; i < 5; ++i) + { + if ((straddleMask[i] & triBit) && (clipPlaneMask & (1 << i))) // <- second part maybe not needed? + { + nClippedVerts = ClipPolygon(vtxBuf[bufIdx ^ 1], vtxBuf[bufIdx], mCSFrustumPlanes[i], nClippedVerts); + bufIdx ^= 1; + } + } + + if (nClippedVerts >= 3) + { + // Write all triangles into the clip buffer and process them next loop iteration + clippedTrisBuffer[clipWriteIdx * 3 + 0] = vtxBuf[bufIdx][0]; + clippedTrisBuffer[clipWriteIdx * 3 + 1] = vtxBuf[bufIdx][1]; + clippedTrisBuffer[clipWriteIdx * 3 + 2] = vtxBuf[bufIdx][2]; + clipWriteIdx = (clipWriteIdx + 1) & (MAX_CLIPPED - 1); + for (int i = 2; i < nClippedVerts - 1; i++) + { + clippedTrisBuffer[clipWriteIdx * 3 + 0] = vtxBuf[bufIdx][0]; + clippedTrisBuffer[clipWriteIdx * 3 + 1] = vtxBuf[bufIdx][i]; + clippedTrisBuffer[clipWriteIdx * 3 + 2] = vtxBuf[bufIdx][i + 1]; + clipWriteIdx = (clipWriteIdx + 1) & (MAX_CLIPPED - 1); + } + } + } + // since all triangles were copied to clip buffer for next iteration, skip further processing + triMask = 0; +#else + unsigned int clipMask = (straddleMask[0] | straddleMask[1] | straddleMask[2] | straddleMask[3] | straddleMask[4]) & (triClipMask & triMask); + while (clipMask) + { + // Find and setup next triangle to clip + unsigned int triIdx = find_clear_lsb(&clipMask); + unsigned int triBit = (1U << triIdx); + assert(triIdx < SIMD_LANES); + + int bufIdx = 0; + int nClippedVerts = 3; + for (int i = 0; i < 3; i++) + vtxBuf[0][i] = _mm_setr_ps(simd_f32(vtxX[i])[triIdx], simd_f32(vtxY[i])[triIdx], simd_f32(vtxW[i])[triIdx], 1.0f); + + // Clip triangle with straddling planes. + for (int i = 0; i < 5; ++i) + { + if ((straddleMask[i] & triBit) && (clipPlaneMask & (1 << i))) + { + nClippedVerts = ClipPolygon(vtxBuf[bufIdx ^ 1], vtxBuf[bufIdx], mCSFrustumPlanes[i], nClippedVerts); + bufIdx ^= 1; + } + } + + if (nClippedVerts >= 3) + { + // Write the first triangle back into the list of currently processed triangles + for (int i = 0; i < 3; i++) + { + simd_f32(vtxX[i])[triIdx] = simd_f32(vtxBuf[bufIdx][i])[0]; + simd_f32(vtxY[i])[triIdx] = simd_f32(vtxBuf[bufIdx][i])[1]; + simd_f32(vtxW[i])[triIdx] = simd_f32(vtxBuf[bufIdx][i])[2]; + } + // Write the remaining triangles into the clip buffer and process them next loop iteration + for (int i = 2; i < nClippedVerts - 1; i++) + { + clippedTrisBuffer[clipWriteIdx * 3 + 0] = vtxBuf[bufIdx][0]; + clippedTrisBuffer[clipWriteIdx * 3 + 1] = vtxBuf[bufIdx][i]; + clippedTrisBuffer[clipWriteIdx * 3 + 2] = vtxBuf[bufIdx][i + 1]; + clipWriteIdx = (clipWriteIdx + 1) & (MAX_CLIPPED - 1); + } + } + else // Kill triangles that was removed by clipping + triMask &= ~triBit; + } +#endif + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Vertex transform & projection + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + FORCE_INLINE void TransformVerts(__mw *vtxX, __mw *vtxY, __mw *vtxW, const float *modelToClipMatrix) + { + if (modelToClipMatrix != nullptr) + { + for (int i = 0; i < 3; ++i) + { + __mw tmpX, tmpY, tmpW; + tmpX = _mmw_fmadd_ps(vtxX[i], _mmw_set1_ps(modelToClipMatrix[0]), _mmw_fmadd_ps(vtxY[i], _mmw_set1_ps(modelToClipMatrix[4]), _mmw_fmadd_ps(vtxW[i], _mmw_set1_ps(modelToClipMatrix[8]), _mmw_set1_ps(modelToClipMatrix[12])))); + tmpY = _mmw_fmadd_ps(vtxX[i], _mmw_set1_ps(modelToClipMatrix[1]), _mmw_fmadd_ps(vtxY[i], _mmw_set1_ps(modelToClipMatrix[5]), _mmw_fmadd_ps(vtxW[i], _mmw_set1_ps(modelToClipMatrix[9]), _mmw_set1_ps(modelToClipMatrix[13])))); + tmpW = _mmw_fmadd_ps(vtxX[i], _mmw_set1_ps(modelToClipMatrix[3]), _mmw_fmadd_ps(vtxY[i], _mmw_set1_ps(modelToClipMatrix[7]), _mmw_fmadd_ps(vtxW[i], _mmw_set1_ps(modelToClipMatrix[11]), _mmw_set1_ps(modelToClipMatrix[15])))); + vtxX[i] = tmpX; vtxY[i] = tmpY; vtxW[i] = tmpW; + } + } + } + +#if PRECISE_COVERAGE != 0 + FORCE_INLINE void ProjectVertices(__mwi *ipVtxX, __mwi *ipVtxY, __mw *pVtxX, __mw *pVtxY, __mw *pVtxZ, const __mw *vtxX, const __mw *vtxY, const __mw *vtxW) + { +#if USE_D3D != 0 + static const int vertexOrder[] = {2, 1, 0}; +#else + static const int vertexOrder[] = {0, 1, 2}; +#endif + + // Project vertices and transform to screen space. Snap to sub-pixel coordinates with FP_BITS precision. + for (int i = 0; i < 3; i++) + { + int idx = vertexOrder[i]; + __mw rcpW = _mmw_div_ps(_mmw_set1_ps(1.0f), vtxW[i]); + __mw screenX = _mmw_fmadd_ps(_mmw_mul_ps(vtxX[i], mHalfWidth), rcpW, mCenterX); + __mw screenY = _mmw_fmadd_ps(_mmw_mul_ps(vtxY[i], mHalfHeight), rcpW, mCenterY); + ipVtxX[idx] = _mmw_cvtps_epi32(_mmw_mul_ps(screenX, _mmw_set1_ps(float(1 << FP_BITS)))); + ipVtxY[idx] = _mmw_cvtps_epi32(_mmw_mul_ps(screenY, _mmw_set1_ps(float(1 << FP_BITS)))); + pVtxX[idx] = _mmw_mul_ps(_mmw_cvtepi32_ps(ipVtxX[idx]), _mmw_set1_ps(FP_INV)); + pVtxY[idx] = _mmw_mul_ps(_mmw_cvtepi32_ps(ipVtxY[idx]), _mmw_set1_ps(FP_INV)); + pVtxZ[idx] = rcpW; + } + } +#else + FORCE_INLINE void ProjectVertices(__mw *pVtxX, __mw *pVtxY, __mw *pVtxZ, const __mw *vtxX, const __mw *vtxY, const __mw *vtxW) + { +#if USE_D3D != 0 + static const int vertexOrder[] = {2, 1, 0}; +#else + static const int vertexOrder[] = {0, 1, 2}; +#endif + // Project vertices and transform to screen space. Round to nearest integer pixel coordinate + for (int i = 0; i < 3; i++) + { + int idx = vertexOrder[i]; + __mw rcpW = _mmw_div_ps(_mmw_set1_ps(1.0f), vtxW[i]); + + // The rounding modes are set to match HW rasterization with OpenGL. In practice our samples are placed + // in the (1,0) corner of each pixel, while HW rasterizer uses (0.5, 0.5). We get (1,0) because of the + // floor used when interpolating along triangle edges. The rounding modes match an offset of (0.5, -0.5) + pVtxX[idx] = _mmw_ceil_ps(_mmw_fmadd_ps(_mmw_mul_ps(vtxX[i], mHalfWidth), rcpW, mCenterX)); + pVtxY[idx] = _mmw_floor_ps(_mmw_fmadd_ps(_mmw_mul_ps(vtxY[i], mHalfHeight), rcpW, mCenterY)); + pVtxZ[idx] = rcpW; + } + } +#endif + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Common SSE/AVX input assembly functions, note that there are specialized gathers for the general case in the SSE/AVX specific files + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + FORCE_INLINE void GatherVerticesFast(__mw *vtxX, __mw *vtxY, __mw *vtxW, const float *inVtx, const unsigned int *inTrisPtr, int numLanes) + { + // This function assumes that the vertex layout is four packed x, y, z, w-values. + // Since the layout is known we can get some additional performance by using a + // more optimized gather strategy. + assert(numLanes >= 1); + + // Gather vertices + __mw v[4], swz[4]; + for (int i = 0; i < 3; i++) + { + // Load 4 (x,y,z,w) vectors per SSE part of the SIMD register (so 4 vectors for SSE, 8 vectors for AVX) + // this fetch uses templates to unroll the loop + VtxFetch4(v, inTrisPtr, i, inVtx, numLanes); + + // Transpose each individual SSE part of the SSE/AVX register (similar to _MM_TRANSPOSE4_PS) + swz[0] = _mmw_shuffle_ps(v[0], v[1], 0x44); + swz[2] = _mmw_shuffle_ps(v[0], v[1], 0xEE); + swz[1] = _mmw_shuffle_ps(v[2], v[3], 0x44); + swz[3] = _mmw_shuffle_ps(v[2], v[3], 0xEE); + + vtxX[i] = _mmw_shuffle_ps(swz[0], swz[1], 0x88); + vtxY[i] = _mmw_shuffle_ps(swz[0], swz[1], 0xDD); + vtxW[i] = _mmw_shuffle_ps(swz[2], swz[3], 0xDD); + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Rasterization functions + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + FORCE_INLINE void ComputeBoundingBox(__mwi &bbminX, __mwi &bbminY, __mwi &bbmaxX, __mwi &bbmaxY, const __mw *vX, const __mw *vY, const ScissorRect *scissor) + { + static const __mwi SIMD_PAD_W_MASK = _mmw_set1_epi32(~(TILE_WIDTH - 1)); + static const __mwi SIMD_PAD_H_MASK = _mmw_set1_epi32(~(TILE_HEIGHT - 1)); + + // Find Min/Max vertices + bbminX = _mmw_cvttps_epi32(_mmw_min_ps(vX[0], _mmw_min_ps(vX[1], vX[2]))); + bbminY = _mmw_cvttps_epi32(_mmw_min_ps(vY[0], _mmw_min_ps(vY[1], vY[2]))); + bbmaxX = _mmw_cvttps_epi32(_mmw_max_ps(vX[0], _mmw_max_ps(vX[1], vX[2]))); + bbmaxY = _mmw_cvttps_epi32(_mmw_max_ps(vY[0], _mmw_max_ps(vY[1], vY[2]))); + + // Clamp to tile boundaries + bbminX = _mmw_and_epi32(bbminX, SIMD_PAD_W_MASK); + bbmaxX = _mmw_and_epi32(_mmw_add_epi32(bbmaxX, _mmw_set1_epi32(TILE_WIDTH)), SIMD_PAD_W_MASK); + bbminY = _mmw_and_epi32(bbminY, SIMD_PAD_H_MASK); + bbmaxY = _mmw_and_epi32(_mmw_add_epi32(bbmaxY, _mmw_set1_epi32(TILE_HEIGHT)), SIMD_PAD_H_MASK); + + // Clip to scissor + bbminX = _mmw_max_epi32(bbminX, _mmw_set1_epi32(scissor->mMinX)); + bbmaxX = _mmw_min_epi32(bbmaxX, _mmw_set1_epi32(scissor->mMaxX)); + bbminY = _mmw_max_epi32(bbminY, _mmw_set1_epi32(scissor->mMinY)); + bbmaxY = _mmw_min_epi32(bbmaxY, _mmw_set1_epi32(scissor->mMaxY)); + } + +#if PRECISE_COVERAGE != 0 + FORCE_INLINE void SortVertices(__mwi *vX, __mwi *vY) + { + // Rotate the triangle in the winding order until v0 is the vertex with lowest Y value + for (int i = 0; i < 2; i++) + { + __mwi ey1 = _mmw_sub_epi32(vY[1], vY[0]); + __mwi ey2 = _mmw_sub_epi32(vY[2], vY[0]); + __mwi swapMask = _mmw_or_epi32(_mmw_or_epi32(ey1, ey2), _mmw_cmpeq_epi32(simd_cast<__mwi>(ey2), SIMD_BITS_ZERO)); + __mwi sX, sY; + sX = _mmw_blendv_epi32(vX[2], vX[0], swapMask); + vX[0] = _mmw_blendv_epi32(vX[0], vX[1], swapMask); + vX[1] = _mmw_blendv_epi32(vX[1], vX[2], swapMask); + vX[2] = sX; + sY = _mmw_blendv_epi32(vY[2], vY[0], swapMask); + vY[0] = _mmw_blendv_epi32(vY[0], vY[1], swapMask); + vY[1] = _mmw_blendv_epi32(vY[1], vY[2], swapMask); + vY[2] = sY; + } + } + + FORCE_INLINE int CullBackfaces(__mwi *ipVtxX, __mwi *ipVtxY, __mw *pVtxX, __mw *pVtxY, __mw *pVtxZ, const __mw &ccwMask, BackfaceWinding bfWinding) + { + // Reverse vertex order if non cw faces are considered front facing (rasterizer code requires CCW order) + if (!(bfWinding & BACKFACE_CW)) + { + __mw tmpX, tmpY, tmpZ; + __mwi itmpX, itmpY; + itmpX = _mmw_blendv_epi32(ipVtxX[2], ipVtxX[0], simd_cast<__mwi>(ccwMask)); + itmpY = _mmw_blendv_epi32(ipVtxY[2], ipVtxY[0], simd_cast<__mwi>(ccwMask)); + tmpX = _mmw_blendv_ps(pVtxX[2], pVtxX[0], ccwMask); + tmpY = _mmw_blendv_ps(pVtxY[2], pVtxY[0], ccwMask); + tmpZ = _mmw_blendv_ps(pVtxZ[2], pVtxZ[0], ccwMask); + ipVtxX[2] = _mmw_blendv_epi32(ipVtxX[0], ipVtxX[2], simd_cast<__mwi>(ccwMask)); + ipVtxY[2] = _mmw_blendv_epi32(ipVtxY[0], ipVtxY[2], simd_cast<__mwi>(ccwMask)); + pVtxX[2] = _mmw_blendv_ps(pVtxX[0], pVtxX[2], ccwMask); + pVtxY[2] = _mmw_blendv_ps(pVtxY[0], pVtxY[2], ccwMask); + pVtxZ[2] = _mmw_blendv_ps(pVtxZ[0], pVtxZ[2], ccwMask); + ipVtxX[0] = itmpX; + ipVtxY[0] = itmpY; + pVtxX[0] = tmpX; + pVtxY[0] = tmpY; + pVtxZ[0] = tmpZ; + } + + // Return a lane mask with all front faces set + return ((bfWinding & BACKFACE_CCW) ? 0 : _mmw_movemask_ps(ccwMask)) | ((bfWinding & BACKFACE_CW) ? 0 : ~_mmw_movemask_ps(ccwMask)); + } +#else + FORCE_INLINE void SortVertices(__mw *vX, __mw *vY) + { + // Rotate the triangle in the winding order until v0 is the vertex with lowest Y value + for (int i = 0; i < 2; i++) + { + __mw ey1 = _mmw_sub_ps(vY[1], vY[0]); + __mw ey2 = _mmw_sub_ps(vY[2], vY[0]); + __mw swapMask = _mmw_or_ps(_mmw_or_ps(ey1, ey2), simd_cast<__mw>(_mmw_cmpeq_epi32(simd_cast<__mwi>(ey2), SIMD_BITS_ZERO))); + __mw sX, sY; + sX = _mmw_blendv_ps(vX[2], vX[0], swapMask); + vX[0] = _mmw_blendv_ps(vX[0], vX[1], swapMask); + vX[1] = _mmw_blendv_ps(vX[1], vX[2], swapMask); + vX[2] = sX; + sY = _mmw_blendv_ps(vY[2], vY[0], swapMask); + vY[0] = _mmw_blendv_ps(vY[0], vY[1], swapMask); + vY[1] = _mmw_blendv_ps(vY[1], vY[2], swapMask); + vY[2] = sY; + } + } + + FORCE_INLINE int CullBackfaces(__mw *pVtxX, __mw *pVtxY, __mw *pVtxZ, const __mw &ccwMask, BackfaceWinding bfWinding) + { + // Reverse vertex order if non cw faces are considered front facing (rasterizer code requires CCW order) + if (!(bfWinding & BACKFACE_CW)) + { + __mw tmpX, tmpY, tmpZ; + tmpX = _mmw_blendv_ps(pVtxX[2], pVtxX[0], ccwMask); + tmpY = _mmw_blendv_ps(pVtxY[2], pVtxY[0], ccwMask); + tmpZ = _mmw_blendv_ps(pVtxZ[2], pVtxZ[0], ccwMask); + pVtxX[2] = _mmw_blendv_ps(pVtxX[0], pVtxX[2], ccwMask); + pVtxY[2] = _mmw_blendv_ps(pVtxY[0], pVtxY[2], ccwMask); + pVtxZ[2] = _mmw_blendv_ps(pVtxZ[0], pVtxZ[2], ccwMask); + pVtxX[0] = tmpX; + pVtxY[0] = tmpY; + pVtxZ[0] = tmpZ; + } + + // Return a lane mask with all front faces set + return ((bfWinding & BACKFACE_CCW) ? 0 : _mmw_movemask_ps(ccwMask)) | ((bfWinding & BACKFACE_CW) ? 0 : ~_mmw_movemask_ps(ccwMask)); + } +#endif + + FORCE_INLINE void ComputeDepthPlane(const __mw *pVtxX, const __mw *pVtxY, const __mw *pVtxZ, __mw &zPixelDx, __mw &zPixelDy) const + { + // Setup z(x,y) = z0 + dx*x + dy*y screen space depth plane equation + __mw x2 = _mmw_sub_ps(pVtxX[2], pVtxX[0]); + __mw x1 = _mmw_sub_ps(pVtxX[1], pVtxX[0]); + __mw y1 = _mmw_sub_ps(pVtxY[1], pVtxY[0]); + __mw y2 = _mmw_sub_ps(pVtxY[2], pVtxY[0]); + __mw z1 = _mmw_sub_ps(pVtxZ[1], pVtxZ[0]); + __mw z2 = _mmw_sub_ps(pVtxZ[2], pVtxZ[0]); + __mw d = _mmw_div_ps(_mmw_set1_ps(1.0f), _mmw_fmsub_ps(x1, y2, _mmw_mul_ps(y1, x2))); + zPixelDx = _mmw_mul_ps(_mmw_fmsub_ps(z1, y2, _mmw_mul_ps(y1, z2)), d); + zPixelDy = _mmw_mul_ps(_mmw_fmsub_ps(x1, z2, _mmw_mul_ps(z1, x2)), d); + } + + FORCE_INLINE void UpdateTileQuick(int tileIdx, const __mwi &coverage, const __mw &zTriv) + { + // Update heuristic used in the paper "Masked Software Occlusion Culling", + // good balance between performance and accuracy + STATS_ADD(mStats.mOccluders.mNumTilesUpdated, 1); + assert(tileIdx >= 0 && tileIdx < mTilesWidth*mTilesHeight); + + __mwi mask = mMaskedHiZBuffer[tileIdx].mMask; + __mw *zMin = mMaskedHiZBuffer[tileIdx].mZMin; + + // Swizzle coverage mask to 8x4 subtiles and test if any subtiles are not covered at all + __mwi rastMask = coverage; + __mwi deadLane = _mmw_cmpeq_epi32(rastMask, SIMD_BITS_ZERO); + + // Mask out all subtiles failing the depth test (don't update these subtiles) + deadLane = _mmw_or_epi32(deadLane, _mmw_srai_epi32(simd_cast<__mwi>(_mmw_sub_ps(zTriv, zMin[0])), 31)); + rastMask = _mmw_andnot_epi32(deadLane, rastMask); + + // Use distance heuristic to discard layer 1 if incoming triangle is significantly nearer to observer + // than the buffer contents. See Section 3.2 in "Masked Software Occlusion Culling" + __mwi coveredLane = _mmw_cmpeq_epi32(rastMask, SIMD_BITS_ONE); + __mw diff = _mmw_fmsub_ps(zMin[1], _mmw_set1_ps(2.0f), _mmw_add_ps(zTriv, zMin[0])); + __mwi discardLayerMask = _mmw_andnot_epi32(deadLane, _mmw_or_epi32(_mmw_srai_epi32(simd_cast<__mwi>(diff), 31), coveredLane)); + + // Update the mask with incoming triangle coverage + mask = _mmw_or_epi32(_mmw_andnot_epi32(discardLayerMask, mask), rastMask); + + __mwi maskFull = _mmw_cmpeq_epi32(mask, SIMD_BITS_ONE); + + // Compute new value for zMin[1]. This has one of four outcomes: zMin[1] = min(zMin[1], zTriv), zMin[1] = zTriv, + // zMin[1] = FLT_MAX or unchanged, depending on if the layer is updated, discarded, fully covered, or not updated + __mw opA = _mmw_blendv_ps(zTriv, zMin[1], simd_cast<__mw>(deadLane)); + __mw opB = _mmw_blendv_ps(zMin[1], zTriv, simd_cast<__mw>(discardLayerMask)); + __mw z1min = _mmw_min_ps(opA, opB); + zMin[1] = _mmw_blendv_ps(z1min, _mmw_set1_ps(FLT_MAX), simd_cast<__mw>(maskFull)); + + // Propagate zMin[1] back to zMin[0] if tile was fully covered, and update the mask + zMin[0] = _mmw_blendv_ps(zMin[0], z1min, simd_cast<__mw>(maskFull)); + mMaskedHiZBuffer[tileIdx].mMask = _mmw_andnot_epi32(maskFull, mask); + } + + FORCE_INLINE void UpdateTileAccurate(int tileIdx, const __mwi &coverage, const __mw &zTriv) + { + assert(tileIdx >= 0 && tileIdx < mTilesWidth*mTilesHeight); + + __mw *zMin = mMaskedHiZBuffer[tileIdx].mZMin; + __mwi &mask = mMaskedHiZBuffer[tileIdx].mMask; + + // Swizzle coverage mask to 8x4 subtiles + __mwi rastMask = coverage; + + // Perform individual depth tests with layer 0 & 1 and mask out all failing pixels + __mw sdist0 = _mmw_sub_ps(zMin[0], zTriv); + __mw sdist1 = _mmw_sub_ps(zMin[1], zTriv); + __mwi sign0 = _mmw_srai_epi32(simd_cast<__mwi>(sdist0), 31); + __mwi sign1 = _mmw_srai_epi32(simd_cast<__mwi>(sdist1), 31); + __mwi triMask = _mmw_and_epi32(rastMask, _mmw_or_epi32(_mmw_andnot_epi32(mask, sign0), _mmw_and_epi32(mask, sign1))); + + // Early out if no pixels survived the depth test (this test is more accurate than + // the early culling test in TraverseScanline()) + __mwi t0 = _mmw_cmpeq_epi32(triMask, SIMD_BITS_ZERO); + __mwi t0inv = _mmw_not_epi32(t0); + if (_mmw_testz_epi32(t0inv, t0inv)) + return; + + STATS_ADD(mStats.mOccluders.mNumTilesUpdated, 1); + + __mw zTri = _mmw_blendv_ps(zTriv, zMin[0], simd_cast<__mw>(t0)); + + // Test if incoming triangle completely overwrites layer 0 or 1 + __mwi layerMask0 = _mmw_andnot_epi32(triMask, _mmw_not_epi32(mask)); + __mwi layerMask1 = _mmw_andnot_epi32(triMask, mask); + __mwi lm0 = _mmw_cmpeq_epi32(layerMask0, SIMD_BITS_ZERO); + __mwi lm1 = _mmw_cmpeq_epi32(layerMask1, SIMD_BITS_ZERO); + __mw z0 = _mmw_blendv_ps(zMin[0], zTri, simd_cast<__mw>(lm0)); + __mw z1 = _mmw_blendv_ps(zMin[1], zTri, simd_cast<__mw>(lm1)); + + // Compute distances used for merging heuristic + __mw d0 = _mmw_abs_ps(sdist0); + __mw d1 = _mmw_abs_ps(sdist1); + __mw d2 = _mmw_abs_ps(_mmw_sub_ps(z0, z1)); + + // Find minimum distance + __mwi c01 = simd_cast<__mwi>(_mmw_sub_ps(d0, d1)); + __mwi c02 = simd_cast<__mwi>(_mmw_sub_ps(d0, d2)); + __mwi c12 = simd_cast<__mwi>(_mmw_sub_ps(d1, d2)); + // Two tests indicating which layer the incoming triangle will merge with or + // overwrite. d0min indicates that the triangle will overwrite layer 0, and + // d1min flags that the triangle will overwrite layer 1. + __mwi d0min = _mmw_or_epi32(_mmw_and_epi32(c01, c02), _mmw_or_epi32(lm0, t0)); + __mwi d1min = _mmw_andnot_epi32(d0min, _mmw_or_epi32(c12, lm1)); + + /////////////////////////////////////////////////////////////////////////////// + // Update depth buffer entry. NOTE: we always merge into layer 0, so if the + // triangle should be merged with layer 1, we first swap layer 0 & 1 and then + // merge into layer 0. + /////////////////////////////////////////////////////////////////////////////// + + // Update mask based on which layer the triangle overwrites or was merged into + __mw inner = _mmw_blendv_ps(simd_cast<__mw>(triMask), simd_cast<__mw>(layerMask1), simd_cast<__mw>(d0min)); + mask = simd_cast<__mwi>(_mmw_blendv_ps(inner, simd_cast<__mw>(layerMask0), simd_cast<__mw>(d1min))); + + // Update the zMin[0] value. There are four outcomes: overwrite with layer 1, + // merge with layer 1, merge with zTri or overwrite with layer 1 and then merge + // with zTri. + __mw e0 = _mmw_blendv_ps(z0, z1, simd_cast<__mw>(d1min)); + __mw e1 = _mmw_blendv_ps(z1, zTri, simd_cast<__mw>(_mmw_or_epi32(d1min, d0min))); + zMin[0] = _mmw_min_ps(e0, e1); + + // Update the zMin[1] value. There are three outcomes: keep current value, + // overwrite with zTri, or overwrite with z1 + __mw z1t = _mmw_blendv_ps(zTri, z1, simd_cast<__mw>(d0min)); + zMin[1] = _mmw_blendv_ps(z1t, z0, simd_cast<__mw>(d1min)); + } + + template + FORCE_INLINE int TraverseScanline(int leftOffset, int rightOffset, int tileIdx, int rightEvent, int leftEvent, const __mwi *events, const __mw &zTriMin, const __mw &zTriMax, const __mw &iz0, float zx) + { + // Floor edge events to integer pixel coordinates (shift out fixed point bits) + int eventOffset = leftOffset << TILE_WIDTH_SHIFT; + __mwi right[NRIGHT], left[NLEFT]; + for (int i = 0; i < NRIGHT; ++i) + right[i] = _mmw_max_epi32(_mmw_sub_epi32(_mmw_srai_epi32(events[rightEvent + i], FP_BITS), _mmw_set1_epi32(eventOffset)), SIMD_BITS_ZERO); + for (int i = 0; i < NLEFT; ++i) + left[i] = _mmw_max_epi32(_mmw_sub_epi32(_mmw_srai_epi32(events[leftEvent - i], FP_BITS), _mmw_set1_epi32(eventOffset)), SIMD_BITS_ZERO); + + __mw z0 = _mmw_add_ps(iz0, _mmw_set1_ps(zx*leftOffset)); + int tileIdxEnd = tileIdx + rightOffset; + tileIdx += leftOffset; + for (;;) + { + if (TEST_Z) + STATS_ADD(mStats.mOccludees.mNumTilesTraversed, 1); + else + STATS_ADD(mStats.mOccluders.mNumTilesTraversed, 1); + + // Perform a coarse test to quickly discard occluded tiles +#if QUICK_MASK != 0 + // Only use the reference layer (layer 0) to cull as it is always conservative + __mw zMinBuf = mMaskedHiZBuffer[tileIdx].mZMin[0]; +#else + // Compute zMin for the overlapped layers + __mwi mask = mMaskedHiZBuffer[tileIdx].mMask; + __mw zMin0 = _mmw_blendv_ps(mMaskedHiZBuffer[tileIdx].mZMin[0], mMaskedHiZBuffer[tileIdx].mZMin[1], simd_cast<__mw>(_mmw_cmpeq_epi32(mask, _mmw_set1_epi32(~0)))); + __mw zMin1 = _mmw_blendv_ps(mMaskedHiZBuffer[tileIdx].mZMin[1], mMaskedHiZBuffer[tileIdx].mZMin[0], simd_cast<__mw>(_mmw_cmpeq_epi32(mask, _mmw_setzero_epi32()))); + __mw zMinBuf = _mmw_min_ps(zMin0, zMin1); +#endif + __mw dist0 = _mmw_sub_ps(zTriMax, zMinBuf); + if (_mmw_movemask_ps(dist0) != SIMD_ALL_LANES_MASK) + { + // Compute coverage mask for entire 32xN using shift operations + __mwi accumulatedMask = _mmw_sllv_ones(left[0]); + for (int i = 1; i < NLEFT; ++i) + accumulatedMask = _mmw_and_epi32(accumulatedMask, _mmw_sllv_ones(left[i])); + for (int i = 0; i < NRIGHT; ++i) + accumulatedMask = _mmw_andnot_epi32(_mmw_sllv_ones(right[i]), accumulatedMask); + + if (TEST_Z) + { + // Perform a conservative visibility test (test zMax against buffer for each covered 8x4 subtile) + __mw zSubTileMax = _mmw_min_ps(z0, zTriMax); + __mwi zPass = simd_cast<__mwi>(_mmw_cmpge_ps(zSubTileMax, zMinBuf)); + + __mwi rastMask = _mmw_transpose_epi8(accumulatedMask); + __mwi deadLane = _mmw_cmpeq_epi32(rastMask, SIMD_BITS_ZERO); + zPass = _mmw_andnot_epi32(deadLane, zPass); + + if (!_mmw_testz_epi32(zPass, zPass)) + return CullingResult::VISIBLE; + } + else + { + // Compute interpolated min for each 8x4 subtile and update the masked hierarchical z buffer entry + __mw zSubTileMin = _mmw_max_ps(z0, zTriMin); +#if QUICK_MASK != 0 + UpdateTileQuick(tileIdx, _mmw_transpose_epi8(accumulatedMask), zSubTileMin); +#else + UpdateTileAccurate(tileIdx, _mmw_transpose_epi8(accumulatedMask), zSubTileMin); +#endif + } + } + + // Update buffer address, interpolate z and edge events + tileIdx++; + if (tileIdx >= tileIdxEnd) + break; + z0 = _mmw_add_ps(z0, _mmw_set1_ps(zx)); + for (int i = 0; i < NRIGHT; ++i) + right[i] = _mmw_subs_epu16(right[i], SIMD_TILE_WIDTH); // Trick, use sub saturated to avoid checking against < 0 for shift (values should fit in 16 bits) + for (int i = 0; i < NLEFT; ++i) + left[i] = _mmw_subs_epu16(left[i], SIMD_TILE_WIDTH); + } + + return TEST_Z ? CullingResult::OCCLUDED : CullingResult::VISIBLE; + } + + + template +#if PRECISE_COVERAGE != 0 + FORCE_INLINE int RasterizeTriangle(unsigned int triIdx, int bbWidth, int tileRowIdx, int tileMidRowIdx, int tileEndRowIdx, const __mwi *eventStart, const __mw *slope, const __mwi *slopeTileDelta, const __mw &zTriMin, const __mw &zTriMax, __mw &z0, float zx, float zy, const __mwi *edgeY, const __mwi *absEdgeX, const __mwi *slopeSign, const __mwi *eventStartRemainder, const __mwi *slopeTileRemainder) +#else + FORCE_INLINE int RasterizeTriangle(unsigned int triIdx, int bbWidth, int tileRowIdx, int tileMidRowIdx, int tileEndRowIdx, const __mwi *eventStart, const __mwi *slope, const __mwi *slopeTileDelta, const __mw &zTriMin, const __mw &zTriMax, __mw &z0, float zx, float zy) +#endif + { + if (TEST_Z) + STATS_ADD(mStats.mOccludees.mNumRasterizedTriangles, 1); + else + STATS_ADD(mStats.mOccluders.mNumRasterizedTriangles, 1); + + int cullResult; + +#if PRECISE_COVERAGE != 0 + #define LEFT_EDGE_BIAS -1 + #define RIGHT_EDGE_BIAS 1 + #define UPDATE_TILE_EVENTS_Y(i) \ + triEventRemainder[i] = _mmw_sub_epi32(triEventRemainder[i], triSlopeTileRemainder[i]); \ + __mwi overflow##i = _mmw_srai_epi32(triEventRemainder[i], 31); \ + triEventRemainder[i] = _mmw_add_epi32(triEventRemainder[i], _mmw_and_epi32(overflow##i, triEdgeY[i])); \ + triEvent[i] = _mmw_add_epi32(triEvent[i], _mmw_add_epi32(triSlopeTileDelta[i], _mmw_and_epi32(overflow##i, triSlopeSign[i]))) + + __mwi triEvent[3], triSlopeSign[3], triSlopeTileDelta[3], triEdgeY[3], triSlopeTileRemainder[3], triEventRemainder[3]; + for (int i = 0; i < 3; ++i) + { + triSlopeSign[i] = _mmw_set1_epi32(simd_i32(slopeSign[i])[triIdx]); + triSlopeTileDelta[i] = _mmw_set1_epi32(simd_i32(slopeTileDelta[i])[triIdx]); + triEdgeY[i] = _mmw_set1_epi32(simd_i32(edgeY[i])[triIdx]); + triSlopeTileRemainder[i] = _mmw_set1_epi32(simd_i32(slopeTileRemainder[i])[triIdx]); + + __mw triSlope = _mmw_set1_ps(simd_f32(slope[i])[triIdx]); + __mwi triAbsEdgeX = _mmw_set1_epi32(simd_i32(absEdgeX[i])[triIdx]); + __mwi triStartRemainder = _mmw_set1_epi32(simd_i32(eventStartRemainder[i])[triIdx]); + __mwi triEventStart = _mmw_set1_epi32(simd_i32(eventStart[i])[triIdx]); + + __mwi scanlineDelta = _mmw_cvttps_epi32(_mmw_mul_ps(triSlope, SIMD_LANE_YCOORD_F)); + __mwi scanlineSlopeRemainder = _mmw_sub_epi32(_mmw_mullo_epi32(triAbsEdgeX, SIMD_LANE_YCOORD_I), _mmw_mullo_epi32(_mmw_abs_epi32(scanlineDelta), triEdgeY[i])); + + triEventRemainder[i] = _mmw_sub_epi32(triStartRemainder, scanlineSlopeRemainder); + __mwi overflow = _mmw_srai_epi32(triEventRemainder[i], 31); + triEventRemainder[i] = _mmw_add_epi32(triEventRemainder[i], _mmw_and_epi32(overflow, triEdgeY[i])); + triEvent[i] = _mmw_add_epi32(_mmw_add_epi32(triEventStart, scanlineDelta), _mmw_and_epi32(overflow, triSlopeSign[i])); + } + +#else + #define LEFT_EDGE_BIAS 0 + #define RIGHT_EDGE_BIAS 0 + #define UPDATE_TILE_EVENTS_Y(i) triEvent[i] = _mmw_add_epi32(triEvent[i], triSlopeTileDelta[i]); + + // Get deltas used to increment edge events each time we traverse one scanline of tiles + __mwi triSlopeTileDelta[3]; + triSlopeTileDelta[0] = _mmw_set1_epi32(simd_i32(slopeTileDelta[0])[triIdx]); + triSlopeTileDelta[1] = _mmw_set1_epi32(simd_i32(slopeTileDelta[1])[triIdx]); + triSlopeTileDelta[2] = _mmw_set1_epi32(simd_i32(slopeTileDelta[2])[triIdx]); + + // Setup edge events for first batch of SIMD_LANES scanlines + __mwi triEvent[3]; + triEvent[0] = _mmw_add_epi32(_mmw_set1_epi32(simd_i32(eventStart[0])[triIdx]), _mmw_mullo_epi32(SIMD_LANE_IDX, _mmw_set1_epi32(simd_i32(slope[0])[triIdx]))); + triEvent[1] = _mmw_add_epi32(_mmw_set1_epi32(simd_i32(eventStart[1])[triIdx]), _mmw_mullo_epi32(SIMD_LANE_IDX, _mmw_set1_epi32(simd_i32(slope[1])[triIdx]))); + triEvent[2] = _mmw_add_epi32(_mmw_set1_epi32(simd_i32(eventStart[2])[triIdx]), _mmw_mullo_epi32(SIMD_LANE_IDX, _mmw_set1_epi32(simd_i32(slope[2])[triIdx]))); +#endif + + // For big triangles track start & end tile for each scanline and only traverse the valid region + int startDelta, endDelta, topDelta, startEvent, endEvent, topEvent; + if (TIGHT_TRAVERSAL) + { + startDelta = simd_i32(slopeTileDelta[2])[triIdx] + LEFT_EDGE_BIAS; + endDelta = simd_i32(slopeTileDelta[0])[triIdx] + RIGHT_EDGE_BIAS; + topDelta = simd_i32(slopeTileDelta[1])[triIdx] + (MID_VTX_RIGHT ? RIGHT_EDGE_BIAS : LEFT_EDGE_BIAS); + + // Compute conservative bounds for the edge events over a 32xN tile + startEvent = simd_i32(eventStart[2])[triIdx] + min(0, startDelta); + endEvent = simd_i32(eventStart[0])[triIdx] + max(0, endDelta) + (TILE_WIDTH << FP_BITS); + if (MID_VTX_RIGHT) + topEvent = simd_i32(eventStart[1])[triIdx] + max(0, topDelta) + (TILE_WIDTH << FP_BITS); + else + topEvent = simd_i32(eventStart[1])[triIdx] + min(0, topDelta); + } + + if (tileRowIdx <= tileMidRowIdx) + { + int tileStopIdx = min(tileEndRowIdx, tileMidRowIdx); + // Traverse the bottom half of the triangle + while (tileRowIdx < tileStopIdx) + { + int start = 0, end = bbWidth; + if (TIGHT_TRAVERSAL) + { + // Compute tighter start and endpoints to avoid traversing empty space + start = max(0, min(bbWidth - 1, startEvent >> (TILE_WIDTH_SHIFT + FP_BITS))); + end = min(bbWidth, ((int)endEvent >> (TILE_WIDTH_SHIFT + FP_BITS))); + startEvent += startDelta; + endEvent += endDelta; + } + + // Traverse the scanline and update the masked hierarchical z buffer + cullResult = TraverseScanline(start, end, tileRowIdx, 0, 2, triEvent, zTriMin, zTriMax, z0, zx); + + if (TEST_Z && cullResult == CullingResult::VISIBLE) // Early out if performing occlusion query + return CullingResult::VISIBLE; + + // move to the next scanline of tiles, update edge events and interpolate z + tileRowIdx += mTilesWidth; + z0 = _mmw_add_ps(z0, _mmw_set1_ps(zy)); + UPDATE_TILE_EVENTS_Y(0); + UPDATE_TILE_EVENTS_Y(2); + } + + // Traverse the middle scanline of tiles. We must consider all three edges only in this region + if (tileRowIdx < tileEndRowIdx) + { + int start = 0, end = bbWidth; + if (TIGHT_TRAVERSAL) + { + // Compute tighter start and endpoints to avoid traversing lots of empty space + start = max(0, min(bbWidth - 1, startEvent >> (TILE_WIDTH_SHIFT + FP_BITS))); + end = min(bbWidth, ((int)endEvent >> (TILE_WIDTH_SHIFT + FP_BITS))); + + // Switch the traversal start / end to account for the upper side edge + endEvent = MID_VTX_RIGHT ? topEvent : endEvent; + endDelta = MID_VTX_RIGHT ? topDelta : endDelta; + startEvent = MID_VTX_RIGHT ? startEvent : topEvent; + startDelta = MID_VTX_RIGHT ? startDelta : topDelta; + startEvent += startDelta; + endEvent += endDelta; + } + + // Traverse the scanline and update the masked hierarchical z buffer. + if (MID_VTX_RIGHT) + cullResult = TraverseScanline(start, end, tileRowIdx, 0, 2, triEvent, zTriMin, zTriMax, z0, zx); + else + cullResult = TraverseScanline(start, end, tileRowIdx, 0, 2, triEvent, zTriMin, zTriMax, z0, zx); + + if (TEST_Z && cullResult == CullingResult::VISIBLE) // Early out if performing occlusion query + return CullingResult::VISIBLE; + + tileRowIdx += mTilesWidth; + } + + // Traverse the top half of the triangle + if (tileRowIdx < tileEndRowIdx) + { + // move to the next scanline of tiles, update edge events and interpolate z + z0 = _mmw_add_ps(z0, _mmw_set1_ps(zy)); + int i0 = MID_VTX_RIGHT + 0; + int i1 = MID_VTX_RIGHT + 1; + UPDATE_TILE_EVENTS_Y(i0); + UPDATE_TILE_EVENTS_Y(i1); + for (;;) + { + int start = 0, end = bbWidth; + if (TIGHT_TRAVERSAL) + { + // Compute tighter start and endpoints to avoid traversing lots of empty space + start = max(0, min(bbWidth - 1, startEvent >> (TILE_WIDTH_SHIFT + FP_BITS))); + end = min(bbWidth, ((int)endEvent >> (TILE_WIDTH_SHIFT + FP_BITS))); + startEvent += startDelta; + endEvent += endDelta; + } + + // Traverse the scanline and update the masked hierarchical z buffer + cullResult = TraverseScanline(start, end, tileRowIdx, MID_VTX_RIGHT + 0, MID_VTX_RIGHT + 1, triEvent, zTriMin, zTriMax, z0, zx); + + if (TEST_Z && cullResult == CullingResult::VISIBLE) // Early out if performing occlusion query + return CullingResult::VISIBLE; + + // move to the next scanline of tiles, update edge events and interpolate z + tileRowIdx += mTilesWidth; + if (tileRowIdx >= tileEndRowIdx) + break; + z0 = _mmw_add_ps(z0, _mmw_set1_ps(zy)); + UPDATE_TILE_EVENTS_Y(i0); + UPDATE_TILE_EVENTS_Y(i1); + } + } + } + else + { + if (TIGHT_TRAVERSAL) + { + // For large triangles, switch the traversal start / end to account for the upper side edge + endEvent = MID_VTX_RIGHT ? topEvent : endEvent; + endDelta = MID_VTX_RIGHT ? topDelta : endDelta; + startEvent = MID_VTX_RIGHT ? startEvent : topEvent; + startDelta = MID_VTX_RIGHT ? startDelta : topDelta; + } + + // Traverse the top half of the triangle + if (tileRowIdx < tileEndRowIdx) + { + int i0 = MID_VTX_RIGHT + 0; + int i1 = MID_VTX_RIGHT + 1; + for (;;) + { + int start = 0, end = bbWidth; + if (TIGHT_TRAVERSAL) + { + // Compute tighter start and endpoints to avoid traversing lots of empty space + start = max(0, min(bbWidth - 1, startEvent >> (TILE_WIDTH_SHIFT + FP_BITS))); + end = min(bbWidth, ((int)endEvent >> (TILE_WIDTH_SHIFT + FP_BITS))); + startEvent += startDelta; + endEvent += endDelta; + } + + // Traverse the scanline and update the masked hierarchical z buffer + cullResult = TraverseScanline(start, end, tileRowIdx, MID_VTX_RIGHT + 0, MID_VTX_RIGHT + 1, triEvent, zTriMin, zTriMax, z0, zx); + + if (TEST_Z && cullResult == CullingResult::VISIBLE) // Early out if performing occlusion query + return CullingResult::VISIBLE; + + // move to the next scanline of tiles, update edge events and interpolate z + tileRowIdx += mTilesWidth; + if (tileRowIdx >= tileEndRowIdx) + break; + z0 = _mmw_add_ps(z0, _mmw_set1_ps(zy)); + UPDATE_TILE_EVENTS_Y(i0); + UPDATE_TILE_EVENTS_Y(i1); + } + } + } + + return TEST_Z ? CullingResult::OCCLUDED : CullingResult::VISIBLE; + } + + template +#if PRECISE_COVERAGE != 0 + FORCE_INLINE int RasterizeTriangleBatch(__mwi ipVtxX[3], __mwi ipVtxY[3], __mw pVtxX[3], __mw pVtxY[3], __mw pVtxZ[3], unsigned int triMask, const ScissorRect *scissor) +#else + FORCE_INLINE int RasterizeTriangleBatch(__mw pVtxX[3], __mw pVtxY[3], __mw pVtxZ[3], unsigned int triMask, const ScissorRect *scissor) +#endif + { + int cullResult = CullingResult::VIEW_CULLED; + + ////////////////////////////////////////////////////////////////////////////// + // Compute bounding box and clamp to tile coordinates + ////////////////////////////////////////////////////////////////////////////// + + __mwi bbPixelMinX, bbPixelMinY, bbPixelMaxX, bbPixelMaxY; + ComputeBoundingBox(bbPixelMinX, bbPixelMinY, bbPixelMaxX, bbPixelMaxY, pVtxX, pVtxY, scissor); + + // Clamp bounding box to tiles (it's already padded in computeBoundingBox) + __mwi bbTileMinX = _mmw_srai_epi32(bbPixelMinX, TILE_WIDTH_SHIFT); + __mwi bbTileMinY = _mmw_srai_epi32(bbPixelMinY, TILE_HEIGHT_SHIFT); + __mwi bbTileMaxX = _mmw_srai_epi32(bbPixelMaxX, TILE_WIDTH_SHIFT); + __mwi bbTileMaxY = _mmw_srai_epi32(bbPixelMaxY, TILE_HEIGHT_SHIFT); + __mwi bbTileSizeX = _mmw_sub_epi32(bbTileMaxX, bbTileMinX); + __mwi bbTileSizeY = _mmw_sub_epi32(bbTileMaxY, bbTileMinY); + + // Cull triangles with zero bounding box + __mwi bboxSign = _mmw_or_epi32(_mmw_sub_epi32(bbTileSizeX, _mmw_set1_epi32(1)), _mmw_sub_epi32(bbTileSizeY, _mmw_set1_epi32(1))); + triMask &= ~_mmw_movemask_ps(simd_cast<__mw>(bboxSign)) & SIMD_ALL_LANES_MASK; + if (triMask == 0x0) + return cullResult; + + if (!TEST_Z) + cullResult = CullingResult::VISIBLE; + + ////////////////////////////////////////////////////////////////////////////// + // Set up screen space depth plane + ////////////////////////////////////////////////////////////////////////////// + + __mw zPixelDx, zPixelDy; + ComputeDepthPlane(pVtxX, pVtxY, pVtxZ, zPixelDx, zPixelDy); + + // Compute z value at min corner of bounding box. Offset to make sure z is conservative for all 8x4 subtiles + __mw bbMinXV0 = _mmw_sub_ps(_mmw_cvtepi32_ps(bbPixelMinX), pVtxX[0]); + __mw bbMinYV0 = _mmw_sub_ps(_mmw_cvtepi32_ps(bbPixelMinY), pVtxY[0]); + __mw zPlaneOffset = _mmw_fmadd_ps(zPixelDx, bbMinXV0, _mmw_fmadd_ps(zPixelDy, bbMinYV0, pVtxZ[0])); + __mw zTileDx = _mmw_mul_ps(zPixelDx, _mmw_set1_ps((float)TILE_WIDTH)); + __mw zTileDy = _mmw_mul_ps(zPixelDy, _mmw_set1_ps((float)TILE_HEIGHT)); + if (TEST_Z) + { + zPlaneOffset = _mmw_add_ps(zPlaneOffset, _mmw_max_ps(_mmw_setzero_ps(), _mmw_mul_ps(zPixelDx, _mmw_set1_ps(SUB_TILE_WIDTH)))); + zPlaneOffset = _mmw_add_ps(zPlaneOffset, _mmw_max_ps(_mmw_setzero_ps(), _mmw_mul_ps(zPixelDy, _mmw_set1_ps(SUB_TILE_HEIGHT)))); + } + else + { + zPlaneOffset = _mmw_add_ps(zPlaneOffset, _mmw_min_ps(_mmw_setzero_ps(), _mmw_mul_ps(zPixelDx, _mmw_set1_ps(SUB_TILE_WIDTH)))); + zPlaneOffset = _mmw_add_ps(zPlaneOffset, _mmw_min_ps(_mmw_setzero_ps(), _mmw_mul_ps(zPixelDy, _mmw_set1_ps(SUB_TILE_HEIGHT)))); + } + + // Compute Zmin and Zmax for the triangle (used to narrow the range for difficult tiles) + __mw zMin = _mmw_min_ps(pVtxZ[0], _mmw_min_ps(pVtxZ[1], pVtxZ[2])); + __mw zMax = _mmw_max_ps(pVtxZ[0], _mmw_max_ps(pVtxZ[1], pVtxZ[2])); + + ////////////////////////////////////////////////////////////////////////////// + // Sort vertices (v0 has lowest Y, and the rest is in winding order) and + // compute edges. Also find the middle vertex and compute tile + ////////////////////////////////////////////////////////////////////////////// + +#if PRECISE_COVERAGE != 0 + + // Rotate the triangle in the winding order until v0 is the vertex with lowest Y value + SortVertices(ipVtxX, ipVtxY); + + // Compute edges + __mwi edgeX[3] = { _mmw_sub_epi32(ipVtxX[1], ipVtxX[0]), _mmw_sub_epi32(ipVtxX[2], ipVtxX[1]), _mmw_sub_epi32(ipVtxX[2], ipVtxX[0]) }; + __mwi edgeY[3] = { _mmw_sub_epi32(ipVtxY[1], ipVtxY[0]), _mmw_sub_epi32(ipVtxY[2], ipVtxY[1]), _mmw_sub_epi32(ipVtxY[2], ipVtxY[0]) }; + + // Classify if the middle vertex is on the left or right and compute its position + int midVtxRight = ~_mmw_movemask_ps(simd_cast<__mw>(edgeY[1])); + __mwi midPixelX = _mmw_blendv_epi32(ipVtxX[1], ipVtxX[2], edgeY[1]); + __mwi midPixelY = _mmw_blendv_epi32(ipVtxY[1], ipVtxY[2], edgeY[1]); + __mwi midTileY = _mmw_srai_epi32(_mmw_max_epi32(midPixelY, SIMD_BITS_ZERO), TILE_HEIGHT_SHIFT + FP_BITS); + __mwi bbMidTileY = _mmw_max_epi32(bbTileMinY, _mmw_min_epi32(bbTileMaxY, midTileY)); + + // Compute edge events for the bottom of the bounding box, or for the middle tile in case of + // the edge originating from the middle vertex. + __mwi xDiffi[2], yDiffi[2]; + xDiffi[0] = _mmw_sub_epi32(ipVtxX[0], _mmw_slli_epi32(bbPixelMinX, FP_BITS)); + xDiffi[1] = _mmw_sub_epi32(midPixelX, _mmw_slli_epi32(bbPixelMinX, FP_BITS)); + yDiffi[0] = _mmw_sub_epi32(ipVtxY[0], _mmw_slli_epi32(bbPixelMinY, FP_BITS)); + yDiffi[1] = _mmw_sub_epi32(midPixelY, _mmw_slli_epi32(bbMidTileY, FP_BITS + TILE_HEIGHT_SHIFT)); + + ////////////////////////////////////////////////////////////////////////////// + // Edge slope setup - Note we do not conform to DX/GL rasterization rules + ////////////////////////////////////////////////////////////////////////////// + + // Potentially flip edge to ensure that all edges have positive Y slope. + edgeX[1] = _mmw_blendv_epi32(edgeX[1], _mmw_neg_epi32(edgeX[1]), edgeY[1]); + edgeY[1] = _mmw_abs_epi32(edgeY[1]); + + // Compute floating point slopes + __mw slope[3]; + slope[0] = _mmw_div_ps(_mmw_cvtepi32_ps(edgeX[0]), _mmw_cvtepi32_ps(edgeY[0])); + slope[1] = _mmw_div_ps(_mmw_cvtepi32_ps(edgeX[1]), _mmw_cvtepi32_ps(edgeY[1])); + slope[2] = _mmw_div_ps(_mmw_cvtepi32_ps(edgeX[2]), _mmw_cvtepi32_ps(edgeY[2])); + + // Modify slope of horizontal edges to make sure they mask out pixels above/below the edge. The slope is set to screen + // width to mask out all pixels above or below the horizontal edge. We must also add a small bias to acount for that + // vertices may end up off screen due to clipping. We're assuming that the round off error is no bigger than 1.0 + __mw horizontalSlopeDelta = _mmw_set1_ps(2.0f * ((float)mWidth + 2.0f*(GUARD_BAND_PIXEL_SIZE + 1.0f))); + __mwi horizontalSlope0 = _mmw_cmpeq_epi32(edgeY[0], _mmw_setzero_epi32()); + __mwi horizontalSlope1 = _mmw_cmpeq_epi32(edgeY[1], _mmw_setzero_epi32()); + slope[0] = _mmw_blendv_ps(slope[0], horizontalSlopeDelta, simd_cast<__mw>(horizontalSlope0)); + slope[1] = _mmw_blendv_ps(slope[1], _mmw_neg_ps(horizontalSlopeDelta), simd_cast<__mw>(horizontalSlope1)); + + __mwi vy[3] = { yDiffi[0], yDiffi[1], yDiffi[0] }; + __mwi offset0 = _mmw_and_epi32(_mmw_add_epi32(yDiffi[0], _mmw_set1_epi32(FP_HALF_PIXEL - 1)), _mmw_set1_epi32((int)((~0u) << FP_BITS))); + __mwi offset1 = _mmw_and_epi32(_mmw_add_epi32(yDiffi[1], _mmw_set1_epi32(FP_HALF_PIXEL - 1)), _mmw_set1_epi32((int)((~0u) << FP_BITS))); + vy[0] = _mmw_blendv_epi32(yDiffi[0], offset0, horizontalSlope0); + vy[1] = _mmw_blendv_epi32(yDiffi[1], offset1, horizontalSlope1); + + // Compute edge events for the bottom of the bounding box, or for the middle tile in case of + // the edge originating from the middle vertex. + __mwi slopeSign[3], absEdgeX[3]; + __mwi slopeTileDelta[3], eventStartRemainder[3], slopeTileRemainder[3], eventStart[3]; + for (int i = 0; i < 3; i++) + { + // Common, compute slope sign (used to propagate the remainder term when overflowing) is postive or negative x-direction + slopeSign[i] = _mmw_blendv_epi32(_mmw_set1_epi32(1), _mmw_set1_epi32(-1), edgeX[i]); + absEdgeX[i] = _mmw_abs_epi32(edgeX[i]); + + // Delta and error term for one vertical tile step. The exact delta is exactDelta = edgeX / edgeY, due to limited precision we + // repersent the delta as delta = qoutient + remainder / edgeY, where quotient = int(edgeX / edgeY). In this case, since we step + // one tile of scanlines at a time, the slope is computed for a tile-sized step. + slopeTileDelta[i] = _mmw_cvttps_epi32(_mmw_mul_ps(slope[i], _mmw_set1_ps(FP_TILE_HEIGHT))); + slopeTileRemainder[i] = _mmw_sub_epi32(_mmw_slli_epi32(absEdgeX[i], FP_TILE_HEIGHT_SHIFT), _mmw_mullo_epi32(_mmw_abs_epi32(slopeTileDelta[i]), edgeY[i])); + + // Jump to bottom scanline of tile row, this is the bottom of the bounding box, or the middle vertex of the triangle. + // The jump can be in both positive and negative y-direction due to clipping / offscreen vertices. + __mwi tileStartDir = _mmw_blendv_epi32(slopeSign[i], _mmw_neg_epi32(slopeSign[i]), vy[i]); + __mwi tieBreaker = _mmw_blendv_epi32(_mmw_set1_epi32(0), _mmw_set1_epi32(1), tileStartDir); + __mwi tileStartSlope = _mmw_cvttps_epi32(_mmw_mul_ps(slope[i], _mmw_cvtepi32_ps(_mmw_neg_epi32(vy[i])))); + __mwi tileStartRemainder = _mmw_sub_epi32(_mmw_mullo_epi32(absEdgeX[i], _mmw_abs_epi32(vy[i])), _mmw_mullo_epi32(_mmw_abs_epi32(tileStartSlope), edgeY[i])); + + eventStartRemainder[i] = _mmw_sub_epi32(tileStartRemainder, tieBreaker); + __mwi overflow = _mmw_srai_epi32(eventStartRemainder[i], 31); + eventStartRemainder[i] = _mmw_add_epi32(eventStartRemainder[i], _mmw_and_epi32(overflow, edgeY[i])); + eventStartRemainder[i] = _mmw_blendv_epi32(eventStartRemainder[i], _mmw_sub_epi32(_mmw_sub_epi32(edgeY[i], eventStartRemainder[i]), _mmw_set1_epi32(1)), vy[i]); + + //eventStart[i] = xDiffi[i & 1] + tileStartSlope + (overflow & tileStartDir) + _mmw_set1_epi32(FP_HALF_PIXEL - 1) + tieBreaker; + eventStart[i] = _mmw_add_epi32(_mmw_add_epi32(xDiffi[i & 1], tileStartSlope), _mmw_and_epi32(overflow, tileStartDir)); + eventStart[i] = _mmw_add_epi32(_mmw_add_epi32(eventStart[i], _mmw_set1_epi32(FP_HALF_PIXEL - 1)), tieBreaker); + } + +#else // PRECISE_COVERAGE + + SortVertices(pVtxX, pVtxY); + + // Compute edges + __mw edgeX[3] = { _mmw_sub_ps(pVtxX[1], pVtxX[0]), _mmw_sub_ps(pVtxX[2], pVtxX[1]), _mmw_sub_ps(pVtxX[2], pVtxX[0]) }; + __mw edgeY[3] = { _mmw_sub_ps(pVtxY[1], pVtxY[0]), _mmw_sub_ps(pVtxY[2], pVtxY[1]), _mmw_sub_ps(pVtxY[2], pVtxY[0]) }; + + // Classify if the middle vertex is on the left or right and compute its position + int midVtxRight = ~_mmw_movemask_ps(edgeY[1]); + __mw midPixelX = _mmw_blendv_ps(pVtxX[1], pVtxX[2], edgeY[1]); + __mw midPixelY = _mmw_blendv_ps(pVtxY[1], pVtxY[2], edgeY[1]); + __mwi midTileY = _mmw_srai_epi32(_mmw_max_epi32(_mmw_cvttps_epi32(midPixelY), SIMD_BITS_ZERO), TILE_HEIGHT_SHIFT); + __mwi bbMidTileY = _mmw_max_epi32(bbTileMinY, _mmw_min_epi32(bbTileMaxY, midTileY)); + + ////////////////////////////////////////////////////////////////////////////// + // Edge slope setup - Note we do not conform to DX/GL rasterization rules + ////////////////////////////////////////////////////////////////////////////// + + // Compute floating point slopes + __mw slope[3]; + slope[0] = _mmw_div_ps(edgeX[0], edgeY[0]); + slope[1] = _mmw_div_ps(edgeX[1], edgeY[1]); + slope[2] = _mmw_div_ps(edgeX[2], edgeY[2]); + + // Modify slope of horizontal edges to make sure they mask out pixels above/below the edge. The slope is set to screen + // width to mask out all pixels above or below the horizontal edge. We must also add a small bias to acount for that + // vertices may end up off screen due to clipping. We're assuming that the round off error is no bigger than 1.0 + __mw horizontalSlopeDelta = _mmw_set1_ps((float)mWidth + 2.0f*(GUARD_BAND_PIXEL_SIZE + 1.0f)); + slope[0] = _mmw_blendv_ps(slope[0], horizontalSlopeDelta, _mmw_cmpeq_ps(edgeY[0], _mmw_setzero_ps())); + slope[1] = _mmw_blendv_ps(slope[1], _mmw_neg_ps(horizontalSlopeDelta), _mmw_cmpeq_ps(edgeY[1], _mmw_setzero_ps())); + + // Convert floaing point slopes to fixed point + __mwi slopeFP[3]; + slopeFP[0] = _mmw_cvttps_epi32(_mmw_mul_ps(slope[0], _mmw_set1_ps(1 << FP_BITS))); + slopeFP[1] = _mmw_cvttps_epi32(_mmw_mul_ps(slope[1], _mmw_set1_ps(1 << FP_BITS))); + slopeFP[2] = _mmw_cvttps_epi32(_mmw_mul_ps(slope[2], _mmw_set1_ps(1 << FP_BITS))); + + // Fan out edge slopes to avoid (rare) cracks at vertices. We increase right facing slopes + // by 1 LSB, which results in overshooting vertices slightly, increasing triangle coverage. + // e0 is always right facing, e1 depends on if the middle vertex is on the left or right + slopeFP[0] = _mmw_add_epi32(slopeFP[0], _mmw_set1_epi32(1)); + slopeFP[1] = _mmw_add_epi32(slopeFP[1], _mmw_srli_epi32(_mmw_not_epi32(simd_cast<__mwi>(edgeY[1])), 31)); + + // Compute slope deltas for an SIMD_LANES scanline step (tile height) + __mwi slopeTileDelta[3]; + slopeTileDelta[0] = _mmw_slli_epi32(slopeFP[0], TILE_HEIGHT_SHIFT); + slopeTileDelta[1] = _mmw_slli_epi32(slopeFP[1], TILE_HEIGHT_SHIFT); + slopeTileDelta[2] = _mmw_slli_epi32(slopeFP[2], TILE_HEIGHT_SHIFT); + + // Compute edge events for the bottom of the bounding box, or for the middle tile in case of + // the edge originating from the middle vertex. + __mwi xDiffi[2], yDiffi[2]; + xDiffi[0] = _mmw_slli_epi32(_mmw_sub_epi32(_mmw_cvttps_epi32(pVtxX[0]), bbPixelMinX), FP_BITS); + xDiffi[1] = _mmw_slli_epi32(_mmw_sub_epi32(_mmw_cvttps_epi32(midPixelX), bbPixelMinX), FP_BITS); + yDiffi[0] = _mmw_sub_epi32(_mmw_cvttps_epi32(pVtxY[0]), bbPixelMinY); + yDiffi[1] = _mmw_sub_epi32(_mmw_cvttps_epi32(midPixelY), _mmw_slli_epi32(bbMidTileY, TILE_HEIGHT_SHIFT)); + + __mwi eventStart[3]; + eventStart[0] = _mmw_sub_epi32(xDiffi[0], _mmw_mullo_epi32(slopeFP[0], yDiffi[0])); + eventStart[1] = _mmw_sub_epi32(xDiffi[1], _mmw_mullo_epi32(slopeFP[1], yDiffi[1])); + eventStart[2] = _mmw_sub_epi32(xDiffi[0], _mmw_mullo_epi32(slopeFP[2], yDiffi[0])); +#endif + + ////////////////////////////////////////////////////////////////////////////// + // Split bounding box into bottom - middle - top region. + ////////////////////////////////////////////////////////////////////////////// + + __mwi bbBottomIdx = _mmw_add_epi32(bbTileMinX, _mmw_mullo_epi32(bbTileMinY, _mmw_set1_epi32(mTilesWidth))); + __mwi bbTopIdx = _mmw_add_epi32(bbTileMinX, _mmw_mullo_epi32(_mmw_add_epi32(bbTileMinY, bbTileSizeY), _mmw_set1_epi32(mTilesWidth))); + __mwi bbMidIdx = _mmw_add_epi32(bbTileMinX, _mmw_mullo_epi32(midTileY, _mmw_set1_epi32(mTilesWidth))); + + ////////////////////////////////////////////////////////////////////////////// + // Loop over non-culled triangle and change SIMD axis to per-pixel + ////////////////////////////////////////////////////////////////////////////// + while (triMask) + { + unsigned int triIdx = find_clear_lsb(&triMask); + int triMidVtxRight = (midVtxRight >> triIdx) & 1; + + // Get Triangle Zmin zMax + __mw zTriMax = _mmw_set1_ps(simd_f32(zMax)[triIdx]); + __mw zTriMin = _mmw_set1_ps(simd_f32(zMin)[triIdx]); + + // Setup Zmin value for first set of 8x4 subtiles + __mw z0 = _mmw_fmadd_ps(_mmw_set1_ps(simd_f32(zPixelDx)[triIdx]), SIMD_SUB_TILE_COL_OFFSET_F, + _mmw_fmadd_ps(_mmw_set1_ps(simd_f32(zPixelDy)[triIdx]), SIMD_SUB_TILE_ROW_OFFSET_F, _mmw_set1_ps(simd_f32(zPlaneOffset)[triIdx]))); + float zx = simd_f32(zTileDx)[triIdx]; + float zy = simd_f32(zTileDy)[triIdx]; + + // Get dimension of bounding box bottom, mid & top segments + int bbWidth = simd_i32(bbTileSizeX)[triIdx]; + int bbHeight = simd_i32(bbTileSizeY)[triIdx]; + int tileRowIdx = simd_i32(bbBottomIdx)[triIdx]; + int tileMidRowIdx = simd_i32(bbMidIdx)[triIdx]; + int tileEndRowIdx = simd_i32(bbTopIdx)[triIdx]; + + if (bbWidth > BIG_TRIANGLE && bbHeight > BIG_TRIANGLE) // For big triangles we use a more expensive but tighter traversal algorithm + { +#if PRECISE_COVERAGE != 0 + if (triMidVtxRight) + cullResult &= RasterizeTriangle(triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slope, slopeTileDelta, zTriMin, zTriMax, z0, zx, zy, edgeY, absEdgeX, slopeSign, eventStartRemainder, slopeTileRemainder); + else + cullResult &= RasterizeTriangle(triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slope, slopeTileDelta, zTriMin, zTriMax, z0, zx, zy, edgeY, absEdgeX, slopeSign, eventStartRemainder, slopeTileRemainder); +#else + if (triMidVtxRight) + cullResult &= RasterizeTriangle(triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slopeFP, slopeTileDelta, zTriMin, zTriMax, z0, zx, zy); + else + cullResult &= RasterizeTriangle(triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slopeFP, slopeTileDelta, zTriMin, zTriMax, z0, zx, zy); +#endif + } + else + { +#if PRECISE_COVERAGE != 0 + if (triMidVtxRight) + cullResult &= RasterizeTriangle(triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slope, slopeTileDelta, zTriMin, zTriMax, z0, zx, zy, edgeY, absEdgeX, slopeSign, eventStartRemainder, slopeTileRemainder); + else + cullResult &= RasterizeTriangle(triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slope, slopeTileDelta, zTriMin, zTriMax, z0, zx, zy, edgeY, absEdgeX, slopeSign, eventStartRemainder, slopeTileRemainder); +#else + if (triMidVtxRight) + cullResult &= RasterizeTriangle(triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slopeFP, slopeTileDelta, zTriMin, zTriMax, z0, zx, zy); + else + cullResult &= RasterizeTriangle(triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slopeFP, slopeTileDelta, zTriMin, zTriMax, z0, zx, zy); +#endif + } + + if (TEST_Z && cullResult == CullingResult::VISIBLE) + return CullingResult::VISIBLE; + } + + return cullResult; + } + + template + FORCE_INLINE CullingResult RenderTriangles(const float *inVtx, const unsigned int *inTris, int nTris, const float *modelToClipMatrix, BackfaceWinding bfWinding, ClipPlanes clipPlaneMask, const VertexLayout &vtxLayout) + { + assert(mMaskedHiZBuffer != nullptr); + + if (TEST_Z) + STATS_ADD(mStats.mOccludees.mNumProcessedTriangles, nTris); + else + STATS_ADD(mStats.mOccluders.mNumProcessedTriangles, nTris); + +#if PRECISE_COVERAGE != 0 + int originalRoundingMode = _MM_GET_ROUNDING_MODE(); + _MM_SET_ROUNDING_MODE(_MM_ROUND_NEAREST); +#endif + + int clipHead = 0; + int clipTail = 0; + __m128 clipTriBuffer[MAX_CLIPPED * 3]; + int cullResult = CullingResult::VIEW_CULLED; + + const unsigned int *inTrisPtr = inTris; + int numLanes = SIMD_LANES; + int triIndex = 0; + while (triIndex < nTris || clipHead != clipTail) + { + __mw vtxX[3], vtxY[3], vtxW[3]; + unsigned int triMask = SIMD_ALL_LANES_MASK; + + GatherTransformClip( clipHead, clipTail, numLanes, nTris, triIndex, vtxX, vtxY, vtxW, inVtx, inTrisPtr, vtxLayout, modelToClipMatrix, clipTriBuffer, triMask, clipPlaneMask ); + + if (triMask == 0x0) + continue; + + ////////////////////////////////////////////////////////////////////////////// + // Project, transform to screen space and perform backface culling. Note + // that we use z = 1.0 / vtx.w for depth, which means that z = 0 is far and + // z = 1 is near. We must also use a greater than depth test, and in effect + // everything is reversed compared to regular z implementations. + ////////////////////////////////////////////////////////////////////////////// + + __mw pVtxX[3], pVtxY[3], pVtxZ[3]; + +#if PRECISE_COVERAGE != 0 + __mwi ipVtxX[3], ipVtxY[3]; + ProjectVertices(ipVtxX, ipVtxY, pVtxX, pVtxY, pVtxZ, vtxX, vtxY, vtxW); +#else + ProjectVertices(pVtxX, pVtxY, pVtxZ, vtxX, vtxY, vtxW); +#endif + + // Perform backface test. + __mw triArea1 = _mmw_mul_ps(_mmw_sub_ps(pVtxX[1], pVtxX[0]), _mmw_sub_ps(pVtxY[2], pVtxY[0])); + __mw triArea2 = _mmw_mul_ps(_mmw_sub_ps(pVtxX[0], pVtxX[2]), _mmw_sub_ps(pVtxY[0], pVtxY[1])); + __mw triArea = _mmw_sub_ps(triArea1, triArea2); + __mw ccwMask = _mmw_cmpgt_ps(triArea, _mmw_setzero_ps()); + +#if PRECISE_COVERAGE != 0 + triMask &= CullBackfaces(ipVtxX, ipVtxY, pVtxX, pVtxY, pVtxZ, ccwMask, bfWinding); +#else + triMask &= CullBackfaces(pVtxX, pVtxY, pVtxZ, ccwMask, bfWinding); +#endif + + if (triMask == 0x0) + continue; + + ////////////////////////////////////////////////////////////////////////////// + // Setup and rasterize a SIMD batch of triangles + ////////////////////////////////////////////////////////////////////////////// +#if PRECISE_COVERAGE != 0 + cullResult &= RasterizeTriangleBatch(ipVtxX, ipVtxY, pVtxX, pVtxY, pVtxZ, triMask, &mFullscreenScissor); +#else + cullResult &= RasterizeTriangleBatch(pVtxX, pVtxY, pVtxZ, triMask, &mFullscreenScissor); +#endif + + if (TEST_Z && cullResult == CullingResult::VISIBLE) { +#if PRECISE_COVERAGE != 0 + _MM_SET_ROUNDING_MODE(originalRoundingMode); +#endif + return CullingResult::VISIBLE; + } + } + +#if PRECISE_COVERAGE != 0 + _MM_SET_ROUNDING_MODE(originalRoundingMode); +#endif + return (CullingResult)cullResult; + } + + CullingResult RenderTriangles(const float *inVtx, const unsigned int *inTris, int nTris, const float *modelToClipMatrix, BackfaceWinding bfWinding, ClipPlanes clipPlaneMask, const VertexLayout &vtxLayout) override + { + CullingResult retVal; + + if (vtxLayout.mStride == 16 && vtxLayout.mOffsetY == 4 && vtxLayout.mOffsetW == 12) + retVal = (CullingResult)RenderTriangles<0, 1>(inVtx, inTris, nTris, modelToClipMatrix, bfWinding, clipPlaneMask, vtxLayout); + else + retVal = (CullingResult)RenderTriangles<0, 0>(inVtx, inTris, nTris, modelToClipMatrix, bfWinding, clipPlaneMask, vtxLayout); + +#if MOC_RECORDER_ENABLE + RecordRenderTriangles( inVtx, inTris, nTris, modelToClipMatrix, clipPlaneMask, bfWinding, vtxLayout, retVal ); +#endif + return retVal; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Occlusion query functions + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + CullingResult TestTriangles(const float *inVtx, const unsigned int *inTris, int nTris, const float *modelToClipMatrix, BackfaceWinding bfWinding, ClipPlanes clipPlaneMask, const VertexLayout &vtxLayout) override + { + CullingResult retVal; + + if (vtxLayout.mStride == 16 && vtxLayout.mOffsetY == 4 && vtxLayout.mOffsetW == 12) + retVal = (CullingResult)RenderTriangles<1, 1>(inVtx, inTris, nTris, modelToClipMatrix, bfWinding, clipPlaneMask, vtxLayout); + else + retVal = (CullingResult)RenderTriangles<1, 0>(inVtx, inTris, nTris, modelToClipMatrix, bfWinding, clipPlaneMask, vtxLayout); + +#if MOC_RECORDER_ENABLE + { + std::lock_guard lock( mRecorderMutex ); + if( mRecorder != nullptr ) mRecorder->RecordTestTriangles( retVal, inVtx, inTris, nTris, modelToClipMatrix, clipPlaneMask, bfWinding, vtxLayout ); + } +#endif + return retVal; + } + + CullingResult TestRect( float xmin, float ymin, float xmax, float ymax, float wmin ) const override + { + STATS_ADD(mStats.mOccludees.mNumProcessedRectangles, 1); + assert(mMaskedHiZBuffer != nullptr); + + static const __m128i SIMD_TILE_PAD = _mm_setr_epi32(0, TILE_WIDTH, 0, TILE_HEIGHT); + static const __m128i SIMD_TILE_PAD_MASK = _mm_setr_epi32(~(TILE_WIDTH - 1), ~(TILE_WIDTH - 1), ~(TILE_HEIGHT - 1), ~(TILE_HEIGHT - 1)); + static const __m128i SIMD_SUB_TILE_PAD = _mm_setr_epi32(0, SUB_TILE_WIDTH, 0, SUB_TILE_HEIGHT); + static const __m128i SIMD_SUB_TILE_PAD_MASK = _mm_setr_epi32(~(SUB_TILE_WIDTH - 1), ~(SUB_TILE_WIDTH - 1), ~(SUB_TILE_HEIGHT - 1), ~(SUB_TILE_HEIGHT - 1)); + + ////////////////////////////////////////////////////////////////////////////// + // Compute screen space bounding box and guard for out of bounds + ////////////////////////////////////////////////////////////////////////////// +#if USE_D3D != 0 + __m128 pixelBBox = _mmx_fmadd_ps(_mm_setr_ps(xmin, xmax, ymax, ymin), mIHalfSize, mICenter); +#else + __m128 pixelBBox = _mmx_fmadd_ps(_mm_setr_ps(xmin, xmax, ymin, ymax), mIHalfSize, mICenter); +#endif + __m128i pixelBBoxi = _mm_cvttps_epi32(pixelBBox); + pixelBBoxi = _mmx_max_epi32(_mm_setzero_si128(), _mmx_min_epi32(mIScreenSize, pixelBBoxi)); + + ////////////////////////////////////////////////////////////////////////////// + // Pad bounding box to (32xN) tiles. Tile BB is used for looping / traversal + ////////////////////////////////////////////////////////////////////////////// + __m128i tileBBoxi = _mm_and_si128(_mm_add_epi32(pixelBBoxi, SIMD_TILE_PAD), SIMD_TILE_PAD_MASK); + int txMin = simd_i32(tileBBoxi)[0] >> TILE_WIDTH_SHIFT; + int txMax = simd_i32(tileBBoxi)[1] >> TILE_WIDTH_SHIFT; + int tileRowIdx = (simd_i32(tileBBoxi)[2] >> TILE_HEIGHT_SHIFT)*mTilesWidth; + int tileRowIdxEnd = (simd_i32(tileBBoxi)[3] >> TILE_HEIGHT_SHIFT)*mTilesWidth; + + if (simd_i32(tileBBoxi)[0] == simd_i32(tileBBoxi)[1] || simd_i32(tileBBoxi)[2] == simd_i32(tileBBoxi)[3]) + { +#if MOC_RECORDER_ENABLE + { + std::lock_guard lock( mRecorderMutex ); + if( mRecorder != nullptr ) mRecorder->RecordTestRect( CullingResult::VIEW_CULLED, xmin, ymin, xmax, ymax, wmin ); + } +#endif + return CullingResult::VIEW_CULLED; + } + + /////////////////////////////////////////////////////////////////////////////// + // Pad bounding box to (8x4) subtiles. Skip SIMD lanes outside the subtile BB + /////////////////////////////////////////////////////////////////////////////// + __m128i subTileBBoxi = _mm_and_si128(_mm_add_epi32(pixelBBoxi, SIMD_SUB_TILE_PAD), SIMD_SUB_TILE_PAD_MASK); + __mwi stxmin = _mmw_set1_epi32(simd_i32(subTileBBoxi)[0] - 1); // - 1 to be able to use GT test + __mwi stymin = _mmw_set1_epi32(simd_i32(subTileBBoxi)[2] - 1); // - 1 to be able to use GT test + __mwi stxmax = _mmw_set1_epi32(simd_i32(subTileBBoxi)[1]); + __mwi stymax = _mmw_set1_epi32(simd_i32(subTileBBoxi)[3]); + + // Setup pixel coordinates used to discard lanes outside subtile BB + __mwi startPixelX = _mmw_add_epi32(SIMD_SUB_TILE_COL_OFFSET, _mmw_set1_epi32(simd_i32(tileBBoxi)[0])); + __mwi pixelY = _mmw_add_epi32(SIMD_SUB_TILE_ROW_OFFSET, _mmw_set1_epi32(simd_i32(tileBBoxi)[2])); + + ////////////////////////////////////////////////////////////////////////////// + // Compute z from w. Note that z is reversed order, 0 = far, 1 = near, which + // means we use a greater than test, so zMax is used to test for visibility. + ////////////////////////////////////////////////////////////////////////////// + __mw zMax = _mmw_div_ps(_mmw_set1_ps(1.0f), _mmw_set1_ps(wmin)); + + for (;;) + { + __mwi pixelX = startPixelX; + for (int tx = txMin;;) + { + STATS_ADD(mStats.mOccludees.mNumTilesTraversed, 1); + + int tileIdx = tileRowIdx + tx; + assert(tileIdx >= 0 && tileIdx < mTilesWidth*mTilesHeight); + + // Fetch zMin from masked hierarchical Z buffer +#if QUICK_MASK != 0 + __mw zBuf = mMaskedHiZBuffer[tileIdx].mZMin[0]; +#else + __mwi mask = mMaskedHiZBuffer[tileIdx].mMask; + __mw zMin0 = _mmw_blendv_ps(mMaskedHiZBuffer[tileIdx].mZMin[0], mMaskedHiZBuffer[tileIdx].mZMin[1], simd_cast<__mw>(_mmw_cmpeq_epi32(mask, _mmw_set1_epi32(~0)))); + __mw zMin1 = _mmw_blendv_ps(mMaskedHiZBuffer[tileIdx].mZMin[1], mMaskedHiZBuffer[tileIdx].mZMin[0], simd_cast<__mw>(_mmw_cmpeq_epi32(mask, _mmw_setzero_epi32()))); + __mw zBuf = _mmw_min_ps(zMin0, zMin1); +#endif + // Perform conservative greater than test against hierarchical Z buffer (zMax >= zBuf means the subtile is visible) + __mwi zPass = simd_cast<__mwi>(_mmw_cmpge_ps(zMax, zBuf)); //zPass = zMax >= zBuf ? ~0 : 0 + + // Mask out lanes corresponding to subtiles outside the bounding box + __mwi bboxTestMin = _mmw_and_epi32(_mmw_cmpgt_epi32(pixelX, stxmin), _mmw_cmpgt_epi32(pixelY, stymin)); + __mwi bboxTestMax = _mmw_and_epi32(_mmw_cmpgt_epi32(stxmax, pixelX), _mmw_cmpgt_epi32(stymax, pixelY)); + __mwi boxMask = _mmw_and_epi32(bboxTestMin, bboxTestMax); + zPass = _mmw_and_epi32(zPass, boxMask); + + // If not all tiles failed the conservative z test we can immediately terminate the test + if (!_mmw_testz_epi32(zPass, zPass)) + { +#if MOC_RECORDER_ENABLE + { + std::lock_guard lock( mRecorderMutex ); + if( mRecorder != nullptr ) mRecorder->RecordTestRect( CullingResult::VISIBLE, xmin, ymin, xmax, ymax, wmin ); + } +#endif + return CullingResult::VISIBLE; + } + + if (++tx >= txMax) + break; + pixelX = _mmw_add_epi32(pixelX, _mmw_set1_epi32(TILE_WIDTH)); + } + + tileRowIdx += mTilesWidth; + if (tileRowIdx >= tileRowIdxEnd) + break; + pixelY = _mmw_add_epi32(pixelY, _mmw_set1_epi32(TILE_HEIGHT)); + } +#if MOC_RECORDER_ENABLE + { + std::lock_guard lock( mRecorderMutex ); + if( mRecorder != nullptr ) mRecorder->RecordTestRect( CullingResult::OCCLUDED, xmin, ymin, xmax, ymax, wmin ); + } +#endif + return CullingResult::OCCLUDED; + } + + template + FORCE_INLINE void BinTriangles(const float *inVtx, const unsigned int *inTris, int nTris, TriList *triLists, unsigned int nBinsW, unsigned int nBinsH, const float *modelToClipMatrix, BackfaceWinding bfWinding, ClipPlanes clipPlaneMask, const VertexLayout &vtxLayout) + { + assert(mMaskedHiZBuffer != nullptr); + +#if PRECISE_COVERAGE != 0 + int originalRoundingMode = _MM_GET_ROUNDING_MODE(); + _MM_SET_ROUNDING_MODE(_MM_ROUND_NEAREST); +#endif + + STATS_ADD(mStats.mOccluders.mNumProcessedTriangles, nTris); + + int clipHead = 0; + int clipTail = 0; + __m128 clipTriBuffer[MAX_CLIPPED * 3]; + + const unsigned int *inTrisPtr = inTris; + int numLanes = SIMD_LANES; + int triIndex = 0; + while (triIndex < nTris || clipHead != clipTail) + { + unsigned int triMask = SIMD_ALL_LANES_MASK; + __mw vtxX[3], vtxY[3], vtxW[3]; + + GatherTransformClip( clipHead, clipTail, numLanes, nTris, triIndex, vtxX, vtxY, vtxW, inVtx, inTrisPtr, vtxLayout, modelToClipMatrix, clipTriBuffer, triMask, clipPlaneMask ); + + if (triMask == 0x0) + continue; + + ////////////////////////////////////////////////////////////////////////////// + // Project, transform to screen space and perform backface culling. Note + // that we use z = 1.0 / vtx.w for depth, which means that z = 0 is far and + // z = 1 is near. We must also use a greater than depth test, and in effect + // everything is reversed compared to regular z implementations. + ////////////////////////////////////////////////////////////////////////////// + + __mw pVtxX[3], pVtxY[3], pVtxZ[3]; + +#if PRECISE_COVERAGE != 0 + __mwi ipVtxX[3], ipVtxY[3]; + ProjectVertices(ipVtxX, ipVtxY, pVtxX, pVtxY, pVtxZ, vtxX, vtxY, vtxW); +#else + ProjectVertices(pVtxX, pVtxY, pVtxZ, vtxX, vtxY, vtxW); +#endif + + // Perform backface test. + __mw triArea1 = _mmw_mul_ps(_mmw_sub_ps(pVtxX[1], pVtxX[0]), _mmw_sub_ps(pVtxY[2], pVtxY[0])); + __mw triArea2 = _mmw_mul_ps(_mmw_sub_ps(pVtxX[0], pVtxX[2]), _mmw_sub_ps(pVtxY[0], pVtxY[1])); + __mw triArea = _mmw_sub_ps(triArea1, triArea2); + __mw ccwMask = _mmw_cmpgt_ps(triArea, _mmw_setzero_ps()); + +#if PRECISE_COVERAGE != 0 + triMask &= CullBackfaces(ipVtxX, ipVtxY, pVtxX, pVtxY, pVtxZ, ccwMask, bfWinding); +#else + triMask &= CullBackfaces(pVtxX, pVtxY, pVtxZ, ccwMask, bfWinding); +#endif + + if (triMask == 0x0) + continue; + + ////////////////////////////////////////////////////////////////////////////// + // Bin triangles + ////////////////////////////////////////////////////////////////////////////// + + unsigned int binWidth; + unsigned int binHeight; + ComputeBinWidthHeight(nBinsW, nBinsH, binWidth, binHeight); + + // Compute pixel bounding box + __mwi bbPixelMinX, bbPixelMinY, bbPixelMaxX, bbPixelMaxY; + ComputeBoundingBox(bbPixelMinX, bbPixelMinY, bbPixelMaxX, bbPixelMaxY, pVtxX, pVtxY, &mFullscreenScissor); + + while (triMask) + { + unsigned int triIdx = find_clear_lsb(&triMask); + + // Clamp bounding box to bins + int startX = min(nBinsW-1, simd_i32(bbPixelMinX)[triIdx] / binWidth); + int startY = min(nBinsH-1, simd_i32(bbPixelMinY)[triIdx] / binHeight); + int endX = min(nBinsW, (simd_i32(bbPixelMaxX)[triIdx] + binWidth - 1) / binWidth); + int endY = min(nBinsH, (simd_i32(bbPixelMaxY)[triIdx] + binHeight - 1) / binHeight); + + for (int y = startY; y < endY; ++y) + { + for (int x = startX; x < endX; ++x) + { + int binIdx = x + y * nBinsW; + unsigned int writeTriIdx = triLists[binIdx].mTriIdx; + for (int i = 0; i < 3; ++i) + { +#if PRECISE_COVERAGE != 0 + ((int*)triLists[binIdx].mPtr)[i * 3 + writeTriIdx * 9 + 0] = simd_i32(ipVtxX[i])[triIdx]; + ((int*)triLists[binIdx].mPtr)[i * 3 + writeTriIdx * 9 + 1] = simd_i32(ipVtxY[i])[triIdx]; +#else + triLists[binIdx].mPtr[i * 3 + writeTriIdx * 9 + 0] = simd_f32(pVtxX[i])[triIdx]; + triLists[binIdx].mPtr[i * 3 + writeTriIdx * 9 + 1] = simd_f32(pVtxY[i])[triIdx]; +#endif + triLists[binIdx].mPtr[i * 3 + writeTriIdx * 9 + 2] = simd_f32(pVtxZ[i])[triIdx]; + } + triLists[binIdx].mTriIdx++; + } + } + } + } +#if PRECISE_COVERAGE != 0 + _MM_SET_ROUNDING_MODE(originalRoundingMode); +#endif + } + + void BinTriangles(const float *inVtx, const unsigned int *inTris, int nTris, TriList *triLists, unsigned int nBinsW, unsigned int nBinsH, const float *modelToClipMatrix, BackfaceWinding bfWinding, ClipPlanes clipPlaneMask, const VertexLayout &vtxLayout) override + { + if (vtxLayout.mStride == 16 && vtxLayout.mOffsetY == 4 && vtxLayout.mOffsetW == 12) + BinTriangles(inVtx, inTris, nTris, triLists, nBinsW, nBinsH, modelToClipMatrix, bfWinding, clipPlaneMask, vtxLayout); + else + BinTriangles(inVtx, inTris, nTris, triLists, nBinsW, nBinsH, modelToClipMatrix, bfWinding, clipPlaneMask, vtxLayout); + } + + template + void GatherTransformClip( int & clipHead, int & clipTail, int & numLanes, int nTris, int & triIndex, __mw * vtxX, __mw * vtxY, __mw * vtxW, const float * inVtx, const unsigned int * &inTrisPtr, const VertexLayout & vtxLayout, const float * modelToClipMatrix, __m128 * clipTriBuffer, unsigned int &triMask, ClipPlanes clipPlaneMask ) + { + ////////////////////////////////////////////////////////////////////////////// + // Assemble triangles from the index list + ////////////////////////////////////////////////////////////////////////////// + unsigned int triClipMask = SIMD_ALL_LANES_MASK; + + if( clipHead != clipTail ) + { + int clippedTris = clipHead > clipTail ? clipHead - clipTail : MAX_CLIPPED + clipHead - clipTail; + clippedTris = min( clippedTris, SIMD_LANES ); + +#if CLIPPING_PRESERVES_ORDER != 0 + // if preserving order, don't mix clipped and new triangles, handle the clip buffer fully + // and then continue gathering; this is not as efficient - ideally we want to gather + // at the end (if clip buffer has less than SIMD_LANES triangles) but that requires + // more modifications below - something to do in the future. + numLanes = 0; +#else + // Fill out SIMD registers by fetching more triangles. + numLanes = max( 0, min( SIMD_LANES - clippedTris, nTris - triIndex ) ); +#endif + + if( numLanes > 0 ) { + if( FAST_GATHER ) + GatherVerticesFast( vtxX, vtxY, vtxW, inVtx, inTrisPtr, numLanes ); + else + GatherVertices( vtxX, vtxY, vtxW, inVtx, inTrisPtr, numLanes, vtxLayout ); + + TransformVerts( vtxX, vtxY, vtxW, modelToClipMatrix ); + } + + for( int clipTri = numLanes; clipTri < numLanes + clippedTris; clipTri++ ) + { + int triIdx = clipTail * 3; + for( int i = 0; i < 3; i++ ) + { + simd_f32( vtxX[i] )[clipTri] = simd_f32( clipTriBuffer[triIdx + i] )[0]; + simd_f32( vtxY[i] )[clipTri] = simd_f32( clipTriBuffer[triIdx + i] )[1]; + simd_f32( vtxW[i] )[clipTri] = simd_f32( clipTriBuffer[triIdx + i] )[2]; + } + clipTail = ( clipTail + 1 ) & ( MAX_CLIPPED - 1 ); + } + + triIndex += numLanes; + inTrisPtr += numLanes * 3; + + triMask = ( 1U << ( clippedTris + numLanes ) ) - 1; + triClipMask = ( 1U << numLanes ) - 1; // Don't re-clip already clipped triangles + } + else + { + numLanes = min( SIMD_LANES, nTris - triIndex ); + triMask = ( 1U << numLanes ) - 1; + triClipMask = triMask; + + if( FAST_GATHER ) + GatherVerticesFast( vtxX, vtxY, vtxW, inVtx, inTrisPtr, numLanes ); + else + GatherVertices( vtxX, vtxY, vtxW, inVtx, inTrisPtr, numLanes, vtxLayout ); + + TransformVerts( vtxX, vtxY, vtxW, modelToClipMatrix ); + + triIndex += SIMD_LANES; + inTrisPtr += SIMD_LANES * 3; + } + + ////////////////////////////////////////////////////////////////////////////// + // Clip transformed triangles + ////////////////////////////////////////////////////////////////////////////// + + if( clipPlaneMask != ClipPlanes::CLIP_PLANE_NONE ) + ClipTriangleAndAddToBuffer( vtxX, vtxY, vtxW, clipTriBuffer, clipHead, triMask, triClipMask, clipPlaneMask ); + } + + void RenderTrilist(const TriList &triList, const ScissorRect *scissor) override + { + assert(mMaskedHiZBuffer != nullptr); + + // Setup fullscreen scissor rect as default + scissor = scissor == nullptr ? &mFullscreenScissor : scissor; + + for (unsigned int i = 0; i < triList.mTriIdx; i += SIMD_LANES) + { + ////////////////////////////////////////////////////////////////////////////// + // Fetch triangle vertices + ////////////////////////////////////////////////////////////////////////////// + + unsigned int numLanes = min((unsigned int)SIMD_LANES, triList.mTriIdx - i); + unsigned int triMask = (1U << numLanes) - 1; + + __mw pVtxX[3], pVtxY[3], pVtxZ[3]; +#if PRECISE_COVERAGE != 0 + __mwi ipVtxX[3], ipVtxY[3]; + for (unsigned int l = 0; l < numLanes; ++l) + { + unsigned int triIdx = i + l; + for (int v = 0; v < 3; ++v) + { + simd_i32(ipVtxX[v])[l] = ((int*)triList.mPtr)[v * 3 + triIdx * 9 + 0]; + simd_i32(ipVtxY[v])[l] = ((int*)triList.mPtr)[v * 3 + triIdx * 9 + 1]; + simd_f32(pVtxZ[v])[l] = triList.mPtr[v * 3 + triIdx * 9 + 2]; + } + } + + for (int v = 0; v < 3; ++v) + { + pVtxX[v] = _mmw_mul_ps(_mmw_cvtepi32_ps(ipVtxX[v]), _mmw_set1_ps(FP_INV)); + pVtxY[v] = _mmw_mul_ps(_mmw_cvtepi32_ps(ipVtxY[v]), _mmw_set1_ps(FP_INV)); + } + + ////////////////////////////////////////////////////////////////////////////// + // Setup and rasterize a SIMD batch of triangles + ////////////////////////////////////////////////////////////////////////////// + + RasterizeTriangleBatch(ipVtxX, ipVtxY, pVtxX, pVtxY, pVtxZ, triMask, scissor); +#else + for (unsigned int l = 0; l < numLanes; ++l) + { + unsigned int triIdx = i + l; + for (int v = 0; v < 3; ++v) + { + simd_f32(pVtxX[v])[l] = triList.mPtr[v * 3 + triIdx * 9 + 0]; + simd_f32(pVtxY[v])[l] = triList.mPtr[v * 3 + triIdx * 9 + 1]; + simd_f32(pVtxZ[v])[l] = triList.mPtr[v * 3 + triIdx * 9 + 2]; + } + } + + ////////////////////////////////////////////////////////////////////////////// + // Setup and rasterize a SIMD batch of triangles + ////////////////////////////////////////////////////////////////////////////// + + RasterizeTriangleBatch(pVtxX, pVtxY, pVtxZ, triMask, scissor); +#endif + + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Debugging and statistics + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + MaskedOcclusionCulling::Implementation GetImplementation() override + { + return gInstructionSet; + } + + void ComputePixelDepthBuffer(float *depthData, bool flipY) override + { + assert(mMaskedHiZBuffer != nullptr); + for (int y = 0; y < mHeight; y++) + { + for (int x = 0; x < mWidth; x++) + { + // Compute 32xN tile index (SIMD value offset) + int tx = x / TILE_WIDTH; + int ty = y / TILE_HEIGHT; + int tileIdx = ty * mTilesWidth + tx; + + // Compute 8x4 subtile index (SIMD lane offset) + int stx = (x % TILE_WIDTH) / SUB_TILE_WIDTH; + int sty = (y % TILE_HEIGHT) / SUB_TILE_HEIGHT; + int subTileIdx = sty * 4 + stx; + + // Compute pixel index in subtile (bit index in 32-bit word) + int px = (x % SUB_TILE_WIDTH); + int py = (y % SUB_TILE_HEIGHT); + int bitIdx = py * 8 + px; + + int pixelLayer = (simd_i32(mMaskedHiZBuffer[tileIdx].mMask)[subTileIdx] >> bitIdx) & 1; + float pixelDepth = simd_f32(mMaskedHiZBuffer[tileIdx].mZMin[pixelLayer])[subTileIdx]; + + if( flipY ) + depthData[( mHeight - y - 1 ) * mWidth + x] = pixelDepth; + else + depthData[y * mWidth + x] = pixelDepth; + } + } + } + + OcclusionCullingStatistics GetStatistics() override + { + return mStats; + } + +}; diff --git a/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/PackageInfo.json b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/PackageInfo.json new file mode 100644 index 0000000000..08b29fd566 --- /dev/null +++ b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/PackageInfo.json @@ -0,0 +1,6 @@ +{ + "PackageName": "Masked Occlusion Culling", + "URL": "https://software.intel.com/content/www/us/en/develop/articles/masked-software-occlusion-culling.html", + "License": "Apache 2.0", + "LicenseFile": "LICENSE.txt" +} diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h index c7e9cc4706..2354a4feea 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h @@ -31,7 +31,6 @@ #include #include - #include #include @@ -97,6 +96,9 @@ namespace AZ }; LodData m_lodData; + //! Flag indicating if the object is visible, i.e., was not culled out in the last frame + bool m_isVisible = true; + void SetDebugName([[maybe_unused]] const AZ::Name& debugName) { #ifdef AZ_CULL_DEBUG_ENABLED @@ -213,6 +215,21 @@ namespace AZ void Activate(const class Scene* parentScene); void Deactivate(); + struct OcclusionPlane + { + // World space corners of the occluson plane + Vector3 m_cornerBL; + Vector3 m_cornerTL; + Vector3 m_cornerTR; + Vector3 m_cornerBR; + + Aabb m_aabb; + }; + using OcclusionPlaneVector = AZStd::vector; + + //! Sets a list of occlusion planes to be used during the culling process. + void SetOcclusionPlanes(const OcclusionPlaneVector& occlusionPlanes) { m_occlusionPlanes = occlusionPlanes; } + //! Notifies the CullingScene that culling will begin for this frame. void BeginCulling(const AZStd::vector& views); @@ -251,12 +268,9 @@ namespace AZ const Scene* m_parentScene = nullptr; AzFramework::IVisibilityScene* m_visScene = nullptr; - CullingDebugContext m_debugCtx; - AZStd::concurrency_checker m_cullDataConcurrencyCheck; - - AZStd::mutex m_mutex; + OcclusionPlaneVector m_occlusionPlanes; }; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h index 1cba71ae7e..3d0a8cd3a8 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h @@ -184,8 +184,8 @@ namespace AZ //! Collect all different view tags from this pass virtual void GetPipelineViewTags(SortedPipelineViewTags& outTags) const; - //! Adds this pass' DrawListTags to the outDrawListMask. - virtual void GetViewDrawListInfo(RHI::DrawListMask& outDrawListMask, PassesByDrawList& outPassesByDrawList, const PipelineViewTag& viewTag) const; + //! Adds this pass' DrawListTags to the outDrawListMask. + virtual void GetViewDrawListInfo(RHI::DrawListMask& outDrawListMask, PassesByDrawList& outPassesByDrawList, const PipelineViewTag& viewTag) const; //! Check if the pass has a DrawListTag. Pass' DrawListTag can be used to filter draw items. virtual RHI::DrawListTag GetDrawListTag() const; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassAttachment.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassAttachment.h index 5509398f55..fd2a49a941 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassAttachment.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassAttachment.h @@ -52,7 +52,8 @@ namespace AZ const RHI::TransientBufferDescriptor GetTransientBufferDescriptor() const; //! Updates the size and format of this attachment using the sources below if specified - void Update(); + //! @param updateImportedAttachments - Imported attchments will only update if this is true. + void Update(bool updateImportedAttachments = false); //! Sets all formats to nearest device supported formats and warns if changes where made void ValidateDeviceFormats(const AZStd::vector& formatFallbacks, RHI::FormatCapabilities capabilities = RHI::FormatCapabilities::None); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h index aad099dc23..fabec8d896 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h @@ -24,6 +24,8 @@ #include #include +class MaskedOcclusionCulling; + namespace AZ { namespace RHI @@ -57,7 +59,7 @@ namespace AZ //! Only use this function to create a new view object. And force using smart pointer to manage view's life time static ViewPtr CreateView(const AZ::Name& name, UsageFlags usage); - ~View() = default; + ~View(); void SetDrawListMask(const RHI::DrawListMask& drawListMask); RHI::DrawListMask GetDrawListMask() const { return m_drawListMask; } @@ -86,6 +88,9 @@ namespace AZ //! Sets the viewToClip matrix and recalculates the other matrices void SetViewToClipMatrix(const AZ::Matrix4x4& viewToClip); + //! Sets a pixel offset on the view, usually used for jittering the camera for anti-aliasing techniques. + void SetClipSpaceOffset(float xOffset, float yOffset); + const AZ::Matrix4x4& GetWorldToViewMatrix() const; //! Use GetViewToWorldMatrix().GetTranslation() to get the camera's position. const AZ::Matrix4x4& GetViewToWorldMatrix() const; @@ -126,6 +131,12 @@ namespace AZ //! Notifies consumers when the world to clip matrix has changed. void ConnectWorldToClipMatrixChangedHandler(MatrixChangedEvent::Handler& handler); + //! Prepare for view culling + void BeginCulling(); + + //! Returns the masked occlusion culling interface + MaskedOcclusionCulling* GetMaskedOcclusionCulling(); + private: View() = delete; View(const AZ::Name& name, UsageFlags usage); @@ -165,7 +176,6 @@ namespace AZ Matrix4x4 m_worldToViewMatrix; Matrix4x4 m_viewToWorldMatrix; Matrix4x4 m_viewToClipMatrix; - Matrix4x4 m_clipToViewMatrix; Matrix4x4 m_clipToWorldMatrix; // View's position in world space @@ -180,19 +190,20 @@ namespace AZ // Cached matrix to transform from world space to clip space Matrix4x4 m_worldToClipMatrix; - Matrix4x4 m_worldToClipPrevMatrix; + Matrix4x4 m_worldToViewPrevMatrix; + Matrix4x4 m_viewToClipPrevMatrix; + + // Clip space offset for camera jitter with taa + Vector2 m_clipSpaceOffset = Vector2(0.0f, 0.0f); // Flags whether view matrices are dirty which requires rebuild srg bool m_needBuildSrg = true; - // Following two bools form a delay circuit to update history of next frame - // if vp matrix is changed during current frame, this is required because - // view class doesn't contain subroutines called at the end of each frame - bool m_worldToClipMatrixChanged = true; - bool m_worldToClipPrevMatrixNeedsUpdate = false; - MatrixChangedEvent m_onWorldToClipMatrixChange; MatrixChangedEvent m_onWorldToViewMatrixChange; + + // Masked Occlusion Culling interface + MaskedOcclusionCulling* m_maskedOcclusionCulling = nullptr; }; AZ_DEFINE_ENUM_BITWISE_OPERATORS(View::UsageFlags); diff --git a/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Android.h b/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Android.h new file mode 100644 index 0000000000..e1c8d827bf --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Android.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#define AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED 0 diff --git a/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Platform.h b/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Platform.h new file mode 100644 index 0000000000..60835c7025 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Platform.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include "Atom_RPI_Traits_Android.h" diff --git a/Gems/Atom/RPI/Code/Source/Platform/Android/PAL_android.cmake b/Gems/Atom/RPI/Code/Source/Platform/Android/PAL_android.cmake new file mode 100644 index 0000000000..4542e7c707 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Android/PAL_android.cmake @@ -0,0 +1,13 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set (PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED FALSE) +set (PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED FALSE) diff --git a/Gems/Atom/RPI/Code/Source/Platform/Android/platform_android_files.cmake b/Gems/Atom/RPI/Code/Source/Platform/Android/platform_android_files.cmake new file mode 100644 index 0000000000..83ddc410d6 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Android/platform_android_files.cmake @@ -0,0 +1,15 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Atom_RPI_Traits_Platform.h + Atom_RPI_Traits_Android.h +) diff --git a/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Linux.h b/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Linux.h new file mode 100644 index 0000000000..e1c8d827bf --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Linux.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#define AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED 0 diff --git a/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Platform.h b/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Platform.h new file mode 100644 index 0000000000..f7a51ddbf7 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Platform.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include "Atom_RPI_Traits_Linux.h" diff --git a/Gems/Atom/RPI/Code/Source/Platform/Linux/PAL_linux.cmake b/Gems/Atom/RPI/Code/Source/Platform/Linux/PAL_linux.cmake index e9a14ba928..4542e7c707 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/Linux/PAL_linux.cmake +++ b/Gems/Atom/RPI/Code/Source/Platform/Linux/PAL_linux.cmake @@ -10,3 +10,4 @@ # set (PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED FALSE) +set (PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED FALSE) diff --git a/Gems/Atom/RPI/Code/Source/Platform/Linux/platform_linux_files.cmake b/Gems/Atom/RPI/Code/Source/Platform/Linux/platform_linux_files.cmake new file mode 100644 index 0000000000..99df861f9c --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Linux/platform_linux_files.cmake @@ -0,0 +1,15 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Atom_RPI_Traits_Platform.h + Atom_RPI_Traits_Linux.h +) diff --git a/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Mac.h b/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Mac.h new file mode 100644 index 0000000000..e1c8d827bf --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Mac.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#define AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED 0 diff --git a/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Platform.h b/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Platform.h new file mode 100644 index 0000000000..87bc2190f3 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Platform.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include "Atom_RPI_Traits_Mac.h" diff --git a/Gems/Atom/RPI/Code/Source/Platform/Mac/PAL_mac.cmake b/Gems/Atom/RPI/Code/Source/Platform/Mac/PAL_mac.cmake index c060b8bbaa..f177b9dfb9 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/Mac/PAL_mac.cmake +++ b/Gems/Atom/RPI/Code/Source/Platform/Mac/PAL_mac.cmake @@ -10,3 +10,4 @@ # set (PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED TRUE) +set (PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED FALSE) diff --git a/Gems/Atom/RPI/Code/Source/Platform/Mac/platform_mac_files.cmake b/Gems/Atom/RPI/Code/Source/Platform/Mac/platform_mac_files.cmake new file mode 100644 index 0000000000..b1baca036e --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Mac/platform_mac_files.cmake @@ -0,0 +1,15 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Atom_RPI_Traits_Platform.h + Atom_RPI_Traits_Mac.h +) diff --git a/Gems/Atom/RPI/Code/Source/Platform/Windows/Atom_RPI_Traits_Platform.h b/Gems/Atom/RPI/Code/Source/Platform/Windows/Atom_RPI_Traits_Platform.h new file mode 100644 index 0000000000..dc655ed3a9 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Windows/Atom_RPI_Traits_Platform.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include "Atom_RPI_Traits_Windows.h" diff --git a/Gems/Atom/RPI/Code/Source/Platform/Windows/Atom_RPI_Traits_Windows.h b/Gems/Atom/RPI/Code/Source/Platform/Windows/Atom_RPI_Traits_Windows.h new file mode 100644 index 0000000000..0deebe4706 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Windows/Atom_RPI_Traits_Windows.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#define AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED 1 diff --git a/Gems/Atom/RPI/Code/Source/Platform/Windows/PAL_windows.cmake b/Gems/Atom/RPI/Code/Source/Platform/Windows/PAL_windows.cmake index c060b8bbaa..b989233ccd 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/Windows/PAL_windows.cmake +++ b/Gems/Atom/RPI/Code/Source/Platform/Windows/PAL_windows.cmake @@ -10,3 +10,17 @@ # set (PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED TRUE) +set (PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED TRUE) + +ly_add_source_properties( + SOURCES External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp + PROPERTY COMPILE_OPTIONS + VALUES /arch:AVX2 /W3 +) +ly_add_source_properties( + SOURCES + External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp + External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp + PROPERTY COMPILE_OPTIONS + VALUES /W3 +) \ No newline at end of file diff --git a/Gems/Atom/RPI/Code/Source/Platform/Windows/platform_windows_files.cmake b/Gems/Atom/RPI/Code/Source/Platform/Windows/platform_windows_files.cmake new file mode 100644 index 0000000000..e49944d8ef --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Windows/platform_windows_files.cmake @@ -0,0 +1,15 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Atom_RPI_Traits_Platform.h + Atom_RPI_Traits_Windows.h +) diff --git a/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_Platform.h b/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_Platform.h new file mode 100644 index 0000000000..48fe26cc61 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_Platform.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include "Atom_RPI_Traits_iOS.h" diff --git a/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_iOS.h b/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_iOS.h new file mode 100644 index 0000000000..e1c8d827bf --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_iOS.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#define AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED 0 diff --git a/Gems/Atom/RPI/Code/Source/Platform/iOS/PAL_ios.cmake b/Gems/Atom/RPI/Code/Source/Platform/iOS/PAL_ios.cmake new file mode 100644 index 0000000000..4542e7c707 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/iOS/PAL_ios.cmake @@ -0,0 +1,13 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set (PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED FALSE) +set (PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED FALSE) diff --git a/Gems/Atom/RPI/Code/Source/Platform/iOS/platform_ios_files.cmake b/Gems/Atom/RPI/Code/Source/Platform/iOS/platform_ios_files.cmake new file mode 100644 index 0000000000..3eae72e612 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/iOS/platform_ios_files.cmake @@ -0,0 +1,15 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Atom_RPI_Traits_Platform.h + Atom_RPI_Traits_iOS.h +) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index c5e9d949aa..9f0a17f294 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -20,15 +20,20 @@ #include +#include #include #include - #include #include #include #include #include #include +#include + +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED +#include +#endif //Enables more inner-loop profiling scopes (can create high overhead in RadTelemetry if there are many-many objects in a scene) //#define AZ_CULL_PROFILE_DETAILED @@ -272,21 +277,26 @@ namespace AZ public: AZ_CLASS_ALLOCATOR(AddObjectsToViewJob, ThreadPoolAllocator, 0); + struct JobData + { + CullingDebugContext* m_debugCtx = nullptr; + const Scene* m_scene = nullptr; + View* m_view = nullptr; + Frustum m_frustum; +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED + MaskedOcclusionCulling* m_maskedOcclusionCulling = nullptr; +#endif + }; + private: - CullingDebugContext* m_debugCtx; - const Scene* m_scene; - View* m_view; - Frustum m_frustum; + const AZStd::shared_ptr m_jobData; CullingScene::WorkListType m_worklist; public: - AddObjectsToViewJob(CullingDebugContext& debugCtx, const Scene& scene, View& view, Frustum& frustum, CullingScene::WorkListType& worklist) + AddObjectsToViewJob(const AZStd::shared_ptr& jobData, CullingScene::WorkListType& worklist) : Job(true, nullptr) //auto-deletes, no JobContext - , m_debugCtx(&debugCtx) - , m_scene(&scene) - , m_view(&view) - , m_frustum(frustum) //capture by value - , m_worklist(AZStd::move(worklist)) //capture by value + , m_jobData(jobData) + , m_worklist(worklist) { } @@ -295,37 +305,50 @@ namespace AZ { AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender); - const View::UsageFlags viewFlags = m_view->GetUsageFlags(); - const RHI::DrawListMask drawListMask = m_view->GetDrawListMask(); + const View::UsageFlags viewFlags = m_jobData->m_view->GetUsageFlags(); + const RHI::DrawListMask drawListMask = m_jobData->m_view->GetDrawListMask(); uint32_t numDrawPackets = 0; uint32_t numVisibleCullables = 0; for (const AzFramework::IVisibilityScene::NodeData& nodeData : m_worklist) { //If a node is entirely contained within the frustum, then we can skip the fine grained culling. - bool nodeIsContainedInFrustum = ShapeIntersection::Contains(m_frustum, nodeData.m_bounds); + bool nodeIsContainedInFrustum = ShapeIntersection::Contains(m_jobData->m_frustum, nodeData.m_bounds); #ifdef AZ_CULL_PROFILE_VERBOSE AZ_PROFILE_SCOPE_DYNAMIC(Debug::ProfileCategory::AzRender, "process node (view: %s, skip fine cull: %d", m_view->GetName().GetCStr(), nodeIsContainedInFrustum ? 1 : 0); #endif - if (nodeIsContainedInFrustum || !m_debugCtx->m_enableFrustumCulling) + if (nodeIsContainedInFrustum || !m_jobData->m_debugCtx->m_enableFrustumCulling) { //Add all objects within this node to the view, without any extra culling for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries) { - if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable) { - Cullable* c = static_cast(visibleEntry->m_userData); - if ((c->m_cullData.m_drawListMask & drawListMask).none() || - c->m_cullData.m_hideFlags & viewFlags || - c->m_cullData.m_scene != m_scene) //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this + if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable) { - continue; + Cullable* c = static_cast(visibleEntry->m_userData); + + // reset visibility flag to false, update to true if all culling checks pass + c->m_isVisible = false; + + if ((c->m_cullData.m_drawListMask & drawListMask).none() || + c->m_cullData.m_hideFlags & viewFlags || + c->m_cullData.m_scene != m_jobData->m_scene) //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this + { + continue; + } + +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED + if (TestOcclusionCulling(visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE) +#endif + { + numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_jobData->m_view); + ++numVisibleCullables; + c->m_isVisible = true; + } } - numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_view); - ++numVisibleCullables; } } } @@ -337,68 +360,78 @@ namespace AZ if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable) { Cullable* c = static_cast(visibleEntry->m_userData); + + // reset visibility flag to false, update to true if all culling checks pass + c->m_isVisible = false; + if ((c->m_cullData.m_drawListMask & drawListMask).none() || c->m_cullData.m_hideFlags & viewFlags || - c->m_cullData.m_scene != m_scene) //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this + c->m_cullData.m_scene != m_jobData->m_scene) //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this { continue; } - IntersectResult res = ShapeIntersection::Classify(m_frustum, c->m_cullData.m_boundingSphere); + IntersectResult res = ShapeIntersection::Classify(m_jobData->m_frustum, c->m_cullData.m_boundingSphere); if (res == IntersectResult::Exterior) { continue; } - else if (res == IntersectResult::Interior || ShapeIntersection::Overlaps(m_frustum, c->m_cullData.m_boundingObb)) + else if (res == IntersectResult::Interior || ShapeIntersection::Overlaps(m_jobData->m_frustum, c->m_cullData.m_boundingObb)) { - numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_view); - ++numVisibleCullables; +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED + if (TestOcclusionCulling(visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE) +#endif + { + numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_jobData->m_view); + ++numVisibleCullables; + c->m_isVisible = true; + } } } } } - if (m_debugCtx->m_debugDraw && (m_view->GetName() == m_debugCtx->m_currentViewSelectionName)) + if (m_jobData->m_debugCtx->m_debugDraw && (m_jobData->m_view->GetName() == m_jobData->m_debugCtx->m_currentViewSelectionName)) { AZ_PROFILE_SCOPE(Debug::ProfileCategory::AzRender, "debug draw culling"); - AuxGeomDrawPtr auxGeomPtr = AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(m_scene); + AuxGeomDrawPtr auxGeomPtr = AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(m_jobData->m_scene); if (auxGeomPtr) { //Draw the node bounds // "Fully visible" nodes are nodes that are fully inside the frustum. "Partially visible" nodes intersect the edges of the frustum. // Since the nodes of an octree have lots of overlapping boxes with coplanar edges, it's easier to view these separately, so // we have a few debug booleans to toggle which ones to draw. - if (nodeIsContainedInFrustum && m_debugCtx->m_drawFullyVisibleNodes) + if (nodeIsContainedInFrustum && m_jobData->m_debugCtx->m_drawFullyVisibleNodes) { auxGeomPtr->DrawAabb(nodeData.m_bounds, Colors::Lime, RPI::AuxGeomDraw::DrawStyle::Line, RPI::AuxGeomDraw::DepthTest::Off); } - else if (!nodeIsContainedInFrustum && m_debugCtx->m_drawPartiallyVisibleNodes) + else if (!nodeIsContainedInFrustum && m_jobData->m_debugCtx->m_drawPartiallyVisibleNodes) { auxGeomPtr->DrawAabb(nodeData.m_bounds, Colors::Yellow, RPI::AuxGeomDraw::DrawStyle::Line, RPI::AuxGeomDraw::DepthTest::Off); } //Draw bounds on individual objects - if (m_debugCtx->m_drawBoundingBoxes || m_debugCtx->m_drawBoundingSpheres || m_debugCtx->m_drawLodRadii) + if (m_jobData->m_debugCtx->m_drawBoundingBoxes || m_jobData->m_debugCtx->m_drawBoundingSpheres || m_jobData->m_debugCtx->m_drawLodRadii) { for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries) { if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable) { Cullable* c = static_cast(visibleEntry->m_userData); - if (m_debugCtx->m_drawBoundingBoxes) + if (m_jobData->m_debugCtx->m_drawBoundingBoxes) { auxGeomPtr->DrawObb(c->m_cullData.m_boundingObb, Matrix3x4::Identity(), nodeIsContainedInFrustum ? Colors::Lime : Colors::Yellow, AuxGeomDraw::DrawStyle::Line); } - if (m_debugCtx->m_drawBoundingSpheres) + if (m_jobData->m_debugCtx->m_drawBoundingSpheres) { auxGeomPtr->DrawSphere(c->m_cullData.m_boundingSphere.GetCenter(), c->m_cullData.m_boundingSphere.GetRadius(), Color(0.5f, 0.5f, 0.5f, 0.3f), AuxGeomDraw::DrawStyle::Shaded); } - if (m_debugCtx->m_drawLodRadii) + if (m_jobData->m_debugCtx->m_drawLodRadii) { auxGeomPtr->DrawSphere(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData.m_lodSelectionRadius, @@ -411,9 +444,9 @@ namespace AZ } } - if (m_debugCtx->m_enableStats) + if (m_jobData->m_debugCtx->m_enableStats) { - CullingDebugContext::CullStats& cullStats = m_debugCtx->GetCullStatsForView(m_view); + CullingDebugContext::CullStats& cullStats = m_jobData->m_debugCtx->GetCullStatsForView(m_jobData->m_view); //no need for mutex here since these are all atomics cullStats.m_numVisibleDrawPackets += numDrawPackets; @@ -421,6 +454,63 @@ namespace AZ ++cullStats.m_numJobs; } } + +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED + MaskedOcclusionCulling::CullingResult TestOcclusionCulling(AzFramework::VisibilityEntry* visibleEntry) + { + if (!m_jobData->m_maskedOcclusionCulling) + { + return MaskedOcclusionCulling::CullingResult::VISIBLE; + } + + if (visibleEntry->m_boundingVolume.Contains(m_jobData->m_view->GetCameraTransform().GetTranslation())) + { + // camera is inside bounding volume + return MaskedOcclusionCulling::CullingResult::VISIBLE; + } + + const Vector3& minBound = visibleEntry->m_boundingVolume.GetMin(); + const Vector3& maxBound = visibleEntry->m_boundingVolume.GetMax(); + + // compute bounding volume corners + Vector4 corners[8]; + corners[0] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), minBound.GetY(), minBound.GetZ(), 1.0f); + corners[1] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), minBound.GetY(), maxBound.GetZ(), 1.0f); + corners[2] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), minBound.GetY(), maxBound.GetZ(), 1.0f); + corners[3] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), minBound.GetY(), minBound.GetZ(), 1.0f); + corners[4] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), maxBound.GetY(), minBound.GetZ(), 1.0f); + corners[5] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), maxBound.GetY(), maxBound.GetZ(), 1.0f); + corners[6] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), maxBound.GetY(), maxBound.GetZ(), 1.0f); + corners[7] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), maxBound.GetY(), minBound.GetZ(), 1.0f); + + // find min clip-space depth and NDC min/max + float minDepth = FLT_MAX; + float ndcMinX = FLT_MAX; + float ndcMinY = FLT_MAX; + float ndcMaxX = -FLT_MAX; + float ndcMaxY = -FLT_MAX; + for (uint32_t index = 0; index < 8; ++index) + { + minDepth = AZStd::min(minDepth, corners[index].GetW()); + + // convert to NDC + corners[index] /= corners[index].GetW(); + + ndcMinX = AZStd::min(ndcMinX, corners[index].GetX()); + ndcMinY = AZStd::min(ndcMinY, corners[index].GetY()); + ndcMaxX = AZStd::max(ndcMaxX, corners[index].GetX()); + ndcMaxY = AZStd::max(ndcMaxY, corners[index].GetY()); + } + + if (minDepth < 0.00000001f) + { + return MaskedOcclusionCulling::VISIBLE; + } + + // test against the occlusion buffer, which contains only the manually placed occlusion planes + return m_jobData->m_maskedOcclusionCulling->TestRect(ndcMinX, ndcMinY, ndcMaxX, ndcMaxY, minDepth); + } +#endif }; void CullingScene::ProcessCullables(const Scene& scene, View& view, AZ::Job& parentJob) @@ -454,8 +544,67 @@ namespace AZ cullStats.m_cameraViewToWorld = view.GetViewToWorldMatrix(); } +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED + // setup occlusion culling, if necessary + MaskedOcclusionCulling* maskedOcclusionCulling = m_occlusionPlanes.empty() ? nullptr : view.GetMaskedOcclusionCulling(); + if (maskedOcclusionCulling) + { + // frustum cull occlusion planes + using VisibleOcclusionPlane = AZStd::pair; + AZStd::vector visibleOccluders; + for (const auto& occlusionPlane : m_occlusionPlanes) + { + if (ShapeIntersection::Overlaps(frustum, occlusionPlane.m_aabb)) + { + // occluder is visible, compute view space distance and add to list + float depth = (view.GetWorldToViewMatrix() * occlusionPlane.m_aabb.GetMin()).GetZ(); + depth = AZStd::min(depth, (view.GetWorldToViewMatrix() * occlusionPlane.m_aabb.GetMax()).GetZ()); + + visibleOccluders.push_back(AZStd::make_pair(occlusionPlane, depth)); + } + } + + // sort the occlusion planes by view space distance, front-to-back + AZStd::sort(visibleOccluders.begin(), visibleOccluders.end(), [](const VisibleOcclusionPlane& LHS, const VisibleOcclusionPlane& RHS) + { + return LHS.second > RHS.second; + }); + + for (const VisibleOcclusionPlane& occlusionPlane: visibleOccluders) + { + // convert to clip-space + Vector4 projectedBL = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerBL); + Vector4 projectedTL = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerTL); + Vector4 projectedTR = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerTR); + Vector4 projectedBR = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerBR); + + // store to float array + float verts[16]; + projectedBL.StoreToFloat4(&verts[0]); + projectedTL.StoreToFloat4(&verts[4]); + projectedTR.StoreToFloat4(&verts[8]); + projectedBR.StoreToFloat4(&verts[12]); + + static uint32_t indices[6] = { 0, 1, 2, 2, 3, 0 }; + + // render into the occlusion buffer, specifying BACKFACE_NONE so it functions as a double-sided occluder + maskedOcclusionCulling->RenderTriangles((float*)verts, indices, 2, nullptr, MaskedOcclusionCulling::BACKFACE_NONE); + } + } +#endif + WorkListType worklist; - auto nodeVisitorLambda = [this, &scene, &view, &parentJob, &frustum, &worklist](const AzFramework::IVisibilityScene::NodeData& nodeData) -> void + + AZStd::shared_ptr jobData = AZStd::make_shared(); + jobData->m_debugCtx = &m_debugCtx; + jobData->m_scene = &scene; + jobData->m_view = &view; + jobData->m_frustum = frustum; +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED + jobData->m_maskedOcclusionCulling = maskedOcclusionCulling; +#endif + + auto nodeVisitorLambda = [this, jobData, &parentJob, &frustum, &worklist](const AzFramework::IVisibilityScene::NodeData& nodeData) -> void { AZ_PROFILE_SCOPE(Debug::ProfileCategory::AzRender, "nodeVisitorLambda()"); AZ_Assert(nodeData.m_entries.size() > 0, "should not get called with 0 entries"); @@ -468,7 +617,7 @@ namespace AZ if (worklist.size() == worklist.capacity()) { //Kick off a job to process the (full) worklist - AddObjectsToViewJob* job = aznew AddObjectsToViewJob(m_debugCtx, scene, view, frustum, worklist); //pool allocated (cheap), auto-deletes when job finishes + AddObjectsToViewJob* job = aznew AddObjectsToViewJob(jobData, worklist); //pool allocated (cheap), auto-deletes when job finishes worklist.clear(); parentJob.SetContinuation(job); job->Start(); @@ -486,8 +635,16 @@ namespace AZ if (worklist.size() > 0) { + AZStd::shared_ptr remainingJobData = AZStd::make_shared(); + remainingJobData->m_debugCtx = &m_debugCtx; + remainingJobData->m_scene = &scene; + remainingJobData->m_view = &view; + remainingJobData->m_frustum = frustum; +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED + remainingJobData->m_maskedOcclusionCulling = maskedOcclusionCulling; +#endif //Kick off a job to process any remaining workitems - AddObjectsToViewJob* job = aznew AddObjectsToViewJob(m_debugCtx, scene, view, frustum, worklist); //pool allocated (cheap), auto-deletes when job finishes + AddObjectsToViewJob* job = aznew AddObjectsToViewJob(remainingJobData, worklist); //pool allocated (cheap), auto-deletes when job finishes parentJob.SetContinuation(job); job->Start(); } @@ -576,6 +733,11 @@ namespace AZ m_debugCtx.ResetCullStats(); m_debugCtx.m_numCullablesInScene = GetNumCullables(); + for (auto& view : views) + { + view->BeginCulling(); + } + AuxGeomDrawPtr auxGeom; if (m_debugCtx.m_debugDraw) { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp index 8d613eb2bb..1e230a7fc0 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp @@ -914,23 +914,40 @@ namespace AZ { // make sure to only import the resource one time RHI::AttachmentId attachmentId = attachment->GetAttachmentId(); - if (!attachmentDatabase.IsAttachmentValid(attachmentId)) + const RHI::FrameAttachment* currentAttachment = attachmentDatabase.FindAttachment(attachmentId); + + if (azrtti_istypeof(attachment->m_importedResource.get())) { - if (azrtti_istypeof(attachment->m_importedResource.get())) + Image* image = static_cast(attachment->m_importedResource.get()); + if (currentAttachment == nullptr) { - Image* image = static_cast(attachment->m_importedResource.get()); attachmentDatabase.ImportImage(attachmentId, image->GetRHIImage()); } - else if (azrtti_istypeof(attachment->m_importedResource.get())) + else + { + AZ_Assert(currentAttachment->GetResource() == image->GetRHIImage(), + "Importing image attachment named \"%s\" but a different attachment with the " + "same name already exists in the database.\n", attachmentId.GetCStr()); + } + } + else if (azrtti_istypeof(attachment->m_importedResource.get())) + { + Buffer* buffer = static_cast(attachment->m_importedResource.get()); + if (currentAttachment == nullptr) { - Buffer* buffer = static_cast(attachment->m_importedResource.get()); attachmentDatabase.ImportBuffer(attachmentId, buffer->GetRHIBuffer()); } else { - AZ_RPI_PASS_ERROR(false, "Can't import unknown resource type"); + AZ_Assert(currentAttachment->GetResource() == buffer->GetRHIBuffer(), + "Importing buffer attachment named \"%s\" but a different attachment with the " + "same name already exists in the database.\n", attachmentId.GetCStr()); } } + else + { + AZ_RPI_PASS_ERROR(false, "Can't import unknown resource type"); + } } } } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassAttachment.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassAttachment.cpp index a5082c5ee6..3bd26a5906 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassAttachment.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassAttachment.cpp @@ -114,9 +114,9 @@ namespace AZ return RHI::TransientBufferDescriptor(GetAttachmentId(), m_descriptor.m_buffer); } - void PassAttachment::Update() + void PassAttachment::Update(bool updateImportedAttachments) { - if (m_descriptor.m_type == RHI::AttachmentType::Image && m_lifetime == RHI::AttachmentLifetimeType::Transient) + if (m_descriptor.m_type == RHI::AttachmentType::Image && (m_lifetime == RHI::AttachmentLifetimeType::Transient || updateImportedAttachments == true)) { if (m_settingFlags.m_getFormatFromPipeline && m_renderPipelineSource) { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp index 21a46693d5..24dfcb7097 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp @@ -15,18 +15,28 @@ #include #include #include - +#include +#include #include #include #include #include #include +#include + +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED +#include +#endif namespace AZ { namespace RPI { + // fixed-size software occlusion culling buffer + const uint32_t MaskedSoftwareOcclusionCullingWidth = 1920; + const uint32_t MaskedSoftwareOcclusionCullingHeight = 1080; + ViewPtr View::CreateView(const AZ::Name& name, UsageFlags usage) { View* view = aznew View(name, usage); @@ -51,6 +61,21 @@ namespace AZ { m_shaderResourceGroup = ShaderResourceGroup::Create(viewSrgAsset); } +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED + m_maskedOcclusionCulling = MaskedOcclusionCulling::Create(); + m_maskedOcclusionCulling->SetResolution(MaskedSoftwareOcclusionCullingWidth, MaskedSoftwareOcclusionCullingHeight); +#endif + } + + View::~View() + { +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED + if (m_maskedOcclusionCulling) + { + MaskedOcclusionCulling::Destroy(m_maskedOcclusionCulling); + m_maskedOcclusionCulling = nullptr; + } +#endif } void View::SetDrawListMask(const RHI::DrawListMask& drawListMask) @@ -102,7 +127,6 @@ namespace AZ m_worldToViewMatrix = worldToView; m_worldToClipMatrix = m_viewToClipMatrix * m_worldToViewMatrix; - m_worldToClipMatrixChanged = true; m_onWorldToViewMatrixChange.Signal(m_worldToViewMatrix); m_onWorldToClipMatrixChange.Signal(m_worldToClipMatrix); @@ -141,8 +165,6 @@ namespace AZ m_worldToViewMatrix = m_viewToWorldMatrix.GetInverseFast(); m_worldToClipMatrix = m_viewToClipMatrix * m_worldToViewMatrix; - m_clipToWorldMatrix = m_viewToWorldMatrix * m_clipToViewMatrix; - m_worldToClipMatrixChanged = true; m_onWorldToViewMatrixChange.Signal(m_worldToViewMatrix); m_onWorldToClipMatrixChange.Signal(m_worldToClipMatrix); @@ -153,12 +175,8 @@ namespace AZ void View::SetViewToClipMatrix(const AZ::Matrix4x4& viewToClip) { m_viewToClipMatrix = viewToClip; - m_clipToViewMatrix = viewToClip.GetInverseFull(); m_worldToClipMatrix = m_viewToClipMatrix * m_worldToViewMatrix; - m_worldToClipMatrixChanged = true; - - m_clipToWorldMatrix = m_viewToWorldMatrix * m_clipToViewMatrix; // Update z depth constant simultaneously // zNear -> n, zFar -> f @@ -185,6 +203,12 @@ namespace AZ InvalidateSrg(); } + + void View::SetClipSpaceOffset(float xOffset, float yOffset) + { + m_clipSpaceOffset.Set(xOffset, yOffset); + InvalidateSrg(); + } const AZ::Matrix4x4& View::GetWorldToViewMatrix() const { @@ -343,36 +367,68 @@ namespace AZ void View::UpdateSrg() { - if (m_worldToClipPrevMatrixNeedsUpdate) + if (m_needBuildSrg) { - m_shaderResourceGroup->SetConstant(m_worldToClipPrevMatrixConstantIndex, m_worldToClipPrevMatrix); - m_worldToClipPrevMatrixNeedsUpdate = false; + if (m_clipSpaceOffset.IsZero()) + { + Matrix4x4 worldToClipPrevMatrix = m_viewToClipPrevMatrix * m_worldToViewPrevMatrix; + m_shaderResourceGroup->SetConstant(m_worldToClipPrevMatrixConstantIndex, worldToClipPrevMatrix); + m_shaderResourceGroup->SetConstant(m_viewProjectionMatrixConstantIndex, m_worldToClipMatrix); + m_shaderResourceGroup->SetConstant(m_projectionMatrixConstantIndex, m_viewToClipMatrix); + m_shaderResourceGroup->SetConstant(m_clipToWorldMatrixConstantIndex, m_clipToWorldMatrix); + m_shaderResourceGroup->SetConstant(m_projectionMatrixInverseConstantIndex, m_viewToClipMatrix.GetInverseFull()); + } + else + { + // Offset the current and previous frame clip matricies + Matrix4x4 offsetViewToClipMatrix = m_viewToClipMatrix; + offsetViewToClipMatrix.SetElement(0, 2, m_clipSpaceOffset.GetX()); + offsetViewToClipMatrix.SetElement(1, 2, m_clipSpaceOffset.GetY()); + + Matrix4x4 offsetViewToClipPrevMatrix = m_viewToClipPrevMatrix; + offsetViewToClipPrevMatrix.SetElement(0, 2, m_clipSpaceOffset.GetX()); + offsetViewToClipPrevMatrix.SetElement(1, 2, m_clipSpaceOffset.GetY()); + + // Build other matricies dependent on the view to clip matricies + Matrix4x4 offsetWorldToClipMatrix = offsetViewToClipMatrix * m_worldToViewMatrix; + Matrix4x4 offsetWorldToClipPrevMatrix = offsetViewToClipPrevMatrix * m_worldToViewPrevMatrix; + + Matrix4x4 offsetClipToViewMatrix = offsetViewToClipMatrix.GetInverseFull(); + Matrix4x4 offsetClipToWorldMatrix = m_viewToWorldMatrix * offsetClipToViewMatrix; + + m_shaderResourceGroup->SetConstant(m_worldToClipPrevMatrixConstantIndex, offsetWorldToClipPrevMatrix); + m_shaderResourceGroup->SetConstant(m_viewProjectionMatrixConstantIndex, offsetWorldToClipMatrix); + m_shaderResourceGroup->SetConstant(m_projectionMatrixConstantIndex, offsetViewToClipMatrix); + m_shaderResourceGroup->SetConstant(m_clipToWorldMatrixConstantIndex, offsetClipToWorldMatrix); + m_shaderResourceGroup->SetConstant(m_projectionMatrixInverseConstantIndex, offsetViewToClipMatrix.GetInverseFull()); + } + + m_shaderResourceGroup->SetConstant(m_worldPositionConstantIndex, m_position); + m_shaderResourceGroup->SetConstant(m_viewMatrixConstantIndex, m_worldToViewMatrix); + m_shaderResourceGroup->SetConstant(m_viewMatrixInverseConstantIndex, m_worldToViewMatrix.GetInverseFull()); + m_shaderResourceGroup->SetConstant(m_zConstantsConstantIndex, m_nearZ_farZ_farZTimesNearZ_farZMinusNearZ); + m_shaderResourceGroup->SetConstant(m_unprojectionConstantsIndex, m_unprojectionConstants); + + m_shaderResourceGroup->Compile(); + m_needBuildSrg = false; } - if (m_worldToClipMatrixChanged) - { - m_worldToClipPrevMatrix = m_worldToClipMatrix; - m_worldToClipPrevMatrixNeedsUpdate = true; - m_worldToClipMatrixChanged = false; - } + m_viewToClipPrevMatrix = m_viewToClipMatrix; + m_worldToViewPrevMatrix = m_worldToViewMatrix; - if (!m_needBuildSrg) - { - return; - } + m_clipSpaceOffset.Set(0); + } - m_shaderResourceGroup->SetConstant(m_worldPositionConstantIndex, m_position); - m_shaderResourceGroup->SetConstant(m_viewProjectionMatrixConstantIndex, m_worldToClipMatrix); - m_shaderResourceGroup->SetConstant(m_viewMatrixConstantIndex, m_worldToViewMatrix); - m_shaderResourceGroup->SetConstant(m_viewMatrixInverseConstantIndex, m_worldToViewMatrix.GetInverseFull()); - m_shaderResourceGroup->SetConstant(m_projectionMatrixConstantIndex, m_viewToClipMatrix); - m_shaderResourceGroup->SetConstant(m_projectionMatrixInverseConstantIndex, m_viewToClipMatrix.GetInverseFull()); - m_shaderResourceGroup->SetConstant(m_zConstantsConstantIndex, m_nearZ_farZ_farZTimesNearZ_farZMinusNearZ); - m_shaderResourceGroup->SetConstant(m_clipToWorldMatrixConstantIndex, m_clipToWorldMatrix); - m_shaderResourceGroup->SetConstant(m_unprojectionConstantsIndex, m_unprojectionConstants); + void View::BeginCulling() + { +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED + m_maskedOcclusionCulling->ClearBuffer(); +#endif + } - m_shaderResourceGroup->Compile(); - m_needBuildSrg = false; + MaskedOcclusionCulling* View::GetMaskedOcclusionCulling() + { + return m_maskedOcclusionCulling; } } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/atom_rpi_masked_occlusion_files.cmake b/Gems/Atom/RPI/Code/atom_rpi_masked_occlusion_files.cmake new file mode 100644 index 0000000000..5828848c81 --- /dev/null +++ b/Gems/Atom/RPI/Code/atom_rpi_masked_occlusion_files.cmake @@ -0,0 +1,18 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + External/MaskedOcclusionCulling/MaskedOcclusionCulling.h + External/MaskedOcclusionCulling/MaskedOcclusionCullingCommon.inl + External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp + External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp + External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp +) \ No newline at end of file diff --git a/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake b/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake index 6242f3f140..a1d98bbd38 100644 --- a/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake +++ b/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake @@ -180,4 +180,4 @@ set(FILES Source/RPI.Public/GpuQuery/Query.cpp Source/RPI.Public/GpuQuery/QueryPool.cpp Source/RPI.Public/GpuQuery/TimestampQueryPool.cpp -) +) \ No newline at end of file diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.cpp new file mode 100644 index 0000000000..b14bcf28b8 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.cpp @@ -0,0 +1,44 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include + +namespace AZ +{ + namespace Render + { + + DiffuseGlobalIlluminationComponent::DiffuseGlobalIlluminationComponent(const DiffuseGlobalIlluminationComponentConfig& config) + : BaseClass(config) + { + } + + void DiffuseGlobalIlluminationComponent::Reflect(AZ::ReflectContext* context) + { + BaseClass::Reflect(context); + + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class(); + } + + if (auto behaviorContext = azrtti_cast(context)) + { + behaviorContext->ConstantProperty("DiffuseGlobalIlluminationComponentTypeId", BehaviorConstant(Uuid(DiffuseGlobalIlluminationComponentTypeId))) + ->Attribute(AZ::Script::Attributes::Module, "render") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common); + } + } + + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.h new file mode 100644 index 0000000000..a54e5868cd --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.h @@ -0,0 +1,38 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class DiffuseGlobalIlluminationComponent final + : public AzFramework::Components::ComponentAdapter + { + public: + using BaseClass = AzFramework::Components::ComponentAdapter; + AZ_COMPONENT(AZ::Render::DiffuseGlobalIlluminationComponent, DiffuseGlobalIlluminationComponentTypeId , BaseClass); + + DiffuseGlobalIlluminationComponent() = default; + DiffuseGlobalIlluminationComponent(const DiffuseGlobalIlluminationComponentConfig& config); + + static void Reflect(AZ::ReflectContext* context); + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.cpp new file mode 100644 index 0000000000..406da9db2e --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.cpp @@ -0,0 +1,32 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include + +namespace AZ +{ + namespace Render + { + void DiffuseGlobalIlluminationComponentConfig::Reflect(ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1) + ->Field("QualityLevel", &DiffuseGlobalIlluminationComponentConfig::m_qualityLevel) + ; + } + } + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h new file mode 100644 index 0000000000..fb99a99e1a --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h @@ -0,0 +1,35 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class DiffuseGlobalIlluminationComponentConfig final + : public ComponentConfig + { + public: + AZ_RTTI(DiffuseGlobalIlluminationComponentConfig, "{0D0835D6-6094-4EF8-BEAC-5FF8A4E4C119}", ComponentConfig); + AZ_CLASS_ALLOCATOR(DiffuseGlobalIlluminationComponentConfig, SystemAllocator, 0); + + static void Reflect(ReflectContext* context); + + DiffuseGlobalIlluminationQualityLevel m_qualityLevel; + }; + } +} diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConstants.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConstants.h new file mode 100644 index 0000000000..e89aa65584 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConstants.h @@ -0,0 +1,23 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +namespace AZ +{ + namespace Render + { + static constexpr const char* const DiffuseGlobalIlluminationComponentTypeId = "{D51F8033-EF0D-4A13-BED3-5B193555B8D2}"; + static constexpr const char* const EditorDiffuseGlobalIlluminationComponentTypeId = "{169378DD-4052-4A60-BD63-90B02CFA69C1}"; + + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp new file mode 100644 index 0000000000..a9feb0185c --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp @@ -0,0 +1,85 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include + +namespace AZ +{ + namespace Render + { + void DiffuseGlobalIlluminationComponentController::Reflect(ReflectContext* context) + { + DiffuseGlobalIlluminationComponentConfig::Reflect(context); + + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("Configuration", &DiffuseGlobalIlluminationComponentController::m_configuration); + } + } + + void DiffuseGlobalIlluminationComponentController::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC("DiffuseGlobalIlluminationService", 0x11b9cbe1)); + } + + void DiffuseGlobalIlluminationComponentController::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC("DiffuseGlobalIlluminationService", 0x11b9cbe1)); + } + + void DiffuseGlobalIlluminationComponentController::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + AZ_UNUSED(required); + } + + DiffuseGlobalIlluminationComponentController::DiffuseGlobalIlluminationComponentController(const DiffuseGlobalIlluminationComponentConfig& config) + : m_configuration(config) + { + } + + void DiffuseGlobalIlluminationComponentController::Activate(EntityId entityId) + { + AZ_UNUSED(entityId); + + const RPI::Scene* scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene().get(); + m_featureProcessor = scene->GetFeatureProcessor(); + + OnConfigChanged(); + } + + void DiffuseGlobalIlluminationComponentController::Deactivate() + { + } + + void DiffuseGlobalIlluminationComponentController::SetConfiguration(const DiffuseGlobalIlluminationComponentConfig& config) + { + m_configuration = config; + + OnConfigChanged(); + } + + const DiffuseGlobalIlluminationComponentConfig& DiffuseGlobalIlluminationComponentController::GetConfiguration() const + { + return m_configuration; + } + + void DiffuseGlobalIlluminationComponentController::OnConfigChanged() + { + m_featureProcessor->SetQualityLevel(m_configuration.m_qualityLevel); + } + + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.h new file mode 100644 index 0000000000..81da772129 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.h @@ -0,0 +1,52 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class DiffuseGlobalIlluminationComponentController final + { + public: + friend class EditorDiffuseGlobalIlluminationComponent; + + AZ_TYPE_INFO(AZ::Render::DiffuseGlobalIlluminationComponentController, "{7DE7D2A0-2526-447C-A11F-C31EE1332C26}"); + static void Reflect(AZ::ReflectContext* context); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + + DiffuseGlobalIlluminationComponentController() = default; + DiffuseGlobalIlluminationComponentController(const DiffuseGlobalIlluminationComponentConfig& config); + + void Activate(EntityId entityId); + void Deactivate(); + void SetConfiguration(const DiffuseGlobalIlluminationComponentConfig& config); + const DiffuseGlobalIlluminationComponentConfig& GetConfiguration() const; + + private: + AZ_DISABLE_COPY(DiffuseGlobalIlluminationComponentController); + + void OnConfigChanged(); + + DiffuseGlobalIlluminationComponentConfig m_configuration; + DiffuseGlobalIlluminationFeatureProcessorInterface* m_featureProcessor = nullptr; + }; + } +} diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponent.cpp similarity index 96% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponent.cpp rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponent.cpp index 509cbb86e6..b042f594c6 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponent.cpp @@ -10,7 +10,7 @@ * */ -#include +#include namespace AZ { diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponent.h similarity index 90% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponent.h rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponent.h index 964ce157bd..b77dceefac 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponent.h @@ -12,8 +12,8 @@ #pragma once -#include -#include +#include +#include #include namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentConstants.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentConstants.h similarity index 100% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentConstants.h rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentConstants.h diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.cpp similarity index 99% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.cpp index 5fb835de15..f5ac36e7f6 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.cpp @@ -10,8 +10,8 @@ * */ -#include -#include +#include +#include #include #include diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.h similarity index 97% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.h rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.h index 4122a07ba2..51a17cb2cf 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.h @@ -15,10 +15,10 @@ #include #include #include -#include +#include #include #include -#include +#include namespace AZ { diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.cpp new file mode 100644 index 0000000000..bdb5686e89 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.cpp @@ -0,0 +1,82 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +//#include "Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h" + +#include +#include + +namespace AZ +{ + namespace Render + { + void EditorDiffuseGlobalIlluminationComponent::Reflect(AZ::ReflectContext* context) + { + BaseClass::Reflect(context); + + if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1); + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class( + "Diffuse Global Illumination", "Diffuse Global Illumination configuration") + ->ClassElement(Edit::ClassElements::EditorData, "") + ->Attribute(Edit::Attributes::Category, "Atom") + ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Component_Placeholder.svg") + ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.png") + ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector({ AZ_CRC("Level", 0x9aeacc13), AZ_CRC("Game", 0x232b318c) })) + ->Attribute(Edit::Attributes::AutoExpand, true) + ->Attribute(Edit::Attributes::HelpPageURL, "https://") + ; + + editContext->Class( + "ToneMapperComponentControl", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &DiffuseGlobalIlluminationComponentController::m_configuration, "Configuration", "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ; + + editContext->Class("DiffuseGlobalIlluminationComponentConfig", "") + ->ClassElement(Edit::ClassElements::EditorData, "") + ->DataElement(Edit::UIHandlers::ComboBox, &DiffuseGlobalIlluminationComponentConfig::m_qualityLevel, "Quality Level", "Quality Level") + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + ->EnumAttribute(DiffuseGlobalIlluminationQualityLevel::Low, "Low") + ->EnumAttribute(DiffuseGlobalIlluminationQualityLevel::Medium, "Medium") + ->EnumAttribute(DiffuseGlobalIlluminationQualityLevel::High, "High") + ; + } + } + + if (auto behaviorContext = azrtti_cast(context)) + { + behaviorContext->ConstantProperty("EditorDiffuseGlobalIlluminationComponentTypeId", BehaviorConstant(Uuid(EditorDiffuseGlobalIlluminationComponentTypeId))) + ->Attribute(AZ::Script::Attributes::Module, "render") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation); + } + } + + EditorDiffuseGlobalIlluminationComponent::EditorDiffuseGlobalIlluminationComponent(const DiffuseGlobalIlluminationComponentConfig& config) + : BaseClass(config) + { + } + + u32 EditorDiffuseGlobalIlluminationComponent::OnConfigurationChanged() + { + m_controller.OnConfigChanged(); + return Edit::PropertyRefreshLevels::AttributesAndValues; + } + } +} diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.h new file mode 100644 index 0000000000..2a478665c5 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.h @@ -0,0 +1,40 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include + +namespace AZ +{ + namespace Render + { + class EditorDiffuseGlobalIlluminationComponent final + : public AzToolsFramework::Components::EditorComponentAdapter + { + public: + + using BaseClass = AzToolsFramework::Components::EditorComponentAdapter; + AZ_EDITOR_COMPONENT(AZ::Render::EditorDiffuseGlobalIlluminationComponent, EditorDiffuseGlobalIlluminationComponentTypeId, BaseClass); + + static void Reflect(AZ::ReflectContext* context); + + EditorDiffuseGlobalIlluminationComponent() = default; + EditorDiffuseGlobalIlluminationComponent(const DiffuseGlobalIlluminationComponentConfig& config); + + //! EditorRenderComponentAdapter overrides... + AZ::u32 OnConfigurationChanged() override; + }; + + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp similarity index 99% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.cpp rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp index 1e5b959803..caf9ecd007 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp @@ -10,7 +10,7 @@ * */ -#include +#include #include #include #include diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.h similarity index 97% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.h rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.h index 15c46d45ba..2e013ca479 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.h @@ -16,8 +16,8 @@ #include #include #include -#include -#include +#include +#include #include namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp index bdc82acda6..65fa7bc286 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp @@ -70,6 +70,12 @@ namespace AZ } } + if (classElement.GetVersion() < 5) + { + classElement.RemoveElementByName(AZ_CRC_CE("matModUvOverrides")); + classElement.RemoveElementByName(AZ_CRC_CE("propertyOverrides")); + } + return true; } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp index 2ef4e1e229..6edbd8c3ed 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp @@ -18,12 +18,14 @@ #include #include #include -#include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -47,7 +49,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -55,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -111,9 +115,11 @@ namespace AZ EntityReferenceComponent::CreateDescriptor(), GradientWeightModifierComponent::CreateDescriptor(), DiffuseProbeGridComponent::CreateDescriptor(), + DiffuseGlobalIlluminationComponent::CreateDescriptor(), DeferredFogComponent::CreateDescriptor(), SurfaceData::SurfaceDataMeshComponent::CreateDescriptor(), AttachmentComponent::CreateDescriptor(), + OcclusionCullingPlaneComponent::CreateDescriptor(), #ifdef ATOMLYINTEGRATION_FEATURE_COMMON_EDITOR EditorAreaLightComponent::CreateDescriptor(), @@ -142,9 +148,11 @@ namespace AZ EditorEntityReferenceComponent::CreateDescriptor(), EditorGradientWeightModifierComponent::CreateDescriptor(), EditorDiffuseProbeGridComponent::CreateDescriptor(), + EditorDiffuseGlobalIlluminationComponent::CreateDescriptor(), EditorDeferredFogComponent::CreateDescriptor(), SurfaceData::EditorSurfaceDataMeshComponent::CreateDescriptor(), EditorAttachmentComponent::CreateDescriptor(), + EditorOcclusionCullingPlaneComponent::CreateDescriptor(), #endif }); } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp new file mode 100644 index 0000000000..b027be3171 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp @@ -0,0 +1,95 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + void EditorOcclusionCullingPlaneComponent::Reflect(AZ::ReflectContext* context) + { + BaseClass::Reflect(context); + + if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1, ConvertToEditorRenderComponentAdapter<1>) + ; + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class( + "Occlusion Culling Plane", "The OcclusionCullingPlane component is used to cull meshes that are inside the view frustum and behind the occlusion plane") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Category, "Atom") + ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Component_Placeholder.svg") + ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.png") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ; + + editContext->Class( + "OcclusionCullingPlaneComponentController", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &OcclusionCullingPlaneComponentController::m_configuration, "Configuration", "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ; + + editContext->Class( + "OcclusionCullingPlaneComponentConfig", "") + ->ClassElement(AZ::Edit::ClassElements::Group, "Settings") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::CheckBox, &OcclusionCullingPlaneComponentConfig::m_showVisualization, "Show Visualization", "Show the occlusion culling plane visualization") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + ->DataElement(AZ::Edit::UIHandlers::CheckBox, &OcclusionCullingPlaneComponentConfig::m_transparentVisualization, "Transparent Visualization", "Sets the occlusion culling plane visualization as transparent") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + ; + } + } + + if (auto behaviorContext = azrtti_cast(context)) + { + behaviorContext->ConstantProperty("EditorOcclusionCullingPlaneComponentTypeId", BehaviorConstant(Uuid(EditorOcclusionCullingPlaneComponentTypeId))) + ->Attribute(AZ::Script::Attributes::Module, "render") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation); + } + } + + EditorOcclusionCullingPlaneComponent::EditorOcclusionCullingPlaneComponent() + { + } + + EditorOcclusionCullingPlaneComponent::EditorOcclusionCullingPlaneComponent(const OcclusionCullingPlaneComponentConfig& config) + : BaseClass(config) + { + } + + void EditorOcclusionCullingPlaneComponent::Activate() + { + BaseClass::Activate(); + AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(GetEntityId()); + } + + void EditorOcclusionCullingPlaneComponent::Deactivate() + { + AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect(); + BaseClass::Deactivate(); + } + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.h new file mode 100644 index 0000000000..8070c1d553 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.h @@ -0,0 +1,43 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class EditorOcclusionCullingPlaneComponent final + : public EditorRenderComponentAdapter + , private AzFramework::EntityDebugDisplayEventBus::Handler + { + public: + using BaseClass = EditorRenderComponentAdapter; + AZ_EDITOR_COMPONENT(AZ::Render::EditorOcclusionCullingPlaneComponent, EditorOcclusionCullingPlaneComponentTypeId, BaseClass); + + static void Reflect(AZ::ReflectContext* context); + + EditorOcclusionCullingPlaneComponent(); + EditorOcclusionCullingPlaneComponent(const OcclusionCullingPlaneComponentConfig& config); + + // AZ::Component overrides + void Activate() override; + void Deactivate() override; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.cpp new file mode 100644 index 0000000000..567809266d --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.cpp @@ -0,0 +1,43 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include + +namespace AZ +{ + namespace Render + { + OcclusionCullingPlaneComponent::OcclusionCullingPlaneComponent(const OcclusionCullingPlaneComponentConfig& config) + : BaseClass(config) + { + } + + void OcclusionCullingPlaneComponent::Reflect(AZ::ReflectContext* context) + { + BaseClass::Reflect(context); + + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ; + } + + if (auto behaviorContext = azrtti_cast(context)) + { + behaviorContext->ConstantProperty("OcclusionCullingPlaneComponentTypeId", BehaviorConstant(Uuid(OcclusionCullingPlaneComponentTypeId))) + ->Attribute(AZ::Script::Attributes::Module, "render") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common); + } + } + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.h new file mode 100644 index 0000000000..7e7b48bd45 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.h @@ -0,0 +1,37 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class OcclusionCullingPlaneComponent final + : public AzFramework::Components::ComponentAdapter + { + public: + using BaseClass = AzFramework::Components::ComponentAdapter; + AZ_COMPONENT(AZ::Render::OcclusionCullingPlaneComponent, OcclusionCullingPlaneComponentTypeId, BaseClass); + + OcclusionCullingPlaneComponent() = default; + OcclusionCullingPlaneComponent(const OcclusionCullingPlaneComponentConfig& config); + + static void Reflect(AZ::ReflectContext* context); + }; + + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentConstants.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentConstants.h new file mode 100644 index 0000000000..59276de9ee --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentConstants.h @@ -0,0 +1,22 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +namespace AZ +{ + namespace Render + { + static constexpr const char* const OcclusionCullingPlaneComponentTypeId = "{F7537387-15A8-48F0-A1F3-D19C5886B886}"; + static constexpr const char* const EditorOcclusionCullingPlaneComponentTypeId = "{BE7CC17B-32EB-49B0-BAD9-D26E3A059012}"; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.cpp new file mode 100644 index 0000000000..cf8107776f --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.cpp @@ -0,0 +1,143 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace AZ +{ + namespace Render + { + void OcclusionCullingPlaneComponentConfig::Reflect(ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("ShowVisualization", &OcclusionCullingPlaneComponentConfig::m_showVisualization) + ->Field("TransparentVisualization", &OcclusionCullingPlaneComponentConfig::m_transparentVisualization) + ; + } + } + + void OcclusionCullingPlaneComponentController::Reflect(ReflectContext* context) + { + OcclusionCullingPlaneComponentConfig::Reflect(context); + + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("Configuration", &OcclusionCullingPlaneComponentController::m_configuration); + } + } + + void OcclusionCullingPlaneComponentController::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + dependent.push_back(AZ_CRC("TransformService", 0x8ee22c50)); + } + + void OcclusionCullingPlaneComponentController::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC("OcclusionCullingPlaneService", 0x9123f33d)); + } + + void OcclusionCullingPlaneComponentController::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC("OcclusionCullingPlaneService", 0x9123f33d)); + } + + void OcclusionCullingPlaneComponentController::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + required.push_back(AZ_CRC("TransformService")); + } + + OcclusionCullingPlaneComponentController::OcclusionCullingPlaneComponentController(const OcclusionCullingPlaneComponentConfig& config) + : m_configuration(config) + { + } + + void OcclusionCullingPlaneComponentController::Activate(AZ::EntityId entityId) + { + m_entityId = entityId; + + TransformNotificationBus::Handler::BusConnect(m_entityId); + + m_featureProcessor = RPI::Scene::GetFeatureProcessorForEntity(entityId); + AZ_Assert(m_featureProcessor, "OcclusionCullingPlaneComponentController was unable to find a OcclusionCullingPlaneFeatureProcessor on the EntityContext provided."); + + m_transformInterface = TransformBus::FindFirstHandler(entityId); + AZ_Assert(m_transformInterface, "Unable to attach to a TransformBus handler"); + if (!m_transformInterface) + { + return; + } + + // add this occlusion plane to the feature processor + const AZ::Transform& transform = m_transformInterface->GetWorldTM(); + m_handle = m_featureProcessor->AddOcclusionCullingPlane(transform); + + // set visualization + m_featureProcessor->ShowVisualization(m_handle, m_configuration.m_showVisualization); + m_featureProcessor->SetTransparentVisualization(m_handle, m_configuration.m_transparentVisualization); + } + + void OcclusionCullingPlaneComponentController::Deactivate() + { + if (m_featureProcessor) + { + m_featureProcessor->RemoveOcclusionCullingPlane(m_handle); + } + + Data::AssetBus::MultiHandler::BusDisconnect(); + TransformNotificationBus::Handler::BusDisconnect(); + + m_transformInterface = nullptr; + m_featureProcessor = nullptr; + } + + void OcclusionCullingPlaneComponentController::SetConfiguration(const OcclusionCullingPlaneComponentConfig& config) + { + m_configuration = config; + } + + const OcclusionCullingPlaneComponentConfig& OcclusionCullingPlaneComponentController::GetConfiguration() const + { + return m_configuration; + } + + void OcclusionCullingPlaneComponentController::OnTransformChanged([[maybe_unused]] const AZ::Transform& local, const AZ::Transform& world) + { + if (!m_featureProcessor) + { + return; + } + + m_featureProcessor->SetTransform(m_handle, world); + } + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h new file mode 100644 index 0000000000..2d977a2cf3 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h @@ -0,0 +1,81 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class OcclusionCullingPlaneComponentConfig final + : public AZ::ComponentConfig + { + public: + AZ_RTTI(AZ::Render::OcclusionCullingPlaneComponentConfig, "{D0E107CA-5AFB-4675-BC97-94BCA5F248DB}", ComponentConfig); + AZ_CLASS_ALLOCATOR(OcclusionCullingPlaneComponentConfig, SystemAllocator, 0); + static void Reflect(AZ::ReflectContext* context); + + bool m_showVisualization = true; + bool m_transparentVisualization = false; + + OcclusionCullingPlaneComponentConfig() = default; + }; + + class OcclusionCullingPlaneComponentController final + : public Data::AssetBus::MultiHandler + , private TransformNotificationBus::Handler + { + public: + friend class EditorOcclusionCullingPlaneComponent; + + AZ_CLASS_ALLOCATOR(OcclusionCullingPlaneComponentController, AZ::SystemAllocator, 0); + AZ_RTTI(AZ::Render::OcclusionCullingPlaneComponentController, "{8EDA3C7D-5171-4843-9969-4D84DB13F221}"); + + static void Reflect(AZ::ReflectContext* context); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + + OcclusionCullingPlaneComponentController() = default; + OcclusionCullingPlaneComponentController(const OcclusionCullingPlaneComponentConfig& config); + + void Activate(AZ::EntityId entityId); + void Deactivate(); + void SetConfiguration(const OcclusionCullingPlaneComponentConfig& config); + const OcclusionCullingPlaneComponentConfig& GetConfiguration() const; + + private: + + AZ_DISABLE_COPY(OcclusionCullingPlaneComponentController); + + // TransformNotificationBus overrides + void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override; + + // handle for this occlusion plane in the feature processor + OcclusionCullingPlaneHandle m_handle; + + OcclusionCullingPlaneFeatureProcessorInterface* m_featureProcessor = nullptr; + TransformInterface* m_transformInterface = nullptr; + AZ::EntityId m_entityId; + OcclusionCullingPlaneComponentConfig m_configuration; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake index 9072cd54f2..020f9014aa 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake @@ -24,8 +24,10 @@ set(FILES Source/CoreLights/EditorDirectionalLightComponent.cpp Source/Decals/EditorDecalComponent.h Source/Decals/EditorDecalComponent.cpp - Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.h - Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.cpp + Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.h + Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp + Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.h + Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.cpp Source/Grid/EditorGridComponent.h Source/Grid/EditorGridComponent.cpp Source/ImageBasedLights/EditorImageBasedLightComponent.h @@ -54,6 +56,8 @@ set(FILES Source/Mesh/EditorMeshSystemComponent.h Source/Mesh/MeshThumbnail.h Source/Mesh/MeshThumbnail.cpp + Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.h + Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp Source/PostProcess/EditorPostFxLayerComponent.cpp Source/PostProcess/EditorPostFxLayerComponent.h Source/PostProcess/Bloom/EditorBloomComponent.cpp diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake index e13d1d37d6..3a08d81c90 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake @@ -43,10 +43,16 @@ set(FILES Source/Decals/DecalComponent.cpp Source/Decals/DecalComponentController.h Source/Decals/DecalComponentController.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridComponent.h - Source/DiffuseProbeGrid/DiffuseProbeGridComponent.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.h - Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridComponent.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridComponent.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.cpp + Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.h + Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.cpp + Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.h + Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp + Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h + Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.cpp Source/Grid/GridComponent.h Source/Grid/GridComponent.cpp Source/Grid/GridComponentConfig.cpp @@ -66,6 +72,10 @@ set(FILES Source/Mesh/MeshComponent.cpp Source/Mesh/MeshComponentController.h Source/Mesh/MeshComponentController.cpp + Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.h + Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.cpp + Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h + Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.cpp Source/PostProcess/PostFxLayerComponent.cpp Source/PostProcess/PostFxLayerComponent.h Source/PostProcess/PostFxLayerComponentConfig.cpp diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp index f41ef165c8..b0065708fb 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp @@ -299,8 +299,7 @@ namespace EMotionFX void ActorComponent::OnAssetReady(AZ::Data::Asset asset) { m_configuration.m_actorAsset = asset; - Actor* actor = m_configuration.m_actorAsset->GetActor(); - AZ_Assert(m_configuration.m_actorAsset.IsReady() && actor, "Actor asset should be loaded and actor valid."); + AZ_Assert(m_configuration.m_actorAsset.IsReady() && m_configuration.m_actorAsset->GetActor(), "Actor asset should be loaded and actor valid."); CheckActorCreation(); } diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp index c93c09cfbc..bd1cf40da8 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp @@ -81,8 +81,10 @@ namespace Multiplayer }; m_netSpawnTicket = AzFramework::EntitySpawnTicket(m_networkSpawnableAsset); + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_preInsertionCallback = AZStd::move(preInsertionCallback); AzFramework::SpawnableEntitiesInterface::Get()->SpawnEntities( - m_netSpawnTicket, AzFramework::SpawnablePriority_Default, { m_netEntityIndex }, preInsertionCallback); + m_netSpawnTicket, { m_netEntityIndex }, AZStd::move(optionalArgs)); } } @@ -90,7 +92,7 @@ namespace Multiplayer { if(m_netSpawnTicket.IsValid()) { - AzFramework::SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_netSpawnTicket, AzFramework::SpawnablePriority_Default); + AzFramework::SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_netSpawnTicket); } } diff --git a/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp b/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp index 6ace4db592..7b09b65de4 100644 --- a/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp +++ b/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp @@ -30,7 +30,6 @@ namespace UnitTest { SetupAllocator(); AZ::NameDictionary::Create(); - m_spawnableComponent = new AzFramework::SpawnableSystemComponent(); m_netComponent = new AzNetworking::NetworkingSystemComponent(); m_mpComponent = new Multiplayer::MultiplayerSystemComponent(); @@ -46,7 +45,6 @@ namespace UnitTest { delete m_mpComponent; delete m_netComponent; - delete m_spawnableComponent; AZ::NameDictionary::Destroy(); TeardownAllocator(); } @@ -76,7 +74,6 @@ namespace UnitTest AzNetworking::NetworkingSystemComponent* m_netComponent = nullptr; Multiplayer::MultiplayerSystemComponent* m_mpComponent = nullptr; - AzFramework::SpawnableSystemComponent* m_spawnableComponent = nullptr; }; TEST_F(MultiplayerSystemTests, TestInitEvent) diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp index ad53236108..a7875c2615 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp @@ -134,7 +134,9 @@ namespace ScriptCanvas::Nodeables::Spawning m_spawnBatchSizes.push_back(view.size()); }; - AzFramework::SpawnableEntitiesInterface::Get()->SpawnAllEntities( - m_spawnTicket, AzFramework::SpawnablePriority_Default, preSpawnCB, spawnCompleteCB); + AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; + optionalArgs.m_preInsertionCallback = AZStd::move(preSpawnCB); + optionalArgs.m_completionCallback = AZStd::move(spawnCompleteCB); + AzFramework::SpawnableEntitiesInterface::Get()->SpawnAllEntities(m_spawnTicket, AZStd::move(optionalArgs)); } } diff --git a/Registry/Platform/Mac/streamer.editor.setreg b/Registry/Platform/Mac/streamer.editor.setreg new file mode 100644 index 0000000000..ccec8c0cc6 --- /dev/null +++ b/Registry/Platform/Mac/streamer.editor.setreg @@ -0,0 +1,33 @@ +{ + "Amazon": + { + "AzCore": + { + "Streamer": + { + "Profiles": + { + "Generic": + { + "Stack": + [ + { + "$type": "AZ::IO::StorageDriveConfig", + // The maximum number of file handles that the drive will cache. + // On Mac the default limit for the number of file handles an application + // can have open is set to 256. So we need to set this to a lower value than on PC. + // This limit is set by "launchctl limit maxfiles" + "MaxFileHandles": 65 + }, + { + "$type": "AzFramework::RemoteStorageDriveConfig", + // The maximum number of file handles that the drive will cache. + "MaxFileHandles": 1024 + } + ] + } + } + } + } + } +} diff --git a/Templates/DefaultProject/template.json b/Templates/DefaultProject/template.json index 26b868d315..6f74fb6b26 100644 --- a/Templates/DefaultProject/template.json +++ b/Templates/DefaultProject/template.json @@ -4,8 +4,9 @@ "restricted_platform_relative_path": "Templates", "origin": "The primary repo for DefaultProject goes here: i.e. http://www.mydomain.com", "license": "What license DefaultProject uses goes here: i.e. https://opensource.org/licenses/MIT", - "display_name": "DefaultProject", + "display_name": "Default", "summary": "A short description of DefaultProject.", + "included_gems": ["Atom","Camera","EMotionFX","UI","Maestro","Input","ImGui"], "canonical_tags": [], "user_tags": [ "DefaultProject" @@ -651,4 +652,4 @@ "origin": "Shaders" } ] -} \ No newline at end of file +} diff --git a/cmake/Platform/Common/Directory.Build.props b/cmake/Platform/Common/Directory.Build.props index b74fa48471..951d3c6605 100644 --- a/cmake/Platform/Common/Directory.Build.props +++ b/cmake/Platform/Common/Directory.Build.props @@ -15,4 +15,9 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. true true + + + TurnOffAllWarnings + + \ No newline at end of file diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index 016c0d623d..4aeaf21e95 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -182,8 +182,20 @@ set_property(TARGET ${TARGET_NAME} endif() endif() - file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/${NAME_PLACEHOLDER}_$.cmake" CONTENT "${target_file_contents}") - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/${NAME_PLACEHOLDER}_$.cmake" + if(IS_ABSOLUTE ${target_source_dir}) + # This normally applies the target_source_dir is outside of the engine root + # such as when invoking ly_setup_subdirectory from the project + # Therefore the final directory component of the target source directory is used first 8 characters + # of a SHA256 hash + string(SHA256 target_source_hash ${target_source_dir}) + string(SUBSTRING ${target_source_hash} 0 8 target_source_hash) + get_filename_component(target_source_folder_name ${target_source_dir} NAME) + set(target_source_dir "${target_source_folder_name}-${target_source_hash}") + endif() + + set(target_install_source_dir ${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}) + file(GENERATE OUTPUT "${target_install_source_dir}/${NAME_PLACEHOLDER}_$.cmake" CONTENT "${target_file_contents}") + install(FILES "${target_install_source_dir}/${NAME_PLACEHOLDER}_$.cmake" DESTINATION ${target_source_dir} COMPONENT ${install_component} ) @@ -235,18 +247,32 @@ function(ly_setup_subdirectory absolute_target_source_dir) endforeach() file(READ ${LY_ROOT_FOLDER}/cmake/install/Copyright.in cmake_copyright_comment) - # Write out all the agreegated ly_add_target function calls and the final ly_create_alias() calls to the target CMakeList.txt - file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/CMakeLists.txt + + if(IS_ABSOLUTE ${target_source_dir}) + # This normally applies the target_source_dir is outside of the engine root + # such as when invoking ly_setup_subdirectory from the project + # Therefore the final directory component of the target source directory is used first 8 characters + # of a SHA256 hash + string(SHA256 target_source_hash ${target_source_dir}) + string(SUBSTRING ${target_source_hash} 0 8 target_source_hash) + get_filename_component(target_source_folder_name ${target_source_dir} NAME) + set(target_source_dir "${target_source_folder_name}-${target_source_hash}") + endif() + + # Initialize the target install source directory to path underneath the current binary directory + set(target_install_source_dir ${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}) + # Write out all the aggregated ly_add_target function calls and the final ly_create_alias() calls to the target CMakeLists.txt + file(WRITE ${target_install_source_dir}/CMakeLists.txt "${cmake_copyright_comment}" "${all_configured_targets}" "\n" "${CREATE_ALIASES_PLACEHOLDER}" ) - # get the component ID. if the property isn't set for the directory, it will auto fallback to use CMAKE_INSTALL_DEFAULT_COMPONENT_NAME + # get the component ID. if the property isn't set for the directory, it will auto fallback to use CMAKE_INSTALL_DEFAULT_COMPONENT_NAME get_property(install_component DIRECTORY ${absolute_target_source_dir} PROPERTY INSTALL_COMPONENT) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/CMakeLists.txt" + install(FILES "${target_install_source_dir}/CMakeLists.txt" DESTINATION ${target_source_dir} COMPONENT ${install_component} ) diff --git a/cmake/Platform/Common/MSVC/Configurations_msvc.cmake b/cmake/Platform/Common/MSVC/Configurations_msvc.cmake index 357d578c44..bcff2adeb7 100644 --- a/cmake/Platform/Common/MSVC/Configurations_msvc.cmake +++ b/cmake/Platform/Common/MSVC/Configurations_msvc.cmake @@ -69,10 +69,7 @@ ly_append_configurations_options( /wd4267 # conversion, possible loss of data /wd4310 # cast truncates constant value /wd4324 # structure was padded due to alignment specifier - /wd4366 # the result of unary operator may be unaligned /wd4389 # comparison, signed/unsigned mismatch - /wd4436 # the result of unary operator may be unaligned - /wd4450 # declaration hides global declaration # Enabling warnings that are disabled by default from /W4 # https://docs.microsoft.com/en-us/cpp/preprocessor/compiler-warnings-that-are-off-by-default?view=vs-2019 @@ -127,7 +124,7 @@ ly_append_configurations_options( /INCREMENTAL:NO ) -set(LY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG TRUE CACHE BOOL "Indicates if incremental linking is used in debug configurations (default = TRUE)") +set(LY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG FALSE CACHE BOOL "Indicates if incremental linking is used in debug configurations (default = FALSE)") if(LY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG) ly_append_configurations_options( COMPILATION_DEBUG diff --git a/cmake/Projects.cmake b/cmake/Projects.cmake index adaf7ee15f..eadfa79c1b 100644 --- a/cmake/Projects.cmake +++ b/cmake/Projects.cmake @@ -167,9 +167,6 @@ endfunction() # Add the projects here so the above function is found foreach(project ${LY_PROJECTS}) file(REAL_PATH ${project} full_directory_path BASE_DIRECTORY ${CMAKE_SOURCE_DIR}) - if(NOT LY_FIRST_PROJECT) - ly_set(LY_FIRST_PROJECT_PATH ${full_directory_path}) - endif() string(SHA256 full_directory_hash ${full_directory_path}) # Truncate the full_directory_hash down to 8 characters to avoid hitting the Windows 260 character path limit @@ -182,4 +179,13 @@ foreach(project ${LY_PROJECTS}) ly_generate_project_build_path_setreg(${full_directory_path}) add_project_json_external_subdirectories(${full_directory_path}) endforeach() + +# If just one project is defined we pass it as a parameter to the applications +list(LENGTH LY_PROJECTS projects_length) +if(projects_length EQUAL "1") + list(GET LY_PROJECTS 0 project) + file(REAL_PATH ${project} full_directory_path BASE_DIRECTORY ${CMAKE_SOURCE_DIR}) + ly_set(LY_DEFAULT_PROJECT_PATH ${full_directory_path}) +endif() + ly_set(LY_PROJECTS_FOLDER_NAME ${LY_PROJECTS_FOLDER_NAME}) diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index 2f6be2b060..1bce2988bf 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -313,13 +313,13 @@ def PreBuildCommonSteps(Map pipelineConfig, String repositoryName, String projec script: 'python/get_python.bat' } - if(env.CLEAN_OUTPUT_DIRECTORY?.toBoolean() || env.CLEAN_ASSETS?.toBoolean()) { - def command = "${pipelineConfig.PYTHON_DIR}/python" - if(env.IS_UNIX) command += '.sh' - else command += '.cmd' - command += " -u ${pipelineConfig.BUILD_ENTRY_POINT} --platform ${platform} --type clean" - palSh(command, "Running ${platform} clean") - } + // Always run the clean step, the scripts detect what variables were set, but it also cleans if + // the NODE_LABEL has changed + def command = "${pipelineConfig.PYTHON_DIR}/python" + if(env.IS_UNIX) command += '.sh' + else command += '.cmd' + command += " -u ${pipelineConfig.BUILD_ENTRY_POINT} --platform ${platform} --type clean" + palSh(command, "Running ${platform} clean") } } @@ -578,14 +578,12 @@ finally { ) } node('controller') { - emailRecipients = [[$class: 'RequesterRecipientProvider']] - if (env.WATCHED_BRANCHES.tokenize(',').contains(branchName)) { - emailRecipients.add([$class: 'CulpritsRecipientProvider']) - } step([ - $class: 'Mailer', - notifyEveryUnstableBuild: true, - recipients: emailextrecipients(emailRecipients) + $class: 'Mailer', + notifyEveryUnstableBuild: true, + recipients: emailextrecipients([ + [$class: 'RequesterRecipientProvider'] + ]) ]) } } catch(Exception e) { diff --git a/scripts/build/Platform/Linux/build_linux.sh b/scripts/build/Platform/Linux/build_linux.sh index ef1b1dcb96..f88d3ab116 100755 --- a/scripts/build/Platform/Linux/build_linux.sh +++ b/scripts/build/Platform/Linux/build_linux.sh @@ -14,7 +14,6 @@ set -o errexit # exit on the first failure encountered BASEDIR=$(dirname "$0") source $BASEDIR/env_linux.sh -source $BASEDIR/clean_linux.sh mkdir -p ${OUTPUT_DIRECTORY} SOURCE_DIRECTORY=${PWD} diff --git a/scripts/build/Platform/Mac/build_mac.sh b/scripts/build/Platform/Mac/build_mac.sh index 4a61f97fe4..473a968d98 100755 --- a/scripts/build/Platform/Mac/build_mac.sh +++ b/scripts/build/Platform/Mac/build_mac.sh @@ -14,7 +14,6 @@ set -o errexit # exit on the first failure encountered BASEDIR=$(dirname "$0") source $BASEDIR/env_mac.sh -source $BASEDIR/clean_mac.sh mkdir -p ${OUTPUT_DIRECTORY} SOURCE_DIRECTORY=${PWD} diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index fc4668de0a..552ef2c6fd 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -87,7 +87,7 @@ "PARAMETERS": { "CONFIGURATION": "debug", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG=FALSE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" @@ -101,7 +101,7 @@ "PARAMETERS": { "CONFIGURATION": "debug", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG=FALSE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_smoke TEST_SUITE_main", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", diff --git a/scripts/build/Platform/Windows/build_windows.cmd b/scripts/build/Platform/Windows/build_windows.cmd index 474c1720df..4dcd12d008 100644 --- a/scripts/build/Platform/Windows/build_windows.cmd +++ b/scripts/build/Platform/Windows/build_windows.cmd @@ -13,7 +13,6 @@ REM SETLOCAL EnableDelayedExpansion CALL %~dp0env_windows.cmd -CALL %~dp0clean_windows.cmd IF NOT EXIST "%OUTPUT_DIRECTORY%" ( MKDIR %OUTPUT_DIRECTORY%. diff --git a/scripts/o3de/o3de/register.py b/scripts/o3de/o3de/register.py index 4e73edca7f..b3a6a1e44c 100644 --- a/scripts/o3de/o3de/register.py +++ b/scripts/o3de/o3de/register.py @@ -580,65 +580,65 @@ def register(engine_path: str or pathlib.Path = None, if not project_path: logger.error(f'Project path cannot be empty.') return 1 - result = register_project_path(json_data, project_path, remove, engine_path) + result = result or register_project_path(json_data, project_path, remove, engine_path) - elif isinstance(gem_path, str) or isinstance(gem_path, pathlib.PurePath): + if isinstance(gem_path, str) or isinstance(gem_path, pathlib.PurePath): if not gem_path: logger.error(f'Gem path cannot be empty.') return 1 - result = register_gem_path(json_data, gem_path, remove, + result = result or register_gem_path(json_data, gem_path, remove, external_subdir_engine_path, external_subdir_project_path) - elif isinstance(external_subdir_path, str) or isinstance(external_subdir_path, pathlib.PurePath): + if isinstance(external_subdir_path, str) or isinstance(external_subdir_path, pathlib.PurePath): if not external_subdir_path: logger.error(f'External Subdirectory path is None.') return 1 - result = register_external_subdirectory(json_data, external_subdir_path, remove, + result = result or register_external_subdirectory(json_data, external_subdir_path, remove, external_subdir_engine_path, external_subdir_project_path) - elif isinstance(template_path, str) or isinstance(template_path, pathlib.PurePath): + if isinstance(template_path, str) or isinstance(template_path, pathlib.PurePath): if not template_path: logger.error(f'Template path cannot be empty.') return 1 - result = register_template_path(json_data, template_path, remove, engine_path) + result = result or register_template_path(json_data, template_path, remove, engine_path) - elif isinstance(restricted_path, str) or isinstance(restricted_path, pathlib.PurePath): + if isinstance(restricted_path, str) or isinstance(restricted_path, pathlib.PurePath): if not restricted_path: logger.error(f'Restricted path cannot be empty.') return 1 - result = register_restricted_path(json_data, restricted_path, remove, engine_path) + result = result or register_restricted_path(json_data, restricted_path, remove, engine_path) - elif isinstance(repo_uri, str) or isinstance(repo_uri, pathlib.PurePath): + if isinstance(repo_uri, str) or isinstance(repo_uri, pathlib.PurePath): if not repo_uri: logger.error(f'Repo URI cannot be empty.') return 1 - result = register_repo(json_data, repo_uri, remove) + result = result or register_repo(json_data, repo_uri, remove) - elif isinstance(default_engines_folder, str) or isinstance(default_engines_folder, pathlib.PurePath): - result = register_default_engines_folder(json_data, default_engines_folder, remove) + if isinstance(default_engines_folder, str) or isinstance(default_engines_folder, pathlib.PurePath): + result = result or register_default_engines_folder(json_data, default_engines_folder, remove) - elif isinstance(default_projects_folder, str) or isinstance(default_projects_folder, pathlib.PurePath): - result = register_default_projects_folder(json_data, default_projects_folder, remove) + if isinstance(default_projects_folder, str) or isinstance(default_projects_folder, pathlib.PurePath): + result = result or register_default_projects_folder(json_data, default_projects_folder, remove) - elif isinstance(default_gems_folder, str) or isinstance(default_gems_folder, pathlib.PurePath): - result = register_default_gems_folder(json_data, default_gems_folder, remove) + if isinstance(default_gems_folder, str) or isinstance(default_gems_folder, pathlib.PurePath): + result = result or register_default_gems_folder(json_data, default_gems_folder, remove) - elif isinstance(default_templates_folder, str) or isinstance(default_templates_folder, pathlib.PurePath): - result = register_default_templates_folder(json_data, default_templates_folder, remove) + if isinstance(default_templates_folder, str) or isinstance(default_templates_folder, pathlib.PurePath): + result = result or register_default_templates_folder(json_data, default_templates_folder, remove) - elif isinstance(default_restricted_folder, str) or isinstance(default_restricted_folder, pathlib.PurePath): - result = register_default_restricted_folder(json_data, default_restricted_folder, remove) + if isinstance(default_restricted_folder, str) or isinstance(default_restricted_folder, pathlib.PurePath): + result = result or register_default_restricted_folder(json_data, default_restricted_folder, remove) - elif default_third_party_folder: - result = register_default_third_party_folder(json_data, default_third_party_folder, remove) + if isinstance(default_third_party_folder, str) or isinstance(default_third_party_folder, pathlib.PurePath): + result = result or register_default_third_party_folder(json_data, default_third_party_folder, remove) # engine is done LAST # Now that everything that could have an engine context is done, if the engine is supplied that means this is # registering the engine itself - elif isinstance(engine_path, str) or isinstance(engine_path, pathlib.PurePath): + if isinstance(engine_path, str) or isinstance(engine_path, pathlib.PurePath): if not engine_path: logger.error(f'Engine path cannot be empty.') return 1 - result = register_engine_path(json_data, engine_path, remove, force) + result = result or register_engine_path(json_data, engine_path, remove, force) if not result: manifest.save_o3de_manifest(json_data)