GradientSignalReferencesTests.cpp 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967
  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 <AzCore/std/smart_ptr/make_shared.h>
  10. #include <AzCore/Math/MathUtils.h>
  11. #include <Tests/GradientSignalTestFixtures.h>
  12. #include <GradientSignal/Components/MixedGradientComponent.h>
  13. #include <GradientSignal/Components/ReferenceGradientComponent.h>
  14. #include <GradientSignal/Components/ShapeAreaFalloffGradientComponent.h>
  15. #include <GradientSignal/Components/SurfaceAltitudeGradientComponent.h>
  16. #include <GradientSignal/Components/SurfaceMaskGradientComponent.h>
  17. #include <GradientSignal/Components/SurfaceSlopeGradientComponent.h>
  18. #include <GradientSignal/Components/RandomGradientComponent.h>
  19. #include <GradientSignal/Components/ConstantGradientComponent.h>
  20. #include <GradientSignal/Components/DitherGradientComponent.h>
  21. #include <GradientSignal/Components/ImageGradientComponent.h>
  22. namespace UnitTest
  23. {
  24. struct GradientSignalReferencesTestsFixture
  25. : public GradientSignalTest
  26. {
  27. void TestMixedGradientComponent(int dataSize, const AZStd::vector<float>& layer1Data, const AZStd::vector<float>& layer2Data,
  28. const AZStd::vector<float>& expectedOutput, GradientSignal::MixedGradientLayer::MixingOperation operation, float opacity)
  29. {
  30. auto mockLayer1 = CreateEntity();
  31. const AZ::EntityId id1 = mockLayer1->GetId();
  32. MockGradientArrayRequestsBus mockLayer1GradientRequestsBus(id1, layer1Data, dataSize);
  33. auto mockLayer2 = CreateEntity();
  34. const AZ::EntityId id2 = mockLayer2->GetId();
  35. MockGradientArrayRequestsBus mockLayer2GradientRequestsBus(id2, layer2Data, dataSize);
  36. GradientSignal::MixedGradientConfig config;
  37. GradientSignal::MixedGradientLayer layer;
  38. layer.m_enabled = true;
  39. layer.m_operation = GradientSignal::MixedGradientLayer::MixingOperation::Initialize;
  40. layer.m_gradientSampler.m_gradientId = mockLayer1->GetId();
  41. layer.m_gradientSampler.m_opacity = 1.0f;
  42. config.m_layers.push_back(layer);
  43. layer.m_operation = operation;
  44. layer.m_gradientSampler.m_gradientId = mockLayer2->GetId();
  45. layer.m_gradientSampler.m_opacity = opacity;
  46. config.m_layers.push_back(layer);
  47. auto entity = CreateEntity();
  48. entity->CreateComponent<GradientSignal::MixedGradientComponent>(config);
  49. ActivateEntity(entity.get());
  50. TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
  51. }
  52. void TestSurfaceSlopeGradientComponent(int dataSize, const AZStd::vector<float>& inputAngles, const AZStd::vector<float>& expectedOutput,
  53. float slopeMin, float slopeMax, GradientSignal::SurfaceSlopeGradientConfig::RampType rampType,
  54. float falloffMidpoint, float falloffRange, float falloffStrength)
  55. {
  56. auto surfaceEntity = CreateEntity();
  57. auto mockSurface = surfaceEntity->CreateComponent<MockSurfaceProviderComponent>();
  58. mockSurface->m_bounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f), AZ::Vector3(aznumeric_cast<float>(dataSize)));
  59. mockSurface->m_tags.emplace_back("test_mask");
  60. AzFramework::SurfaceData::SurfacePoint point;
  61. // Fill our mock surface with the correct normal value for each point based on our test angle set.
  62. for (int y = 0; y < dataSize; y++)
  63. {
  64. for (int x = 0; x < dataSize; x++)
  65. {
  66. float angle = AZ::DegToRad(inputAngles[(y * dataSize) + x]);
  67. point.m_position = AZ::Vector3(aznumeric_cast<float>(x), aznumeric_cast<float>(y), 0.0f);
  68. point.m_normal = AZ::Vector3(sinf(angle), 0.0f, cosf(angle));
  69. mockSurface->m_surfacePoints[AZStd::make_pair(static_cast<float>(x), static_cast<float>(y))] =
  70. AZStd::span<const AzFramework::SurfaceData::SurfacePoint>(&point, 1);
  71. }
  72. }
  73. ActivateEntity(surfaceEntity.get());
  74. GradientSignal::SurfaceSlopeGradientConfig config;
  75. config.m_slopeMin = slopeMin;
  76. config.m_slopeMax = slopeMax;
  77. config.m_rampType = rampType;
  78. config.m_smoothStep.m_falloffMidpoint = falloffMidpoint;
  79. config.m_smoothStep.m_falloffRange = falloffRange;
  80. config.m_smoothStep.m_falloffStrength = falloffStrength;
  81. auto entity = CreateEntity();
  82. entity->CreateComponent<GradientSignal::SurfaceSlopeGradientComponent>(config);
  83. ActivateEntity(entity.get());
  84. TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
  85. }
  86. };
  87. TEST_F(GradientSignalReferencesTestsFixture, MixedGradientComponent_OperationInitialize)
  88. {
  89. // Mixed Gradient: Create two layers and set the second one to blend with "Initialize" with an opacity of 0.5f.
  90. // The output should exactly match the second layer at an opacity of 0.5f. (i.e. doesn't blend with layer 1, just overwrites)
  91. constexpr int dataSize = 3;
  92. AZStd::vector<float> inputLayer1 =
  93. {
  94. 0.0f, 0.1f, 0.2f,
  95. 0.4f, 0.5f, 0.6f,
  96. 0.8f, 0.9f, 1.0f
  97. };
  98. AZStd::vector<float> inputLayer2 =
  99. {
  100. 0.06f, 0.16f, 0.26f,
  101. 0.46f, 0.56f, 0.66f,
  102. 0.86f, 0.94f, 0.96f
  103. };
  104. // These values should be layer 2 * 0.5f, with no influence from layer 1.
  105. AZStd::vector<float> expectedOutput =
  106. {
  107. 0.03f, 0.08f, 0.13f,
  108. 0.23f, 0.28f, 0.33f,
  109. 0.43f, 0.47f, 0.48f
  110. };
  111. TestMixedGradientComponent(dataSize, inputLayer1, inputLayer2, expectedOutput, GradientSignal::MixedGradientLayer::MixingOperation::Initialize, 0.5f);
  112. }
  113. TEST_F(GradientSignalReferencesTestsFixture, MixedGradientComponent_OperationNormal)
  114. {
  115. // Mixed Gradient: Create two layers and set the second one to blend with "Normal" with an opacity of 0.5f.
  116. // Unlike "Initialize", this should blend the two layers based on the opacity.
  117. constexpr int dataSize = 3;
  118. AZStd::vector<float> inputLayer1 =
  119. {
  120. 0.0f, 0.1f, 0.2f,
  121. 0.4f, 0.5f, 0.6f,
  122. 0.8f, 0.9f, 1.0f
  123. };
  124. AZStd::vector<float> inputLayer2 =
  125. {
  126. 0.06f, 0.16f, 0.26f,
  127. 0.46f, 0.56f, 0.66f,
  128. 0.86f, 0.94f, 0.96f
  129. };
  130. // These values should be layer 2 * 0.5f, with no influence from layer 1.
  131. AZStd::vector<float> expectedOutput =
  132. {
  133. 0.03f, 0.13f, 0.23f,
  134. 0.43f, 0.53f, 0.63f,
  135. 0.83f, 0.92f, 0.98f
  136. };
  137. TestMixedGradientComponent(dataSize, inputLayer1, inputLayer2, expectedOutput, GradientSignal::MixedGradientLayer::MixingOperation::Normal, 0.5f);
  138. }
  139. TEST_F(GradientSignalReferencesTestsFixture, MixedGradientComponent_OperationMin)
  140. {
  141. // Mixed Gradient: Create two layers and set the second one to blend with "Min".
  142. // Tests a < b, a = b, a > b, and extreme ranges (0's and 1's)
  143. constexpr int dataSize = 3;
  144. AZStd::vector<float> inputLayer1 =
  145. {
  146. 0.0f, 0.1f, 0.2f,
  147. 0.4f, 0.5f, 0.6f,
  148. 0.0f, 1.0f, 1.0f
  149. };
  150. AZStd::vector<float> inputLayer2 =
  151. {
  152. 0.2f, 0.2f, 0.2f,
  153. 0.4f, 0.4f, 0.4f,
  154. 1.0f, 0.0f, 1.0f
  155. };
  156. AZStd::vector<float> expectedOutput =
  157. {
  158. 0.0f, 0.1f, 0.2f, // layer 1 <= layer 2
  159. 0.4f, 0.4f, 0.4f, // layer 2 <= layer 1
  160. 0.0f, 0.0f, 1.0f // test the extremes
  161. };
  162. TestMixedGradientComponent(dataSize, inputLayer1, inputLayer2, expectedOutput, GradientSignal::MixedGradientLayer::MixingOperation::Min, 1.0f);
  163. }
  164. TEST_F(GradientSignalReferencesTestsFixture, MixedGradientComponent_OperationMax)
  165. {
  166. // Mixed Gradient: Create two layers and set the second one to blend with "Max".
  167. // Tests a < b, a = b, a > b, and extreme ranges (0's and 1's)
  168. constexpr int dataSize = 3;
  169. AZStd::vector<float> inputLayer1 =
  170. {
  171. 0.0f, 0.1f, 0.2f,
  172. 0.4f, 0.5f, 0.6f,
  173. 0.0f, 1.0f, 1.0f
  174. };
  175. AZStd::vector<float> inputLayer2 =
  176. {
  177. 0.2f, 0.2f, 0.2f,
  178. 0.4f, 0.4f, 0.4f,
  179. 1.0f, 0.0f, 1.0f
  180. };
  181. AZStd::vector<float> expectedOutput =
  182. {
  183. 0.2f, 0.2f, 0.2f, // layer 2 >= layer 1
  184. 0.4f, 0.5f, 0.6f, // layer 1 >= layer 2
  185. 1.0f, 1.0f, 1.0f // test the extremes
  186. };
  187. TestMixedGradientComponent(dataSize, inputLayer1, inputLayer2, expectedOutput, GradientSignal::MixedGradientLayer::MixingOperation::Max, 1.0f);
  188. }
  189. TEST_F(GradientSignalReferencesTestsFixture, MixedGradientComponent_OperationAdd)
  190. {
  191. // Mixed Gradient: Create two layers and set the second one to blend with "Add".
  192. // Tests a + b = 0, a + b < 1, a + b = 1, and a + b > 1 (clamps to 1)
  193. constexpr int dataSize = 3;
  194. AZStd::vector<float> inputLayer1 =
  195. {
  196. 0.0f, 0.1f, 0.2f,
  197. 0.4f, 0.5f, 0.6f,
  198. 0.8f, 0.9f, 1.0f
  199. };
  200. AZStd::vector<float> inputLayer2 =
  201. {
  202. 0.0f, 0.1f, 0.1f,
  203. 0.4f, 0.4f, 0.4f,
  204. 0.6f, 0.6f, 1.0f
  205. };
  206. AZStd::vector<float> expectedOutput =
  207. {
  208. 0.0f, 0.2f, 0.3f,
  209. 0.8f, 0.9f, 1.0f,
  210. 1.0f, 1.0f, 1.0f
  211. };
  212. TestMixedGradientComponent(dataSize, inputLayer1, inputLayer2, expectedOutput, GradientSignal::MixedGradientLayer::MixingOperation::Add, 1.0f);
  213. }
  214. TEST_F(GradientSignalReferencesTestsFixture, MixedGradientComponent_OperationSubtract)
  215. {
  216. // Mixed Gradient: Create two layers and set the second one to blend with "Subtract".
  217. // Tests a - b = 0, a - b = 1, a - b > 0, and a - b < 0 (clamps to 0)
  218. constexpr int dataSize = 3;
  219. AZStd::vector<float> inputLayer1 =
  220. {
  221. 0.0f, 0.3f, 1.0f,
  222. 0.5f, 0.7f, 1.0f,
  223. 0.5f, 0.4f, 0.3f
  224. };
  225. AZStd::vector<float> inputLayer2 =
  226. {
  227. 0.0f, 0.3f, 0.0f,
  228. 0.4f, 0.5f, 0.6f,
  229. 0.8f, 0.9f, 1.0f
  230. };
  231. AZStd::vector<float> expectedOutput =
  232. {
  233. 0.0f, 0.0f, 1.0f, // a - b = 0, a - b = 0, a - b = 1
  234. 0.1f, 0.2f, 0.4f, // a - b > 0
  235. 0.0f, 0.0f, 0.0f // a - b < 0
  236. };
  237. TestMixedGradientComponent(dataSize, inputLayer1, inputLayer2, expectedOutput, GradientSignal::MixedGradientLayer::MixingOperation::Subtract, 1.0f);
  238. }
  239. TEST_F(GradientSignalReferencesTestsFixture, MixedGradientComponent_OperationMultiply)
  240. {
  241. // Mixed Gradient: Create two layers and set the second one to blend with "Multiply".
  242. // Tests a * 0 = 0, 0 * b = 0, a * 1 = a, 1 * b = b, a * b < 1
  243. constexpr int dataSize = 3;
  244. AZStd::vector<float> inputLayer1 =
  245. {
  246. 0.0f, 0.1f, 0.0f,
  247. 0.4f, 1.0f, 1.0f,
  248. 0.8f, 0.9f, 1.0f
  249. };
  250. AZStd::vector<float> inputLayer2 =
  251. {
  252. 0.0f, 0.0f, 0.2f,
  253. 1.0f, 0.5f, 1.0f,
  254. 0.6f, 0.3f, 0.5f
  255. };
  256. AZStd::vector<float> expectedOutput =
  257. {
  258. 0.0f, 0.0f, 0.0f, // 0 * 0 = 0, a * 0 = 0, 0 * b = 0
  259. 0.4f, 0.5f, 1.0f, // a * 1 = a, 1 * b = b, 1 * 1 = 1
  260. 0.48f, 0.27f, 0.5f // a * b = c
  261. };
  262. TestMixedGradientComponent(dataSize, inputLayer1, inputLayer2, expectedOutput, GradientSignal::MixedGradientLayer::MixingOperation::Multiply, 1.0f);
  263. }
  264. TEST_F(GradientSignalReferencesTestsFixture, MixedGradientComponent_OperationScreen)
  265. {
  266. // Mixed Gradient: Create two layers and set the second one to blend with "Screen".
  267. // Screen is defined as "1 - (1 - a) * (1 - b)"
  268. constexpr int dataSize = 3;
  269. AZStd::vector<float> inputLayer1 =
  270. {
  271. 0.0f, 0.1f, 0.0f,
  272. 0.4f, 1.0f, 1.0f,
  273. 0.8f, 0.9f, 0.2f
  274. };
  275. AZStd::vector<float> inputLayer2 =
  276. {
  277. 0.0f, 0.0f, 0.2f,
  278. 1.0f, 0.5f, 1.0f,
  279. 0.6f, 0.3f, 0.4f
  280. };
  281. AZStd::vector<float> expectedOutput =
  282. {
  283. 0.0f, 0.1f, 0.2f, // 1 - (1 - 0) * (1 - 0) = 0, 1 - (1 - a) * (1 - 0) = a, 1 - (1 - 0) * (1 - b) = b
  284. 1.0f, 1.0f, 1.0f, // 1 - (1 - a) * (1 - 1) = 1, 1 - (1 - 1) * (1 - b) = 1, 1 - (1 - 1) * (1 - 1) = 1
  285. 0.92f, 0.93f, 0.52f // 1 - (1 - a) * (1 - b) = c where c >= a and c >= b
  286. };
  287. TestMixedGradientComponent(dataSize, inputLayer1, inputLayer2, expectedOutput, GradientSignal::MixedGradientLayer::MixingOperation::Screen, 1.0f);
  288. }
  289. TEST_F(GradientSignalReferencesTestsFixture, MixedGradientComponent_OperationAverage)
  290. {
  291. // Mixed Gradient: Create two layers and set the second one to blend with "Average".
  292. // Tests a < b, a > b, a = b, 0, 1
  293. constexpr int dataSize = 3;
  294. AZStd::vector<float> inputLayer1 =
  295. {
  296. 0.0f, 0.1f, 0.2f,
  297. 0.4f, 0.5f, 0.6f,
  298. 0.8f, 0.9f, 1.0f
  299. };
  300. AZStd::vector<float> inputLayer2 =
  301. {
  302. 0.0f, 0.5f, 0.6f,
  303. 0.2f, 0.0f, 0.2f,
  304. 0.8f, 0.9f, 1.0f
  305. };
  306. AZStd::vector<float> expectedOutput =
  307. {
  308. 0.0f, 0.3f, 0.4f, // 0, a < b
  309. 0.3f, 0.25f, 0.4f, // a > b
  310. 0.8f, 0.9f, 1.0f // a = b
  311. };
  312. TestMixedGradientComponent(dataSize, inputLayer1, inputLayer2, expectedOutput, GradientSignal::MixedGradientLayer::MixingOperation::Average, 1.0f);
  313. }
  314. TEST_F(GradientSignalReferencesTestsFixture, MixedGradientComponent_OperationOverlay)
  315. {
  316. // Mixed Gradient: Create two layers and set the second one to blend with "Overlay".
  317. // When a < 0.5, the output should be 2 * a * b
  318. // When a > 0.5, the output should be (1 - (2 * (1 - a) * (1 - b)))
  319. // (At a = 0.5, both formulas are equivalent)
  320. constexpr int dataSize = 3;
  321. AZStd::vector<float> inputLayer1 =
  322. {
  323. 0.0f, 0.1f, 0.2f,
  324. 0.5f, 0.6f, 0.7f,
  325. 1.0f, 0.9f, 1.0f
  326. };
  327. AZStd::vector<float> inputLayer2 =
  328. {
  329. 0.1f, 0.4f, 0.8f,
  330. 0.9f, 0.2f, 0.3f,
  331. 0.7f, 1.0f, 1.0f
  332. };
  333. AZStd::vector<float> expectedOutput =
  334. {
  335. 0.0f, 0.08f, 0.32f, // a < 0.5, 2 * a * b
  336. 0.9f, 0.36f, 0.58f, // a >= 0.5, (1 - (2 * (1 - a) * (1 - b)))
  337. 1.0f, 1.0f, 1.0f // if a > 0.5 and a or b = 1, the result should be 1
  338. };
  339. TestMixedGradientComponent(dataSize, inputLayer1, inputLayer2, expectedOutput, GradientSignal::MixedGradientLayer::MixingOperation::Overlay, 1.0f);
  340. }
  341. TEST_F(GradientSignalReferencesTestsFixture, ReferenceGradientComponent_KnownValues)
  342. {
  343. // Verify that the Reference Gradient successfully "passes through" and provides back the
  344. // exact same values as the gradient it's referencing.
  345. constexpr int dataSize = 2;
  346. AZStd::vector<float> inputData =
  347. {
  348. 0.0f, 1.0f,
  349. 0.2f, 0.1122f
  350. };
  351. AZStd::vector<float> expectedOutput = inputData;
  352. auto mockReference = CreateEntity();
  353. const AZ::EntityId id = mockReference->GetId();
  354. MockGradientArrayRequestsBus mockGradientRequestsBus(id, inputData, dataSize);
  355. // Create a reference gradient with an arbitrary box shape on it.
  356. const float HalfBounds = 64.0f;
  357. auto entity = BuildTestReferenceGradient(HalfBounds, mockReference->GetId());
  358. TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
  359. }
  360. TEST_F(GradientSignalReferencesTestsFixture, ReferenceGradientComponent_CyclicReferences)
  361. {
  362. // Verify that gradient references can validate and disconnect cyclic connections
  363. // Create a constant gradient with an arbitrary box shape on it.
  364. const float HalfBounds = 64.0f;
  365. auto constantGradientEntity = BuildTestConstantGradient(HalfBounds);
  366. // Verify cyclic reference test passes when pointing to gradient generator entity
  367. auto referenceGradientEntity1 = CreateEntity();
  368. GradientSignal::ReferenceGradientConfig referenceGradientConfig1;
  369. referenceGradientConfig1.m_gradientSampler.m_ownerEntityId = referenceGradientEntity1->GetId();
  370. referenceGradientConfig1.m_gradientSampler.m_gradientId = constantGradientEntity->GetId();
  371. referenceGradientEntity1->CreateComponent<GradientSignal::ReferenceGradientComponent>(referenceGradientConfig1);
  372. ActivateEntity(referenceGradientEntity1.get());
  373. EXPECT_TRUE(referenceGradientConfig1.m_gradientSampler.ValidateGradientEntityId());
  374. // Verify cyclic reference test passes when nesting references to gradient generator entity
  375. auto referenceGradientEntity2 = CreateEntity();
  376. GradientSignal::ReferenceGradientConfig referenceGradientConfig2;
  377. referenceGradientConfig2.m_gradientSampler.m_ownerEntityId = referenceGradientEntity2->GetId();
  378. referenceGradientConfig2.m_gradientSampler.m_gradientId = referenceGradientEntity1->GetId();
  379. referenceGradientEntity2->CreateComponent<GradientSignal::ReferenceGradientComponent>(referenceGradientConfig2);
  380. ActivateEntity(referenceGradientEntity2.get());
  381. EXPECT_TRUE(referenceGradientConfig2.m_gradientSampler.ValidateGradientEntityId());
  382. // Verify cyclic reference test fails when referencing self
  383. auto referenceGradientEntity3 = CreateEntity();
  384. GradientSignal::ReferenceGradientConfig referenceGradientConfig3;
  385. referenceGradientConfig3.m_gradientSampler.m_ownerEntityId = referenceGradientEntity3->GetId();
  386. referenceGradientConfig3.m_gradientSampler.m_gradientId = referenceGradientEntity3->GetId();
  387. referenceGradientEntity3->CreateComponent<GradientSignal::ReferenceGradientComponent>(referenceGradientConfig3);
  388. ActivateEntity(referenceGradientEntity3.get());
  389. EXPECT_FALSE(referenceGradientConfig3.m_gradientSampler.ValidateGradientEntityId());
  390. EXPECT_EQ(referenceGradientConfig3.m_gradientSampler.m_gradientId, AZ::EntityId());
  391. // Verify cyclic reference test fails with nested, circular reference
  392. auto referenceGradientEntity4 = CreateEntity();
  393. auto referenceGradientEntity5 = CreateEntity();
  394. auto referenceGradientEntity6 = CreateEntity();
  395. GradientSignal::ReferenceGradientConfig referenceGradientConfig4;
  396. referenceGradientConfig4.m_gradientSampler.m_ownerEntityId = referenceGradientEntity4->GetId();
  397. referenceGradientConfig4.m_gradientSampler.m_gradientId = referenceGradientEntity5->GetId();
  398. referenceGradientEntity4->CreateComponent<GradientSignal::ReferenceGradientComponent>(referenceGradientConfig4);
  399. ActivateEntity(referenceGradientEntity4.get());
  400. GradientSignal::ReferenceGradientConfig referenceGradientConfig5;
  401. referenceGradientConfig5.m_gradientSampler.m_ownerEntityId = referenceGradientEntity5->GetId();
  402. referenceGradientConfig5.m_gradientSampler.m_gradientId = referenceGradientEntity6->GetId();
  403. referenceGradientEntity5->CreateComponent<GradientSignal::ReferenceGradientComponent>(referenceGradientConfig5);
  404. ActivateEntity(referenceGradientEntity5.get());
  405. GradientSignal::ReferenceGradientConfig referenceGradientConfig6;
  406. referenceGradientConfig6.m_gradientSampler.m_ownerEntityId = referenceGradientEntity6->GetId();
  407. referenceGradientConfig6.m_gradientSampler.m_gradientId = referenceGradientEntity4->GetId();
  408. referenceGradientEntity6->CreateComponent<GradientSignal::ReferenceGradientComponent>(referenceGradientConfig6);
  409. ActivateEntity(referenceGradientEntity6.get());
  410. EXPECT_FALSE(referenceGradientConfig6.m_gradientSampler.ValidateGradientEntityId());
  411. EXPECT_EQ(referenceGradientConfig6.m_gradientSampler.m_gradientId, AZ::EntityId());
  412. }
  413. TEST_F(GradientSignalReferencesTestsFixture, ShapeAreaFalloffGradientComponent_ZeroFalloff)
  414. {
  415. // Verify that if we have a 0-width falloff, only the points that fall directly on the shape
  416. // get a 1, and everything else gets a 0
  417. constexpr int dataSize = 3;
  418. AZStd::vector<float> expectedOutput =
  419. {
  420. 1.0f, 1.0f, 0.0f,
  421. 1.0f, 1.0f, 0.0f,
  422. 0.0f, 0.0f, 0.0f
  423. };
  424. // Create an AABB from -1 to 1, so points at coorindates 0 and 1 fall on it, but any points at coordinate 2 won't.
  425. auto entityShape = CreateEntity();
  426. entityShape->CreateComponent<MockShapeComponent>();
  427. MockShapeComponentHandler mockShapeComponentHandler(entityShape->GetId());
  428. mockShapeComponentHandler.m_GetEncompassingAabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-1.0f), AZ::Vector3(1.0f));
  429. GradientSignal::ShapeAreaFalloffGradientConfig config;
  430. config.m_shapeEntityId = entityShape->GetId();
  431. config.m_falloffWidth = 0.0f;
  432. config.m_falloffType = GradientSignal::FalloffType::Outer;
  433. auto entity = CreateEntity();
  434. entity->CreateComponent<GradientSignal::ShapeAreaFalloffGradientComponent>(config);
  435. ActivateEntity(entity.get());
  436. TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
  437. }
  438. TEST_F(GradientSignalReferencesTestsFixture, ShapeAreaFalloffGradientComponent_NonZeroFalloff)
  439. {
  440. // Verify for a range of non-zero falloffs that we get back expected 1-0 values across the falloff range.
  441. // We should get 1 on the shape, and "falloff" down to 0 as we get further away.
  442. // For this test, we put the corner of our shape at (0, 0) so that everything past (0, 0) is falloff.
  443. // Create our test shape from -1 to 0, so we have a corner directly on (0, 0).
  444. auto entityShape = CreateEntity();
  445. entityShape->CreateComponent<MockShapeComponent>();
  446. MockShapeComponentHandler mockShapeComponentHandler(entityShape->GetId());
  447. mockShapeComponentHandler.m_GetEncompassingAabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-1.0f), AZ::Vector3(0.0f));
  448. // Run through a range of falloffs
  449. for (float falloff = 1.0f; falloff <= 5.0f; falloff++)
  450. {
  451. // Choose a dataSize larger than our largest tested falloff value to additionally test that
  452. // we get consistent 0 values eveywhere past the falloff distance.
  453. constexpr int dataSize = 7;
  454. AZStd::vector<float> expectedOutput;
  455. GradientSignal::ShapeAreaFalloffGradientConfig config;
  456. config.m_shapeEntityId = entityShape->GetId();
  457. config.m_falloffWidth = falloff;
  458. config.m_falloffType = GradientSignal::FalloffType::Outer;
  459. // To determine our expected output, we get the distance from (0, 0) and inverse lerp across the falloff - 0 range
  460. // to convert into our expected 0 - 1 output value range.
  461. for (int y = 0; y < dataSize; y++)
  462. {
  463. for (int x = 0; x < dataSize; x++)
  464. {
  465. // Get the number of meters away from the corner of the AABB sitting at (0, 0).
  466. float distance = AZ::Vector3::CreateZero().GetDistance(AZ::Vector3(static_cast<float>(x), static_cast<float>(y), 0.0f));
  467. // We inverse lerp from falloff - 0 so that our values go from 1 at 0 distance down to 0 at falloff distance.
  468. expectedOutput.push_back(AZ::GetClamp(AZ::LerpInverse(config.m_falloffWidth, 0.0f, distance), 0.0f, 1.0f));
  469. }
  470. }
  471. auto entity = CreateEntity();
  472. entity->CreateComponent<GradientSignal::ShapeAreaFalloffGradientComponent>(config);
  473. ActivateEntity(entity.get());
  474. TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
  475. }
  476. }
  477. TEST_F(GradientSignalReferencesTestsFixture, SurfaceAltitudeGradientComponent_PinnedShape)
  478. {
  479. // When using a Surface Altitude Gradient with a pinned shape, the altitude values that
  480. // come back should be based on the AABB range of the pinned shape.
  481. constexpr int dataSize = 2;
  482. AZStd::vector<float> expectedOutput =
  483. {
  484. 0.0f, 0.2f,
  485. 0.5f, 1.0f,
  486. };
  487. // We're pinning a shape, so the bounding box of (0, 0, 0) - (10, 10, 10) will be the one that applies.
  488. auto entityShape = CreateEntity();
  489. entityShape->CreateComponent<MockShapeComponent>();
  490. MockShapeComponentHandler mockShapeComponentHandler(entityShape->GetId());
  491. mockShapeComponentHandler.m_GetEncompassingAabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3::CreateZero(), AZ::Vector3(10.0f));
  492. // Set a different altitude for each point we're going to test. We'll use 0, 2, 5, 10 to test various points along the range.
  493. auto surfaceEntity = CreateEntity();
  494. auto mockSurface = surfaceEntity->CreateComponent<MockSurfaceProviderComponent>();
  495. mockSurface->m_bounds = mockShapeComponentHandler.m_GetEncompassingAabb;
  496. AzFramework::SurfaceData::SurfacePoint mockOutputs[] =
  497. {
  498. { AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3::CreateAxisZ() },
  499. { AZ::Vector3(0.0f, 0.0f, 2.0f), AZ::Vector3::CreateAxisZ() },
  500. { AZ::Vector3(0.0f, 0.0f, 5.0f), AZ::Vector3::CreateAxisZ() },
  501. { AZ::Vector3(0.0f, 0.0f, 10.0f), AZ::Vector3::CreateAxisZ() },
  502. };
  503. mockSurface->m_surfacePoints[AZStd::make_pair(0.0f, 0.0f)] =
  504. AZStd::span<const AzFramework::SurfaceData::SurfacePoint>(&mockOutputs[0], 1);
  505. mockSurface->m_surfacePoints[AZStd::make_pair(1.0f, 0.0f)] =
  506. AZStd::span<const AzFramework::SurfaceData::SurfacePoint>(&mockOutputs[1], 1);
  507. mockSurface->m_surfacePoints[AZStd::make_pair(0.0f, 1.0f)] =
  508. AZStd::span<const AzFramework::SurfaceData::SurfacePoint>(&mockOutputs[2], 1);
  509. mockSurface->m_surfacePoints[AZStd::make_pair(1.0f, 1.0f)] =
  510. AZStd::span<const AzFramework::SurfaceData::SurfacePoint>(&mockOutputs[3], 1);
  511. ActivateEntity(surfaceEntity.get());
  512. // We set the min/max to values other than 0-10 to help validate that they aren't used in the case of the pinned shape.
  513. GradientSignal::SurfaceAltitudeGradientConfig config;
  514. config.m_shapeEntityId = entityShape->GetId();
  515. config.m_altitudeMin = 1.0f;
  516. config.m_altitudeMax = 24.0f;
  517. auto entity = CreateEntity();
  518. entity->CreateComponent<GradientSignal::SurfaceAltitudeGradientComponent>(config);
  519. ActivateEntity(entity.get());
  520. TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
  521. }
  522. TEST_F(GradientSignalReferencesTestsFixture, SurfaceAltitudeGradientComponent_NoShape)
  523. {
  524. // When using a Surface Altitude Gradient without a shape, the altitude values that
  525. // come back should be based on the min / max range of the component.
  526. constexpr int dataSize = 2;
  527. AZStd::vector<float> expectedOutput =
  528. {
  529. 0.0f, 0.2f,
  530. 0.5f, 1.0f,
  531. };
  532. auto entityShape = CreateEntity();
  533. // Set a different altitude for each point we're going to test. We'll use 0, 2, 5, 10 to test various points along the range.
  534. auto surfaceEntity = CreateEntity();
  535. auto mockSurface = surfaceEntity->CreateComponent<MockSurfaceProviderComponent>();
  536. mockSurface->m_bounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f), AZ::Vector3(1.0f));
  537. AzFramework::SurfaceData::SurfacePoint mockOutputs[] = {
  538. { AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3::CreateAxisZ() },
  539. { AZ::Vector3(0.0f, 0.0f, 2.0f), AZ::Vector3::CreateAxisZ() },
  540. { AZ::Vector3(0.0f, 0.0f, 5.0f), AZ::Vector3::CreateAxisZ() },
  541. { AZ::Vector3(0.0f, 0.0f, 10.0f), AZ::Vector3::CreateAxisZ() },
  542. };
  543. mockSurface->m_surfacePoints[AZStd::make_pair(0.0f, 0.0f)] =
  544. AZStd::span<const AzFramework::SurfaceData::SurfacePoint>(&mockOutputs[0], 1);
  545. mockSurface->m_surfacePoints[AZStd::make_pair(1.0f, 0.0f)] =
  546. AZStd::span<const AzFramework::SurfaceData::SurfacePoint>(&mockOutputs[1], 1);
  547. mockSurface->m_surfacePoints[AZStd::make_pair(0.0f, 1.0f)] =
  548. AZStd::span<const AzFramework::SurfaceData::SurfacePoint>(&mockOutputs[2], 1);
  549. mockSurface->m_surfacePoints[AZStd::make_pair(1.0f, 1.0f)] =
  550. AZStd::span<const AzFramework::SurfaceData::SurfacePoint>(&mockOutputs[3], 1);
  551. ActivateEntity(surfaceEntity.get());
  552. // We set the min/max to 0-10, but don't set a shape.
  553. GradientSignal::SurfaceAltitudeGradientConfig config;
  554. config.m_altitudeMin = 0.0f;
  555. config.m_altitudeMax = 10.0f;
  556. auto entity = CreateEntity();
  557. entity->CreateComponent<GradientSignal::SurfaceAltitudeGradientComponent>(config);
  558. ActivateEntity(entity.get());
  559. TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
  560. }
  561. TEST_F(GradientSignalReferencesTestsFixture, SurfaceAltitudeGradientComponent_MissingSurfaceIsZero)
  562. {
  563. // Querying altitude where the surface doesn't exist results in a value of 0.
  564. constexpr int dataSize = 2;
  565. AZStd::vector<float> expectedOutput =
  566. {
  567. 0.0f, 0.0f,
  568. 0.0f, 0.0f,
  569. };
  570. auto entityShape = CreateEntity();
  571. // We set the min/max to -5 - 15 so that a height of 0 would produce a non-zero value.
  572. GradientSignal::SurfaceAltitudeGradientConfig config;
  573. config.m_altitudeMin = -5.0f;
  574. config.m_altitudeMax = 15.0f;
  575. auto entity = CreateEntity();
  576. entity->CreateComponent<GradientSignal::SurfaceAltitudeGradientComponent>(config);
  577. ActivateEntity(entity.get());
  578. TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
  579. }
  580. TEST_F(GradientSignalReferencesTestsFixture, SurfaceAltitudeGradientComponent_ClampToMinMax)
  581. {
  582. // Verify that surface altitudes outside of the min / max range get clamped to 0.0 and 1.0.
  583. constexpr int dataSize = 2;
  584. AZStd::vector<float> expectedOutput =
  585. {
  586. 0.0f, 0.0f,
  587. 1.0f, 1.0f,
  588. };
  589. auto entityShape = CreateEntity();
  590. auto surfaceEntity = CreateEntity();
  591. auto mockSurface = surfaceEntity->CreateComponent<MockSurfaceProviderComponent>();
  592. mockSurface->m_bounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f), AZ::Vector3(1.0f));
  593. AzFramework::SurfaceData::SurfacePoint mockOutputs[] = {
  594. { AZ::Vector3(0.0f, 0.0f, -10.0f), AZ::Vector3::CreateAxisZ() },
  595. { AZ::Vector3(0.0f, 0.0f, -5.0f), AZ::Vector3::CreateAxisZ() },
  596. { AZ::Vector3(0.0f, 0.0f, 15.0f), AZ::Vector3::CreateAxisZ() },
  597. { AZ::Vector3(0.0f, 0.0f, 20.0f), AZ::Vector3::CreateAxisZ() },
  598. };
  599. // Altitude value below min - should result in 0.0f.
  600. mockSurface->m_surfacePoints[AZStd::make_pair(0.0f, 0.0f)] =
  601. AZStd::span<const AzFramework::SurfaceData::SurfacePoint>(&mockOutputs[0], 1);
  602. // Altitude value at exactly min - should result in 0.0f.
  603. mockSurface->m_surfacePoints[AZStd::make_pair(1.0f, 0.0f)] =
  604. AZStd::span<const AzFramework::SurfaceData::SurfacePoint>(&mockOutputs[1], 1);
  605. // Altitude value at exactly max - should result in 1.0f.
  606. mockSurface->m_surfacePoints[AZStd::make_pair(0.0f, 1.0f)] =
  607. AZStd::span<const AzFramework::SurfaceData::SurfacePoint>(&mockOutputs[2], 1);
  608. // Altitude value above max - should result in 1.0f.
  609. mockSurface->m_surfacePoints[AZStd::make_pair(1.0f, 1.0f)] =
  610. AZStd::span<const AzFramework::SurfaceData::SurfacePoint>(&mockOutputs[3], 1);
  611. ActivateEntity(surfaceEntity.get());
  612. // We set the min/max to -5 - 15. By using a range without 0 at either end, and not having 0 as the midpoint,
  613. // it should be easier to verify that we're successfully clamping to 0 and 1.
  614. GradientSignal::SurfaceAltitudeGradientConfig config;
  615. config.m_altitudeMin = -5.0f;
  616. config.m_altitudeMax = 15.0f;
  617. auto entity = CreateEntity();
  618. entity->CreateComponent<GradientSignal::SurfaceAltitudeGradientComponent>(config);
  619. ActivateEntity(entity.get());
  620. TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
  621. }
  622. TEST_F(GradientSignalReferencesTestsFixture, SurfaceMaskGradientComponent_SingleMaskExpectedValues)
  623. {
  624. // When querying a surface that contains the expected mask, verify we get back exactly the
  625. // values we expect for each point.
  626. constexpr int dataSize = 2;
  627. AZStd::vector<float> expectedOutput =
  628. {
  629. 0.0f, 0.2f,
  630. 0.5f, 1.0f,
  631. };
  632. auto surfaceEntity = CreateEntity();
  633. auto mockSurface = surfaceEntity->CreateComponent<MockSurfaceProviderComponent>();
  634. mockSurface->m_bounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f), AZ::Vector3(aznumeric_cast<float>(dataSize)));
  635. mockSurface->m_tags.emplace_back("test_mask");
  636. AzFramework::SurfaceData::SurfacePoint point;
  637. // Fill our mock surface with the test_mask set and the expected gradient value at each point.
  638. for (int y = 0; y < dataSize; y++)
  639. {
  640. for (int x = 0; x < dataSize; x++)
  641. {
  642. point.m_position = AZ::Vector3(aznumeric_cast<float>(x), aznumeric_cast<float>(y), 0.0f);
  643. point.m_normal = AZ::Vector3::CreateAxisZ();
  644. point.m_surfaceTags.clear();
  645. point.m_surfaceTags.emplace_back(AZ_CRC_CE("test_mask"), expectedOutput[(y * dataSize) + x]);
  646. mockSurface->m_surfacePoints[AZStd::make_pair(static_cast<float>(x), static_cast<float>(y))] =
  647. AZStd::span<const AzFramework::SurfaceData::SurfacePoint>(&point, 1);
  648. }
  649. }
  650. ActivateEntity(surfaceEntity.get());
  651. GradientSignal::SurfaceMaskGradientConfig config;
  652. config.m_surfaceTagList.push_back(AZ_CRC_CE("test_mask"));
  653. auto entity = CreateEntity();
  654. entity->CreateComponent<GradientSignal::SurfaceMaskGradientComponent>(config);
  655. ActivateEntity(entity.get());
  656. TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
  657. }
  658. TEST_F(GradientSignalReferencesTestsFixture, SurfaceMaskGradientComponent_NoValues)
  659. {
  660. // When querying a surface that contains no points (either lack of surface, or filtered-out surface tag), verify we get back 0.0f.
  661. // NOTE: Because we're mocking the SurfaceDataSystem, which is the system that contains the mask filtering logic,
  662. // we don't have separate tests for wrong mask vs no points. From the gradient's perspective, these should both
  663. // get no points returned from the system.
  664. constexpr int dataSize = 2;
  665. AZStd::vector<float> expectedOutput =
  666. {
  667. 0.0f, 0.0f,
  668. 0.0f, 0.0f,
  669. };
  670. GradientSignal::SurfaceMaskGradientConfig config;
  671. config.m_surfaceTagList.push_back(AZ_CRC_CE("test_mask"));
  672. auto entity = CreateEntity();
  673. entity->CreateComponent<GradientSignal::SurfaceMaskGradientComponent>(config);
  674. ActivateEntity(entity.get());
  675. TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
  676. }
  677. TEST_F(GradientSignalReferencesTestsFixture, SurfaceSlopeGradientComponent_KnownValues)
  678. {
  679. // When using a Surface Slope Gradient, verify that we get back expected slope values
  680. // for given sets of normals and min / max ranges.
  681. constexpr int dataSize = 3;
  682. AZStd::vector<float> expectedOutput =
  683. {
  684. 1.0f, 0.9f, 0.8f,
  685. 0.6f, 0.5f, 0.4f,
  686. 0.2f, 0.1f, 0.0f
  687. };
  688. AZStd::vector<AZStd::pair<float, float>> minMaxTests =
  689. {
  690. AZStd::make_pair(0.0f, 90.0f), // Test the regular full min/max range (note that values above 90 degrees aren't supported)
  691. AZStd::make_pair(90.0f, 0.0f), // Test an inverted min/max range
  692. AZStd::make_pair(10.0f, 70.0f) // Test an asymmetric range within the full 0 - 90 degree range.
  693. };
  694. AZStd::vector<GradientSignal::SurfaceSlopeGradientConfig::RampType> rampTests =
  695. {
  696. GradientSignal::SurfaceSlopeGradientConfig::RampType::LINEAR_RAMP_DOWN,
  697. GradientSignal::SurfaceSlopeGradientConfig::RampType::LINEAR_RAMP_UP,
  698. };
  699. for (auto rampTest : rampTests)
  700. {
  701. for (auto minMax : minMaxTests)
  702. {
  703. AZStd::vector<float> inputAngles;
  704. const float slopeMin = minMax.first;
  705. const float slopeMax = minMax.second;
  706. // Fill our mock surface with normals that match the correct test angle for each point.
  707. for (int y = 0; y < dataSize; y++)
  708. {
  709. for (int x = 0; x < dataSize; x++)
  710. {
  711. float angle = 0.0f;
  712. // For linear ramps, the input angle should be whatever our desired output is,
  713. // lerped either between slopeMin-slopeMax, or slopeMax-slopeMin, depending on the
  714. // direction of the ramp.
  715. switch (rampTest)
  716. {
  717. case GradientSignal::SurfaceSlopeGradientConfig::RampType::LINEAR_RAMP_DOWN:
  718. angle = AZ::Lerp(slopeMax, slopeMin, expectedOutput[(y * dataSize) + x]);
  719. break;
  720. case GradientSignal::SurfaceSlopeGradientConfig::RampType::LINEAR_RAMP_UP:
  721. angle = AZ::Lerp(slopeMin, slopeMax, expectedOutput[(y * dataSize) + x]);
  722. break;
  723. }
  724. inputAngles.push_back(angle);
  725. }
  726. }
  727. TestSurfaceSlopeGradientComponent(dataSize, inputAngles, expectedOutput,
  728. slopeMin, slopeMax, rampTest, 0.0f, 0.0f, 0.0f);
  729. }
  730. }
  731. }
  732. TEST_F(GradientSignalReferencesTestsFixture, SurfaceSlopeGradientComponent_ClampToMinMax)
  733. {
  734. // Verify that surface slope outside of the min / max range get clamped to 1.0 and 0.0.
  735. // NOTE: We expect the Surface Slope Gradient to produce a signal value of 1.0 at or below the min,
  736. // and 0.0 at or above the max.
  737. constexpr int dataSize = 2;
  738. AZStd::vector<float> inputAngles =
  739. {
  740. 5.0f, 20.0f, // test that values below or at the min clamp to 1.0
  741. 50.0f, 70.0f, // test that values at or above the max clamp to 0.0
  742. };
  743. AZStd::vector<float> expectedOutput =
  744. {
  745. 1.0f, 1.0f,
  746. 0.0f, 0.0f,
  747. };
  748. // We set the min/max to 20 - 50 as a mostly arbitrary choice that represents a range that's not
  749. // centered around the midpoint of a full 0 - 90 degree range.
  750. TestSurfaceSlopeGradientComponent(dataSize, inputAngles, expectedOutput, 20.0f, 50.0f,
  751. GradientSignal::SurfaceSlopeGradientConfig::RampType::LINEAR_RAMP_DOWN, 0.0f, 0.0f, 0.0f);
  752. }
  753. TEST_F(GradientSignalReferencesTestsFixture, SurfaceSlopeGradientComponent_SmoothStep)
  754. {
  755. // Verify that surface slope produces expected results when used with a smooth step.
  756. // Smooth step creates a ramp up and down. We expect the following (within our min/max angle range):
  757. // inputs 0 to (midpoint - range/2): 0
  758. // inputs (midpoint - range/2) to (midpoint - range/2)+softness: ramp up
  759. // inputs (midpoint - range/2)+softness to (midpoint + range/2)-softness: 1
  760. // inputs (midpoint + range/2)-softness) to (midpoint + range/2): ramp down
  761. // inputs (midpoint + range/2) to 1: 0
  762. // We'll test with midpoint = 0.5, range = 0.6, softness = 0.1 so that we have easy ranges to verify.
  763. constexpr int dataSize = 5;
  764. AZStd::vector<float> inputData =
  765. {
  766. 0.00f, 0.05f, 0.10f, 0.15f, 0.20f, // Should all be 0
  767. 0.21f, 0.23f, 0.25f, 0.27f, 0.29f, // Should ramp up
  768. 0.30f, 0.40f, 0.50f, 0.60f, 0.70f, // Should all be 1
  769. 0.71f, 0.73f, 0.75f, 0.77f, 0.79f, // Should ramp down
  770. 0.80f, 0.85f, 0.90f, 0.95f, 1.00f // Should all be 0
  771. };
  772. // For smoothstep ramp curves, we expect the values to be symmetric between the up and down ramp,
  773. // hit 0.5 at the middle of the ramp, and be symmetric on both sides of the midpoint of the ramp.
  774. AZStd::vector<float> expectedOutput =
  775. {
  776. 0.000f, 0.000f, 0.000f, 0.000f, 0.000f, // 0.00 - 0.20 input -> 0.0 output
  777. 0.028f, 0.216f, 0.500f, 0.784f, 0.972f, // 0.21 - 0.29 input -> pre-verified ramp up values
  778. 1.000f, 1.000f, 1.000f, 1.000f, 1.000f, // 0.30 - 0.70 input -> 1.0 output
  779. 0.972f, 0.784f, 0.500f, 0.216f, 0.028f, // 0.71 - 0.79 input -> pre-verified ramp down values
  780. 0.000f, 0.000f, 0.000f, 0.000f, 0.000f, // 0.80 - 1.00 input -> 0.0 output
  781. };
  782. AZStd::vector<float> inputAngles;
  783. // We set the min/max to 20 - 50 as a mostly arbitrary choice that represents a range that's not
  784. // centered around the midpoint of a full 0 - 90 degree range.
  785. const float slopeMin = 20.0f;
  786. const float slopeMax = 50.0f;
  787. // Fill our mock surface with the correct normal value for each point based on our test angle set.
  788. for (int y = 0; y < dataSize; y++)
  789. {
  790. for (int x = 0; x < dataSize; x++)
  791. {
  792. // Map our input values of 0-1 into our slope Min-Max range to create our desired input angles.
  793. inputAngles.push_back(AZ::Lerp(slopeMin, slopeMax, inputData[(y * dataSize) + x]));
  794. }
  795. }
  796. TestSurfaceSlopeGradientComponent(dataSize, inputAngles, expectedOutput, slopeMin, slopeMax,
  797. GradientSignal::SurfaceSlopeGradientConfig::RampType::SMOOTH_STEP, 0.5f, 0.6f, 0.1f);
  798. }
  799. TEST_F(GradientSignalReferencesTestsFixture, SurfaceSlopeGradientComponent_SmoothStep_ClampToZero)
  800. {
  801. // Verify that surface slope outside of the min / max range get clamped to 0.0 when using smooth step.
  802. constexpr int dataSize = 2;
  803. AZStd::vector<float> inputAngles =
  804. {
  805. 5.0f, 20.0f, // test that values below or at the min clamp to 0.0
  806. 50.0f, 70.0f, // test that values at or above the max clamp to 0.0
  807. };
  808. AZStd::vector<float> expectedOutput =
  809. {
  810. 0.0f, 0.0f,
  811. 0.0f, 0.0f,
  812. };
  813. TestSurfaceSlopeGradientComponent(dataSize, inputAngles, expectedOutput, 20.0f, 50.0f,
  814. GradientSignal::SurfaceSlopeGradientConfig::RampType::SMOOTH_STEP, 0.5f, 0.6f, 0.1f);
  815. }
  816. }