Integrating latest 47acbe8
This commit is contained in:
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzToolsFramework/Prefab/Spawnable/ComponentRequirementsValidator.h>
|
||||
|
||||
#include <AzCore/Outcome/Outcome.h>
|
||||
#include <AzCore/std/algorithm.h>
|
||||
#include <AzToolsFramework/ToolsComponents/GenericComponentWrapper.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
void ComponentRequirementsValidator::SetPlatformTags(AZ::PlatformTagSet platformTags)
|
||||
{
|
||||
m_platformTags = AZStd::move(platformTags);
|
||||
}
|
||||
|
||||
|
||||
void ComponentRequirementsValidator::SetEntities(const AZStd::vector<AZ::Entity*>& entities)
|
||||
{
|
||||
m_immutableEntities.clear();
|
||||
m_immutableEntities.assign(entities.cbegin(), entities.cend());
|
||||
}
|
||||
|
||||
ComponentRequirementsValidator::ValidationResult ComponentRequirementsValidator::Validate(
|
||||
const AZ::Component* component)
|
||||
{
|
||||
AZ::ComponentValidationResult result = component->ValidateComponentRequirements(m_immutableEntities, m_platformTags);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
// Try to cast to GenericComponentWrapper, and if we can, get the internal template.
|
||||
const char* componentName = component->RTTI_GetTypeName();
|
||||
const auto* asEditorComponent = azrtti_cast<const Components::EditorComponentBase*>(component);
|
||||
const Components::GenericComponentWrapper* wrapper = azrtti_cast<const Components::GenericComponentWrapper*>(asEditorComponent);
|
||||
if (wrapper && wrapper->GetTemplate())
|
||||
{
|
||||
componentName = wrapper->GetTemplate()->RTTI_GetTypeName();
|
||||
}
|
||||
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Editor Component '%s' could not pass validation due to the error - %s",
|
||||
componentName,
|
||||
result.GetError().c_str())
|
||||
);
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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 <AzCore/Component/Component.h>
|
||||
#include <AzCore/Component/ComponentExport.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/optional.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
class ComponentRequirementsValidator
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(ComponentRequirementsValidator, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(AzToolsFramework::Prefab::PrefabConversionUtils::ComponentRequirementsValidator, "{1E9CD55D-FFEA-4E71-A316-731E25E6C981}");
|
||||
|
||||
virtual ~ComponentRequirementsValidator() = default;
|
||||
|
||||
void SetPlatformTags(AZ::PlatformTagSet platformTags);
|
||||
void SetEntities(const AZStd::vector<AZ::Entity*>& entities);
|
||||
|
||||
using ValidationResult = AZ::Outcome<void, AZStd::string>;
|
||||
ValidationResult Validate(const AZ::Component* component);
|
||||
|
||||
private:
|
||||
AZ::ImmutableEntityVector m_immutableEntities;
|
||||
AZ::PlatformTagSet m_platformTags;
|
||||
|
||||
};
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
+632
@@ -0,0 +1,632 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Component/ComponentExport.h>
|
||||
#include <AzCore/RTTI/ReflectContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/std/string/string_view.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorOnlyEntityComponentBus.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
EditorInfoRemover::~EditorInfoRemover()
|
||||
{
|
||||
for (auto* handler : m_editorOnlyEntityHandlerCandidates)
|
||||
{
|
||||
delete handler;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorInfoRemover::Process(PrefabProcessorContext& prefabProcessorContext)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
|
||||
if (!serializeContext)
|
||||
{
|
||||
AZ_Assert(serializeContext, "Failed to retrieve serialize context.");
|
||||
return;
|
||||
}
|
||||
|
||||
prefabProcessorContext.ListPrefabs([this, &serializeContext, &prefabProcessorContext](AZStd::string_view prefabName, PrefabDom& prefab)
|
||||
{
|
||||
auto result = RemoveEditorInfo(prefab, serializeContext, prefabProcessorContext);
|
||||
if (!result)
|
||||
{
|
||||
AZ_Assert(false,
|
||||
"Converting to runtime Prefab '%.*s' failed, Error: %s .",
|
||||
AZ_STRING_ARG(prefabName),
|
||||
result.GetError().c_str());
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void EditorInfoRemover::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext != nullptr)
|
||||
{
|
||||
serializeContext->Class<EditorInfoRemover, PrefabProcessor>()->Version(1);
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::vector<AZ::Entity*> EditorInfoRemover::GetEntitiesFromInstance(AZStd::unique_ptr<Instance>& instance)
|
||||
{
|
||||
AZStd::vector<AZ::Entity*> result;
|
||||
|
||||
instance->GetNestedEntities(
|
||||
[&result](const AZStd::unique_ptr<AZ::Entity>& entity)
|
||||
{
|
||||
result.emplace_back(entity.get());
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void EditorInfoRemover::SetEditorOnlyEntityHandlerFromCandidates(const EntityList& entities)
|
||||
{
|
||||
ClearEditorOnlyEntityIds();
|
||||
m_editorOnlyEntityHandler = nullptr;
|
||||
for (auto& handlerCandidate : m_editorOnlyEntityHandlerCandidates)
|
||||
{
|
||||
// See if this handler can handle at least one of the entities.
|
||||
for (auto entity : entities)
|
||||
{
|
||||
if (handlerCandidate->IsEntityUniquelyForThisHandler(entity))
|
||||
{
|
||||
m_editorOnlyEntityHandler = handlerCandidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (HasValidEditorOnlyHandler())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool EditorInfoRemover::HasValidEditorOnlyHandler() const
|
||||
{
|
||||
return m_editorOnlyEntityHandler != nullptr;
|
||||
}
|
||||
|
||||
void EditorInfoRemover::ClearEditorOnlyEntityIds()
|
||||
{
|
||||
m_editorOnlyEntityIds.clear();
|
||||
}
|
||||
|
||||
void EditorInfoRemover::AddEntityIdIfEditorOnly(AZ::Entity* entity)
|
||||
{
|
||||
bool isEditorOnly = false;
|
||||
EditorOnlyEntityComponentRequestBus::EventResult(isEditorOnly, entity->GetId(), &EditorOnlyEntityComponentRequests::IsEditorOnlyEntity);
|
||||
if (isEditorOnly && HasValidEditorOnlyHandler())
|
||||
{
|
||||
m_editorOnlyEntityHandler->AddEditorOnlyEntity(entity, m_editorOnlyEntityIds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify and remove any entities marked as editor-only.
|
||||
* If any are discovered, adjust descendants' transforms to retain spatial relationships.
|
||||
* Note we cannot use EBuses for this purpose, since we're crunching data, and can't assume any entities are active.
|
||||
*/
|
||||
EditorInfoRemover::RemoveEditorOnlyEntitiesResult EditorInfoRemover::RemoveEditorOnlyEntities(EntityList& entities)
|
||||
{
|
||||
if (HasValidEditorOnlyHandler())
|
||||
{
|
||||
const auto handlerResult =
|
||||
m_editorOnlyEntityHandler->HandleEditorOnlyEntities(entities, m_editorOnlyEntityIds, *m_serializeContext);
|
||||
if (!handlerResult)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Error occurred when handle editor-only entities. Error: %s",
|
||||
handlerResult.GetError().c_str())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove editor-only entities from the given entity list.
|
||||
AZStd::erase_if(
|
||||
entities,
|
||||
[this](auto entity)
|
||||
{
|
||||
return m_editorOnlyEntityIds.find(entity->GetId()) != m_editorOnlyEntityIds.end();
|
||||
}
|
||||
);
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
EditorInfoRemover::ExportEntityResult EditorInfoRemover::ExportEntity(AZ::Entity* sourceEntity, PrefabProcessorContext& context)
|
||||
{
|
||||
// For export, components can assume they're initialized, but not activated.
|
||||
if (sourceEntity->GetState() == AZ::Entity::State::Constructed)
|
||||
{
|
||||
sourceEntity->Init();
|
||||
}
|
||||
|
||||
AZ::Entity* exportEntity = aznew AZ::Entity(sourceEntity->GetId(), sourceEntity->GetName().c_str());
|
||||
exportEntity->SetRuntimeActiveByDefault(sourceEntity->IsRuntimeActiveByDefault());
|
||||
|
||||
AddEntityIdIfEditorOnly(sourceEntity);
|
||||
|
||||
const AZ::Entity::ComponentArrayType& editorComponents = sourceEntity->GetComponents();
|
||||
EntityList exportedEntities;
|
||||
for (AZ::Component* component : editorComponents)
|
||||
{
|
||||
auto result = ExportComponent(component, context, sourceEntity, exportEntity);
|
||||
if (!result)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Entity '%s' %s - export component '%s' failed. Error: %s",
|
||||
exportEntity->GetName().c_str(),
|
||||
exportEntity->GetId().ToString().c_str(),
|
||||
component->RTTI_GetTypeName(),
|
||||
result.GetError().c_str())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-sort prior to exporting so it isn't required at instantiation time.
|
||||
const auto sortResult = exportEntity->EvaluateDependenciesGetDetails();
|
||||
/* :CBR_TODO: verify AZ::Entity::DependencySortResult::HasIncompatibleServices and
|
||||
AZ::Entity::DependencySortResult::DescriptorNotRegistered are still covered here*/
|
||||
if (!sortResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Entity '%s' %s - dependency evaluation failed. Error: %s",
|
||||
exportEntity->GetName().c_str(),
|
||||
exportEntity->GetId().ToString().c_str(),
|
||||
sortResult.GetError().m_message.c_str()));
|
||||
}
|
||||
|
||||
return AZ::Success(exportEntity);
|
||||
}
|
||||
|
||||
bool EditorInfoRemover::ReadComponentAttribute(
|
||||
AZ::Component* component,
|
||||
AZ::Edit::Attribute* attribute,
|
||||
AZStd::vector<AZ::Crc32>& attributeTags)
|
||||
{
|
||||
attributeTags.clear();
|
||||
PropertyAttributeReader reader(component, attribute);
|
||||
return reader.Read<AZStd::vector<AZ::Crc32>>(attributeTags);
|
||||
}
|
||||
|
||||
EditorInfoRemover::ShouldExportResult EditorInfoRemover::ShouldExportComponent(
|
||||
AZ::Component* component,
|
||||
PrefabProcessorContext& context) const
|
||||
{
|
||||
const AZ::SerializeContext::ClassData* classData = m_serializeContext->FindClassData(component->RTTI_GetType());
|
||||
if (!classData || !classData->m_editData)
|
||||
{
|
||||
return AZ::Success(true);
|
||||
}
|
||||
|
||||
const AZ::Edit::ElementData* editorDataElement = classData->m_editData->FindElementData(AZ::Edit::ClassElements::EditorData);
|
||||
if (!editorDataElement)
|
||||
{
|
||||
return AZ::Success(true);
|
||||
}
|
||||
|
||||
AZStd::vector<AZ::Crc32> attributeTags;
|
||||
const auto& platformTags = context.GetPlatformTags();
|
||||
|
||||
// If the component has declared the 'ExportIfAllPlatforms' attribute, skip export if any of the flags are not present.
|
||||
AZ::Edit::Attribute* allTagsAttribute = editorDataElement->FindAttribute(AZ::Edit::Attributes::ExportIfAllPlatformTags);
|
||||
if (allTagsAttribute)
|
||||
{
|
||||
if (!ReadComponentAttribute(component, allTagsAttribute, attributeTags))
|
||||
{
|
||||
return AZ::Failure(
|
||||
AZStd::string("'ExportIfAllPlatforms' attribute is not bound to the correct return type. Expects AZStd::vector<AZ::Crc32>.")
|
||||
);
|
||||
}
|
||||
|
||||
for (AZ::Crc32 tag : attributeTags)
|
||||
{
|
||||
if (platformTags.find(tag) == platformTags.end())
|
||||
{
|
||||
// Export platform tags does not contain all tags specified in 'ExportIfAllPlatforms' attribute.
|
||||
return AZ::Success(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the component has declared the 'ExportIfAnyPlatforms' attribute, skip export if none of the flags are present.
|
||||
AZ::Edit::Attribute* anyTagsAttribute = editorDataElement->FindAttribute(AZ::Edit::Attributes::ExportIfAnyPlatformTags);
|
||||
if (anyTagsAttribute)
|
||||
{
|
||||
if (!ReadComponentAttribute(component, anyTagsAttribute, attributeTags))
|
||||
{
|
||||
return AZ::Failure(
|
||||
AZStd::string("'ExportIfAnyPlatforms' attribute is not bound to the correct return type. Expects AZStd::vector<AZ::Crc32>.")
|
||||
);
|
||||
}
|
||||
|
||||
bool anyFlagSet = false;
|
||||
for (AZ::Crc32 tag : attributeTags)
|
||||
{
|
||||
if (platformTags.find(tag) != platformTags.end())
|
||||
{
|
||||
anyFlagSet = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!anyFlagSet)
|
||||
{
|
||||
// None of the flags in 'ExportIfAnyPlatforms' was present in the export platform tags.
|
||||
return AZ::Success(false);
|
||||
}
|
||||
}
|
||||
|
||||
return AZ::Success(true);
|
||||
}
|
||||
|
||||
EditorInfoRemover::ResolveExportedComponentResult EditorInfoRemover::ResolveExportedComponent(
|
||||
AZ::ExportedComponent& component,
|
||||
PrefabProcessorContext& prefabProcessorContext)
|
||||
{
|
||||
AZ::Component* inputComponent = component.m_component;
|
||||
if (!inputComponent)
|
||||
{
|
||||
return AZ::Success(component);
|
||||
}
|
||||
|
||||
// Don't export the component if it has unmet platform tag requirements.
|
||||
ShouldExportResult shouldExportResult = ShouldExportComponent(inputComponent, prefabProcessorContext);
|
||||
if (!shouldExportResult)
|
||||
{
|
||||
return AZ::Failure(shouldExportResult.TakeError());
|
||||
}
|
||||
|
||||
if (!shouldExportResult.GetValue())
|
||||
{
|
||||
// If the platform tag requirements aren't met, return a null component that's been flagged as exported,
|
||||
// so that we know not to try and process it any further.
|
||||
return AZ::Success(AZ::ExportedComponent());
|
||||
}
|
||||
|
||||
// Determine if the component has a custom export callback, and invoke it if so.
|
||||
// If there's no custom export callback, just return what we were given.
|
||||
const AZ::SerializeContext::ClassData* classData = m_serializeContext->FindClassData(inputComponent->RTTI_GetType());
|
||||
if (!classData || !classData->m_editData)
|
||||
{
|
||||
return AZ::Success(component);
|
||||
}
|
||||
const AZ::Edit::ElementData* editorDataElement = classData->m_editData->FindElementData(AZ::Edit::ClassElements::EditorData);
|
||||
if (!editorDataElement)
|
||||
{
|
||||
return AZ::Success(component);
|
||||
}
|
||||
AZ::Edit::Attribute* exportCallbackAttribute = editorDataElement->FindAttribute(AZ::Edit::Attributes::RuntimeExportCallback);
|
||||
if (!exportCallbackAttribute)
|
||||
{
|
||||
return AZ::Success(component);
|
||||
}
|
||||
|
||||
PropertyAttributeReader reader(inputComponent, exportCallbackAttribute);
|
||||
AZ::ExportedComponent exportedComponent;
|
||||
if (reader.Read<AZ::ExportedComponent>(exportedComponent, inputComponent, prefabProcessorContext.GetPlatformTags()))
|
||||
{
|
||||
// If the callback handled the export and provided a different component instance, continue to resolve recursively.
|
||||
if (exportedComponent.m_componentExportHandled && (exportedComponent.m_component != inputComponent))
|
||||
{
|
||||
return ResolveExportedComponent(exportedComponent, prefabProcessorContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
// It provided the *same* component back (or didn't handle the export at all), so we're done.
|
||||
return AZ::Success(exportedComponent);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Bound 'CustomExportCallback' does not have the required return type/signature."));
|
||||
}
|
||||
}
|
||||
|
||||
EditorInfoRemover::BuildGameEntityResult EditorInfoRemover::BuildGameEntity(
|
||||
AzToolsFramework::Components::EditorComponentBase* editorComponent,
|
||||
AZ::Entity* sourceEntity,
|
||||
AZ::Entity* exportEntity)
|
||||
{
|
||||
const size_t oldComponentCount = exportEntity->GetComponents().size();
|
||||
editorComponent->BuildGameEntity(exportEntity);
|
||||
AZ::ComponentId newID = editorComponent->GetId();
|
||||
for (auto i = oldComponentCount; i < exportEntity->GetComponents().size(); ++i)
|
||||
{
|
||||
AZ::Component* exportComponent = exportEntity->GetComponents()[i];
|
||||
|
||||
// Verify that the result of BuildGameEntity() wasn't an editor component.
|
||||
auto* exportAsEditorComponent = azrtti_cast<Components::EditorComponentBase*>(exportComponent);
|
||||
if (exportAsEditorComponent)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Entity '%s' %s - component '%s' exported an editor component from BuildGameEntity() for runtime use.",
|
||||
sourceEntity->GetName().c_str(),
|
||||
sourceEntity->GetId().ToString().c_str(),
|
||||
editorComponent->RTTI_GetType().ToString<AZStd::string>().c_str()));
|
||||
}
|
||||
else if (editorComponent->GetId() == AZ::InvalidComponentId)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Entity '%s' %s - component '%s' doesn't have a valid component Id.",
|
||||
sourceEntity->GetName().c_str(),
|
||||
sourceEntity->GetId().ToString().c_str(),
|
||||
editorComponent->RTTI_GetType().ToString<AZStd::string>().c_str()));
|
||||
}
|
||||
|
||||
exportComponent->SetId(newID++);
|
||||
// The first time round set the new component the same as the editor one. This will change in a separate ticket
|
||||
// when 8 bit runtime Ids are implemented.
|
||||
// Make sure the newID isn't already on the source Entity. If it is increment the ID and try again.
|
||||
while (sourceEntity->FindComponent(newID))
|
||||
{
|
||||
++newID;
|
||||
}
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
EditorInfoRemover::ExportComponentResult EditorInfoRemover::ExportComponent(
|
||||
AZ::Component* component,
|
||||
PrefabProcessorContext& prefabProcessorContext,
|
||||
AZ::Entity* sourceEntity,
|
||||
AZ::Entity* exportEntity)
|
||||
{
|
||||
auto validationResult = m_componentRequirementsValidator.Validate(component);
|
||||
if (!validationResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Entity '%s' %s - validation of component '%s' failed: %s",
|
||||
sourceEntity->GetName().c_str(),
|
||||
sourceEntity->GetId().ToString().c_str(),
|
||||
component->RTTI_GetTypeName(),
|
||||
validationResult.GetError().c_str())
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
AZ::ExportedComponent exportComponent(component, false, false);
|
||||
auto exportResult = ResolveExportedComponent(
|
||||
exportComponent, prefabProcessorContext);
|
||||
if (!exportResult)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Entity '%s' %s - component '%s' could not be exported due to export attributes: %s.",
|
||||
sourceEntity->GetName().c_str(),
|
||||
sourceEntity->GetId().ToString().c_str(),
|
||||
component->RTTI_GetTypeName(),
|
||||
exportResult.GetError().c_str()));
|
||||
}
|
||||
|
||||
AZ::ExportedComponent& exportedComponent = exportResult.GetValue();
|
||||
|
||||
// If ResolveExportedComponent didn't handle the component export, then we'll do the following:
|
||||
// - For editor components, fall back on the legacy BuildGameEntity() path for handling component exports.
|
||||
// - For runtime components, provide a default behavior of "clone / add" to export the component.
|
||||
if (!exportedComponent.m_componentExportHandled)
|
||||
{
|
||||
auto* asEditorComponent = azrtti_cast<Components::EditorComponentBase*>(component);
|
||||
// Editor components: Try to use BuildGameEntity()
|
||||
if (asEditorComponent) // BEGIN BuildGameEntity compatibility path for editor components not using the newer RuntimeExportCallback functionality.
|
||||
{
|
||||
auto buildGameEntityResult = BuildGameEntity(asEditorComponent, sourceEntity, exportEntity);
|
||||
if (!buildGameEntityResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Entity '%s' %s - component '%s' to build game entity failed. Error: %s.",
|
||||
sourceEntity->GetName().c_str(),
|
||||
sourceEntity->GetId().ToString().c_str(),
|
||||
component->RTTI_GetTypeName(),
|
||||
buildGameEntityResult.GetError().c_str()));
|
||||
}
|
||||
|
||||
// Since this is an editor component, we very specifically do *not* want to clone and add it as a runtime
|
||||
// component by default, so regardless of whether or not the BuildGameEntity() call did anything,
|
||||
// null out the editor component and mark it handled.
|
||||
return AZ::Success();
|
||||
|
||||
} // END BuildGameEntity compatibility path for editor components not using the newer RuntimeExportCallback functionality.
|
||||
else
|
||||
{
|
||||
// Nothing else has handled the component export, so fall back on the default behavior
|
||||
// for runtime components: clone and add the runtime component that already exists.
|
||||
exportedComponent = AZ::ExportedComponent(component, false);
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, either ResolveExportedComponent or the default logic above should have set the component export
|
||||
// as being handled. If not, there is likely a new code path that requires a default export behavior.
|
||||
AZ_Assert(exportedComponent.m_componentExportHandled,
|
||||
"Entity '%s' %s - component '%s' had no export handlers and could not be added to the entity.",
|
||||
exportEntity->GetName().c_str(),
|
||||
exportEntity->GetId().ToString().c_str(),
|
||||
component->RTTI_GetTypeName());
|
||||
|
||||
// If we have an exported component, we add it to the exported entity.
|
||||
// If we don't (m_component == nullptr), this component chose not to be exported, so we skip it.
|
||||
if (exportedComponent.m_componentExportHandled && exportedComponent.m_component)
|
||||
{
|
||||
AZ::Component* runtimeComponent = exportedComponent.m_component;
|
||||
|
||||
// Verify that we aren't trying to export an editor component.
|
||||
auto* exportAsEditorComponent = azrtti_cast<Components::EditorComponentBase*>(runtimeComponent);
|
||||
if (exportAsEditorComponent)
|
||||
{
|
||||
auto* asEditorComponent =
|
||||
azrtti_cast<Components::EditorComponentBase*>(component);
|
||||
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Entity '%s' %s - component '%s' is trying to export an Editor component for runtime use.",
|
||||
sourceEntity->GetName().c_str(),
|
||||
sourceEntity->GetId().ToString().c_str(),
|
||||
asEditorComponent->RTTI_GetType().ToString<AZStd::string>().c_str()));
|
||||
}
|
||||
|
||||
// If the final component is not owned by us, make our own copy.
|
||||
if (!exportedComponent.m_deleteAfterExport)
|
||||
{
|
||||
runtimeComponent = m_serializeContext->CloneObject(runtimeComponent);
|
||||
}
|
||||
|
||||
// Synchronize to source component Id, and add to the export entity.
|
||||
runtimeComponent->SetId(component->GetId());
|
||||
|
||||
if (!exportEntity->AddComponent(runtimeComponent))
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Entity '%s' %s - component '%s' could not be added to this entity.",
|
||||
exportEntity->GetName().c_str(),
|
||||
exportEntity->GetId().ToString().c_str(),
|
||||
runtimeComponent->RTTI_GetTypeName())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
EditorInfoRemover::RemoveEditorInfoResult EditorInfoRemover::RemoveEditorInfo(
|
||||
PrefabDom& prefab,
|
||||
AZ::SerializeContext* serializeContext,
|
||||
PrefabProcessorContext& prefabProcessorContext)
|
||||
{
|
||||
if (!serializeContext)
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Invalid Serialize Context used."));
|
||||
}
|
||||
m_serializeContext = serializeContext;
|
||||
|
||||
m_componentRequirementsValidator.SetPlatformTags(prefabProcessorContext.GetPlatformTags());
|
||||
|
||||
// convert Prefab DOM into Prefab Instance.
|
||||
AZStd::unique_ptr<Instance> instance(aznew Instance());
|
||||
if (!Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom(*instance, prefab, false))
|
||||
{
|
||||
PrefabDomValueReference sourceReference = PrefabDomUtils::FindPrefabDomValue(prefab, PrefabDomUtils::SourceName);
|
||||
|
||||
AZStd::string errorMessage("Failed to Load Prefab Instance from given Prefab Dom during Removal of Editor Info.");
|
||||
if (sourceReference.has_value() &&
|
||||
sourceReference->get().IsString() &&
|
||||
sourceReference->get().GetStringLength() != 0)
|
||||
{
|
||||
AZStd::string_view source(sourceReference->get().GetString(), sourceReference->get().GetStringLength());
|
||||
errorMessage += AZStd::string::format("Prefab Source: %.*s", AZ_STRING_ARG(source));
|
||||
}
|
||||
|
||||
return AZ::Failure(errorMessage);
|
||||
}
|
||||
|
||||
// grab all nested entities from the Instance as source entities.
|
||||
EntityList sourceEntities = GetEntitiesFromInstance(instance);
|
||||
EntityList exportEntities;
|
||||
|
||||
// prepare for validation of component requirements.
|
||||
m_componentRequirementsValidator.SetEntities(sourceEntities);
|
||||
|
||||
// find valid editor-only entity handler for removing editor-only entities later.
|
||||
SetEditorOnlyEntityHandlerFromCandidates(sourceEntities);
|
||||
|
||||
// export entities.
|
||||
for (AZ::Entity* entity : sourceEntities)
|
||||
{
|
||||
const auto result = ExportEntity(entity, prefabProcessorContext);
|
||||
if (!result)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Entity '%s' %s - export entity failed. Error: %s",
|
||||
entity->GetName().c_str(),
|
||||
entity->GetId().ToString().c_str(),
|
||||
result.GetError().c_str())
|
||||
);
|
||||
}
|
||||
|
||||
exportEntities.emplace_back(result.GetValue());
|
||||
}
|
||||
|
||||
// remove editor-only entities with valid editor-only entity handler.
|
||||
const auto removeEditorOnlyEntitiesResult = RemoveEditorOnlyEntities(exportEntities);
|
||||
if (!removeEditorOnlyEntitiesResult)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Remove Editor-Only Entities failed. Error: '%s'",
|
||||
removeEditorOnlyEntitiesResult.GetError().c_str())
|
||||
);
|
||||
}
|
||||
|
||||
// validate component requirements for exported entities.
|
||||
m_componentRequirementsValidator.SetEntities(exportEntities);
|
||||
for (AZ::Entity* exportEntity : exportEntities)
|
||||
{
|
||||
const AZ::Entity::ComponentArrayType& gameComponents = exportEntity->GetComponents();
|
||||
for (const AZ::Component* component : gameComponents)
|
||||
{
|
||||
const auto result = m_componentRequirementsValidator.Validate(component);
|
||||
if (!result)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Entity '%s' %s - validation of export component '%s' failed: %s",
|
||||
exportEntity->GetName().c_str(),
|
||||
exportEntity->GetId().ToString().c_str(),
|
||||
component->RTTI_GetTypeName(),
|
||||
result.GetError().c_str())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove editor-only entities from instance.
|
||||
AZStd::unordered_map<AZ::EntityId, AZ::Entity*> exportEntitiesMap;
|
||||
AZStd::for_each(exportEntities.begin(), exportEntities.end(),
|
||||
[&exportEntitiesMap](auto& entity)
|
||||
{
|
||||
exportEntitiesMap.emplace(entity->GetId(), entity);
|
||||
}
|
||||
);
|
||||
instance->RemoveNestedEntities(
|
||||
[&exportEntitiesMap](const AZStd::unique_ptr<AZ::Entity>& entity)
|
||||
{
|
||||
return exportEntitiesMap.find(entity->GetId()) == exportEntitiesMap.end();
|
||||
}
|
||||
);
|
||||
|
||||
// replace entities of instance with exported ones.
|
||||
instance->GetNestedEntities(
|
||||
[&exportEntitiesMap](AZStd::unique_ptr<AZ::Entity>& entity)
|
||||
{
|
||||
auto entityId = entity->GetId();
|
||||
entity.release();
|
||||
entity.reset(exportEntitiesMap[entityId]);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
// save the final result in the target Prefab DOM.
|
||||
if (!PrefabDomUtils::StoreInstanceInPrefabDom(*instance, prefab))
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Saving exported Prefab Instance within a Prefab Dom failed.")
|
||||
);
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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 <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/Outcome/Outcome.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/ComponentRequirementsValidator.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/EditorOnlyEntityHandler/EditorOnlyEntityHandler.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/EditorOnlyEntityHandler/UiEditorOnlyEntityHandler.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/EditorOnlyEntityHandler/WorldEditorOnlyEntityHandler.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabProcessor.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class ReflectContext;
|
||||
}
|
||||
|
||||
namespace AzToolsFramework::Components
|
||||
{
|
||||
class EditorComponentBase;
|
||||
}
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
class EditorInfoRemover
|
||||
: public PrefabProcessor
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(EditorInfoRemover, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(AzToolsFramework::Prefab::PrefabConversionUtils::EditorInfoRemover,
|
||||
"{50B48C7E-C9DE-48DE-8438-1A186A8EEAC8}", PrefabProcessor);
|
||||
|
||||
~EditorInfoRemover() override;
|
||||
|
||||
void Process(PrefabProcessorContext& prefabProcessorContext) override;
|
||||
|
||||
using RemoveEditorInfoResult = AZ::Outcome<void, AZStd::string>;
|
||||
RemoveEditorInfoResult RemoveEditorInfo(
|
||||
PrefabDom& prefab,
|
||||
AZ::SerializeContext* serializeContext,
|
||||
PrefabProcessorContext& prefabProcessorContext);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
protected:
|
||||
using EntityList = AZStd::vector<AZ::Entity*>;
|
||||
static EntityList GetEntitiesFromInstance(
|
||||
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance>& instance);
|
||||
|
||||
static bool ReadComponentAttribute(
|
||||
AZ::Component* component,
|
||||
AZ::Edit::Attribute* attribute,
|
||||
AZStd::vector<AZ::Crc32>& attributeTags);
|
||||
|
||||
void SetEditorOnlyEntityHandlerFromCandidates(const EntityList& entities);
|
||||
|
||||
bool HasValidEditorOnlyHandler() const;
|
||||
|
||||
void ClearEditorOnlyEntityIds();
|
||||
|
||||
void AddEntityIdIfEditorOnly(AZ::Entity* entity);
|
||||
|
||||
using RemoveEditorOnlyEntitiesResult = AZ::Outcome<void, AZStd::string>;
|
||||
RemoveEditorOnlyEntitiesResult RemoveEditorOnlyEntities(EntityList& entities);
|
||||
|
||||
using ExportEntityResult = AZ::Outcome<AZ::Entity*, AZStd::string>;
|
||||
ExportEntityResult ExportEntity(AZ::Entity* sourceEntity, PrefabProcessorContext& context);
|
||||
|
||||
using ResolveExportedComponentResult = AZ::Outcome<AZ::ExportedComponent, AZStd::string>;
|
||||
ResolveExportedComponentResult ResolveExportedComponent(
|
||||
AZ::ExportedComponent& component, PrefabProcessorContext& prefabProcessorContext);
|
||||
|
||||
using ShouldExportResult = AZ::Outcome<bool, AZStd::string>;
|
||||
ShouldExportResult ShouldExportComponent(
|
||||
AZ::Component* component,
|
||||
PrefabProcessorContext& prefabProcessorContext) const;
|
||||
|
||||
using BuildGameEntityResult = AZ::Outcome<void, AZStd::string>;
|
||||
BuildGameEntityResult BuildGameEntity(
|
||||
AzToolsFramework::Components::EditorComponentBase* editorComponent,
|
||||
AZ::Entity* sourceEntity,
|
||||
AZ::Entity* exportEntity
|
||||
);
|
||||
|
||||
using ExportComponentResult = AZ::Outcome<void, AZStd::string>;
|
||||
ExportComponentResult ExportComponent(
|
||||
AZ::Component* component,
|
||||
PrefabProcessorContext& prefabProcessorContext,
|
||||
AZ::Entity* sourceEntity,
|
||||
AZ::Entity* exportEntity);
|
||||
|
||||
AZ::SerializeContext* m_serializeContext{ nullptr };
|
||||
EditorOnlyEntityHandler* m_editorOnlyEntityHandler{ nullptr };
|
||||
EditorOnlyEntityHandlers m_editorOnlyEntityHandlerCandidates{
|
||||
aznew WorldEditorOnlyEntityHandler(),
|
||||
aznew UiEditorOnlyEntityHandler() };
|
||||
ComponentRequirementsValidator m_componentRequirementsValidator;
|
||||
EntityIdSet m_editorOnlyEntityIds;
|
||||
};
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzToolsFramework/Prefab/Spawnable/EditorOnlyEntityHandler/EditorOnlyEntityHandler.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
void EditorOnlyEntityHandler::AddEditorOnlyEntity(
|
||||
AZ::Entity* editorOnlyEntity,
|
||||
AZStd::unordered_set<AZ::EntityId>& editorOnlyEntities)
|
||||
{
|
||||
editorOnlyEntities.insert(editorOnlyEntity->GetId());
|
||||
}
|
||||
|
||||
EditorOnlyEntityHandler::Result EditorOnlyEntityHandler::HandleEditorOnlyEntities(
|
||||
const EntityList& /*entities*/,
|
||||
const EntityIdSet& /*editorOnlyEntityIds*/,
|
||||
AZ::SerializeContext& /*serializeContext*/)
|
||||
{
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
EditorOnlyEntityHandler::Result EditorOnlyEntityHandler::ValidateReferences(
|
||||
const EntityList& entities,
|
||||
const EntityIdSet& editorOnlyEntityIds,
|
||||
AZ::SerializeContext& serializeContext)
|
||||
{
|
||||
EditorOnlyEntityHandler::Result result = AZ::Success();
|
||||
|
||||
// Inspect all runtime entities via the serialize context and identify any references to editor-only entity Ids.
|
||||
for (AZ::Entity* runtimeEntity : entities)
|
||||
{
|
||||
if (editorOnlyEntityIds.end() != editorOnlyEntityIds.find(runtimeEntity->GetId()))
|
||||
{
|
||||
continue; // This is not a runtime entity, so no need to validate its references as it's going away.
|
||||
}
|
||||
|
||||
AZ::EntityUtils::EnumerateEntityIds<AZ::Entity>(
|
||||
runtimeEntity,
|
||||
[&editorOnlyEntityIds, &result, runtimeEntity](const AZ::EntityId& id, bool /*isEntityId*/, const AZ::SerializeContext::ClassElement* /*elementData*/)
|
||||
{
|
||||
if (editorOnlyEntityIds.end() != editorOnlyEntityIds.find(id))
|
||||
{
|
||||
result = AZ::Failure(
|
||||
AZStd::string::format(
|
||||
"A runtime entity (%s) contains references to an entity marked as editor-only.",
|
||||
runtimeEntity->GetName().c_str()
|
||||
)
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
&serializeContext
|
||||
);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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 <AzCore/Component/Entity.h>
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
/**
|
||||
* Callback handler interface for processing prefab prior to stripping of editor-only entities.
|
||||
*/
|
||||
class EditorOnlyEntityHandler
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(EditorOnlyEntityHandler, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(AzToolsFramework::Prefab::PrefabConversionUtils::EditorOnlyEntityHandler, "{C420F65D-18AE-4CAF-BB18-70FA4FE73243}");
|
||||
|
||||
virtual ~EditorOnlyEntityHandler() = default;
|
||||
|
||||
virtual bool IsEntityUniquelyForThisHandler(AZ::Entity* entity) const = 0;
|
||||
|
||||
/**
|
||||
* Adds the given entity ID to the set of editor only entities.
|
||||
*
|
||||
* Handlers can customize this behavior, such as additionally adding child entities
|
||||
* when a parent is marked as editor-only.
|
||||
*/
|
||||
virtual void AddEditorOnlyEntity(
|
||||
AZ::Entity* editorOnlyEntity,
|
||||
AZStd::unordered_set<AZ::EntityId>& editorOnlyEntities);
|
||||
|
||||
using Result = AZ::Outcome<void, AZStd::string>;
|
||||
|
||||
/**
|
||||
* This handler is responsible for making any necessary modifications to other entities in the Prefab prior to the removal
|
||||
* of all editor-only entities.
|
||||
* After this callback returns, editor-only entities will be removed from the Prefab.
|
||||
* See \ref WorldEditorOnlyEntityHandler below for an example of processing and validation that occurs for standard world entities.
|
||||
* @param entities a list of all entities in the Prefab, including those marked as editor-only.
|
||||
* @param editorOnlyEntityIds a precomputed set containing Ids for all entities within the 'entities' list that were marked as editor-only.
|
||||
* @param serializeContext useful to inspect entity data for validation purposes.
|
||||
*/
|
||||
virtual Result HandleEditorOnlyEntities(
|
||||
const EntityList& /*entities*/,
|
||||
const EntityIdSet& /*editorOnlyEntityIds*/,
|
||||
AZ::SerializeContext& /*serializeContext*/);
|
||||
|
||||
// Verify that none of the runtime entities reference editor-only entities. Fail w/ details if so.
|
||||
static Result ValidateReferences(
|
||||
const EntityList& entities,
|
||||
const EntityIdSet& editorOnlyEntityIds,
|
||||
AZ::SerializeContext& serializeContext);
|
||||
};
|
||||
|
||||
using EditorOnlyEntityHandlers = AZStd::vector<EditorOnlyEntityHandler*>;
|
||||
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzToolsFramework/Prefab/Spawnable/EditorOnlyEntityHandler/UiEditorOnlyEntityHandler.h>
|
||||
|
||||
#include <AzFramework/InGameUI/UiFrameworkBus.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
bool UiEditorOnlyEntityHandler::IsEntityUniquelyForThisHandler(AZ::Entity* entity) const
|
||||
{
|
||||
// Assume that an entity is a UI element if it has a UI element component.
|
||||
bool uniqueForThisHandler = false;
|
||||
UiFrameworkBus::BroadcastResult(uniqueForThisHandler, &UiFrameworkInterface::HasUiElementComponent, entity);
|
||||
|
||||
return uniqueForThisHandler;
|
||||
}
|
||||
|
||||
void UiEditorOnlyEntityHandler::AddEditorOnlyEntity(AZ::Entity* editorOnlyEntity, EntityIdSet& editorOnlyEntities)
|
||||
{
|
||||
UiFrameworkBus::Broadcast(&UiFrameworkInterface::AddEditorOnlyEntity, editorOnlyEntity, editorOnlyEntities);
|
||||
}
|
||||
|
||||
EditorOnlyEntityHandler::Result UiEditorOnlyEntityHandler::HandleEditorOnlyEntities(
|
||||
const AzToolsFramework::EntityList& exportEntities,
|
||||
const AzToolsFramework::EntityIdSet& editorOnlyEntityIds,
|
||||
AZ::SerializeContext& serializeContext)
|
||||
{
|
||||
UiFrameworkBus::Broadcast(&UiFrameworkInterface::HandleEditorOnlyEntities, exportEntities, editorOnlyEntityIds);
|
||||
|
||||
// Perform a final check to verify that all editor-only entities have been removed
|
||||
auto result = ValidateReferences(exportEntities, editorOnlyEntityIds, serializeContext);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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/Prefab/Spawnable/EditorOnlyEntityHandler/EditorOnlyEntityHandler.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
/**
|
||||
* EditorOnlyEntity handler for UI entities.
|
||||
* - Removes editor-only entities and their descedent hierarchy entirely.
|
||||
* -- This differs from the world-entity handler where editor-only entities
|
||||
* are removed "in-place".
|
||||
* - Validates that no editor entities are referenced by non-editor entities.
|
||||
*/
|
||||
class UiEditorOnlyEntityHandler
|
||||
: public EditorOnlyEntityHandler
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(UiEditorOnlyEntityHandler, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(AzToolsFramework::Prefab::PrefabConversionUtils::UiEditorOnlyEntityHandler, "{949CF813-4A8E-4D55-B323-0ED2A967CDCC}", EditorOnlyEntityHandler);
|
||||
|
||||
bool IsEntityUniquelyForThisHandler(AZ::Entity* entity) const override;
|
||||
|
||||
void AddEditorOnlyEntity(AZ::Entity* editorOnlyEntity, EntityIdSet& editorOnlyEntities) override;
|
||||
|
||||
Result HandleEditorOnlyEntities(
|
||||
const AzToolsFramework::EntityList& entities,
|
||||
const AzToolsFramework::EntityIdSet& editorOnlyEntityIds,
|
||||
AZ::SerializeContext& serializeContext) override;
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzToolsFramework/Prefab/Spawnable/EditorOnlyEntityHandler/WorldEditorOnlyEntityHandler.h>
|
||||
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
bool WorldEditorOnlyEntityHandler::IsEntityUniquelyForThisHandler(AZ::Entity* entity) const
|
||||
{
|
||||
return AZ::EntityUtils::FindFirstDerivedComponent<AZ::TransformInterface>(entity) != nullptr;
|
||||
}
|
||||
|
||||
EditorOnlyEntityHandler::Result WorldEditorOnlyEntityHandler::HandleEditorOnlyEntities(const AzToolsFramework::EntityList& entities, const AzToolsFramework::EntityIdSet& editorOnlyEntityIds, AZ::SerializeContext& serializeContext)
|
||||
{
|
||||
FixTransformRelationships(entities, editorOnlyEntityIds);
|
||||
|
||||
return ValidateReferences(entities, editorOnlyEntityIds, serializeContext);
|
||||
}
|
||||
|
||||
void WorldEditorOnlyEntityHandler::FixTransformRelationships(const AzToolsFramework::EntityList& entities, const AzToolsFramework::EntityIdSet& editorOnlyEntityIds)
|
||||
{
|
||||
AZStd::unordered_map<AZ::EntityId, AZStd::vector<AZ::Entity*>> parentToChildren;
|
||||
|
||||
// Build a map of entity Ids to their parent Ids, for faster lookup during processing.
|
||||
for (AZ::Entity* entity : entities)
|
||||
{
|
||||
AZ::TransformInterface* transformComponent = AZ::EntityUtils::FindFirstDerivedComponent<AZ::TransformInterface>(entity);
|
||||
if (transformComponent)
|
||||
{
|
||||
const AZ::EntityId parentId = transformComponent->GetParentId();
|
||||
if (parentId.IsValid())
|
||||
{
|
||||
parentToChildren[parentId].push_back(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Identify any editor-only entities. If we encounter one, adjust transform relationships
|
||||
// for all of its children to ensure relative transforms are maintained and respected at
|
||||
// runtime.
|
||||
// This works regardless of entity ordering in the Prefab because we add reassigned children to
|
||||
// parentToChildren cache during the operation.
|
||||
for (AZ::Entity* entity : entities)
|
||||
{
|
||||
if (editorOnlyEntityIds.end() == editorOnlyEntityIds.find(entity->GetId()))
|
||||
{
|
||||
continue; // This is not an editor-only entity.
|
||||
}
|
||||
|
||||
AZ::TransformInterface* transformComponent = AZ::EntityUtils::FindFirstDerivedComponent<AZ::TransformInterface>(entity);
|
||||
if (transformComponent)
|
||||
{
|
||||
const AZ::Transform& parentLocalTm = transformComponent->GetLocalTM();
|
||||
|
||||
// Identify all transform children and adjust them to be children of the removed entity's parent.
|
||||
for (AZ::Entity* childEntity : parentToChildren[entity->GetId()])
|
||||
{
|
||||
AZ::TransformInterface* childTransformComponent = AZ::EntityUtils::FindFirstDerivedComponent<AZ::TransformInterface>(childEntity);
|
||||
|
||||
if (childTransformComponent && childTransformComponent->GetParentId() == entity->GetId())
|
||||
{
|
||||
const AZ::Transform localTm = childTransformComponent->GetLocalTM();
|
||||
childTransformComponent->SetParent(transformComponent->GetParentId());
|
||||
childTransformComponent->SetLocalTM(parentLocalTm * localTm);
|
||||
|
||||
parentToChildren[transformComponent->GetParentId()].push_back(childEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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/Prefab/Spawnable/EditorOnlyEntityHandler/EditorOnlyEntityHandler.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
/**
|
||||
* EditorOnlyEntity handler for world entities.
|
||||
* - Fixes up transform relationships so entities removed mid-hierarchy still result in valid runtime transform relationships
|
||||
* and correct relative transforms.
|
||||
* - Validates that no editor entities are referenced by non-editor entities.
|
||||
*/
|
||||
class WorldEditorOnlyEntityHandler
|
||||
: public EditorOnlyEntityHandler
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(WorldEditorOnlyEntityHandler, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(AzToolsFramework::Prefab::PrefabConversionUtils::WorldEditorOnlyEntityHandler, "{55587AE2-B583-48E4-9634-6BFACF6CBF04}", EditorOnlyEntityHandler);
|
||||
|
||||
bool IsEntityUniquelyForThisHandler(AZ::Entity* entity) const override;
|
||||
|
||||
Result HandleEditorOnlyEntities(
|
||||
const AzToolsFramework::EntityList& entities,
|
||||
const AzToolsFramework::EntityIdSet& editorOnlyEntityIds,
|
||||
AZ::SerializeContext& serializeContext) override;
|
||||
|
||||
// Adjust transform relationships to maintain integrity of the transform hierarchy at runtime, even if editor-only
|
||||
// entities were positioned within the transform hierarchy.
|
||||
static void FixTransformRelationships(
|
||||
const AzToolsFramework::EntityList& entities,
|
||||
const AzToolsFramework::EntityIdSet& editorOnlyEntityIds);
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/IO/ByteContainerStream.h>
|
||||
#include <AzCore/RTTI/ReflectContext.h>
|
||||
#include <AzCore/RTTI/TypeInfo.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/Utils.h>
|
||||
#include <AzFramework/Spawnable/Spawnable.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabCatchmentProcessor.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
void PrefabCatchmentProcessor::Process(PrefabProcessorContext& context)
|
||||
{
|
||||
context.ListPrefabs([&context](AZStd::string_view prefabName, PrefabDom& prefab)
|
||||
{
|
||||
ProcessPrefab(context, prefabName, prefab);
|
||||
});
|
||||
}
|
||||
|
||||
void PrefabCatchmentProcessor::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext != nullptr)
|
||||
{
|
||||
serializeContext->Class<PrefabCatchmentProcessor, PrefabProcessor>()->Version(1);
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabCatchmentProcessor::ProcessPrefab(PrefabProcessorContext& context, AZStd::string_view prefabName, PrefabDom& prefab)
|
||||
{
|
||||
|
||||
AZStd::string uniqueName = prefabName;
|
||||
uniqueName += '.';
|
||||
uniqueName += AzFramework::Spawnable::FileExtension;
|
||||
|
||||
auto serializer = [](AZStd::vector<uint8_t>& output, const ProcessedObjectStore& object) -> bool
|
||||
{
|
||||
AZ::IO::ByteContainerStream stream(&output);
|
||||
return AZ::Utils::SaveObjectToStream(stream, AZ::DataStream::StreamType::ST_BINARY,
|
||||
AZStd::any_cast<void>(&object.GetObject()), object.GetObject().type());
|
||||
};
|
||||
|
||||
auto spawnable = SpawnableUtils::CreateSpawnable(prefab);
|
||||
SpawnableUtils::SortEntitiesByTransformHierarchy(spawnable);
|
||||
AZStd::any spawnableAny(AZStd::move(spawnable));
|
||||
|
||||
context.GetProcessedObjects().emplace_back(AZStd::move(uniqueName), AZStd::move(spawnableAny),
|
||||
AZStd::move(serializer), AZ::AzTypeInfo<AzFramework::Spawnable>::Uuid());
|
||||
|
||||
context.RemovePrefab(prefabName);
|
||||
}
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabProcessor.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class ReflectContext;
|
||||
}
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
class PrefabCatchmentProcessor
|
||||
: public PrefabProcessor
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(PrefabCatchmentProcessor, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(AzToolsFramework::Prefab::PrefabConversionUtils::PrefabCatchmentProcessor,
|
||||
"{F71E2FBA-22ED-44C7-B4C8-D2CF4B2C7B97}", PrefabProcessor);
|
||||
|
||||
~PrefabCatchmentProcessor() override = default;
|
||||
|
||||
void Process(PrefabProcessorContext& context) override;
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
protected:
|
||||
static void ProcessPrefab(PrefabProcessorContext& context, AZStd::string_view prefabName, PrefabDom& prefab);
|
||||
};
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabConversionPipeline.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
bool PrefabConversionPipeline::LoadStackProfile(AZStd::string_view stackProfile)
|
||||
{
|
||||
m_processors.clear();
|
||||
|
||||
AZStd::string registryKey = "/Amazon/Tools/Prefab/Processing/Stack/";
|
||||
registryKey += stackProfile;
|
||||
|
||||
auto registry = AZ::SettingsRegistry::Get();
|
||||
AZ_Assert(registry, "PrefabConversionPipeline is created before the Settings Registry is available.");
|
||||
return registry->GetObject(m_processors, registryKey);
|
||||
}
|
||||
|
||||
void PrefabConversionPipeline::ProcessPrefab(PrefabProcessorContext& context)
|
||||
{
|
||||
for (auto& processor : m_processors)
|
||||
{
|
||||
processor->Process(context);
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabConversionPipeline::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext != nullptr)
|
||||
{
|
||||
serializeContext->Class<PrefabProcessor>()->Version(1);
|
||||
serializeContext->RegisterGenericType<PrefabProcessorList>();
|
||||
serializeContext->RegisterGenericType<PrefabProcessorListEntry>();
|
||||
}
|
||||
}
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzCore/std/string/string_view.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabProcessor.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
class PrefabConversionPipeline final
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(PrefabConversionPipeline, AZ::SystemAllocator, 0);
|
||||
|
||||
using PrefabProcessorListEntry = AZStd::unique_ptr<PrefabProcessor>;
|
||||
using PrefabProcessorList = AZStd::vector<PrefabProcessorListEntry>;
|
||||
|
||||
bool LoadStackProfile(AZStd::string_view stackProfile);
|
||||
|
||||
void ProcessPrefab(PrefabProcessorContext& context);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
private:
|
||||
PrefabProcessorList m_processors;
|
||||
};
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
class PrefabProcessor
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(PrefabProcessor, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(PrefabProcessor, "{393C95DF-C0DA-4EF0-A081-9CA899649DDD}");
|
||||
|
||||
virtual ~PrefabProcessor() = default;
|
||||
|
||||
virtual void Process(PrefabProcessorContext& context) = 0;
|
||||
};
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
bool PrefabProcessorContext::AddPrefab(AZStd::string prefabName, PrefabDom prefab)
|
||||
{
|
||||
auto result = m_prefabs.emplace(AZStd::move(prefabName), AZStd::move(prefab));
|
||||
return result.second;
|
||||
}
|
||||
|
||||
bool PrefabProcessorContext::RemovePrefab(AZStd::string_view prefabName)
|
||||
{
|
||||
if (!m_isIterating)
|
||||
{
|
||||
return m_prefabs.erase(prefabName) > 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_delayedDelete.emplace_back(prefabName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PrefabProcessorContext::ListPrefabs(const AZStd::function<void(AZStd::string_view, PrefabDom&)>& callback)
|
||||
{
|
||||
m_isIterating = true;
|
||||
for (auto& it : m_prefabs)
|
||||
{
|
||||
if (AZStd::find(m_delayedDelete.begin(), m_delayedDelete.end(), it.first) == m_delayedDelete.end())
|
||||
{
|
||||
callback(it.first, it.second);
|
||||
}
|
||||
}
|
||||
m_isIterating = false;
|
||||
|
||||
// Clear out any prefabs that have been deleted.
|
||||
for (AZStd::string& deleted : m_delayedDelete)
|
||||
{
|
||||
m_prefabs.erase(deleted);
|
||||
}
|
||||
m_delayedDelete.clear();
|
||||
}
|
||||
|
||||
void PrefabProcessorContext::ListPrefabs(const AZStd::function<void(AZStd::string_view, const PrefabDom&)>& callback) const
|
||||
{
|
||||
for (const auto& it : m_prefabs)
|
||||
{
|
||||
callback(it.first, it.second);
|
||||
}
|
||||
}
|
||||
|
||||
bool PrefabProcessorContext::HasPrefabs() const
|
||||
{
|
||||
return !m_prefabs.empty();
|
||||
}
|
||||
|
||||
PrefabProcessorContext::ProcessedObjectStoreContainer& PrefabProcessorContext::GetProcessedObjects()
|
||||
{
|
||||
return m_products;
|
||||
}
|
||||
|
||||
const PrefabProcessorContext::ProcessedObjectStoreContainer& PrefabProcessorContext::GetProcessedObjects() const
|
||||
{
|
||||
return m_products;
|
||||
}
|
||||
|
||||
const AZ::PlatformTagSet& PrefabProcessorContext::GetPlatformTags() const
|
||||
{
|
||||
return m_platformTags;
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 <AzCore/Component/ComponentExport.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/std/containers/unordered_map.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 <AzToolsFramework/Prefab/PrefabDomTypes.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
class PrefabProcessorContext
|
||||
{
|
||||
public:
|
||||
using ProcessedObjectStoreContainer = AZStd::vector<ProcessedObjectStore>;
|
||||
|
||||
AZ_CLASS_ALLOCATOR(PrefabProcessorContext, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(PrefabProcessorContext, "{C7D77E3A-C544-486B-B774-7C82C38FE22F}");
|
||||
|
||||
virtual ~PrefabProcessorContext() = default;
|
||||
|
||||
virtual bool AddPrefab(AZStd::string prefabName, PrefabDom prefab);
|
||||
virtual bool RemovePrefab(AZStd::string_view prefabName);
|
||||
virtual void ListPrefabs(const AZStd::function<void(AZStd::string_view, PrefabDom&)>& callback);
|
||||
virtual void ListPrefabs(const AZStd::function<void(AZStd::string_view, const PrefabDom&)>& callback) const;
|
||||
virtual bool HasPrefabs() const;
|
||||
|
||||
virtual ProcessedObjectStoreContainer& GetProcessedObjects();
|
||||
virtual const ProcessedObjectStoreContainer& GetProcessedObjects() const;
|
||||
|
||||
virtual const AZ::PlatformTagSet& GetPlatformTags() const;
|
||||
|
||||
protected:
|
||||
using NamedPrefabContainer = AZStd::unordered_map<AZStd::string, PrefabDom>;
|
||||
|
||||
NamedPrefabContainer m_prefabs;
|
||||
ProcessedObjectStoreContainer m_products;
|
||||
AZStd::vector<AZStd::string> m_delayedDelete;
|
||||
AZ::PlatformTagSet m_platformTags;
|
||||
bool m_isIterating{ false };
|
||||
};
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
+65
@@ -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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Casting/lossy_cast.h>
|
||||
#include <AzCore/Math/Uuid.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
ProcessedObjectStore::ProcessedObjectStore(AZStd::string uniqueId, AZStd::any object, SerializerFunction objectSerializer,
|
||||
AZ::Data::AssetType assetType)
|
||||
: m_uniqueId(AZStd::move(uniqueId))
|
||||
, m_object(AZStd::move(object))
|
||||
, m_objectSerializer(AZStd::move(objectSerializer))
|
||||
, m_assetType(AZStd::move(assetType))
|
||||
{
|
||||
}
|
||||
|
||||
bool ProcessedObjectStore::Serialize(AZStd::vector<uint8_t>& output) const
|
||||
{
|
||||
if (m_objectSerializer)
|
||||
{
|
||||
return m_objectSerializer(output, *this);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const AZStd::any& ProcessedObjectStore::GetObject() const
|
||||
{
|
||||
return m_object;
|
||||
}
|
||||
|
||||
AZStd::any ProcessedObjectStore::ReleaseObject()
|
||||
{
|
||||
return AZStd::move(m_object);
|
||||
}
|
||||
|
||||
uint32_t ProcessedObjectStore::BuildSubId() const
|
||||
{
|
||||
AZ::Uuid subIdHash = AZ::Uuid::CreateData(m_uniqueId.data(), m_uniqueId.size());
|
||||
return azlossy_caster(subIdHash.GetHash());
|
||||
}
|
||||
|
||||
const AZ::Data::AssetType& ProcessedObjectStore::GetAssetType() const
|
||||
{
|
||||
return m_assetType;
|
||||
}
|
||||
|
||||
const AZStd::string& ProcessedObjectStore::GetId() const
|
||||
{
|
||||
return m_uniqueId;
|
||||
}
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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 <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/std/any.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/functional.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
//! Storage for objects created through the Prefab processing pipeline.
|
||||
//! These typically store the created object for immediate use in the editor plus additional information
|
||||
//! to allow the Prefab Builder to convert the object into a serialized form and register it with the
|
||||
//! Asset Database.
|
||||
class ProcessedObjectStore
|
||||
{
|
||||
public:
|
||||
using SerializerFunction = AZStd::function<bool(AZStd::vector<uint8_t>&, const ProcessedObjectStore&)>;
|
||||
|
||||
//! 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 is stable between runs.
|
||||
//! @param object The object that generated during processing of a Prefab.
|
||||
//! @param objectSerializer The callback used to convert the provided object into a binary stream.
|
||||
//! @param assetType The asset type of the asset.
|
||||
//! @param storagePath The relative path where the asset will be stored if/when committed to disk.
|
||||
ProcessedObjectStore(AZStd::string uniqueId, AZStd::any object, SerializerFunction objectSerializer, AZ::Data::AssetType assetType);
|
||||
|
||||
bool Serialize(AZStd::vector<uint8_t>& output) const;
|
||||
uint32_t BuildSubId() const;
|
||||
|
||||
const AZStd::any& GetObject() const;
|
||||
AZStd::any ReleaseObject();
|
||||
|
||||
const AZ::Data::AssetType& GetAssetType() const;
|
||||
const AZStd::string& GetId() const;
|
||||
|
||||
private:
|
||||
AZStd::any m_object;
|
||||
SerializerFunction m_objectSerializer;
|
||||
AZ::Data::AssetType m_assetType;
|
||||
AZStd::string m_uniqueId;
|
||||
};
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/std/algorithm.h>
|
||||
#include <AzCore/std/sort.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/SpawnableMetaDataBuilder.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
SpawnableMetaDataBuilder& SpawnableMetaDataBuilder::Add(AZStd::string_view key, bool value)
|
||||
{
|
||||
return AddGeneric(key, value);
|
||||
}
|
||||
|
||||
SpawnableMetaDataBuilder& SpawnableMetaDataBuilder::Add(AZStd::string_view key, uint64_t value)
|
||||
{
|
||||
return AddGeneric(key, value);
|
||||
}
|
||||
|
||||
SpawnableMetaDataBuilder& SpawnableMetaDataBuilder::Add(AZStd::string_view key, int64_t value)
|
||||
{
|
||||
return AddGeneric(key, value);
|
||||
}
|
||||
|
||||
SpawnableMetaDataBuilder& SpawnableMetaDataBuilder::Add(AZStd::string_view key, double value)
|
||||
{
|
||||
return AddGeneric(key, value);
|
||||
}
|
||||
|
||||
SpawnableMetaDataBuilder& SpawnableMetaDataBuilder::Add(AZStd::string_view key, AZStd::string value)
|
||||
{
|
||||
return AddGeneric(key, AZStd::move(value));
|
||||
}
|
||||
|
||||
SpawnableMetaDataBuilder& SpawnableMetaDataBuilder::AppendArray(AZStd::string_view arrayKey, bool value)
|
||||
{
|
||||
return AppendArrayGeneric(arrayKey, value);
|
||||
}
|
||||
|
||||
SpawnableMetaDataBuilder& SpawnableMetaDataBuilder::AppendArray(AZStd::string_view arrayKey, uint64_t value)
|
||||
{
|
||||
return AppendArrayGeneric(arrayKey, value);
|
||||
}
|
||||
|
||||
SpawnableMetaDataBuilder& SpawnableMetaDataBuilder::AppendArray(AZStd::string_view arrayKey, int64_t value)
|
||||
{
|
||||
return AppendArrayGeneric(arrayKey, value);
|
||||
}
|
||||
|
||||
SpawnableMetaDataBuilder& SpawnableMetaDataBuilder::AppendArray(AZStd::string_view arrayKey, double value)
|
||||
{
|
||||
return AppendArrayGeneric(arrayKey, value);
|
||||
}
|
||||
|
||||
SpawnableMetaDataBuilder& SpawnableMetaDataBuilder::AppendArray(AZStd::string_view arrayKey, AZStd::string value)
|
||||
{
|
||||
return AppendArrayGeneric(arrayKey, AZStd::move(value));
|
||||
}
|
||||
|
||||
bool SpawnableMetaDataBuilder::Remove(AZStd::string_view key)
|
||||
{
|
||||
auto it = m_table.find(HashKey(key));
|
||||
if (it != m_table.end())
|
||||
{
|
||||
RemoveAllEntriesIfArray(key, it);
|
||||
m_table.erase(it);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SpawnableMetaDataBuilder::RemoveArrayEntry(AZStd::string_view arrayKey, uint64_t index)
|
||||
{
|
||||
return RemoveArrayEntry(arrayKey, aznumeric_cast<AzFramework::SpawnableMetaDataArrayIndex>(index));
|
||||
}
|
||||
|
||||
bool SpawnableMetaDataBuilder::RemoveArrayEntry(AZStd::string_view arrayKey, AzFramework::SpawnableMetaDataArrayIndex index)
|
||||
{
|
||||
auto it = m_table.find(HashKey(arrayKey));
|
||||
if (it != m_table.end())
|
||||
{
|
||||
if (AzFramework::SpawnableMetaDataArraySize* size =
|
||||
AZStd::get_if<AzFramework::SpawnableMetaDataArraySize>(&it->second); size != nullptr)
|
||||
{
|
||||
if (index < *size)
|
||||
{
|
||||
AZ::HashValue64 indexHash = HashArrayKey(arrayKey, index);
|
||||
index++;
|
||||
for (; index < *size; ++index)
|
||||
{
|
||||
AZ::HashValue64 nextIndexHash = HashArrayKey(arrayKey, index);
|
||||
m_table[indexHash] = AZStd::move(m_table[nextIndexHash]);
|
||||
indexHash = nextIndexHash;
|
||||
}
|
||||
[[maybe_unused]] size_t removedCount = m_table.erase(indexHash);
|
||||
AZ_Assert(removedCount == 1, "RemoveArrayEntry did not correctly detect an edge case.");
|
||||
|
||||
(*size)--;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t SpawnableMetaDataBuilder::GetEntryCount() const
|
||||
{
|
||||
return m_table.size();
|
||||
}
|
||||
|
||||
AzFramework::SpawnableMetaData SpawnableMetaDataBuilder::BuildMetaData() const
|
||||
{
|
||||
AzFramework::SpawnableMetaData::Table readOnlyTable;
|
||||
|
||||
readOnlyTable.reserve(m_table.size());
|
||||
AZStd::transform(m_table.begin(), m_table.end(), AZStd::back_inserter(readOnlyTable),
|
||||
[](const auto& entry)
|
||||
{
|
||||
return AzFramework::SpawnableMetaData::TableEntry(entry.first, entry.second);
|
||||
});
|
||||
|
||||
AZStd::sort(readOnlyTable.begin(), readOnlyTable.end(),
|
||||
[](const auto& lhs, const auto& rhs)
|
||||
{
|
||||
return lhs.first < rhs.first;
|
||||
});
|
||||
|
||||
return AzFramework::SpawnableMetaData(AZStd::move(readOnlyTable));
|
||||
}
|
||||
|
||||
AZ::HashValue64 SpawnableMetaDataBuilder::HashKey(AZStd::string_view key) const
|
||||
{
|
||||
return AZ::TypeHash64(reinterpret_cast<const uint8_t*>(key.data()), aznumeric_cast<uint64_t>(key.length()));
|
||||
}
|
||||
|
||||
AZ::HashValue64 SpawnableMetaDataBuilder::HashArrayKey(AZStd::string_view arrayKey, uint64_t index) const
|
||||
{
|
||||
return AZ::TypeHash64(reinterpret_cast<const uint8_t*>(arrayKey.data()), aznumeric_cast<uint64_t>(arrayKey.length()),
|
||||
aznumeric_caster(AzFramework::SpawnableMetaData::ArrayKeyRoot + index));
|
||||
}
|
||||
|
||||
AZ::HashValue64 SpawnableMetaDataBuilder::HashArrayKey(AZStd::string_view arrayKey, AzFramework::SpawnableMetaDataArrayIndex index) const
|
||||
{
|
||||
return HashArrayKey(arrayKey, aznumeric_cast<uint64_t>(index));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
SpawnableMetaDataBuilder& SpawnableMetaDataBuilder::AddGeneric(AZStd::string_view key, T&& value)
|
||||
{
|
||||
auto keyHash = HashKey(key);
|
||||
auto it = m_table.find(keyHash);
|
||||
if (it != m_table.end())
|
||||
{
|
||||
RemoveAllEntriesIfArray(key, it);
|
||||
it->second = AZStd::forward<T>(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_table.emplace(keyHash, AZStd::forward<T>(value));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
SpawnableMetaDataBuilder& SpawnableMetaDataBuilder::AppendArrayGeneric(AZStd::string_view arrayKey, T&& value)
|
||||
{
|
||||
auto arrayKeyHash = HashKey(arrayKey);
|
||||
auto it = m_table.find(arrayKeyHash);
|
||||
if (it != m_table.end())
|
||||
{
|
||||
if (AzFramework::SpawnableMetaDataArraySize* storedValue =
|
||||
AZStd::get_if<AzFramework::SpawnableMetaDataArraySize>(&it->second); storedValue != nullptr)
|
||||
{
|
||||
m_table[HashArrayKey(arrayKey, (*storedValue)++)] = AZStd::forward<T>(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
it->second = AzFramework::SpawnableMetaDataArraySize{ 1 };
|
||||
m_table[HashArrayKey(arrayKey, 0)] = AZStd::forward<T>(value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_table.emplace(arrayKeyHash, AzFramework::SpawnableMetaDataArraySize{ 1 });
|
||||
m_table[HashArrayKey(arrayKey, 0)] = AZStd::forward<T>(value);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void SpawnableMetaDataBuilder::RemoveAllEntriesIfArray(AZStd::string_view arrayKey, Table::iterator sizeEntry)
|
||||
{
|
||||
if (AzFramework::SpawnableMetaDataArraySize* size =
|
||||
AZStd::get_if<AzFramework::SpawnableMetaDataArraySize>(&sizeEntry->second); size != nullptr)
|
||||
{
|
||||
for (AzFramework::SpawnableMetaDataArrayIndex i{ 0 }; i < *size; ++i)
|
||||
{
|
||||
[[maybe_unused]] size_t removedCount = m_table.erase(HashArrayKey(arrayKey, i));
|
||||
AZ_Assert(removedCount == 1, "RemoveArrayEntry did not correctly detect an edge case.");
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 <AzCore/Utils/TypeHash.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzFramework/Spawnable/SpawnableMetaData.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
class SpawnableMetaDataBuilder final
|
||||
{
|
||||
public:
|
||||
SpawnableMetaDataBuilder& Add(AZStd::string_view key, bool value);
|
||||
SpawnableMetaDataBuilder& Add(AZStd::string_view key, uint64_t value);
|
||||
SpawnableMetaDataBuilder& Add(AZStd::string_view key, int64_t value);
|
||||
SpawnableMetaDataBuilder& Add(AZStd::string_view key, double value);
|
||||
SpawnableMetaDataBuilder& Add(AZStd::string_view key, AZStd::string value);
|
||||
|
||||
SpawnableMetaDataBuilder& AppendArray(AZStd::string_view arrayKey, bool value);
|
||||
SpawnableMetaDataBuilder& AppendArray(AZStd::string_view arrayKey, uint64_t value);
|
||||
SpawnableMetaDataBuilder& AppendArray(AZStd::string_view arrayKey, int64_t value);
|
||||
SpawnableMetaDataBuilder& AppendArray(AZStd::string_view arrayKey, double value);
|
||||
SpawnableMetaDataBuilder& AppendArray(AZStd::string_view arrayKey, AZStd::string value);
|
||||
|
||||
bool Remove(AZStd::string_view key);
|
||||
bool RemoveArrayEntry(AZStd::string_view arrayKey, uint64_t index);
|
||||
bool RemoveArrayEntry(AZStd::string_view arrayKey, AzFramework::SpawnableMetaDataArrayIndex index);
|
||||
|
||||
size_t GetEntryCount() const;
|
||||
|
||||
AzFramework::SpawnableMetaData BuildMetaData() const;
|
||||
|
||||
private:
|
||||
using Table = AZStd::unordered_map<AZ::HashValue64, AzFramework::SpawnableMetaData::TableValue>;
|
||||
|
||||
AZ::HashValue64 HashKey(AZStd::string_view key) const;
|
||||
AZ::HashValue64 HashArrayKey(AZStd::string_view arrayKey, uint64_t index) const;
|
||||
AZ::HashValue64 HashArrayKey(AZStd::string_view arrayKey, AzFramework::SpawnableMetaDataArrayIndex index) const;
|
||||
|
||||
template<typename T>
|
||||
SpawnableMetaDataBuilder& AddGeneric(AZStd::string_view key, T&& value);
|
||||
|
||||
template<typename T>
|
||||
SpawnableMetaDataBuilder& AppendArrayGeneric(AZStd::string_view arrayKey, T&& value);
|
||||
|
||||
void RemoveAllEntriesIfArray(AZStd::string_view arrayKey, Table::iterator sizeEntry);
|
||||
|
||||
Table m_table;
|
||||
};
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h>
|
||||
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Component/EntityUtils.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/std/algorithm.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
|
||||
#include <AzFramework/Spawnable/Spawnable.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::SpawnableUtils
|
||||
{
|
||||
|
||||
AzFramework::Spawnable CreateSpawnable(const PrefabDom& prefabDom)
|
||||
{
|
||||
AzFramework::Spawnable spawnable;
|
||||
AZStd::unique_ptr<Instance> instance(aznew Instance());
|
||||
if (!Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom(*instance, prefabDom, false))
|
||||
{
|
||||
AZ_Assert(false,
|
||||
"Failed to Load Prefab Instance from given Prefab DOM while Spawnable creation.");
|
||||
}
|
||||
else
|
||||
{
|
||||
AzFramework::Spawnable::EntityList& entities = spawnable.GetEntities();
|
||||
instance->DetachNestedEntities([&entities](AZStd::unique_ptr<AZ::Entity> entity)
|
||||
{
|
||||
entities.emplace_back(AZStd::move(entity));
|
||||
});
|
||||
}
|
||||
|
||||
return spawnable;
|
||||
}
|
||||
|
||||
void OrganizeEntitiesForSorting(
|
||||
AzFramework::Spawnable::EntityList& entities,
|
||||
AZStd::unordered_set<AZ::EntityId>& existingEntityIds,
|
||||
AZStd::unordered_map<AZ::EntityId, AzFramework::Spawnable::EntityList>& parentIdToChildren,
|
||||
AZStd::vector<AZ::EntityId>& candidateIds,
|
||||
size_t& removedEntitiesCount)
|
||||
{
|
||||
existingEntityIds.clear();
|
||||
parentIdToChildren.clear();
|
||||
candidateIds.clear();
|
||||
removedEntitiesCount = 0;
|
||||
|
||||
for (auto& entity : entities)
|
||||
{
|
||||
if (!entity)
|
||||
{
|
||||
++removedEntitiesCount;
|
||||
continue;
|
||||
}
|
||||
|
||||
AZ::EntityId entityId = entity->GetId();
|
||||
if (!entityId.IsValid())
|
||||
{
|
||||
AZ_Warning("Entity", false, "Hierarchy sort found entity '%s' with invalid ID", entity->GetName().c_str());
|
||||
|
||||
++removedEntitiesCount;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!existingEntityIds.insert(entityId).second)
|
||||
{
|
||||
AZ_Warning("Entity", false, "Hierarchy sort found multiple entities using same ID as entity '%s' %s",
|
||||
entity->GetName().c_str(),
|
||||
entityId.ToString().c_str());
|
||||
|
||||
++removedEntitiesCount;
|
||||
continue;
|
||||
}
|
||||
|
||||
// search for any component that implements the TransformInterface.
|
||||
// don't use EBus because we support sorting entities that haven't been initialized or activated.
|
||||
// entities with no transform component will be treated like entities with no parent.
|
||||
AZ::EntityId parentId;
|
||||
if (AZ::TransformInterface* transformInterface =
|
||||
AZ::EntityUtils::FindFirstDerivedComponent<AZ::TransformInterface>(entity.get()))
|
||||
{
|
||||
parentId = transformInterface->GetParentId();
|
||||
if (parentId == entityId)
|
||||
{
|
||||
AZ_Warning("Entity", false, "Hierarchy sort found entity parented to itself '%s' %s",
|
||||
entity->GetName().c_str(),
|
||||
entityId.ToString().c_str());
|
||||
|
||||
parentId.SetInvalid();
|
||||
}
|
||||
}
|
||||
|
||||
auto& children = parentIdToChildren[parentId];
|
||||
children.emplace_back(nullptr);
|
||||
children.back().swap(entity);
|
||||
}
|
||||
|
||||
// clear 'entities', we'll refill it in sorted order.
|
||||
entities.clear();
|
||||
|
||||
// the first candidates should be the parents of the roots.
|
||||
for (auto& parentChildrenPair : parentIdToChildren)
|
||||
{
|
||||
const AZ::EntityId& parentId = parentChildrenPair.first;
|
||||
|
||||
// we found a root if parent ID doesn't correspond to any entity in the list
|
||||
if (existingEntityIds.find(parentId) == existingEntityIds.end())
|
||||
{
|
||||
candidateIds.push_back(parentId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TraceParentingLoop(
|
||||
const AZ::EntityId& parentFromLoopId,
|
||||
const AZStd::unordered_map<AZ::EntityId, AzFramework::Spawnable::EntityList>& parentIdToChildren)
|
||||
{
|
||||
|
||||
// Find name to use in warning message
|
||||
AZStd::string_view parentFromLoopName;
|
||||
for (const auto& parentIdChildrenPair : parentIdToChildren)
|
||||
{
|
||||
for (const auto& entity : parentIdChildrenPair.second)
|
||||
{
|
||||
if (entity->GetId() == parentFromLoopId)
|
||||
{
|
||||
parentFromLoopName = entity->GetName();
|
||||
break;
|
||||
}
|
||||
if (!parentFromLoopName.empty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AZ_Warning("Entity", false, "Hierarchy sort found parenting loop involving entity '%.*s' %s",
|
||||
AZ_STRING_ARG(parentFromLoopName),
|
||||
parentFromLoopId.ToString().c_str());
|
||||
}
|
||||
|
||||
void SortEntitiesByTransformHierarchy(AzFramework::Spawnable& spawnable)
|
||||
{
|
||||
auto& entities = spawnable.GetEntities();
|
||||
const size_t originalEntityCount = entities.size();
|
||||
|
||||
// IDs of those present in 'entities'. Does not include parent ID if parent not found in 'entities'
|
||||
AZStd::unordered_set<AZ::EntityId> existingEntityIds;
|
||||
|
||||
// map children by their parent ID (even if parent not found in 'entities')
|
||||
AZStd::unordered_map<AZ::EntityId, AzFramework::Spawnable::EntityList> parentIdToChildren;
|
||||
|
||||
// use 'candidateIds' to track the parent IDs we're going to process next.
|
||||
AZStd::vector<AZ::EntityId> candidateIds;
|
||||
candidateIds.reserve(originalEntityCount + 1);
|
||||
|
||||
size_t removedCount = 0;
|
||||
OrganizeEntitiesForSorting(entities, existingEntityIds, parentIdToChildren, candidateIds, removedCount);
|
||||
|
||||
// process candidates until everything is sorted:
|
||||
// - add candidate's children to the final sorted order
|
||||
// - add candidate's children to list of candidates, so we can process *their* children in a future loop
|
||||
// - erase parent/children entry from parentToChildrenIds
|
||||
// - continue until nothing is left in parentToChildrenIds
|
||||
for (size_t candidateIndex = 0; !parentIdToChildren.empty(); ++candidateIndex)
|
||||
{
|
||||
// if there are no more candidates, but there are still unsorted children, then we have an infinite loop.
|
||||
// pick an arbitrary parent from the loop to be the next candidate.
|
||||
if (candidateIndex == candidateIds.size())
|
||||
{
|
||||
const AZ::EntityId& parentFromLoopId = parentIdToChildren.begin()->first;
|
||||
|
||||
#ifdef AZ_ENABLE_TRACING
|
||||
TraceParentingLoop(parentFromLoopId, parentIdToChildren);
|
||||
#endif // AZ_ENABLE_TRACING
|
||||
|
||||
candidateIds.push_back(parentFromLoopId);
|
||||
}
|
||||
|
||||
const AZ::EntityId& parentId = candidateIds[candidateIndex];
|
||||
|
||||
auto foundChildren = parentIdToChildren.find(parentId);
|
||||
if (foundChildren != parentIdToChildren.end())
|
||||
{
|
||||
for (auto& child : foundChildren->second)
|
||||
{
|
||||
candidateIds.push_back(child->GetId());
|
||||
entities.emplace_back(nullptr);
|
||||
entities.back().swap(child);
|
||||
}
|
||||
|
||||
parentIdToChildren.erase(foundChildren);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AZ_Assert(entities.size() + removedCount == originalEntityCount,
|
||||
"Wrong number of entities after sort. Original entity count = %zu, Sorted entity count = %zu, Removed entity count = %zu",
|
||||
originalEntityCount,
|
||||
entities.size(),
|
||||
removedCount
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework::Prefab::SpawnableUtils
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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 <AzFramework/Spawnable/Spawnable.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomTypes.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::SpawnableUtils
|
||||
{
|
||||
AzFramework::Spawnable CreateSpawnable(const PrefabDom& prefabDom);
|
||||
|
||||
void SortEntitiesByTransformHierarchy(AzFramework::Spawnable& spawnable);
|
||||
} // namespace AzToolsFramework::Prefab::SpawnableUtils
|
||||
Reference in New Issue
Block a user