TextureSettings.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <BuilderSettings/TextureSettings.h>
  9. #include <AzCore/RTTI/ReflectContext.h>
  10. #include <AzCore/Serialization/EditContext.h>
  11. #include <AzFramework/StringFunc/StringFunc.h>
  12. #include <AzCore/IO/FileIO.h>
  13. #include <AzCore/IO/SystemFile.h>
  14. #include <AzCore/Serialization/Utils.h>
  15. #include <BuilderSettings/BuilderSettingManager.h>
  16. #include <ImageLoader/ImageLoaders.h>
  17. #include <Editor/EditorCommon.h>
  18. namespace ImageProcessingAtom
  19. {
  20. const char* TextureSettings::ExtensionName = ".assetinfo";
  21. TextureSettings::TextureSettings()
  22. : m_sizeReduceLevel(0)
  23. , m_suppressEngineReduce(false)
  24. , m_enableMipmap(true)
  25. , m_maintainAlphaCoverage(false)
  26. , m_mipGenEval(MipGenEvalType::sum)
  27. , m_mipGenType(MipGenType::blackmanHarris)
  28. {
  29. const int defaultMipMapValue = 50;
  30. for (int i = 0; i < s_MaxMipMaps; ++i)
  31. {
  32. m_mipAlphaAdjust.push_back(defaultMipMapValue);
  33. }
  34. }
  35. void TextureSettings::Reflect(AZ::ReflectContext* context)
  36. {
  37. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  38. if (serialize)
  39. {
  40. serialize->Class<TextureSettings>()
  41. ->Version(2)
  42. ->Field("PresetID", &TextureSettings::m_presetId)
  43. ->Field("Preset", &TextureSettings::m_preset)
  44. ->Field("SizeReduceLevel", &TextureSettings::m_sizeReduceLevel)
  45. ->Field("EngineReduce", &TextureSettings::m_suppressEngineReduce)
  46. ->Field("EnableMipmap", &TextureSettings::m_enableMipmap)
  47. ->Field("MipMapGenEval", &TextureSettings::m_mipGenEval)
  48. ->Field("MipMapGenType", &TextureSettings::m_mipGenType)
  49. ->Field("MaintainAlphaCoverage", &TextureSettings::m_maintainAlphaCoverage)
  50. ->Field("MipMapAlphaAdjustments", &TextureSettings::m_mipAlphaAdjust)
  51. ->Field("PlatformSpecificOverrides", &TextureSettings::m_platfromOverrides)
  52. ->Field("OverridingPlatform", &TextureSettings::m_overridingPlatform)
  53. ->Field("Tags", &TextureSettings::m_tags);
  54. AZ::EditContext* edit = serialize->GetEditContext();
  55. if (edit)
  56. {
  57. edit->Class<TextureSettings>("Texture Setting", "")
  58. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  59. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  60. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  61. ->DataElement(AZ::Edit::UIHandlers::Default, &TextureSettings::m_mipAlphaAdjust, "Alpha Test Bias", "Multiplies the mipmap's alpha channel by a scale value that is based on alpha coverage. \
  62. Specify a value from 0 to 100 for each mipmap to offset the alpha test values and ensure the mipmap's alpha coverage matches the original image.")
  63. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  64. ->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, false)
  65. ->ElementAttribute(AZ::Edit::UIHandlers::Handler, AZ::Edit::UIHandlers::Slider)
  66. ->ElementAttribute(AZ::Edit::Attributes::Min, 0)
  67. ->ElementAttribute(AZ::Edit::Attributes::Max, 100)
  68. ->ElementAttribute(AZ::Edit::Attributes::Step, 1)
  69. ->DataElement(AZ::Edit::UIHandlers::ComboBox, &TextureSettings::m_mipGenType, "Filter Type", "Filter Types specify sample sizes and algorithms \
  70. for determining the color of each pixel as the texture resolution is reduced for each mipmap.")
  71. ->EnumAttribute(MipGenType::point, "Point")
  72. ->EnumAttribute(MipGenType::box, "Average")
  73. ->EnumAttribute(MipGenType::triangle, "Linear")
  74. ->EnumAttribute(MipGenType::quadratic, "Bilinear")
  75. ->EnumAttribute(MipGenType::gaussian, "Gaussian")
  76. ->EnumAttribute(MipGenType::blackmanHarris, "BlackmanHarris")
  77. ->EnumAttribute(MipGenType::kaiserSinc, "KaiserSinc")
  78. ->DataElement(AZ::Edit::UIHandlers::ComboBox, &TextureSettings::m_mipGenEval, "Pixel Sampler", "The Pixel Sampler specifies how the final pixel value is calculated when mipmaps are generated.")
  79. ->EnumAttribute(MipGenEvalType::max, "Max")
  80. ->EnumAttribute(MipGenEvalType::min, "Min")
  81. ->EnumAttribute(MipGenEvalType::sum, "Sum")
  82. ->DataElement(AZ::Edit::UIHandlers::CheckBox, &TextureSettings::m_maintainAlphaCoverage, "Adjust Alpha", "Enable to manually adjust the alpha channel of the mipmaps with the Alpha Test Bias values.")
  83. ;
  84. }
  85. }
  86. }
  87. bool TextureSettings::operator!=(const TextureSettings& other) const
  88. {
  89. return !(*this == other);
  90. }
  91. bool TextureSettings::operator==(const TextureSettings& other) const
  92. {
  93. /////////////////////////////
  94. // Compare Alpha Adjust
  95. /////////////////////////////
  96. bool matchingAlphaTestAdjust = true;
  97. for (AZ::u8 curIndex = 0; curIndex < s_MaxMipMaps; ++curIndex)
  98. {
  99. if (m_mipAlphaAdjust[curIndex] != other.m_mipAlphaAdjust[curIndex])
  100. {
  101. matchingAlphaTestAdjust = false;
  102. break;
  103. }
  104. }
  105. return
  106. matchingAlphaTestAdjust &&
  107. m_preset == other.m_preset &&
  108. m_sizeReduceLevel == other.m_sizeReduceLevel &&
  109. m_suppressEngineReduce == other.m_suppressEngineReduce &&
  110. m_maintainAlphaCoverage == other.m_maintainAlphaCoverage &&
  111. m_mipGenEval == other.m_mipGenEval &&
  112. m_mipGenType == other.m_mipGenType &&
  113. m_tags == other.m_tags;
  114. }
  115. bool TextureSettings::Equals(const TextureSettings& other, AZ::SerializeContext* serializeContext)
  116. {
  117. /////////////////////////////
  118. // Compare Common Settings
  119. /////////////////////////////
  120. if (*this != other)
  121. {
  122. return false;
  123. }
  124. /////////////////////////////
  125. // Compare Overrides
  126. /////////////////////////////
  127. const MultiplatformTextureSettings selfOverrides = GetMultiplatformTextureSetting(*this, serializeContext);
  128. const MultiplatformTextureSettings otherOverrides = GetMultiplatformTextureSetting(other, serializeContext);
  129. auto selfOverridesIter = selfOverrides.begin();
  130. auto otherOverridesIter = otherOverrides.begin();
  131. while (selfOverridesIter != selfOverrides.end() && otherOverridesIter != otherOverrides.end())
  132. {
  133. if (selfOverridesIter->second != otherOverridesIter->second)
  134. {
  135. return false;
  136. }
  137. otherOverridesIter++;
  138. selfOverridesIter++;
  139. }
  140. AZ_Assert(selfOverridesIter == selfOverrides.end() && otherOverridesIter == otherOverrides.end(), "Both iterators must be at the end by now.")
  141. return true;
  142. }
  143. float TextureSettings::ComputeMIPAlphaOffset(AZ::u32 mip) const
  144. {
  145. if (mip / 2 + 1 >= s_MaxMipMaps)
  146. {
  147. return 0;
  148. }
  149. float fVal = static_cast<float>(m_mipAlphaAdjust[s_MaxMipMaps - 1]);
  150. if (mip / 2 + 1 < s_MaxMipMaps)
  151. {
  152. float fInterpolationSlider1 = static_cast<float>(m_mipAlphaAdjust[mip / 2]);
  153. float fInterpolationSlider2 = static_cast<float>(m_mipAlphaAdjust[mip / 2 + 1]);
  154. fVal = fInterpolationSlider1 + (fInterpolationSlider2 - fInterpolationSlider1) * (mip & 1) * 0.5f;
  155. }
  156. return 0.5f - fVal / 100.0f;
  157. }
  158. void TextureSettings::ApplyPreset(PresetName presetName)
  159. {
  160. const PresetSettings* presetSetting = BuilderSettingManager::Instance()->GetPreset(presetName);
  161. if (presetSetting != nullptr)
  162. {
  163. m_sizeReduceLevel = presetSetting->m_sizeReduceLevel;
  164. m_suppressEngineReduce = presetSetting->m_suppressEngineReduce;
  165. if (presetSetting->m_mipmapSetting)
  166. {
  167. m_mipGenType = presetSetting->m_mipmapSetting->m_type;
  168. }
  169. m_preset = presetName;
  170. }
  171. else
  172. {
  173. AZ_Error("Image Processing", false, "Cannot set an invalid preset %s!", presetName.GetCStr());
  174. }
  175. }
  176. StringOutcome TextureSettings::LoadTextureSetting(const AZStd::string& filepath, TextureSettings& textureSettingPtrOut, AZ::SerializeContext* serializeContext /*= nullptr*/)
  177. {
  178. auto loadedTextureSettingPtr = AZStd::unique_ptr<TextureSettings>(AZ::Utils::LoadObjectFromFile<TextureSettings>(filepath, serializeContext));
  179. if (!loadedTextureSettingPtr)
  180. {
  181. return AZ::Failure(AZStd::string());
  182. }
  183. textureSettingPtrOut = *loadedTextureSettingPtr;
  184. // In old format, the preset name doesn't exist. Using preset id to get preset name
  185. // We can remove this when we fully deprecate the preset uuid
  186. if (textureSettingPtrOut.m_preset.IsEmpty())
  187. {
  188. textureSettingPtrOut.m_preset = BuilderSettingManager::Instance()->GetPresetNameFromId(textureSettingPtrOut.m_presetId);
  189. }
  190. return AZ::Success(AZStd::string());
  191. }
  192. StringOutcome TextureSettings::WriteTextureSetting(const AZStd::string& filepath, TextureSettings& textureSetting, AZ::SerializeContext* serializeContext)
  193. {
  194. if (false == AZ::Utils::SaveObjectToFile<TextureSettings>(filepath, AZ::DataStream::StreamType::ST_XML, &textureSetting, serializeContext))
  195. {
  196. return STRING_OUTCOME_ERROR("Failed to write to file: " + filepath);
  197. }
  198. return STRING_OUTCOME_SUCCESS;
  199. }
  200. MultiplatformTextureSettings TextureSettings::GenerateDefaultMultiplatformTextureSettings(const AZStd::string& imageFilepath)
  201. {
  202. MultiplatformTextureSettings settings;
  203. PlatformNameList platformsList = BuilderSettingManager::Instance()->GetPlatformList();
  204. PresetName suggestedPreset = BuilderSettingManager::Instance()->GetSuggestedPreset(imageFilepath);
  205. // If the suggested preset doesn't exist (or was failed to be loaded), return empty texture settings
  206. if (BuilderSettingManager::Instance()->GetPreset(suggestedPreset) == nullptr)
  207. {
  208. AZ_Error("Image Processing", false, "Failed to find suggested preset [%s]", suggestedPreset.GetCStr());
  209. return settings;
  210. }
  211. for (PlatformName& platform : platformsList)
  212. {
  213. TextureSettings textureSettings;
  214. textureSettings.ApplyPreset(suggestedPreset);
  215. settings.insert(AZStd::pair<PlatformName, TextureSettings>(platform, textureSettings));
  216. }
  217. return settings;
  218. }
  219. StringOutcome TextureSettings::GetPlatformSpecificTextureSetting(const PlatformName& platformName, const TextureSettings& baseTextureSettings,
  220. TextureSettings& textureSettingsOut, AZ::SerializeContext* serializeContext)
  221. {
  222. // Obtain the DataPatch (if platform exists)
  223. auto overrideIter = baseTextureSettings.m_platfromOverrides.find(platformName);
  224. if (overrideIter == baseTextureSettings.m_platfromOverrides.end())
  225. {
  226. return STRING_OUTCOME_ERROR(AZStd::string::format("TextureSettings preset [%s] does not have override for platform [%s]",
  227. baseTextureSettings.m_preset.GetCStr(), platformName.c_str()));
  228. }
  229. AZ::DataPatch& platformOverride = const_cast<AZ::DataPatch&>(overrideIter->second);
  230. // Update settings instance with override values.
  231. if (platformOverride.IsData())
  232. {
  233. // Apply the AZ::DataPatch to obtain a platform-overridden version of the TextureSettings.
  234. AZStd::unique_ptr<TextureSettings> platformSpecificTextureSettings(platformOverride.Apply(&baseTextureSettings, serializeContext));
  235. AZ_Assert(platformSpecificTextureSettings->m_mipAlphaAdjust.size() == s_MaxMipMaps, "Unexpected m_mipAlphaAdjust size.");
  236. // Adjust overrides data to imply 'platformSpecificTextureSettings' *IS* the override.
  237. platformSpecificTextureSettings->m_platfromOverrides.clear();
  238. platformSpecificTextureSettings->m_overridingPlatform = platformName;
  239. textureSettingsOut = *platformSpecificTextureSettings;
  240. }
  241. else
  242. {
  243. textureSettingsOut = baseTextureSettings;
  244. }
  245. return STRING_OUTCOME_SUCCESS;
  246. }
  247. const MultiplatformTextureSettings TextureSettings::GetMultiplatformTextureSetting(const TextureSettings& textureSettings, AZ::SerializeContext* serializeContext)
  248. {
  249. MultiplatformTextureSettings loadedSettingsReturn;
  250. PlatformNameList platformsList = BuilderSettingManager::Instance()->GetPlatformList();
  251. // Generate MultiplatformTextureSettings based on existing available overrides.
  252. for (const PlatformName& curPlatformName : platformsList)
  253. {
  254. // Start with a copy of the base settings
  255. TextureSettings curPlatformOverride = textureSettings;
  256. if (!GetPlatformSpecificTextureSetting(curPlatformName, textureSettings, curPlatformOverride, serializeContext).IsSuccess())
  257. {
  258. // We have failed to obtain an override. Maintain base settings to indicate zero overrides.
  259. // We still want to designate these TextureSettings as an (empty) override.
  260. curPlatformOverride.m_platfromOverrides.clear();
  261. curPlatformOverride.m_overridingPlatform = curPlatformName;
  262. }
  263. // Add as an entry to the multiplatform texture settings
  264. loadedSettingsReturn.insert(AZStd::pair<PlatformName, TextureSettings>(curPlatformName, curPlatformOverride));
  265. }
  266. // return a copy of the results
  267. return loadedSettingsReturn;
  268. }
  269. const MultiplatformTextureSettings TextureSettings::GetMultiplatformTextureSetting(const AZStd::string& imageFilepath, bool& canOverridePreset, AZ::SerializeContext* serializeContext)
  270. {
  271. TextureSettings loadedTextureSetting;
  272. // Attempt to get metadata file path from image path.
  273. AZStd::string metadataFilepath = imageFilepath + TextureSettings::ExtensionName;
  274. bool hasMetafile = AZ::IO::SystemFile::Exists(metadataFilepath.c_str());
  275. canOverridePreset = true;
  276. // If the image has an accompanying metadata...
  277. if(hasMetafile)
  278. {
  279. // Parse the metadata file.
  280. if (LoadTextureSetting(metadataFilepath, loadedTextureSetting, serializeContext).IsSuccess())
  281. {
  282. canOverridePreset = false;
  283. return GetMultiplatformTextureSetting(loadedTextureSetting, serializeContext);
  284. }
  285. else
  286. {
  287. AZ_Error("Image processing", false, "Failed to load the image's meta file %s", metadataFilepath.c_str());
  288. }
  289. }
  290. return GenerateDefaultMultiplatformTextureSettings(imageFilepath);
  291. }
  292. StringOutcome TextureSettings::ApplySettings(const TextureSettings& settings, const PlatformName& overridePlatform, AZ::SerializeContext* serializeContext)
  293. {
  294. if (overridePlatform.empty())
  295. {
  296. *this = settings;
  297. }
  298. else
  299. {
  300. AZ::DataPatch newOverride;
  301. if (false == newOverride.Create<TextureSettings, TextureSettings>(this, &settings, AZ::DataPatch::FlagsMap(), AZ::DataPatch::FlagsMap(), serializeContext))
  302. {
  303. return STRING_OUTCOME_ERROR("Failed to create TextureSettings platform override data. See AZ_Error log for details.");
  304. }
  305. m_platfromOverrides[overridePlatform] = newOverride;
  306. }
  307. return STRING_OUTCOME_SUCCESS;
  308. }
  309. }