diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.cpp index b5db46d0db..490a151925 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.cpp @@ -129,7 +129,7 @@ namespace AzToolsFramework void Instance::SetLinkId(LinkId linkId) { - m_linkId = AZStd::move(linkId); + m_linkId = linkId; } LinkId Instance::GetLinkId() const @@ -154,23 +154,26 @@ namespace AzToolsFramework bool Instance::AddEntity(AZ::Entity& entity) { - EntityAlias newEntityAlias = GenerateEntityAlias(); - return AddEntity(entity, newEntityAlias); + return AddEntity(entity, GenerateEntityAlias()); } - bool Instance::AddEntity(AZ::Entity& entity, EntityAlias entityAlias) + bool Instance::AddEntity(AZStd::unique_ptr&& entity) { - if (!RegisterEntity(entity.GetId(), entityAlias)) - { - return false; - } + return AddEntity(AZStd::move(entity), GenerateEntityAlias()); + } - if (!m_entities.emplace(AZStd::make_pair(entityAlias, &entity)).second) - { - return false; - } + bool Instance::AddEntity(AZ::Entity& entity, EntityAlias entityAlias) + { + return + RegisterEntity(entity.GetId(), entityAlias) && + m_entities.emplace(AZStd::move(entityAlias), &entity).second; + } - return true; + bool Instance::AddEntity(AZStd::unique_ptr&& entity, EntityAlias entityAlias) + { + return + RegisterEntity(entity->GetId(), entityAlias) && + m_entities.emplace(AZStd::move(entityAlias), AZStd::move(entity)).second; } AZStd::unique_ptr Instance::DetachEntity(const AZ::EntityId& entityId) @@ -228,6 +231,23 @@ namespace AzToolsFramework m_entities.clear(); } + AZStd::unique_ptr Instance::ReplaceEntity(AZStd::unique_ptr&& entity, EntityAliasView alias) + { + AZStd::unique_ptr result; + auto it = m_entities.find(alias); + if (it != m_entities.end()) + { + // Swap entity ids as these need to remain stable + AZ::EntityId originalId = it->second->GetId(); + it->second->SetId(entity->GetId()); + entity->SetId(originalId); + + result = AZStd::move(it->second); + it->second = AZStd::move(entity); + } + return result; + } + void Instance::RemoveNestedEntities( const AZStd::function&)>& filter) { @@ -377,7 +397,12 @@ namespace AzToolsFramework return entityAliases; } - void Instance::GetNestedEntityIds(const AZStd::function& callback) + size_t Instance::GetEntityAliasCount() const + { + return m_entities.size(); + } + + void Instance::GetNestedEntityIds(const AZStd::function& callback) const { GetEntityIds(callback); @@ -387,7 +412,7 @@ namespace AzToolsFramework } } - void Instance::GetEntityIds(const AZStd::function& callback) + void Instance::GetEntityIds(const AZStd::function& callback) const { for (auto&&[entityAlias, entityId] : m_templateToInstanceEntityIdMap) { @@ -398,6 +423,17 @@ namespace AzToolsFramework } } + void Instance::GetEntityIdToAlias(const AZStd::function& callback) const + { + for (auto&& [entityAlias, entityId] : m_templateToInstanceEntityIdMap) + { + if (!callback(entityId, entityAlias)) + { + break; + } + } + } + bool Instance::GetEntities_Impl(const AZStd::function&)>& callback) { for (auto& [entityAlias, entity] : m_entities) @@ -514,24 +550,81 @@ namespace AzToolsFramework } } - EntityAliasOptionalReference Instance::GetEntityAlias(const AZ::EntityId& id) + EntityAliasOptionalReference Instance::GetEntityAlias(AZ::EntityId id) { - if (m_instanceToTemplateEntityIdMap.count(id)) + auto it = m_instanceToTemplateEntityIdMap.find(id); + return it != m_instanceToTemplateEntityIdMap.end() ? EntityAliasOptionalReference(it->second) + : EntityAliasOptionalReference(AZStd::nullopt); + } + + EntityAliasView Instance::GetEntityAlias(AZ::EntityId id) const + { + auto it = m_instanceToTemplateEntityIdMap.find(id); + return it != m_instanceToTemplateEntityIdMap.end() ? EntityAliasView(it->second) : EntityAliasView(); + } + + AZStd::pair Instance::FindInstanceAndAlias(AZ::EntityId entity) + { + auto it = m_instanceToTemplateEntityIdMap.find(entity); + if (it != m_instanceToTemplateEntityIdMap.end()) { - return m_instanceToTemplateEntityIdMap[id]; + return AZStd::pair(this, it->second); } - - return AZStd::nullopt; + else + { + for (auto&& [_, instance] : m_nestedInstances) + { + AZStd::pair next = instance->FindInstanceAndAlias(entity); + if (next.first != nullptr) + { + return next; + } + } + } + return AZStd::pair(nullptr, ""); } - AZ::EntityId Instance::GetEntityId(const EntityAlias& alias) + AZStd::pair Instance::FindInstanceAndAlias(AZ::EntityId entity) const { - if (m_templateToInstanceEntityIdMap.count(alias)) + return const_cast(this)->FindInstanceAndAlias(entity); + } + + EntityOptionalReference Instance::GetEntity(const EntityAlias& alias) + { + auto it = m_entities.find(alias); + return it != m_entities.end() ? EntityOptionalReference(*it->second) : EntityOptionalReference(AZStd::nullopt); + } + + EntityOptionalConstReference Instance::GetEntity(const EntityAlias& alias) const + { + auto it = m_entities.find(alias); + return it != m_entities.end() ? EntityOptionalConstReference(*it->second) : EntityOptionalConstReference(AZStd::nullopt); + } + + AZ::EntityId Instance::GetEntityId(const EntityAlias& alias) const + { + auto it = m_templateToInstanceEntityIdMap.find(alias); + return it != m_templateToInstanceEntityIdMap.end() ? it->second : AZ::EntityId(); + } + + AZ::EntityId Instance::GetEntityIdFromAliasPath(AliasPathView relativeAliasPath) const + { + const Instance* instance = this; + AliasPathView path = relativeAliasPath.ParentPath(); + for (auto it : path) { - return m_templateToInstanceEntityIdMap[alias]; + InstanceOptionalConstReference child = instance->FindNestedInstance(it.Native()); + if (child.has_value()) + { + instance = &(child->get()); + } + else + { + return AZ::EntityId(); + } } - - return AZ::EntityId(); + + return instance->GetEntityId(relativeAliasPath.Filename().Native()); } AZStd::vector Instance::GetNestedInstanceAliases(TemplateId templateId) const @@ -572,6 +665,32 @@ namespace AzToolsFramework return aliasPathResult; } + AliasPath Instance::GetAliasPathRelativeToInstance(const AZ::EntityId& entity) const + { + AliasPath result = AliasPath(s_aliasPathSeparator); + auto&& [instance, alias] = FindInstanceAndAlias(entity); + if (instance) + { + AZStd::vector instanceChain; + + while (instance && instance != this) + { + instanceChain.push_back(instance); + instance = instance->m_parent; + } + + for (auto it = instanceChain.rbegin(); it != instanceChain.rend(); ++it) + { + result.Append((*it)->m_alias); + } + return result.Append(alias); + } + else + { + return result; + } + } + EntityAlias Instance::GenerateEntityAlias() { return AZStd::string::format("Entity_%s", AZ::Entity::MakeId().ToString().c_str()); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h index 50a39268fe..25971093cd 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h @@ -38,6 +38,7 @@ namespace AzToolsFramework using AliasPath = AZ::IO::Path; using AliasPathView = AZ::IO::PathView; using EntityAlias = AZStd::string; + using EntityAliasView = AZStd::string_view; using InstanceAlias = AZStd::string; class Instance; @@ -83,9 +84,17 @@ namespace AzToolsFramework void SetContainerEntityName(AZStd::string_view containerName); bool AddEntity(AZ::Entity& entity); + bool AddEntity(AZStd::unique_ptr&& entity); bool AddEntity(AZ::Entity& entity, EntityAlias entityAlias); + bool AddEntity(AZStd::unique_ptr&& entity, EntityAlias entityAlias); AZStd::unique_ptr DetachEntity(const AZ::EntityId& entityId); void DetachEntities(const AZStd::function)>& callback); + /** + * Replaces the entity stored under the provided alias with a new one. + * + * @return The original entity or a nullptr if not found. + */ + AZStd::unique_ptr ReplaceEntity(AZStd::unique_ptr&& entity, EntityAliasView alias); /** * Detaches all entities in the instance hierarchy. @@ -109,13 +118,15 @@ namespace AzToolsFramework * @return The list of EntityAliases */ AZStd::vector GetEntityAliases(); + size_t GetEntityAliasCount() const; /** * Gets the ids for the entities in the Instance DOM. Can recursively trace all nested instances. */ - void GetNestedEntityIds(const AZStd::function& callback); + void GetNestedEntityIds(const AZStd::function& callback) const; - void GetEntityIds(const AZStd::function& callback); + void GetEntityIds(const AZStd::function& callback) const; + void GetEntityIdToAlias(const AZStd::function& callback) const; /** * Gets the entities in the Instance DOM. Can recursively trace all nested instances. @@ -131,14 +142,33 @@ namespace AzToolsFramework * * @return entityAlias via optional */ - AZStd::optional> GetEntityAlias(const AZ::EntityId& id); + EntityAliasOptionalReference GetEntityAlias(AZ::EntityId id); + EntityAliasView GetEntityAlias(AZ::EntityId id) const; + /** + * Searches for the entity in this instance and its nested instances. + * + * @return The instance that owns the entity and the alias under which the entity is known. + * If the entity isn't found then the instance will be null and the alias empty. + */ + AZStd::pair FindInstanceAndAlias(AZ::EntityId entity); + AZStd::pair FindInstanceAndAlias(AZ::EntityId entity) const; + + EntityOptionalReference GetEntity(const EntityAlias& alias); + EntityOptionalConstReference GetEntity(const EntityAlias& alias) const; /** * Gets the id for a given EnitityAlias in the Instance DOM. * * @return entityId, invalid ID if not found */ - AZ::EntityId GetEntityId(const EntityAlias& alias); + AZ::EntityId GetEntityId(const EntityAlias& alias) const; + + /** + * Retrieves the entity id from an alias path that's relative to this instance. + * + * @return entityId, invalid ID if not found + */ + AZ::EntityId GetEntityIdFromAliasPath(AliasPathView relativeAliasPath) const; /** @@ -180,6 +210,7 @@ namespace AzToolsFramework static EntityAlias GenerateEntityAlias(); AliasPath GetAbsoluteInstanceAliasPath() const; + AliasPath GetAliasPathRelativeToInstance(const AZ::EntityId& entity) const; static InstanceAlias GenerateInstanceAlias(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabCatchmentProcessor.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabCatchmentProcessor.cpp index 7b7107ae3e..7a54950c47 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabCatchmentProcessor.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabCatchmentProcessor.cpp @@ -13,9 +13,12 @@ #include #include #include +#include +#include #include #include + namespace AzToolsFramework::Prefab::PrefabConversionUtils { void PrefabCatchmentProcessor::Process(PrefabProcessorContext& context) @@ -37,7 +40,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils ->Value("Text", SerializationFormats::Text); serializeContext->Class() - ->Version(2) + ->Version(3) ->Field("SerializationFormat", &PrefabCatchmentProcessor::m_serializationFormat); } } @@ -45,6 +48,8 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils void PrefabCatchmentProcessor::ProcessPrefab(PrefabProcessorContext& context, AZStd::string_view prefabName, PrefabDom& prefab, AZ::DataStream::StreamType serializationFormat) { + using namespace AzToolsFramework::Prefab::SpawnableUtils; + AZStd::string uniqueName = prefabName; uniqueName += AzFramework::Spawnable::DotFileExtension; @@ -59,33 +64,38 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils AZStd::move(uniqueName), context.GetSourceUuid(), AZStd::move(serializer)); AZ_Assert(spawnable, "Failed to create a new spawnable."); - bool result = SpawnableUtils::CreateSpawnable(*spawnable, prefab, object.GetReferencedAssets()); - if (result) + Instance instance; + if (Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom( + instance, prefab, object.GetReferencedAssets(), + Prefab::PrefabDomUtils::LoadFlags::AssignRandomEntityId)) // Always assign random entity ids because the spawnable is + // going to be used to create clones of the entities. { + // Resolve entity aliases that store PrefabDOM information to use the spawnable instead. This is done before the entities are + // moved from the instance as they'd otherwise can't be found. + context.ResolveSpawnableEntityAliases(prefabName, *spawnable, instance); + AzFramework::Spawnable::EntityList& entities = spawnable->GetEntities(); - for (auto it = entities.begin(); it != entities.end(); ) - { - if (*it) + instance.DetachAllEntitiesInHierarchy( + [&entities, &context](AZStd::unique_ptr entity) { - (*it)->InvalidateDependencies(); - AZ::Entity::DependencySortOutcome evaluation = (*it)->EvaluateDependenciesGetDetails(); - if (evaluation.IsSuccess()) + if (entity) { - ++it; + entity->InvalidateDependencies(); + AZ::Entity::DependencySortOutcome evaluation = entity->EvaluateDependenciesGetDetails(); + if (evaluation.IsSuccess()) + { + entities.emplace_back(AZStd::move(entity)); + } + else + { + AZ_Error( + "Prefabs", false, "Entity '%s' %s cannot be activated for the following reason: %s", + entity->GetName().c_str(), entity->GetId().ToString().c_str(), evaluation.GetError().m_message.c_str()); + context.ErrorEncountered(); + } } - else - { - AZ_Error( - "Prefabs", false, "Entity '%s' %s cannot be activated for the following reason: %s", (*it)->GetName().c_str(), - (*it)->GetId().ToString().c_str(), evaluation.GetError().m_message.c_str()); - it = entities.erase(it); - } - } - else - { - it = entities.erase(it); - } - } + }); + SpawnableUtils::SortEntitiesByTransformHierarchy(*spawnable); context.GetProcessedObjects().push_back(AZStd::move(object)); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabConversionPipeline.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabConversionPipeline.cpp index ac3ada7557..c40f0d2b2e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabConversionPipeline.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabConversionPipeline.cpp @@ -54,6 +54,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils { processor->Process(context); } + context.ResolveLinks(); } size_t PrefabConversionPipeline::CalculateProcessorFingerprint(AZ::SerializeContext* context) { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.cpp index 0a7fb3a224..e12ddb616f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.cpp @@ -6,12 +6,26 @@ * */ +#include #include - +#include #include +#include namespace AzToolsFramework::Prefab::PrefabConversionUtils { + EntityAliasSpawnableLink::EntityAliasSpawnableLink(AzFramework::Spawnable& spawnable, AZ::EntityId index) + : m_spawnable(spawnable) + , m_index(index) + { + } + + EntityAliasPrefabLink::EntityAliasPrefabLink(AZStd::string prefabName, AzToolsFramework::Prefab::AliasPath alias) + : m_prefabName(AZStd::move(prefabName)) + , m_alias(AZStd::move(alias)) + { + } + PrefabProcessorContext::PrefabProcessorContext(const AZ::Uuid& sourceUuid) : m_sourceUuid(sourceUuid) {} @@ -45,7 +59,8 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils return !m_prefabs.empty(); } - bool PrefabProcessorContext::RegisterSpawnableProductAssetDependency(AZStd::string prefabName, AZStd::string dependentPrefabName) + bool PrefabProcessorContext::RegisterSpawnableProductAssetDependency( + AZStd::string prefabName, AZStd::string dependentPrefabName, EntityAliasSpawnableLoadBehavior loadBehavior) { using ConversionUtils = PrefabConversionUtils::ProcessedObjectStore; @@ -55,10 +70,11 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils dependentPrefabName += AzFramework::Spawnable::DotFileExtension; uint32_t spawnablePrefabSubId = ConversionUtils::BuildSubId(AZStd::move(dependentPrefabName)); - return RegisterSpawnableProductAssetDependency(spawnableSubId, spawnablePrefabSubId); + return RegisterSpawnableProductAssetDependency(spawnableSubId, spawnablePrefabSubId, loadBehavior); } - bool PrefabProcessorContext::RegisterSpawnableProductAssetDependency(AZStd::string prefabName, const AZ::Data::AssetId& dependentAssetId) + bool PrefabProcessorContext::RegisterSpawnableProductAssetDependency( + AZStd::string prefabName, const AZ::Data::AssetId& dependentAssetId, EntityAliasSpawnableLoadBehavior loadBehavior) { using ConversionUtils = PrefabConversionUtils::ProcessedObjectStore; @@ -67,20 +83,78 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils AZ::Data::AssetId spawnableAssetId(GetSourceUuid(), spawnableSubId); - return RegisterProductAssetDependency(spawnableAssetId, dependentAssetId); + return RegisterProductAssetDependency(spawnableAssetId, dependentAssetId, ToAssetLoadBehavior(loadBehavior)); } - bool PrefabProcessorContext::RegisterSpawnableProductAssetDependency(uint32_t spawnableAssetSubId, uint32_t dependentSpawnableAssetSubId) + bool PrefabProcessorContext::RegisterSpawnableProductAssetDependency( + uint32_t spawnableAssetSubId, uint32_t dependentSpawnableAssetSubId, EntityAliasSpawnableLoadBehavior loadBehavior) { AZ::Data::AssetId spawnableAssetId(GetSourceUuid(), spawnableAssetSubId); AZ::Data::AssetId dependentSpawnableAssetId(GetSourceUuid(), dependentSpawnableAssetSubId); - return RegisterProductAssetDependency(spawnableAssetId, dependentSpawnableAssetId); + return RegisterProductAssetDependency(spawnableAssetId, dependentSpawnableAssetId, ToAssetLoadBehavior(loadBehavior)); } bool PrefabProcessorContext::RegisterProductAssetDependency(const AZ::Data::AssetId& assetId, const AZ::Data::AssetId& dependentAssetId) { - return m_registeredProductAssetDependencies[assetId].emplace(dependentAssetId).second; + return RegisterProductAssetDependency(assetId, dependentAssetId, AZ::Data::AssetLoadBehavior::NoLoad); + } + + bool PrefabProcessorContext::RegisterProductAssetDependency( + const AZ::Data::AssetId& assetId, const AZ::Data::AssetId& dependentAssetId, AZ::Data::AssetLoadBehavior loadBehavior) + { + auto dependencies = m_registeredProductAssetDependencies.equal_range(assetId); + if (dependencies.first != dependencies.second) + { + for (auto it = dependencies.first; it != dependencies.second; ++it) + { + if (it->second.m_assetId == dependentAssetId) + { + if (it->second.m_loadBehavior < loadBehavior) + { + it->second.m_loadBehavior = loadBehavior; + } + return true; + } + } + } + + return m_registeredProductAssetDependencies.emplace(assetId, AssetDependencyInfo{ dependentAssetId, loadBehavior }).second; + } + + void PrefabProcessorContext::RegisterSpawnableEntityAlias(EntityAliasStore link) + { + m_entityAliases.push_back(AZStd::move(link)); + } + + void PrefabProcessorContext::ResolveSpawnableEntityAliases( + AZStd::string_view prefabName, AzFramework::Spawnable& spawnable, const AzToolsFramework::Prefab::Instance& instance) + { + using namespace AzToolsFramework::Prefab; + + for (EntityAliasStore& entityAlias : m_entityAliases) + { + auto sourcePrefab = AZStd::get_if(&entityAlias.m_source); + if (sourcePrefab != nullptr && sourcePrefab->m_prefabName == prefabName) + { + AZ::EntityId id = instance.GetEntityIdFromAliasPath(sourcePrefab->m_alias); + AZ_Assert( + id.IsValid(), + "Entity '%s' was not found in Prefab Instance created from '%s' even though it was previously found.", + sourcePrefab->m_alias.c_str(), sourcePrefab->m_prefabName.c_str()); + entityAlias.m_source.emplace(spawnable, id); + } + + auto targetPrefab = AZStd::get_if(&entityAlias.m_target); + if (targetPrefab != nullptr && targetPrefab->m_prefabName == prefabName) + { + AZ::EntityId id = instance.GetEntityIdFromAliasPath(targetPrefab->m_alias); + AZ_Assert( + id.IsValid(), "Entity '%s' was not found in Prefab Instance created from '%s' even though it was previously found.", + targetPrefab->m_alias.c_str(), targetPrefab->m_prefabName.c_str()); + entityAlias.m_target.emplace(spawnable, id); + } + } } PrefabProcessorContext::ProcessedObjectStoreContainer& PrefabProcessorContext::GetProcessedObjects() @@ -118,6 +192,46 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils return m_sourceUuid; } + void PrefabProcessorContext::ResolveLinks() + { + // Store the aliases visitor here when first encountered to avoid the visitor sorting the aliases for every addition. + // Once this map goes out of scope the visitors will be destroyed and in turn sort their aliases. + AZStd::unordered_map aliasVisitors; + + for (EntityAliasStore& alias : m_entityAliases) + { + auto source = AZStd::get_if(&alias.m_source); + AZ_Assert(source, "Entity alias found that has a source that's not yet resolved to a spawnable"); + auto target = AZStd::get_if(&alias.m_target); + AZ_Assert(target, "Entity alias found that has a target that's not yet resolved to a spawnable"); + + uint32_t sourceIndex = SpawnableUtils::FindEntityIndex(source->m_index, source->m_spawnable); + AZ_Assert( + sourceIndex != SpawnableUtils::InvalidEntityIndex, "Entity %zu not found in source spawnable while resolving to index.", + aznumeric_cast(source->m_index)); + uint32_t targetIndex = SpawnableUtils::FindEntityIndex(target->m_index, target->m_spawnable); + AZ_Assert( + targetIndex != SpawnableUtils::InvalidEntityIndex, "Entity %zu not found in target spawnable while resolving to index.", + aznumeric_cast(target->m_index)); + + AZ::Data::AssetLoadBehavior loadBehavior = ToAssetLoadBehavior(alias.m_loadBehavior); + + auto it = aliasVisitors.find(source->m_spawnable.GetId()); + if (it == aliasVisitors.end()) + { + AzFramework::Spawnable::EntityAliasVisitor visitor = source->m_spawnable.TryGetAliases(); + AZ_Assert(visitor.HasLock(), "Unable to obtain lock for a newly create spawnable."); + it = aliasVisitors.emplace(source->m_spawnable.GetId(), AZStd::move(visitor)).first; + } + it->second.AddAlias( + AZ::Data::Asset(&target->m_spawnable, loadBehavior), alias.m_tag, sourceIndex, targetIndex, + alias.m_aliasType, alias.m_loadBehavior == EntityAliasSpawnableLoadBehavior::QueueLoad); + + // Register the dependency between the two spawnables. + RegisterProductAssetDependency(source->m_spawnable.GetId(), target->m_spawnable.GetId(), loadBehavior); + } + } + bool PrefabProcessorContext::HasCompletedSuccessfully() const { return m_completedSuccessfully; @@ -127,4 +241,10 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils { m_completedSuccessfully = false; } + + AZ::Data::AssetLoadBehavior PrefabProcessorContext::ToAssetLoadBehavior(EntityAliasSpawnableLoadBehavior loadBehavior) const + { + return loadBehavior == EntityAliasSpawnableLoadBehavior::DependentLoad ? AZ::Data::AssetLoadBehavior::PreLoad + : AZ::Data::AssetLoadBehavior::NoLoad; + } } // namespace AzToolsFramework::Prefab::PrefabConversionUtils diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h index 7c21d446de..7fc01367b2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h @@ -9,24 +9,84 @@ #pragma once #include +#include #include #include #include +#include #include #include #include #include +#include +#include #include #include namespace AzToolsFramework::Prefab::PrefabConversionUtils { + enum class EntityAliasType : uint8_t + { + Disabled, //!< No alias is added. + OptionalReplace, //!< At runtime the entity might be replaced. If the alias is disabled the original entity will be spawned. + //!< The original entity will be left in the spawnable and a copy is returned. + Replace, //!< At runtime the entity will be replaced. If the alias is disabled nothing will be spawned not. The original + //!< entity is returned and a blank entity is left. + Additional, //!< At runtime the alias entity will be added as an additional but unrelated entity with a new entity id. + //!< An empty entity will be returned. + Merge //!< At runtime the components in both entities will be merged. An empty entity will be returned. The added + //!< components may no conflict with the entities already in the root entity. + }; + + enum class EntityAliasSpawnableLoadBehavior : uint8_t + { + NoLoad, //!< Don't load the spawnable referenced in the entity alias. Loading will be up to the caller. + QueueLoad, //!< Queue the spawnable referenced in the entity alias for loading. This will be an async load because asset + //!< handlers aren't allowed to start a blocking load as this can lead to deadlocks. + DependentLoad //!< The spawnable referenced in the entity alias is made a dependency of the spawnable that holds the entity + //!< alias. This will cause the spawnable to be automatically loaded along with the owning spawnable. + }; + + struct EntityAliasSpawnableLink + { + EntityAliasSpawnableLink() = default; + EntityAliasSpawnableLink(AzFramework::Spawnable& spawnable, AZ::EntityId index); + + AzFramework::Spawnable& m_spawnable; + AZ::EntityId m_index; + }; + + struct EntityAliasPrefabLink + { + EntityAliasPrefabLink() = default; + EntityAliasPrefabLink(AZStd::string prefabName, AzToolsFramework::Prefab::AliasPath alias); + + AZStd::string m_prefabName; + AzToolsFramework::Prefab::AliasPath m_alias; + }; + + struct EntityAliasStore + { + using LinkStore = AZStd::variant; + + LinkStore m_source; + LinkStore m_target; + uint32_t m_tag; + AzFramework::Spawnable::EntityAliasType m_aliasType; + EntityAliasSpawnableLoadBehavior m_loadBehavior; + }; + + struct AssetDependencyInfo + { + AZ::Data::AssetId m_assetId; + AZ::Data::AssetLoadBehavior m_loadBehavior; + }; + class PrefabProcessorContext { public: using ProcessedObjectStoreContainer = AZStd::vector; - using ProductAssetDependencyContainer = - AZStd::unordered_map>; + using ProductAssetDependencyContainer = AZStd::unordered_multimap; AZ_CLASS_ALLOCATOR(PrefabProcessorContext, AZ::SystemAllocator, 0); AZ_RTTI(PrefabProcessorContext, "{C7D77E3A-C544-486B-B774-7C82C38FE22F}"); @@ -39,11 +99,20 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils virtual void ListPrefabs(const AZStd::function& callback) const; virtual bool HasPrefabs() const; - virtual bool RegisterSpawnableProductAssetDependency(AZStd::string prefabName, AZStd::string dependentPrefabName); - virtual bool RegisterSpawnableProductAssetDependency(AZStd::string prefabName, const AZ::Data::AssetId& dependentAssetId); - virtual bool RegisterSpawnableProductAssetDependency(uint32_t spawnableAssetSubId, uint32_t dependentSpawnableAssetSubId); + virtual bool RegisterSpawnableProductAssetDependency( + AZStd::string prefabName, AZStd::string dependentPrefabName, EntityAliasSpawnableLoadBehavior loadBehavior); + virtual bool RegisterSpawnableProductAssetDependency( + AZStd::string prefabName, const AZ::Data::AssetId& dependentAssetId, EntityAliasSpawnableLoadBehavior loadBehavior); + virtual bool RegisterSpawnableProductAssetDependency( + uint32_t spawnableAssetSubId, uint32_t dependentSpawnableAssetSubId, EntityAliasSpawnableLoadBehavior loadBehavior); virtual bool RegisterProductAssetDependency(const AZ::Data::AssetId& assetId, const AZ::Data::AssetId& dependentAssetId); + virtual bool RegisterProductAssetDependency( + const AZ::Data::AssetId& assetId, const AZ::Data::AssetId& dependentAssetId, AZ::Data::AssetLoadBehavior loadBehavior); + virtual void RegisterSpawnableEntityAlias(EntityAliasStore link); + virtual void ResolveSpawnableEntityAliases( + AZStd::string_view prefabName, AzFramework::Spawnable& spawnable, const AzToolsFramework::Prefab::Instance& instance); + virtual ProcessedObjectStoreContainer& GetProcessedObjects(); virtual const ProcessedObjectStoreContainer& GetProcessedObjects() const; @@ -54,13 +123,19 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils virtual const AZ::PlatformTagSet& GetPlatformTags() const; virtual const AZ::Uuid& GetSourceUuid() const; + virtual void ResolveLinks(); + virtual bool HasCompletedSuccessfully() const; virtual void ErrorEncountered(); protected: using NamedPrefabContainer = AZStd::unordered_map; + using SpawnableEntityAliasStore = AZStd::vector; + + AZ::Data::AssetLoadBehavior ToAssetLoadBehavior(EntityAliasSpawnableLoadBehavior loadBehavior) const; NamedPrefabContainer m_prefabs; + SpawnableEntityAliasStore m_entityAliases; ProcessedObjectStoreContainer m_products; ProductAssetDependencyContainer m_registeredProductAssetDependencies; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.cpp index 9c59b6b559..a6770ee2c7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.cpp @@ -11,7 +11,16 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils { - ProcessedObjectStore::ProcessedObjectStore(AZStd::string uniqueId, AZStd::unique_ptr asset, SerializerFunction assetSerializer) + void ProcessedObjectStore::AssetSmartPtrDeleter::operator()(AZ::Data::AssetData* asset) + { + if (asset->GetUseCount() == 0) + { + // Only delete the asset if it wasn't turned into a full asset + delete asset; + } + } + + ProcessedObjectStore::ProcessedObjectStore(AZStd::string uniqueId, AssetSmartPtr asset, SerializerFunction assetSerializer) : m_uniqueId(AZStd::move(uniqueId)) , m_assetSerializer(AZStd::move(assetSerializer)) , m_asset(AZStd::move(asset)) @@ -62,7 +71,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils return m_referencedAssets; } - AZStd::unique_ptr ProcessedObjectStore::ReleaseAsset() + auto ProcessedObjectStore::ReleaseAsset() -> AssetSmartPtr { return AZStd::move(m_asset); } @@ -72,6 +81,11 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils return AzFramework::SpawnableAssetHandler::BuildSubId(id); } + uint32_t ProcessedObjectStore::GetSubId() const + { + return m_asset->GetId().m_subId; + } + const AZStd::string& ProcessedObjectStore::GetId() const { return m_uniqueId; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.h index 61d529842c..c75e72840e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.h @@ -26,6 +26,12 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils public: using SerializerFunction = AZStd::function&, const ProcessedObjectStore&)>; + struct AssetSmartPtrDeleter + { + void operator()(AZ::Data::AssetData* asset); + }; + using AssetSmartPtr = AZStd::unique_ptr; + //! Constructs a new instance. //! @param uniqueId A name for the object that's unique within the scope of the Prefab. This name will be used to generate a sub id for the product //! which requires that the name to be stable between runs. @@ -37,24 +43,24 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils bool Serialize(AZStd::vector& output) const; static uint32_t BuildSubId(AZStd::string_view id); + uint32_t GetSubId() const; bool HasAsset() const; const AZ::Data::AssetType& GetAssetType() const; const AZ::Data::AssetData& GetAsset() const; AZ::Data::AssetData& GetAsset(); - AZStd::unique_ptr ReleaseAsset(); + AssetSmartPtr ReleaseAsset(); AZStd::vector>& GetReferencedAssets(); const AZStd::vector>& GetReferencedAssets() const; - const AZStd::string& GetId() const; private: - ProcessedObjectStore(AZStd::string uniqueId, AZStd::unique_ptr asset, SerializerFunction assetSerializer); + ProcessedObjectStore(AZStd::string uniqueId, AssetSmartPtr asset, SerializerFunction assetSerializer); SerializerFunction m_assetSerializer; - AZStd::unique_ptr m_asset; + AssetSmartPtr m_asset; AZStd::vector> m_referencedAssets; AZStd::string m_uniqueId; }; @@ -66,7 +72,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils static_assert(AZStd::is_base_of_v, "ProcessedObjectStore can only be created from a class that derives from AZ::Data::AssetData."); AZ::Data::AssetId assetId(sourceId, BuildSubId(uniqueId)); - auto instance = AZStd::make_unique(assetId, AZ::Data::AssetData::AssetStatus::Ready); + auto instance = AssetSmartPtr(aznew T(assetId, AZ::Data::AssetData::AssetStatus::Ready)); ProcessedObjectStore resultLeft(AZStd::move(uniqueId), AZStd::move(instance), AZStd::move(assetSerializer)); T* resultRight = static_cast(&resultLeft.GetAsset()); return AZStd::make_pair(AZStd::move(resultLeft), resultRight); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp index 902f439280..154337e957 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp @@ -8,9 +8,11 @@ #include +#include #include #include #include +#include #include #include #include @@ -20,18 +22,134 @@ namespace AzToolsFramework::Prefab::SpawnableUtils { + namespace Internal + { + AZ::SerializeContext* GetSerializeContext() + { + AZ::SerializeContext* result = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(result, &AZ::ComponentApplicationBus::Events::GetSerializeContext); + AZ_Assert(result, "SpawnbleUtils was unable to locate the Serialize Context."); + return result; + } + + AZ::Entity* FindEntity(AZ::EntityId entity, AzToolsFramework::Prefab::Instance& source) + { + AZ::Entity* result = nullptr; + source.GetEntities( + [&result, entity](AZStd::unique_ptr& instance) + { + if (instance->GetId() != entity) + { + return true; + } + else + { + result = instance.get(); + return false; + } + }); + return result; + } + + AZ::Entity* FindEntity(AZ::EntityId entity, AzFramework::Spawnable& source) + { + uint32_t index = AzToolsFramework::Prefab::SpawnableUtils::FindEntityIndex(entity, source); + return index != InvalidEntityIndex ? source.GetEntities()[index].get() : nullptr; + } + + template + AZStd::unique_ptr CloneEntity(AZ::EntityId entity, T& source) + { + AZ::Entity* target = Internal::FindEntity(entity, source); + AZ_Assert( + target, "SpawnbleUtils were unable to locate entity with id %zu in Instance or Spawnable for cloning.", + aznumeric_cast(entity)); + auto clone = AZStd::make_unique(); + + static AZ::SerializeContext* sc = GetSerializeContext(); + sc->CloneObjectInplace(*clone, target); + clone->SetId(AZ::Entity::MakeId()); + + return clone; + } + + AZStd::unique_ptr ReplaceEntityWithPlaceholder(AZ::EntityId entity, AzToolsFramework::Prefab::Instance& source) + { + auto&& [instance, alias] = source.FindInstanceAndAlias(entity); + AZ_Assert( + instance, "SpawnbleUtils were unable to locate entity alias with id %zu in Instance '%s' for replacing.", + aznumeric_cast(entity), source.GetTemplateSourcePath().c_str()); + + EntityOptionalReference entityData = instance->GetEntity(alias); + AZ_Assert( + entityData.has_value(), "SpawnbleUtils were unable to locate entity '%.*s' in Instance '%s' for replacing.", + AZ_STRING_ARG(alias), source.GetTemplateSourcePath().c_str()); + auto placeholder = AZStd::make_unique(entityData->get().GetId(), entityData->get().GetName()); + return instance->ReplaceEntity(AZStd::move(placeholder), alias); + } + + AZStd::unique_ptr ReplaceEntityWithPlaceholder(AZ::EntityId entity, AzFramework::Spawnable& source) + { + uint32_t index = AzToolsFramework::Prefab::SpawnableUtils::FindEntityIndex(entity, source); + AZ_Assert( + index != InvalidEntityIndex, "SpawnbleUtils were unable to locate entity alias with id %zu in Spawnable for replacing.", + aznumeric_cast(entity)); + + AZStd::unique_ptr original = AZStd::move(source.GetEntities()[index]); + AZ_Assert( + original, "SpawnbleUtils were unable to locate entity with id %zu in Spawnable for replacing.", + aznumeric_cast(entity)); + + source.GetEntities()[index] = AZStd::make_unique(original->GetId(), original->GetName()); + + return original; + } + + template + AZStd::pair, AzFramework::Spawnable::EntityAliasType> ApplyAlias( + Source& source, AZ::EntityId entity, AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType) + { + namespace PCU = AzToolsFramework::Prefab::PrefabConversionUtils; + using ResultPair = AZStd::pair, AzFramework::Spawnable::EntityAliasType>; + + switch (aliasType) + { + case PCU::EntityAliasType::Disabled: + // No need to do anything as the alias is disabled. + return ResultPair(nullptr, AzFramework::Spawnable::EntityAliasType::Disabled); + case PCU::EntityAliasType::OptionalReplace: + return ResultPair(CloneEntity(entity, source), AzFramework::Spawnable::EntityAliasType::Replace); + case PCU::EntityAliasType::Replace: + return ResultPair(ReplaceEntityWithPlaceholder(entity, source), AzFramework::Spawnable::EntityAliasType::Replace); + case PCU::EntityAliasType::Additional: + ResultPair(AZStd::make_unique(AZ::Entity::MakeId()), AzFramework::Spawnable::EntityAliasType::Additional); + case PCU::EntityAliasType::Merge: + // Use the same entity id as the original entity so at runtime the entity ids can be verified to match. + ResultPair(AZStd::make_unique(entity), AzFramework::Spawnable::EntityAliasType::Merge); + default: + AZ_Assert( + false, "Invalid PrefabProcessorContext::EntityAliasType type (%i) provided.", aznumeric_cast(aliasType)); + return ResultPair(nullptr, AzFramework::Spawnable::EntityAliasType::Disabled); + } + } + } + bool CreateSpawnable(AzFramework::Spawnable& spawnable, const PrefabDom& prefabDom) { AZStd::vector> referencedAssets; return CreateSpawnable(spawnable, prefabDom, referencedAssets); } - bool CreateSpawnable(AzFramework::Spawnable& spawnable, const PrefabDom& prefabDom, AZStd::vector>& referencedAssets) + bool CreateSpawnable( + AzFramework::Spawnable& spawnable, + const PrefabDom& prefabDom, + AZStd::vector>& referencedAssets) { Instance instance; - if (Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom(instance, prefabDom, referencedAssets, - Prefab::PrefabDomUtils::LoadFlags::AssignRandomEntityId)) // Always assign random entity ids because the spawnable is - // going to be used to create clones of the entities. + if (Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom( + instance, prefabDom, referencedAssets, + Prefab::PrefabDomUtils::LoadFlags::AssignRandomEntityId)) // Always assign random entity ids because the spawnable is + // going to be used to create clones of the entities. { AzFramework::Spawnable::EntityList& entities = spawnable.GetEntities(); instance.DetachAllEntitiesInHierarchy( @@ -47,6 +165,122 @@ namespace AzToolsFramework::Prefab::SpawnableUtils } } + AZ::Entity* CreateEntityAlias( + AZStd::string sourcePrefabName, + AzToolsFramework::Prefab::Instance& source, + AZStd::string targetPrefabName, + AzToolsFramework::Prefab::Instance& target, + AZ::EntityId entity, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior, + uint32_t tag, + AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context) + { + using namespace AzToolsFramework::Prefab::PrefabConversionUtils; + + AliasPath alias = source.GetAliasPathRelativeToInstance(entity); + if (!alias.empty()) + { + auto&& [replacement, storedAliasType] = Internal::ApplyAlias(source, entity, aliasType); + + AZ::Entity* result = replacement.get(); + target.AddEntity(AZStd::move(replacement), alias.Filename().Native()); + + EntityAliasStore store; + store.m_aliasType = storedAliasType; + store.m_source.emplace(AZStd::move(sourcePrefabName), AZStd::move(alias)); + store.m_target.emplace( + AZStd::move(targetPrefabName), target.GetAliasPathRelativeToInstance(result->GetId())); + store.m_loadBehavior = loadBehavior; + store.m_tag = tag; + context.RegisterSpawnableEntityAlias(AZStd::move(store)); + + return result; + } + else + { + AZ_Assert(false, "Entity with id %zu was not found in the source prefab.", static_cast(entity)); + return nullptr; + } + } + + AZ::Entity* CreateEntityAlias( + AZStd::string sourcePrefabName, + AzToolsFramework::Prefab::Instance& source, + AzFramework::Spawnable& target, + AZ::EntityId entity, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior, + uint32_t tag, + AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context) + { + using namespace AzToolsFramework::Prefab::PrefabConversionUtils; + + AliasPath alias = source.GetAliasPathRelativeToInstance(entity); + if (!alias.empty()) + { + auto&& [replacement, storedAliasType] = Internal::ApplyAlias(source, entity, aliasType); + + AZ::Entity* result = replacement.get(); + target.GetEntities().push_back(AZStd::move(replacement)); + + EntityAliasStore store; + store.m_aliasType = storedAliasType; + store.m_source.emplace(AZStd::move(sourcePrefabName), AZStd::move(alias)); + store.m_target.emplace(target, result->GetId()); + store.m_tag = tag; + store.m_loadBehavior = loadBehavior; + context.RegisterSpawnableEntityAlias(AZStd::move(store)); + + return result; + } + else + { + AZ_Assert(false, "Entity with id %zu was not found in the source prefab.", static_cast(entity)); + return nullptr; + } + } + + AZ::Entity* CreateEntityAlias( + AzFramework::Spawnable& source, + AzFramework::Spawnable& target, + AZ::EntityId entity, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior, + uint32_t tag, + AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context) + { + using namespace AzToolsFramework::Prefab::PrefabConversionUtils; + + auto&& [replacement, storedAliasType] = Internal::ApplyAlias(source, entity, aliasType); + AZ::Entity* result = replacement.get(); + target.GetEntities().push_back(AZStd::move(replacement)); + + EntityAliasStore store; + store.m_aliasType = storedAliasType; + store.m_source.emplace(source, entity); + store.m_target.emplace(target, result->GetId()); + store.m_tag = tag; + store.m_loadBehavior = loadBehavior; + context.RegisterSpawnableEntityAlias(AZStd::move(store)); + + return result; + } + + uint32_t FindEntityIndex(AZ::EntityId entity, const AzFramework::Spawnable& spawnable) + { + auto begin = spawnable.GetEntities().begin(); + auto end = spawnable.GetEntities().end(); + for(auto it = begin; it != end; ++it) + { + if ((*it)->GetId() == entity) + { + return AZStd::distance(begin, it); + } + } + return InvalidEntityIndex; + } + template void OrganizeEntitiesForSorting( AZStd::vector& entities, diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h index ffcf13e081..892b83455d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h @@ -8,14 +8,59 @@ #pragma once +#include +#include #include #include +#include + +namespace AZ +{ + class Entity; +} + +namespace AzToolsFramework::Prefab +{ + class Instance; +} namespace AzToolsFramework::Prefab::SpawnableUtils { + static constexpr uint32_t InvalidEntityIndex = AZStd::numeric_limits::max(); + bool CreateSpawnable(AzFramework::Spawnable& spawnable, const PrefabDom& prefabDom); bool CreateSpawnable(AzFramework::Spawnable& spawnable, const PrefabDom& prefabDom, AZStd::vector>& referencedAssets); + AZ::Entity* CreateEntityAlias( + AZStd::string sourcePrefabName, + AzToolsFramework::Prefab::Instance& source, + AZStd::string targetPrefabName, + AzToolsFramework::Prefab::Instance& target, + AZ::EntityId entity, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior, + uint32_t tag, + AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context); + AZ::Entity* CreateEntityAlias( + AZStd::string sourcePrefabName, + AzToolsFramework::Prefab::Instance& source, + AzFramework::Spawnable& target, + AZ::EntityId entity, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior, + uint32_t tag, + AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context); + AZ::Entity* CreateEntityAlias( + AzFramework::Spawnable& source, + AzFramework::Spawnable& target, + AZ::EntityId entity, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior, + uint32_t tag, + AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context); + + uint32_t FindEntityIndex(AZ::EntityId entity, const AzFramework::Spawnable& spawnable); + void SortEntitiesByTransformHierarchy(AzFramework::Spawnable& spawnable); template diff --git a/Gems/Prefab/PrefabBuilder/PrefabBuilderComponent.cpp b/Gems/Prefab/PrefabBuilder/PrefabBuilderComponent.cpp index a86d7b57e2..6107226783 100644 --- a/Gems/Prefab/PrefabBuilder/PrefabBuilderComponent.cpp +++ b/Gems/Prefab/PrefabBuilder/PrefabBuilderComponent.cpp @@ -175,6 +175,8 @@ namespace AZ::Prefab const AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext::ProductAssetDependencyContainer& registeredDependencies, AZStd::vector& outputProducts) const { + using namespace AzToolsFramework::Prefab::PrefabConversionUtils; + outputProducts.reserve(store.size()); AZStd::vector data; @@ -211,17 +213,14 @@ namespace AZ::Prefab if (AssetBuilderSDK::OutputObject(&object.GetAsset(), object.GetAssetType(), productPath.String(), object.GetAssetType(), object.GetAsset().GetId().m_subId, product)) { - auto findRegisteredDependencies = registeredDependencies.find(object.GetAsset().GetId()); - if (findRegisteredDependencies != registeredDependencies.end()) - { - AZStd::transform(findRegisteredDependencies->second.begin(), findRegisteredDependencies->second.end(), - AZStd::back_inserter(product.m_dependencies), - [](const AZ::Data::AssetId& productId) -> AssetBuilderSDK::ProductDependency - { - return AssetBuilderSDK::ProductDependency(productId, - AZ::Data::ProductDependencyInfo::CreateFlags(AZ::Data::AssetLoadBehavior::NoLoad)); - }); - } + auto range = registeredDependencies.equal_range(object.GetAsset().GetId()); + AZStd::transform(range.first, range.second, + AZStd::back_inserter(product.m_dependencies), + [](const auto& dependency) -> AssetBuilderSDK::ProductDependency + { + return AssetBuilderSDK::ProductDependency( + dependency.second.m_assetId, AZ::Data::ProductDependencyInfo::CreateFlags(dependency.second.m_loadBehavior)); + }); outputProducts.push_back(AZStd::move(product)); }