Added additional functions to edit prefabs during processing to PrefabDocument.

Signed-off-by: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com>
This commit is contained in:
AMZN-koppersr
2021-12-15 10:05:44 -08:00
parent b8e3b27ea9
commit 7b5868d198
10 changed files with 193 additions and 89 deletions
@@ -608,6 +608,31 @@ namespace AzToolsFramework
}
AZ::EntityId Instance::GetEntityIdFromAliasPath(AliasPathView relativeAliasPath) const
{
return GetInstanceAndEntityIdFromAliasPath(relativeAliasPath).second;
}
AZStd::pair<Instance*, AZ::EntityId> Instance::GetInstanceAndEntityIdFromAliasPath(AliasPathView relativeAliasPath)
{
Instance* instance = this;
AliasPathView path = relativeAliasPath.ParentPath();
for (auto it : path)
{
InstanceOptionalReference child = instance->FindNestedInstance(it.Native());
if (child.has_value())
{
instance = &(child->get());
}
else
{
return AZStd::pair<Instance*, AZ::EntityId>(nullptr, AZ::EntityId());
}
}
return AZStd::pair<Instance*, AZ::EntityId>(instance, instance->GetEntityId(relativeAliasPath.Filename().Native()));
}
AZStd::pair<const Instance*, AZ::EntityId> Instance::GetInstanceAndEntityIdFromAliasPath(AliasPathView relativeAliasPath) const
{
const Instance* instance = this;
AliasPathView path = relativeAliasPath.ParentPath();
@@ -620,11 +645,11 @@ namespace AzToolsFramework
}
else
{
return AZ::EntityId();
return AZStd::pair<const Instance*, AZ::EntityId>(nullptr, AZ::EntityId());
}
}
return instance->GetEntityId(relativeAliasPath.Filename().Native());
return AZStd::pair<const Instance*, AZ::EntityId>(instance, instance->GetEntityId(relativeAliasPath.Filename().Native()));
}
AZStd::vector<InstanceAlias> Instance::GetNestedInstanceAliases(TemplateId templateId) const
@@ -169,6 +169,13 @@ namespace AzToolsFramework
* @return entityId, invalid ID if not found
*/
AZ::EntityId GetEntityIdFromAliasPath(AliasPathView relativeAliasPath) const;
/**
* Retrieves the instance pointer and entity id from an alias path that's relative to this instance.
*
* @return A pair with the Instance and entity id. The Instance is set to null and entityId is set to invalid if not found.
*/
AZStd::pair<Instance*, AZ::EntityId> GetInstanceAndEntityIdFromAliasPath(AliasPathView relativeAliasPath);
AZStd::pair<const Instance*, AZ::EntityId> GetInstanceAndEntityIdFromAliasPath(AliasPathView relativeAliasPath) const;
/**
@@ -0,0 +1,69 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/base.h>
#include <AzCore/Component/EntityId.h>
#include <AzCore/std/containers/variant.h>
#include <AzCore/std/string/string.h>
#include <AzFramework/Spawnable/Spawnable.h>
#include <AzToolsFramework/Prefab/Instance/Instance.h>
namespace AzToolsFramework::Prefab::PrefabConversionUtils
{
enum class EntityAliasType : uint8_t
{
Disable, //!< 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. 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. This option will allow
//!< to disable loading the referenced spawnable through the event fired from the spawnables asset handler.
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(AzFramework::Spawnable& spawnable, AZ::EntityId index);
AzFramework::Spawnable& m_spawnable;
AZ::EntityId m_index;
};
struct EntityAliasPrefabLink
{
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;
};
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
@@ -8,6 +8,7 @@
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
#include <AzToolsFramework/Prefab/Spawnable/PrefabDocument.h>
#include <AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h>
namespace AzToolsFramework::Prefab::PrefabConversionUtils
{
@@ -68,6 +69,43 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
return AZStd::move(m_dom);
}
void PrefabDocument::ListEntitiesWithComponentType(
AZ::TypeId componentType, const AZStd::function<bool(AzToolsFramework::Prefab::AliasPath&&)>& callback) const
{
m_instance->GetAllEntitiesInHierarchyConst(
[this, &componentType, &callback](const AZ::Entity& entity) -> bool
{
if (entity.FindComponent(componentType))
{
return callback(m_instance->GetAliasPathRelativeToInstance(entity.GetId()));
}
else
{
return true;
}
});
}
AZ::Entity* PrefabDocument::CreateEntityAlias(
PrefabDocument& source,
AzToolsFramework::Prefab::AliasPathView entity,
AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType,
AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior,
uint32_t tag,
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context)
{
auto&& [sourceInstance, entityId] = source.m_instance->GetInstanceAndEntityIdFromAliasPath(entity);
if (sourceInstance != nullptr && entityId.IsValid())
{
return SpawnableUtils::CreateEntityAlias(
source.m_name, *sourceInstance, m_name, *m_instance, entityId, aliasType, loadBehavior, tag, context);
}
else
{
return nullptr;
}
}
AzToolsFramework::Prefab::Instance& PrefabDocument::GetInstance()
{
// Assume that changes will be made to the instance.
@@ -91,18 +129,16 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
}
else
{
AZStd::string errorMessage("Failed to construct Prefab instance from given PrefabDOM");
#ifdef AZ_ENABLE_TRACING
AZStd::string_view sourceName = m_name;
PrefabDomValueConstReference sourceReference = PrefabDomUtils::FindPrefabDomValue(prefab, PrefabDomUtils::SourceName);
if (sourceReference.has_value() && sourceReference->get().IsString() && sourceReference->get().GetStringLength() != 0)
{
errorMessage += " (Source: ";
errorMessage += AZStd::string_view(sourceReference->get().GetString(), sourceReference->get().GetStringLength());
errorMessage += ')';
sourceName = AZStd::string_view(sourceReference->get().GetString(), sourceReference->get().GetStringLength());
}
errorMessage += '.';
AZ_Error("PrefabDocument", false, errorMessage.c_str());
AZ_Error(
"PrefabDocument", false, "Failed to construct Prefab instance from given PrefabDOM '%.*s'.", AZ_STRING_ARG(sourceName));
#endif
return false;
}
}
@@ -8,13 +8,18 @@
#pragma once
#include <AzCore/RTTI/TypeInfo.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/string/string.h>
#include <AzToolsFramework/Prefab/Instance/Instance.h>
#include <AzToolsFramework/Prefab/PrefabDomTypes.h>
#include <AzToolsFramework/Prefab/Spawnable/EntityAliasTypes.h>
#include <AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h>
namespace AzToolsFramework::Prefab::PrefabConversionUtils
{
class PrefabProcessorContext;
class PrefabDocument final
{
public:
@@ -32,6 +37,18 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
const PrefabDom& GetDom() const;
PrefabDom&& TakeDom();
template<typename Component>
void ListEntitiesWithComponentType(const AZStd::function<bool(AzToolsFramework::Prefab::AliasPath&&)>& callback) const;
void ListEntitiesWithComponentType(
AZ::TypeId componentType, const AZStd::function<bool(AzToolsFramework::Prefab::AliasPath&&)>& callback) const;
AZ::Entity* CreateEntityAlias(
PrefabDocument& source,
AzToolsFramework::Prefab::AliasPathView entity,
AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType,
AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior,
uint32_t tag,
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context);
// Where possible, prefer functions directly on the PrefabDocument Instead of using the Instance.
AzToolsFramework::Prefab::Instance& GetInstance();
const AzToolsFramework::Prefab::Instance& GetInstance() const;
@@ -45,3 +62,5 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
mutable bool m_isDirty{ false };
};
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
#include <AzToolsFramework/Prefab/Spawnable/PrefabDocument.inl>
@@ -0,0 +1,16 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
namespace AzToolsFramework::Prefab::PrefabConversionUtils
{
template<typename Component>
void PrefabDocument::ListEntitiesWithComponentType(const AZStd::function<bool(AzToolsFramework::Prefab::AliasPath&&)>& callback) const
{
ListEntitiesWithComponentType(azrtti_typeid<Component>(), callback);
}
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
@@ -21,61 +21,12 @@
#include <AzFramework/Spawnable/Spawnable.h>
#include <AzToolsFramework/Prefab/Instance/Instance.h>
#include <AzToolsFramework/Prefab/PrefabDomTypes.h>
#include <AzToolsFramework/Prefab/Spawnable/EntityAliasTypes.h>
#include <AzToolsFramework/Prefab/Spawnable/PrefabDocument.h>
#include <AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.h>
namespace AzToolsFramework::Prefab::PrefabConversionUtils
{
enum class EntityAliasType : uint8_t
{
Disable, //!< 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. 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. This option will allow
//!< to disable loading the referenced spawnable through the event fired from the spawnables asset handler.
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(AzFramework::Spawnable& spawnable, AZ::EntityId index);
AzFramework::Spawnable& m_spawnable;
AZ::EntityId m_index;
};
struct EntityAliasPrefabLink
{
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;
@@ -21,6 +21,7 @@
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Prefab/Instance/Instance.h>
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
#include <AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h>
namespace AzToolsFramework::Prefab::SpawnableUtils
{
@@ -222,32 +223,6 @@ namespace AzToolsFramework::Prefab::SpawnableUtils
}
}
void PatchParents(const AzToolsFramework::Prefab::Instance& source, AzToolsFramework::Prefab::Instance& target)
{
target.GetEntities(
[&source, &target](AZStd::unique_ptr<AZ::Entity>& entity)
{
AzFramework::TransformComponent* transform = entity->FindComponent<AzFramework::TransformComponent>();
if (transform)
{
if (transform->GetParentId().IsValid())
{
AliasPath originalParentAlias = source.GetAliasPathRelativeToInstance(transform->GetParentId());
if (!originalParentAlias.empty())
{
AZ::EntityId targetParentId = target.GetEntityIdFromAliasPath(originalParentAlias);
if (targetParentId.IsValid())
{
// If this is valid then the parent was moved to the target spawnable so adjust the entity id.
transform->SetParent(targetParentId);
}
}
}
}
return true;
});
}
uint32_t FindEntityIndex(AZ::EntityId entity, const AzFramework::Spawnable& spawnable)
{
auto begin = spawnable.GetEntities().begin();
@@ -12,7 +12,7 @@
#include <AzCore/std/limits.h>
#include <AzFramework/Spawnable/Spawnable.h>
#include <AzToolsFramework/Prefab/PrefabDomTypes.h>
#include <AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h>
#include <AzToolsFramework/Prefab/Spawnable/EntityAliasTypes.h>
namespace AZ
{
@@ -24,6 +24,11 @@ namespace AzToolsFramework::Prefab
class Instance;
}
namespace AzToolsFramework::Prefab::PrefabConversionUtils
{
class PrefabProcessorContext;
}
namespace AzToolsFramework::Prefab::SpawnableUtils
{
static constexpr uint32_t InvalidEntityIndex = AZStd::numeric_limits<uint32_t>::max();
@@ -41,8 +46,7 @@ namespace AzToolsFramework::Prefab::SpawnableUtils
AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior,
uint32_t tag,
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context);
void PatchParents(const AzToolsFramework::Prefab::Instance& source, AzToolsFramework::Prefab::Instance& target);
uint32_t FindEntityIndex(AZ::EntityId entity, const AzFramework::Spawnable& spawnable);
void SortEntitiesByTransformHierarchy(AzFramework::Spawnable& spawnable);
@@ -723,6 +723,7 @@ set(FILES
Prefab/Spawnable/EditorOnlyEntityHandler/UiEditorOnlyEntityHandler.cpp
Prefab/Spawnable/EditorOnlyEntityHandler/WorldEditorOnlyEntityHandler.h
Prefab/Spawnable/EditorOnlyEntityHandler/WorldEditorOnlyEntityHandler.cpp
Prefab/Spawnable/EntityAliasTypes.h
Prefab/Spawnable/InMemorySpawnableAssetContainer.h
Prefab/Spawnable/InMemorySpawnableAssetContainer.cpp
Prefab/Spawnable/PrefabCatchmentProcessor.h
@@ -731,6 +732,7 @@ set(FILES
Prefab/Spawnable/PrefabConversionPipeline.cpp
Prefab/Spawnable/PrefabConverterStackProfileNames.h
Prefab/Spawnable/PrefabDocument.h
Prefab/Spawnable/PrefabDocument.inl
Prefab/Spawnable/PrefabDocument.cpp
Prefab/Spawnable/ProcesedObjectStore.h
Prefab/Spawnable/ProcesedObjectStore.cpp