/* * 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 #include #include #include #include #include #include #include #include #include #include #include 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 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(); 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 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(&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 convertedData; TypeId outputTypeId = loadedClassId; void* outputData = loadedInstance; if (isConvertible) { const ConvertibleSource* convertible = reinterpret_cast(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