Support for nested slice conversions (#1121)

This set of changes enables conversions for singly-nested slices. Multiple nesting hierarchies are only partially supported at this point. Conversion is also significantly more deterministic, which makes it easier to convert single slices without needing to reconvert every slice or level that relies on it as well.
Changes:

- Added version of Instance::AddInstance() that takes in an alias to allow for deterministic aliases
- Added a "SliceConverterEditorEntityContextComponent" that's used to specifically disable entity activation on creation. The disabling is done this way vs adding a new public API, because the disable shouldn't be required in any normal case outside of this tool.
- Disabled more AWS gems for the SliceConverter, as they're unneeded and cause issues if they're around in the tool.
- Added a small null check to the Camera Controller.
- Added the actual support for slice instance conversion. This instantiates the entities, applies the data patches, turns them into a prefab instance, and generates a JSON patch out of the changes.
main
Mike Balfour 5 years ago committed by GitHub
parent c155271167
commit d90a3d46a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -971,6 +971,10 @@ namespace AZ
*/
void RestoreCachedInstances();
/// Returns data flags for use when instantiating an instance of this slice.
/// These data flags include those harvested from the entire slice ancestry.
const DataFlagsPerEntity& GetDataFlagsForInstances() const;
protected:
//////////////////////////////////////////////////////////////////////////
@ -1004,9 +1008,6 @@ namespace AZ
DataFlagsPerEntity* GetCorrectBundleOfDataFlags(EntityId entityId);
const DataFlagsPerEntity* GetCorrectBundleOfDataFlags(EntityId entityId) const;
/// Returns data flags for use when instantiating an instance of this slice.
/// These data flags include those harvested from the entire slice ancestry.
const DataFlagsPerEntity& GetDataFlagsForInstances() const;
void BuildDataFlagsForInstances();
/**

@ -18,6 +18,7 @@
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Prefab/Instance/Instance.h>
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
#include <AzToolsFramework/Prefab/Instance/TemplateInstanceMapperInterface.h>
@ -49,8 +50,7 @@ namespace AzToolsFramework
m_alias = GenerateInstanceAlias();
m_containerEntity = containerEntity ? AZStd::move(containerEntity)
: AZStd::make_unique<AZ::Entity>();
EntityAlias containerEntityAlias = GenerateEntityAlias();
RegisterEntity(m_containerEntity->GetId(), containerEntityAlias);
RegisterEntity(m_containerEntity->GetId(), PrefabDomUtils::ContainerEntityName);
}
Instance::~Instance()
@ -311,8 +311,15 @@ namespace AzToolsFramework
Instance& Instance::AddInstance(AZStd::unique_ptr<Instance> instance)
{
InstanceAlias newInstanceAlias = GenerateInstanceAlias();
return AddInstance(AZStd::move(instance), newInstanceAlias);
}
Instance& Instance::AddInstance(AZStd::unique_ptr<Instance> instance, InstanceAlias newInstanceAlias)
{
AZ_Assert(instance.get(), "instance argument is nullptr");
AZ_Assert(m_nestedInstances.find(newInstanceAlias) == m_nestedInstances.end(), "InstanceAlias' unique id collision, this should never happen.");
AZ_Assert(
m_nestedInstances.find(newInstanceAlias) == m_nestedInstances.end(),
"InstanceAlias' unique id collision, this should never happen.");
instance->m_parent = this;
instance->m_alias = newInstanceAlias;
return *(m_nestedInstances[newInstanceAlias] = std::move(instance));

@ -93,6 +93,7 @@ namespace AzToolsFramework
void Reset();
Instance& AddInstance(AZStd::unique_ptr<Instance> instance);
Instance& AddInstance(AZStd::unique_ptr<Instance> instance, InstanceAlias instanceAlias);
AZStd::unique_ptr<Instance> DetachNestedInstance(const InstanceAlias& instanceAlias);
/**

@ -17,6 +17,7 @@
#include <AzCore/Utils/Utils.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerNullComponent.h>
#include <SliceConverterEditorEntityContextComponent.h>
namespace AZ
{
@ -34,6 +35,9 @@ namespace AZ
Application::Application(int argc, char** argv)
: AzToolsFramework::ToolsApplication(&argc, &argv)
{
// We need a specialized variant of EditorEntityContextCompnent for the SliceConverter, so we register the descriptor here.
RegisterComponentDescriptor(AzToolsFramework::SliceConverterEditorEntityContextComponent::CreateDescriptor());
AZ::IO::FixedMaxPath projectPath = AZ::Utils::GetProjectPath();
if (projectPath.empty())
{
@ -110,10 +114,21 @@ namespace AZ
AZ::ComponentTypeList Application::GetRequiredSystemComponents() const
{
// Use all of the default system components, but also add in the ThumbnailerNullComponent so that components requiring
// a ThumbnailService can still be started up.
// By default, we use all of the standard system components.
AZ::ComponentTypeList components = AzToolsFramework::ToolsApplication::GetRequiredSystemComponents();
// Also add in the ThumbnailerNullComponent so that components requiring a ThumbnailService can still be started up.
components.emplace_back(azrtti_typeid<AzToolsFramework::Thumbnailer::ThumbnailerNullComponent>());
// The Slice Converter requires a specialized variant of the EditorEntityContextComponent that exposes the ability
// to disable the behavior of activating entities on creation. During conversion, the creation flow will be triggered,
// but entity activation requires a significant amount of subsystem initialization that's unneeded for conversion.
// So, to get around this, we swap out EditorEntityContextComponent with SliceConverterEditorEntityContextComponent.
components.erase(
AZStd::remove(
components.begin(), components.end(), azrtti_typeid<AzToolsFramework::EditorEntityContextComponent>()),
components.end());
components.emplace_back(azrtti_typeid<AzToolsFramework::SliceConverterEditorEntityContextComponent>());
return components;
}
} // namespace SerializeContextTools

@ -30,13 +30,16 @@
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
#include <AzToolsFramework/Prefab/EditorPrefabComponent.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <AzToolsFramework/Prefab/Instance/InstanceUpdateExecutorInterface.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
#include <Application.h>
#include <SliceConverter.h>
#include <SliceConverterEditorEntityContextComponent.h>
#include <Utilities.h>
// SliceConverter reads in a slice file (saved in an ObjectStream format), instantiates it, creates a prefab out of the data,
// and saves the prefab in a JSON format. This can be used for one-time migrations of slices or slice-based levels to prefabs.
//
@ -99,12 +102,26 @@ namespace AZ
bool result = true;
rapidjson::StringBuffer scratchBuffer;
// For slice conversion, disable the EditorEntityContextComponent logic that activates entities on creation.
// This prevents a lot of error messages and crashes during conversion due to lack of full environment and subsystem setup.
AzToolsFramework::SliceConverterEditorEntityContextComponent::DisableOnContextEntityLogic();
// Loop through the list of requested files and convert them.
AZStd::vector<AZStd::string> fileList = Utilities::ReadFileListFromCommandLine(application, "files");
for (AZStd::string& filePath : fileList)
{
bool convertResult = ConvertSliceFile(convertSettings.m_serializeContext, filePath, isDryRun);
result = result && convertResult;
// Clear out all registered prefab templates between each top-level file that gets processed.
auto prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
for (auto templateId : m_createdTemplateIds)
{
// We don't just want to call RemoveAllTemplates() because the root template should remain between file conversions.
prefabSystemComponentInterface->RemoveTemplate(templateId);
}
m_aliasIdMapper.clear();
m_createdTemplateIds.clear();
}
DisconnectFromAssetProcessor();
@ -114,6 +131,13 @@ namespace AZ
bool SliceConverter::ConvertSliceFile(
AZ::SerializeContext* serializeContext, const AZStd::string& slicePath, bool isDryRun)
{
/* To convert a slice file, we read the input file in via ObjectStream, then use the "class ready" callback to convert
* the data in memory to a Prefab.
* If the input file is a level file (.ly), we actually need to load the level slice file ("levelentities.editor_xml") from
* within the level file, which effectively is a zip file of the level slice file and a bunch of legacy level files that won't
* be converted, since the systems that would use them no longer exist.
*/
bool result = true;
bool packOpened = false;
@ -144,7 +168,7 @@ namespace AZ
AZ_STRING_ARG(fileExtension.Native()));
}
auto callback = [&outputPath, isDryRun](void* classPtr, const Uuid& classId, SerializeContext* context)
auto callback = [this, &outputPath, isDryRun](void* classPtr, const Uuid& classId, SerializeContext* context)
{
if (classId != azrtti_typeid<AZ::Entity>())
{
@ -178,6 +202,13 @@ namespace AZ
bool SliceConverter::ConvertSliceToPrefab(
AZ::SerializeContext* serializeContext, AZ::IO::PathView outputPath, bool isDryRun, AZ::Entity* rootEntity)
{
/* Given a root slice entity, we convert it to a prefab by doing the following:
* - Locate the SliceComponent
* - Take all the entities directly located on the slice, and put them into a prefab
* - Fix up any top-level entities to have the prefab container entity as their parent
* - If there are any nested slice instances, convert the nested slices to prefabs, then convert the instances.
*/
auto prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
// Find the slice from the root entity.
@ -192,9 +223,14 @@ namespace AZ
SliceComponent::EntityList sliceEntities = sliceComponent->GetNewEntities();
AZ_Printf("Convert-Slice", " Slice contains %zu entities.\n", sliceEntities.size());
// Create the Prefab with the entities from the slice
// Create the Prefab with the entities from the slice.
// The entities are added in a separate step so that we can give them deterministic entity aliases that match their entity Ids
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> sourceInstance(
prefabSystemComponentInterface->CreatePrefab(sliceEntities, {}, outputPath));
prefabSystemComponentInterface->CreatePrefab({}, {}, outputPath));
for (auto& entity : sliceEntities)
{
sourceInstance->AddEntity(*entity, AZStd::string::format("Entity_%s", entity->GetId().ToString().c_str()));
}
// Dispatch events here, because prefab creation might trigger asset loads in rare circumstances.
AZ::Data::AssetManager::Instance().DispatchEvents();
@ -204,12 +240,28 @@ namespace AZ
AzToolsFramework::Prefab::EntityOptionalReference container = sourceInstance->GetContainerEntity();
FixPrefabEntities(container->get(), sliceEntities);
// Keep track of the template Id we created, we're going to remove it at the end of slice file conversion to make sure
// the data doesn't stick around between file conversions.
auto templateId = sourceInstance->GetTemplateId();
if (templateId == AzToolsFramework::Prefab::InvalidTemplateId)
{
AZ_Printf("Convert-Slice", " Path error. Path could be invalid, or the prefab may not be loaded in this level.\n");
return false;
}
m_createdTemplateIds.emplace(templateId);
// Save off a mapping of the original slice entity IDs to the new prefab template entity aliases.
// When converting nested slices, this mapping will be needed to fix up the parent entity hierarchy correctly.
auto entityAliases = sourceInstance->GetEntityAliases();
for (auto& alias : entityAliases)
{
auto id = sourceInstance->GetEntityId(alias);
auto result = m_aliasIdMapper.emplace(TemplateEntityIdPair(templateId, id), alias);
if (!result.second)
{
AZ_Printf("Convert-Slice", " Duplicate entity alias -> entity id entries found, conversion may not be successful.\n");
}
}
// Update the prefab template with the fixed-up data in our prefab instance.
AzToolsFramework::Prefab::PrefabDom prefabDom;
@ -254,21 +306,26 @@ namespace AZ
// via an EditorRequests EBus in CreatePrefab, but the subsystem that listens for it isn't present in this tool.)
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
&AzToolsFramework::EditorEntityContextRequestBus::Events::AddRequiredComponents, containerEntity);
containerEntity.AddComponent(aznew AzToolsFramework::Prefab::EditorPrefabComponent());
if (containerEntity.FindComponent<AzToolsFramework::Prefab::EditorPrefabComponent>() == nullptr)
{
containerEntity.AddComponent(aznew AzToolsFramework::Prefab::EditorPrefabComponent());
}
// Make all the components on the container entity have deterministic component IDs, so that multiple runs of the tool
// on the same slice will produce the same prefab output. We're going to cheat a bit and just use the component type hash
// as the component ID. This would break if we had multiple components of the same type, but that currently doesn't
// happen for the container entity.
auto containerComponents = containerEntity.GetComponents();
for (auto& component : containerComponents)
{
component->SetId(component->GetUnderlyingComponentType().GetHash());
}
// Reparent any root-level slice entities to the container entity.
for (auto entity : sliceEntities)
{
AzToolsFramework::Components::TransformComponent* transformComponent =
entity->FindComponent<AzToolsFramework::Components::TransformComponent>();
if (transformComponent)
{
if (!transformComponent->GetParentId().IsValid())
{
transformComponent->SetParent(containerEntity.GetId());
transformComponent->UpdateCachedWorldTransform();
}
}
constexpr bool onlySetIfInvalid = true;
SetParentEntity(*entity, containerEntity.GetId(), onlySetIfInvalid);
}
}
@ -276,9 +333,13 @@ namespace AZ
SliceComponent* sliceComponent, AzToolsFramework::Prefab::Instance* sourceInstance,
AZ::SerializeContext* serializeContext, bool isDryRun)
{
/* Given a root slice, find all the nested slices and convert them. */
// Get the list of nested slices that this slice uses.
const SliceComponent::SliceList& sliceList = sliceComponent->GetSlices();
auto prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
// For each nested slice, convert it.
for (auto& slice : sliceList)
{
// Get the nested slice asset
@ -312,7 +373,7 @@ namespace AZ
return false;
}
// Load the prefab template for the newly-created nested prefab.
// Find the prefab template we created for the newly-created nested prefab.
// To get the template, we need to take our absolute slice path and turn it into a project-relative prefab path.
AZ::IO::Path nestedPrefabPath = assetPath;
nestedPrefabPath.ReplaceExtension("prefab");
@ -346,11 +407,25 @@ namespace AZ
}
bool SliceConverter::ConvertSliceInstance(
[[maybe_unused]] AZ::SliceComponent::SliceInstance& instance,
[[maybe_unused]] AZ::Data::Asset<AZ::SliceAsset>& sliceAsset,
AZ::SliceComponent::SliceInstance& instance,
AZ::Data::Asset<AZ::SliceAsset>& sliceAsset,
AzToolsFramework::Prefab::TemplateReference nestedTemplate,
AzToolsFramework::Prefab::Instance* topLevelInstance)
{
/* To convert a slice instance, it's important to understand the similarities and differences between slices and prefabs.
* Both slices and prefabs have the concept of instances of a nested slice/prefab, where each instance can have its own
* set of changed data (transforms, component values, etc).
* For slices, the changed data comes from applying a DataPatch to an instantiated set of entities from the nested slice.
* From prefabs, the changed data comes from Json patches that are applied to the instantiated set of entities from the
* nested prefab. The prefab instance entities also have different IDs than the slice instance entities, so we'll need
* to remap some of them along the way.
* To get from one to the other, we'll need to do the following:
* - Instantiate the nested slice and nested prefab
* - Patch the nested slice instance and fix up the entity ID references
* - Replace the nested prefab instance entities with the fixed-up slice ones
* - Add the nested instance (and the link patch) to the top-level prefab
*/
auto instanceToTemplateInterface = AZ::Interface<AzToolsFramework::Prefab::InstanceToTemplateInterface>::Get();
auto prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
@ -371,22 +446,83 @@ namespace AZ
AzToolsFramework::Prefab::PrefabDom unmodifiedNestedInstanceDom;
instanceToTemplateInterface->GenerateDomForInstance(unmodifiedNestedInstanceDom, *(nestedInstance.get()));
// Currently, DataPatch conversions for nested slices aren't implemented, so all nested slice overrides will
// be lost.
AZ_Warning(
"Convert-Slice", false, " Nested slice instances will lose all of their override data during conversion.",
nestedTemplate->get().GetFilePath().c_str());
// Set the container entity of the nested prefab to have the top-level prefab as the parent.
// Once DataPatch conversions are supported, this will need to change to nest the prefab under the appropriate entity
// within the level.
// Instantiate a new instance of the nested slice
SliceComponent* dependentSlice = sliceAsset.Get()->GetComponent();
[[maybe_unused]] AZ::SliceComponent::InstantiateResult instantiationResult = dependentSlice->Instantiate();
AZ_Assert(instantiationResult == AZ::SliceComponent::InstantiateResult::Success, "Failed to instantiate instance");
// Apply the data patch for this instance of the nested slice. This will provide us with a version of the slice's entities
// with all data overrides applied to them.
DataPatch::FlagsMap sourceDataFlags = dependentSlice->GetDataFlagsForInstances().GetDataFlagsForPatching();
DataPatch::FlagsMap targetDataFlags = instance.GetDataFlags().GetDataFlagsForPatching(&instance.GetEntityIdToBaseMap());
AZ::ObjectStream::FilterDescriptor filterDesc(AZ::Data::AssetFilterNoAssetLoading);
AZ::SliceComponent::InstantiatedContainer sourceObjects(false);
dependentSlice->GetEntities(sourceObjects.m_entities);
dependentSlice->GetAllMetadataEntities(sourceObjects.m_metadataEntities);
const DataPatch& dataPatch = instance.GetDataPatch();
auto instantiated =
dataPatch.Apply(&sourceObjects, dependentSlice->GetSerializeContext(), filterDesc, sourceDataFlags, targetDataFlags);
// Run through all the instantiated entities and fix up their parent hierarchy:
// - Invalid parents need to get set to the container.
// - Valid parents into the top-level instance mean that the nested slice instance is also child-nested under an entity.
// Prefabs handle this type of nesting differently - we need to set the parent to the container, and the container's
// parent to that other instance.
auto containerEntity = nestedInstance->GetContainerEntity();
AzToolsFramework::Components::TransformComponent* transformComponent =
containerEntity->get().FindComponent<AzToolsFramework::Components::TransformComponent>();
if (transformComponent)
auto containerEntityId = containerEntity->get().GetId();
for (auto entity : instantiated->m_entities)
{
transformComponent->SetParent(topLevelInstance->GetContainerEntityId());
transformComponent->UpdateCachedWorldTransform();
AzToolsFramework::Components::TransformComponent* transformComponent =
entity->FindComponent<AzToolsFramework::Components::TransformComponent>();
if (transformComponent)
{
bool onlySetIfInvalid = true;
auto parentId = transformComponent->GetParentId();
if (parentId.IsValid())
{
auto parentAlias = m_aliasIdMapper.find(TemplateEntityIdPair(topLevelInstance->GetTemplateId(), parentId));
if (parentAlias != m_aliasIdMapper.end())
{
// Set the container's parent to this entity's parent, and set this entity's parent to the container
// (i.e. go from A->B to A->container->B)
auto newParentId = topLevelInstance->GetEntityId(parentAlias->second);
SetParentEntity(containerEntity->get(), newParentId, false);
onlySetIfInvalid = false;
}
}
SetParentEntity(*entity, containerEntityId, onlySetIfInvalid);
}
}
// Replace all the entities in the instance with the new patched ones.
// (This is easier than trying to figure out what the patched data changes are - we can let the JSON patch handle it for us)
nestedInstance->RemoveNestedEntities(
[](const AZStd::unique_ptr<AZ::Entity>&)
{
return true;
});
for (auto& entity : instantiated->m_entities)
{
auto entityAlias = m_aliasIdMapper.find(TemplateEntityIdPair(nestedInstance->GetTemplateId(), entity->GetId()));
if (entityAlias != m_aliasIdMapper.end())
{
nestedInstance->AddEntity(*entity, entityAlias->second);
}
else
{
AZ_Assert(false, "Failed to find entity alias.");
nestedInstance->AddEntity(*entity);
}
}
// Set the container entity of the nested prefab to have the top-level prefab as the parent if it hasn't already gotten
// another entity as its parent.
{
constexpr bool onlySetIfInvalid = true;
SetParentEntity(containerEntity->get(), topLevelInstance->GetContainerEntityId(), onlySetIfInvalid);
}
// Add the nested instance itself to the top-level prefab. To do this, we need to add it to our top-level instance,
@ -395,7 +531,22 @@ namespace AZ
AzToolsFramework::Prefab::PrefabDom topLevelInstanceDomBefore;
instanceToTemplateInterface->GenerateDomForInstance(topLevelInstanceDomBefore, *topLevelInstance);
AzToolsFramework::Prefab::Instance& addedInstance = topLevelInstance->AddInstance(AZStd::move(nestedInstance));
// When creating the new instance, we would like to have deterministic instance aliases. Prefabs that depend on this one
// will have patches that reference the alias, so if we reconvert this slice a second time, we would like it to produce
// the same results. To get a deterministic and unique alias, we rely on the slice instance. The slice instance contains
// a map of slice entity IDs to unique instance entity IDs. We'll just consistently use the first entry in the map as the
// unique instance ID.
AZStd::string instanceAlias;
auto entityIdMap = instance.GetEntityIdMap();
if (!entityIdMap.empty())
{
instanceAlias = AZStd::string::format("Instance_%s", entityIdMap.begin()->second.ToString().c_str());
}
else
{
instanceAlias = AZStd::string::format("Instance_%s", AZ::Entity::MakeId().ToString().c_str());
}
AzToolsFramework::Prefab::Instance& addedInstance = topLevelInstance->AddInstance(AZStd::move(nestedInstance), instanceAlias);
AzToolsFramework::Prefab::PrefabDom topLevelInstanceDomAfter;
instanceToTemplateInterface->GenerateDomForInstance(topLevelInstanceDomAfter, *topLevelInstance);
@ -418,9 +569,26 @@ namespace AZ
AzToolsFramework::Prefab::InvalidLinkId);
prefabSystemComponentInterface->PropagateTemplateChanges(topLevelInstance->GetTemplateId());
AZ::Interface<AzToolsFramework::Prefab::InstanceUpdateExecutorInterface>::Get()->UpdateTemplateInstancesInQueue();
return true;
}
void SliceConverter::SetParentEntity(const AZ::Entity& entity, const AZ::EntityId& parentId, bool onlySetIfInvalid)
{
AzToolsFramework::Components::TransformComponent* transformComponent =
entity.FindComponent<AzToolsFramework::Components::TransformComponent>();
if (transformComponent)
{
// Only set the parent if we didn't set the onlySetIfInvalid flag, or if we did and the parent is currently invalid
if (!onlySetIfInvalid || !transformComponent->GetParentId().IsValid())
{
transformComponent->SetParent(parentId);
transformComponent->UpdateCachedWorldTransform();
}
}
}
void SliceConverter::PrintPrefab(AzToolsFramework::Prefab::TemplateId templateId)
{
auto prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();

@ -39,24 +39,35 @@ namespace AZ
class SliceConverter : public Converter
{
public:
static bool ConvertSliceFiles(Application& application);
bool ConvertSliceFiles(Application& application);
private:
static bool ConnectToAssetProcessor();
static void DisconnectFromAssetProcessor();
using TemplateEntityIdPair = AZStd::pair<AzToolsFramework::Prefab::TemplateId, AZ::EntityId>;
static bool ConvertSliceFile(AZ::SerializeContext* serializeContext, const AZStd::string& slicePath, bool isDryRun);
static bool ConvertSliceToPrefab(
bool ConnectToAssetProcessor();
void DisconnectFromAssetProcessor();
bool ConvertSliceFile(AZ::SerializeContext* serializeContext, const AZStd::string& slicePath, bool isDryRun);
bool ConvertSliceToPrefab(
AZ::SerializeContext* serializeContext, AZ::IO::PathView outputPath, bool isDryRun, AZ::Entity* rootEntity);
static void FixPrefabEntities(AZ::Entity& containerEntity, SliceComponent::EntityList& sliceEntities);
static bool ConvertNestedSlices(
void FixPrefabEntities(AZ::Entity& containerEntity, SliceComponent::EntityList& sliceEntities);
bool ConvertNestedSlices(
SliceComponent* sliceComponent, AzToolsFramework::Prefab::Instance* sourceInstance,
AZ::SerializeContext* serializeContext, bool isDryRun);
static bool ConvertSliceInstance(
bool ConvertSliceInstance(
AZ::SliceComponent::SliceInstance& instance, AZ::Data::Asset<AZ::SliceAsset>& sliceAsset,
AzToolsFramework::Prefab::TemplateReference nestedTemplate, AzToolsFramework::Prefab::Instance* topLevelInstance);
static void PrintPrefab(AzToolsFramework::Prefab::TemplateId templateId);
static bool SavePrefab(AZ::IO::PathView outputPath, AzToolsFramework::Prefab::TemplateId templateId);
void SetParentEntity(const AZ::Entity& entity, const AZ::EntityId& parentId, bool onlySetIfInvalid);
void PrintPrefab(AzToolsFramework::Prefab::TemplateId templateId);
bool SavePrefab(AZ::IO::PathView outputPath, AzToolsFramework::Prefab::TemplateId templateId);
// Track all of the entity IDs created and the prefab entity aliases that map to them. This mapping is used
// with nested slice conversion to remap parent entity IDs to the correct prefab entity IDs.
AZStd::unordered_map<TemplateEntityIdPair, AzToolsFramework::Prefab::EntityAlias> m_aliasIdMapper;
// Track all of the created prefab template IDs on a slice conversion so that they can get removed at the end of the
// conversion for that file.
AZStd::unordered_set<AzToolsFramework::Prefab::TemplateId> m_createdTemplateIds;
};
} // namespace SerializeContextTools
} // namespace AZ

@ -0,0 +1,65 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzToolsFramework/Entity/EditorEntityContextComponent.h>
namespace AzToolsFramework
{
// This class is an inelegant workaround for use by the Slice Converter to selectively disable entity add/remove logic
// during slice conversion in the EditorEntityContextComponent. Specifically, the standard versions of these methods will
// attempt to activate the entities as they're added. This is both unnecessary and undesirable during slice conversion, since
// entity activation requires a lot of subsystems to be active and valid.
// Instead, by selectively disabling this logic, the entities can remain in an initialized state, which is sufficient for conversion,
// without requiring those extra subsystems.
// This problem also could have been solved by adding APIs to the EditorEntityContextComponent or the EntityContext, but there aren't
// any other known valid use cases for disabling this logic, so the extra APIs would simply encourage "bad behavior" by using them
// when they likely aren't necessary or desired.
class SliceConverterEditorEntityContextComponent
: public EditorEntityContextComponent
{
public:
AZ_COMPONENT(SliceConverterEditorEntityContextComponent, "{1CB0C38F-8E85-4422-91C6-E1F3B9B4B853}");
SliceConverterEditorEntityContextComponent() : EditorEntityContextComponent() {}
// Simple API to selectively disable this logic *only* when performing slice to prefab conversion.
static void DisableOnContextEntityLogic()
{
m_enableOnContextEntityLogic = false;
}
protected:
void OnContextEntitiesAdded([[maybe_unused]] const EntityList& entities) override
{
if (m_enableOnContextEntityLogic)
{
EditorEntityContextComponent::OnContextEntitiesAdded(entities);
}
}
void OnContextEntityRemoved([[maybe_unused]] const AZ::EntityId& id) override
{
if (m_enableOnContextEntityLogic)
{
EditorEntityContextComponent::OnContextEntityRemoved(id);
}
}
// By default, act just like the EditorEntityContextComponent
static inline bool m_enableOnContextEntityLogic = true;
};
} // namespace AzToolsFramework

@ -125,7 +125,8 @@ int main(int argc, char** argv)
}
else if (AZ::StringFunc::Equal("convert-slice", action.c_str()))
{
result = SliceConverter::ConvertSliceFiles(application);
SliceConverter sliceConverter;
result = sliceConverter.ConvertSliceFiles(application);
}
else
{

@ -17,6 +17,7 @@ set(FILES
Dumper.h
Dumper.cpp
main.cpp
SliceConverterEditorEntityContextComponent.h
SliceConverter.h
SliceConverter.cpp
Utilities.h

@ -178,7 +178,10 @@ namespace Camera
if ((!m_viewSystem)||(!m_system))
{
// perform first-time init
m_system = gEnv->pSystem;
if (gEnv)
{
m_system = gEnv->pSystem;
}
if (m_system)
{
// Initialize local view.

@ -10,6 +10,9 @@
"PythonAssetBuilder.Editor": {
"AutoLoad": false
},
"AWSCore": {
"AutoLoad": false
},
"AWSCore.Editor": {
"AutoLoad": false
},
@ -21,6 +24,9 @@
},
"AWSMetrics": {
"AutoLoad": false
},
"AWSMetrics.Editor": {
"AutoLoad": false
}
}
}

Loading…
Cancel
Save