|
@@ -25,6 +25,7 @@
|
|
|
#include <AzToolsFramework/Prefab/PrefabLoader.h>
|
|
|
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
|
|
|
#include <AzToolsFramework/Prefab/PrefabUndoHelpers.h>
|
|
|
+#include <AzToolsFramework/Prefab/Spawnable/PrefabConverterStackProfileNames.h>
|
|
|
|
|
|
namespace AzToolsFramework
|
|
|
{
|
|
@@ -379,9 +380,9 @@ namespace AzToolsFramework
|
|
|
return m_rootInstance ? m_rootInstance->GetTemplateId() : Prefab::InvalidTemplateId;
|
|
|
}
|
|
|
|
|
|
- const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& PrefabEditorEntityOwnershipService::GetPlayInEditorAssetData()
|
|
|
+ const Prefab::PrefabConversionUtils::InMemorySpawnableAssetContainer::SpawnableAssets& PrefabEditorEntityOwnershipService::GetPlayInEditorAssetData() const
|
|
|
{
|
|
|
- return m_playInEditorData.m_assets;
|
|
|
+ return m_playInEditorData.m_assetsCache.GetAllInMemorySpawnableAssets();
|
|
|
}
|
|
|
|
|
|
void PrefabEditorEntityOwnershipService::OnEntityRemoved(AZ::EntityId entityId)
|
|
@@ -405,176 +406,55 @@ namespace AzToolsFramework
|
|
|
m_validateEntitiesCallback = AZStd::move(validateEntitiesCallback);
|
|
|
}
|
|
|
|
|
|
- void PrefabEditorEntityOwnershipService::LoadReferencedAssets(AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& referencedAssets)
|
|
|
+ void PrefabEditorEntityOwnershipService::StartPlayInEditor()
|
|
|
{
|
|
|
- // Start our loads on all assets by calling GetAsset from the AssetManager
|
|
|
- for (AZ::Data::Asset<AZ::Data::AssetData>& asset : referencedAssets)
|
|
|
- {
|
|
|
- if (!asset.GetId().IsValid())
|
|
|
- {
|
|
|
- AZ_Error("Prefab", false, "Invalid asset found referenced in scene while entering game mode");
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- const AZ::Data::AssetLoadBehavior loadBehavior = asset.GetAutoLoadBehavior();
|
|
|
-
|
|
|
- if (loadBehavior == AZ::Data::AssetLoadBehavior::NoLoad)
|
|
|
- {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- AZ::Data::AssetId assetId = asset.GetId();
|
|
|
- AZ::Data::AssetType assetType = asset.GetType();
|
|
|
-
|
|
|
- asset = AZ::Data::AssetManager::Instance().GetAsset(assetId, assetType, loadBehavior);
|
|
|
-
|
|
|
- if (!asset.GetId().IsValid())
|
|
|
- {
|
|
|
- AZ_Error("Prefab", false, "Invalid asset found referenced in scene while entering game mode");
|
|
|
- continue;
|
|
|
- }
|
|
|
- }
|
|
|
+ // This is a workaround until the replacement for GameEntityContext is done
|
|
|
+ AzFramework::GameEntityContextEventBus::Broadcast(&AzFramework::GameEntityContextEventBus::Events::OnPreGameEntitiesStarted);
|
|
|
|
|
|
- // For all Preload assets we block until they're ready
|
|
|
- // We do this as a seperate pass so that we don't interrupt queuing up all other asset loads
|
|
|
- for (AZ::Data::Asset<AZ::Data::AssetData>& asset : referencedAssets)
|
|
|
+ if (m_rootInstance && !m_playInEditorData.m_isEnabled)
|
|
|
{
|
|
|
- if (!asset.GetId().IsValid())
|
|
|
+ // Construct the runtime entities and products
|
|
|
+ bool readyToCreateRootSpawnable = m_playInEditorData.m_assetsCache.IsActivated();
|
|
|
+ if (!readyToCreateRootSpawnable &&
|
|
|
+ !m_playInEditorData.m_assetsCache.Activate(Prefab::PrefabConversionUtils::PlayInEditor))
|
|
|
{
|
|
|
- AZ_Error("Prefab", false, "Invalid asset found referenced in scene while entering game mode");
|
|
|
- continue;
|
|
|
+ AZ_Error("Prefab", false, "Failed to create a prefab processing stack from key '%.*s'.", AZ_STRING_ARG(Prefab::PrefabConversionUtils::PlayInEditor));
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- const AZ::Data::AssetLoadBehavior loadBehavior = asset.GetAutoLoadBehavior();
|
|
|
-
|
|
|
- if (loadBehavior != AZ::Data::AssetLoadBehavior::PreLoad)
|
|
|
+ auto createRootSpawnableResult = m_playInEditorData.m_assetsCache.CreateInMemorySpawnableAsset(m_rootInstance->GetTemplateId(), DefaultMainSpawnableName, true);
|
|
|
+ if (createRootSpawnableResult.IsSuccess())
|
|
|
{
|
|
|
- continue;
|
|
|
- }
|
|
|
+ // make sure that PRE_NOTIFY assets get their notify before we activate, so that we can preserve the order of
|
|
|
+ // (load asset) -> (notify) -> (init) -> (activate)
|
|
|
+ AZ::Data::AssetManager::Instance().DispatchEvents();
|
|
|
|
|
|
- asset.BlockUntilLoadComplete();
|
|
|
+ m_playInEditorData.m_entities.Reset(createRootSpawnableResult.GetValue());
|
|
|
+ m_playInEditorData.m_entities.SpawnAllEntities();
|
|
|
|
|
|
- if (asset.IsError())
|
|
|
+ // This is a workaround until the replacement for GameEntityContext is done
|
|
|
+ AzFramework::GameEntityContextEventBus::Broadcast(
|
|
|
+ &AzFramework::GameEntityContextEventBus::Events::OnGameEntitiesStarted);
|
|
|
+ }
|
|
|
+ else
|
|
|
{
|
|
|
- AZ_Error("Prefab", false, "Asset with id %s failed to preload while entering game mode",
|
|
|
- asset.GetId().ToString<AZStd::string>().c_str());
|
|
|
-
|
|
|
- continue;
|
|
|
+ AZ_Error("Prefab", false, "Failed to create the root spawnable due to the error: '%.*s'", AZ_STRING_ARG(createRootSpawnableResult.GetError()));
|
|
|
+ return;
|
|
|
}
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- void PrefabEditorEntityOwnershipService::StartPlayInEditor()
|
|
|
- {
|
|
|
- // This is a workaround until the replacement for GameEntityContext is done
|
|
|
- AzFramework::GameEntityContextEventBus::Broadcast(&AzFramework::GameEntityContextEventBus::Events::OnPreGameEntitiesStarted);
|
|
|
|
|
|
- if (m_rootInstance && !m_playInEditorData.m_isEnabled)
|
|
|
- {
|
|
|
- // Construct the runtime entities and products
|
|
|
- Prefab::TemplateReference templateReference = m_prefabSystemComponent->FindTemplate(m_rootInstance->GetTemplateId());
|
|
|
- if (templateReference.has_value())
|
|
|
+ m_rootInstance->GetAllEntitiesInHierarchy([this](AZStd::unique_ptr<AZ::Entity>& entity)
|
|
|
{
|
|
|
- bool converterLoaded = m_playInEditorData.m_converter.IsLoaded();
|
|
|
- if (!converterLoaded)
|
|
|
+ AZ_Assert(entity, "Invalid entity found in root instance while starting play in editor.");
|
|
|
+ if (entity->GetState() == AZ::Entity::State::Active)
|
|
|
{
|
|
|
- converterLoaded = m_playInEditorData.m_converter.LoadStackProfile("PlayInEditor");
|
|
|
- }
|
|
|
- if (converterLoaded)
|
|
|
- {
|
|
|
- // Use a random uuid as this is only a temporary source.
|
|
|
- AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext context(AZ::Uuid::CreateRandom());
|
|
|
- Prefab::PrefabDom copy;
|
|
|
- copy.CopyFrom(templateReference->get().GetPrefabDom(), copy.GetAllocator(), false);
|
|
|
- context.AddPrefab(DefaultMainSpawnableName, AZStd::move(copy));
|
|
|
- m_playInEditorData.m_converter.ProcessPrefab(context);
|
|
|
- if (context.HasCompletedSuccessfully() && !context.GetProcessedObjects().empty())
|
|
|
- {
|
|
|
- static constexpr size_t NoRootSpawnable = AZStd::numeric_limits<size_t>::max();
|
|
|
- size_t rootSpawnableIndex = NoRootSpawnable;
|
|
|
-
|
|
|
- // Create temporary assets from the processed data.
|
|
|
- for (auto& product : context.GetProcessedObjects())
|
|
|
- {
|
|
|
- if (product.GetAssetType() == AZ::AzTypeInfo<AzFramework::Spawnable>::Uuid() &&
|
|
|
- product.GetId() ==
|
|
|
- AZStd::string::format("%s.%s", DefaultMainSpawnableName, AzFramework::Spawnable::FileExtension))
|
|
|
- {
|
|
|
- rootSpawnableIndex = m_playInEditorData.m_assets.size();
|
|
|
- }
|
|
|
-
|
|
|
- AZ::Data::AssetInfo info;
|
|
|
- info.m_assetId = product.GetAsset().GetId();
|
|
|
- info.m_assetType = product.GetAssetType();
|
|
|
- info.m_relativePath = product.GetId();
|
|
|
-
|
|
|
- AZ::Data::AssetCatalogRequestBus::Broadcast(
|
|
|
- &AZ::Data::AssetCatalogRequestBus::Events::RegisterAsset, info.m_assetId, info);
|
|
|
- m_playInEditorData.m_assets.emplace_back(product.ReleaseAsset().release(), AZ::Data::AssetLoadBehavior::Default);
|
|
|
-
|
|
|
- // Ensure the product asset is registered with the AssetManager
|
|
|
- // Hold on to the returned asset to keep ref count alive until we assign it the latest data
|
|
|
- AZ::Data::Asset<AZ::Data::AssetData> asset =
|
|
|
- AZ::Data::AssetManager::Instance().FindOrCreateAsset(info.m_assetId, info.m_assetType, AZ::Data::AssetLoadBehavior::Default);
|
|
|
-
|
|
|
- // Update the asset registered in the AssetManager with the data of our product from the Prefab Processor
|
|
|
- AZ::Data::AssetManager::Instance().AssignAssetData(m_playInEditorData.m_assets.back());
|
|
|
- }
|
|
|
-
|
|
|
- for (auto& product : context.GetProcessedObjects())
|
|
|
- {
|
|
|
- LoadReferencedAssets(product.GetReferencedAssets());
|
|
|
- }
|
|
|
-
|
|
|
- // make sure that PRE_NOTIFY assets get their notify before we activate, so that we can preserve the order of
|
|
|
- // (load asset) -> (notify) -> (init) -> (activate)
|
|
|
- AZ::Data::AssetManager::Instance().DispatchEvents();
|
|
|
-
|
|
|
- if (rootSpawnableIndex != NoRootSpawnable)
|
|
|
- {
|
|
|
- m_playInEditorData.m_entities.Reset(m_playInEditorData.m_assets[rootSpawnableIndex]);
|
|
|
- m_playInEditorData.m_entities.SpawnAllEntities();
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- AZ_Error("Prefab", false,
|
|
|
- "Processing of the level prefab failed to produce a root spawnable while entering game mode. "
|
|
|
- "Unable to fully enter game mode.");
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // This is a workaround until the replacement for GameEntityContext is done
|
|
|
- AzFramework::GameEntityContextEventBus::Broadcast(
|
|
|
- &AzFramework::GameEntityContextEventBus::Events::OnGameEntitiesStarted);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- AZ_Error("Prefab", false,
|
|
|
- "Failed to convert the prefab into assets. "
|
|
|
- "Confirm that the 'PlayInEditor' prefab processor stack is capable of producing a useable product asset.");
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- AZ_Error("Prefab", false, "Failed to create a prefab processing stack from key 'PlayInEditor'.");
|
|
|
- return;
|
|
|
+ entity->Deactivate();
|
|
|
+ m_playInEditorData.m_deactivatedEntities.push_back(entity.get());
|
|
|
}
|
|
|
+ return true;
|
|
|
+ });
|
|
|
|
|
|
- m_rootInstance->GetAllEntitiesInHierarchy([this](AZStd::unique_ptr<AZ::Entity>& entity)
|
|
|
- {
|
|
|
- AZ_Assert(entity, "Invalid entity found in root instance while starting play in editor.");
|
|
|
- if (entity->GetState() == AZ::Entity::State::Active)
|
|
|
- {
|
|
|
- entity->Deactivate();
|
|
|
- m_playInEditorData.m_deactivatedEntities.push_back(entity.get());
|
|
|
- }
|
|
|
- return true;
|
|
|
- });
|
|
|
- }
|
|
|
+ m_playInEditorData.m_isEnabled = true;
|
|
|
}
|
|
|
-
|
|
|
- m_playInEditorData.m_isEnabled = true;
|
|
|
}
|
|
|
|
|
|
void PrefabEditorEntityOwnershipService::StopPlayInEditor()
|
|
@@ -588,7 +468,7 @@ namespace AzToolsFramework
|
|
|
|
|
|
m_playInEditorData.m_entities.DespawnAllEntities();
|
|
|
m_playInEditorData.m_entities.Alert(
|
|
|
- [assets = AZStd::move(m_playInEditorData.m_assets),
|
|
|
+ [allSpawnableAssetData = m_playInEditorData.m_assetsCache.MoveAllInMemorySpawnableAssets(),
|
|
|
deactivatedEntities = AZStd::move(m_playInEditorData.m_deactivatedEntities)]([[maybe_unused]] uint32_t generation) mutable
|
|
|
{
|
|
|
auto end = deactivatedEntities.rend();
|
|
@@ -598,11 +478,10 @@ namespace AzToolsFramework
|
|
|
(*it)->Activate();
|
|
|
}
|
|
|
|
|
|
- for (auto& asset : assets)
|
|
|
+ for (auto& [spawnableName, spawnableAssetData] : allSpawnableAssetData)
|
|
|
{
|
|
|
- if (asset)
|
|
|
+ for (auto& asset : spawnableAssetData.m_assets)
|
|
|
{
|
|
|
- // Explicitly release because this needs to happen before the asset is unregistered.
|
|
|
asset.Release();
|
|
|
AZ::Data::AssetCatalogRequestBus::Broadcast(
|
|
|
&AZ::Data::AssetCatalogRequestBus::Events::UnregisterAsset, asset.GetId());
|