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.
204 lines
8.7 KiB
C++
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 = 10;
|
|
|
|
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
|