/* * 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 #include #include #include namespace AZ { namespace RPI { namespace MaterialUtils { GetImageAssetResult GetImageAssetReference(Data::Asset& imageAsset, AZStd::string_view materialSourceFilePath, const AZStd::string imageFilePath) { imageAsset = {}; if (imageFilePath.empty()) { // The image value was present but specified an empty string, meaning the texture asset should be explicitly cleared. return GetImageAssetResult::Empty; } else { // We use TraceLevel::None because fallback textures are available and we'll return GetImageAssetResult::Missing below in that case. // Callers of GetImageAssetReference will be responsible for logging warnings or errors as needed. Outcome imageAssetId = AssetUtils::MakeAssetId(materialSourceFilePath, imageFilePath, StreamingImageAsset::GetImageAssetSubId(), AssetUtils::TraceLevel::None); if (!imageAssetId.IsSuccess()) { // When the AssetId cannot be found, we don't want to outright fail, because the runtime has mechanisms for displaying fallback textures which gives the // user a better recovery workflow. On the other hand we can't just provide an empty/invalid Asset because that would be interpreted as simply // no value was present and result in using no texture, and this would amount to a silent failure. // So we use a randomly generated (well except for the "BADA55E7" bit ;) UUID which the runtime and tools will interpret as a missing asset and represent // it as such. static const Uuid InvalidAssetPlaceholderId = "{BADA55E7-1A1D-4940-B655-9D08679BD62F}"; imageAsset = Data::Asset{InvalidAssetPlaceholderId, azrtti_typeid(), imageFilePath}; return GetImageAssetResult::Missing; } imageAsset = Data::Asset{imageAssetId.GetValue(), azrtti_typeid(), imageFilePath}; return GetImageAssetResult::Found; } } bool ResolveMaterialPropertyEnumValue(const MaterialPropertyDescriptor* propertyDescriptor, const AZ::Name& enumName, MaterialPropertyValue& outResolvedValue) { uint32_t enumValue = propertyDescriptor->GetEnumValue(enumName); if (enumValue == MaterialPropertyDescriptor::InvalidEnumValue) { AZ_Error("Material", false, "Enum name \"%s\" can't be found in property \"%s\".", enumName.GetCStr(), propertyDescriptor->GetName().GetCStr()); return false; } outResolvedValue = enumValue; return true; } AZ::Outcome LoadMaterialTypeSourceData(const AZStd::string& filePath, rapidjson::Document* document, ImportedJsonFiles* importedFiles) { rapidjson::Document localDocument; if (document == nullptr) { AZ::Outcome loadOutcome = AZ::JsonSerializationUtils::ReadJsonFile(filePath, AZ::RPI::JsonUtils::DefaultMaxFileSize); if (!loadOutcome.IsSuccess()) { AZ_Error("AZ::RPI::JsonUtils", false, "%s", loadOutcome.GetError().c_str()); return AZ::Failure(); } localDocument = loadOutcome.TakeValue(); document = &localDocument; } AZ::BaseJsonImporter jsonImporter; AZ::JsonImportSettings importSettings; importSettings.m_importer = &jsonImporter; importSettings.m_loadedJsonPath = filePath; AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::ResolveImports(document->GetObject(), document->GetAllocator(), importSettings); if (importedFiles) { *importedFiles = importSettings.m_importer->GetImportedFiles(); } MaterialTypeSourceData materialType; JsonDeserializerSettings settings; JsonReportingHelper reportingHelper; reportingHelper.Attach(settings); JsonSerialization::Load(materialType, *document, settings); materialType.ConvertToNewDataFormat(); materialType.ResolveUvEnums(); if (reportingHelper.ErrorsReported()) { return AZ::Failure(); } else { return AZ::Success(AZStd::move(materialType)); } } void CheckForUnrecognizedJsonFields(const AZStd::string_view* acceptedFieldNames, uint32_t acceptedFieldNameCount, const rapidjson::Value& object, JsonDeserializerContext& context, JsonSerializationResult::ResultCode &result) { for (auto iter = object.MemberBegin(); iter != object.MemberEnd(); ++iter) { bool matched = false; for (uint32_t i = 0; i < acceptedFieldNameCount; ++i) { if (iter->name.GetString() == acceptedFieldNames[i]) { matched = true; break; } } if (!matched) { ScopedContextPath subPath{context, iter->name.GetString()}; result.Combine(context.Report(JsonSerializationResult::Tasks::ReadField, JsonSerializationResult::Outcomes::Skipped, "Skipping unrecognized field")); } } } bool BuildersShouldFinalizeMaterialAssets() { // We default to the faster workflow for developers. Enable this registry setting when releasing the // game for faster load times and obfuscation of material assets. bool shouldFinalize = false; if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) { settingsRegistry->Get(shouldFinalize, "/O3DE/Atom/RPI/MaterialBuilder/FinalizeMaterialAssets"); } return shouldFinalize; } } } }