Added support for setting up entity aliases during the prefab to spawnable conversion.
Signed-off-by: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com>
This commit is contained in:
@@ -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(AZStd::unique_ptr<AZ::Entity>&& entity)
|
||||
{
|
||||
return AddEntity(AZStd::move(entity), GenerateEntityAlias());
|
||||
}
|
||||
|
||||
bool Instance::AddEntity(AZ::Entity& entity, EntityAlias entityAlias)
|
||||
{
|
||||
if (!RegisterEntity(entity.GetId(), entityAlias))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return
|
||||
RegisterEntity(entity.GetId(), entityAlias) &&
|
||||
m_entities.emplace(AZStd::move(entityAlias), &entity).second;
|
||||
}
|
||||
|
||||
if (!m_entities.emplace(AZStd::make_pair(entityAlias, &entity)).second)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
bool Instance::AddEntity(AZStd::unique_ptr<AZ::Entity>&& entity, EntityAlias entityAlias)
|
||||
{
|
||||
return
|
||||
RegisterEntity(entity->GetId(), entityAlias) &&
|
||||
m_entities.emplace(AZStd::move(entityAlias), AZStd::move(entity)).second;
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> Instance::DetachEntity(const AZ::EntityId& entityId)
|
||||
@@ -228,6 +231,23 @@ namespace AzToolsFramework
|
||||
m_entities.clear();
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> Instance::ReplaceEntity(AZStd::unique_ptr<AZ::Entity>&& entity, EntityAliasView alias)
|
||||
{
|
||||
AZStd::unique_ptr<AZ::Entity> 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<bool(const AZStd::unique_ptr<AZ::Entity>&)>& filter)
|
||||
{
|
||||
@@ -377,7 +397,12 @@ namespace AzToolsFramework
|
||||
return entityAliases;
|
||||
}
|
||||
|
||||
void Instance::GetNestedEntityIds(const AZStd::function<bool(AZ::EntityId)>& callback)
|
||||
size_t Instance::GetEntityAliasCount() const
|
||||
{
|
||||
return m_entities.size();
|
||||
}
|
||||
|
||||
void Instance::GetNestedEntityIds(const AZStd::function<bool(AZ::EntityId)>& callback) const
|
||||
{
|
||||
GetEntityIds(callback);
|
||||
|
||||
@@ -387,7 +412,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::GetEntityIds(const AZStd::function<bool(AZ::EntityId)>& callback)
|
||||
void Instance::GetEntityIds(const AZStd::function<bool(AZ::EntityId)>& callback) const
|
||||
{
|
||||
for (auto&&[entityAlias, entityId] : m_templateToInstanceEntityIdMap)
|
||||
{
|
||||
@@ -398,6 +423,17 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::GetEntityIdToAlias(const AZStd::function<bool(AZ::EntityId, EntityAliasView)>& callback) const
|
||||
{
|
||||
for (auto&& [entityAlias, entityId] : m_templateToInstanceEntityIdMap)
|
||||
{
|
||||
if (!callback(entityId, entityAlias))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Instance::GetEntities_Impl(const AZStd::function<bool(AZStd::unique_ptr<AZ::Entity>&)>& 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))
|
||||
{
|
||||
return m_instanceToTemplateEntityIdMap[id];
|
||||
}
|
||||
|
||||
return AZStd::nullopt;
|
||||
auto it = m_instanceToTemplateEntityIdMap.find(id);
|
||||
return it != m_instanceToTemplateEntityIdMap.end() ? EntityAliasOptionalReference(it->second)
|
||||
: EntityAliasOptionalReference(AZStd::nullopt);
|
||||
}
|
||||
|
||||
AZ::EntityId Instance::GetEntityId(const EntityAlias& alias)
|
||||
EntityAliasView Instance::GetEntityAlias(AZ::EntityId id) const
|
||||
{
|
||||
if (m_templateToInstanceEntityIdMap.count(alias))
|
||||
auto it = m_instanceToTemplateEntityIdMap.find(id);
|
||||
return it != m_instanceToTemplateEntityIdMap.end() ? EntityAliasView(it->second) : EntityAliasView();
|
||||
}
|
||||
|
||||
AZStd::pair<Instance*, EntityAliasView> Instance::FindInstanceAndAlias(AZ::EntityId entity)
|
||||
{
|
||||
auto it = m_instanceToTemplateEntityIdMap.find(entity);
|
||||
if (it != m_instanceToTemplateEntityIdMap.end())
|
||||
{
|
||||
return m_templateToInstanceEntityIdMap[alias];
|
||||
return AZStd::pair<Instance*, EntityAliasView>(this, it->second);
|
||||
}
|
||||
|
||||
return AZ::EntityId();
|
||||
else
|
||||
{
|
||||
for (auto&& [_, instance] : m_nestedInstances)
|
||||
{
|
||||
AZStd::pair<Instance*, EntityAliasView> next = instance->FindInstanceAndAlias(entity);
|
||||
if (next.first != nullptr)
|
||||
{
|
||||
return next;
|
||||
}
|
||||
}
|
||||
}
|
||||
return AZStd::pair<Instance*, EntityAliasView>(nullptr, "");
|
||||
}
|
||||
|
||||
AZStd::pair<const Instance*, EntityAliasView> Instance::FindInstanceAndAlias(AZ::EntityId entity) const
|
||||
{
|
||||
return const_cast<Instance*>(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)
|
||||
{
|
||||
InstanceOptionalConstReference child = instance->FindNestedInstance(it.Native());
|
||||
if (child.has_value())
|
||||
{
|
||||
instance = &(child->get());
|
||||
}
|
||||
else
|
||||
{
|
||||
return AZ::EntityId();
|
||||
}
|
||||
}
|
||||
|
||||
return instance->GetEntityId(relativeAliasPath.Filename().Native());
|
||||
}
|
||||
|
||||
AZStd::vector<InstanceAlias> 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<const Instance*> 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());
|
||||
|
||||
@@ -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<AZ::Entity>&& entity);
|
||||
bool AddEntity(AZ::Entity& entity, EntityAlias entityAlias);
|
||||
bool AddEntity(AZStd::unique_ptr<AZ::Entity>&& entity, EntityAlias entityAlias);
|
||||
AZStd::unique_ptr<AZ::Entity> DetachEntity(const AZ::EntityId& entityId);
|
||||
void DetachEntities(const AZStd::function<void(AZStd::unique_ptr<AZ::Entity>)>& 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<AZ::Entity> ReplaceEntity(AZStd::unique_ptr<AZ::Entity>&& entity, EntityAliasView alias);
|
||||
|
||||
/**
|
||||
* Detaches all entities in the instance hierarchy.
|
||||
@@ -109,13 +118,15 @@ namespace AzToolsFramework
|
||||
* @return The list of EntityAliases
|
||||
*/
|
||||
AZStd::vector<EntityAlias> 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<bool(AZ::EntityId)>& callback);
|
||||
void GetNestedEntityIds(const AZStd::function<bool(AZ::EntityId)>& callback) const;
|
||||
|
||||
void GetEntityIds(const AZStd::function<bool(AZ::EntityId)>& callback);
|
||||
void GetEntityIds(const AZStd::function<bool(AZ::EntityId)>& callback) const;
|
||||
void GetEntityIdToAlias(const AZStd::function<bool(AZ::EntityId, EntityAliasView)>& 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<AZStd::reference_wrapper<EntityAlias>> 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<Instance*, EntityAliasView> FindInstanceAndAlias(AZ::EntityId entity);
|
||||
AZStd::pair<const Instance*, EntityAliasView> 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();
|
||||
|
||||
|
||||
+33
-23
@@ -13,9 +13,12 @@
|
||||
#include <AzCore/Serialization/Utils.h>
|
||||
#include <AzFramework/Spawnable/Spawnable.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabCatchmentProcessor.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h>
|
||||
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
void PrefabCatchmentProcessor::Process(PrefabProcessorContext& context)
|
||||
@@ -37,7 +40,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
->Value("Text", SerializationFormats::Text);
|
||||
|
||||
serializeContext->Class<PrefabCatchmentProcessor, PrefabProcessor>()
|
||||
->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<AZ::Entity> 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));
|
||||
}
|
||||
|
||||
+1
@@ -54,6 +54,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
processor->Process(context);
|
||||
}
|
||||
context.ResolveLinks();
|
||||
}
|
||||
size_t PrefabConversionPipeline::CalculateProcessorFingerprint(AZ::SerializeContext* context)
|
||||
{
|
||||
|
||||
+128
-8
@@ -6,12 +6,26 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzFramework/Spawnable/Spawnable.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h>
|
||||
|
||||
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<EntityAliasPrefabLink>(&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<EntityAliasSpawnableLink>(spawnable, id);
|
||||
}
|
||||
|
||||
auto targetPrefab = AZStd::get_if<EntityAliasPrefabLink>(&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<EntityAliasSpawnableLink>(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<AZ::Data::AssetId, AzFramework::Spawnable::EntityAliasVisitor> aliasVisitors;
|
||||
|
||||
for (EntityAliasStore& alias : m_entityAliases)
|
||||
{
|
||||
auto source = AZStd::get_if<EntityAliasSpawnableLink>(&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<EntityAliasSpawnableLink>(&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<AZ::u64>(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<AZ::u64>(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<AzFramework::Spawnable>(&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
|
||||
|
||||
+80
-5
@@ -9,24 +9,84 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/ComponentExport.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzCore/std/containers/variant.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/functional.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/string/string_view.h>
|
||||
#include <AzFramework/Spawnable/Spawnable.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomTypes.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.h>
|
||||
|
||||
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<AZStd::monostate, EntityAliasSpawnableLink, EntityAliasPrefabLink>;
|
||||
|
||||
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<ProcessedObjectStore>;
|
||||
using ProductAssetDependencyContainer =
|
||||
AZStd::unordered_map<AZ::Data::AssetId, AZStd::unordered_set<AZ::Data::AssetId>>;
|
||||
using ProductAssetDependencyContainer = AZStd::unordered_multimap<AZ::Data::AssetId, AssetDependencyInfo>;
|
||||
|
||||
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<void(AZStd::string_view, const PrefabDom&)>& 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<AZStd::string, PrefabDom>;
|
||||
using SpawnableEntityAliasStore = AZStd::vector<EntityAliasStore>;
|
||||
|
||||
AZ::Data::AssetLoadBehavior ToAssetLoadBehavior(EntityAliasSpawnableLoadBehavior loadBehavior) const;
|
||||
|
||||
NamedPrefabContainer m_prefabs;
|
||||
SpawnableEntityAliasStore m_entityAliases;
|
||||
ProcessedObjectStoreContainer m_products;
|
||||
ProductAssetDependencyContainer m_registeredProductAssetDependencies;
|
||||
|
||||
|
||||
+16
-2
@@ -11,7 +11,16 @@
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
ProcessedObjectStore::ProcessedObjectStore(AZStd::string uniqueId, AZStd::unique_ptr<AZ::Data::AssetData> 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<AZ::Data::AssetData> 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;
|
||||
|
||||
+11
-5
@@ -26,6 +26,12 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
public:
|
||||
using SerializerFunction = AZStd::function<bool(AZStd::vector<uint8_t>&, const ProcessedObjectStore&)>;
|
||||
|
||||
struct AssetSmartPtrDeleter
|
||||
{
|
||||
void operator()(AZ::Data::AssetData* asset);
|
||||
};
|
||||
using AssetSmartPtr = AZStd::unique_ptr<AZ::Data::AssetData, AssetSmartPtrDeleter>;
|
||||
|
||||
//! 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<uint8_t>& 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<AZ::Data::AssetData> ReleaseAsset();
|
||||
AssetSmartPtr ReleaseAsset();
|
||||
|
||||
AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& GetReferencedAssets();
|
||||
const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& GetReferencedAssets() const;
|
||||
|
||||
|
||||
const AZStd::string& GetId() const;
|
||||
|
||||
private:
|
||||
ProcessedObjectStore(AZStd::string uniqueId, AZStd::unique_ptr<AZ::Data::AssetData> asset, SerializerFunction assetSerializer);
|
||||
ProcessedObjectStore(AZStd::string uniqueId, AssetSmartPtr asset, SerializerFunction assetSerializer);
|
||||
|
||||
SerializerFunction m_assetSerializer;
|
||||
AZStd::unique_ptr<AZ::Data::AssetData> m_asset;
|
||||
AssetSmartPtr m_asset;
|
||||
AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>> m_referencedAssets;
|
||||
AZStd::string m_uniqueId;
|
||||
};
|
||||
@@ -66,7 +72,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
static_assert(AZStd::is_base_of_v<AZ::Data::AssetData, T>,
|
||||
"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<T>(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<T*>(&resultLeft.GetAsset());
|
||||
return AZStd::make_pair<ProcessedObjectStore, T*>(AZStd::move(resultLeft), resultRight);
|
||||
|
||||
+238
-4
@@ -8,9 +8,11 @@
|
||||
|
||||
#include <AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h>
|
||||
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Component/EntityUtils.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/std/algorithm.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzFramework/Spawnable/Spawnable.h>
|
||||
@@ -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<AZ::Entity>& 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<typename T>
|
||||
AZStd::unique_ptr<AZ::Entity> 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<AZ::u64>(entity));
|
||||
auto clone = AZStd::make_unique<AZ::Entity>();
|
||||
|
||||
static AZ::SerializeContext* sc = GetSerializeContext();
|
||||
sc->CloneObjectInplace(*clone, target);
|
||||
clone->SetId(AZ::Entity::MakeId());
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> 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<AZ::u64>(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<AZ::Entity>(entityData->get().GetId(), entityData->get().GetName());
|
||||
return instance->ReplaceEntity(AZStd::move(placeholder), alias);
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> 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<AZ::u64>(entity));
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> original = AZStd::move(source.GetEntities()[index]);
|
||||
AZ_Assert(
|
||||
original, "SpawnbleUtils were unable to locate entity with id %zu in Spawnable for replacing.",
|
||||
aznumeric_cast<AZ::u64>(entity));
|
||||
|
||||
source.GetEntities()[index] = AZStd::make_unique<AZ::Entity>(original->GetId(), original->GetName());
|
||||
|
||||
return original;
|
||||
}
|
||||
|
||||
template<typename Source>
|
||||
AZStd::pair<AZStd::unique_ptr<AZ::Entity>, AzFramework::Spawnable::EntityAliasType> ApplyAlias(
|
||||
Source& source, AZ::EntityId entity, AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType)
|
||||
{
|
||||
namespace PCU = AzToolsFramework::Prefab::PrefabConversionUtils;
|
||||
using ResultPair = AZStd::pair<AZStd::unique_ptr<AZ::Entity>, 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>(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<AZ::Entity>(entity), AzFramework::Spawnable::EntityAliasType::Merge);
|
||||
default:
|
||||
AZ_Assert(
|
||||
false, "Invalid PrefabProcessorContext::EntityAliasType type (%i) provided.", aznumeric_cast<uint64_t>(aliasType));
|
||||
return ResultPair(nullptr, AzFramework::Spawnable::EntityAliasType::Disabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CreateSpawnable(AzFramework::Spawnable& spawnable, const PrefabDom& prefabDom)
|
||||
{
|
||||
AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>> referencedAssets;
|
||||
return CreateSpawnable(spawnable, prefabDom, referencedAssets);
|
||||
}
|
||||
|
||||
bool CreateSpawnable(AzFramework::Spawnable& spawnable, const PrefabDom& prefabDom, AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& referencedAssets)
|
||||
bool CreateSpawnable(
|
||||
AzFramework::Spawnable& spawnable,
|
||||
const PrefabDom& prefabDom,
|
||||
AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& 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<EntityAliasPrefabLink>(AZStd::move(sourcePrefabName), AZStd::move(alias));
|
||||
store.m_target.emplace<EntityAliasPrefabLink>(
|
||||
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<AZ::u64>(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<EntityAliasPrefabLink>(AZStd::move(sourcePrefabName), AZStd::move(alias));
|
||||
store.m_target.emplace<EntityAliasSpawnableLink>(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<AZ::u64>(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<EntityAliasSpawnableLink>(source, entity);
|
||||
store.m_target.emplace<EntityAliasSpawnableLink>(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<typename EntityPtr>
|
||||
void OrganizeEntitiesForSorting(
|
||||
AZStd::vector<EntityPtr>& entities,
|
||||
|
||||
@@ -8,14 +8,59 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/EntityId.h>
|
||||
#include <AzCore/std/limits.h>
|
||||
#include <AzFramework/Spawnable/Spawnable.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomTypes.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class Entity;
|
||||
}
|
||||
|
||||
namespace AzToolsFramework::Prefab
|
||||
{
|
||||
class Instance;
|
||||
}
|
||||
|
||||
namespace AzToolsFramework::Prefab::SpawnableUtils
|
||||
{
|
||||
static constexpr uint32_t InvalidEntityIndex = AZStd::numeric_limits<uint32_t>::max();
|
||||
|
||||
bool CreateSpawnable(AzFramework::Spawnable& spawnable, const PrefabDom& prefabDom);
|
||||
bool CreateSpawnable(AzFramework::Spawnable& spawnable, const PrefabDom& prefabDom, AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& 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 <typename EntityPtr>
|
||||
|
||||
@@ -175,6 +175,8 @@ namespace AZ::Prefab
|
||||
const AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext::ProductAssetDependencyContainer& registeredDependencies,
|
||||
AZStd::vector<AssetBuilderSDK::JobProduct>& outputProducts) const
|
||||
{
|
||||
using namespace AzToolsFramework::Prefab::PrefabConversionUtils;
|
||||
|
||||
outputProducts.reserve(store.size());
|
||||
|
||||
AZStd::vector<uint8_t> 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));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user