GradientSignalImageTests.cpp 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999
  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 <Tests/GradientSignalTestFixtures.h>
  9. #include <Tests/GradientSignalTestHelpers.h>
  10. #include <AzTest/AzTest.h>
  11. #include <AzCore/Asset/AssetManager.h>
  12. #include <AzCore/Memory/PoolAllocator.h>
  13. #include <AzCore/Math/MathUtils.h>
  14. #include <AzCore/Math/Vector2.h>
  15. #include <AzFramework/Asset/AssetCatalogBus.h>
  16. #include <AzFramework/Components/TransformComponent.h>
  17. #include <AZTestShared/Math/MathTestHelpers.h>
  18. #include <GradientSignal/Components/ImageGradientComponent.h>
  19. #include <GradientSignal/Components/GradientTransformComponent.h>
  20. #include <LmbrCentral/Shape/BoxShapeComponentBus.h>
  21. namespace UnitTest
  22. {
  23. struct GradientSignalImageTestsFixture
  24. : public GradientSignalTest
  25. {
  26. struct PixelTestSetup
  27. {
  28. // How to create the source image
  29. AZ::u32 m_imageSize; // size of the image to create.
  30. AZ::Vector2 m_pixel; // location of the one pixel to set in the image.
  31. uint8_t m_setPixelValues[4]; // values to set the RGBA channels to for the one pixel that's set.
  32. // How to initialize the gradient components
  33. AZ::u32 m_shapeBoundsSize; // size of the gradient bounding box in meters
  34. float m_tiling; // value to use for tilingX and tilingY setting
  35. GradientSignal::WrappingType m_wrappingType; // wrapping type to use on the Gradient Transform
  36. // How to loop through and validate the results
  37. AZ::u32 m_validationSize; // number of points in X and Y to loop through for querying the gradient
  38. float m_stepSize; // step size for walking through X and Y in world space for the gradient query
  39. float m_expectedSetPixelGradientValue; // the gradient value we expect to find for the pixel that's been set
  40. AZ::Vector2 m_expectedPixels[32]; // the list of expected locations that we expect to find a non-zero gradient for
  41. bool m_advancedMode = false;
  42. GradientSignal::ChannelToUse m_channelToUse = GradientSignal::ChannelToUse::Red;
  43. GradientSignal::CustomScaleType m_customScaleType = GradientSignal::CustomScaleType::None;
  44. float m_scaleRangeMin = 0.0f;
  45. float m_scaleRangeMax = 1.0f;
  46. GradientSignal::SamplingType m_samplingType = GradientSignal::SamplingType::Point;
  47. static const AZ::Vector2 EndOfList;
  48. };
  49. void TestPixels(GradientSignal::GradientSampler& sampler, AZ::u32 width, AZ::u32 height, float stepSize, float expectedValue, const AZStd::vector<AZ::Vector3>& expectedPoints)
  50. {
  51. AZStd::vector<AZ::Vector3> foundPoints;
  52. for (float y = 0.0f; y < static_cast<float>(height); y += stepSize)
  53. {
  54. for (float x = 0.0f; x < static_cast<float>(width); x += stepSize)
  55. {
  56. float texelOffset = 0.0f;
  57. GradientSignal::GradientSampleParams params;
  58. params.m_position = AZ::Vector3(x + texelOffset, y + texelOffset, 0.0f);
  59. float value = sampler.GetValue(params);
  60. if (AZ::IsClose(value, expectedValue))
  61. {
  62. foundPoints.push_back(AZ::Vector3(x, y, 0.0f));
  63. }
  64. else
  65. {
  66. EXPECT_TRUE(value == 0.0f);
  67. }
  68. }
  69. }
  70. EXPECT_EQ(expectedPoints.size(), foundPoints.size());
  71. if (expectedPoints.size() == foundPoints.size())
  72. {
  73. for (int point = 0; point < expectedPoints.size(); point++)
  74. {
  75. EXPECT_EQ(static_cast<float>(expectedPoints[point].GetX()), static_cast<float>(foundPoints[point].GetX()));
  76. EXPECT_EQ(static_cast<float>(expectedPoints[point].GetY()), static_cast<float>(foundPoints[point].GetY()));
  77. }
  78. }
  79. }
  80. AZStd::unique_ptr<AZ::Entity> CreateGradientConfigEntity(const PixelTestSetup& test)
  81. {
  82. // Create the base entity
  83. auto entity = CreateEntity();
  84. float shapeHalfBounds = test.m_shapeBoundsSize / 2.0f;
  85. // Create the Image Gradient Component.
  86. GradientSignal::ImageGradientConfig config;
  87. config.m_imageAsset = UnitTest::CreateSpecificPixelImageAsset(
  88. test.m_imageSize, test.m_imageSize,
  89. static_cast<AZ::u32>(test.m_pixel.GetX()), static_cast<AZ::u32>(test.m_pixel.GetY()), test.m_setPixelValues);
  90. config.m_tiling = AZ::Vector2(test.m_tiling);
  91. config.m_channelToUse = test.m_channelToUse;
  92. config.m_customScaleType = test.m_customScaleType;
  93. config.m_scaleRangeMin = test.m_scaleRangeMin;
  94. config.m_scaleRangeMax = test.m_scaleRangeMax;
  95. config.m_samplingType = test.m_samplingType;
  96. entity->CreateComponent<GradientSignal::ImageGradientComponent>(config);
  97. // Create the Gradient Transform Component.
  98. GradientSignal::GradientTransformConfig gradientTransformConfig;
  99. gradientTransformConfig.m_wrappingType = test.m_wrappingType;
  100. entity->CreateComponent<GradientSignal::GradientTransformComponent>(gradientTransformConfig);
  101. LmbrCentral::BoxShapeConfig boxConfig(AZ::Vector3(shapeHalfBounds * 2.0f));
  102. auto boxComponent = entity->CreateComponent(LmbrCentral::AxisAlignedBoxShapeComponentTypeId);
  103. boxComponent->SetConfiguration(boxConfig);
  104. // Create a transform that locates our gradient in the center of our desired mock Shape.
  105. auto transform = entity->CreateComponent<AzFramework::TransformComponent>();
  106. transform->SetWorldTM(AZ::Transform::CreateTranslation(AZ::Vector3(shapeHalfBounds)));
  107. // All components are created, so activate the entity
  108. ActivateEntity(entity.get());
  109. return entity;
  110. }
  111. void RunPixelTest(const PixelTestSetup& test)
  112. {
  113. // Create the gradient entity with the proper configuration
  114. auto entity = CreateGradientConfigEntity(test);
  115. // Build up a list of the locations that we expect to have non-zero values.
  116. AZStd::vector<AZ::Vector3> expectedPoints;
  117. for (const auto& expectedPoint : test.m_expectedPixels)
  118. {
  119. if (expectedPoint == PixelTestSetup::EndOfList)
  120. {
  121. break;
  122. }
  123. expectedPoints.push_back(AZ::Vector3(expectedPoint.GetX(), expectedPoint.GetY(), 0.0f));
  124. }
  125. // Create a gradient sampler and run through a series of points to see if they match expectations.
  126. GradientSignal::GradientSampler gradientSampler;
  127. gradientSampler.m_gradientId = entity->GetId();
  128. TestPixels(
  129. gradientSampler, test.m_validationSize, test.m_validationSize, test.m_stepSize, test.m_expectedSetPixelGradientValue,
  130. expectedPoints);
  131. }
  132. };
  133. const AZ::Vector2 GradientSignalImageTestsFixture::PixelTestSetup::EndOfList = AZ::Vector2(-1.0f);
  134. TEST_F(GradientSignalImageTestsFixture, ImageGradientComponentSinglePixelLower)
  135. {
  136. // Set one pixel, map Gradient 1:1 to lookup space, get same pixel back
  137. PixelTestSetup test =
  138. {
  139. 4, AZ::Vector2( 0, 0 ), {255, 255, 255, 255}, // Source image: 4 x 4 with (0, 0) set to 0xFFFFFFFF
  140. 4, 1.0f, // Mapped Shape: 4 x 4 with tiling (1.0, 1.0), unbounded
  141. GradientSignal::WrappingType::None,
  142. 4, 1.0f, 1.0f, // Validate that in 4 x 4 range, only 0, 0 is set to 1.0
  143. { AZ::Vector2(0, 0), PixelTestSetup::EndOfList }
  144. };
  145. RunPixelTest(test);
  146. }
  147. TEST_F(GradientSignalImageTestsFixture, ImageGradientComponentSinglePixelUpper)
  148. {
  149. // Set one pixel, map Gradient 1:1 to lookup space, get same pixel back
  150. PixelTestSetup test =
  151. {
  152. 4, AZ::Vector2(3, 3), {255, 255, 255, 255}, // Source image: 4 x 4 with (3, 3) set to 0xFFFFFFFF
  153. 4, 1.0f, // Mapped Shape: 4 x 4 with tiling (1.0, 1.0), unbounded
  154. GradientSignal::WrappingType::None,
  155. 4, 1.0f, 1.0f, // Validate that in 4 x 4 range, only 3, 3 is set to 1.0
  156. { AZ::Vector2(3, 3), PixelTestSetup::EndOfList }
  157. };
  158. RunPixelTest(test);
  159. }
  160. TEST_F(GradientSignalImageTestsFixture, ImageGradientComponentSinglePixelUnbounded)
  161. {
  162. // Validate that our image repeats correctly when using "unbounded"
  163. PixelTestSetup test =
  164. {
  165. 4, AZ::Vector2( 0, 0 ), {255, 255, 255, 255}, // Source image: 4 x 4 with (0, 0) set to 0xFFFFFFFF
  166. 4, 1.0f, // Mapped Shape: 4 x 4 with tiling (1.0, 1.0), unbounded
  167. GradientSignal::WrappingType::None,
  168. 8, 1.0f, 1.0f, // Validate that in 8 x 8 range, the pixel repeats every 4 pixels
  169. { AZ::Vector2(0, 0), AZ::Vector2(4, 0), AZ::Vector2(0, 4), AZ::Vector2(4, 4), PixelTestSetup::EndOfList }
  170. };
  171. RunPixelTest(test);
  172. }
  173. TEST_F(GradientSignalImageTestsFixture, ImageGradientComponentSinglePixelClampToZero)
  174. {
  175. // Validate that our image does *not* repeat when using "Clamp to Zero"
  176. PixelTestSetup test =
  177. {
  178. 4, AZ::Vector2(0, 0), {255, 255, 255, 255}, // Source image: 4 x 4 with (0, 0) set to 0xFFFFFFFF
  179. 4, 1.0f, // Mapped Shape: 4 x 4 with tiling (1.0, 1.0), unbounded
  180. GradientSignal::WrappingType::ClampToZero,
  181. 8, 1.0f, 1.0f, // Validate that in 8 x 8 range, the pixel does *not* repeat
  182. { AZ::Vector2(0, 0), PixelTestSetup::EndOfList }
  183. };
  184. RunPixelTest(test);
  185. }
  186. TEST_F(GradientSignalImageTestsFixture, ImageGradientComponentSinglePixelClampToEdge)
  187. {
  188. // Validate that our image stretches the edge correctly when using "Clamp to Edge"
  189. PixelTestSetup test =
  190. {
  191. 4, AZ::Vector2(3, 3), {255, 255, 255, 255}, // Source image: 4 x 4 with (3, 3) set to 0xFFFFFFFF
  192. 4, 1.0f, // Mapped Shape: 4 x 4 with tiling (1.0, 1.0), unbounded
  193. GradientSignal::WrappingType::ClampToEdge,
  194. 8, 1.0f, 1.0f, // Validate that in 8 x 8 range, a corner pixel "stretches" to everything right and down from it
  195. { AZ::Vector2(3, 3), AZ::Vector2(4, 3), AZ::Vector2(5, 3), AZ::Vector2(6, 3), AZ::Vector2(7, 3),
  196. AZ::Vector2(3, 4), AZ::Vector2(4, 4), AZ::Vector2(5, 4), AZ::Vector2(6, 4), AZ::Vector2(7, 4),
  197. AZ::Vector2(3, 5), AZ::Vector2(4, 5), AZ::Vector2(5, 5), AZ::Vector2(6, 5), AZ::Vector2(7, 5),
  198. AZ::Vector2(3, 6), AZ::Vector2(4, 6), AZ::Vector2(5, 6), AZ::Vector2(6, 6), AZ::Vector2(7, 6),
  199. AZ::Vector2(3, 7), AZ::Vector2(4, 7), AZ::Vector2(5, 7), AZ::Vector2(6, 7), AZ::Vector2(7, 7),
  200. PixelTestSetup::EndOfList }
  201. };
  202. RunPixelTest(test);
  203. }
  204. TEST_F(GradientSignalImageTestsFixture, ImageGradientComponentSinglePixelRepeat)
  205. {
  206. // Validate that our image repeats correctly when using "Repeat"
  207. PixelTestSetup test =
  208. {
  209. 4, AZ::Vector2(0, 0), {255, 255, 255, 255}, // Source image: 4 x 4 with (0, 0) set to 0xFFFFFFFF
  210. 4, 1.0f, // Mapped Shape: 4 x 4 with tiling (1.0, 1.0), unbounded
  211. GradientSignal::WrappingType::Repeat,
  212. 8, 1.0f, 1.0f, // Validate that in 8 x 8 range, the pixel repeats every 4 pixels
  213. { AZ::Vector2(0, 0), AZ::Vector2(4, 0), AZ::Vector2(0, 4), AZ::Vector2(4, 4),
  214. PixelTestSetup::EndOfList }
  215. };
  216. RunPixelTest(test);
  217. }
  218. TEST_F(GradientSignalImageTestsFixture, ImageGradientComponentSinglePixelMirror)
  219. {
  220. // Validate that our image repeats correctly when using "Mirror"
  221. PixelTestSetup test =
  222. {
  223. 4, AZ::Vector2(0, 0), {255, 255, 255, 255}, // Source image: 4 x 4 with (0, 0) set to 0xFFFFFFFF
  224. 4, 1.0f, // Mapped Shape: 4 x 4 with tiling (1.0, 1.0), unbounded
  225. GradientSignal::WrappingType::Mirror,
  226. 16, 1.0f, 1.0f, // Validate that in 16 x 16 range, we get a mirrored repeat
  227. { AZ::Vector2(0, 0), AZ::Vector2(7, 0), AZ::Vector2(8, 0), AZ::Vector2(15, 0),
  228. AZ::Vector2(0, 7), AZ::Vector2(7, 7), AZ::Vector2(8, 7), AZ::Vector2(15, 7),
  229. AZ::Vector2(0, 8), AZ::Vector2(7, 8), AZ::Vector2(8, 8), AZ::Vector2(15, 8),
  230. AZ::Vector2(0,15), AZ::Vector2(7,15), AZ::Vector2(8,15), AZ::Vector2(15,15),
  231. PixelTestSetup::EndOfList }
  232. };
  233. RunPixelTest(test);
  234. }
  235. TEST_F(GradientSignalImageTestsFixture, ImageGradientComponentSinglePixelTilingUnbounded)
  236. {
  237. // Validate that our image repeats correctly when using "Unbounded" with a tiling factor.
  238. // Because we advance by 3/4 pixel, we expect to read values from pixels 0, 0, 1, 2, 3, 4 (0), 5 (1).
  239. // So we expect sample pixels 0, 1, and 6 to have values.
  240. PixelTestSetup test =
  241. {
  242. 4, AZ::Vector2(0, 0), {255, 255, 255, 255}, // Source image: 4 x 4 with (0, 0) set to 0xFFFFFFFF
  243. 4, 0.75f, // Mapped Shape: 4 x 4 with tiling (0.75, 0.75), unbounded
  244. GradientSignal::WrappingType::None,
  245. 8, 1.0f, 1.0f, // Validate that in 8 x 8 range, unbounded tiling works
  246. { AZ::Vector2(0, 0), AZ::Vector2(1, 0), AZ::Vector2(6, 0),
  247. AZ::Vector2(0, 1), AZ::Vector2(1, 1), AZ::Vector2(6, 1),
  248. AZ::Vector2(0, 6), AZ::Vector2(1, 6), AZ::Vector2(6, 6),
  249. PixelTestSetup::EndOfList }
  250. };
  251. RunPixelTest(test);
  252. }
  253. TEST_F(GradientSignalImageTestsFixture, ImageGradientComponentSinglePixelTilingRepeat)
  254. {
  255. // Validate that our image repeats correctly when using "Repeat" with a tiling factor.
  256. // Because we advance by 3/4 pixel, but repeat our UVs after 4 pixels, we expect to read values from pixels 0, 0, 1, 2, 0, 0, 1, 2
  257. // So we expect sample pixels 0, 1, 4, and 5 to have values.
  258. PixelTestSetup test =
  259. {
  260. 4, AZ::Vector2(0, 0), {255, 255, 255, 255}, // Source image: 4 x 4 with (0, 0) set to 0xFFFFFFFF
  261. 4, 0.75f, // Mapped Shape: 4 x 4 with tiling (0.75, 0.75), repeating
  262. GradientSignal::WrappingType::Repeat,
  263. 8, 1.0f, 1.0f, // Validate that in 8 x 8 range, repeat tiling works
  264. { AZ::Vector2(0, 0), AZ::Vector2(1, 0), AZ::Vector2(4, 0), AZ::Vector2(5, 0),
  265. AZ::Vector2(0, 1), AZ::Vector2(1, 1), AZ::Vector2(4, 1), AZ::Vector2(5, 1),
  266. AZ::Vector2(0, 4), AZ::Vector2(1, 4), AZ::Vector2(4, 4), AZ::Vector2(5, 4),
  267. AZ::Vector2(0, 5), AZ::Vector2(1, 5), AZ::Vector2(4, 5), AZ::Vector2(5, 5),
  268. PixelTestSetup::EndOfList }
  269. };
  270. RunPixelTest(test);
  271. }
  272. TEST_F(GradientSignalImageTestsFixture, ImageGradientComponentSinglePixelUnboundedScaled)
  273. {
  274. // Validate that our image is sampled correctly when scaling our sampling area
  275. PixelTestSetup test =
  276. {
  277. 4, AZ::Vector2(0, 0), {255, 255, 255, 255}, // Source image: 4 x 4 with (0, 0) set to 0xFFFFFFFF
  278. 4, 1.0f, // Mapped Shape: 4 x 4 with tiling (1.0, 1.0), unbounded
  279. GradientSignal::WrappingType::None,
  280. 4, 0.5f, 1.0f, // Validate that in 4 x 4 range sampled with 8 x 8 pixels, our 1 pixel turns into 4 pixels
  281. { AZ::Vector2(0, 0), AZ::Vector2(0.5f, 0),
  282. AZ::Vector2(0, 0.5f), AZ::Vector2(0.5f, 0.5f),
  283. PixelTestSetup::EndOfList }
  284. };
  285. RunPixelTest(test);
  286. }
  287. TEST_F(GradientSignalImageTestsFixture, ImageGradientComponentAdvancedChannelR)
  288. {
  289. // Set one pixel, map Gradient 1:1 to lookup space, get same pixel back
  290. PixelTestSetup test =
  291. {
  292. 4, AZ::Vector2(0, 0), {200, 150, 100, 50}, // Source image: 4 x 4 with (0, 0) set to different values in each channel
  293. 4, 1.0f, // Mapped Shape: 4 x 4 with tiling (1.0, 1.0), unbounded
  294. GradientSignal::WrappingType::None,
  295. 4, 1.0f, 200.0f/255.0f, // Validate that in 4 x 4 range, only 0, 0 is set to 200/255 (red channel)
  296. { AZ::Vector2(0, 0), PixelTestSetup::EndOfList },
  297. true, // Enabled the advanced mode
  298. GradientSignal::ChannelToUse::Red // Use default Red channel
  299. };
  300. RunPixelTest(test);
  301. }
  302. TEST_F(GradientSignalImageTestsFixture, ImageGradientComponentAdvancedChannelG)
  303. {
  304. // Set one pixel, map Gradient 1:1 to lookup space, get same pixel back
  305. PixelTestSetup test =
  306. {
  307. 4, AZ::Vector2(0, 0), {200, 150, 100, 50}, // Source image: 4 x 4 with (0, 0) set to different values in each channel
  308. 4, 1.0f, // Mapped Shape: 4 x 4 with tiling (1.0, 1.0), unbounded
  309. GradientSignal::WrappingType::None,
  310. 4, 1.0f, 150.0f/255.0f, // Validate that in 4 x 4 range, only 0, 0 is set to 150/255 (green channel)
  311. { AZ::Vector2(0, 0), PixelTestSetup::EndOfList },
  312. true, // Enabled the advanced mode
  313. GradientSignal::ChannelToUse::Green // Use Green channel
  314. };
  315. RunPixelTest(test);
  316. }
  317. TEST_F(GradientSignalImageTestsFixture, ImageGradientComponentAdvancedChannelB)
  318. {
  319. // Set one pixel, map Gradient 1:1 to lookup space, get same pixel back
  320. PixelTestSetup test =
  321. {
  322. 4, AZ::Vector2(0, 0), {200, 150, 100, 50}, // Source image: 4 x 4 with (0, 0) set to different values in each channel
  323. 4, 1.0f, // Mapped Shape: 4 x 4 with tiling (1.0, 1.0), unbounded
  324. GradientSignal::WrappingType::None,
  325. 4, 1.0f, 100.0f/255.0f, // Validate that in 4 x 4 range, only 0, 0 is set to 100/255 (blue channel)
  326. { AZ::Vector2(0, 0), PixelTestSetup::EndOfList },
  327. true, // Enabled the advanced mode
  328. GradientSignal::ChannelToUse::Blue // Use Blue channel
  329. };
  330. RunPixelTest(test);
  331. }
  332. TEST_F(GradientSignalImageTestsFixture, ImageGradientComponentAdvancedChannelA)
  333. {
  334. // Set one pixel, map Gradient 1:1 to lookup space, get same pixel back
  335. PixelTestSetup test =
  336. {
  337. 4, AZ::Vector2(0, 0), {200, 150, 100, 50}, // Source image: 4 x 4 with (0, 0) set to different values in each channel
  338. 4, 1.0f, // Mapped Shape: 4 x 4 with tiling (1.0, 1.0), unbounded
  339. GradientSignal::WrappingType::None,
  340. 4, 1.0f, 50.0f/255.0f, // Validate that in 4 x 4 range, only 0, 0 is set to 50/255 (alpha channel)
  341. { AZ::Vector2(0, 0), PixelTestSetup::EndOfList },
  342. true, // Enabled the advanced mode
  343. GradientSignal::ChannelToUse::Alpha // Use Alpha channel
  344. };
  345. RunPixelTest(test);
  346. }
  347. TEST_F(GradientSignalImageTestsFixture, ImageGradientComponentAdvancedTerrarium)
  348. {
  349. // Set one pixel, map Gradient 1:1 to lookup space, get same pixel back
  350. PixelTestSetup test =
  351. {
  352. 4, AZ::Vector2(0, 0), {200, 150, 100, 50}, // Source image: 4 x 4 with (0, 0) set to different values in each channel
  353. 4, 1.0f, // Mapped Shape: 4 x 4 with tiling (1.0, 1.0), unbounded
  354. GradientSignal::WrappingType::None,
  355. 4, 1.0f, 1.0f, // Validate that in 4 x 4 range, only 0, 0 is set
  356. { AZ::Vector2(0, 0), PixelTestSetup::EndOfList },
  357. true, // Enabled the advanced mode
  358. GradientSignal::ChannelToUse::Terrarium // Use Terrarium format
  359. };
  360. // The expected value is based on Terrarium file format equation:
  361. // (red * 256 + green + blue / 256) - 32768
  362. // More information can be found here: https://www.mapzen.com/blog/terrain-tile-service/
  363. // an RGB of (200, 150, 100) produces a Terrarium world height of (200*256 + 150 + 100/256) - 32768 = 18582.390625
  364. // However, the final gradient value is expected to be 0-1, so the terrarium value range of [-32768, 32768) is mapped by
  365. // adding 32768 and dividing by 65536.
  366. float terrariumBaseHeight =
  367. (test.m_setPixelValues[0] * 256.0f) + test.m_setPixelValues[1] + (test.m_setPixelValues[2] / 256.0f) - 32768.0f;
  368. test.m_expectedSetPixelGradientValue = (terrariumBaseHeight + 32768.0f) / 65536.0f;
  369. RunPixelTest(test);
  370. }
  371. TEST_F(GradientSignalImageTestsFixture, ImageGradientComponentAdvancedManualScale)
  372. {
  373. // Set one pixel, map Gradient 1:1 to lookup space, get same pixel back
  374. const float customMin = 0.0f;
  375. const float customMax = 0.5f;
  376. PixelTestSetup test =
  377. {
  378. 4, AZ::Vector2(0, 0), {32, 64, 16, 0}, // Source image: 4 x 4 with (0, 0) set to different values in each channel
  379. 4, 1.0f, // Mapped Shape: 4 x 4 with tiling (1.0, 1.0), unbounded
  380. GradientSignal::WrappingType::None,
  381. 4, 1.0f, 1.0f, // Validate that in 4 x 4 range, only 0, 0 is set
  382. { AZ::Vector2(0, 0), PixelTestSetup::EndOfList },
  383. true, // Enabled the advanced mode
  384. GradientSignal::ChannelToUse::Red,
  385. GradientSignal::CustomScaleType::Manual, // Enable manual scale
  386. customMin, // Custom min
  387. customMax // Custom max
  388. };
  389. // This test uses the red channel, so we expect the output to be our red value inverse lerped between customMin and customMax.
  390. // Since red is 32/255 (~1/8) and our scale range is 0 - 1/2, the expected value should be ~1/4.
  391. test.m_expectedSetPixelGradientValue = AZ::LerpInverse(customMin, customMax, test.m_setPixelValues[0] / 255.0f);
  392. RunPixelTest(test);
  393. }
  394. TEST_F(GradientSignalImageTestsFixture, ImageGradientComponentAdvancedAutoScale)
  395. {
  396. // Set one pixel, map Gradient 1:1 to lookup space, get same pixel back
  397. PixelTestSetup test =
  398. {
  399. 4, AZ::Vector2(0, 0), {200, 150, 100, 50}, // Source image: 4 x 4 with (0, 0) set to different values in each channel
  400. 4, 1.0f, // Mapped Shape: 4 x 4 with tiling (1.0, 1.0), unbounded
  401. GradientSignal::WrappingType::None,
  402. 4, 1.0f, 1.0f, // Validate that in 4 x 4 range, only 0, 0 is set
  403. { AZ::Vector2(0, 0), PixelTestSetup::EndOfList },
  404. true, // Enabled the advanced mode
  405. GradientSignal::ChannelToUse::Green, // Use Green channel
  406. GradientSignal::CustomScaleType::Auto // Enable Auto scale
  407. };
  408. // Since all of our pixels are set to 0, except one which is set to our specified value, our specified value should get
  409. // auto-scaled to 1.0 since it's the highest value in the image.
  410. test.m_expectedSetPixelGradientValue = 1.0f;
  411. RunPixelTest(test);
  412. }
  413. TEST_F(GradientSignalImageTestsFixture, ImageGradientComponentAdvancedBilinearSamplingClampToZero)
  414. {
  415. // Set one pixel, map Gradient 1:1 to lookup space, get same pixel back
  416. PixelTestSetup test =
  417. {
  418. 4, AZ::Vector2(0.25f, 0.25f), {200, 150, 100, 50}, // Source image: 4 x 4 with (0, 0) set to different values in each channel
  419. 4, 1.0f, // Mapped Shape: 4 x 4 with tiling (1.0, 1.0), unbounded
  420. GradientSignal::WrappingType::ClampToZero,
  421. 4, 0.25f, 1.0f, // Validate that in 4 x 4 range, only 0, 0 is set
  422. { AZ::Vector2(0.25f, 0.25f), PixelTestSetup::EndOfList },
  423. true, // Enabled the advanced mode
  424. GradientSignal::ChannelToUse::Red, // Use Red channel (default)
  425. GradientSignal::CustomScaleType::None, // No custom scale (default)
  426. 0.0f, // Min scale (won't be used since None scale type is set)
  427. 1.0f, // Max scale (won't be used since None scale type is set)
  428. GradientSignal::SamplingType::Bilinear // Enable the bilinear sampling type
  429. };
  430. // Create the gradient entity with the proper configuration
  431. auto entity = CreateGradientConfigEntity(test);
  432. // Create a gradient sampler and run through a series of points to see if they match expectations.
  433. GradientSignal::GradientSampler gradientSampler;
  434. gradientSampler.m_gradientId = entity->GetId();
  435. // Iterate over the points with a 0.25 step size so that we can test
  436. // fractional values to verify the interpolation is working
  437. for (float y = 0.0f; y < static_cast<float>(test.m_validationSize); y += test.m_stepSize)
  438. {
  439. for (float x = 0.0f; x < static_cast<float>(test.m_validationSize); x += test.m_stepSize)
  440. {
  441. GradientSignal::GradientSampleParams params;
  442. params.m_position = AZ::Vector3(x, y, 0.0f);
  443. float value = gradientSampler.GetValue(params);
  444. // Only the 0,0 point has a non-zero value, so anything out of that bounds
  445. // will be 0
  446. if (floor(x) > 0 || floor(y) > 0)
  447. {
  448. EXPECT_TRUE(value == 0.0f);
  449. }
  450. // Otherwise, figure out the expected interpolated value
  451. else
  452. {
  453. // Compute the delta X/Y for sampling pixel
  454. float deltaX = x - floor(x);
  455. float deltaY = y - floor(y);
  456. // Only the 0,0 pixel is set to a specific value, so the other
  457. // points in the grid we will lookup will all be 0
  458. const float valueX0Y0 = test.m_setPixelValues[0] / 255.0f;
  459. const float valueX1Y0 = 0.0f;
  460. const float valueX0Y1 = 0.0f;
  461. const float valueX1Y1 = 0.0f;
  462. const float valueXY0 = AZ::Lerp(valueX0Y0, valueX1Y0, deltaX);
  463. const float valueXY1 = AZ::Lerp(valueX0Y1, valueX1Y1, deltaX);
  464. const float expectedValue = AZ::Lerp(valueXY0, valueXY1, deltaY);
  465. EXPECT_TRUE(AZ::IsClose(value, expectedValue));
  466. }
  467. }
  468. }
  469. }
  470. TEST_F(GradientSignalImageTestsFixture, ImageGradientComponentAdvancedBilinearSamplingClampToEdge)
  471. {
  472. // Set one pixel, map Gradient 1:1 to lookup space, get same pixel back
  473. PixelTestSetup test =
  474. {
  475. 4, AZ::Vector2(0.25f, 0.25f), {200, 150, 100, 50}, // Source image: 4 x 4 with (0, 0) set to different values in each channel
  476. 4, 1.0f, // Mapped Shape: 4 x 4 with tiling (1.0, 1.0), unbounded
  477. GradientSignal::WrappingType::ClampToEdge,
  478. 4, 0.25f, 1.0f, // Validate that in 4 x 4 range, only 0, 0 is set
  479. { AZ::Vector2(0.25f, 0.25f), PixelTestSetup::EndOfList },
  480. true, // Enabled the advanced mode
  481. GradientSignal::ChannelToUse::Red, // Use Red channel (default)
  482. GradientSignal::CustomScaleType::None, // No custom scale (default)
  483. 0.0f, // Min scale (won't be used since None scale type is set)
  484. 1.0f, // Max scale (won't be used since None scale type is set)
  485. GradientSignal::SamplingType::Bilinear // Enable the bilinear sampling type
  486. };
  487. // Create the gradient entity with the proper configuration
  488. auto entity = CreateGradientConfigEntity(test);
  489. // Create a gradient sampler and run through a series of points to see if they match expectations.
  490. GradientSignal::GradientSampler gradientSampler;
  491. gradientSampler.m_gradientId = entity->GetId();
  492. // Iterate over the points with a 0.25 step size so that we can test
  493. // fractional values to verify the interpolation is working
  494. for (float y = 0.0f; y < static_cast<float>(test.m_validationSize); y += test.m_stepSize)
  495. {
  496. for (float x = 0.0f; x < static_cast<float>(test.m_validationSize); x += test.m_stepSize)
  497. {
  498. GradientSignal::GradientSampleParams params;
  499. params.m_position = AZ::Vector3(x, y, 0.0f);
  500. float value = gradientSampler.GetValue(params);
  501. // Only the 0,0 point has a non-zero value, so anything out of that bounds
  502. // will be 0
  503. if (floor(x) > 0 || floor(y) > 0)
  504. {
  505. EXPECT_TRUE(value == 0.0f);
  506. }
  507. // Otherwise, figure out the expected interpolated value
  508. else
  509. {
  510. // Compute the delta X/Y for sampling pixel
  511. float deltaX = x - floor(x);
  512. float deltaY = y - floor(y);
  513. // Only the 0,0 pixel is set to a specific value, so the other
  514. // points in the grid we will lookup will all be 0
  515. const float valueX0Y0 = test.m_setPixelValues[0] / 255.0f;
  516. const float valueX1Y0 = 0.0f;
  517. const float valueX0Y1 = 0.0f;
  518. const float valueX1Y1 = 0.0f;
  519. const float valueXY0 = AZ::Lerp(valueX0Y0, valueX1Y0, deltaX);
  520. const float valueXY1 = AZ::Lerp(valueX0Y1, valueX1Y1, deltaX);
  521. const float expectedValue = AZ::Lerp(valueXY0, valueXY1, deltaY);
  522. EXPECT_TRUE(AZ::IsClose(value, expectedValue));
  523. }
  524. }
  525. }
  526. }
  527. TEST_F(GradientSignalImageTestsFixture, ImageGradientComponentAdvancedBilinearSamplingMirror)
  528. {
  529. // Set one pixel, map Gradient 1:1 to lookup space, get same pixel back
  530. PixelTestSetup test =
  531. {
  532. 4, AZ::Vector2(0.25f, 0.25f), {200, 150, 100, 50}, // Source image: 4 x 4 with (0, 0) set to different values in each channel
  533. 4, 1.0f, // Mapped Shape: 4 x 4 with tiling (1.0, 1.0), unbounded
  534. GradientSignal::WrappingType::Mirror,
  535. 4, 0.25f, 1.0f, // Validate that in 4 x 4 range, only 0, 0 is set
  536. { AZ::Vector2(0.25f, 0.25f), PixelTestSetup::EndOfList },
  537. true, // Enabled the advanced mode
  538. GradientSignal::ChannelToUse::Red, // Use Red channel (default)
  539. GradientSignal::CustomScaleType::None, // No custom scale (default)
  540. 0.0f, // Min scale (won't be used since None scale type is set)
  541. 1.0f, // Max scale (won't be used since None scale type is set)
  542. GradientSignal::SamplingType::Bilinear // Enable the bilinear sampling type
  543. };
  544. // Create the gradient entity with the proper configuration
  545. auto entity = CreateGradientConfigEntity(test);
  546. // Create a gradient sampler and run through a series of points to see if they match expectations.
  547. GradientSignal::GradientSampler gradientSampler;
  548. gradientSampler.m_gradientId = entity->GetId();
  549. // Iterate over the points with a 0.25 step size so that we can test
  550. // fractional values to verify the interpolation is working
  551. for (float y = 0.0f; y < static_cast<float>(test.m_validationSize); y += test.m_stepSize)
  552. {
  553. for (float x = 0.0f; x < static_cast<float>(test.m_validationSize); x += test.m_stepSize)
  554. {
  555. GradientSignal::GradientSampleParams params;
  556. params.m_position = AZ::Vector3(x, y, 0.0f);
  557. float value = gradientSampler.GetValue(params);
  558. // Only the 0,0 point has a non-zero value, so anything out of that bounds
  559. // will be 0
  560. if (floor(x) > 0 || floor(y) > 0)
  561. {
  562. EXPECT_TRUE(value == 0.0f);
  563. }
  564. // Otherwise, figure out the expected interpolated value
  565. else
  566. {
  567. // Compute the delta X/Y for sampling pixel
  568. float deltaX = x - floor(x);
  569. float deltaY = y - floor(y);
  570. // Only the 0,0 pixel is set to a specific value, so the other
  571. // points in the grid we will lookup will all be 0
  572. const float valueX0Y0 = test.m_setPixelValues[0] / 255.0f;
  573. const float valueX1Y0 = 0.0f;
  574. const float valueX0Y1 = 0.0f;
  575. const float valueX1Y1 = 0.0f;
  576. const float valueXY0 = AZ::Lerp(valueX0Y0, valueX1Y0, deltaX);
  577. const float valueXY1 = AZ::Lerp(valueX0Y1, valueX1Y1, deltaX);
  578. const float expectedValue = AZ::Lerp(valueXY0, valueXY1, deltaY);
  579. EXPECT_TRUE(AZ::IsClose(value, expectedValue));
  580. }
  581. }
  582. }
  583. }
  584. TEST_F(GradientSignalImageTestsFixture, ImageGradientComponentAdvancedBilinearSamplingRepeat)
  585. {
  586. // Set one pixel, map Gradient 1:1 to lookup space, get same pixel back
  587. PixelTestSetup test =
  588. {
  589. 4, AZ::Vector2(0.25f, 0.25f), {200, 150, 100, 50}, // Source image: 4 x 4 with (0, 0) set to different values in each channel
  590. 4, 1.0f, // Mapped Shape: 4 x 4 with tiling (1.0, 1.0), unbounded
  591. GradientSignal::WrappingType::Repeat,
  592. 4, 0.25f, 1.0f, // Validate that in 4 x 4 range, only 0, 0 is set
  593. { AZ::Vector2(0.25f, 0.25f), PixelTestSetup::EndOfList },
  594. true, // Enabled the advanced mode
  595. GradientSignal::ChannelToUse::Red, // Use Red channel (default)
  596. GradientSignal::CustomScaleType::None, // No custom scale (default)
  597. 0.0f, // Min scale (won't be used since None scale type is set)
  598. 1.0f, // Max scale (won't be used since None scale type is set)
  599. GradientSignal::SamplingType::Bilinear // Enable the bilinear sampling type
  600. };
  601. // Create the gradient entity with the proper configuration
  602. auto entity = CreateGradientConfigEntity(test);
  603. // Create a gradient sampler and run through a series of points to see if they match expectations.
  604. GradientSignal::GradientSampler gradientSampler;
  605. gradientSampler.m_gradientId = entity->GetId();
  606. // Helper method to return the pixel value given x/y coordinate
  607. // This helper method will handle the repeating logic for us
  608. auto getPixelValue = [test](AZ::u32 pixelX, AZ::u32 pixelY) {
  609. pixelX = pixelX % test.m_validationSize;
  610. pixelY = pixelY % test.m_validationSize;
  611. if (pixelX == 0 && pixelY == 0)
  612. {
  613. return test.m_setPixelValues[0] / 255.0f;
  614. }
  615. else
  616. {
  617. return 0.0f;
  618. }
  619. };
  620. // Iterate over the points with a 0.25 step size so that we can test
  621. // fractional values to verify the interpolation is working
  622. for (float y = 0.0f; y < static_cast<float>(test.m_validationSize); y += test.m_stepSize)
  623. {
  624. for (float x = 0.0f; x < static_cast<float>(test.m_validationSize); x += test.m_stepSize)
  625. {
  626. GradientSignal::GradientSampleParams params;
  627. params.m_position = AZ::Vector3(x, y, 0.0f);
  628. float value = gradientSampler.GetValue(params);
  629. // Compute the delta X/Y for sampling pixel
  630. float deltaX = x - floor(x);
  631. float deltaY = y - floor(y);
  632. // Any x/y pixel value that ends up being 0,0 based on the
  633. // Repeat wrapping type has a specific pixel value.
  634. // All other pixel values are 0.0f, so we use the getPixelValue
  635. // helper method to handle getting the proper pixel value for us.
  636. AZ::u32 pixelX = aznumeric_cast<AZ::u32>(floor(x));
  637. AZ::u32 pixelY = aznumeric_cast<AZ::u32>(floor(y));
  638. const float valueX0Y0 = getPixelValue(pixelX, pixelY);
  639. const float valueX1Y0 = getPixelValue(pixelX + 1, pixelY);;
  640. const float valueX0Y1 = getPixelValue(pixelX, pixelY + 1);
  641. const float valueX1Y1 = getPixelValue(pixelX + 1, pixelY + 1);
  642. const float valueXY0 = AZ::Lerp(valueX0Y0, valueX1Y0, deltaX);
  643. const float valueXY1 = AZ::Lerp(valueX0Y1, valueX1Y1, deltaX);
  644. const float expectedValue = AZ::Lerp(valueXY0, valueXY1, deltaY);
  645. EXPECT_TRUE(AZ::IsClose(value, expectedValue));
  646. }
  647. }
  648. }
  649. TEST_F(GradientSignalImageTestsFixture, GradientTransformComponent_TransformTypes)
  650. {
  651. // Verify that each transform type for the transform component works correctly.
  652. // The setup on this test is rather complex, but the concept is fairly simple. The idea is that we create an
  653. // ImageGradient with a single specific pixel set, and then set our transforms in a way that we can verify that
  654. // the correct transform is used an applied to move the pixel to the place we expect in the sampled output.
  655. // In specific, we create a 3x3 image with the center pixel set. We map it to a 2x2 box, since that will cause 3x3
  656. // samples to be sampled (shapes are inclusive on both sides). This gives us a 1:1 mapping to sample. By default,
  657. // the box centered at (0, 0) means that the one pixel at (0, 0) is set.
  658. // In our tests, we change only the transform(s) that we expect to get used to translate the pixel to (2, 2), and
  659. // validate that only (2, 2) is set in our output.
  660. struct TransformTypeTest
  661. {
  662. GradientSignal::TransformType transformType; // The type of transform to test
  663. float entityWorldTM; // Set the entity's World translation to (x, x, x)
  664. float entityLocalTM; // Set the entity's Local translation to (x, x, x)
  665. float shapeWorldTM; // Set the shape entity's World translation to (x, x, x)
  666. float shapeLocalTM; // Set the shape entity's Local translation to (x, x, x)
  667. int expectedPixelLocation; // The one pixel we expect to be set in the output is (x, x)
  668. };
  669. const TransformTypeTest transformTypeTests[] =
  670. {
  671. // For our basic transform tests, if we set the correct transform's translation, that should directly map to which output
  672. // pixel is set.
  673. { GradientSignal::TransformType::World_ThisEntity, 2.0f, 1.0f, 1.0f, 1.0f, 2 },
  674. { GradientSignal::TransformType::Local_ThisEntity, 1.0f, 2.0f, 1.0f, 1.0f, 2 },
  675. { GradientSignal::TransformType::World_ReferenceEntity, 1.0f, 1.0f, 2.0f, 1.0f, 2 },
  676. { GradientSignal::TransformType::Local_ReferenceEntity, 1.0f, 1.0f, 1.0f, 2.0f, 2 },
  677. // No matter what the other transforms are set to, when using origin we expected our image to be centered at 0, so
  678. // it should be the pixel at (0, 0) that's set, no matter what our transforms are set to.
  679. { GradientSignal::TransformType::World_Origin, 1.0f, 2.0f, 4.0f, 7.0f, 0 },
  680. // Since this is "relative to reference", if we put our reference at 3 and our entity at 5, the relative value
  681. // should be 2.
  682. { GradientSignal::TransformType::Relative, 5.0f, 0.0f, 3.0f, 0.0f, 2 },
  683. };
  684. for (auto test : transformTypeTests)
  685. {
  686. constexpr int dataSize = 8;
  687. // Set our expected output to 0 except for the one pixel we're expecting to find.
  688. AZStd::vector<float> expectedOutput(dataSize * dataSize, 0.0f);
  689. expectedOutput[(test.expectedPixelLocation * dataSize) + test.expectedPixelLocation] = 1.0f;
  690. // Create a reference shape entity.
  691. auto mockShape = CreateEntity();
  692. // Set up the local and world transforms for the reference shape entity.
  693. MockTransformHandler mockShapeTransformHandler;
  694. mockShapeTransformHandler.m_GetLocalTMOutput = AZ::Transform::CreateTranslation(AZ::Vector3(test.shapeLocalTM)); // Used for Local_ReferenceEntity
  695. mockShapeTransformHandler.m_GetWorldTMOutput = AZ::Transform::CreateTranslation(AZ::Vector3(test.shapeWorldTM)); // Used for World_ReferenceEntity
  696. mockShapeTransformHandler.BusConnect(mockShape->GetId());
  697. // Create the mock shape that maps our 3x3 image to a 3x3 sample space in the world.
  698. mockShape->CreateComponent<MockShapeComponent>();
  699. MockShapeComponentHandler mockShapeComponentHandler(mockShape->GetId());
  700. // Create a 2x2 box shape (shapes are inclusive, so that's 3x3 sampling space), so that each pixel in the image directly maps to 1 meter in the box.
  701. mockShapeComponentHandler.m_GetEncompassingAabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f), AZ::Vector3(2.0f));
  702. mockShapeComponentHandler.m_GetLocalBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-1.0f), AZ::Vector3(1.0f));
  703. // Shapes internally just cache the WorldTM, so make sure we've done the same for our test data.
  704. mockShapeComponentHandler.m_GetTransform = mockShapeTransformHandler.m_GetWorldTMOutput;
  705. // Create our gradient entity.
  706. auto entity = CreateEntity();
  707. // Create an ImageGradient with a 3x3 asset with the center pixel set.
  708. GradientSignal::ImageGradientConfig gradientConfig;
  709. uint8_t setPixelValues[] = { 255, 255, 255, 255 };
  710. gradientConfig.m_imageAsset = UnitTest::CreateSpecificPixelImageAsset(3, 3, 1, 1, setPixelValues);
  711. entity->CreateComponent<GradientSignal::ImageGradientComponent>(gradientConfig);
  712. // Create the test GradientTransform
  713. GradientSignal::GradientTransformConfig config;
  714. // We use ClampToZero to ensure that the only pixel that's set in the output is the center of where our image has been placed.
  715. config.m_wrappingType = GradientSignal::WrappingType::ClampToZero;
  716. // Turn on shape references, as these are needed for some of the transform types.
  717. config.m_advancedMode = true;
  718. config.m_allowReference = true;
  719. config.m_shapeReference = mockShape->GetId();
  720. // Set the rest of the parameters.
  721. config.m_transformType = test.transformType;
  722. config.m_frequencyZoom = 1.0f;
  723. config.m_overrideBounds = false;
  724. config.m_overrideTranslate = false;
  725. config.m_overrideRotate = false;
  726. config.m_overrideScale = false;
  727. config.m_is3d = false;
  728. entity->CreateComponent<GradientSignal::GradientTransformComponent>(config);
  729. // Set up the transform on the gradient entity.
  730. MockTransformHandler mockTransformHandler;
  731. mockTransformHandler.m_GetLocalTMOutput = AZ::Transform::CreateTranslation(AZ::Vector3(test.entityLocalTM)); // Used for Local_ThisEntity
  732. mockTransformHandler.m_GetWorldTMOutput = AZ::Transform::CreateTranslation(AZ::Vector3(test.entityWorldTM)); // Used for World_ThisEntity
  733. mockTransformHandler.BusConnect(entity->GetId());
  734. // Put a default shape on our gradient entity. This is only used for previews, so it doesn't matter what it gets set to.
  735. entity->CreateComponent<MockShapeComponent>();
  736. MockShapeComponentHandler mockShapeHandler(entity->GetId());
  737. ActivateEntity(entity.get());
  738. TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
  739. }
  740. }
  741. struct GradientSignalImagePaintingTestsFixture
  742. : public GradientSignalTest
  743. {
  744. AZStd::unique_ptr<AZ::Entity> CreateTestImageGradientEntity(
  745. float boundsSize, uint32_t width, uint32_t height, AZStd::span<const uint8_t> pixels)
  746. {
  747. // Create an entity with a Box Shape
  748. auto entity = CreateTestEntity(boundsSize / 2.0f);
  749. // Create an Image Gradient Component with an image asset based on the passed-in pixel data.
  750. GradientSignal::ImageGradientConfig config;
  751. config.m_imageAsset = CreateImageAssetFromPixelData(width, height, AZ::RHI::Format::R8_UNORM, pixels);
  752. m_imageGradientComponent = entity->CreateComponent<GradientSignal::ImageGradientComponent>(config);
  753. // Create a Gradient Transform Component with default parameters
  754. entity->CreateComponent<GradientSignal::GradientTransformComponent>();
  755. ActivateEntity(entity.get());
  756. EXPECT_EQ(entity->GetState(), AZ::Entity::State::Active);
  757. return entity;
  758. }
  759. // Keep track of the image gradient component so that we have an easy way to get its component ID.
  760. GradientSignal::ImageGradientComponent* m_imageGradientComponent = nullptr;
  761. };
  762. TEST_F(GradientSignalImagePaintingTestsFixture, ImageGradientHasWorkingEyedropper)
  763. {
  764. // This test verifies that the paintbrush eyedropper can read values correctly from an Image Gradient.
  765. // Create an Image Gradient in a box that goes from (0, 0, 0) to (4, 4, 4) in world space.
  766. // We'll create a 4x4 image to map onto it, so each pixel is 1 x 1 m in size.
  767. // The lower left corner of the image maps to (0, 0) and the upper right to (4, 4).
  768. constexpr uint32_t width = 4;
  769. constexpr uint32_t height = 4;
  770. // The pixel values themselves are arbitrary, they're just all set to different values to help verify that the correct pixel
  771. // colors are getting read by the eyedropper at each world location.
  772. AZStd::vector<uint8_t> pixels = {
  773. // 0 - 1 m 1 - 2 m 2 - 3 m 3 - 4 m
  774. 0x11, 0x22, 0x33, 0x44, // 3 - 4 m
  775. 0x55, 0x66, 0x77, 0x88, // 2 - 3 m
  776. 0x99, 0xAA, 0xBB, 0xCC, // 1 - 2 m
  777. 0xDD, 0xEE, 0xFF, 0x00, // 0 - 1 m
  778. };
  779. constexpr float BoxBounds = 4.0f;
  780. auto entity = CreateTestImageGradientEntity(BoxBounds, width, height, pixels);
  781. AzFramework::PaintBrushSettings brushSettings;
  782. AzFramework::PaintBrush paintBrush({ entity->GetId(), m_imageGradientComponent->GetId() });
  783. paintBrush.BeginPaintMode();
  784. AZ::Aabb shapeBounds = AZ::Aabb::CreateNull();
  785. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  786. shapeBounds, entity->GetId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb);
  787. // Loop through each pixel, use the eyedropper in world space to try to look it up, and verify the intensities match.
  788. for (uint32_t pixelIndex = 0; pixelIndex < pixels.size(); pixelIndex++)
  789. {
  790. uint32_t pixelX = pixelIndex % width;
  791. uint32_t pixelY = pixelIndex / width;
  792. auto location = PixelCoordinatesToWorldSpace(pixelX, pixelY, shapeBounds, width, height);
  793. // Use the eyedropper for each world position and verify that it matches the value in the gradient image.
  794. AZ::Color pixelColor = paintBrush.UseEyedropper(location);
  795. EXPECT_EQ(pixels[pixelIndex], pixelColor.GetR8());
  796. }
  797. paintBrush.EndPaintMode();
  798. entity.reset();
  799. }
  800. TEST_F(GradientSignalImagePaintingTestsFixture, ImageGradientCanBePainted)
  801. {
  802. // This test verifies that the paintbrush paint commands can modify values correctly in an Image Gradient.
  803. // Create an Image Gradient in a box that goes from (0, 0, 0) to (4, 4, 4) in world space.
  804. // We'll create a 4x4 image to map onto it, so each pixel is 1 x 1 m in size.
  805. // The lower left corner of the image maps to (0, 0) and the upper right to (4, 4).
  806. constexpr uint32_t width = 4;
  807. constexpr uint32_t height = 4;
  808. AZStd::vector<uint8_t> pixels(width * height);
  809. constexpr float BoxBounds = 4.0f;
  810. auto entity = CreateTestImageGradientEntity(BoxBounds, width, height, pixels);
  811. AZ::Aabb shapeBounds = AZ::Aabb::CreateNull();
  812. LmbrCentral::ShapeComponentRequestsBus::EventResult(
  813. shapeBounds, entity->GetId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb);
  814. // Choose arbitrary but equal color values except for the alpha, which is set to opaque.
  815. AZ::Color brushColor(0.5f, 0.5f, 0.5f, 1.0f);
  816. AzFramework::PaintBrushSettings brushSettings;
  817. brushSettings.SetColor(brushColor);
  818. brushSettings.SetSize(1.0f);
  819. EXPECT_THAT(brushSettings.GetColor(), UnitTest::IsClose(brushColor));
  820. // Find the location of the center of an arbitrary pixel in world space.
  821. constexpr uint32_t paintedPixelX = 2;
  822. constexpr uint32_t paintedPixelY = 1;
  823. auto paintedPixelLocation = PixelCoordinatesToWorldSpace(paintedPixelX, paintedPixelY, shapeBounds, width, height);
  824. AzFramework::PaintBrush paintBrush({ entity->GetId(), m_imageGradientComponent->GetId() });
  825. paintBrush.BeginPaintMode();
  826. // Verify that before painting, the pixel intensity is 0.
  827. AZ::Color startColor = paintBrush.UseEyedropper(paintedPixelLocation);
  828. EXPECT_EQ(startColor.GetR(), 0.0f);
  829. // Paint one pixel with the paintbrush.
  830. paintBrush.BeginBrushStroke(brushSettings);
  831. paintBrush.PaintToLocation(paintedPixelLocation, brushSettings);
  832. paintBrush.EndBrushStroke();
  833. // Loop through each pixel, use the eyedropper in world space to try to look it up, and verify the intensities match expectations.
  834. // Most of the pixels should still be 0.0, but the one painted pixel should be 0.5 (our brush intensity).
  835. for (uint32_t pixelIndex = 0; pixelIndex < pixels.size(); pixelIndex++)
  836. {
  837. uint32_t pixelX = pixelIndex % width;
  838. uint32_t pixelY = pixelIndex / width;
  839. auto queryLocation = PixelCoordinatesToWorldSpace(pixelX, pixelY, shapeBounds, width, height);
  840. // Use the eyedropper for each world position and verify that it matches the expected color.
  841. uint8_t expectedIntensity = (pixelX == paintedPixelX) && (pixelY == paintedPixelY) ? brushColor.GetR8() : (uint8_t)(0);
  842. AZ::Color pixelColor = paintBrush.UseEyedropper(queryLocation);
  843. EXPECT_EQ(pixelColor.GetR8(), expectedIntensity);
  844. }
  845. paintBrush.EndPaintMode();
  846. entity.reset();
  847. }
  848. }