3
0

MaterialPropertySerializer.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  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 <Atom/RPI.Edit/Material/MaterialPropertySerializer.h>
  9. #include <Atom/RPI.Edit/Material/MaterialPropertyId.h>
  10. #include <Atom/RPI.Edit/Material/MaterialUtils.h>
  11. #include <Atom/RPI.Edit/Material/MaterialPropertySourceData.h>
  12. #include <AzCore/Serialization/Json/BaseJsonSerializer.h>
  13. #include <AzCore/Serialization/Json/JsonSerializationResult.h>
  14. #include <AzCore/Serialization/Json/JsonSerialization.h>
  15. #include <AzCore/Serialization/Json/StackedString.h>
  16. #include <AzCore/Math/Color.h>
  17. #include <AzCore/Math/Vector2.h>
  18. #include <AzCore/Math/Vector3.h>
  19. #include <AzCore/Math/Vector4.h>
  20. namespace AZ
  21. {
  22. namespace RPI
  23. {
  24. namespace JsonMaterialPropertySerializerInternal
  25. {
  26. namespace Field
  27. {
  28. static constexpr const char name[] = "name";
  29. static constexpr const char id[] = "id"; // For backward compatibility
  30. static constexpr const char displayName[] = "displayName";
  31. static constexpr const char description[] = "description";
  32. static constexpr const char type[] = "type";
  33. static constexpr const char visibility[] = "visibility";
  34. static constexpr const char defaultValue[] = "defaultValue";
  35. static constexpr const char min[] = "min";
  36. static constexpr const char max[] = "max";
  37. static constexpr const char softMin[] = "softMin";
  38. static constexpr const char softMax[] = "softMax";
  39. static constexpr const char step[] = "step";
  40. static constexpr const char connection[] = "connection";
  41. static constexpr const char enumValues[] = "enumValues";
  42. static constexpr const char enumIsUv[] = "enumIsUv";
  43. static constexpr const char vectorLabels[] = "vectorLabels";
  44. }
  45. static const AZStd::string_view AcceptedFields[] =
  46. {
  47. Field::name,
  48. Field::id,
  49. Field::displayName,
  50. Field::description,
  51. Field::type,
  52. Field::visibility,
  53. Field::defaultValue,
  54. Field::min,
  55. Field::max,
  56. Field::softMin,
  57. Field::softMax,
  58. Field::step,
  59. Field::connection,
  60. Field::enumValues,
  61. Field::enumIsUv,
  62. Field::vectorLabels
  63. };
  64. }
  65. AZ_CLASS_ALLOCATOR_IMPL(JsonMaterialPropertySerializer, SystemAllocator);
  66. template<typename T>
  67. JsonSerializationResult::ResultCode JsonMaterialPropertySerializer::LoadVariant(
  68. MaterialPropertyValue& intoValue,
  69. const rapidjson::Value& inputValue,
  70. JsonDeserializerContext& context)
  71. {
  72. T value;
  73. JsonSerializationResult::ResultCode result = ContinueLoading(&value, azrtti_typeid<T>(), inputValue, context);
  74. if (result.GetOutcome() == JsonSerializationResult::Outcomes::Success)
  75. {
  76. intoValue = value;
  77. }
  78. return result;
  79. }
  80. template<typename T>
  81. JsonSerializationResult::ResultCode JsonMaterialPropertySerializer::LoadVariant(
  82. MaterialPropertyValue& intoValue,
  83. const T& defaultValue,
  84. const rapidjson::Value& inputValue,
  85. JsonDeserializerContext& context)
  86. {
  87. T value = defaultValue;
  88. JsonSerializationResult::ResultCode result = ContinueLoading(&value, azrtti_typeid<T>(), inputValue, context);
  89. intoValue = value;
  90. return result;
  91. }
  92. template<typename T>
  93. JsonSerializationResult::ResultCode JsonMaterialPropertySerializer::LoadNumericValues(
  94. MaterialPropertySourceData* intoProperty,
  95. const T& defaultValue,
  96. const rapidjson::Value& inputValue,
  97. JsonDeserializerContext& context)
  98. {
  99. namespace JSR = JsonSerializationResult;
  100. using namespace JsonMaterialPropertySerializerInternal;
  101. JSR::ResultCode result(JSR::Tasks::ReadField);
  102. if (inputValue.HasMember(Field::defaultValue))
  103. {
  104. ScopedContextPath subPath{context, Field::defaultValue};
  105. result.Combine(LoadVariant<T>(intoProperty->m_value, defaultValue, inputValue[Field::defaultValue], context));
  106. }
  107. else
  108. {
  109. intoProperty->m_value = defaultValue;
  110. result.Combine(JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::PartialDefaults));
  111. }
  112. // The following do not report PartialDefault because when these are omitted/null the data in the property will also be null
  113. if (inputValue.HasMember(Field::min))
  114. {
  115. ScopedContextPath subPath{context, Field::min};
  116. result.Combine(LoadVariant<T>(intoProperty->m_min, inputValue[Field::min], context));
  117. }
  118. if (inputValue.HasMember(Field::max))
  119. {
  120. ScopedContextPath subPath{context, Field::max};
  121. result.Combine(LoadVariant<T>(intoProperty->m_max, inputValue[Field::max], context));
  122. }
  123. if (inputValue.HasMember(Field::softMin))
  124. {
  125. ScopedContextPath subPath{ context, Field::softMin };
  126. result.Combine(LoadVariant<T>(intoProperty->m_softMin, inputValue[Field::softMin], context));
  127. }
  128. if (inputValue.HasMember(Field::softMax))
  129. {
  130. ScopedContextPath subPath{ context, Field::softMax };
  131. result.Combine(LoadVariant<T>(intoProperty->m_softMax, inputValue[Field::softMax], context));
  132. }
  133. if (inputValue.HasMember(Field::step))
  134. {
  135. ScopedContextPath subPath{context, Field::step};
  136. result.Combine(LoadVariant<T>(intoProperty->m_step, inputValue[Field::step], context));
  137. }
  138. return result;
  139. }
  140. template<typename T>
  141. JsonSerializationResult::ResultCode JsonMaterialPropertySerializer::LoadNonNumericValues(
  142. MaterialPropertySourceData* intoProperty,
  143. const T& defaultValue,
  144. const rapidjson::Value& inputValue,
  145. JsonDeserializerContext& context)
  146. {
  147. namespace JSR = JsonSerializationResult;
  148. using namespace JsonMaterialPropertySerializerInternal;
  149. JSR::ResultCode result(JSR::Tasks::ReadField);
  150. if (inputValue.HasMember(Field::defaultValue))
  151. {
  152. ScopedContextPath subPath{context, Field::defaultValue};
  153. result.Combine(LoadVariant<T>(intoProperty->m_value, defaultValue, inputValue[Field::defaultValue], context));
  154. }
  155. else
  156. {
  157. intoProperty->m_value = defaultValue;
  158. result.Combine(JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::PartialDefaults));
  159. }
  160. return result;
  161. }
  162. JsonSerializationResult::Result JsonMaterialPropertySerializer::Load(void* outputValue, const Uuid& outputValueTypeId,
  163. const rapidjson::Value& inputValue, JsonDeserializerContext& context)
  164. {
  165. namespace JSR = JsonSerializationResult;
  166. using namespace JsonMaterialPropertySerializerInternal;
  167. AZ_Assert(azrtti_typeid<MaterialPropertySourceData>() == outputValueTypeId,
  168. "Unable to deserialize material property to json because the provided type is %s",
  169. outputValueTypeId.ToString<AZStd::string>().c_str());
  170. AZ_UNUSED(outputValueTypeId);
  171. MaterialPropertySourceData* property = reinterpret_cast<MaterialPropertySourceData*>(outputValue);
  172. AZ_Assert(property, "Output value for JsonMaterialPropertySerializer can't be null.");
  173. JSR::ResultCode result(JSR::Tasks::ReadField);
  174. if (!inputValue.IsObject())
  175. {
  176. return context.Report(JsonSerializationResult::Tasks::ReadField, JsonSerializationResult::Outcomes::Unsupported, "Property definition must be a JSON object.");
  177. }
  178. MaterialUtils::CheckForUnrecognizedJsonFields(AcceptedFields, AZ_ARRAY_SIZE(AcceptedFields), inputValue, context, result);
  179. JsonSerializationResult::ResultCode nameResult = ContinueLoadingFromJsonObjectField(&property->m_name, azrtti_typeid<AZStd::string>(), inputValue, Field::name, context);
  180. if (nameResult.GetOutcome() == JsonSerializationResult::Outcomes::DefaultsUsed)
  181. {
  182. // This "id" key is for backward compatibility.
  183. result.Combine(ContinueLoadingFromJsonObjectField(&property->m_name, azrtti_typeid<AZStd::string>(), inputValue, Field::id, context));
  184. }
  185. else
  186. {
  187. result.Combine(nameResult);
  188. }
  189. result.Combine(ContinueLoadingFromJsonObjectField(&property->m_displayName, azrtti_typeid<AZStd::string>(), inputValue, Field::displayName, context));
  190. result.Combine(ContinueLoadingFromJsonObjectField(&property->m_description, azrtti_typeid<AZStd::string>(), inputValue, Field::description, context));
  191. result.Combine(ContinueLoadingFromJsonObjectField(&property->m_dataType, azrtti_typeid<MaterialPropertyDataType>(), inputValue, Field::type, context));
  192. switch (property->m_dataType)
  193. {
  194. case MaterialPropertyDataType::Bool:
  195. result.Combine(LoadNonNumericValues<bool>(property, false, inputValue, context));
  196. break;
  197. case MaterialPropertyDataType::Int:
  198. result.Combine(LoadNumericValues<int32_t>(property, 0, inputValue, context));
  199. break;
  200. case MaterialPropertyDataType::UInt:
  201. result.Combine(LoadNumericValues<uint32_t>(property, 0u, inputValue, context));
  202. break;
  203. case MaterialPropertyDataType::Float:
  204. result.Combine(LoadNumericValues<float>(property, 0.0f, inputValue, context));
  205. break;
  206. case MaterialPropertyDataType::Vector2:
  207. result.Combine(LoadNonNumericValues<Vector2>(property, Vector2{0.0f, 0.0f}, inputValue, context));
  208. result.Combine(LoadVectorLabels(property, inputValue, context));
  209. break;
  210. case MaterialPropertyDataType::Vector3:
  211. result.Combine(LoadNonNumericValues<Vector3>(property, Vector3{0.0f, 0.0f, 0.0f}, inputValue, context));
  212. result.Combine(LoadVectorLabels(property, inputValue, context));
  213. break;
  214. case MaterialPropertyDataType::Vector4:
  215. result.Combine(LoadNonNumericValues<Vector4>(property, Vector4{0.0f, 0.0f, 0.0f, 0.0f}, inputValue, context));
  216. result.Combine(LoadVectorLabels(property, inputValue, context));
  217. break;
  218. case MaterialPropertyDataType::Color:
  219. result.Combine(LoadNonNumericValues<Color>(property, AZ::Colors::White, inputValue, context));
  220. break;
  221. case MaterialPropertyDataType::Image:
  222. case MaterialPropertyDataType::Enum:
  223. result.Combine(LoadNonNumericValues<AZStd::string>(property, "", inputValue, context));
  224. default:
  225. result.Combine(JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::Skipped));
  226. break;
  227. }
  228. result.Combine(ContinueLoadingFromJsonObjectField(&property->m_visibility, azrtti_typeid<MaterialPropertyVisibility>(), inputValue, Field::visibility, context));
  229. if (inputValue.HasMember(Field::connection))
  230. {
  231. ScopedContextPath subPath{context, Field::connection};
  232. if (inputValue[Field::connection].IsArray())
  233. {
  234. result.Combine(ContinueLoading(&property->m_outputConnections, azrtti_typeid(property->m_outputConnections), inputValue[Field::connection], context));
  235. }
  236. else
  237. {
  238. property->m_outputConnections.emplace_back();
  239. result.Combine(ContinueLoading(&property->m_outputConnections.back(), azrtti_typeid(property->m_outputConnections.back()), inputValue[Field::connection], context));
  240. }
  241. }
  242. if (inputValue.HasMember(Field::enumValues))
  243. {
  244. result.Combine(ContinueLoading(&property->m_enumValues, azrtti_typeid(property->m_enumValues), inputValue[Field::enumValues], context));
  245. }
  246. result.Combine(ContinueLoadingFromJsonObjectField(&property->m_enumIsUv, azrtti_typeid<bool>(), inputValue, Field::enumIsUv, context));
  247. if (result.GetProcessing() == JsonSerializationResult::Processing::Completed)
  248. {
  249. return context.Report(result, "Successfully loaded property definition.");
  250. }
  251. else
  252. {
  253. return context.Report(result, "Partially loaded property definition.");
  254. }
  255. }
  256. template<typename T>
  257. JsonSerializationResult::ResultCode JsonMaterialPropertySerializer::StoreNumericValues(
  258. rapidjson::Value& outputValue,
  259. const MaterialPropertySourceData* property,
  260. const T& defaultValue,
  261. JsonSerializerContext& context)
  262. {
  263. namespace JSR = JsonSerializationResult;
  264. using namespace JsonMaterialPropertySerializerInternal;
  265. JSR::ResultCode result(JSR::Tasks::WriteValue);
  266. if (property->m_value.Is<T>())
  267. {
  268. result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::defaultValue, &property->m_value.GetValue<T>(), &defaultValue, azrtti_typeid<T>(), context));
  269. }
  270. if (property->m_min.Is<T>())
  271. {
  272. result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::min, &property->m_min.GetValue<T>(), nullptr, azrtti_typeid<T>(), context));
  273. }
  274. if (property->m_max.Is<T>())
  275. {
  276. result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::max, &property->m_max.GetValue<T>(), nullptr, azrtti_typeid<T>(), context));
  277. }
  278. if (property->m_softMin.Is<T>())
  279. {
  280. result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::softMin, &property->m_softMin.GetValue<T>(), nullptr, azrtti_typeid<T>(), context));
  281. }
  282. if (property->m_softMax.Is<T>())
  283. {
  284. result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::softMax, &property->m_softMax.GetValue<T>(), nullptr, azrtti_typeid<T>(), context));
  285. }
  286. if (property->m_step.Is<T>())
  287. {
  288. result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::step, &property->m_step.GetValue<T>(), nullptr, azrtti_typeid<T>(), context));
  289. }
  290. return result;
  291. }
  292. template<typename T>
  293. JsonSerializationResult::ResultCode JsonMaterialPropertySerializer::StoreNonNumericValues(
  294. rapidjson::Value& outputValue,
  295. const MaterialPropertySourceData* property,
  296. const T& defaultValue,
  297. JsonSerializerContext& context)
  298. {
  299. namespace JSR = JsonSerializationResult;
  300. using namespace JsonMaterialPropertySerializerInternal;
  301. JsonSerializationResult::ResultCode result(JSR::Tasks::WriteValue);
  302. if (property->m_value.Is<T>())
  303. {
  304. result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::defaultValue, &property->m_value.GetValue<T>(), &defaultValue, azrtti_typeid<T>(), context));
  305. }
  306. return result;
  307. }
  308. JsonSerializationResult::Result JsonMaterialPropertySerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
  309. [[maybe_unused]] const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context)
  310. {
  311. namespace JSR = JsonSerializationResult;
  312. using namespace JsonMaterialPropertySerializerInternal;
  313. AZ_Assert(azrtti_typeid<MaterialPropertySourceData>() == valueTypeId,
  314. "Unable to serialize material property to json because the provided type is %s",
  315. valueTypeId.ToString<AZStd::string>().c_str());
  316. AZ_UNUSED(valueTypeId);
  317. const MaterialPropertySourceData* property = reinterpret_cast<const MaterialPropertySourceData*>(inputValue);
  318. AZ_Assert(property, "Input value for JsonMaterialPropertySerializer can't be null.");
  319. JSR::ResultCode result(JSR::Tasks::WriteValue);
  320. outputValue.SetObject();
  321. const AZStd::string emptyString;
  322. result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::name, &property->m_name, &emptyString, azrtti_typeid<AZStd::string>(), context));
  323. result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::displayName, &property->m_displayName, &emptyString, azrtti_typeid<AZStd::string>(), context));
  324. result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::description, &property->m_description, &emptyString, azrtti_typeid<AZStd::string>(), context));
  325. MaterialPropertyDataType defaultDataType = MaterialPropertyDataType::Invalid;
  326. result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::type, &property->m_dataType, &defaultDataType, azrtti_typeid(property->m_dataType), context));
  327. result.Combine(StoreVectorLabels(outputValue, property, context));
  328. switch (property->m_dataType)
  329. {
  330. case MaterialPropertyDataType::Bool:
  331. result.Combine(StoreNonNumericValues<bool>(outputValue, property, false, context));
  332. break;
  333. case MaterialPropertyDataType::Int:
  334. result.Combine(StoreNumericValues<int32_t>(outputValue, property, 0, context));
  335. break;
  336. case MaterialPropertyDataType::UInt:
  337. result.Combine(StoreNumericValues<uint32_t>(outputValue, property, 0u, context));
  338. break;
  339. case MaterialPropertyDataType::Float:
  340. result.Combine(StoreNumericValues<float>(outputValue, property, 0.0f, context));
  341. break;
  342. case MaterialPropertyDataType::Vector2:
  343. result.Combine(StoreNonNumericValues<Vector2>(outputValue, property, Vector2{0.0f, 0.0f}, context));
  344. break;
  345. case MaterialPropertyDataType::Vector3:
  346. result.Combine(StoreNonNumericValues<Vector3>(outputValue, property, Vector3{0.0f, 0.0f, 0.0f}, context));
  347. break;
  348. case MaterialPropertyDataType::Vector4:
  349. result.Combine(StoreNonNumericValues<Vector4>(outputValue, property, Vector4{0.0f, 0.0f, 0.0f, 0.0f}, context));
  350. break;
  351. case MaterialPropertyDataType::Color:
  352. result.Combine(StoreNonNumericValues<Color>(outputValue, property, AZ::Colors::White, context));
  353. break;
  354. case MaterialPropertyDataType::Image:
  355. case MaterialPropertyDataType::Enum:
  356. result.Combine(StoreNonNumericValues<AZStd::string>(outputValue, property, AZStd::string{""}, context));
  357. break;
  358. }
  359. const MaterialPropertyVisibility defaultVisibility = MaterialPropertyVisibility::Default;
  360. result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::visibility, &property->m_visibility, &defaultVisibility, azrtti_typeid(property->m_visibility), context));
  361. // Support loading a "connection" property as a single entry in m_outputConnections
  362. MaterialPropertySourceData::Connection defaultConnection;
  363. if (property->m_outputConnections.size() == 1)
  364. {
  365. result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::connection, &property->m_outputConnections.back(), &defaultConnection, azrtti_typeid(property->m_outputConnections.back()), context));
  366. }
  367. else
  368. {
  369. result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::connection, &property->m_outputConnections, &defaultConnection, azrtti_typeid(property->m_outputConnections), context));
  370. }
  371. // Enum list
  372. if (property->m_enumValues.size() > 0)
  373. {
  374. result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::enumValues, &property->m_enumValues, nullptr, azrtti_typeid(property->m_enumValues), context));
  375. }
  376. const bool defaultEnumIsUv = false;
  377. result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::enumIsUv, &property->m_enumIsUv, &defaultEnumIsUv, azrtti_typeid(property->m_enumIsUv), context));
  378. if (result.GetProcessing() == JsonSerializationResult::Processing::Completed)
  379. {
  380. return context.Report(result, "Successfully stored property definition.");
  381. }
  382. else
  383. {
  384. return context.Report(result, "Partially stored property definition.");
  385. }
  386. }
  387. JsonSerializationResult::ResultCode JsonMaterialPropertySerializer::LoadVectorLabels(MaterialPropertySourceData* intoProperty,
  388. const rapidjson::Value& inputValue, JsonDeserializerContext& context)
  389. {
  390. namespace JSR = JsonSerializationResult;
  391. using namespace JsonMaterialPropertySerializerInternal;
  392. JSR::ResultCode result(JSR::Tasks::ReadField);
  393. if (inputValue.HasMember(Field::vectorLabels))
  394. {
  395. result.Combine(ContinueLoading(&intoProperty->m_vectorLabels, azrtti_typeid(intoProperty->m_vectorLabels), inputValue[Field::vectorLabels], context));
  396. }
  397. return result;
  398. }
  399. JsonSerializationResult::ResultCode JsonMaterialPropertySerializer::StoreVectorLabels(rapidjson::Value& outputValue,
  400. const MaterialPropertySourceData* property, JsonSerializerContext& context)
  401. {
  402. AZStd::string emptyString;
  403. namespace JSR = JsonSerializationResult;
  404. using namespace JsonMaterialPropertySerializerInternal;
  405. JsonSerializationResult::ResultCode result(JSR::Tasks::WriteValue);
  406. if (!property->m_vectorLabels.empty())
  407. {
  408. result.Combine(ContinueStoringToJsonObjectField(outputValue, Field::vectorLabels, &property->m_vectorLabels, nullptr, azrtti_typeid(property->m_vectorLabels), context));
  409. }
  410. return result;
  411. }
  412. } // namespace RPI
  413. } // namespace AZ