From b5599ca739627e94e75b139f3177267d1fbdcae2 Mon Sep 17 00:00:00 2001 From: sconel Date: Wed, 26 May 2021 16:45:04 -0700 Subject: [PATCH] Add asset picker support to spawn SC node and thread safety measures --- .../Serialization/EditContextConstants.inl | 1 + .../Spawnable/SpawnableAssetHandler.cpp | 7 ++ .../Spawnable/SpawnableAssetHandler.h | 1 + .../Prefab/Spawnable/ProcesedObjectStore.cpp | 5 +- .../UI/PropertyEditor/PropertyAssetCtrl.cpp | 46 +++++++++- .../UI/PropertyEditor/PropertyAssetCtrl.hxx | 7 ++ .../SpawnNodeable.ScriptCanvasNodeable.xml | 13 +++ .../Libraries/Spawning/SpawnNodeable.cpp | 90 ++++++++++++++----- .../Libraries/Spawning/SpawnNodeable.h | 18 +++- 9 files changed, 158 insertions(+), 30 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Serialization/EditContextConstants.inl b/Code/Framework/AzCore/AzCore/Serialization/EditContextConstants.inl index 1016027966..dfd0707ed2 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/EditContextConstants.inl +++ b/Code/Framework/AzCore/AzCore/Serialization/EditContextConstants.inl @@ -123,6 +123,7 @@ namespace AZ const static AZ::Crc32 NameLabelOverride = AZ_CRC("NameLabelOverride", 0x9ff79cab); const static AZ::Crc32 AssetPickerTitle = AZ_CRC_CE("AssetPickerTitle"); + const static AZ::Crc32 HideProductFilesInAssetPicker = AZ_CRC_CE("HideProductFilesInAssetPicker"); const static AZ::Crc32 ChildNameLabelOverride = AZ_CRC("ChildNameLabelOverride", 0x73dd2909); //! Container attribute that is used to override labels for its elements given the index of the element const static AZ::Crc32 IndexedChildNameLabelOverride = AZ_CRC("IndexedChildNameLabelOverride", 0x5f313ac2); diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.cpp index b3ba1568bd..da046ff172 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.cpp @@ -10,6 +10,7 @@ * */ +#include #include #include #include @@ -88,4 +89,10 @@ namespace AzFramework { extensions.push_back(Spawnable::FileExtension); } + + uint32_t SpawnableAssetHandler::BuildSubId(AZStd::string_view id) + { + AZ::Uuid subIdHash = AZ::Uuid::CreateData(id.data(), id.size()); + return azlossy_caster(subIdHash.GetHash()); + } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.h index deef314955..78268bf71a 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.h @@ -47,6 +47,7 @@ namespace AzFramework const char* GetGroup() const override; const char* GetBrowserIcon() const override; void GetAssetTypeExtensions(AZStd::vector& extensions) override; + static uint32_t BuildSubId(AZStd::string_view id); protected: LoadResult LoadAssetData( diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.cpp index 78d1332a71..050afd813d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.cpp @@ -10,7 +10,7 @@ * */ -#include +#include #include namespace AzToolsFramework::Prefab::PrefabConversionUtils @@ -73,8 +73,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils uint32_t ProcessedObjectStore::BuildSubId(AZStd::string_view id) { - AZ::Uuid subIdHash = AZ::Uuid::CreateData(id.data(), id.size()); - return azlossy_caster(subIdHash.GetHash()); + return AzFramework::SpawnableAssetHandler::BuildSubId(id); } const AZStd::string& ProcessedObjectStore::GetId() const diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp index 23f8378df5..4bf261122d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp @@ -777,6 +777,23 @@ namespace AzToolsFramework selection.SetDefaultDirectory(defaultDirectory); } + if (m_hideProductFilesInAssetPicker) + { + FilterConstType displayFilter = selection.GetDisplayFilter(); + + EntryTypeFilter* productsFilter = new EntryTypeFilter(); + productsFilter->SetEntryType(AssetBrowserEntry::AssetEntryType::Product); + + InverseFilter* noProductsFilter = new InverseFilter(); + noProductsFilter->SetFilter(FilterConstType(productsFilter)); + + CompositeFilter* compFilter = new CompositeFilter(CompositeFilter::LogicOperatorType::AND); + compFilter->AddFilter(FilterConstType(displayFilter)); + compFilter->AddFilter(FilterConstType(noProductsFilter)); + + selection.SetDisplayFilter(FilterConstType(compFilter)); + } + AssetBrowserComponentRequestBus::Broadcast(&AssetBrowserComponentRequests::PickAssets, selection, parentWidget()); if (selection.IsValid()) { @@ -785,7 +802,16 @@ namespace AzToolsFramework AZ_Assert(product || folder, "Incorrect entry type selected. Expected product or folder."); if (product) { - SetSelectedAssetID(product->GetAssetId()); + AZ::Data::AssetId selectedAssetId = product->GetAssetId(); + + // If we hid the product files a source asset was picked + // Clear the sub id as a source could have N products with different sub ids + if (m_hideProductFilesInAssetPicker) + { + selectedAssetId.m_subId = 0; + } + + SetSelectedAssetID(selectedAssetId); } else if (folder) { @@ -1172,6 +1198,16 @@ namespace AzToolsFramework return m_showProductAssetName; } + void PropertyAssetCtrl::SetHideProductFilesInAssetPicker(bool hide) + { + m_hideProductFilesInAssetPicker = hide; + } + + bool PropertyAssetCtrl::GetHideProductFilesInAssetPicker() const + { + return m_hideProductFilesInAssetPicker; + } + void PropertyAssetCtrl::SetShowThumbnail(bool enable) { m_showThumbnail = enable; @@ -1297,6 +1333,14 @@ namespace AzToolsFramework GUI->SetShowProductAssetName(showProductAssetName); } } + else if(attrib == AZ::Edit::Attributes::HideProductFilesInAssetPicker) + { + bool hideProductFilesInAssetPicker = false; + if (attrValue->Read(hideProductFilesInAssetPicker)) + { + GUI->SetHideProductFilesInAssetPicker(hideProductFilesInAssetPicker); + } + } else if (attrib == AZ::Edit::Attributes::ClearNotify) { PropertyAssetCtrl::ClearCallbackType* func = azdynamic_cast(attrValue->GetAttribute()); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx index 37af3d0594..5a6310eb35 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx @@ -158,6 +158,10 @@ namespace AzToolsFramework //! Assets can be either source or product assets generated from source assets. By default, source assets are shown in the property asset. You can override that with this flag. bool m_showProductAssetName = true; + //! Assets can be either source or product assets generated from source assets. + //! By default the asset picker shows both on an AZ::Asset<> property. You can hide product assets with this flag. + bool m_hideProductFilesInAssetPicker = false; + bool m_showThumbnail = false; bool m_showThumbnailDropDownButton = false; EditCallbackType* m_thumbnailCallback = nullptr; @@ -211,6 +215,9 @@ namespace AzToolsFramework void SetShowProductAssetName(bool enable); bool GetShowProductAssetName() const; + void SetHideProductFilesInAssetPicker(bool hide); + bool GetHideProductFilesInAssetPicker() const; + void SetShowThumbnail(bool enable); bool GetShowThumbnail() const; void SetShowThumbnailDropDownButton(bool enable); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.ScriptCanvasNodeable.xml b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.ScriptCanvasNodeable.xml index b2f48fae5f..d0c4cfd806 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.ScriptCanvasNodeable.xml +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.ScriptCanvasNodeable.xml @@ -7,6 +7,7 @@ Base="ScriptCanvas::Nodeable" Icon="Icons/ScriptCanvas/Placeholder.png" Category="Spawning" + Version="0" GeneratePropertyFriend="True" Namespace="ScriptCanvas" Description="Spawn"> @@ -21,5 +22,17 @@ /> + + + + + + + + + diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp index 0e067b65bf..93a248de5d 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp @@ -14,6 +14,7 @@ #include #include +#include namespace ScriptCanvas { @@ -23,9 +24,6 @@ namespace ScriptCanvas { SpawnNodeable::SpawnNodeable() { - AZ::Data::AssetId cubeAssetId("{90552CB9-2F29-5A2E-976C-61BF7ADACB81}", 272062881); - m_spawnableAsset = AZ::Data::AssetManager::Instance().GetAsset(cubeAssetId, AZ::Data::AssetLoadBehavior::PreLoad); - AZ::Data::AssetManager::Instance().BlockUntilLoadComplete(m_spawnableAsset); } SpawnNodeable::SpawnNodeable(const SpawnNodeable& rhs) @@ -35,35 +33,85 @@ namespace ScriptCanvas void SpawnNodeable::OnInitializeExecutionState() { + if (!AZ::TickBus::Handler::BusIsConnected()) + { + AZ::TickBus::Handler::BusConnect(); + } + + m_spawnTicket.IsValid(); m_spawnTicket = AzFramework::EntitySpawnTicket(m_spawnableAsset); } void SpawnNodeable::OnDeactivate() { + if (AZ::TickBus::Handler::BusIsConnected()) + { + AZ::TickBus::Handler::BusDisconnect(); + } + m_spawnTicket = AzFramework::EntitySpawnTicket(); } - //void SpawnNodeable::Translation(Data::Vector3Type translation) - //{ - // m_translation = translation; - //} + void SpawnNodeable::OnTick([[maybe_unused]] float delta, [[maybe_unused]] AZ::ScriptTimePoint timePoint) + { + AZStd::vector swappedSpawnedEntityList; + AZStd::vector swappedSpawnBatchSizes; + { + AZStd::lock_guard lock(m_recursiveMutex); - //void SpawnNodeable::Rotation(Data::Vector3Type rotation) - //{ - // m_rotation = rotation; - //} + swappedSpawnedEntityList.swap(m_spawnedEntityList); + swappedSpawnBatchSizes.swap(m_spawnBatchSizes); + } - //void SpawnNodeable::Scale(Data::Vector3Type scale) - //{ - // m_scale = scale; - //} + AZ::EntityId* batchBegin = swappedSpawnedEntityList.data(); + for (size_t batchSize : swappedSpawnBatchSizes) + { + if (batchSize == 0) + { + continue; + } + + AZStd::vector spawnedEntitiesBatch( + batchBegin, batchBegin + batchSize); + + CallOnSpawn(AZStd::move(spawnedEntitiesBatch)); + + batchBegin += batchSize; + } + } + + void SpawnNodeable::OnSpawnAssetChanged() + { + if (m_spawnableAsset.GetId().IsValid()) + { + AZStd::string rootSpawnableFile; + AzFramework::StringFunc::Path::GetFileName(m_spawnableAsset.GetHint().c_str(), rootSpawnableFile); + + rootSpawnableFile += AzFramework::Spawnable::DotFileExtension; + + AZ::u32 rootSubId = AzFramework::SpawnableAssetHandler::BuildSubId(AZStd::move(rootSpawnableFile)); + + if (m_spawnableAsset.GetId().m_subId != rootSubId) + { + AZ::Data::AssetId rootAssetId = m_spawnableAsset.GetId(); + rootAssetId.m_subId = rootSubId; + + m_spawnableAsset = AZ::Data::AssetManager::Instance(). + FindOrCreateAsset(rootAssetId, AZ::Data::AssetLoadBehavior::Default); + } + } + } void SpawnNodeable::RequestSpawn(Data::Vector3Type translation, Data::Vector3Type rotation, Data::NumberType scale) { + if (!m_spawnableAsset.IsReady()) + { + return; + } + auto preSpawnCB = [this, translation, rotation, scale]([[maybe_unused]] AzFramework::EntitySpawnTicket& ticket, AzFramework::SpawnableEntityContainerView view) { - AZ::Entity* rootEntity = *view.begin(); AzFramework::TransformComponent* entityTransform = @@ -81,15 +129,13 @@ namespace ScriptCanvas auto spawnCompleteCB = [this]([[maybe_unused]] AzFramework::EntitySpawnTicket& ticket, AzFramework::SpawnableConstEntityContainerView view) { - AZStd::vector spawnedEntities; - spawnedEntities.resize(view.size()); - + AZStd::lock_guard lock(m_recursiveMutex); + m_spawnedEntityList.reserve(m_spawnedEntityList.size() + view.size()); for (const AZ::Entity* entity : view) { - spawnedEntities.emplace_back(entity->GetId()); + m_spawnedEntityList.emplace_back(entity->GetId()); } - - CallOnSpawn(spawnedEntities); + m_spawnBatchSizes.push_back(view.size()); }; AzFramework::SpawnableEntitiesInterface::Get()->SpawnAllEntities(m_spawnTicket, preSpawnCB, spawnCompleteCB); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.h index 4d73449d58..25cb92742e 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.h @@ -14,7 +14,10 @@ #include +#include + #include + #include #include #include @@ -26,21 +29,28 @@ namespace ScriptCanvas namespace Spawning { class SpawnNodeable - : public ScriptCanvas::Nodeable + : public ScriptCanvas::Nodeable, + public AZ::TickBus::Handler { SCRIPTCANVAS_NODE(SpawnNodeable); public: SpawnNodeable(); - SpawnNodeable(const SpawnNodeable& rhs); void OnInitializeExecutionState() override; - void OnDeactivate() override; + //TickBus + void OnTick(float delta, AZ::ScriptTimePoint timePoint) override; + + void OnSpawnAssetChanged(); + private: - AZ::Data::Asset m_spawnableAsset; AzFramework::EntitySpawnTicket m_spawnTicket; + + AZStd::vector m_spawnedEntityList; + AZStd::vector m_spawnBatchSizes; + AZStd::recursive_mutex m_recursiveMutex; }; } }