You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/Atom/RPI/Code/Source/RPI.Builders/Common/AnyAssetBuilder.cpp

204 lines
8.7 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AssetBuilderSDK/SerializationDependencies.h>
#include <Common/AnyAssetBuilder.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Serialization/Utils.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/std/algorithm.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzCore/Serialization/Json/JsonUtils.h>
#include <Atom/RPI.Edit/Common/ConvertibleSource.h>
#include <Atom/RPI.Reflect/System/AnyAsset.h>
namespace AZ
{
namespace RPI
{
namespace
{
[[maybe_unused]] const char* AnyAssetBuilderName = "AnyAssetBuilder";
const char* AnyAssetBuilderJobKey = "Any Asset Builder";
const char* AnyAssetBuilderDefaultExtension = "azasset";
const char* AnyAssetSourceExtensions[] =
{
"azasset",
"attimage",
"azbuffer",
};
const uint32_t NumberOfSourceExtensions = AZ_ARRAY_SIZE(AnyAssetSourceExtensions);
}
void AnyAssetBuilder::RegisterBuilder()
{
// build source extension patterns
AZStd::vector<AssetBuilderSDK::AssetBuilderPattern> patterns(NumberOfSourceExtensions);
AZStd::for_each(patterns.begin(), patterns.end(), [&, index = 0](AssetBuilderSDK::AssetBuilderPattern& pattern) mutable
{
pattern = AssetBuilderSDK::AssetBuilderPattern(AZStd::string("*.") + AnyAssetSourceExtensions[index++], AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard);
});
// setup builder descriptor
AssetBuilderSDK::AssetBuilderDesc builderDescriptor;
builderDescriptor.m_name = AnyAssetBuilderJobKey;
builderDescriptor.m_patterns.insert(builderDescriptor.m_patterns.end(), patterns.begin(), patterns.end());
builderDescriptor.m_busId = azrtti_typeid<AnyAssetBuilder>();
builderDescriptor.m_createJobFunction = AZStd::bind(&AnyAssetBuilder::CreateJobs, this,
AZStd::placeholders::_1, AZStd::placeholders::_2);
builderDescriptor.m_processJobFunction = AZStd::bind(&AnyAssetBuilder::ProcessJob, this,
AZStd::placeholders::_1, AZStd::placeholders::_2);
builderDescriptor.m_version = 9;
BusConnect(builderDescriptor.m_busId);
AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBusTraits::RegisterBuilderInformation, builderDescriptor);
}
AnyAssetBuilder::~AnyAssetBuilder()
{
BusDisconnect();
}
void AnyAssetBuilder::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response)
{
if (m_isShuttingDown)
{
response.m_result = AssetBuilderSDK::CreateJobsResultCode::ShuttingDown;
return;
}
for (const AssetBuilderSDK::PlatformInfo& platformInfo : request.m_enabledPlatforms)
{
AssetBuilderSDK::JobDescriptor descriptor;
descriptor.m_jobKey = AnyAssetBuilderJobKey;
descriptor.SetPlatformIdentifier(platformInfo.m_identifier.c_str());
// [GFX TODO][ATOM-2830] Set 'm_critical' back to 'false' once proper fix for Atom startup issues are in
descriptor.m_critical = true;
response.m_createJobOutputs.push_back(descriptor);
}
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
}
void AnyAssetBuilder::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
{
AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId);
if (jobCancelListener.IsCancelled() || m_isShuttingDown)
{
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
return;
}
// Get serialization context
SerializeContext* context = nullptr;
ComponentApplicationBus::BroadcastResult(context, &ComponentApplicationBus::Events::GetSerializeContext);
if (!context)
{
AZ_Assert(false, "No serialize context");
return;
}
Outcome<AZStd::any, AZStd::string> loadResult = AZ::JsonSerializationUtils::LoadAnyObjectFromFile(request.m_fullPath);
if (!loadResult.IsSuccess())
{
AZ_Error(AnyAssetBuilderName, false, "Failed to load file [%s] as an any asset", request.m_fullPath.c_str());
AZ_Error(AnyAssetBuilderName, false, "Loading issues: %s", loadResult.GetError().data());
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
return ;
}
AZStd::any loadedClass = loadResult.GetValue();
auto loadedClassId = loadedClass.get_type_info().m_id;
void* loadedInstance = AZStd::any_cast<void>(&loadedClass);
// Apply converter if the source data class is a ConvertibleSource
bool isConvertible = false;
context->EnumerateBase(
[&isConvertible](const AZ::SerializeContext::ClassData* classData, AZ::Uuid)
{
if (classData && classData->m_typeId == ConvertibleSource::TYPEINFO_Uuid())
{
isConvertible = true;
}
return true;
},
loadedClassId);
AZStd::shared_ptr<void> convertedData;
TypeId outputTypeId = loadedClassId;
void* outputData = loadedInstance;
if (isConvertible)
{
const ConvertibleSource* convertible = reinterpret_cast<const ConvertibleSource*>(loadedInstance);
// convert and save the converted data to outputData and its type id to outputTypeId
bool converted = convertible->Convert(outputTypeId, convertedData);
if (!converted)
{
AZ_Error(AnyAssetBuilderName, false, "Failed to convert asset [%s]", request.m_fullPath.c_str());
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
return;
}
outputData = convertedData.get();
}
// Get file name from source file path, then replace the extension to generate product file name
// Note that we preserve the file extension since it is used during reflection
AZStd::string destFileName;
AzFramework::StringFunc::Path::GetFullFileName(request.m_fullPath.c_str(), destFileName);
// Construct product full path
AZStd::string destPath;
AzFramework::StringFunc::Path::ConstructFull(request.m_tempDirPath.c_str(), destFileName.c_str(), destPath, true);
// Save the asset to binary format for production
bool result = Utils::SaveObjectToFile(destPath, DataStream::ST_BINARY, outputData, outputTypeId, context);
if (result == false)
{
AZ_Error(AnyAssetBuilderName, false, "Failed to save asset to %s", destPath.c_str());
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
return;
}
// Success. Save output product(s) to response.
// Note that we use the classId we found in the source file, unless it's a standard .azasset
AZStd::string sourceFileExtension;
AzFramework::StringFunc::Path::GetExtension(request.m_fullPath.c_str(), sourceFileExtension, false);
auto destClassId = (sourceFileExtension == AnyAssetBuilderDefaultExtension) ? AnyAsset::RTTI_Type() : loadedClassId;
AssetBuilderSDK::JobProduct jobProduct(destPath, destClassId, 0);
if (AssetBuilderSDK::OutputObject(outputData, outputTypeId, destPath, destClassId, 0, jobProduct))
{
response.m_outputProducts.push_back(jobProduct);
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
}
}
void AnyAssetBuilder::ShutDown()
{
m_isShuttingDown = true;
}
} // namespace RPI_Builder
} // namespace AZ