MaterialPropertyUtil.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  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 <AtomToolsFramework/DynamicProperty/DynamicProperty.h>
  9. #include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
  10. #include <Atom/RPI.Edit/Common/AssetUtils.h>
  11. #include <Atom/RPI.Reflect/Image/ImageAsset.h>
  12. #include <Atom/RPI.Reflect/Image/StreamingImageAsset.h>
  13. #include <Atom/RPI.Reflect/Material/MaterialAsset.h>
  14. #include <Atom/RPI.Reflect/Material/MaterialTypeAsset.h>
  15. #include <AzCore/Math/Color.h>
  16. #include <AzCore/Math/Vector2.h>
  17. #include <AzCore/Math/Vector3.h>
  18. #include <AzCore/Math/Vector4.h>
  19. #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
  20. #include <AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.h>
  21. namespace AtomToolsFramework
  22. {
  23. AZ::RPI::MaterialPropertyValue ConvertToRuntimeType(const AZStd::any& value)
  24. {
  25. return AZ::RPI::MaterialPropertyValue::FromAny(value);
  26. }
  27. AZStd::any ConvertToEditableType(const AZ::RPI::MaterialPropertyValue& value)
  28. {
  29. if (value.Is<AZ::Data::Asset<AZ::RPI::ImageAsset>>())
  30. {
  31. const auto& imageAsset = value.GetValue<AZ::Data::Asset<AZ::RPI::ImageAsset>>();
  32. return AZStd::any(AZ::Data::Asset<AZ::RPI::StreamingImageAsset>(imageAsset.GetId(), azrtti_typeid<AZ::RPI::StreamingImageAsset>(), imageAsset.GetHint()));
  33. }
  34. else if (value.Is<AZ::Data::Instance<AZ::RPI::Image>>())
  35. {
  36. const auto& image = value.GetValue<AZ::Data::Instance<AZ::RPI::Image>>();
  37. return AZStd::any(AZ::Data::Asset<AZ::RPI::StreamingImageAsset>(image->GetAssetId(), azrtti_typeid<AZ::RPI::StreamingImageAsset>()));
  38. }
  39. return AZ::RPI::MaterialPropertyValue::ToAny(value);
  40. }
  41. AtomToolsFramework::DynamicPropertyType ConvertToEditableType(const AZ::RPI::MaterialPropertyDataType dataType)
  42. {
  43. switch (dataType)
  44. {
  45. case AZ::RPI::MaterialPropertyDataType::Bool:
  46. return AtomToolsFramework::DynamicPropertyType::Bool;
  47. case AZ::RPI::MaterialPropertyDataType::Int:
  48. return AtomToolsFramework::DynamicPropertyType::Int;
  49. case AZ::RPI::MaterialPropertyDataType::UInt:
  50. return AtomToolsFramework::DynamicPropertyType::UInt;
  51. case AZ::RPI::MaterialPropertyDataType::Float:
  52. return AtomToolsFramework::DynamicPropertyType::Float;
  53. case AZ::RPI::MaterialPropertyDataType::Vector2:
  54. return AtomToolsFramework::DynamicPropertyType::Vector2;
  55. case AZ::RPI::MaterialPropertyDataType::Vector3:
  56. return AtomToolsFramework::DynamicPropertyType::Vector3;
  57. case AZ::RPI::MaterialPropertyDataType::Vector4:
  58. return AtomToolsFramework::DynamicPropertyType::Vector4;
  59. case AZ::RPI::MaterialPropertyDataType::Color:
  60. return AtomToolsFramework::DynamicPropertyType::Color;
  61. case AZ::RPI::MaterialPropertyDataType::Image:
  62. return AtomToolsFramework::DynamicPropertyType::Asset;
  63. case AZ::RPI::MaterialPropertyDataType::Enum:
  64. return AtomToolsFramework::DynamicPropertyType::Enum;
  65. }
  66. AZ_Assert(false, "Attempting to convert an unsupported property type.");
  67. return AtomToolsFramework::DynamicPropertyType::Invalid;
  68. }
  69. void ConvertToPropertyConfig(AtomToolsFramework::DynamicPropertyConfig& propertyConfig, const AZ::RPI::MaterialTypeSourceData::PropertyDefinition& propertyDefinition)
  70. {
  71. propertyConfig.m_dataType = ConvertToEditableType(propertyDefinition.m_dataType);
  72. propertyConfig.m_name = propertyDefinition.m_name;
  73. propertyConfig.m_displayName = propertyDefinition.m_displayName;
  74. propertyConfig.m_description = propertyDefinition.m_description;
  75. propertyConfig.m_defaultValue = ConvertToEditableType(propertyDefinition.m_value);
  76. propertyConfig.m_min = ConvertToEditableType(propertyDefinition.m_min);
  77. propertyConfig.m_max = ConvertToEditableType(propertyDefinition.m_max);
  78. propertyConfig.m_softMin = ConvertToEditableType(propertyDefinition.m_softMin);
  79. propertyConfig.m_softMax = ConvertToEditableType(propertyDefinition.m_softMax);
  80. propertyConfig.m_step = ConvertToEditableType(propertyDefinition.m_step);
  81. propertyConfig.m_enumValues = propertyDefinition.m_enumValues;
  82. propertyConfig.m_vectorLabels = propertyDefinition.m_vectorLabels;
  83. propertyConfig.m_visible = propertyDefinition.m_visibility != AZ::RPI::MaterialPropertyVisibility::Hidden;
  84. propertyConfig.m_readOnly = propertyDefinition.m_visibility == AZ::RPI::MaterialPropertyVisibility::Disabled;
  85. // Update the description for material properties to include script name assuming id is set beforehand
  86. propertyConfig.m_description = AZStd::string::format(
  87. "%s%s(Script Name = '%s')",
  88. propertyConfig.m_description.c_str(),
  89. propertyConfig.m_description.empty() ? "" : "\n",
  90. propertyConfig.m_id.GetCStr());
  91. }
  92. void ConvertToPropertyConfig(AtomToolsFramework::DynamicPropertyConfig& propertyConfig, const AZ::RPI::MaterialPropertyDynamicMetadata& propertyMetaData)
  93. {
  94. propertyConfig.m_description = propertyMetaData.m_description;
  95. propertyConfig.m_min = ConvertToEditableType(propertyMetaData.m_propertyRange.m_min);
  96. propertyConfig.m_max = ConvertToEditableType(propertyMetaData.m_propertyRange.m_max);
  97. propertyConfig.m_softMin = ConvertToEditableType(propertyMetaData.m_propertyRange.m_softMin);
  98. propertyConfig.m_softMax = ConvertToEditableType(propertyMetaData.m_propertyRange.m_softMax);
  99. propertyConfig.m_visible = propertyMetaData.m_visibility != AZ::RPI::MaterialPropertyVisibility::Hidden;
  100. propertyConfig.m_readOnly = propertyMetaData.m_visibility == AZ::RPI::MaterialPropertyVisibility::Disabled;
  101. }
  102. void ConvertToPropertyMetaData(AZ::RPI::MaterialPropertyDynamicMetadata& propertyMetaData, const AtomToolsFramework::DynamicPropertyConfig& propertyConfig)
  103. {
  104. propertyMetaData.m_description = propertyConfig.m_description;
  105. propertyMetaData.m_propertyRange.m_min = ConvertToRuntimeType(propertyConfig.m_min);
  106. propertyMetaData.m_propertyRange.m_max = ConvertToRuntimeType(propertyConfig.m_max);
  107. propertyMetaData.m_propertyRange.m_softMin = ConvertToRuntimeType(propertyConfig.m_softMin);
  108. propertyMetaData.m_propertyRange.m_softMax = ConvertToRuntimeType(propertyConfig.m_softMax);
  109. if (!propertyConfig.m_visible)
  110. {
  111. propertyMetaData.m_visibility = AZ::RPI::MaterialPropertyVisibility::Hidden;
  112. }
  113. else if (propertyConfig.m_readOnly)
  114. {
  115. propertyMetaData.m_visibility = AZ::RPI::MaterialPropertyVisibility::Disabled;
  116. }
  117. else
  118. {
  119. propertyMetaData.m_visibility = AZ::RPI::MaterialPropertyVisibility::Enabled;
  120. }
  121. }
  122. template<typename T>
  123. bool ComparePropertyValues(const AZStd::any& valueA, const AZStd::any& valueB)
  124. {
  125. return valueA.is<T>() && valueB.is<T>() && *AZStd::any_cast<T>(&valueA) == *AZStd::any_cast<T>(&valueB);
  126. }
  127. bool ArePropertyValuesEqual(const AZStd::any& valueA, const AZStd::any& valueB)
  128. {
  129. if (valueA.type() != valueB.type())
  130. {
  131. return false;
  132. }
  133. if (ComparePropertyValues<bool>(valueA, valueB) ||
  134. ComparePropertyValues<int32_t>(valueA, valueB) ||
  135. ComparePropertyValues<uint32_t>(valueA, valueB) ||
  136. ComparePropertyValues<float>(valueA, valueB) ||
  137. ComparePropertyValues<AZ::Vector2>(valueA, valueB) ||
  138. ComparePropertyValues<AZ::Vector3>(valueA, valueB) ||
  139. ComparePropertyValues<AZ::Vector4>(valueA, valueB) ||
  140. ComparePropertyValues<AZ::Color>(valueA, valueB) ||
  141. ComparePropertyValues<AZ::Data::AssetId>(valueA, valueB) ||
  142. ComparePropertyValues<AZ::Data::Asset<AZ::Data::AssetData>>(valueA, valueB) ||
  143. ComparePropertyValues<AZ::Data::Asset<AZ::RPI::ImageAsset>>(valueA, valueB) ||
  144. ComparePropertyValues<AZ::Data::Asset<AZ::RPI::StreamingImageAsset>>(valueA, valueB) ||
  145. ComparePropertyValues<AZ::Data::Asset<AZ::RPI::MaterialAsset>>(valueA, valueB) ||
  146. ComparePropertyValues<AZ::Data::Asset<AZ::RPI::MaterialTypeAsset>>(valueA, valueB) ||
  147. ComparePropertyValues<AZStd::string>(valueA, valueB))
  148. {
  149. return true;
  150. }
  151. return false;
  152. }
  153. bool ConvertToExportFormat(
  154. const AZStd::string& exportPath,
  155. [[maybe_unused]] const AZ::Name& propertyId,
  156. const AZ::RPI::MaterialTypeSourceData::PropertyDefinition& propertyDefinition,
  157. AZ::RPI::MaterialPropertyValue& propertyValue)
  158. {
  159. if (propertyDefinition.m_dataType == AZ::RPI::MaterialPropertyDataType::Enum && propertyValue.Is<uint32_t>())
  160. {
  161. const uint32_t index = propertyValue.GetValue<uint32_t>();
  162. if (index >= propertyDefinition.m_enumValues.size())
  163. {
  164. AZ_Error("AtomToolsFramework", false, "Invalid value for material enum property: '%s'.", propertyId.GetCStr());
  165. return false;
  166. }
  167. propertyValue = propertyDefinition.m_enumValues[index];
  168. return true;
  169. }
  170. // Image asset references must be converted from asset IDs to a relative source file path
  171. if (propertyDefinition.m_dataType == AZ::RPI::MaterialPropertyDataType::Image)
  172. {
  173. AZStd::string imagePath;
  174. AZ::Data::AssetId imageAssetId;
  175. if (propertyValue.Is<AZ::Data::Asset<AZ::RPI::ImageAsset>>())
  176. {
  177. const auto& imageAsset = propertyValue.GetValue<AZ::Data::Asset<AZ::RPI::ImageAsset>>();
  178. imageAssetId = imageAsset.GetId();
  179. }
  180. if (propertyValue.Is<AZ::Data::Instance<AZ::RPI::Image>>())
  181. {
  182. const auto& image = propertyValue.GetValue<AZ::Data::Instance<AZ::RPI::Image>>();
  183. if (image)
  184. {
  185. imageAssetId = image->GetAssetId();
  186. }
  187. }
  188. imagePath = AZ::RPI::AssetUtils::GetSourcePathByAssetId(imageAssetId);
  189. if (imageAssetId.IsValid() && imagePath.empty())
  190. {
  191. AZ_Error("AtomToolsFramework", false, "Image asset could not be found for property: '%s'.", propertyId.GetCStr());
  192. return false;
  193. }
  194. else
  195. {
  196. propertyValue = GetExteralReferencePath(exportPath, imagePath);
  197. return true;
  198. }
  199. }
  200. return true;
  201. }
  202. AZStd::string GetExteralReferencePath(
  203. const AZStd::string& exportPath, const AZStd::string& referencePath, const bool relativeToExportPath)
  204. {
  205. if (referencePath.empty())
  206. {
  207. return {};
  208. }
  209. if (!relativeToExportPath)
  210. {
  211. AZStd::string watchFolder;
  212. AZ::Data::AssetInfo assetInfo;
  213. bool sourceInfoFound = false;
  214. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
  215. sourceInfoFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, referencePath.c_str(),
  216. assetInfo, watchFolder);
  217. if (sourceInfoFound)
  218. {
  219. return assetInfo.m_relativePath;
  220. }
  221. }
  222. AZ::IO::BasicPath<AZStd::string> exportFolder(exportPath);
  223. exportFolder.RemoveFilename();
  224. return AZ::IO::PathView(referencePath).LexicallyRelative(exportFolder).StringAsPosix();
  225. }
  226. const AtomToolsFramework::DynamicProperty* FindDynamicPropertyForInstanceDataNode(const AzToolsFramework::InstanceDataNode* pNode)
  227. {
  228. // Traverse up the hierarchy from the input node to search for an instance corresponding to material inspector property
  229. for (const AzToolsFramework::InstanceDataNode* currentNode = pNode; currentNode; currentNode = currentNode->GetParent())
  230. {
  231. const AZ::SerializeContext* context = currentNode->GetSerializeContext();
  232. const AZ::SerializeContext::ClassData* classData = currentNode->GetClassMetadata();
  233. if (context && classData)
  234. {
  235. if (context->CanDowncast(
  236. classData->m_typeId, azrtti_typeid<AtomToolsFramework::DynamicProperty>(), classData->m_azRtti, nullptr))
  237. {
  238. return static_cast<const AtomToolsFramework::DynamicProperty*>(currentNode->FirstInstance());
  239. }
  240. }
  241. }
  242. return nullptr;
  243. }
  244. } // namespace AtomToolsFramework