Integrating up through commit 90f050496

This commit is contained in:
alexpete
2021-04-07 14:03:29 -07:00
parent 8f2ed080a9
commit c2cbd430fe
2694 changed files with 285622 additions and 176874 deletions
+33 -3
View File
@@ -14,22 +14,52 @@ if(NOT PAL_TRAIT_BUILD_HOST_TOOLS)
endif()
ly_add_target(
NAME Prefab.PrefabBuilder MODULE
NAME PrefabBuilder.Static STATIC
NAMESPACE Gem
FILES_CMAKE
prefabbuilder_files.cmake
BUILD_DEPENDENCIES
PRIVATE
PUBLIC
AZ::AzCore
AZ::AzToolsFramework
AZ::AssetBuilderSDK
)
ly_add_target(
NAME PrefabBuilder MODULE
NAMESPACE Gem
INCLUDE_DIRECTORIES
PRIVATE
.
FILES_CMAKE
prefabbuilder_module_files.cmake
BUILD_DEPENDENCIES
PRIVATE
Gem::PrefabBuilder.Static
)
ly_add_target(
NAME PrefabBuilder.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
NAMESPACE Gem
FILES_CMAKE
prefabbuilder_tests_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
.
BUILD_DEPENDENCIES
PRIVATE
AZ::AzTest
Gem::PrefabBuilder.Static
)
ly_add_googletest(
NAME Gem::PrefabBuilder.Tests
)
ly_add_target_dependencies(
TARGETS
AssetBuilder
AssetProcessor
AssetProcessorBatch
DEPENDENT_TARGETS
Gem::Prefab.PrefabBuilder
Gem::PrefabBuilder
)
@@ -0,0 +1,340 @@
/*
* 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 "PrefabBuilderComponent.h"
namespace AZ::Prefab
{
void PrefabBuilderComponent::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
serialize->Class<PrefabBuilderComponent, AZ::Component>()->Version(0)->Attribute(
AZ::Edit::Attributes::SystemComponentTags, AZStd::vector<AZ::Crc32>({AssetBuilderSDK::ComponentTags::AssetBuilder}));
}
}
AzToolsFramework::Fingerprinting::TypeFingerprint PrefabBuilderComponent::CalculateBuilderFingerprint() const
{
AzToolsFramework::Fingerprinting::TypeCollection typeCollection = m_typeFingerprinter->GatherAllTypesForComponents();
AzToolsFramework::Fingerprinting::TypeFingerprint fingerprint = m_typeFingerprinter->GenerateFingerprintForAllTypes(typeCollection);
AZStd::hash_combine(fingerprint, m_pipeline.GetFingerprint());
return fingerprint;
}
void PrefabBuilderComponent::Activate()
{
AssetBuilderSDK::AssetBuilderCommandBus::Handler::BusConnect(m_builderId);
m_pipeline.LoadStackProfile("GameObjectCreation");
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
AZ_Assert(serializeContext, "SerializeContext not found");
m_typeFingerprinter = AZStd::make_unique<AzToolsFramework::Fingerprinting::TypeFingerprinter>(*serializeContext);
AzToolsFramework::Fingerprinting::TypeFingerprint fingerprint = CalculateBuilderFingerprint();
AssetBuilderSDK::AssetBuilderDesc builderDesc;
builderDesc.m_name = "Prefab Builder";
builderDesc.m_patterns.emplace_back("*.prefab", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard);
builderDesc.m_builderType = AssetBuilderSDK::AssetBuilderDesc::AssetBuilderType::External;
builderDesc.m_busId = m_builderId;
builderDesc.m_analysisFingerprint = AZStd::to_string(fingerprint);
builderDesc.m_createJobFunction =
[this](const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) {
CreateJobs(request, response);
};
builderDesc.m_processJobFunction =
[this](const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) {
ProcessJob(request, response);
};
AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBusTraits::RegisterBuilderInformation, builderDesc);
}
void PrefabBuilderComponent::Deactivate()
{
AssetBuilderSDK::AssetBuilderCommandBus::Handler::BusDisconnect(m_builderId);
}
void PrefabBuilderComponent::ShutDown()
{
m_isShuttingDown = true;
}
AzToolsFramework::Fingerprinting::TypeFingerprint PrefabBuilderComponent::CalculatePrefabFingerprint(
const AzToolsFramework::Prefab::PrefabDom& genericDocument) const
{
AzToolsFramework::Fingerprinting::TypeFingerprint fingerprint = m_pipeline.GetFingerprint();
// Deserialize all of the entities and their components (for this prefab only)
AzToolsFramework::Prefab::Instance::AliasToEntityMap entities;
auto entitiesIterator = genericDocument.FindMember(AzToolsFramework::Prefab::PrefabDomUtils::EntitiesName);
if (entitiesIterator != genericDocument.MemberEnd())
{
auto&& entitiesJson = entitiesIterator->value;
if (entitiesJson.IsObject())
{
JsonSerializationResult::ResultCode result = AZ::JsonSerialization::Load(entities, entitiesJson);
if (result.GetProcessing() != JsonSerializationResult::Processing::Halted)
{
// Add the fingerprint of all the components and their types
AZStd::hash_combine(fingerprint, m_typeFingerprinter->GenerateFingerprintForAllTypesInObject(&entities));
}
}
}
return fingerprint;
}
AZStd::vector<AssetBuilderSDK::SourceFileDependency> PrefabBuilderComponent::GetSourceDependencies(const AzToolsFramework::Prefab::PrefabDom& genericDocument)
{
AZStd::vector<AssetBuilderSDK::SourceFileDependency> sourceFileDependencies;
auto instancesIterator = genericDocument.FindMember(AzToolsFramework::Prefab::PrefabDomUtils::InstancesName);
if (instancesIterator != genericDocument.MemberEnd())
{
auto&& instances = instancesIterator->value;
if (instances.IsObject())
{
for (auto&& entry : instances.GetObject())
{
auto sourceIterator = entry.value.FindMember(AzToolsFramework::Prefab::PrefabDomUtils::SourceName);
if (sourceIterator != entry.value.MemberEnd())
{
auto&& source = sourceIterator->value;
if (source.IsString())
{
sourceFileDependencies.emplace_back(source.GetString(), Uuid::CreateNull());
}
}
}
}
}
return sourceFileDependencies;
}
void PrefabBuilderComponent::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const
{
using namespace AzToolsFramework::Prefab;
using namespace AzToolsFramework::Prefab::PrefabConversionUtils;
if (m_isShuttingDown)
{
response.m_result = AssetBuilderSDK::CreateJobsResultCode::ShuttingDown;
return;
}
AZStd::string fullPath;
AZ::StringFunc::Path::ConstructFull(request.m_watchFolder.c_str(), request.m_sourceFile.c_str(), fullPath);
// Load the JSON Dom
AZ::Outcome<PrefabDom, AZStd::string> readPrefabFileResult = AzFramework::FileFunc::ReadJsonFile(AZ::IO::Path(fullPath));
if (!readPrefabFileResult.IsSuccess())
{
AZ_Error(
"Prefab", false,
"PrefabLoader::LoadPrefabFile - Failed to load Prefab file from '%s'."
"Error message: '%s'",
fullPath.c_str(), readPrefabFileResult.GetError().c_str());
return;
}
auto genericDocument = readPrefabFileResult.TakeValue();
size_t fingerprint = CalculatePrefabFingerprint(genericDocument);
AZStd::vector<AssetBuilderSDK::SourceFileDependency> sourceFileDependencies = GetSourceDependencies(genericDocument);
for (const AssetBuilderSDK::PlatformInfo& info : request.m_enabledPlatforms)
{
AssetBuilderSDK::JobDescriptor job;
job.m_jobKey = PrefabJobKey;
job.SetPlatformIdentifier(info.m_identifier.c_str());
job.m_additionalFingerprintInfo = AZStd::to_string(fingerprint);
// Add a fingerprint job dependency on any referenced prefab so this prefab will rebuild if the dependent fingerprint changes
for (const AssetBuilderSDK::SourceFileDependency& sourceFileDependency : sourceFileDependencies)
{
job.m_jobDependencyList.emplace_back(
PrefabJobKey, info.m_identifier, AssetBuilderSDK::JobDependencyType::Fingerprint, sourceFileDependency);
}
response.m_createJobOutputs.push_back(AZStd::move(job));
}
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
}
bool PrefabBuilderComponent::StoreProducts(
AZ::IO::PathView tempDirPath,
const AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext::ProcessedObjectStoreContainer& store,
AZStd::vector<AssetBuilderSDK::JobProduct>& outputProducts) const
{
outputProducts.reserve(store.size());
AZStd::vector<uint8_t> data;
for (auto& object : store)
{
AZ_TracePrintf("Prefab Builder", " Serializing Prefab product '%s'.\n", object.GetId().c_str());
if (!object.Serialize(data))
{
AZ_Error("Prefab Builder", false, "Failed to serialize object '%s'.", object.GetId().c_str());
return false;
}
AZ::IO::Path productPath = tempDirPath;
productPath /= object.GetId();
AZ_TracePrintf("Prefab Builder", " Storing Prefab product '%s'.\n", object.GetId().c_str());
AZStd::unique_ptr<AZ::IO::GenericStream> productFile = GetOutputStream(productPath);
if (!productFile->IsOpen())
{
AZ_Error("Prefab Builder", false, "Unable to open product file at '%s'.", productPath.c_str());
return false;
}
if (productFile->Write(data.size(), data.data()) != data.size())
{
AZ_Error("Prefab Builder", false, "Unable to write product file at '%s'.", productPath.c_str());
return false;
}
AssetBuilderSDK::JobProduct product;
if (AssetBuilderSDK::OutputObject(&object.GetAsset(), object.GetAssetType(), productPath.String(), object.GetAssetType(),
object.GetAsset().GetId().m_subId, product))
{
outputProducts.push_back(AZStd::move(product));
}
data.clear();
}
return true;
}
AZStd::unique_ptr<AZ::IO::GenericStream> PrefabBuilderComponent::GetOutputStream(const AZ::IO::Path& path) const
{
return AZStd::make_unique<AZ::IO::FileIOStream>(path.c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeCreatePath);
}
bool PrefabBuilderComponent::ProcessPrefab(
const AZ::PlatformTagSet& platformTags, const char* filePath, AZ::IO::PathView tempDirPath, const AZ::Uuid& sourceFileUuid,
AzToolsFramework::Prefab::PrefabDom& mutableRootDom, AZStd::vector<AssetBuilderSDK::JobProduct>& jobProducts)
{
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext context(sourceFileUuid);
AZStd::string rootPrefabName;
if (!StringFunc::Path::GetFileName(filePath, rootPrefabName))
{
AZ_Error("Prefab Builder", false, "Unable to extract filename from '%s'.",
filePath);
return false;
}
context.AddPrefab(AZStd::move(rootPrefabName), AZStd::move(mutableRootDom));
context.SetPlatformTags(AZStd::move(platformTags));
AZ_TracePrintf("Prefab Builder", "Sending Prefab to the processor stack.\n");
m_pipeline.ProcessPrefab(context);
if (context.HasCompletedSuccessfully())
{
AZ_TracePrintf("Prefab Builder", "Finalizing products.\n");
if (!context.HasPrefabs())
{
if (StoreProducts(tempDirPath, context.GetProcessedObjects(), jobProducts))
{
return true;
}
else
{
AZ_Error("Prefab Builder", false, "One or more objects couldn't be committed to disk.");
}
}
else
{
AZ_Error("Prefab Builder", false, "After processing there were still Prefabs left.");
}
}
else
{
AZ_Error("Prefab Builder", false, "Failed to fully process the target prefab.");
}
return false;
}
void PrefabBuilderComponent::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
{
using namespace AzToolsFramework::Prefab;
using namespace AzToolsFramework::Prefab::PrefabConversionUtils;
if (m_isShuttingDown)
{
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
return;
}
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
auto* system = AZ::Interface<PrefabSystemComponentInterface>::Get();
if (!system)
{
AZ_Error("Prefab Builder", false, "Prefab system is not available.");
return;
}
auto* loader = AZ::Interface<PrefabLoaderInterface>::Get();
if (!loader)
{
AZ_Error("Prefab Builder", false, "Prefab loader is not available.");
return;
}
AZ_TracePrintf("Prefab Builder", "Loading Prefab in '%s'.\n", request.m_fullPath.c_str());
TemplateId templateId = loader->LoadTemplateFromFile(AZStd::string_view(request.m_fullPath));
if (templateId == InvalidTemplateId)
{
AZ_Error("Prefab Builder", false, "Failed to load Prefab template.");
return;
}
PrefabDom mutableRootDom;
mutableRootDom.CopyFrom(system->FindTemplateDom(templateId), mutableRootDom.GetAllocator());
AZ::PlatformTagSet platformTags;
const auto& tags = request.m_platformInfo.m_tags;
AZStd::for_each(tags.begin(), tags.end(), [&platformTags](const auto& tag) {
platformTags.emplace(AZ::Crc32(tag.c_str(), tag.size(), true));
});
if (ProcessPrefab(
platformTags, request.m_fullPath.c_str(), request.m_tempDirPath.c_str(), request.m_sourceFileUUID, mutableRootDom,
response.m_outputProducts))
{
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
}
AZ_TracePrintf("Prefab Builder", "Cleaning up.\n");
system->RemoveAllTemplates();
AZ_TracePrintf("Prefab Builder", "Prefab processing completed.\n");
}
} // namespace AZ::Prefab
@@ -0,0 +1,82 @@
/*
* 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 <AssetBuilderSDK/AssetBuilderSDK.h>
#include <AssetBuilderSDK/SerializationDependencies.h>
#include <AzCore/Component/Component.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/Module/Module.h>
#include <AzCore/Module/DynamicModuleHandle.h>
#include <AzFramework/FileFunc/FileFunc.h>
#include <AzToolsFramework/Prefab/PrefabSystemComponent.h>
#include <AzToolsFramework/Prefab/Spawnable/PrefabConversionPipeline.h>
#include <Fingerprinting/TypeFingerprinter.h>
#include <Prefab/PrefabDomUtils.h>
namespace AZ::Prefab
{
class PrefabBuilderComponent
: public AssetBuilderSDK::AssetBuilderCommandBus::Handler
, public AZ::Component
{
public:
static constexpr const char* BuilderId = "{A2E0791C-4607-4363-A7FD-73D01ED49660}";
static constexpr const char* PrefabJobKey = "Prefabs";
AZ_COMPONENT(PrefabBuilderComponent, BuilderId);
static void Reflect(AZ::ReflectContext* context);
PrefabBuilderComponent()
: m_builderId(BuilderId)
{
}
//////////////////////////////////////////////////////////////////////////
// AZ::Component
void Activate() override;
void Deactivate() override;
//////////////////////////////////////////////////////////////////////////
void ShutDown() override;
AzToolsFramework::Fingerprinting::TypeFingerprint CalculateBuilderFingerprint() const;
AzToolsFramework::Fingerprinting::TypeFingerprint CalculatePrefabFingerprint(
const AzToolsFramework::Prefab::PrefabDom& genericDocument) const;
static AZStd::vector<AssetBuilderSDK::SourceFileDependency> GetSourceDependencies(
const AzToolsFramework::Prefab::PrefabDom& genericDocument);
bool ProcessPrefab(
const AZ::PlatformTagSet& platformTags, const char* filePath, AZ::IO::PathView tempDirPath, const AZ::Uuid& sourceFileUuid,
AzToolsFramework::Prefab::PrefabDom& mutableRootDom,
AZStd::vector<AssetBuilderSDK::JobProduct>& jobProducts);
protected:
virtual AZStd::unique_ptr<AZ::IO::GenericStream> GetOutputStream(const AZ::IO::Path& path) const;
private:
void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const;
bool StoreProducts(
AZ::IO::PathView tempDirPath,
const AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext::ProcessedObjectStoreContainer& store,
AZStd::vector<AssetBuilderSDK::JobProduct>& outputProducts) const;
void ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response);
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabConversionPipeline m_pipeline;
AZ::Uuid m_builderId;
AZStd::atomic_bool m_isShuttingDown{ false };
AZStd::unique_ptr<AzToolsFramework::Fingerprinting::TypeFingerprinter> m_typeFingerprinter;
};
} // namespace AZ::Prefab
@@ -10,219 +10,11 @@
*
*/
#include <AssetBuilderSDK/AssetBuilderSDK.h>
#include <AzCore/Component/Component.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/Module/Module.h>
#include <AzCore/Module/DynamicModuleHandle.h>
#include <AzToolsFramework/Prefab/PrefabSystemComponent.h>
#include <AzToolsFramework/Prefab/Spawnable/PrefabConversionPipeline.h>
#include <PrefabBuilderComponent.h>
namespace AZ::Prefab
{
class PrefabBuilderComponent
: public AssetBuilderSDK::AssetBuilderCommandBus::Handler
{
public:
PrefabBuilderComponent()
: m_builderId("{A2E0791C-4607-4363-A7FD-73D01ED49660}")
{
AssetBuilderSDK::AssetBuilderCommandBus::Handler::BusConnect(m_builderId);
}
~PrefabBuilderComponent()
{
if (m_prefabSystem)
{
m_prefabSystem->Deactivate();
}
AssetBuilderSDK::AssetBuilderCommandBus::Handler::BusDisconnect(m_builderId);
}
void Register()
{
AssetBuilderSDK::AssetBuilderDesc builderDesc;
builderDesc.m_name = "Prefab Builder";
builderDesc.m_patterns.emplace_back("*.prefab", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard);
builderDesc.m_builderType = AssetBuilderSDK::AssetBuilderDesc::AssetBuilderType::Internal;
builderDesc.m_busId = m_builderId;
builderDesc.m_createJobFunction =
[this](const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response)
{
CreateJobs(request, response);
};
builderDesc.m_processJobFunction =
[this](const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
{
ProcessJob(request, response);
};
AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBusTraits::RegisterBuilderInformation, builderDesc);
}
void ShutDown() override
{
m_isShuttingDown = true;
}
private:
void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response)
{
if (m_isShuttingDown)
{
response.m_result = AssetBuilderSDK::CreateJobsResultCode::ShuttingDown;
return;
}
for (const AssetBuilderSDK::PlatformInfo& info : request.m_enabledPlatforms)
{
AssetBuilderSDK::JobDescriptor job;
job.m_jobKey = "Prefabs";
job.SetPlatformIdentifier(info.m_identifier.c_str());
response.m_createJobOutputs.push_back(AZStd::move(job));
}
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
}
bool StoreProducts(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response,
const AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext::ProcessedObjectStoreContainer& store)
{
response.m_outputProducts.reserve(store.size());
AZStd::vector<uint8_t> data;
for (auto& object : store)
{
AZ_TracePrintf("Prefab Builder", " Serializing Prefab product '%s'.\n", object.GetId().c_str());
if (!object.Serialize(data))
{
AZ_Error("Prefab Builder", false, "Failed to serialize object '%s'.", object.GetId().c_str());
return false;
}
AZ::IO::Path productPath = request.m_tempDirPath;
productPath /= object.GetId();
AZ_TracePrintf("Prefab Builder", " Storing Prefab product '%s'.\n", object.GetId().c_str());
AZ::IO::SystemFile productFile;
if (!productFile.Open(productPath.c_str(),
AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY | AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH))
{
AZ_Error("Prefab Builder", false, "Unable to open product file at '%s'.", productPath.c_str());
return false;
}
if (productFile.Write(data.data(), data.size()) != data.size())
{
AZ_Error("Prefab Builder", false, "Unable to write product file at '%s'.", productPath.c_str());
return false;
}
AssetBuilderSDK::JobProduct product;
product.m_productFileName = productPath.String();
product.m_productAssetType = object.GetAssetType();
product.m_productSubID = object.BuildSubId();
// TODO: Handle dependencies.
product.m_dependenciesHandled = true;
response.m_outputProducts.push_back(AZStd::move(product));
data.clear();
}
return true;
}
void ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
{
using namespace AzToolsFramework::Prefab;
using namespace AzToolsFramework::Prefab::PrefabConversionUtils;
if (m_isShuttingDown)
{
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
return;
}
if (!m_prefabSystem)
{
m_prefabSystem = AZStd::make_unique<AzToolsFramework::Prefab::PrefabSystemComponent>();
m_prefabSystem->Init();
m_prefabSystem->Activate();
m_pipeline.LoadStackProfile("GameObjectCreation");
}
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
auto* system = AZ::Interface<PrefabSystemComponentInterface>::Get();
if (!system)
{
AZ_Error("Prefab Builder", false, "Prefab system is not available.");
return;
}
auto* loader = AZ::Interface<PrefabLoaderInterface>::Get();
if (!loader)
{
AZ_Error("Prefab Builder", false, "Prefab loader is not available.");
return;
}
AZ_TracePrintf("Prefab Builder", "Loading Prefab in '%s'.\n", request.m_fullPath.c_str());
TemplateId templateId = loader->LoadTemplate(request.m_fullPath);
if (templateId == InvalidTemplateId)
{
AZ_Error("Prefab Builder", false, "Failed to load Prefab template.");
return;
}
PrefabProcessorContext context;
PrefabDom mutableRootDom;
// TODO: LoadTemplate should be updated so there's no need to get the extra copy here.
mutableRootDom.CopyFrom(system->FindTemplateDom(templateId), mutableRootDom.GetAllocator());
AZStd::string rootPrefabName;
if (!StringFunc::Path::GetFileName(request.m_fullPath.c_str(), rootPrefabName))
{
AZ_Warning("Prefab Builder", false, "Unable to extract filename from '%s', using 'Root' instead.",
request.m_fullPath.c_str());
rootPrefabName = "Root";
}
context.AddPrefab(AZStd::move(rootPrefabName), AZStd::move(mutableRootDom));
AZ_TracePrintf("Prefab Builder", "Sending Prefab to the processor stack.\n");
m_pipeline.ProcessPrefab(context);
AZ_TracePrintf("Prefab Builder", "Finalizing products.\n");
if (!context.HasPrefabs())
{
if (StoreProducts(request, response, context.GetProcessedObjects()))
{
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
}
else
{
AZ_Error("Prefab Builder", false, "One or more objects couldn't be committed to disk.");
}
}
else
{
AZ_Error("Prefab Builder", false, "After processing there were still Prefabs left.");
}
AZ_TracePrintf("Prefab Builder", "Cleaning up.\n");
system->RemoveTemplate(templateId);
AZ_TracePrintf("Prefab Builder", "Prefab processing completed.\n");
}
AZStd::unique_ptr<AzToolsFramework::Prefab::PrefabSystemComponent> m_prefabSystem;
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabConversionPipeline m_pipeline;
AZ::Uuid m_builderId;
AZStd::atomic_bool m_isShuttingDown{ false };
};
class PrefabBuilderModule
: public Module
{
@@ -233,20 +25,10 @@ namespace AZ::Prefab
PrefabBuilderModule()
: Module()
{
m_builder.Register();
m_descriptors.insert(m_descriptors.end(), {
PrefabBuilderComponent::CreateDescriptor()
});
}
void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC_CE("PrefabBuilder"));
}
void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC_CE("PrefabBuilder"));
}
PrefabBuilderComponent m_builder;
};
} // namespace AZ::Prefab
@@ -0,0 +1,198 @@
/*
* 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 "PrefabBuilderTests.h"
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Serialization/Json/JsonSystemComponent.h>
#include <AzCore/Serialization/Json/RegistrationContext.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
namespace UnitTest
{
AZ::Entity* CreateEntity(const char* entityName, AZStd::initializer_list<AZ::Component*> componentsToAdd = {})
{
// Circumvent the EntityContext system and generate a new entity with a transformcomponent
AZ::Entity* newEntity = aznew AZ::Entity(entityName);
newEntity->CreateComponent(AZ::TransformComponentTypeId);
newEntity->Init();
for (auto&& component : componentsToAdd)
{
newEntity->AddComponent(component);
}
newEntity->Activate();
return newEntity;
}
TEST_F(PrefabBuilderTests, SourceDependencies)
{
static constexpr const char* ChildPrefabPath = "child.prefab";
using namespace AzToolsFramework::Prefab;
AZStd::vector<AZStd::unique_ptr<Instance>> childInstances;
auto* prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
auto* prefabLoaderInterface = AZ::Interface<PrefabLoaderInterface>::Get();
ASSERT_NE(prefabSystemComponentInterface, nullptr);
ASSERT_NE(prefabLoaderInterface, nullptr);
// Create a child entity and a prefab containing it
childInstances.push_back(prefabSystemComponentInterface->CreatePrefab({CreateEntity("child")}, {}, ChildPrefabPath));
// Create a parent entity and a prefab for it, pass in the child prefab for it to reference
auto parentInstance = prefabSystemComponentInterface->CreatePrefab({CreateEntity("parent")}, AZStd::move(childInstances), "parent.prefab");
AZStd::string serializedInstance;
// Save to a string so we can load it as a PrefabDom and so that the nested instance becomes a Source file reference
ASSERT_TRUE(prefabLoaderInterface->SaveTemplateToString(parentInstance->GetTemplateId(), serializedInstance));
AZ::Outcome<PrefabDom, AZStd::string> readPrefabFileResult = AzFramework::FileFunc::ReadJsonFromString(serializedInstance);
ASSERT_TRUE(readPrefabFileResult.IsSuccess());
// Now that we have a PrefabDom, test extracting the Source file reference as a Source Dependency
auto sourceFileDependencies = AZ::Prefab::PrefabBuilderComponent::GetSourceDependencies(readPrefabFileResult.TakeValue());
ASSERT_EQ(sourceFileDependencies.size(), 1);
EXPECT_STREQ(sourceFileDependencies[0].m_sourceFileDependencyPath.c_str(), ChildPrefabPath);
}
TEST_F(PrefabBuilderTests, ProductDependencies)
{
constexpr const char* ChildPrefabPath = "child.prefab";
using namespace AzToolsFramework::Prefab;
AZ::Uuid TestAssetUuid("{7725567D-D420-46C2-B481-E0F79212CD34}");
AZ::Data::AssetId TestAssetId(TestAssetUuid, 0);
AZStd::vector<AZStd::unique_ptr<Instance>> childInstances;
auto* prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
ASSERT_NE(prefabSystemComponentInterface, nullptr);
auto* component = aznew TestComponent();
AZ::Data::Asset<AZ::Data::AssetData> asset = AZ::Data::Asset<TestAsset>(TestAssetId, azrtti_typeid<TestAsset>());
component->m_asset = asset;
AZ::Entity* childEntity = CreateEntity("child", {component});
// Create a child prefab with an entity that has an Asset<T> reference on it
childInstances.push_back(prefabSystemComponentInterface->CreatePrefab(
{childEntity}, {}, ChildPrefabPath, AZStd::unique_ptr<AZ::Entity>(CreateEntity("Container"))));
// Create a parent prefab that has a nested instance reference to the child prefab
auto parentInstance =
prefabSystemComponentInterface->CreatePrefab({CreateEntity("parent")}, AZStd::move(childInstances), "parent.prefab", AZStd::unique_ptr<AZ::Entity>(CreateEntity("Container")));
AZStd::string serializedInstance;
TestPrefabBuilderComponent prefabBuilderComponent;
prefabBuilderComponent.Activate();
AZStd::vector<AssetBuilderSDK::JobProduct> jobProducts;
auto&& prefabDom = prefabSystemComponentInterface->FindTemplateDom(parentInstance->GetTemplateId());
ASSERT_TRUE(prefabBuilderComponent.ProcessPrefab({AZ::Crc32("pc")}, "parent.prefab", "unused", AZ::Uuid(), prefabDom, jobProducts));
ASSERT_EQ(jobProducts.size(), 1);
ASSERT_EQ(jobProducts[0].m_dependencies.size(), 1);
ASSERT_EQ(jobProducts[0].m_dependencies[0].m_dependencyId, TestAssetId);
prefabBuilderComponent.Deactivate();
}
AZStd::pair<size_t, size_t> GetFingerprint(AzToolsFramework::Prefab::PrefabDom& dom)
{
TestPrefabBuilderComponent prefabBuilderComponent;
prefabBuilderComponent.Activate();
size_t builderFingerprint = prefabBuilderComponent.CalculateBuilderFingerprint();
size_t fingerprint = prefabBuilderComponent.CalculatePrefabFingerprint(dom);
prefabBuilderComponent.Deactivate();
return {fingerprint, builderFingerprint};
}
TEST_F(PrefabBuilderTests, FingerprintTest)
{
auto* prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
ASSERT_NE(prefabSystemComponentInterface, nullptr);
auto* component = aznew TestComponent();
AZ::Entity* entity = CreateEntity("test", {component});
auto parentInstance = prefabSystemComponentInterface->CreatePrefab(
{entity}, {}, "test.prefab",
AZStd::unique_ptr<AZ::Entity>(CreateEntity("Container")));
AZStd::vector<AssetBuilderSDK::JobProduct> jobProducts;
auto&& prefabDom = prefabSystemComponentInterface->FindTemplateDom(parentInstance->GetTemplateId());
auto [v0Dom, v0Builder] = GetFingerprint(prefabDom);
auto [sanityDom, sanityBuilder] = GetFingerprint(prefabDom);
// Make sure the fingerprint is stable without changes
ASSERT_EQ(v0Dom, sanityDom);
ASSERT_EQ(v0Builder, sanityBuilder);
AZ::SerializeContext* context = m_app.GetSerializeContext();
// Unreflect VersionChangingData, change its version, and reflect it again
context->EnableRemoveReflection();
VersionChangingData::Reflect(context);
VersionChangingData::m_version = 1;
context->DisableRemoveReflection();
VersionChangingData::Reflect(context);
// Get the new fingerprint and check that it changed
auto [v1Dom, v1Builder] = GetFingerprint(prefabDom);
ASSERT_NE(v0Dom, v1Dom); // Verify the fingerprint for the object changed
ASSERT_NE(v0Builder, v1Builder); // Verify the fingerprint for the entire builder changed
}
AZStd::unique_ptr<AZ::IO::GenericStream> TestPrefabBuilderComponent::GetOutputStream(const AZ::IO::Path&) const
{
return AZStd::make_unique<SelfContainedMemoryStream>();
}
void PrefabBuilderTests::SetUp()
{
AZ::ComponentApplication::Descriptor desc;
m_app.Start(desc);
m_app.CreateReflectionManager();
m_testComponentDescriptor = AZStd::unique_ptr<AZ::ComponentDescriptor>{TestComponent::CreateDescriptor()};
m_testComponentDescriptor->Reflect(m_app.GetSerializeContext());
TestAsset::Reflect(m_app.GetSerializeContext());
VersionChangingData::Reflect(m_app.GetSerializeContext());
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// in the unit tests.
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
}
void PrefabBuilderTests::TearDown()
{
m_testComponentDescriptor = nullptr;
m_app.Stop();
}
}
AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV);
@@ -0,0 +1,99 @@
/*
* 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 <PrefabBuilderComponent.h>
#include <AzTest/AzTest.h>
#include <Application/ToolsApplication.h>
#include <AzCore/Component/ComponentApplication.h>
namespace UnitTest
{
struct VersionChangingData : AZ::Data::AssetData
{
AZ_TYPE_INFO(VersionChangingData, "{E3A37E19-AE61-4C2F-809E-03B4D83261E8}");
static void Reflect(AZ::ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<VersionChangingData>()->Version(m_version);
}
}
inline static int m_version = 0;
};
struct TestAsset : AZ::Data::AssetData
{
AZ_TYPE_INFO(TestAsset, "{8E736462-5424-4720-A2D9-F71DFC5905E3}");
static void Reflect(AZ::ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<TestAsset>();
}
}
};
struct TestComponent : AZ::Component
{
AZ_COMPONENT(TestComponent, "{E3982C6A-0B01-4B04-A3E2-D95729D4B9C6}");
static void Reflect(AZ::ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<TestComponent>()
->Field("Asset", &TestComponent::m_asset)
->Field("Version", &TestComponent::m_version);
}
}
void Activate() override
{
}
void Deactivate() override
{
}
AZ::Data::Asset<TestAsset> m_asset{};
VersionChangingData m_version;
};
struct SelfContainedMemoryStream : AZ::IO::ByteContainerStream<AZStd::vector<char>>
{
SelfContainedMemoryStream() : ByteContainerStream<AZStd::vector<char>>{&m_bufferData}
{
}
AZStd::vector<char> m_bufferData;
};
struct TestPrefabBuilderComponent : AZ::Prefab::PrefabBuilderComponent
{
protected:
AZStd::unique_ptr<AZ::IO::GenericStream> GetOutputStream(const AZ::IO::Path& path) const override;
};
struct PrefabBuilderTests : ::testing::Test
{
protected:
void SetUp() override;
void TearDown() override;
AzToolsFramework::ToolsApplication m_app;
AZStd::unique_ptr<AZ::ComponentDescriptor> m_testComponentDescriptor{};
};
}
+17
View File
@@ -0,0 +1,17 @@
{
"gem_name": "PrefabBuilder",
"GemFormatVersion": 4,
"Uuid": "88500802311C4793B03484BE2091B607",
"Name": "PrefabBuilder",
"DisplayName": "PrefabBuilder",
"Version": "0.1.0",
"Summary": "Asset Processor builder module for Prefabs",
"Tags": ["Prefabs"],
"IconPath": "preview.png",
"Modules": [
{
"Name": "Editor",
"Type": "EditorModule"
}
]
}
@@ -10,5 +10,6 @@
#
set(FILES
PrefabBuilderModule.cpp
PrefabBuilderComponent.h
PrefabBuilderComponent.cpp
)
@@ -0,0 +1,14 @@
#
# 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.
#
set(FILES
PrefabBuilderModule.cpp
)
@@ -0,0 +1,15 @@
#
# 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.
#
set(FILES
PrefabBuilderTests.h
PrefabBuilderTests.cpp
)