3
0

MaterialPropertySerializerTests.cpp 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943
  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 <AzTest/AzTest.h>
  9. #include <Common/RPITestFixture.h>
  10. #include <Common/JsonTestUtils.h>
  11. #include <Atom/RPI.Edit/Material/MaterialTypeSourceData.h>
  12. #include <Atom/RPI.Edit/Material/MaterialPropertySerializer.h>
  13. #include <AzCore/Serialization/Json/JsonUtils.h>
  14. #include <Common/TestUtils.h>
  15. #include <Tests/Serialization/Json/JsonSerializerConformityTests.h>
  16. namespace JsonSerializationTests
  17. {
  18. class MaterialPropertySerializerTestDescription :
  19. public JsonSerializerConformityTestDescriptor<AZ::RPI::MaterialPropertySourceData>
  20. {
  21. public:
  22. void Reflect(AZStd::unique_ptr<AZ::SerializeContext>& context) override
  23. {
  24. AZ::RPI::MaterialTypeSourceData::Reflect(context.get());
  25. AZ::RPI::MaterialPropertySourceData::Reflect(context.get());
  26. AZ::RPI::MaterialPropertyDescriptor::Reflect(context.get());
  27. AZ::RPI::ReflectMaterialDynamicMetadata(context.get());
  28. }
  29. void Reflect(AZStd::unique_ptr<AZ::JsonRegistrationContext>& context) override
  30. {
  31. AZ::RPI::MaterialPropertySourceData::Reflect(context.get());
  32. AZ::RPI::MaterialTypeSourceData::Reflect(context.get());
  33. }
  34. AZStd::shared_ptr<AZ::BaseJsonSerializer> CreateSerializer() override
  35. {
  36. return AZStd::make_shared<AZ::RPI::JsonMaterialPropertySerializer>();
  37. }
  38. AZStd::shared_ptr<AZ::RPI::MaterialPropertySourceData> CreateDefaultInstance() override
  39. {
  40. return AZStd::make_shared<AZ::RPI::MaterialPropertySourceData>();
  41. }
  42. AZStd::shared_ptr<AZ::RPI::MaterialPropertySourceData> CreatePartialDefaultInstance() override
  43. {
  44. auto result = AZStd::make_shared<AZ::RPI::MaterialPropertySourceData>("testProperty");
  45. result->m_dataType = AZ::RPI::MaterialPropertyDataType::Float;
  46. result->m_step = 1.0f;
  47. result->m_value = 0.0f;
  48. return result;
  49. }
  50. AZStd::string_view GetJsonForPartialDefaultInstance() override
  51. {
  52. return R"(
  53. {
  54. "name": "testProperty",
  55. "type": "Float",
  56. "step": 1.0
  57. })";
  58. }
  59. AZStd::shared_ptr<AZ::RPI::MaterialPropertySourceData> CreateFullySetInstance() override
  60. {
  61. auto result = AZStd::make_shared<AZ::RPI::MaterialPropertySourceData>("testProperty");
  62. result->m_description = "description";
  63. result->m_displayName = "display_name";
  64. result->m_dataType = AZ::RPI::MaterialPropertyDataType::Float;
  65. result->m_value = 2.0f;
  66. result->m_enumIsUv = true;
  67. result->m_min = 1.0f;
  68. result->m_max = 10.0f;
  69. result->m_softMin = 2.0f;
  70. result->m_softMax = 9.0f;
  71. result->m_step = 1.5f;
  72. result->m_visibility = AZ::RPI::MaterialPropertyVisibility::Hidden;
  73. result->m_outputConnections.emplace_back(AZ::RPI::MaterialPropertyOutputType::ShaderOption, "o_foo");
  74. return result;
  75. }
  76. AZStd::string_view GetJsonForFullySetInstance() override
  77. {
  78. return R"(
  79. {
  80. "name": "testProperty",
  81. "displayName": "display_name",
  82. "description": "description",
  83. "type": "Float",
  84. "defaultValue": 2.0,
  85. "min": 1.0,
  86. "max": 10.0,
  87. "softMin": 2.0,
  88. "softMax": 9.0,
  89. "step": 1.5,
  90. "visibility": "Hidden",
  91. "connection":
  92. {
  93. "type": "ShaderOption",
  94. "name": "o_foo"
  95. },
  96. "enumIsUv": true
  97. })";
  98. }
  99. void ConfigureFeatures(JsonSerializerConformityTestDescriptorFeatures& features) override
  100. {
  101. features.EnableJsonType(rapidjson::kObjectType);
  102. }
  103. bool AreEqual(
  104. const AZ::RPI::MaterialPropertySourceData& lhs,
  105. const AZ::RPI::MaterialPropertySourceData& rhs) override
  106. {
  107. if (lhs.GetName() != rhs.GetName()) { return false; }
  108. if (lhs.m_description != rhs.m_description) { return false; }
  109. if (lhs.m_displayName != rhs.m_displayName) { return false; }
  110. if (lhs.m_dataType != rhs.m_dataType) { return false; }
  111. if (lhs.m_value != rhs.m_value) { return false; }
  112. if (lhs.m_enumIsUv != rhs.m_enumIsUv) { return false; }
  113. if (lhs.m_min != rhs.m_min) { return false; }
  114. if (lhs.m_max != rhs.m_max) { return false; }
  115. if (lhs.m_softMin != rhs.m_softMin) { return false; }
  116. if (lhs.m_softMax != rhs.m_softMax) { return false; }
  117. if (lhs.m_step != rhs.m_step) { return false; }
  118. if (lhs.m_visibility != rhs.m_visibility) { return false; }
  119. if (lhs.m_outputConnections.size() != rhs.m_outputConnections.size()) { return false; }
  120. for (size_t i = 0; i < lhs.m_outputConnections.size(); ++i)
  121. {
  122. auto& leftConnection = lhs.m_outputConnections[i];
  123. auto& rightConnection = rhs.m_outputConnections[i];
  124. if (leftConnection.m_type != rightConnection.m_type) { return false; }
  125. if (leftConnection.m_name!= rightConnection.m_name) { return false; }
  126. }
  127. return true;
  128. }
  129. };
  130. using MaterialPropertySerializerTestTypes = ::testing::Types<MaterialPropertySerializerTestDescription>;
  131. IF_JSON_CONFORMITY_ENABLED(INSTANTIATE_TYPED_TEST_CASE_P(MaterialPropertySerializerTests, JsonSerializerConformityTests, MaterialPropertySerializerTestTypes));
  132. } // namespace JsonSerializationTests
  133. namespace UnitTest
  134. {
  135. using namespace AZ;
  136. using namespace RPI;
  137. class MaterialPropertySerializerTests
  138. : public RPITestFixture
  139. {
  140. protected:
  141. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  142. void Reflect(ReflectContext* context) override
  143. {
  144. RPITestFixture::Reflect(context);
  145. MaterialPropertySourceData::Reflect(context);
  146. MaterialTypeSourceData::Reflect(context);
  147. }
  148. template<typename T>
  149. void TestStoreToJson(const T& object, AZStd::string_view expectedJson)
  150. {
  151. AZStd::string outputJson;
  152. JsonTestResult storeResult = StoreTestDataToJson(object, outputJson);
  153. EXPECT_EQ(AZ::JsonSerializationResult::Tasks::WriteValue, storeResult.m_jsonResultCode.GetTask());
  154. EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, storeResult.m_jsonResultCode.GetProcessing());
  155. EXPECT_EQ(AZ::JsonSerializationResult::Outcomes::PartialDefaults, storeResult.m_jsonResultCode.GetOutcome());
  156. ExpectSimilarJson(expectedJson, outputJson);
  157. }
  158. };
  159. // "GeneraData" refers to data that isn't dependent on the "type" field
  160. TEST_F(MaterialPropertySerializerTests, LoadAndStoreJson_GeneralData)
  161. {
  162. const AZStd::string inputJson = R"(
  163. {
  164. "name": "testProperty",
  165. "displayName": "Test Property",
  166. "description": "This is a property description",
  167. "type": "Float"
  168. }
  169. )";
  170. MaterialPropertySourceData propertyData;
  171. JsonTestResult loadResult = LoadTestDataFromJson(propertyData, inputJson);
  172. EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask());
  173. EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing());
  174. EXPECT_EQ(AZ::JsonSerializationResult::Outcomes::PartialDefaults, loadResult.m_jsonResultCode.GetOutcome());
  175. EXPECT_EQ("testProperty", propertyData.GetName());
  176. EXPECT_EQ("Test Property", propertyData.m_displayName);
  177. EXPECT_EQ("This is a property description", propertyData.m_description);
  178. EXPECT_EQ(MaterialPropertyDataType::Float, propertyData.m_dataType);
  179. EXPECT_TRUE(loadResult.ContainsMessage("/name", "Success"));
  180. EXPECT_TRUE(loadResult.ContainsMessage("/displayName", "Success"));
  181. EXPECT_TRUE(loadResult.ContainsMessage("/description", "Success"));
  182. EXPECT_TRUE(loadResult.ContainsMessage("/type", "Success"));
  183. EXPECT_FALSE(loadResult.ContainsOutcome(JsonSerializationResult::Outcomes::Skipped));
  184. TestStoreToJson(propertyData, inputJson);
  185. }
  186. // "GeneraData" refers to data that isn't dependent on the "type" field
  187. TEST_F(MaterialPropertySerializerTests, LoadAndStoreJson_DefaultGeneralData)
  188. {
  189. // Note we are keeping id and type because they are required fields
  190. const AZStd::string inputJson = R"(
  191. {
  192. "name": "testProperty",
  193. "type": "Float"
  194. }
  195. )";
  196. MaterialPropertySourceData propertyData;
  197. JsonTestResult loadResult = LoadTestDataFromJson(propertyData, inputJson);
  198. EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask());
  199. EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing());
  200. EXPECT_EQ(AZ::JsonSerializationResult::Outcomes::PartialDefaults, loadResult.m_jsonResultCode.GetOutcome());
  201. EXPECT_TRUE(propertyData.m_displayName.empty());
  202. EXPECT_TRUE(propertyData.m_description.empty());
  203. EXPECT_TRUE(loadResult.ContainsMessage("/name", "Success"));
  204. EXPECT_TRUE(loadResult.ContainsMessage("/type", "Success"));
  205. EXPECT_FALSE(loadResult.ContainsOutcome(JsonSerializationResult::Outcomes::Skipped));
  206. TestStoreToJson(propertyData, inputJson);
  207. }
  208. TEST_F(MaterialPropertySerializerTests, Load_Error_NotAnObject)
  209. {
  210. const AZStd::string inputJson = R"(
  211. []
  212. )";
  213. MaterialPropertySourceData propertyData;
  214. JsonTestResult loadResult = LoadTestDataFromJson(propertyData, inputJson);
  215. EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask());
  216. EXPECT_EQ(AZ::JsonSerializationResult::Processing::Altered, loadResult.m_jsonResultCode.GetProcessing());
  217. EXPECT_EQ(AZ::JsonSerializationResult::Outcomes::Unsupported, loadResult.m_jsonResultCode.GetOutcome());
  218. EXPECT_TRUE(loadResult.ContainsMessage("", "Property definition must be a JSON object"));
  219. }
  220. TEST_F(MaterialPropertySerializerTests, Load_Error_InvalidDataType)
  221. {
  222. const AZStd::string inputJson = R"(
  223. {
  224. "name": "testProperty",
  225. "type": "foo"
  226. }
  227. )";
  228. MaterialPropertySourceData propertyData;
  229. JsonTestResult loadResult = LoadTestDataFromJson(propertyData, inputJson);
  230. EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask());
  231. EXPECT_EQ(AZ::JsonSerializationResult::Processing::PartialAlter, loadResult.m_jsonResultCode.GetProcessing());
  232. EXPECT_EQ(AZ::JsonSerializationResult::Outcomes::Unsupported, loadResult.m_jsonResultCode.GetOutcome());
  233. EXPECT_EQ(AZ::RPI::MaterialPropertyDataType::Invalid, propertyData.m_dataType);
  234. EXPECT_TRUE(loadResult.ContainsMessage("/name", "Success"));
  235. EXPECT_TRUE(loadResult.ContainsMessage("/type", "Enum value could not read"));
  236. }
  237. TEST_F(MaterialPropertySerializerTests, LoadAndStoreJson_NumericType_AllValues)
  238. {
  239. const AZStd::string inputJson = R"(
  240. [
  241. {
  242. "name": "testProperty1",
  243. "type": "Float",
  244. "defaultValue": 0.5,
  245. "min": 0.1,
  246. "max": 1.5,
  247. "softMin": 0.2,
  248. "softMax": 1.0,
  249. "step": 0.05
  250. },
  251. {
  252. "name": "testProperty2",
  253. "type": "Int",
  254. "defaultValue": -1,
  255. "min": -5,
  256. "max": 5,
  257. "softMin": -4,
  258. "softMax": 4,
  259. "step": 1
  260. },
  261. {
  262. "name": "testProperty3",
  263. "type": "UInt",
  264. "defaultValue": 4294901761,
  265. "min": 4294901760,
  266. "max": 4294901775,
  267. "softMin": 4294901761,
  268. "softMax": 4294901774,
  269. "step": 1
  270. }
  271. ]
  272. )";
  273. AZStd::vector<MaterialPropertySourceData> propertyData;
  274. JsonTestResult loadResult = LoadTestDataFromJson(propertyData, inputJson);
  275. EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask());
  276. EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing());
  277. EXPECT_EQ(MaterialPropertyDataType::Float, propertyData[0].m_dataType);
  278. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(0.5f), propertyData[0].m_value);
  279. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(0.1f), propertyData[0].m_min);
  280. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(1.5f), propertyData[0].m_max);
  281. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(0.2f), propertyData[0].m_softMin);
  282. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(1.0f), propertyData[0].m_softMax);
  283. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(0.05f), propertyData[0].m_step);
  284. EXPECT_EQ(MaterialPropertyDataType::Int, propertyData[1].m_dataType);
  285. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(-1), propertyData[1].m_value);
  286. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(-5), propertyData[1].m_min);
  287. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(5), propertyData[1].m_max);
  288. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(-4), propertyData[1].m_softMin);
  289. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(4), propertyData[1].m_softMax);
  290. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(1), propertyData[1].m_step);
  291. EXPECT_EQ(MaterialPropertyDataType::UInt, propertyData[2].m_dataType);
  292. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(0xFFFF0001u), propertyData[2].m_value);
  293. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(0xFFFF0000u), propertyData[2].m_min);
  294. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(0xFFFF000Fu), propertyData[2].m_max);
  295. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(0xFFFF0001u), propertyData[2].m_softMin);
  296. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(0xFFFF000Eu), propertyData[2].m_softMax);
  297. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(1u), propertyData[2].m_step);
  298. for (int i = 0; i < propertyData.size(); ++i)
  299. {
  300. AZStd::string prefix = AZStd::string::format("/%d", i);
  301. EXPECT_TRUE(loadResult.ContainsMessage(prefix + "/name", "Success"));
  302. EXPECT_TRUE(loadResult.ContainsMessage(prefix + "/type", "Success"));
  303. EXPECT_TRUE(loadResult.ContainsMessage(prefix + "/defaultValue", "Success"));
  304. EXPECT_TRUE(loadResult.ContainsMessage(prefix + "/min", "Success"));
  305. EXPECT_TRUE(loadResult.ContainsMessage(prefix + "/max", "Success"));
  306. EXPECT_TRUE(loadResult.ContainsMessage(prefix + "/softMin", "Success"));
  307. EXPECT_TRUE(loadResult.ContainsMessage(prefix + "/softMax", "Success"));
  308. EXPECT_TRUE(loadResult.ContainsMessage(prefix + "/step", "Success"));
  309. }
  310. EXPECT_FALSE(loadResult.ContainsOutcome(JsonSerializationResult::Outcomes::Skipped));
  311. TestStoreToJson(propertyData, inputJson);
  312. }
  313. TEST_F(MaterialPropertySerializerTests, LoadAndStoreJson_NumericType_DefaultValues)
  314. {
  315. const AZStd::string inputJson = R"(
  316. [
  317. {
  318. "name": "testProperty1",
  319. "displayName": "Test Property 1",
  320. "description": "Test",
  321. "type": "Float"
  322. },
  323. {
  324. "name": "testProperty2",
  325. "displayName": "Test Property 2",
  326. "description": "Test",
  327. "type": "Int"
  328. },
  329. {
  330. "name": "testProperty3",
  331. "displayName": "Test Property 3",
  332. "description": "Test",
  333. "type": "UInt"
  334. }
  335. ]
  336. )";
  337. AZStd::vector<MaterialPropertySourceData> propertyData;
  338. JsonTestResult loadResult = LoadTestDataFromJson(propertyData, inputJson);
  339. EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask());
  340. EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing());
  341. EXPECT_EQ(AZ::JsonSerializationResult::Outcomes::PartialDefaults, loadResult.m_jsonResultCode.GetOutcome());
  342. EXPECT_EQ(MaterialPropertyDataType::Float, propertyData[0].m_dataType);
  343. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(0.0f), propertyData[0].m_value);
  344. EXPECT_EQ(MaterialPropertyDataType::Int, propertyData[1].m_dataType);
  345. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(0), propertyData[1].m_value);
  346. EXPECT_EQ(MaterialPropertyDataType::UInt, propertyData[2].m_dataType);
  347. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(0u), propertyData[2].m_value);
  348. for (const MaterialPropertySourceData& property : propertyData)
  349. {
  350. EXPECT_FALSE(property.m_min.IsValid());
  351. EXPECT_FALSE(property.m_max.IsValid());
  352. EXPECT_FALSE(property.m_softMin.IsValid());
  353. EXPECT_FALSE(property.m_softMax.IsValid());
  354. EXPECT_FALSE(property.m_step.IsValid());
  355. }
  356. EXPECT_FALSE(loadResult.ContainsOutcome(JsonSerializationResult::Outcomes::Skipped));
  357. TestStoreToJson(propertyData, inputJson);
  358. }
  359. TEST_F(MaterialPropertySerializerTests, LoadAndStoreJson_VectorLabels_LabelValues)
  360. {
  361. const AZStd::string inputJson = R"(
  362. [
  363. {
  364. "name": "testProperty1",
  365. "type": "Vector2",
  366. "vectorLabels": ["U", "V"],
  367. "defaultValue": [0.6, 0.5]
  368. },
  369. {
  370. "name": "testProperty2",
  371. "type": "Vector4",
  372. "vectorLabels": ["A", "B", "C", "D"],
  373. "defaultValue": [0.3, 0.4, 0.5, 0.6]
  374. }
  375. ]
  376. )";
  377. AZStd::vector<MaterialPropertySourceData> propertyData;
  378. JsonTestResult loadResult = LoadTestDataFromJson(propertyData, inputJson);
  379. EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask());
  380. EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing());
  381. EXPECT_EQ(MaterialPropertyDataType::Vector2, propertyData[0].m_dataType);
  382. EXPECT_TRUE(propertyData[0].m_vectorLabels.size() == 2);
  383. EXPECT_EQ("U", propertyData[0].m_vectorLabels[0]);
  384. EXPECT_EQ("V", propertyData[0].m_vectorLabels[1]);
  385. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(AZ::Vector2{ 0.6f, 0.5f }), propertyData[0].m_value);
  386. EXPECT_EQ(MaterialPropertyDataType::Vector4, propertyData[1].m_dataType);
  387. EXPECT_TRUE(propertyData[1].m_vectorLabels.size() == 4);
  388. EXPECT_EQ("A", propertyData[1].m_vectorLabels[0]);
  389. EXPECT_EQ("B", propertyData[1].m_vectorLabels[1]);
  390. EXPECT_EQ("C", propertyData[1].m_vectorLabels[2]);
  391. EXPECT_EQ("D", propertyData[1].m_vectorLabels[3]);
  392. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(AZ::Vector4{ 0.3f, 0.4f, 0.5f, 0.6f }), propertyData[1].m_value);
  393. TestStoreToJson(propertyData, inputJson);
  394. }
  395. TEST_F(MaterialPropertySerializerTests, LoadAndStoreJson_Visibility)
  396. {
  397. const AZStd::string inputJson = R"(
  398. [
  399. {
  400. "name": "visibilityIsDefault",
  401. "type": "Float"
  402. },
  403. {
  404. "name": "visibilityIsEditable",
  405. "type": "Float",
  406. "visibility": "Enabled"
  407. },
  408. {
  409. "name": "visibilityIsDisabled",
  410. "type": "Float",
  411. "visibility": "Disabled"
  412. },
  413. {
  414. "name": "visibilityIsHidden",
  415. "type": "Float",
  416. "visibility": "Hidden"
  417. }
  418. ]
  419. )";
  420. const AZStd::string expectedOutputJson = R"(
  421. [
  422. {
  423. "name": "visibilityIsDefault",
  424. "type": "Float"
  425. },
  426. {
  427. "name": "visibilityIsEditable",
  428. "type": "Float"
  429. },
  430. {
  431. "name": "visibilityIsDisabled",
  432. "type": "Float",
  433. "visibility": "Disabled"
  434. },
  435. {
  436. "name": "visibilityIsHidden",
  437. "type": "Float",
  438. "visibility": "Hidden"
  439. }
  440. ]
  441. )";
  442. AZStd::vector<MaterialPropertySourceData> propertyData;
  443. JsonTestResult loadResult = LoadTestDataFromJson(propertyData, inputJson);
  444. EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask());
  445. EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing());
  446. EXPECT_EQ(AZ::JsonSerializationResult::Outcomes::PartialDefaults, loadResult.m_jsonResultCode.GetOutcome()); // Because other fields like description are not included
  447. EXPECT_EQ(propertyData[0].m_visibility, MaterialPropertyVisibility::Enabled);
  448. EXPECT_EQ(propertyData[1].m_visibility, MaterialPropertyVisibility::Enabled);
  449. EXPECT_EQ(propertyData[2].m_visibility, MaterialPropertyVisibility::Disabled);
  450. EXPECT_EQ(propertyData[3].m_visibility, MaterialPropertyVisibility::Hidden);
  451. TestStoreToJson(propertyData, expectedOutputJson);
  452. }
  453. TEST_F(MaterialPropertySerializerTests, Load_NumericType_AlternateValueRepresentation)
  454. {
  455. // These alternate representations are supported by the fact that default JSON serializers
  456. // for numeric values use a flexible "best-effort" paradigm
  457. const AZStd::string inputJson = R"(
  458. [
  459. {
  460. "name": "testProperty1",
  461. "type": "Float",
  462. "defaultValue": true,
  463. "min": -1,
  464. "max": "100.5",
  465. "step": "1"
  466. },
  467. {
  468. "name": "testProperty2",
  469. "type": "Int",
  470. "defaultValue": true,
  471. "min": -1.5,
  472. "max": "100",
  473. "step": "1"
  474. },
  475. {
  476. "name": "testProperty3",
  477. "type": "UInt",
  478. "defaultValue": "4294963200",
  479. "min": true,
  480. "max": "0xFFFFFF00",
  481. "step": 2.5
  482. }
  483. ]
  484. )";
  485. AZStd::vector<MaterialPropertySourceData> propertyData;
  486. JsonTestResult loadResult = LoadTestDataFromJson(propertyData, inputJson);
  487. EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask());
  488. EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing());
  489. EXPECT_EQ(MaterialPropertyDataType::Float, propertyData[0].m_dataType);
  490. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(1.0f), propertyData[0].m_value);
  491. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(-1.0f), propertyData[0].m_min);
  492. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(100.5f), propertyData[0].m_max);
  493. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(1.0f), propertyData[0].m_step);
  494. EXPECT_EQ(MaterialPropertyDataType::Int, propertyData[1].m_dataType);
  495. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(1), propertyData[1].m_value);
  496. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(-1), propertyData[1].m_min);
  497. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(100), propertyData[1].m_max);
  498. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(1), propertyData[1].m_step);
  499. EXPECT_EQ(MaterialPropertyDataType::UInt, propertyData[2].m_dataType);
  500. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(0xFFFFF000u), propertyData[2].m_value);
  501. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(1u), propertyData[2].m_min);
  502. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(0xFFFFFF00u), propertyData[2].m_max);
  503. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(2u), propertyData[2].m_step);
  504. EXPECT_FALSE(loadResult.ContainsOutcome(JsonSerializationResult::Outcomes::Skipped));
  505. }
  506. TEST_F(MaterialPropertySerializerTests, LoadAndStoreJson_NonNumericType_AllValues)
  507. {
  508. const AZStd::string inputJson = R"(
  509. [
  510. {
  511. "name": "testProperty1",
  512. "type": "Bool",
  513. "defaultValue": true
  514. },
  515. {
  516. "name": "testProperty2",
  517. "type": "Vector2",
  518. "defaultValue": [0.1, 0.2]
  519. },
  520. {
  521. "name": "testProperty3",
  522. "type": "Vector3",
  523. "defaultValue": [0.3, 0.4, 0.5]
  524. },
  525. {
  526. "name": "testProperty4",
  527. "type": "Vector4",
  528. "defaultValue": [0.6, 0.5, 0.8, 0.4]
  529. },
  530. {
  531. "name": "testProperty5",
  532. "type": "Color",
  533. "defaultValue": [0.1, 0.2, 0.3]
  534. },
  535. {
  536. "name": "testProperty6",
  537. "type": "Image",
  538. "defaultValue": "Default.png"
  539. }
  540. ]
  541. )";
  542. AZStd::vector<MaterialPropertySourceData> propertyData;
  543. JsonTestResult loadResult = LoadTestDataFromJson(propertyData, inputJson);
  544. EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask());
  545. EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing());
  546. EXPECT_EQ(MaterialPropertyDataType::Bool, propertyData[0].m_dataType);
  547. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(true), propertyData[0].m_value);
  548. EXPECT_EQ(MaterialPropertyDataType::Vector2, propertyData[1].m_dataType);
  549. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(AZ::Vector2{0.1f, 0.2f}), propertyData[1].m_value);
  550. EXPECT_EQ(MaterialPropertyDataType::Vector3, propertyData[2].m_dataType);
  551. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(AZ::Vector3{0.3f, 0.4f, 0.5f}), propertyData[2].m_value);
  552. EXPECT_EQ(MaterialPropertyDataType::Vector4, propertyData[3].m_dataType);
  553. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(AZ::Vector4{0.6f, 0.5f, 0.8f, 0.4f}), propertyData[3].m_value);
  554. EXPECT_EQ(MaterialPropertyDataType::Color, propertyData[4].m_dataType);
  555. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(AZ::Color{0.1f, 0.2f, 0.3f, 1.0f}), propertyData[4].m_value);
  556. EXPECT_EQ(MaterialPropertyDataType::Image, propertyData[5].m_dataType);
  557. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(AZStd::string{"Default.png"}), propertyData[5].m_value);
  558. for (int i = 0; i < propertyData.size(); ++i)
  559. {
  560. AZStd::string prefix = AZStd::string::format("/%d", i);
  561. EXPECT_TRUE(loadResult.ContainsMessage(prefix + "/name", "Success"));
  562. EXPECT_TRUE(loadResult.ContainsMessage(prefix + "/type", "Success"));
  563. EXPECT_TRUE(loadResult.ContainsMessage(prefix + "/defaultValue", "Success"));
  564. }
  565. EXPECT_FALSE(loadResult.ContainsOutcome(JsonSerializationResult::Outcomes::Skipped));
  566. TestStoreToJson(propertyData, inputJson);
  567. }
  568. TEST_F(MaterialPropertySerializerTests, LoadAndStoreJson_NonNumericType_DefaultValues)
  569. {
  570. const AZStd::string inputJson = R"(
  571. [
  572. {
  573. "name": "testProperty1",
  574. "type": "Bool"
  575. },
  576. {
  577. "name": "testProperty2",
  578. "type": "Vector2"
  579. },
  580. {
  581. "name": "testProperty3",
  582. "type": "Vector3"
  583. },
  584. {
  585. "name": "testProperty4",
  586. "type": "Vector4"
  587. },
  588. {
  589. "name": "testProperty5",
  590. "type": "Color"
  591. },
  592. {
  593. "name": "testProperty6",
  594. "type": "Image"
  595. }
  596. ]
  597. )";
  598. AZStd::vector<MaterialPropertySourceData> propertyData;
  599. JsonTestResult loadResult = LoadTestDataFromJson(propertyData, inputJson);
  600. EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask());
  601. EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing());
  602. EXPECT_EQ(AZ::JsonSerializationResult::Outcomes::PartialDefaults, loadResult.m_jsonResultCode.GetOutcome());
  603. EXPECT_EQ(MaterialPropertyDataType::Bool, propertyData[0].m_dataType);
  604. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(false), propertyData[0].m_value);
  605. EXPECT_EQ(MaterialPropertyDataType::Vector2, propertyData[1].m_dataType);
  606. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(AZ::Vector2{0.0f, 0.0f}), propertyData[1].m_value);
  607. EXPECT_EQ(MaterialPropertyDataType::Vector3, propertyData[2].m_dataType);
  608. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(AZ::Vector3{0.0f, 0.0f, 0.0f}), propertyData[2].m_value);
  609. EXPECT_EQ(MaterialPropertyDataType::Vector4, propertyData[3].m_dataType);
  610. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(AZ::Vector4{0.0f, 0.0f, 0.0f, 0.0f}), propertyData[3].m_value);
  611. EXPECT_EQ(MaterialPropertyDataType::Color, propertyData[4].m_dataType);
  612. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(AZ::Color{1.0f, 1.0f, 1.0f, 1.0f}), propertyData[4].m_value);
  613. EXPECT_EQ(MaterialPropertyDataType::Image, propertyData[5].m_dataType);
  614. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(AZStd::string{""}), propertyData[5].m_value);
  615. EXPECT_FALSE(loadResult.ContainsOutcome(JsonSerializationResult::Outcomes::Skipped));
  616. TestStoreToJson(propertyData, inputJson);
  617. }
  618. TEST_F(MaterialPropertySerializerTests, Load_NonNumericType_AlternateValueRepresentation)
  619. {
  620. const AZStd::string inputJson = R"(
  621. [
  622. {
  623. "name": "testProperty1",
  624. "type": "Bool",
  625. "defaultValue": 1
  626. },
  627. {
  628. "name": "testProperty2",
  629. "type": "Vector2",
  630. "defaultValue": { "x": 0.4, "y": 0.1 }
  631. },
  632. {
  633. "name": "testProperty3",
  634. "type": "Vector3",
  635. "defaultValue": { "x": 0.4, "y": 0.1, "z": 0.5 }
  636. },
  637. {
  638. "name": "testProperty4",
  639. "type": "Vector4",
  640. "defaultValue": { "x": 0.4, "y": 0.1, "z": 0.5, "w": 0.6 }
  641. },
  642. {
  643. "name": "testProperty5",
  644. "type": "Color",
  645. "defaultValue": { "hex": "FF00FF" }
  646. }
  647. ]
  648. )";
  649. AZStd::vector<MaterialPropertySourceData> propertyData;
  650. JsonTestResult loadResult = LoadTestDataFromJson(propertyData, inputJson);
  651. EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask());
  652. EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing());
  653. EXPECT_EQ(MaterialPropertyDataType::Bool, propertyData[0].m_dataType);
  654. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(true), propertyData[0].m_value);
  655. EXPECT_EQ(MaterialPropertyDataType::Vector2, propertyData[1].m_dataType);
  656. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(AZ::Vector2{0.4f, 0.1f}), propertyData[1].m_value);
  657. EXPECT_EQ(MaterialPropertyDataType::Vector3, propertyData[2].m_dataType);
  658. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(AZ::Vector3{0.4f, 0.1f, 0.5f}), propertyData[2].m_value);
  659. EXPECT_EQ(MaterialPropertyDataType::Vector4, propertyData[3].m_dataType);
  660. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(AZ::Vector4{0.4f, 0.1f, 0.5f, 0.6f}), propertyData[3].m_value);
  661. EXPECT_EQ(MaterialPropertyDataType::Color, propertyData[4].m_dataType);
  662. EXPECT_EQ(AZ::RPI::MaterialPropertyValue(AZ::Color{1.0f, 0.0f, 1.0f, 1.0f}), propertyData[4].m_value);
  663. EXPECT_FALSE(loadResult.ContainsOutcome(JsonSerializationResult::Outcomes::Skipped));
  664. }
  665. TEST_F(MaterialPropertySerializerTests, LoadAndStoreJson_OneConnection)
  666. {
  667. const AZStd::string inputJson = R"(
  668. {
  669. "name": "testProperty",
  670. "type": "Float",
  671. "connection": {
  672. "type": "ShaderOption",
  673. "name": "o_foo"
  674. }
  675. }
  676. )";
  677. MaterialPropertySourceData propertyData;
  678. JsonTestResult loadResult = LoadTestDataFromJson(propertyData, inputJson);
  679. EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask());
  680. EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing());
  681. EXPECT_EQ(1, propertyData.m_outputConnections.size());
  682. EXPECT_EQ(MaterialPropertyOutputType::ShaderOption, propertyData.m_outputConnections[0].m_type);
  683. EXPECT_EQ("o_foo", propertyData.m_outputConnections[0].m_name);
  684. EXPECT_TRUE(loadResult.ContainsMessage("/connection/type", "Success"));
  685. EXPECT_TRUE(loadResult.ContainsMessage("/connection/name", "Success"));
  686. EXPECT_FALSE(loadResult.ContainsOutcome(JsonSerializationResult::Outcomes::Skipped));
  687. TestStoreToJson(propertyData, inputJson);
  688. }
  689. TEST_F(MaterialPropertySerializerTests, LoadUsingOldFormat)
  690. {
  691. // Tests backward compatibility for when "id" was the key instead of "name", for both the property and its connections.
  692. const AZStd::string inputJson = R"(
  693. {
  694. "id": "testProperty",
  695. "type": "Float",
  696. "connection": {
  697. "type": "ShaderOption",
  698. "id": "o_foo"
  699. }
  700. }
  701. )";
  702. MaterialPropertySourceData propertyData;
  703. JsonTestResult loadResult = LoadTestDataFromJson(propertyData, inputJson);
  704. EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask());
  705. EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing());
  706. EXPECT_EQ("testProperty", propertyData.GetName());
  707. EXPECT_EQ(1, propertyData.m_outputConnections.size());
  708. EXPECT_EQ(MaterialPropertyOutputType::ShaderOption, propertyData.m_outputConnections[0].m_type);
  709. EXPECT_EQ("o_foo", propertyData.m_outputConnections[0].m_name);
  710. EXPECT_TRUE(loadResult.ContainsMessage("/connection/type", "Success"));
  711. EXPECT_TRUE(loadResult.ContainsMessage("/connection/id", "Success"));
  712. EXPECT_FALSE(loadResult.ContainsOutcome(JsonSerializationResult::Outcomes::Skipped));
  713. }
  714. TEST_F(MaterialPropertySerializerTests, LoadAndStoreJson_MultipleConnections)
  715. {
  716. const AZStd::string inputJson = R"(
  717. {
  718. "name": "testProperty",
  719. "type": "Float",
  720. "connection": [
  721. {
  722. "type": "ShaderInput",
  723. "name": "o_foo"
  724. },
  725. {
  726. "type": "ShaderOption",
  727. "name": "o_bar"
  728. }
  729. ]
  730. }
  731. )";
  732. MaterialPropertySourceData propertyData;
  733. JsonTestResult loadResult = LoadTestDataFromJson(propertyData, inputJson);
  734. EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask());
  735. EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing());
  736. EXPECT_EQ(2, propertyData.m_outputConnections.size());
  737. EXPECT_EQ(MaterialPropertyOutputType::ShaderInput, propertyData.m_outputConnections[0].m_type);
  738. EXPECT_EQ("o_foo", propertyData.m_outputConnections[0].m_name);
  739. EXPECT_EQ(MaterialPropertyOutputType::ShaderOption, propertyData.m_outputConnections[1].m_type);
  740. EXPECT_EQ("o_bar", propertyData.m_outputConnections[1].m_name);
  741. EXPECT_TRUE(loadResult.ContainsMessage("/connection/0/type", "Success"));
  742. EXPECT_TRUE(loadResult.ContainsMessage("/connection/0/name", "Success"));
  743. EXPECT_TRUE(loadResult.ContainsMessage("/connection/1/type", "Success"));
  744. EXPECT_TRUE(loadResult.ContainsMessage("/connection/1/name", "Success"));
  745. EXPECT_FALSE(loadResult.ContainsOutcome(JsonSerializationResult::Outcomes::Skipped));
  746. TestStoreToJson(propertyData, inputJson);
  747. }
  748. TEST_F(MaterialPropertySerializerTests, Load_Warning_SkippedTopLevelField)
  749. {
  750. // "conection" is misspelled
  751. const AZStd::string inputJson = R"(
  752. {
  753. "name": "testProperty",
  754. "type": "Float",
  755. "conection": [
  756. {
  757. "type": "ShaderInput",
  758. "name": "o_foo"
  759. }
  760. ]
  761. }
  762. )";
  763. MaterialPropertySourceData propertyData;
  764. JsonTestResult loadResult = LoadTestDataFromJson(propertyData, inputJson);
  765. EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask());
  766. EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing());
  767. EXPECT_EQ(propertyData.GetName(), "testProperty");
  768. EXPECT_EQ(propertyData.m_dataType, MaterialPropertyDataType::Float);
  769. EXPECT_EQ(propertyData.m_outputConnections.size(), 0);
  770. EXPECT_TRUE(loadResult.ContainsMessage("/conection", "skip"));
  771. }
  772. TEST_F(MaterialPropertySerializerTests, Load_Warning_SkippedConnectionField)
  773. {
  774. // "nam" is misspelled
  775. const AZStd::string inputJson = R"(
  776. {
  777. "name": "testProperty",
  778. "type": "Float",
  779. "connection": [
  780. {
  781. "type": "ShaderInput",
  782. "nam": "o_foo"
  783. }
  784. ]
  785. }
  786. )";
  787. MaterialPropertySourceData propertyData;
  788. JsonTestResult loadResult = LoadTestDataFromJson(propertyData, inputJson);
  789. EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask());
  790. EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing());
  791. EXPECT_EQ(propertyData.GetName(), "testProperty");
  792. EXPECT_EQ(propertyData.m_dataType, MaterialPropertyDataType::Float);
  793. EXPECT_EQ(propertyData.m_outputConnections.size(), 1);
  794. EXPECT_EQ(propertyData.m_outputConnections[0].m_name, "");
  795. EXPECT_EQ(propertyData.m_outputConnections[0].m_type, MaterialPropertyOutputType::ShaderInput);
  796. EXPECT_TRUE(loadResult.ContainsMessage("/connection/0/nam", "skip"));
  797. }
  798. }